mdkg 0.3.7 → 0.3.9
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 +50 -0
- package/CLI_COMMAND_MATRIX.md +85 -24
- package/README.md +51 -26
- package/dist/cli.js +72 -35
- package/dist/command-contract.json +354 -9
- package/dist/commands/capability.js +1 -0
- package/dist/commands/doctor.js +7 -7
- package/dist/commands/format.js +40 -1
- package/dist/commands/handoff.js +6 -0
- package/dist/commands/init.js +84 -3
- package/dist/commands/new.js +12 -3
- package/dist/commands/skill.js +1 -1
- package/dist/commands/skill_mirror.js +22 -17
- package/dist/commands/skill_support.js +1 -1
- package/dist/commands/spec.js +76 -17
- package/dist/commands/subgraph.js +7 -4
- package/dist/commands/upgrade.js +137 -30
- package/dist/commands/validate.js +106 -3
- package/dist/commands/work.js +12 -5
- package/dist/core/config.js +132 -0
- package/dist/graph/agent_file_types.js +59 -20
- package/dist/graph/capabilities_indexer.js +45 -3
- package/dist/graph/indexer.js +5 -0
- package/dist/graph/template_schema.js +37 -17
- package/dist/graph/validate_graph.js +11 -5
- package/dist/init/AGENT_START.md +10 -9
- package/dist/init/CLI_COMMAND_MATRIX.md +30 -17
- package/dist/init/README.md +11 -9
- package/dist/init/config.json +15 -0
- package/dist/init/core/COLLABORATION.md +45 -0
- package/dist/init/core/HUMAN.md +7 -1
- package/dist/init/core/core.md +1 -0
- package/dist/init/core/rule-1-mdkg-conventions.md +3 -0
- package/dist/init/core/rule-3-cli-contract.md +5 -5
- package/dist/init/init-manifest.json +78 -13
- package/dist/init/llms.txt +2 -1
- package/dist/init/skills/default/author-mdkg-skill/SKILL.md +211 -0
- package/dist/init/skills/default/pursue-mdkg-goal/SKILL.md +10 -0
- package/dist/init/skills/default/select-work-and-ground-context/SKILL.md +8 -0
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +73 -9
- package/dist/init/templates/default/manifest.md +45 -0
- package/dist/init/templates/specs/agent.MANIFEST.md +80 -0
- package/dist/init/templates/specs/api.MANIFEST.md +33 -0
- package/dist/init/templates/specs/base.MANIFEST.md +120 -0
- package/dist/init/templates/specs/capability.MANIFEST.md +45 -0
- package/dist/init/templates/specs/integration.MANIFEST.md +25 -0
- package/dist/init/templates/specs/model.MANIFEST.md +21 -0
- package/dist/init/templates/specs/project.MANIFEST.md +39 -0
- package/dist/init/templates/specs/runtime-agent.MANIFEST.md +49 -0
- package/dist/init/templates/specs/runtime-image.MANIFEST.md +21 -0
- package/dist/init/templates/specs/tool.MANIFEST.md +25 -0
- package/dist/util/argparse.js +3 -0
- package/package.json +19 -3
package/dist/commands/upgrade.js
CHANGED
|
@@ -15,11 +15,13 @@ const project_db_1 = require("../core/project_db");
|
|
|
15
15
|
const errors_1 = require("../util/errors");
|
|
16
16
|
const frontmatter_1 = require("../graph/frontmatter");
|
|
17
17
|
const workspace_files_1 = require("../graph/workspace_files");
|
|
18
|
+
const agent_file_types_1 = require("../graph/agent_file_types");
|
|
19
|
+
const atomic_1 = require("../util/atomic");
|
|
18
20
|
const init_manifest_1 = require("./init_manifest");
|
|
19
21
|
const skill_support_1 = require("./skill_support");
|
|
20
22
|
const skill_mirror_1 = require("./skill_mirror");
|
|
21
23
|
const DEFAULT_SEED_SUBDIR = path_1.default.resolve(__dirname, "..", "init");
|
|
22
|
-
const PROTECTED_CORE_DOCS = new Set([".mdkg/core/SOUL.md", ".mdkg/core/HUMAN.md"]);
|
|
24
|
+
const PROTECTED_CORE_DOCS = new Set([".mdkg/core/SOUL.md", ".mdkg/core/COLLABORATION.md", ".mdkg/core/HUMAN.md"]);
|
|
23
25
|
const CREATE_ONLY_PRESERVED = new Set([".mdkg/core/core.md"]);
|
|
24
26
|
const LOCAL_STATE_IGNORE_ENTRIES = [
|
|
25
27
|
".mdkg/state/",
|
|
@@ -49,12 +51,11 @@ function requireSeedAssets(seedRoot) {
|
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
}
|
|
52
|
-
function isAgentWorkspace(root) {
|
|
54
|
+
function isAgentWorkspace(root, config) {
|
|
53
55
|
return [
|
|
54
56
|
path_1.default.join(root, ".mdkg", "skills"),
|
|
55
|
-
path_1.default.join(root, ".agents", "skills"),
|
|
56
|
-
path_1.default.join(root, ".claude", "skills"),
|
|
57
57
|
path_1.default.join(root, ".mdkg", "work", "events", "events.jsonl"),
|
|
58
|
+
...(0, skill_mirror_1.configuredSkillMirrorTargets)(config).map((target) => path_1.default.join(root, target)),
|
|
58
59
|
].some((candidate) => fs_1.default.existsSync(candidate));
|
|
59
60
|
}
|
|
60
61
|
function copyFile(src, dest) {
|
|
@@ -65,6 +66,13 @@ function writeFile(filePath, content) {
|
|
|
65
66
|
fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
|
|
66
67
|
fs_1.default.writeFileSync(filePath, content, "utf8");
|
|
67
68
|
}
|
|
69
|
+
function repoRelativePath(root, filePath) {
|
|
70
|
+
return path_1.default.relative(root, filePath).split(path_1.default.sep).join("/");
|
|
71
|
+
}
|
|
72
|
+
function renderFrontmatterDocument(frontmatter, body) {
|
|
73
|
+
const frontmatterBlock = ["---", ...(0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER), "---"].join("\n");
|
|
74
|
+
return body.length > 0 ? `${frontmatterBlock}\n${body}` : frontmatterBlock;
|
|
75
|
+
}
|
|
68
76
|
function createSummary() {
|
|
69
77
|
return {
|
|
70
78
|
created: 0,
|
|
@@ -148,15 +156,6 @@ function planSeedFile(options) {
|
|
|
148
156
|
options.summary.unchanged += 1;
|
|
149
157
|
return false;
|
|
150
158
|
}
|
|
151
|
-
if (PROTECTED_CORE_DOCS.has(options.file.path)) {
|
|
152
|
-
record(options.summary, options.changes, {
|
|
153
|
-
path: options.file.path,
|
|
154
|
-
category: options.file.category,
|
|
155
|
-
action: "conflict",
|
|
156
|
-
reason: "protected core document exists; local content preserved",
|
|
157
|
-
});
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
159
|
const known = options.knownHashes.get(options.file.path);
|
|
161
160
|
if (known?.has(currentHash)) {
|
|
162
161
|
record(options.summary, options.changes, {
|
|
@@ -171,6 +170,15 @@ function planSeedFile(options) {
|
|
|
171
170
|
options.managedCurrentFiles.push(options.file);
|
|
172
171
|
return true;
|
|
173
172
|
}
|
|
173
|
+
if (PROTECTED_CORE_DOCS.has(options.file.path)) {
|
|
174
|
+
record(options.summary, options.changes, {
|
|
175
|
+
path: options.file.path,
|
|
176
|
+
category: options.file.category,
|
|
177
|
+
action: "conflict",
|
|
178
|
+
reason: "protected core document exists; local content preserved",
|
|
179
|
+
});
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
174
182
|
record(options.summary, options.changes, {
|
|
175
183
|
path: options.file.path,
|
|
176
184
|
category: options.file.category,
|
|
@@ -244,14 +252,26 @@ function migrateProjectDbConfig(input) {
|
|
|
244
252
|
};
|
|
245
253
|
return { config: raw, changed: true };
|
|
246
254
|
}
|
|
255
|
+
function migrateCustomizationConfig(input) {
|
|
256
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
257
|
+
return { config: input, changed: false };
|
|
258
|
+
}
|
|
259
|
+
const raw = { ...input };
|
|
260
|
+
if (raw.customization !== undefined) {
|
|
261
|
+
return { config: raw, changed: false };
|
|
262
|
+
}
|
|
263
|
+
raw.customization = (0, config_1.defaultCustomizationConfig)();
|
|
264
|
+
return { config: raw, changed: true };
|
|
265
|
+
}
|
|
247
266
|
function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
248
267
|
const cfgPath = (0, paths_1.configPath)(root);
|
|
249
268
|
const raw = JSON.parse(fs_1.default.readFileSync(cfgPath, "utf8"));
|
|
250
269
|
const migrated = (0, migrate_1.migrateConfig)(raw);
|
|
251
270
|
const bundleConfig = migrateLegacyBundleImportsConfig(migrated.config);
|
|
252
271
|
const nextConfig = migrateProjectDbConfig(bundleConfig.config);
|
|
253
|
-
|
|
254
|
-
|
|
272
|
+
const customizationConfig = migrateCustomizationConfig(nextConfig.config);
|
|
273
|
+
(0, config_1.validateConfigSchema)(customizationConfig.config);
|
|
274
|
+
if (migrated.from === migrated.to && !bundleConfig.changed && !nextConfig.changed && !customizationConfig.changed) {
|
|
255
275
|
summary.unchanged += 1;
|
|
256
276
|
return;
|
|
257
277
|
}
|
|
@@ -262,6 +282,9 @@ function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
|
262
282
|
if (nextConfig.changed) {
|
|
263
283
|
reasons.push("project db config defaults");
|
|
264
284
|
}
|
|
285
|
+
if (customizationConfig.changed) {
|
|
286
|
+
reasons.push("customization overlay defaults");
|
|
287
|
+
}
|
|
265
288
|
if (migrated.from !== migrated.to) {
|
|
266
289
|
reasons.push(`schema_version ${migrated.from} -> ${migrated.to}`);
|
|
267
290
|
}
|
|
@@ -272,9 +295,32 @@ function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
|
272
295
|
reason: reasons.join(" and "),
|
|
273
296
|
});
|
|
274
297
|
if (!dryRun) {
|
|
275
|
-
writeFile(cfgPath, `${JSON.stringify(
|
|
298
|
+
writeFile(cfgPath, `${JSON.stringify(customizationConfig.config, null, 2)}\n`);
|
|
276
299
|
}
|
|
277
300
|
}
|
|
301
|
+
function sameStringArray(left, right) {
|
|
302
|
+
return left.length === right.length && left.every((value, index) => value === right[index]);
|
|
303
|
+
}
|
|
304
|
+
function hasOperatorCustomization(customization) {
|
|
305
|
+
const defaults = (0, config_1.defaultCustomizationConfig)();
|
|
306
|
+
return (customization.standards.profile !== defaults.standards.profile ||
|
|
307
|
+
customization.standards.refs.length > 0 ||
|
|
308
|
+
customization.core_docs.custom_paths.length > 0 ||
|
|
309
|
+
!sameStringArray(customization.skill_mirrors.targets, defaults.skill_mirrors.targets));
|
|
310
|
+
}
|
|
311
|
+
function reportPreservedCustomizationOverlay(root, summary, changes) {
|
|
312
|
+
const config = (0, config_1.validateConfigSchema)((0, migrate_1.migrateConfig)(JSON.parse(fs_1.default.readFileSync((0, paths_1.configPath)(root), "utf8"))).config);
|
|
313
|
+
if (!hasOperatorCustomization(config.customization)) {
|
|
314
|
+
summary.unchanged += 1;
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
record(summary, changes, {
|
|
318
|
+
path: ".mdkg/config.json",
|
|
319
|
+
category: "customization_overlay",
|
|
320
|
+
action: "skip",
|
|
321
|
+
reason: "operator customization overlay is preserved; upgrade does not replace organization standards, custom core docs, or configured skill mirror targets",
|
|
322
|
+
});
|
|
323
|
+
}
|
|
278
324
|
function migrateClosedGoalActiveNodes(root, dryRun, summary, changes) {
|
|
279
325
|
const config = (0, config_1.validateConfigSchema)((0, migrate_1.migrateConfig)(JSON.parse(fs_1.default.readFileSync((0, paths_1.configPath)(root), "utf8"))).config);
|
|
280
326
|
const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(root, config);
|
|
@@ -323,9 +369,59 @@ function migrateClosedGoalActiveNodes(root, dryRun, summary, changes) {
|
|
|
323
369
|
frontmatter.last_active_node = activeNode;
|
|
324
370
|
}
|
|
325
371
|
delete frontmatter.active_node;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
372
|
+
writeFile(filePath, renderFrontmatterDocument(frontmatter, parsed.body.length > 0 ? parsed.body : ""));
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
}
|
|
376
|
+
if (planned === 0) {
|
|
377
|
+
summary.unchanged += 1;
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
function migrateLegacySpecManifests(root, dryRun, summary, changes) {
|
|
381
|
+
const config = (0, config_1.validateConfigSchema)((0, migrate_1.migrateConfig)(JSON.parse(fs_1.default.readFileSync((0, paths_1.configPath)(root), "utf8"))).config);
|
|
382
|
+
const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(root, config);
|
|
383
|
+
let planned = 0;
|
|
384
|
+
for (const files of Object.values(filesByAlias)) {
|
|
385
|
+
for (const filePath of files) {
|
|
386
|
+
if (path_1.default.basename(filePath) !== agent_file_types_1.LEGACY_SPEC_BASENAME) {
|
|
387
|
+
continue;
|
|
388
|
+
}
|
|
389
|
+
const content = fs_1.default.readFileSync(filePath, "utf8");
|
|
390
|
+
let parsed;
|
|
391
|
+
try {
|
|
392
|
+
parsed = (0, frontmatter_1.parseFrontmatter)(content, filePath);
|
|
393
|
+
}
|
|
394
|
+
catch {
|
|
395
|
+
continue;
|
|
396
|
+
}
|
|
397
|
+
if (parsed.frontmatter.type !== "spec") {
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
planned += 1;
|
|
401
|
+
const targetPath = path_1.default.join(path_1.default.dirname(filePath), agent_file_types_1.CANONICAL_MANIFEST_BASENAME);
|
|
402
|
+
const relativePath = repoRelativePath(root, filePath);
|
|
403
|
+
const relativeTargetPath = repoRelativePath(root, targetPath);
|
|
404
|
+
if (fs_1.default.existsSync(targetPath)) {
|
|
405
|
+
record(summary, changes, {
|
|
406
|
+
path: relativePath,
|
|
407
|
+
target_path: relativeTargetPath,
|
|
408
|
+
category: "manifest_migration",
|
|
409
|
+
action: "conflict",
|
|
410
|
+
reason: "sibling MANIFEST.md already exists; legacy SPEC.md content preserved",
|
|
411
|
+
});
|
|
412
|
+
continue;
|
|
413
|
+
}
|
|
414
|
+
record(summary, changes, {
|
|
415
|
+
path: relativePath,
|
|
416
|
+
target_path: relativeTargetPath,
|
|
417
|
+
category: "manifest_migration",
|
|
418
|
+
action: "migrate",
|
|
419
|
+
reason: "rename legacy SPEC.md to MANIFEST.md and normalize type: spec to type: manifest",
|
|
420
|
+
});
|
|
421
|
+
if (!dryRun) {
|
|
422
|
+
const frontmatter = { ...parsed.frontmatter, type: "manifest" };
|
|
423
|
+
(0, atomic_1.atomicWriteFile)(filePath, renderFrontmatterDocument(frontmatter, parsed.body));
|
|
424
|
+
fs_1.default.renameSync(filePath, targetPath);
|
|
329
425
|
}
|
|
330
426
|
}
|
|
331
427
|
}
|
|
@@ -422,6 +518,9 @@ function ensureArchiveIgnorePolicy(root, dryRun, summary, changes) {
|
|
|
422
518
|
function isWritableChange(change) {
|
|
423
519
|
return change.action === "create" || change.action === "update" || change.action === "migrate" || change.action === "sync";
|
|
424
520
|
}
|
|
521
|
+
function writablePathForChange(change) {
|
|
522
|
+
return change.target_path ?? change.path;
|
|
523
|
+
}
|
|
425
524
|
function buildApplySideEffects(options) {
|
|
426
525
|
const hasDirectWrites = options.changes.some(isWritableChange);
|
|
427
526
|
if (!hasDirectWrites && options.existingManifest) {
|
|
@@ -441,12 +540,15 @@ function buildApplySideEffects(options) {
|
|
|
441
540
|
category: "skill_registry",
|
|
442
541
|
action: "update",
|
|
443
542
|
reason: "refreshes canonical skill registry after apply",
|
|
444
|
-
}, {
|
|
445
|
-
path: ".agents/skills,.claude/skills",
|
|
446
|
-
category: "skill_mirror",
|
|
447
|
-
action: "sync",
|
|
448
|
-
reason: "syncs managed skill mirrors after apply",
|
|
449
543
|
});
|
|
544
|
+
if (options.mirrorTargets.length > 0) {
|
|
545
|
+
effects.push({
|
|
546
|
+
path: options.mirrorTargets.join(","),
|
|
547
|
+
category: "skill_mirror",
|
|
548
|
+
action: "sync",
|
|
549
|
+
reason: "syncs configured managed skill mirrors after apply",
|
|
550
|
+
});
|
|
551
|
+
}
|
|
450
552
|
}
|
|
451
553
|
return effects;
|
|
452
554
|
}
|
|
@@ -468,7 +570,8 @@ function emitHumanReceipt(receipt) {
|
|
|
468
570
|
}
|
|
469
571
|
else {
|
|
470
572
|
for (const change of receipt.changes) {
|
|
471
|
-
|
|
573
|
+
const target = change.target_path ? ` -> ${change.target_path}` : "";
|
|
574
|
+
console.log(` ${change.action}: ${change.path}${target} (${change.reason})`);
|
|
472
575
|
}
|
|
473
576
|
}
|
|
474
577
|
if (receipt.apply_side_effects.length > 0) {
|
|
@@ -497,10 +600,13 @@ function runUpgradeCommand(options) {
|
|
|
497
600
|
const knownHashes = buildKnownHashes([existingManifest, ...legacyManifests]);
|
|
498
601
|
const summary = createSummary();
|
|
499
602
|
const changes = [];
|
|
500
|
-
const
|
|
603
|
+
const initialConfig = (0, config_1.validateConfigSchema)((0, migrate_1.migrateConfig)(JSON.parse(fs_1.default.readFileSync((0, paths_1.configPath)(root), "utf8"))).config);
|
|
604
|
+
const agentWorkspace = isAgentWorkspace(root, initialConfig);
|
|
501
605
|
const managedCurrentFiles = [];
|
|
502
606
|
migrateConfigIfNeeded(root, dryRun, summary, changes);
|
|
607
|
+
reportPreservedCustomizationOverlay(root, summary, changes);
|
|
503
608
|
migrateClosedGoalActiveNodes(root, dryRun, summary, changes);
|
|
609
|
+
migrateLegacySpecManifests(root, dryRun, summary, changes);
|
|
504
610
|
for (const file of currentManifest.files) {
|
|
505
611
|
if (!shouldIncludeFile(file, agentWorkspace)) {
|
|
506
612
|
continue;
|
|
@@ -523,6 +629,7 @@ function runUpgradeCommand(options) {
|
|
|
523
629
|
const applySideEffects = buildApplySideEffects({
|
|
524
630
|
existingManifest,
|
|
525
631
|
agentWorkspace,
|
|
632
|
+
mirrorTargets: (0, skill_mirror_1.configuredSkillMirrorTargets)(initialConfig),
|
|
526
633
|
changes,
|
|
527
634
|
});
|
|
528
635
|
for (const effect of applySideEffects) {
|
|
@@ -539,13 +646,13 @@ function runUpgradeCommand(options) {
|
|
|
539
646
|
if (agentWorkspace && applySideEffects.length > 0) {
|
|
540
647
|
const config = (0, config_1.validateConfigSchema)((0, migrate_1.migrateConfig)(JSON.parse(fs_1.default.readFileSync((0, paths_1.configPath)(root), "utf8"))).config);
|
|
541
648
|
(0, skill_support_1.refreshSkillsRegistry)(root, config);
|
|
542
|
-
(0, skill_mirror_1.scaffoldMirrorRoots)(root);
|
|
649
|
+
(0, skill_mirror_1.scaffoldMirrorRoots)(root, config);
|
|
543
650
|
(0, skill_mirror_1.syncSkillMirrors)({ root, config, createRoots: true });
|
|
544
651
|
}
|
|
545
652
|
}
|
|
546
|
-
const preservedCustomizations = changes.filter((change) => change.action === "conflict");
|
|
547
|
-
const blockingConflicts =
|
|
548
|
-
const willWritePaths = changes.filter(isWritableChange).map(
|
|
653
|
+
const preservedCustomizations = changes.filter((change) => change.action === "conflict" || change.category === "customization_overlay");
|
|
654
|
+
const blockingConflicts = changes.filter((change) => change.action === "conflict" && change.category === "manifest_migration");
|
|
655
|
+
const willWritePaths = changes.filter(isWritableChange).map(writablePathForChange);
|
|
549
656
|
const receipt = {
|
|
550
657
|
action: "upgrade",
|
|
551
658
|
dry_run: dryRun,
|
|
@@ -129,6 +129,20 @@ function collectRawContentWarnings(qid, node) {
|
|
|
129
129
|
}
|
|
130
130
|
return warnings;
|
|
131
131
|
}
|
|
132
|
+
function collectManifestCompatibilityWarnings(qid, filePath, node) {
|
|
133
|
+
const basename = path_1.default.basename(filePath);
|
|
134
|
+
if (basename === agent_file_types_1.LEGACY_SPEC_BASENAME && node.type === "spec") {
|
|
135
|
+
return [
|
|
136
|
+
`${qid}: manifest.compat.spec_legacy warning: SPEC.md is legacy; MANIFEST.md is the canonical manifest filename. Rename this file before the compatibility release closes.`,
|
|
137
|
+
];
|
|
138
|
+
}
|
|
139
|
+
if (basename === agent_file_types_1.CANONICAL_MANIFEST_BASENAME && node.type === "spec") {
|
|
140
|
+
return [
|
|
141
|
+
`${qid}: manifest.compat.type_spec warning: MANIFEST.md uses legacy type: spec; use type: manifest before the compatibility release closes.`,
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
return [];
|
|
145
|
+
}
|
|
132
146
|
function collectChangedPaths(root) {
|
|
133
147
|
const result = (0, child_process_1.spawnSync)("git", ["-C", root, "status", "--porcelain", "--", ".mdkg"], {
|
|
134
148
|
encoding: "utf8",
|
|
@@ -166,6 +180,7 @@ function warningPath(message, nodes) {
|
|
|
166
180
|
}
|
|
167
181
|
function warningDiagnostic(message, nodes) {
|
|
168
182
|
const qid = qidFromWarning(message);
|
|
183
|
+
const nodeType = qid && nodes[qid] ? nodes[qid].type : undefined;
|
|
169
184
|
const pathValue = warningPath(message, nodes);
|
|
170
185
|
const rawMatch = /raw-content\.([a-z_]+)/.exec(message);
|
|
171
186
|
if (rawMatch) {
|
|
@@ -175,6 +190,7 @@ function warningDiagnostic(message, nodes) {
|
|
|
175
190
|
severity: "warning",
|
|
176
191
|
message,
|
|
177
192
|
qid,
|
|
193
|
+
node_type: nodeType,
|
|
178
194
|
path: pathValue,
|
|
179
195
|
ref: qid,
|
|
180
196
|
remediation: "Replace raw secrets, prompts, tokens, or payloads with refs, hashes, redacted summaries, or archive/artifact links.",
|
|
@@ -187,6 +203,7 @@ function warningDiagnostic(message, nodes) {
|
|
|
187
203
|
severity: "warning",
|
|
188
204
|
message,
|
|
189
205
|
qid,
|
|
206
|
+
node_type: nodeType,
|
|
190
207
|
path: pathValue,
|
|
191
208
|
ref: qid,
|
|
192
209
|
remediation: "Run mdkg format --headings --dry-run to review missing heading additions, then --apply if acceptable.",
|
|
@@ -198,16 +215,32 @@ function warningDiagnostic(message, nodes) {
|
|
|
198
215
|
category: "templates",
|
|
199
216
|
severity: "warning",
|
|
200
217
|
message,
|
|
218
|
+
node_type: nodeType,
|
|
201
219
|
path: pathValue,
|
|
202
220
|
remediation: "Run mdkg upgrade --apply to vendor missing built-in template schemas when the managed asset update is safe.",
|
|
203
221
|
};
|
|
204
222
|
}
|
|
223
|
+
const manifestCompatMatch = /manifest\.compat\.([a-z_]+)/.exec(message);
|
|
224
|
+
if (manifestCompatMatch) {
|
|
225
|
+
return {
|
|
226
|
+
id: `manifest.compat.${manifestCompatMatch[1]}`,
|
|
227
|
+
category: "manifest-compatibility",
|
|
228
|
+
severity: "warning",
|
|
229
|
+
message,
|
|
230
|
+
qid,
|
|
231
|
+
node_type: nodeType,
|
|
232
|
+
path: pathValue,
|
|
233
|
+
ref: qid,
|
|
234
|
+
remediation: "Rename legacy SPEC.md files to MANIFEST.md and update transitional type: spec frontmatter to type: manifest before the compatibility release closes.",
|
|
235
|
+
};
|
|
236
|
+
}
|
|
205
237
|
if (message.includes("sqlite") || message.includes("index")) {
|
|
206
238
|
return {
|
|
207
239
|
id: "cache.index",
|
|
208
240
|
category: "cache",
|
|
209
241
|
severity: "warning",
|
|
210
242
|
message,
|
|
243
|
+
node_type: nodeType,
|
|
211
244
|
path: pathValue,
|
|
212
245
|
remediation: "Run mdkg index or mdkg db index rebuild when generated cache state should be refreshed.",
|
|
213
246
|
};
|
|
@@ -218,6 +251,7 @@ function warningDiagnostic(message, nodes) {
|
|
|
218
251
|
category: "skills",
|
|
219
252
|
severity: "warning",
|
|
220
253
|
message,
|
|
254
|
+
node_type: nodeType,
|
|
221
255
|
path: pathValue,
|
|
222
256
|
remediation: "Run mdkg skill sync after reviewing managed skill mirror drift.",
|
|
223
257
|
};
|
|
@@ -228,6 +262,7 @@ function warningDiagnostic(message, nodes) {
|
|
|
228
262
|
category: "subgraph",
|
|
229
263
|
severity: "warning",
|
|
230
264
|
message,
|
|
265
|
+
node_type: nodeType,
|
|
231
266
|
path: pathValue,
|
|
232
267
|
remediation: "Run mdkg subgraph verify or refresh the source bundle after reviewing child graph freshness.",
|
|
233
268
|
};
|
|
@@ -238,11 +273,67 @@ function warningDiagnostic(message, nodes) {
|
|
|
238
273
|
severity: "warning",
|
|
239
274
|
message,
|
|
240
275
|
qid,
|
|
276
|
+
node_type: nodeType,
|
|
241
277
|
path: pathValue,
|
|
242
278
|
ref: qid,
|
|
243
279
|
remediation: "Review the warning and apply the focused mdkg command suggested by the message when appropriate.",
|
|
244
280
|
};
|
|
245
281
|
}
|
|
282
|
+
function countBuckets(values) {
|
|
283
|
+
const counts = new Map();
|
|
284
|
+
for (const value of values) {
|
|
285
|
+
if (!value) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
counts.set(value, (counts.get(value) ?? 0) + 1);
|
|
289
|
+
}
|
|
290
|
+
return Array.from(counts.entries())
|
|
291
|
+
.map(([key, count]) => ({ key, count }))
|
|
292
|
+
.sort((a, b) => b.count - a.count || a.key.localeCompare(b.key));
|
|
293
|
+
}
|
|
294
|
+
function normalizeLimit(limit) {
|
|
295
|
+
if (limit === undefined) {
|
|
296
|
+
return 50;
|
|
297
|
+
}
|
|
298
|
+
if (!Number.isInteger(limit) || limit < 0) {
|
|
299
|
+
throw new errors_1.ValidationError("--limit must be a non-negative integer");
|
|
300
|
+
}
|
|
301
|
+
return limit;
|
|
302
|
+
}
|
|
303
|
+
function buildWarningSummary(diagnostics, limit) {
|
|
304
|
+
const effectiveLimit = limit === null || limit === undefined ? diagnostics.length : limit;
|
|
305
|
+
const emitted = Math.min(diagnostics.length, effectiveLimit);
|
|
306
|
+
const truncated = emitted < diagnostics.length;
|
|
307
|
+
return {
|
|
308
|
+
total: diagnostics.length,
|
|
309
|
+
emitted,
|
|
310
|
+
truncated,
|
|
311
|
+
omitted_count: diagnostics.length - emitted,
|
|
312
|
+
limit: limit === undefined ? null : limit,
|
|
313
|
+
affected_file_count: new Set(diagnostics.map((diagnostic) => diagnostic.path).filter(Boolean)).size,
|
|
314
|
+
by_id: countBuckets(diagnostics.map((diagnostic) => diagnostic.id)),
|
|
315
|
+
by_category: countBuckets(diagnostics.map((diagnostic) => diagnostic.category)),
|
|
316
|
+
by_node_type: countBuckets(diagnostics.map((diagnostic) => diagnostic.node_type)),
|
|
317
|
+
top_qids: countBuckets(diagnostics.map((diagnostic) => diagnostic.qid)).slice(0, 25),
|
|
318
|
+
top_paths: countBuckets(diagnostics.map((diagnostic) => diagnostic.path)).slice(0, 25),
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
function shapeValidateReceiptForSummary(receipt, limit) {
|
|
322
|
+
const effectiveLimit = normalizeLimit(limit);
|
|
323
|
+
const warningDiagnostics = receipt.warning_diagnostics.slice(0, effectiveLimit);
|
|
324
|
+
return {
|
|
325
|
+
...receipt,
|
|
326
|
+
warnings: warningDiagnostics.map((warning) => warning.message),
|
|
327
|
+
warning_diagnostics: warningDiagnostics,
|
|
328
|
+
warning_summary: buildWarningSummary(receipt.warning_diagnostics, effectiveLimit),
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
function writeJsonReceipt(root, jsonOut, receipt) {
|
|
332
|
+
const outPath = path_1.default.resolve(root, jsonOut);
|
|
333
|
+
fs_1.default.mkdirSync(path_1.default.dirname(outPath), { recursive: true });
|
|
334
|
+
fs_1.default.writeFileSync(outPath, `${JSON.stringify(receipt, null, 2)}\n`, "utf8");
|
|
335
|
+
return outPath;
|
|
336
|
+
}
|
|
246
337
|
function isCoreListFile(filePath) {
|
|
247
338
|
return path_1.default.basename(filePath) === "core.md" && path_1.default.basename(path_1.default.dirname(filePath)) === "core";
|
|
248
339
|
}
|
|
@@ -412,6 +503,7 @@ function collectValidateReceipt(options) {
|
|
|
412
503
|
const idsByWorkspace = {};
|
|
413
504
|
for (const [alias, files] of Object.entries(filesByAlias)) {
|
|
414
505
|
idsByWorkspace[alias] = new Map();
|
|
506
|
+
errors.push(...(0, agent_file_types_1.collectManifestSiblingConflicts)(files, (dirPath) => path_1.default.relative(options.root, dirPath).split(path_1.default.sep).join("/") || "."));
|
|
415
507
|
for (const filePath of files) {
|
|
416
508
|
if (isCoreListFile(filePath)) {
|
|
417
509
|
continue;
|
|
@@ -450,6 +542,7 @@ function collectValidateReceipt(options) {
|
|
|
450
542
|
}
|
|
451
543
|
}
|
|
452
544
|
warnings.push(...collectRawContentWarnings(qid, node));
|
|
545
|
+
warnings.push(...collectManifestCompatibilityWarnings(qid, filePath, node));
|
|
453
546
|
}
|
|
454
547
|
catch (err) {
|
|
455
548
|
const message = err instanceof Error ? err.message : "unknown error";
|
|
@@ -546,6 +639,7 @@ function collectValidateReceipt(options) {
|
|
|
546
639
|
error_count: uniqueErrors.length,
|
|
547
640
|
warnings: uniqueWarnings,
|
|
548
641
|
warning_diagnostics: filteredWarningDiagnostics,
|
|
642
|
+
warning_summary: buildWarningSummary(filteredWarningDiagnostics),
|
|
549
643
|
errors: uniqueErrors,
|
|
550
644
|
...(outPath ? { report_path: outPath } : {}),
|
|
551
645
|
...(options.changedOnly
|
|
@@ -555,18 +649,27 @@ function collectValidateReceipt(options) {
|
|
|
555
649
|
return receipt;
|
|
556
650
|
}
|
|
557
651
|
function runValidateCommand(options) {
|
|
558
|
-
|
|
652
|
+
let receipt = collectValidateReceipt(options);
|
|
653
|
+
if (options.jsonOut) {
|
|
654
|
+
const jsonOutPath = path_1.default.resolve(options.root, options.jsonOut);
|
|
655
|
+
receipt = { ...receipt, json_receipt_path: jsonOutPath };
|
|
656
|
+
writeJsonReceipt(options.root, options.jsonOut, receipt);
|
|
657
|
+
}
|
|
658
|
+
const displayReceipt = options.summary ? shapeValidateReceiptForSummary(receipt, options.limit) : receipt;
|
|
559
659
|
if (options.json) {
|
|
560
|
-
console.log(JSON.stringify(
|
|
660
|
+
console.log(JSON.stringify(displayReceipt, null, 2));
|
|
561
661
|
if (receipt.error_count > 0) {
|
|
562
662
|
throw new errors_1.ValidationError(`validation failed with ${receipt.error_count} error(s)`);
|
|
563
663
|
}
|
|
564
664
|
return;
|
|
565
665
|
}
|
|
566
666
|
if (!options.quiet) {
|
|
567
|
-
for (const warning of
|
|
667
|
+
for (const warning of displayReceipt.warnings) {
|
|
568
668
|
console.error(`warning: ${warning}`);
|
|
569
669
|
}
|
|
670
|
+
if (displayReceipt.warning_summary.truncated) {
|
|
671
|
+
console.error(`warning summary: omitted ${displayReceipt.warning_summary.omitted_count} warning(s); rerun without --summary or use --json-out <path> for full diagnostics`);
|
|
672
|
+
}
|
|
570
673
|
}
|
|
571
674
|
if (receipt.error_count > 0) {
|
|
572
675
|
if (receipt.report_path) {
|
package/dist/commands/work.js
CHANGED
|
@@ -247,6 +247,10 @@ function workflowDiagnosticCode(message) {
|
|
|
247
247
|
if (message.includes("missing recommended heading")) {
|
|
248
248
|
return "heading.missing";
|
|
249
249
|
}
|
|
250
|
+
const manifestCompatMatch = /manifest\.compat\.([a-z_]+)/.exec(message);
|
|
251
|
+
if (manifestCompatMatch) {
|
|
252
|
+
return `manifest.compat.${manifestCompatMatch[1]}`;
|
|
253
|
+
}
|
|
250
254
|
if (message.includes("references missing") || message.includes("references missing node")) {
|
|
251
255
|
return "reference.missing";
|
|
252
256
|
}
|
|
@@ -384,8 +388,8 @@ function resolveTriggerWorkNode(index, ws, refRaw) {
|
|
|
384
388
|
if (node.type === "work") {
|
|
385
389
|
return { workNode: node };
|
|
386
390
|
}
|
|
387
|
-
if (node.type
|
|
388
|
-
throw new errors_1.UsageError(`work trigger requires a WORK.md or SPEC.md ref, got ${node.type}: ${node.qid}`);
|
|
391
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
392
|
+
throw new errors_1.UsageError(`work trigger requires a WORK.md or MANIFEST.md/SPEC.md ref, got ${node.type}: ${node.qid}`);
|
|
389
393
|
}
|
|
390
394
|
const candidates = new Map();
|
|
391
395
|
const specDir = path_1.default.posix.dirname(node.path);
|
|
@@ -411,11 +415,12 @@ function resolveTriggerWorkNode(index, ws, refRaw) {
|
|
|
411
415
|
}
|
|
412
416
|
}
|
|
413
417
|
const workNodes = Array.from(candidates.values()).sort((a, b) => a.qid.localeCompare(b.qid));
|
|
418
|
+
const manifestLabel = node.type === "spec" ? "legacy SPEC.md" : "MANIFEST.md";
|
|
414
419
|
if (workNodes.length === 0) {
|
|
415
|
-
throw new errors_1.NotFoundError(
|
|
420
|
+
throw new errors_1.NotFoundError(`${manifestLabel} ${node.qid} has no resolvable WORK.md contract`);
|
|
416
421
|
}
|
|
417
422
|
if (workNodes.length > 1) {
|
|
418
|
-
throw new errors_1.UsageError(
|
|
423
|
+
throw new errors_1.UsageError(`${manifestLabel} ${node.qid} has multiple work contracts; trigger one explicitly: ${workNodes
|
|
419
424
|
.map((workNode) => workNode.qid)
|
|
420
425
|
.join(", ")}`);
|
|
421
426
|
}
|
|
@@ -757,7 +762,9 @@ function runWorkContractNewCommandLocked(options) {
|
|
|
757
762
|
const agentId = normalizePortableIdRef(options.agentId, "--agent-id");
|
|
758
763
|
const kind = options.kind.toLowerCase();
|
|
759
764
|
const resolvedAgent = (0, qid_1.resolveQid)(index, agentId, ws);
|
|
760
|
-
const relates = resolvedAgent.status === "ok" && index.nodes[resolvedAgent.qid]?.type
|
|
765
|
+
const relates = resolvedAgent.status === "ok" && (0, agent_file_types_1.isManifestSemanticType)(index.nodes[resolvedAgent.qid]?.type ?? "")
|
|
766
|
+
? [agentId]
|
|
767
|
+
: [];
|
|
761
768
|
const requiredCapabilities = parseCsvList(options.requiredCapabilities);
|
|
762
769
|
const receipt = createAgentWorkflowNode({
|
|
763
770
|
root: options.root,
|