@tuan_son.dinh/gsd 2.6.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/LICENSE +21 -0
- package/README.md +453 -0
- package/dist/app-paths.d.ts +4 -0
- package/dist/app-paths.js +6 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +269 -0
- package/dist/loader.d.ts +2 -0
- package/dist/loader.js +70 -0
- package/dist/logo.d.ts +16 -0
- package/dist/logo.js +25 -0
- package/dist/onboarding.d.ts +43 -0
- package/dist/onboarding.js +418 -0
- package/dist/pi-migration.d.ts +14 -0
- package/dist/pi-migration.js +57 -0
- package/dist/resource-loader.d.ts +22 -0
- package/dist/resource-loader.js +60 -0
- package/dist/tool-bootstrap.d.ts +4 -0
- package/dist/tool-bootstrap.js +74 -0
- package/dist/wizard.d.ts +7 -0
- package/dist/wizard.js +25 -0
- package/package.json +60 -0
- package/patches/@mariozechner+pi-coding-agent+0.57.1.patch +108 -0
- package/patches/@mariozechner+pi-tui+0.57.1.patch +47 -0
- package/pkg/dist/modes/interactive/theme/dark.json +85 -0
- package/pkg/dist/modes/interactive/theme/light.json +84 -0
- package/pkg/dist/modes/interactive/theme/theme-schema.json +335 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts +78 -0
- package/pkg/dist/modes/interactive/theme/theme.d.ts.map +1 -0
- package/pkg/dist/modes/interactive/theme/theme.js +949 -0
- package/pkg/dist/modes/interactive/theme/theme.js.map +1 -0
- package/pkg/package.json +8 -0
- package/scripts/postinstall.js +127 -0
- package/src/resources/GSD-WORKFLOW.md +661 -0
- package/src/resources/agents/researcher.md +29 -0
- package/src/resources/agents/scout.md +56 -0
- package/src/resources/agents/worker.md +31 -0
- package/src/resources/extensions/ask-user-questions.ts +249 -0
- package/src/resources/extensions/bg-shell/index.ts +2808 -0
- package/src/resources/extensions/browser-tools/BROWSER-TOOLS-V2-PROPOSAL.md +1277 -0
- package/src/resources/extensions/browser-tools/core.js +1057 -0
- package/src/resources/extensions/browser-tools/index.ts +4989 -0
- package/src/resources/extensions/browser-tools/package.json +20 -0
- package/src/resources/extensions/context7/index.ts +428 -0
- package/src/resources/extensions/context7/package.json +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +352 -0
- package/src/resources/extensions/google-search/index.ts +323 -0
- package/src/resources/extensions/google-search/package.json +9 -0
- package/src/resources/extensions/gsd/activity-log.ts +69 -0
- package/src/resources/extensions/gsd/auto.ts +2744 -0
- package/src/resources/extensions/gsd/commands.ts +313 -0
- package/src/resources/extensions/gsd/crash-recovery.ts +85 -0
- package/src/resources/extensions/gsd/dashboard-overlay.ts +521 -0
- package/src/resources/extensions/gsd/docs/preferences-reference.md +176 -0
- package/src/resources/extensions/gsd/doctor.ts +690 -0
- package/src/resources/extensions/gsd/files.ts +732 -0
- package/src/resources/extensions/gsd/git-service.ts +597 -0
- package/src/resources/extensions/gsd/gitignore.ts +168 -0
- package/src/resources/extensions/gsd/guided-flow.ts +817 -0
- package/src/resources/extensions/gsd/index.ts +558 -0
- package/src/resources/extensions/gsd/metrics.ts +374 -0
- package/src/resources/extensions/gsd/migrate/command.ts +218 -0
- package/src/resources/extensions/gsd/migrate/index.ts +42 -0
- package/src/resources/extensions/gsd/migrate/parser.ts +323 -0
- package/src/resources/extensions/gsd/migrate/parsers.ts +624 -0
- package/src/resources/extensions/gsd/migrate/preview.ts +48 -0
- package/src/resources/extensions/gsd/migrate/transformer.ts +346 -0
- package/src/resources/extensions/gsd/migrate/types.ts +370 -0
- package/src/resources/extensions/gsd/migrate/validator.ts +55 -0
- package/src/resources/extensions/gsd/migrate/writer.ts +539 -0
- package/src/resources/extensions/gsd/observability-validator.ts +408 -0
- package/src/resources/extensions/gsd/package.json +11 -0
- package/src/resources/extensions/gsd/paths.ts +308 -0
- package/src/resources/extensions/gsd/preferences.ts +757 -0
- package/src/resources/extensions/gsd/prompt-loader.ts +50 -0
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +25 -0
- package/src/resources/extensions/gsd/prompts/complete-slice.md +29 -0
- package/src/resources/extensions/gsd/prompts/discuss.md +189 -0
- package/src/resources/extensions/gsd/prompts/doctor-heal.md +29 -0
- package/src/resources/extensions/gsd/prompts/execute-task.md +61 -0
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +3 -0
- package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +59 -0
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +23 -0
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -0
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +11 -0
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -0
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +65 -0
- package/src/resources/extensions/gsd/prompts/plan-slice.md +51 -0
- package/src/resources/extensions/gsd/prompts/queue.md +85 -0
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +48 -0
- package/src/resources/extensions/gsd/prompts/replan-slice.md +39 -0
- package/src/resources/extensions/gsd/prompts/research-milestone.md +37 -0
- package/src/resources/extensions/gsd/prompts/research-slice.md +28 -0
- package/src/resources/extensions/gsd/prompts/review-migration.md +66 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +109 -0
- package/src/resources/extensions/gsd/prompts/system.md +187 -0
- package/src/resources/extensions/gsd/prompts/worktree-merge.md +123 -0
- package/src/resources/extensions/gsd/session-forensics.ts +487 -0
- package/src/resources/extensions/gsd/skill-discovery.ts +137 -0
- package/src/resources/extensions/gsd/state.ts +460 -0
- package/src/resources/extensions/gsd/templates/context.md +76 -0
- package/src/resources/extensions/gsd/templates/decisions.md +8 -0
- package/src/resources/extensions/gsd/templates/milestone-summary.md +73 -0
- package/src/resources/extensions/gsd/templates/plan.md +131 -0
- package/src/resources/extensions/gsd/templates/preferences.md +24 -0
- package/src/resources/extensions/gsd/templates/project.md +31 -0
- package/src/resources/extensions/gsd/templates/reassessment.md +28 -0
- package/src/resources/extensions/gsd/templates/requirements.md +81 -0
- package/src/resources/extensions/gsd/templates/research.md +46 -0
- package/src/resources/extensions/gsd/templates/roadmap.md +118 -0
- package/src/resources/extensions/gsd/templates/slice-context.md +58 -0
- package/src/resources/extensions/gsd/templates/slice-summary.md +99 -0
- package/src/resources/extensions/gsd/templates/state.md +19 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +52 -0
- package/src/resources/extensions/gsd/templates/task-summary.md +57 -0
- package/src/resources/extensions/gsd/templates/uat.md +54 -0
- package/src/resources/extensions/gsd/tests/activity-log-prune.test.ts +327 -0
- package/src/resources/extensions/gsd/tests/auto-preflight.test.ts +56 -0
- package/src/resources/extensions/gsd/tests/auto-supervisor.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/complete-milestone.test.ts +225 -0
- package/src/resources/extensions/gsd/tests/cost-projection.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +341 -0
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +689 -0
- package/src/resources/extensions/gsd/tests/discuss-prompt.test.ts +38 -0
- package/src/resources/extensions/gsd/tests/doctor.test.ts +505 -0
- package/src/resources/extensions/gsd/tests/git-service.test.ts +1313 -0
- package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +308 -0
- package/src/resources/extensions/gsd/tests/metrics-io.test.ts +201 -0
- package/src/resources/extensions/gsd/tests/metrics.test.ts +217 -0
- package/src/resources/extensions/gsd/tests/migrate-command.test.ts +390 -0
- package/src/resources/extensions/gsd/tests/migrate-parser.test.ts +786 -0
- package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +657 -0
- package/src/resources/extensions/gsd/tests/migrate-validator-parsers.test.ts +443 -0
- package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +318 -0
- package/src/resources/extensions/gsd/tests/migrate-writer.test.ts +420 -0
- package/src/resources/extensions/gsd/tests/must-have-parser.test.ts +309 -0
- package/src/resources/extensions/gsd/tests/parsers.test.ts +1351 -0
- package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +163 -0
- package/src/resources/extensions/gsd/tests/plan-quality-validator.test.ts +386 -0
- package/src/resources/extensions/gsd/tests/reassess-prompt.test.ts +171 -0
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +155 -0
- package/src/resources/extensions/gsd/tests/remote-status.test.ts +99 -0
- package/src/resources/extensions/gsd/tests/replan-slice.test.ts +521 -0
- package/src/resources/extensions/gsd/tests/requirements.test.ts +125 -0
- package/src/resources/extensions/gsd/tests/resolve-ts-hooks.mjs +34 -0
- package/src/resources/extensions/gsd/tests/resolve-ts.mjs +11 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +348 -0
- package/src/resources/extensions/gsd/tests/unit-runtime.test.ts +247 -0
- package/src/resources/extensions/gsd/tests/workflow-config.test.mjs +53 -0
- package/src/resources/extensions/gsd/tests/workspace-index.test.ts +94 -0
- package/src/resources/extensions/gsd/tests/worktree-integration.test.ts +253 -0
- package/src/resources/extensions/gsd/tests/worktree-manager.test.ts +160 -0
- package/src/resources/extensions/gsd/tests/worktree.test.ts +264 -0
- package/src/resources/extensions/gsd/types.ts +159 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +184 -0
- package/src/resources/extensions/gsd/workspace-index.ts +203 -0
- package/src/resources/extensions/gsd/worktree-command.ts +845 -0
- package/src/resources/extensions/gsd/worktree-manager.ts +392 -0
- package/src/resources/extensions/gsd/worktree.ts +183 -0
- package/src/resources/extensions/mac-tools/index.ts +852 -0
- package/src/resources/extensions/mac-tools/swift-cli/Package.swift +22 -0
- package/src/resources/extensions/mac-tools/swift-cli/Sources/main.swift +1318 -0
- package/src/resources/extensions/mcporter/index.ts +429 -0
- package/src/resources/extensions/remote-questions/config.ts +81 -0
- package/src/resources/extensions/remote-questions/discord-adapter.ts +128 -0
- package/src/resources/extensions/remote-questions/format.ts +163 -0
- package/src/resources/extensions/remote-questions/manager.ts +192 -0
- package/src/resources/extensions/remote-questions/remote-command.ts +307 -0
- package/src/resources/extensions/remote-questions/slack-adapter.ts +92 -0
- package/src/resources/extensions/remote-questions/status.ts +31 -0
- package/src/resources/extensions/remote-questions/store.ts +77 -0
- package/src/resources/extensions/remote-questions/types.ts +75 -0
- package/src/resources/extensions/search-the-web/cache.ts +78 -0
- package/src/resources/extensions/search-the-web/command-search-provider.ts +95 -0
- package/src/resources/extensions/search-the-web/format.ts +258 -0
- package/src/resources/extensions/search-the-web/http.ts +238 -0
- package/src/resources/extensions/search-the-web/index.ts +65 -0
- package/src/resources/extensions/search-the-web/native-search.ts +157 -0
- package/src/resources/extensions/search-the-web/provider.ts +118 -0
- package/src/resources/extensions/search-the-web/tavily.ts +116 -0
- package/src/resources/extensions/search-the-web/tool-fetch-page.ts +519 -0
- package/src/resources/extensions/search-the-web/tool-llm-context.ts +561 -0
- package/src/resources/extensions/search-the-web/tool-search.ts +576 -0
- package/src/resources/extensions/search-the-web/url-utils.ts +91 -0
- package/src/resources/extensions/shared/confirm-ui.ts +126 -0
- package/src/resources/extensions/shared/interview-ui.ts +613 -0
- package/src/resources/extensions/shared/next-action-ui.ts +197 -0
- package/src/resources/extensions/shared/progress-widget.ts +282 -0
- package/src/resources/extensions/shared/terminal.ts +23 -0
- package/src/resources/extensions/shared/thinking-widget.ts +107 -0
- package/src/resources/extensions/shared/ui.ts +400 -0
- package/src/resources/extensions/shared/wizard-ui.ts +551 -0
- package/src/resources/extensions/slash-commands/audit.ts +88 -0
- package/src/resources/extensions/slash-commands/clear.ts +10 -0
- package/src/resources/extensions/slash-commands/create-extension.ts +297 -0
- package/src/resources/extensions/slash-commands/create-slash-command.ts +234 -0
- package/src/resources/extensions/slash-commands/index.ts +12 -0
- package/src/resources/extensions/subagent/agents.ts +126 -0
- package/src/resources/extensions/subagent/index.ts +1020 -0
- package/src/resources/extensions/voice/index.ts +195 -0
- package/src/resources/extensions/voice/speech-recognizer.swift +154 -0
- package/src/resources/skills/debug-like-expert/SKILL.md +231 -0
- package/src/resources/skills/debug-like-expert/references/debugging-mindset.md +253 -0
- package/src/resources/skills/debug-like-expert/references/hypothesis-testing.md +373 -0
- package/src/resources/skills/debug-like-expert/references/investigation-techniques.md +337 -0
- package/src/resources/skills/debug-like-expert/references/verification-patterns.md +425 -0
- package/src/resources/skills/debug-like-expert/references/when-to-research.md +361 -0
- package/src/resources/skills/frontend-design/SKILL.md +45 -0
- package/src/resources/skills/swiftui/SKILL.md +208 -0
- package/src/resources/skills/swiftui/references/animations.md +921 -0
- package/src/resources/skills/swiftui/references/architecture.md +1561 -0
- package/src/resources/skills/swiftui/references/layout-system.md +1186 -0
- package/src/resources/skills/swiftui/references/navigation.md +1492 -0
- package/src/resources/skills/swiftui/references/networking-async.md +214 -0
- package/src/resources/skills/swiftui/references/performance.md +1706 -0
- package/src/resources/skills/swiftui/references/platform-integration.md +204 -0
- package/src/resources/skills/swiftui/references/state-management.md +1443 -0
- package/src/resources/skills/swiftui/references/swiftdata.md +297 -0
- package/src/resources/skills/swiftui/references/testing-debugging.md +247 -0
- package/src/resources/skills/swiftui/references/uikit-appkit-interop.md +218 -0
- package/src/resources/skills/swiftui/workflows/add-feature.md +191 -0
- package/src/resources/skills/swiftui/workflows/build-new-app.md +311 -0
- package/src/resources/skills/swiftui/workflows/debug-swiftui.md +192 -0
- package/src/resources/skills/swiftui/workflows/optimize-performance.md +197 -0
- package/src/resources/skills/swiftui/workflows/ship-app.md +203 -0
- package/src/resources/skills/swiftui/workflows/write-tests.md +235 -0
|
@@ -0,0 +1,521 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GSD Dashboard Overlay
|
|
3
|
+
*
|
|
4
|
+
* Full-screen overlay showing auto-mode progress: milestone/slice/task
|
|
5
|
+
* breakdown, current unit, completed units, timing, and activity log.
|
|
6
|
+
* Toggled with Ctrl+Alt+G or opened from /gsd status.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { Theme } from "@mariozechner/pi-coding-agent";
|
|
10
|
+
import { truncateToWidth, visibleWidth, matchesKey, Key } from "@mariozechner/pi-tui";
|
|
11
|
+
import { deriveState } from "./state.js";
|
|
12
|
+
import { loadFile, parseRoadmap, parsePlan } from "./files.js";
|
|
13
|
+
import { resolveMilestoneFile, resolveSliceFile } from "./paths.js";
|
|
14
|
+
import { getAutoDashboardData, type AutoDashboardData } from "./auto.js";
|
|
15
|
+
import {
|
|
16
|
+
getLedger, getProjectTotals, aggregateByPhase, aggregateBySlice,
|
|
17
|
+
aggregateByModel, formatCost, formatTokenCount, formatCostProjection,
|
|
18
|
+
} from "./metrics.js";
|
|
19
|
+
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
20
|
+
import { getActiveWorktreeName } from "./worktree-command.js";
|
|
21
|
+
|
|
22
|
+
function formatDuration(ms: number): string {
|
|
23
|
+
const s = Math.floor(ms / 1000);
|
|
24
|
+
if (s < 60) return `${s}s`;
|
|
25
|
+
const m = Math.floor(s / 60);
|
|
26
|
+
const rs = s % 60;
|
|
27
|
+
if (m < 60) return `${m}m ${rs}s`;
|
|
28
|
+
const h = Math.floor(m / 60);
|
|
29
|
+
const rm = m % 60;
|
|
30
|
+
return `${h}h ${rm}m`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function unitLabel(type: string): string {
|
|
34
|
+
switch (type) {
|
|
35
|
+
case "research-milestone": return "Research";
|
|
36
|
+
case "plan-milestone": return "Plan";
|
|
37
|
+
case "research-slice": return "Research";
|
|
38
|
+
case "plan-slice": return "Plan";
|
|
39
|
+
case "execute-task": return "Execute";
|
|
40
|
+
case "complete-slice": return "Complete";
|
|
41
|
+
case "reassess-roadmap": return "Reassess";
|
|
42
|
+
default: return type;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function centerLine(content: string, width: number): string {
|
|
47
|
+
const vis = visibleWidth(content);
|
|
48
|
+
if (vis >= width) return truncateToWidth(content, width);
|
|
49
|
+
const leftPad = Math.floor((width - vis) / 2);
|
|
50
|
+
return " ".repeat(leftPad) + content;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function padRight(content: string, width: number): string {
|
|
54
|
+
const vis = visibleWidth(content);
|
|
55
|
+
return content + " ".repeat(Math.max(0, width - vis));
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function joinColumns(left: string, right: string, width: number): string {
|
|
59
|
+
const leftW = visibleWidth(left);
|
|
60
|
+
const rightW = visibleWidth(right);
|
|
61
|
+
if (leftW + rightW + 2 > width) {
|
|
62
|
+
return truncateToWidth(`${left} ${right}`, width);
|
|
63
|
+
}
|
|
64
|
+
return left + " ".repeat(width - leftW - rightW) + right;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function fitColumns(parts: string[], width: number, separator = " "): string {
|
|
68
|
+
const filtered = parts.filter(Boolean);
|
|
69
|
+
if (filtered.length === 0) return "";
|
|
70
|
+
let result = filtered[0];
|
|
71
|
+
for (let i = 1; i < filtered.length; i++) {
|
|
72
|
+
const candidate = `${result}${separator}${filtered[i]}`;
|
|
73
|
+
if (visibleWidth(candidate) > width) break;
|
|
74
|
+
result = candidate;
|
|
75
|
+
}
|
|
76
|
+
return truncateToWidth(result, width);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export class GSDDashboardOverlay {
|
|
80
|
+
private tui: { requestRender: () => void };
|
|
81
|
+
private theme: Theme;
|
|
82
|
+
private onClose: () => void;
|
|
83
|
+
private cachedWidth?: number;
|
|
84
|
+
private cachedLines?: string[];
|
|
85
|
+
private refreshTimer: ReturnType<typeof setInterval>;
|
|
86
|
+
private scrollOffset = 0;
|
|
87
|
+
private dashData: AutoDashboardData;
|
|
88
|
+
private milestoneData: MilestoneView | null = null;
|
|
89
|
+
private loading = true;
|
|
90
|
+
|
|
91
|
+
constructor(
|
|
92
|
+
tui: { requestRender: () => void },
|
|
93
|
+
theme: Theme,
|
|
94
|
+
onClose: () => void,
|
|
95
|
+
) {
|
|
96
|
+
this.tui = tui;
|
|
97
|
+
this.theme = theme;
|
|
98
|
+
this.onClose = onClose;
|
|
99
|
+
this.dashData = getAutoDashboardData();
|
|
100
|
+
|
|
101
|
+
this.loadData().then(() => {
|
|
102
|
+
this.loading = false;
|
|
103
|
+
this.invalidate();
|
|
104
|
+
this.tui.requestRender();
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
this.refreshTimer = setInterval(() => {
|
|
108
|
+
this.dashData = getAutoDashboardData();
|
|
109
|
+
this.loadData().then(() => {
|
|
110
|
+
this.invalidate();
|
|
111
|
+
this.tui.requestRender();
|
|
112
|
+
});
|
|
113
|
+
}, 2000);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
private async loadData(): Promise<void> {
|
|
117
|
+
const base = this.dashData.basePath || process.cwd();
|
|
118
|
+
try {
|
|
119
|
+
const state = await deriveState(base);
|
|
120
|
+
if (!state.activeMilestone) {
|
|
121
|
+
this.milestoneData = null;
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const mid = state.activeMilestone.id;
|
|
126
|
+
const view: MilestoneView = {
|
|
127
|
+
id: mid,
|
|
128
|
+
title: state.activeMilestone.title,
|
|
129
|
+
slices: [],
|
|
130
|
+
phase: state.phase,
|
|
131
|
+
progress: {
|
|
132
|
+
milestones: {
|
|
133
|
+
total: state.progress?.milestones.total ?? state.registry.length,
|
|
134
|
+
done: state.progress?.milestones.done ?? state.registry.filter(entry => entry.status === "complete").length,
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
const roadmapFile = resolveMilestoneFile(base, mid, "ROADMAP");
|
|
140
|
+
const roadmapContent = roadmapFile ? await loadFile(roadmapFile) : null;
|
|
141
|
+
if (roadmapContent) {
|
|
142
|
+
const roadmap = parseRoadmap(roadmapContent);
|
|
143
|
+
for (const s of roadmap.slices) {
|
|
144
|
+
const sliceView: SliceView = {
|
|
145
|
+
id: s.id,
|
|
146
|
+
title: s.title,
|
|
147
|
+
done: s.done,
|
|
148
|
+
risk: s.risk,
|
|
149
|
+
active: state.activeSlice?.id === s.id,
|
|
150
|
+
tasks: [],
|
|
151
|
+
};
|
|
152
|
+
|
|
153
|
+
if (sliceView.active) {
|
|
154
|
+
const planFile = resolveSliceFile(base, mid, s.id, "PLAN");
|
|
155
|
+
const planContent = planFile ? await loadFile(planFile) : null;
|
|
156
|
+
if (planContent) {
|
|
157
|
+
const plan = parsePlan(planContent);
|
|
158
|
+
sliceView.taskProgress = {
|
|
159
|
+
done: plan.tasks.filter(t => t.done).length,
|
|
160
|
+
total: plan.tasks.length,
|
|
161
|
+
};
|
|
162
|
+
for (const t of plan.tasks) {
|
|
163
|
+
sliceView.tasks.push({
|
|
164
|
+
id: t.id,
|
|
165
|
+
title: t.title,
|
|
166
|
+
done: t.done,
|
|
167
|
+
active: state.activeTask?.id === t.id,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
view.slices.push(sliceView);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
this.milestoneData = view;
|
|
178
|
+
} catch {
|
|
179
|
+
// Don't crash the overlay
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
handleInput(data: string): void {
|
|
184
|
+
if (matchesKey(data, Key.escape) || matchesKey(data, Key.ctrl("c")) || matchesKey(data, Key.ctrlAlt("g"))) {
|
|
185
|
+
clearInterval(this.refreshTimer);
|
|
186
|
+
this.onClose();
|
|
187
|
+
return;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
if (matchesKey(data, Key.down) || matchesKey(data, "j")) {
|
|
191
|
+
this.scrollOffset++;
|
|
192
|
+
this.invalidate();
|
|
193
|
+
this.tui.requestRender();
|
|
194
|
+
return;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
if (matchesKey(data, Key.up) || matchesKey(data, "k")) {
|
|
198
|
+
this.scrollOffset = Math.max(0, this.scrollOffset - 1);
|
|
199
|
+
this.invalidate();
|
|
200
|
+
this.tui.requestRender();
|
|
201
|
+
return;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (data === "g") {
|
|
205
|
+
this.scrollOffset = 0;
|
|
206
|
+
this.invalidate();
|
|
207
|
+
this.tui.requestRender();
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
if (data === "G") {
|
|
212
|
+
this.scrollOffset = 999;
|
|
213
|
+
this.invalidate();
|
|
214
|
+
this.tui.requestRender();
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
render(width: number): string[] {
|
|
220
|
+
if (this.cachedLines && this.cachedWidth === width) {
|
|
221
|
+
return this.cachedLines;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const content = this.buildContentLines(width);
|
|
225
|
+
const viewportHeight = Math.max(5, process.stdout.rows ? process.stdout.rows - 8 : 24);
|
|
226
|
+
const chromeHeight = 2;
|
|
227
|
+
const visibleContentRows = Math.max(1, viewportHeight - chromeHeight);
|
|
228
|
+
const maxScroll = Math.max(0, content.length - visibleContentRows);
|
|
229
|
+
this.scrollOffset = Math.min(this.scrollOffset, maxScroll);
|
|
230
|
+
const visibleContent = content.slice(this.scrollOffset, this.scrollOffset + visibleContentRows);
|
|
231
|
+
|
|
232
|
+
const lines = this.wrapInBox(visibleContent, width);
|
|
233
|
+
|
|
234
|
+
this.cachedWidth = width;
|
|
235
|
+
this.cachedLines = lines;
|
|
236
|
+
return lines;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private wrapInBox(inner: string[], width: number): string[] {
|
|
240
|
+
const th = this.theme;
|
|
241
|
+
const border = (s: string) => th.fg("borderAccent", s);
|
|
242
|
+
const innerWidth = width - 4;
|
|
243
|
+
const lines: string[] = [];
|
|
244
|
+
|
|
245
|
+
lines.push(border("╭" + "─".repeat(width - 2) + "╮"));
|
|
246
|
+
for (const line of inner) {
|
|
247
|
+
const truncated = truncateToWidth(line, innerWidth);
|
|
248
|
+
const padWidth = Math.max(0, innerWidth - visibleWidth(truncated));
|
|
249
|
+
lines.push(border("│") + " " + truncated + " ".repeat(padWidth) + " " + border("│"));
|
|
250
|
+
}
|
|
251
|
+
lines.push(border("╰" + "─".repeat(width - 2) + "╯"));
|
|
252
|
+
return lines;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
private buildContentLines(width: number): string[] {
|
|
256
|
+
const th = this.theme;
|
|
257
|
+
const shellWidth = width - 4;
|
|
258
|
+
const contentWidth = Math.min(shellWidth, 128);
|
|
259
|
+
const sidePad = Math.max(0, Math.floor((shellWidth - contentWidth) / 2));
|
|
260
|
+
const leftMargin = " ".repeat(sidePad);
|
|
261
|
+
const lines: string[] = [];
|
|
262
|
+
|
|
263
|
+
const row = (content = ""): string => {
|
|
264
|
+
const truncated = truncateToWidth(content, contentWidth);
|
|
265
|
+
return leftMargin + padRight(truncated, contentWidth);
|
|
266
|
+
};
|
|
267
|
+
const blank = () => row("");
|
|
268
|
+
const hr = () => row(th.fg("dim", "─".repeat(contentWidth)));
|
|
269
|
+
const centered = (content: string) => row(centerLine(content, contentWidth));
|
|
270
|
+
|
|
271
|
+
const title = th.fg("accent", th.bold("GSD Dashboard"));
|
|
272
|
+
const status = this.dashData.active
|
|
273
|
+
? `${Date.now() % 2000 < 1000 ? th.fg("success", "●") : th.fg("dim", "○")} ${th.fg("success", "AUTO")}`
|
|
274
|
+
: this.dashData.paused
|
|
275
|
+
? th.fg("warning", "⏸ PAUSED")
|
|
276
|
+
: th.fg("dim", "idle");
|
|
277
|
+
const worktreeName = getActiveWorktreeName();
|
|
278
|
+
const worktreeTag = worktreeName
|
|
279
|
+
? ` ${th.fg("warning", `⎇ ${worktreeName}`)}`
|
|
280
|
+
: "";
|
|
281
|
+
const elapsed = th.fg("dim", formatDuration(this.dashData.elapsed));
|
|
282
|
+
lines.push(row(joinColumns(`${title} ${status}${worktreeTag}`, elapsed, contentWidth)));
|
|
283
|
+
lines.push(blank());
|
|
284
|
+
|
|
285
|
+
if (this.dashData.currentUnit) {
|
|
286
|
+
const cu = this.dashData.currentUnit;
|
|
287
|
+
const currentElapsed = th.fg("dim", formatDuration(Date.now() - cu.startedAt));
|
|
288
|
+
lines.push(row(joinColumns(
|
|
289
|
+
`${th.fg("text", "Now")}: ${th.fg("accent", unitLabel(cu.type))} ${th.fg("text", cu.id)}`,
|
|
290
|
+
currentElapsed,
|
|
291
|
+
contentWidth,
|
|
292
|
+
)));
|
|
293
|
+
lines.push(blank());
|
|
294
|
+
} else if (this.dashData.paused) {
|
|
295
|
+
lines.push(row(th.fg("dim", "/gsd auto to resume")));
|
|
296
|
+
lines.push(blank());
|
|
297
|
+
} else {
|
|
298
|
+
lines.push(row(th.fg("dim", "No unit running · /gsd auto to start")));
|
|
299
|
+
lines.push(blank());
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (this.loading) {
|
|
303
|
+
lines.push(centered(th.fg("dim", "Loading dashboard…")));
|
|
304
|
+
return lines;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (this.milestoneData) {
|
|
308
|
+
const mv = this.milestoneData;
|
|
309
|
+
lines.push(row(th.fg("text", th.bold(`${mv.id}: ${mv.title}`))));
|
|
310
|
+
lines.push(blank());
|
|
311
|
+
|
|
312
|
+
const totalSlices = mv.slices.length;
|
|
313
|
+
const doneSlices = mv.slices.filter(s => s.done).length;
|
|
314
|
+
const totalMilestones = mv.progress.milestones.total;
|
|
315
|
+
const doneMilestones = mv.progress.milestones.done;
|
|
316
|
+
const activeSlice = mv.slices.find(s => s.active);
|
|
317
|
+
|
|
318
|
+
lines.push(blank());
|
|
319
|
+
|
|
320
|
+
if (activeSlice?.taskProgress) {
|
|
321
|
+
lines.push(row(this.renderProgressRow("Tasks", activeSlice.taskProgress.done, activeSlice.taskProgress.total, "accent", contentWidth)));
|
|
322
|
+
}
|
|
323
|
+
lines.push(row(this.renderProgressRow("Slices", doneSlices, totalSlices, "success", contentWidth)));
|
|
324
|
+
lines.push(row(this.renderProgressRow("Milestones", doneMilestones, totalMilestones, "warning", contentWidth)));
|
|
325
|
+
|
|
326
|
+
lines.push(blank());
|
|
327
|
+
|
|
328
|
+
for (const s of mv.slices) {
|
|
329
|
+
const icon = s.done ? th.fg("success", "✓")
|
|
330
|
+
: s.active ? th.fg("accent", "▸")
|
|
331
|
+
: th.fg("dim", "○");
|
|
332
|
+
const titleText = s.active ? th.fg("accent", `${s.id}: ${s.title}`)
|
|
333
|
+
: s.done ? th.fg("muted", `${s.id}: ${s.title}`)
|
|
334
|
+
: th.fg("dim", `${s.id}: ${s.title}`);
|
|
335
|
+
const risk = th.fg("dim", s.risk);
|
|
336
|
+
lines.push(row(joinColumns(` ${icon} ${titleText}`, risk, contentWidth)));
|
|
337
|
+
|
|
338
|
+
if (s.active && s.tasks.length > 0) {
|
|
339
|
+
for (const t of s.tasks) {
|
|
340
|
+
const tIcon = t.done ? th.fg("success", "✓")
|
|
341
|
+
: t.active ? th.fg("warning", "▸")
|
|
342
|
+
: th.fg("dim", "·");
|
|
343
|
+
const tTitle = t.active ? th.fg("warning", `${t.id}: ${t.title}`)
|
|
344
|
+
: t.done ? th.fg("muted", `${t.id}: ${t.title}`)
|
|
345
|
+
: th.fg("dim", `${t.id}: ${t.title}`);
|
|
346
|
+
lines.push(row(` ${tIcon} ${truncateToWidth(tTitle, contentWidth - 6)}`));
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
lines.push(centered(th.fg("dim", "No active milestone.")));
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
if (this.dashData.completedUnits.length > 0) {
|
|
355
|
+
lines.push(blank());
|
|
356
|
+
lines.push(hr());
|
|
357
|
+
lines.push(row(th.fg("text", th.bold("Completed"))));
|
|
358
|
+
lines.push(blank());
|
|
359
|
+
|
|
360
|
+
const recent = [...this.dashData.completedUnits].reverse().slice(0, 10);
|
|
361
|
+
for (const u of recent) {
|
|
362
|
+
const left = ` ${th.fg("success", "✓")} ${th.fg("muted", unitLabel(u.type))} ${th.fg("muted", u.id)}`;
|
|
363
|
+
const right = th.fg("dim", formatDuration(u.finishedAt - u.startedAt));
|
|
364
|
+
lines.push(row(joinColumns(left, right, contentWidth)));
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
if (this.dashData.completedUnits.length > 10) {
|
|
368
|
+
lines.push(row(th.fg("dim", ` ...and ${this.dashData.completedUnits.length - 10} more`)));
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
const ledger = getLedger();
|
|
373
|
+
if (ledger && ledger.units.length > 0) {
|
|
374
|
+
const totals = getProjectTotals(ledger.units);
|
|
375
|
+
|
|
376
|
+
lines.push(blank());
|
|
377
|
+
lines.push(hr());
|
|
378
|
+
lines.push(row(th.fg("text", th.bold("Cost & Usage"))));
|
|
379
|
+
lines.push(blank());
|
|
380
|
+
|
|
381
|
+
lines.push(row(fitColumns([
|
|
382
|
+
`${th.fg("warning", formatCost(totals.cost))} total`,
|
|
383
|
+
`${th.fg("text", formatTokenCount(totals.tokens.total))} tokens`,
|
|
384
|
+
`${th.fg("text", String(totals.toolCalls))} tools`,
|
|
385
|
+
`${th.fg("text", String(totals.units))} units`,
|
|
386
|
+
], contentWidth, ` ${th.fg("dim", "·")} `)));
|
|
387
|
+
|
|
388
|
+
lines.push(row(fitColumns([
|
|
389
|
+
`${th.fg("dim", "in:")} ${th.fg("text", formatTokenCount(totals.tokens.input))}`,
|
|
390
|
+
`${th.fg("dim", "out:")} ${th.fg("text", formatTokenCount(totals.tokens.output))}`,
|
|
391
|
+
`${th.fg("dim", "cache-r:")} ${th.fg("text", formatTokenCount(totals.tokens.cacheRead))}`,
|
|
392
|
+
`${th.fg("dim", "cache-w:")} ${th.fg("text", formatTokenCount(totals.tokens.cacheWrite))}`,
|
|
393
|
+
], contentWidth, " ")));
|
|
394
|
+
|
|
395
|
+
const phases = aggregateByPhase(ledger.units);
|
|
396
|
+
if (phases.length > 0) {
|
|
397
|
+
lines.push(blank());
|
|
398
|
+
lines.push(row(th.fg("dim", "By Phase")));
|
|
399
|
+
for (const p of phases) {
|
|
400
|
+
const pct = totals.cost > 0 ? Math.round((p.cost / totals.cost) * 100) : 0;
|
|
401
|
+
const left = ` ${th.fg("text", p.phase.padEnd(14))}${th.fg("warning", formatCost(p.cost).padStart(8))}`;
|
|
402
|
+
const right = th.fg("dim", `${String(pct).padStart(3)}% ${formatTokenCount(p.tokens.total)} tok ${p.units} units`);
|
|
403
|
+
lines.push(row(joinColumns(left, right, contentWidth)));
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const slices = aggregateBySlice(ledger.units);
|
|
408
|
+
if (slices.length > 0) {
|
|
409
|
+
lines.push(blank());
|
|
410
|
+
lines.push(row(th.fg("dim", "By Slice")));
|
|
411
|
+
for (const s of slices) {
|
|
412
|
+
const pct = totals.cost > 0 ? Math.round((s.cost / totals.cost) * 100) : 0;
|
|
413
|
+
const left = ` ${th.fg("text", s.sliceId.padEnd(14))}${th.fg("warning", formatCost(s.cost).padStart(8))}`;
|
|
414
|
+
const right = th.fg("dim", `${String(pct).padStart(3)}% ${formatTokenCount(s.tokens.total)} tok ${formatDuration(s.duration)}`);
|
|
415
|
+
lines.push(row(joinColumns(left, right, contentWidth)));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Cost projection — only when active milestone data is available
|
|
420
|
+
if (this.milestoneData) {
|
|
421
|
+
const mv = this.milestoneData;
|
|
422
|
+
const msTotalSlices = mv.slices.length;
|
|
423
|
+
const msDoneSlices = mv.slices.filter(s => s.done).length;
|
|
424
|
+
const remainingCount = msTotalSlices - msDoneSlices;
|
|
425
|
+
const overlayPrefs = loadEffectiveGSDPreferences()?.preferences;
|
|
426
|
+
const projLines = formatCostProjection(slices, remainingCount, overlayPrefs?.budget_ceiling);
|
|
427
|
+
if (projLines.length > 0) {
|
|
428
|
+
lines.push(blank());
|
|
429
|
+
for (const line of projLines) {
|
|
430
|
+
const colored = line.toLowerCase().includes('ceiling')
|
|
431
|
+
? th.fg("warning", line)
|
|
432
|
+
: th.fg("dim", line);
|
|
433
|
+
lines.push(row(colored));
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
const models = aggregateByModel(ledger.units);
|
|
439
|
+
if (models.length > 1) {
|
|
440
|
+
lines.push(blank());
|
|
441
|
+
lines.push(row(th.fg("dim", "By Model")));
|
|
442
|
+
for (const m of models) {
|
|
443
|
+
const pct = totals.cost > 0 ? Math.round((m.cost / totals.cost) * 100) : 0;
|
|
444
|
+
const modelName = truncateToWidth(m.model, 38);
|
|
445
|
+
const left = ` ${th.fg("text", modelName.padEnd(38))}${th.fg("warning", formatCost(m.cost).padStart(8))}`;
|
|
446
|
+
const right = th.fg("dim", `${String(pct).padStart(3)}% ${m.units} units`);
|
|
447
|
+
lines.push(row(joinColumns(left, right, contentWidth)));
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
lines.push(blank());
|
|
452
|
+
lines.push(row(`${th.fg("dim", "avg/unit:")} ${th.fg("text", formatCost(totals.cost / totals.units))} ${th.fg("dim", "·")} ${th.fg("text", formatTokenCount(Math.round(totals.tokens.total / totals.units)))} tokens`));
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
lines.push(blank());
|
|
456
|
+
lines.push(hr());
|
|
457
|
+
lines.push(centered(th.fg("dim", "↑↓ scroll · g/G top/end · esc close")));
|
|
458
|
+
|
|
459
|
+
return lines;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
private renderProgressRow(
|
|
463
|
+
label: string,
|
|
464
|
+
done: number,
|
|
465
|
+
total: number,
|
|
466
|
+
color: "success" | "accent" | "warning",
|
|
467
|
+
width: number,
|
|
468
|
+
): string {
|
|
469
|
+
const th = this.theme;
|
|
470
|
+
const pct = total > 0 ? Math.round((done / total) * 100) : 0;
|
|
471
|
+
const labelWidth = 12;
|
|
472
|
+
const rightWidth = 14;
|
|
473
|
+
const gap = 2;
|
|
474
|
+
const labelText = truncateToWidth(label, labelWidth, "").padEnd(labelWidth);
|
|
475
|
+
const ratioText = `${done}/${total}`;
|
|
476
|
+
const rightText = `${String(pct).padStart(3)}% ${ratioText.padStart(rightWidth - 5)}`;
|
|
477
|
+
const barWidth = Math.max(12, width - labelWidth - rightWidth - gap * 2);
|
|
478
|
+
const filled = total > 0 ? Math.round((done / total) * barWidth) : 0;
|
|
479
|
+
const bar = th.fg(color, "█".repeat(filled)) + th.fg("dim", "░".repeat(Math.max(0, barWidth - filled)));
|
|
480
|
+
return `${th.fg("dim", labelText)}${" ".repeat(gap)}${bar}${" ".repeat(gap)}${th.fg("dim", rightText)}`;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
invalidate(): void {
|
|
484
|
+
this.cachedWidth = undefined;
|
|
485
|
+
this.cachedLines = undefined;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
dispose(): void {
|
|
489
|
+
clearInterval(this.refreshTimer);
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
interface MilestoneView {
|
|
494
|
+
id: string;
|
|
495
|
+
title: string;
|
|
496
|
+
slices: SliceView[];
|
|
497
|
+
phase: string;
|
|
498
|
+
progress: {
|
|
499
|
+
milestones: {
|
|
500
|
+
total: number;
|
|
501
|
+
done: number;
|
|
502
|
+
};
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
interface SliceView {
|
|
507
|
+
id: string;
|
|
508
|
+
title: string;
|
|
509
|
+
done: boolean;
|
|
510
|
+
risk: string;
|
|
511
|
+
active: boolean;
|
|
512
|
+
tasks: TaskView[];
|
|
513
|
+
taskProgress?: { done: number; total: number };
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
interface TaskView {
|
|
517
|
+
id: string;
|
|
518
|
+
title: string;
|
|
519
|
+
done: boolean;
|
|
520
|
+
active: boolean;
|
|
521
|
+
}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# GSD Preferences Reference
|
|
2
|
+
|
|
3
|
+
Full documentation for `~/.gsd/preferences.md` (global) and `.gsd/preferences.md` (project).
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Notes
|
|
8
|
+
|
|
9
|
+
- Keep this skill-first.
|
|
10
|
+
- Prefer explicit skill names or absolute paths.
|
|
11
|
+
- Use absolute paths for personal/local skills when you want zero ambiguity.
|
|
12
|
+
- These preferences guide which skills GSD should load and follow; they do not override higher-priority instructions in the current conversation.
|
|
13
|
+
|
|
14
|
+
---
|
|
15
|
+
|
|
16
|
+
## Field Guide
|
|
17
|
+
|
|
18
|
+
- `version`: schema version. Start at `1`.
|
|
19
|
+
|
|
20
|
+
- `always_use_skills`: skills GSD should use whenever they are relevant.
|
|
21
|
+
|
|
22
|
+
- `prefer_skills`: soft defaults GSD should prefer when relevant.
|
|
23
|
+
|
|
24
|
+
- `avoid_skills`: skills GSD should avoid unless clearly needed.
|
|
25
|
+
|
|
26
|
+
- `skill_rules`: situational rules with a human-readable `when` trigger and one or more of `use`, `prefer`, or `avoid`.
|
|
27
|
+
|
|
28
|
+
- `custom_instructions`: extra durable instructions related to skill use.
|
|
29
|
+
|
|
30
|
+
- `models`: per-stage model selection for auto-mode. Keys: `research`, `planning`, `execution`, `completion`. Values: model IDs (e.g. `claude-sonnet-4-6`, `claude-opus-4-6`). Omit a key to use whatever model is currently active.
|
|
31
|
+
|
|
32
|
+
- `skill_discovery`: controls how GSD discovers and applies skills during auto-mode. Valid values:
|
|
33
|
+
- `auto` — skills are found and applied automatically without prompting.
|
|
34
|
+
- `suggest` — (default) skills are identified during research but not installed automatically.
|
|
35
|
+
- `off` — skill discovery is disabled entirely.
|
|
36
|
+
|
|
37
|
+
- `auto_supervisor`: configures the auto-mode supervisor that monitors agent progress and enforces timeouts. Keys:
|
|
38
|
+
- `model`: model ID to use for the supervisor process (defaults to the currently active model).
|
|
39
|
+
- `soft_timeout_minutes`: minutes before the supervisor issues a soft warning (default: 20).
|
|
40
|
+
- `idle_timeout_minutes`: minutes of inactivity before the supervisor intervenes (default: 10).
|
|
41
|
+
- `hard_timeout_minutes`: minutes before the supervisor forces termination (default: 30).
|
|
42
|
+
|
|
43
|
+
- `git`: configures GSD's git behavior. All fields are optional — omit any to use defaults. Keys:
|
|
44
|
+
- `auto_push`: boolean — automatically push commits to the remote after committing. Default: `false`.
|
|
45
|
+
- `push_branches`: boolean — push newly created slice branches to the remote. Default: `false`.
|
|
46
|
+
- `remote`: string — git remote name to push to. Default: `"origin"`.
|
|
47
|
+
- `snapshots`: boolean — create snapshot commits (WIP saves) during long-running tasks. Default: `false`.
|
|
48
|
+
- `pre_merge_check`: boolean or `"auto"` — run pre-merge checks before merging a slice branch. `true` always runs, `false` never runs, `"auto"` runs when CI is detected. Default: `false`.
|
|
49
|
+
- `commit_type`: string — override the conventional commit type prefix. Must be one of: `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `perf`, `ci`, `build`, `style`. Default: inferred from diff content.
|
|
50
|
+
- `main_branch`: string — the primary branch name for new git repos (e.g., `"main"`, `"master"`, `"trunk"`). Also used by `getMainBranch()` as the preferred branch when auto-detection is ambiguous. Default: `"main"`.
|
|
51
|
+
|
|
52
|
+
- `planning_depth`: controls how much research and verification happens before execution. Valid values:
|
|
53
|
+
- `thorough` — (default) full research, planning with self-audit, observability, and reassessment.
|
|
54
|
+
- `standard` — skip separate research units, skip plan self-audit. Planning still does inline codebase exploration.
|
|
55
|
+
- `minimal` — all of standard, plus skip reassessment and observability planning.
|
|
56
|
+
|
|
57
|
+
- `workflow`: fine-grained overrides for the planning pipeline. Individual keys override `planning_depth`. Keys:
|
|
58
|
+
- `skip_milestone_research`: skip the research-milestone unit (default: false).
|
|
59
|
+
- `skip_slice_research`: skip the research-slice unit (default: false).
|
|
60
|
+
- `skip_plan_self_audit`: remove the 10-point self-audit from slice planning (default: false).
|
|
61
|
+
- `skip_reassessment`: skip roadmap reassessment after each slice (default: false).
|
|
62
|
+
- `skip_observability`: remove observability/diagnostics from plans and suppress warnings (default: false).
|
|
63
|
+
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
## Best Practices
|
|
67
|
+
|
|
68
|
+
- Keep `always_use_skills` short.
|
|
69
|
+
- Use `skill_rules` for situational routing, not broad personality preferences.
|
|
70
|
+
- Prefer skill names for stable built-in skills.
|
|
71
|
+
- Prefer absolute paths for local personal skills.
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## Models Example
|
|
76
|
+
|
|
77
|
+
```yaml
|
|
78
|
+
---
|
|
79
|
+
version: 1
|
|
80
|
+
models:
|
|
81
|
+
research: claude-sonnet-4-6
|
|
82
|
+
planning: claude-opus-4-6
|
|
83
|
+
execution: claude-sonnet-4-6
|
|
84
|
+
completion: claude-sonnet-4-6
|
|
85
|
+
---
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Opus for planning (where architectural decisions matter most), Sonnet for everything else (faster, cheaper). Omit any key to use the currently selected model.
|
|
89
|
+
|
|
90
|
+
---
|
|
91
|
+
|
|
92
|
+
## Example Variations
|
|
93
|
+
|
|
94
|
+
**Minimal — always load a UAT skill and route Clerk tasks:**
|
|
95
|
+
|
|
96
|
+
```yaml
|
|
97
|
+
---
|
|
98
|
+
version: 1
|
|
99
|
+
always_use_skills:
|
|
100
|
+
- /Users/you/.claude/skills/verify-uat
|
|
101
|
+
skill_rules:
|
|
102
|
+
- when: finishing implementation and human judgment matters
|
|
103
|
+
use:
|
|
104
|
+
- /Users/you/.claude/skills/verify-uat
|
|
105
|
+
---
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Richer routing — prefer cleanup and authentication skills:**
|
|
109
|
+
|
|
110
|
+
```yaml
|
|
111
|
+
---
|
|
112
|
+
version: 1
|
|
113
|
+
prefer_skills:
|
|
114
|
+
- commit-ignore
|
|
115
|
+
skill_rules:
|
|
116
|
+
- when: task involves Clerk authentication
|
|
117
|
+
use:
|
|
118
|
+
- clerk
|
|
119
|
+
- clerk-setup
|
|
120
|
+
- when: the user is looking for installable capability rather than implementation
|
|
121
|
+
prefer:
|
|
122
|
+
- find-skills
|
|
123
|
+
---
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## Git Preferences Example
|
|
129
|
+
|
|
130
|
+
```yaml
|
|
131
|
+
---
|
|
132
|
+
version: 1
|
|
133
|
+
git:
|
|
134
|
+
auto_push: true
|
|
135
|
+
push_branches: true
|
|
136
|
+
remote: origin
|
|
137
|
+
snapshots: true
|
|
138
|
+
pre_merge_check: auto
|
|
139
|
+
commit_type: feat
|
|
140
|
+
---
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
All git fields are optional. Omit any field to use the default behavior. Project-level preferences override global preferences on a per-field basis.
|
|
144
|
+
|
|
145
|
+
---
|
|
146
|
+
|
|
147
|
+
## Workflow Examples
|
|
148
|
+
|
|
149
|
+
**Standard depth — skip research, streamline planning:**
|
|
150
|
+
|
|
151
|
+
```yaml
|
|
152
|
+
---
|
|
153
|
+
version: 1
|
|
154
|
+
planning_depth: standard
|
|
155
|
+
---
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
**Minimal depth — fastest pipeline, skip everything optional:**
|
|
159
|
+
|
|
160
|
+
```yaml
|
|
161
|
+
---
|
|
162
|
+
version: 1
|
|
163
|
+
planning_depth: minimal
|
|
164
|
+
---
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**Custom mix — skip research but keep self-audit:**
|
|
168
|
+
|
|
169
|
+
```yaml
|
|
170
|
+
---
|
|
171
|
+
version: 1
|
|
172
|
+
planning_depth: standard
|
|
173
|
+
workflow:
|
|
174
|
+
skip_plan_self_audit: false
|
|
175
|
+
---
|
|
176
|
+
```
|