@ktpartners/dgs-platform 2.8.0 → 3.0.4

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 (94) hide show
  1. package/CHANGELOG.md +96 -0
  2. package/README.md +41 -13
  3. package/agents/dgs-plan-checker.md +29 -3
  4. package/agents/dgs-planner.md +10 -0
  5. package/commands/dgs/abandon-quick.md +28 -0
  6. package/commands/dgs/add-tests.md +2 -2
  7. package/commands/dgs/audit-milestone.md +2 -2
  8. package/commands/dgs/capture-principle.md +11 -11
  9. package/commands/dgs/cleanup.md +2 -2
  10. package/commands/dgs/complete-milestone.md +11 -11
  11. package/commands/dgs/complete-quick.md +28 -0
  12. package/commands/dgs/create-milestone-job.md +2 -2
  13. package/commands/dgs/debug.md +3 -3
  14. package/commands/dgs/develop-idea.md +1 -1
  15. package/commands/dgs/fast.md +3 -1
  16. package/commands/dgs/health.md +1 -1
  17. package/commands/dgs/map-codebase.md +6 -6
  18. package/commands/dgs/new-milestone.md +5 -5
  19. package/commands/dgs/new-project.md +6 -6
  20. package/commands/dgs/plan-milestone-gaps.md +1 -1
  21. package/commands/dgs/progress.md +3 -3
  22. package/commands/dgs/quick-abandon.md +8 -0
  23. package/commands/dgs/quick-complete.md +8 -0
  24. package/commands/dgs/quick.md +10 -3
  25. package/commands/dgs/research-idea.md +2 -2
  26. package/commands/dgs/research-phase.md +3 -3
  27. package/commands/dgs/switch-project.md +1 -1
  28. package/commands/dgs/write-spec.md +3 -3
  29. package/deliver-great-systems/bin/dgs-tools.cjs +284 -30
  30. package/deliver-great-systems/bin/lib/commands.cjs +316 -31
  31. package/deliver-great-systems/bin/lib/commands.test.cjs +336 -0
  32. package/deliver-great-systems/bin/lib/config.cjs +39 -6
  33. package/deliver-great-systems/bin/lib/context.cjs +120 -0
  34. package/deliver-great-systems/bin/lib/core.cjs +28 -11
  35. package/deliver-great-systems/bin/lib/execution.cjs +49 -17
  36. package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
  37. package/deliver-great-systems/bin/lib/ideas.cjs +206 -91
  38. package/deliver-great-systems/bin/lib/ideas.test.cjs +244 -1
  39. package/deliver-great-systems/bin/lib/init.cjs +306 -39
  40. package/deliver-great-systems/bin/lib/init.test.cjs +416 -6
  41. package/deliver-great-systems/bin/lib/jobs.cjs +124 -21
  42. package/deliver-great-systems/bin/lib/jobs.test.cjs +193 -74
  43. package/deliver-great-systems/bin/lib/migration.cjs +409 -1
  44. package/deliver-great-systems/bin/lib/migration.test.cjs +158 -1
  45. package/deliver-great-systems/bin/lib/milestone.cjs +54 -29
  46. package/deliver-great-systems/bin/lib/phase.cjs +128 -2
  47. package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
  48. package/deliver-great-systems/bin/lib/projects.cjs +28 -8
  49. package/deliver-great-systems/bin/lib/projects.test.cjs +86 -0
  50. package/deliver-great-systems/bin/lib/quick.cjs +584 -0
  51. package/deliver-great-systems/bin/lib/quick.test.cjs +596 -0
  52. package/deliver-great-systems/bin/lib/repos.cjs +25 -1
  53. package/deliver-great-systems/bin/lib/roadmap.cjs +34 -13
  54. package/deliver-great-systems/bin/lib/specs.cjs +3 -81
  55. package/deliver-great-systems/bin/lib/state-transition-gate.test.cjs +160 -0
  56. package/deliver-great-systems/bin/lib/state.cjs +142 -54
  57. package/deliver-great-systems/bin/lib/sync.cjs +75 -0
  58. package/deliver-great-systems/bin/lib/verify.cjs +80 -1
  59. package/deliver-great-systems/bin/lib/worktrees.cjs +764 -0
  60. package/deliver-great-systems/bin/lib/worktrees.test.cjs +887 -0
  61. package/deliver-great-systems/templates/claude-md.md +16 -0
  62. package/deliver-great-systems/workflows/abandon-quick.md +89 -0
  63. package/deliver-great-systems/workflows/add-idea.md +3 -3
  64. package/deliver-great-systems/workflows/add-tests.md +14 -0
  65. package/deliver-great-systems/workflows/add-todo.md +1 -0
  66. package/deliver-great-systems/workflows/approve-spec.md +25 -4
  67. package/deliver-great-systems/workflows/audit-phase.md +15 -5
  68. package/deliver-great-systems/workflows/cancel-job.md +1 -1
  69. package/deliver-great-systems/workflows/check-todos.md +2 -3
  70. package/deliver-great-systems/workflows/complete-milestone.md +197 -22
  71. package/deliver-great-systems/workflows/complete-quick.md +68 -0
  72. package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
  73. package/deliver-great-systems/workflows/create-milestone-job.md +4 -4
  74. package/deliver-great-systems/workflows/develop-idea.md +11 -11
  75. package/deliver-great-systems/workflows/diagnose-issues.md +14 -0
  76. package/deliver-great-systems/workflows/discuss-idea.md +1 -1
  77. package/deliver-great-systems/workflows/execute-phase.md +121 -32
  78. package/deliver-great-systems/workflows/execute-plan.md +12 -21
  79. package/deliver-great-systems/workflows/help.md +33 -29
  80. package/deliver-great-systems/workflows/init-product.md +2 -18
  81. package/deliver-great-systems/workflows/new-milestone.md +40 -24
  82. package/deliver-great-systems/workflows/new-project.md +22 -680
  83. package/deliver-great-systems/workflows/progress-all.md +133 -0
  84. package/deliver-great-systems/workflows/quick-abandon.md +89 -0
  85. package/deliver-great-systems/workflows/quick-complete.md +68 -0
  86. package/deliver-great-systems/workflows/quick.md +152 -23
  87. package/deliver-great-systems/workflows/refine-spec.md +1 -1
  88. package/deliver-great-systems/workflows/research-idea.md +8 -8
  89. package/deliver-great-systems/workflows/resume-project.md +2 -2
  90. package/deliver-great-systems/workflows/run-job.md +8 -8
  91. package/deliver-great-systems/workflows/validate-phase.md +39 -1
  92. package/deliver-great-systems/workflows/verify-work.md +14 -0
  93. package/deliver-great-systems/workflows/write-spec.md +2 -2
  94. package/package.json +1 -1
