gsd-pi 2.60.0-dev.2580e65 → 2.60.0-dev.d9052f5
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/resources/extensions/ask-user-questions.js +4 -7
- package/dist/resources/extensions/gsd/auto/phases.js +7 -15
- package/dist/resources/extensions/gsd/auto-dashboard.js +8 -21
- package/dist/resources/extensions/gsd/auto-dispatch.js +3 -6
- package/dist/resources/extensions/gsd/auto-model-selection.js +9 -58
- package/dist/resources/extensions/gsd/auto-post-unit.js +2 -3
- package/dist/resources/extensions/gsd/auto-prompts.js +20 -36
- package/dist/resources/extensions/gsd/auto-recovery.js +18 -37
- package/dist/resources/extensions/gsd/auto-start.js +5 -9
- package/dist/resources/extensions/gsd/auto-timers.js +5 -11
- package/dist/resources/extensions/gsd/auto-unit-closeout.js +3 -5
- package/dist/resources/extensions/gsd/auto-verification.js +2 -3
- package/dist/resources/extensions/gsd/auto-worktree.js +55 -120
- package/dist/resources/extensions/gsd/auto.js +17 -39
- package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +3 -6
- package/dist/resources/extensions/gsd/bootstrap/db-tools.js +2 -2
- package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +10 -4
- package/dist/resources/extensions/gsd/bootstrap/journal-tools.js +1 -2
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +0 -7
- package/dist/resources/extensions/gsd/bootstrap/system-context.js +10 -11
- package/dist/resources/extensions/gsd/commands/catalog.js +0 -2
- package/dist/resources/extensions/gsd/commands-codebase.js +21 -48
- package/dist/resources/extensions/gsd/commands-inspect.js +1 -2
- package/dist/resources/extensions/gsd/commands-maintenance.js +19 -32
- package/dist/resources/extensions/gsd/complexity-classifier.js +4 -8
- package/dist/resources/extensions/gsd/custom-verification.js +2 -3
- package/dist/resources/extensions/gsd/gsd-db.js +13 -33
- package/dist/resources/extensions/gsd/guided-flow.js +9 -19
- package/dist/resources/extensions/gsd/init-wizard.js +0 -12
- package/dist/resources/extensions/gsd/markdown-renderer.js +9 -11
- package/dist/resources/extensions/gsd/md-importer.js +4 -5
- package/dist/resources/extensions/gsd/milestone-actions.js +2 -3
- package/dist/resources/extensions/gsd/milestone-ids.js +1 -2
- package/dist/resources/extensions/gsd/model-router.js +121 -156
- package/dist/resources/extensions/gsd/parallel-merge.js +3 -5
- package/dist/resources/extensions/gsd/parallel-orchestrator.js +14 -26
- package/dist/resources/extensions/gsd/preferences-types.js +0 -1
- package/dist/resources/extensions/gsd/preferences-validation.js +0 -45
- package/dist/resources/extensions/gsd/preferences.js +3 -15
- package/dist/resources/extensions/gsd/prompt-loader.js +2 -3
- package/dist/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/dist/resources/extensions/gsd/rule-registry.js +6 -7
- package/dist/resources/extensions/gsd/safe-fs.js +8 -6
- package/dist/resources/extensions/gsd/tools/complete-milestone.js +2 -3
- package/dist/resources/extensions/gsd/tools/complete-slice.js +2 -3
- package/dist/resources/extensions/gsd/tools/complete-task.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-milestone.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -3
- package/dist/resources/extensions/gsd/tools/plan-task.js +1 -2
- package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +4 -4
- package/dist/resources/extensions/gsd/tools/reopen-slice.js +1 -2
- package/dist/resources/extensions/gsd/tools/reopen-task.js +1 -2
- package/dist/resources/extensions/gsd/tools/replan-slice.js +1 -2
- package/dist/resources/extensions/gsd/tools/validate-milestone.js +1 -2
- package/dist/resources/extensions/gsd/triage-resolution.js +4 -11
- package/dist/resources/extensions/gsd/workflow-events.js +1 -2
- package/dist/resources/extensions/gsd/workflow-logger.js +4 -37
- package/dist/resources/extensions/gsd/workflow-migration.js +12 -14
- package/dist/resources/extensions/gsd/workflow-projections.js +2 -2
- package/dist/resources/extensions/gsd/workflow-reconcile.js +2 -2
- package/dist/resources/extensions/gsd/worktree-manager.js +14 -26
- package/dist/resources/extensions/shared/interview-ui.js +1 -3
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +19 -19
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +19 -19
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +2 -2
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/package.json +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/loader.js +0 -5
- package/packages/pi-coding-agent/dist/core/extensions/loader.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts +1 -2
- package/packages/pi-coding-agent/dist/core/extensions/runner.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/runner.js +0 -16
- package/packages/pi-coding-agent/dist/core/extensions/runner.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts +0 -26
- package/packages/pi-coding-agent/dist/core/extensions/types.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/extensions/types.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.d.ts.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/config.js +1 -6
- package/packages/pi-coding-agent/dist/core/lsp/config.js.map +1 -1
- package/packages/pi-coding-agent/dist/core/lsp/defaults.json +2 -2
- package/packages/pi-coding-agent/src/core/extensions/loader.ts +0 -6
- package/packages/pi-coding-agent/src/core/extensions/runner.ts +0 -19
- package/packages/pi-coding-agent/src/core/extensions/types.ts +0 -26
- package/packages/pi-coding-agent/src/core/lsp/config.ts +1 -7
- package/packages/pi-coding-agent/src/core/lsp/defaults.json +2 -2
- package/src/resources/extensions/ask-user-questions.ts +3 -7
- package/src/resources/extensions/gsd/auto/phases.ts +7 -17
- package/src/resources/extensions/gsd/auto-dashboard.ts +8 -22
- package/src/resources/extensions/gsd/auto-dispatch.ts +3 -7
- package/src/resources/extensions/gsd/auto-model-selection.ts +15 -77
- package/src/resources/extensions/gsd/auto-post-unit.ts +4 -4
- package/src/resources/extensions/gsd/auto-prompts.ts +20 -37
- package/src/resources/extensions/gsd/auto-recovery.ts +18 -38
- package/src/resources/extensions/gsd/auto-start.ts +9 -10
- package/src/resources/extensions/gsd/auto-timers.ts +5 -12
- package/src/resources/extensions/gsd/auto-unit-closeout.ts +2 -6
- package/src/resources/extensions/gsd/auto-verification.ts +6 -3
- package/src/resources/extensions/gsd/auto-worktree.ts +55 -121
- package/src/resources/extensions/gsd/auto.ts +17 -40
- package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +3 -4
- package/src/resources/extensions/gsd/bootstrap/db-tools.ts +2 -2
- package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +16 -4
- package/src/resources/extensions/gsd/bootstrap/journal-tools.ts +1 -2
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +0 -8
- package/src/resources/extensions/gsd/bootstrap/system-context.ts +10 -11
- package/src/resources/extensions/gsd/commands/catalog.ts +0 -2
- package/src/resources/extensions/gsd/commands-codebase.ts +20 -52
- package/src/resources/extensions/gsd/commands-inspect.ts +1 -2
- package/src/resources/extensions/gsd/commands-maintenance.ts +19 -28
- package/src/resources/extensions/gsd/complexity-classifier.ts +4 -9
- package/src/resources/extensions/gsd/custom-verification.ts +2 -3
- package/src/resources/extensions/gsd/gsd-db.ts +14 -12
- package/src/resources/extensions/gsd/guided-flow.ts +8 -9
- package/src/resources/extensions/gsd/init-wizard.ts +0 -12
- package/src/resources/extensions/gsd/markdown-renderer.ts +17 -11
- package/src/resources/extensions/gsd/md-importer.ts +4 -5
- package/src/resources/extensions/gsd/milestone-actions.ts +2 -3
- package/src/resources/extensions/gsd/milestone-ids.ts +1 -2
- package/src/resources/extensions/gsd/model-router.ts +173 -199
- package/src/resources/extensions/gsd/parallel-merge.ts +3 -5
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +14 -18
- package/src/resources/extensions/gsd/preferences-types.ts +0 -13
- package/src/resources/extensions/gsd/preferences-validation.ts +0 -45
- package/src/resources/extensions/gsd/preferences.ts +3 -16
- package/src/resources/extensions/gsd/prompt-loader.ts +2 -3
- package/src/resources/extensions/gsd/prompts/rethink.md +1 -1
- package/src/resources/extensions/gsd/rule-registry.ts +6 -7
- package/src/resources/extensions/gsd/safe-fs.ts +5 -6
- package/src/resources/extensions/gsd/tests/codebase-generator.test.ts +0 -63
- package/src/resources/extensions/gsd/tests/complexity-classifier.test.ts +2 -27
- package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
- package/src/resources/extensions/gsd/tests/model-router.test.ts +3 -403
- package/src/resources/extensions/gsd/tests/preferences.test.ts +0 -62
- package/src/resources/extensions/gsd/tests/remote-questions.test.ts +0 -21
- package/src/resources/extensions/gsd/tests/workflow-logger.test.ts +6 -6
- package/src/resources/extensions/gsd/tools/complete-milestone.ts +6 -3
- package/src/resources/extensions/gsd/tools/complete-slice.ts +6 -3
- package/src/resources/extensions/gsd/tools/complete-task.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-milestone.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-slice.ts +6 -3
- package/src/resources/extensions/gsd/tools/plan-task.ts +3 -2
- package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +6 -4
- package/src/resources/extensions/gsd/tools/reopen-slice.ts +3 -2
- package/src/resources/extensions/gsd/tools/reopen-task.ts +3 -2
- package/src/resources/extensions/gsd/tools/replan-slice.ts +3 -2
- package/src/resources/extensions/gsd/tools/validate-milestone.ts +3 -2
- package/src/resources/extensions/gsd/triage-resolution.ts +4 -11
- package/src/resources/extensions/gsd/types.ts +0 -1
- package/src/resources/extensions/gsd/workflow-events.ts +1 -2
- package/src/resources/extensions/gsd/workflow-logger.ts +5 -52
- package/src/resources/extensions/gsd/workflow-migration.ts +12 -14
- package/src/resources/extensions/gsd/workflow-projections.ts +2 -2
- package/src/resources/extensions/gsd/workflow-reconcile.ts +2 -2
- package/src/resources/extensions/gsd/worktree-manager.ts +14 -16
- package/src/resources/extensions/shared/interview-ui.ts +1 -3
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts +0 -2
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.d.ts.map +0 -1
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js +0 -47
- package/packages/pi-coding-agent/dist/core/lsp/lsp-legacy-alias.test.js.map +0 -1
- package/packages/pi-coding-agent/src/core/lsp/lsp-legacy-alias.test.ts +0 -70
- package/src/resources/extensions/gsd/tests/capability-router.test.ts +0 -347
- package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +0 -1188
- package/src/resources/extensions/gsd/tests/integration/state-machine-runtime-failures.test.ts +0 -841
- package/src/resources/extensions/gsd/tests/silent-catch-diagnostics.test.ts +0 -284
- package/src/resources/extensions/gsd/tests/workflow-logger-audit.test.ts +0 -120
- package/src/resources/extensions/shared/tests/interview-notes-loop.test.ts +0 -144
- /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{ogyMN7M-3bGGuRY08L5HR → JVkoVYumy0cDhOQISEYdG}/_ssgManifest.js +0 -0
|
@@ -1,284 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Verify that catch blocks across GSD source files use the centralized
|
|
3
|
-
* workflow-logger (logWarning/logError) instead of raw process.stderr.write,
|
|
4
|
-
* console.error, or being completely empty (#3348, #3345).
|
|
5
|
-
*
|
|
6
|
-
* Two tests:
|
|
7
|
-
* 1. Auto-mode files must have zero empty catch blocks (fully migrated).
|
|
8
|
-
* 2. All GSD files must not use raw stderr/console in catch blocks.
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import { describe, test } from "node:test";
|
|
12
|
-
import assert from "node:assert/strict";
|
|
13
|
-
import { readFileSync, readdirSync, statSync } from "node:fs";
|
|
14
|
-
import { join, dirname, relative } from "node:path";
|
|
15
|
-
import { fileURLToPath } from "node:url";
|
|
16
|
-
|
|
17
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
const gsdDir = join(__dirname, "..");
|
|
19
|
-
|
|
20
|
-
/** Files exempt from the raw-stderr/console check */
|
|
21
|
-
const EXEMPT_FILES = new Set([
|
|
22
|
-
"workflow-logger.ts", // The logger itself
|
|
23
|
-
"debug-logger.ts", // Separate opt-in debug system
|
|
24
|
-
]);
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Files that have been fully migrated to workflow-logger and must not
|
|
28
|
-
* regress to empty catch blocks. Covers auto-mode, tools, bootstrap,
|
|
29
|
-
* and core infrastructure files.
|
|
30
|
-
*/
|
|
31
|
-
const MIGRATED_FILES = new Set([
|
|
32
|
-
// auto-mode (detected dynamically below)
|
|
33
|
-
// tools/
|
|
34
|
-
"tools/complete-task.ts",
|
|
35
|
-
"tools/complete-slice.ts",
|
|
36
|
-
"tools/complete-milestone.ts",
|
|
37
|
-
"tools/plan-milestone.ts",
|
|
38
|
-
"tools/plan-slice.ts",
|
|
39
|
-
"tools/plan-task.ts",
|
|
40
|
-
"tools/reassess-roadmap.ts",
|
|
41
|
-
"tools/reopen-task.ts",
|
|
42
|
-
"tools/reopen-slice.ts",
|
|
43
|
-
"tools/replan-slice.ts",
|
|
44
|
-
"tools/validate-milestone.ts",
|
|
45
|
-
// bootstrap/
|
|
46
|
-
"bootstrap/agent-end-recovery.ts",
|
|
47
|
-
"bootstrap/system-context.ts",
|
|
48
|
-
"bootstrap/db-tools.ts",
|
|
49
|
-
"bootstrap/dynamic-tools.ts",
|
|
50
|
-
"bootstrap/journal-tools.ts",
|
|
51
|
-
// core infrastructure
|
|
52
|
-
"gsd-db.ts",
|
|
53
|
-
"workflow-logger.ts",
|
|
54
|
-
"workflow-reconcile.ts",
|
|
55
|
-
"workflow-migration.ts",
|
|
56
|
-
"workflow-projections.ts",
|
|
57
|
-
"workflow-events.ts",
|
|
58
|
-
"worktree-manager.ts",
|
|
59
|
-
"parallel-orchestrator.ts",
|
|
60
|
-
"parallel-merge.ts",
|
|
61
|
-
"guided-flow.ts",
|
|
62
|
-
"preferences.ts",
|
|
63
|
-
"commands-maintenance.ts",
|
|
64
|
-
"commands-inspect.ts",
|
|
65
|
-
"safe-fs.ts",
|
|
66
|
-
"markdown-renderer.ts",
|
|
67
|
-
"md-importer.ts",
|
|
68
|
-
"milestone-actions.ts",
|
|
69
|
-
"milestone-ids.ts",
|
|
70
|
-
"rule-registry.ts",
|
|
71
|
-
"custom-verification.ts",
|
|
72
|
-
"prompt-loader.ts",
|
|
73
|
-
"auto-verification.ts",
|
|
74
|
-
]);
|
|
75
|
-
|
|
76
|
-
/** Patterns that indicate a catch block already uses workflow-logger */
|
|
77
|
-
const LOGGER_PATTERNS = [
|
|
78
|
-
/logWarning\s*\(/,
|
|
79
|
-
/logError\s*\(/,
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
function getAutoModeFiles(): string[] {
|
|
83
|
-
const files: string[] = [];
|
|
84
|
-
|
|
85
|
-
// Top-level auto*.ts files
|
|
86
|
-
for (const f of readdirSync(gsdDir)) {
|
|
87
|
-
if (f.startsWith("auto") && f.endsWith(".ts") && !f.endsWith(".test.ts")) {
|
|
88
|
-
files.push(join(gsdDir, f));
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
// auto/ subdirectory
|
|
93
|
-
const autoSubDir = join(gsdDir, "auto");
|
|
94
|
-
for (const f of readdirSync(autoSubDir)) {
|
|
95
|
-
if (f.endsWith(".ts") && !f.endsWith(".test.ts")) {
|
|
96
|
-
files.push(join(autoSubDir, f));
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return files;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function getGsdSourceFiles(): string[] {
|
|
104
|
-
const files: string[] = [];
|
|
105
|
-
|
|
106
|
-
function walk(dir: string): void {
|
|
107
|
-
for (const entry of readdirSync(dir)) {
|
|
108
|
-
const full = join(dir, entry);
|
|
109
|
-
if (entry === "tests" || entry === "node_modules") continue;
|
|
110
|
-
try {
|
|
111
|
-
const st = statSync(full);
|
|
112
|
-
if (st.isDirectory()) {
|
|
113
|
-
walk(full);
|
|
114
|
-
} else if (entry.endsWith(".ts") && !entry.endsWith(".test.ts") && !entry.endsWith(".d.ts")) {
|
|
115
|
-
files.push(full);
|
|
116
|
-
}
|
|
117
|
-
} catch {
|
|
118
|
-
continue;
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
walk(gsdDir);
|
|
124
|
-
return files;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
/**
|
|
128
|
-
* Scan a file for empty catch blocks — catches whose body contains
|
|
129
|
-
* only whitespace and/or comments but no executable statements.
|
|
130
|
-
*/
|
|
131
|
-
function findEmptyCatches(filePath: string): Array<{ line: number; text: string }> {
|
|
132
|
-
const content = readFileSync(filePath, "utf-8");
|
|
133
|
-
const lines = content.split("\n");
|
|
134
|
-
const results: Array<{ line: number; text: string }> = [];
|
|
135
|
-
|
|
136
|
-
for (let i = 0; i < lines.length; i++) {
|
|
137
|
-
const line = lines[i];
|
|
138
|
-
|
|
139
|
-
// Match catch block opening
|
|
140
|
-
if (!/\}\s*catch\s*(\([^)]*\))?\s*\{/.test(line)) continue;
|
|
141
|
-
|
|
142
|
-
// Inline single-line catch: } catch { ... }
|
|
143
|
-
const inlineMatch = line.match(/\}\s*catch\s*(\([^)]*\))?\s*\{(.*)\}\s*;?\s*$/);
|
|
144
|
-
if (inlineMatch) {
|
|
145
|
-
const body = inlineMatch[2].trim();
|
|
146
|
-
const stripped = body.replace(/\/\*.*?\*\//g, "").replace(/\/\/.*/g, "").trim();
|
|
147
|
-
if (!stripped) {
|
|
148
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
149
|
-
}
|
|
150
|
-
continue;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
// Multi-line catch — scan until matching }
|
|
154
|
-
let j = i + 1;
|
|
155
|
-
let depth = 1;
|
|
156
|
-
const bodyLines: string[] = [];
|
|
157
|
-
while (j < lines.length && depth > 0) {
|
|
158
|
-
for (const ch of lines[j]) {
|
|
159
|
-
if (ch === "{") depth++;
|
|
160
|
-
else if (ch === "}") depth--;
|
|
161
|
-
}
|
|
162
|
-
bodyLines.push(lines[j].trim());
|
|
163
|
-
j++;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
const meaningful = bodyLines.slice(0, -1).filter(
|
|
167
|
-
(l) => l && !l.startsWith("//") && !l.startsWith("/*") && !l.startsWith("*") && l !== "}",
|
|
168
|
-
);
|
|
169
|
-
|
|
170
|
-
if (meaningful.length === 0) {
|
|
171
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
return results;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Scan a file for catch blocks that use raw process.stderr.write or
|
|
180
|
-
* console.error/warn instead of workflow-logger.
|
|
181
|
-
*/
|
|
182
|
-
function findRawStderrCatches(filePath: string): Array<{ line: number; text: string }> {
|
|
183
|
-
const content = readFileSync(filePath, "utf-8");
|
|
184
|
-
const lines = content.split("\n");
|
|
185
|
-
const results: Array<{ line: number; text: string }> = [];
|
|
186
|
-
|
|
187
|
-
for (let i = 0; i < lines.length; i++) {
|
|
188
|
-
const line = lines[i];
|
|
189
|
-
if (!/\}\s*catch\s*(\([^)]*\))?\s*\{/.test(line)) continue;
|
|
190
|
-
|
|
191
|
-
// Inline single-line catch
|
|
192
|
-
const inlineMatch = line.match(/\}\s*catch\s*(\([^)]*\))?\s*\{(.*)\}\s*;?\s*$/);
|
|
193
|
-
if (inlineMatch) {
|
|
194
|
-
const body = inlineMatch[2];
|
|
195
|
-
if (!LOGGER_PATTERNS.some((p) => p.test(body))) {
|
|
196
|
-
if (/process\.stderr\.write/.test(body) || /console\.(error|warn)/.test(body)) {
|
|
197
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
continue;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// Multi-line catch
|
|
204
|
-
let j = i + 1;
|
|
205
|
-
let depth = 1;
|
|
206
|
-
const bodyLines: string[] = [];
|
|
207
|
-
while (j < lines.length && depth > 0) {
|
|
208
|
-
for (const ch of lines[j]) {
|
|
209
|
-
if (ch === "{") depth++;
|
|
210
|
-
else if (ch === "}") depth--;
|
|
211
|
-
}
|
|
212
|
-
bodyLines.push(lines[j]);
|
|
213
|
-
j++;
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const bodyText = bodyLines.slice(0, -1).join("\n");
|
|
217
|
-
if (!LOGGER_PATTERNS.some((p) => p.test(bodyText))) {
|
|
218
|
-
if (/process\.stderr\.write/.test(bodyText) || /console\.(error|warn)/.test(bodyText)) {
|
|
219
|
-
results.push({ line: i + 1, text: line.trim() });
|
|
220
|
-
}
|
|
221
|
-
}
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return results;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
describe("workflow-logger coverage (#3348)", () => {
|
|
228
|
-
test("no empty catch blocks remain in migrated files", () => {
|
|
229
|
-
// Combine auto-mode files + explicitly migrated files
|
|
230
|
-
const autoFiles = getAutoModeFiles();
|
|
231
|
-
const allFiles = getGsdSourceFiles();
|
|
232
|
-
const migratedPaths = new Set(autoFiles);
|
|
233
|
-
for (const file of allFiles) {
|
|
234
|
-
const rel = relative(gsdDir, file);
|
|
235
|
-
if (MIGRATED_FILES.has(rel)) {
|
|
236
|
-
migratedPaths.add(file);
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
assert.ok(migratedPaths.size > 0, "should find migrated source files");
|
|
241
|
-
|
|
242
|
-
const violations: string[] = [];
|
|
243
|
-
for (const file of migratedPaths) {
|
|
244
|
-
const rel = relative(gsdDir, file);
|
|
245
|
-
const basename = rel.split("/").pop()!;
|
|
246
|
-
// gsd-db.ts has intentionally silent provider probes
|
|
247
|
-
if (basename === "gsd-db.ts" || basename === "session-lock.ts") continue;
|
|
248
|
-
|
|
249
|
-
const empties = findEmptyCatches(file);
|
|
250
|
-
for (const empty of empties) {
|
|
251
|
-
violations.push(`${rel}:${empty.line} — ${empty.text}`);
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
assert.equal(
|
|
256
|
-
violations.length,
|
|
257
|
-
0,
|
|
258
|
-
`Found ${violations.length} empty catch block(s) in migrated files:\n${violations.join("\n")}`,
|
|
259
|
-
);
|
|
260
|
-
});
|
|
261
|
-
|
|
262
|
-
test("catch blocks use workflow-logger instead of raw stderr/console", () => {
|
|
263
|
-
const files = getGsdSourceFiles();
|
|
264
|
-
assert.ok(files.length > 0, "should find GSD source files");
|
|
265
|
-
|
|
266
|
-
const violations: string[] = [];
|
|
267
|
-
for (const file of files) {
|
|
268
|
-
const rel = relative(gsdDir, file);
|
|
269
|
-
const basename = rel.split("/").pop()!;
|
|
270
|
-
if (EXEMPT_FILES.has(basename)) continue;
|
|
271
|
-
|
|
272
|
-
const issues = findRawStderrCatches(file);
|
|
273
|
-
for (const issue of issues) {
|
|
274
|
-
violations.push(`${rel}:${issue.line} — ${issue.text}`);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
assert.equal(
|
|
279
|
-
violations.length,
|
|
280
|
-
0,
|
|
281
|
-
`Found ${violations.length} catch block(s) using raw stderr/console instead of workflow-logger:\n${violations.join("\n")}`,
|
|
282
|
-
);
|
|
283
|
-
});
|
|
284
|
-
});
|
|
@@ -1,120 +0,0 @@
|
|
|
1
|
-
// GSD Extension — Workflow Logger Audit Persistence Tests
|
|
2
|
-
// Validates error-only persistence, sanitization, and warning ephemeral behavior.
|
|
3
|
-
|
|
4
|
-
import { describe, test, beforeEach, afterEach } from "node:test";
|
|
5
|
-
import assert from "node:assert/strict";
|
|
6
|
-
import { mkdtempSync, mkdirSync, readFileSync, existsSync, rmSync } from "node:fs";
|
|
7
|
-
import { join } from "node:path";
|
|
8
|
-
import { tmpdir } from "node:os";
|
|
9
|
-
|
|
10
|
-
import {
|
|
11
|
-
logWarning,
|
|
12
|
-
logError,
|
|
13
|
-
setLogBasePath,
|
|
14
|
-
_resetLogs,
|
|
15
|
-
peekLogs,
|
|
16
|
-
drainLogs,
|
|
17
|
-
} from "../workflow-logger.ts";
|
|
18
|
-
|
|
19
|
-
function createTempProject(): string {
|
|
20
|
-
const tmp = mkdtempSync(join(tmpdir(), "gsd-wflog-test-"));
|
|
21
|
-
mkdirSync(join(tmp, ".gsd"), { recursive: true });
|
|
22
|
-
return tmp;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function readAuditLines(basePath: string): Record<string, unknown>[] {
|
|
26
|
-
const auditPath = join(basePath, ".gsd", "audit-log.jsonl");
|
|
27
|
-
if (!existsSync(auditPath)) return [];
|
|
28
|
-
const content = readFileSync(auditPath, "utf-8").trim();
|
|
29
|
-
if (!content) return [];
|
|
30
|
-
return content.split("\n").map((line) => JSON.parse(line));
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
describe("workflow-logger audit persistence", () => {
|
|
34
|
-
let tmp: string;
|
|
35
|
-
|
|
36
|
-
beforeEach(() => {
|
|
37
|
-
tmp = createTempProject();
|
|
38
|
-
_resetLogs();
|
|
39
|
-
setLogBasePath(tmp);
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
afterEach(() => {
|
|
43
|
-
_resetLogs();
|
|
44
|
-
setLogBasePath(null as unknown as string);
|
|
45
|
-
rmSync(tmp, { recursive: true, force: true });
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
test("logError persists to audit-log.jsonl", () => {
|
|
49
|
-
logError("engine", "something broke");
|
|
50
|
-
const lines = readAuditLines(tmp);
|
|
51
|
-
assert.equal(lines.length, 1);
|
|
52
|
-
assert.equal(lines[0].severity, "error");
|
|
53
|
-
assert.equal(lines[0].component, "engine");
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
test("logWarning does NOT persist to audit-log.jsonl", () => {
|
|
57
|
-
logWarning("engine", "something fishy");
|
|
58
|
-
const lines = readAuditLines(tmp);
|
|
59
|
-
assert.equal(lines.length, 0, "warnings must not be persisted to audit log");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
test("logWarning still appears in in-memory buffer", () => {
|
|
63
|
-
logWarning("recovery", "probe miss");
|
|
64
|
-
const entries = peekLogs();
|
|
65
|
-
assert.equal(entries.length, 1);
|
|
66
|
-
assert.equal(entries[0].severity, "warn");
|
|
67
|
-
assert.equal(entries[0].component, "recovery");
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("persisted error messages are truncated at 200 chars", () => {
|
|
71
|
-
const longMessage = "x".repeat(300);
|
|
72
|
-
logError("engine", longMessage);
|
|
73
|
-
const lines = readAuditLines(tmp);
|
|
74
|
-
assert.equal(lines.length, 1);
|
|
75
|
-
const msg = lines[0].message as string;
|
|
76
|
-
assert.ok(msg.length <= 215, `message should be truncated, got ${msg.length} chars`);
|
|
77
|
-
assert.ok(msg.endsWith("…[truncated]"));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
test("persisted errors have context filtered to safe allowlist", () => {
|
|
81
|
-
logError("tool", "tool failed", {
|
|
82
|
-
fn: "saveDecisionToDb",
|
|
83
|
-
tool: "gsd_decision_save",
|
|
84
|
-
error: "SQLITE_BUSY: database is locked",
|
|
85
|
-
file: "/home/user/project/gsd.db",
|
|
86
|
-
});
|
|
87
|
-
const lines = readAuditLines(tmp);
|
|
88
|
-
assert.equal(lines.length, 1);
|
|
89
|
-
const ctx = lines[0].context as Record<string, string>;
|
|
90
|
-
assert.ok(ctx, "context should exist");
|
|
91
|
-
assert.equal(ctx.fn, "saveDecisionToDb");
|
|
92
|
-
assert.equal(ctx.tool, "gsd_decision_save");
|
|
93
|
-
assert.equal(ctx.error, undefined, "error key must be stripped from persisted context");
|
|
94
|
-
assert.equal(ctx.file, undefined, "file key must be stripped from persisted context");
|
|
95
|
-
});
|
|
96
|
-
|
|
97
|
-
test("persisted errors omit context when no safe keys present", () => {
|
|
98
|
-
logError("bootstrap", "ensureDbOpen failed", {
|
|
99
|
-
error: "ENOENT",
|
|
100
|
-
cwd: "/home/user/project",
|
|
101
|
-
});
|
|
102
|
-
const lines = readAuditLines(tmp);
|
|
103
|
-
assert.equal(lines.length, 1);
|
|
104
|
-
assert.equal(lines[0].context, undefined, "context should be omitted when no safe keys match");
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
test("mixed warnings and errors only persist errors", () => {
|
|
108
|
-
logWarning("recovery", "main not found");
|
|
109
|
-
logWarning("recovery", "master not found");
|
|
110
|
-
logError("engine", "fatal failure");
|
|
111
|
-
logWarning("prompt", "cache miss");
|
|
112
|
-
|
|
113
|
-
const lines = readAuditLines(tmp);
|
|
114
|
-
assert.equal(lines.length, 1, "only the error should be persisted");
|
|
115
|
-
assert.equal(lines[0].severity, "error");
|
|
116
|
-
|
|
117
|
-
const buffered = drainLogs();
|
|
118
|
-
assert.equal(buffered.length, 4, "all entries should be in the in-memory buffer");
|
|
119
|
-
});
|
|
120
|
-
});
|
|
@@ -1,144 +0,0 @@
|
|
|
1
|
-
// GSD2 — Regression test for interview-ui "None of the above" notes loop
|
|
2
|
-
// Copyright (c) 2026 Jeremy McSpadden <jeremy@fluxlabs.net>
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Regression test for bug #3502:
|
|
6
|
-
*
|
|
7
|
-
* Selecting "None of the above" opens the notes field, but pressing Enter
|
|
8
|
-
* after typing a note called goNextOrSubmit() which saw the cursor still
|
|
9
|
-
* on the "None of the above" slot and re-opened notes — trapping the user
|
|
10
|
-
* in an infinite loop.
|
|
11
|
-
*
|
|
12
|
-
* The fix adds a `!states[currentIdx].notes` guard so auto-open only fires
|
|
13
|
-
* when notes are still empty.
|
|
14
|
-
*/
|
|
15
|
-
|
|
16
|
-
import { describe, it } from "node:test";
|
|
17
|
-
import assert from "node:assert/strict";
|
|
18
|
-
import { showInterviewRound, type Question, type RoundResult } from "../interview-ui.js";
|
|
19
|
-
|
|
20
|
-
// Raw terminal sequences that matchesKey() recognises
|
|
21
|
-
const ENTER = "\r";
|
|
22
|
-
const DOWN = "\x1b[B";
|
|
23
|
-
const TAB = "\t";
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Drive showInterviewRound with a scripted sequence of key inputs.
|
|
27
|
-
* We mock ctx.ui.custom() to capture the widget, feed it inputs, and
|
|
28
|
-
* resolve when done() is called.
|
|
29
|
-
*/
|
|
30
|
-
function runWithInputs(
|
|
31
|
-
questions: Question[],
|
|
32
|
-
inputs: string[],
|
|
33
|
-
): Promise<RoundResult> {
|
|
34
|
-
return new Promise((resolve, reject) => {
|
|
35
|
-
const timeout = setTimeout(() => reject(new Error("Timed out — likely stuck in infinite loop")), 3000);
|
|
36
|
-
|
|
37
|
-
const mockCtx = {
|
|
38
|
-
ui: {
|
|
39
|
-
custom: (factory: any) => {
|
|
40
|
-
const mockTui = {
|
|
41
|
-
requestRender: () => {},
|
|
42
|
-
};
|
|
43
|
-
const mockTheme = {
|
|
44
|
-
// Minimal theme stubs — render output is not asserted
|
|
45
|
-
fg: (_c: string, t: string) => t,
|
|
46
|
-
bold: (t: string) => t,
|
|
47
|
-
dim: (t: string) => t,
|
|
48
|
-
italic: (t: string) => t,
|
|
49
|
-
strikethrough: (t: string) => t,
|
|
50
|
-
accent: (t: string) => t,
|
|
51
|
-
success: (t: string) => t,
|
|
52
|
-
warning: (t: string) => t,
|
|
53
|
-
error: (t: string) => t,
|
|
54
|
-
info: (t: string) => t,
|
|
55
|
-
muted: (t: string) => t,
|
|
56
|
-
dimmed: (t: string) => t,
|
|
57
|
-
};
|
|
58
|
-
const mockKb = {};
|
|
59
|
-
|
|
60
|
-
const widget = factory(mockTui, mockTheme, mockKb, (result: RoundResult) => {
|
|
61
|
-
clearTimeout(timeout);
|
|
62
|
-
resolve(result);
|
|
63
|
-
});
|
|
64
|
-
|
|
65
|
-
// Feed each input sequentially
|
|
66
|
-
for (const input of inputs) {
|
|
67
|
-
widget.handleInput(input);
|
|
68
|
-
}
|
|
69
|
-
},
|
|
70
|
-
},
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
showInterviewRound(questions, {}, mockCtx as any).catch(reject);
|
|
74
|
-
});
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
describe("interview-ui notes loop regression (#3502)", () => {
|
|
78
|
-
const questions: Question[] = [
|
|
79
|
-
{
|
|
80
|
-
id: "q1",
|
|
81
|
-
header: "Project Type",
|
|
82
|
-
question: "What type of project?",
|
|
83
|
-
options: [
|
|
84
|
-
{ label: "Web App", description: "Frontend or full-stack" },
|
|
85
|
-
{ label: "CLI Tool", description: "Command-line utility" },
|
|
86
|
-
],
|
|
87
|
-
},
|
|
88
|
-
];
|
|
89
|
-
|
|
90
|
-
it("does not loop when Enter is pressed after typing a note on 'None of the above'", async () => {
|
|
91
|
-
// With 2 options, "None of the above" is index 2 (0-based)
|
|
92
|
-
// Cursor starts at 0, so press Down twice to reach it
|
|
93
|
-
const result = await runWithInputs(questions, [
|
|
94
|
-
DOWN, // cursor → index 1 (CLI Tool)
|
|
95
|
-
DOWN, // cursor → index 2 (None of the above)
|
|
96
|
-
ENTER, // commit → auto-opens notes field
|
|
97
|
-
"u", "n", "s", "u", "r", "e", // type "unsure"
|
|
98
|
-
ENTER, // should advance to review, NOT reopen notes
|
|
99
|
-
ENTER, // submit from review screen
|
|
100
|
-
]);
|
|
101
|
-
|
|
102
|
-
// If we get here, the loop did not occur (timeout would have fired)
|
|
103
|
-
assert.ok(result, "should return a result");
|
|
104
|
-
assert.equal(result.endInterview, false);
|
|
105
|
-
|
|
106
|
-
const answer = result.answers.q1;
|
|
107
|
-
assert.ok(answer, "answer for q1 should exist");
|
|
108
|
-
assert.equal(answer.notes, "unsure", "notes should contain typed text");
|
|
109
|
-
assert.equal(answer.selected, "None of the above");
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
it("still auto-opens notes when selecting 'None of the above' with no prior notes", async () => {
|
|
113
|
-
// Press Down twice to "None of the above", Enter to select
|
|
114
|
-
// Then immediately Enter again (empty notes) — this should re-open notes
|
|
115
|
-
// because the guard only skips when notes are non-empty.
|
|
116
|
-
// Type something on second open, then Enter to proceed.
|
|
117
|
-
const result = await runWithInputs(questions, [
|
|
118
|
-
DOWN, // cursor → 1
|
|
119
|
-
DOWN, // cursor → 2 (None of the above)
|
|
120
|
-
ENTER, // commit → auto-opens notes
|
|
121
|
-
ENTER, // empty notes → goNextOrSubmit → should re-open notes (empty guard)
|
|
122
|
-
"o", "k", // type "ok"
|
|
123
|
-
ENTER, // now notes = "ok" → should advance to review
|
|
124
|
-
ENTER, // submit
|
|
125
|
-
]);
|
|
126
|
-
|
|
127
|
-
assert.ok(result, "should return a result");
|
|
128
|
-
const answer = result.answers.q1;
|
|
129
|
-
assert.ok(answer, "answer for q1 should exist");
|
|
130
|
-
assert.equal(answer.notes, "ok");
|
|
131
|
-
});
|
|
132
|
-
|
|
133
|
-
it("normal option selection is unaffected", async () => {
|
|
134
|
-
const result = await runWithInputs(questions, [
|
|
135
|
-
ENTER, // select first option (Web App) and advance to review
|
|
136
|
-
ENTER, // submit from review screen
|
|
137
|
-
]);
|
|
138
|
-
|
|
139
|
-
assert.ok(result, "should return a result");
|
|
140
|
-
const answer = result.answers.q1;
|
|
141
|
-
assert.ok(answer, "answer for q1 should exist");
|
|
142
|
-
assert.equal(answer.selected, "Web App");
|
|
143
|
-
});
|
|
144
|
-
});
|
|
File without changes
|
|
File without changes
|