gsd-pi 2.38.0-dev.8f5c161 → 2.38.0-dev.98b44dc
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 +15 -11
- package/dist/resource-loader.js +34 -1
- package/dist/resources/extensions/browser-tools/index.js +3 -1
- package/dist/resources/extensions/browser-tools/tools/verify.js +97 -0
- package/dist/resources/extensions/github-sync/cli.js +284 -0
- package/dist/resources/extensions/github-sync/index.js +73 -0
- package/dist/resources/extensions/github-sync/mapping.js +67 -0
- package/dist/resources/extensions/github-sync/sync.js +424 -0
- package/dist/resources/extensions/github-sync/templates.js +118 -0
- package/dist/resources/extensions/github-sync/types.js +7 -0
- package/dist/resources/extensions/gsd/auto-dispatch.js +1 -1
- package/dist/resources/extensions/gsd/auto-loop.js +538 -469
- package/dist/resources/extensions/gsd/auto-post-unit.js +28 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +197 -19
- package/dist/resources/extensions/gsd/auto-worktree.js +3 -3
- package/dist/resources/extensions/gsd/commands.js +2 -1
- package/dist/resources/extensions/gsd/doctor-providers.js +3 -0
- package/dist/resources/extensions/gsd/doctor.js +20 -1
- package/dist/resources/extensions/gsd/exit-command.js +2 -1
- package/dist/resources/extensions/gsd/files.js +46 -7
- package/dist/resources/extensions/gsd/git-service.js +30 -12
- package/dist/resources/extensions/gsd/gitignore.js +16 -3
- package/dist/resources/extensions/gsd/guided-flow.js +149 -38
- package/dist/resources/extensions/gsd/health-widget-core.js +32 -70
- package/dist/resources/extensions/gsd/health-widget.js +3 -86
- package/dist/resources/extensions/gsd/index.js +22 -19
- package/dist/resources/extensions/gsd/migrate-external.js +18 -1
- package/dist/resources/extensions/gsd/native-git-bridge.js +37 -0
- package/dist/resources/extensions/gsd/paths.js +3 -0
- package/dist/resources/extensions/gsd/preferences-types.js +1 -0
- package/dist/resources/extensions/gsd/preferences-validation.js +58 -0
- package/dist/resources/extensions/gsd/preferences.js +20 -9
- package/dist/resources/extensions/gsd/prompt-loader.js +6 -2
- package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/execute-task.md +3 -1
- package/dist/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/dist/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/dist/resources/extensions/gsd/prompts/run-uat.md +3 -1
- package/dist/resources/extensions/gsd/roadmap-mutations.js +24 -0
- package/dist/resources/extensions/gsd/state.js +41 -22
- package/dist/resources/extensions/gsd/templates/runtime.md +21 -0
- package/dist/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/dist/resources/extensions/mcp-client/index.js +14 -1
- package/dist/resources/extensions/remote-questions/status.js +4 -2
- package/dist/resources/extensions/remote-questions/store.js +4 -2
- package/dist/resources/extensions/shared/frontmatter.js +1 -1
- package/package.json +1 -1
- package/packages/pi-ai/dist/utils/oauth/anthropic.js +2 -2
- package/packages/pi-ai/dist/utils/oauth/anthropic.js.map +1 -1
- package/packages/pi-ai/src/utils/oauth/anthropic.ts +2 -2
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +205 -7
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.d.ts +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +6 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts +1 -1
- package/packages/pi-coding-agent/dist/index.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/index.js +1 -1
- package/packages/pi-coding-agent/dist/index.js.map +1 -1
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +223 -7
- package/packages/pi-coding-agent/src/core/skills.ts +9 -1
- package/packages/pi-coding-agent/src/index.ts +1 -0
- package/src/resources/extensions/browser-tools/index.ts +3 -0
- package/src/resources/extensions/browser-tools/tools/verify.ts +117 -0
- package/src/resources/extensions/github-sync/cli.ts +364 -0
- package/src/resources/extensions/github-sync/index.ts +93 -0
- package/src/resources/extensions/github-sync/mapping.ts +81 -0
- package/src/resources/extensions/github-sync/sync.ts +556 -0
- package/src/resources/extensions/github-sync/templates.ts +183 -0
- package/src/resources/extensions/github-sync/tests/cli.test.ts +20 -0
- package/src/resources/extensions/github-sync/tests/commit-linking.test.ts +39 -0
- package/src/resources/extensions/github-sync/tests/mapping.test.ts +104 -0
- package/src/resources/extensions/github-sync/tests/templates.test.ts +110 -0
- package/src/resources/extensions/github-sync/types.ts +47 -0
- package/src/resources/extensions/gsd/auto-dispatch.ts +1 -1
- package/src/resources/extensions/gsd/auto-loop.ts +342 -304
- package/src/resources/extensions/gsd/auto-post-unit.ts +29 -3
- package/src/resources/extensions/gsd/auto-prompts.ts +242 -19
- package/src/resources/extensions/gsd/auto-worktree.ts +3 -3
- package/src/resources/extensions/gsd/commands.ts +2 -2
- package/src/resources/extensions/gsd/doctor-providers.ts +4 -0
- package/src/resources/extensions/gsd/doctor.ts +22 -1
- package/src/resources/extensions/gsd/exit-command.ts +2 -2
- package/src/resources/extensions/gsd/files.ts +49 -9
- package/src/resources/extensions/gsd/git-service.ts +44 -10
- package/src/resources/extensions/gsd/gitignore.ts +17 -3
- package/src/resources/extensions/gsd/guided-flow.ts +177 -44
- package/src/resources/extensions/gsd/health-widget-core.ts +28 -80
- package/src/resources/extensions/gsd/health-widget.ts +3 -89
- package/src/resources/extensions/gsd/index.ts +21 -16
- package/src/resources/extensions/gsd/migrate-external.ts +18 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +37 -0
- package/src/resources/extensions/gsd/paths.ts +4 -0
- package/src/resources/extensions/gsd/preferences-types.ts +4 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +50 -0
- package/src/resources/extensions/gsd/preferences.ts +23 -9
- package/src/resources/extensions/gsd/prompt-loader.ts +7 -2
- package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/execute-task.md +3 -1
- package/src/resources/extensions/gsd/prompts/guided-complete-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-execute-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/guided-resume-task.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/plan-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/reassess-roadmap.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-milestone.md +1 -1
- package/src/resources/extensions/gsd/prompts/research-slice.md +1 -1
- package/src/resources/extensions/gsd/prompts/run-uat.md +3 -1
- package/src/resources/extensions/gsd/roadmap-mutations.ts +29 -0
- package/src/resources/extensions/gsd/state.ts +38 -20
- package/src/resources/extensions/gsd/templates/runtime.md +21 -0
- package/src/resources/extensions/gsd/templates/task-plan.md +3 -0
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +106 -31
- package/src/resources/extensions/gsd/tests/auto-worktree-milestone-merge.test.ts +4 -3
- package/src/resources/extensions/gsd/tests/derive-state.test.ts +43 -0
- package/src/resources/extensions/gsd/tests/gitignore-tracked-gsd.test.ts +50 -0
- package/src/resources/extensions/gsd/tests/health-widget.test.ts +16 -54
- package/src/resources/extensions/gsd/tests/parsers.test.ts +131 -14
- package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +209 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +5 -1
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +140 -0
- package/src/resources/extensions/gsd/types.ts +18 -0
- package/src/resources/extensions/gsd/verification-evidence.ts +16 -0
- package/src/resources/extensions/mcp-client/index.ts +17 -1
- package/src/resources/extensions/remote-questions/status.ts +4 -2
- package/src/resources/extensions/remote-questions/store.ts +4 -2
- package/src/resources/extensions/shared/frontmatter.ts +1 -1
|
@@ -5,10 +5,9 @@
|
|
|
5
5
|
* runtime integrations so the regressions can be tested directly.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import { existsSync
|
|
8
|
+
import { existsSync } from "node:fs";
|
|
9
|
+
import { detectProjectState } from "./detection.js";
|
|
9
10
|
import { gsdRoot } from "./paths.js";
|
|
10
|
-
import { join } from "node:path";
|
|
11
|
-
import type { GSDState, Phase } from "./types.js";
|
|
12
11
|
|
|
13
12
|
export type HealthWidgetProjectState = "none" | "initialized" | "active";
|
|
14
13
|
|
|
@@ -20,75 +19,19 @@ export interface HealthWidgetData {
|
|
|
20
19
|
environmentErrorCount: number;
|
|
21
20
|
environmentWarningCount: number;
|
|
22
21
|
lastRefreshed: number;
|
|
23
|
-
executionPhase?: Phase;
|
|
24
|
-
executionStatus?: string;
|
|
25
|
-
executionTarget?: string;
|
|
26
|
-
nextAction?: string;
|
|
27
|
-
blocker?: string | null;
|
|
28
|
-
activeMilestoneId?: string;
|
|
29
|
-
activeSliceId?: string;
|
|
30
|
-
activeTaskId?: string;
|
|
31
|
-
progress?: GSDState["progress"];
|
|
32
|
-
eta?: string | null;
|
|
33
22
|
}
|
|
34
23
|
|
|
35
24
|
export function detectHealthWidgetProjectState(basePath: string): HealthWidgetProjectState {
|
|
36
|
-
|
|
37
|
-
if (!existsSync(root)) return "none";
|
|
25
|
+
if (!existsSync(gsdRoot(basePath))) return "none";
|
|
38
26
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
try {
|
|
42
|
-
const milestonesDir = join(root, "milestones");
|
|
43
|
-
if (existsSync(milestonesDir)) {
|
|
44
|
-
const entries = readdirSync(milestonesDir, { withFileTypes: true });
|
|
45
|
-
if (entries.some(e => e.isDirectory())) return "active";
|
|
46
|
-
}
|
|
47
|
-
} catch { /* non-fatal */ }
|
|
48
|
-
|
|
49
|
-
return "initialized";
|
|
27
|
+
const { state } = detectProjectState(basePath);
|
|
28
|
+
return state === "v2-gsd" ? "active" : "initialized";
|
|
50
29
|
}
|
|
51
30
|
|
|
52
31
|
function formatCost(n: number): string {
|
|
53
32
|
return n >= 1 ? `$${n.toFixed(2)}` : `${(n * 100).toFixed(1)}¢`;
|
|
54
33
|
}
|
|
55
34
|
|
|
56
|
-
function formatProgress(progress?: GSDState["progress"]): string | null {
|
|
57
|
-
if (!progress) return null;
|
|
58
|
-
|
|
59
|
-
const parts: string[] = [];
|
|
60
|
-
parts.push(`M ${progress.milestones.done}/${progress.milestones.total}`);
|
|
61
|
-
if (progress.slices) parts.push(`S ${progress.slices.done}/${progress.slices.total}`);
|
|
62
|
-
if (progress.tasks) parts.push(`T ${progress.tasks.done}/${progress.tasks.total}`);
|
|
63
|
-
return parts.length > 0 ? `Progress: ${parts.join(" · ")}` : null;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
function formatEnvironmentSummary(errorCount: number, warningCount: number): string | null {
|
|
67
|
-
if (errorCount <= 0 && warningCount <= 0) return null;
|
|
68
|
-
|
|
69
|
-
const parts: string[] = [];
|
|
70
|
-
if (errorCount > 0) parts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
71
|
-
if (warningCount > 0) parts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
72
|
-
return `Env: ${parts.join(", ")}`;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
function formatBudgetSummary(data: HealthWidgetData): string | null {
|
|
76
|
-
if (data.budgetCeiling !== undefined && data.budgetCeiling > 0) {
|
|
77
|
-
const pct = Math.min(100, (data.budgetSpent / data.budgetCeiling) * 100);
|
|
78
|
-
return `Budget: ${formatCost(data.budgetSpent)}/${formatCost(data.budgetCeiling)} (${pct.toFixed(0)}%)`;
|
|
79
|
-
}
|
|
80
|
-
if (data.budgetSpent > 0) {
|
|
81
|
-
return `Spent: ${formatCost(data.budgetSpent)}`;
|
|
82
|
-
}
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function buildExecutionHeadline(data: HealthWidgetData): string {
|
|
87
|
-
const status = data.executionStatus ?? "Active project";
|
|
88
|
-
const target = data.executionTarget ?? data.blocker ?? "loading status…";
|
|
89
|
-
return ` GSD ${status}${target ? ` - ${target}` : ""}`;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
35
|
/**
|
|
93
36
|
* Build compact health lines for the widget.
|
|
94
37
|
* Returns a string array suitable for setWidget().
|
|
@@ -102,28 +45,33 @@ export function buildHealthLines(data: HealthWidgetData): string[] {
|
|
|
102
45
|
return [" GSD Project initialized — run /gsd to continue setup"];
|
|
103
46
|
}
|
|
104
47
|
|
|
105
|
-
const
|
|
106
|
-
const details: string[] = [];
|
|
107
|
-
|
|
108
|
-
const progress = formatProgress(data.progress);
|
|
109
|
-
if (progress) details.push(progress);
|
|
110
|
-
|
|
111
|
-
if (data.providerIssue) details.push(data.providerIssue);
|
|
48
|
+
const parts: string[] = [];
|
|
112
49
|
|
|
113
|
-
const
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
)
|
|
117
|
-
|
|
50
|
+
const totalIssues = data.environmentErrorCount + data.environmentWarningCount + (data.providerIssue ? 1 : 0);
|
|
51
|
+
if (totalIssues === 0) {
|
|
52
|
+
parts.push("● System OK");
|
|
53
|
+
} else if (data.environmentErrorCount > 0 || data.providerIssue?.includes("✗")) {
|
|
54
|
+
parts.push(`✗ ${totalIssues} issue${totalIssues > 1 ? "s" : ""}`);
|
|
55
|
+
} else {
|
|
56
|
+
parts.push(`⚠ ${totalIssues} warning${totalIssues > 1 ? "s" : ""}`);
|
|
57
|
+
}
|
|
118
58
|
|
|
119
|
-
|
|
120
|
-
|
|
59
|
+
if (data.budgetCeiling !== undefined && data.budgetCeiling > 0) {
|
|
60
|
+
const pct = Math.min(100, (data.budgetSpent / data.budgetCeiling) * 100);
|
|
61
|
+
parts.push(`Budget: ${formatCost(data.budgetSpent)}/${formatCost(data.budgetCeiling)} (${pct.toFixed(0)}%)`);
|
|
62
|
+
} else if (data.budgetSpent > 0) {
|
|
63
|
+
parts.push(`Spent: ${formatCost(data.budgetSpent)}`);
|
|
64
|
+
}
|
|
121
65
|
|
|
122
|
-
if (data.
|
|
66
|
+
if (data.providerIssue) {
|
|
67
|
+
parts.push(data.providerIssue);
|
|
68
|
+
}
|
|
123
69
|
|
|
124
|
-
if (
|
|
125
|
-
|
|
70
|
+
if (data.environmentErrorCount > 0) {
|
|
71
|
+
parts.push(`Env: ${data.environmentErrorCount} error${data.environmentErrorCount > 1 ? "s" : ""}`);
|
|
72
|
+
} else if (data.environmentWarningCount > 0) {
|
|
73
|
+
parts.push(`Env: ${data.environmentWarningCount} warning${data.environmentWarningCount > 1 ? "s" : ""}`);
|
|
126
74
|
}
|
|
127
75
|
|
|
128
|
-
return
|
|
76
|
+
return [` ${parts.join(" │ ")}`];
|
|
129
77
|
}
|
|
@@ -16,7 +16,6 @@ import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
|
16
16
|
import { loadLedgerFromDisk, getProjectTotals } from "./metrics.js";
|
|
17
17
|
import { describeNextUnit, estimateTimeRemaining, updateSliceProgressCache } from "./auto-dashboard.js";
|
|
18
18
|
import { projectRoot } from "./commands.js";
|
|
19
|
-
import { deriveState, invalidateStateCache } from "./state.js";
|
|
20
19
|
import {
|
|
21
20
|
buildHealthLines,
|
|
22
21
|
detectHealthWidgetProjectState,
|
|
@@ -25,7 +24,7 @@ import {
|
|
|
25
24
|
|
|
26
25
|
// ── Data loader ────────────────────────────────────────────────────────────────
|
|
27
26
|
|
|
28
|
-
function
|
|
27
|
+
function loadHealthWidgetData(basePath: string): HealthWidgetData {
|
|
29
28
|
let budgetCeiling: number | undefined;
|
|
30
29
|
let budgetSpent = 0;
|
|
31
30
|
let providerIssue: string | null = null;
|
|
@@ -69,90 +68,6 @@ function loadBaseHealthWidgetData(basePath: string): HealthWidgetData {
|
|
|
69
68
|
};
|
|
70
69
|
}
|
|
71
70
|
|
|
72
|
-
function compactText(text: string, max = 64): string {
|
|
73
|
-
const trimmed = text.replace(/\s+/g, " ").trim();
|
|
74
|
-
if (trimmed.length <= max) return trimmed;
|
|
75
|
-
return `${trimmed.slice(0, max - 1).trimEnd()}…`;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function summarizeExecutionStatus(state: GSDState): string {
|
|
79
|
-
switch (state.phase) {
|
|
80
|
-
case "blocked": return "Blocked";
|
|
81
|
-
case "paused": return "Paused";
|
|
82
|
-
case "complete": return "Complete";
|
|
83
|
-
case "executing": return "Executing";
|
|
84
|
-
case "planning": return "Planning";
|
|
85
|
-
case "pre-planning": return "Pre-planning";
|
|
86
|
-
case "summarizing": return "Summarizing";
|
|
87
|
-
case "validating-milestone": return "Validating";
|
|
88
|
-
case "completing-milestone": return "Completing";
|
|
89
|
-
case "needs-discussion": return "Needs discussion";
|
|
90
|
-
case "replanning-slice": return "Replanning";
|
|
91
|
-
default: return "Active";
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function summarizeExecutionTarget(state: GSDState): string {
|
|
96
|
-
switch (state.phase) {
|
|
97
|
-
case "needs-discussion":
|
|
98
|
-
return state.activeMilestone ? `Discuss ${state.activeMilestone.id}` : "Discuss milestone draft";
|
|
99
|
-
case "pre-planning":
|
|
100
|
-
return state.activeMilestone ? `Plan ${state.activeMilestone.id}` : "Research & plan milestone";
|
|
101
|
-
case "planning":
|
|
102
|
-
return state.activeSlice ? `Plan ${state.activeSlice.id}` : "Plan next slice";
|
|
103
|
-
case "executing":
|
|
104
|
-
return state.activeTask ? `Execute ${state.activeTask.id}` : "Execute next task";
|
|
105
|
-
case "summarizing":
|
|
106
|
-
return state.activeSlice ? `Complete ${state.activeSlice.id}` : "Complete current slice";
|
|
107
|
-
case "validating-milestone":
|
|
108
|
-
return state.activeMilestone ? `Validate ${state.activeMilestone.id}` : "Validate milestone";
|
|
109
|
-
case "completing-milestone":
|
|
110
|
-
return state.activeMilestone ? `Complete ${state.activeMilestone.id}` : "Complete milestone";
|
|
111
|
-
case "replanning-slice":
|
|
112
|
-
return state.activeSlice ? `Replan ${state.activeSlice.id}` : "Replan current slice";
|
|
113
|
-
case "blocked":
|
|
114
|
-
return `waiting on ${compactText(state.blockers[0] ?? state.nextAction, 56)}`;
|
|
115
|
-
case "paused":
|
|
116
|
-
return compactText(state.nextAction || "waiting to resume", 56);
|
|
117
|
-
case "complete":
|
|
118
|
-
return "All milestones complete";
|
|
119
|
-
default:
|
|
120
|
-
return compactText(describeNextUnit(state).label, 56);
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
async function enrichHealthWidgetData(basePath: string, baseData: HealthWidgetData): Promise<HealthWidgetData> {
|
|
125
|
-
if (baseData.projectState !== "active") return baseData;
|
|
126
|
-
|
|
127
|
-
try {
|
|
128
|
-
invalidateStateCache();
|
|
129
|
-
const state = await deriveState(basePath);
|
|
130
|
-
|
|
131
|
-
if (state.activeMilestone) {
|
|
132
|
-
// Warm the slice-progress cache so estimateTimeRemaining() has data
|
|
133
|
-
updateSliceProgressCache(basePath, state.activeMilestone.id, state.activeSlice?.id);
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
return {
|
|
137
|
-
...baseData,
|
|
138
|
-
executionPhase: state.phase,
|
|
139
|
-
executionStatus: summarizeExecutionStatus(state),
|
|
140
|
-
executionTarget: summarizeExecutionTarget(state),
|
|
141
|
-
nextAction: state.nextAction,
|
|
142
|
-
blocker: state.blockers[0] ?? null,
|
|
143
|
-
activeMilestoneId: state.activeMilestone?.id,
|
|
144
|
-
activeSliceId: state.activeSlice?.id,
|
|
145
|
-
activeTaskId: state.activeTask?.id,
|
|
146
|
-
progress: state.progress,
|
|
147
|
-
eta: state.phase === "blocked" || state.phase === "paused" || state.phase === "complete"
|
|
148
|
-
? null
|
|
149
|
-
: estimateTimeRemaining(),
|
|
150
|
-
};
|
|
151
|
-
} catch {
|
|
152
|
-
return baseData;
|
|
153
|
-
}
|
|
154
|
-
}
|
|
155
|
-
|
|
156
71
|
// ── Widget init ────────────────────────────────────────────────────────────────
|
|
157
72
|
|
|
158
73
|
const REFRESH_INTERVAL_MS = 60_000;
|
|
@@ -167,7 +82,7 @@ export function initHealthWidget(ctx: ExtensionContext): void {
|
|
|
167
82
|
const basePath = projectRoot();
|
|
168
83
|
|
|
169
84
|
// String-array fallback — used in RPC mode (factory is a no-op there)
|
|
170
|
-
const initialData =
|
|
85
|
+
const initialData = loadHealthWidgetData(basePath);
|
|
171
86
|
ctx.ui.setWidget("gsd-health", buildHealthLines(initialData), { placement: "belowEditor" });
|
|
172
87
|
|
|
173
88
|
// Factory-based widget for TUI mode — replaces the string-array above
|
|
@@ -180,8 +95,7 @@ export function initHealthWidget(ctx: ExtensionContext): void {
|
|
|
180
95
|
if (refreshInFlight) return;
|
|
181
96
|
refreshInFlight = true;
|
|
182
97
|
try {
|
|
183
|
-
|
|
184
|
-
data = await enrichHealthWidgetData(basePath, baseData);
|
|
98
|
+
data = loadHealthWidgetData(basePath);
|
|
185
99
|
cachedLines = undefined;
|
|
186
100
|
_tui.requestRender();
|
|
187
101
|
} catch { /* non-fatal */ } finally {
|
|
@@ -92,6 +92,23 @@ function warnDeprecatedAgentInstructions(): void {
|
|
|
92
92
|
// ── Depth verification state ──────────────────────────────────────────────
|
|
93
93
|
let depthVerificationDone = false;
|
|
94
94
|
|
|
95
|
+
// ── DB lazy-open helper ───────────────────────────────────────────────────
|
|
96
|
+
// In manual sessions (no auto-mode), the DB is never opened by bootstrapAutoSession.
|
|
97
|
+
// This helper ensures the DB is lazily opened on first tool call that needs it.
|
|
98
|
+
async function ensureDbOpen(): Promise<boolean> {
|
|
99
|
+
try {
|
|
100
|
+
const db = await import("./gsd-db.js");
|
|
101
|
+
if (db.isDbAvailable()) return true;
|
|
102
|
+
const dbPath = join(process.cwd(), ".gsd", "gsd.db");
|
|
103
|
+
if (existsSync(dbPath)) {
|
|
104
|
+
return db.openDatabase(dbPath);
|
|
105
|
+
}
|
|
106
|
+
return false;
|
|
107
|
+
} catch {
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
95
112
|
// ── Queue phase tracking ──────────────────────────────────────────────────
|
|
96
113
|
// When true, the LLM is in a queue flow writing CONTEXT.md files.
|
|
97
114
|
// The write-gate applies during queue flows just like discussion flows.
|
|
@@ -300,12 +317,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
300
317
|
when_context: Type.Optional(Type.String({ description: "When/context for the decision (e.g. milestone ID)" })),
|
|
301
318
|
}),
|
|
302
319
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
303
|
-
//
|
|
304
|
-
|
|
305
|
-
try {
|
|
306
|
-
const db = await import("./gsd-db.js");
|
|
307
|
-
dbAvailable = db.isDbAvailable();
|
|
308
|
-
} catch { /* dynamic import failed */ }
|
|
320
|
+
// Ensure DB is open (lazy-open on first tool call in manual sessions)
|
|
321
|
+
const dbAvailable = await ensureDbOpen();
|
|
309
322
|
|
|
310
323
|
if (!dbAvailable) {
|
|
311
324
|
return {
|
|
@@ -367,11 +380,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
367
380
|
supporting_slices: Type.Optional(Type.String({ description: "Supporting slices" })),
|
|
368
381
|
}),
|
|
369
382
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
370
|
-
|
|
371
|
-
try {
|
|
372
|
-
const db = await import("./gsd-db.js");
|
|
373
|
-
dbAvailable = db.isDbAvailable();
|
|
374
|
-
} catch { /* dynamic import failed */ }
|
|
383
|
+
const dbAvailable = await ensureDbOpen();
|
|
375
384
|
|
|
376
385
|
if (!dbAvailable) {
|
|
377
386
|
return {
|
|
@@ -441,11 +450,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
441
450
|
content: Type.String({ description: "The full markdown content of the artifact" }),
|
|
442
451
|
}),
|
|
443
452
|
async execute(_toolCallId, params, _signal, _onUpdate, _ctx) {
|
|
444
|
-
|
|
445
|
-
try {
|
|
446
|
-
const db = await import("./gsd-db.js");
|
|
447
|
-
dbAvailable = db.isDbAvailable();
|
|
448
|
-
} catch { /* dynamic import failed */ }
|
|
453
|
+
const dbAvailable = await ensureDbOpen();
|
|
449
454
|
|
|
450
455
|
if (!dbAvailable) {
|
|
451
456
|
return {
|
|
@@ -6,11 +6,13 @@
|
|
|
6
6
|
* symlink replaces the original directory so all paths remain valid.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
+
import { execFileSync } from "node:child_process";
|
|
9
10
|
import { existsSync, lstatSync, mkdirSync, readdirSync, realpathSync, renameSync, cpSync, rmSync, symlinkSync } from "node:fs";
|
|
10
11
|
import { join } from "node:path";
|
|
11
12
|
import { externalGsdRoot } from "./repo-identity.js";
|
|
12
13
|
import { getErrorMessage } from "./error-utils.js";
|
|
13
14
|
import { hasGitTrackedGsdFiles } from "./gitignore.js";
|
|
15
|
+
import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
|
|
14
16
|
|
|
15
17
|
export interface MigrationResult {
|
|
16
18
|
migrated: boolean;
|
|
@@ -144,7 +146,22 @@ export function migrateToExternalState(basePath: string): MigrationResult {
|
|
|
144
146
|
return { migrated: false, error: `Migration verification failed: ${getErrorMessage(verifyErr)}` };
|
|
145
147
|
}
|
|
146
148
|
|
|
147
|
-
//
|
|
149
|
+
// Clean the git index — any .gsd/* files tracked before migration now
|
|
150
|
+
// sit behind the symlink and git can't follow it, causing them to show
|
|
151
|
+
// as deleted. Remove them from the index so the working tree stays clean.
|
|
152
|
+
// --ignore-unmatch makes this a no-op on fresh projects with no tracked .gsd/.
|
|
153
|
+
try {
|
|
154
|
+
execFileSync("git", ["rm", "-r", "--cached", "--ignore-unmatch", ".gsd"], {
|
|
155
|
+
cwd: basePath,
|
|
156
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
157
|
+
env: GIT_NO_PROMPT_ENV,
|
|
158
|
+
timeout: 10_000,
|
|
159
|
+
});
|
|
160
|
+
} catch {
|
|
161
|
+
// Non-fatal — git may be unavailable or nothing was tracked
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Remove .gsd.migrating only after symlink is verified and index is clean
|
|
148
165
|
rmSync(migratingPath, { recursive: true, force: true });
|
|
149
166
|
|
|
150
167
|
return { migrated: true };
|
|
@@ -671,6 +671,43 @@ export function nativeAddAll(basePath: string): void {
|
|
|
671
671
|
gitFileExec(basePath, ["add", "-A"]);
|
|
672
672
|
}
|
|
673
673
|
|
|
674
|
+
/**
|
|
675
|
+
* Stage all files with pathspec exclusions (git add -A -- ':!pattern' ...).
|
|
676
|
+
* Excluded paths are never hashed by git, preventing hangs on large
|
|
677
|
+
* untracked artifact trees (57GB+, 11K+ files). See #1605.
|
|
678
|
+
*
|
|
679
|
+
* Falls back to plain `git add -A` when no exclusions are provided.
|
|
680
|
+
* Always uses the CLI path (not libgit2) because libgit2's add_all
|
|
681
|
+
* does not support pathspec exclusion syntax.
|
|
682
|
+
*
|
|
683
|
+
* When excluded paths are already covered by .gitignore, git may exit
|
|
684
|
+
* with code 1 and an "ignored by .gitignore" warning. This is harmless
|
|
685
|
+
* (the staging succeeds for all non-ignored files) and is suppressed.
|
|
686
|
+
*/
|
|
687
|
+
export function nativeAddAllWithExclusions(basePath: string, exclusions: readonly string[]): void {
|
|
688
|
+
if (exclusions.length === 0) {
|
|
689
|
+
nativeAddAll(basePath);
|
|
690
|
+
return;
|
|
691
|
+
}
|
|
692
|
+
const pathspecs = exclusions.map(e => `:!${e}`);
|
|
693
|
+
try {
|
|
694
|
+
execFileSync("git", ["add", "-A", "--", ...pathspecs], {
|
|
695
|
+
cwd: basePath,
|
|
696
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
697
|
+
encoding: "utf-8",
|
|
698
|
+
env: GIT_NO_PROMPT_ENV,
|
|
699
|
+
});
|
|
700
|
+
} catch (err: unknown) {
|
|
701
|
+
// git exits 1 when pathspec exclusions reference paths already covered
|
|
702
|
+
// by .gitignore. The staging itself succeeds — only suppress that case.
|
|
703
|
+
const stderr = (err as { stderr?: string })?.stderr ?? "";
|
|
704
|
+
if (stderr.includes("ignored by one of your .gitignore files")) {
|
|
705
|
+
return;
|
|
706
|
+
}
|
|
707
|
+
throw new GSDError(GSD_GIT_ERROR, `git add -A with exclusions failed in ${basePath}: ${getErrorMessage(err)}`);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
674
711
|
/**
|
|
675
712
|
* Stage specific files.
|
|
676
713
|
* Native: libgit2 index add.
|
|
@@ -356,6 +356,10 @@ export function milestonesDir(basePath: string): string {
|
|
|
356
356
|
return join(gsdRoot(basePath), "milestones");
|
|
357
357
|
}
|
|
358
358
|
|
|
359
|
+
export function resolveRuntimeFile(basePath: string): string {
|
|
360
|
+
return join(gsdRoot(basePath), "RUNTIME.md");
|
|
361
|
+
}
|
|
362
|
+
|
|
359
363
|
export function resolveGsdRootFile(basePath: string, key: GSDRootFileKey): string {
|
|
360
364
|
const root = gsdRoot(basePath);
|
|
361
365
|
const canonical = join(root, GSD_ROOT_FILES[key]);
|
|
@@ -20,6 +20,7 @@ import type {
|
|
|
20
20
|
ReactiveExecutionConfig,
|
|
21
21
|
} from "./types.js";
|
|
22
22
|
import type { DynamicRoutingConfig } from "./model-router.js";
|
|
23
|
+
import type { GitHubSyncConfig } from "../github-sync/types.js";
|
|
23
24
|
|
|
24
25
|
// ─── Workflow Modes ──────────────────────────────────────────────────────────
|
|
25
26
|
|
|
@@ -86,6 +87,7 @@ export const KNOWN_PREFERENCE_KEYS = new Set<string>([
|
|
|
86
87
|
"context_selection",
|
|
87
88
|
"widget_mode",
|
|
88
89
|
"reactive_execution",
|
|
90
|
+
"github",
|
|
89
91
|
]);
|
|
90
92
|
|
|
91
93
|
/** Canonical list of all dispatch unit types. */
|
|
@@ -215,6 +217,8 @@ export interface GSDPreferences {
|
|
|
215
217
|
widget_mode?: "full" | "small" | "min" | "off";
|
|
216
218
|
/** Reactive (graph-derived parallel) task execution within slices. Disabled by default. */
|
|
217
219
|
reactive_execution?: ReactiveExecutionConfig;
|
|
220
|
+
/** GitHub sync configuration. Opt-in: syncs GSD events to GitHub Issues, Milestones, and PRs. */
|
|
221
|
+
github?: GitHubSyncConfig;
|
|
218
222
|
}
|
|
219
223
|
|
|
220
224
|
export interface LoadedGSDPreferences {
|
|
@@ -696,5 +696,55 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|
|
696
696
|
}
|
|
697
697
|
}
|
|
698
698
|
|
|
699
|
+
// ─── GitHub Sync ────────────────────────────────────────────────────────
|
|
700
|
+
if (preferences.github !== undefined) {
|
|
701
|
+
if (typeof preferences.github === "object" && preferences.github !== null) {
|
|
702
|
+
const gh = preferences.github as unknown as Record<string, unknown>;
|
|
703
|
+
const validGh: Record<string, unknown> = {};
|
|
704
|
+
|
|
705
|
+
if (gh.enabled !== undefined) {
|
|
706
|
+
if (typeof gh.enabled === "boolean") validGh.enabled = gh.enabled;
|
|
707
|
+
else errors.push("github.enabled must be a boolean");
|
|
708
|
+
}
|
|
709
|
+
if (gh.repo !== undefined) {
|
|
710
|
+
if (typeof gh.repo === "string" && gh.repo.includes("/")) validGh.repo = gh.repo;
|
|
711
|
+
else errors.push('github.repo must be a string in "owner/repo" format');
|
|
712
|
+
}
|
|
713
|
+
if (gh.project !== undefined) {
|
|
714
|
+
const p = typeof gh.project === "number" ? gh.project : Number(gh.project);
|
|
715
|
+
if (Number.isFinite(p) && p > 0) validGh.project = Math.floor(p);
|
|
716
|
+
else errors.push("github.project must be a positive number");
|
|
717
|
+
}
|
|
718
|
+
if (gh.labels !== undefined) {
|
|
719
|
+
if (Array.isArray(gh.labels) && gh.labels.every((l: unknown) => typeof l === "string")) {
|
|
720
|
+
validGh.labels = gh.labels;
|
|
721
|
+
} else {
|
|
722
|
+
errors.push("github.labels must be an array of strings");
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
if (gh.auto_link_commits !== undefined) {
|
|
726
|
+
if (typeof gh.auto_link_commits === "boolean") validGh.auto_link_commits = gh.auto_link_commits;
|
|
727
|
+
else errors.push("github.auto_link_commits must be a boolean");
|
|
728
|
+
}
|
|
729
|
+
if (gh.slice_prs !== undefined) {
|
|
730
|
+
if (typeof gh.slice_prs === "boolean") validGh.slice_prs = gh.slice_prs;
|
|
731
|
+
else errors.push("github.slice_prs must be a boolean");
|
|
732
|
+
}
|
|
733
|
+
|
|
734
|
+
const knownGhKeys = new Set(["enabled", "repo", "project", "labels", "auto_link_commits", "slice_prs"]);
|
|
735
|
+
for (const key of Object.keys(gh)) {
|
|
736
|
+
if (!knownGhKeys.has(key)) {
|
|
737
|
+
warnings.push(`unknown github key "${key}" — ignored`);
|
|
738
|
+
}
|
|
739
|
+
}
|
|
740
|
+
|
|
741
|
+
if (Object.keys(validGh).length > 0) {
|
|
742
|
+
validated.github = validGh as unknown as import("../github-sync/types.js").GitHubSyncConfig;
|
|
743
|
+
}
|
|
744
|
+
} else {
|
|
745
|
+
errors.push("github must be an object");
|
|
746
|
+
}
|
|
747
|
+
}
|
|
748
|
+
|
|
699
749
|
return { preferences: validated, errors, warnings };
|
|
700
750
|
}
|
|
@@ -14,7 +14,6 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
14
14
|
import { homedir } from "node:os";
|
|
15
15
|
import { join } from "node:path";
|
|
16
16
|
|
|
17
|
-
const gsdHome = process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
18
17
|
import { gsdRoot } from "./paths.js";
|
|
19
18
|
import { parse as parseYaml } from "yaml";
|
|
20
19
|
import type { PostUnitHookConfig, PreDispatchHookConfig, TokenProfile } from "./types.js";
|
|
@@ -83,24 +82,36 @@ export {
|
|
|
83
82
|
|
|
84
83
|
// ─── Path Constants & Getters ───────────────────────────────────────────────
|
|
85
84
|
|
|
86
|
-
|
|
87
|
-
|
|
85
|
+
function gsdHome(): string {
|
|
86
|
+
return process.env.GSD_HOME || join(homedir(), ".gsd");
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function globalPreferencesPath(): string {
|
|
90
|
+
return join(gsdHome(), "preferences.md");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function legacyGlobalPreferencesPath(): string {
|
|
94
|
+
return join(homedir(), ".pi", "agent", "gsd-preferences.md");
|
|
95
|
+
}
|
|
96
|
+
|
|
88
97
|
function projectPreferencesPath(): string {
|
|
89
98
|
return join(gsdRoot(process.cwd()), "preferences.md");
|
|
90
99
|
}
|
|
91
100
|
// Bootstrap in gitignore.ts historically created PREFERENCES.md (uppercase) by mistake.
|
|
92
101
|
// Check uppercase as a fallback so those files aren't silently ignored.
|
|
93
|
-
|
|
102
|
+
function globalPreferencesPathUppercase(): string {
|
|
103
|
+
return join(gsdHome(), "PREFERENCES.md");
|
|
104
|
+
}
|
|
94
105
|
function projectPreferencesPathUppercase(): string {
|
|
95
106
|
return join(gsdRoot(process.cwd()), "PREFERENCES.md");
|
|
96
107
|
}
|
|
97
108
|
|
|
98
109
|
export function getGlobalGSDPreferencesPath(): string {
|
|
99
|
-
return
|
|
110
|
+
return globalPreferencesPath();
|
|
100
111
|
}
|
|
101
112
|
|
|
102
113
|
export function getLegacyGlobalGSDPreferencesPath(): string {
|
|
103
|
-
return
|
|
114
|
+
return legacyGlobalPreferencesPath();
|
|
104
115
|
}
|
|
105
116
|
|
|
106
117
|
export function getProjectGSDPreferencesPath(): string {
|
|
@@ -110,9 +121,9 @@ export function getProjectGSDPreferencesPath(): string {
|
|
|
110
121
|
// ─── Loading ────────────────────────────────────────────────────────────────
|
|
111
122
|
|
|
112
123
|
export function loadGlobalGSDPreferences(): LoadedGSDPreferences | null {
|
|
113
|
-
return loadPreferencesFile(
|
|
114
|
-
?? loadPreferencesFile(
|
|
115
|
-
?? loadPreferencesFile(
|
|
124
|
+
return loadPreferencesFile(globalPreferencesPath(), "global")
|
|
125
|
+
?? loadPreferencesFile(globalPreferencesPathUppercase(), "global")
|
|
126
|
+
?? loadPreferencesFile(legacyGlobalPreferencesPath(), "global");
|
|
116
127
|
}
|
|
117
128
|
|
|
118
129
|
export function loadProjectGSDPreferences(): LoadedGSDPreferences | null {
|
|
@@ -271,6 +282,9 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
|
|
|
271
282
|
context_selection: override.context_selection ?? base.context_selection,
|
|
272
283
|
auto_visualize: override.auto_visualize ?? base.auto_visualize,
|
|
273
284
|
auto_report: override.auto_report ?? base.auto_report,
|
|
285
|
+
github: (base.github || override.github)
|
|
286
|
+
? { ...(base.github ?? {}), ...(override.github ?? {}) } as import("../github-sync/types.js").GitHubSyncConfig
|
|
287
|
+
: undefined,
|
|
274
288
|
};
|
|
275
289
|
}
|
|
276
290
|
|
|
@@ -78,6 +78,11 @@ export function loadPrompt(name: string, vars: Record<string, string> = {}): str
|
|
|
78
78
|
templateCache.set(name, content);
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
const effectiveVars = {
|
|
82
|
+
skillActivation: "If a `GSD Skill Preferences` block is present in system context, use it and the `<available_skills>` catalog in your system prompt to decide which skills to load and follow for this unit, without relaxing required verification or artifact rules.",
|
|
83
|
+
...vars,
|
|
84
|
+
};
|
|
85
|
+
|
|
81
86
|
// Check BEFORE substitution: find all {{varName}} placeholders the template
|
|
82
87
|
// declares and verify every one has a value in vars. Checking after substitution
|
|
83
88
|
// would also flag {{...}} patterns injected by inlined content (e.g. template
|
|
@@ -86,7 +91,7 @@ export function loadPrompt(name: string, vars: Record<string, string> = {}): str
|
|
|
86
91
|
if (declared) {
|
|
87
92
|
const missing = [...new Set(declared)]
|
|
88
93
|
.map(m => m.slice(2, -2))
|
|
89
|
-
.filter(key => !(key in
|
|
94
|
+
.filter(key => !(key in effectiveVars));
|
|
90
95
|
if (missing.length > 0) {
|
|
91
96
|
throw new GSDError(
|
|
92
97
|
GSD_PARSE_ERROR,
|
|
@@ -97,7 +102,7 @@ export function loadPrompt(name: string, vars: Record<string, string> = {}): str
|
|
|
97
102
|
}
|
|
98
103
|
}
|
|
99
104
|
|
|
100
|
-
for (const [key, value] of Object.entries(
|
|
105
|
+
for (const [key, value] of Object.entries(effectiveVars)) {
|
|
101
106
|
content = content.replaceAll(`{{${key}}}`, value);
|
|
102
107
|
}
|
|
103
108
|
|
|
@@ -16,7 +16,7 @@ All relevant context has been preloaded below — the roadmap, all slice summari
|
|
|
16
16
|
|
|
17
17
|
Then:
|
|
18
18
|
1. Use the **Milestone Summary** output template from the inlined context above
|
|
19
|
-
2.
|
|
19
|
+
2. {{skillActivation}}
|
|
20
20
|
3. Verify each **success criterion** from the milestone definition in `{{roadmapPath}}`. For each criterion, confirm it was met with specific evidence from slice summaries, test results, or observable behavior. List any criterion that was NOT met.
|
|
21
21
|
4. Verify the milestone's **definition of done** — all slices are `[x]`, all slice summaries exist, and any cross-slice integration points work correctly.
|
|
22
22
|
5. Validate **requirement status transitions**. For each requirement that changed status during this milestone, confirm the transition is supported by evidence. Requirements can move between Active, Validated, Deferred, Blocked, or Out of Scope — but only with proof.
|
|
@@ -20,7 +20,7 @@ All relevant context has been preloaded below — the slice plan, all task summa
|
|
|
20
20
|
|
|
21
21
|
Then:
|
|
22
22
|
1. Use the **Slice Summary** and **UAT** output templates from the inlined context above
|
|
23
|
-
2.
|
|
23
|
+
2. {{skillActivation}}
|
|
24
24
|
3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
|
|
25
25
|
4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
|
|
26
26
|
5. If `.gsd/REQUIREMENTS.md` exists, update it based on what this slice actually proved. Move requirements between Active, Validated, Deferred, Blocked, or Out of Scope only when the evidence from execution supports that change.
|
|
@@ -10,6 +10,8 @@ A researcher explored the codebase and a planner decomposed the work — you are
|
|
|
10
10
|
|
|
11
11
|
{{overridesSection}}
|
|
12
12
|
|
|
13
|
+
{{runtimeContext}}
|
|
14
|
+
|
|
13
15
|
{{resumeSection}}
|
|
14
16
|
|
|
15
17
|
{{carryForwardSection}}
|
|
@@ -26,7 +28,7 @@ A researcher explored the codebase and a planner decomposed the work — you are
|
|
|
26
28
|
|
|
27
29
|
Then:
|
|
28
30
|
0. Narrate step transitions, key implementation decisions, and verification outcomes as you work. Keep it terse — one line between tool-call clusters, not between every call — but write complete sentences in user-facing prose, not shorthand notes or scratchpad fragments.
|
|
29
|
-
1.
|
|
31
|
+
1. {{skillActivation}} Follow any activated skills before writing code. If no skills match this task, skip this step.
|
|
30
32
|
2. Execute the steps in the inlined task plan, adapting minor local mismatches when the surrounding code differs from the planner's snapshot
|
|
31
33
|
3. Build the real thing. If the task plan says "create login endpoint", build an endpoint that actually authenticates against a real store, not one that returns a hardcoded success response. If the task plan says "create dashboard page", build a page that renders real data from the API, not a component with hardcoded props. Stubs and mocks are for tests, not for the shipped feature.
|
|
32
34
|
4. Write or update tests as part of execution — tests are verification, not an afterthought. If the slice plan defines test files in its Verification section and this is the first task, create them (they should initially fail).
|