The 5 no-control-regex warnings are all the same intentional ANSI-color-strip
pattern /\x1b\[[0-9;]*m/g across 5 test files. The \x1b (ESC) control char is
the required leading byte of an ANSI SGR sequence, so matching it is the whole
point of stripping color codes from captured CLI/console output. Add an inline
eslint-disable-next-line with justification at each site (not a refactor — the
control char is essential, not accidental), then flip no-control-regex from
warn to error so the debt can't regrow.
Refs #736
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Six independent windows-only failure clusters identified from the
windows-22 CI log on 1be0e4e2:
- scripts/command-contract-helpers.cjs: parseFrontmatter split on `\n`
→ on Windows checkout (autocrlf=true) every line carries trailing \r,
lines.indexOf('---', 1) returns -1, all fields read as missing. Drove
the bulk of three "67 subtests failed" suite-level errors covering
workstreams.md, workspace.md, verify-work.md, add-tests.md, etc.
Fix: split(/\r?\n/).
- tests/enh-3271-sdk-adr-structure.test.cjs: same CRLF pattern — H2
captures pulled '\r' into headings like "decision\r", breaking
equality checks on "## Decision" / "## Consequences". Fix: same.
- tests/runtime-bridge-sync-smoke.test.cjs: await import(BRIDGE_PATH)
passed a Windows absolute path to Node's ESM loader, which rejects
with "Only URLs with a scheme in: file, data, and node are
supported." Fix: wrap once at module scope with pathToFileURL.
pathToFileURL is a no-op for POSIX absolute paths.
- tests/bug-2957-claude-global-postinstall-message.test.cjs: hardcoded
'/tmp/gsd-test-settings.json' resolved to D:\tmp\... on Windows
where the parent dir doesn't exist → ENOENT on fs.writeFileSync
inside finishInstall. Fix: os.tmpdir() + pid suffix.
- tests/bug-2774-worktree-cleanup-workspace-safety.test.cjs: the
"while/read loop" subtest and the "end-to-end against real git
worktrees" describe both assert POSIX shell behavior (process
substitution `< <(...)`, RUNNER~1 8.3-shortname mismatch). Skip on
win32 with explicit reasons (satisfies the
no-unconditional-win32-skip guard).
- tests/bug-2838-summary-rescue-gitignored-planning.test.cjs: entire
describe extracts bash rescue blocks from workflow .md files and
runs them; the shell contract itself is the test's point. Skip on
win32 with reason.
- tests/helpers.cjs cleanup(): bumped rmSync retry budget from
10×100ms to 20×250ms — 1s wasn't enough for Windows Defender's
deferred handle release; bumping to 5s should absorb the residual
EBUSY failures observed in bug-1736 / bug-2248 / bug-2698 after the
first retry bump landed in 1be0e4e2.
Validated: holodeck (ubuntu docker) 11224/0 pass.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(#2957): claude+global post-install instructs restart and skill fallback
`npx get-shit-done-cc --claude --global` writes skills to
`~/.claude/skills/gsd-*/SKILL.md` (CC 2.1.88+ format) and removes the
legacy `~/.claude/commands/gsd/`. The post-install message still told
users to type `/gsd-new-project` without mentioning the required Claude
Code restart or the skill-name fallback. On configurations where CC
does not auto-surface skills in the slash menu, users hit "no commands
appear" and assumed the install failed.
Split the post-install message: the existing single-line instruction
stays for every non-Claude runtime and for `--claude --local`. For
`--claude --global` it now reads:
Restart Claude Code, then in any directory either type
/gsd-new-project or ask Claude to run the gsd-new-project skill.
This covers both invocation paths and surfaces the restart requirement.
Add tests/bug-2957-claude-global-postinstall-message.test.cjs as a
regression guard: captures the printed message for claude+global,
claude+local, and opencode+global; asserts content for each. Verified
the test fails on main (pre-fix) and passes after the fix.
Closes#2957
* test(#2957): assert legacy generic instruction is replaced not extended
CodeRabbit flagged that the test would still pass if the new restart/
fallback copy were printed *alongside* the old 'open a blank directory'
instruction. Adding a doesNotMatch assertion proves the claude+global
branch replaces the legacy line rather than appending to it.