gsd-pi 2.29.0-dev.2ccf3fb → 2.29.0-dev.4c155ee

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 (116) hide show
  1. package/dist/headless.js +4 -0
  2. package/dist/resources/extensions/gsd/auto-dashboard.ts +31 -0
  3. package/dist/resources/extensions/gsd/auto-dispatch.ts +32 -3
  4. package/dist/resources/extensions/gsd/auto-post-unit.ts +39 -10
  5. package/dist/resources/extensions/gsd/auto-prompts.ts +40 -17
  6. package/dist/resources/extensions/gsd/auto-recovery.ts +2 -1
  7. package/dist/resources/extensions/gsd/auto-start.ts +18 -32
  8. package/dist/resources/extensions/gsd/auto-worktree.ts +21 -182
  9. package/dist/resources/extensions/gsd/auto.ts +2 -9
  10. package/dist/resources/extensions/gsd/captures.ts +4 -10
  11. package/dist/resources/extensions/gsd/commands-handlers.ts +2 -1
  12. package/dist/resources/extensions/gsd/commands.ts +2 -1
  13. package/dist/resources/extensions/gsd/detection.ts +2 -1
  14. package/dist/resources/extensions/gsd/doctor-checks.ts +49 -1
  15. package/dist/resources/extensions/gsd/doctor-types.ts +3 -1
  16. package/dist/resources/extensions/gsd/forensics.ts +2 -2
  17. package/dist/resources/extensions/gsd/git-service.ts +3 -2
  18. package/dist/resources/extensions/gsd/gitignore.ts +9 -63
  19. package/dist/resources/extensions/gsd/gsd-db.ts +1 -165
  20. package/dist/resources/extensions/gsd/guided-flow.ts +8 -5
  21. package/dist/resources/extensions/gsd/index.ts +3 -3
  22. package/dist/resources/extensions/gsd/md-importer.ts +3 -2
  23. package/dist/resources/extensions/gsd/mechanical-completion.ts +430 -0
  24. package/dist/resources/extensions/gsd/migrate/command.ts +3 -2
  25. package/dist/resources/extensions/gsd/migrate/writer.ts +2 -1
  26. package/dist/resources/extensions/gsd/migrate-external.ts +123 -0
  27. package/dist/resources/extensions/gsd/paths.ts +24 -2
  28. package/dist/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  29. package/dist/resources/extensions/gsd/preferences-models.ts +7 -1
  30. package/dist/resources/extensions/gsd/preferences-validation.ts +2 -1
  31. package/dist/resources/extensions/gsd/preferences.ts +10 -5
  32. package/dist/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  33. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  34. package/dist/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  35. package/dist/resources/extensions/gsd/repo-identity.ts +148 -0
  36. package/dist/resources/extensions/gsd/resource-version.ts +99 -0
  37. package/dist/resources/extensions/gsd/session-forensics.ts +4 -3
  38. package/dist/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  39. package/dist/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  40. package/dist/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  41. package/dist/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  42. package/dist/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  43. package/dist/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  44. package/dist/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  45. package/dist/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  46. package/dist/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  47. package/dist/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  48. package/dist/resources/extensions/gsd/triage-resolution.ts +2 -1
  49. package/dist/resources/extensions/gsd/types.ts +2 -0
  50. package/dist/resources/extensions/gsd/worktree-command.ts +1 -11
  51. package/dist/resources/extensions/gsd/worktree-manager.ts +3 -2
  52. package/dist/resources/extensions/gsd/worktree.ts +42 -5
  53. package/dist/resources/skills/react-best-practices/SKILL.md +1 -1
  54. package/package.json +1 -1
  55. package/packages/pi-coding-agent/dist/core/lsp/client.d.ts.map +1 -1
  56. package/packages/pi-coding-agent/dist/core/lsp/client.js +3 -0
  57. package/packages/pi-coding-agent/dist/core/lsp/client.js.map +1 -1
  58. package/packages/pi-coding-agent/src/core/lsp/client.ts +3 -0
  59. package/src/resources/extensions/gsd/auto-dashboard.ts +31 -0
  60. package/src/resources/extensions/gsd/auto-dispatch.ts +32 -3
  61. package/src/resources/extensions/gsd/auto-post-unit.ts +39 -10
  62. package/src/resources/extensions/gsd/auto-prompts.ts +40 -17
  63. package/src/resources/extensions/gsd/auto-recovery.ts +2 -1
  64. package/src/resources/extensions/gsd/auto-start.ts +18 -32
  65. package/src/resources/extensions/gsd/auto-worktree.ts +21 -182
  66. package/src/resources/extensions/gsd/auto.ts +2 -9
  67. package/src/resources/extensions/gsd/captures.ts +4 -10
  68. package/src/resources/extensions/gsd/commands-handlers.ts +2 -1
  69. package/src/resources/extensions/gsd/commands.ts +2 -1
  70. package/src/resources/extensions/gsd/detection.ts +2 -1
  71. package/src/resources/extensions/gsd/doctor-checks.ts +49 -1
  72. package/src/resources/extensions/gsd/doctor-types.ts +3 -1
  73. package/src/resources/extensions/gsd/forensics.ts +2 -2
  74. package/src/resources/extensions/gsd/git-service.ts +3 -2
  75. package/src/resources/extensions/gsd/gitignore.ts +9 -63
  76. package/src/resources/extensions/gsd/gsd-db.ts +1 -165
  77. package/src/resources/extensions/gsd/guided-flow.ts +8 -5
  78. package/src/resources/extensions/gsd/index.ts +3 -3
  79. package/src/resources/extensions/gsd/md-importer.ts +3 -2
  80. package/src/resources/extensions/gsd/mechanical-completion.ts +430 -0
  81. package/src/resources/extensions/gsd/migrate/command.ts +3 -2
  82. package/src/resources/extensions/gsd/migrate/writer.ts +2 -1
  83. package/src/resources/extensions/gsd/migrate-external.ts +123 -0
  84. package/src/resources/extensions/gsd/paths.ts +24 -2
  85. package/src/resources/extensions/gsd/post-unit-hooks.ts +6 -5
  86. package/src/resources/extensions/gsd/preferences-models.ts +7 -1
  87. package/src/resources/extensions/gsd/preferences-validation.ts +2 -1
  88. package/src/resources/extensions/gsd/preferences.ts +10 -5
  89. package/src/resources/extensions/gsd/prompts/discuss-headless.md +4 -2
  90. package/src/resources/extensions/gsd/prompts/plan-milestone.md +26 -2
  91. package/src/resources/extensions/gsd/prompts/plan-slice.md +15 -1
  92. package/src/resources/extensions/gsd/repo-identity.ts +148 -0
  93. package/src/resources/extensions/gsd/resource-version.ts +99 -0
  94. package/src/resources/extensions/gsd/session-forensics.ts +4 -3
  95. package/src/resources/extensions/gsd/tests/activity-log.test.ts +2 -2
  96. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +3 -3
  97. package/src/resources/extensions/gsd/tests/auto-worktree.test.ts +0 -58
  98. package/src/resources/extensions/gsd/tests/doctor-runtime.test.ts +3 -4
  99. package/src/resources/extensions/gsd/tests/feature-branch-lifecycle-integration.test.ts +5 -18
  100. package/src/resources/extensions/gsd/tests/git-service.test.ts +10 -37
  101. package/src/resources/extensions/gsd/tests/knowledge.test.ts +4 -4
  102. package/src/resources/extensions/gsd/tests/mechanical-completion.test.ts +356 -0
  103. package/src/resources/extensions/gsd/tests/plan-slice-prompt.test.ts +1 -0
  104. package/src/resources/extensions/gsd/tests/token-profile.test.ts +14 -16
  105. package/src/resources/extensions/gsd/triage-resolution.ts +2 -1
  106. package/src/resources/extensions/gsd/types.ts +2 -0
  107. package/src/resources/extensions/gsd/worktree-command.ts +1 -11
  108. package/src/resources/extensions/gsd/worktree-manager.ts +3 -2
  109. package/src/resources/extensions/gsd/worktree.ts +42 -5
  110. package/src/resources/skills/react-best-practices/SKILL.md +1 -1
  111. package/dist/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  112. package/dist/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  113. package/dist/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
  114. package/src/resources/extensions/gsd/auto-worktree-sync.ts +0 -199
  115. package/src/resources/extensions/gsd/tests/worktree-db-integration.test.ts +0 -205
  116. package/src/resources/extensions/gsd/tests/worktree-db.test.ts +0 -442
