* fix(sdk): forward --ws workstream flag through query dispatch (closes #2524) - cli.ts: pass args.ws as workstream to registry.dispatch() - registry.ts: add workstream? param to dispatch(), thread to handler - utils.ts: add optional workstream? to QueryHandler type signature - helpers.ts: planningPaths() accepts workstream? and uses relPlanningPath() - All ~26 query handlers updated to receive and pass workstream to planningPaths() - Config/commit/intel handlers use _workstream (project-global, not scoped) - Add failing-then-passing test: tests/bug-2524-sdk-query-ws-flag.test.cjs Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(sdk): forward workstream to all downstream query helpers Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(test): rewrite #2524 test as static source assertions — no sdk/dist build in CI Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
110 lines
3.8 KiB
TypeScript
110 lines
3.8 KiB
TypeScript
/**
|
||
* `state load` — full project config + STATE.md raw text (CJS `cmdStateLoad`).
|
||
*
|
||
* Uses the same `loadConfig(cwd)` as `get-shit-done/bin/lib/state.cjs` by resolving
|
||
* `core.cjs` next to a shipped/bundled/user `get-shit-done` install (same probe order
|
||
* as `resolveGsdToolsPath`). This keeps JSON output **byte-compatible** with
|
||
* `node gsd-tools.cjs state load` for monorepo and standard installs.
|
||
*
|
||
* Distinct from {@link stateJson} (`state json` / `state.json`) which mirrors
|
||
* `cmdStateJson` (rebuilt frontmatter only).
|
||
*/
|
||
|
||
import { readFile } from 'node:fs/promises';
|
||
import { existsSync } from 'node:fs';
|
||
import { join } from 'node:path';
|
||
import { homedir } from 'node:os';
|
||
import { createRequire } from 'node:module';
|
||
import { fileURLToPath } from 'node:url';
|
||
import { planningPaths } from './helpers.js';
|
||
import type { QueryHandler } from './utils.js';
|
||
import { GSDError, ErrorClassification } from '../errors.js';
|
||
|
||
const BUNDLED_CORE_CJS = fileURLToPath(
|
||
new URL('../../../get-shit-done/bin/lib/core.cjs', import.meta.url),
|
||
);
|
||
|
||
function resolveCoreCjsPath(projectDir: string): string | null {
|
||
const candidates = [
|
||
BUNDLED_CORE_CJS,
|
||
join(projectDir, '.claude', 'get-shit-done', 'bin', 'lib', 'core.cjs'),
|
||
join(homedir(), '.claude', 'get-shit-done', 'bin', 'lib', 'core.cjs'),
|
||
];
|
||
return candidates.find(p => existsSync(p)) ?? null;
|
||
}
|
||
|
||
function loadConfigCjs(projectDir: string): Record<string, unknown> {
|
||
const corePath = resolveCoreCjsPath(projectDir);
|
||
if (!corePath) {
|
||
throw new GSDError(
|
||
'state load: get-shit-done/bin/lib/core.cjs not found. Install GSD (e.g. npm i -g get-shit-done-cc) or clone with get-shit-done next to the SDK.',
|
||
ErrorClassification.Blocked,
|
||
);
|
||
}
|
||
const req = createRequire(import.meta.url);
|
||
const { loadConfig } = req(corePath) as { loadConfig: (cwd: string) => Record<string, unknown> };
|
||
return loadConfig(projectDir);
|
||
}
|
||
|
||
/**
|
||
* Query handler for `state load` / bare `state` (normalize → `state.load`).
|
||
*
|
||
* Port of `cmdStateLoad` from `get-shit-done/bin/lib/state.cjs` lines 44–86.
|
||
*/
|
||
export const stateProjectLoad: QueryHandler = async (_args, projectDir, workstream) => {
|
||
const config = loadConfigCjs(projectDir);
|
||
const planDir = planningPaths(projectDir, workstream).planning;
|
||
|
||
let stateRaw = '';
|
||
try {
|
||
stateRaw = await readFile(join(planDir, 'STATE.md'), 'utf-8');
|
||
} catch (err) {
|
||
if ((err as NodeJS.ErrnoException).code !== 'ENOENT') {
|
||
throw err;
|
||
}
|
||
}
|
||
|
||
const configExists = existsSync(join(planDir, 'config.json'));
|
||
const roadmapExists = existsSync(join(planDir, 'ROADMAP.md'));
|
||
const stateExists = stateRaw.length > 0;
|
||
|
||
return {
|
||
data: {
|
||
config,
|
||
state_raw: stateRaw,
|
||
state_exists: stateExists,
|
||
roadmap_exists: roadmapExists,
|
||
config_exists: configExists,
|
||
},
|
||
};
|
||
};
|
||
|
||
/**
|
||
* `--raw` stdout for `state load` (matches CJS `cmdStateLoad` lines 65–83).
|
||
*/
|
||
export function formatStateLoadRawStdout(data: unknown): string {
|
||
const d = data as Record<string, unknown>;
|
||
const c = d.config as Record<string, unknown> | undefined;
|
||
if (!c) {
|
||
return typeof data === 'string' ? data : JSON.stringify(data, null, 2);
|
||
}
|
||
const configExists = d.config_exists;
|
||
const roadmapExists = d.roadmap_exists;
|
||
const stateExists = d.state_exists;
|
||
const lines = [
|
||
`model_profile=${c.model_profile}`,
|
||
`commit_docs=${c.commit_docs}`,
|
||
`branching_strategy=${c.branching_strategy}`,
|
||
`phase_branch_template=${c.phase_branch_template}`,
|
||
`milestone_branch_template=${c.milestone_branch_template}`,
|
||
`parallelization=${c.parallelization}`,
|
||
`research=${c.research}`,
|
||
`plan_checker=${c.plan_checker}`,
|
||
`verifier=${c.verifier}`,
|
||
`config_exists=${configExists}`,
|
||
`roadmap_exists=${roadmapExists}`,
|
||
`state_exists=${stateExists}`,
|
||
];
|
||
return lines.join('\n');
|
||
}
|