gsd-pi 2.24.0 → 2.26.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 +13 -3
- package/dist/headless.js +24 -4
- 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 +0 -9
- package/dist/resources/GSD-WORKFLOW.md +12 -9
- package/dist/resources/extensions/async-jobs/index.ts +9 -1
- package/dist/resources/extensions/bg-shell/index.ts +3 -2
- 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-prompts.ts +14 -0
- package/dist/resources/extensions/gsd/auto-recovery.ts +7 -4
- package/dist/resources/extensions/gsd/auto-worktree.ts +132 -3
- package/dist/resources/extensions/gsd/auto.ts +265 -48
- package/dist/resources/extensions/gsd/cache.ts +3 -1
- package/dist/resources/extensions/gsd/doctor-proactive.ts +7 -6
- package/dist/resources/extensions/gsd/doctor.ts +26 -1
- package/dist/resources/extensions/gsd/files.ts +13 -2
- package/dist/resources/extensions/gsd/git-service.ts +74 -14
- package/dist/resources/extensions/gsd/gsd-db.ts +78 -1
- package/dist/resources/extensions/gsd/guided-flow.ts +54 -22
- package/dist/resources/extensions/gsd/index.ts +62 -8
- 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/migrate/writer.ts +39 -0
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
- package/dist/resources/extensions/gsd/preferences.ts +2 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
- package/dist/resources/extensions/gsd/prompts/discuss.md +5 -5
- 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 +3 -3
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/roadmap-slices.ts +45 -1
- package/dist/resources/extensions/gsd/state.ts +17 -6
- package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
- package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/dist/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
- package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
- 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/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 +13 -7
- package/dist/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
- package/dist/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
- 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/triage-ui.ts +1 -1
- package/dist/resources/extensions/gsd/types.ts +2 -0
- 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 +19 -5
- package/dist/resources/extensions/shared/path-display.ts +19 -0
- package/package.json +1 -6
- 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 +64 -0
- package/packages/pi-ai/dist/providers/anthropic.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/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 +65 -1
- package/packages/pi-ai/src/providers/mistral.ts +3 -0
- package/packages/pi-ai/src/types.ts +19 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +7 -0
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.js +32 -0
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js +1 -1
- package/packages/pi-coding-agent/dist/core/keybindings.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js +12 -1
- package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/index.js +7 -0
- package/packages/pi-coding-agent/dist/core/lsp/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.d.ts +2 -2
- package/packages/pi-coding-agent/dist/core/sdk.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/sdk.js +8 -3
- package/packages/pi-coding-agent/dist/core/sdk.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts +3 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/settings-manager.js +8 -0
- package/packages/pi-coding-agent/dist/core/settings-manager.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/slash-commands.js +1 -0
- package/packages/pi-coding-agent/dist/core/slash-commands.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js +2 -1
- package/packages/pi-coding-agent/dist/core/system-prompt.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +2 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +5 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts +41 -3
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.js +301 -62
- package/packages/pi-coding-agent/dist/modes/interactive/components/model-selector.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 +5 -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 +135 -30
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts +8 -0
- package/packages/pi-coding-agent/dist/tests/path-display.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/tests/path-display.test.js +60 -0
- package/packages/pi-coding-agent/dist/tests/path-display.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/utils/clipboard-image.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/utils/clipboard-image.js +32 -6
- package/packages/pi-coding-agent/dist/utils/clipboard-image.js.map +1 -1
- package/packages/pi-coding-agent/dist/utils/path-display.d.ts +34 -0
- package/packages/pi-coding-agent/dist/utils/path-display.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/utils/path-display.js +36 -0
- package/packages/pi-coding-agent/dist/utils/path-display.js.map +1 -0
- package/packages/pi-coding-agent/src/core/agent-session.ts +36 -0
- package/packages/pi-coding-agent/src/core/keybindings.ts +1 -1
- package/packages/pi-coding-agent/src/core/lsp/client.ts +11 -1
- package/packages/pi-coding-agent/src/core/lsp/index.ts +7 -0
- package/packages/pi-coding-agent/src/core/sdk.ts +17 -1
- package/packages/pi-coding-agent/src/core/settings-manager.ts +11 -0
- package/packages/pi-coding-agent/src/core/slash-commands.ts +1 -0
- package/packages/pi-coding-agent/src/core/system-prompt.ts +2 -1
- package/packages/pi-coding-agent/src/index.ts +15 -0
- package/packages/pi-coding-agent/src/modes/interactive/components/model-selector.ts +347 -62
- 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 +124 -4
- package/packages/pi-coding-agent/src/tests/path-display.test.ts +85 -0
- package/packages/pi-coding-agent/src/utils/clipboard-image.ts +33 -6
- package/packages/pi-coding-agent/src/utils/path-display.ts +36 -0
- package/src/resources/GSD-WORKFLOW.md +12 -9
- package/src/resources/extensions/async-jobs/index.ts +9 -1
- package/src/resources/extensions/bg-shell/index.ts +3 -2
- 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-prompts.ts +14 -0
- package/src/resources/extensions/gsd/auto-recovery.ts +7 -4
- package/src/resources/extensions/gsd/auto-worktree.ts +132 -3
- package/src/resources/extensions/gsd/auto.ts +265 -48
- package/src/resources/extensions/gsd/cache.ts +3 -1
- package/src/resources/extensions/gsd/doctor-proactive.ts +7 -6
- package/src/resources/extensions/gsd/doctor.ts +26 -1
- package/src/resources/extensions/gsd/files.ts +13 -2
- package/src/resources/extensions/gsd/git-service.ts +74 -14
- package/src/resources/extensions/gsd/gsd-db.ts +78 -1
- package/src/resources/extensions/gsd/guided-flow.ts +54 -22
- package/src/resources/extensions/gsd/index.ts +62 -8
- 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/migrate/writer.ts +39 -0
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +122 -4
- package/src/resources/extensions/gsd/preferences.ts +2 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -4
- package/src/resources/extensions/gsd/prompts/discuss.md +5 -5
- 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 +3 -3
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/roadmap-slices.ts +45 -1
- package/src/resources/extensions/gsd/state.ts +17 -6
- package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +54 -0
- package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +58 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +70 -0
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +23 -3
- 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/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 +13 -7
- package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/smart-entry-draft.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +8 -4
- 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/triage-ui.ts +1 -1
- package/src/resources/extensions/gsd/types.ts +2 -0
- 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 +19 -5
- package/src/resources/extensions/shared/path-display.ts +19 -0
|
@@ -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
|
*/
|
|
@@ -2035,6 +2063,12 @@ export class InteractiveMode {
|
|
|
2035
2063
|
this.handleThinkingCommand(arg);
|
|
2036
2064
|
return;
|
|
2037
2065
|
}
|
|
2066
|
+
if (text === "/edit-mode" || text.startsWith("/edit-mode ")) {
|
|
2067
|
+
const arg = text.startsWith("/edit-mode ") ? text.slice(11).trim() : undefined;
|
|
2068
|
+
this.editor.setText("");
|
|
2069
|
+
this.handleEditModeCommand(arg);
|
|
2070
|
+
return;
|
|
2071
|
+
}
|
|
2038
2072
|
if (text === "/debug") {
|
|
2039
2073
|
this.handleDebugCommand();
|
|
2040
2074
|
this.editor.setText("");
|
|
@@ -2201,6 +2235,35 @@ export class InteractiveMode {
|
|
|
2201
2235
|
component.updateArgs(content.arguments);
|
|
2202
2236
|
}
|
|
2203
2237
|
}
|
|
2238
|
+
} else if (content.type === "serverToolUse") {
|
|
2239
|
+
// Server-side tool (e.g., native web search) — show as pending tool execution
|
|
2240
|
+
if (!this.pendingTools.has(content.id)) {
|
|
2241
|
+
const component = new ToolExecutionComponent(
|
|
2242
|
+
content.name,
|
|
2243
|
+
content.input ?? {},
|
|
2244
|
+
{
|
|
2245
|
+
showImages: this.settingsManager.getShowImages(),
|
|
2246
|
+
},
|
|
2247
|
+
undefined,
|
|
2248
|
+
this.ui,
|
|
2249
|
+
);
|
|
2250
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2251
|
+
this.chatContainer.addChild(component);
|
|
2252
|
+
this.pendingTools.set(content.id, component);
|
|
2253
|
+
}
|
|
2254
|
+
} else if (content.type === "webSearchResult") {
|
|
2255
|
+
// Server-side tool result — resolve the pending server tool execution
|
|
2256
|
+
const component = this.pendingTools.get(content.toolUseId);
|
|
2257
|
+
if (component) {
|
|
2258
|
+
const searchContent = content.content;
|
|
2259
|
+
const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
|
|
2260
|
+
const resultText = this.formatWebSearchResult(searchContent);
|
|
2261
|
+
component.updateResult({
|
|
2262
|
+
content: [{ type: "text", text: resultText }],
|
|
2263
|
+
isError: !!isError,
|
|
2264
|
+
});
|
|
2265
|
+
this.pendingTools.delete(content.toolUseId);
|
|
2266
|
+
}
|
|
2204
2267
|
}
|
|
2205
2268
|
}
|
|
2206
2269
|
this.ui.requestRender();
|
|
@@ -2594,6 +2657,33 @@ export class InteractiveMode {
|
|
|
2594
2657
|
} else {
|
|
2595
2658
|
this.pendingTools.set(content.id, component);
|
|
2596
2659
|
}
|
|
2660
|
+
} else if (content.type === "serverToolUse") {
|
|
2661
|
+
// Server-side tool (e.g., native web search)
|
|
2662
|
+
const component = new ToolExecutionComponent(
|
|
2663
|
+
content.name,
|
|
2664
|
+
content.input ?? {},
|
|
2665
|
+
{ showImages: this.settingsManager.getShowImages() },
|
|
2666
|
+
undefined,
|
|
2667
|
+
this.ui,
|
|
2668
|
+
);
|
|
2669
|
+
component.setExpanded(this.toolOutputExpanded);
|
|
2670
|
+
this.chatContainer.addChild(component);
|
|
2671
|
+
// Find matching webSearchResult in this message's content
|
|
2672
|
+
const resultBlock = message.content.find(
|
|
2673
|
+
(c) => c.type === "webSearchResult" && c.toolUseId === content.id,
|
|
2674
|
+
);
|
|
2675
|
+
if (resultBlock && resultBlock.type === "webSearchResult") {
|
|
2676
|
+
const searchContent = resultBlock.content;
|
|
2677
|
+
const isError = searchContent && typeof searchContent === "object" && "type" in (searchContent as any) && (searchContent as any).type === "web_search_tool_result_error";
|
|
2678
|
+
const resultText = this.formatWebSearchResult(searchContent);
|
|
2679
|
+
component.updateResult({
|
|
2680
|
+
content: [{ type: "text", text: resultText }],
|
|
2681
|
+
isError: !!isError,
|
|
2682
|
+
});
|
|
2683
|
+
} else {
|
|
2684
|
+
// No result yet (aborted stream?) — show as pending
|
|
2685
|
+
this.pendingTools.set(content.id, component);
|
|
2686
|
+
}
|
|
2597
2687
|
}
|
|
2598
2688
|
}
|
|
2599
2689
|
} else if (message.role === "toolResult") {
|
|
@@ -2807,6 +2897,27 @@ export class InteractiveMode {
|
|
|
2807
2897
|
this.showThinkingSelector();
|
|
2808
2898
|
}
|
|
2809
2899
|
|
|
2900
|
+
private handleEditModeCommand(arg?: string): void {
|
|
2901
|
+
const modes = ["standard", "hashline"] as const;
|
|
2902
|
+
|
|
2903
|
+
if (arg) {
|
|
2904
|
+
const mode = arg.toLowerCase();
|
|
2905
|
+
if (!modes.includes(mode as typeof modes[number])) {
|
|
2906
|
+
this.showStatus(`Invalid edit mode "${arg}". Available: standard, hashline`);
|
|
2907
|
+
return;
|
|
2908
|
+
}
|
|
2909
|
+
this.session.setEditMode(mode as "standard" | "hashline");
|
|
2910
|
+
this.showStatus(`Edit mode: ${mode}${mode === "hashline" ? " (LINE#ID anchored edits)" : " (text-match edits)"}`);
|
|
2911
|
+
return;
|
|
2912
|
+
}
|
|
2913
|
+
|
|
2914
|
+
// Toggle
|
|
2915
|
+
const current = this.session.editMode;
|
|
2916
|
+
const next = current === "standard" ? "hashline" : "standard";
|
|
2917
|
+
this.session.setEditMode(next);
|
|
2918
|
+
this.showStatus(`Edit mode: ${next}${next === "hashline" ? " (LINE#ID anchored edits)" : " (text-match edits)"}`);
|
|
2919
|
+
}
|
|
2920
|
+
|
|
2810
2921
|
private showThinkingSelector(): void {
|
|
2811
2922
|
const availableLevels = this.session.getAvailableThinkingLevels();
|
|
2812
2923
|
this.showSelector((done) => {
|
|
@@ -3799,12 +3910,16 @@ export class InteractiveMode {
|
|
|
3799
3910
|
const selector = new OAuthSelectorComponent(
|
|
3800
3911
|
mode,
|
|
3801
3912
|
this.session.modelRegistry.authStorage,
|
|
3802
|
-
|
|
3913
|
+
(providerId: string) => {
|
|
3803
3914
|
done();
|
|
3804
3915
|
|
|
3805
|
-
|
|
3806
|
-
|
|
3807
|
-
|
|
3916
|
+
// OAuthSelectorComponent calls this synchronously (no await),
|
|
3917
|
+
// so we must catch async errors here to prevent unhandled rejections
|
|
3918
|
+
// when the user cancels the login dialog (#821).
|
|
3919
|
+
const handleAsync = async () => {
|
|
3920
|
+
if (mode === "login") {
|
|
3921
|
+
await this.showLoginDialog(providerId);
|
|
3922
|
+
} else {
|
|
3808
3923
|
// Logout flow
|
|
3809
3924
|
const providerInfo = this.session.modelRegistry.authStorage
|
|
3810
3925
|
.getOAuthProviders()
|
|
@@ -3835,6 +3950,11 @@ export class InteractiveMode {
|
|
|
3835
3950
|
this.showError(`Logout failed: ${error instanceof Error ? error.message : String(error)}`);
|
|
3836
3951
|
}
|
|
3837
3952
|
}
|
|
3953
|
+
};
|
|
3954
|
+
handleAsync().catch(() => {
|
|
3955
|
+
// Swallow — showLoginDialog already handles its own errors.
|
|
3956
|
+
// This prevents unhandled rejections when login is cancelled.
|
|
3957
|
+
});
|
|
3838
3958
|
},
|
|
3839
3959
|
() => {
|
|
3840
3960
|
done();
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform path display tests.
|
|
3
|
+
*
|
|
4
|
+
* Verifies that toPosixPath correctly normalizes Windows paths and that
|
|
5
|
+
* the system prompt builder produces forward-slash paths for LLM consumption.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import test from "node:test";
|
|
9
|
+
import assert from "node:assert/strict";
|
|
10
|
+
import { toPosixPath } from "../utils/path-display.js";
|
|
11
|
+
import { buildSystemPrompt } from "../core/system-prompt.js";
|
|
12
|
+
|
|
13
|
+
// ─── toPosixPath ────────────────────────────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
test("toPosixPath: converts Windows backslash paths to forward slashes", () => {
|
|
16
|
+
assert.equal(toPosixPath("C:\\Users\\name\\project"), "C:/Users/name/project");
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
test("toPosixPath: handles mixed separators", () => {
|
|
20
|
+
assert.equal(toPosixPath("C:\\Users/name\\project/src"), "C:/Users/name/project/src");
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test("toPosixPath: no-op for Unix paths", () => {
|
|
24
|
+
assert.equal(toPosixPath("/home/user/project"), "/home/user/project");
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test("toPosixPath: handles empty string", () => {
|
|
28
|
+
assert.equal(toPosixPath(""), "");
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
test("toPosixPath: handles Windows UNC paths", () => {
|
|
32
|
+
assert.equal(toPosixPath("\\\\server\\share\\dir"), "//server/share/dir");
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
test("toPosixPath: handles .gsd/worktrees path on Windows", () => {
|
|
36
|
+
assert.equal(
|
|
37
|
+
toPosixPath("C:\\Users\\name\\project\\.gsd\\worktrees\\M001"),
|
|
38
|
+
"C:/Users/name/project/.gsd/worktrees/M001",
|
|
39
|
+
);
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
// ─── System prompt path normalization ───────────────────────────────────────
|
|
43
|
+
|
|
44
|
+
test("buildSystemPrompt: cwd uses forward slashes even with Windows input", () => {
|
|
45
|
+
const prompt = buildSystemPrompt({
|
|
46
|
+
cwd: "C:\\Users\\name\\development\\app-name",
|
|
47
|
+
});
|
|
48
|
+
assert.ok(
|
|
49
|
+
prompt.includes("C:/Users/name/development/app-name"),
|
|
50
|
+
"System prompt should contain forward-slash path",
|
|
51
|
+
);
|
|
52
|
+
assert.ok(
|
|
53
|
+
!prompt.includes("C:\\Users\\name\\development\\app-name"),
|
|
54
|
+
"System prompt must NOT contain backslash path",
|
|
55
|
+
);
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test("buildSystemPrompt: Unix paths pass through unchanged", () => {
|
|
59
|
+
const prompt = buildSystemPrompt({
|
|
60
|
+
cwd: "/home/user/project",
|
|
61
|
+
});
|
|
62
|
+
assert.ok(prompt.includes("/home/user/project"));
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// ─── Regression: no backslash paths in LLM-visible text ────────────────────
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Pattern that matches Windows-style absolute paths with backslashes.
|
|
69
|
+
* Catches: C:\Users\..., D:\Projects\..., \\server\share\...
|
|
70
|
+
* Does not match: escaped chars in regex, JSON strings, etc.
|
|
71
|
+
*/
|
|
72
|
+
const WINDOWS_ABS_PATH_RE = /[A-Z]:\\[A-Za-z]/;
|
|
73
|
+
|
|
74
|
+
test("buildSystemPrompt: no Windows absolute paths with backslashes in output", () => {
|
|
75
|
+
// Simulate a Windows-like cwd
|
|
76
|
+
const prompt = buildSystemPrompt({
|
|
77
|
+
cwd: "D:\\Projects\\my-app\\.gsd\\worktrees\\M002",
|
|
78
|
+
});
|
|
79
|
+
const lines = prompt.split("\n");
|
|
80
|
+
const violations = lines.filter(line => WINDOWS_ABS_PATH_RE.test(line));
|
|
81
|
+
assert.equal(
|
|
82
|
+
violations.length, 0,
|
|
83
|
+
`System prompt contains Windows backslash paths:\n${violations.join("\n")}`,
|
|
84
|
+
);
|
|
85
|
+
});
|
|
@@ -114,16 +114,43 @@ function readClipboardImageViaWlPaste(): ClipboardImage | null {
|
|
|
114
114
|
.filter(Boolean);
|
|
115
115
|
|
|
116
116
|
const selectedType = selectPreferredImageMimeType(types);
|
|
117
|
-
if (
|
|
118
|
-
|
|
117
|
+
if (selectedType) {
|
|
118
|
+
const data = runCommand("wl-paste", ["--type", selectedType, "--no-newline"]);
|
|
119
|
+
if (data.ok && data.stdout.length > 0) {
|
|
120
|
+
return { bytes: data.stdout, mimeType: baseMimeType(selectedType) };
|
|
121
|
+
}
|
|
119
122
|
}
|
|
120
123
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
+
// Fallback for WSLg/BMP: when only image/bmp is available, ask wl-paste
|
|
125
|
+
// to convert to PNG on the fly. wl-paste supports format conversion for
|
|
126
|
+
// some compositor types. If that fails, try reading BMP and converting
|
|
127
|
+
// via ImageMagick (#813).
|
|
128
|
+
const hasBmp = types.some((t) => baseMimeType(t) === "image/bmp");
|
|
129
|
+
if (!selectedType && hasBmp) {
|
|
130
|
+
// Try requesting PNG directly — wl-paste may convert
|
|
131
|
+
const pngData = runCommand("wl-paste", ["--type", "image/png", "--no-newline"]);
|
|
132
|
+
if (pngData.ok && pngData.stdout.length > 0) {
|
|
133
|
+
return { bytes: pngData.stdout, mimeType: "image/png" };
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Try reading BMP and converting via ImageMagick convert
|
|
137
|
+
const bmpData = runCommand("wl-paste", ["--type", "image/bmp", "--no-newline"]);
|
|
138
|
+
if (bmpData.ok && bmpData.stdout.length > 0) {
|
|
139
|
+
const converted = spawnSync("convert", ["bmp:-", "png:-"], {
|
|
140
|
+
input: bmpData.stdout,
|
|
141
|
+
timeout: 5000,
|
|
142
|
+
maxBuffer: DEFAULT_MAX_BUFFER_BYTES,
|
|
143
|
+
});
|
|
144
|
+
if (!converted.error && converted.status === 0 && converted.stdout.length > 0) {
|
|
145
|
+
const stdout = Buffer.isBuffer(converted.stdout)
|
|
146
|
+
? converted.stdout
|
|
147
|
+
: Buffer.from(converted.stdout);
|
|
148
|
+
return { bytes: stdout, mimeType: "image/png" };
|
|
149
|
+
}
|
|
150
|
+
}
|
|
124
151
|
}
|
|
125
152
|
|
|
126
|
-
return
|
|
153
|
+
return null;
|
|
127
154
|
}
|
|
128
155
|
|
|
129
156
|
function readClipboardImageViaXclip(): ClipboardImage | null {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-platform path display utilities.
|
|
3
|
+
*
|
|
4
|
+
* Paths injected into LLM prompts, tool results, or any text the model
|
|
5
|
+
* processes must use forward slashes. Windows backslash paths cause bash
|
|
6
|
+
* failures when the model copies them into shell commands — bash interprets
|
|
7
|
+
* backslashes as escape characters, silently stripping them.
|
|
8
|
+
*
|
|
9
|
+
* Node's `path` module and `fs` module handle native separators correctly
|
|
10
|
+
* for filesystem operations. This module is ONLY for paths that enter
|
|
11
|
+
* text consumed by the LLM or interpreted by a shell.
|
|
12
|
+
*
|
|
13
|
+
* Usage:
|
|
14
|
+
* import { toPosixPath } from "./path-display.js";
|
|
15
|
+
* prompt += `Current working directory: ${toPosixPath(cwd)}`;
|
|
16
|
+
*
|
|
17
|
+
* NOT for:
|
|
18
|
+
* fs.readFile(path) — use native path as-is
|
|
19
|
+
* path.join(a, b) — use native path module
|
|
20
|
+
* spawn(cmd, { cwd: path }) — Node handles this correctly
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Convert a filesystem path to forward-slash (POSIX) form for display.
|
|
25
|
+
*
|
|
26
|
+
* On Unix this is a no-op. On Windows it converts `C:\Users\name\project`
|
|
27
|
+
* to `C:/Users/name/project`, which is valid in:
|
|
28
|
+
* - Git Bash / MSYS2
|
|
29
|
+
* - WSL bash
|
|
30
|
+
* - PowerShell
|
|
31
|
+
* - Node.js APIs (which accept both separators)
|
|
32
|
+
* - Most Windows programs
|
|
33
|
+
*/
|
|
34
|
+
export function toPosixPath(fsPath: string): string {
|
|
35
|
+
return fsPath.replaceAll("\\", "/");
|
|
36
|
+
}
|
|
@@ -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
|
|
|
@@ -54,6 +54,14 @@ export default function AsyncJobs(pi: ExtensionAPI) {
|
|
|
54
54
|
? output.slice(0, maxLen) + "\n\n[... truncated, use await_job for full output]"
|
|
55
55
|
: output;
|
|
56
56
|
|
|
57
|
+
// Deliver as follow-up without triggering a new LLM turn (#875).
|
|
58
|
+
// When the agent is streaming: the message is queued and picked up
|
|
59
|
+
// by the agent loop's getFollowUpMessages() after the current turn.
|
|
60
|
+
// When the agent is idle: the message is appended to context so it's
|
|
61
|
+
// visible on the next user-initiated prompt. Previously triggerTurn:true
|
|
62
|
+
// caused spurious autonomous turns — the model would interpret completed
|
|
63
|
+
// job output as requiring action and cascade into unbounded self-reinforcing
|
|
64
|
+
// loops (running more commands, spawning more jobs, burning context).
|
|
57
65
|
pi.sendMessage(
|
|
58
66
|
{
|
|
59
67
|
customType: "async_job_result",
|
|
@@ -64,7 +72,7 @@ export default function AsyncJobs(pi: ExtensionAPI) {
|
|
|
64
72
|
].join("\n"),
|
|
65
73
|
display: true,
|
|
66
74
|
},
|
|
67
|
-
{ deliverAs: "followUp"
|
|
75
|
+
{ deliverAs: "followUp" },
|
|
68
76
|
);
|
|
69
77
|
},
|
|
70
78
|
});
|
|
@@ -66,6 +66,7 @@ import { waitForReady } from "./readiness-detector.js";
|
|
|
66
66
|
import { queryShellEnv, sendAndWait, runOnSession } from "./interaction.js";
|
|
67
67
|
import { formatUptime, formatTokenCount, resolveBgShellPersistenceCwd } from "./utilities.js";
|
|
68
68
|
import { BgManagerOverlay } from "./overlay.js";
|
|
69
|
+
import { toPosixPath } from "../shared/path-display.js";
|
|
69
70
|
|
|
70
71
|
// ── Re-exports for consumers ───────────────────────────────────────────────
|
|
71
72
|
|
|
@@ -337,7 +338,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
337
338
|
text += ` type: ${bg.processType}\n`;
|
|
338
339
|
text += ` status: ${bg.status}\n`;
|
|
339
340
|
text += ` command: ${bg.command}\n`;
|
|
340
|
-
text += ` cwd: ${bg.cwd}`;
|
|
341
|
+
text += ` cwd: ${toPosixPath(bg.cwd)}`;
|
|
341
342
|
|
|
342
343
|
if (bg.group) text += `\n group: ${bg.group}`;
|
|
343
344
|
if (bg.readyPort) text += `\n ready_port: ${bg.readyPort}`;
|
|
@@ -694,7 +695,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
694
695
|
}
|
|
695
696
|
|
|
696
697
|
let text = `Shell environment for ${bg.id} (${bg.label}):\n`;
|
|
697
|
-
text += ` cwd: ${envResult.cwd}\n`;
|
|
698
|
+
text += ` cwd: ${toPosixPath(envResult.cwd)}\n`;
|
|
698
699
|
text += ` shell: ${envResult.shell}\n`;
|
|
699
700
|
|
|
700
701
|
const envEntries = Object.entries(envResult.env);
|
|
@@ -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
|
|
|
@@ -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
|
|
|
@@ -1071,6 +1078,12 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1071
1078
|
// Non-fatal — captures module may not be available
|
|
1072
1079
|
}
|
|
1073
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
|
+
|
|
1074
1087
|
return loadPrompt("reassess-roadmap", {
|
|
1075
1088
|
workingDirectory: base,
|
|
1076
1089
|
milestoneId: mid,
|
|
@@ -1081,6 +1094,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1081
1094
|
assessmentPath,
|
|
1082
1095
|
inlinedContext,
|
|
1083
1096
|
deferredCaptures,
|
|
1097
|
+
commitInstruction: reassessCommitInstruction,
|
|
1084
1098
|
});
|
|
1085
1099
|
}
|
|
1086
1100
|
|
|
@@ -90,6 +90,10 @@ export function resolveExpectedArtifactPath(unitType: string, unitId: string, ba
|
|
|
90
90
|
const dir = resolveMilestonePath(base, mid);
|
|
91
91
|
return dir ? join(dir, buildMilestoneFileName(mid, "SUMMARY")) : null;
|
|
92
92
|
}
|
|
93
|
+
case "replan-slice": {
|
|
94
|
+
const dir = resolveSlicePath(base, mid, sid!);
|
|
95
|
+
return dir ? join(dir, buildSliceFileName(sid!, "REPLAN")) : null;
|
|
96
|
+
}
|
|
93
97
|
case "rewrite-docs":
|
|
94
98
|
return null;
|
|
95
99
|
default:
|
|
@@ -127,10 +131,9 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
const absPath = resolveExpectedArtifactPath(unitType, unitId, base);
|
|
130
|
-
//
|
|
131
|
-
//
|
|
132
|
-
|
|
133
|
-
if (!absPath) return unitType === "replan-slice";
|
|
134
|
+
// For unit types with no verifiable artifact (null path), the parent directory
|
|
135
|
+
// is missing on disk — treat as stale completion state so the key gets evicted (#313).
|
|
136
|
+
if (!absPath) return false;
|
|
134
137
|
if (!existsSync(absPath)) return false;
|
|
135
138
|
|
|
136
139
|
// plan-slice must produce a plan with actual task entries, not just a scaffold.
|