gsd-pi 2.26.0 → 2.27.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.
Files changed (171) hide show
  1. package/README.md +43 -6
  2. package/dist/cli.js +4 -2
  3. package/dist/headless.d.ts +3 -0
  4. package/dist/headless.js +136 -8
  5. package/dist/help-text.js +3 -0
  6. package/dist/loader.js +33 -4
  7. package/dist/resources/extensions/bg-shell/index.ts +19 -2
  8. package/dist/resources/extensions/bg-shell/process-manager.ts +45 -0
  9. package/dist/resources/extensions/bg-shell/types.ts +21 -1
  10. package/dist/resources/extensions/gsd/auto/session.ts +224 -0
  11. package/dist/resources/extensions/gsd/auto-budget.ts +32 -0
  12. package/dist/resources/extensions/gsd/auto-dashboard.ts +63 -10
  13. package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
  14. package/dist/resources/extensions/gsd/auto-dispatch.ts +23 -10
  15. package/dist/resources/extensions/gsd/auto-model-selection.ts +179 -0
  16. package/dist/resources/extensions/gsd/auto-observability.ts +74 -0
  17. package/dist/resources/extensions/gsd/auto-prompts.ts +0 -1
  18. package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
  19. package/dist/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
  20. package/dist/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
  21. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
  22. package/dist/resources/extensions/gsd/auto.ts +977 -1551
  23. package/dist/resources/extensions/gsd/commands.ts +3 -3
  24. package/dist/resources/extensions/gsd/dashboard-overlay.ts +47 -72
  25. package/dist/resources/extensions/gsd/doctor-proactive.ts +9 -4
  26. package/dist/resources/extensions/gsd/export-html.ts +1001 -0
  27. package/dist/resources/extensions/gsd/export.ts +49 -1
  28. package/dist/resources/extensions/gsd/git-service.ts +6 -0
  29. package/dist/resources/extensions/gsd/gitignore.ts +4 -1
  30. package/dist/resources/extensions/gsd/guided-flow.ts +24 -5
  31. package/dist/resources/extensions/gsd/index.ts +54 -1
  32. package/dist/resources/extensions/gsd/native-git-bridge.ts +30 -2
  33. package/dist/resources/extensions/gsd/observability-validator.ts +21 -0
  34. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
  35. package/dist/resources/extensions/gsd/preferences.ts +62 -1
  36. package/dist/resources/extensions/gsd/prompts/execute-task.md +4 -3
  37. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  38. package/dist/resources/extensions/gsd/reports.ts +510 -0
  39. package/dist/resources/extensions/gsd/roadmap-slices.ts +1 -1
  40. package/dist/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
  41. package/dist/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
  42. package/dist/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
  43. package/dist/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
  44. package/dist/resources/extensions/gsd/state.ts +30 -0
  45. package/dist/resources/extensions/gsd/templates/task-summary.md +9 -0
  46. package/dist/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
  47. package/dist/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
  48. package/dist/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
  49. package/dist/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
  50. package/dist/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
  51. package/dist/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
  52. package/dist/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
  53. package/dist/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
  54. package/dist/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
  55. package/dist/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
  56. package/dist/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
  57. package/dist/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
  58. package/dist/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
  59. package/dist/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
  60. package/dist/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
  61. package/dist/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
  62. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
  63. package/dist/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
  64. package/dist/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
  65. package/dist/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
  66. package/dist/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
  67. package/dist/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
  68. package/dist/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
  69. package/dist/resources/extensions/gsd/tests/worktree.test.ts +3 -1
  70. package/dist/resources/extensions/gsd/types.ts +38 -0
  71. package/dist/resources/extensions/gsd/verification-evidence.ts +183 -0
  72. package/dist/resources/extensions/gsd/verification-gate.ts +567 -0
  73. package/dist/resources/extensions/gsd/visualizer-data.ts +25 -3
  74. package/dist/resources/extensions/gsd/visualizer-overlay.ts +31 -21
  75. package/dist/resources/extensions/gsd/visualizer-views.ts +15 -66
  76. package/dist/resources/extensions/search-the-web/tool-search.ts +26 -0
  77. package/dist/resources/extensions/shared/format-utils.ts +85 -0
  78. package/dist/resources/extensions/shared/tests/format-utils.test.ts +153 -0
  79. package/dist/resources/extensions/subagent/index.ts +46 -1
  80. package/dist/resources/extensions/subagent/isolation.ts +9 -6
  81. package/package.json +1 -1
  82. package/packages/pi-ai/dist/providers/openai-completions.js +7 -4
  83. package/packages/pi-ai/dist/providers/openai-completions.js.map +1 -1
  84. package/packages/pi-ai/src/providers/openai-completions.ts +7 -4
  85. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  86. package/packages/pi-coding-agent/dist/core/lsp/client.js +7 -0
  87. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  88. package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
  89. package/packages/pi-coding-agent/dist/core/lsp/config.js +9 -2
  90. package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
  91. package/packages/pi-coding-agent/src/core/lsp/client.ts +8 -0
  92. package/packages/pi-coding-agent/src/core/lsp/config.ts +9 -2
  93. package/packages/pi-tui/dist/components/editor.d.ts.map +1 -1
  94. package/packages/pi-tui/dist/components/editor.js +1 -1
  95. package/packages/pi-tui/dist/components/editor.js.map +1 -1
  96. package/packages/pi-tui/src/components/editor.ts +3 -1
  97. package/scripts/link-workspace-packages.cjs +22 -6
  98. package/src/resources/extensions/bg-shell/index.ts +19 -2
  99. package/src/resources/extensions/bg-shell/process-manager.ts +45 -0
  100. package/src/resources/extensions/bg-shell/types.ts +21 -1
  101. package/src/resources/extensions/gsd/auto/session.ts +224 -0
  102. package/src/resources/extensions/gsd/auto-budget.ts +32 -0
  103. package/src/resources/extensions/gsd/auto-dashboard.ts +63 -10
  104. package/src/resources/extensions/gsd/auto-direct-dispatch.ts +229 -0
  105. package/src/resources/extensions/gsd/auto-dispatch.ts +23 -10
  106. package/src/resources/extensions/gsd/auto-model-selection.ts +179 -0
  107. package/src/resources/extensions/gsd/auto-observability.ts +74 -0
  108. package/src/resources/extensions/gsd/auto-prompts.ts +0 -1
  109. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +262 -0
  110. package/src/resources/extensions/gsd/auto-tool-tracking.ts +54 -0
  111. package/src/resources/extensions/gsd/auto-unit-closeout.ts +46 -0
  112. package/src/resources/extensions/gsd/auto-worktree-sync.ts +207 -0
  113. package/src/resources/extensions/gsd/auto.ts +977 -1551
  114. package/src/resources/extensions/gsd/commands.ts +3 -3
  115. package/src/resources/extensions/gsd/dashboard-overlay.ts +47 -72
  116. package/src/resources/extensions/gsd/doctor-proactive.ts +9 -4
  117. package/src/resources/extensions/gsd/export-html.ts +1001 -0
  118. package/src/resources/extensions/gsd/export.ts +49 -1
  119. package/src/resources/extensions/gsd/git-service.ts +6 -0
  120. package/src/resources/extensions/gsd/gitignore.ts +4 -1
  121. package/src/resources/extensions/gsd/guided-flow.ts +24 -5
  122. package/src/resources/extensions/gsd/index.ts +54 -1
  123. package/src/resources/extensions/gsd/native-git-bridge.ts +30 -2
  124. package/src/resources/extensions/gsd/observability-validator.ts +21 -0
  125. package/src/resources/extensions/gsd/parallel-orchestrator.ts +231 -20
  126. package/src/resources/extensions/gsd/preferences.ts +62 -1
  127. package/src/resources/extensions/gsd/prompts/execute-task.md +4 -3
  128. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  129. package/src/resources/extensions/gsd/reports.ts +510 -0
  130. package/src/resources/extensions/gsd/roadmap-slices.ts +1 -1
  131. package/src/resources/extensions/gsd/skills/gsd-headless/SKILL.md +178 -0
  132. package/src/resources/extensions/gsd/skills/gsd-headless/references/answer-injection.md +54 -0
  133. package/src/resources/extensions/gsd/skills/gsd-headless/references/commands.md +59 -0
  134. package/src/resources/extensions/gsd/skills/gsd-headless/references/multi-session.md +185 -0
  135. package/src/resources/extensions/gsd/state.ts +30 -0
  136. package/src/resources/extensions/gsd/templates/task-summary.md +9 -0
  137. package/src/resources/extensions/gsd/tests/auto-dashboard.test.ts +13 -0
  138. package/src/resources/extensions/gsd/tests/continue-here.test.ts +81 -0
  139. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +5 -0
  140. package/src/resources/extensions/gsd/tests/derive-state-deps.test.ts +1 -0
  141. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +1 -0
  142. package/src/resources/extensions/gsd/tests/derive-state.test.ts +10 -1
  143. package/src/resources/extensions/gsd/tests/dispatch-missing-task-plans.test.ts +132 -0
  144. package/src/resources/extensions/gsd/tests/doctor-proactive.test.ts +14 -0
  145. package/src/resources/extensions/gsd/tests/integration-mixed-milestones.test.ts +1 -0
  146. package/src/resources/extensions/gsd/tests/milestone-transition-worktree.test.ts +1 -1
  147. package/src/resources/extensions/gsd/tests/native-has-changes-cache.test.ts +61 -0
  148. package/src/resources/extensions/gsd/tests/network-error-fallback.test.ts +51 -1
  149. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +331 -0
  150. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +298 -0
  151. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +465 -0
  152. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +39 -10
  153. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +71 -0
  154. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +42 -0
  155. package/src/resources/extensions/gsd/tests/triage-dispatch.test.ts +9 -9
  156. package/src/resources/extensions/gsd/tests/verification-evidence.test.ts +743 -0
  157. package/src/resources/extensions/gsd/tests/verification-gate.test.ts +965 -0
  158. package/src/resources/extensions/gsd/tests/visualizer-data.test.ts +1 -1
  159. package/src/resources/extensions/gsd/tests/visualizer-overlay.test.ts +44 -10
  160. package/src/resources/extensions/gsd/tests/worktree.test.ts +3 -1
  161. package/src/resources/extensions/gsd/types.ts +38 -0
  162. package/src/resources/extensions/gsd/verification-evidence.ts +183 -0
  163. package/src/resources/extensions/gsd/verification-gate.ts +567 -0
  164. package/src/resources/extensions/gsd/visualizer-data.ts +25 -3
  165. package/src/resources/extensions/gsd/visualizer-overlay.ts +31 -21
  166. package/src/resources/extensions/gsd/visualizer-views.ts +15 -66
  167. package/src/resources/extensions/search-the-web/tool-search.ts +26 -0
  168. package/src/resources/extensions/shared/format-utils.ts +85 -0
  169. package/src/resources/extensions/shared/tests/format-utils.test.ts +153 -0
  170. package/src/resources/extensions/subagent/index.ts +46 -1
  171. package/src/resources/extensions/subagent/isolation.ts +9 -6
