@tekyzinc/gsd-t 3.29.11 → 4.0.12

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 (108) hide show
  1. package/CHANGELOG.md +146 -0
  2. package/bin/gsd-t-parallel.cjs +64 -13
  3. package/bin/gsd-t.js +48 -376
  4. package/bin/journey-coverage.cjs +6 -3
  5. package/bin/parallel-cli.cjs +13 -1
  6. package/commands/gsd-t-debug.md +47 -533
  7. package/commands/gsd-t-design-decompose.md +24 -497
  8. package/commands/gsd-t-doc-ripple.md +23 -139
  9. package/commands/gsd-t-execute.md +41 -958
  10. package/commands/gsd-t-help.md +0 -2
  11. package/commands/gsd-t-impact.md +26 -295
  12. package/commands/gsd-t-integrate.md +44 -369
  13. package/commands/gsd-t-milestone.md +25 -124
  14. package/commands/gsd-t-partition.md +28 -539
  15. package/commands/gsd-t-plan.md +26 -472
  16. package/commands/gsd-t-prd.md +25 -311
  17. package/commands/gsd-t-quick.md +1 -1
  18. package/commands/gsd-t-resume.md +0 -33
  19. package/commands/gsd-t-verify.md +52 -569
  20. package/commands/gsd-t-wave.md +41 -430
  21. package/package.json +1 -1
  22. package/scripts/gsd-t-calibration-hook.js +3 -1
  23. package/scripts/hooks/pre-commit-journey-coverage +0 -2
  24. package/scripts/hooks/pre-commit-playwright-gate +0 -2
  25. package/templates/CLAUDE-global.md +43 -173
  26. package/templates/CLAUDE-project.md +118 -104
  27. package/templates/prompts/design-verify-subagent.md +3 -0
  28. package/templates/prompts/qa-subagent.md +3 -0
  29. package/templates/prompts/red-team-subagent.md +5 -2
  30. package/templates/workflows/_lib.js +176 -0
  31. package/templates/workflows/gsd-t-debug.workflow.js +86 -0
  32. package/templates/workflows/gsd-t-execute.workflow.js +206 -0
  33. package/templates/workflows/gsd-t-integrate.workflow.js +71 -0
  34. package/templates/workflows/gsd-t-phase.workflow.js +94 -0
  35. package/templates/workflows/gsd-t-quick.workflow.js +72 -0
  36. package/templates/workflows/gsd-t-verify.workflow.js +348 -0
  37. package/templates/workflows/gsd-t-wave.workflow.js +43 -0
  38. package/bin/check-headless-sessions.js +0 -140
  39. package/bin/context-budget-audit.cjs +0 -447
  40. package/bin/context-meter-config.cjs +0 -101
  41. package/bin/context-meter-config.test.cjs +0 -101
  42. package/bin/event-stream.cjs +0 -205
  43. package/bin/gsd-t-benchmark-orchestrator.js +0 -437
  44. package/bin/gsd-t-capture-lint.cjs +0 -440
  45. package/bin/gsd-t-economics.cjs +0 -315
  46. package/bin/gsd-t-in-session-usage.cjs +0 -213
  47. package/bin/gsd-t-orchestrator-config.cjs +0 -161
  48. package/bin/gsd-t-orchestrator-queue.cjs +0 -180
  49. package/bin/gsd-t-orchestrator-recover.cjs +0 -231
  50. package/bin/gsd-t-orchestrator-worker.cjs +0 -219
  51. package/bin/gsd-t-orchestrator.js +0 -535
  52. package/bin/gsd-t-parallel-probe.cjs +0 -132
  53. package/bin/gsd-t-ratelimit-probe-worker.cjs +0 -237
  54. package/bin/gsd-t-ratelimit-probe.cjs +0 -648
  55. package/bin/gsd-t-report-tokens.cjs +0 -549
  56. package/bin/gsd-t-stream-feed-client.cjs +0 -151
  57. package/bin/gsd-t-token-backfill.cjs +0 -366
  58. package/bin/gsd-t-token-capture.cjs +0 -321
  59. package/bin/gsd-t-token-dashboard.cjs +0 -353
  60. package/bin/gsd-t-token-regenerate-log.cjs +0 -129
  61. package/bin/gsd-t-tool-attribution.cjs +0 -377
  62. package/bin/gsd-t-tool-cost.cjs +0 -195
  63. package/bin/gsd-t-transcript-tee.cjs +0 -246
  64. package/bin/gsd-t-unattended-heartbeat.cjs +0 -188
  65. package/bin/gsd-t-unattended-platform.cjs +0 -551
  66. package/bin/gsd-t-unattended-platform.js +0 -381
  67. package/bin/gsd-t-unattended-safety.cjs +0 -773
  68. package/bin/gsd-t-unattended-safety.js +0 -788
  69. package/bin/gsd-t-unattended.cjs +0 -2109
  70. package/bin/gsd-t-unattended.js +0 -1367
  71. package/bin/gsd-t-worker-dispatch.cjs +0 -211
  72. package/bin/handoff-lock.cjs +0 -249
  73. package/bin/handoff-lock.js +0 -249
  74. package/bin/headless-auto-spawn.cjs +0 -711
  75. package/bin/headless-auto-spawn.js +0 -373
  76. package/bin/headless-exit-codes.cjs +0 -67
  77. package/bin/live-activity-report.cjs +0 -615
  78. package/bin/log-tail.cjs +0 -81
  79. package/bin/m44-proof-measure.cjs +0 -285
  80. package/bin/m46-iter-proof.cjs +0 -149
  81. package/bin/m46-worker-proof.cjs +0 -201
  82. package/bin/m55-substrate-proof.cjs +0 -134
  83. package/bin/metrics-rollup.js +0 -200
  84. package/bin/model-windows.cjs +0 -99
  85. package/bin/model-windows.test.cjs +0 -75
  86. package/bin/parallelism-report.cjs +0 -535
  87. package/bin/runway-estimator.cjs +0 -242
  88. package/bin/spawn-plan-derive.cjs +0 -163
  89. package/bin/spawn-plan-status-updater.cjs +0 -292
  90. package/bin/spawn-plan-writer.cjs +0 -204
  91. package/bin/supervisor-pid-fingerprint.cjs +0 -126
  92. package/bin/token-budget.cjs +0 -265
  93. package/bin/unattended-watch-format.cjs +0 -178
  94. package/bin/watch-progress.js +0 -155
  95. package/commands/gsd-t-unattended-stop.md +0 -85
  96. package/commands/gsd-t-unattended-watch.md +0 -465
  97. package/commands/gsd-t-unattended.md +0 -476
  98. package/commands/gsd-t-visualize.md +0 -116
  99. package/scripts/gsd-t-context-meter.e2e.test.js +0 -364
  100. package/scripts/gsd-t-context-meter.js +0 -341
  101. package/scripts/gsd-t-context-meter.test.js +0 -471
  102. package/scripts/gsd-t-dashboard-server.js +0 -1203
  103. package/scripts/gsd-t-post-commit-spawn-plan.sh +0 -86
  104. package/scripts/gsd-t-transcript.html +0 -1821
  105. package/scripts/hooks/gsd-t-conversation-capture.js +0 -439
  106. package/scripts/hooks/gsd-t-in-session-usage-hook.js +0 -84
  107. package/scripts/spawn-plan-fmt-tokens.cjs +0 -80
  108. package/templates/hooks/post-commit-spawn-plan.sh +0 -85
