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
@@ -1,7 +1,7 @@
1
- import { existsSync, mkdirSync, lstatSync, readdirSync, readFileSync } from "node:fs";
1
+ import { existsSync, mkdirSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { loadFile, parsePlan, parseRoadmap, parseSummary, saveFile, parseTaskPlanMustHaves, countMustHavesMentionedInSummary } from "./files.js";
4
- import { resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, milestonesDir, gsdRoot, relMilestoneFile, relSliceFile, relTaskFile, relSlicePath, relGsdRootFile, resolveGsdRootFile, relMilestonePath } from "./paths.js";
4
+ import { resolveMilestoneFile, resolveMilestonePath, resolveSliceFile, resolveSlicePath, resolveTaskFile, resolveTasksDir, milestonesDir, relMilestoneFile, relSliceFile, relTaskFile, relSlicePath, relGsdRootFile, resolveGsdRootFile } from "./paths.js";
5
5
  import { deriveState, isMilestoneComplete } from "./state.js";
6
6
  import { invalidateAllCaches } from "./cache.js";
7
7
  import { loadEffectiveGSDPreferences } from "./preferences.js";
@@ -9,7 +9,7 @@ import { COMPLETION_TRANSITION_CODES } from "./doctor-types.js";
9
9
  import { checkGitHealth, checkRuntimeHealth } from "./doctor-checks.js";
10
10
  import { checkEnvironmentHealth } from "./doctor-environment.js";
11
11
  import { runProviderChecks } from "./doctor-providers.js";
12
- export { summarizeDoctorIssues, filterDoctorIssues, formatDoctorReport, formatDoctorIssuesForPrompt, formatDoctorReportJson } from "./doctor-format.js";
12
+ export { summarizeDoctorIssues, filterDoctorIssues, formatDoctorReport, formatDoctorIssuesForPrompt } from "./doctor-format.js";
13
13
  export { runEnvironmentChecks, runFullEnvironmentChecks, formatEnvironmentReport } from "./doctor-environment.js";
14
14
  export { computeProgressScore, computeProgressScoreWithContext, formatProgressLine, formatProgressReport } from "./progress-score.js";
15
15
  /**
@@ -324,68 +324,10 @@ export async function selectDoctorScope(basePath, requestedScope) {
324
324
  }
325
325
  return state.registry[0]?.id;
326
326
  }
327
- // ── Helper: circular dependency detection ──────────────────────────────────
328
- function detectCircularDependencies(slices) {
329
- const known = new Set(slices.map(s => s.id));
330
- const adj = new Map();
331
- for (const s of slices)
332
- adj.set(s.id, s.depends.filter(d => known.has(d)));
333
- const state = new Map();
334
- for (const s of slices)
335
- state.set(s.id, "unvisited");
336
- const cycles = [];
337
- function dfs(id, path) {
338
- const st = state.get(id);
339
- if (st === "done")
340
- return;
341
- if (st === "visiting") {
342
- cycles.push([...path.slice(path.indexOf(id)), id]);
343
- return;
344
- }
345
- state.set(id, "visiting");
346
- for (const dep of adj.get(id) ?? [])
347
- dfs(dep, [...path, id]);
348
- state.set(id, "done");
349
- }
350
- for (const s of slices)
351
- if (state.get(s.id) === "unvisited")
352
- dfs(s.id, []);
353
- return cycles;
354
- }
355
- async function appendDoctorHistory(basePath, report) {
356
- try {
357
- const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
358
- const entry = JSON.stringify({
359
- ts: new Date().toISOString(),
360
- ok: report.ok,
361
- errors: report.issues.filter(i => i.severity === "error").length,
362
- warnings: report.issues.filter(i => i.severity === "warning").length,
363
- fixes: report.fixesApplied.length,
364
- codes: [...new Set(report.issues.map(i => i.code))],
365
- });
366
- const existing = existsSync(historyPath) ? readFileSync(historyPath, "utf-8") : "";
367
- await saveFile(historyPath, existing + entry + "\n");
368
- }
369
- catch { /* non-fatal */ }
370
- }
371
- /** Read the last N doctor history entries. Returns most-recent-first. */
372
- export async function readDoctorHistory(basePath, lastN = 50) {
373
- try {
374
- const historyPath = join(gsdRoot(basePath), "doctor-history.jsonl");
375
- if (!existsSync(historyPath))
376
- return [];
377
- const lines = readFileSync(historyPath, "utf-8").split("\n").filter(l => l.trim());
378
- return lines.slice(-lastN).reverse().map(l => JSON.parse(l));
379
- }
380
- catch {
381
- return [];
382
- }
383
- }
384
327
  export async function runGSDDoctor(basePath, options) {
385
328
  const issues = [];
386
329
  const fixesApplied = [];
387
330
  const fix = options?.fix === true;
388
- const dryRun = options?.dryRun === true;
389
331
  const fixLevel = options?.fixLevel ?? "all";
390
332
  // Issue codes that represent completion state transitions — creating summary
391
333
  // stubs, marking slices/milestones done in the roadmap. These belong to the
@@ -394,18 +336,12 @@ export async function runGSDDoctor(basePath, options) {
394
336
  // detected and reported but never auto-fixed.
395
337
  /** Whether a given issue code should be auto-fixed at the current fixLevel. */
396
338
  const shouldFix = (code) => {
397
- if (!fix || dryRun)
339
+ if (!fix)
398
340
  return false;
399
341
  if (fixLevel === "task" && COMPLETION_TRANSITION_CODES.has(code))
400
342
  return false;
401
343
  return true;
402
344
  };
403
- /** Log a dry-run "would fix" entry when fix=true but dryRun=true. */
404
- const dryRunCanFix = (code, message) => {
405
- if (dryRun && fix && !(fixLevel === "task" && COMPLETION_TRANSITION_CODES.has(code))) {
406
- fixesApplied.push(`[dry-run] would fix: ${message}`);
407
- }
408
- };
409
345
  const prefs = loadEffectiveGSDPreferences();
410
346
  if (prefs) {
411
347
  const prefIssues = validatePreferenceShape(prefs.preferences);
@@ -421,30 +357,18 @@ export async function runGSDDoctor(basePath, options) {
421
357
  });
422
358
  }
423
359
  }