@@ -418,7 +418,7 @@ assertTrue(
418
418
  );
419
419
 
420
420
  assertTrue(
421
- overlaySrc.includes("0 Health"),
421
+ overlaySrc.includes("0 Export"),
422
422
  "overlay has 10 tab labels",
423
423
  );
424
424
 
@@ -24,18 +24,28 @@ assertTrue(
24
24
  );
25
25
 
26
26
  assertTrue(
27
- overlaySrc.includes('"5 Agent"'),
28
- "has Agent tab label",
27
+ overlaySrc.includes('"2 Timeline"'),
28
+ "has Timeline tab label",
29
29
  );
30
30
 
31
31
  assertTrue(
32
- overlaySrc.includes('"6 Changes"'),
33
- "has Changes tab label",
32
+ overlaySrc.includes('"3 Deps"'),
33
+ "has Deps tab label",
34
34
  );
35
35
 
36
36
  assertTrue(
37
- overlaySrc.includes('"7 Export"'),
38
- "has Export tab label",
37
+ overlaySrc.includes('"5 Health"'),
38
+ "has Health tab label",
39
+ );
40
+
41
+ assertTrue(
42
+ overlaySrc.includes('"6 Agent"'),
43
+ "has Agent tab label",
44
+ );
45
+
46
+ assertTrue(
47
+ overlaySrc.includes('"7 Changes"'),
48
+ "has Changes tab label",
39
49
  );
