mdkg 0.3.2 → 0.3.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 +74 -0
- package/CLI_COMMAND_MATRIX.md +28 -11
- package/README.md +10 -6
- package/dist/cli.js +87 -10
- package/dist/command-contract.json +406 -11
- package/dist/commands/fix.js +468 -16
- package/dist/commands/goal.js +148 -12
- package/dist/commands/new.js +4 -3
- package/dist/graph/node.js +4 -3
- package/dist/graph/validate_graph.js +21 -0
- package/dist/init/AGENT_START.md +2 -2
- package/dist/init/CLI_COMMAND_MATRIX.md +15 -4
- package/dist/init/README.md +8 -3
- package/dist/init/init-manifest.json +5 -5
- package/dist/init/skills/default/pursue-mdkg-goal/SKILL.md +2 -1
- package/package.json +4 -2
package/dist/commands/goal.js
CHANGED
|
@@ -7,12 +7,14 @@ exports.runGoalShowCommand = runGoalShowCommand;
|
|
|
7
7
|
exports.runGoalEvaluateCommand = runGoalEvaluateCommand;
|
|
8
8
|
exports.runGoalNextCommand = runGoalNextCommand;
|
|
9
9
|
exports.runGoalSelectCommand = runGoalSelectCommand;
|
|
10
|
+
exports.runGoalActivateCommand = runGoalActivateCommand;
|
|
10
11
|
exports.runGoalCurrentCommand = runGoalCurrentCommand;
|
|
11
12
|
exports.runGoalClearCommand = runGoalClearCommand;
|
|
12
13
|
exports.runGoalClaimCommand = runGoalClaimCommand;
|
|
13
14
|
exports.runGoalPauseCommand = runGoalPauseCommand;
|
|
14
15
|
exports.runGoalResumeCommand = runGoalResumeCommand;
|
|
15
16
|
exports.runGoalDoneCommand = runGoalDoneCommand;
|
|
17
|
+
exports.runGoalArchiveCommand = runGoalArchiveCommand;
|
|
16
18
|
const fs_1 = __importDefault(require("fs"));
|
|
17
19
|
const path_1 = __importDefault(require("path"));
|
|
18
20
|
const config_1 = require("../core/config");
|
|
@@ -35,11 +37,13 @@ const GOAL_STATE_BY_ACTION = {
|
|
|
35
37
|
pause: "paused",
|
|
36
38
|
resume: "active",
|
|
37
39
|
done: "achieved",
|
|
40
|
+
archive: "archived",
|
|
38
41
|
};
|
|
39
42
|
const STATUS_BY_ACTION = {
|
|
40
43
|
pause: "blocked",
|
|
41
44
|
resume: "progress",
|
|
42
45
|
done: "done",
|
|
46
|
+
archive: "archived",
|
|
43
47
|
};
|
|
44
48
|
function normalizeWorkspace(value) {
|
|
45
49
|
if (!value) {
|
|
@@ -95,6 +99,22 @@ function writeSelectedGoalState(root, node, now) {
|
|
|
95
99
|
};
|
|
96
100
|
(0, atomic_1.atomicWriteFile)(selectedGoalPath(root), `${JSON.stringify(state, null, 2)}\n`);
|
|
97
101
|
}
|
|
102
|
+
function readNodeFrontmatter(root, node) {
|
|
103
|
+
const filePath = path_1.default.resolve(root, node.path);
|
|
104
|
+
const parsed = (0, frontmatter_1.parseFrontmatter)(fs_1.default.readFileSync(filePath, "utf8"), filePath);
|
|
105
|
+
return {
|
|
106
|
+
filePath,
|
|
107
|
+
frontmatter: { ...parsed.frontmatter },
|
|
108
|
+
body: parsed.body,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
function writeNodeFrontmatterFile(filePath, frontmatter, body, now) {
|
|
112
|
+
frontmatter.updated = (0, date_1.formatDate)(now);
|
|
113
|
+
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
114
|
+
const frontmatterBlock = ["---", ...lines, "---"].join("\n");
|
|
115
|
+
const content = body.length > 0 ? `${frontmatterBlock}\n${body}` : frontmatterBlock;
|
|
116
|
+
(0, atomic_1.atomicWriteFile)(filePath, content);
|
|
117
|
+
}
|
|
98
118
|
function removeSelectedGoalState(root) {
|
|
99
119
|
const filePath = selectedGoalPath(root);
|
|
100
120
|
if (!fs_1.default.existsSync(filePath)) {
|
|
@@ -125,6 +145,14 @@ function activeGoalCandidates(index, wsHint) {
|
|
|
125
145
|
.filter((node) => node.status === "progress" && node.attributes.goal_state === "active")
|
|
126
146
|
.sort((a, b) => a.qid.localeCompare(b.qid));
|
|
127
147
|
}
|
|
148
|
+
function activeGoalConflicts(index, target) {
|
|
149
|
+
return activeGoalCandidates(index, target.ws).filter((node) => node.qid !== target.qid);
|
|
150
|
+
}
|
|
151
|
+
function isArchivedGoal(node) {
|
|
152
|
+
return Boolean(node &&
|
|
153
|
+
node.type === "goal" &&
|
|
154
|
+
(node.status === "archived" || node.attributes.goal_state === "archived"));
|
|
155
|
+
}
|
|
128
156
|
function resolveGoalSelection(root, index, idOrQid, wsHint) {
|
|
129
157
|
const warnings = [];
|
|
130
158
|
if (idOrQid) {
|
|
@@ -137,19 +165,24 @@ function resolveGoalSelection(root, index, idOrQid, wsHint) {
|
|
|
137
165
|
const selected = readSelectedGoalState(root, warnings);
|
|
138
166
|
if (selected) {
|
|
139
167
|
const node = index.nodes[selected.qid];
|
|
140
|
-
if (node && node.type === "goal" && !node.source?.imported) {
|
|
168
|
+
if (node && node.type === "goal" && !node.source?.imported && !isArchivedGoal(node)) {
|
|
141
169
|
return { node, source: "selected", warnings };
|
|
142
170
|
}
|
|
143
|
-
|
|
171
|
+
if (isArchivedGoal(node)) {
|
|
172
|
+
warnings.push(`selected goal ${selected.qid} is archived; run \`mdkg goal activate <goal-id>\` or \`mdkg goal clear\``);
|
|
173
|
+
}
|
|
174
|
+
else {
|
|
175
|
+
warnings.push(`selected goal ${selected.qid} is not available; run \`mdkg goal select <goal-id>\``);
|
|
176
|
+
}
|
|
144
177
|
}
|
|
145
178
|
const active = activeGoalCandidates(index, wsHint);
|
|
146
179
|
if (active.length === 1) {
|
|
147
180
|
return { node: active[0], source: "unique_active", warnings };
|
|
148
181
|
}
|
|
149
182
|
if (active.length > 1) {
|
|
150
|
-
throw new errors_1.UsageError(`multiple active goals found: ${active.map((node) => node.qid).join(", ")}; run \`mdkg goal
|
|
183
|
+
throw new errors_1.UsageError(`multiple active goals found: ${active.map((node) => node.qid).join(", ")}; run \`mdkg goal activate <goal-id>\``);
|
|
151
184
|
}
|
|
152
|
-
throw new errors_1.NotFoundError("no selected goal or unique active goal found; run `mdkg goal
|
|
185
|
+
throw new errors_1.NotFoundError("no selected goal or unique active goal found; run `mdkg goal activate <goal-id>`");
|
|
153
186
|
}
|
|
154
187
|
function loadGoal(root, idOrQid, wsHint) {
|
|
155
188
|
const config = (0, config_1.loadConfig)(root);
|
|
@@ -201,11 +234,7 @@ function goalReceipt(root, loaded) {
|
|
|
201
234
|
};
|
|
202
235
|
}
|
|
203
236
|
function writeGoalFile(loaded, now) {
|
|
204
|
-
loaded.frontmatter.
|
|
205
|
-
const lines = (0, frontmatter_1.formatFrontmatter)(loaded.frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
206
|
-
const frontmatterBlock = ["---", ...lines, "---"].join("\n");
|
|
207
|
-
const content = loaded.body.length > 0 ? `${frontmatterBlock}\n${loaded.body}` : frontmatterBlock;
|
|
208
|
-
(0, atomic_1.atomicWriteFile)(loaded.filePath, content);
|
|
237
|
+
writeNodeFrontmatterFile(loaded.filePath, loaded.frontmatter, loaded.body, now);
|
|
209
238
|
}
|
|
210
239
|
function maybeReindex(root, config) {
|
|
211
240
|
if (!config.index.auto_reindex) {
|
|
@@ -215,6 +244,9 @@ function maybeReindex(root, config) {
|
|
|
215
244
|
}
|
|
216
245
|
function ensureStatusAllowed(config, status) {
|
|
217
246
|
const normalized = status.toLowerCase();
|
|
247
|
+
if (normalized === "archived") {
|
|
248
|
+
return normalized;
|
|
249
|
+
}
|
|
218
250
|
const allowed = new Set(config.work.status_enum.map((value) => value.toLowerCase()));
|
|
219
251
|
if (!allowed.has(normalized)) {
|
|
220
252
|
throw new errors_1.UsageError(`goal status ${normalized} is not allowed by work.status_enum`);
|
|
@@ -301,6 +333,24 @@ function runGoalEvaluateCommand(options) {
|
|
|
301
333
|
}
|
|
302
334
|
function runGoalNextCommand(options) {
|
|
303
335
|
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
336
|
+
if (isArchivedGoal(loaded.node)) {
|
|
337
|
+
const warnings = [...loaded.warnings, `${loaded.node.qid} is archived and has no actionable next work`];
|
|
338
|
+
if (options.json) {
|
|
339
|
+
console.log(JSON.stringify({
|
|
340
|
+
action: "selected",
|
|
341
|
+
goal: goalReceipt(options.root, loaded),
|
|
342
|
+
goal_source: loaded.resolutionSource,
|
|
343
|
+
node: null,
|
|
344
|
+
warnings,
|
|
345
|
+
}, null, 2));
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
for (const warning of warnings) {
|
|
349
|
+
console.error(`warning: ${warning}`);
|
|
350
|
+
}
|
|
351
|
+
console.error("no actionable local work found for goal");
|
|
352
|
+
return;
|
|
353
|
+
}
|
|
304
354
|
const statusPreference = loaded.config.work.next.status_preference.map((status) => status.toLowerCase());
|
|
305
355
|
const statusRanks = new Set(statusPreference);
|
|
306
356
|
const warnings = [...loaded.warnings];
|
|
@@ -362,6 +412,9 @@ function runGoalSelectCommand(options) {
|
|
|
362
412
|
const config = (0, config_1.loadConfig)(options.root);
|
|
363
413
|
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => {
|
|
364
414
|
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
415
|
+
if (isArchivedGoal(loaded.node)) {
|
|
416
|
+
throw new errors_1.UsageError(`cannot select archived goal ${loaded.node.qid}`);
|
|
417
|
+
}
|
|
365
418
|
const now = options.now ?? new Date();
|
|
366
419
|
writeSelectedGoalState(options.root, loaded.node, now);
|
|
367
420
|
const receipt = {
|
|
@@ -379,6 +432,74 @@ function runGoalSelectCommand(options) {
|
|
|
379
432
|
console.log(`selected goal: ${loaded.node.qid}`);
|
|
380
433
|
});
|
|
381
434
|
}
|
|
435
|
+
function runGoalActivateCommand(options) {
|
|
436
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
437
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => {
|
|
438
|
+
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
439
|
+
const currentState = String(loaded.frontmatter.goal_state ?? "");
|
|
440
|
+
const currentStatus = String(loaded.frontmatter.status ?? "");
|
|
441
|
+
if (loaded.node.status === "done" || currentStatus === "done" || currentState === "achieved") {
|
|
442
|
+
throw new errors_1.UsageError(`cannot activate achieved goal ${loaded.node.qid}`);
|
|
443
|
+
}
|
|
444
|
+
if (loaded.node.status === "archived" || currentStatus === "archived" || currentState === "archived") {
|
|
445
|
+
throw new errors_1.UsageError(`cannot activate archived goal ${loaded.node.qid}`);
|
|
446
|
+
}
|
|
447
|
+
const now = options.now ?? new Date();
|
|
448
|
+
const pausedGoals = [];
|
|
449
|
+
const conflicts = activeGoalConflicts(loaded.index, loaded.node);
|
|
450
|
+
for (const conflict of conflicts) {
|
|
451
|
+
const conflictFile = readNodeFrontmatter(options.root, conflict);
|
|
452
|
+
conflictFile.frontmatter.goal_state = "paused";
|
|
453
|
+
conflictFile.frontmatter.status = ensureStatusAllowed(config, "blocked");
|
|
454
|
+
writeNodeFrontmatterFile(conflictFile.filePath, conflictFile.frontmatter, conflictFile.body, now);
|
|
455
|
+
pausedGoals.push({
|
|
456
|
+
workspace: conflict.ws,
|
|
457
|
+
id: conflict.id,
|
|
458
|
+
qid: conflict.qid,
|
|
459
|
+
path: conflict.path,
|
|
460
|
+
previous_status: conflict.status ?? "",
|
|
461
|
+
previous_goal_state: String(conflict.attributes.goal_state ?? ""),
|
|
462
|
+
status: "blocked",
|
|
463
|
+
goal_state: "paused",
|
|
464
|
+
});
|
|
465
|
+
}
|
|
466
|
+
loaded.frontmatter.goal_state = "active";
|
|
467
|
+
loaded.frontmatter.status = ensureStatusAllowed(config, "progress");
|
|
468
|
+
writeGoalFile(loaded, now);
|
|
469
|
+
writeSelectedGoalState(options.root, loaded.node, now);
|
|
470
|
+
maybeReindex(options.root, loaded.config);
|
|
471
|
+
(0, event_support_1.appendAutomaticEvent)({
|
|
472
|
+
root: options.root,
|
|
473
|
+
ws: loaded.node.ws,
|
|
474
|
+
kind: "GOAL_ACTIVATE",
|
|
475
|
+
status: "ok",
|
|
476
|
+
refs: [loaded.node.id, ...conflicts.map((node) => node.id)],
|
|
477
|
+
notes: `goal activate via mdkg goal activate`,
|
|
478
|
+
now,
|
|
479
|
+
});
|
|
480
|
+
const receipt = {
|
|
481
|
+
action: "activated",
|
|
482
|
+
goal: goalReceipt(options.root, loaded),
|
|
483
|
+
activated_goal: goalReceipt(options.root, loaded),
|
|
484
|
+
paused_goals: pausedGoals,
|
|
485
|
+
selection: {
|
|
486
|
+
path: SELECTED_GOAL_STATE_PATH,
|
|
487
|
+
selected_at: now.toISOString(),
|
|
488
|
+
},
|
|
489
|
+
warnings: conflicts.length > 0
|
|
490
|
+
? [`paused ${conflicts.length} competing active goal(s) in workspace ${loaded.node.ws}`]
|
|
491
|
+
: [],
|
|
492
|
+
};
|
|
493
|
+
if (options.json) {
|
|
494
|
+
console.log(JSON.stringify(receipt, null, 2));
|
|
495
|
+
return;
|
|
496
|
+
}
|
|
497
|
+
for (const warning of receipt.warnings) {
|
|
498
|
+
console.error(`warning: ${warning}`);
|
|
499
|
+
}
|
|
500
|
+
console.log(`goal activate: ${loaded.node.qid}`);
|
|
501
|
+
});
|
|
502
|
+
}
|
|
382
503
|
function runGoalCurrentCommand(options) {
|
|
383
504
|
const config = (0, config_1.loadConfig)(options.root);
|
|
384
505
|
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
@@ -389,12 +510,17 @@ function runGoalCurrentCommand(options) {
|
|
|
389
510
|
const selected = readSelectedGoalState(options.root, warnings);
|
|
390
511
|
if (selected) {
|
|
391
512
|
const selectedNode = index.nodes[selected.qid];
|
|
392
|
-
if (selectedNode && selectedNode.type === "goal" && !selectedNode.source?.imported) {
|
|
513
|
+
if (selectedNode && selectedNode.type === "goal" && !selectedNode.source?.imported && !isArchivedGoal(selectedNode)) {
|
|
393
514
|
node = selectedNode;
|
|
394
515
|
source = "selected";
|
|
395
516
|
}
|
|
396
517
|
else {
|
|
397
|
-
|
|
518
|
+
if (isArchivedGoal(selectedNode)) {
|
|
519
|
+
warnings.push(`selected goal ${selected.qid} is archived; run \`mdkg goal activate <goal-id>\` or \`mdkg goal clear\``);
|
|
520
|
+
}
|
|
521
|
+
else {
|
|
522
|
+
warnings.push(`selected goal ${selected.qid} is not available; run \`mdkg goal select <goal-id>\``);
|
|
523
|
+
}
|
|
398
524
|
}
|
|
399
525
|
}
|
|
400
526
|
if (!node) {
|
|
@@ -405,7 +531,7 @@ function runGoalCurrentCommand(options) {
|
|
|
405
531
|
}
|
|
406
532
|
else if (active.length > 1) {
|
|
407
533
|
source = "ambiguous";
|
|
408
|
-
warnings.push(`multiple active goals found: ${active.map((goal) => goal.qid).join(", ")}
|
|
534
|
+
warnings.push(`multiple active goals found: ${active.map((goal) => goal.qid).join(", ")}; run \`mdkg goal activate <goal-id>\``);
|
|
409
535
|
}
|
|
410
536
|
}
|
|
411
537
|
const receipt = {
|
|
@@ -464,6 +590,9 @@ function runGoalClaimCommand(options) {
|
|
|
464
590
|
const config = (0, config_1.loadConfig)(options.root);
|
|
465
591
|
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => {
|
|
466
592
|
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
593
|
+
if (isArchivedGoal(loaded.node)) {
|
|
594
|
+
throw new errors_1.UsageError(`cannot claim work for archived goal ${loaded.node.qid}`);
|
|
595
|
+
}
|
|
467
596
|
const resolved = (0, qid_1.resolveQid)(loaded.index, options.workId, loaded.node.ws);
|
|
468
597
|
if (resolved.status !== "ok") {
|
|
469
598
|
throw new errors_1.NotFoundError((0, qid_1.formatResolveError)("work", options.workId, resolved, loaded.node.ws));
|
|
@@ -510,6 +639,9 @@ function runGoalClaimCommand(options) {
|
|
|
510
639
|
}
|
|
511
640
|
function runGoalStateMutationLocked(action, options) {
|
|
512
641
|
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
642
|
+
if (action !== "archive" && isArchivedGoal(loaded.node)) {
|
|
643
|
+
throw new errors_1.UsageError(`cannot ${action} archived goal ${loaded.node.qid}`);
|
|
644
|
+
}
|
|
513
645
|
const now = options.now ?? new Date();
|
|
514
646
|
loaded.frontmatter.goal_state = GOAL_STATE_BY_ACTION[action];
|
|
515
647
|
loaded.frontmatter.status = ensureStatusAllowed(loaded.config, STATUS_BY_ACTION[action]);
|
|
@@ -546,3 +678,7 @@ function runGoalDoneCommand(options) {
|
|
|
546
678
|
const config = (0, config_1.loadConfig)(options.root);
|
|
547
679
|
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runGoalStateMutationLocked("done", options));
|
|
548
680
|
}
|
|
681
|
+
function runGoalArchiveCommand(options) {
|
|
682
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
683
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runGoalStateMutationLocked("archive", options));
|
|
684
|
+
}
|
package/dist/commands/new.js
CHANGED
|
@@ -228,11 +228,12 @@ function runNewCommandLocked(options) {
|
|
|
228
228
|
let status;
|
|
229
229
|
if (node_1.WORK_TYPES.has(type)) {
|
|
230
230
|
const allowed = new Set(config.work.status_enum.map((value) => value.toLowerCase()));
|
|
231
|
+
const allowedForType = type === "goal" ? new Set([...allowed, "archived"]) : allowed;
|
|
231
232
|
status = statusInput ?? (type === "goal" && allowed.has("progress")
|
|
232
233
|
? "progress"
|
|
233
234
|
: config.work.status_enum[0]?.toLowerCase());
|
|
234
|
-
if (!status || !
|
|
235
|
-
throw new errors_1.UsageError(`--status must be one of ${Array.from(
|
|
235
|
+
if (!status || !allowedForType.has(status)) {
|
|
236
|
+
throw new errors_1.UsageError(`--status must be one of ${Array.from(allowedForType).join(", ")}`);
|
|
236
237
|
}
|
|
237
238
|
}
|
|
238
239
|
else if (type === "dec") {
|
|
@@ -346,7 +347,7 @@ function runNewCommandLocked(options) {
|
|
|
346
347
|
skills: skills.length > 0 ? skills : undefined,
|
|
347
348
|
cases: cases.length > 0 ? cases : undefined,
|
|
348
349
|
supersedes: options.supersedes ? options.supersedes.toLowerCase() : undefined,
|
|
349
|
-
goal_state: type === "goal" ? (status === "done" ? "achieved" : status === "blocked" ? "blocked" : "active") : undefined,
|
|
350
|
+
goal_state: type === "goal" ? (status === "done" ? "achieved" : status === "blocked" ? "blocked" : status === "archived" ? "archived" : "active") : undefined,
|
|
350
351
|
goal_condition: type === "goal" ? title : undefined,
|
|
351
352
|
max_iterations: type === "goal" ? 25 : undefined,
|
|
352
353
|
blocked_after_attempts: type === "goal" ? 3 : undefined,
|
package/dist/graph/node.js
CHANGED
|
@@ -30,7 +30,7 @@ exports.ALLOWED_TYPES = new Set([
|
|
|
30
30
|
...agent_file_types_1.AGENT_FILE_TYPES,
|
|
31
31
|
]);
|
|
32
32
|
const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
|
|
33
|
-
const GOAL_STATE = new Set(["active", "paused", "achieved", "blocked", "budget_limited"]);
|
|
33
|
+
const GOAL_STATE = new Set(["active", "paused", "achieved", "blocked", "budget_limited", "archived"]);
|
|
34
34
|
const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
35
35
|
const GOAL_ATTRIBUTE_KEYS = [
|
|
36
36
|
"goal_state",
|
|
@@ -280,13 +280,14 @@ function parseNode(content, filePath, options) {
|
|
|
280
280
|
const statusValue = optionalString(frontmatter, "status", filePath);
|
|
281
281
|
let status = undefined;
|
|
282
282
|
const workStatus = new Set(options.workStatusEnum.map((value) => value.toLowerCase()));
|
|
283
|
+
const allowedWorkStatus = type === "goal" ? new Set([...workStatus, "archived"]) : workStatus;
|
|
283
284
|
if (exports.WORK_TYPES.has(type)) {
|
|
284
285
|
if (!statusValue) {
|
|
285
286
|
throw formatError(filePath, "status is required for work items");
|
|
286
287
|
}
|
|
287
288
|
const normalized = requireLowercase(statusValue, "status", filePath);
|
|
288
|
-
if (!
|
|
289
|
-
throw formatError(filePath, `status must be one of ${Array.from(
|
|
289
|
+
if (!allowedWorkStatus.has(normalized)) {
|
|
290
|
+
throw formatError(filePath, `status must be one of ${Array.from(allowedWorkStatus).join(", ")}`);
|
|
290
291
|
}
|
|
291
292
|
status = normalized;
|
|
292
293
|
}
|
|
@@ -485,6 +485,26 @@ function validateGoalRefs(index, allowMissing, errors) {
|
|
|
485
485
|
}
|
|
486
486
|
}
|
|
487
487
|
}
|
|
488
|
+
function validateSingleActiveRootGoals(index, errors) {
|
|
489
|
+
const activeByWorkspace = {};
|
|
490
|
+
for (const [qid, node] of Object.entries(index.nodes)) {
|
|
491
|
+
if (node.type !== "goal" || node.source?.imported) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
if (node.status === "progress" && node.attributes.goal_state === "active") {
|
|
495
|
+
if (!activeByWorkspace[node.ws]) {
|
|
496
|
+
activeByWorkspace[node.ws] = [];
|
|
497
|
+
}
|
|
498
|
+
activeByWorkspace[node.ws].push(qid);
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
for (const [workspace, qids] of Object.entries(activeByWorkspace)) {
|
|
502
|
+
if (qids.length <= 1) {
|
|
503
|
+
continue;
|
|
504
|
+
}
|
|
505
|
+
pushError(errors, `${workspace}: multiple active root goals found: ${qids.sort().join(", ")}; run mdkg goal activate <goal-id> to select exactly one`);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
488
508
|
function detectPrevNextCycles(index, errors) {
|
|
489
509
|
const nodes = index.nodes;
|
|
490
510
|
const seen = new Set();
|
|
@@ -533,6 +553,7 @@ function collectGraphErrors(index, options = {}) {
|
|
|
533
553
|
validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors);
|
|
534
554
|
validateArchiveUriRefs(index, allowMissing, errors);
|
|
535
555
|
validateGoalRefs(index, allowMissing, errors);
|
|
556
|
+
validateSingleActiveRootGoals(index, errors);
|
|
536
557
|
detectPrevNextCycles(index, errors);
|
|
537
558
|
return errors;
|
|
538
559
|
}
|
package/dist/init/AGENT_START.md
CHANGED
|
@@ -24,7 +24,7 @@ Agent operating prompt:
|
|
|
24
24
|
- Use `mdkg show <id>` for direct inspection and `mdkg show <id> --meta` for card-only inspection.
|
|
25
25
|
- Use `mdkg search "..."` and `mdkg next` to discover current work.
|
|
26
26
|
- Use `mdkg new goal "..."` for long-running recursive objectives that need an explicit end condition, active node, required skills, required checks, and completion evidence.
|
|
27
|
-
- Use `mdkg goal
|
|
27
|
+
- Use `mdkg goal activate <goal-id>` to make one local root goal active, then `mdkg goal next` to surface one scoped feature, task, bug, test, or spike at a time; normal `mdkg next` remains for non-goal concrete work.
|
|
28
28
|
- Use `mdkg goal claim [goal-id] <work-id>` only after accepting the surfaced work item; `mdkg goal next` is read-only.
|
|
29
29
|
- Treat goal `required_checks` as report-only guidance from mdkg. Run commands yourself, then record evidence in the goal or active work item.
|
|
30
30
|
- Record skill improvement candidates during normal goal execution; edit `SKILL.md` only when the active node is explicit skill-maintenance work.
|
|
@@ -78,7 +78,7 @@ If the active task is known:
|
|
|
78
78
|
- `mdkg validate`
|
|
79
79
|
|
|
80
80
|
If an active goal is known:
|
|
81
|
-
- `mdkg goal
|
|
81
|
+
- `mdkg goal activate <goal-id>`
|
|
82
82
|
- `mdkg goal current`
|
|
83
83
|
- `mdkg goal next`
|
|
84
84
|
- `mdkg goal claim <work-id>`
|
|
@@ -27,14 +27,20 @@ Primary commands:
|
|
|
27
27
|
- `mdkg task`
|
|
28
28
|
- `mdkg validate`
|
|
29
29
|
- `mdkg status [--json]`
|
|
30
|
-
- `mdkg fix plan [--family index|refs|ids|all] [--target <id-or-qid>] [--json]`
|
|
30
|
+
- `mdkg fix plan [--family index|refs|ids|all] [--target <id-or-qid>] [--base-ref <ref>] [--json]`
|
|
31
|
+
- `mdkg fix apply [--family ids] [--target <id-or-qid>] [--base-ref <ref>] [--json]`
|
|
32
|
+
- `mdkg fix ids [--target <id-or-qid>] [--base-ref <ref>] [--apply] [--json]`
|
|
31
33
|
|
|
32
34
|
Operator health:
|
|
33
35
|
- `mdkg status [--json]` is a read-only summary for scripts and agents
|
|
34
36
|
- reports mdkg version/config, git state, graph/index freshness, selected-goal state, project DB verification summary, and generated cache status
|
|
35
37
|
- 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
|
|
37
|
-
-
|
|
38
|
+
- `mdkg fix plan ...` is dry-run repair planning only; it writes nothing
|
|
39
|
+
- duplicate-ID graph repairs can be applied with `mdkg fix apply --family ids` or `mdkg fix ids --apply`
|
|
40
|
+
- use `--base-ref main` when mainline IDs should win branch-merge repair
|
|
41
|
+
- unresolved Git add/add conflict stages are split by keeping stage 2 at the conflicted path and writing stage 3 to a new canonical ID/path
|
|
42
|
+
- graph-reference and index/cache findings remain review-only guidance
|
|
43
|
+
- `fix plan --json` returns a receipt-shaped plan with selected families, risk counts, paths, reason codes, and per-change `apply_supported` metadata
|
|
38
44
|
|
|
39
45
|
Index backend:
|
|
40
46
|
- fresh mdkg workspaces default to `index.backend: sqlite`
|
|
@@ -242,14 +248,16 @@ Work semantic mirrors:
|
|
|
242
248
|
Goal nodes:
|
|
243
249
|
- `mdkg goal show <goal-id-or-qid> [--json]`
|
|
244
250
|
- `mdkg goal select <goal-id-or-qid> [--json]`
|
|
251
|
+
- `mdkg goal activate <goal-id-or-qid> [--json]`
|
|
245
252
|
- `mdkg goal current [--json]`
|
|
246
253
|
- `mdkg goal clear [--json]`
|
|
247
254
|
- `mdkg goal next [goal-id-or-qid] [--json]`
|
|
248
255
|
- `mdkg goal claim [goal-id-or-qid] <work-id-or-qid> [--json]`
|
|
249
256
|
- `mdkg goal evaluate <goal-id-or-qid> [--json]`
|
|
250
|
-
- `mdkg goal pause|resume|done <goal-id-or-qid> [--json]`
|
|
257
|
+
- `mdkg goal pause|resume|done|archive <goal-id-or-qid> [--json]`
|
|
251
258
|
- `mdkg goal show <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
252
259
|
- `mdkg goal select <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
260
|
+
- `mdkg goal activate <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
253
261
|
- `mdkg goal current [--ws <alias>] [--json]`
|
|
254
262
|
- `mdkg goal next [goal-id-or-qid] [--ws <alias>] [--json]`
|
|
255
263
|
- `mdkg goal claim <work-id-or-qid> [--ws <alias>] [--json]`
|
|
@@ -258,7 +266,10 @@ Goal nodes:
|
|
|
258
266
|
- `mdkg goal pause <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
259
267
|
- `mdkg goal resume <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
260
268
|
- `mdkg goal done <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
269
|
+
- `mdkg goal archive <goal-id-or-qid> [--ws <alias>] [--json]`
|
|
261
270
|
- goals orchestrate recursive progress through explicit `scope_refs`; tasks, bugs, tests, spikes, and features remain concrete executable units
|
|
271
|
+
- `goal activate` makes one local root goal active, pauses competing local active goals in the same workspace, and writes selected-goal state
|
|
272
|
+
- `goal archive` marks a superseded historical goal archived so it remains readable but not actionable
|
|
262
273
|
- `goal next` is read-only; use `goal claim` to set `active_node`
|
|
263
274
|
- `mdkg goal evaluate` is report-only and never runs commands from `required_checks`
|
|
264
275
|
- 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
|
@@ -35,7 +35,7 @@ mdkg fix plan --json
|
|
|
35
35
|
mdkg validate
|
|
36
36
|
```
|
|
37
37
|
|
|
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
|
|
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 activate/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
39
|
|
|
40
40
|
Use `mdkg status --json` for a read-only operator summary of Git, graph,
|
|
41
41
|
selected-goal, project DB, and generated-cache health before mutating work. Use
|
|
@@ -45,8 +45,13 @@ fresh init, run `mdkg index` first so strict doctor can load generated caches.
|
|
|
45
45
|
|
|
46
46
|
Use `mdkg fix plan --json` for dry-run repair guidance. It reports generated
|
|
47
47
|
index/cache repair hints, missing graph references, and duplicate local ids as
|
|
48
|
-
receipt-shaped planned changes with risk levels and
|
|
49
|
-
`
|
|
48
|
+
receipt-shaped planned changes with risk levels and per-change
|
|
49
|
+
`apply_supported` metadata. Duplicate-ID graph repairs can be applied with
|
|
50
|
+
`mdkg fix apply --family ids --json` or `mdkg fix ids --apply --json`; use
|
|
51
|
+
`--base-ref main` when mainline IDs should win. Index/cache and graph-reference
|
|
52
|
+
findings remain review-only. For unresolved Git add/add conflicts, `fix ids`
|
|
53
|
+
keeps stage 2 at the conflicted path, rewrites stage 3 to the next unused
|
|
54
|
+
canonical ID/path, and records a receipt.
|
|
50
55
|
|
|
51
56
|
Use research spikes for investigation and planning work that should produce a
|
|
52
57
|
reviewable recommendation before implementation:
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"schema_version": 1,
|
|
3
3
|
"tool": "mdkg",
|
|
4
|
-
"mdkg_version": "0.3.
|
|
4
|
+
"mdkg_version": "0.3.4",
|
|
5
5
|
"files": [
|
|
6
6
|
{
|
|
7
7
|
"path": ".mdkg/config.json",
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
{
|
|
62
62
|
"path": ".mdkg/README.md",
|
|
63
63
|
"category": "mdkg_doc",
|
|
64
|
-
"sha256": "
|
|
64
|
+
"sha256": "5962993e8dffd53ccbceaaedd0b8f2a868421a3a1cc16d7002b661b39b7ebf79"
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
"path": ".mdkg/skills/build-pack-and-execute-task/SKILL.md",
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
{
|
|
72
72
|
"path": ".mdkg/skills/pursue-mdkg-goal/SKILL.md",
|
|
73
73
|
"category": "default_skill",
|
|
74
|
-
"sha256": "
|
|
74
|
+
"sha256": "2bc888092d95b362e402494af4dbe6909204bd5578613d6159753a84efaae16d"
|
|
75
75
|
},
|
|
76
76
|
{
|
|
77
77
|
"path": ".mdkg/skills/select-work-and-ground-context/SKILL.md",
|
|
@@ -246,7 +246,7 @@
|
|
|
246
246
|
{
|
|
247
247
|
"path": "AGENT_START.md",
|
|
248
248
|
"category": "startup_doc",
|
|
249
|
-
"sha256": "
|
|
249
|
+
"sha256": "46e734b6eae92eb957c3e29fb4d206e4be3fd6d32dd00e888e01a0dc75979428"
|
|
250
250
|
},
|
|
251
251
|
{
|
|
252
252
|
"path": "AGENTS.md",
|
|
@@ -261,7 +261,7 @@
|
|
|
261
261
|
{
|
|
262
262
|
"path": "CLI_COMMAND_MATRIX.md",
|
|
263
263
|
"category": "startup_doc",
|
|
264
|
-
"sha256": "
|
|
264
|
+
"sha256": "7322e6a2c47261f87dacd15ba0d93eba0d1e32eec0900a1e9f447ad13aaae860"
|
|
265
265
|
},
|
|
266
266
|
{
|
|
267
267
|
"path": "llms.txt",
|
|
@@ -27,7 +27,8 @@ Move one durable mdkg goal forward without losing scope, evidence, or user inten
|
|
|
27
27
|
## Steps
|
|
28
28
|
|
|
29
29
|
1. Resolve the goal:
|
|
30
|
-
- If a goal id is provided, run `mdkg goal
|
|
30
|
+
- If a goal id is provided and you are beginning durable work on it, run `mdkg goal activate <goal-id>`.
|
|
31
|
+
- Use `mdkg goal select <goal-id>` only when you need a local pointer without changing lifecycle state.
|
|
31
32
|
- Otherwise run `mdkg goal current`.
|
|
32
33
|
- If the result is missing or ambiguous, ask the user to select a goal.
|
|
33
34
|
2. Inspect the goal with `mdkg goal show <goal-id>` and build context with `mdkg pack <goal-id>`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mdkg",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.4",
|
|
4
4
|
"description": "Markdown Knowledge Graph",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -28,8 +28,10 @@
|
|
|
28
28
|
"smoke:operator-health": "npm run build && node scripts/smoke-operator-health.js",
|
|
29
29
|
"smoke:fix-plan": "npm run build && node scripts/smoke-fix-plan.js",
|
|
30
30
|
"smoke:branch-conflicts": "npm run build && node scripts/smoke-branch-conflicts.js",
|
|
31
|
+
"smoke:id-repair": "npm run build && node scripts/smoke-id-repair.js",
|
|
31
32
|
"smoke:command-docs": "npm run build && node scripts/smoke-command-docs.js",
|
|
32
33
|
"smoke:spike": "npm run build && node scripts/smoke-spike.js",
|
|
34
|
+
"smoke:goal-lifecycle": "npm run build && node scripts/smoke-goal-lifecycle.js",
|
|
33
35
|
"smoke:bundle": "npm run build && node scripts/smoke-bundle.js",
|
|
34
36
|
"smoke:bundle-import": "npm run smoke:subgraph",
|
|
35
37
|
"smoke:visibility": "npm run build && node scripts/smoke-visibility.js",
|
|
@@ -40,7 +42,7 @@
|
|
|
40
42
|
"cli:check": "npm run build && node scripts/cli_help_snapshot.js --check",
|
|
41
43
|
"cli:contract": "npm run build && node scripts/generate-command-contract.js --check",
|
|
42
44
|
"prepack": "npm run build && node scripts/assert-publish-ready.js",
|
|
43
|
-
"prepublishOnly": "npm run test && npm run cli:check && npm run cli:contract && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:db && npm run smoke:db-queue && npm run smoke:db-queue-cli && npm run smoke:db-events && npm run smoke:db-materializer && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:work-invocation && npm run smoke:cli-ux-polish && npm run smoke:operator-health && npm run smoke:fix-plan && npm run smoke:branch-conflicts && npm run smoke:command-docs && npm run smoke:spike && npm run smoke:bundle && npm run smoke:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && npm run smoke:goal && node scripts/assert-publish-ready.js",
|
|
45
|
+
"prepublishOnly": "npm run test && npm run cli:check && npm run cli:contract && node dist/cli.js validate && npm run smoke:consumer && npm run smoke:matrix && npm run smoke:upgrade && npm run smoke:init && npm run smoke:capabilities && npm run smoke:db && npm run smoke:db-queue && npm run smoke:db-queue-cli && npm run smoke:db-events && npm run smoke:db-materializer && npm run smoke:db-snapshot && npm run smoke:archive-work && npm run smoke:work-invocation && npm run smoke:cli-ux-polish && npm run smoke:operator-health && npm run smoke:fix-plan && npm run smoke:branch-conflicts && npm run smoke:id-repair && npm run smoke:command-docs && npm run smoke:spike && npm run smoke:goal-lifecycle && npm run smoke:bundle && npm run smoke:subgraph && npm run smoke:visibility && npm run smoke:sqlite && npm run smoke:parallel && npm run smoke:goal && node scripts/assert-publish-ready.js",
|
|
44
46
|
"postinstall": "node scripts/postinstall.js",
|
|
45
47
|
"smoke:subgraph": "npm run build && node scripts/smoke-subgraph.js"
|
|
46
48
|
},
|