mdkg 0.3.0 → 0.3.2
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 +90 -1
- package/CLI_COMMAND_MATRIX.md +1188 -0
- package/README.md +86 -5
- package/dist/cli.js +130 -10
- package/dist/command-contract.json +7473 -0
- package/dist/commands/doctor.js +370 -86
- package/dist/commands/fix.js +934 -0
- package/dist/commands/format.js +8 -2
- package/dist/commands/goal.js +1 -1
- package/dist/commands/next.js +2 -2
- package/dist/commands/skill.js +13 -3
- package/dist/commands/skill_support.js +3 -3
- package/dist/commands/status.js +270 -0
- package/dist/commands/subgraph.js +300 -0
- package/dist/commands/task.js +2 -2
- package/dist/commands/validate.js +12 -1
- package/dist/commands/workspace.js +19 -7
- package/dist/graph/goal_scope.js +1 -1
- package/dist/graph/node.js +2 -1
- package/dist/init/CLI_COMMAND_MATRIX.md +20 -2
- package/dist/init/README.md +35 -2
- package/dist/init/core/rule-5-release-and-versioning.md +13 -4
- package/dist/init/init-manifest.json +9 -4
- package/dist/init/templates/default/spike.md +81 -0
- package/dist/pack/order.js +3 -2
- package/dist/util/argparse.js +1 -0
- package/package.json +11 -3
|
@@ -10,6 +10,8 @@ exports.runSubgraphRemoveCommand = runSubgraphRemoveCommand;
|
|
|
10
10
|
exports.runSubgraphEnableCommand = runSubgraphEnableCommand;
|
|
11
11
|
exports.runSubgraphDisableCommand = runSubgraphDisableCommand;
|
|
12
12
|
exports.runSubgraphVerifyCommand = runSubgraphVerifyCommand;
|
|
13
|
+
exports.runSubgraphAuditCommand = runSubgraphAuditCommand;
|
|
14
|
+
exports.runSubgraphUpgradePlanCommand = runSubgraphUpgradePlanCommand;
|
|
13
15
|
exports.runSubgraphRefreshCommand = runSubgraphRefreshCommand;
|
|
14
16
|
exports.runSubgraphSyncCommand = runSubgraphSyncCommand;
|
|
15
17
|
exports.runSubgraphMaterializeCommand = runSubgraphMaterializeCommand;
|
|
@@ -321,6 +323,304 @@ function runSubgraphVerifyCommand(options) {
|
|
|
321
323
|
throw new errors_1.ValidationError("subgraph verify failed");
|
|
322
324
|
}
|
|
323
325
|
}
|
|
326
|
+
function pushAuditCheck(receipt, check) {
|
|
327
|
+
receipt.checks.push(check);
|
|
328
|
+
if (!check.ok && check.severity === "error") {
|
|
329
|
+
receipt.errors.push(`${check.id}: ${check.message}`);
|
|
330
|
+
}
|
|
331
|
+
else if (!check.ok && check.severity === "warning") {
|
|
332
|
+
receipt.warnings.push(`${check.id}: ${check.message}`);
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
function inspectSourcePathForAudit(root, alias, subgraph) {
|
|
336
|
+
try {
|
|
337
|
+
return inspectSourcePath(root, alias, subgraph, true);
|
|
338
|
+
}
|
|
339
|
+
catch {
|
|
340
|
+
return undefined;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
function auditOneAlias(options) {
|
|
344
|
+
const receipt = {
|
|
345
|
+
alias: options.alias,
|
|
346
|
+
enabled: options.health.enabled,
|
|
347
|
+
visibility: options.health.visibility,
|
|
348
|
+
source_path: options.health.source_path,
|
|
349
|
+
source_repo: options.health.source_repo,
|
|
350
|
+
capability_summary: {
|
|
351
|
+
node_count: options.nodeTypes.length,
|
|
352
|
+
spec_count: options.nodeTypes.filter((type) => type === "spec").length,
|
|
353
|
+
work_count: options.nodeTypes.filter((type) => type === "work").length,
|
|
354
|
+
skill_count: options.nodeTypes.filter((type) => type === "skill").length,
|
|
355
|
+
},
|
|
356
|
+
checks: [],
|
|
357
|
+
warnings: [],
|
|
358
|
+
errors: [],
|
|
359
|
+
ok: true,
|
|
360
|
+
};
|
|
361
|
+
pushAuditCheck(receipt, {
|
|
362
|
+
id: "subgraph.enabled",
|
|
363
|
+
ok: options.subgraph.enabled,
|
|
364
|
+
severity: options.subgraph.enabled ? "info" : "warning",
|
|
365
|
+
message: options.subgraph.enabled ? "subgraph is enabled" : "subgraph is disabled",
|
|
366
|
+
});
|
|
367
|
+
if (!options.subgraph.source_path) {
|
|
368
|
+
pushAuditCheck(receipt, {
|
|
369
|
+
id: "subgraph.source_path.configured",
|
|
370
|
+
ok: false,
|
|
371
|
+
severity: "warning",
|
|
372
|
+
message: "source_path is not configured; sync and upgrade planning cannot inspect child Git state",
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
else {
|
|
376
|
+
let gitState;
|
|
377
|
+
try {
|
|
378
|
+
gitState = inspectSourcePath(options.root, options.alias, options.subgraph, true);
|
|
379
|
+
receipt.source_git_head = gitState.head;
|
|
380
|
+
receipt.source_repo_current = gitState.sourceRepo;
|
|
381
|
+
receipt.dirty_tracked = gitState.dirtyTracked;
|
|
382
|
+
receipt.dirty_tracked_paths = gitState.dirtyTrackedPaths;
|
|
383
|
+
pushAuditCheck(receipt, {
|
|
384
|
+
id: "subgraph.source_path.git_root",
|
|
385
|
+
ok: true,
|
|
386
|
+
severity: "info",
|
|
387
|
+
message: "source_path is a contained child Git repo root with .mdkg",
|
|
388
|
+
path: options.subgraph.source_path,
|
|
389
|
+
});
|
|
390
|
+
pushAuditCheck(receipt, {
|
|
391
|
+
id: "subgraph.source_path.clean",
|
|
392
|
+
ok: !gitState.dirtyTracked,
|
|
393
|
+
severity: "warning",
|
|
394
|
+
message: gitState.dirtyTracked
|
|
395
|
+
? `source_path has dirty tracked changes: ${gitState.dirtyTrackedPaths.join(", ")}`
|
|
396
|
+
: "source_path has no dirty tracked changes",
|
|
397
|
+
path: options.subgraph.source_path,
|
|
398
|
+
details: { dirty_tracked_paths: gitState.dirtyTrackedPaths },
|
|
399
|
+
});
|
|
400
|
+
if (options.subgraph.source_repo && options.subgraph.source_repo !== gitState.sourceRepo) {
|
|
401
|
+
pushAuditCheck(receipt, {
|
|
402
|
+
id: "subgraph.source_repo.current",
|
|
403
|
+
ok: false,
|
|
404
|
+
severity: "warning",
|
|
405
|
+
message: `configured source_repo differs from child repo head: ${options.subgraph.source_repo} -> ${gitState.sourceRepo}`,
|
|
406
|
+
details: { configured: options.subgraph.source_repo, current: gitState.sourceRepo },
|
|
407
|
+
});
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
catch (err) {
|
|
411
|
+
pushAuditCheck(receipt, {
|
|
412
|
+
id: "subgraph.source_path.git_root",
|
|
413
|
+
ok: false,
|
|
414
|
+
severity: "error",
|
|
415
|
+
message: err instanceof Error ? err.message : String(err),
|
|
416
|
+
path: options.subgraph.source_path,
|
|
417
|
+
});
|
|
418
|
+
}
|
|
419
|
+
if (gitState) {
|
|
420
|
+
for (const source of options.subgraph.sources) {
|
|
421
|
+
const bundlePath = path_1.default.resolve(options.root, source.path);
|
|
422
|
+
pushAuditCheck(receipt, {
|
|
423
|
+
id: "subgraph.bundle.root_owned",
|
|
424
|
+
ok: !isPathWithin(gitState.sourceRoot, bundlePath),
|
|
425
|
+
severity: "error",
|
|
426
|
+
message: isPathWithin(gitState.sourceRoot, bundlePath)
|
|
427
|
+
? `bundle path must be root-owned and outside source_path: ${source.path}`
|
|
428
|
+
: `bundle path is root-owned and outside source_path: ${source.path}`,
|
|
429
|
+
path: source.path,
|
|
430
|
+
});
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
for (const source of options.health.sources) {
|
|
435
|
+
pushAuditCheck(receipt, {
|
|
436
|
+
id: "subgraph.bundle.enabled",
|
|
437
|
+
ok: source.enabled,
|
|
438
|
+
severity: source.enabled ? "info" : "warning",
|
|
439
|
+
message: source.enabled ? `source is enabled: ${source.path}` : `source is disabled: ${source.path}`,
|
|
440
|
+
path: source.path,
|
|
441
|
+
});
|
|
442
|
+
pushAuditCheck(receipt, {
|
|
443
|
+
id: "subgraph.bundle.valid",
|
|
444
|
+
ok: source.error_count === 0,
|
|
445
|
+
severity: "error",
|
|
446
|
+
message: source.error_count === 0
|
|
447
|
+
? `bundle source is valid: ${source.path}`
|
|
448
|
+
: `bundle source has errors: ${source.errors.join("; ")}`,
|
|
449
|
+
path: source.path,
|
|
450
|
+
details: { errors: source.errors },
|
|
451
|
+
});
|
|
452
|
+
pushAuditCheck(receipt, {
|
|
453
|
+
id: "subgraph.bundle.fresh",
|
|
454
|
+
ok: !source.stale,
|
|
455
|
+
severity: "warning",
|
|
456
|
+
message: source.stale
|
|
457
|
+
? `bundle source is stale: ${source.warnings.join("; ")}`
|
|
458
|
+
: `bundle source is fresh: ${source.path}`,
|
|
459
|
+
path: source.path,
|
|
460
|
+
details: { warnings: source.warnings },
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
if (options.targetRoot) {
|
|
464
|
+
const outputDir = path_1.default.join(options.targetRoot, options.alias);
|
|
465
|
+
const markerPath = path_1.default.join(outputDir, ".mdkg-materialized.json");
|
|
466
|
+
const exists = fs_1.default.existsSync(outputDir);
|
|
467
|
+
const hasMarker = fs_1.default.existsSync(markerPath);
|
|
468
|
+
pushAuditCheck(receipt, {
|
|
469
|
+
id: "subgraph.materialize.target_safe",
|
|
470
|
+
ok: !exists || hasMarker,
|
|
471
|
+
severity: "error",
|
|
472
|
+
message: !exists
|
|
473
|
+
? `materialize target is available: ${relativeToRoot(options.root, outputDir)}`
|
|
474
|
+
: hasMarker
|
|
475
|
+
? `materialize target has mdkg marker: ${relativeToRoot(options.root, outputDir)}`
|
|
476
|
+
: `materialize target exists without mdkg marker: ${relativeToRoot(options.root, outputDir)}`,
|
|
477
|
+
path: relativeToRoot(options.root, outputDir),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
receipt.ok = receipt.errors.length === 0;
|
|
481
|
+
return receipt;
|
|
482
|
+
}
|
|
483
|
+
function runSubgraphAuditCommand(options) {
|
|
484
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
485
|
+
const aliases = selectAliases(config, options.alias, options.all);
|
|
486
|
+
const projection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index;
|
|
487
|
+
const targetRoot = options.target ? path_1.default.resolve(options.root, normalizeContained(options.target, "--target")) : undefined;
|
|
488
|
+
const results = aliases.map((alias) => {
|
|
489
|
+
const health = projection.subgraphs.find((item) => item.alias === alias);
|
|
490
|
+
if (!health) {
|
|
491
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
492
|
+
}
|
|
493
|
+
const nodeTypes = Object.values(projection.nodes)
|
|
494
|
+
.filter((node) => node.ws === alias)
|
|
495
|
+
.map((node) => node.type);
|
|
496
|
+
return auditOneAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], health, nodeTypes, targetRoot });
|
|
497
|
+
});
|
|
498
|
+
const errors = results.flatMap((item) => item.errors.map((error) => `${item.alias}: ${error}`));
|
|
499
|
+
const warnings = results.flatMap((item) => item.warnings.map((warning) => `${item.alias}: ${warning}`));
|
|
500
|
+
const receipt = {
|
|
501
|
+
action: "audited",
|
|
502
|
+
ok: errors.length === 0,
|
|
503
|
+
count: results.length,
|
|
504
|
+
target: targetRoot ? relativeToRoot(options.root, targetRoot) : undefined,
|
|
505
|
+
errors,
|
|
506
|
+
warnings,
|
|
507
|
+
subgraphs: results,
|
|
508
|
+
};
|
|
509
|
+
if (options.json) {
|
|
510
|
+
writeJson(receipt);
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
console.log(`subgraphs audited: ${results.length}`);
|
|
514
|
+
for (const item of results) {
|
|
515
|
+
const status = item.errors.length > 0 ? "error" : item.warnings.length > 0 ? "warning" : "ok";
|
|
516
|
+
console.log(`${item.alias} | ${status} | checks:${item.checks.length}`);
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
if (!receipt.ok) {
|
|
520
|
+
throw new errors_1.ValidationError("subgraph audit failed");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
function upgradePlanForAlias(options) {
|
|
524
|
+
const actions = [];
|
|
525
|
+
const blockers = [];
|
|
526
|
+
if (!options.subgraph.enabled) {
|
|
527
|
+
actions.push({
|
|
528
|
+
action: "none",
|
|
529
|
+
status: "skipped",
|
|
530
|
+
reason: "subgraph is disabled",
|
|
531
|
+
});
|
|
532
|
+
return { alias: options.alias, ok: true, capability_summary: options.audit.capability_summary, actions, blockers };
|
|
533
|
+
}
|
|
534
|
+
if (!options.subgraph.source_path) {
|
|
535
|
+
blockers.push("source_path is required for upgrade planning");
|
|
536
|
+
}
|
|
537
|
+
const sourceGitError = options.audit.checks.find((check) => check.id === "subgraph.source_path.git_root" && !check.ok && check.severity === "error");
|
|
538
|
+
if (sourceGitError) {
|
|
539
|
+
blockers.push(sourceGitError.message);
|
|
540
|
+
}
|
|
541
|
+
if (options.audit.dirty_tracked) {
|
|
542
|
+
blockers.push(`source_path has dirty tracked changes: ${options.audit.dirty_tracked_paths?.join(", ")}`);
|
|
543
|
+
}
|
|
544
|
+
const rootOwnedErrors = options.audit.checks.filter((check) => check.id === "subgraph.bundle.root_owned" && !check.ok);
|
|
545
|
+
blockers.push(...rootOwnedErrors.map((check) => check.message));
|
|
546
|
+
const bundleErrors = options.health.sources.flatMap((source) => source.errors.map((error) => `${source.path}: ${error}`));
|
|
547
|
+
blockers.push(...bundleErrors);
|
|
548
|
+
const needsSync = options.health.stale ||
|
|
549
|
+
!options.subgraph.source_repo ||
|
|
550
|
+
(options.audit.source_repo_current !== undefined && options.subgraph.source_repo !== options.audit.source_repo_current);
|
|
551
|
+
if (blockers.length > 0) {
|
|
552
|
+
actions.push({
|
|
553
|
+
action: "subgraph.sync",
|
|
554
|
+
status: "blocked",
|
|
555
|
+
command: `mdkg subgraph sync ${options.alias} --dry-run --json`,
|
|
556
|
+
blockers,
|
|
557
|
+
});
|
|
558
|
+
}
|
|
559
|
+
else if (needsSync) {
|
|
560
|
+
actions.push({
|
|
561
|
+
action: "subgraph.sync",
|
|
562
|
+
status: "planned",
|
|
563
|
+
command: `mdkg subgraph sync ${options.alias} --dry-run --json`,
|
|
564
|
+
apply_command: `mdkg subgraph sync ${options.alias} --json`,
|
|
565
|
+
reason: options.health.stale ? "bundle is stale or source HEAD changed" : "configured source_repo is missing or behind current child head",
|
|
566
|
+
current_source_repo: options.audit.source_repo_current,
|
|
567
|
+
configured_source_repo: options.subgraph.source_repo,
|
|
568
|
+
});
|
|
569
|
+
}
|
|
570
|
+
else {
|
|
571
|
+
actions.push({
|
|
572
|
+
action: "subgraph.verify",
|
|
573
|
+
status: "planned",
|
|
574
|
+
command: `mdkg subgraph verify ${options.alias} --json`,
|
|
575
|
+
reason: "bundle snapshot is current; verify before downstream use",
|
|
576
|
+
});
|
|
577
|
+
}
|
|
578
|
+
actions.push({
|
|
579
|
+
action: "subgraph.materialize",
|
|
580
|
+
status: "optional",
|
|
581
|
+
command: `mdkg subgraph materialize ${options.alias} --target .mdkg/subgraphs --gitignore --json`,
|
|
582
|
+
reason: "generate an ignored read-only inspection tree when human review needs file-level child graph context",
|
|
583
|
+
});
|
|
584
|
+
return { alias: options.alias, ok: blockers.length === 0, capability_summary: options.audit.capability_summary, actions, blockers };
|
|
585
|
+
}
|
|
586
|
+
function runSubgraphUpgradePlanCommand(options) {
|
|
587
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
588
|
+
const aliases = selectAliases(config, options.alias, options.all);
|
|
589
|
+
const projection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index;
|
|
590
|
+
const plans = aliases.map((alias) => {
|
|
591
|
+
const health = projection.subgraphs.find((item) => item.alias === alias);
|
|
592
|
+
if (!health) {
|
|
593
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
594
|
+
}
|
|
595
|
+
const nodeTypes = Object.values(projection.nodes)
|
|
596
|
+
.filter((node) => node.ws === alias)
|
|
597
|
+
.map((node) => node.type);
|
|
598
|
+
const audit = auditOneAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], health, nodeTypes });
|
|
599
|
+
return upgradePlanForAlias({ root: options.root, alias, subgraph: config.subgraphs[alias], audit, health });
|
|
600
|
+
});
|
|
601
|
+
const blockers = plans.flatMap((plan) => plan.blockers.map((blocker) => `${plan.alias}: ${blocker}`));
|
|
602
|
+
const receipt = {
|
|
603
|
+
action: "upgrade_plan",
|
|
604
|
+
ok: blockers.length === 0,
|
|
605
|
+
count: plans.length,
|
|
606
|
+
apply_supported: false,
|
|
607
|
+
mutation_policy: "read_only_plan",
|
|
608
|
+
blockers,
|
|
609
|
+
subgraphs: plans,
|
|
610
|
+
};
|
|
611
|
+
if (options.json) {
|
|
612
|
+
writeJson(receipt);
|
|
613
|
+
}
|
|
614
|
+
else {
|
|
615
|
+
console.log(`subgraph upgrade plan: ${plans.length}`);
|
|
616
|
+
for (const plan of plans) {
|
|
617
|
+
console.log(`${plan.alias} | ${plan.ok ? "ok" : "blocked"} | actions:${plan.actions.length}`);
|
|
618
|
+
}
|
|
619
|
+
}
|
|
620
|
+
if (!receipt.ok) {
|
|
621
|
+
throw new errors_1.ValidationError("subgraph upgrade plan blocked");
|
|
622
|
+
}
|
|
623
|
+
}
|
|
324
624
|
function runSubgraphRefreshCommand(options) {
|
|
325
625
|
withSubgraphLock(options.root, () => {
|
|
326
626
|
const config = (0, config_1.loadConfig)(options.root);
|
package/dist/commands/task.js
CHANGED
|
@@ -22,7 +22,7 @@ const atomic_1 = require("../util/atomic");
|
|
|
22
22
|
const lock_1 = require("../util/lock");
|
|
23
23
|
const event_support_1 = require("./event_support");
|
|
24
24
|
const checkpoint_1 = require("./checkpoint");
|
|
25
|
-
const MUTABLE_TASK_TYPES = new Set(["feat", "task", "bug", "test"]);
|
|
25
|
+
const MUTABLE_TASK_TYPES = new Set(["feat", "task", "bug", "test", "spike"]);
|
|
26
26
|
const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
27
27
|
function parseCsvList(raw) {
|
|
28
28
|
if (!raw) {
|
|
@@ -113,7 +113,7 @@ function loadMutableTaskNode(root, idOrQid, wsHint) {
|
|
|
113
113
|
throw new errors_1.UsageError(`cannot mutate read-only subgraph node ${node.qid}; update the source workspace for subgraph ${node.source.subgraph_alias}`);
|
|
114
114
|
}
|
|
115
115
|
if (!MUTABLE_TASK_TYPES.has(node.type)) {
|
|
116
|
-
throw new errors_1.UsageError(`mdkg task only supports feat, task, bug, and
|
|
116
|
+
throw new errors_1.UsageError(`mdkg task only supports feat, task, bug, test, and spike nodes; use markdown editing for ${node.type}:${node.id}`);
|
|
117
117
|
}
|
|
118
118
|
const filePath = path_1.default.resolve(root, node.path);
|
|
119
119
|
const content = fs_1.default.readFileSync(filePath, "utf8");
|
|
@@ -36,6 +36,17 @@ const RECOMMENDED_HEADINGS = {
|
|
|
36
36
|
"Links / Artifacts",
|
|
37
37
|
],
|
|
38
38
|
feat: ["Overview", "Acceptance Criteria", "Notes"],
|
|
39
|
+
spike: [
|
|
40
|
+
"Research Question",
|
|
41
|
+
"Context And Constraints",
|
|
42
|
+
"Search Plan",
|
|
43
|
+
"Findings",
|
|
44
|
+
"Options And Tradeoffs",
|
|
45
|
+
"Recommendation",
|
|
46
|
+
"Follow-Up Nodes To Create",
|
|
47
|
+
"Skill Candidates",
|
|
48
|
+
"Evidence And Sources",
|
|
49
|
+
],
|
|
39
50
|
epic: ["Goal", "Scope", "Milestones", "Out of Scope", "Risks", "Links / Artifacts"],
|
|
40
51
|
checkpoint: [
|
|
41
52
|
"Summary",
|
|
@@ -274,7 +285,7 @@ function runValidateCommand(options) {
|
|
|
274
285
|
});
|
|
275
286
|
if (idsByWorkspace[alias].has(node.id)) {
|
|
276
287
|
const firstPath = idsByWorkspace[alias].get(node.id);
|
|
277
|
-
errors.push(`${filePath}: duplicate id ${node.id} in workspace ${alias} (also in ${firstPath})`);
|
|
288
|
+
errors.push(`${path_1.default.relative(options.root, filePath).split(path_1.default.sep).join("/")}: duplicate id ${node.id} in workspace ${alias} (also in ${firstPath ? path_1.default.relative(options.root, firstPath).split(path_1.default.sep).join("/") : "unknown"})`);
|
|
278
289
|
continue;
|
|
279
290
|
}
|
|
280
291
|
idsByWorkspace[alias].set(node.id, filePath);
|
|
@@ -4,16 +4,18 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.runWorkspaceListCommand = runWorkspaceListCommand;
|
|
7
|
-
exports.runWorkspaceAddCommand = runWorkspaceAddCommand;
|
|
8
|
-
exports.runWorkspaceRemoveCommand = runWorkspaceRemoveCommand;
|
|
9
7
|
exports.runWorkspaceEnableCommand = runWorkspaceEnableCommand;
|
|
10
8
|
exports.runWorkspaceDisableCommand = runWorkspaceDisableCommand;
|
|
9
|
+
exports.runWorkspaceAddCommand = runWorkspaceAddCommand;
|
|
10
|
+
exports.runWorkspaceRemoveCommand = runWorkspaceRemoveCommand;
|
|
11
11
|
const fs_1 = __importDefault(require("fs"));
|
|
12
12
|
const path_1 = __importDefault(require("path"));
|
|
13
13
|
const config_1 = require("../core/config");
|
|
14
14
|
const migrate_1 = require("../core/migrate");
|
|
15
15
|
const workspace_path_1 = require("../core/workspace_path");
|
|
16
|
+
const atomic_1 = require("../util/atomic");
|
|
16
17
|
const errors_1 = require("../util/errors");
|
|
18
|
+
const lock_1 = require("../util/lock");
|
|
17
19
|
const ALIAS_RE = /^[a-z][a-z0-9_]*$/;
|
|
18
20
|
function workspaceReceipt(alias, workspace) {
|
|
19
21
|
return {
|
|
@@ -59,7 +61,7 @@ function readRawConfig(root) {
|
|
|
59
61
|
return { path: configPath, raw: migrated };
|
|
60
62
|
}
|
|
61
63
|
function writeRawConfig(configPath, raw) {
|
|
62
|
-
|
|
64
|
+
(0, atomic_1.atomicWriteFile)(configPath, `${JSON.stringify(raw, null, 2)}\n`);
|
|
63
65
|
}
|
|
64
66
|
function normalizeAlias(alias) {
|
|
65
67
|
if (alias.toLowerCase() === "all") {
|
|
@@ -114,7 +116,7 @@ function normalizeVisibility(value) {
|
|
|
114
116
|
}
|
|
115
117
|
throw new errors_1.UsageError("--visibility must be private, internal, or public");
|
|
116
118
|
}
|
|
117
|
-
function
|
|
119
|
+
function runWorkspaceAddCommandLocked(options) {
|
|
118
120
|
const alias = normalizeAlias(options.alias);
|
|
119
121
|
const workspacePath = normalizeCommandWorkspacePath(options.workspacePath, "workspace path");
|
|
120
122
|
const mdkgDir = normalizeCommandWorkspacePath(options.mdkgDir ?? ".mdkg", "workspace mdkg dir");
|
|
@@ -153,7 +155,7 @@ function runWorkspaceAddCommand(options) {
|
|
|
153
155
|
fs_1.default.mkdirSync(path_1.default.join(wsRoot, "work"), { recursive: true });
|
|
154
156
|
printWorkspaceMutationReceipt("added", workspaceReceipt(alias, workspace), options.json);
|
|
155
157
|
}
|
|
156
|
-
function
|
|
158
|
+
function runWorkspaceRemoveCommandLocked(options) {
|
|
157
159
|
const alias = normalizeAlias(options.alias);
|
|
158
160
|
if (alias === "root") {
|
|
159
161
|
throw new errors_1.UsageError("cannot remove root workspace");
|
|
@@ -196,8 +198,18 @@ function setWorkspaceEnabled(options, enabled) {
|
|
|
196
198
|
printWorkspaceMutationReceipt(enabled ? "enabled" : "disabled", workspaceReceipt(alias, updated), options.json);
|
|
197
199
|
}
|
|
198
200
|
function runWorkspaceEnableCommand(options) {
|
|
199
|
-
|
|
201
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
202
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => setWorkspaceEnabled(options, true));
|
|
200
203
|
}
|
|
201
204
|
function runWorkspaceDisableCommand(options) {
|
|
202
|
-
|
|
205
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
206
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => setWorkspaceEnabled(options, false));
|
|
207
|
+
}
|
|
208
|
+
function runWorkspaceAddCommand(options) {
|
|
209
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
210
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runWorkspaceAddCommandLocked(options));
|
|
211
|
+
}
|
|
212
|
+
function runWorkspaceRemoveCommand(options) {
|
|
213
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
214
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runWorkspaceRemoveCommandLocked(options));
|
|
203
215
|
}
|
package/dist/graph/goal_scope.js
CHANGED
|
@@ -5,7 +5,7 @@ exports.goalScopeRefs = goalScopeRefs;
|
|
|
5
5
|
exports.collectGoalScope = collectGoalScope;
|
|
6
6
|
const qid_1 = require("../util/qid");
|
|
7
7
|
exports.GOAL_SCOPE_CONTAINER_TYPES = new Set(["epic", "feat"]);
|
|
8
|
-
exports.GOAL_SCOPE_ACTIONABLE_TYPES = new Set(["feat", "task", "bug", "test"]);
|
|
8
|
+
exports.GOAL_SCOPE_ACTIONABLE_TYPES = new Set(["feat", "task", "bug", "test", "spike"]);
|
|
9
9
|
exports.GOAL_SCOPE_ALLOWED_TYPES = new Set([
|
|
10
10
|
...exports.GOAL_SCOPE_CONTAINER_TYPES,
|
|
11
11
|
...exports.GOAL_SCOPE_ACTIONABLE_TYPES,
|
package/dist/graph/node.js
CHANGED
|
@@ -10,7 +10,7 @@ const id_1 = require("../util/id");
|
|
|
10
10
|
const refs_1 = require("../util/refs");
|
|
11
11
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
12
12
|
const DEC_ID_RE = /^dec-[0-9]+$/;
|
|
13
|
-
exports.WORK_TYPES = new Set(["goal", "epic", "feat", "task", "bug", "checkpoint", "test"]);
|
|
13
|
+
exports.WORK_TYPES = new Set(["goal", "epic", "feat", "task", "bug", "spike", "checkpoint", "test"]);
|
|
14
14
|
exports.DEC_TYPES = new Set(["dec"]);
|
|
15
15
|
exports.ALLOWED_TYPES = new Set([
|
|
16
16
|
"rule",
|
|
@@ -23,6 +23,7 @@ exports.ALLOWED_TYPES = new Set([
|
|
|
23
23
|
"feat",
|
|
24
24
|
"task",
|
|
25
25
|
"bug",
|
|
26
|
+
"spike",
|
|
26
27
|
"checkpoint",
|
|
27
28
|
"test",
|
|
28
29
|
"archive",
|
|
@@ -26,6 +26,15 @@ Primary commands:
|
|
|
26
26
|
- `mdkg goal`
|
|
27
27
|
- `mdkg task`
|
|
28
28
|
- `mdkg validate`
|
|
29
|
+
- `mdkg status [--json]`
|
|
30
|
+
- `mdkg fix plan [--family index|refs|ids|all] [--target <id-or-qid>] [--json]`
|
|
31
|
+
|
|
32
|
+
Operator health:
|
|
33
|
+
- `mdkg status [--json]` is a read-only summary for scripts and agents
|
|
34
|
+
- reports mdkg version/config, git state, graph/index freshness, selected-goal state, project DB verification summary, and generated cache status
|
|
35
|
+
- does not rebuild indexes, run migrations, repair files, mutate graph nodes, or change selected-goal state
|
|
36
|
+
- `mdkg fix plan ...` is dry-run repair planning only; it writes nothing and `fix apply` is not exposed
|
|
37
|
+
- `fix plan --json` returns a receipt-shaped plan with selected families, risk counts, paths, reason codes, and `apply_supported: false`
|
|
29
38
|
|
|
30
39
|
Index backend:
|
|
31
40
|
- fresh mdkg workspaces default to `index.backend: sqlite`
|
|
@@ -83,6 +92,7 @@ Validation commands:
|
|
|
83
92
|
Node creation commands:
|
|
84
93
|
- `mdkg new <type> "<title>" [options] [--json]`
|
|
85
94
|
- `mdkg new goal "<title>" [options] [--json]`
|
|
95
|
+
- `mdkg new spike "<research question>" [options] [--json]`
|
|
86
96
|
|
|
87
97
|
Agent workflow file type creation:
|
|
88
98
|
- `mdkg new spec "<title>" [options] [--json]`
|
|
@@ -100,6 +110,9 @@ Agent workflow notes:
|
|
|
100
110
|
- `spec` and `work` scaffold as validation-clean standalone docs.
|
|
101
111
|
- `work_order`, `receipt`, `feedback`, `dispute`, and `proposal` need real refs before strict `mdkg validate` passes.
|
|
102
112
|
- `goal` nodes capture recursive objective state and required checks, but normal `mdkg next` does not select them.
|
|
113
|
+
- `spike` nodes are actionable research/planning work under `.mdkg/work/`; use `mdkg task start|update|done` for lifecycle state.
|
|
114
|
+
- Spikes record sources, findings, recommendations, follow-up node ideas, and skill candidates in Markdown body sections; they do not perform web search, execute research, create follow-up nodes, generate `SKILL.md`, or expose a `mdkg spike ...` namespace automatically.
|
|
115
|
+
- after fresh init, run `mdkg index` before treating `mdkg doctor --strict --json` as a clean health gate; init writes source scaffold files and index writes generated caches.
|
|
103
116
|
|
|
104
117
|
Workspace registry commands:
|
|
105
118
|
- `mdkg workspace ls [--json]`
|
|
@@ -116,6 +129,7 @@ Task mutation commands:
|
|
|
116
129
|
- `mdkg task start <id-or-qid> [--ws <alias>] [--run-id <id>] [--note "<text>"] [--json]`
|
|
117
130
|
- `mdkg task update <id-or-qid> [options] [--json]`
|
|
118
131
|
- `mdkg task done <id-or-qid> [--checkpoint "<title>"] [options] [--json]`
|
|
132
|
+
- task commands support task-like `feat`, `task`, `bug`, `test`, and `spike` nodes
|
|
119
133
|
|
|
120
134
|
Checkpoint commands:
|
|
121
135
|
- `mdkg checkpoint new <title> [--ws <alias>] [--json]`
|
|
@@ -148,7 +162,7 @@ Capability discovery:
|
|
|
148
162
|
- capability records are deterministic cache projections from Markdown
|
|
149
163
|
- records include source hash, headings, refs, and `indexed_at`
|
|
150
164
|
- SPEC and WORK capability records include read-only `linkage` arrays for related SPECs, work contracts, work orders, and receipts when those graph mirrors exist
|
|
151
|
-
- normal task, epic, feat, bug, test, and checkpoint nodes are intentionally excluded
|
|
165
|
+
- normal task, epic, feat, bug, test, spike, and checkpoint nodes are intentionally excluded
|
|
152
166
|
|
|
153
167
|
Spec capability records:
|
|
154
168
|
- `mdkg spec list [--json]`
|
|
@@ -193,10 +207,14 @@ Subgraph orchestration:
|
|
|
193
207
|
- `mdkg subgraph disable <alias> [--json]`
|
|
194
208
|
- `mdkg subgraph verify [alias|--all] [--json]`
|
|
195
209
|
- `mdkg subgraph refresh [alias|--all] [--json]`
|
|
210
|
+
- `mdkg subgraph audit [alias|--all] [--target <path>] [--json]`
|
|
211
|
+
- `mdkg subgraph upgrade-plan [alias|--all] [--json]`
|
|
196
212
|
- `mdkg subgraph sync [alias|--all] [--dry-run] [--allow-dirty] [--json]`
|
|
197
213
|
- `mdkg subgraph materialize [alias|--all] --target <path> [--clean] [--gitignore] [--json]`
|
|
198
214
|
- subgraphs are read-only planning views and use subgraph-alias qids such as `child_repo:task-1`
|
|
199
215
|
- subgraph refresh reloads configured bundle sources only and never mutates child repos
|
|
216
|
+
- subgraph audit reports read-only source/bundle/materialized-target safety checks
|
|
217
|
+
- subgraph upgrade-plan returns a read-only downstream plan with `apply_supported: false`
|
|
200
218
|
- subgraph sync builds root-owned bundle snapshots from configured clean child Git repo `source_path` entries
|
|
201
219
|
- subgraph materialize extracts generated inspection trees under a target directory; `.mdkg/subgraphs/` is local generated state
|
|
202
220
|
- public/internal subgraphs require public bundle profiles
|
|
@@ -240,7 +258,7 @@ Goal nodes:
|
|
|
240
258
|
- `mdkg goal pause <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
241
259
|
- `mdkg goal resume <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
242
260
|
- `mdkg goal done <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
243
|
-
- goals orchestrate recursive progress through explicit `scope_refs`; tasks, bugs, tests, and features remain concrete executable units
|
|
261
|
+
- goals orchestrate recursive progress through explicit `scope_refs`; tasks, bugs, tests, spikes, and features remain concrete executable units
|
|
244
262
|
- `goal next` is read-only; use `goal claim` to set `active_node`
|
|
245
263
|
- `mdkg goal evaluate` is report-only and never runs commands from `required_checks`
|
|
246
264
|
- skill improvements discovered during normal goal execution should be recorded as candidates or proposals unless the active node is skill-maintenance
|
package/dist/init/README.md
CHANGED
|
@@ -8,7 +8,7 @@ mdkg is pre-v1 public alpha software. Graph, cache, bundle, and DAL contracts ma
|
|
|
8
8
|
|
|
9
9
|
- `core/`: rules, operating guide, and pinned docs
|
|
10
10
|
- `design/`: product/engineering decision records
|
|
11
|
-
- `work/`: epics, tasks, bugs, tests, checkpoints
|
|
11
|
+
- `work/`: epics, tasks, bugs, tests, spikes, checkpoints
|
|
12
12
|
- `templates/`: default node templates
|
|
13
13
|
- `archive/`: sidecar metadata and deterministic compressed source/artifact caches
|
|
14
14
|
- `bundles/`: optional committed full graph snapshot bundles
|
|
@@ -20,6 +20,7 @@ mdkg is pre-v1 public alpha software. Graph, cache, bundle, and DAL contracts ma
|
|
|
20
20
|
|
|
21
21
|
```bash
|
|
22
22
|
mdkg upgrade
|
|
23
|
+
mdkg index
|
|
23
24
|
mdkg new task "..." --status todo --priority 1
|
|
24
25
|
mdkg search "..."
|
|
25
26
|
mdkg show <id>
|
|
@@ -29,10 +30,38 @@ mdkg spec list --json
|
|
|
29
30
|
mdkg archive list
|
|
30
31
|
mdkg bundle create --profile private
|
|
31
32
|
mdkg subgraph list --json
|
|
33
|
+
mdkg status --json
|
|
34
|
+
mdkg fix plan --json
|
|
32
35
|
mdkg validate
|
|
33
36
|
```
|
|
34
37
|
|
|
35
|
-
This repo is already initialized. Use `mdkg upgrade` to preview safe scaffold updates, `mdkg new` to create work, `mdkg new goal "..."` plus `mdkg goal select/current/next/claim/evaluate` for recursive long-running objectives, `mdkg search`/`mdkg show` to inspect graph state, `mdkg capability ...` to inspect cached skill/spec/work/core/design capabilities, `mdkg spec ...` for focused optional SPEC records, `mdkg capability resolve ...` to rank local and subgraph capabilities, `mdkg archive ...` to register source/artifact sidecars, `mdkg work ...` to create work contract/order/receipt semantic mirrors and deterministic trigger/verification records, `mdkg bundle ...` to create full graph snapshot bundles, `mdkg subgraph ...` to register read-only child graph planning views, `mdkg pack <id>` to build deterministic context, and `mdkg validate` before closeout.
|
|
38
|
+
This repo is already initialized. Use `mdkg upgrade` to preview safe scaffold updates, `mdkg index` to create or refresh generated graph/skill/capability/subgraph and SQLite caches after init, `mdkg new` to create work, `mdkg new goal "..."` plus `mdkg goal select/current/next/claim/evaluate` for recursive long-running objectives, `mdkg search`/`mdkg show` to inspect graph state, `mdkg capability ...` to inspect cached skill/spec/work/core/design capabilities, `mdkg spec ...` for focused optional SPEC records, `mdkg capability resolve ...` to rank local and subgraph capabilities, `mdkg archive ...` to register source/artifact sidecars, `mdkg work ...` to create work contract/order/receipt semantic mirrors and deterministic trigger/verification records, `mdkg bundle ...` to create full graph snapshot bundles, `mdkg subgraph ...` to register read-only child graph planning views, `mdkg pack <id>` to build deterministic context, and `mdkg validate` before closeout.
|
|
39
|
+
|
|
40
|
+
Use `mdkg status --json` for a read-only operator summary of Git, graph,
|
|
41
|
+
selected-goal, project DB, and generated-cache health before mutating work. Use
|
|
42
|
+
`mdkg doctor --strict --json` for typed diagnostic checks in CI or agent
|
|
43
|
+
orchestration. These commands inspect state; they do not apply repairs. After a
|
|
44
|
+
fresh init, run `mdkg index` first so strict doctor can load generated caches.
|
|
45
|
+
|
|
46
|
+
Use `mdkg fix plan --json` for dry-run repair guidance. It reports generated
|
|
47
|
+
index/cache repair hints, missing graph references, and duplicate local ids as
|
|
48
|
+
receipt-shaped planned changes with risk levels and `apply_supported: false`.
|
|
49
|
+
`fix apply` is not exposed; repair application is intentionally deferred.
|
|
50
|
+
|
|
51
|
+
Use research spikes for investigation and planning work that should produce a
|
|
52
|
+
reviewable recommendation before implementation:
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
mdkg new spike "research docs launch workflow" --status todo --priority 1
|
|
56
|
+
mdkg task start spike-1
|
|
57
|
+
mdkg show spike-1
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Spikes use the existing task lifecycle and goal routing. They do not perform web
|
|
61
|
+
search, execute research, create follow-up nodes, or generate `SKILL.md` files
|
|
62
|
+
automatically. Record sources, findings, recommendations, follow-up node ideas,
|
|
63
|
+
and skill candidates in the spike body, then create follow-up tasks or tests
|
|
64
|
+
intentionally with `mdkg new task ...` or `mdkg new test ...`.
|
|
36
65
|
|
|
37
66
|
Agent workflow docs can use semantic ids:
|
|
38
67
|
|
|
@@ -130,8 +159,12 @@ Register child bundle snapshots as read-only subgraphs with:
|
|
|
130
159
|
mdkg subgraph add child_repo child-repo/.mdkg/bundles/private/all.mdkg.zip --source-path child-repo
|
|
131
160
|
mdkg capability resolve "child capability" --json
|
|
132
161
|
mdkg subgraph verify child_repo --json
|
|
162
|
+
mdkg subgraph audit child_repo --target .mdkg/subgraphs --json
|
|
163
|
+
mdkg subgraph upgrade-plan child_repo --json
|
|
133
164
|
```
|
|
134
165
|
|
|
166
|
+
Use `mdkg subgraph audit child_repo --target .mdkg/subgraphs --json` to inspect source-path Git state, dirty tracked child files, bundle validity/freshness, root-owned bundle-path safety, optional materialized-target marker safety, and count-only capability summaries. Use `mdkg subgraph upgrade-plan child_repo --json` for a read-only downstream plan; it returns `apply_supported: false`.
|
|
167
|
+
|
|
135
168
|
Use `mdkg subgraph sync child_repo --dry-run --json` before writing a refreshed root-owned child bundle snapshot, then `mdkg subgraph sync child_repo --json` when the receipt is acceptable. `sync` inspects the configured child Git repo `source_path`, refuses dirty tracked changes by default, verifies the new bundle, and records `source_repo` as `<branch>@<sha>` without committing, pulling, pushing, checking out, resetting, or mutating child mdkg Markdown.
|
|
136
169
|
|
|
137
170
|
Use `mdkg subgraph materialize child_repo --target .mdkg/subgraphs --gitignore --json` only when you need a generated read-only inspection tree. Materialized views are local generated state and are not root graph nodes.
|
|
@@ -67,10 +67,19 @@ It MUST NOT include:
|
|
|
67
67
|
- verify `package.json` `"files"` includes only expected files
|
|
68
68
|
- optionally run `npm pack` and inspect tarball contents
|
|
69
69
|
|
|
70
|
-
7)
|
|
71
|
-
- `npm
|
|
72
|
-
|
|
73
|
-
|
|
70
|
+
7) Confirm npm auth
|
|
71
|
+
- if using an exported `NPM_TOKEN`, create a temporary npm userconfig that
|
|
72
|
+
references `${NPM_TOKEN}` literally:
|
|
73
|
+
`//registry.npmjs.org/:_authToken=${NPM_TOKEN}`
|
|
74
|
+
- run `npm whoami --registry=https://registry.npmjs.org/ --userconfig=/private/tmp/mdkg-npm-publish.npmrc`
|
|
75
|
+
- do not print the token, do not commit the temp userconfig, and do not add
|
|
76
|
+
unsupported `always-auth` config
|
|
77
|
+
|
|
78
|
+
8) Publish
|
|
79
|
+
- `npm publish --registry=https://registry.npmjs.org/ --userconfig=/private/tmp/mdkg-npm-publish.npmrc`
|
|
80
|
+
- omit `--userconfig` only when an existing npm login has already been verified
|
|
81
|
+
|
|
82
|
+
9) Tag and push
|
|
74
83
|
- create git tag matching version (example `v0.1.0`)
|
|
75
84
|
- push commits and tags
|
|
76
85
|
|