Self-run adversarial pass on PR #3046 before next reviewer round-trip. Three lockdown tests added — none uncovered new bugs, all lock current behavior so a future change doesn't silently flip a convention. 1. Single-quote YAML version (`milestone: 'v0.9'`) Parity with the existing double-quote test. The strip pattern `/^["']|["']$/g` handles both — locked here so a future change to either character class doesn't silently regress one form. 2. Heading-anchor wins over <details> fallback (precedence lock) When a ROADMAP has BOTH `### v0.9` heading AND `<details><summary>v0.9</summary>` block, the heading-level lookup matches first and the fallback never fires. Test asserts the heading slice is returned starting at offset 0 AND the synthesized `## v0.9 ... details-anchored` heading is NOT prepended (proves fallback didn't run). Also documented in-test that the heading-anchor slice naturally includes downstream <details> blocks verbatim — a property of the heading path, not of this PR's fallback. 3. Multiple <details> blocks for same version → first match wins `content.match(detailsPattern)` (non-`g`) returns first match in document order. Locked so a future change to the matcher (e.g. switching to `matchAll` and picking last) doesn't silently change which block is treated as active. Adversarial-checklist coverage on commit 781cc6f8: - Boundary cases: empty / whitespace / single-char / single-quote / double-quote / digit-suffix (`v0.91`) / dot-suffix (`v0.9.0`) / hyphen-suffix (`v0.9-rc.1`, intentional same-milestone match per existing currentVersionStr convention) — all covered. - Sibling consistency: parseMilestoneFromState, getMilestoneInfo, extractCurrentMilestone all strip quotes identically. - Comment-vs-behavior: walked nested-guard, empty-guard, lookahead, tag-strip by hand against the regex; all comments accurate. - Downstream consumers: roadmapAnalyze + roadmapGetPhase both verified end-to-end via tests + FAMP smoke. - Failure-mode locality: all fall-through paths produce loud failures (empty arrays, `{found:false}`); no silent confident-wrong outputs. 48/48 roadmap.test.ts tests pass.
@gsd-build/sdk
TypeScript SDK for Get Shit Done: deterministic query/mutation handlers, plan execution, and event-stream telemetry so agents focus on judgment, not shell plumbing.
Install
npm install @gsd-build/sdk
Quickstart — programmatic
import { GSD, createRegistry } from '@gsd-build/sdk';
const gsd = new GSD({ projectDir: process.cwd(), sessionId: 'my-run' });
const tools = gsd.createTools();
const registry = createRegistry(gsd.eventStream, 'my-run');
const { data } = await registry.dispatch('state.json', [], process.cwd());
Quickstart — CLI
From a project that depends on this package, invoke the CLI with Node (recommended in CI and local dev):
node ./node_modules/@gsd-build/sdk/dist/cli.js query state.json
node ./node_modules/@gsd-build/sdk/dist/cli.js query roadmap.analyze
If no native handler is registered for a command, the CLI can transparently shell out to get-shit-done/bin/gsd-tools.cjs (see stderr warning), unless GSD_QUERY_FALLBACK=off.
What ships
| Area | Entry |
|---|---|
| Query registry | createRegistry() in src/query/index.ts — same handlers as gsd-sdk query |
| Tools bridge | GSDTools — native dispatch with optional CJS subprocess fallback |
| Orchestrators | PhaseRunner, InitRunner, GSD |
| CLI | gsd-sdk — query, run, init, auto |
Guides
- Handler registry & contracts:
src/query/QUERY-HANDLERS.md - Repository docs (when present):
docs/ARCHITECTURE.md,docs/CLI-TOOLS.mdat repo root
Environment
| Variable | Purpose |
|---|---|
GSD_QUERY_FALLBACK |
off / never disables CLI fallback to gsd-tools.cjs for unknown commands |
GSD_AGENTS_DIR |
Override directory scanned for installed GSD agents (~/.claude/agents by default) |