* chore(#604): rename get-shit-done/ runtime directory to gsd-core/ Renames the installed runtime directory `get-shit-done/` to `gsd-core/` so the on-disk name matches the package (`@opengsd/gsd-core`), repo, and binary (`gsd-tools`). The npm package name and binary are unchanged; npx/npm consumers are unaffected. Mechanical (bulk, ~90% of the diff): - `git mv get-shit-done gsd-core` - Swept path/identifier references across the repo via `perl -pe 's/get-shit-done(?!-\w)/gsd-core/g'`. The negative lookahead preserves the five legitimate slug variants that are NOT the directory: get-shit-done-{OLD,cc,classic,cli,redux} (old package/repo names). - Build/manifest wiring: package.json (bin, files, coverage globs), tsconfig.build.json (outDir), ~86 .gitignore build-output entries, stryker.config.mjs, scan-ignore files, install.js path strings. - Frozen (not rewritten): CHANGELOG.md history; translated docs (README.<locale>.md and docs/{ja-JP,ko-KR,pt-BR,zh-CN}/). New logic (review here): - src/installer-migrations/003-rename-get-shit-done-to-gsd-core.cts: a proper ADR-0008 installer migration. On upgrade it walks the legacy `~/.claude/get-shit-done/` tree, classifies each file via the prior install manifest, and emits remove-managed / backup-and-remove for managed files while PRESERVING unknown user-added files. Symlink-safe (skips a symlinked root and symlinked entries; bounds-checks every path under configDir). The framework rolls back on install failure. Emptied dirs may remain (framework has no recursive dir-removal primitive) — documented. - scripts/lint-legacy-dir-name.cjs: CI regression guard forbidding the bare `get-shit-done` directory token (split token to avoid self-match; case- insensitive; `(?!-\w)` lookahead allows the slug variants; allowlists CHANGELOG, translated docs, and `gsd-allow-legacy-name` marker lines). Wired into the lint-tests CI job. - Restored scripts/lint-package-identity-drift.cjs detection regexes (the mechanical sweep had wrongly rewritten the old-name patterns it exists to detect) and marked them as intentional legacy references. - TDD tests for the migration and the guard; do.md slash-command guard regex tightened so a `/gsd-core/bin` path segment is not mistaken for a command; changeset + docs/installer-migrations.md row added. Breaking: the installed runtime path moves `~/.claude/get-shit-done/` -> `~/.claude/gsd-core/`. Migration 003 removes the stale legacy dir's managed files (preserving user files) on upgrade. Users with custom hooks/configs hardcoding the old path must update them. Closes #604 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unsweep pending changesets + allowlist injection-example docs CI fixes for the rename PR: - Do not sweep pending .changeset/*.md (ephemeral release-note fragments, like CHANGELOG); reverted those body edits so 5 pre-existing malformed fragments (missing type/pr) no longer enter the PR diff and trip docs-lint. Allowlisted .changeset/ in the legacy-name guard accordingly. - Allowlisted TEST-EXAMPLES.md and docs/explanation/security-model.md in prompt-injection-scan.sh: they contain intentional injection examples / security-model prose; the path-reference rewrites are kept. CodeQL alerts on this PR are pre-existing (alert lines unchanged by this PR; none in the new migration/guard) and are out of scope for the rename. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): resolve CodeQL alerts surfaced on this PR The rename diff touched files carrying pre-existing CodeQL findings; per the no-pre-existing-dismissal rule, fixing every surfaced alert rather than waving them off. All behavior-preserving: - scripts/ci-test-scope.cjs: build the config-path match from string .includes() instead of a RegExp over an arg-derived value (js/regex-injection). - src/profile-output.cts: escape backslashes before pipe-escaping desc/safeName so the table-cell escape is complete (js/incomplete-sanitization). - tests/{bug-2643,bug-2808,docs-parity-live-registry}: two-pass HTML-comment strip so a bare/unclosed `<!--` cannot survive (js/incomplete-multi-character-sanitization). - tests/inline-plan-threshold: drop the no-op `\s`->`\s` identity replace, keep the meaningful POSIX-class conversion (js/identity-replacement). Verified: build:lib green; the touched test files + ci-test-scope + profile-output suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): correctly resolve remaining CodeQL alerts (regex-injection + sanitization) The prior commit's fixes for two alerts were ineffective: - ci-test-scope.cjs js/regex-injection: the alert is the CLI-arg-derived `file` reaching static regex `.test(file)` calls (not the config rule). Removed ALL regex over file/t — startsWith/includes/=== string checks + an isWindowsHint helper — so there is no regex sink for the tainted value. - js/incomplete-multi-character-sanitization (3 test files): a single `.replace(/<!--...-->/g,'')` can let `<!--` re-form. Replaced with a fixpoint loop (replace until stable) plus a final bare-opener strip. Verified: no regex over file/t remains; ci-test-scope + the 3 test suites pass; lint:legacy-name clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): make ci-test-scope + comment-strippers regex-free to clear CodeQL CodeQL flags the regex PATTERNS syntactically (regex-injection on the --files arg split; incomplete-multi-character-sanitization on the <!--...--> replace), so loop fixes do not satisfy it. Made these paths regex-free: - ci-test-scope.cjs splitFiles: char-by-char separator tokenizer (no /[,\\s]+/). - 3 test files: indexOf/slice HTML-comment stripper (no .replace(/<!--/)). Behavior preserved; ci-test-scope + the 3 suites pass; guard clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): unblock security base64 scan on the large rename diff The security job hit its 10m timeout: base64-scan.sh choked on the binary test fixture tests/feat-3594-parser-property-style.test.cjs (embedded NUL/ non-UTF8 bytes -> thousands of bogus blobs + "ignored null byte" warnings), and the ~800-file rename diff is slow to scan regardless. - scripts/base64-scan.sh: skip binary-by-content files (grep -Iq .) — they can't carry base64-obfuscated *text* and feeding NUL bytes through the per-line scanner is pathologically slow. collect_files already filtered binary *extensions*; this catches binary *content* in text extensions. - .github/workflows/security-scan.yml: raise the security job timeout 10m->30m to accommodate very large diffs (the scan itself is unchanged). Verified locally: scan skips the fixture, 0 "ignored null byte" warnings, 0 findings, exit 0. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): sweep get-shit-done refs introduced by merging next The branch was updated with next (#614/#384/#618 etc.), which reference the get-shit-done/ dir (still named that on next). Swept the stale references in the merged files to gsd-core so the rename stays consistent and lint:legacy-name passes: - commands/gsd/discuss-phase.md (runtime-launcher shim paths) - src/core.cts (getAgentsDir layout comments) - tests/bug-384-agents-runtime-aware.test.cjs (require path to runtime lib) Verified: guard 0 violations; build green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): exclude gsd-core/ path segments from bug-3683 command cross-ref invariant The #614 runtime-launcher shim added to discuss-phase.md references `${_GSD_RUNTIME_ROOT}/gsd-core/bin/...`. bug-3683's REF_PATTERN excluded path-y refs only via lookbehind, but `}` precedes `/gsd-core/` in the shim, so it mis-read the directory path as a dangling `/gsd-core` command ref (same class as the #604 bug-2954 fix). Added a trailing `(?![\w-]*\/)` so `/gsd-<x>/...` path segments are not treated as slash-command references. Verified locally on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22 image) full suite: 0 failures - bug-3683 + bug-2954 pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): lazily resolve findProjectRoot in gsd-tools (harden flaky CI) CI intermittently failed state.test's gsd-tools subprocess with "findProjectRoot is not a function" (flip-flopping across legs; not reproducible on mac full suite, gsd-test linux full suite, test:unit, or state.test x8). findProjectRoot is a re-export from core.cjs (sourced from project-root.cjs); binding it via destructure at module-load can be undefined under a load-ordering edge. Resolve it lazily at call time via a small wrapper so the lookup happens after core.cjs is fully initialized. Verified green on BOTH platforms before pushing: - mac (node 26) full suite: 0 failures - gsd-test-runner (linux, node22) full suite: 0 failures - state.test.cjs: 106/106; gsd-tools loads cleanly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(#604): allowlist verification-patterns.md placeholder examples in secret scan The rename git-mv'd references/verification-patterns.md into gsd-core/, pulling it into the secret-scan diff. It documents stub/placeholder RED-FLAG env-var examples (illustrative Stripe test-key / database-URL / API-key placeholders) — not real credentials. Added it to .secretscanignore with the strict annotation, mirroring the existing gsd-core/workflows/plan-phase.md exception. Verified locally: secret-scan-lint --strict OK; secret-scan --diff origin/next exits 0 with 0 findings. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -59,7 +59,7 @@ function writeConfigJson(planningDir) {
|
||||
// Issue #26 reproducer (verbatim):
|
||||
// mkdir -p .planning/phases/999.1-foo
|
||||
// echo "# Roadmap" > .planning/ROADMAP.md
|
||||
// node .claude/get-shit-done/bin/gsd-tools.cjs validate health
|
||||
// node .claude/gsd-core/bin/gsd-tools.cjs validate health
|
||||
// # Bug: emits W005 about 999.1-foo not following NN-name format
|
||||
//
|
||||
// verify.cjs must consume phaseDirNameRe from validate.cjs so
|
||||
@@ -96,7 +96,7 @@ describe('Drift item W005 — phaseDirNameRe: 999.X-name dirs must not trigger W
|
||||
});
|
||||
|
||||
test('phaseDirNameRe is exported from validate.cjs', () => {
|
||||
const gen = require('../get-shit-done/bin/lib/validate.cjs');
|
||||
const gen = require('../gsd-core/bin/lib/validate.cjs');
|
||||
assert.ok(gen.phaseDirNameRe instanceof RegExp,
|
||||
'validate.cjs must export phaseDirNameRe as a RegExp');
|
||||
const re = gen.phaseDirNameRe;
|
||||
@@ -173,7 +173,7 @@ describe('Drift item W006-archived — MILESTONE_ARCHIVE_DIR_RE and PHASE_TOKEN_
|
||||
});
|
||||
|
||||
test('MILESTONE_ARCHIVE_DIR_RE is exported and matches vN.N-phases dirs', () => {
|
||||
const gen = require('../get-shit-done/bin/lib/validate.cjs');
|
||||
const gen = require('../gsd-core/bin/lib/validate.cjs');
|
||||
assert.ok(gen.MILESTONE_ARCHIVE_DIR_RE instanceof RegExp,
|
||||
'validate.cjs must export MILESTONE_ARCHIVE_DIR_RE');
|
||||
const re = gen.MILESTONE_ARCHIVE_DIR_RE;
|
||||
@@ -184,7 +184,7 @@ describe('Drift item W006-archived — MILESTONE_ARCHIVE_DIR_RE and PHASE_TOKEN_
|
||||
});
|
||||
|
||||
test('PHASE_TOKEN_FROM_DIR_RE is exported and extracts phase tokens correctly', () => {
|
||||
const gen = require('../get-shit-done/bin/lib/validate.cjs');
|
||||
const gen = require('../gsd-core/bin/lib/validate.cjs');
|
||||
assert.ok(gen.PHASE_TOKEN_FROM_DIR_RE instanceof RegExp,
|
||||
'validate.cjs must export PHASE_TOKEN_FROM_DIR_RE');
|
||||
const re = gen.PHASE_TOKEN_FROM_DIR_RE;
|
||||
@@ -239,7 +239,7 @@ describe('Drift item I001 — canonicalPlanStem: long PLAN stem matches short SU
|
||||
});
|
||||
|
||||
test('canonicalPlanStem is exported from validate.cjs', () => {
|
||||
const gen = require('../get-shit-done/bin/lib/validate.cjs');
|
||||
const gen = require('../gsd-core/bin/lib/validate.cjs');
|
||||
assert.strictEqual(typeof gen.canonicalPlanStem, 'function',
|
||||
'validate.cjs must export canonicalPlanStem as a function');
|
||||
assert.strictEqual(gen.canonicalPlanStem('68-01-scaffolding'), '68-01');
|
||||
|
||||
@@ -35,7 +35,7 @@ const { cleanup } = require('./helpers.cjs');
|
||||
// ── Load cmdPhaseComplete directly from phase.cjs (bypass the SDK router) ────
|
||||
// phase-command-router.cjs delegates to SDK when available; we must test the
|
||||
// CJS implementation directly since that is where the bug lives.
|
||||
const phaseModule = require('../get-shit-done/bin/lib/phase.cjs');
|
||||
const phaseModule = require('../gsd-core/bin/lib/phase.cjs');
|
||||
const { cmdPhaseComplete } = phaseModule;
|
||||
|
||||
// ── Fixture builder ──────────────────────────────────────────────────────────
|
||||
@@ -193,7 +193,7 @@ function extractFrontmatterField(stateContent, fieldName) {
|
||||
// Capture stdout from cmdPhaseComplete (it calls output() which writes to stdout)
|
||||
function capturePhaseComplete(cwd, phaseNum) {
|
||||
const { execFileSync } = require('child_process');
|
||||
const TOOLS = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
||||
const TOOLS = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
// We invoke gsd-tools directly for the full CJS path, but with GSD_DISABLE_SDK_BRIDGE=1
|
||||
// to force the CJS implementation. Since no env var disables bridge, we call cmdPhaseComplete
|
||||
// directly and redirect output capture.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Regression / migration-gate test for #551 and ADR-457 (TS migration).
|
||||
*
|
||||
* ESLint must apply the correct policy to every get-shit-done/bin/lib/*.cjs
|
||||
* ESLint must apply the correct policy to every gsd-core/bin/lib/*.cjs
|
||||
* file as modules migrate from hand-written CJS to tsc-generated artifacts:
|
||||
*
|
||||
* - tsc-generated artifact (has src/<basename>.cts counterpart) → MUST be
|
||||
@@ -28,7 +28,7 @@ const path = require('node:path');
|
||||
const { ESLint } = require('eslint');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const LIB_DIR = path.join(ROOT, 'get-shit-done', 'bin', 'lib');
|
||||
const LIB_DIR = path.join(ROOT, 'gsd-core', 'bin', 'lib');
|
||||
const SRC_DIR = path.join(ROOT, 'src');
|
||||
|
||||
/**
|
||||
|
||||
@@ -13,7 +13,7 @@ const {
|
||||
createMemoryPointerAdapter,
|
||||
getActiveWorkstream,
|
||||
setActiveWorkstream,
|
||||
} = require('../get-shit-done/bin/lib/active-workstream-store.cjs');
|
||||
} = require('../gsd-core/bin/lib/active-workstream-store.cjs');
|
||||
|
||||
describe('active-workstream-store', () => {
|
||||
test('validateWorkstreamName accepts canonical names', () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ const {
|
||||
parseCliWorkstream,
|
||||
resolveActiveWorkstream,
|
||||
applyResolvedWorkstreamEnv,
|
||||
} = require('../get-shit-done/bin/lib/active-workstream-store.cjs');
|
||||
} = require('../gsd-core/bin/lib/active-workstream-store.cjs');
|
||||
|
||||
// ── Helpers ───────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Property-based tests for adr-parser.cjs
|
||||
*
|
||||
* Module: get-shit-done/bin/lib/adr-parser.cjs
|
||||
* Module: gsd-core/bin/lib/adr-parser.cjs
|
||||
* Exported: parseAdrMarkdown(markdown, options), shouldRejectAdrStatus(status)
|
||||
*
|
||||
* Properties tested:
|
||||
@@ -22,7 +22,7 @@ const fc = require('./helpers/fast-check-setup.cjs');
|
||||
const {
|
||||
parseAdrMarkdown,
|
||||
shouldRejectAdrStatus,
|
||||
} = require('../get-shit-done/bin/lib/adr-parser.cjs');
|
||||
} = require('../gsd-core/bin/lib/adr-parser.cjs');
|
||||
|
||||
// Required output keys
|
||||
const REQUIRED_KEYS = [
|
||||
|
||||
@@ -4,7 +4,7 @@ const assert = require('node:assert/strict');
|
||||
const {
|
||||
parseAdrMarkdown,
|
||||
shouldRejectAdrStatus,
|
||||
} = require('../get-shit-done/bin/lib/adr-parser.cjs');
|
||||
} = require('../gsd-core/bin/lib/adr-parser.cjs');
|
||||
|
||||
describe('adr-parser', () => {
|
||||
test('maps common ADR header synonyms into canonical fields', () => {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
/**
|
||||
* Example-based unit tests for adr-parser.cjs
|
||||
*
|
||||
* Target: get-shit-done/bin/lib/adr-parser.cjs
|
||||
* Target: gsd-core/bin/lib/adr-parser.cjs
|
||||
* Purpose: kill surviving mutants by asserting EXACT values from every branch
|
||||
*
|
||||
* Gap coverage:
|
||||
@@ -28,7 +28,7 @@ const {
|
||||
parseAdrMarkdown,
|
||||
shouldRejectAdrStatus,
|
||||
CANONICAL_HEADERS,
|
||||
} = require('../get-shit-done/bin/lib/adr-parser.cjs');
|
||||
} = require('../gsd-core/bin/lib/adr-parser.cjs');
|
||||
|
||||
// ─────────────────────────────────────────────────────────────────────────────
|
||||
// normalizeAdrHeader — exact transformation chain
|
||||
|
||||
@@ -179,22 +179,22 @@ test('resolveBaseRef prefers explicit env override', () => {
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// NEW: Transitive test (RED against old code)
|
||||
// Fixture: tests/t.test.cjs -> ../get-shit-done/bin/lib/depA.cjs -> ./depB.cjs
|
||||
// Changed: get-shit-done/bin/lib/depB.cjs
|
||||
// Fixture: tests/t.test.cjs -> ../gsd-core/bin/lib/depA.cjs -> ./depB.cjs
|
||||
// Changed: gsd-core/bin/lib/depB.cjs
|
||||
// Expected: tests/t.test.cjs is selected
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
test('transitive: changing a deep dependency selects the test that depends on it', (t) => {
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/depB.cjs': `'use strict';\nmodule.exports = { b: 1 };\n`,
|
||||
'get-shit-done/bin/lib/depA.cjs': `'use strict';\nconst depB = require('./depB.cjs');\nmodule.exports = { a: depB };\n`,
|
||||
'tests/t.test.cjs': `'use strict';\nconst depA = require('../get-shit-done/bin/lib/depA.cjs');\n`,
|
||||
'gsd-core/bin/lib/depB.cjs': `'use strict';\nmodule.exports = { b: 1 };\n`,
|
||||
'gsd-core/bin/lib/depA.cjs': `'use strict';\nconst depB = require('./depB.cjs');\nmodule.exports = { a: depB };\n`,
|
||||
'tests/t.test.cjs': `'use strict';\nconst depA = require('../gsd-core/bin/lib/depA.cjs');\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/t.test.cjs']);
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/depB.cjs'],
|
||||
['gsd-core/bin/lib/depB.cjs'],
|
||||
['tests/t.test.cjs'],
|
||||
reverseIndex,
|
||||
);
|
||||
@@ -211,16 +211,16 @@ test('transitive: changing a deep dependency selects the test that depends on it
|
||||
|
||||
test('adversarial(a): cycle depA<->depB — changing depA selects test, no hang', (t) => {
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/depA.cjs': `'use strict';\nconst depB = require('./depB.cjs');\nmodule.exports = {};\n`,
|
||||
'get-shit-done/bin/lib/depB.cjs': `'use strict';\nconst depA = require('./depA.cjs');\nmodule.exports = {};\n`,
|
||||
'tests/cycle.test.cjs': `'use strict';\nconst depA = require('../get-shit-done/bin/lib/depA.cjs');\n`,
|
||||
'gsd-core/bin/lib/depA.cjs': `'use strict';\nconst depB = require('./depB.cjs');\nmodule.exports = {};\n`,
|
||||
'gsd-core/bin/lib/depB.cjs': `'use strict';\nconst depA = require('./depA.cjs');\nmodule.exports = {};\n`,
|
||||
'tests/cycle.test.cjs': `'use strict';\nconst depA = require('../gsd-core/bin/lib/depA.cjs');\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
// Must complete without hanging
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/cycle.test.cjs']);
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/depA.cjs'],
|
||||
['gsd-core/bin/lib/depA.cjs'],
|
||||
['tests/cycle.test.cjs'],
|
||||
reverseIndex,
|
||||
);
|
||||
@@ -234,7 +234,7 @@ test('adversarial(a): cycle depA<->depB — changing depA selects test, no hang'
|
||||
test('adversarial(b): missing require (gone file) — null resolve, no crash', (t) => {
|
||||
const dir = makeFixture({
|
||||
// Requires a file that does not exist
|
||||
'tests/missing.test.cjs': `'use strict';\nconst x = require('../get-shit-done/bin/lib/gone.cjs');\n`,
|
||||
'tests/missing.test.cjs': `'use strict';\nconst x = require('../gsd-core/bin/lib/gone.cjs');\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
@@ -246,7 +246,7 @@ test('adversarial(b): missing require (gone file) — null resolve, no crash', (
|
||||
|
||||
// Changing the missing file produces no selection (it doesn't exist, so no dependents)
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/gone.cjs'],
|
||||
['gsd-core/bin/lib/gone.cjs'],
|
||||
['tests/missing.test.cjs'],
|
||||
reverseIndex,
|
||||
);
|
||||
@@ -257,14 +257,14 @@ test('adversarial(b): missing require (gone file) — null resolve, no crash', (
|
||||
|
||||
test('adversarial(c): .json dependency — changing data.json selects the test', (t) => {
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/data.json': `{"key":"value"}`,
|
||||
'tests/json.test.cjs': `'use strict';\nconst data = require('../get-shit-done/bin/lib/data.json');\n`,
|
||||
'gsd-core/bin/lib/data.json': `{"key":"value"}`,
|
||||
'tests/json.test.cjs': `'use strict';\nconst data = require('../gsd-core/bin/lib/data.json');\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/json.test.cjs']);
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/data.json'],
|
||||
['gsd-core/bin/lib/data.json'],
|
||||
['tests/json.test.cjs'],
|
||||
reverseIndex,
|
||||
);
|
||||
@@ -277,15 +277,15 @@ test('adversarial(c): .json dependency — changing data.json selects the test',
|
||||
|
||||
test('adversarial(d): re-export chain — changing depB selects the test that requires the re-exporter', (t) => {
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/depB.cjs': `'use strict';\nmodule.exports = { deep: true };\n`,
|
||||
'get-shit-done/bin/lib/reexporter.cjs': `'use strict';\nmodule.exports = require('./depB.cjs');\n`,
|
||||
'tests/reexport.test.cjs': `'use strict';\nconst x = require('../get-shit-done/bin/lib/reexporter.cjs');\n`,
|
||||
'gsd-core/bin/lib/depB.cjs': `'use strict';\nmodule.exports = { deep: true };\n`,
|
||||
'gsd-core/bin/lib/reexporter.cjs': `'use strict';\nmodule.exports = require('./depB.cjs');\n`,
|
||||
'tests/reexport.test.cjs': `'use strict';\nconst x = require('../gsd-core/bin/lib/reexporter.cjs');\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/reexport.test.cjs']);
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/depB.cjs'],
|
||||
['gsd-core/bin/lib/depB.cjs'],
|
||||
['tests/reexport.test.cjs'],
|
||||
reverseIndex,
|
||||
);
|
||||
@@ -312,7 +312,7 @@ test('adversarial(e): bare and node: specifiers are ignored', () => {
|
||||
test('adversarial(f): WIDEN — changing a src file with no test dependents widens to unit/all', (t) => {
|
||||
// orphan.cjs is a source file no test file requires (statically)
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/orphan.cjs': `'use strict';\nmodule.exports = {};\n`,
|
||||
'gsd-core/bin/lib/orphan.cjs': `'use strict';\nmodule.exports = {};\n`,
|
||||
'tests/unrelated.test.cjs': `'use strict';\n// requires nothing\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
@@ -320,7 +320,7 @@ test('adversarial(f): WIDEN — changing a src file with no test dependents wide
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/unrelated.test.cjs']);
|
||||
|
||||
// Verify orphan.cjs has no dependents in the index
|
||||
const dependents = reverseIndex.get('get-shit-done/bin/lib/orphan.cjs');
|
||||
const dependents = reverseIndex.get('gsd-core/bin/lib/orphan.cjs');
|
||||
assert.ok(
|
||||
!dependents || dependents.size === 0,
|
||||
'orphan.cjs must have no transitive test dependents',
|
||||
@@ -328,7 +328,7 @@ test('adversarial(f): WIDEN — changing a src file with no test dependents wide
|
||||
|
||||
// The widen backstop is tested via the WIDEN_SIGNAL attached to pickAffectedTests result
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/orphan.cjs'],
|
||||
['gsd-core/bin/lib/orphan.cjs'],
|
||||
['tests/unrelated.test.cjs'],
|
||||
reverseIndex,
|
||||
{ detectWiden: true },
|
||||
@@ -344,7 +344,7 @@ test('adversarial(g): dynamic require in changed file with no static dependents
|
||||
// dynamic.cjs uses a template literal require — not statically parseable
|
||||
// No test requires dynamic.cjs statically
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/dynamic.cjs': `'use strict';\nconst x = 'foo';\nconst m = require(\`./\${x}\`);\nmodule.exports = {};\n`,
|
||||
'gsd-core/bin/lib/dynamic.cjs': `'use strict';\nconst x = 'foo';\nconst m = require(\`./\${x}\`);\nmodule.exports = {};\n`,
|
||||
'tests/unrelated.test.cjs': `'use strict';\n// does not require dynamic.cjs\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
@@ -352,7 +352,7 @@ test('adversarial(g): dynamic require in changed file with no static dependents
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, ['tests/unrelated.test.cjs']);
|
||||
|
||||
const selected = pickAffectedTests(
|
||||
['get-shit-done/bin/lib/dynamic.cjs'],
|
||||
['gsd-core/bin/lib/dynamic.cjs'],
|
||||
['tests/unrelated.test.cjs'],
|
||||
reverseIndex,
|
||||
{ detectWiden: true },
|
||||
@@ -531,7 +531,7 @@ test('regression(delete-only-source): deleting a source file triggers widen, nev
|
||||
// must trigger the widen backstop — gone.cjs has no static dependents because
|
||||
// it was never built into the forward graph (it doesn't exist).
|
||||
const dir = makeFixture({
|
||||
'get-shit-done/bin/lib/other.cjs': `'use strict';\nmodule.exports = {};\n`,
|
||||
'gsd-core/bin/lib/other.cjs': `'use strict';\nmodule.exports = {};\n`,
|
||||
'tests/unrelated.test.cjs': `'use strict';\n// requires nothing from gone.cjs\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
@@ -541,7 +541,7 @@ test('regression(delete-only-source): deleting a source file triggers widen, nev
|
||||
const reverseIndex = buildTransitiveReverseIndex(dir, allTests);
|
||||
|
||||
// gone.cjs does not exist in the fixture — simulates a delete-only PR.
|
||||
const changedFiles = ['get-shit-done/bin/lib/gone.cjs'];
|
||||
const changedFiles = ['gsd-core/bin/lib/gone.cjs'];
|
||||
|
||||
const selected = pickAffectedTests(changedFiles, allTests, reverseIndex, { detectWiden: true });
|
||||
|
||||
@@ -590,8 +590,8 @@ test('regression(rename-stale-old-path): deleted old path triggers widen, protec
|
||||
// dependents → widen backstop fires → mode:suites (PR_FULL_SUITES).
|
||||
const dir = makeFixture({
|
||||
// newname.cjs exists; oldname.cjs intentionally absent (it was renamed away)
|
||||
'get-shit-done/bin/lib/newname.cjs': `'use strict';\nmodule.exports = { v: 2 };\n`,
|
||||
'tests/newname.test.cjs': `'use strict';\nconst x = require('../get-shit-done/bin/lib/newname.cjs');\n`,
|
||||
'gsd-core/bin/lib/newname.cjs': `'use strict';\nmodule.exports = { v: 2 };\n`,
|
||||
'tests/newname.test.cjs': `'use strict';\nconst x = require('../gsd-core/bin/lib/newname.cjs');\n`,
|
||||
'tests/unrelated.test.cjs': `'use strict';\n// no dependency on oldname or newname\n`,
|
||||
});
|
||||
t.after(() => cleanup(dir));
|
||||
@@ -603,12 +603,12 @@ test('regression(rename-stale-old-path): deleted old path triggers widen, protec
|
||||
// changedFiles mirrors what --no-renames git diff emits for a rename:
|
||||
// Delete(old) + Add(new)
|
||||
const changedFiles = [
|
||||
'get-shit-done/bin/lib/oldname.cjs', // deleted old path — absent from disk
|
||||
'get-shit-done/bin/lib/newname.cjs', // added new path — present on disk
|
||||
'gsd-core/bin/lib/oldname.cjs', // deleted old path — absent from disk
|
||||
'gsd-core/bin/lib/newname.cjs', // added new path — present on disk
|
||||
];
|
||||
|
||||
// Assert: oldname.cjs must have zero static dependents (not in graph)
|
||||
const oldDependents = reverseIndex.get('get-shit-done/bin/lib/oldname.cjs');
|
||||
const oldDependents = reverseIndex.get('gsd-core/bin/lib/oldname.cjs');
|
||||
assert.ok(
|
||||
!oldDependents || oldDependents.size === 0,
|
||||
`oldname.cjs must have no static dependents (absent from disk), got: ${JSON.stringify(oldDependents && [...oldDependents])}`,
|
||||
|
||||
@@ -18,7 +18,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const COMMANDS_DIR = path.join(__dirname, '..', 'commands', 'gsd');
|
||||
|
||||
const ALL_AGENTS = fs.readdirSync(AGENTS_DIR)
|
||||
@@ -384,7 +384,7 @@ describe('DISCUSS: discussion log generation', () => {
|
||||
});
|
||||
|
||||
test('discussion-log template exists', () => {
|
||||
const templatePath = path.join(__dirname, '..', 'get-shit-done', 'templates', 'discussion-log.md');
|
||||
const templatePath = path.join(__dirname, '..', 'gsd-core', 'templates', 'discussion-log.md');
|
||||
assert.ok(
|
||||
fs.existsSync(templatePath),
|
||||
'discussion-log.md template must exist'
|
||||
|
||||
@@ -14,12 +14,12 @@ const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const AGENTS_DIR_NAME = 'agents';
|
||||
const MODEL_PROFILES = require('../get-shit-done/bin/lib/model-profiles.cjs').MODEL_PROFILES;
|
||||
const MODEL_PROFILES = require('../gsd-core/bin/lib/model-profiles.cjs').MODEL_PROFILES;
|
||||
const EXPECTED_AGENTS = Object.keys(MODEL_PROFILES);
|
||||
|
||||
/**
|
||||
* Create a fake GSD install directory structure that mirrors what the installer
|
||||
* produces. gsd-tools.cjs lives at <configDir>/get-shit-done/bin/gsd-tools.cjs,
|
||||
* produces. gsd-tools.cjs lives at <configDir>/gsd-core/bin/gsd-tools.cjs,
|
||||
* so the agents dir is at <configDir>/agents/.
|
||||
*
|
||||
* We use --cwd to point at the project, and GSD_INSTALL_DIR env to override
|
||||
@@ -50,7 +50,7 @@ describe('init commands: agents_installed field (#1371)', () => {
|
||||
cleanup(tmpDir);
|
||||
});
|
||||
|
||||
// Point the SDK at the repo's agents/ dir (sibling of get-shit-done/) via the
|
||||
// Point the SDK at the repo's agents/ dir (sibling of gsd-core/) via the
|
||||
// GSD_AGENTS_DIR override. The SDK side of init resolves agents from
|
||||
// GSD_AGENTS_DIR or the runtime config dir (~/.claude/agents for Claude); it
|
||||
// does NOT walk up from cwd like the CJS-era code did. Without this override
|
||||
@@ -145,7 +145,7 @@ describe('validate health: agent installation check W010 (#1371)', () => {
|
||||
});
|
||||
|
||||
test('health check reports healthy when agents are installed (repo layout)', () => {
|
||||
// In the repo, agents/ exists as a sibling of get-shit-done/, so the
|
||||
// In the repo, agents/ exists as a sibling of gsd-core/, so the
|
||||
// health check should find them via the gsd-tools.cjs path resolution
|
||||
const result = runGsdTools('validate health --raw', tmpDir);
|
||||
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*
|
||||
* Raising a budget is a deliberate choice — adjust the constant, write a
|
||||
* rationale in the PR, and make sure the bloat is not duplicated content
|
||||
* that belongs in `get-shit-done/references/`.
|
||||
* that belongs in `gsd-core/references/`.
|
||||
*
|
||||
* Tighten-only invariant (issue #597): ceilings track the tier high-water mark
|
||||
* within GRACE lines. Budgets may only decrease, never silently creep upward.
|
||||
@@ -90,7 +90,7 @@ describe('SIZE: agent line-count budget', () => {
|
||||
assert.ok(
|
||||
lines <= limit,
|
||||
`${agent}.md has ${lines} lines — exceeds ${tier} budget of ${limit}. ` +
|
||||
`Extract shared boilerplate to get-shit-done/references/ or raise the budget ` +
|
||||
`Extract shared boilerplate to gsd-core/references/ or raise the budget ` +
|
||||
`in tests/agent-size-budget.test.cjs with a rationale.`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -25,9 +25,9 @@ const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const AGENTS_DIR = path.join(REPO_ROOT, 'agents');
|
||||
const COMMANDS_DIR = path.join(REPO_ROOT, 'commands', 'gsd');
|
||||
const WORKFLOWS_DIR = path.join(REPO_ROOT, 'get-shit-done', 'workflows');
|
||||
const TEMPLATES_DIR = path.join(REPO_ROOT, 'get-shit-done', 'templates');
|
||||
const REFERENCES_DIR = path.join(REPO_ROOT, 'get-shit-done', 'references');
|
||||
const WORKFLOWS_DIR = path.join(REPO_ROOT, 'gsd-core', 'workflows');
|
||||
const TEMPLATES_DIR = path.join(REPO_ROOT, 'gsd-core', 'templates');
|
||||
const REFERENCES_DIR = path.join(REPO_ROOT, 'gsd-core', 'references');
|
||||
|
||||
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -20,17 +20,17 @@ describe('analyze-dependencies command', () => {
|
||||
|
||||
// Legacy placeholder: was previously a separate test; now just passes trivially.
|
||||
test('workflow file is sufficient without a standalone command file', () => {
|
||||
const p = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'analyze-dependencies.md');
|
||||
const p = path.join(__dirname, '..', 'gsd-core', 'workflows', 'analyze-dependencies.md');
|
||||
assert.ok(fs.existsSync(p), 'workflows/analyze-dependencies.md should still exist');
|
||||
});
|
||||
|
||||
test('workflow file exists', () => {
|
||||
const p = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'analyze-dependencies.md');
|
||||
const p = path.join(__dirname, '..', 'gsd-core', 'workflows', 'analyze-dependencies.md');
|
||||
assert.ok(fs.existsSync(p), 'workflows/analyze-dependencies.md should exist');
|
||||
});
|
||||
|
||||
test('workflow describes dependency analysis approach', () => {
|
||||
const p = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'analyze-dependencies.md');
|
||||
const p = path.join(__dirname, '..', 'gsd-core', 'workflows', 'analyze-dependencies.md');
|
||||
const content = fs.readFileSync(p, 'utf-8');
|
||||
assert.ok(content.includes('ROADMAP') || content.includes('phase'),
|
||||
'workflow should reference ROADMAP.md/phases');
|
||||
@@ -41,7 +41,7 @@ describe('analyze-dependencies command', () => {
|
||||
});
|
||||
|
||||
test('workflow mentions file overlap detection', () => {
|
||||
const p = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'analyze-dependencies.md');
|
||||
const p = path.join(__dirname, '..', 'gsd-core', 'workflows', 'analyze-dependencies.md');
|
||||
const content = fs.readFileSync(p, 'utf-8');
|
||||
assert.ok(
|
||||
content.includes('file') && (content.includes('overlap') || content.includes('conflict')),
|
||||
|
||||
@@ -17,9 +17,9 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const PAUSE_WORK = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'pause-work.md');
|
||||
const DISCUSS_PHASE = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase.md');
|
||||
const EXECUTE_PHASE = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
|
||||
const PAUSE_WORK = path.join(__dirname, '..', 'gsd-core', 'workflows', 'pause-work.md');
|
||||
const DISCUSS_PHASE = path.join(__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase.md');
|
||||
const EXECUTE_PHASE = path.join(__dirname, '..', 'gsd-core', 'workflows', 'execute-phase.md');
|
||||
|
||||
describe('pause-work.md — severity column in Critical Anti-Patterns template', () => {
|
||||
test('template includes a Severity column header in the anti-patterns table', () => {
|
||||
|
||||
@@ -12,7 +12,7 @@ const {
|
||||
CANONICAL_EXACT,
|
||||
CANONICAL_PATTERNS,
|
||||
isCanonicalPlanningFile,
|
||||
} = require('../get-shit-done/bin/lib/artifacts.cjs');
|
||||
} = require('../gsd-core/bin/lib/artifacts.cjs');
|
||||
|
||||
describe('CANONICAL_EXACT', () => {
|
||||
test('is a Set', () => {
|
||||
|
||||
@@ -25,7 +25,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
|
||||
|
||||
/**
|
||||
* Return true if the file content contains a TEXT_MODE / text_mode fallback
|
||||
@@ -81,7 +81,7 @@ describe('AskUserQuestion text-mode fallback (#2012)', () => {
|
||||
' number.',
|
||||
'',
|
||||
'Workflows missing the fallback:',
|
||||
...violations.map(v => ' get-shit-done/workflows/' + v),
|
||||
...violations.map(v => ' gsd-core/workflows/' + v),
|
||||
].join('\n')
|
||||
);
|
||||
});
|
||||
|
||||
@@ -24,7 +24,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const libDir = path.resolve(__dirname, '..', 'get-shit-done', 'bin', 'lib');
|
||||
const libDir = path.resolve(__dirname, '..', 'gsd-core', 'bin', 'lib');
|
||||
|
||||
/**
|
||||
* Find all fs.writeFileSync(...) call sites in a file.
|
||||
|
||||
@@ -24,7 +24,7 @@ const path = require('path');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const COMMANDS_DIR = path.join(REPO_ROOT, 'commands', 'gsd');
|
||||
const WORKFLOWS_DIR = path.join(REPO_ROOT, 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(REPO_ROOT, 'gsd-core', 'workflows');
|
||||
|
||||
// ─── 1. Command file — audit-fix.md ──────────────────────────────────────────
|
||||
|
||||
@@ -123,7 +123,7 @@ describe('AUDIT-FIX: workflow file', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(
|
||||
fs.existsSync(wfPath),
|
||||
'audit-fix.md must exist in get-shit-done/workflows/'
|
||||
'audit-fix.md must exist in gsd-core/workflows/'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -31,8 +31,8 @@ const AUTONOMOUS_SIZE_LIMIT = 38 * 1024;
|
||||
|
||||
// ─── File paths ──────────────────────────────────────────────────────────────
|
||||
|
||||
const AUTONOMOUS_PATH = path.join(PROJECT_ROOT, 'get-shit-done', 'workflows', 'autonomous.md');
|
||||
const SMART_DISCUSS_REF = path.join(PROJECT_ROOT, 'get-shit-done', 'references', 'autonomous-smart-discuss.md');
|
||||
const AUTONOMOUS_PATH = path.join(PROJECT_ROOT, 'gsd-core', 'workflows', 'autonomous.md');
|
||||
const SMART_DISCUSS_REF = path.join(PROJECT_ROOT, 'gsd-core', 'references', 'autonomous-smart-discuss.md');
|
||||
|
||||
// ─── autonomous.md size ──────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
describe('autonomous --interactive flag (#1413)', () => {
|
||||
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'autonomous.md');
|
||||
const workflowPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
||||
const commandPath = path.join(__dirname, '..', 'commands', 'gsd', 'autonomous.md');
|
||||
|
||||
test('command definition includes --interactive in argument-hint', () => {
|
||||
|
||||
@@ -19,7 +19,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
describe('autonomous --to N flag (#1644)', () => {
|
||||
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'autonomous.md');
|
||||
const workflowPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
||||
const commandPath = path.join(__dirname, '..', 'commands', 'gsd', 'autonomous.md');
|
||||
|
||||
// --- Command definition tests ---
|
||||
|
||||
@@ -11,7 +11,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'autonomous.md');
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'autonomous.md');
|
||||
|
||||
describe('autonomous workflow ui-phase and ui-review integration (#1375)', () => {
|
||||
let content;
|
||||
|
||||
@@ -25,7 +25,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const { createTempProject, cleanup, runGsdTools, captureConsole } = require('./helpers.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
// ─── helpers ─────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ const assert = require('node:assert/strict');
|
||||
const {
|
||||
compareSemverCore,
|
||||
isStableTripletSemver,
|
||||
} = require('../get-shit-done/bin/lib/semver-compare.cjs');
|
||||
} = require('../gsd-core/bin/lib/semver-compare.cjs');
|
||||
const { isInstalledAheadOfLatest } = require('../hooks/gsd-statusline.js');
|
||||
|
||||
describe('bug #10: semver policy consolidation', () => {
|
||||
|
||||
@@ -47,7 +47,7 @@ describe('#14: /gsd:progress --next --auto flag must be documented and propagate
|
||||
|
||||
test('next.md show_and_execute step handles --auto to chain steps', () => {
|
||||
const workflow = fs.readFileSync(
|
||||
path.join(ROOT, 'get-shit-done', 'workflows', 'next.md'),
|
||||
path.join(ROOT, 'gsd-core', 'workflows', 'next.md'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
@@ -59,7 +59,7 @@ describe('#14: /gsd:progress --next --auto flag must be documented and propagate
|
||||
|
||||
test('next.md --auto chaining re-invokes /gsd:progress --next after step completion', () => {
|
||||
const workflow = fs.readFileSync(
|
||||
path.join(ROOT, 'get-shit-done', 'workflows', 'next.md'),
|
||||
path.join(ROOT, 'gsd-core', 'workflows', 'next.md'),
|
||||
'utf8'
|
||||
);
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const ROOT = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const ASK_USER_QUESTION_OPTION_CAP = 4;
|
||||
|
||||
function walkMarkdownFiles(dir, out = []) {
|
||||
|
||||
@@ -6,7 +6,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const LEGACY_HINT = 'npx get-shit-done-cc@latest --claude --local';
|
||||
const CURRENT_HINT = 'npx -y @opengsd/gsd-core@latest --claude --local';
|
||||
|
||||
|
||||
@@ -24,7 +24,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { resolveModelInternal } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
const { resolveModelInternal } = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
||||
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
|
||||
describe('bug #1891: @file: resolution in gsd-tools.cjs', () => {
|
||||
let src;
|
||||
|
||||
@@ -12,19 +12,19 @@ function read(rel) {
|
||||
}
|
||||
|
||||
test('bridge collapse removes cjs-sdk-bridge and runtime-bridge-sync seam', () => {
|
||||
const bridgePath = path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'cjs-sdk-bridge.cjs');
|
||||
const bridgePath = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'cjs-sdk-bridge.cjs');
|
||||
const sdkDir = path.join(ROOT, 'sdk');
|
||||
|
||||
assert.equal(fs.existsSync(bridgePath), false, 'cjs-sdk-bridge.cjs must be removed');
|
||||
assert.equal(fs.existsSync(sdkDir), false, 'sdk directory must be removed');
|
||||
|
||||
const routers = [
|
||||
'get-shit-done/bin/lib/init-command-router.cjs',
|
||||
'get-shit-done/bin/lib/roadmap-command-router.cjs',
|
||||
'get-shit-done/bin/lib/state-command-router.cjs',
|
||||
'get-shit-done/bin/lib/validate-command-router.cjs',
|
||||
'get-shit-done/bin/lib/verify-command-router.cjs',
|
||||
'get-shit-done/bin/lib/phases-command-router.cjs',
|
||||
'gsd-core/bin/lib/init-command-router.cjs',
|
||||
'gsd-core/bin/lib/roadmap-command-router.cjs',
|
||||
'gsd-core/bin/lib/state-command-router.cjs',
|
||||
'gsd-core/bin/lib/validate-command-router.cjs',
|
||||
'gsd-core/bin/lib/verify-command-router.cjs',
|
||||
'gsd-core/bin/lib/phases-command-router.cjs',
|
||||
];
|
||||
|
||||
for (const rel of routers) {
|
||||
|
||||
@@ -19,7 +19,7 @@ const { describe, test, before } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('path');
|
||||
|
||||
const projection = require(path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const { projectLocalHookPrefix, projectShellCommandText } = projection;
|
||||
|
||||
describe('bug #1906: local hook commands use $CLAUDE_PROJECT_DIR', () => {
|
||||
|
||||
@@ -29,9 +29,9 @@ function createFakeInstall(prefix = 'gsd-uninstall-test-') {
|
||||
const dir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
|
||||
// Simulate the minimum directory/file layout produced by the installer:
|
||||
// get-shit-done/ directory, agents/ directory, and the manifest file.
|
||||
fs.mkdirSync(path.join(dir, 'get-shit-done', 'workflows'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, 'get-shit-done', 'workflows', 'execute-phase.md'), '# stub');
|
||||
// gsd-core/ directory, agents/ directory, and the manifest file.
|
||||
fs.mkdirSync(path.join(dir, 'gsd-core', 'workflows'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, 'gsd-core', 'workflows', 'execute-phase.md'), '# stub');
|
||||
|
||||
fs.mkdirSync(path.join(dir, 'agents'), { recursive: true });
|
||||
fs.writeFileSync(path.join(dir, 'agents', 'gsd-executor.md'), '# stub');
|
||||
@@ -40,7 +40,7 @@ function createFakeInstall(prefix = 'gsd-uninstall-test-') {
|
||||
version: '1.34.0',
|
||||
timestamp: new Date().toISOString(),
|
||||
files: {
|
||||
'get-shit-done/workflows/execute-phase.md': 'abc123',
|
||||
'gsd-core/workflows/execute-phase.md': 'abc123',
|
||||
'agents/gsd-executor.md': 'def456',
|
||||
},
|
||||
};
|
||||
@@ -110,8 +110,8 @@ describe('uninstall — manifest cleanup (#1908)', () => {
|
||||
// For a local install, getGlobalDir is not called — targetDir = cwd + dirName.
|
||||
// Simulate by creating .claude/ inside tmpDir and placing artefacts there.
|
||||
const localDir = path.join(tmpDir, '.claude');
|
||||
fs.mkdirSync(path.join(localDir, 'get-shit-done', 'workflows'), { recursive: true });
|
||||
fs.writeFileSync(path.join(localDir, 'get-shit-done', 'workflows', 'execute-phase.md'), '# stub');
|
||||
fs.mkdirSync(path.join(localDir, 'gsd-core', 'workflows'), { recursive: true });
|
||||
fs.writeFileSync(path.join(localDir, 'gsd-core', 'workflows', 'execute-phase.md'), '# stub');
|
||||
const localManifestPath = path.join(localDir, MANIFEST_NAME);
|
||||
fs.writeFileSync(localManifestPath, JSON.stringify({ version: '1.34.0', files: {} }, null, 2));
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* Regression tests for bug #1924: gsd-update silently deletes user-generated files
|
||||
*
|
||||
* Running the installer (gsd-update / re-install) must not delete:
|
||||
* - get-shit-done/USER-PROFILE.md (created by /gsd-profile-user)
|
||||
* - gsd-core/USER-PROFILE.md (created by /gsd-profile-user)
|
||||
* - commands/gsd/dev-preferences.md (created by /gsd-profile-user)
|
||||
*
|
||||
* Root cause:
|
||||
@@ -17,7 +17,7 @@
|
||||
* cleanup — no preserve. This wipes dev-preferences.md.
|
||||
*
|
||||
* Fix requirement:
|
||||
* - install() must preserve USER-PROFILE.md across the get-shit-done/ wipe
|
||||
* - install() must preserve USER-PROFILE.md across the gsd-core/ wipe
|
||||
* - install() must preserve dev-preferences.md across the commands/gsd/ wipe
|
||||
*
|
||||
* Closes: #1924
|
||||
@@ -88,8 +88,8 @@ describe('#1924: USER-PROFILE.md preserved across re-install (global Claude)', (
|
||||
test('USER-PROFILE.md exists after initial install + user creation', () => {
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// Simulate /gsd-profile-user creating USER-PROFILE.md inside get-shit-done/
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
// Simulate /gsd-profile-user creating USER-PROFILE.md inside gsd-core/
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
fs.writeFileSync(profilePath, '# My Profile\n\nCustom user content.\n');
|
||||
|
||||
assert.ok(
|
||||
@@ -103,7 +103,7 @@ describe('#1924: USER-PROFILE.md preserved across re-install (global Claude)', (
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// User runs /gsd-profile-user, creating USER-PROFILE.md
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
const originalContent = '# My Profile\n\nThis is my custom user profile content.\n';
|
||||
fs.writeFileSync(profilePath, originalContent);
|
||||
|
||||
@@ -123,14 +123,14 @@ describe('#1924: USER-PROFILE.md preserved across re-install (global Claude)', (
|
||||
);
|
||||
});
|
||||
|
||||
test('USER-PROFILE.md is preserved even when get-shit-done/ is wiped and recreated', () => {
|
||||
test('USER-PROFILE.md is preserved even when gsd-core/ is wiped and recreated', () => {
|
||||
runInstaller(tmpDir);
|
||||
|
||||
const gsdDir = path.join(tmpDir, 'get-shit-done');
|
||||
const gsdDir = path.join(tmpDir, 'gsd-core');
|
||||
const profilePath = path.join(gsdDir, 'USER-PROFILE.md');
|
||||
|
||||
// Confirm get-shit-done/ was created by install
|
||||
assert.ok(fs.existsSync(gsdDir), 'get-shit-done/ must exist after install');
|
||||
// Confirm gsd-core/ was created by install
|
||||
assert.ok(fs.existsSync(gsdDir), 'gsd-core/ must exist after install');
|
||||
|
||||
// Write profile
|
||||
fs.writeFileSync(profilePath, '# Profile\n\nMy coding style preferences.\n');
|
||||
@@ -138,11 +138,11 @@ describe('#1924: USER-PROFILE.md preserved across re-install (global Claude)', (
|
||||
// Re-install
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// get-shit-done/ must still exist AND profile must be intact
|
||||
assert.ok(fs.existsSync(gsdDir), 'get-shit-done/ must still exist after re-install');
|
||||
// gsd-core/ must still exist AND profile must be intact
|
||||
assert.ok(fs.existsSync(gsdDir), 'gsd-core/ must still exist after re-install');
|
||||
assert.ok(
|
||||
fs.existsSync(profilePath),
|
||||
'USER-PROFILE.md must still exist after get-shit-done/ was wiped and recreated'
|
||||
'USER-PROFILE.md must still exist after gsd-core/ was wiped and recreated'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -227,26 +227,26 @@ describe('#1924: dev-preferences.md preserved across re-install (global Claude)'
|
||||
});
|
||||
});
|
||||
|
||||
// ─── Test 3: profile-user.md backup path is outside get-shit-done/ ───────────
|
||||
// ─── Test 3: profile-user.md backup path is outside gsd-core/ ───────────
|
||||
|
||||
describe('#1924: profile-user.md backup path must be outside get-shit-done/', () => {
|
||||
test('profile-user.md backup uses ~/.claude/USER-PROFILE.backup.md not ~/.claude/get-shit-done/USER-PROFILE.backup.md', () => {
|
||||
describe('#1924: profile-user.md backup path must be outside gsd-core/', () => {
|
||||
test('profile-user.md backup uses ~/.claude/USER-PROFILE.backup.md not ~/.claude/gsd-core/USER-PROFILE.backup.md', () => {
|
||||
const workflowPath = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'profile-user.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'profile-user.md'
|
||||
);
|
||||
const content = fs.readFileSync(workflowPath, 'utf8');
|
||||
|
||||
// The backup must NOT be inside get-shit-done/ because that directory is wiped on update
|
||||
// The backup must NOT be inside gsd-core/ because that directory is wiped on update
|
||||
assert.ok(
|
||||
!content.includes('get-shit-done/USER-PROFILE.backup.md'),
|
||||
'backup path must NOT be inside get-shit-done/ — that directory is wiped on gsd-update'
|
||||
!content.includes('gsd-core/USER-PROFILE.backup.md'),
|
||||
'backup path must NOT be inside gsd-core/ — that directory is wiped on gsd-update'
|
||||
);
|
||||
|
||||
// The backup should be at ~/.claude/USER-PROFILE.backup.md (outside get-shit-done/)
|
||||
// The backup should be at ~/.claude/USER-PROFILE.backup.md (outside gsd-core/)
|
||||
assert.ok(
|
||||
content.includes('USER-PROFILE.backup.md') &&
|
||||
!content.includes('/get-shit-done/USER-PROFILE.backup.md'),
|
||||
'backup path must be outside get-shit-done/ (e.g. ~/.claude/USER-PROFILE.backup.md)'
|
||||
!content.includes('/gsd-core/USER-PROFILE.backup.md'),
|
||||
'backup path must be outside gsd-core/ (e.g. ~/.claude/USER-PROFILE.backup.md)'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -22,7 +22,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
|
||||
const state = require('../get-shit-done/bin/lib/state.cjs');
|
||||
const state = require('../gsd-core/bin/lib/state.cjs');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
describe('buildStateFrontmatter cache invalidation (#1967)', () => {
|
||||
|
||||
@@ -29,7 +29,7 @@ const { spawnSync } = require('node:child_process');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const HOOK_PATH = path.resolve(__dirname, '..', 'hooks', 'gsd-context-monitor.js');
|
||||
const GSD_TOOLS = path.resolve(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
||||
const GSD_TOOLS = path.resolve(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
|
||||
// Windows can hold a transient handle on the temp dir after a spawnSync child
|
||||
// exits (AV scanner / handle-release lag), so cleanup()'s internal rmSync retry
|
||||
|
||||
@@ -19,7 +19,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const workflowPath = path.resolve(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'execute-phase.md'
|
||||
);
|
||||
|
||||
describe('bug #2002: offer_next checks CONTEXT.md before suggesting next step', () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const workflowPath = path.resolve(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'pr-branch.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'pr-branch.md'
|
||||
);
|
||||
|
||||
describe('bug #2004: pr-branch preserves structural planning commits', () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ const path = require('node:path');
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
|
||||
const TEMPLATE_PATHS = [
|
||||
path.join(REPO_ROOT, 'get-shit-done', 'templates', 'state.md'),
|
||||
path.join(REPO_ROOT, 'gsd-core', 'templates', 'state.md'),
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
'use strict';
|
||||
/**
|
||||
* Regression test for bug #211: gsd_run launcher must probe
|
||||
* $HOME/.claude/get-shit-done/bin/gsd-tools.cjs before emitting the hard error.
|
||||
* $HOME/.claude/gsd-core/bin/gsd-tools.cjs before emitting the hard error.
|
||||
*
|
||||
* Asserts:
|
||||
* (A) The canonical snippet file contains the ~/.claude fallback arm.
|
||||
* (B) A representative propagated workflow file contains the ~/.claude fallback arm.
|
||||
* (C) Behavioral: when RUNTIME_DIR misses and gsd-tools is NOT on PATH,
|
||||
* a stub at $HOME/.claude/get-shit-done/bin/gsd-tools.cjs is resolved and invoked.
|
||||
* a stub at $HOME/.claude/gsd-core/bin/gsd-tools.cjs is resolved and invoked.
|
||||
* (D) The resolution order is preserved: local -> PATH -> ~/.claude -> hard error.
|
||||
* When all three miss, exit non-zero.
|
||||
*/
|
||||
@@ -24,12 +24,12 @@ const os = require('node:os');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
const SNIPPET_FILE = path.join(WORKFLOWS_DIR, '_runtime-launcher.snippet.sh');
|
||||
// Representative propagated workflow file (has a gsd_run call):
|
||||
const REPRESENTATIVE_FILE = path.join(WORKFLOWS_DIR, 'add-backlog.md');
|
||||
|
||||
const CLAUDE_HOME_PROBE = '.claude/get-shit-done/bin/';
|
||||
const CLAUDE_HOME_PROBE = '.claude/gsd-core/bin/';
|
||||
|
||||
describe('bug-211: launcher ~/.claude home fallback', () => {
|
||||
// --- (A) Snippet contains the arm ----------------------------------------
|
||||
@@ -53,13 +53,13 @@ describe('bug-211: launcher ~/.claude home fallback', () => {
|
||||
});
|
||||
|
||||
// --- (C) Behavioral: ~/.claude stub is resolved when local and PATH both miss
|
||||
test('(C) gsd_run resolves $HOME/.claude/get-shit-done/bin/ stub when no local install and gsd-tools not on PATH', () => {
|
||||
// Build a fake $HOME with a stub at .claude/get-shit-done/bin/gsd-tools.cjs
|
||||
test('(C) gsd_run resolves $HOME/.claude/gsd-core/bin/ stub when no local install and gsd-tools not on PATH', () => {
|
||||
// Build a fake $HOME with a stub at .claude/gsd-core/bin/gsd-tools.cjs
|
||||
const fakeHome = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-211-home-'));
|
||||
// RUNTIME_DIR points to a directory with no gsd-tools.cjs
|
||||
const fakeRuntime = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-211-rt-'));
|
||||
try {
|
||||
const claudeBinDir = path.join(fakeHome, '.claude', 'get-shit-done', 'bin');
|
||||
const claudeBinDir = path.join(fakeHome, '.claude', 'gsd-core', 'bin');
|
||||
fs.mkdirSync(claudeBinDir, { recursive: true });
|
||||
|
||||
// Stub gsd-tools.cjs that prints a marker
|
||||
@@ -118,8 +118,8 @@ describe('bug-211: launcher ~/.claude home fallback', () => {
|
||||
// GSD_TOOLS must point into the fake ~/.claude dir
|
||||
const normStdout = stdout.replace(/\\/g, '/');
|
||||
assert.ok(
|
||||
normStdout.includes('.claude/get-shit-done/bin/'),
|
||||
`Expected GSD_TOOLS to resolve into .claude/get-shit-done/bin/, got:\n${stdout.trim()}`,
|
||||
normStdout.includes('.claude/gsd-core/bin/'),
|
||||
`Expected GSD_TOOLS to resolve into .claude/gsd-core/bin/, got:\n${stdout.trim()}`,
|
||||
);
|
||||
// The stub must have been invoked
|
||||
assert.ok(
|
||||
@@ -139,7 +139,7 @@ describe('bug-211: launcher ~/.claude home fallback', () => {
|
||||
// noToolsBin so PATH check finds nothing
|
||||
const noToolsBin = path.join(fakeHome, 'nobin');
|
||||
fs.mkdirSync(noToolsBin, { recursive: true });
|
||||
// NO .claude/get-shit-done/bin stub created in fakeHome
|
||||
// NO .claude/gsd-core/bin stub created in fakeHome
|
||||
try {
|
||||
const snippet = fs.readFileSync(SNIPPET_FILE, 'utf8');
|
||||
const scriptContent =
|
||||
|
||||
@@ -12,7 +12,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools } = require('./helpers.cjs');
|
||||
|
||||
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
||||
const GSD_TOOLS_SRC = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
|
||||
describe('bug #224: --pick stdout capture contract', () => {
|
||||
let src;
|
||||
|
||||
@@ -23,7 +23,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EXECUTE_PHASE = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'execute-phase.md'
|
||||
);
|
||||
|
||||
/**
|
||||
|
||||
@@ -18,7 +18,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md');
|
||||
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
||||
|
||||
describe('bug-2388: plan-phase must not rename or create git branches', () => {
|
||||
test('plan-phase.md exists', () => {
|
||||
|
||||
@@ -19,9 +19,9 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EXECUTE_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md');
|
||||
const VERIFY_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'verify-phase.md');
|
||||
const AUDIT_FIX_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'audit-fix.md');
|
||||
const EXECUTE_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'execute-phase.md');
|
||||
const VERIFY_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'verify-phase.md');
|
||||
const AUDIT_FIX_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'audit-fix.md');
|
||||
|
||||
function assertMakefileCheckBeforeNpmTest(filePath, label) {
|
||||
const content = fs.readFileSync(filePath, 'utf-8');
|
||||
|
||||
@@ -16,11 +16,11 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md');
|
||||
const PLAN_PHASE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
||||
|
||||
describe('plan-phase commit_docs support (#2399)', () => {
|
||||
test('plan-phase.md exists', () => {
|
||||
assert.ok(fs.existsSync(PLAN_PHASE_PATH), 'get-shit-done/workflows/plan-phase.md must exist');
|
||||
assert.ok(fs.existsSync(PLAN_PHASE_PATH), 'gsd-core/workflows/plan-phase.md must exist');
|
||||
});
|
||||
|
||||
test('plan-phase.md has a commit step for plan artifacts', () => {
|
||||
|
||||
@@ -21,7 +21,7 @@ const path = require('path');
|
||||
const WORKFLOW_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'execute-phase.md'
|
||||
);
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
* that cause the installer to warn about leaked paths.
|
||||
*
|
||||
* Files affected: agents/gsd-debugger.md (configDir = ~/.claude) and
|
||||
* get-shit-done/workflows/update.md (comment with e.g. ~/.claude).
|
||||
* gsd-core/workflows/update.md (comment with e.g. ~/.claude).
|
||||
*/
|
||||
|
||||
process.env.GSD_TEST_MODE = '1';
|
||||
@@ -54,17 +54,17 @@ describe('convertClaudeToAntigravityContent bare path replacement (#2418)', () =
|
||||
});
|
||||
|
||||
test('still replaces ~/.claude/ (with trailing slash) correctly', () => {
|
||||
const input = 'See ~/.claude/get-shit-done/workflows/';
|
||||
const input = 'See ~/.claude/gsd-core/workflows/';
|
||||
const result = convertClaudeToAntigravityContent(input, true);
|
||||
assert.ok(
|
||||
result.includes('~/.gemini/antigravity/get-shit-done/workflows/'),
|
||||
result.includes('~/.gemini/antigravity/gsd-core/workflows/'),
|
||||
`Expected path with trailing slash to be replaced, got: ${result}`
|
||||
);
|
||||
assert.ok(!result.includes('~/.claude/'), `Expected ~/ .claude/ to be fully replaced, got: ${result}`);
|
||||
});
|
||||
|
||||
test('does not double-replace ~/.claude/ paths', () => {
|
||||
const input = 'See ~/.claude/get-shit-done/';
|
||||
const input = 'See ~/.claude/gsd-core/';
|
||||
const result = convertClaudeToAntigravityContent(input, true);
|
||||
// Result should contain exactly one occurrence of the replacement path
|
||||
const count = (result.match(/~\/.gemini\/antigravity\//g) || []).length;
|
||||
@@ -100,7 +100,7 @@ describe('convertClaudeToAntigravityContent bare path replacement (#2418)', () =
|
||||
});
|
||||
|
||||
test('does not double-replace ~/.claude/ paths', () => {
|
||||
const input = 'See ~/.claude/get-shit-done/';
|
||||
const input = 'See ~/.claude/gsd-core/';
|
||||
const result = convertClaudeToAntigravityContent(input, false);
|
||||
// .agent/ should appear exactly once
|
||||
const count = (result.match(/\.agent\//g) || []).length;
|
||||
@@ -133,7 +133,7 @@ describe('convertClaudeToAntigravityContent bare path replacement (#2418)', () =
|
||||
});
|
||||
|
||||
test('update.md has no leaked ~/.claude after global Antigravity conversion', () => {
|
||||
const updatePath = path.join(repoRoot, 'get-shit-done', 'workflows', 'update.md');
|
||||
const updatePath = path.join(repoRoot, 'gsd-core', 'workflows', 'update.md');
|
||||
if (!fs.existsSync(updatePath)) return; // skip if file doesn't exist
|
||||
const converted = convertFile(updatePath, true);
|
||||
const matches = converted.match(leakedPathRegex);
|
||||
|
||||
@@ -19,8 +19,8 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const NEW_PROJECT_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'new-project.md');
|
||||
const NEW_MILESTONE_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'new-milestone.md');
|
||||
const NEW_PROJECT_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'new-project.md');
|
||||
const NEW_MILESTONE_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'new-milestone.md');
|
||||
const AGENTS_DIR = path.join(__dirname, '..', 'agents');
|
||||
|
||||
describe('gsd-project-researcher agent registration (#2419)', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
*/
|
||||
|
||||
// allow-test-rule: source-text-is-the-product
|
||||
// get-shit-done/workflows/update.md is the installed runtime workflow —
|
||||
// gsd-core/workflows/update.md is the installed runtime workflow —
|
||||
// its text IS the deployed behavioral contract.
|
||||
|
||||
const { describe, test } = require('node:test');
|
||||
@@ -61,14 +61,14 @@ describe('reapply-patches pristine baseline detection (#2424)', () => {
|
||||
|
||||
test('update.md workflow references backup-meta.json for pristine-hash baseline', () => {
|
||||
// #2790: The behavioral contract (pristine_hashes from backup-meta.json as primary
|
||||
// baseline source) is implemented in the update.md workflow (get-shit-done/workflows/update.md),
|
||||
// baseline source) is implemented in the update.md workflow (gsd-core/workflows/update.md),
|
||||
// not the command file. The command delegates via --reapply flag.
|
||||
// Verify the underlying workflow has this content.
|
||||
const workflowPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'update.md');
|
||||
const workflowPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'update.md');
|
||||
const workflowContent = fs.readFileSync(workflowPath, 'utf-8');
|
||||
assert.ok(
|
||||
workflowContent.includes('backup-meta.json'),
|
||||
'get-shit-done/workflows/update.md must reference backup-meta.json (pristine_hashes baseline source)'
|
||||
'gsd-core/workflows/update.md must reference backup-meta.json (pristine_hashes baseline source)'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -15,13 +15,13 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const QUICK_MD = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md');
|
||||
const QUICK_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
||||
|
||||
describe('quick.md pre-dispatch PLAN.md commit (#2432)', () => {
|
||||
let content;
|
||||
|
||||
test('quick.md exists', () => {
|
||||
assert.ok(fs.existsSync(QUICK_MD), 'get-shit-done/workflows/quick.md must exist');
|
||||
assert.ok(fs.existsSync(QUICK_MD), 'gsd-core/workflows/quick.md must exist');
|
||||
content = fs.readFileSync(QUICK_MD, 'utf-8');
|
||||
});
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const UPDATE_MD = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'update.md');
|
||||
const UPDATE_MD = path.join(__dirname, '..', 'gsd-core', 'workflows', 'update.md');
|
||||
|
||||
describe('update.md — no bare ~.claude path references (#2470)', () => {
|
||||
const content = fs.readFileSync(UPDATE_MD, 'utf-8');
|
||||
|
||||
@@ -19,9 +19,9 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const PLAN_PHASE = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'plan-phase.md');
|
||||
const VERIFY_PHASE = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'verify-phase.md');
|
||||
const SCHEMA_MANIFEST_JSON = path.join(__dirname, '..', 'get-shit-done', 'bin', 'shared', 'config-schema.manifest.json');
|
||||
const PLAN_PHASE = path.join(__dirname, '..', 'gsd-core', 'workflows', 'plan-phase.md');
|
||||
const VERIFY_PHASE = path.join(__dirname, '..', 'gsd-core', 'workflows', 'verify-phase.md');
|
||||
const SCHEMA_MANIFEST_JSON = path.join(__dirname, '..', 'gsd-core', 'bin', 'shared', 'config-schema.manifest.json');
|
||||
|
||||
describe('plan-phase decision-coverage gate (#2492)', () => {
|
||||
const md = fs.readFileSync(PLAN_PHASE, 'utf-8');
|
||||
|
||||
@@ -24,7 +24,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const EXECUTE_PHASE = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'execute-phase.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'execute-phase.md'
|
||||
);
|
||||
|
||||
describe('execute-phase.md — resurrection-detection guard (#2501)', () => {
|
||||
|
||||
@@ -23,7 +23,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const INSERT_PHASE_PATH = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'insert-phase.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'insert-phase.md'
|
||||
);
|
||||
|
||||
describe('bug-2502: insert-phase must update STATE.md next-phase recommendation', () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const VERIFY_PHASE_PATH = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'verify-phase.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'verify-phase.md'
|
||||
);
|
||||
|
||||
/**
|
||||
@@ -39,7 +39,7 @@ describe('bug #2504: UAT auto-pass for foundation/infrastructure phases', () =>
|
||||
test('verify-phase workflow file exists', () => {
|
||||
assert.ok(
|
||||
fs.existsSync(VERIFY_PHASE_PATH),
|
||||
'get-shit-done/workflows/verify-phase.md should exist'
|
||||
'gsd-core/workflows/verify-phase.md should exist'
|
||||
);
|
||||
});
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const SETTINGS_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'settings.md');
|
||||
const SETTINGS_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'settings.md');
|
||||
|
||||
describe('bug #2506: settings.md non-Claude runtime warning for model profiles', () => {
|
||||
let content;
|
||||
|
||||
@@ -28,18 +28,18 @@ const path = require('path');
|
||||
const WORKFLOW_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'execute-phase.md'
|
||||
);
|
||||
|
||||
describe('bug #2516: executor_model "inherit" must not be passed literally to Task()', () => {
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'get-shit-done/workflows/execute-phase.md should exist');
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/execute-phase.md should exist');
|
||||
});
|
||||
|
||||
test('workflow contains instructions for handling the "inherit" case', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'get-shit-done/workflows/execute-phase.md should exist');
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/execute-phase.md should exist');
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
const hasInheritInstruction =
|
||||
content.includes('"inherit"') &&
|
||||
@@ -54,7 +54,7 @@ describe('bug #2516: executor_model "inherit" must not be passed literally to Ta
|
||||
});
|
||||
|
||||
test('workflow does not instruct passing model="inherit" literally to Task', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'get-shit-done/workflows/execute-phase.md should exist');
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/execute-phase.md should exist');
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
// The workflow must not have an unconditional model="{executor_model}" template
|
||||
// that would pass "inherit" through. It should document conditional logic.
|
||||
@@ -99,7 +99,7 @@ describe('bug #2516: executor_model "inherit" must not be passed literally to Ta
|
||||
});
|
||||
|
||||
test('workflow documents that omitting model= causes inheritance from orchestrator', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'get-shit-done/workflows/execute-phase.md should exist');
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/execute-phase.md should exist');
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
const hasInheritanceExplanation =
|
||||
content.includes('inherit') &&
|
||||
|
||||
@@ -17,13 +17,13 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md');
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
||||
|
||||
describe('bug #2523: quick-task final commit includes deferred-items.md', () => {
|
||||
const content = fs.readFileSync(WORKFLOW_PATH, 'utf-8');
|
||||
|
||||
test('workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'get-shit-done/workflows/quick.md must exist');
|
||||
assert.ok(fs.existsSync(WORKFLOW_PATH), 'gsd-core/workflows/quick.md must exist');
|
||||
});
|
||||
|
||||
test('Step 8 file list references deferred-items.md', () => {
|
||||
|
||||
@@ -16,7 +16,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
const { createTempProject, cleanup, runGsdTools } = require('./helpers.cjs');
|
||||
|
||||
const { VALID_CONFIG_KEYS, isValidConfigKey } = require('../get-shit-done/bin/lib/config-schema.cjs');
|
||||
const { VALID_CONFIG_KEYS, isValidConfigKey } = require('../gsd-core/bin/lib/config-schema.cjs');
|
||||
|
||||
describe('VALID_CONFIG_KEYS correctness', () => {
|
||||
test('#2530: workflow._auto_chain_active must not be in VALID_CONFIG_KEYS (internal state)', () => {
|
||||
|
||||
@@ -51,21 +51,21 @@ const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
|
||||
// bug-3584 test is updated to cover it.
|
||||
const RUNTIME_EMITTER_EXCLUDES = new Set([
|
||||
// Primary runtime-slash emitter (bug-3584 canonical contract):
|
||||
path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'runtime-slash.cjs'),
|
||||
path.join(ROOT, 'gsd-core', 'bin', 'lib', 'runtime-slash.cjs'),
|
||||
// phase-lifecycle-policy.ts emits runtime-persisted slash references (bug-3584):
|
||||
path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'phase-lifecycle-policy.ts'),
|
||||
path.join(ROOT, 'gsd-core', 'bin', 'lib', 'phase-lifecycle-policy.ts'),
|
||||
// Generated CJS files match the TS source's emitted form — never hand-edited:
|
||||
// (matched below by .generated.cjs extension — see collectFiles exclusion)
|
||||
]);
|
||||
|
||||
const SEARCH_DIRS = [
|
||||
// NOTE: get-shit-done/bin/lib is intentionally EXCLUDED from SEARCH_DIRS.
|
||||
// NOTE: gsd-core/bin/lib is intentionally EXCLUDED from SEARCH_DIRS.
|
||||
// runtime-slash.cjs and *.generated.cjs live there and use the hyphen form
|
||||
// per bug-3584's runtime-emitter contract. The full bin/lib tree is
|
||||
// runtime-emitter territory — scanning it would cause false positives.
|
||||
path.join(ROOT, 'get-shit-done', 'workflows'),
|
||||
path.join(ROOT, 'get-shit-done', 'references'),
|
||||
path.join(ROOT, 'get-shit-done', 'templates'),
|
||||
path.join(ROOT, 'gsd-core', 'workflows'),
|
||||
path.join(ROOT, 'gsd-core', 'references'),
|
||||
path.join(ROOT, 'gsd-core', 'templates'),
|
||||
COMMANDS_DIR,
|
||||
path.join(ROOT, 'agents'),
|
||||
path.join(ROOT, 'hooks'),
|
||||
@@ -118,7 +118,7 @@ describe('slash-command namespace invariant (#3443)', () => {
|
||||
// SCOPED ACTIVE INVARIANT (2026-05-23 re-activation after Codex adversarial review of PR #164).
|
||||
//
|
||||
// Scan is scoped to Claude-facing source directories only (SEARCH_DIRS above).
|
||||
// get-shit-done/bin/lib/ is excluded entirely — runtime-slash.cjs and
|
||||
// gsd-core/bin/lib/ is excluded entirely — runtime-slash.cjs and
|
||||
// *.generated.cjs there use hyphen form per bug-3584's runtime-emitter contract.
|
||||
//
|
||||
// If this test fails: check CONTEXT.md § "Slash-command form: directory-level matrix"
|
||||
|
||||
@@ -56,9 +56,9 @@ describe('convertClaudeToCopilotContent — bare ~/.claude (issue #2545)', () =>
|
||||
});
|
||||
|
||||
test('does not double-replace trailing-slash form', () => {
|
||||
const input = '@~/.claude/get-shit-done/foo.md\n';
|
||||
const input = '@~/.claude/gsd-core/foo.md\n';
|
||||
const out = convertClaudeToCopilotContent(input, true);
|
||||
assert.match(out, /~\/\.copilot\/get-shit-done\/foo\.md/);
|
||||
assert.match(out, /~\/\.copilot\/gsd-core\/foo\.md/);
|
||||
assert.ok(!/\.copilot\/\.copilot/.test(out));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,12 +19,12 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const DISCUSS_PHASE = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'discuss-phase.md',
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'discuss-phase.md',
|
||||
);
|
||||
// After #2551 progressive-disclosure refactor, the scout_codebase phase-type
|
||||
// table and split-reads warning live in references/scout-codebase.md.
|
||||
const SCOUT_REF = path.join(
|
||||
__dirname, '..', 'get-shit-done', 'references', 'scout-codebase.md',
|
||||
__dirname, '..', 'gsd-core', 'references', 'scout-codebase.md',
|
||||
);
|
||||
|
||||
function readDiscussContext() {
|
||||
|
||||
@@ -15,7 +15,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
const { getMilestonePhaseFilter } = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
describe('bug #2554 — getMilestonePhaseFilter decimal phase dirs', () => {
|
||||
let tmpDir;
|
||||
|
||||
@@ -17,7 +17,7 @@ const { describe, test } = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const projection = require(path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const { projectLocalHookPrefix, projectShellCommandText } = projection;
|
||||
|
||||
describe('bug #2557: Gemini/Antigravity local hooks use relative paths (not $CLAUDE_PROJECT_DIR)', () => {
|
||||
|
||||
@@ -17,7 +17,7 @@ const path = require('path');
|
||||
const { execFileSync } = require('child_process');
|
||||
const { createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const { loadConfig } = require('../get-shit-done/bin/lib/core.cjs');
|
||||
const { loadConfig } = require('../gsd-core/bin/lib/core.cjs');
|
||||
|
||||
function makeSubRepo(parent, name) {
|
||||
const dir = path.join(parent, name);
|
||||
|
||||
@@ -25,7 +25,7 @@ const {
|
||||
skillFrontmatterName,
|
||||
} = require(path.join(ROOT, 'bin', 'install.js'));
|
||||
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
|
||||
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
|
||||
|
||||
function collectFiles(dir, results) {
|
||||
@@ -61,7 +61,19 @@ function collectFiles(dir, results) {
|
||||
* happens at the call site so the extractor stays neutral.
|
||||
*/
|
||||
function extractSkillCalls(content) {
|
||||
const stripped = content.replace(/<!--[\s\S]*?-->/g, '');
|
||||
// regex-free HTML-comment stripper (CodeQL: avoid incomplete-multi-character-sanitization)
|
||||
let stripped = '';
|
||||
{
|
||||
let rest = content;
|
||||
let idx;
|
||||
while ((idx = rest.indexOf('<!--')) !== -1) {
|
||||
stripped += rest.slice(0, idx);
|
||||
const end = rest.indexOf('-->', idx + 4);
|
||||
if (end === -1) { rest = ''; break; }
|
||||
rest = rest.slice(end + 3);
|
||||
}
|
||||
stripped += rest;
|
||||
}
|
||||
const calls = [];
|
||||
// Body class excludes backslash so the extractor doesn't include an
|
||||
// escape character that precedes the closing quote in embedded examples
|
||||
|
||||
@@ -13,7 +13,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('path');
|
||||
|
||||
const { extractOneLinerFromBody } = require(
|
||||
path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'core.cjs')
|
||||
path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs')
|
||||
);
|
||||
|
||||
describe('bug #2660: extractOneLinerFromBody', () => {
|
||||
|
||||
@@ -40,7 +40,7 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'execute-plan.md');
|
||||
const WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'execute-plan.md');
|
||||
|
||||
function writeRoadmap(tmpDir, content) {
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), content);
|
||||
|
||||
@@ -28,7 +28,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const { runGsdTools, createTempProject, cleanup } = require('./helpers.cjs');
|
||||
const { extractFrontmatter } = require('../get-shit-done/bin/lib/frontmatter.cjs');
|
||||
const { extractFrontmatter } = require('../gsd-core/bin/lib/frontmatter.cjs');
|
||||
|
||||
function makePlanProject(files = {}) {
|
||||
const dir = createTempProject();
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*
|
||||
* USER-PROFILE.md is a user-owned artifact created/refreshed by /gsd-profile-user.
|
||||
* preserveUserArtifacts() correctly preserves it across reinstalls. But writeManifest()
|
||||
* also records it under "get-shit-done/USER-PROFILE.md" with a SHA-256 of whatever was
|
||||
* also records it under "gsd-core/USER-PROFILE.md" with a SHA-256 of whatever was
|
||||
* on disk at install time. On the next install, saveLocalPatches() compares the on-disk
|
||||
* (refreshed) hash to the manifest hash, finds them different, and emits the spurious
|
||||
* "Found N locally modified GSD file(s) — backed up to gsd-local-patches/" warning.
|
||||
@@ -53,11 +53,11 @@ describe('#2771: USER-PROFILE.md is excluded from gsd-file-manifest.json', () =>
|
||||
beforeEach(() => { tmpDir = createTempDir('gsd-2771-manifest-'); });
|
||||
afterEach(() => { cleanup(tmpDir); });
|
||||
|
||||
test('writeManifest excludes get-shit-done/USER-PROFILE.md even when present on disk', () => {
|
||||
test('writeManifest excludes gsd-core/USER-PROFILE.md even when present on disk', () => {
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// Simulate /gsd-profile-user creating USER-PROFILE.md
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
fs.writeFileSync(profilePath, '# My Profile\n\nFirst version.\n');
|
||||
|
||||
// Re-install: writeManifest runs again with USER-PROFILE.md present on disk
|
||||
@@ -68,8 +68,8 @@ describe('#2771: USER-PROFILE.md is excluded from gsd-file-manifest.json', () =>
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
||||
|
||||
assert.ok(
|
||||
!Object.prototype.hasOwnProperty.call(manifest.files, 'get-shit-done/USER-PROFILE.md'),
|
||||
'manifest.files must NOT contain get-shit-done/USER-PROFILE.md — it is a user artifact, not distribution'
|
||||
!Object.prototype.hasOwnProperty.call(manifest.files, 'gsd-core/USER-PROFILE.md'),
|
||||
'manifest.files must NOT contain gsd-core/USER-PROFILE.md — it is a user artifact, not distribution'
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -85,7 +85,7 @@ describe('#2771: USER-PROFILE.md is still preserved across reinstall', () => {
|
||||
test('USER-PROFILE.md content survives reinstall (preservation regression guard)', () => {
|
||||
runInstaller(tmpDir);
|
||||
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
const content = '# Profile\n\nUser content from /gsd-profile-user.\n';
|
||||
fs.writeFileSync(profilePath, content);
|
||||
|
||||
@@ -109,7 +109,7 @@ describe('#2771: refreshed USER-PROFILE.md does not trigger local-patches warnin
|
||||
runInstaller(tmpDir);
|
||||
|
||||
// /gsd-profile-user creates USER-PROFILE.md (v1)
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
fs.writeFileSync(profilePath, '# Profile v1\n');
|
||||
|
||||
// Reinstall — manifest written with v1 contents (under buggy code) or excluded (under fix)
|
||||
@@ -123,7 +123,7 @@ describe('#2771: refreshed USER-PROFILE.md does not trigger local-patches warnin
|
||||
const output = runInstaller(tmpDir);
|
||||
|
||||
const patchesDir = path.join(tmpDir, PATCHES_DIR_NAME);
|
||||
const patchFile = path.join(patchesDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const patchFile = path.join(patchesDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
assert.ok(
|
||||
!fs.existsSync(patchFile),
|
||||
'USER-PROFILE.md must NOT appear in gsd-local-patches/ — it is a user artifact, not a modified distribution file'
|
||||
@@ -152,7 +152,7 @@ describe('#2771: legacy manifest entries for USER_OWNED_ARTIFACTS are normalized
|
||||
// Initial install
|
||||
runInstaller(tmpDir);
|
||||
|
||||
const profilePath = path.join(tmpDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const profilePath = path.join(tmpDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
fs.writeFileSync(profilePath, '# Profile v1\n');
|
||||
|
||||
// Reinstall to populate manifest under the (now-fixed) writer
|
||||
@@ -163,7 +163,7 @@ describe('#2771: legacy manifest entries for USER_OWNED_ARTIFACTS are normalized
|
||||
const manifestPath = path.join(tmpDir, MANIFEST_NAME);
|
||||
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
||||
manifest.files = manifest.files || {};
|
||||
manifest.files['get-shit-done/USER-PROFILE.md'] = 'deadbeef'.repeat(8); // stale hash
|
||||
manifest.files['gsd-core/USER-PROFILE.md'] = 'deadbeef'.repeat(8); // stale hash
|
||||
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
||||
|
||||
// /gsd-profile-user --refresh rewrites USER-PROFILE.md
|
||||
@@ -173,7 +173,7 @@ describe('#2771: legacy manifest entries for USER_OWNED_ARTIFACTS are normalized
|
||||
const output = runInstaller(tmpDir);
|
||||
|
||||
const patchesDir = path.join(tmpDir, PATCHES_DIR_NAME);
|
||||
const patchFile = path.join(patchesDir, 'get-shit-done', 'USER-PROFILE.md');
|
||||
const patchFile = path.join(patchesDir, 'gsd-core', 'USER-PROFILE.md');
|
||||
assert.ok(
|
||||
!fs.existsSync(patchFile),
|
||||
'legacy USER-PROFILE.md manifest entry must be normalized away — not backed up as a patch'
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
* any submodule path.
|
||||
*
|
||||
* Behavioral test: the bash decision pipeline from
|
||||
* get-shit-done/workflows/execute-phase.md is extracted verbatim into an
|
||||
* gsd-core/workflows/execute-phase.md is extracted verbatim into an
|
||||
* executable snippet here, then run via execFileSync('bash', ...) against
|
||||
* real fixture projects built with `createTempGitProject()`. We assert
|
||||
* the resulting USE_WORKTREES_FOR_PLAN value (printed on the final line
|
||||
@@ -340,14 +340,14 @@ describe('execute-phase.md dispatch wires USE_WORKTREES_FOR_PLAN (#2772)', () =>
|
||||
const workflowPath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'execute-phase.md'
|
||||
);
|
||||
const gatePath = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'execute-phase',
|
||||
'steps',
|
||||
@@ -439,7 +439,7 @@ describe('execute-phase.md dispatch wires USE_WORKTREES_FOR_PLAN (#2772)', () =>
|
||||
// (b) actually abort when run against a fixture that stages a submodule path.
|
||||
|
||||
describe('quick.md executor pre-commit submodule guard (#2772)', () => {
|
||||
const quickPath = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'quick.md');
|
||||
const quickPath = path.join(__dirname, '..', 'gsd-core', 'workflows', 'quick.md');
|
||||
|
||||
test('quick.md executor prompt injects SUBMODULE_PATHS', () => {
|
||||
const md = fs.readFileSync(quickPath, 'utf-8');
|
||||
|
||||
@@ -14,7 +14,7 @@
|
||||
* file was never deleted.
|
||||
*
|
||||
* Fix: add `rm -f "$HOME/.cache/gsd/gsd-update-check.json"` to the
|
||||
* run_update step's cache-clear block in get-shit-done/workflows/update.md.
|
||||
* run_update step's cache-clear block in gsd-core/workflows/update.md.
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
@@ -27,7 +27,7 @@ const path = require('node:path');
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const UPDATE_WORKFLOW = path.join(
|
||||
REPO_ROOT,
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'update.md'
|
||||
);
|
||||
|
||||
@@ -86,7 +86,7 @@ describe('bug-2798: context_window is a valid config key', () => {
|
||||
t.skip('sdk/dist/query/config-schema.js not built — run `cd sdk && npm run build` to enable this integration test');
|
||||
return;
|
||||
}
|
||||
const cjsSchema = require(path.join(REPO_ROOT, 'get-shit-done', 'bin', 'lib', 'config-schema.cjs'));
|
||||
const cjsSchema = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'config-schema.cjs'));
|
||||
const sdkSchema = require(path.join(REPO_ROOT, 'sdk', 'dist', 'query', 'config-schema.js'));
|
||||
|
||||
assert.ok(
|
||||
|
||||
@@ -24,7 +24,7 @@ const childProc = require('node:child_process');
|
||||
const { createTempProject, cleanup, TOOLS_PATH } = require('./helpers.cjs');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const WORKFLOW_FILE = path.join(REPO_ROOT, 'get-shit-done', 'workflows', 'ingest-docs.md');
|
||||
const WORKFLOW_FILE = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'ingest-docs.md');
|
||||
|
||||
function spawnGsdTools(args, projectDir) {
|
||||
let stdout = '';
|
||||
@@ -136,13 +136,13 @@ describe('bug-2801: ingest-docs.md workflow calls gsd-tools not gsd-sdk', () =>
|
||||
// Per #2851 the only valid form is the absolute-path node invocation; the
|
||||
// legacy bare `gsd-tools` is the bug being fixed and must not be accepted.
|
||||
const initLine = bashLines.find((l) =>
|
||||
/\bnode\s+["']?\$HOME\/\.claude\/get-shit-done\/bin\/gsd-tools\.cjs["']?\s+init\s+ingest-docs\b/.test(l)
|
||||
/\bnode\s+["']?\$HOME\/\.claude\/gsd-core\/bin\/gsd-tools\.cjs["']?\s+init\s+ingest-docs\b/.test(l)
|
||||
);
|
||||
assert.ok(initLine, 'workflow must invoke init ingest-docs via canonical node-path gsd-tools.cjs');
|
||||
});
|
||||
|
||||
test('cmdInitIngestDocs is exported from init.cjs', () => {
|
||||
const init = require(path.join(REPO_ROOT, 'get-shit-done', 'bin', 'lib', 'init.cjs'));
|
||||
const init = require(path.join(REPO_ROOT, 'gsd-core', 'bin', 'lib', 'init.cjs'));
|
||||
assert.strictEqual(typeof init.cmdInitIngestDocs, 'function', 'cmdInitIngestDocs must be exported');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -40,13 +40,13 @@ const { convertClaudeCommandToClaudeSkill, installRuntimeArtifacts, uninstallRun
|
||||
const {
|
||||
loadSkillsManifest,
|
||||
resolveProfile,
|
||||
} = require(path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
} = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
|
||||
// Full resolved profile — installs all available skills from the source dir
|
||||
const _manifest = loadSkillsManifest();
|
||||
const resolvedProfileFull = resolveProfile({ modes: [], manifest: _manifest });
|
||||
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(ROOT, 'gsd-core', 'workflows');
|
||||
const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
|
||||
|
||||
function walkMd(dir) {
|
||||
@@ -128,7 +128,19 @@ describe('bug-2808: SKILL.md name: uses hyphen form', () => {
|
||||
for (const f of workflowFiles) {
|
||||
const src = fs.readFileSync(f, 'utf-8');
|
||||
// Strip HTML comments to avoid matching commented-out examples.
|
||||
const stripped = src.replace(/<!--[\s\S]*?-->/g, '');
|
||||
// regex-free HTML-comment stripper (CodeQL: avoid incomplete-multi-character-sanitization)
|
||||
let stripped = '';
|
||||
{
|
||||
let rest = src;
|
||||
let idx;
|
||||
while ((idx = rest.indexOf('<!--')) !== -1) {
|
||||
stripped += rest.slice(0, idx);
|
||||
const end = rest.indexOf('-->', idx + 4);
|
||||
if (end === -1) { rest = ''; break; }
|
||||
rest = rest.slice(end + 3);
|
||||
}
|
||||
stripped += rest;
|
||||
}
|
||||
// Scan each line for Skill() calls using the colon form.
|
||||
// Parsing line-by-line is more precise than a multi-line regex
|
||||
// and avoids false positives from incidental matches in prose.
|
||||
|
||||
@@ -119,7 +119,7 @@ describe('bug-2831: OpenCode pathPrefix uses absolute path on all platforms', ()
|
||||
const srcFile = path.join(srcCmdDir, 'autonomous.md');
|
||||
fs.writeFileSync(
|
||||
srcFile,
|
||||
'---\nname: autonomous\n---\n<execution_context>\n@~/.claude/get-shit-done/workflows/autonomous.md\n@$HOME/.claude/get-shit-done/references/ui-brand.md\n</execution_context>\n'
|
||||
'---\nname: autonomous\n---\n<execution_context>\n@~/.claude/gsd-core/workflows/autonomous.md\n@$HOME/.claude/gsd-core/references/ui-brand.md\n</execution_context>\n'
|
||||
);
|
||||
|
||||
const homeDir = path.join(tmp, 'home').replace(/\\/g, '/');
|
||||
|
||||
@@ -21,7 +21,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
|
||||
const auditModule = require('../get-shit-done/bin/lib/audit.cjs');
|
||||
const auditModule = require('../gsd-core/bin/lib/audit.cjs');
|
||||
const { auditOpenArtifacts } = auditModule;
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
@@ -159,7 +159,7 @@ describe('bug #2836: workflows/help.md one-liner reconciliation', () => {
|
||||
test('help.md quick-task one-liner uses ${quick_id}-SUMMARY.md pattern', () => {
|
||||
// After #3039, help content moved into help/modes/full.md.
|
||||
const helpPath = path.resolve(
|
||||
__dirname, '..', 'get-shit-done', 'workflows', 'help', 'modes', 'full.md'
|
||||
__dirname, '..', 'gsd-core', 'workflows', 'help', 'modes', 'full.md'
|
||||
);
|
||||
const content = fs.readFileSync(helpPath, 'utf-8');
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
const REPO_ROOT = path.join(__dirname, '..');
|
||||
const EXECUTE_PHASE_PATH = path.join(REPO_ROOT, 'get-shit-done', 'workflows', 'execute-phase.md');
|
||||
const QUICK_PATH = path.join(REPO_ROOT, 'get-shit-done', 'workflows', 'quick.md');
|
||||
const EXECUTE_PHASE_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'execute-phase.md');
|
||||
const QUICK_PATH = path.join(REPO_ROOT, 'gsd-core', 'workflows', 'quick.md');
|
||||
|
||||
/**
|
||||
* Parse a workflow markdown file into a structured contract object.
|
||||
|
||||
@@ -5,12 +5,12 @@
|
||||
* Use the resolver snippets for SDK calls, or an explicit local CJS path when
|
||||
* a command intentionally targets the checked-in legacy script:
|
||||
*
|
||||
* node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" <subcommand> [args]
|
||||
* node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" <subcommand> [args]
|
||||
*
|
||||
* Some workflow markdown files leaked the bare `gsd-tools <subcommand>` form,
|
||||
* which fails with `command not found` at runtime.
|
||||
*
|
||||
* This test parses every markdown file in get-shit-done/workflows/ structurally:
|
||||
* This test parses every markdown file in gsd-core/workflows/ structurally:
|
||||
* it tokenizes the content into fenced code blocks, then on each shell-block
|
||||
* line checks whether `gsd-tools` appears as a bare command (not preceded by
|
||||
* `node `, not part of the filename `gsd-tools.cjs`, not inside a comment).
|
||||
@@ -26,7 +26,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
|
||||
/**
|
||||
* Extract shell-fenced code blocks from a markdown file.
|
||||
@@ -116,7 +116,7 @@ function lineHasBareGsdTools(line) {
|
||||
}
|
||||
|
||||
describe('bug-2851: workflow files must not call bare `gsd-tools` (#2245 sweep regression)', () => {
|
||||
test('no get-shit-done/workflows/*.md file contains a bare gsd-tools command', () => {
|
||||
test('no gsd-core/workflows/*.md file contains a bare gsd-tools command', () => {
|
||||
const files = fs.readdirSync(WORKFLOWS_DIR).filter((f) => f.endsWith('.md'));
|
||||
assert.ok(files.length > 0, 'expected workflow files to exist');
|
||||
|
||||
@@ -138,7 +138,7 @@ describe('bug-2851: workflow files must not call bare `gsd-tools` (#2245 sweep r
|
||||
violations,
|
||||
[],
|
||||
'Bare `gsd-tools` invocations found in workflow shell blocks. ' +
|
||||
'Use a resolver snippet or `node "$HOME/.claude/get-shit-done/bin/gsd-tools.cjs" <subcommand>` instead.\n' +
|
||||
'Use a resolver snippet or `node "$HOME/.claude/gsd-core/bin/gsd-tools.cjs" <subcommand>` instead.\n' +
|
||||
violations.join('\n'),
|
||||
);
|
||||
});
|
||||
@@ -153,7 +153,7 @@ describe('bug-2851: workflow files must not call bare `gsd-tools` (#2245 sweep r
|
||||
foundGapAnalysisCall = true;
|
||||
assert.match(
|
||||
line,
|
||||
/\bnode\s+["']?\$HOME\/\.claude\/get-shit-done\/bin\/gsd-tools\.cjs["']?\s+gap-analysis\b/,
|
||||
/\bnode\s+["']?\$HOME\/\.claude\/gsd-core\/bin\/gsd-tools\.cjs["']?\s+gap-analysis\b/,
|
||||
`gap-analysis call must use canonical absolute-path invocation, got: ${line.trim()}`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ describe('audit-open — output shape (#2911)', () => {
|
||||
assert.equal(typeof parsed, 'object', 'parsed payload must be an object');
|
||||
assert.ok(parsed !== null, 'parsed payload must not be null');
|
||||
|
||||
// Shape contract from auditOpenArtifacts() in get-shit-done/bin/lib/audit.cjs.
|
||||
// Shape contract from auditOpenArtifacts() in gsd-core/bin/lib/audit.cjs.
|
||||
assert.equal(typeof parsed.scanned_at, 'string', 'must include scanned_at ISO timestamp');
|
||||
assert.equal(typeof parsed.has_open_items, 'boolean', 'must include has_open_items boolean');
|
||||
assert.equal(typeof parsed.counts, 'object', 'must include counts object');
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* Tests for issue #2912 — /gsd-progress can use stale CLAUDE.md project block
|
||||
* instead of GSD tracking files as authoritative source.
|
||||
*
|
||||
* Fix: the `report` step in get-shit-done/workflows/progress.md must contain
|
||||
* Fix: the `report` step in gsd-core/workflows/progress.md must contain
|
||||
* an explicit "context authority" directive establishing PROJECT.md, STATE.md,
|
||||
* and ROADMAP.md as the authoritative sources for the progress report, and
|
||||
* forbidding the use of CLAUDE.md `## Project` blocks as a source for any
|
||||
@@ -21,7 +21,7 @@ const path = require('path');
|
||||
const WORKFLOW_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'progress.md'
|
||||
);
|
||||
|
||||
@@ -31,7 +31,7 @@ const { cleanup } = require('./helpers.cjs');
|
||||
const EXECUTE_PHASE_PATH = path.join(
|
||||
__dirname,
|
||||
'..',
|
||||
'get-shit-done',
|
||||
'gsd-core',
|
||||
'workflows',
|
||||
'execute-phase.md'
|
||||
);
|
||||
|
||||
@@ -110,14 +110,14 @@ describe('detect-custom-files — skills/ directory missing from GSD_MANAGED_DIR
|
||||
);
|
||||
});
|
||||
|
||||
// Test 3: regression guard — still detects custom files in get-shit-done/workflows/
|
||||
test('regression: still detects custom files in get-shit-done/workflows/', () => {
|
||||
// Test 3: regression guard — still detects custom files in gsd-core/workflows/
|
||||
test('regression: still detects custom files in gsd-core/workflows/', () => {
|
||||
writeManifest(tmpDir, {
|
||||
'get-shit-done/workflows/plan-phase.md': '# Plan Phase\n',
|
||||
'gsd-core/workflows/plan-phase.md': '# Plan Phase\n',
|
||||
'skills/gsd-planner/SKILL.md': '# GSD Planner Skill\n',
|
||||
});
|
||||
|
||||
writeCustomFile(tmpDir, 'get-shit-done/workflows/custom-workflow.md', '# My Custom Workflow\n');
|
||||
writeCustomFile(tmpDir, 'gsd-core/workflows/custom-workflow.md', '# My Custom Workflow\n');
|
||||
|
||||
const result = runGsdTools(
|
||||
['detect-custom-files', '--config-dir', tmpDir],
|
||||
@@ -128,7 +128,7 @@ describe('detect-custom-files — skills/ directory missing from GSD_MANAGED_DIR
|
||||
|
||||
const json = JSON.parse(result.output);
|
||||
assert.ok(
|
||||
json.custom_files.includes('get-shit-done/workflows/custom-workflow.md'),
|
||||
json.custom_files.includes('gsd-core/workflows/custom-workflow.md'),
|
||||
`custom workflow should still be detected; got: ${JSON.stringify(json.custom_files)}`
|
||||
);
|
||||
});
|
||||
|
||||
@@ -25,8 +25,8 @@ const path = require('node:path');
|
||||
const os = require('node:os');
|
||||
const { execFileSync } = require('node:child_process');
|
||||
|
||||
const GSD_TOOLS = path.join(__dirname, '..', 'get-shit-done', 'bin', 'gsd-tools.cjs');
|
||||
const { ERROR_REASON } = require(path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'core.cjs'));
|
||||
const GSD_TOOLS = path.join(__dirname, '..', 'gsd-core', 'bin', 'gsd-tools.cjs');
|
||||
const { ERROR_REASON } = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'core.cjs'));
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
describe('bug-2943: config-get returns schema default for context_window', () => {
|
||||
|
||||
@@ -26,7 +26,7 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const SPIKE_CMD_PATH = path.join(__dirname, '..', 'commands', 'gsd', 'spike.md');
|
||||
const SPIKE_WORKFLOW_PATH = path.join(__dirname, '..', 'get-shit-done', 'workflows', 'spike.md');
|
||||
const SPIKE_WORKFLOW_PATH = path.join(__dirname, '..', 'gsd-core', 'workflows', 'spike.md');
|
||||
|
||||
/**
|
||||
* Parse YAML frontmatter + body from a markdown file.
|
||||
@@ -138,9 +138,9 @@ describe('bug-2948: /gsd:spike --wrap-up dispatch wiring', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('get-shit-done/workflows/spike.md — companion references', () => {
|
||||
describe('gsd-core/workflows/spike.md — companion references', () => {
|
||||
test('spike workflow file exists', () => {
|
||||
assert.ok(fs.existsSync(SPIKE_WORKFLOW_PATH), 'get-shit-done/workflows/spike.md should exist');
|
||||
assert.ok(fs.existsSync(SPIKE_WORKFLOW_PATH), 'gsd-core/workflows/spike.md should exist');
|
||||
});
|
||||
|
||||
test('does NOT reference the deleted /gsd-spike-wrap-up entry-point', () => {
|
||||
|
||||
@@ -20,7 +20,7 @@ const path = require('path');
|
||||
|
||||
const ROOT = path.resolve(__dirname, '..');
|
||||
const SKETCH_COMMAND = path.join(ROOT, 'commands/gsd/sketch.md');
|
||||
const SKETCH_WORKFLOW = path.join(ROOT, 'get-shit-done/workflows/sketch.md');
|
||||
const SKETCH_WORKFLOW = path.join(ROOT, 'gsd-core/workflows/sketch.md');
|
||||
|
||||
describe('bug-2949: sketch --wrap-up dispatch wiring', () => {
|
||||
test('commands/gsd/sketch.md contains --wrap-up dispatch logic', () => {
|
||||
|
||||
@@ -22,7 +22,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const WORKFLOWS_DIR = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
|
||||
function read(filename) {
|
||||
return fs.readFileSync(path.join(WORKFLOWS_DIR, filename), 'utf-8');
|
||||
|
||||
@@ -38,8 +38,8 @@ const COMMANDS_DIR = path.join(ROOT, 'commands', 'gsd');
|
||||
// After #3039, the canonical command reference is the `--full` mode file.
|
||||
// `workflows/help.md` is now a small dispatcher; the bidirectional parity
|
||||
// invariant lives with the comprehensive reference body.
|
||||
const HELP_MD = path.join(ROOT, 'get-shit-done', 'workflows', 'help', 'modes', 'full.md');
|
||||
const DO_MD = path.join(ROOT, 'get-shit-done', 'workflows', 'do.md');
|
||||
const HELP_MD = path.join(ROOT, 'gsd-core', 'workflows', 'help', 'modes', 'full.md');
|
||||
const DO_MD = path.join(ROOT, 'gsd-core', 'workflows', 'do.md');
|
||||
|
||||
function parseFrontmatter(content) {
|
||||
const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
||||
@@ -79,7 +79,10 @@ function extractSlashReferences(contents) {
|
||||
const names = new Set();
|
||||
// Negative lookbehind: must not be preceded by a letter (avoids matching npm scope
|
||||
// paths like @opengsd/gsd-core where `/gsd-` appears inside a package URL).
|
||||
const tokenRe = /(?<![a-z])\/gsd[:-]([a-z][a-z0-9-]*)/g;
|
||||
// Negative lookahead (?![\w-]*\/): excludes filesystem path segments like
|
||||
// `/gsd-core/bin` where the captured name is followed by a `/`, which would
|
||||
// be a directory segment rather than a slash command name.
|
||||
const tokenRe = /(?<![a-z])\/gsd[:-]([a-z][a-z0-9-]*)(?![\w-]*\/)/g;
|
||||
let match;
|
||||
while ((match = tokenRe.exec(contents)) !== null) {
|
||||
names.add(match[1]);
|
||||
|
||||
@@ -30,10 +30,10 @@ const cp = require('node:child_process');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
// Script lives at get-shit-done/bin/ so the installer ships it under
|
||||
// `${GSD_HOME}/get-shit-done/bin/` (issue #2994). The top-level scripts/
|
||||
// Script lives at gsd-core/bin/ so the installer ships it under
|
||||
// `${GSD_HOME}/gsd-core/bin/` (issue #2994). The top-level scripts/
|
||||
// directory is not copied to user installs.
|
||||
const SCRIPT = path.join(ROOT, 'get-shit-done', 'bin', 'verify-reapply-patches.cjs');
|
||||
const SCRIPT = path.join(ROOT, 'gsd-core', 'bin', 'verify-reapply-patches.cjs');
|
||||
const { REASON } = require(SCRIPT);
|
||||
|
||||
let tmpRoot;
|
||||
|
||||
@@ -17,7 +17,7 @@ process.env.GSD_TEST_MODE = '1';
|
||||
* migration claim that "Legacy commands/gsd directory removed
|
||||
* (replaced by skills/)".
|
||||
*
|
||||
* Root cause: the writer at get-shit-done/bin/lib/profile-output.cjs
|
||||
* Root cause: the writer at gsd-core/bin/lib/profile-output.cjs
|
||||
* fell back to commands/gsd/dev-preferences.md when no --output was passed.
|
||||
* The /gsd-profile-user workflow does not pass --output, so every refresh
|
||||
* deterministically re-creates the legacy directory.
|
||||
@@ -43,8 +43,8 @@ const os = require('node:os');
|
||||
const { cleanup } = require('./helpers.cjs');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const PROFILE_OUTPUT = path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'profile-output.cjs');
|
||||
const WORKFLOW = path.join(ROOT, 'get-shit-done', 'workflows', 'profile-user.md');
|
||||
const PROFILE_OUTPUT = path.join(ROOT, 'gsd-core', 'bin', 'lib', 'profile-output.cjs');
|
||||
const WORKFLOW = path.join(ROOT, 'gsd-core', 'workflows', 'profile-user.md');
|
||||
const INSTALL = path.join(ROOT, 'bin', 'install.js');
|
||||
|
||||
describe('Bug #2973: dev-preferences default writer path is skills/gsd-dev-preferences/SKILL.md', () => {
|
||||
@@ -173,7 +173,7 @@ describe('Bug #2973 (#3003 CR): installRuntimeArtifacts preserves user-owned gsd
|
||||
// installRuntimeArtifacts → _copyStaged overlays only staged skill dirs;
|
||||
// gsd-dev-preferences (not in source) is left untouched.
|
||||
const inst = require(INSTALL);
|
||||
const { loadSkillsManifest, resolveProfile } = require(path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
const { loadSkillsManifest, resolveProfile } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2973-wipe-'));
|
||||
try {
|
||||
const configDir = path.join(tmp, 'config');
|
||||
@@ -213,7 +213,7 @@ describe('Bug #2973 (#3003 CR): installRuntimeArtifacts preserves user-owned gsd
|
||||
// uninstallRuntimeArtifacts removes all gsd-* entries; installRuntimeArtifacts
|
||||
// then writes fresh ones from source.
|
||||
const inst = require(INSTALL);
|
||||
const { loadSkillsManifest, resolveProfile } = require(path.join(ROOT, 'get-shit-done', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
const { loadSkillsManifest, resolveProfile } = require(path.join(ROOT, 'gsd-core', 'bin', 'lib', 'install-profiles.cjs'));
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2973-wipe-shipped-'));
|
||||
try {
|
||||
const configDir = path.join(tmp, 'config');
|
||||
|
||||
@@ -4,7 +4,7 @@ process.env.GSD_TEST_MODE = '1';
|
||||
|
||||
/**
|
||||
* Bug #2986: Layer-3 fault-detection audit found 4.62% Stryker mutation
|
||||
* score on get-shit-done/bin/lib/config-schema.cjs (6 killed, 124 survived).
|
||||
* score on gsd-core/bin/lib/config-schema.cjs (6 killed, 124 survived).
|
||||
* Surviving mutants document tests that "exercise paths" but don't
|
||||
* "verify outputs" -- a polarity flip or predicate swap inside the lib
|
||||
* passed every existing test.
|
||||
@@ -48,7 +48,7 @@ const {
|
||||
VALID_CONFIG_KEYS,
|
||||
DYNAMIC_KEY_PATTERNS,
|
||||
isValidConfigKey,
|
||||
} = require('../get-shit-done/bin/lib/config-schema.cjs');
|
||||
} = require('../gsd-core/bin/lib/config-schema.cjs');
|
||||
|
||||
describe('Bug #2986: M1/M4 -- isValidConfigKey returns true for EVERY static key in VALID_CONFIG_KEYS', () => {
|
||||
// Stryker mutants like `if (false) return true;` would silently flip
|
||||
|
||||
@@ -8,7 +8,7 @@ const cp = require('node:child_process');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const { checkLatestVersion, CHECK_REASON, PACKAGE_NAME } = require(
|
||||
path.join(ROOT, 'get-shit-done', 'bin', 'check-latest-version.cjs'),
|
||||
path.join(ROOT, 'gsd-core', 'bin', 'check-latest-version.cjs'),
|
||||
);
|
||||
|
||||
// checkLatestVersion is a pure-ish function: it spawns one fixed npm
|
||||
@@ -36,7 +36,7 @@ describe('Bug #2992: deterministic latest-version check', () => {
|
||||
});
|
||||
|
||||
describe('Bug #2992: error paths', () => {
|
||||
const { checkLatestVersion, CHECK_REASON } = require(require('node:path').join(__dirname, '..', 'get-shit-done', 'bin', 'check-latest-version.cjs'));
|
||||
const { checkLatestVersion, CHECK_REASON } = require(require('node:path').join(__dirname, '..', 'gsd-core', 'bin', 'check-latest-version.cjs'));
|
||||
|
||||
test('FAIL_NPM_FAILED when npm exits non-zero (e.g. offline, 404)', () => {
|
||||
const r = checkLatestVersion({
|
||||
|
||||
@@ -6,12 +6,12 @@ process.env.GSD_TEST_MODE = '1';
|
||||
* Bug #2994: scripts/verify-reapply-patches.cjs ships in tarball but is
|
||||
* not installed at ${GSD_HOME}/scripts/.
|
||||
*
|
||||
* Root cause: bin/install.js copies the get-shit-done/ source tree to
|
||||
* ${configDir}/get-shit-done/ but does NOT copy the top-level scripts/
|
||||
* Root cause: bin/install.js copies the gsd-core/ source tree to
|
||||
* ${configDir}/gsd-core/ but does NOT copy the top-level scripts/
|
||||
* directory. The verifier script lived under scripts/ so /gsd-reapply-patches
|
||||
* Step 5 hit `Cannot find module …/scripts/verify-reapply-patches.cjs`.
|
||||
*
|
||||
* Fix: move the script to get-shit-done/bin/verify-reapply-patches.cjs
|
||||
* Fix: move the script to gsd-core/bin/verify-reapply-patches.cjs
|
||||
* (which IS installed) and update reapply-patches.md to point there.
|
||||
*
|
||||
* This test enforces the structural invariant that prevents regression.
|
||||
@@ -23,19 +23,19 @@ const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..');
|
||||
const RUNTIME_SCRIPT_PATH = path.join(ROOT, 'get-shit-done', 'bin', 'verify-reapply-patches.cjs');
|
||||
const RUNTIME_SCRIPT_PATH = path.join(ROOT, 'gsd-core', 'bin', 'verify-reapply-patches.cjs');
|
||||
const STALE_SCRIPT_PATH = path.join(ROOT, 'scripts', 'verify-reapply-patches.cjs');
|
||||
const REAPPLY_WORKFLOW = path.join(ROOT, 'get-shit-done', 'workflows', 'reapply-patches.md');
|
||||
const REAPPLY_WORKFLOW = path.join(ROOT, 'gsd-core', 'workflows', 'reapply-patches.md');
|
||||
|
||||
describe('Bug #2994: verify-reapply-patches.cjs lives at the runtime-installed path', () => {
|
||||
test('the script exists under get-shit-done/bin/ (installed by copyWithPathReplacement)', () => {
|
||||
test('the script exists under gsd-core/bin/ (installed by copyWithPathReplacement)', () => {
|
||||
assert.equal(fs.existsSync(RUNTIME_SCRIPT_PATH), true,
|
||||
`Expected verifier script at ${RUNTIME_SCRIPT_PATH} -- installer copies get-shit-done/ recursively`);
|
||||
`Expected verifier script at ${RUNTIME_SCRIPT_PATH} -- installer copies gsd-core/ recursively`);
|
||||
});
|
||||
|
||||
test('the script does NOT live at the legacy scripts/ path (not installed)', () => {
|
||||
assert.equal(fs.existsSync(STALE_SCRIPT_PATH), false,
|
||||
`scripts/ is not copied by installer; verifier must be under get-shit-done/bin/ instead`);
|
||||
`scripts/ is not copied by installer; verifier must be under gsd-core/bin/ instead`);
|
||||
});
|
||||
|
||||
test('the script is requireable (loads without throwing)', () => {
|
||||
@@ -64,17 +64,17 @@ describe('Bug #2994: reapply-patches workflow references the runtime-installed p
|
||||
const invocations = extractScriptInvocations(md);
|
||||
assert.ok(invocations.length > 0, 'sanity: expected at least one node ${GSD_HOME}/... invocation in reapply-patches.md');
|
||||
|
||||
const violations = invocations.filter(inv => !inv.relPath.startsWith('get-shit-done/'));
|
||||
const violations = invocations.filter(inv => !inv.relPath.startsWith('gsd-core/'));
|
||||
assert.deepEqual(violations, [], `invocations under non-installed paths: ${JSON.stringify(violations)}`);
|
||||
});
|
||||
|
||||
test('reapply-patches.md references the verifier at get-shit-done/bin/verify-reapply-patches.cjs', () => {
|
||||
test('reapply-patches.md references the verifier at gsd-core/bin/verify-reapply-patches.cjs', () => {
|
||||
const md = fs.readFileSync(REAPPLY_WORKFLOW, 'utf-8');
|
||||
const invocations = extractScriptInvocations(md);
|
||||
const verifierInvocations = invocations.filter(inv => inv.relPath.endsWith('verify-reapply-patches.cjs'));
|
||||
assert.deepEqual(
|
||||
verifierInvocations.map(i => i.relPath),
|
||||
['get-shit-done/bin/verify-reapply-patches.cjs'],
|
||||
['gsd-core/bin/verify-reapply-patches.cjs'],
|
||||
'workflow must call the runtime-installed verifier path exactly once',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -24,9 +24,9 @@ const { cleanup } = require('./helpers.cjs');
|
||||
let tmpRoot;
|
||||
function fixtureRepo({ workflows, files }) {
|
||||
// workflows: { 'foo.md': '...content with ${GSD_HOME}/...' }
|
||||
// files: [ 'get-shit-done/bin/x.cjs', ... ] — files to create in repo
|
||||
// files: [ 'gsd-core/bin/x.cjs', ... ] — files to create in repo
|
||||
const repoRoot = fs.mkdtempSync(path.join(tmpRoot, 'repo-'));
|
||||
const workflowsDir = path.join(repoRoot, 'get-shit-done', 'workflows');
|
||||
const workflowsDir = path.join(repoRoot, 'gsd-core', 'workflows');
|
||||
fs.mkdirSync(workflowsDir, { recursive: true });
|
||||
for (const [name, body] of Object.entries(workflows || {})) {
|
||||
fs.writeFileSync(path.join(workflowsDir, name), body);
|
||||
@@ -53,14 +53,14 @@ describe('Bug #2995: post-install script-paths audit (#2995)', () => {
|
||||
test('returns { ok: true, findings: [] } when workflow refs an existing, installed-path script', () => {
|
||||
const { repoRoot, workflowsDir } = fixtureRepo({
|
||||
workflows: {
|
||||
'good.md': 'node "${GSD_HOME}/get-shit-done/bin/foo.cjs" --json\n',
|
||||
'good.md': 'node "${GSD_HOME}/gsd-core/bin/foo.cjs" --json\n',
|
||||
},
|
||||
files: ['get-shit-done/bin/foo.cjs'],
|
||||
files: ['gsd-core/bin/foo.cjs'],
|
||||
});
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done', 'commands', 'agents', 'hooks'],
|
||||
installedPrefixes: ['gsd-core', 'commands', 'agents', 'hooks'],
|
||||
});
|
||||
assert.deepEqual(r, { ok: true, findings: [] });
|
||||
});
|
||||
@@ -72,20 +72,20 @@ describe('Bug #2995: detection paths', () => {
|
||||
test('reports MISSING_FROM_REPO when the referenced file does not exist in the repo', () => {
|
||||
const { repoRoot, workflowsDir } = fixtureRepo({
|
||||
workflows: {
|
||||
'foo.md': 'node "${GSD_HOME}/get-shit-done/bin/typo.cjs" --json\n',
|
||||
'foo.md': 'node "${GSD_HOME}/gsd-core/bin/typo.cjs" --json\n',
|
||||
},
|
||||
files: [],
|
||||
});
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done'],
|
||||
installedPrefixes: ['gsd-core'],
|
||||
});
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings.length, 1);
|
||||
assert.deepEqual(r.findings[0], {
|
||||
workflow: 'foo.md',
|
||||
path: 'get-shit-done/bin/typo.cjs',
|
||||
path: 'gsd-core/bin/typo.cjs',
|
||||
kind: AUDIT_FINDING.MISSING_FROM_REPO,
|
||||
});
|
||||
});
|
||||
@@ -100,7 +100,7 @@ describe('Bug #2995: detection paths', () => {
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done', 'commands', 'agents', 'hooks'],
|
||||
installedPrefixes: ['gsd-core', 'commands', 'agents', 'hooks'],
|
||||
});
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings.length, 1);
|
||||
@@ -114,14 +114,14 @@ describe('Bug #2995: detection paths', () => {
|
||||
test('handles ${GSD_HOME:-$HOME/.claude}/... default-fallback syntax', () => {
|
||||
const { repoRoot, workflowsDir } = fixtureRepo({
|
||||
workflows: {
|
||||
'a.md': 'node "${GSD_HOME:-$HOME/.claude}/get-shit-done/bin/x.cjs"\n',
|
||||
'a.md': 'node "${GSD_HOME:-$HOME/.claude}/gsd-core/bin/x.cjs"\n',
|
||||
},
|
||||
files: ['get-shit-done/bin/x.cjs'],
|
||||
files: ['gsd-core/bin/x.cjs'],
|
||||
});
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done'],
|
||||
installedPrefixes: ['gsd-core'],
|
||||
});
|
||||
assert.deepEqual(r, { ok: true, findings: [] });
|
||||
});
|
||||
@@ -131,16 +131,16 @@ describe('Bug #2995: detection paths', () => {
|
||||
workflows: {
|
||||
'multi.md': [
|
||||
'node "${GSD_HOME}/scripts/a.cjs"',
|
||||
'node "${GSD_HOME}/get-shit-done/bin/b.cjs"',
|
||||
'node "${GSD_HOME}/get-shit-done/bin/missing.cjs"',
|
||||
'node "${GSD_HOME}/gsd-core/bin/b.cjs"',
|
||||
'node "${GSD_HOME}/gsd-core/bin/missing.cjs"',
|
||||
].join('\n') + '\n',
|
||||
},
|
||||
files: ['scripts/a.cjs', 'get-shit-done/bin/b.cjs'],
|
||||
files: ['scripts/a.cjs', 'gsd-core/bin/b.cjs'],
|
||||
});
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done'],
|
||||
installedPrefixes: ['gsd-core'],
|
||||
});
|
||||
assert.equal(r.ok, false);
|
||||
assert.equal(r.findings.length, 2);
|
||||
@@ -157,7 +157,7 @@ describe('Bug #2995: detection paths', () => {
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done'],
|
||||
installedPrefixes: ['gsd-core'],
|
||||
});
|
||||
assert.deepEqual(r, { ok: true, findings: [] });
|
||||
});
|
||||
@@ -170,7 +170,7 @@ describe('Bug #2995: real workflow audit', () => {
|
||||
// copies into ${configDir}/. Touching this set requires updating both
|
||||
// bin/install.js AND this constant — the parity is intentional.
|
||||
const INSTALLED_PREFIXES = [
|
||||
'get-shit-done', // workflows, references, bin/lib, templates
|
||||
'gsd-core', // workflows, references, bin/lib, templates
|
||||
'commands', // commands/gsd/*.md (Claude Code local + Gemini global)
|
||||
'skills', // skills/gsd-*/SKILL.md (Claude Code 2.1.88+ global, Codex, etc.)
|
||||
'agents', // agents/gsd-*.md
|
||||
@@ -181,12 +181,12 @@ describe('Bug #2995: real workflow audit', () => {
|
||||
// land in the same PR that fixes the underlying issue; CI surfaces any NEW
|
||||
// gap as a hard failure.
|
||||
// (#2994 entry removed: this PR moves verify-reapply-patches.cjs to
|
||||
// get-shit-done/bin/ which IS an installed prefix, closing the gap.)
|
||||
// gsd-core/bin/ which IS an installed prefix, closing the gap.)
|
||||
const KNOWN_GAPS = new Set();
|
||||
|
||||
test('no NEW workflow refs fail to resolve at the deployed path (KNOWN_GAPS allow-listed)', () => {
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir: require('node:path').join(ROOT, 'get-shit-done', 'workflows'),
|
||||
workflowsDir: require('node:path').join(ROOT, 'gsd-core', 'workflows'),
|
||||
repoRoot: ROOT,
|
||||
installedPrefixes: INSTALLED_PREFIXES,
|
||||
});
|
||||
@@ -220,7 +220,7 @@ describe('Bug #2995: real workflow audit', () => {
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir,
|
||||
repoRoot,
|
||||
installedPrefixes: ['get-shit-done', 'agents', 'hooks', 'commands'],
|
||||
installedPrefixes: ['gsd-core', 'agents', 'hooks', 'commands'],
|
||||
});
|
||||
assert.equal(r.ok, false);
|
||||
const kinds = r.findings.filter((f) => f.path === 'scripts/missing.cjs').map((f) => f.kind).sort();
|
||||
@@ -233,7 +233,7 @@ describe('Bug #2995: real workflow audit', () => {
|
||||
|
||||
test('KNOWN_GAPS entries still match real findings — fixed gaps must be removed from the allow-list', () => {
|
||||
const r = auditWorkflowScriptPaths({
|
||||
workflowsDir: require('node:path').join(ROOT, 'get-shit-done', 'workflows'),
|
||||
workflowsDir: require('node:path').join(ROOT, 'gsd-core', 'workflows'),
|
||||
repoRoot: ROOT,
|
||||
installedPrefixes: INSTALLED_PREFIXES,
|
||||
});
|
||||
|
||||
@@ -62,10 +62,10 @@ describe('Bug #2998: populatePristineDir is exported and writes pristine for mod
|
||||
const pristineDir = path.join(tmp, 'gsd-pristine');
|
||||
try {
|
||||
// Pick a real installed-side relPath from the package source. The
|
||||
// install transforms map source `get-shit-done/<rel>` to installed
|
||||
// `get-shit-done/<rel>` for skills-aware runtimes (like claude),
|
||||
// install transforms map source `gsd-core/<rel>` to installed
|
||||
// `gsd-core/<rel>` for skills-aware runtimes (like claude),
|
||||
// so the relPath is the same on both sides.
|
||||
const candidate = path.join('get-shit-done', 'workflows', 'reapply-patches.md');
|
||||
const candidate = path.join('gsd-core', 'workflows', 'reapply-patches.md');
|
||||
const sourcePath = path.join(ROOT, candidate);
|
||||
assert.equal(fs.existsSync(sourcePath), true,
|
||||
`precondition: source file exists at ${candidate}`);
|
||||
@@ -99,13 +99,13 @@ describe('Bug #2998: populatePristineDir is exported and writes pristine for mod
|
||||
const written = INSTALL.populatePristineDir({
|
||||
packageSrc: ROOT,
|
||||
pristineDir,
|
||||
modified: ['get-shit-done/this-path-does-not-exist.md'],
|
||||
modified: ['gsd-core/this-path-does-not-exist.md'],
|
||||
runtime: 'claude',
|
||||
pathPrefix: '$HOME/.claude/',
|
||||
isGlobal: true,
|
||||
});
|
||||
assert.equal(written, 0, 'expected zero pristine files for non-existent source paths');
|
||||
const out = path.join(pristineDir, 'get-shit-done/this-path-does-not-exist.md');
|
||||
const out = path.join(pristineDir, 'gsd-core/this-path-does-not-exist.md');
|
||||
assert.equal(fs.existsSync(out), false, 'pristine should not contain ghost paths');
|
||||
} finally {
|
||||
cleanup(tmp);
|
||||
@@ -119,7 +119,7 @@ describe('Bug #2998: populatePristineDir is exported and writes pristine for mod
|
||||
const tmp1 = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2998-d1-'));
|
||||
const tmp2 = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2998-d2-'));
|
||||
try {
|
||||
const candidate = path.join('get-shit-done', 'workflows', 'reapply-patches.md');
|
||||
const candidate = path.join('gsd-core', 'workflows', 'reapply-patches.md');
|
||||
const ctx = {
|
||||
packageSrc: ROOT,
|
||||
modified: [candidate],
|
||||
@@ -165,11 +165,11 @@ describe('Bug #2998 (#3004 CR): pristine expansion covers every manifest install
|
||||
}
|
||||
});
|
||||
|
||||
test('a mix of get-shit-done/ and agents/ paths in modified list are all staged', () => {
|
||||
test('a mix of gsd-core/ and agents/ paths in modified list are all staged', () => {
|
||||
const tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'gsd-2998-mix-'));
|
||||
const pristineDir = path.join(tmp, 'gsd-pristine');
|
||||
try {
|
||||
const a = path.join('get-shit-done', 'workflows', 'reapply-patches.md');
|
||||
const a = path.join('gsd-core', 'workflows', 'reapply-patches.md');
|
||||
const b = path.join('agents', 'gsd-planner.md');
|
||||
assert.equal(fs.existsSync(path.join(ROOT, a)), true);
|
||||
assert.equal(fs.existsSync(path.join(ROOT, b)), true);
|
||||
|
||||
@@ -37,7 +37,7 @@ const assert = require('node:assert/strict');
|
||||
const path = require('node:path');
|
||||
|
||||
const INSTALL = require(path.join(__dirname, '..', 'bin', 'install.js'));
|
||||
const projection = require(path.join(__dirname, '..', 'get-shit-done', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const projection = require(path.join(__dirname, '..', 'gsd-core', 'bin', 'lib', 'shell-command-projection.cjs'));
|
||||
const { buildCodexHookBlock, rewriteLegacyCodexHookBlock, resolveNodeRunner } = INSTALL;
|
||||
const { projectCodexHookTomlCommand } = projection;
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
* 1. sdk/src/cli.ts — leave --help in queryArgv so it travels to the
|
||||
* handler/fallback. Only honor the global help flag when there is
|
||||
* no subcommand to dispatch to.
|
||||
* 2. get-shit-done/bin/gsd-tools.cjs — render the top-level usage on
|
||||
* 2. gsd-core/bin/gsd-tools.cjs — render the top-level usage on
|
||||
* --help instead of erroring. Anti-hallucination invariant from
|
||||
* #1818 is preserved (the destructive command never executes).
|
||||
*
|
||||
|
||||
@@ -8,7 +8,7 @@ const path = require('node:path');
|
||||
describe('bug #3050: update backup skips unreadable files non-fatally', () => {
|
||||
test('update workflow backup loop wraps copyFileSync in try/catch and logs non-fatal skip', () => {
|
||||
const content = fs.readFileSync(
|
||||
path.join(__dirname, '..', 'get-shit-done', 'workflows', 'update.md'),
|
||||
path.join(__dirname, '..', 'gsd-core', 'workflows', 'update.md'),
|
||||
'utf8',
|
||||
);
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ describe('bug #3054: user-facing docs should not reference removed /gsd-next com
|
||||
const root = path.join(__dirname, '..');
|
||||
const files = [
|
||||
...walkMd(path.join(root, 'docs')),
|
||||
...walkMd(path.join(root, 'get-shit-done', 'workflows')),
|
||||
...walkMd(path.join(root, 'gsd-core', 'workflows')),
|
||||
...fs.readdirSync(root).filter((f) => /^README.*\.md$/.test(f)).map((f) => path.join(root, f)),
|
||||
];
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ const assert = require('node:assert/strict');
|
||||
const fs = require('node:fs');
|
||||
const path = require('node:path');
|
||||
|
||||
const ROOT = path.join(__dirname, '..', 'get-shit-done', 'workflows');
|
||||
const ROOT = path.join(__dirname, '..', 'gsd-core', 'workflows');
|
||||
|
||||
function read(rel) {
|
||||
return fs.readFileSync(path.join(ROOT, rel), 'utf8');
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user