gsd-pi 2.39.0 → 2.40.0-dev.4a93031
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/dist/resource-loader.js +66 -2
- package/dist/resources/extensions/async-jobs/index.js +10 -0
- package/dist/resources/extensions/get-secrets-from-user.js +1 -1
- package/dist/resources/extensions/gsd/auto-dashboard.js +7 -0
- package/dist/resources/extensions/gsd/auto-loop.js +761 -673
- package/dist/resources/extensions/gsd/auto-post-unit.js +10 -2
- package/dist/resources/extensions/gsd/auto-prompts.js +3 -3
- package/dist/resources/extensions/gsd/auto-start.js +6 -1
- package/dist/resources/extensions/gsd/auto.js +6 -4
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +126 -0
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +233 -0
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +59 -0
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +38 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +156 -0
- package/dist/resources/extensions/gsd/bootstrap/register-shortcuts.js +46 -0
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +300 -0
- package/dist/resources/extensions/gsd/bootstrap/write-gate.js +38 -0
- package/dist/resources/extensions/gsd/commands/catalog.js +278 -0
- package/dist/resources/extensions/gsd/commands/context.js +84 -0
- package/dist/resources/extensions/gsd/commands/dispatcher.js +21 -0
- package/dist/resources/extensions/gsd/commands/handlers/auto.js +72 -0
- package/dist/resources/extensions/gsd/commands/handlers/core.js +246 -0
- package/dist/resources/extensions/gsd/commands/handlers/ops.js +166 -0
- package/dist/resources/extensions/gsd/commands/handlers/parallel.js +94 -0
- package/dist/resources/extensions/gsd/commands/handlers/workflow.js +102 -0
- package/dist/resources/extensions/gsd/commands/index.js +11 -0
- package/dist/resources/extensions/gsd/commands-handlers.js +1 -1
- package/dist/resources/extensions/gsd/commands.js +8 -1190
- package/dist/resources/extensions/gsd/dashboard-overlay.js +9 -0
- package/dist/resources/extensions/gsd/doctor-proactive.js +80 -10
- package/dist/resources/extensions/gsd/doctor.js +32 -2
- package/dist/resources/extensions/gsd/export-html.js +46 -0
- package/dist/resources/extensions/gsd/files.js +1 -1
- package/dist/resources/extensions/gsd/health-widget.js +1 -1
- package/dist/resources/extensions/gsd/index.js +4 -1115
- package/dist/resources/extensions/gsd/progress-score.js +20 -1
- package/dist/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/dist/resources/extensions/gsd/visualizer-data.js +26 -1
- package/dist/resources/extensions/gsd/visualizer-views.js +52 -0
- package/dist/welcome-screen.d.ts +3 -2
- package/dist/welcome-screen.js +66 -22
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/agent-session.d.ts +12 -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 +107 -24
- package/packages/pi-coding-agent/dist/core/agent-session.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts +2 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js +70 -0
- package/packages/pi-coding-agent/dist/core/skill-tool.test.js.map +1 -0
- package/packages/pi-coding-agent/dist/core/skills.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/skills.js +2 -1
- package/packages/pi-coding-agent/dist/core/skills.js.map +1 -1
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts +17 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js +244 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/chat-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts +3 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js +58 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/extension-ui-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts +12 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js +54 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/input-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts +6 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js +63 -0
- package/packages/pi-coding-agent/dist/modes/interactive/controllers/model-controller.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts +38 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.d.ts.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js +2 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode-state.js.map +1 -0
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.d.ts +1 -1
- 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 +15 -457
- package/packages/pi-coding-agent/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/packages/pi-coding-agent/src/core/agent-session.ts +122 -23
- package/packages/pi-coding-agent/src/core/skill-tool.test.ts +89 -0
- package/packages/pi-coding-agent/src/core/skills.ts +2 -1
- package/packages/pi-coding-agent/src/modes/interactive/controllers/chat-controller.ts +302 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/extension-ui-controller.ts +59 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/input-controller.ts +68 -0
- package/packages/pi-coding-agent/src/modes/interactive/controllers/model-controller.ts +71 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode-state.ts +37 -0
- package/packages/pi-coding-agent/src/modes/interactive/interactive-mode.ts +18 -510
- package/pkg/package.json +1 -1
- package/src/resources/extensions/async-jobs/index.ts +11 -0
- package/src/resources/extensions/get-secrets-from-user.ts +1 -1
- package/src/resources/extensions/gsd/auto-dashboard.ts +10 -0
- package/src/resources/extensions/gsd/auto-loop.ts +1075 -921
- package/src/resources/extensions/gsd/auto-post-unit.ts +10 -2
- package/src/resources/extensions/gsd/auto-prompts.ts +3 -3
- package/src/resources/extensions/gsd/auto-start.ts +6 -1
- package/src/resources/extensions/gsd/auto.ts +13 -10
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +142 -0
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +238 -0
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +90 -0
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +46 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +167 -0
- package/src/resources/extensions/gsd/bootstrap/register-shortcuts.ts +55 -0
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +340 -0
- package/src/resources/extensions/gsd/bootstrap/write-gate.ts +51 -0
- package/src/resources/extensions/gsd/commands/catalog.ts +301 -0
- package/src/resources/extensions/gsd/commands/context.ts +101 -0
- package/src/resources/extensions/gsd/commands/dispatcher.ts +32 -0
- package/src/resources/extensions/gsd/commands/handlers/auto.ts +74 -0
- package/src/resources/extensions/gsd/commands/handlers/core.ts +274 -0
- package/src/resources/extensions/gsd/commands/handlers/ops.ts +169 -0
- package/src/resources/extensions/gsd/commands/handlers/parallel.ts +118 -0
- package/src/resources/extensions/gsd/commands/handlers/workflow.ts +109 -0
- package/src/resources/extensions/gsd/commands/index.ts +14 -0
- package/src/resources/extensions/gsd/commands-handlers.ts +1 -1
- package/src/resources/extensions/gsd/commands.ts +10 -1329
- package/src/resources/extensions/gsd/dashboard-overlay.ts +10 -0
- package/src/resources/extensions/gsd/doctor-proactive.ts +106 -10
- package/src/resources/extensions/gsd/doctor.ts +47 -3
- package/src/resources/extensions/gsd/export-html.ts +51 -0
- package/src/resources/extensions/gsd/files.ts +1 -1
- package/src/resources/extensions/gsd/health-widget.ts +2 -1
- package/src/resources/extensions/gsd/index.ts +12 -1314
- package/src/resources/extensions/gsd/progress-score.ts +23 -0
- package/src/resources/extensions/gsd/prompts/forensics.md +121 -46
- package/src/resources/extensions/gsd/tests/auto-loop.test.ts +13 -9
- package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +3 -3
- package/src/resources/extensions/gsd/tests/provider-errors.test.ts +16 -16
- package/src/resources/extensions/gsd/tests/skill-activation.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +10 -10
- package/src/resources/extensions/gsd/visualizer-data.ts +51 -1
- package/src/resources/extensions/gsd/visualizer-views.ts +58 -0
- /package/dist/resources/extensions/{env-utils.js → gsd/env-utils.js} +0 -0
- /package/src/resources/extensions/{env-utils.ts → gsd/env-utils.ts} +0 -0
|
@@ -320,6 +320,16 @@ export class GSDDashboardOverlay {
|
|
|
320
320
|
: progressScore.level === "yellow" ? th.fg("warning", "●")
|
|
321
321
|
: th.fg("error", "●");
|
|
322
322
|
lines.push(row(`${progressIcon} ${th.fg("text", progressScore.summary)}`));
|
|
323
|
+
|
|
324
|
+
// Show signal details when degraded — real-time visibility into what doctor found
|
|
325
|
+
if (progressScore.level !== "green" && progressScore.signals.length > 0) {
|
|
326
|
+
for (const signal of progressScore.signals) {
|
|
327
|
+
const prefix = signal.kind === "positive" ? th.fg("success", " ✓")
|
|
328
|
+
: signal.kind === "negative" ? th.fg("error", " ✗")
|
|
329
|
+
: th.fg("dim", " ·");
|
|
330
|
+
lines.push(row(`${prefix} ${th.fg("dim", signal.label)}`));
|
|
331
|
+
}
|
|
332
|
+
}
|
|
323
333
|
}
|
|
324
334
|
lines.push(blank());
|
|
325
335
|
|
|
@@ -26,12 +26,26 @@ import { nativeBranchExists, nativeIsRepo } from "./native-git-bridge.js";
|
|
|
26
26
|
|
|
27
27
|
// ── Health Score Tracking ──────────────────────────────────────────────────
|
|
28
28
|
|
|
29
|
+
/** Compact issue detail stored per snapshot for real-time visibility. */
|
|
30
|
+
export interface HealthIssueDetail {
|
|
31
|
+
code: string;
|
|
32
|
+
message: string;
|
|
33
|
+
severity: "error" | "warning" | "info";
|
|
34
|
+
unitId: string;
|
|
35
|
+
}
|
|
36
|
+
|
|
29
37
|
export interface HealthSnapshot {
|
|
30
38
|
timestamp: number;
|
|
31
39
|
errors: number;
|
|
32
40
|
warnings: number;
|
|
33
41
|
fixesApplied: number;
|
|
34
42
|
unitIndex: number; // which unit dispatch triggered this snapshot
|
|
43
|
+
/** Top issues from the doctor run that produced this snapshot. */
|
|
44
|
+
issues: HealthIssueDetail[];
|
|
45
|
+
/** Fixes that were auto-applied during this snapshot's doctor run. */
|
|
46
|
+
fixes: string[];
|
|
47
|
+
/** Milestone/slice scope this snapshot belongs to (e.g. "M001" or "M001/S02"). */
|
|
48
|
+
scope?: string;
|
|
35
49
|
}
|
|
36
50
|
|
|
37
51
|
/** In-memory health history for the current auto-mode session. */
|
|
@@ -43,11 +57,33 @@ let consecutiveErrorUnits = 0;
|
|
|
43
57
|
/** Unit index counter for health tracking. */
|
|
44
58
|
let healthUnitIndex = 0;
|
|
45
59
|
|
|
60
|
+
/** Previous progress level for state transition detection. */
|
|
61
|
+
let previousProgressLevel: "green" | "yellow" | "red" = "green";
|
|
62
|
+
|
|
63
|
+
/** Callback for state transition notifications. Set by auto-mode. */
|
|
64
|
+
let onLevelChange: ((from: string, to: string, summary: string) => void) | null = null;
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Register a callback for progress level transitions (green→yellow, yellow→red, etc.).
|
|
68
|
+
* Called once when auto-mode starts. Pass null to unregister.
|
|
69
|
+
*/
|
|
70
|
+
export function setLevelChangeCallback(cb: ((from: string, to: string, summary: string) => void) | null): void {
|
|
71
|
+
onLevelChange = cb;
|
|
72
|
+
previousProgressLevel = "green";
|
|
73
|
+
}
|
|
74
|
+
|
|
46
75
|
/**
|
|
47
76
|
* Record a health snapshot after a doctor run.
|
|
48
|
-
* Called from the post-unit hook in auto.ts.
|
|
77
|
+
* Called from the post-unit hook in auto-post-unit.ts.
|
|
49
78
|
*/
|
|
50
|
-
export function recordHealthSnapshot(
|
|
79
|
+
export function recordHealthSnapshot(
|
|
80
|
+
errors: number,
|
|
81
|
+
warnings: number,
|
|
82
|
+
fixesApplied: number,
|
|
83
|
+
issues?: HealthIssueDetail[],
|
|
84
|
+
fixes?: string[],
|
|
85
|
+
scope?: string,
|
|
86
|
+
): void {
|
|
51
87
|
healthUnitIndex++;
|
|
52
88
|
healthHistory.push({
|
|
53
89
|
timestamp: Date.now(),
|
|
@@ -55,6 +91,9 @@ export function recordHealthSnapshot(errors: number, warnings: number, fixesAppl
|
|
|
55
91
|
warnings,
|
|
56
92
|
fixesApplied,
|
|
57
93
|
unitIndex: healthUnitIndex,
|
|
94
|
+
issues: issues ?? [],
|
|
95
|
+
fixes: fixes ?? [],
|
|
96
|
+
scope,
|
|
58
97
|
});
|
|
59
98
|
|
|
60
99
|
// Keep only the last 50 snapshots to bound memory
|
|
@@ -67,6 +106,19 @@ export function recordHealthSnapshot(errors: number, warnings: number, fixesAppl
|
|
|
67
106
|
} else {
|
|
68
107
|
consecutiveErrorUnits = 0;
|
|
69
108
|
}
|
|
109
|
+
|
|
110
|
+
// Detect progress level transitions and notify
|
|
111
|
+
if (onLevelChange) {
|
|
112
|
+
const newLevel = consecutiveErrorUnits >= 3 ? "red"
|
|
113
|
+
: consecutiveErrorUnits >= 1 || getHealthTrend() === "degrading" ? "yellow"
|
|
114
|
+
: "green";
|
|
115
|
+
if (newLevel !== previousProgressLevel) {
|
|
116
|
+
const topIssue = (issues ?? []).find(i => i.severity === "error") ?? (issues ?? [])[0];
|
|
117
|
+
const detail = topIssue ? `: ${topIssue.message}` : "";
|
|
118
|
+
onLevelChange(previousProgressLevel, newLevel, `Health ${previousProgressLevel} → ${newLevel}${detail}`);
|
|
119
|
+
previousProgressLevel = newLevel;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
70
122
|
}
|
|
71
123
|
|
|
72
124
|
/**
|
|
@@ -104,6 +156,27 @@ export function getHealthHistory(): readonly HealthSnapshot[] {
|
|
|
104
156
|
return healthHistory;
|
|
105
157
|
}
|
|
106
158
|
|
|
159
|
+
/**
|
|
160
|
+
* Get the latest health issues from the most recent snapshot.
|
|
161
|
+
* Returns issues from the last snapshot that had any, for real-time visibility.
|
|
162
|
+
*/
|
|
163
|
+
export function getLatestHealthIssues(): HealthIssueDetail[] {
|
|
164
|
+
for (let i = healthHistory.length - 1; i >= 0; i--) {
|
|
165
|
+
if (healthHistory[i]!.issues.length > 0) return healthHistory[i]!.issues;
|
|
166
|
+
}
|
|
167
|
+
return [];
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get the latest fixes applied from the most recent snapshot.
|
|
172
|
+
*/
|
|
173
|
+
export function getLatestHealthFixes(): string[] {
|
|
174
|
+
for (let i = healthHistory.length - 1; i >= 0; i--) {
|
|
175
|
+
if (healthHistory[i]!.fixes.length > 0) return healthHistory[i]!.fixes;
|
|
176
|
+
}
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
|
|
107
180
|
/**
|
|
108
181
|
* Reset health tracking state. Called on auto-mode start/stop.
|
|
109
182
|
*/
|
|
@@ -111,6 +184,7 @@ export function resetHealthTracking(): void {
|
|
|
111
184
|
healthHistory = [];
|
|
112
185
|
consecutiveErrorUnits = 0;
|
|
113
186
|
healthUnitIndex = 0;
|
|
187
|
+
previousProgressLevel = "green";
|
|
114
188
|
}
|
|
115
189
|
|
|
116
190
|
// ── Pre-Dispatch Health Gate ───────────────────────────────────────────────
|
|
@@ -285,26 +359,48 @@ export function resetEscalation(): void {
|
|
|
285
359
|
|
|
286
360
|
/**
|
|
287
361
|
* Format a health summary for display in the auto-mode dashboard.
|
|
362
|
+
* Human-readable with full words, not abbreviations.
|
|
288
363
|
*/
|
|
289
364
|
export function formatHealthSummary(): string {
|
|
290
365
|
if (healthHistory.length === 0) return "No health data yet.";
|
|
291
366
|
|
|
292
367
|
const latest = healthHistory[healthHistory.length - 1]!;
|
|
293
368
|
const trend = getHealthTrend();
|
|
294
|
-
const
|
|
369
|
+
const trendLabel = trend === "improving" ? "improving"
|
|
370
|
+
: trend === "degrading" ? "degrading"
|
|
371
|
+
: trend === "stable" ? "stable"
|
|
372
|
+
: "unknown";
|
|
295
373
|
const totalFixes = healthHistory.reduce((sum, s) => sum + s.fixesApplied, 0);
|
|
296
374
|
|
|
297
|
-
const parts = [
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
375
|
+
const parts: string[] = [];
|
|
376
|
+
|
|
377
|
+
// Error/warning summary
|
|
378
|
+
if (latest.errors === 0 && latest.warnings === 0) {
|
|
379
|
+
parts.push("No issues");
|
|
380
|
+
} else {
|
|
381
|
+
const counts: string[] = [];
|
|
382
|
+
if (latest.errors > 0) counts.push(`${latest.errors} error${latest.errors > 1 ? "s" : ""}`);
|
|
383
|
+
if (latest.warnings > 0) counts.push(`${latest.warnings} warning${latest.warnings > 1 ? "s" : ""}`);
|
|
384
|
+
parts.push(counts.join(", "));
|
|
385
|
+
}
|
|
386
|
+
|
|
387
|
+
parts.push(`trend ${trendLabel}`);
|
|
388
|
+
|
|
389
|
+
if (totalFixes > 0) {
|
|
390
|
+
parts.push(`${totalFixes} fix${totalFixes > 1 ? "es" : ""} applied`);
|
|
391
|
+
}
|
|
302
392
|
|
|
303
393
|
if (consecutiveErrorUnits > 0) {
|
|
304
|
-
parts.push(
|
|
394
|
+
parts.push(`${consecutiveErrorUnits} of ${ESCALATION_THRESHOLD} consecutive errors before escalation`);
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
// Include top issue from latest snapshot
|
|
398
|
+
if (latest.issues.length > 0) {
|
|
399
|
+
const topIssue = latest.issues.find(i => i.severity === "error") ?? latest.issues[0]!;
|
|
400
|
+
parts.push(`latest: ${topIssue.message}`);
|
|
305
401
|
}
|
|
306
402
|
|
|
307
|
-
return parts.join("
|
|
403
|
+
return parts.join(" · ");
|
|
308
404
|
}
|
|
309
405
|
|
|
310
406
|
/**
|
|
@@ -387,18 +387,62 @@ function detectCircularDependencies(slices: RoadmapSliceEntry[]): string[][] {
|
|
|
387
387
|
}
|
|
388
388
|
|
|
389
389
|
// ── Helper: doctor run history ──────────────────────────────────────────────
|
|
390
|
-
interface DoctorHistoryEntry {
|
|
390
|
+
export interface DoctorHistoryEntry {
|
|
391
|
+
ts: string;
|
|
392
|
+
ok: boolean;
|
|
393
|
+
errors: number;
|
|
394
|
+
warnings: number;
|
|
395
|
+
fixes: number;
|
|
396
|
+
codes: string[];
|
|
397
|
+
/** Issue messages with severity and scope (added in Phase 2). */
|
|
398
|
+
issues?: Array<{ severity: string; code: string; message: string; unitId: string }>;
|
|
399
|
+
/** Fix descriptions applied during this run (added in Phase 2). */
|
|
400
|
+
fixDescriptions?: string[];
|
|
401
|
+
/** Milestone/slice scope this doctor run was scoped to (e.g. "M001/S02"). */
|
|
402
|
+
scope?: string;
|
|
403
|
+
/** Human-readable one-line summary of this doctor run. */
|
|
404
|
+
summary?: string;
|
|
405
|
+
}
|
|
391
406
|
|
|
392
407
|
async function appendDoctorHistory(basePath: string, report: DoctorReport): Promise<void> {
|
|
393
408
|
try {
|
|
394
409
|
const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
|
|
410
|
+
const errorCount = report.issues.filter(i => i.severity === "error").length;
|
|
411
|
+
const warningCount = report.issues.filter(i => i.severity === "warning").length;
|
|
412
|
+
const issueDetails = report.issues
|
|
413
|
+
.filter(i => i.severity === "error" || i.severity === "warning")
|
|
414
|
+
.slice(0, 10) // cap to keep JSONL lines bounded
|
|
415
|
+
.map(i => ({ severity: i.severity, code: i.code, message: i.message, unitId: i.unitId }));
|
|
416
|
+
|
|
417
|
+
// Human-readable one-line summary
|
|
418
|
+
const summaryParts: string[] = [];
|
|
419
|
+
if (report.ok) {
|
|
420
|
+
summaryParts.push("Clean");
|
|
421
|
+
} else {
|
|
422
|
+
const counts: string[] = [];
|
|
423
|
+
if (errorCount > 0) counts.push(`${errorCount} error${errorCount > 1 ? "s" : ""}`);
|
|
424
|
+
if (warningCount > 0) counts.push(`${warningCount} warning${warningCount > 1 ? "s" : ""}`);
|
|
425
|
+
summaryParts.push(counts.join(", "));
|
|
426
|
+
}
|
|
427
|
+
if (report.fixesApplied.length > 0) {
|
|
428
|
+
summaryParts.push(`${report.fixesApplied.length} fixed`);
|
|
429
|
+
}
|
|
430
|
+
if (issueDetails.length > 0) {
|
|
431
|
+
const topIssue = issueDetails.find(i => i.severity === "error") ?? issueDetails[0]!;
|
|
432
|
+
summaryParts.push(topIssue.message);
|
|
433
|
+
}
|
|
434
|
+
|
|
395
435
|
const entry = JSON.stringify({
|
|
396
436
|
ts: new Date().toISOString(),
|
|
397
437
|
ok: report.ok,
|
|
398
|
-
errors:
|
|
399
|
-
warnings:
|
|
438
|
+
errors: errorCount,
|
|
439
|
+
warnings: warningCount,
|
|
400
440
|
fixes: report.fixesApplied.length,
|
|
401
441
|
codes: [...new Set(report.issues.map(i => i.code))],
|
|
442
|
+
issues: issueDetails.length > 0 ? issueDetails : undefined,
|
|
443
|
+
fixDescriptions: report.fixesApplied.length > 0 ? report.fixesApplied : undefined,
|
|
444
|
+
scope: (report as any).scope as string | undefined,
|
|
445
|
+
summary: summaryParts.join(" · "),
|
|
402
446
|
} satisfies DoctorHistoryEntry);
|
|
403
447
|
const existing = existsSync(historyPath) ? readFileSync(historyPath, "utf-8") : "";
|
|
404
448
|
await saveFile(historyPath, existing + entry + "\n");
|
|
@@ -296,9 +296,60 @@ function buildHealthSection(data: VisualizerData): string {
|
|
|
296
296
|
</tbody>
|
|
297
297
|
</table>` : '';
|
|
298
298
|
|
|
299
|
+
// Progress score section
|
|
300
|
+
let progressHtml = '';
|
|
301
|
+
if (h.progressScore) {
|
|
302
|
+
const ps = h.progressScore;
|
|
303
|
+
const scoreColor = ps.level === 'green' ? '#22c55e' : ps.level === 'yellow' ? '#eab308' : '#ef4444';
|
|
304
|
+
const signalRows = ps.signals.map(s => {
|
|
305
|
+
const icon = s.kind === 'positive' ? '✓' : s.kind === 'negative' ? '✗' : '·';
|
|
306
|
+
const color = s.kind === 'positive' ? '#22c55e' : s.kind === 'negative' ? '#ef4444' : '#888';
|
|
307
|
+
return `<div style="margin-left:1em;color:${color}">${icon} ${esc(s.label)}</div>`;
|
|
308
|
+
}).join('');
|
|
309
|
+
progressHtml = `
|
|
310
|
+
<h3>Progress Score</h3>
|
|
311
|
+
<div style="font-size:1.1em;font-weight:bold;color:${scoreColor}">● ${esc(ps.summary)}</div>
|
|
312
|
+
${signalRows}`;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Doctor history section
|
|
316
|
+
let historyHtml = '';
|
|
317
|
+
const doctorHistory = h.doctorHistory ?? [];
|
|
318
|
+
if (doctorHistory.length > 0) {
|
|
319
|
+
const historyRows = doctorHistory.slice(0, 20).map(entry => {
|
|
320
|
+
const statusIcon = entry.ok ? '✓' : '✗';
|
|
321
|
+
const statusColor = entry.ok ? '#22c55e' : '#ef4444';
|
|
322
|
+
const ts = entry.ts.replace('T', ' ').slice(0, 19);
|
|
323
|
+
const scopeTag = entry.scope ? `<span class="mono" style="color:#888"> [${esc(entry.scope)}]</span>` : '';
|
|
324
|
+
const summaryText = entry.summary ? esc(entry.summary) : `${entry.errors} errors, ${entry.warnings} warnings, ${entry.fixes} fixes`;
|
|
325
|
+
const issueDetails = (entry.issues ?? []).slice(0, 3).map(i => {
|
|
326
|
+
const iColor = i.severity === 'error' ? '#ef4444' : '#eab308';
|
|
327
|
+
return `<div style="margin-left:2em;color:${iColor};font-size:0.85em">${i.severity === 'error' ? '✗' : '⚠'} ${esc(i.message)} <span class="mono" style="color:#888">${esc(i.unitId)}</span></div>`;
|
|
328
|
+
}).join('');
|
|
329
|
+
const fixDetails = (entry.fixDescriptions ?? []).slice(0, 2).map(f =>
|
|
330
|
+
`<div style="margin-left:2em;color:#22c55e;font-size:0.85em">↳ ${esc(f)}</div>`
|
|
331
|
+
).join('');
|
|
332
|
+
return `<tr style="color:${statusColor}">
|
|
333
|
+
<td class="mono">${statusIcon}</td>
|
|
334
|
+
<td class="mono">${esc(ts)}${scopeTag}</td>
|
|
335
|
+
<td>${summaryText}</td>
|
|
336
|
+
</tr>
|
|
337
|
+
${issueDetails || fixDetails ? `<tr><td colspan="3">${issueDetails}${fixDetails}</td></tr>` : ''}`;
|
|
338
|
+
}).join('');
|
|
339
|
+
|
|
340
|
+
historyHtml = `
|
|
341
|
+
<h3>Doctor Run History</h3>
|
|
342
|
+
<table class="tbl">
|
|
343
|
+
<thead><tr><th></th><th>Time</th><th>Summary</th></tr></thead>
|
|
344
|
+
<tbody>${historyRows}</tbody>
|
|
345
|
+
</table>`;
|
|
346
|
+
}
|
|
347
|
+
|
|
299
348
|
return section('health', 'Health', `
|
|
300
349
|
<table class="tbl tbl-kv"><tbody>${rows.join('')}</tbody></table>
|
|
301
350
|
${tierRows}
|
|
351
|
+
${progressHtml}
|
|
352
|
+
${historyHtml}
|
|
302
353
|
`);
|
|
303
354
|
}
|
|
304
355
|
|
|
@@ -20,7 +20,7 @@ import type {
|
|
|
20
20
|
ManifestStatus,
|
|
21
21
|
} from './types.js';
|
|
22
22
|
|
|
23
|
-
import { checkExistingEnvKeys } from '
|
|
23
|
+
import { checkExistingEnvKeys } from './env-utils.js';
|
|
24
24
|
import { parseRoadmapSlices } from './roadmap-slices.js';
|
|
25
25
|
import { nativeParseRoadmap, nativeExtractSection, nativeParsePlanFile, nativeParseSummaryFile, NATIVE_UNAVAILABLE } from './native-parser-bridge.js';
|
|
26
26
|
import { debugTime, debugCount } from './debug-logger.js';
|
|
@@ -15,7 +15,8 @@ import { runEnvironmentChecks } from "./doctor-environment.js";
|
|
|
15
15
|
import { loadEffectiveGSDPreferences } from "./preferences.js";
|
|
16
16
|
import { loadLedgerFromDisk, getProjectTotals } from "./metrics.js";
|
|
17
17
|
import { describeNextUnit, estimateTimeRemaining, updateSliceProgressCache } from "./auto-dashboard.js";
|
|
18
|
-
import { projectRoot } from "./commands.js";
|
|
18
|
+
import { projectRoot } from "./commands/context.js";
|
|
19
|
+
import { deriveState, invalidateStateCache } from "./state.js";
|
|
19
20
|
import {
|
|
20
21
|
buildHealthLines,
|
|
21
22
|
detectHealthWidgetProjectState,
|