@@ -6,8 +6,7 @@
6
6
  // Schema is initialized on first open with WAL mode for file-backed DBs.
7
7
 
8
8
  import { createRequire } from 'node:module';
9
- import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
10
- import { dirname } from 'node:path';
9
+ import { existsSync } from 'node:fs';
11
10
  import type { Decision, Requirement } from './types.js';
12
11
  import { GSDError, GSD_STALE_STATE } from './errors.js';
13
12
 
@@ -565,169 +564,6 @@ export function getActiveRequirements(): Requirement[] {
565
564
  }));
566
565
  }
567
566
 
568
- // ─── Worktree DB Operations ────────────────────────────────────────────────
569
-
570
- /**
571
- * Copy a gsd.db file to a new worktree location.
572
- * Copies only the .db file — skips -wal and -shm files so the copy starts clean.
573
- * Returns true on success, false on failure (never throws).
574
- */
575
- export function copyWorktreeDb(srcDbPath: string, destDbPath: string): boolean {
576
- try {
577
- if (!existsSync(srcDbPath)) {
578
- return false; // source doesn't exist — expected when no DB yet
579
- }
580
- const destDir = dirname(destDbPath);
581
- mkdirSync(destDir, { recursive: true });
582
- copyFileSync(srcDbPath, destDbPath);
583
- return true;
584
- } catch (err) {
585
- process.stderr.write(`gsd-db: failed to copy DB to worktree: ${(err as Error).message}\n`);
586
- return false;
587
- }
588
- }
589
-
590
- /**
591
- * Reconcile rows from a worktree DB back into the main DB using ATTACH DATABASE.
592
- * Merges all three tables (decisions, requirements, artifacts) via INSERT OR REPLACE.
593
- * Detects conflicts where both DBs modified the same row.
594
- *
595
- * ATTACH must happen outside any transaction. INSERT OR REPLACE runs inside a transaction.
596
- * DETACH happens after commit (or rollback on error).
597
- */
598
- export function reconcileWorktreeDb(
599
- mainDbPath: string,
600
- worktreeDbPath: string,
601
- ): { decisions: number; requirements: number; artifacts: number; conflicts: string[] } {
602
- const zero = { decisions: 0, requirements: 0, artifacts: 0, conflicts: [] as string[] };
603
-
604
- // Validate worktree DB exists
605
- if (!existsSync(worktreeDbPath)) {
606
- return zero;
607
- }
608
-
609
- // Safety: reject single quotes which could break the ATTACH DATABASE '...' SQL literal.
610
- // SQLite ATTACH doesn't support parameterized binding. We block the one dangerous char
611
- // rather than allowlisting, since OS temp paths vary widely (tildes, parens, unicode).
612
- if (worktreeDbPath.includes("'")) {
613
- process.stderr.write(`gsd-db: worktree DB reconciliation failed: path contains unsafe characters\n`);
614
- return zero;
615
- }
616
-
617
- // Ensure main DB is open
618
- if (!currentDb) {
619
- const opened = openDatabase(mainDbPath);
620
- if (!opened) {
621
- process.stderr.write(`gsd-db: worktree DB reconciliation failed: cannot open main DB\n`);
622
- return zero;
623
- }
624
- }
625
-
626
- const adapter = currentDb!;
627
- const conflicts: string[] = [];
628
-
629
- try {
630
- // ATTACH must be outside transaction
631
- adapter.exec(`ATTACH DATABASE '${worktreeDbPath}' AS wt`);
632
-
633
- try {
634
- // ── Conflict detection phase ──
635
- // Decisions: same id, different content
636
- const decisionConflicts = adapter.prepare(
637
- `SELECT m.id FROM decisions m
638
- INNER JOIN wt.decisions w ON m.id = w.id
639
- WHERE m.decision != w.decision
640
- OR m.choice != w.choice
641
- OR m.rationale != w.rationale
642
- OR m.superseded_by IS NOT w.superseded_by`,
643
- ).all();
644
- for (const row of decisionConflicts) {
645
- conflicts.push(`decision ${row['id']}: modified in both main and worktree`);
646
- }
647
-
648
- // Requirements: same id, different content
649
- const reqConflicts = adapter.prepare(
650
- `SELECT m.id FROM requirements m
651
- INNER JOIN wt.requirements w ON m.id = w.id
652
- WHERE m.description != w.description
653
- OR m.status != w.status
654
- OR m.notes != w.notes
655
- OR m.superseded_by IS NOT w.superseded_by`,
656
- ).all();
657
- for (const row of reqConflicts) {
658
- conflicts.push(`requirement ${row['id']}: modified in both main and worktree`);
659
- }
660
-
661
- // Artifacts: same path, different content
662
- const artifactConflicts = adapter.prepare(
663
- `SELECT m.path FROM artifacts m
664
- INNER JOIN wt.artifacts w ON m.path = w.path
665
- WHERE m.full_content != w.full_content
666
- OR m.artifact_type != w.artifact_type`,
667
- ).all();
668
- for (const row of artifactConflicts) {
669
- conflicts.push(`artifact ${row['path']}: modified in both main and worktree`);
670
- }
671
-
672
- // ── Merge phase (inside manual transaction) ──
673
- adapter.exec('BEGIN');
674
- try {
675
- // Decisions: exclude seq to let main auto-assign
676
- adapter.exec(
677
- `INSERT OR REPLACE INTO decisions (id, when_context, scope, decision, choice, rationale, revisable, superseded_by)
678
- SELECT id, when_context, scope, decision, choice, rationale, revisable, superseded_by FROM wt.decisions`,
679
- );
680
- const dCount = adapter.prepare('SELECT changes() as cnt').get();
681
-
682
- // Requirements: full row copy
683
- adapter.exec(
684
- `INSERT OR REPLACE INTO requirements (id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by)
685
- SELECT id, class, status, description, why, source, primary_owner, supporting_slices, validation, notes, full_content, superseded_by FROM wt.requirements`,
686
- );
687
- const rCount = adapter.prepare('SELECT changes() as cnt').get();
688
-
689
- // Artifacts: copy with fresh imported_at timestamp
690
- adapter.exec(
691
- `INSERT OR REPLACE INTO artifacts (path, artifact_type, milestone_id, slice_id, task_id, full_content, imported_at)
692
- SELECT path, artifact_type, milestone_id, slice_id, task_id, full_content, datetime('now') FROM wt.artifacts`,
693
- );
694
- const aCount = adapter.prepare('SELECT changes() as cnt').get();
695
-
696
- adapter.exec('COMMIT');
697
-
698
- const result = {
699
- decisions: (dCount?.['cnt'] as number) || 0,
700
- requirements: (rCount?.['cnt'] as number) || 0,
701
- artifacts: (aCount?.['cnt'] as number) || 0,
702
- conflicts,
703
- };
704
-
705
- if (conflicts.length > 0) {
706
- process.stderr.write(`gsd-db: reconciliation conflicts:\n${conflicts.map(c => ` - ${c}`).join('\n')}\n`);
707
- }
708
- process.stderr.write(
709
- `gsd-db: reconciled ${result.decisions} decisions, ${result.requirements} requirements, ${result.artifacts} artifacts (${conflicts.length} conflicts)\n`,
710
- );
711
-
712
- return result;
713
- } catch (err) {
714
- adapter.exec('ROLLBACK');
715
- throw err;
716
- }
717
- } finally {
718
- // DETACH always, even on error
719
- try {
720
- adapter.exec('DETACH DATABASE wt');
721
- } catch {
722
- // swallow — may already be detached
723
- }
724
- }
725
- } catch (err) {
726
- process.stderr.write(`gsd-db: worktree DB reconciliation failed: ${(err as Error).message}\n`);
727
- return zero;
728
- }
729
- }
730
-
731
567
  /**
732
568
  * Returns the PID of the process that opened the current DB connection.
733
569
  * Returns 0 if no connection is open.
@@ -104,7 +104,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
104
104
  const missing = milestoneIds.filter(id => {
105
105
  const hasContext = !!resolveMilestoneFile(basePath, id, "CONTEXT");
106
106
  const hasDraft = !!resolveMilestoneFile(basePath, id, "CONTEXT-DRAFT");
107
- const hasDir = existsSync(join(basePath, ".gsd", "milestones", id));
107
+ const hasDir = existsSync(join(gsdRoot(basePath), "milestones", id));
108
108
  return !hasContext && !hasDraft && !hasDir;
109
109
  });
110
110
  if (missing.length > 0) {
@@ -122,7 +122,7 @@ export function checkAutoStartAfterDiscuss(): boolean {
122
122
  // The LLM writes DISCUSSION-MANIFEST.json after each Phase 3 gate decision.
123
123
  // If the manifest exists but gates_completed < total, the LLM hasn't finished
124
124
  // presenting all readiness gates to the user — block auto-start.
125
- const manifestPath = join(basePath, ".gsd", "DISCUSSION-MANIFEST.json");
125
+ const manifestPath = join(gsdRoot(basePath), "DISCUSSION-MANIFEST.json");
126
126
  if (existsSync(manifestPath)) {
127
127
  try {
128
128
  const manifest = JSON.parse(readFileSync(manifestPath, "utf-8"));
@@ -295,7 +295,7 @@ export async function showHeadlessMilestoneCreation(
295
295
  const nextId = nextMilestoneId(existingIds, prefs?.preferences?.unique_milestone_ids ?? false);
296
296
 
297
297
  // Create milestone directory
298
- const milestoneDir = join(basePath, ".gsd", "milestones", nextId, "slices");
298
+ const milestoneDir = join(gsdRoot(basePath), "milestones", nextId, "slices");
299
299
  mkdirSync(milestoneDir, { recursive: true });
300
300
 
301
301
  // Build and dispatch the headless discuss prompt
@@ -410,11 +410,14 @@ export async function showDiscuss(
410
410
  basePath: string,
411
411
  ): Promise<void> {
412
412
  // Guard: no .gsd/ project
413
- if (!existsSync(join(basePath, ".gsd"))) {
413
+ if (!existsSync(gsdRoot(basePath))) {
414
414
  ctx.ui.notify("No GSD project found. Run /gsd to start one first.", "warning");
415
415
  return;
416
416
  }
417
417
 
418
+ // Invalidate caches to pick up artifacts written by a just-completed discuss/plan
419
+ invalidateAllCaches();
420
+
418
421
  const state = await deriveState(basePath);
419
422
 
420
423
  // Guard: no active milestone
@@ -750,7 +753,7 @@ export async function showSmartEntry(
750
753
  }
751
754
 
752
755
  // ── Detection preamble — run before any bootstrap ────────────────────
753
- if (!existsSync(join(basePath, ".gsd"))) {
756
+ if (!existsSync(gsdRoot(basePath))) {
754
757
  const detection = detectProjectState(basePath);
755
758
 
756
759
  // v1 .planning/ detected — offer migration before anything else
@@ -75,7 +75,7 @@ async function ensureDbAvailable(): Promise<boolean> {
75
75
  if (db.isDbAvailable()) return true;
76
76
 
77
77
  // Auto-initialize: open (and create if needed) the DB at the standard path
78
- const gsdDir = join(process.cwd(), ".gsd");
78
+ const gsdDir = gsdRoot(process.cwd());
79
79
  if (!existsSync(gsdDir)) return false; // No GSD project — can't create DB
80
80
  const dbPath = join(gsdDir, "gsd.db");
81
81
  return db.openDatabase(dbPath);
@@ -629,7 +629,7 @@ export default function (pi: ExtensionAPI) {
629
629
  description: shortcutDesc("Open GSD dashboard", "/gsd status"),
630
630
  handler: async (ctx) => {
631
631
  // Only show if .gsd/ exists
632
- if (!existsSync(join(process.cwd(), ".gsd"))) {
632
+ if (!existsSync(gsdRoot(process.cwd()))) {
633
633
  ctx.ui.notify("No .gsd/ directory found. Run /gsd to start.", "info");
634
634
  return;
635
635
  }
@@ -659,7 +659,7 @@ export default function (pi: ExtensionAPI) {
659
659
 
660
660
  // ── before_agent_start: inject GSD contract into true system prompt ─────
661
661
  pi.on("before_agent_start", async (event, ctx: ExtensionContext) => {
662
- if (!existsSync(join(process.cwd(), ".gsd"))) return;
662
+ if (!existsSync(gsdRoot(process.cwd()))) return;
663
663
 
664
664
  const stopContextTimer = debugTime("context-inject");
665
665
  const systemContent = loadPrompt("system");
@@ -18,6 +18,7 @@ import {
18
18
  import {
19
19
  resolveGsdRootFile,
20
20
  milestonesDir,
21
+ gsdRoot,
21
22
  resolveTaskFiles,
22
23
  } from './paths.js';
23
24
  import { findMilestoneIds } from './guided-flow.js';
@@ -298,7 +299,7 @@ const TASK_SUFFIXES = ['PLAN', 'SUMMARY', 'CONTINUE', 'CONTEXT', 'RESEARCH'];
298
299
  */
299
300
  function importHierarchyArtifacts(gsdDir: string): number {
300
301
  let count = 0;
301
- const gsdPath = join(gsdDir, '.gsd');
302
+ const gsdPath = gsdRoot(gsdDir);
302
303
 
303
304
  // Root-level artifacts: PROJECT.md, QUEUE.md
304
305
  const rootFiles = ['PROJECT.md', 'QUEUE.md', 'SECRETS-MANIFEST.md'];
@@ -487,7 +488,7 @@ export function migrateFromMarkdown(gsdDir: string): {
487
488
  requirements: number;
488
489
  artifacts: number;
489
490
  } {
490
- const dbPath = join(gsdDir, '.gsd', 'gsd.db');
491
+ const dbPath = join(gsdRoot(gsdDir), 'gsd.db');
491
492
 
492
493
  // Open DB if not already open
493
494
  if (!_getAdapter()) {