424
- // Git health checks timed
425
- const t0git = Date.now();
360
+ // Git health checks (orphaned worktrees, stale branches, corrupt merge state, tracked runtime files)
426
361
  const isolationMode = options?.isolationMode ??
427
362
  (prefs?.preferences?.git?.isolation === "none" ? "none" :
428
363
  prefs?.preferences?.git?.isolation === "branch" ? "branch" : "worktree");
429
364
  await checkGitHealth(basePath, issues, fixesApplied, shouldFix, isolationMode);
430
- const gitMs = Date.now() - t0git;
431
- // Runtime health checks — timed
432
- const t0runtime = Date.now();
365
+ // Runtime health checks (crash locks, completed-units, hook state, activity logs, STATE.md, gitignore)
433
366
  await checkRuntimeHealth(basePath, issues, fixesApplied, shouldFix);
434
- const runtimeMs = Date.now() - t0runtime;
435
- // Environment health checks timed
436
- const t0env = Date.now();
437
- await checkEnvironmentHealth(basePath, issues, {
438
- includeRemote: !options?.scope,
439
- includeBuild: options?.includeBuild,
440
- includeTests: options?.includeTests,
441
- });
442
- const envMs = Date.now() - t0env;
367
+ // Environment health checks (#1221: missing tools, port conflicts, stale deps, disk space)
368
+ await checkEnvironmentHealth(basePath, issues, { includeRemote: !options?.scope });
443
369
  const milestonesPath = milestonesDir(basePath);
444
370
  if (!existsSync(milestonesPath)) {
445
- const report = { ok: issues.every(i => i.severity !== "error"), basePath, issues, fixesApplied, timing: { git: gitMs, runtime: runtimeMs, environment: envMs, gsdState: 0 } };
446
- await appendDoctorHistory(basePath, report);
447
- return report;
371
+ return { ok: issues.every(issue => issue.severity !== "error"), basePath, issues, fixesApplied };
448
372
  }
449
373
  const requirementsPath = resolveGsdRootFile(basePath, "REQUIREMENTS");
450
374
  const requirementsContent = await loadFile(requirementsPath);
