mirror of
https://github.com/glittercowboy/get-shit-done
synced 2026-04-25 17:25:23 +02:00
* test: Bootstrapped sdk/ as TypeScript ESM package with full GSD-1 PLAN.… - "sdk/package.json" - "sdk/tsconfig.json" - "sdk/vitest.config.ts" - "sdk/src/types.ts" - "sdk/src/plan-parser.ts" - "sdk/src/plan-parser.test.ts" GSD-Task: S01/T01 * test: Implemented config reader and gsd-tools bridge with 25 unit tests… - "sdk/src/config.ts" - "sdk/src/config.test.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/gsd-tools.test.ts" GSD-Task: S01/T02 * test: Built prompt-builder, session-runner, and GSD class — 85 total un… - "sdk/src/prompt-builder.ts" - "sdk/src/prompt-builder.test.ts" - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/types.ts" GSD-Task: S01/T03 * test: Created E2E integration test with fixtures proving full SDK pipel… - "sdk/src/e2e.integration.test.ts" - "sdk/test-fixtures/sample-plan.md" - "sdk/test-fixtures/.planning/config.json" - "sdk/test-fixtures/.planning/STATE.md" - "vitest.config.ts" - "tsconfig.json" GSD-Task: S01/T04 * test: Added PhaseType/GSDEventType enums, 16-variant GSDEvent union, GS… - "sdk/src/types.ts" - "sdk/src/event-stream.ts" - "sdk/src/logger.ts" - "sdk/src/event-stream.test.ts" - "sdk/src/logger.test.ts" GSD-Task: S02/T01 * test: Built ContextEngine for phase-aware context file resolution, getT… - "sdk/src/context-engine.ts" - "sdk/src/tool-scoping.ts" - "sdk/src/phase-prompt.ts" - "sdk/src/context-engine.test.ts" - "sdk/src/tool-scoping.test.ts" - "sdk/src/phase-prompt.test.ts" GSD-Task: S02/T02 * test: Wired event stream into session runner, added onEvent()/addTransp… - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/e2e.integration.test.ts" GSD-Task: S02/T03 * feat: Added PhaseStepType enum, PhaseOpInfo interface, phase lifecycle… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/session-runner.ts" - "sdk/src/index.ts" - "sdk/src/phase-runner-types.test.ts" GSD-Task: S03/T01 * test: Implemented PhaseRunner state machine with 39 unit tests covering… - "sdk/src/phase-runner.ts" - "sdk/src/phase-runner.test.ts" GSD-Task: S03/T02 * test: Wired PhaseRunner into GSD.runPhase() public API with full re-exp… - "sdk/src/index.ts" - "sdk/src/phase-runner.integration.test.ts" - "sdk/src/phase-runner.ts" GSD-Task: S03/T03 * test: Expanded runVerifyStep with full gap closure cycle (plan → execut… - "sdk/src/types.ts" - "sdk/src/phase-runner.ts" - "sdk/src/phase-runner.test.ts" GSD-Task: S04/T02 * fix: Added 3 integration tests proving phasePlanIndex returns correct t… - "sdk/src/phase-runner.integration.test.ts" - "sdk/src/index.ts" GSD-Task: S04/T03 * test: Add milestone-level types, typed roadmapAnalyze(), GSD.run() orch… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/index.ts" - "sdk/src/milestone-runner.test.ts" GSD-Task: S05/T01 * test: Added CLITransport (structured stdout log lines) and WSTransport… - "sdk/src/cli-transport.ts" - "sdk/src/cli-transport.test.ts" - "sdk/src/ws-transport.ts" - "sdk/src/ws-transport.test.ts" - "sdk/src/index.ts" - "sdk/package.json" GSD-Task: S05/T02 * test: Added gsd-sdk CLI entry point with argument parsing, bin field, p… - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/package.json" GSD-Task: S05/T03 * feat: Add InitNewProjectInfo type, initNewProject()/configSet() GSDTool… - "sdk/src/types.ts" - "sdk/src/gsd-tools.ts" - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/src/gsd-tools.test.ts" GSD-Task: S01/T01 * chore: Created InitRunner orchestrator with setup → config → PROJECT.md… - "sdk/src/init-runner.ts" - "sdk/src/types.ts" - "sdk/src/index.ts" GSD-Task: S01/T02 * test: Wired InitRunner into CLI main() for full gsd-sdk init dispatch a… - "sdk/src/cli.ts" - "sdk/src/init-runner.test.ts" - "sdk/src/cli.test.ts" GSD-Task: S01/T03 * test: Add PlanCheck step, AI self-discuss, and retryOnce wrapper to Pha… - "sdk/src/types.ts" - "sdk/src/phase-runner.ts" - "sdk/src/session-runner.ts" - "sdk/src/phase-runner.test.ts" - "sdk/src/phase-runner-types.test.ts" GSD-Task: S02/T01 * feat: Rewrite CLITransport with ANSI colors, phase banners, spawn indic… - "sdk/src/cli-transport.ts" - "sdk/src/cli-transport.test.ts" GSD-Task: S02/T02 * test: Add `gsd-sdk auto` command with autoMode config override, USAGE t… - "sdk/src/cli.ts" - "sdk/src/cli.test.ts" - "sdk/src/index.ts" - "sdk/src/types.ts" GSD-Task: S02/T03 * fix: CLI shebang + gsd-tools non-JSON output handling Three bugs found during first real gsd-sdk run: 1. cli.ts shebang was commented out — shell executed JS as bash, triggering ImageMagick's import command instead of Node 2. configSet() called exec() which JSON.parse()d the output, but gsd-tools config-set returns 'key=value' text, not JSON. Added execRaw() method for commands that return plain text. 3. Same JSON parse bug affected commit() (returns git SHA), stateLoad(), verifySummary(), initExecutePhase(), stateBeginPhase(), and phaseComplete(). All switched to execRaw(). Tests updated to match real gsd-tools output format (plain text instead of mocked JSON). 376/376 tests pass.
194 lines
6.2 KiB
TypeScript
194 lines
6.2 KiB
TypeScript
/**
|
|
* Prompt builder — assembles executor prompts from parsed plans.
|
|
*
|
|
* Converts a ParsedPlan into a structured prompt that tells the
|
|
* executor agent exactly what to do: follow the tasks sequentially,
|
|
* verify each one, and produce a SUMMARY.md at the end.
|
|
*/
|
|
|
|
import type { ParsedPlan, PlanTask } from './types.js';
|
|
|
|
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
|
|
const DEFAULT_ALLOWED_TOOLS = ['Read', 'Write', 'Edit', 'Bash', 'Grep', 'Glob'];
|
|
|
|
// ─── Agent definition parsing ────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Extract the tools list from a gsd-executor.md agent definition.
|
|
* Falls back to DEFAULT_ALLOWED_TOOLS if parsing fails.
|
|
*/
|
|
export function parseAgentTools(agentDef: string): string[] {
|
|
// Look for "tools:" in the YAML frontmatter
|
|
const frontmatterMatch = agentDef.match(/^---\s*\n([\s\S]*?)\n---/);
|
|
if (!frontmatterMatch) return DEFAULT_ALLOWED_TOOLS;
|
|
|
|
const toolsMatch = frontmatterMatch[1].match(/^tools:\s*(.+)$/m);
|
|
if (!toolsMatch) return DEFAULT_ALLOWED_TOOLS;
|
|
|
|
const tools = toolsMatch[1]
|
|
.split(',')
|
|
.map((t) => t.trim())
|
|
.filter(Boolean);
|
|
|
|
return tools.length > 0 ? tools : DEFAULT_ALLOWED_TOOLS;
|
|
}
|
|
|
|
/**
|
|
* Extract the role instructions from a gsd-executor.md agent definition.
|
|
* Returns the <role>...</role> block content, or empty string.
|
|
*/
|
|
export function parseAgentRole(agentDef: string): string {
|
|
const match = agentDef.match(/<role>([\s\S]*?)<\/role>/i);
|
|
return match ? match[1].trim() : '';
|
|
}
|
|
|
|
// ─── Prompt assembly ─────────────────────────────────────────────────────────
|
|
|
|
/**
|
|
* Format a single task into a prompt block.
|
|
*/
|
|
function formatTask(task: PlanTask, index: number): string {
|
|
const lines: string[] = [];
|
|
lines.push(`### Task ${index + 1}: ${task.name}`);
|
|
|
|
if (task.files.length > 0) {
|
|
lines.push(`**Files:** ${task.files.join(', ')}`);
|
|
}
|
|
|
|
if (task.read_first.length > 0) {
|
|
lines.push(`**Read first:** ${task.read_first.join(', ')}`);
|
|
}
|
|
|
|
lines.push('');
|
|
lines.push('**Action:**');
|
|
lines.push(task.action);
|
|
|
|
if (task.verify) {
|
|
lines.push('');
|
|
lines.push('**Verify:**');
|
|
lines.push(task.verify);
|
|
}
|
|
|
|
if (task.done) {
|
|
lines.push('');
|
|
lines.push('**Done when:**');
|
|
lines.push(task.done);
|
|
}
|
|
|
|
if (task.acceptance_criteria.length > 0) {
|
|
lines.push('');
|
|
lines.push('**Acceptance criteria:**');
|
|
for (const criterion of task.acceptance_criteria) {
|
|
lines.push(`- ${criterion}`);
|
|
}
|
|
}
|
|
|
|
return lines.join('\n');
|
|
}
|
|
|
|
/**
|
|
* Build the executor prompt from a parsed plan and optional agent definition.
|
|
*
|
|
* The prompt instructs the executor to:
|
|
* 1. Follow the plan tasks sequentially
|
|
* 2. Run verification for each task
|
|
* 3. Commit each task individually
|
|
* 4. Produce a SUMMARY.md file on completion
|
|
*
|
|
* @param plan - Parsed plan structure from plan-parser
|
|
* @param agentDef - Raw content of gsd-executor.md agent definition (optional)
|
|
* @returns Assembled prompt string
|
|
*/
|
|
export function buildExecutorPrompt(plan: ParsedPlan, agentDef?: string): string {
|
|
const sections: string[] = [];
|
|
|
|
// ── Role instructions from agent definition ──
|
|
if (agentDef) {
|
|
const role = parseAgentRole(agentDef);
|
|
if (role) {
|
|
sections.push(`## Role\n\n${role}`);
|
|
}
|
|
}
|
|
|
|
// ── Objective ──
|
|
if (plan.objective) {
|
|
sections.push(`## Objective\n\n${plan.objective}`);
|
|
} else {
|
|
sections.push(`## Objective\n\nExecute plan: ${plan.frontmatter.plan || plan.frontmatter.phase || 'unnamed'}`);
|
|
}
|
|
|
|
// ── Plan metadata ──
|
|
const meta: string[] = [];
|
|
if (plan.frontmatter.phase) meta.push(`Phase: ${plan.frontmatter.phase}`);
|
|
if (plan.frontmatter.plan) meta.push(`Plan: ${plan.frontmatter.plan}`);
|
|
if (plan.frontmatter.type) meta.push(`Type: ${plan.frontmatter.type}`);
|
|
if (meta.length > 0) {
|
|
sections.push(`## Plan Info\n\n${meta.join('\n')}`);
|
|
}
|
|
|
|
// ── Context references ──
|
|
if (plan.context_refs.length > 0) {
|
|
const refs = plan.context_refs.map((r) => `- @${r}`).join('\n');
|
|
sections.push(`## Context Files\n\nRead these files for context before starting:\n${refs}`);
|
|
}
|
|
|
|
// ── Tasks ──
|
|
if (plan.tasks.length > 0) {
|
|
const taskBlocks = plan.tasks.map((t, i) => formatTask(t, i)).join('\n\n---\n\n');
|
|
sections.push(`## Tasks\n\nExecute these tasks sequentially. For each task: read any referenced files, execute the action, run verification, confirm done criteria, then commit.\n\n${taskBlocks}`);
|
|
} else {
|
|
sections.push(`## Tasks\n\nNo tasks defined in this plan. Review the objective and determine if any actions are needed.`);
|
|
}
|
|
|
|
// ── Must-haves ──
|
|
if (plan.frontmatter.must_haves) {
|
|
const mh = plan.frontmatter.must_haves;
|
|
const parts: string[] = [];
|
|
|
|
if (mh.truths.length > 0) {
|
|
parts.push('**Truths (invariants):**');
|
|
for (const t of mh.truths) {
|
|
parts.push(`- ${t}`);
|
|
}
|
|
}
|
|
|
|
if (mh.artifacts.length > 0) {
|
|
parts.push('**Required artifacts:**');
|
|
for (const a of mh.artifacts) {
|
|
parts.push(`- \`${a.path}\`: ${a.provides}`);
|
|
}
|
|
}
|
|
|
|
if (mh.key_links.length > 0) {
|
|
parts.push('**Key links:**');
|
|
for (const l of mh.key_links) {
|
|
parts.push(`- ${l.from} → ${l.to} via ${l.via}`);
|
|
}
|
|
}
|
|
|
|
if (parts.length > 0) {
|
|
sections.push(`## Must-Haves\n\n${parts.join('\n')}`);
|
|
}
|
|
}
|
|
|
|
// ── Completion instructions ──
|
|
sections.push(
|
|
`## Completion\n\n` +
|
|
`After all tasks are complete:\n` +
|
|
`1. Run any overall verification or success criteria checks\n` +
|
|
`2. Create a SUMMARY.md file documenting:\n` +
|
|
` - One-line summary of what was accomplished\n` +
|
|
` - Tasks completed with commit hashes\n` +
|
|
` - Any deviations from the plan\n` +
|
|
` - Files created or modified\n` +
|
|
` - Known issues (if any)\n` +
|
|
`3. Commit the SUMMARY.md\n` +
|
|
`4. Report completion`,
|
|
);
|
|
|
|
return sections.join('\n\n');
|
|
}
|
|
|
|
export { DEFAULT_ALLOWED_TOOLS };
|