chore(#604): rename get-shit-done/ runtime directory to gsd-core/ (#615)

* 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:
Tom Boucher
2026-06-02 18:35:29 -04:00
committed by GitHub
parent b177c1704f
commit 463cffd894
845 changed files with 3316 additions and 2373 deletions

View File

@@ -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');

View File

@@ -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.

View File

@@ -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');
/**

View File

@@ -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', () => {

View File

@@ -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 ───────────────────────────────────────────────────────────────────

View File

@@ -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 = [

View File

@@ -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', () => {

View File

@@ -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

View File

@@ -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])}`,

View File

@@ -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'

View File

@@ -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}`);

View File

@@ -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.`
);
});

View File

@@ -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 ─────────────────────────────────────────────────────────────────

View File

@@ -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')),

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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')
);
});

View File

@@ -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.

View 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/'
);
});

View File

@@ -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 ──────────────────────────────────────────────────────

View File

@@ -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', () => {

View File

@@ -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 ---

View File

@@ -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;

View File

@@ -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 ─────────────────────────────────────────────────────────────────

View File

@@ -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', () => {

View File

@@ -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'
);

View File

@@ -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 = []) {

View File

@@ -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';

View File

@@ -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 ──────────────────────────────────────────────────────────────────

View File

@@ -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;

View File

@@ -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) {

View File

@@ -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', () => {

View File

@@ -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));

View File

@@ -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)'
);
});
});

View File

@@ -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)', () => {

View File

@@ -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

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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'),
];
/**

View File

@@ -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 =

View File

@@ -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;

View File

@@ -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'
);
/**

View File

@@ -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', () => {

View File

@@ -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');

View File

@@ -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', () => {

View File

@@ -21,7 +21,7 @@ const path = require('path');
const WORKFLOW_PATH = path.join(
__dirname,
'..',
'get-shit-done',
'gsd-core',
'workflows',
'execute-phase.md'
);

View File

@@ -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);

View File

@@ -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)', () => {

View File

@@ -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)'
);
});

View File

@@ -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');
});

View File

@@ -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');

View File

@@ -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');

View File

@@ -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)', () => {

View File

@@ -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', () => {

View File

@@ -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'
);
});

View File

@@ -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;

View File

@@ -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') &&

View File

@@ -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', () => {

View File

@@ -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)', () => {

View File

@@ -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"

View File

@@ -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));
});
});

View File

@@ -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() {

View File

@@ -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;

View File

@@ -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)', () => {

View File

@@ -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);

View File

@@ -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

View File

@@ -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', () => {

View File

@@ -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);

View File

@@ -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();

View File

@@ -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'

View File

@@ -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');

View File

@@ -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'
);

View File

@@ -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(

View File

@@ -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');
});
});

View File

@@ -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.

View File

@@ -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, '/');

View File

@@ -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');

View File

@@ -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.

View File

@@ -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()}`,
);
}

View File

@@ -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');

View File

@@ -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'
);

View File

@@ -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'
);

View File

@@ -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)}`
);
});

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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', () => {

View File

@@ -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');

View File

@@ -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]);

View File

@@ -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;

View File

@@ -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');

View File

@@ -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

View File

@@ -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({

View File

@@ -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',
);
});

View File

@@ -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,
});

View File

@@ -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);

View File

@@ -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;

View File

@@ -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).
*

View File

@@ -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',
);

View File

@@ -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)),
];

View File

@@ -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