40
50
 
41
51
  assertTrue(
@@ -49,8 +59,8 @@ assertTrue(
49
59
  );
50
60
 
51
61
  assertTrue(
52
- overlaySrc.includes('"0 Health"'),
53
- "has Health tab label",
62
+ overlaySrc.includes('"0 Export"'),
63
+ "has Export tab label",
54
64
  );
55
65
 
56
66
  console.log("\n=== Overlay: Filter Mode ===");
@@ -162,8 +172,8 @@ assertTrue(
162
172
  console.log("\n=== Overlay: Export Key Interception ===");
163
173
 
164
174
  assertTrue(
165
- overlaySrc.includes("activeTab === 6"),
166
- "export key handling checks for tab 7 (index 6)",
175
+ overlaySrc.includes("activeTab === 9"),
176
+ "export key handling checks for tab 0 (index 9)",
167
177
  );
168
178
 
169
179
  assertTrue(
@@ -200,4 +210,28 @@ assertTrue(
200
210
  "scroll offsets sized to TAB_COUNT",
201
211
  );
202
212
 
213
+ console.log("\n=== Overlay: Terminal Resize Handling ===");
214
+
215
+ assertTrue(
216
+ overlaySrc.includes('resizeHandler'),
217
+ "has resizeHandler property",
218
+ );
219
+
220
+ assertTrue(
221
+ overlaySrc.includes('"resize"'),
222
+ "listens for resize events",
223
+ );
224
+
225
+ assertTrue(
226
+ overlaySrc.includes('removeListener("resize"'),
227
+ "removes resize listener on dispose",
228
+ );
229
+
230
+ console.log("\n=== Overlay: Shared Imports ===");
231
+
232
+ assertTrue(
233
+ overlaySrc.includes('from "../shared/format-utils.js"'),
234
+ "imports from shared format-utils",
235
+ );
236
+
203
237
  report();
@@ -15,6 +15,7 @@ import {
15
15
  SLICE_BRANCH_RE,
16
16
  } from "../worktree.ts";
17
17
  import { readIntegrationBranch } from "../git-service.ts";
18
+ import { _resetHasChangesCache } from "../native-git-bridge.ts";
18
19
  import { createTestContext } from './test-helpers.ts';
19
20
 
20
21
  const { assertEq, assertTrue, report } = createTestContext();
@@ -40,7 +41,8 @@ async function main(): Promise<void> {
40
41
  const cleanResult = autoCommitCurrentBranch(base, "execute-task", "M001/S01/T01");
41
42
  assertEq(cleanResult, null, "returns null for clean repo");
42
43
 
43
- // Make dirty
44
+ // Make dirty — reset the nativeHasChanges cache so the fresh dirt is detected
45
+ _resetHasChangesCache();
44
46
  writeFileSync(join(base, "dirty.txt"), "uncommitted\n", "utf-8");
45
47
  const dirtyResult = autoCommitCurrentBranch(base, "execute-task", "M001/S01/T01");
46
48
  assertTrue(dirtyResult !== null, "returns commit message for dirty repo");
@@ -46,6 +46,44 @@ export interface TaskPlanEntry {
46
46
  verify?: string; // e.g. "run tests" — extracted from "- Verify:" subline
47
47
  }
48
48
 
49
+ // ─── Verification Gate ─────────────────────────────────────────────────────
50
+
51
+ /** Result of a single verification command execution */
52
+ export interface VerificationCheck {
53
+ command: string; // e.g. "npm run lint"
54
+ exitCode: number; // 0 = pass
55
+ stdout: string;
56
+ stderr: string;
57
+ durationMs: number;
58
+ }
59
+
60
+ /** A runtime error captured from bg-shell processes or browser console */
61
+ export interface RuntimeError {
62
+ source: "bg-shell" | "browser";
63
+ severity: "crash" | "error" | "warning";
64
+ message: string;
65
+ blocking: boolean;
66
+ }
67
+
68
+ /** A dependency vulnerability warning from npm audit */
69
+ export interface AuditWarning {
70
+ name: string;
71
+ severity: "low" | "moderate" | "high" | "critical";
72
+ title: string;
73
+ url: string;
74
+ fixAvailable: boolean;
75
+ }
76
+
77
+ /** Aggregate result from the verification gate */
78
+ export interface VerificationResult {
79
+ passed: boolean; // true if all checks passed (or no checks discovered)
80
+ checks: VerificationCheck[]; // per-command results
81
+ discoverySource: "preference" | "task-plan" | "package-json" | "none";
82
+ timestamp: number; // Date.now() at gate start
83
+ runtimeErrors?: RuntimeError[]; // optional — populated by captureRuntimeErrors()
84
+ auditWarnings?: AuditWarning[]; // optional — populated by runDependencyAudit()
85
+ }
86
+
49
87
  export interface SlicePlan {
50
88
  id: string; // e.g. "S01"
51
89
  title: string; // from the H1
@@ -0,0 +1,183 @@
1
+ /**
2
+ * Verification Evidence — JSON persistence and markdown table formatting.
3
+ *
4
+ * Two pure-ish functions:
5
+ * - writeVerificationJSON: persists a machine-readable T##-VERIFY.json artifact
6
+ * - formatEvidenceTable: returns a markdown evidence table string
7
+ *
8
+ * JSON schema uses schemaVersion: 1 for forward-compatibility.
9
+ * stdout/stderr are intentionally excluded from the JSON to avoid unbounded file sizes.
10
+ */
11
+
12
+ import { mkdirSync, writeFileSync } from "node:fs";
13
+ import { join } from "node:path";
14
+ import type { VerificationResult } from "./types.ts";
15
+
16
+ // ─── JSON Evidence Artifact ──────────────────────────────────────────────────
17
+
18
+ export interface EvidenceCheckJSON {
19
+ command: string;
20
+ exitCode: number;
21
+ durationMs: number;
22
+ verdict: "pass" | "fail";
23
+ }
24
+
25
+ export interface RuntimeErrorJSON {
26
+ source: string;
27
+ severity: string;
28
+ message: string;
29
+ blocking: boolean;
30
+ }
31
+
32
+ export interface AuditWarningJSON {
33
+ name: string;
34
+ severity: string;
35
+ title: string;
36
+ url: string;
37
+ fixAvailable: boolean;
38
+ }
39
+
40
+ export interface EvidenceJSON {
41
+ schemaVersion: 1;
42
+ taskId: string;
43
+ unitId: string;
44
+ timestamp: number;
45
+ passed: boolean;
46
+ discoverySource: string;
47
+ checks: EvidenceCheckJSON[];
48
+ retryAttempt?: number;
49
+ maxRetries?: number;
50
+ runtimeErrors?: RuntimeErrorJSON[];
51
+ auditWarnings?: AuditWarningJSON[];
52
+ }
53
+
54
+ /**
55
+ * Write a T##-VERIFY.json artifact to the tasks directory.
56
+ * Creates the directory with mkdirSync({ recursive: true }) if it doesn't exist.
57
+ *
58
+ * stdout/stderr are excluded from the JSON — the full output lives in VerificationResult
59
+ * in memory and is logged to stderr during the gate run.
60
+ */
61
+ export function writeVerificationJSON(
62
+ result: VerificationResult,
63
+ tasksDir: string,
64
+ taskId: string,
65
+ unitId?: string,
66
+ retryAttempt?: number,
67
+ maxRetries?: number,
68
+ ): void {
69
+ mkdirSync(tasksDir, { recursive: true });
70
+
71
+ const evidence: EvidenceJSON = {
72
+ schemaVersion: 1,
73
+ taskId,
74
+ unitId: unitId ?? taskId,
75
+ timestamp: result.timestamp,
76
+ passed: result.passed,
77
+ discoverySource: result.discoverySource,
78
+ checks: result.checks.map((check) => ({
79
+ command: check.command,
80
+ exitCode: check.exitCode,
81
+ durationMs: check.durationMs,
82
+ verdict: check.exitCode === 0 ? "pass" : "fail",
83
+ })),
84
+ ...(retryAttempt !== undefined ? { retryAttempt } : {}),
85
+ ...(maxRetries !== undefined ? { maxRetries } : {}),
86
+ };
87
+
88
+ if (result.runtimeErrors && result.runtimeErrors.length > 0) {
89
+ evidence.runtimeErrors = result.runtimeErrors.map(e => ({
90
+ source: e.source,
91
+ severity: e.severity,
92
+ message: e.message,
93
+ blocking: e.blocking,
94
+ }));
95
+ }
96
+
97
+ if (result.auditWarnings && result.auditWarnings.length > 0) {
98
+ evidence.auditWarnings = result.auditWarnings.map(w => ({
99
+ name: w.name,
100
+ severity: w.severity,
101
+ title: w.title,
102
+ url: w.url,
103
+ fixAvailable: w.fixAvailable,
104
+ }));
105
+ }
106
+
107
+ const filePath = join(tasksDir, `${taskId}-VERIFY.json`);
108
+ writeFileSync(filePath, JSON.stringify(evidence, null, 2) + "\n", "utf-8");
109
+ }
110
+
111
+ // ─── Markdown Evidence Table ─────────────────────────────────────────────────
112
+
113
+ /**
114
+ * Format duration in milliseconds as seconds with 1 decimal place.
115
+ * e.g. 2340 → "2.3s", 150 → "0.2s", 0 → "0.0s"
116
+ */
117
+ function formatDuration(ms: number): string {
118
+ return `${(ms / 1000).toFixed(1)}s`;
119
+ }
120
+
121
+ /**
122
+ * Generate a markdown evidence table from a VerificationResult.
123
+ *
124
+ * Returns a "no checks" note if result.checks is empty.
125
+ * Otherwise returns a 5-column markdown table: #, Command, Exit Code, Verdict, Duration.
126
+ */
127
+ export function formatEvidenceTable(result: VerificationResult): string {
128
+ if (result.checks.length === 0) {
129
+ return "_No verification checks discovered._";
130
+ }
131
+
132
+ const lines: string[] = [
133
+ "| # | Command | Exit Code | Verdict | Duration |",
134
+ "|---|---------|-----------|---------|----------|",
135
+ ];
136
+
137
+ for (let i = 0; i < result.checks.length; i++) {
138
+ const check = result.checks[i];
139
+ const num = i + 1;
140
+ const verdict =
141
+ check.exitCode === 0 ? "✅ pass" : "❌ fail";
142
+ const duration = formatDuration(check.durationMs);
143
+
144
+ lines.push(
145
+ `| ${num} | ${check.command} | ${check.exitCode} | ${verdict} | ${duration} |`,
146
+ );
147
+ }
148
+
149
+ if (result.runtimeErrors && result.runtimeErrors.length > 0) {
150
+ lines.push("");
151
+ lines.push("**Runtime Errors**");
152
+ lines.push("");
153
+ lines.push("| # | Source | Severity | Blocking | Message |");
154
+ lines.push("|---|--------|----------|----------|---------|");
155
+ for (let i = 0; i < result.runtimeErrors.length; i++) {
156
+ const err = result.runtimeErrors[i];
157
+ const blockIcon = err.blocking ? "🚫 yes" : "ℹ️ no";
158
+ lines.push(`| ${i + 1} | ${err.source} | ${err.severity} | ${blockIcon} | ${err.message.slice(0, 100)} |`);
159
+ }
160
+ }
161
+
162
+ if (result.auditWarnings && result.auditWarnings.length > 0) {
163
+ const severityEmoji: Record<string, string> = {
164
+ critical: "🔴",
165
+ high: "🟠",
166
+ moderate: "🟡",
167
+ low: "⚪",
168
+ };
169
+ lines.push("");
170
+ lines.push("**Audit Warnings**");
171
+ lines.push("");
172
+ lines.push("| # | Package | Severity | Title | Fix Available |");
173
+ lines.push("|---|---------|----------|-------|---------------|");
174
+ for (let i = 0; i < result.auditWarnings.length; i++) {
175
+ const w = result.auditWarnings[i];
176
+ const emoji = severityEmoji[w.severity] ?? "⚪";
177
+ const fix = w.fixAvailable ? "✅ yes" : "❌ no";
178
+ lines.push(`| ${i + 1} | ${w.name} | ${emoji} ${w.severity} | ${w.title} | ${fix} |`);
179
+ }
180
+ }
181
+
182
+ return lines.join("\n");
183
+ }