gsd-pi 2.23.0 → 2.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/cli.js +12 -3
- package/dist/headless.d.ts +4 -0
- package/dist/headless.js +118 -10
- package/dist/help-text.js +22 -7
- package/dist/models-resolver.d.ts +0 -11
- package/dist/models-resolver.js +0 -15
- package/dist/resource-loader.d.ts +0 -1
- package/dist/resource-loader.js +64 -18
- package/dist/resources/GSD-WORKFLOW.md +12 -9
- package/dist/resources/extensions/bg-shell/overlay.ts +18 -17
- package/dist/resources/extensions/get-secrets-from-user.ts +5 -23
- package/dist/resources/extensions/gsd/activity-log.ts +5 -3
- package/dist/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/dist/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/dist/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/dist/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/dist/resources/extensions/gsd/auto.ts +307 -77
- package/dist/resources/extensions/gsd/cache.ts +3 -1
- package/dist/resources/extensions/gsd/commands.ts +176 -10
- package/dist/resources/extensions/gsd/complexity.ts +1 -0
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/dist/resources/extensions/gsd/doctor.ts +58 -11
- package/dist/resources/extensions/gsd/exit-command.ts +2 -2
- package/dist/resources/extensions/gsd/git-service.ts +74 -14
- package/dist/resources/extensions/gsd/gitignore.ts +1 -0
- package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +109 -12
- package/dist/resources/extensions/gsd/index.ts +48 -2
- package/dist/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/dist/resources/extensions/gsd/memory-store.ts +441 -0
- package/dist/resources/extensions/gsd/migrate/command.ts +2 -2
- package/dist/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/dist/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/dist/resources/extensions/gsd/preferences.ts +65 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/dist/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/dist/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/queue.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/dist/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/dist/resources/extensions/gsd/session-status-io.ts +197 -0
- package/dist/resources/extensions/gsd/state.ts +72 -30
- package/dist/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/dist/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/dist/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/dist/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/dist/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/dist/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/dist/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/dist/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/dist/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/dist/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/dist/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/dist/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/dist/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/dist/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/dist/resources/extensions/gsd/triage-ui.ts +1 -1
- package/dist/resources/extensions/gsd/types.ts +15 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/dist/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/dist/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/dist/resources/extensions/gsd/worktree.ts +9 -2
- package/dist/resources/extensions/search-the-web/native-search.ts +15 -5
- package/dist/resources/extensions/subagent/index.ts +5 -0
- package/dist/resources/extensions/subagent/worker-registry.ts +99 -0
- package/dist/update-check.d.ts +9 -0
- package/dist/update-check.js +97 -0
- package/package.json +6 -1
- package/packages/pi-agent-core/dist/agent-loop.js +2 -0
- package/packages/pi-agent-core/dist/agent-loop.js.map +1 -1
- package/packages/pi-agent-core/src/agent-loop.ts +2 -0
- package/packages/pi-ai/dist/providers/anthropic.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/anthropic.js +55 -7
- package/packages/pi-ai/dist/providers/anthropic.js.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/azure-openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/azure-openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/google-vertex.js +21 -9
- package/packages/pi-ai/dist/providers/google-vertex.js.map +1 -1
- package/packages/pi-ai/dist/providers/mistral.js +3 -0
- package/packages/pi-ai/dist/providers/mistral.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-completions.js +12 -4
- package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.d.ts.map +1 -1
- package/packages/pi-ai/dist/providers/openai-responses.js +12 -4
- package/packages/pi-ai/dist/providers/openai-responses.js.map +1 -1
- package/packages/pi-ai/dist/types.d.ts +23 -1
- package/packages/pi-ai/dist/types.d.ts.map +1 -1
- package/packages/pi-ai/dist/types.js.map +1 -1
- package/packages/pi-ai/src/providers/anthropic.ts +59 -9
- package/packages/pi-ai/src/providers/azure-openai-responses.ts +16 -4
- package/packages/pi-ai/src/providers/google-vertex.ts +32 -17
- package/packages/pi-ai/src/providers/mistral.ts +3 -0
- package/packages/pi-ai/src/providers/openai-completions.ts +16 -4
- package/packages/pi-ai/src/providers/openai-responses.ts +16 -4
- package/packages/pi-ai/src/types.ts +19 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/components/tool-execution.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +4 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js +72 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +1 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +2 -2
- package/packages/pi-coding-agent/src/modes/interactive/components/tool-execution.ts +18 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +84 -0
- package/scripts/postinstall.js +7 -109
- package/src/resources/GSD-WORKFLOW.md +12 -9
- package/src/resources/extensions/bg-shell/overlay.ts +18 -17
- package/src/resources/extensions/get-secrets-from-user.ts +5 -23
- package/src/resources/extensions/gsd/activity-log.ts +5 -3
- package/src/resources/extensions/gsd/auto-dispatch.ts +51 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +87 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +41 -2
- package/src/resources/extensions/gsd/auto-worktree.ts +134 -4
- package/src/resources/extensions/gsd/auto.ts +307 -77
- package/src/resources/extensions/gsd/cache.ts +3 -1
- package/src/resources/extensions/gsd/commands.ts +176 -10
- package/src/resources/extensions/gsd/complexity.ts +1 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +38 -0
- package/src/resources/extensions/gsd/doctor.ts +58 -11
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/git-service.ts +74 -14
- package/src/resources/extensions/gsd/gitignore.ts +1 -0
- package/src/resources/extensions/gsd/gsd-db.ts +78 -1
- package/src/resources/extensions/gsd/guided-flow.ts +109 -12
- package/src/resources/extensions/gsd/index.ts +48 -2
- package/src/resources/extensions/gsd/memory-extractor.ts +352 -0
- package/src/resources/extensions/gsd/memory-store.ts +441 -0
- package/src/resources/extensions/gsd/migrate/command.ts +2 -2
- package/src/resources/extensions/gsd/parallel-eligibility.ts +233 -0
- package/src/resources/extensions/gsd/parallel-merge.ts +156 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +496 -0
- package/src/resources/extensions/gsd/preferences.ts +65 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +86 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +4 -4
- package/src/resources/extensions/gsd/prompts/execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/queue.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/validate-milestone.md +40 -61
- package/src/resources/extensions/gsd/provider-error-pause.ts +29 -2
- package/src/resources/extensions/gsd/session-status-io.ts +197 -0
- package/src/resources/extensions/gsd/state.ts +72 -30
- package/src/resources/extensions/gsd/tests/agent-end-provider-error.test.ts +81 -0
- package/src/resources/extensions/gsd/tests/auto-budget-alerts.test.ts +20 -3
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +256 -2
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +34 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +8 -1
- package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +9 -15
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +9 -0
- package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +14 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +70 -4
- package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -2
- package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +8 -0
- package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -3
- package/src/resources/extensions/gsd/tests/memory-extractor.test.ts +180 -0
- package/src/resources/extensions/gsd/tests/memory-store.test.ts +345 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +5 -5
- package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +656 -0
- package/src/resources/extensions/gsd/tests/parallel-workers-multi-milestone-e2e.test.ts +354 -0
- package/src/resources/extensions/gsd/tests/queue-reorder-e2e.test.ts +1 -0
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +316 -0
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +147 -2
- package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +88 -10
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +314 -87
- package/src/resources/extensions/gsd/tests/worker-registry.test.ts +148 -0
- package/src/resources/extensions/gsd/triage-ui.ts +1 -1
- package/src/resources/extensions/gsd/types.ts +15 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +291 -10
- package/src/resources/extensions/gsd/visualizer-overlay.ts +237 -28
- package/src/resources/extensions/gsd/visualizer-views.ts +462 -48
- package/src/resources/extensions/gsd/worktree.ts +9 -2
- package/src/resources/extensions/search-the-web/native-search.ts +15 -5
- package/src/resources/extensions/subagent/index.ts +5 -0
- package/src/resources/extensions/subagent/worker-registry.ts +99 -0
|
@@ -2553,7 +2553,7 @@ export class AgentSession {
|
|
|
2553
2553
|
if (message.retryAfterMs !== undefined) {
|
|
2554
2554
|
const cap = settings.maxDelayMs > 0 ? settings.maxDelayMs : Infinity;
|
|
2555
2555
|
if (message.retryAfterMs > cap) {
|
|
2556
|
-
// Server wants us to wait longer than
|
|
2556
|
+
// Server wants us to wait longer than maxDelayMs — give up to let auto-mode handle recovery
|
|
2557
2557
|
this._emit({
|
|
2558
2558
|
type: "auto_retry_end",
|
|
2559
2559
|
success: false,
|
|
@@ -20,7 +20,7 @@ export interface RetrySettings {
|
|
|
20
20
|
enabled?: boolean; // default: true
|
|
21
21
|
maxRetries?: number; // default: 3
|
|
22
22
|
baseDelayMs?: number; // default: 2000 (exponential backoff: 2s, 4s, 8s)
|
|
23
|
-
maxDelayMs?: number; // default:
|
|
23
|
+
maxDelayMs?: number; // default: 300000 (max server-requested delay before failing)
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
export interface TerminalSettings {
|
|
@@ -752,7 +752,7 @@ export class SettingsManager {
|
|
|
752
752
|
enabled: this.getRetryEnabled(),
|
|
753
753
|
maxRetries: this.settings.retry?.maxRetries ?? 3,
|
|
754
754
|
baseDelayMs: this.settings.retry?.baseDelayMs ?? 2000,
|
|
755
|
-
maxDelayMs: this.settings.retry?.maxDelayMs ??
|
|
755
|
+
maxDelayMs: this.settings.retry?.maxDelayMs ?? 300000,
|
|
756
756
|
};
|
|
757
757
|
}
|
|
758
758
|
|
|
@@ -903,6 +903,24 @@ export class ToolExecutionComponent extends Container {
|
|
|
903
903
|
text += `\n${theme.fg("warning", `[Truncated: ${warnings.join(", ")}]`)}`;
|
|
904
904
|
}
|
|
905
905
|
}
|
|
906
|
+
} else if (this.toolName === "web_search") {
|
|
907
|
+
// Server-side Anthropic web search
|
|
908
|
+
text = theme.fg("toolTitle", theme.bold("web search"));
|
|
909
|
+
|
|
910
|
+
if (this.result) {
|
|
911
|
+
const output = this.getTextOutput().trim();
|
|
912
|
+
if (output) {
|
|
913
|
+
const lines = output.split("\n");
|
|
914
|
+
const maxLines = this.expanded ? lines.length : 10;
|
|
915
|
+
const displayLines = lines.slice(0, maxLines);
|
|
916
|
+
const remaining = lines.length - maxLines;
|
|
917
|
+
|
|
918
|
+
text += `\n\n${displayLines.map((line: string) => theme.fg("toolOutput", line)).join("\n")}`;
|
|
919
|
+
if (remaining > 0) {
|
|
920
|
+
text += `${theme.fg("muted", `\n... (${remaining} more lines,`)} ${keyHint("expandTools", "to expand")})`;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
906
924
|
} else {
|
|
907
925
|
// Generic tool (shouldn't reach here for custom tools)
|
|
908
926
|
text = theme.fg("toolTitle", theme.bold(this.toolName));
|
|
@@ -1165,6 +1165,34 @@ export class InteractiveMode {
|
|
|
1165
1165
|
return registeredTool?.definition;
|
|
1166
1166
|
}
|
|
1167
1167
|
|
|
1168
|
+
/**
|
|
1169
|
+
* Format web search result content for display in the TUI.
|
|
1170
|
+
*/
|
|
1171
|
+
private formatWebSearchResult(content: unknown): string {
|
|
1172
|
+
if (!content) return "Web search completed";
|
|
1173
|
+
|
|
1174
|
+
// Error result
|
|
1175
|
+
if (typeof content === "object" && "type" in (content as any) && (content as any).type === "web_search_tool_result_error") {
|
|
1176
|
+
const error = content as any;
|
|
1177
|
+
return `Search error: ${error.error_code || "unknown"}`;
|
|
1178
|
+
}
|
|
1179
|
+
|
|
1180
|
+
// Array of search results
|
|
1181
|
+
if (Array.isArray(content)) {
|
|
1182
|
+
const results = content.filter((r: any) => r.type === "web_search_result");
|
|
1183
|
+
if (results.length === 0) return "No results found";
|
|
1184
|
+
return results
|
|
1185
|
+
.map((r: any) => {
|
|
1186
|
+
const title = r.title || "Untitled";
|
|
1187
|
+
const url = r.url || "";
|
|
1188
|
+
return `${title}\n ${url}`;
|
|
1189
|
+
})
|
|
1190
|
+
.join("\n");
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
return "Web search completed";
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1168
1196
|
/**
|
|
1169
1197
|
* Set up keyboard shortcuts registered by extensions.
|
|
1170
1198
|
*/
|
|
@@ -2201,6 +2229,35 @@ export class InteractiveMode {
|
|
|
2201
2229
|
component.updateArgs(content.arguments);
|
|
2202
2230
|
}
|
|
2203
2231
|
}
|
|
2232
|
+
} else if (content.type === "serverToolUse") {
|
|
2233
|
+
// Server-side tool (e.g., native web search) — show as pending tool execution
|
|
2234
|
+
if (!this.pendingTools.has(content.id)) {
|
|
2235
|
+
const component = new ToolExecutionComponent(
|
|
2236
|
+
content.name,
|
|
2237
|
+
content.input ?? {},
|
|
2238
|
+
{
|
|
2239
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2240
|
+
},
|
|
2241
|
+
undefined,
|
|
2242
|
+
this.ui,
|
|
2243
|
+
);
|
|
2244
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2245
|
+
this.chatContainer.addChild(component);
|
|
2246
|
+
this.pendingTools.set(content.id, component);
|
|
2247
|
+
}
|
|
2248
|
+
} else if (content.type === "webSearchResult") {
|
|
2249
|
+
// Server-side tool result — resolve the pending server tool execution
|
|
2250
|
+
const component = this.pendingTools.get(content.toolUseId);
|
|
2251
|
+
if (component) {
|
|
2252
|
+
const searchContent = content.content;
|
|
2253
|
+
const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
|
|
2254
|
+
const resultText = this.formatWebSearchResult(searchContent);
|
|
2255
|
+
component.updateResult({
|
|
2256
|
+
content: [{ type: "text", text: resultText }],
|
|
2257
|
+
isError: !!isError,
|
|
2258
|
+
});
|
|
2259
|
+
this.pendingTools.delete(content.toolUseId);
|
|
2260
|
+
}
|
|
2204
2261
|
}
|
|
2205
2262
|
}
|
|
2206
2263
|
this.ui.requestRender();
|
|
@@ -2594,6 +2651,33 @@ export class InteractiveMode {
|
|
|
2594
2651
|
} else {
|
|
2595
2652
|
this.pendingTools.set(content.id, component);
|
|
2596
2653
|
}
|
|
2654
|
+
} else if (content.type === "serverToolUse") {
|
|
2655
|
+
// Server-side tool (e.g., native web search)
|
|
2656
|
+
const component = new ToolExecutionComponent(
|
|
2657
|
+
content.name,
|
|
2658
|
+
content.input ?? {},
|
|
2659
|
+
{ showImages: this.settingsManager.getShowImages() },
|
|
2660
|
+
undefined,
|
|
2661
|
+
this.ui,
|
|
2662
|
+
);
|
|
2663
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2664
|
+
this.chatContainer.addChild(component);
|
|
2665
|
+
// Find matching webSearchResult in this message's content
|
|
2666
|
+
const resultBlock = message.content.find(
|
|
2667
|
+
(c) => c.type === "webSearchResult" && c.toolUseId === content.id,
|
|
2668
|
+
);
|
|
2669
|
+
if (resultBlock && resultBlock.type === "webSearchResult") {
|
|
2670
|
+
const searchContent = resultBlock.content;
|
|
2671
|
+
const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
|
|
2672
|
+
const resultText = this.formatWebSearchResult(searchContent);
|
|
2673
|
+
component.updateResult({
|
|
2674
|
+
content: [{ type: "text", text: resultText }],
|
|
2675
|
+
isError: !!isError,
|
|
2676
|
+
});
|
|
2677
|
+
} else {
|
|
2678
|
+
// No result yet (aborted stream?) — show as pending
|
|
2679
|
+
this.pendingTools.set(content.id, component);
|
|
2680
|
+
}
|
|
2597
2681
|
}
|
|
2598
2682
|
}
|
|
2599
2683
|
} else if (message.role === "toolResult") {
|
package/scripts/postinstall.js
CHANGED
|
@@ -1,125 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
import { exec as execCb } from 'child_process'
|
|
4
|
-
import { createRequire } from 'module'
|
|
5
|
-
import os from 'os'
|
|
6
4
|
import { dirname, resolve } from 'path'
|
|
7
5
|
import { fileURLToPath } from 'url'
|
|
8
6
|
|
|
9
7
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
10
|
-
const require = createRequire(import.meta.url)
|
|
11
|
-
const pkg = require(resolve(__dirname, '..', 'package.json'))
|
|
12
8
|
const cwd = resolve(__dirname, '..')
|
|
13
|
-
const
|
|
9
|
+
const shouldSkip =
|
|
14
10
|
process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === '1' ||
|
|
15
11
|
process.env.PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD === 'true'
|
|
16
12
|
|
|
17
|
-
|
|
18
|
-
// Async exec helper — captures stdout+stderr, never inherits to terminal
|
|
19
|
-
// ---------------------------------------------------------------------------
|
|
20
|
-
function run(cmd, options = {}) {
|
|
13
|
+
function run(cmd) {
|
|
21
14
|
return new Promise((resolve) => {
|
|
22
|
-
execCb(cmd, { cwd
|
|
23
|
-
resolve({ ok: !error, stdout, stderr
|
|
15
|
+
execCb(cmd, { cwd }, (error, stdout, stderr) => {
|
|
16
|
+
resolve({ ok: !error, stdout, stderr })
|
|
24
17
|
})
|
|
25
18
|
})
|
|
26
19
|
}
|
|
27
20
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
// always forwarded. Clack writes to process.stdout, so we reroute it.
|
|
32
|
-
// ---------------------------------------------------------------------------
|
|
33
|
-
process.stdout.write = process.stderr.write.bind(process.stderr)
|
|
34
|
-
|
|
35
|
-
// ---------------------------------------------------------------------------
|
|
36
|
-
// ASCII banner — printed before clack UI for brand recognition
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
const cyan = '\x1b[36m'
|
|
39
|
-
const dim = '\x1b[2m'
|
|
40
|
-
const reset = '\x1b[0m'
|
|
41
|
-
|
|
42
|
-
const banner =
|
|
43
|
-
'\n' +
|
|
44
|
-
cyan +
|
|
45
|
-
' ██████╗ ███████╗██████╗ \n' +
|
|
46
|
-
' ██╔════╝ ██╔════╝██╔══██╗\n' +
|
|
47
|
-
' ██║ ███╗███████╗██║ ██║\n' +
|
|
48
|
-
' ██║ ██║╚════██║██║ ██║\n' +
|
|
49
|
-
' ╚██████╔╝███████║██████╔╝\n' +
|
|
50
|
-
' ╚═════╝ ╚══════╝╚═════╝ ' +
|
|
51
|
-
reset + '\n' +
|
|
52
|
-
'\n' +
|
|
53
|
-
` Get Shit Done ${dim}v${pkg.version}${reset}\n`
|
|
54
|
-
|
|
55
|
-
// ---------------------------------------------------------------------------
|
|
56
|
-
// Main — wrapped in async IIFE, with graceful fallback if clack fails
|
|
57
|
-
// ---------------------------------------------------------------------------
|
|
58
|
-
;(async () => {
|
|
59
|
-
process.stderr.write(banner)
|
|
60
|
-
|
|
61
|
-
let p, pc
|
|
62
|
-
|
|
63
|
-
try {
|
|
64
|
-
p = await import('@clack/prompts')
|
|
65
|
-
pc = (await import('picocolors')).default
|
|
66
|
-
} catch {
|
|
67
|
-
// Clack or picocolors unavailable — fall back to minimal output
|
|
68
|
-
process.stderr.write(` Run gsd to get started.\n\n`)
|
|
69
|
-
if (!shouldSkipBrowserDownload) {
|
|
70
|
-
await run('npx playwright install chromium')
|
|
71
|
-
}
|
|
72
|
-
return
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// --- Branded intro -------------------------------------------------------
|
|
76
|
-
p.intro('Setup')
|
|
77
|
-
|
|
78
|
-
const results = []
|
|
79
|
-
const s = p.spinner()
|
|
80
|
-
|
|
81
|
-
// --- Playwright browser --------------------------------------------------
|
|
82
|
-
// Avoid --with-deps: install scripts should not block on interactive sudo
|
|
83
|
-
// prompts. If Linux libs are missing, suggest the explicit follow-up.
|
|
84
|
-
s.start('Setting up browser tools…')
|
|
85
|
-
if (shouldSkipBrowserDownload) {
|
|
86
|
-
s.stop(pc.yellow('Browser tools skipped via PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD'))
|
|
87
|
-
results.push({
|
|
88
|
-
label: 'Browser tools skipped — run ' + pc.cyan('npx playwright install chromium') + ' later if needed',
|
|
89
|
-
ok: false,
|
|
90
|
-
})
|
|
91
|
-
} else {
|
|
92
|
-
const pwResult = await run('npx playwright install chromium')
|
|
93
|
-
if (pwResult.ok) {
|
|
94
|
-
s.stop('Browser tools ready')
|
|
95
|
-
results.push({ label: 'Browser tools ready', ok: true })
|
|
96
|
-
} else {
|
|
97
|
-
const output = `${pwResult.stdout ?? ''}${pwResult.stderr ?? ''}`
|
|
98
|
-
if (os.platform() === 'linux' && output.includes('Host system is missing dependencies to run browsers.')) {
|
|
99
|
-
s.stop(pc.yellow('Browser downloaded, missing Linux deps'))
|
|
100
|
-
results.push({
|
|
101
|
-
label: 'Run ' + pc.cyan('sudo npx playwright install-deps chromium') + ' to finish setup',
|
|
102
|
-
ok: false,
|
|
103
|
-
})
|
|
104
|
-
} else {
|
|
105
|
-
s.stop(pc.yellow('Browser tools — skipped (non-fatal)'))
|
|
106
|
-
results.push({
|
|
107
|
-
label: 'Browser tools unavailable — run ' + pc.cyan('npx playwright install chromium'),
|
|
108
|
-
ok: false,
|
|
109
|
-
})
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// --- Summary note --------------------------------------------------------
|
|
115
|
-
const lines = results.map(
|
|
116
|
-
(r) => (r.ok ? pc.green('✓') : pc.yellow('⚠')) + ' ' + r.label
|
|
117
|
-
)
|
|
118
|
-
lines.push('')
|
|
119
|
-
lines.push('Run ' + pc.cyan('gsd') + ' to get started.')
|
|
120
|
-
|
|
121
|
-
p.note(lines.join('\n'), 'Installed')
|
|
122
|
-
|
|
123
|
-
// --- Outro ---------------------------------------------------------------
|
|
124
|
-
p.outro(pc.green('Done!'))
|
|
125
|
-
})()
|
|
21
|
+
if (!shouldSkip) {
|
|
22
|
+
await run('npx playwright install chromium')
|
|
23
|
+
}
|
|
@@ -565,25 +565,28 @@ One commit per slice. Individually revertable. Reads like a changelog.
|
|
|
565
565
|
|
|
566
566
|
```
|
|
567
567
|
gsd/M001/S01:
|
|
568
|
-
test(S01): round-trip tests passing
|
|
568
|
+
test(S01/T03): round-trip tests passing
|
|
569
569
|
feat(S01/T03): file writer with round-trip fidelity
|
|
570
|
-
chore(S01/T03): auto-commit after task
|
|
571
570
|
feat(S01/T02): markdown parser for plan files
|
|
572
|
-
chore(S01/T02): auto-commit after task
|
|
573
571
|
feat(S01/T01): core types and interfaces
|
|
574
|
-
|
|
572
|
+
docs(S01): add slice plan
|
|
575
573
|
```
|
|
576
574
|
|
|
577
575
|
### Commit Conventions
|
|
578
576
|
|
|
579
577
|
| When | Format | Example |
|
|
580
578
|
|------|--------|---------|
|
|
581
|
-
|
|
|
582
|
-
|
|
|
583
|
-
|
|
|
584
|
-
|
|
|
579
|
+
| Task completed | `{type}(S01/T02): <one-liner from summary>` | Type inferred from title (`feat`, `fix`, `test`, etc.) |
|
|
580
|
+
| Plan/docs committed | `docs(S01): add slice plan` | Planning artifacts |
|
|
581
|
+
| Slice squash to main | `type(M001/S01): <slice title>` | Type inferred from title |
|
|
582
|
+
| State rebuild | `chore(S01/T02): auto-commit after state-rebuild` | Bookkeeping only |
|
|
585
583
|
|
|
586
|
-
|
|
584
|
+
The system reads the task summary after execution and builds a meaningful commit message:
|
|
585
|
+
- **Subject**: `{type}({sliceId}/{taskId}): {one-liner}` — the one-liner from the summary frontmatter
|
|
586
|
+
- **Type**: Inferred from the task title and one-liner (`feat`, `fix`, `test`, `refactor`, `docs`, `perf`, `chore`)
|
|
587
|
+
- **Body**: Key files from the summary frontmatter (up to 8 files listed)
|
|
588
|
+
|
|
589
|
+
Commit types: `feat`, `fix`, `test`, `refactor`, `docs`, `perf`, `chore`
|
|
587
590
|
|
|
588
591
|
### Squash Merge Message
|
|
589
592
|
|
|
@@ -328,12 +328,9 @@ export class BgManagerOverlay {
|
|
|
328
328
|
return this.box(inner, width);
|
|
329
329
|
}
|
|
330
330
|
|
|
331
|
-
private
|
|
331
|
+
private processStatusHeader(p: typeof this.viewingProcess, activeTab: "output" | "events"): { statusIcon: string; headerLine: string } {
|
|
332
332
|
const th = this.theme;
|
|
333
|
-
|
|
334
|
-
if (!p) return [""];
|
|
335
|
-
const inner: string[] = [];
|
|
336
|
-
|
|
333
|
+
if (!p) return { statusIcon: "", headerLine: "" };
|
|
337
334
|
const statusIcon = p.alive
|
|
338
335
|
? (p.status === "ready" ? th.fg("success", "●")
|
|
339
336
|
: p.status === "error" ? th.fg("error", "●")
|
|
@@ -343,9 +340,21 @@ export class BgManagerOverlay {
|
|
|
343
340
|
const uptime = th.fg("dim", formatUptime(Date.now() - p.startedAt));
|
|
344
341
|
const typeTag = th.fg("dim", `[${p.processType}]`);
|
|
345
342
|
const portInfo = p.ports.length > 0 ? th.fg("dim", ` :${p.ports.join(",")}`) : "";
|
|
346
|
-
const tabIndicator =
|
|
343
|
+
const tabIndicator = activeTab === "output"
|
|
344
|
+
? th.fg("accent", "[Output]") + " " + th.fg("dim", "Events")
|
|
345
|
+
: th.fg("dim", "Output") + " " + th.fg("accent", "[Events]");
|
|
346
|
+
const headerLine = `${statusIcon} ${name} ${typeTag} ${uptime}${portInfo} ${tabIndicator}`;
|
|
347
|
+
return { statusIcon, headerLine };
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
private renderOutput(width: number): string[] {
|
|
351
|
+
const th = this.theme;
|
|
352
|
+
const p = this.viewingProcess;
|
|
353
|
+
if (!p) return [""];
|
|
354
|
+
const inner: string[] = [];
|
|
347
355
|
|
|
348
|
-
|
|
356
|
+
const { headerLine } = this.processStatusHeader(p, "output");
|
|
357
|
+
inner.push(headerLine);
|
|
349
358
|
inner.push("");
|
|
350
359
|
|
|
351
360
|
// Unified buffer is already chronologically interleaved
|
|
@@ -384,16 +393,8 @@ export class BgManagerOverlay {
|
|
|
384
393
|
if (!p) return [""];
|
|
385
394
|
const inner: string[] = [];
|
|
386
395
|
|
|
387
|
-
const
|
|
388
|
-
|
|
389
|
-
: p.status === "error" ? th.fg("error", "●")
|
|
390
|
-
: th.fg("warning", "●"))
|
|
391
|
-
: th.fg("dim", "○");
|
|
392
|
-
const name = th.fg("muted", p.label);
|
|
393
|
-
const uptime = th.fg("dim", formatUptime(Date.now() - p.startedAt));
|
|
394
|
-
const tabIndicator = th.fg("dim", "Output") + " " + th.fg("accent", "[Events]");
|
|
395
|
-
|
|
396
|
-
inner.push(`${statusIcon} ${name} ${uptime} ${tabIndicator}`);
|
|
396
|
+
const { headerLine } = this.processStatusHeader(p, "events");
|
|
397
|
+
inner.push(headerLine);
|
|
397
398
|
inner.push("");
|
|
398
399
|
|
|
399
400
|
if (p.events.length === 0) {
|
|
@@ -369,32 +369,14 @@ async function applySecrets(
|
|
|
369
369
|
}
|
|
370
370
|
}
|
|
371
371
|
|
|
372
|
-
if (destination === "vercel" && opts.exec) {
|
|
372
|
+
if ((destination === "vercel" || destination === "convex") && opts.exec) {
|
|
373
373
|
const env = opts.environment ?? "development";
|
|
374
374
|
for (const { key, value } of provided) {
|
|
375
|
+
const cmd = destination === "vercel"
|
|
376
|
+
? `printf %s ${shellEscapeSingle(value)} | vercel env add ${key} ${env}`
|
|
377
|
+
: `npx convex env set ${key} ${shellEscapeSingle(value)}`;
|
|
375
378
|
try {
|
|
376
|
-
const result = await opts.exec("sh", [
|
|
377
|
-
"-c",
|
|
378
|
-
`printf %s ${shellEscapeSingle(value)} | vercel env add ${key} ${env}`,
|
|
379
|
-
]);
|
|
380
|
-
if (result.code !== 0) {
|
|
381
|
-
errors.push(`${key}: ${result.stderr.slice(0, 200)}`);
|
|
382
|
-
} else {
|
|
383
|
-
applied.push(key);
|
|
384
|
-
}
|
|
385
|
-
} catch (err: any) {
|
|
386
|
-
errors.push(`${key}: ${err.message}`);
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
if (destination === "convex" && opts.exec) {
|
|
392
|
-
for (const { key, value } of provided) {
|
|
393
|
-
try {
|
|
394
|
-
const result = await opts.exec("sh", [
|
|
395
|
-
"-c",
|
|
396
|
-
`npx convex env set ${key} ${shellEscapeSingle(value)}`,
|
|
397
|
-
]);
|
|
379
|
+
const result = await opts.exec("sh", ["-c", cmd]);
|
|
398
380
|
if (result.code !== 0) {
|
|
399
381
|
errors.push(`${key}: ${result.stderr.slice(0, 200)}`);
|
|
400
382
|
} else {
|
|
@@ -103,10 +103,10 @@ export function saveActivityLog(
|
|
|
103
103
|
basePath: string,
|
|
104
104
|
unitType: string,
|
|
105
105
|
unitId: string,
|
|
106
|
-
):
|
|
106
|
+
): string | null {
|
|
107
107
|
try {
|
|
108
108
|
const entries = ctx.sessionManager.getEntries();
|
|
109
|
-
if (!entries || entries.length === 0) return;
|
|
109
|
+
if (!entries || entries.length === 0) return null;
|
|
110
110
|
|
|
111
111
|
const activityDir = join(gsdRoot(basePath), "activity");
|
|
112
112
|
mkdirSync(activityDir, { recursive: true });
|
|
@@ -116,7 +116,7 @@ export function saveActivityLog(
|
|
|
116
116
|
const unitKey = `${unitType}\0${safeUnitId}`;
|
|
117
117
|
// Use lightweight fingerprint instead of serializing all entries (#611)
|
|
118
118
|
const key = snapshotKey(unitType, safeUnitId, entries);
|
|
119
|
-
if (state.lastSnapshotKeyByUnit.get(unitKey) === key) return;
|
|
119
|
+
if (state.lastSnapshotKeyByUnit.get(unitKey) === key) return null;
|
|
120
120
|
|
|
121
121
|
const filePath = nextActivityFilePath(activityDir, state, unitType, safeUnitId);
|
|
122
122
|
// Stream entries to disk line-by-line instead of building one massive string (#611).
|
|
@@ -131,9 +131,11 @@ export function saveActivityLog(
|
|
|
131
131
|
}
|
|
132
132
|
state.nextSeq += 1;
|
|
133
133
|
state.lastSnapshotKeyByUnit.set(unitKey, key);
|
|
134
|
+
return filePath;
|
|
134
135
|
} catch (e) {
|
|
135
136
|
// Don't let logging failures break auto-mode
|
|
136
137
|
void e;
|
|
138
|
+
return null;
|
|
137
139
|
}
|
|
138
140
|
}
|
|
139
141
|
|
|
@@ -14,9 +14,11 @@ import type { GSDPreferences } from "./preferences.js";
|
|
|
14
14
|
import type { UatType } from "./files.js";
|
|
15
15
|
import { loadFile, extractUatType, loadActiveOverrides } from "./files.js";
|
|
16
16
|
import {
|
|
17
|
-
resolveMilestoneFile, resolveSliceFile,
|
|
18
|
-
relSliceFile,
|
|
17
|
+
resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveTaskFile,
|
|
18
|
+
relSliceFile, buildMilestoneFileName,
|
|
19
19
|
} from "./paths.js";
|
|
20
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
21
|
+
import { join } from "node:path";
|
|
20
22
|
import {
|
|
21
23
|
buildResearchMilestonePrompt,
|
|
22
24
|
buildPlanMilestonePrompt,
|
|
@@ -25,6 +27,7 @@ import {
|
|
|
25
27
|
buildExecuteTaskPrompt,
|
|
26
28
|
buildCompleteSlicePrompt,
|
|
27
29
|
buildCompleteMilestonePrompt,
|
|
30
|
+
buildValidateMilestonePrompt,
|
|
28
31
|
buildReplanSlicePrompt,
|
|
29
32
|
buildRunUatPrompt,
|
|
30
33
|
buildReassessRoadmapPrompt,
|
|
@@ -246,6 +249,20 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
246
249
|
const sTitle = state.activeSlice!.title;
|
|
247
250
|
const tid = state.activeTask.id;
|
|
248
251
|
const tTitle = state.activeTask.title;
|
|
252
|
+
|
|
253
|
+
// Guard: refuse to dispatch execute-task when the task plan file is missing.
|
|
254
|
+
// This prevents the agent from running blind after a failed plan-slice that
|
|
255
|
+
// wrote S{sid}-PLAN.md but omitted the individual T{tid}-PLAN.md files.
|
|
256
|
+
// (See issue #739 — missing task plan caused runaway execution and EPIPE crash.)
|
|
257
|
+
const taskPlanPath = resolveTaskFile(basePath, mid, sid, tid, "PLAN");
|
|
258
|
+
if (!taskPlanPath || !existsSync(taskPlanPath)) {
|
|
259
|
+
return {
|
|
260
|
+
action: "stop",
|
|
261
|
+
reason: `Task plan ${tid}-PLAN.md is missing for ${mid}/${sid}/${tid}. Re-run plan-slice to regenerate task plans, or create the file manually and resume.`,
|
|
262
|
+
level: "error",
|
|
263
|
+
};
|
|
264
|
+
}
|
|
265
|
+
|
|
249
266
|
return {
|
|
250
267
|
action: "dispatch",
|
|
251
268
|
unitType: "execute-task",
|
|
@@ -254,6 +271,38 @@ const DISPATCH_RULES: DispatchRule[] = [
|
|
|
254
271
|
};
|
|
255
272
|
},
|
|
256
273
|
},
|
|
274
|
+
{
|
|
275
|
+
name: "validating-milestone → validate-milestone",
|
|
276
|
+
match: async ({ state, mid, midTitle, basePath, prefs }) => {
|
|
277
|
+
if (state.phase !== "validating-milestone") return null;
|
|
278
|
+
// Skip preference: write a minimal pass-through VALIDATION file
|
|
279
|
+
if (prefs?.phases?.skip_milestone_validation) {
|
|
280
|
+
const mDir = resolveMilestonePath(basePath, mid);
|
|
281
|
+
if (mDir) {
|
|
282
|
+
if (!existsSync(mDir)) mkdirSync(mDir, { recursive: true });
|
|
283
|
+
const validationPath = join(mDir, buildMilestoneFileName(mid, "VALIDATION"));
|
|
284
|
+
const content = [
|
|
285
|
+
"---",
|
|
286
|
+
"verdict: pass",
|
|
287
|
+
"remediation_round: 0",
|
|
288
|
+
"---",
|
|
289
|
+
"",
|
|
290
|
+
"# Milestone Validation (skipped by preference)",
|
|
291
|
+
"",
|
|
292
|
+
"Milestone validation was skipped via `skip_milestone_validation` preference.",
|
|
293
|
+
].join("\n");
|
|
294
|
+
writeFileSync(validationPath, content, "utf-8");
|
|
295
|
+
}
|
|
296
|
+
return { action: "skip" };
|
|
297
|
+
}
|
|
298
|
+
return {
|
|
299
|
+
action: "dispatch",
|
|
300
|
+
unitType: "validate-milestone",
|
|
301
|
+
unitId: mid,
|
|
302
|
+
prompt: await buildValidateMilestonePrompt(mid, midTitle, basePath),
|
|
303
|
+
};
|
|
304
|
+
},
|
|
305
|
+
},
|
|
257
306
|
{
|
|
258
307
|
name: "completing-milestone → complete-milestone",
|
|
259
308
|
match: async ({ state, mid, midTitle, basePath }) => {
|
|
@@ -637,6 +637,12 @@ export async function buildPlanSlicePrompt(
|
|
|
637
637
|
const executorContextConstraints = formatExecutorConstraints();
|
|
638
638
|
|
|
639
639
|
const outputRelPath = relSliceFile(base, mid, sid, "PLAN");
|
|
640
|
+
const prefs = loadEffectiveGSDPreferences();
|
|
641
|
+
const commitDocsEnabled = prefs?.preferences?.git?.commit_docs !== false;
|
|
642
|
+
const commitInstruction = commitDocsEnabled
|
|
643
|
+
? `Commit: \`docs(${sid}): add slice plan\``
|
|
644
|
+
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
645
|
+
|
|
640
646
|
return loadPrompt("plan-slice", {
|
|
641
647
|
workingDirectory: base,
|
|
642
648
|
milestoneId: mid, sliceId: sid, sliceTitle: sTitle,
|
|
@@ -647,6 +653,7 @@ export async function buildPlanSlicePrompt(
|
|
|
647
653
|
inlinedContext,
|
|
648
654
|
dependencySummaries: depContent,
|
|
649
655
|
executorContextConstraints,
|
|
656
|
+
commitInstruction,
|
|
650
657
|
});
|
|
651
658
|
}
|
|
652
659
|
|
|
@@ -855,6 +862,79 @@ export async function buildCompleteMilestonePrompt(
|
|
|
855
862
|
});
|
|
856
863
|
}
|
|
857
864
|
|
|
865
|
+
export async function buildValidateMilestonePrompt(
|
|
866
|
+
mid: string, midTitle: string, base: string, level?: InlineLevel,
|
|
867
|
+
): Promise<string> {
|
|
868
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
869
|
+
const roadmapPath = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
870
|
+
const roadmapRel = relMilestoneFile(base, mid, "ROADMAP");
|
|
871
|
+
|
|
872
|
+
const inlined: string[] = [];
|
|
873
|
+
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
874
|
+
|
|
875
|
+
// Inline all slice summaries and UAT results
|
|
876
|
+
const roadmapContent = roadmapPath ? await loadFile(roadmapPath) : null;
|
|
877
|
+
if (roadmapContent) {
|
|
878
|
+
const roadmap = parseRoadmap(roadmapContent);
|
|
879
|
+
const seenSlices = new Set<string>();
|
|
880
|
+
for (const slice of roadmap.slices) {
|
|
881
|
+
if (seenSlices.has(slice.id)) continue;
|
|
882
|
+
seenSlices.add(slice.id);
|
|
883
|
+
const summaryPath = resolveSliceFile(base, mid, slice.id, "SUMMARY");
|
|
884
|
+
const summaryRel = relSliceFile(base, mid, slice.id, "SUMMARY");
|
|
885
|
+
inlined.push(await inlineFile(summaryPath, summaryRel, `${slice.id} Summary`));
|
|
886
|
+
|
|
887
|
+
const uatPath = resolveSliceFile(base, mid, slice.id, "UAT-RESULT");
|
|
888
|
+
const uatRel = relSliceFile(base, mid, slice.id, "UAT-RESULT");
|
|
889
|
+
const uatInline = await inlineFileOptional(uatPath, uatRel, `${slice.id} UAT Result`);
|
|
890
|
+
if (uatInline) inlined.push(uatInline);
|
|
891
|
+
}
|
|
892
|
+
}
|
|
893
|
+
|
|
894
|
+
// Inline existing VALIDATION file if this is a re-validation round
|
|
895
|
+
const validationPath = resolveMilestoneFile(base, mid, "VALIDATION");
|
|
896
|
+
const validationRel = relMilestoneFile(base, mid, "VALIDATION");
|
|
897
|
+
const validationContent = validationPath ? await loadFile(validationPath) : null;
|
|
898
|
+
let remediationRound = 0;
|
|
899
|
+
if (validationContent) {
|
|
900
|
+
const roundMatch = validationContent.match(/remediation_round:\s*(\d+)/);
|
|
901
|
+
remediationRound = roundMatch ? parseInt(roundMatch[1], 10) + 1 : 1;
|
|
902
|
+
inlined.push(`### Previous Validation (re-validation round ${remediationRound})\nSource: \`${validationRel}\`\n\n${validationContent.trim()}`);
|
|
903
|
+
}
|
|
904
|
+
|
|
905
|
+
// Inline root GSD files
|
|
906
|
+
if (inlineLevel !== "minimal") {
|
|
907
|
+
const requirementsInline = await inlineRequirementsFromDb(base);
|
|
908
|
+
if (requirementsInline) inlined.push(requirementsInline);
|
|
909
|
+
const decisionsInline = await inlineDecisionsFromDb(base, mid);
|
|
910
|
+
if (decisionsInline) inlined.push(decisionsInline);
|
|
911
|
+
const projectInline = await inlineProjectFromDb(base);
|
|
912
|
+
if (projectInline) inlined.push(projectInline);
|
|
913
|
+
}
|
|
914
|
+
const knowledgeInline = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
915
|
+
if (knowledgeInline) inlined.push(knowledgeInline);
|
|
916
|
+
// Inline milestone context file
|
|
917
|
+
const contextPath = resolveMilestoneFile(base, mid, "CONTEXT");
|
|
918
|
+
const contextRel = relMilestoneFile(base, mid, "CONTEXT");
|
|
919
|
+
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
920
|
+
if (contextInline) inlined.push(contextInline);
|
|
921
|
+
|
|
922
|
+
const inlinedContext = `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
923
|
+
|
|
924
|
+
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
925
|
+
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
926
|
+
|
|
927
|
+
return loadPrompt("validate-milestone", {
|
|
928
|
+
workingDirectory: base,
|
|
929
|
+
milestoneId: mid,
|
|
930
|
+
milestoneTitle: midTitle,
|
|
931
|
+
roadmapPath: roadmapOutputPath,
|
|
932
|
+
inlinedContext,
|
|
933
|
+
validationPath: validationOutputPath,
|
|
934
|
+
remediationRound: String(remediationRound),
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
858
938
|
export async function buildReplanSlicePrompt(
|
|
859
939
|
mid: string, midTitle: string, sid: string, sTitle: string, base: string,
|
|
860
940
|
): Promise<string> {
|
|
@@ -998,6 +1078,12 @@ export async function buildReassessRoadmapPrompt(
|
|
|
998
1078
|
// Non-fatal — captures module may not be available
|
|
999
1079
|
}
|
|
1000
1080
|
|
|
1081
|
+
const reassessPrefs = loadEffectiveGSDPreferences();
|
|
1082
|
+
const reassessCommitDocsEnabled = reassessPrefs?.preferences?.git?.commit_docs !== false;
|
|
1083
|
+
const reassessCommitInstruction = reassessCommitDocsEnabled
|
|
1084
|
+
? `Commit: \`docs(${mid}): reassess roadmap after ${completedSliceId}\``
|
|
1085
|
+
: "Do not commit — planning docs are not tracked in git for this project.";
|
|
1086
|
+
|
|
1001
1087
|
return loadPrompt("reassess-roadmap", {
|
|
1002
1088
|
workingDirectory: base,
|
|
1003
1089
|
milestoneId: mid,
|
|
@@ -1008,6 +1094,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1008
1094
|
assessmentPath,
|
|
1009
1095
|
inlinedContext,
|
|
1010
1096
|
deferredCaptures,
|
|
1097
|
+
commitInstruction: reassessCommitInstruction,
|
|
1011
1098
|
});
|
|
1012
1099
|
}
|
|
1013
1100
|
|