@@ -508,46 +432,6 @@ export async function runGSDDoctor(basePath, options) {
508
432
  if (!roadmapContent)
509
433
  continue;
510
434
  const roadmap = parseRoadmap(roadmapContent);
511
- // ── Circular dependency detection ──────────────────────────────────────
512
- for (const cycle of detectCircularDependencies(roadmap.slices)) {
513
- issues.push({
514
- severity: "error",
515
- code: "circular_slice_dependency",
516
- scope: "milestone",
517
- unitId: milestoneId,
518
- message: `Circular dependency detected: ${cycle.join(" → ")}`,
519
- file: relMilestoneFile(basePath, milestoneId, "ROADMAP"),
520
- fixable: false,
521
- });
522
- }
523
- // ── Orphaned slice directories ─────────────────────────────────────────
524
- try {
525
- const slicesDir = join(milestonePath, "slices");
526
- if (existsSync(slicesDir)) {
527
- const knownSliceIds = new Set(roadmap.slices.map(s => s.id));
528
- for (const entry of readdirSync(slicesDir)) {
529
- try {
530
- if (!lstatSync(join(slicesDir, entry)).isDirectory())
531
- continue;
532
- }
533
- catch {
534
- continue;
535
- }
536
- if (!knownSliceIds.has(entry)) {
537
- issues.push({
538
- severity: "warning",
539
- code: "orphaned_slice_directory",
540
- scope: "milestone",
541
- unitId: milestoneId,
542
- message: `Directory "${entry}" exists in ${milestoneId}/slices/ but is not referenced in the roadmap`,
543
- file: `${relMilestonePath(basePath, milestoneId)}/slices/${entry}`,
544
- fixable: false,
545
- });
546
- }
547
- }
548
- }
549
- }
550
- catch { /* non-fatal */ }
551
435
  for (const slice of roadmap.slices) {
552
436
  const unitId = `${milestoneId}/${slice.id}`;
553
437
  if (options?.scope && !matchesScope(unitId, options.scope) && options.scope !== milestoneId)
@@ -618,34 +502,6 @@ export async function runGSDDoctor(basePath, options) {
618
502
  }
619
503
  continue;
620
504
  }
621
- // ── Duplicate task IDs ───────────────────────────────────────────────
622
- const taskIdCounts = new Map();
623
- for (const task of plan.tasks)
624
- taskIdCounts.set(task.id, (taskIdCounts.get(task.id) ?? 0) + 1);
625
- for (const [taskId, count] of taskIdCounts) {
626
- if (count > 1) {
627
- issues.push({ severity: "error", code: "duplicate_task_id", scope: "slice", unitId,
628
- message: `Task ID "${taskId}" appears ${count} times in ${slice.id}-PLAN.md — duplicate IDs cause dispatch failures`,
629
- file: relSliceFile(basePath, milestoneId, slice.id, "PLAN"), fixable: false });
630
- }
631
- }
632
- // ── Task files on disk not in plan ────────────────────────────────────
633
- try {
634
- if (tasksDir) {
635
- const planTaskIds = new Set(plan.tasks.map(t => t.id));
636
- for (const f of readdirSync(tasksDir)) {
637
- if (!f.endsWith("-SUMMARY.md"))
638
- continue;
639
- const diskTaskId = f.replace(/-SUMMARY\.md$/, "");
640
- if (!planTaskIds.has(diskTaskId)) {
641
- issues.push({ severity: "info", code: "task_file_not_in_plan", scope: "slice", unitId,
642
- message: `Task summary "${f}" exists on disk but "${diskTaskId}" is not in ${slice.id}-PLAN.md`,
643
- file: relTaskFile(basePath, milestoneId, slice.id, diskTaskId, "SUMMARY"), fixable: false });
644
- }
645
- }
646
- }
647
- }
648
- catch { /* non-fatal */ }
649
505
  let allTasksDone = plan.tasks.length > 0;
650
506
  for (const task of plan.tasks) {
651
507
  const taskUnitId = `${unitId}/${task.id}`;
@@ -661,7 +517,6 @@ export async function runGSDDoctor(basePath, options) {
661
517
  file: relTaskFile(basePath, milestoneId, slice.id, task.id, "SUMMARY"),
662
518
  fixable: true,
663
519
  });
664
- dryRunCanFix("task_done_missing_summary", `create stub summary for ${taskUnitId}`);
665
520
  if (shouldFix("task_done_missing_summary")) {
666
521
  const stubPath = join(basePath, ".gsd", "milestones", milestoneId, "slices", slice.id, "tasks", `${task.id}-SUMMARY.md`);
667
522
  const stubContent = [
@@ -720,22 +575,6 @@ export async function runGSDDoctor(basePath, options) {
720
575
  }
721
576
  }
722
577
  }
723
- // ── Future timestamp check ─────────────────────────────────────
724
- if (task.done && hasSummary && summaryPath) {
725
- try {
726
- const rawSummary = await loadFile(summaryPath);
727
- const m = rawSummary?.match(/^completed_at:\s*(.+)$/m);
728
- if (m) {
729
- const ts = new Date(m[1].trim());
730
- if (!isNaN(ts.getTime()) && ts.getTime() > Date.now() + 24 * 60 * 60 * 1000) {
731
- issues.push({ severity: "warning", code: "future_timestamp", scope: "task", unitId: taskUnitId,
732
- message: `Task ${task.id} has completed_at "${m[1].trim()}" which is more than 24h in the future`,
733
- file: relTaskFile(basePath, milestoneId, slice.id, task.id, "SUMMARY"), fixable: false });
734
- }
735
- }
736
- }
737
- catch { /* non-fatal */ }
738
- }
739
578
  allTasksDone = allTasksDone && task.done;
740
579
  }
