gsd-pi 2.38.0-dev.96dc7fb → 2.38.0-dev.eeb3520

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 (45) hide show
  1. package/dist/cli.js +0 -9
  2. package/dist/extension-discovery.d.ts +3 -5
  3. package/dist/extension-discovery.js +9 -14
  4. package/dist/resources/extensions/browser-tools/package.json +1 -3
  5. package/dist/resources/extensions/cmux/index.js +1 -55
  6. package/dist/resources/extensions/context7/package.json +1 -1
  7. package/dist/resources/extensions/google-search/package.json +1 -3
  8. package/dist/resources/extensions/gsd/auto-loop.js +1 -7
  9. package/dist/resources/extensions/gsd/auto-start.js +1 -6
  10. package/dist/resources/extensions/gsd/auto-worktree-sync.js +4 -11
  11. package/dist/resources/extensions/gsd/captures.js +1 -9
  12. package/dist/resources/extensions/gsd/commands-handlers.js +3 -16
  13. package/dist/resources/extensions/gsd/commands.js +1 -20
  14. package/dist/resources/extensions/gsd/doctor-checks.js +0 -82
  15. package/dist/resources/extensions/gsd/doctor-environment.js +0 -78
  16. package/dist/resources/extensions/gsd/doctor-format.js +0 -15
  17. package/dist/resources/extensions/gsd/doctor.js +11 -184
  18. package/dist/resources/extensions/gsd/package.json +1 -1
  19. package/dist/resources/extensions/gsd/worktree.js +16 -35
  20. package/dist/resources/extensions/subagent/index.js +3 -12
  21. package/dist/resources/extensions/universal-config/package.json +1 -1
  22. package/package.json +1 -1
  23. package/packages/pi-coding-agent/dist/core/package-manager.d.ts.map +1 -1
  24. package/packages/pi-coding-agent/dist/core/package-manager.js +4 -8
  25. package/packages/pi-coding-agent/dist/core/package-manager.js.map +1 -1
  26. package/packages/pi-coding-agent/src/core/package-manager.ts +4 -8
  27. package/src/resources/extensions/cmux/index.ts +1 -57
  28. package/src/resources/extensions/gsd/auto-loop.ts +1 -13
  29. package/src/resources/extensions/gsd/auto-start.ts +1 -7
  30. package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -12
  31. package/src/resources/extensions/gsd/captures.ts +1 -10
  32. package/src/resources/extensions/gsd/commands-handlers.ts +2 -17
  33. package/src/resources/extensions/gsd/commands.ts +1 -21
  34. package/src/resources/extensions/gsd/doctor-checks.ts +0 -75
  35. package/src/resources/extensions/gsd/doctor-environment.ts +1 -82
  36. package/src/resources/extensions/gsd/doctor-format.ts +0 -20
  37. package/src/resources/extensions/gsd/doctor-types.ts +1 -16
  38. package/src/resources/extensions/gsd/doctor.ts +13 -177
  39. package/src/resources/extensions/gsd/tests/cmux.test.ts +0 -93
  40. package/src/resources/extensions/gsd/tests/worktree.test.ts +0 -47
  41. package/src/resources/extensions/gsd/worktree.ts +15 -35
  42. package/src/resources/extensions/subagent/index.ts +3 -12
  43. package/dist/welcome-screen.d.ts +0 -12
  44. package/dist/welcome-screen.js +0 -53
  45. package/src/resources/extensions/gsd/tests/doctor-enhancements.test.ts +0 -266
@@ -289,17 +289,10 @@ export class CmuxClient {
289
289
  }
290
290
 