@@ -378,6 +378,75 @@ function pullAll(cwd, options = {}) {
378
378
  * @param {string[]|null} [options.repos=null] - Filter to push only specific repos by name
379
379
  * @returns {{ ok: boolean, results: Array<{ repo: string, path: string, status: string, commits: number|null, message: string }>, problems?: Array, summary: string }}
380
380
  */
381
+ /**
382
+ * Push active worktree branches to remote as backup. Best-effort — failures
383
+ * are appended to results but do not block other sync operations.
384
+ *
385
+ * Reads worktree entries from config.local.json for the current project.
386
+ * Skips stale entries where the worktree directory no longer exists.
387
+ * Push-only — no pull of worktree branches (deferred to future).
388
+ *
389
+ * @param {string} cwd - Planning root
390
+ * @param {Array} results - Results array to append push outcomes to
391
+ */
392
+ function _pushWorktreeBranches(cwd, results) {
393
+ const config = loadConfig(cwd);
394
+ const project = config.current_project;
395
+ if (!project) return;
396
+
397
+ let localConfig;
398
+ try {
399
+ const localPath = getLocalConfigPath(cwd);
400
+ if (!fs.existsSync(localPath)) return;
401
+ localConfig = JSON.parse(fs.readFileSync(localPath, 'utf-8'));
402
+ } catch {
403
+ return; // No config.local.json or invalid JSON — nothing to push
404
+ }
405
+
406
+ const worktrees = (localConfig.projects && localConfig.projects[project] && localConfig.projects[project].worktrees) || {};
407
+
408
+ for (const [slug, entry] of Object.entries(worktrees)) {
409
+ const type = entry.type; // 'milestone' or 'quick'
410
+ const repos = entry.repos || {};
411
+
412
+ for (const [repoName, worktreePath] of Object.entries(repos)) {
413
+ // Skip stale worktrees (directory doesn't exist)
414
+ if (!fs.existsSync(worktreePath)) continue;
415
+
416
+ // Determine branch name based on type
417
+ const branchName = type === 'milestone' ? 'milestone/' + slug : 'quick/' + slug;
418
+ const label = repoName + ' (worktree: ' + slug + ')';
419
+
420
+ try {
421
+ // Check if worktree has a remote
422
+ if (!hasRemote(worktreePath)) continue;
423
+
424
+ // Push the worktree branch from the worktree directory
425
+ const pushResult = execGitWithTimeout(worktreePath, ['push', '-u', 'origin', branchName], 30000);
426
+
427
+ results.push({
428
+ repo: label,
429
+ path: worktreePath,
430
+ status: pushResult.exitCode === 0 ? 'pushed' : 'failed',
431
+ commits: 0,
432
+ message: pushResult.exitCode === 0
433
+ ? 'Pushed worktree branch ' + branchName
434
+ : 'Failed to push ' + branchName + ': ' + (pushResult.stderr || '').trim(),
435
+ });
436
+ } catch (e) {
437
+ // Best-effort — failure does not block other operations
438
+ results.push({
439
+ repo: label,
440
+ path: worktreePath,
441
+ status: 'failed',
442
+ commits: 0,
443
+ message: 'Push failed: ' + (e.message || String(e)),
444
+ });
445
+ }
446
+ }
447
+ }
448
+ }
449
+
381
450
  function pushAll(cwd, options = {}) {
382
451
  const { dryRun = false, force = false, repos: repoFilter = null } = options;
383
452
  let allRepos = collectSyncRepos(cwd);
@@ -468,6 +537,12 @@ function pushAll(cwd, options = {}) {
468
537
  results.push({ repo: label, path: repo.path, status: 'pushed', commits: commitCount, message: `Pushed ${commitLabel}` });
469
538
  }
470
539
 
540
+ // --- Worktree Branch Push ---
541
+ // Push active worktree branches as remote backup (best-effort, non-blocking)
542
+ if (!dryRun) {
543
+ _pushWorktreeBranches(cwd, results);
544
+ }
545
+
471
546
  // Build summary
472
547
  const pushed = results.filter(r => r.status === 'pushed').length;
473
548
  const current = results.filter(r => r.status === 'current').length;
@@ -708,6 +708,86 @@ function cmdValidateHealth(cwd, options, raw) {
708
708
  }
709
709
  }
710
710
 
711
+ // ─── Check 10: Uncommitted tracking files in completed phases ─────────────
712
+ // Surface the condition `phase finalize` was designed to prevent: a completed
713
+ // phase whose ROADMAP.md / STATE.md / REQUIREMENTS.md / VERIFICATION.md are
714
+ // modified in the working tree but not committed (e.g., Claude dropped the
715
+ // commit step between updating trackers and invoking `commit`).
716
+ try {
717
+ const { execGit } = require('./core.cjs');
718
+ const isGit = execGit(cwd, ['rev-parse', '--is-inside-work-tree']);
719
+ if (isGit.exitCode === 0 && isGit.stdout.trim() === 'true') {
720
+ // git resolves symlinks when reporting paths; normalize both sides against
721
+ // the real git root so relative paths compare correctly on macOS
722
+ // (/var/folders → /private/var/folders).
723
+ const gitRootRes = execGit(cwd, ['rev-parse', '--show-toplevel']);
724
+ const gitRoot = gitRootRes.exitCode === 0 ? gitRootRes.stdout.trim() : cwd;
725
+ const statusRes = execGit(cwd, ['status', '--porcelain']);
726
+ if (statusRes.exitCode === 0) {
727
+ // `git status --porcelain` emits "XY path" lines (X=index, Y=worktree,
728
+ // column 3 is a separator space, path starts at column 4). Note:
729
+ // execGit.stdout is already .trim()'d so lines may have lost a leading
730
+ // space when X=' ' (worktree-only changes). Extract the path from
731
+ // whatever follows the first whitespace group.
732
+ const dirtyPaths = statusRes.stdout
733
+ .split('\n')
734
+ .filter(Boolean)
735
+ .map(l => {
736
+ // Match "XY<space>path" OR "<space>Y<space>path" (leading space may be trimmed).
737
+ // A path after 2 status chars + 1 space begins at index 3 of the original,
738
+ // but lines like " M file" got left-trimmed to "M file" (only one status char).
739
+ // Normalize: skip the leading non-space + exactly one space.
740
+ const m = l.match(/^\S{1,2}\s(.+)$/);
741
+ return m ? m[1].trim() : l.trim();
742
+ });
743
+
744
+ // Identify completed phases from ROADMAP.md checkboxes
745
+ const completedPhases = new Set();
746
+ if (fs.existsSync(roadmapPath)) {
747
+ const rm = fs.readFileSync(roadmapPath, 'utf-8');
748
+ const re = /-\s*\[x\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)/gi;
749
+ let m;
750
+ while ((m = re.exec(rm)) !== null) completedPhases.add(m[1]);
751
+ }
752
+
753
+ // Tracking files at the planning root — compute relative to gitRoot
754
+ const trackingRoot = ['ROADMAP.md', 'STATE.md', 'REQUIREMENTS.md']
755
+ .map(f => path.relative(gitRoot, path.join(planningDir, f)));
756
+
757
+ // VERIFICATION.md files inside completed phase dirs
758
+ const trackingVerif = [];
759
+ if (completedPhases.size > 0) {
760
+ try {
761
+ const entries = fs.readdirSync(phasesDir, { withFileTypes: true });
762
+ for (const e of entries) {
763
+ if (!e.isDirectory()) continue;
764
+ const dm = e.name.match(/^(\d+[A-Z]?(?:\.\d+)*)/i);
765
+ if (!dm) continue;
766
+ const phaseNum = dm[1];
767
+ const unpadded = String(parseInt(phaseNum, 10));
768
+ if (!completedPhases.has(phaseNum) && !completedPhases.has(unpadded)) continue;
769
+ const phaseFiles = fs.readdirSync(path.join(phasesDir, e.name));
770
+ for (const f of phaseFiles) {
771
+ if (/-VERIFICATION\.md$/i.test(f)) {
772
+ trackingVerif.push(path.relative(gitRoot, path.join(phasesDir, e.name, f)));
773
+ }
774
+ }
775
+ }
776
+ } catch { /* ignore */ }
777
+ }
778
+
779
+ const allTracking = [...trackingRoot, ...trackingVerif];
780
+ const dirtyTracking = dirtyPaths.filter(p => allTracking.includes(p));
781
+ if (dirtyTracking.length > 0) {
782
+ addIssue('warning', 'W009',
783
+ `Uncommitted tracking files for completed phase(s): ${dirtyTracking.join(', ')}`,
784
+ 'Run `dgs-tools phase finalize <phase>` for the relevant phase, or commit manually'
785
+ );
786
+ }
787
+ }
788
+ }
789
+ } catch { /* non-git or git error — skip silently */ }
790
+
711
791
  // ─── Perform repairs if requested ─────────────────────────────────────────
712
792
  const repairActions = [];
713
793
  if (options.repair && repairs.length > 0) {
@@ -720,7 +800,6 @@ function cmdValidateHealth(cwd, options, raw) {
720
800
  model_profile: 'balanced',
721
801
  commit_docs: true,
722
802
  search_gitignored: false,
723
- branching_strategy: 'none',
724
803
  research: true,
725
804
  plan_checker: true,
726
805
  verifier: true,