741
580
  // Blocker-without-replan detection
@@ -765,12 +604,6 @@ export async function runGSDDoctor(basePath, options) {
765
604
  }
766
605
  }
767
606
  }
768
- // ── Stale REPLAN: exists but all tasks done ────────────────────────
769
- if (replanPath && allTasksDone) {
770
- issues.push({ severity: "info", code: "stale_replan_file", scope: "slice", unitId,
771
- message: `${slice.id} has a REPLAN.md but all tasks are done — REPLAN.md may be stale`,
772
- file: relSliceFile(basePath, milestoneId, slice.id, "REPLAN"), fixable: false });
773
- }
774
607
  const sliceSummaryPath = resolveSliceFile(basePath, milestoneId, slice.id, "SUMMARY");
775
608
  const sliceUatPath = join(slicePath, `${slice.id}-UAT.md`);
776
609
  const hasSliceSummary = !!(sliceSummaryPath && await loadFile(sliceSummaryPath));
@@ -785,7 +618,6 @@ export async function runGSDDoctor(basePath, options) {
785
618
  file: relSliceFile(basePath, milestoneId, slice.id, "SUMMARY"),
786
619
  fixable: true,
787
620
  });
788
- dryRunCanFix("all_tasks_done_missing_slice_summary", `create placeholder summary for ${unitId}`);
789
621
  if (shouldFix("all_tasks_done_missing_slice_summary"))
790
622
  await ensureSliceSummaryStub(basePath, milestoneId, slice.id, fixesApplied);
791
623
  }
@@ -799,7 +631,6 @@ export async function runGSDDoctor(basePath, options) {
799
631
  file: `${relSlicePath(basePath, milestoneId, slice.id)}/${slice.id}-UAT.md`,
800
632
  fixable: true,
801
633
  });
802
- dryRunCanFix("all_tasks_done_missing_slice_uat", `create placeholder UAT for ${unitId}`);
803
634
  if (shouldFix("all_tasks_done_missing_slice_uat"))
804
635
  await ensureSliceUatStub(basePath, milestoneId, slice.id, fixesApplied);
805
636
  }
@@ -813,7 +644,6 @@ export async function runGSDDoctor(basePath, options) {
813
644
  file: relMilestoneFile(basePath, milestoneId, "ROADMAP"),
814
645
  fixable: true,
815
646
  });
816
- dryRunCanFix("all_tasks_done_roadmap_not_checked", `mark ${slice.id} done in roadmap`);
817
647
  if (shouldFix("all_tasks_done_roadmap_not_checked") && (hasSliceSummary || issues.some(issue => issue.code === "all_tasks_done_missing_slice_summary" && issue.unitId === unitId))) {
818
648
  await markSliceDoneInRoadmap(basePath, milestoneId, slice.id, fixesApplied);
819
649
  }
@@ -866,16 +696,13 @@ export async function runGSDDoctor(basePath, options) {
866
696
  });
867
697
  }
868
698
  }
869
- if (fix && !dryRun && fixesApplied.length > 0) {
699
+ if (fix && fixesApplied.length > 0) {
870
700
  await updateStateFile(basePath, fixesApplied);
871
701
  }
872
- const report = {
702
+ return {
873
703
  ok: issues.every(issue => issue.severity !== "error"),
874
704
  basePath,
875
705
  issues,
876
706
  fixesApplied,
877
- timing: { git: gitMs, runtime: runtimeMs, environment: envMs, gsdState: Math.max(0, Date.now() - t0env - envMs) },
878
707
  };
879
- await appendDoctorHistory(basePath, report);
880
- return report;
881
708
  }
@@ -5,7 +5,7 @@
5
5
  "type": "module",
6
6
  "pi": {
7
7
  "extensions": [
8
- "./index.js"
8
+ "./index.ts"
9
9
  ]
10
10
  }
11
11
  }