291
291
  async createSplit(direction: "right" | "down" | "left" | "up"): Promise<string | null> {
292
- return this.createSplitFrom(this.config.surfaceId, direction);
293
- }
294
-
295
- async createSplitFrom(
296
- sourceSurfaceId: string | undefined,
297
- direction: "right" | "down" | "left" | "up",
298
- ): Promise<string | null> {
299
292
  if (!this.config.splits) return null;
300
293
  const before = new Set(await this.listSurfaceIds());
301
294
  const args = ["new-split", direction];
302
- const scopedArgs = this.appendSurface(this.appendWorkspace(args), sourceSurfaceId);
295
+ const scopedArgs = this.appendSurface(this.appendWorkspace(args), this.config.surfaceId);
303
296
  await this.runAsync(scopedArgs);
304
297
  const after = await this.listSurfaceIds();
305
298
  for (const id of after) {
@@ -308,55 +301,6 @@ export class CmuxClient {
308
301
  return null;
309
302
  }
310
303
 
311
- /**
312
- * Create a grid of surfaces for parallel agent execution.
313
- *
314
- * Layout strategy (gsd stays in the original surface):
315
- * 1 agent: [gsd | A]
316
- * 2 agents: [gsd | A]
317
- * [ | B]
318
- * 3 agents: [gsd | A]
319
- * [ C | B]
320
- * 4 agents: [gsd | A]
321
- * [ C | B] (D splits from B downward)
322
- * [ | D]
323
- *
324
- * Returns surface IDs in order, or empty array on failure.
325
- */
326
- async createGridLayout(count: number): Promise<string[]> {
327
- if (!this.config.splits || count <= 0) return [];
328
- const surfaces: string[] = [];
329
-
330
- // First split: create right column from the gsd surface
331
- const rightCol = await this.createSplitFrom(this.config.surfaceId, "right");
332
- if (!rightCol) return [];
333
- surfaces.push(rightCol);
334
- if (count === 1) return surfaces;
335
-
336
- // Second split: split right column down → bottom-right
337
- const bottomRight = await this.createSplitFrom(rightCol, "down");
338
- if (!bottomRight) return surfaces;
339
- surfaces.push(bottomRight);
340
- if (count === 2) return surfaces;
341
-
342
- // Third split: split gsd surface down → bottom-left
343
- const bottomLeft = await this.createSplitFrom(this.config.surfaceId, "down");
344
- if (!bottomLeft) return surfaces;
345
- surfaces.push(bottomLeft);
346
- if (count === 3) return surfaces;
347
-
348
- // Fourth+: split subsequent surfaces down from the last created
349
- let lastSurface = bottomRight;
350
- for (let i = 3; i < count; i++) {
351
- const next = await this.createSplitFrom(lastSurface, "down");
352
- if (!next) break;
353
- surfaces.push(next);
354
- lastSurface = next;
355
- }
356
-
357
- return surfaces;
358
- }
359
-
360
304
  async sendSurface(surfaceId: string, text: string): Promise<boolean> {
361
305
  const payload = text.endsWith("\n") ? text : `${text}\n`;
362
306
  const stdout = await this.runAsync(["send-surface", "--surface", surfaceId, payload]);
@@ -787,7 +787,7 @@ export async function autoLoop(
787
787
  (m: { status: string }) =>
788
788
  m.status !== "complete" && m.status !== "parked",
789
789
  );
790
- if (incomplete.length === 0 && state.registry.length > 0) {
790
+ if (incomplete.length === 0) {
791
791
  // All milestones complete — merge milestone branch before stopping
792
792
  if (s.currentMilestoneId) {
793
793
  deps.resolver.mergeAndExit(s.currentMilestoneId, ctx.ui);
@@ -804,18 +804,6 @@ export async function autoLoop(
804
804
  "success",
805
805
  );
806
806
  await deps.stopAuto(ctx, pi, "All milestones complete");
807
- } else if (incomplete.length === 0 && state.registry.length === 0) {
808
- // Empty registry — no milestones visible, likely a path resolution bug
809
- const diag = `basePath=${s.basePath}, phase=${state.phase}`;
810
- ctx.ui.notify(
811
- `No milestones visible in current scope. Possible path resolution issue.\n Diagnostic: ${diag}`,
812
- "error",
813
- );
814
- await deps.stopAuto(
815
- ctx,
816
- pi,
817
- `No milestones found — check basePath resolution`,
818
- );
819
807
  } else if (state.phase === "blocked") {
820
808
  const blockerMsg = `Blocked: ${state.blockers.join(", ")}`;
821
809
  await deps.stopAuto(ctx, pi, blockerMsg);
@@ -429,16 +429,10 @@ export async function bootstrapAutoSession(
429
429
  s.originalBasePath = base;
430
430
 
431
431
  const isUnderGsdWorktrees = (p: string): boolean => {
432
- // Direct layout: /.gsd/worktrees/
433
432
  const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
434
433
  if (p.includes(marker)) return true;
435
434
  const worktreesSuffix = `${pathSep}.gsd${pathSep}worktrees`;
436
- if (p.endsWith(worktreesSuffix)) return true;
437
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
438
- const symlinkRe = new RegExp(
439
- `\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees(?:\\${pathSep}|$)`,
440
- );
441
- return symlinkRe.test(p);
435
+ return p.endsWith(worktreesSuffix);
442
436
  };
443
437
 
444
438
  if (
@@ -153,18 +153,9 @@ export function checkResourcesStale(
153
153
  * Returns the corrected base path.
154
154
  */
155
155
  export function escapeStaleWorktree(base: string): string {
156
- // Direct layout: /.gsd/worktrees/
157
- const directMarker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
158
- let idx = base.indexOf(directMarker);
159
- if (idx === -1) {
160
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
161
- const symlinkRe = new RegExp(
162
- `\\${pathSep}\\.gsd\\${pathSep}projects\\${pathSep}[a-f0-9]+\\${pathSep}worktrees\\${pathSep}`,
163
- );
164
- const match = base.match(symlinkRe);
165
- if (!match || match.index === undefined) return base;
166
- idx = match.index;
167
- }
156
+ const marker = `${pathSep}.gsd${pathSep}worktrees${pathSep}`;
157
+ const idx = base.indexOf(marker);
158
+ if (idx === -1) return base;
168
159
 
169
160
  // base is inside .gsd/worktrees/<something> — extract the project root
170
161
  const projectRoot = base.slice(0, idx);
@@ -59,17 +59,8 @@ const VALID_CLASSIFICATIONS: readonly string[] = [
59
59
  */
60
60
  export function resolveCapturesPath(basePath: string): string {
61
61
  const resolved = resolve(basePath);
62
- // Direct layout: /.gsd/worktrees/
63
62
  const worktreeMarker = `${sep}.gsd${sep}worktrees${sep}`;
64
- let idx = resolved.indexOf(worktreeMarker);
65
- if (idx === -1) {
66
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/
67
- const symlinkRe = new RegExp(
68
- `\\${sep}\\.gsd\\${sep}projects\\${sep}[a-f0-9]+\\${sep}worktrees\\${sep}`,
69
- );
70
- const match = resolved.match(symlinkRe);
71
- if (match && match.index !== undefined) idx = match.index;
72
- }
63
+ const idx = resolved.indexOf(worktreeMarker);
73
64
  if (idx !== -1) {
74
65
  // basePath is inside a worktree — resolve to project root
75
66
  const projectRoot = resolved.slice(0, idx);
@@ -15,7 +15,6 @@ import { appendOverride, appendKnowledge } from "./files.js";
15
15
  import {
16
16
  formatDoctorIssuesForPrompt,
17
17
  formatDoctorReport,
18
- formatDoctorReportJson,
19
18
  runGSDDoctor,
20
19
  selectDoctorScope,
21
20
  filterDoctorIssues,
@@ -44,30 +43,16 @@ export function dispatchDoctorHeal(pi: ExtensionAPI, scope: string | undefined,
44
43
 
45
44
  export async function handleDoctor(args: string, ctx: ExtensionCommandContext, pi: ExtensionAPI): Promise<void> {
46
45
  const trimmed = args.trim();
47
- // Extract flags before positional parsing
48
- const jsonMode = trimmed.includes("--json");
49
- const dryRun = trimmed.includes("--dry-run");
50
- const includeBuild = trimmed.includes("--build");
51
- const includeTests = trimmed.includes("--test");
52
- const stripped = trimmed.replace(/--json|--dry-run|--build|--test/g, "").trim();
53
- const parts = stripped ? stripped.split(/\s+/) : [];
46
+ const parts = trimmed ? trimmed.split(/\s+/) : [];
54
47
  const mode = parts[0] === "fix" || parts[0] === "heal" || parts[0] === "audit" ? parts[0] : "doctor";
55
48
  const requestedScope = mode === "doctor" ? parts[0] : parts[1];
56
49
  const scope = await selectDoctorScope(projectRoot(), requestedScope);
57
50
  const effectiveScope = mode === "audit" ? requestedScope : scope;
58
51
  const report = await runGSDDoctor(projectRoot(), {
59
- fix: mode === "fix" || mode === "heal" || dryRun,
60
- dryRun,
52
+ fix: mode === "fix" || mode === "heal",
61
53
  scope: effectiveScope,
62
- includeBuild,
63
- includeTests,
64
54
  });
65
55
 
66
- if (jsonMode) {
67
- ctx.ui.notify(formatDoctorReportJson(report), "info");
68
- return;
69
- }
70
-
71
56
  const reportText = formatDoctorReport(report, {
72
57
  scope: effectiveScope,
73
58
  includeWarnings: mode === "audit",
@@ -159,7 +159,7 @@ async function guardRemoteSession(
159
159
 
160
160
  export function registerGSDCommand(pi: ExtensionAPI): void {
161
161
  pi.registerCommand("gsd", {
162
- description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|widget|visualize|queue|quick|capture|triage|dispatch|history|undo|rate|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|logs|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|park|unpark|init|setup|inspect|extensions|update",
162
+ description: "GSD — Get Shit Done: /gsd help|start|templates|next|auto|stop|pause|status|visualize|queue|quick|capture|triage|dispatch|history|undo|skip|export|cleanup|mode|prefs|config|keys|hooks|run-hook|skill-health|doctor|forensics|changelog|migrate|remote|steer|knowledge|new-milestone|parallel|cmux|update",
163
163
  getArgumentCompletions: (prefix: string) => {
164
164
  const subcommands = [
165
165
  { cmd: "help", desc: "Categorized command reference with descriptions" },
@@ -210,11 +210,7 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
210
210
  { cmd: "templates", desc: "List available workflow templates" },
211
211
  { cmd: "extensions", desc: "Manage extensions (list, enable, disable, info)" },
212
212
  ];
213
- const hasTrailingSpace = prefix.endsWith(" ");
214
213
  const parts = prefix.trim().split(/\s+/);
215
- if (hasTrailingSpace && parts.length >= 1) {
216
- parts.push("");
217
- }
218
214
 
219
215
  if (parts.length <= 1) {
220
216
  return subcommands
@@ -513,10 +509,6 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
513
509
  { cmd: "fix", desc: "Auto-fix detected issues" },
514
510
  { cmd: "heal", desc: "AI-driven deep healing" },
515
511
  { cmd: "audit", desc: "Run health audit without fixing" },
516
- { cmd: "--dry-run", desc: "Show what --fix would change without applying" },
517
- { cmd: "--json", desc: "Output report as JSON (CI/tooling friendly)" },
518
- { cmd: "--build", desc: "Include slow build health check (npm run build)" },
519
- { cmd: "--test", desc: "Include slow test health check (npm test)" },
520
512
  ];
521
513
 
522
514
  if (parts.length <= 2) {
@@ -544,18 +536,6 @@ export function registerGSDCommand(pi: ExtensionAPI): void {
544
536
  .map((p) => ({ value: `dispatch ${p.cmd}`, label: p.cmd, description: p.desc }));
545
537
  }
546
538
 
547
- if (parts[0] === "rate" && parts.length <= 2) {
548
- const tierPrefix = parts[1] ?? "";
549
- const tiers = [
550
- { cmd: "over", desc: "Model was overqualified for this task" },
551
- { cmd: "ok", desc: "Model was appropriate for this task" },
552
- { cmd: "under", desc: "Model was underqualified for this task" },
553
- ];
554
- return tiers
555
- .filter((t) => t.cmd.startsWith(tierPrefix))
556
- .map((t) => ({ value: `rate ${t.cmd}`, label: t.cmd, description: t.desc }));
557
- }
558
-
559
539
  return [];
560
540
  },
561
541
 
@@ -657,81 +657,6 @@ export async function checkRuntimeHealth(
657
657
  } catch {
658
658
  // Non-fatal — external state check failed
659
659
  }
660
-
661
- // ── Metrics ledger integrity ───────────────────────────────────────────
662
- try {
663
- const metricsPath = join(root, "metrics.json");
664
- if (existsSync(metricsPath)) {
665
- try {
666
- const raw = readFileSync(metricsPath, "utf-8");
667
- const ledger = JSON.parse(raw);
668
- if (ledger.version !== 1 || !Array.isArray(ledger.units)) {
669
- issues.push({
670
- severity: "warning",
671
- code: "metrics_ledger_corrupt",
672
- scope: "project",
673
- unitId: "project",
674
- message: "metrics.json has an unexpected structure (version !== 1 or units is not an array) — metrics data may be unreliable",
675
- file: ".gsd/metrics.json",
676
- fixable: false,
677
- });
678
- }
679
- } catch {
680
- issues.push({
681
- severity: "warning",
682
- code: "metrics_ledger_corrupt",
683
- scope: "project",
684
- unitId: "project",
685
- message: "metrics.json is not valid JSON — metrics data may be corrupt",
686
- file: ".gsd/metrics.json",
687
- fixable: false,
688
- });
689
- }
690
- }
691
- } catch {
692
- // Non-fatal — metrics check failed
693
- }
694
-
695
- // ── Large planning file detection ──────────────────────────────────────
696
- // Files over 100KB can cause LLM context pressure. Report the worst offenders.
697
- try {
698
- const MAX_FILE_BYTES = 100 * 1024; // 100KB
699
- const milestonesPath = milestonesDir(basePath);
700
- if (existsSync(milestonesPath)) {
701
- const largeFiles: Array<{ path: string; sizeKB: number }> = [];
702
- function scanForLargeFiles(dir: string, depth = 0): void {
703
- if (depth > 6) return;
704
- try {
705
- for (const entry of readdirSync(dir)) {
706
- const full = join(dir, entry);
707
- try {
708
- const s = statSync(full);
709
- if (s.isDirectory()) { scanForLargeFiles(full, depth + 1); continue; }
710
- if (entry.endsWith(".md") && s.size > MAX_FILE_BYTES) {
711
- largeFiles.push({ path: full.replace(basePath + "/", ""), sizeKB: Math.round(s.size / 1024) });
712
- }
713
- } catch { /* skip entry */ }
714
- }
715
- } catch { /* skip dir */ }
716
- }
717
- scanForLargeFiles(milestonesPath);
718
- if (largeFiles.length > 0) {
719
- largeFiles.sort((a, b) => b.sizeKB - a.sizeKB);
720
- const worst = largeFiles[0]!;
721
- issues.push({
722
- severity: "warning",
723
- code: "large_planning_file",
724
- scope: "project",
725
- unitId: "project",
726
- message: `${largeFiles.length} planning file(s) exceed 100KB — largest: ${worst.path} (${worst.sizeKB}KB). Large files cause LLM context pressure.`,
727
- file: worst.path,
728
- fixable: false,
729
- });
730
- }
731
- }
732
- } catch {
733
- // Non-fatal — large file scan failed
734
- }
735
660
  }
736
661
 
737
662
  /**
@@ -407,63 +407,6 @@ function checkGitRemote(basePath: string): EnvironmentCheckResult | null {
407
407
  return { name: "git_remote", status: "ok", message: "Git remote reachable" };
408
408
  }
409
409
 
410
- /**
411
- * Check if the project build passes (opt-in slow check, use --build flag).
412
- * Runs npm run build and reports failure as env_build.
413
- */
414
- function checkBuildHealth(basePath: string): EnvironmentCheckResult | null {
415
- const pkgPath = join(basePath, "package.json");
416
- if (!existsSync(pkgPath)) return null;
417
-
418
- try {
419
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
420
- const buildScript = pkg.scripts?.build;
421
- if (!buildScript) return null;
422
-
423
- const result = tryExec("npm run build 2>&1", basePath);
424
- if (result === null) {
425
- return {
426
- name: "build",
427
- status: "error",
428
- message: "Build failed — npm run build exited non-zero",
429
- detail: "Fix build errors before dispatching work",
430
- };
431
- }
432
- return { name: "build", status: "ok", message: "Build passes" };
433
- } catch {
434
- return null;
435
- }
436
- }
437
-
438
- /**
439
- * Check if tests pass (opt-in slow check, use --test flag).
440
- * Runs npm test and reports failures as env_test.
441
- */
442
- function checkTestHealth(basePath: string): EnvironmentCheckResult | null {
443
- const pkgPath = join(basePath, "package.json");
444
- if (!existsSync(pkgPath)) return null;
445
-
446
- try {
447
- const pkg = JSON.parse(readFileSync(pkgPath, "utf-8"));
448
- const testScript = pkg.scripts?.test;
449
- // Skip if no test script or the default placeholder
450
- if (!testScript || testScript.includes("no test specified")) return null;
451
-
452
- const result = tryExec("npm test 2>&1", basePath);
453
- if (result === null) {
454
- return {
455
- name: "test",
456
- status: "warning",
457
- message: "Tests failing — npm test exited non-zero",
458
- detail: "Fix failing tests before shipping",
459
- };
460
- }
461
- return { name: "test", status: "ok", message: "Tests pass" };
462
- } catch {
463
- return null;
464
- }
465
- }
466
-
467
410
  // ── Public API ─────────────────────────────────────────────────────────────
468
411
 
469
412
  /**
@@ -511,26 +454,6 @@ export function runFullEnvironmentChecks(basePath: string): EnvironmentCheckResu
511
454
  return results;
512
455
  }
513
456
 
514
- /**
515
- * Run slow opt-in checks (build and/or test).
516
- * These are never run on the pre-dispatch gate — only on explicit /gsd doctor --build/--test.
517
- */
518
- export function runSlowEnvironmentChecks(
519
- basePath: string,
520
- options?: { includeBuild?: boolean; includeTests?: boolean },
521
- ): EnvironmentCheckResult[] {
522
- const results: EnvironmentCheckResult[] = [];
523
- if (options?.includeBuild) {
524
- const buildCheck = checkBuildHealth(basePath);
525
- if (buildCheck) results.push(buildCheck);
526
- }
527
- if (options?.includeTests) {
528
- const testCheck = checkTestHealth(basePath);
529
- if (testCheck) results.push(testCheck);
530
- }
531
- return results;
532
- }
533
-
534
457
  /**
535
458
  * Convert environment check results to DoctorIssue format for the doctor pipeline.
536
459
  */
@@ -554,16 +477,12 @@ export function environmentResultsToDoctorIssues(results: EnvironmentCheckResult
554
477
  export async function checkEnvironmentHealth(
555
478
  basePath: string,
556
479
  issues: DoctorIssue[],
557
- options?: { includeRemote?: boolean; includeBuild?: boolean; includeTests?: boolean },
480
+ options?: { includeRemote?: boolean },
558
481
  ): Promise<void> {
559
482
  const results = options?.includeRemote
560
483
  ? runFullEnvironmentChecks(basePath)
561
484
  : runEnvironmentChecks(basePath);
562
485
 
563
- if (options?.includeBuild || options?.includeTests) {
564
- results.push(...runSlowEnvironmentChecks(basePath, options));
565
- }
566
-
567
486
  issues.push(...environmentResultsToDoctorIssues(results));
568
487
  }
569
488
 
@@ -76,23 +76,3 @@ export function formatDoctorIssuesForPrompt(issues: DoctorIssue[]): string {
76
76
  return `- [${prefix}] ${issue.unitId} | ${issue.code} | ${issue.message}${issue.file ? ` | file: ${issue.file}` : ""} | fixable: ${issue.fixable ? "yes" : "no"}`;
77
77
  }).join("\n");
78
78
  }
79
-
80
- /**
81
- * Serialize a doctor report to JSON — suitable for CI/tooling integration.
82
- * Usage: /gsd doctor --json
83
- */
84
- export function formatDoctorReportJson(report: DoctorReport): string {
85
- return JSON.stringify(
86
- {
87
- ok: report.ok,
88
- basePath: report.basePath,
89
- generatedAt: new Date().toISOString(),
90
- summary: summarizeDoctorIssues(report.issues),
91
- issues: report.issues,
92
- fixesApplied: report.fixesApplied,
93
- ...(report.timing ? { timing: report.timing } : {}),
94
- },
95
- null,
96
- 2,
97
- );
98
- }
@@ -53,20 +53,7 @@ export type DoctorIssueCode =
53
53
  | "stranded_lock_directory"
54
54
  // Git / worktree integrity checks
55
55
  | "integration_branch_missing"
56
- | "worktree_directory_orphaned"
57
- // GSD state structural checks
58
- | "circular_slice_dependency"
59
- | "orphaned_slice_directory"
60
- | "duplicate_task_id"
61
- | "task_file_not_in_plan"
62
- | "stale_replan_file"
63
- | "future_timestamp"
64
- // Runtime data integrity
65
- | "metrics_ledger_corrupt"
66
- | "large_planning_file"
67
- // Slow environment checks (opt-in via --build / --test flags)
68
- | "env_build"
69
- | "env_test";
56
+ | "worktree_directory_orphaned";
70
57
 
71
58
  /**
72
59
  * Issue codes that represent expected completion-transition states.
@@ -96,8 +83,6 @@ export interface DoctorReport {
96
83
  basePath: string;
97
84
  issues: DoctorIssue[];
98
85
  fixesApplied: string[];
99
- /** Per-domain check durations in milliseconds. Present on explicit /gsd doctor runs. */
100
- timing?: { git: number; runtime: number; environment: number; gsdState: number };
101
86
  }
102
87
 
103
88
  export interface DoctorSummary {