no-mistakes(review): fix onboard fast root anchoring
This commit is contained in:
committed by
Codesmith
parent
1797207280
commit
b3555b103d
@@ -23,25 +23,30 @@ Display banner:
|
||||
```
|
||||
|
||||
Parse `$ARGUMENTS`:
|
||||
- `--fast` sets `MAP_COMMAND` to `/gsd:map-codebase --fast`; otherwise `/gsd:map-codebase`.
|
||||
- `--fast` sets `MAP_COMMAND` to `/gsd:map-codebase --fast`, passes `--fast` to the init projection, and accepts the default fast map subset (STACK, INTEGRATIONS, ARCHITECTURE, STRUCTURE) as sufficient onboarding context; otherwise `MAP_COMMAND` is `/gsd:map-codebase`.
|
||||
- `--text` sets `TEXT_MODE=true`.
|
||||
|
||||
Run the init projection:
|
||||
|
||||
```bash
|
||||
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="$HOME/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
||||
INIT=$(gsd_run init onboard)
|
||||
INIT_ONBOARD_FLAGS=()
|
||||
if [[ " ${ARGUMENTS:-} " == *" --fast "* ]]; then INIT_ONBOARD_FLAGS=(--fast); fi
|
||||
INIT=$(gsd_run --cwd "$_GSD_RUNTIME_ROOT" init onboard "${INIT_ONBOARD_FLAGS[@]}")
|
||||
if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
|
||||
```
|
||||
|
||||
Parse JSON fields: `planning_exists`, `project_exists`, `requirements_exists`,
|
||||
`roadmap_exists`, `state_exists`, `has_existing_code`, `has_package_file`,
|
||||
`is_brownfield`, `has_codebase_map`,
|
||||
`codebase_map_files_present`, `missing_codebase_map_files`, `has_docs_candidates`,
|
||||
`is_brownfield`, `has_codebase_map`, `has_fast_codebase_map`,
|
||||
`codebase_map_files_present`, `missing_codebase_map_files`, `missing_fast_codebase_map_files`,
|
||||
`has_docs_candidates`,
|
||||
`doc_candidate_count`, `onboarding_summary_exists`, `text_mode`, `commit_docs`, `agents_installed`,
|
||||
`missing_agents`, `has_git`, `git_worktree_root`, `in_nested_subdir`.
|
||||
|
||||
Set `TEXT_MODE=true` if `--text` is present OR `text_mode` from INIT is true.
|
||||
Set `CODEBASE_MAP_READY=has_fast_codebase_map` when `--fast` is present; otherwise set
|
||||
`CODEBASE_MAP_READY=has_codebase_map`.
|
||||
|
||||
**Text mode (`workflow.text_mode: true` in config or `--text` flag):** When `TEXT_MODE` is active, replace every `AskUserQuestion` call in this workflow with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available and would otherwise render as an inert code block.
|
||||
|
||||
@@ -56,15 +61,15 @@ runs the lower-level refresh commands.
|
||||
|
||||
## 3. Codebase Mapping Gate
|
||||
|
||||
If `is_brownfield` is true and `has_codebase_map` is false:
|
||||
If `is_brownfield` is true and `CODEBASE_MAP_READY` is false:
|
||||
|
||||
- If `TEXT_MODE=true`, print:
|
||||
|
||||
```text
|
||||
Existing code was detected, but the complete .planning/codebase/ map is missing.
|
||||
Missing map files: {missing_codebase_map_files}
|
||||
Existing code was detected, but the required .planning/codebase/ map is missing.
|
||||
Missing map files: {--fast ? missing_fast_codebase_map_files : missing_codebase_map_files}
|
||||
|
||||
1. Map codebase first — run {MAP_COMMAND} to understand the repo before project setup (Recommended)
|
||||
1. Map codebase first — run {MAP_COMMAND} from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT} to understand the repo before project setup (Recommended)
|
||||
2. Skip mapping — continue with weaker onboarding context
|
||||
|
||||
Enter number:
|
||||
@@ -74,17 +79,19 @@ Stop and wait for the user's reply.
|
||||
|
||||
- Otherwise use AskUserQuestion:
|
||||
- header: "Codebase"
|
||||
- question: "Existing code was detected, but the complete .planning/codebase/ map is missing. Map it first?"
|
||||
- question: "Existing code was detected, but the required .planning/codebase/ map is missing. Map it first?"
|
||||
- options:
|
||||
- "Map codebase first" — Run `{MAP_COMMAND}` to understand the repo before project setup (Recommended)
|
||||
- "Map codebase first" — Run `{MAP_COMMAND}` from worktree root `{git_worktree_root || _GSD_RUNTIME_ROOT}` to understand the repo before project setup (Recommended)
|
||||
- "Skip mapping" — Continue with weaker onboarding context
|
||||
|
||||
If the user chooses mapping, do not nest the interactive map-codebase workflow. Print:
|
||||
|
||||
```text
|
||||
Run this top-level command first, then rerun /gsd:onboard:
|
||||
Run from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT}:
|
||||
|
||||
{MAP_COMMAND}
|
||||
|
||||
Then rerun /gsd:onboard from the same worktree root.
|
||||
```
|
||||
|
||||
Exit.
|
||||
@@ -96,6 +103,8 @@ If `is_brownfield` is false and `planning_exists` is false and `has_docs_candida
|
||||
```text
|
||||
No existing code was detected. For a greenfield project, run:
|
||||
|
||||
Run from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT}:
|
||||
|
||||
/gsd:new-project
|
||||
```
|
||||
|
||||
@@ -110,7 +119,7 @@ If `has_docs_candidates` is true and `project_exists` is false:
|
||||
```text
|
||||
Detected {doc_candidate_count} possible ADR/PRD/SPEC/RFC document(s).
|
||||
|
||||
1. Ingest docs first — run /gsd:ingest-docs to bootstrap planning from existing docs (Recommended)
|
||||
1. Ingest docs first — run /gsd:ingest-docs from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT} to bootstrap planning from existing docs (Recommended)
|
||||
2. Skip docs ingest — continue to /gsd:new-project
|
||||
|
||||
Enter number:
|
||||
@@ -122,15 +131,17 @@ Stop and wait for the user's reply.
|
||||
- header: "Docs"
|
||||
- question: "Detected {doc_candidate_count} possible ADR/PRD/SPEC/RFC document(s). Ingest them first?"
|
||||
- options:
|
||||
- "Ingest docs first" — Run `/gsd:ingest-docs` to bootstrap planning from existing docs (Recommended)
|
||||
- "Ingest docs first" — Run `/gsd:ingest-docs` from worktree root `{git_worktree_root || _GSD_RUNTIME_ROOT}` to bootstrap planning from existing docs (Recommended)
|
||||
- "Skip docs ingest" — Continue to `/gsd:new-project`
|
||||
|
||||
If the user chooses ingest, do not nest the interactive ingest-docs workflow. Print:
|
||||
|
||||
```text
|
||||
Run this top-level command first, then rerun /gsd:onboard:
|
||||
Run from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT}:
|
||||
|
||||
/gsd:ingest-docs
|
||||
|
||||
Then rerun /gsd:onboard from the same worktree root.
|
||||
```
|
||||
|
||||
Exit.
|
||||
@@ -144,9 +155,11 @@ Print:
|
||||
```text
|
||||
Codebase context is ready for project initialization.
|
||||
|
||||
Run this top-level command, then rerun /gsd:onboard:
|
||||
Run from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT}:
|
||||
|
||||
/gsd:new-project
|
||||
|
||||
Then rerun /gsd:onboard from the same worktree root.
|
||||
```
|
||||
|
||||
Exit.
|
||||
@@ -162,10 +175,12 @@ Planning file status:
|
||||
- ROADMAP.md: {roadmap_exists ? "present" : "missing"}
|
||||
- STATE.md: {state_exists ? "present" : "missing"}
|
||||
|
||||
Run one of these top-level commands, then rerun /gsd:onboard:
|
||||
Run one of these top-level commands from worktree root {git_worktree_root || _GSD_RUNTIME_ROOT}:
|
||||
|
||||
/gsd:ingest-docs --mode merge
|
||||
/gsd:new-milestone
|
||||
|
||||
Then rerun /gsd:onboard from the same worktree root.
|
||||
```
|
||||
|
||||
Exit. Do not write `.planning/onboarding/SUMMARY.md` and do not print the onboarding
|
||||
|
||||
@@ -28,7 +28,7 @@ interface InitModule {
|
||||
cmdInitPlanPhase(cwd: string, phase: string | undefined, raw: boolean, opts: Record<string, string | boolean | null>): void;
|
||||
cmdInitNewProject(cwd: string, raw: boolean): void;
|
||||
cmdInitNewMilestone(cwd: string, raw: boolean): void;
|
||||
cmdInitOnboard(cwd: string, raw: boolean): void;
|
||||
cmdInitOnboard(cwd: string, raw: boolean, opts?: Record<string, string | boolean | null>): void;
|
||||
cmdInitQuick(cwd: string, name: string, raw: boolean): void;
|
||||
cmdInitIngestDocs(cwd: string, raw: boolean): void;
|
||||
cmdInitResume(cwd: string, raw: boolean): void;
|
||||
@@ -72,7 +72,10 @@ function routeInitCommand({ init, args, cwd, raw, error }: RouteInitCommandOptio
|
||||
},
|
||||
'new-project': () => init.cmdInitNewProject(cwd, raw),
|
||||
'new-milestone': () => init.cmdInitNewMilestone(cwd, raw),
|
||||
onboard: () => init.cmdInitOnboard(cwd, raw),
|
||||
onboard: () => {
|
||||
const namedArgs = parseNamedArgs(args, [], ['fast']);
|
||||
init.cmdInitOnboard(cwd, raw, { fast: namedArgs['fast'] });
|
||||
},
|
||||
quick: () => init.cmdInitQuick(cwd, args.slice(2).join(' '), raw),
|
||||
'ingest-docs': () => init.cmdInitIngestDocs(cwd, raw),
|
||||
resume: () => init.cmdInitResume(cwd, raw),
|
||||
|
||||
24
src/init.cts
24
src/init.cts
@@ -109,6 +109,10 @@ const REQUIRED_CODEBASE_MAP_FILES = [
|
||||
'INTEGRATIONS.md', 'CONCERNS.md',
|
||||
];
|
||||
|
||||
const FAST_CODEBASE_MAP_FILES = [
|
||||
'STACK.md', 'INTEGRATIONS.md', 'ARCHITECTURE.md', 'STRUCTURE.md',
|
||||
];
|
||||
|
||||
const PLANNING_DOC_SEGMENTS = new Set([
|
||||
'adr', 'adrs', 'prd', 'prds', 'spec', 'specs', 'rfc', 'rfcs',
|
||||
]);
|
||||
@@ -902,17 +906,29 @@ function cmdInitIngestDocs(cwd: string, raw: boolean): void {
|
||||
output(withProjectRoot(cwd, result), raw);
|
||||
}
|
||||
|
||||
function cmdInitOnboard(cwd: string, raw: boolean): void {
|
||||
function cmdInitOnboard(
|
||||
cwd: string,
|
||||
raw: boolean,
|
||||
options: Record<string, unknown> = {},
|
||||
): void {
|
||||
const config = loadConfig(cwd);
|
||||
const codebaseMapFiles = listCodebaseMapFiles(cwd);
|
||||
const missingCodebaseMapFiles = REQUIRED_CODEBASE_MAP_FILES.filter(
|
||||
(file) => !codebaseMapFiles.includes(file),
|
||||
);
|
||||
const missingFastCodebaseMapFiles = FAST_CODEBASE_MAP_FILES.filter(
|
||||
(file) => !codebaseMapFiles.includes(file),
|
||||
);
|
||||
const docCandidates = listPlanningDocCandidates(cwd);
|
||||
const hasCode = hasCodeFilesInternal(cwd);
|
||||
const hasPackageFile = hasPackageFileInternal(cwd);
|
||||
const isBrownfield = hasCode || hasPackageFile;
|
||||
const hasCodebaseMap = codebaseMapFiles.length === REQUIRED_CODEBASE_MAP_FILES.length;
|
||||
const fastMode = options['fast'] === true;
|
||||
const hasFastCodebaseMap = missingFastCodebaseMapFiles.length === 0;
|
||||
const needsCodebaseMap = isBrownfield && (
|
||||
fastMode ? !hasFastCodebaseMap : !hasCodebaseMap
|
||||
);
|
||||
|
||||
const result: Record<string, unknown> = {
|
||||
commit_docs: config.commit_docs,
|
||||
@@ -929,11 +945,15 @@ function cmdInitOnboard(cwd: string, raw: boolean): void {
|
||||
has_existing_code: hasCode,
|
||||
has_package_file: hasPackageFile,
|
||||
is_brownfield: isBrownfield,
|
||||
needs_codebase_map: isBrownfield && !hasCodebaseMap,
|
||||
fast_mode: fastMode,
|
||||
needs_codebase_map: needsCodebaseMap,
|
||||
has_codebase_map: hasCodebaseMap,
|
||||
has_fast_codebase_map: hasFastCodebaseMap,
|
||||
codebase_dir_exists: fs.existsSync(path.join(planningRoot(cwd), 'codebase')),
|
||||
fast_codebase_map_files_required: FAST_CODEBASE_MAP_FILES,
|
||||
codebase_map_files_present: codebaseMapFiles,
|
||||
missing_codebase_map_files: missingCodebaseMapFiles,
|
||||
missing_fast_codebase_map_files: missingFastCodebaseMapFiles,
|
||||
|
||||
has_docs_candidates: docCandidates.length > 0,
|
||||
doc_candidate_count: docCandidates.length,
|
||||
|
||||
@@ -138,6 +138,29 @@ describe('init onboard public CLI projection', () => {
|
||||
assert.strictEqual(parsed.text_mode, true);
|
||||
});
|
||||
|
||||
test('reports fast codebase map readiness for the default fast subset', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning', 'codebase'), { recursive: true });
|
||||
for (const name of ['STACK', 'INTEGRATIONS', 'ARCHITECTURE', 'STRUCTURE']) {
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'codebase', `${name}.md`), `# ${name}\n`);
|
||||
}
|
||||
fs.writeFileSync(path.join(tmpDir, 'package.json'), '{"name":"fixture"}\n');
|
||||
|
||||
const result = runGsdTools(['init', 'onboard', '--fast', '--raw'], tmpDir, { HOME: tmpDir });
|
||||
assert.ok(result.success, `init onboard should succeed: ${result.error}`);
|
||||
|
||||
const parsed = JSON.parse(result.output);
|
||||
assert.strictEqual(parsed.has_codebase_map, false);
|
||||
assert.strictEqual(parsed.has_fast_codebase_map, true);
|
||||
assert.strictEqual(parsed.needs_codebase_map, false);
|
||||
assert.deepStrictEqual(parsed.fast_codebase_map_files_required, [
|
||||
'STACK.md',
|
||||
'INTEGRATIONS.md',
|
||||
'ARCHITECTURE.md',
|
||||
'STRUCTURE.md',
|
||||
]);
|
||||
assert.deepStrictEqual(parsed.missing_fast_codebase_map_files, []);
|
||||
});
|
||||
|
||||
test('reports missing requirements in otherwise existing planning', () => {
|
||||
fs.mkdirSync(path.join(tmpDir, '.planning'), { recursive: true });
|
||||
fs.writeFileSync(path.join(tmpDir, '.planning', 'PROJECT.md'), '# Project\n');
|
||||
@@ -211,6 +234,10 @@ describe('/gsd:onboard command contract', () => {
|
||||
const content = fs.readFileSync(WF_PATH, 'utf8');
|
||||
|
||||
assert.ok(content.includes('init onboard'), 'workflow must use init onboard projection');
|
||||
assert.match(content, /gsd_run --cwd "\$_GSD_RUNTIME_ROOT" init onboard/, 'workflow must anchor init onboard at runtime root');
|
||||
assert.ok(content.includes('has_fast_codebase_map'), 'workflow must parse fast map readiness');
|
||||
assert.match(content, /CODEBASE_MAP_READY=.*has_fast_codebase_map/s, 'workflow must treat the fast subset as acceptable in fast mode');
|
||||
assert.ok(content.includes('Run from worktree root'), 'workflow handoffs must be anchored at the worktree root');
|
||||
for (const runtimeHome of [
|
||||
'HERMES_HOME',
|
||||
'CURSOR_CONFIG_DIR',
|
||||
|
||||
Reference in New Issue
Block a user