@@ -57,61 +57,42 @@ export function captureIntegrationBranch(basePath, milestoneId, options) {
57
57
  writeIntegrationBranch(basePath, milestoneId, current, options);
58
58
  }
59
59
  // ─── Pure Utility Functions (unchanged) ────────────────────────────────────
60
- /**
61
- * Find the worktrees segment in a path, supporting both direct
62
- * (`/.gsd/worktrees/`) and symlink-resolved (`/.gsd/projects/<hash>/worktrees/`)
63
- * layouts. When `.gsd` is a symlink to `~/.gsd/projects/<hash>`, resolved
64
- * paths contain the intermediate `projects/<hash>/` segment that the old
65
- * single-marker check missed.
66
- */
67
- function findWorktreeSegment(normalizedPath) {
68
- // Direct layout: /.gsd/worktrees/<name>
69
- const directMarker = "/.gsd/worktrees/";
70
- const idx = normalizedPath.indexOf(directMarker);
71
- if (idx !== -1) {
72
- return { gsdIdx: idx, afterWorktrees: idx + directMarker.length };
73
- }
74
- // Symlink-resolved layout: /.gsd/projects/<hash>/worktrees/<name>
75
- const symlinkRe = /\/\.gsd\/projects\/[a-f0-9]+\/worktrees\//;
76
- const match = normalizedPath.match(symlinkRe);
77
- if (match && match.index !== undefined) {
78
- return { gsdIdx: match.index, afterWorktrees: match.index + match[0].length };
79
- }
80
- return null;
81
- }
82
60
  /**
83
61
  * Detect the active worktree name from the current working directory.
84
62
  * Returns null if not inside a GSD worktree (.gsd/worktrees/<name>/).
85
63
  */
86
64
  export function detectWorktreeName(basePath) {
87
65
  const normalizedPath = basePath.replaceAll("\\", "/");
88
- const seg = findWorktreeSegment(normalizedPath);
89
- if (!seg)
66
+ const marker = "/.gsd/worktrees/";
67
+ const idx = normalizedPath.indexOf(marker);
68
+ if (idx === -1)
90
69
  return null;
91
- const afterMarker = normalizedPath.slice(seg.afterWorktrees);
70
+ const afterMarker = normalizedPath.slice(idx + marker.length);
92
71
  const name = afterMarker.split("/")[0];
93
72
  return name || null;
94
73
  }
95
74
  /**
96
75
  * Resolve the project root from a path that may be inside a worktree.
97
- * If the path contains a worktrees segment, returns the portion before
98
- * `/.gsd/`. Otherwise returns the input unchanged.
76
+ * If the path contains `/.gsd/worktrees/<name>/`, returns the portion
77
+ * before `/.gsd/`. Otherwise returns the input unchanged.
99
78
  *
100
79
  * Use this in commands that call `process.cwd()` to ensure they always
101
80
  * operate against the real project root, not a worktree subdirectory.
102
81
  */
103
82
  export function resolveProjectRoot(basePath) {
104
83
  const normalizedPath = basePath.replaceAll("\\", "/");
105
- const seg = findWorktreeSegment(normalizedPath);
106
- if (!seg)
84
+ const marker = "/.gsd/worktrees/";
85
+ const idx = normalizedPath.indexOf(marker);
86
+ if (idx === -1)
107
87
  return basePath;
108
- // Return the original path up to the /.gsd/ boundary
88
+ // Return the original path up to the .gsd/ marker (un-normalized)
89
+ // Account for potential OS-specific separators
109
90
  const sep = basePath.includes("\\") ? "\\" : "/";
110
- const gsdMarker = `${sep}.gsd${sep}`;
111
- const gsdIdx = basePath.indexOf(gsdMarker);
112
- if (gsdIdx !== -1)
113
- return basePath.slice(0, gsdIdx);
114
- return basePath.slice(0, seg.gsdIdx);
91
+ const markerOs = `${sep}.gsd${sep}worktrees${sep}`;
92
+ const idxOs = basePath.indexOf(markerOs);
93
+ if (idxOs !== -1)
94
+ return basePath.slice(0, idxOs);
95
+ return basePath.slice(0, idx);
115
96
  }
