@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.
- package/CHANGELOG.md +96 -0
- package/README.md +41 -13
- package/agents/dgs-plan-checker.md +29 -3
- package/agents/dgs-planner.md +10 -0
- package/commands/dgs/abandon-quick.md +28 -0
- package/commands/dgs/add-tests.md +2 -2
- package/commands/dgs/audit-milestone.md +2 -2
- package/commands/dgs/capture-principle.md +11 -11
- package/commands/dgs/cleanup.md +2 -2
- package/commands/dgs/complete-milestone.md +11 -11
- package/commands/dgs/complete-quick.md +28 -0
- package/commands/dgs/create-milestone-job.md +2 -2
- package/commands/dgs/debug.md +3 -3
- package/commands/dgs/develop-idea.md +1 -1
- package/commands/dgs/fast.md +3 -1
- package/commands/dgs/health.md +1 -1
- package/commands/dgs/map-codebase.md +6 -6
- package/commands/dgs/new-milestone.md +5 -5
- package/commands/dgs/new-project.md +6 -6
- package/commands/dgs/plan-milestone-gaps.md +1 -1
- package/commands/dgs/progress.md +3 -3
- package/commands/dgs/quick-abandon.md +8 -0
- package/commands/dgs/quick-complete.md +8 -0
- package/commands/dgs/quick.md +10 -3
- package/commands/dgs/research-idea.md +2 -2
- package/commands/dgs/research-phase.md +3 -3
- package/commands/dgs/switch-project.md +1 -1
- package/commands/dgs/write-spec.md +3 -3
- package/deliver-great-systems/bin/dgs-tools.cjs +284 -30
- package/deliver-great-systems/bin/lib/commands.cjs +316 -31
- package/deliver-great-systems/bin/lib/commands.test.cjs +336 -0
- package/deliver-great-systems/bin/lib/config.cjs +39 -6
- package/deliver-great-systems/bin/lib/context.cjs +120 -0
- package/deliver-great-systems/bin/lib/core.cjs +28 -11
- package/deliver-great-systems/bin/lib/execution.cjs +49 -17
- package/deliver-great-systems/bin/lib/flat-migration.test.cjs +396 -0
- package/deliver-great-systems/bin/lib/ideas.cjs +206 -91
- package/deliver-great-systems/bin/lib/ideas.test.cjs +244 -1
- package/deliver-great-systems/bin/lib/init.cjs +306 -39
- package/deliver-great-systems/bin/lib/init.test.cjs +416 -6
- package/deliver-great-systems/bin/lib/jobs.cjs +124 -21
- package/deliver-great-systems/bin/lib/jobs.test.cjs +193 -74
- package/deliver-great-systems/bin/lib/migration.cjs +409 -1
- package/deliver-great-systems/bin/lib/migration.test.cjs +158 -1
- package/deliver-great-systems/bin/lib/milestone.cjs +54 -29
- package/deliver-great-systems/bin/lib/phase.cjs +128 -2
- package/deliver-great-systems/bin/lib/phase.test.cjs +420 -0
- package/deliver-great-systems/bin/lib/projects.cjs +28 -8
- package/deliver-great-systems/bin/lib/projects.test.cjs +86 -0
- package/deliver-great-systems/bin/lib/quick.cjs +584 -0
- package/deliver-great-systems/bin/lib/quick.test.cjs +596 -0
- package/deliver-great-systems/bin/lib/repos.cjs +25 -1
- package/deliver-great-systems/bin/lib/roadmap.cjs +34 -13
- package/deliver-great-systems/bin/lib/specs.cjs +3 -81
- package/deliver-great-systems/bin/lib/state-transition-gate.test.cjs +160 -0
- package/deliver-great-systems/bin/lib/state.cjs +142 -54
- package/deliver-great-systems/bin/lib/sync.cjs +75 -0
- package/deliver-great-systems/bin/lib/verify.cjs +80 -1
- package/deliver-great-systems/bin/lib/worktrees.cjs +764 -0
- package/deliver-great-systems/bin/lib/worktrees.test.cjs +887 -0
- package/deliver-great-systems/templates/claude-md.md +16 -0
- package/deliver-great-systems/workflows/abandon-quick.md +89 -0
- package/deliver-great-systems/workflows/add-idea.md +3 -3
- package/deliver-great-systems/workflows/add-tests.md +14 -0
- package/deliver-great-systems/workflows/add-todo.md +1 -0
- package/deliver-great-systems/workflows/approve-spec.md +25 -4
- package/deliver-great-systems/workflows/audit-phase.md +15 -5
- package/deliver-great-systems/workflows/cancel-job.md +1 -1
- package/deliver-great-systems/workflows/check-todos.md +2 -3
- package/deliver-great-systems/workflows/complete-milestone.md +197 -22
- package/deliver-great-systems/workflows/complete-quick.md +68 -0
- package/deliver-great-systems/workflows/consolidate-ideas.md +1 -1
- package/deliver-great-systems/workflows/create-milestone-job.md +4 -4
- package/deliver-great-systems/workflows/develop-idea.md +11 -11
- package/deliver-great-systems/workflows/diagnose-issues.md +14 -0
- package/deliver-great-systems/workflows/discuss-idea.md +1 -1
- package/deliver-great-systems/workflows/execute-phase.md +121 -32
- package/deliver-great-systems/workflows/execute-plan.md +12 -21
- package/deliver-great-systems/workflows/help.md +33 -29
- package/deliver-great-systems/workflows/init-product.md +2 -18
- package/deliver-great-systems/workflows/new-milestone.md +40 -24
- package/deliver-great-systems/workflows/new-project.md +22 -680
- package/deliver-great-systems/workflows/progress-all.md +133 -0
- package/deliver-great-systems/workflows/quick-abandon.md +89 -0
- package/deliver-great-systems/workflows/quick-complete.md +68 -0
- package/deliver-great-systems/workflows/quick.md +152 -23
- package/deliver-great-systems/workflows/refine-spec.md +1 -1
- package/deliver-great-systems/workflows/research-idea.md +8 -8
- package/deliver-great-systems/workflows/resume-project.md +2 -2
- package/deliver-great-systems/workflows/run-job.md +8 -8
- package/deliver-great-systems/workflows/validate-phase.md +39 -1
- package/deliver-great-systems/workflows/verify-work.md +14 -0
- package/deliver-great-systems/workflows/write-spec.md +2 -2
- package/package.json +1 -1
|
@@ -33,6 +33,7 @@ const { execSync } = require('child_process');
|
|
|
33
33
|
const { output, error, getProjectRoot } = require('./core.cjs');
|
|
34
34
|
const { getPlanningRoot } = require('./paths.cjs');
|
|
35
35
|
const { requireGitIdentity, formatAuthorString } = require('./identity.cjs');
|
|
36
|
+
const { extractFrontmatter, spliceFrontmatter, reconstructFrontmatter } = require('./frontmatter.cjs');
|
|
36
37
|
|
|
37
38
|
// ─── Constants ──────────────────────────────────────────────────────────────
|
|
38
39
|
|
|
@@ -48,6 +49,7 @@ const KNOWN_COMMANDS = new Set([
|
|
|
48
49
|
'plan-milestone-gaps',
|
|
49
50
|
'discuss-phase',
|
|
50
51
|
'research-phase',
|
|
52
|
+
'validate-phase',
|
|
51
53
|
'verify-phase',
|
|
52
54
|
]);
|
|
53
55
|
|
|
@@ -92,6 +94,8 @@ const STATUS_TO_MARKER = {
|
|
|
92
94
|
'pending': ' ',
|
|
93
95
|
};
|
|
94
96
|
|
|
97
|
+
const JOB_STATUSES = ['pending', 'in-progress', 'completed', 'failed', 'rolled_back'];
|
|
98
|
+
|
|
95
99
|
// ─── Internal Helpers ───────────────────────────────────────────────────────
|
|
96
100
|
|
|
97
101
|
/**
|
|
@@ -374,13 +378,31 @@ function moveJobFile(filePath, targetDir) {
|
|
|
374
378
|
}
|
|
375
379
|
|
|
376
380
|
/**
|
|
377
|
-
*
|
|
381
|
+
* Read status from a job file: YAML frontmatter first, then bold-key header fallback.
|
|
378
382
|
*
|
|
379
|
-
*
|
|
383
|
+
* @param {string} content - File content
|
|
384
|
+
* @returns {string|null} Status value or null
|
|
385
|
+
*/
|
|
386
|
+
function _readJobStatus(content) {
|
|
387
|
+
// Try YAML frontmatter first
|
|
388
|
+
const fmMatch = content.match(/^---\n([\s\S]+?)\n---/);
|
|
389
|
+
if (fmMatch) {
|
|
390
|
+
const statusMatch = fmMatch[1].match(/^status:\s*(.+)$/m);
|
|
391
|
+
if (statusMatch) return statusMatch[1].trim();
|
|
392
|
+
}
|
|
393
|
+
// Fall back to bold-key header: **Status:** value
|
|
394
|
+
const headerMatch = content.match(/\*\*Status:\*\*\s*(.+)/);
|
|
395
|
+
if (headerMatch) return headerMatch[1].trim().toLowerCase();
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
/**
|
|
400
|
+
* Locate a job file by milestone version.
|
|
401
|
+
* Scans flat jobs/ directory first (frontmatter status), then falls back to legacy subdirectories.
|
|
380
402
|
*
|
|
381
403
|
* @param {string} cwd - Working directory
|
|
382
404
|
* @param {string} version - Milestone version (e.g., "v6.0" or "6.0")
|
|
383
|
-
* @returns {{ found: boolean, path?: string, directory?: string }}
|
|
405
|
+
* @returns {{ found: boolean, path?: string, directory?: string, status?: string }}
|
|
384
406
|
*/
|
|
385
407
|
function findJobFile(cwd, version) {
|
|
386
408
|
// Normalize version: ensure 'v' prefix
|
|
@@ -388,25 +410,56 @@ function findJobFile(cwd, version) {
|
|
|
388
410
|
const fileName = `milestone-${normalized}.md`;
|
|
389
411
|
const jobsDir = path.join(getPlanningRoot(cwd), 'jobs');
|
|
390
412
|
|
|
391
|
-
//
|
|
413
|
+
// Flat-first scan: check jobs/ root directory
|
|
414
|
+
const flatPath = path.join(jobsDir, fileName);
|
|
415
|
+
if (fs.existsSync(flatPath)) {
|
|
416
|
+
const content = fs.readFileSync(flatPath, 'utf-8');
|
|
417
|
+
const status = _readJobStatus(content);
|
|
418
|
+
return { found: true, path: flatPath, directory: null, status: status || 'pending' };
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Legacy subdirectory scan
|
|
392
422
|
for (const dir of ['in-progress', 'pending', 'completed']) {
|
|
393
423
|
const filePath = path.join(jobsDir, dir, fileName);
|
|
394
424
|
if (fs.existsSync(filePath)) {
|
|
395
|
-
|
|
425
|
+
// Legacy fallback warning
|
|
426
|
+
process.stderr.write(`[DGS] Warning: job '${fileName}' found in legacy ${dir}/ directory. Run migration to flatten.\n`);
|
|
427
|
+
|
|
428
|
+
// Read frontmatter status if available
|
|
429
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
430
|
+
const fmStatus = _readJobStatus(content);
|
|
431
|
+
|
|
432
|
+
if (fmStatus && fmStatus !== dir && fmStatus !== dir.replace(/-/g, '_')) {
|
|
433
|
+
process.stderr.write(`[DGS] Warning: job '${fileName}' frontmatter status '${fmStatus}' disagrees with directory '${dir}'. Frontmatter wins.\n`);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
const effectiveStatus = fmStatus || dir;
|
|
437
|
+
return { found: true, path: filePath, directory: dir, status: effectiveStatus };
|
|
396
438
|
}
|
|
397
439
|
}
|
|
398
440
|
|
|
399
|
-
// Search project subdirectories: projects/*/jobs/
|
|
441
|
+
// Search project subdirectories: projects/*/jobs/ (flat first, then legacy)
|
|
400
442
|
const projectsDir = path.join(getPlanningRoot(cwd), 'projects');
|
|
401
443
|
if (fs.existsSync(projectsDir)) {
|
|
402
444
|
const projects = fs.readdirSync(projectsDir).filter(d =>
|
|
403
445
|
fs.statSync(path.join(projectsDir, d)).isDirectory()
|
|
404
446
|
);
|
|
405
447
|
for (const project of projects) {
|
|
448
|
+
// Flat first
|
|
449
|
+
const projFlatPath = path.join(projectsDir, project, 'jobs', fileName);
|
|
450
|
+
if (fs.existsSync(projFlatPath)) {
|
|
451
|
+
const content = fs.readFileSync(projFlatPath, 'utf-8');
|
|
452
|
+
const status = _readJobStatus(content);
|
|
453
|
+
return { found: true, path: projFlatPath, directory: null, status: status || 'pending' };
|
|
454
|
+
}
|
|
455
|
+
// Legacy subdirectory
|
|
406
456
|
for (const dir of ['in-progress', 'pending', 'completed']) {
|
|
407
457
|
const filePath = path.join(projectsDir, project, 'jobs', dir, fileName);
|
|
408
458
|
if (fs.existsSync(filePath)) {
|
|
409
|
-
|
|
459
|
+
process.stderr.write(`[DGS] Warning: job '${fileName}' found in legacy ${dir}/ directory. Run migration to flatten.\n`);
|
|
460
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
461
|
+
const fmStatus = _readJobStatus(content);
|
|
462
|
+
return { found: true, path: filePath, directory: dir, status: fmStatus || dir };
|
|
410
463
|
}
|
|
411
464
|
}
|
|
412
465
|
}
|
|
@@ -628,6 +681,53 @@ function insertGapFixSection(filePath, afterStepIndex, cycleNumber, newPhases, v
|
|
|
628
681
|
* @param {string} phasesJson - JSON string of phases array [{number, name}]
|
|
629
682
|
* @param {boolean} raw - Raw output mode
|
|
630
683
|
*/
|
|
684
|
+
/**
|
|
685
|
+
* Set a job's status in both YAML frontmatter and bold-key header (internal helper — throws on error).
|
|
686
|
+
*
|
|
687
|
+
* Dual-write strategy: YAML frontmatter is added/updated as the source of truth,
|
|
688
|
+
* and the existing **Status:** markdown header is kept in sync for human readability.
|
|
689
|
+
*
|
|
690
|
+
* @param {string} cwd - Working directory
|
|
691
|
+
* @param {string} version - Milestone version (e.g., "v20.0")
|
|
692
|
+
* @param {string} status - Target status (must be in JOB_STATUSES)
|
|
693
|
+
* @returns {{ version: string, previous_status: string, status: string, path: string }}
|
|
694
|
+
* @throws {Error} If status is invalid or job not found
|
|
695
|
+
*/
|
|
696
|
+
function setJobStatus(cwd, version, status) {
|
|
697
|
+
if (!JOB_STATUSES.includes(status)) {
|
|
698
|
+
throw new Error(`Invalid status: ${status}. Allowed: ${JOB_STATUSES.join(', ')}`);
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
const found = findJobFile(cwd, version);
|
|
702
|
+
if (!found.found) {
|
|
703
|
+
throw new Error(`Job not found: ${version}`);
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
let content = fs.readFileSync(found.path, 'utf-8');
|
|
707
|
+
const previousHeader = parseHeader(content);
|
|
708
|
+
const previousStatus = previousHeader.status;
|
|
709
|
+
|
|
710
|
+
// 1. Update bold-key markdown header (existing mechanism)
|
|
711
|
+
updateJobHeader(found.path, 'Status', status);
|
|
712
|
+
|
|
713
|
+
// 2. Update or add YAML frontmatter
|
|
714
|
+
content = fs.readFileSync(found.path, 'utf-8');
|
|
715
|
+
const fm = extractFrontmatter(content);
|
|
716
|
+
fm.status = status;
|
|
717
|
+
|
|
718
|
+
if (content.match(/^---\n/)) {
|
|
719
|
+
// Has frontmatter — splice it
|
|
720
|
+
content = spliceFrontmatter(content, fm);
|
|
721
|
+
} else {
|
|
722
|
+
// No frontmatter — add it at the very top
|
|
723
|
+
const yamlStr = reconstructFrontmatter({ status });
|
|
724
|
+
content = `---\n${yamlStr}\n---\n\n${content}`;
|
|
725
|
+
}
|
|
726
|
+
fs.writeFileSync(found.path, content, 'utf-8');
|
|
727
|
+
|
|
728
|
+
return { version, previous_status: previousStatus, status, path: found.path };
|
|
729
|
+
}
|
|
730
|
+
|
|
631
731
|
function cmdJobsInsertGapFixSection(cwd, file, afterIndexStr, cycleStr, version, phasesJson, raw) {
|
|
632
732
|
if (!file || afterIndexStr === undefined || cycleStr === undefined || !version || !phasesJson) {
|
|
633
733
|
error('Usage: jobs insert-gap-fix-section <file> <after-index> <cycle> <version> <phases-json>');
|
|
@@ -956,9 +1056,11 @@ function generateMilestoneSteps(phases, options) {
|
|
|
956
1056
|
steps.push({ command: 'map-codebase', args: `${num} --auto` });
|
|
957
1057
|
steps.push({ command: 'plan-phase', args: `${num} --non-interactive` });
|
|
958
1058
|
steps.push({ command: 'execute-phase', args: `${num} --non-interactive` });
|
|
1059
|
+
steps.push({ command: 'validate-phase', args: `${num} --scaffold` });
|
|
959
1060
|
steps.push({ command: 'audit-phase', args: `${num}` });
|
|
960
1061
|
} else if (NEEDS_EXECUTION.has(phase.disk_status)) {
|
|
961
1062
|
steps.push({ command: 'execute-phase', args: `${num} --non-interactive` });
|
|
1063
|
+
steps.push({ command: 'validate-phase', args: `${num} --scaffold` });
|
|
962
1064
|
steps.push({ command: 'audit-phase', args: `${num}` });
|
|
963
1065
|
}
|
|
964
1066
|
}
|
|
@@ -966,7 +1068,6 @@ function generateMilestoneSteps(phases, options) {
|
|
|
966
1068
|
// Append audit + complete steps if check is enabled
|
|
967
1069
|
if (check) {
|
|
968
1070
|
steps.push({ command: 'audit-milestone', args: version });
|
|
969
|
-
steps.push({ command: 'complete-milestone', args: version });
|
|
970
1071
|
}
|
|
971
1072
|
|
|
972
1073
|
return steps;
|
|
@@ -984,7 +1085,10 @@ function generateMilestoneSteps(phases, options) {
|
|
|
984
1085
|
function buildJobFileContent(version, check, steps, createdBy) {
|
|
985
1086
|
const created = new Date().toISOString().replace(/\.\d{3}Z$/, 'Z');
|
|
986
1087
|
|
|
987
|
-
|
|
1088
|
+
// YAML frontmatter (source of truth for status)
|
|
1089
|
+
let content = `---\nstatus: pending\n---\n\n`;
|
|
1090
|
+
|
|
1091
|
+
content += `# Milestone Job: ${version}\n\n`;
|
|
988
1092
|
content += `**Version:** ${version}\n`;
|
|
989
1093
|
content += `**Created:** ${created}\n`;
|
|
990
1094
|
if (createdBy) {
|
|
@@ -1219,11 +1323,11 @@ function cmdJobsCreateMilestone(cwd, version, check, raw) {
|
|
|
1219
1323
|
// Build file content
|
|
1220
1324
|
const content = buildJobFileContent(resolvedVersion, check, steps, createdBy);
|
|
1221
1325
|
|
|
1222
|
-
// Write to
|
|
1223
|
-
const
|
|
1224
|
-
fs.mkdirSync(
|
|
1326
|
+
// Write to flat jobs/ directory (status tracked in frontmatter)
|
|
1327
|
+
const jobsDir = path.join(getPlanningRoot(cwd), 'jobs');
|
|
1328
|
+
fs.mkdirSync(jobsDir, { recursive: true });
|
|
1225
1329
|
const fileName = `milestone-${resolvedVersion}.md`;
|
|
1226
|
-
const filePath = path.join(
|
|
1330
|
+
const filePath = path.join(jobsDir, fileName);
|
|
1227
1331
|
fs.writeFileSync(filePath, content, 'utf-8');
|
|
1228
1332
|
|
|
1229
1333
|
// Count phases that contribute at least one step
|
|
@@ -1234,7 +1338,7 @@ function cmdJobsCreateMilestone(cwd, version, check, raw) {
|
|
|
1234
1338
|
const result = {
|
|
1235
1339
|
created: true,
|
|
1236
1340
|
version: resolvedVersion,
|
|
1237
|
-
file: path.join(path.relative(cwd, getPlanningRoot(cwd)) || '.', 'jobs',
|
|
1341
|
+
file: path.join(path.relative(cwd, getPlanningRoot(cwd)) || '.', 'jobs', fileName),
|
|
1238
1342
|
step_count: steps.length,
|
|
1239
1343
|
check,
|
|
1240
1344
|
phase_count: phasesWithSteps.length,
|
|
@@ -1397,17 +1501,14 @@ function cancelJob(cwd, version) {
|
|
|
1397
1501
|
}
|
|
1398
1502
|
}
|
|
1399
1503
|
|
|
1400
|
-
//
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
// Move to pending/
|
|
1404
|
-
const pendingDir = path.join(getPlanningRoot(cwd), 'jobs', 'pending');
|
|
1405
|
-
const moveResult = moveJobFile(filePath, pendingDir);
|
|
1504
|
+
// Set job status to pending via frontmatter edit (no file move)
|
|
1505
|
+
// setJobStatus handles both markdown header and frontmatter update
|
|
1506
|
+
setJobStatus(cwd, version, 'pending');
|
|
1406
1507
|
|
|
1407
1508
|
return {
|
|
1408
1509
|
cancelled: true,
|
|
1409
1510
|
version: version.startsWith('v') ? version : 'v' + version,
|
|
1410
|
-
path:
|
|
1511
|
+
path: filePath,
|
|
1411
1512
|
steps_reset: stepsReset,
|
|
1412
1513
|
};
|
|
1413
1514
|
}
|
|
@@ -1983,6 +2084,8 @@ module.exports = {
|
|
|
1983
2084
|
insertJobSteps,
|
|
1984
2085
|
buildGapFixSteps,
|
|
1985
2086
|
buildPhaseGapFixSteps,
|
|
2087
|
+
JOB_STATUSES,
|
|
2088
|
+
setJobStatus,
|
|
1986
2089
|
insertGapFixSection,
|
|
1987
2090
|
insertPhaseGapFixSection,
|
|
1988
2091
|
updatePhaseFixCycleHeader,
|