feat: add --include flag to init commands to eliminate redundant file reads
Workflows were calling init commands to get parsed JSON metadata, then immediately reading the same files again with cat to pass raw content to agents. This wastes context tokens. Changes: - Add --include flag to init execute-phase, plan-phase, and progress - Support includes: state, config, roadmap, requirements, context, research, verification, uat, project - Update plan-phase.md to use --include (removes 6 cat calls) - Update execute-phase.md to use --include (removes 2 cat calls) - Update execute-plan.md to use --include (removes 2 cat calls) - Update progress.md to use --include (removes 4 cat calls) - Add 7 tests for --include functionality Token savings: ~5,000-10,000 tokens per plan-phase execution, ~1,500-3,000 per other workflow executions. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -53,6 +53,22 @@ const MODEL_PROFILES = {
|
|||||||
|
|
||||||
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
// ─── Helpers ──────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
function parseIncludeFlag(args) {
|
||||||
|
const includeIndex = args.indexOf('--include');
|
||||||
|
if (includeIndex === -1) return new Set();
|
||||||
|
const includeValue = args[includeIndex + 1];
|
||||||
|
if (!includeValue) return new Set();
|
||||||
|
return new Set(includeValue.split(',').map(s => s.trim()));
|
||||||
|
}
|
||||||
|
|
||||||
|
function safeReadFile(filePath) {
|
||||||
|
try {
|
||||||
|
return fs.readFileSync(filePath, 'utf-8');
|
||||||
|
} catch {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function loadConfig(cwd) {
|
function loadConfig(cwd) {
|
||||||
const configPath = path.join(cwd, '.planning', 'config.json');
|
const configPath = path.join(cwd, '.planning', 'config.json');
|
||||||
const defaults = {
|
const defaults = {
|
||||||
@@ -1392,7 +1408,7 @@ function getMilestoneInfo(cwd) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdInitExecutePhase(cwd, phase, raw) {
|
function cmdInitExecutePhase(cwd, phase, includes, raw) {
|
||||||
if (!phase) {
|
if (!phase) {
|
||||||
error('phase required for init execute-phase');
|
error('phase required for init execute-phase');
|
||||||
}
|
}
|
||||||
@@ -1450,10 +1466,21 @@ function cmdInitExecutePhase(cwd, phase, raw) {
|
|||||||
config_exists: pathExistsInternal(cwd, '.planning/config.json'),
|
config_exists: pathExistsInternal(cwd, '.planning/config.json'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Include file contents if requested via --include
|
||||||
|
if (includes.has('state')) {
|
||||||
|
result.state_content = safeReadFile(path.join(cwd, '.planning', 'STATE.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('config')) {
|
||||||
|
result.config_content = safeReadFile(path.join(cwd, '.planning', 'config.json'));
|
||||||
|
}
|
||||||
|
if (includes.has('roadmap')) {
|
||||||
|
result.roadmap_content = safeReadFile(path.join(cwd, '.planning', 'ROADMAP.md'));
|
||||||
|
}
|
||||||
|
|
||||||
output(result, raw);
|
output(result, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdInitPlanPhase(cwd, phase, raw) {
|
function cmdInitPlanPhase(cwd, phase, includes, raw) {
|
||||||
if (!phase) {
|
if (!phase) {
|
||||||
error('phase required for init plan-phase');
|
error('phase required for init plan-phase');
|
||||||
}
|
}
|
||||||
@@ -1491,6 +1518,61 @@ function cmdInitPlanPhase(cwd, phase, raw) {
|
|||||||
roadmap_exists: pathExistsInternal(cwd, '.planning/ROADMAP.md'),
|
roadmap_exists: pathExistsInternal(cwd, '.planning/ROADMAP.md'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Include file contents if requested via --include
|
||||||
|
if (includes.has('state')) {
|
||||||
|
result.state_content = safeReadFile(path.join(cwd, '.planning', 'STATE.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('roadmap')) {
|
||||||
|
result.roadmap_content = safeReadFile(path.join(cwd, '.planning', 'ROADMAP.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('requirements')) {
|
||||||
|
result.requirements_content = safeReadFile(path.join(cwd, '.planning', 'REQUIREMENTS.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('context') && phaseInfo?.directory) {
|
||||||
|
// Find *-CONTEXT.md in phase directory
|
||||||
|
const phaseDirFull = path.join(cwd, phaseInfo.directory);
|
||||||
|
try {
|
||||||
|
const files = fs.readdirSync(phaseDirFull);
|
||||||
|
const contextFile = files.find(f => f.endsWith('-CONTEXT.md') || f === 'CONTEXT.md');
|
||||||
|
if (contextFile) {
|
||||||
|
result.context_content = safeReadFile(path.join(phaseDirFull, contextFile));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (includes.has('research') && phaseInfo?.directory) {
|
||||||
|
// Find *-RESEARCH.md in phase directory
|
||||||
|
const phaseDirFull = path.join(cwd, phaseInfo.directory);
|
||||||
|
try {
|
||||||
|
const files = fs.readdirSync(phaseDirFull);
|
||||||
|
const researchFile = files.find(f => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
|
||||||
|
if (researchFile) {
|
||||||
|
result.research_content = safeReadFile(path.join(phaseDirFull, researchFile));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (includes.has('verification') && phaseInfo?.directory) {
|
||||||
|
// Find *-VERIFICATION.md in phase directory
|
||||||
|
const phaseDirFull = path.join(cwd, phaseInfo.directory);
|
||||||
|
try {
|
||||||
|
const files = fs.readdirSync(phaseDirFull);
|
||||||
|
const verificationFile = files.find(f => f.endsWith('-VERIFICATION.md') || f === 'VERIFICATION.md');
|
||||||
|
if (verificationFile) {
|
||||||
|
result.verification_content = safeReadFile(path.join(phaseDirFull, verificationFile));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
if (includes.has('uat') && phaseInfo?.directory) {
|
||||||
|
// Find *-UAT.md in phase directory
|
||||||
|
const phaseDirFull = path.join(cwd, phaseInfo.directory);
|
||||||
|
try {
|
||||||
|
const files = fs.readdirSync(phaseDirFull);
|
||||||
|
const uatFile = files.find(f => f.endsWith('-UAT.md') || f === 'UAT.md');
|
||||||
|
if (uatFile) {
|
||||||
|
result.uat_content = safeReadFile(path.join(phaseDirFull, uatFile));
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
output(result, raw);
|
output(result, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1857,7 +1939,7 @@ function cmdInitMapCodebase(cwd, raw) {
|
|||||||
output(result, raw);
|
output(result, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
function cmdInitProgress(cwd, raw) {
|
function cmdInitProgress(cwd, includes, raw) {
|
||||||
const config = loadConfig(cwd);
|
const config = loadConfig(cwd);
|
||||||
const milestone = getMilestoneInfo(cwd);
|
const milestone = getMilestoneInfo(cwd);
|
||||||
|
|
||||||
@@ -1947,6 +2029,20 @@ function cmdInitProgress(cwd, raw) {
|
|||||||
state_exists: pathExistsInternal(cwd, '.planning/STATE.md'),
|
state_exists: pathExistsInternal(cwd, '.planning/STATE.md'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Include file contents if requested via --include
|
||||||
|
if (includes.has('state')) {
|
||||||
|
result.state_content = safeReadFile(path.join(cwd, '.planning', 'STATE.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('roadmap')) {
|
||||||
|
result.roadmap_content = safeReadFile(path.join(cwd, '.planning', 'ROADMAP.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('project')) {
|
||||||
|
result.project_content = safeReadFile(path.join(cwd, '.planning', 'PROJECT.md'));
|
||||||
|
}
|
||||||
|
if (includes.has('config')) {
|
||||||
|
result.config_content = safeReadFile(path.join(cwd, '.planning', 'config.json'));
|
||||||
|
}
|
||||||
|
|
||||||
output(result, raw);
|
output(result, raw);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2091,12 +2187,13 @@ function main() {
|
|||||||
|
|
||||||
case 'init': {
|
case 'init': {
|
||||||
const workflow = args[1];
|
const workflow = args[1];
|
||||||
|
const includes = parseIncludeFlag(args);
|
||||||
switch (workflow) {
|
switch (workflow) {
|
||||||
case 'execute-phase':
|
case 'execute-phase':
|
||||||
cmdInitExecutePhase(cwd, args[2], raw);
|
cmdInitExecutePhase(cwd, args[2], includes, raw);
|
||||||
break;
|
break;
|
||||||
case 'plan-phase':
|
case 'plan-phase':
|
||||||
cmdInitPlanPhase(cwd, args[2], raw);
|
cmdInitPlanPhase(cwd, args[2], includes, raw);
|
||||||
break;
|
break;
|
||||||
case 'new-project':
|
case 'new-project':
|
||||||
cmdInitNewProject(cwd, raw);
|
cmdInitNewProject(cwd, raw);
|
||||||
@@ -2126,7 +2223,7 @@ function main() {
|
|||||||
cmdInitMapCodebase(cwd, raw);
|
cmdInitMapCodebase(cwd, raw);
|
||||||
break;
|
break;
|
||||||
case 'progress':
|
case 'progress':
|
||||||
cmdInitProgress(cwd, raw);
|
cmdInitProgress(cwd, includes, raw);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress`);
|
error(`Unknown init workflow: ${workflow}\nAvailable: execute-phase, plan-phase, new-project, new-milestone, quick, resume, verify-work, phase-op, todos, milestone-op, map-codebase, progress`);
|
||||||
|
|||||||
@@ -1052,3 +1052,142 @@ key-decisions:
|
|||||||
assert.strictEqual(output.decisions[1].rationale, 'Stateless auth for scalability', 'second decision rationale');
|
assert.strictEqual(output.decisions[1].rationale, 'Stateless auth for scalability', 'second decision rationale');
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
// init --include flag tests
|
||||||
|
// ─────────────────────────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
describe('init commands with --include flag', () => {
|
||||||
|
let tmpDir;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
tmpDir = createTempProject();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(() => {
|
||||||
|
cleanup(tmpDir);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('init execute-phase includes state and config content', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'STATE.md'),
|
||||||
|
'# State\n\n**Current Phase:** 03\n**Status:** In progress'
|
||||||
|
);
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'config.json'),
|
||||||
|
JSON.stringify({ model_profile: 'balanced' })
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('init execute-phase 03 --include state,config', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.state_content, 'state_content should be included');
|
||||||
|
assert.ok(output.state_content.includes('Current Phase'), 'state content correct');
|
||||||
|
assert.ok(output.config_content, 'config_content should be included');
|
||||||
|
assert.ok(output.config_content.includes('model_profile'), 'config content correct');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('init execute-phase without --include omits content', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State');
|
||||||
|
|
||||||
|
const result = runGsdTools('init execute-phase 03', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.state_content, undefined, 'state_content should be omitted');
|
||||||
|
assert.strictEqual(output.config_content, undefined, 'config_content should be omitted');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('init plan-phase includes multiple file contents', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# Project State');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap v1.0');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'REQUIREMENTS.md'), '# Requirements');
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-CONTEXT.md'), '# Phase Context');
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-RESEARCH.md'), '# Research Findings');
|
||||||
|
|
||||||
|
const result = runGsdTools('init plan-phase 03 --include state,roadmap,requirements,context,research', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.state_content, 'state_content included');
|
||||||
|
assert.ok(output.state_content.includes('Project State'), 'state content correct');
|
||||||
|
assert.ok(output.roadmap_content, 'roadmap_content included');
|
||||||
|
assert.ok(output.roadmap_content.includes('Roadmap v1.0'), 'roadmap content correct');
|
||||||
|
assert.ok(output.requirements_content, 'requirements_content included');
|
||||||
|
assert.ok(output.context_content, 'context_content included');
|
||||||
|
assert.ok(output.research_content, 'research_content included');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('init plan-phase includes verification and uat content', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-VERIFICATION.md'), '# Verification Results');
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-UAT.md'), '# UAT Findings');
|
||||||
|
|
||||||
|
const result = runGsdTools('init plan-phase 03 --include verification,uat', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.verification_content, 'verification_content included');
|
||||||
|
assert.ok(output.verification_content.includes('Verification Results'), 'verification content correct');
|
||||||
|
assert.ok(output.uat_content, 'uat_content included');
|
||||||
|
assert.ok(output.uat_content.includes('UAT Findings'), 'uat content correct');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('init progress includes state, roadmap, project, config', () => {
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'PROJECT.md'), '# Project');
|
||||||
|
fs.writeFileSync(
|
||||||
|
path.join(tmpDir, '.planning', 'config.json'),
|
||||||
|
JSON.stringify({ model_profile: 'quality' })
|
||||||
|
);
|
||||||
|
|
||||||
|
const result = runGsdTools('init progress --include state,roadmap,project,config', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.state_content, 'state_content included');
|
||||||
|
assert.ok(output.roadmap_content, 'roadmap_content included');
|
||||||
|
assert.ok(output.project_content, 'project_content included');
|
||||||
|
assert.ok(output.config_content, 'config_content included');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('missing files return null in content fields', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-01-PLAN.md'), '# Plan');
|
||||||
|
|
||||||
|
const result = runGsdTools('init execute-phase 03 --include state,config', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.strictEqual(output.state_content, null, 'missing state returns null');
|
||||||
|
assert.strictEqual(output.config_content, null, 'missing config returns null');
|
||||||
|
});
|
||||||
|
|
||||||
|
test('partial includes work correctly', () => {
|
||||||
|
const phaseDir = path.join(tmpDir, '.planning', 'phases', '03-api');
|
||||||
|
fs.mkdirSync(phaseDir, { recursive: true });
|
||||||
|
fs.writeFileSync(path.join(phaseDir, '03-01-PLAN.md'), '# Plan');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'STATE.md'), '# State');
|
||||||
|
fs.writeFileSync(path.join(tmpDir, '.planning', 'ROADMAP.md'), '# Roadmap');
|
||||||
|
|
||||||
|
// Only request state, not roadmap
|
||||||
|
const result = runGsdTools('init execute-phase 03 --include state', tmpDir);
|
||||||
|
assert.ok(result.success, `Command failed: ${result.error}`);
|
||||||
|
|
||||||
|
const output = JSON.parse(result.output);
|
||||||
|
assert.ok(output.state_content, 'state_content included');
|
||||||
|
assert.strictEqual(output.roadmap_content, undefined, 'roadmap_content not requested, should be undefined');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -13,14 +13,16 @@ Read STATE.md before any operation to load project context.
|
|||||||
<process>
|
<process>
|
||||||
|
|
||||||
<step name="initialize" priority="first">
|
<step name="initialize" priority="first">
|
||||||
Load all context in one call:
|
Load all context in one call (include file contents to avoid redundant reads):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init execute-phase "${PHASE_ARG}")
|
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init execute-phase "${PHASE_ARG}" --include state,config)
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`.
|
Parse JSON for: `executor_model`, `verifier_model`, `commit_docs`, `parallelization`, `branching_strategy`, `branch_name`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `plans`, `incomplete_plans`, `plan_count`, `incomplete_count`, `state_exists`, `roadmap_exists`.
|
||||||
|
|
||||||
|
**File contents (from --include):** `state_content`, `config_content`. These are null if files don't exist.
|
||||||
|
|
||||||
**If `phase_found` is false:** Error — phase directory not found.
|
**If `phase_found` is false:** Error — phase directory not found.
|
||||||
**If `plan_count` is 0:** Error — no plans found in phase.
|
**If `plan_count` is 0:** Error — no plans found in phase.
|
||||||
**If `state_exists` is false but `.planning/` exists:** Offer reconstruct or continue.
|
**If `state_exists` is false but `.planning/` exists:** Offer reconstruct or continue.
|
||||||
@@ -97,11 +99,13 @@ Execute each wave in sequence. Within a wave: parallel if `PARALLELIZATION=true`
|
|||||||
2. **Read files and spawn agents:**
|
2. **Read files and spawn agents:**
|
||||||
|
|
||||||
Content must be inlined — `@` syntax doesn't work across Task() boundaries.
|
Content must be inlined — `@` syntax doesn't work across Task() boundaries.
|
||||||
|
STATE and CONFIG are already loaded via `--include` in initialize step.
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
PLAN_CONTENT=$(cat "{plan_path}")
|
PLAN_CONTENT=$(cat "{plan_path}")
|
||||||
STATE_CONTENT=$(cat .planning/STATE.md)
|
# Use state_content and config_content from INIT (no need to re-read)
|
||||||
CONFIG_CONTENT=$(cat .planning/config.json 2>/dev/null)
|
STATE_CONTENT=$(echo "$INIT" | jq -r '.state_content // empty')
|
||||||
|
CONFIG_CONTENT=$(echo "$INIT" | jq -r '.config_content // empty')
|
||||||
```
|
```
|
||||||
|
|
||||||
Each agent prompt:
|
Each agent prompt:
|
||||||
|
|||||||
@@ -12,17 +12,18 @@ Read config.json for planning behavior settings.
|
|||||||
<process>
|
<process>
|
||||||
|
|
||||||
<step name="init_context" priority="first">
|
<step name="init_context" priority="first">
|
||||||
Load execution context (uses `init execute-phase` for full context):
|
Load execution context (uses `init execute-phase` for full context, including file contents):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init execute-phase "${PHASE}")
|
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init execute-phase "${PHASE}" --include state,config)
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract from init JSON: `executor_model`, `commit_docs`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`.
|
Extract from init JSON: `executor_model`, `commit_docs`, `phase_dir`, `phase_number`, `plans`, `summaries`, `incomplete_plans`.
|
||||||
|
|
||||||
Also read STATE.md for current position, decisions, blockers:
|
**File contents (from --include):** `state_content`, `config_content`. Access with:
|
||||||
```bash
|
```bash
|
||||||
cat .planning/STATE.md 2>/dev/null
|
STATE_CONTENT=$(echo "$INIT" | jq -r '.state_content // empty')
|
||||||
|
CONFIG_CONTENT=$(echo "$INIT" | jq -r '.config_content // empty')
|
||||||
```
|
```
|
||||||
|
|
||||||
If `.planning/` missing: error.
|
If `.planning/` missing: error.
|
||||||
@@ -30,7 +31,7 @@ If `.planning/` missing: error.
|
|||||||
|
|
||||||
<step name="identify_plan">
|
<step name="identify_plan">
|
||||||
```bash
|
```bash
|
||||||
cat .planning/ROADMAP.md
|
# Use plans/summaries from INIT JSON, or list files
|
||||||
ls .planning/phases/XX-name/*-PLAN.md 2>/dev/null | sort
|
ls .planning/phases/XX-name/*-PLAN.md 2>/dev/null | sort
|
||||||
ls .planning/phases/XX-name/*-SUMMARY.md 2>/dev/null | sort
|
ls .planning/phases/XX-name/*-SUMMARY.md 2>/dev/null | sort
|
||||||
```
|
```
|
||||||
@@ -39,7 +40,7 @@ Find first PLAN without matching SUMMARY. Decimal phases supported (`01.1-hotfix
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
PHASE=$(echo "$PLAN_PATH" | grep -oE '[0-9]+(\.[0-9]+)?-[0-9]+')
|
PHASE=$(echo "$PLAN_PATH" | grep -oE '[0-9]+(\.[0-9]+)?-[0-9]+')
|
||||||
cat .planning/config.json 2>/dev/null
|
# config_content already loaded via --include config in init_context
|
||||||
```
|
```
|
||||||
|
|
||||||
<if mode="yolo">
|
<if mode="yolo">
|
||||||
|
|||||||
@@ -12,14 +12,16 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|||||||
|
|
||||||
## 1. Initialize
|
## 1. Initialize
|
||||||
|
|
||||||
Load all context in one call:
|
Load all context in one call (include file contents to avoid redundant reads):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init plan-phase "$PHASE")
|
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init plan-phase "$PHASE" --include state,roadmap,requirements,context,research,verification,uat)
|
||||||
```
|
```
|
||||||
|
|
||||||
Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`.
|
Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_enabled`, `plan_checker_enabled`, `commit_docs`, `phase_found`, `phase_dir`, `phase_number`, `phase_name`, `phase_slug`, `padded_phase`, `has_research`, `has_context`, `has_plans`, `plan_count`, `planning_exists`, `roadmap_exists`.
|
||||||
|
|
||||||
|
**File contents (from --include):** `state_content`, `roadmap_content`, `requirements_content`, `context_content`, `research_content`, `verification_content`, `uat_content`. These are null if files don't exist.
|
||||||
|
|
||||||
**If `planning_exists` is false:** Error — run `/gsd:new-project` first.
|
**If `planning_exists` is false:** Error — run `/gsd:new-project` first.
|
||||||
|
|
||||||
## 2. Parse and Normalize Arguments
|
## 2. Parse and Normalize Arguments
|
||||||
@@ -45,13 +47,11 @@ PHASE_INFO=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${
|
|||||||
|
|
||||||
## 4. Load CONTEXT.md
|
## 4. Load CONTEXT.md
|
||||||
|
|
||||||
```bash
|
Use `context_content` from init JSON (already loaded via `--include context`).
|
||||||
CONTEXT_CONTENT=$(cat "${PHASE_DIR}"/*-CONTEXT.md 2>/dev/null)
|
|
||||||
```
|
|
||||||
|
|
||||||
**CRITICAL:** Store `CONTEXT_CONTENT` now — pass to researcher, planner, checker, and revision agents.
|
**CRITICAL:** Use `context_content` from INIT — pass to researcher, planner, checker, and revision agents.
|
||||||
|
|
||||||
If CONTEXT.md exists, display: `Using phase context from: ${PHASE_DIR}/*-CONTEXT.md`
|
If `context_content` is not null, display: `Using phase context from: ${PHASE_DIR}/*-CONTEXT.md`
|
||||||
|
|
||||||
## 5. Handle Research
|
## 5. Handle Research
|
||||||
|
|
||||||
@@ -74,7 +74,8 @@ Display banner:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
PHASE_DESC=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}" | jq -r '.section')
|
PHASE_DESC=$(node ~/.claude/get-shit-done/bin/gsd-tools.js roadmap get-phase "${PHASE}" | jq -r '.section')
|
||||||
REQUIREMENTS=$(cat .planning/REQUIREMENTS.md 2>/dev/null | grep -A100 "## Requirements" | head -50)
|
# Use requirements_content from INIT (already loaded via --include requirements)
|
||||||
|
REQUIREMENTS=$(echo "$INIT" | jq -r '.requirements_content // empty' | grep -A100 "## Requirements" | head -50)
|
||||||
STATE_SNAP=$(node ~/.claude/get-shit-done/bin/gsd-tools.js state-snapshot)
|
STATE_SNAP=$(node ~/.claude/get-shit-done/bin/gsd-tools.js state-snapshot)
|
||||||
# Extract decisions from state-snapshot JSON: jq '.decisions[] | "\(.phase): \(.summary) - \(.rationale)"'
|
# Extract decisions from state-snapshot JSON: jq '.decisions[] | "\(.phase): \(.summary) - \(.rationale)"'
|
||||||
```
|
```
|
||||||
@@ -129,17 +130,19 @@ ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null
|
|||||||
|
|
||||||
**If exists:** Offer: 1) Add more plans, 2) View existing, 3) Replan from scratch.
|
**If exists:** Offer: 1) Add more plans, 2) View existing, 3) Replan from scratch.
|
||||||
|
|
||||||
## 7. Read Context Files
|
## 7. Use Context Files from INIT
|
||||||
|
|
||||||
Read and store for planner agent (`@` syntax doesn't work across Task() boundaries):
|
All file contents are already loaded via `--include` in step 1 (`@` syntax doesn't work across Task() boundaries):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
STATE_CONTENT=$(cat .planning/STATE.md)
|
# Extract from INIT JSON (no need to re-read files)
|
||||||
ROADMAP_CONTENT=$(cat .planning/ROADMAP.md)
|
STATE_CONTENT=$(echo "$INIT" | jq -r '.state_content // empty')
|
||||||
REQUIREMENTS_CONTENT=$(cat .planning/REQUIREMENTS.md 2>/dev/null)
|
ROADMAP_CONTENT=$(echo "$INIT" | jq -r '.roadmap_content // empty')
|
||||||
RESEARCH_CONTENT=$(cat "${PHASE_DIR}"/*-RESEARCH.md 2>/dev/null)
|
REQUIREMENTS_CONTENT=$(echo "$INIT" | jq -r '.requirements_content // empty')
|
||||||
VERIFICATION_CONTENT=$(cat "${PHASE_DIR}"/*-VERIFICATION.md 2>/dev/null)
|
RESEARCH_CONTENT=$(echo "$INIT" | jq -r '.research_content // empty')
|
||||||
UAT_CONTENT=$(cat "${PHASE_DIR}"/*-UAT.md 2>/dev/null)
|
VERIFICATION_CONTENT=$(echo "$INIT" | jq -r '.verification_content // empty')
|
||||||
|
UAT_CONTENT=$(echo "$INIT" | jq -r '.uat_content // empty')
|
||||||
|
CONTEXT_CONTENT=$(echo "$INIT" | jq -r '.context_content // empty')
|
||||||
```
|
```
|
||||||
|
|
||||||
## 8. Spawn gsd-planner Agent
|
## 8. Spawn gsd-planner Agent
|
||||||
|
|||||||
@@ -9,14 +9,16 @@ Read all files referenced by the invoking prompt's execution_context before star
|
|||||||
<process>
|
<process>
|
||||||
|
|
||||||
<step name="init_context">
|
<step name="init_context">
|
||||||
**Load progress context:**
|
**Load progress context (with file contents to avoid redundant reads):**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init progress)
|
INIT=$(node ~/.claude/get-shit-done/bin/gsd-tools.js init progress --include state,roadmap,project,config)
|
||||||
```
|
```
|
||||||
|
|
||||||
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`.
|
Extract from init JSON: `project_exists`, `roadmap_exists`, `state_exists`, `phases`, `current_phase`, `next_phase`, `milestone_version`, `completed_count`, `phase_count`, `paused_at`.
|
||||||
|
|
||||||
|
**File contents (from --include):** `state_content`, `roadmap_content`, `project_content`, `config_content`. These are null if files don't exist.
|
||||||
|
|
||||||
If `project_exists` is false (no `.planning/` directory):
|
If `project_exists` is false (no `.planning/` directory):
|
||||||
|
|
||||||
```
|
```
|
||||||
@@ -37,13 +39,16 @@ If missing both ROADMAP.md and PROJECT.md: suggest `/gsd:new-project`.
|
|||||||
</step>
|
</step>
|
||||||
|
|
||||||
<step name="load">
|
<step name="load">
|
||||||
**Load full project context:**
|
**Use project context from INIT:**
|
||||||
|
|
||||||
- Read `.planning/STATE.md` for living memory (position, decisions, issues)
|
All file contents are already loaded via `--include` in init_context step:
|
||||||
- Read `.planning/ROADMAP.md` for phase structure and objectives
|
- `state_content` — living memory (position, decisions, issues)
|
||||||
- Read `.planning/PROJECT.md` for current state (What This Is, Core Value, Requirements)
|
- `roadmap_content` — phase structure and objectives
|
||||||
- Read `.planning/config.json` for settings (model_profile, workflow toggles)
|
- `project_content` — current state (What This Is, Core Value, Requirements)
|
||||||
</step>
|
- `config_content` — settings (model_profile, workflow toggles)
|
||||||
|
|
||||||
|
No additional file reads needed.
|
||||||
|
</step>
|
||||||
|
|
||||||
<step name="recent">
|
<step name="recent">
|
||||||
**Gather recent work context:**
|
**Gather recent work context:**
|
||||||
|
|||||||
Reference in New Issue
Block a user