116
97
  /**
117
98
  * Get the slice branch name, namespaced by worktree when inside one.
@@ -360,7 +360,7 @@ async function runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, si
360
360
  }
361
361
  }
362
362
  }
363
- async function runSingleAgentInCmuxSplit(cmuxClient, directionOrSurfaceId, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
363
+ async function runSingleAgentInCmuxSplit(cmuxClient, direction, defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails) {
364
364
  const agent = agents.find((a) => a.name === agentName);
365
365
  if (!agent) {
366
366
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
@@ -397,12 +397,7 @@ async function runSingleAgentInCmuxSplit(cmuxClient, directionOrSurfaceId, defau
397
397
  const stdoutPath = path.join(tmpOutputDir, "stdout.jsonl");
398
398
  const stderrPath = path.join(tmpOutputDir, "stderr.log");
399
399
  const exitPath = path.join(tmpOutputDir, "exit.code");
400
- // Accept either a pre-created surface ID or a direction to create a new split
401
- const isDirection = directionOrSurfaceId === "right" || directionOrSurfaceId === "down"
402
- || directionOrSurfaceId === "left" || directionOrSurfaceId === "up";
403
- const cmuxSurfaceId = isDirection
404
- ? await cmuxClient.createSplit(directionOrSurfaceId)
405
- : directionOrSurfaceId;
400
+ const cmuxSurfaceId = await cmuxClient.createSplit(direction);
406
401
  if (!cmuxSurfaceId) {
407
402
  return runSingleAgent(defaultCwd, agents, agentName, task, cwd, step, signal, onUpdate, makeDetails);
408
403
  }
@@ -661,14 +656,10 @@ export default function (pi) {
661
656
  const MAX_RETRIES = 1; // Retry failed tasks once
662
657
  const batchId = crypto.randomUUID();
663
658
  const batchSize = params.tasks.length;
664
- // Pre-create a grid layout for cmux splits so agents get a clean tiled arrangement
665
- const gridSurfaces = cmuxSplitsEnabled
666
- ? await cmuxClient.createGridLayout(Math.min(batchSize, MAX_CONCURRENCY))
667
- : [];
668
659
  const results = await mapWithConcurrencyLimit(params.tasks, MAX_CONCURRENCY, async (t, index) => {
669
660
  const workerId = registerWorker(t.agent, t.task, index, batchSize, batchId);
670
661
  const runTask = () => cmuxSplitsEnabled
671
- ? runSingleAgentInCmuxSplit(cmuxClient, gridSurfaces[index] ?? (index % 2 === 0 ? "right" : "down"), ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
662
+ ? runSingleAgentInCmuxSplit(cmuxClient, index % 2 === 0 ? "right" : "down", ctx.cwd, agents, t.agent, t.task, t.cwd, undefined, signal, (partial) => {
672
663
  if (partial.details?.results[0]) {
673
664
  allResults[index] = partial.details.results[0];
674
665
  emitParallelUpdate();
@@ -5,7 +5,7 @@
5
5
  "type": "module",
6
6
  "pi": {
7
7
  "extensions": [
8
- "./index.js"
8
+ "./index.ts"
9
9
  ]
10
10
  }
11
11
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gsd-pi",
3
- "version": "2.38.0-dev.96dc7fb",
3
+ "version": "2.38.0-dev.eeb3520",
4
4
  "description": "GSD — Get Shit Done coding agent",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -1 +1 @@
1
- {"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../../src/core/package-manager.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"package-manager.d.ts","sourceRoot":"","sources":["../../src/core/package-manager.ts"],"names":[],"mappings":"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"}
@@ -292,13 +292,7 @@ function resolveExtensionEntries(dir) {
292
292
  const packageJsonPath = join(dir, "package.json");
293
293
  if (existsSync(packageJsonPath)) {
294
294
  const manifest = readPiManifestFile(packageJsonPath);
295
- if (manifest) {
296
- // When a pi manifest exists, it is authoritative — don't fall through
297
- // to index.ts/index.js auto-detection. This allows library directories
298
- // (like cmux) to opt out by declaring "pi": {} with no extensions.
299
- if (!manifest.extensions?.length) {
300
- return null;
301
- }
295
+ if (manifest?.extensions?.length) {
302
296
  const entries = [];
303
297
  for (const extPath of manifest.extensions) {
304
298
  const resolvedExtPath = resolve(dir, extPath);
@@ -306,7 +300,9 @@ function resolveExtensionEntries(dir) {
306
300
  entries.push(resolvedExtPath);
307
301
  }
308
302
  }
309
- return entries.length > 0 ? entries : null;
303
+ if (entries.length > 0) {
304
+ return entries;
305
+ }
310
306
  }
311
307
  }
312
308
  const indexTs = join(dir, "index.ts");