package/bin/gsd-t.js CHANGED
@@ -34,12 +34,6 @@ try {
34
34
  };
35
35
  }
36
36
 
37
- // Shared headless exit-code helper — lives in its own file so non-entry
38
- // modules (e.g. bin/gsd-t-unattended.cjs) can require it without pulling
39
- // in the full CLI. See commentary near the original declaration site and
40
- // in headless-exit-codes.cjs itself.
41
- const { mapHeadlessExitCode } = require(path.join(__dirname, "headless-exit-codes.cjs"));
42
-
43
37
  // ─── Configuration ───────────────────────────────────────────────────────────
44
38
 
45
39
  const CLAUDE_DIR = path.join(os.homedir(), ".claude");
@@ -1152,7 +1146,7 @@ function configureFigmaMcp() {
1152
1146
 
1153
1147
  // ─── Utility Scripts ─────────────────────────────────────────────────────────
1154
1148
 
1155
- const UTILITY_SCRIPTS = ["gsd-t-tools.js", "gsd-t-statusline.js", "gsd-t-event-writer.js", "gsd-t-dashboard-server.js", "gsd-t-dashboard.html", "gsd-t-transcript.html"];
1149
+ const UTILITY_SCRIPTS = ["gsd-t-tools.js", "gsd-t-statusline.js", "gsd-t-event-writer.js"];
1156
1150
 
1157
1151
  function installUtilityScripts() {
1158
1152
  ensureDir(SCRIPTS_DIR);
@@ -1760,17 +1754,11 @@ async function doInit(projectName) {
1760
1754
  initGsdtDir(projectDir, projectName, today);
1761
1755
  copyBinToolsToProject(projectDir, projectName);
1762
1756
 
1763
- // Context Meter: copy script + deps + config template, append .gitignore,
1764
- // register the global PostToolUse hook.
1765
- if (installContextMeter(projectDir)) {
1766
- success("Context meter installed (scripts/, config template, .gitignore)");
1767
- }
1768
- const cmHook = configureContextMeterHooks(SETTINGS_JSON);
1769
- if (cmHook.installed) {
1770
- if (cmHook.action === "added") success("Context meter PostToolUse hook added");
1771
- else if (cmHook.action === "updated") success("Context meter hook command refreshed");
1772
- else info("Context meter hook already configured");
1773
- }
1757
+ // M61 D1: Context Meter retired (token-budget.cjs + scripts/gsd-t-context-meter.js
1758
+ // deleted). Native /context replaces it. No longer provisioned at install —
1759
+ // installContextMeter / configureContextMeterHooks would register a PostToolUse
1760
+ // hook pointing at a deleted script. Both functions remain defined but unused
1761
+ // (cleaned in M65 follow-up).
1774
1762
 
1775
1763
  if (registerProject(projectDir)) success("Registered in ~/.claude/.gsd-t-projects");
1776
1764
 
@@ -2474,12 +2462,7 @@ function updateSingleProject(projectDir, counts) {
2474
2462
  // a new tool only requires appending to this array. Use .cjs extension so they
2475
2463
  // always run as CommonJS regardless of the project's package.json "type" field.
2476
2464
  const PROJECT_BIN_TOOLS = [
2477
- "archive-progress.cjs", "log-tail.cjs", "context-budget-audit.cjs",
2478
- "context-meter-config.cjs", "token-budget.cjs",
2479
- "gsd-t-token-capture.cjs",
2480
- "gsd-t-unattended.cjs", "gsd-t-unattended-platform.cjs", "gsd-t-unattended-safety.cjs",
2481
- "handoff-lock.cjs", "headless-auto-spawn.cjs",
2482
- "headless-exit-codes.cjs",
2465
+ "archive-progress.cjs",
2483
2466
  // M55 — preflight + parallel-cli + brief + verify-gate libraries copied to project bin/
2484
2467
  // so per-project workflows can `require('./bin/cli-preflight.cjs')` etc. without
2485
2468
  // depending on the globally-installed gsd-t CLI being on PATH.
@@ -3147,7 +3130,11 @@ async function doDoctor(opts) {
3147
3130
  issues += checkDoctorInstallation();
3148
3131
  issues += await checkDoctorProject(opts);
3149
3132
  issues += checkDoctorCgc();
3150
- issues += await checkDoctorContextMeter(process.cwd());
3133
+ // M61 D1: Context Meter retired (token-budget.cjs + context-meter hook deleted).
3134
+ // checkDoctorContextMeter would emit phantom errors for the deleted subsystem
3135
+ // and tell the user to reinstall it. Native /context replaces the meter.
3136
+ // M61 D4: dashboard orphan check left for the deleted dashboard-server is also
3137
+ // dead, but harmless (ps string-match, no require) — deferred.
3151
3138
  issues += checkDoctorDashboardOrphans(opts);
3152
3139
  // M50 D2: opt-in install of the playwright-gate pre-commit hook.
3153
3140
  if (opts && opts.installHooks) {
@@ -3508,11 +3495,43 @@ function buildHeadlessCmd(command, cmdArgs) {
3508
3495
  * Map claude output + process exit code to a GSD-T headless exit code.
3509
3496
  * Exit codes: 0=success, 1=verify-fail, 2=context-budget-exceeded, 3=error,
3510
3497
  * 4=blocked-needs-human, 5=command-dispatch-failed
3498
+ *
3499
+ * Inlined from the former bin/headless-exit-codes.cjs (M65) — that file existed
3500
+ * to decouple the exit-code map from the CLI so the unattended supervisor could
3501
+ * require it without pulling in gsd-t.js. The supervisor was retired in M61 D2,
3502
+ * so the only remaining requirer is this file; the helper now lives inline.
3503
+ *
3504
+ * Match terminal markers, not narration. A bare "tests failed" substring
3505
+ * appears in healthy output ("0 tests failed", "no tests failed", quoted in
3506
+ * prose). Require a non-zero count prefix or a structured terminal marker.
3507
+ * Bug history: M45 worker output contained "tests failed" 6× in narration,
3508
+ * mapping exit 0 → 1 and halting a successful run — keep these regexes anchored.
3511
3509
  */
3512
- // mapHeadlessExitCode now lives in ./headless-exit-codes.cjs — see re-export
3513
- // near the top of this file. Kept out-of-line here so non-entry modules
3514
- // (e.g. bin/gsd-t-unattended.cjs) can require the shared helper without
3515
- // pulling in the full CLI at bin/gsd-t.js.
3510
+ const HEADLESS_NONZERO_FAILURE_COUNT_RE =
3511
+ /(?:^|\b)([1-9]\d*)\s+(?:tests?|specs?|assertions?|examples?|suites?)\s+failed\b/i;
3512
+ const HEADLESS_STRUCTURED_FAIL_RE = /^FAIL[:\s]/m;
3513
+ const HEADLESS_JEST_SUMMARY_FAIL_RE = /^Tests:\s+\d+\s+failed/im;
3514
+ const HEADLESS_VERIFICATION_FAILED_RE =
3515
+ /(?:^|[.!?]\s+)(?:verification|verify|quality gate)\s+failed\b/im;
3516
+ const HEADLESS_CONTEXT_BUDGET_RE =
3517
+ /(?:^|[.!?]\s+)(?:context budget exceeded|context window exceeded|budget exceeded|token limit)\b/im;
3518
+ const HEADLESS_BLOCKED_HUMAN_RE =
3519
+ /\bblocked\b[\s\S]{0,80}?\b(?:needs? human|human input|human approval)\b/i;
3520
+
3521
+ function mapHeadlessExitCode(processExitCode, output) {
3522
+ if (processExitCode !== 0 && processExitCode !== null) return 3;
3523
+ const raw = output || "";
3524
+ if (/^unknown command:/im.test(raw)) return 5;
3525
+ if (HEADLESS_CONTEXT_BUDGET_RE.test(raw)) return 2;
3526
+ if (HEADLESS_BLOCKED_HUMAN_RE.test(raw)) return 4;
3527
+ if (
3528
+ HEADLESS_VERIFICATION_FAILED_RE.test(raw) ||
3529
+ HEADLESS_NONZERO_FAILURE_COUNT_RE.test(raw) ||
3530
+ HEADLESS_STRUCTURED_FAIL_RE.test(raw) ||
3531
+ HEADLESS_JEST_SUMMARY_FAIL_RE.test(raw)
3532
+ ) return 1;
3533
+ return 0;
3534
+ }
3516
3535
 
3517
3536
  /**
3518
3537
  * Generate a headless log file path.
@@ -4151,121 +4170,6 @@ function doMetrics(_args) {
4151
4170
  log(`${DIM}metrics removed in v3.12 — context meter is no longer telemetry-instrumented${RESET}`);
4152
4171
  }
4153
4172
 
4154
- function doStreamFeed(args) {
4155
- const sub = args[0];
4156
- const projectDir = process.cwd();
4157
- const pidFile = path.join(projectDir, ".gsd-t", "stream-feed", ".server.pid");
4158
- const portFile = path.join(projectDir, ".gsd-t", "stream-feed", ".server.port");
4159
-
4160
- function readPid() {
4161
- try { return parseInt(fs.readFileSync(pidFile, "utf8").trim(), 10); }
4162
- catch { return null; }
4163
- }
4164
- function readPort() {
4165
- try { return parseInt(fs.readFileSync(portFile, "utf8").trim(), 10); }
4166
- catch { return null; }
4167
- }
4168
- function isAlive(pid) {
4169
- if (!pid) return false;
4170
- try { process.kill(pid, 0); return true; } catch { return false; }
4171
- }
4172
-
4173
- if (!sub || sub === "help" || sub === "--help" || sub === "-h") {
4174
- log(`\n${BOLD}gsd-t stream-feed${RESET} — Localhost stream-json watcher (M40 D4)\n`);
4175
- log(`${BOLD}Usage:${RESET}`);
4176
- log(` gsd-t stream-feed ${CYAN}start${RESET} [--port N]`);
4177
- log(` gsd-t stream-feed ${CYAN}status${RESET}`);
4178
- log(` gsd-t stream-feed ${CYAN}stop${RESET}`);
4179
- log(`\nDefault port: 7842. Env override: GSD_T_STREAM_FEED_PORT.`);
4180
- return;
4181
- }
4182
-
4183
- if (sub === "start") {
4184
- const existing = readPid();
4185
- if (isAlive(existing)) {
4186
- log(`${YELLOW}stream-feed-server already running${RESET} (pid ${existing}, port ${readPort() || "?"})`);
4187
- return;
4188
- }
4189
- let port = null;
4190
- for (let i = 1; i < args.length; i++) {
4191
- if (args[i] === "--port") port = Number(args[++i]);
4192
- }
4193
- const { spawn } = require("child_process");
4194
- const serverScript = path.join(__dirname, "..", "scripts", "gsd-t-stream-feed-server.js");
4195
- const forward = ["--project-dir", projectDir];
4196
- if (port) forward.push("--port", String(port));
4197
- const feedDir = path.join(projectDir, ".gsd-t", "stream-feed");
4198
- try { fs.mkdirSync(feedDir, { recursive: true }); } catch { /* exists */ }
4199
- const out = fs.openSync(path.join(feedDir, "server.log"), "a");
4200
- const err = fs.openSync(path.join(feedDir, "server.log"), "a");
4201
- const child = spawn(process.execPath, [serverScript, ...forward], {
4202
- detached: true, stdio: ["ignore", out, err], cwd: projectDir,
4203
- });
4204
- child.unref();
4205
- try { fs.writeFileSync(pidFile, String(child.pid)); } catch { /* noop */ }
4206
- try { fs.writeFileSync(portFile, String(port || process.env.GSD_T_STREAM_FEED_PORT || 7842)); } catch { /* noop */ }
4207
- log(`${GREEN}✓${RESET} stream-feed-server started (pid ${child.pid}, port ${port || 7842})`);
4208
- log(` log: .gsd-t/stream-feed/server.log`);
4209
- return;
4210
- }
4211
-
4212
- if (sub === "status") {
4213
- const pid = readPid();
4214
- const port = readPort();
4215
- if (!isAlive(pid)) {
4216
- log(`${DIM}stream-feed-server not running${RESET}`);
4217
- return;
4218
- }
4219
- log(`${GREEN}●${RESET} stream-feed-server running`);
4220
- log(` pid: ${pid}`);
4221
- log(` port: ${port || 7842}`);
4222
- // Try to fetch live stats
4223
- try {
4224
- const http = require("http");
4225
- const done = { v: false };
4226
- const req = http.get({ host: "127.0.0.1", port: port || 7842, path: "/status", timeout: 1000 }, (res) => {
4227
- let body = "";
4228
- res.on("data", (c) => body += c);
4229
- res.on("end", () => {
4230
- try {
4231
- const j = JSON.parse(body);
4232
- log(` frames today: ${j.framesToday}`);
4233
- log(` clients: ${j.clients}`);
4234
- log(` stats: ingested=${j.stats.framesIngested} broadcast=${j.stats.framesBroadcast} kicked=${j.stats.kicked}`);
4235
- } catch { /* noop */ }
4236
- done.v = true;
4237
- });
4238
- });
4239
- req.on("error", () => { done.v = true; });
4240
- req.on("timeout", () => { req.destroy(); done.v = true; });
4241
- } catch { /* noop */ }
4242
- return;
4243
- }
4244
-
4245
- if (sub === "stop") {
4246
- const pid = readPid();
4247
- if (!isAlive(pid)) {
4248
- log(`${DIM}stream-feed-server not running${RESET}`);
4249
- try { fs.unlinkSync(pidFile); } catch { /* noop */ }
4250
- try { fs.unlinkSync(portFile); } catch { /* noop */ }
4251
- return;
4252
- }
4253
- try { process.kill(pid, "SIGTERM"); } catch { /* noop */ }
4254
- log(`${GREEN}✓${RESET} stream-feed-server stopped (pid ${pid})`);
4255
- // Clean up stale PID files after a short wait
4256
- setTimeout(() => {
4257
- if (!isAlive(pid)) {
4258
- try { fs.unlinkSync(pidFile); } catch { /* noop */ }
4259
- try { fs.unlinkSync(portFile); } catch { /* noop */ }
4260
- }
4261
- }, 500);
4262
- return;
4263
- }
4264
-
4265
- error(`Unknown stream-feed subcommand: ${sub}`);
4266
- log(`Try: gsd-t stream-feed --help`);
4267
- process.exit(1);
4268
- }
4269
4173
 
4270
4174
  function showHelp() {
4271
4175
  log(`\n${BOLD}GSD-T${RESET} — Contract-Driven Development for Claude Code\n`);
@@ -4282,12 +4186,7 @@ function showHelp() {
4282
4186
  log(` ${CYAN}changelog${RESET} Open changelog in the browser`);
4283
4187
  log(` ${CYAN}graph${RESET} Code graph operations (index, status, query)`);
4284
4188
  log(` ${CYAN}headless${RESET} Non-interactive execution via claude -p + fast state queries`);
4285
- log(` ${CYAN}orchestrate${RESET} External task orchestrator — one claude -p spawn per task (M40)`);
4286
- log(` ${CYAN}benchmark-orchestrator${RESET} M40 speed gate — compares orchestrator vs in-session wall-clock`);
4287
- log(` ${CYAN}stream-feed${RESET} Localhost stream-json watcher (start|status|stop) — M40 D4`);
4288
4189
  log(` ${CYAN}design-build${RESET} Deterministic design→code pipeline (elements → widgets → pages)`);
4289
- log(` ${CYAN}tool-cost${RESET} Per-tool token/cost attribution (M43 D2) — group-by tool|command|domain`);
4290
- log(` ${CYAN}report tokens${RESET} Generate token-usage optimization report (Run → Iter → CW → Turn → Tool)`);
4291
4190
  log(` ${CYAN}help${RESET} Show this help\n`);
4292
4191
  log(`${BOLD}Examples:${RESET}`);
4293
4192
  log(` ${DIM}$${RESET} npx @tekyzinc/gsd-t install`);
@@ -4495,22 +4394,6 @@ if (require.main === module) {
4495
4394
  case "headless":
4496
4395
  doHeadless(args.slice(1));
4497
4396
  break;
4498
- case "unattended": {
4499
- const { spawnSync } = require("child_process");
4500
- const cjs = path.join(__dirname, "gsd-t-unattended.cjs");
4501
- const res = spawnSync(process.execPath, [cjs, ...args.slice(1)], {
4502
- stdio: "inherit",
4503
- });
4504
- process.exit(res.status == null ? 1 : res.status);
4505
- }
4506
- case "orchestrate": {
4507
- const { spawnSync } = require("child_process");
4508
- const js = path.join(__dirname, "gsd-t-orchestrator.js");
4509
- const res = spawnSync(process.execPath, [js, ...args.slice(1)], {
4510
- stdio: "inherit",
4511
- });
4512
- process.exit(res.status == null ? 1 : res.status);
4513
- }
4514
4397
  case "parallel": {
4515
4398
  // M44 D2 — `gsd-t parallel` wraps M40 orchestrator with task-level
4516
4399
  // parallelism + mode-aware gating math. Extends, does not replace.
@@ -4518,14 +4401,6 @@ if (require.main === module) {
4518
4401
  const code = runParallelCli(args.slice(1), process.env);
4519
4402
  process.exit(code);
4520
4403
  }
4521
- case "benchmark-orchestrator": {
4522
- const { spawnSync } = require("child_process");
4523
- const js = path.join(__dirname, "gsd-t-benchmark-orchestrator.js");
4524
- const res = spawnSync(process.execPath, [js, ...args.slice(1)], {
4525
- stdio: "inherit",
4526
- });
4527
- process.exit(res.status == null ? 1 : res.status);
4528
- }
4529
4404
  case "preflight": {
4530
4405
  // M55 D5 — `gsd-t preflight` thin dispatcher to bin/cli-preflight.cjs.
4531
4406
  const { spawnSync } = require("child_process");
@@ -4589,212 +4464,9 @@ if (require.main === module) {
4589
4464
  });
4590
4465
  process.exit(res.status == null ? 1 : res.status);
4591
4466
  }
4592
- case "stream-feed": {
4593
- doStreamFeed(args.slice(1));
4594
- break;
4595
- }
4596
4467
  case "metrics":
4597
4468
  doMetrics(args.slice(1));
4598
4469
  break;
4599
- case "backfill-tokens": {
4600
- const bfOpts = { projectDir: process.cwd(), since: null, patchLog: false, dryRun: false };
4601
- for (let i = 1; i < args.length; i++) {
4602
- const a = args[i];
4603
- if (a === '--since' && args[i+1]) { bfOpts.since = args[++i]; }
4604
- else if (a.startsWith('--since=')) { bfOpts.since = a.slice(8); }
4605
- else if (a === '--patch-log') { bfOpts.patchLog = true; }
4606
- else if (a === '--dry-run') { bfOpts.dryRun = true; }
4607
- else if (a === '--project-dir' && args[i+1]) { bfOpts.projectDir = args[++i]; }
4608
- else if (a.startsWith('--project-dir=')) { bfOpts.projectDir = a.slice(14); }
4609
- else if (a === '--help' || a === '-h') {
4610
- log('Usage: gsd-t backfill-tokens [--since YYYY-MM-DD] [--patch-log] [--dry-run] [--project-dir PATH]');
4611
- process.exit(0);
4612
- }
4613
- else {
4614
- error(`backfill-tokens: unknown arg: ${a}`);
4615
- process.exit(2);
4616
- }
4617
- }
4618
- const backfill = require(path.join(__dirname, 'gsd-t-token-backfill.cjs'));
4619
- backfill.main(bfOpts)
4620
- .then(({ exitCode }) => process.exit(exitCode || 0))
4621
- .catch((e) => { error(e.message || String(e)); process.exit(3); });
4622
- break;
4623
- }
4624
- case "capture-lint": {
4625
- const clOpts = { projectDir: process.cwd(), mode: 'staged' };
4626
- let checkStreamJson = false;
4627
- for (let i = 1; i < args.length; i++) {
4628
- const a = args[i];
4629
- if (a === '--staged') { clOpts.mode = 'staged'; }
4630
- else if (a === '--all') { clOpts.mode = 'all'; }
4631
- else if (a === '--check-stream-json') { checkStreamJson = true; }
4632
- else if (a === '--project-dir' && args[i+1]) { clOpts.projectDir = args[++i]; }
4633
- else if (a.startsWith('--project-dir=')) { clOpts.projectDir = a.slice(14); }
4634
- else if (a === '--help' || a === '-h') {
4635
- log('Usage: gsd-t capture-lint [--staged] [--all] [--check-stream-json] [--project-dir PATH]');
4636
- log('');
4637
- log('Default mode (M41 D5): rejects bare Task() / claude -p / spawn() calls');
4638
- log(' missing the captureSpawn / recordSpawnRow wrapper.');
4639
- log('');
4640
- log('--check-stream-json (M56 D5): rejects claude -p / spawn(claude, ...) invocations');
4641
- log(' missing --output-format stream-json --verbose flag pair.');
4642
- log(' Allowlist via marker comment: GSD-T-LINT: skip stream-json (reason: …)');
4643
- process.exit(0);
4644
- }
4645
- else {
4646
- error(`capture-lint: unknown arg: ${a}`);
4647
- process.exit(2);
4648
- }
4649
- }
4650
- try {
4651
- const linter = require(path.join(__dirname, 'gsd-t-capture-lint.cjs'));
4652
- const res = checkStreamJson ? linter.mainStreamJson(clOpts) : linter.main(clOpts);
4653
- if (res.error) {
4654
- error(`capture-lint: ${res.error}`);
4655
- process.exit(2);
4656
- }
4657
- for (const v of res.violations) {
4658
- log(`${v.file}:${v.line}: ${v.message}`);
4659
- }
4660
- const modeLabel = checkStreamJson ? 'stream-json' : 'capture';
4661
- if (res.violations.length === 0) {
4662
- log(`${modeLabel}-lint: ${res.files.length} file(s) checked — clean`);
4663
- } else {
4664
- log(`${modeLabel}-lint: ${res.violations.length} violation(s) across ${res.files.length} file(s)`);
4665
- }
4666
- process.exit(res.exitCode);
4667
- } catch (e) {
4668
- error(e.message || String(e));
4669
- process.exit(2);
4670
- }
4671
- break;
4672
- }
4673
- case "tokens": {
4674
- const tkOpts = { projectDir: process.cwd(), since: null, milestone: null, format: 'table', regenerateLog: false, showToolCosts: false };
4675
- for (let i = 1; i < args.length; i++) {
4676
- const a = args[i];
4677
- if (a === '--since' && args[i+1]) { tkOpts.since = args[++i]; }
4678
- else if (a.startsWith('--since=')) { tkOpts.since = a.slice(8); }
4679
- else if (a === '--milestone' && args[i+1]) { tkOpts.milestone = args[++i]; }
4680
- else if (a.startsWith('--milestone=')) { tkOpts.milestone = a.slice(12); }
4681
- else if (a === '--format' && args[i+1]) { tkOpts.format = args[++i]; }
4682
- else if (a.startsWith('--format=')) { tkOpts.format = a.slice(9); }
4683
- else if (a === '--project-dir' && args[i+1]) { tkOpts.projectDir = args[++i]; }
4684
- else if (a.startsWith('--project-dir=')) { tkOpts.projectDir = a.slice(14); }
4685
- else if (a === '--regenerate-log') { tkOpts.regenerateLog = true; }
4686
- else if (a === '--show-tool-costs') { tkOpts.showToolCosts = true; }
4687
- else if (a === '--help' || a === '-h') {
4688
- log('Usage: gsd-t tokens [--since YYYY-MM-DD] [--milestone Mxx] [--format table|json] [--show-tool-costs]');
4689
- log(' gsd-t tokens --regenerate-log (rewrite .gsd-t/token-log.md from token-usage.jsonl)');
4690
- process.exit(0);
4691
- }
4692
- else {
4693
- error(`tokens: unknown arg: ${a}`);
4694
- process.exit(2);
4695
- }
4696
- }
4697
- if (tkOpts.regenerateLog) {
4698
- try {
4699
- const regen = require(path.join(__dirname, 'gsd-t-token-regenerate-log.cjs'));
4700
- const res = regen.regenerateLog({ projectDir: tkOpts.projectDir });
4701
- log(`Regenerated ${res.wrote} (${res.rowCount} row${res.rowCount === 1 ? '' : 's'})`);
4702
- process.exit(0);
4703
- } catch (e) {
4704
- error(e.message || String(e));
4705
- process.exit(3);
4706
- }
4707
- break;
4708
- }
4709
- if (tkOpts.format !== 'table' && tkOpts.format !== 'json') {
4710
- error(`tokens: --format must be 'table' or 'json' (got: ${tkOpts.format})`);
4711
- process.exit(2);
4712
- }
4713
- const dashboard = require(path.join(__dirname, 'gsd-t-token-dashboard.cjs'));
4714
- dashboard.aggregate(tkOpts)
4715
- .then((agg) => {
4716
- const baseOut = tkOpts.format === 'json' ? dashboard.renderJson(agg) : dashboard.renderTable(agg);
4717
- let out = baseOut;
4718
- if (tkOpts.showToolCosts) {
4719
- try {
4720
- out = out + '\n' + dashboard.renderToolCostsSection({
4721
- projectDir: tkOpts.projectDir,
4722
- since: tkOpts.since,
4723
- milestone: tkOpts.milestone,
4724
- format: tkOpts.format,
4725
- });
4726
- } catch (e) {
4727
- // Non-fatal: tool-cost section failure doesn't break primary output.
4728
- out = out + '\n── Top 10 tools by cost ──\n (tool-cost section unavailable: ' + (e.message || String(e)) + ')';
4729
- }
4730
- }
4731
- log(out);
4732
- process.exit(0);
4733
- })
4734
- .catch((e) => { error(e.message || String(e)); process.exit(3); });
4735
- break;
4736
- }
4737
- case "design-build": {
4738
- const orchestrator = require("./design-orchestrator.js");
4739
- orchestrator.run(args.slice(1)).catch(e => { console.error(e); process.exit(1); });
4740
- break;
4741
- }
4742
- case "tool-cost": {
4743
- const toolCost = require(path.join(__dirname, 'gsd-t-tool-cost.cjs'));
4744
- process.exit(toolCost.run(args.slice(1)));
4745
- break;
4746
- }
4747
- case "report": {
4748
- // Nested dispatch: `gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]`
4749
- const sub = args[1];
4750
- if (!sub || sub === '--help' || sub === '-h') {
4751
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4752
- log('');
4753
- log('Subcommands:');
4754
- log(' tokens Generate token-usage optimization report (Run → Iter → CW → Turn → Tool).');
4755
- process.exit(sub ? 0 : 2);
4756
- break;
4757
- }
4758
- if (sub !== 'tokens') {
4759
- error(`report: unknown subcommand: ${sub}`);
4760
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4761
- process.exit(2);
4762
- break;
4763
- }
4764
- const rOpts = { projectDir: process.cwd(), date: null, outPath: null };
4765
- for (let i = 2; i < args.length; i++) {
4766
- const a = args[i];
4767
- if (a === '--date' && args[i+1]) { rOpts.date = args[++i]; }
4768
- else if (a.startsWith('--date=')) { rOpts.date = a.slice(7); }
4769
- else if (a === '--out' && args[i+1]) { rOpts.outPath = args[++i]; }
4770
- else if (a.startsWith('--out=')) { rOpts.outPath = a.slice(6); }
4771
- else if (a === '--project-dir' && args[i+1]) { rOpts.projectDir = args[++i]; }
4772
- else if (a.startsWith('--project-dir=')) { rOpts.projectDir = a.slice(14); }
4773
- else if (a === '--help' || a === '-h') {
4774
- log('Usage: gsd-t report tokens [--date YYYY-MM-DD] [--out PATH]');
4775
- log(' --date YYYY-MM-DD Date stamp in output filename (default: today UTC)');
4776
- log(' --out PATH Output path (default: .gsd-t/reports/token-usage-{DATE}.md)');
4777
- process.exit(0);
4778
- }
4779
- else {
4780
- error(`report tokens: unknown arg: ${a}`);
4781
- process.exit(2);
4782
- }
4783
- }
4784
- try {
4785
- const rep = require(path.join(__dirname, 'gsd-t-report-tokens.cjs'));
4786
- const res = rep.generateReport(rOpts);
4787
- log(`Wrote ${res.path}`);
4788
- const s = res.summary;
4789
- log(` ${s.cws} CW(s) · ${s.turns} turn rows · ${s.compactions} compactions · ${s.sessions} session(s)`);
4790
- log(` ${s.compactionEndedCWs}/${s.cws} CW${s.cws === 1 ? '' : 's'} ended by compaction · top tool: ${s.topTool || '—'}`);
4791
- process.exit(0);
4792
- } catch (e) {
4793
- error(e.message || String(e));
4794
- process.exit(3);
4795
- }
4796
- break;
4797
- }
4798
4470
  case "scan": {
4799
4471
  const exportFlag = args.find(a => a.startsWith('--export='));
4800
4472
  const exportFormat = exportFlag ? exportFlag.split('=')[1] : null;
@@ -3,10 +3,13 @@
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
5
 
6
+ // M61 D4 retired the specific viewer HTML + dashboard server. The detector's
7
+ // HTML-listener scanning stays general-purpose: any `scripts/*.html` is a
8
+ // viewer-source surface (so a future viewer is covered automatically), plus
9
+ // the live journey specs. The retired files simply no longer exist to scan —
10
+ // `gsd-t check-coverage` returns 0 listeners until a new HTML surface appears.
6
11
  const VIEWER_FILE_PATTERNS = [
7
- /^scripts\/gsd-t-transcript\.html$/,
8
- /^scripts\/gsd-t-dashboard-server\.js$/,
9
- /^bin\/gsd-t-dashboard.*\.cjs$/,
12
+ /^scripts\/.*\.html$/,
10
13
  /^e2e\/journeys\/.*\.spec\.ts$/,
11
14
  ];
12
15
 
@@ -26,7 +26,19 @@ const { spawn } = require('child_process');
26
26
  const fs = require('fs');
27
27
  const path = require('path');
28
28
 
29
- const { captureSpawn } = require('./gsd-t-token-capture.cjs');
29
+ // M61 D3: gsd-t-token-capture retired. captureSpawn was a wrapper that
30
+ // parsed result.usage and wrote a token-log row, returning
31
+ // `{ result, usage, rowWritten }`. Native /usage replaces the token-log.
32
+ // Stub preserves the RETURN SHAPE (callers read `wrapped.result`) — only the
33
+ // token-log side-effect is dropped. Returning the bare spawn result would
34
+ // make every parallel-CLI worker read `wrapped.result === undefined` and
35
+ // report ok:false regardless of real outcome (M61 post-audit CRITICAL fix).
36
+ const captureSpawn = async (opts) => {
37
+ if (typeof opts.spawnFn === 'function') {
38
+ return { result: await opts.spawnFn(), usage: null, rowWritten: false };
39
+ }
40
+ return { result: null, usage: null, rowWritten: false };
41
+ };
30
42
  const { attachTee, VALID_ID_RE } = require('./parallel-cli-tee.cjs');
31
43
 
32
44
  const SCHEMA_VERSION = '1.0.0';