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.
Files changed (53) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/CLI_COMMAND_MATRIX.md +85 -24
  3. package/README.md +51 -26
  4. package/dist/cli.js +72 -35
  5. package/dist/command-contract.json +354 -9
  6. package/dist/commands/capability.js +1 -0
  7. package/dist/commands/doctor.js +7 -7
  8. package/dist/commands/format.js +40 -1
  9. package/dist/commands/handoff.js +6 -0
  10. package/dist/commands/init.js +84 -3
  11. package/dist/commands/new.js +12 -3
  12. package/dist/commands/skill.js +1 -1
  13. package/dist/commands/skill_mirror.js +22 -17
  14. package/dist/commands/skill_support.js +1 -1
  15. package/dist/commands/spec.js +76 -17
  16. package/dist/commands/subgraph.js +7 -4
  17. package/dist/commands/upgrade.js +137 -30
  18. package/dist/commands/validate.js +106 -3
  19. package/dist/commands/work.js +12 -5
  20. package/dist/core/config.js +132 -0
  21. package/dist/graph/agent_file_types.js +59 -20
  22. package/dist/graph/capabilities_indexer.js +45 -3
  23. package/dist/graph/indexer.js +5 -0
  24. package/dist/graph/template_schema.js +37 -17
  25. package/dist/graph/validate_graph.js +11 -5
  26. package/dist/init/AGENT_START.md +10 -9
  27. package/dist/init/CLI_COMMAND_MATRIX.md +30 -17
  28. package/dist/init/README.md +11 -9
  29. package/dist/init/config.json +15 -0
  30. package/dist/init/core/COLLABORATION.md +45 -0
  31. package/dist/init/core/HUMAN.md +7 -1
  32. package/dist/init/core/core.md +1 -0
  33. package/dist/init/core/rule-1-mdkg-conventions.md +3 -0
  34. package/dist/init/core/rule-3-cli-contract.md +5 -5
  35. package/dist/init/init-manifest.json +78 -13
  36. package/dist/init/llms.txt +2 -1
  37. package/dist/init/skills/default/author-mdkg-skill/SKILL.md +211 -0
  38. package/dist/init/skills/default/pursue-mdkg-goal/SKILL.md +10 -0
  39. package/dist/init/skills/default/select-work-and-ground-context/SKILL.md +8 -0
  40. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +73 -9
  41. package/dist/init/templates/default/manifest.md +45 -0
  42. package/dist/init/templates/specs/agent.MANIFEST.md +80 -0
  43. package/dist/init/templates/specs/api.MANIFEST.md +33 -0
  44. package/dist/init/templates/specs/base.MANIFEST.md +120 -0
  45. package/dist/init/templates/specs/capability.MANIFEST.md +45 -0
  46. package/dist/init/templates/specs/integration.MANIFEST.md +25 -0
  47. package/dist/init/templates/specs/model.MANIFEST.md +21 -0
  48. package/dist/init/templates/specs/project.MANIFEST.md +39 -0
  49. package/dist/init/templates/specs/runtime-agent.MANIFEST.md +49 -0
  50. package/dist/init/templates/specs/runtime-image.MANIFEST.md +21 -0
  51. package/dist/init/templates/specs/tool.MANIFEST.md +25 -0
  52. package/dist/util/argparse.js +3 -0
  53. package/package.json +19 -3
@@ -17,6 +17,7 @@ const skill_support_1 = require("./skill_support");
17
17
  const skill_mirror_1 = require("./skill_mirror");
18
18
  const DEFAULT_SEED_SUBDIR = path_1.default.resolve(__dirname, "..", "init");
19
19
  const SOUL_PIN_ID = "rule-soul";
20
+ const COLLABORATION_PIN_ID = "rule-7";
20
21
  const HUMAN_PIN_ID = "rule-human";
21
22
  const DEFAULT_CORE_LIST_HEADER = [
22
23
  "# mdkg verbose core list",
@@ -175,6 +176,52 @@ function humanTemplate(created) {
175
176
  "",
176
177
  ].join("\n");
177
178
  }
179
+ function collaborationTemplate(created) {
180
+ return [
181
+ "---",
182
+ `id: ${COLLABORATION_PIN_ID}`,
183
+ "type: rule",
184
+ "title: collaboration profile and operator preferences",
185
+ "tags: [collaboration, preferences, operator]",
186
+ "owners: []",
187
+ "links: []",
188
+ "artifacts: []",
189
+ "relates: [rule-human]",
190
+ "refs: [dec-53]",
191
+ "aliases: [collaboration, operator-profile]",
192
+ `created: ${created}`,
193
+ `updated: ${created}`,
194
+ "---",
195
+ "",
196
+ "# Purpose",
197
+ "",
198
+ "Capture stable collaboration preferences, operating boundaries, and repo-specific human expectations so agents can work with less ambiguity.",
199
+ "",
200
+ "# Scope",
201
+ "",
202
+ "Applies to planning, implementation, review, release, and handoff interactions in this repository.",
203
+ "",
204
+ "# Compatibility",
205
+ "",
206
+ "`COLLABORATION.md` is canonical. `HUMAN.md` remains a one-release legacy alias for repos and agent prompts that still reference it; read `COLLABORATION.md` first and then use `HUMAN.md` only for compatibility notes not yet migrated.",
207
+ "",
208
+ "# Requirements",
209
+ "",
210
+ "- Keep top goals, boundaries, and style preferences current.",
211
+ "- Include ask-before-doing constraints for risky or high-impact actions.",
212
+ "- Record preferred environment assumptions and validation commands.",
213
+ "- Preserve local operator customizations during `mdkg upgrade --apply`.",
214
+ "",
215
+ "# Notes",
216
+ "",
217
+ "Suggested prompts:",
218
+ "- What are your top 3 goals in this repo right now?",
219
+ "- What should never happen without confirmation?",
220
+ "- What coding/review style should the agent prefer?",
221
+ "- What OS/runtime/test commands should be assumed?",
222
+ "",
223
+ ].join("\n");
224
+ }
178
225
  function seededInitEvent(nowIso) {
179
226
  const event = {
180
227
  ts: nowIso,
@@ -277,6 +324,31 @@ function ensureCorePins(coreListPath, requiredPins) {
277
324
  fs_1.default.mkdirSync(path_1.default.dirname(coreListPath), { recursive: true });
278
325
  fs_1.default.writeFileSync(coreListPath, output, "utf8");
279
326
  }
327
+ function refreshManifestHash(root, manifest, relPath) {
328
+ const entry = manifest.files.find((file) => file.path === relPath);
329
+ if (!entry) {
330
+ return;
331
+ }
332
+ const targetPath = path_1.default.join(root, relPath);
333
+ if (fs_1.default.existsSync(targetPath) && fs_1.default.statSync(targetPath).isFile()) {
334
+ entry.sha256 = (0, init_manifest_1.sha256File)(targetPath);
335
+ }
336
+ }
337
+ function ensureCreatedCoreManifestEntry(root, manifest, relPath, stats) {
338
+ if (manifest.files.some((file) => file.path === relPath) || !stats.createdPaths.includes(relPath)) {
339
+ return;
340
+ }
341
+ const targetPath = path_1.default.join(root, relPath);
342
+ if (!fs_1.default.existsSync(targetPath) || !fs_1.default.statSync(targetPath).isFile()) {
343
+ return;
344
+ }
345
+ manifest.files.push({
346
+ path: relPath,
347
+ category: "core",
348
+ sha256: (0, init_manifest_1.sha256File)(targetPath),
349
+ });
350
+ manifest.files.sort((a, b) => a.path.localeCompare(b.path));
351
+ }
280
352
  function runInitCommand(options) {
281
353
  const root = path_1.default.resolve(options.root);
282
354
  const seedRoot = options.seedRoot ? path_1.default.resolve(options.seedRoot) : DEFAULT_SEED_SUBDIR;
@@ -295,6 +367,7 @@ function runInitCommand(options) {
295
367
  const seedReadme = path_1.default.join(seedRoot, "README.md");
296
368
  const seedDefaultSkills = path_1.default.join(seedRoot, "skills", "default");
297
369
  const seedSoul = path_1.default.join(seedCore, "SOUL.md");
370
+ const seedCollaboration = path_1.default.join(seedCore, "COLLABORATION.md");
298
371
  const seedHuman = path_1.default.join(seedCore, "HUMAN.md");
299
372
  const seedManifest = (0, init_manifest_1.createInitManifest)(seedRoot, (0, version_1.readPackageVersion)(), {
300
373
  includeAgentDocs: Boolean(options.agent),
@@ -367,6 +440,7 @@ function runInitCommand(options) {
367
440
  if (options.agent) {
368
441
  const today = (0, date_1.formatDate)(new Date());
369
442
  const soulPath = path_1.default.join(mdkgDir, "core", "SOUL.md");
443
+ const collaborationPath = path_1.default.join(mdkgDir, "core", "COLLABORATION.md");
370
444
  const humanPath = path_1.default.join(mdkgDir, "core", "HUMAN.md");
371
445
  const skillsDir = path_1.default.join(mdkgDir, "skills");
372
446
  const registryPath = path_1.default.join(skillsDir, "registry.md");
@@ -378,6 +452,9 @@ function runInitCommand(options) {
378
452
  if (!fs_1.default.existsSync(seedSoul)) {
379
453
  writeFileIfMissing(root, soulPath, soulTemplate(today), force, stats);
380
454
  }
455
+ if (!fs_1.default.existsSync(seedCollaboration)) {
456
+ writeFileIfMissing(root, collaborationPath, collaborationTemplate(today), force, stats);
457
+ }
381
458
  if (!fs_1.default.existsSync(seedHuman)) {
382
459
  writeFileIfMissing(root, humanPath, humanTemplate(today), force, stats);
383
460
  }
@@ -386,9 +463,13 @@ function runInitCommand(options) {
386
463
  writeFileIfMissing(root, eventsPath, seededInitEvent(new Date().toISOString()), force, stats);
387
464
  }
388
465
  const coreListPath = path_1.default.join(mdkgDir, "core", "core.md");
389
- ensureCorePins(coreListPath, [SOUL_PIN_ID, HUMAN_PIN_ID]);
390
- (0, skill_mirror_1.scaffoldMirrorRoots)(root);
466
+ ensureCorePins(coreListPath, [SOUL_PIN_ID, COLLABORATION_PIN_ID, HUMAN_PIN_ID]);
467
+ refreshManifestHash(root, seedManifest, ".mdkg/core/core.md");
468
+ ensureCreatedCoreManifestEntry(root, seedManifest, ".mdkg/core/SOUL.md", stats);
469
+ ensureCreatedCoreManifestEntry(root, seedManifest, ".mdkg/core/COLLABORATION.md", stats);
470
+ ensureCreatedCoreManifestEntry(root, seedManifest, ".mdkg/core/HUMAN.md", stats);
391
471
  const config = (0, config_1.loadConfig)(root);
472
+ (0, skill_mirror_1.scaffoldMirrorRoots)(root, config);
392
473
  (0, skill_support_1.refreshSkillsRegistry)(root, config);
393
474
  stats.registryRefreshed = true;
394
475
  const mirrorResult = (0, skill_mirror_1.syncSkillMirrors)({ root, config, createRoots: true, force });
@@ -452,7 +533,7 @@ function runInitCommand(options) {
452
533
  }
453
534
  if (options.agent) {
454
535
  console.log("agent bootstrap: AGENT_START.md, AGENTS.md, CLAUDE.md, llms.txt, CLI_COMMAND_MATRIX.md");
455
- console.log("agent core pins: rule-soul, rule-human");
536
+ console.log("agent core pins: rule-soul, rule-7, rule-human");
456
537
  console.log("agent event log: .mdkg/work/events/events.jsonl");
457
538
  console.log(`skill mirrors: ${stats.mirroredSkills} sync operation(s) across ${stats.mirrorTargets} target(s)`);
458
539
  if (stats.registryRefreshed) {
@@ -27,6 +27,7 @@ const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
27
27
  const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
28
28
  const CORE_TYPES = new Set(["rule"]);
29
29
  const DESIGN_TYPES = new Set(["prd", "edd", "dec", "prop"]);
30
+ const LEGACY_NEW_SPEC_WARNING = "warning: mdkg new spec is legacy; use mdkg new manifest. This alias creates MANIFEST.md with type: manifest during the compatibility release.";
30
31
  function parseCsvList(raw) {
31
32
  if (!raw) {
32
33
  return [];
@@ -162,6 +163,9 @@ function fileNameForType(type, id, slug) {
162
163
  }
163
164
  return `${id}-${slug}.md`;
164
165
  }
166
+ function canonicalCreationType(requestedType) {
167
+ return requestedType === "spec" ? "manifest" : requestedType;
168
+ }
165
169
  function ensureExists(index, value, ws, label) {
166
170
  const resolved = (0, qid_1.resolveQid)(index, value, ws);
167
171
  if (resolved.status !== "ok") {
@@ -177,13 +181,15 @@ function runNewCommandLocked(options) {
177
181
  if (!title) {
178
182
  throw new errors_1.UsageError("title cannot be empty");
179
183
  }
180
- const type = options.type.toLowerCase();
181
- if (type === "archive") {
184
+ const requestedType = options.type.toLowerCase();
185
+ if (requestedType === "archive") {
182
186
  throw new errors_1.UsageError("use `mdkg archive add <file>` to create archive sidecars");
183
187
  }
184
- if (!node_1.ALLOWED_TYPES.has(type)) {
188
+ if (!node_1.ALLOWED_TYPES.has(requestedType)) {
185
189
  throw new errors_1.UsageError(`type must be one of ${Array.from(node_1.ALLOWED_TYPES).join(", ")}`);
186
190
  }
191
+ const type = canonicalCreationType(requestedType);
192
+ const legacySpecAlias = requestedType === "spec";
187
193
  const config = (0, config_1.loadConfig)(options.root);
188
194
  const ws = normalizeWorkspace(options.ws);
189
195
  if (!config.workspaces[ws]) {
@@ -389,6 +395,9 @@ function runNewCommandLocked(options) {
389
395
  ...(status ? { status } : {}),
390
396
  ...(priority !== undefined ? { priority } : {}),
391
397
  };
398
+ if (legacySpecAlias) {
399
+ console.error(LEGACY_NEW_SPEC_WARNING);
400
+ }
392
401
  if (options.json) {
393
402
  console.log(JSON.stringify({
394
403
  action: "created",
@@ -186,7 +186,7 @@ function runSkillNewCommandLocked(options) {
186
186
  (0, atomic_1.atomicWriteFile)(canonicalPath, content);
187
187
  (0, skill_support_1.ensureSkillsRegistry)(root, config);
188
188
  (0, skill_support_1.refreshSkillsRegistry)(root, config);
189
- if ((0, skill_mirror_1.shouldMaintainSkillMirrors)(root)) {
189
+ if ((0, skill_mirror_1.shouldMaintainSkillMirrors)(root, config)) {
190
190
  (0, skill_mirror_1.syncSkillMirrors)({ root, config, createRoots: true, force });
191
191
  }
192
192
  if (config.index.auto_reindex) {
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
3
3
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.configuredSkillMirrorTargets = configuredSkillMirrorTargets;
6
7
  exports.syncSkillMirrors = syncSkillMirrors;
7
8
  exports.preflightSkillMirrorTargets = preflightSkillMirrorTargets;
8
9
  exports.shouldMaintainSkillMirrors = shouldMaintainSkillMirrors;
@@ -10,22 +11,26 @@ exports.auditSkillMirrors = auditSkillMirrors;
10
11
  exports.scaffoldMirrorRoots = scaffoldMirrorRoots;
11
12
  const fs_1 = __importDefault(require("fs"));
12
13
  const path_1 = __importDefault(require("path"));
14
+ const config_1 = require("../core/config");
13
15
  const skills_indexer_1 = require("../graph/skills_indexer");
14
16
  const errors_1 = require("../util/errors");
15
- const MIRROR_PRODUCTS = ["agents", "claude"];
16
17
  const MANIFEST_FILE = ".mdkg-managed.json";
17
18
  const MANAGED_ROOT_MARKERS = [
18
19
  path_1.default.join(".mdkg", "core", "SOUL.md"),
20
+ path_1.default.join(".mdkg", "core", "COLLABORATION.md"),
19
21
  path_1.default.join(".mdkg", "core", "HUMAN.md"),
20
22
  ];
21
23
  const ALLOWED_ROOT_ENTRIES = ["SKILL.md", "references", "assets", "scripts"];
22
- function resolveMirrorTargets(root) {
23
- return MIRROR_PRODUCTS.map((product) => {
24
- const rootDir = path_1.default.join(root, `.${product}`);
25
- const skillsRoot = path_1.default.join(rootDir, "skills");
24
+ function configuredSkillMirrorTargets(config) {
25
+ const configured = config?.customization.skill_mirrors.targets ?? (0, config_1.defaultCustomizationConfig)().skill_mirrors.targets;
26
+ return Array.from(new Set(configured.map((value) => value.trim()).filter(Boolean)));
27
+ }
28
+ function resolveMirrorTargets(root, config) {
29
+ return configuredSkillMirrorTargets(config).map((configuredPath) => {
30
+ const skillsRoot = path_1.default.join(root, configuredPath);
26
31
  return {
27
- product,
28
- rootDir,
32
+ configuredPath,
33
+ rootDir: path_1.default.dirname(skillsRoot),
29
34
  skillsRoot,
30
35
  manifestPath: path_1.default.join(skillsRoot, MANIFEST_FILE),
31
36
  };
@@ -55,11 +60,11 @@ function writeManifest(target, managed) {
55
60
  fs_1.default.mkdirSync(target.skillsRoot, { recursive: true });
56
61
  fs_1.default.writeFileSync(target.manifestPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
57
62
  }
58
- function shouldCreateMirrorRoots(root) {
63
+ function shouldCreateMirrorRoots(root, config) {
59
64
  if (MANAGED_ROOT_MARKERS.some((relPath) => fs_1.default.existsSync(path_1.default.join(root, relPath)))) {
60
65
  return true;
61
66
  }
62
- return resolveMirrorTargets(root).some((target) => fs_1.default.existsSync(target.rootDir) || fs_1.default.existsSync(target.skillsRoot));
67
+ return resolveMirrorTargets(root, config).some((target) => fs_1.default.existsSync(target.rootDir) || fs_1.default.existsSync(target.skillsRoot));
63
68
  }
64
69
  function listAllowedEntries(dirPath) {
65
70
  if (!fs_1.default.existsSync(dirPath)) {
@@ -235,7 +240,7 @@ function syncSkillMirrors(options) {
235
240
  const sources = loadCanonicalSources(options.root, options.config);
236
241
  const createRoots = Boolean(options.createRoots);
237
242
  const force = Boolean(options.force);
238
- const targets = resolveMirrorTargets(options.root);
243
+ const targets = resolveMirrorTargets(options.root, options.config);
239
244
  let synced = 0;
240
245
  let pruned = 0;
241
246
  let touchedTargets = 0;
@@ -280,7 +285,7 @@ function preflightSkillMirrorTargets(options) {
280
285
  if (slugs.length === 0) {
281
286
  return;
282
287
  }
283
- for (const target of resolveMirrorTargets(options.root)) {
288
+ for (const target of resolveMirrorTargets(options.root, options.config)) {
284
289
  if (!fs_1.default.existsSync(target.rootDir) && !fs_1.default.existsSync(target.skillsRoot)) {
285
290
  continue;
286
291
  }
@@ -293,18 +298,18 @@ function preflightSkillMirrorTargets(options) {
293
298
  }
294
299
  }
295
300
  }
296
- function shouldMaintainSkillMirrors(root) {
297
- return shouldCreateMirrorRoots(root);
301
+ function shouldMaintainSkillMirrors(root, config) {
302
+ return shouldCreateMirrorRoots(root, config);
298
303
  }
299
304
  function auditSkillMirrors(root, config) {
300
- const shouldAudit = shouldCreateMirrorRoots(root);
305
+ const shouldAudit = shouldCreateMirrorRoots(root, config);
301
306
  if (!shouldAudit) {
302
307
  return [];
303
308
  }
304
309
  const warnings = [];
305
310
  const sources = loadCanonicalSources(root, config);
306
311
  const sourceBySlug = new Map(sources.map((source) => [source.slug, source]));
307
- for (const target of resolveMirrorTargets(root)) {
312
+ for (const target of resolveMirrorTargets(root, config)) {
308
313
  if (!fs_1.default.existsSync(target.skillsRoot)) {
309
314
  warnings.push(`${path_1.default.relative(root, target.skillsRoot)}: mirror root missing; run \`mdkg skill sync\``);
310
315
  continue;
@@ -335,8 +340,8 @@ function auditSkillMirrors(root, config) {
335
340
  }
336
341
  return warnings;
337
342
  }
338
- function scaffoldMirrorRoots(root) {
339
- for (const target of resolveMirrorTargets(root)) {
343
+ function scaffoldMirrorRoots(root, config) {
344
+ for (const target of resolveMirrorTargets(root, config)) {
340
345
  fs_1.default.mkdirSync(target.skillsRoot, { recursive: true });
341
346
  if (!fs_1.default.existsSync(target.manifestPath)) {
342
347
  writeManifest(target, []);
@@ -52,7 +52,7 @@ function registryTemplate() {
52
52
  "This directory stores Agent Skills packages used by mdkg tooling and orchestrators.",
53
53
  "",
54
54
  "Use `mdkg skill new <slug> \"<name>\" --description \"...\"` to scaffold a new skill from the built-in Anthropic-aligned template.",
55
- "Use `mdkg skill sync` to mirror canonical skills into `.agents/skills/` and `.claude/skills/` when agent bootstrap is enabled.",
55
+ "Use `mdkg skill sync` to mirror canonical skills into configured `.mdkg/config.json` targets; defaults are `.agents/skills/` and `.claude/skills/`.",
56
56
  "Use `CLI_COMMAND_MATRIX.md` as the canonical command and flag reference when updating skill procedures.",
57
57
  "",
58
58
  "## Conventions",
@@ -3,6 +3,9 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.runSpecListCommand = runSpecListCommand;
4
4
  exports.runSpecShowCommand = runSpecShowCommand;
5
5
  exports.runSpecValidateCommand = runSpecValidateCommand;
6
+ exports.runManifestListCommand = runManifestListCommand;
7
+ exports.runManifestShowCommand = runManifestShowCommand;
8
+ exports.runManifestValidateCommand = runManifestValidateCommand;
6
9
  const capability_1 = require("./capability");
7
10
  const validate_1 = require("./validate");
8
11
  const errors_1 = require("../util/errors");
@@ -24,6 +27,9 @@ function loadSpecRecords(options) {
24
27
  };
25
28
  return sortSpecRecords((0, capability_1.filterCapabilityRecords)((0, capability_1.loadCapabilityRecords)(listOptions), listOptions));
26
29
  }
30
+ function commandSurface(options) {
31
+ return options.surface ?? "spec";
32
+ }
27
33
  function matchesSpecRef(record, id) {
28
34
  const normalized = id.toLowerCase();
29
35
  return (record.id === id ||
@@ -34,15 +40,15 @@ function matchesSpecRef(record, id) {
34
40
  record.path.toLowerCase() === normalized ||
35
41
  record.aliases.some((alias) => alias.toLowerCase() === normalized));
36
42
  }
37
- function resolveSpecRecord(records, id) {
43
+ function resolveSpecRecord(records, id, surface) {
38
44
  const matches = records.filter((record) => matchesSpecRef(record, id));
39
45
  if (matches.length === 1) {
40
46
  return matches[0];
41
47
  }
42
48
  if (matches.length > 1) {
43
- throw new errors_1.UsageError(`SPEC reference is ambiguous: ${id}`);
49
+ throw new errors_1.UsageError(`${surface === "manifest" ? "manifest capability" : "SPEC"} reference is ambiguous: ${id}`);
44
50
  }
45
- throw new errors_1.NotFoundError(`SPEC not found: ${id}`);
51
+ throw new errors_1.NotFoundError(`${surface === "manifest" ? "manifest capability" : "SPEC"} not found: ${id}`);
46
52
  }
47
53
  function stringValue(value) {
48
54
  return typeof value === "string" ? value : undefined;
@@ -50,33 +56,74 @@ function stringValue(value) {
50
56
  function stringArrayValue(value) {
51
57
  return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
52
58
  }
53
- function printSpecList(records, json) {
59
+ function compatibilityLabel(record) {
60
+ const mode = record.manifest?.compatibility_mode;
61
+ if (mode === "legacy") {
62
+ return "legacy SPEC.md";
63
+ }
64
+ if (mode === "transitional") {
65
+ return "transitional MANIFEST.md type: spec";
66
+ }
67
+ return "MANIFEST.md";
68
+ }
69
+ function legacySpecDeprecation() {
70
+ return "mdkg spec is a legacy alias for mdkg manifest during the one-compatibility-release bridge.";
71
+ }
72
+ function printSpecList(records, json, surface = "spec") {
54
73
  if (json) {
55
- console.log(JSON.stringify({
56
- kind: "spec",
57
- count: records.length,
58
- items: records,
59
- }, null, 2));
74
+ const receipt = surface === "manifest"
75
+ ? {
76
+ kind: "manifest",
77
+ compatibility_kind: "spec",
78
+ count: records.length,
79
+ items: records,
80
+ }
81
+ : {
82
+ kind: "spec",
83
+ canonical_kind: "manifest",
84
+ legacy_alias: true,
85
+ deprecation: legacySpecDeprecation(),
86
+ count: records.length,
87
+ items: records,
88
+ };
89
+ console.log(JSON.stringify(receipt, null, 2));
60
90
  return;
61
91
  }
92
+ if (surface === "spec") {
93
+ console.log(legacySpecDeprecation());
94
+ }
62
95
  if (records.length === 0) {
63
- console.log("no SPEC.md capabilities found");
96
+ console.log("no MANIFEST.md/SPEC.md capabilities found");
64
97
  return;
65
98
  }
66
- console.log(`SPEC.md capabilities: ${records.length}`);
99
+ console.log(`${surface === "manifest" ? "MANIFEST.md" : "MANIFEST.md/SPEC.md"} capabilities: ${records.length}`);
67
100
  for (const record of records) {
68
101
  const kind = stringValue(record.spec?.spec_kind);
69
102
  const kindLabel = kind ? ` | ${kind}` : "";
70
- console.log(`${record.qid} | ${record.visibility}${kindLabel} | ${record.title}`);
103
+ console.log(`${record.qid} | ${record.visibility}${kindLabel} | ${compatibilityLabel(record)} | ${record.title}`);
71
104
  }
72
105
  }
73
- function printSpec(record, json) {
106
+ function printSpec(record, json, surface = "spec") {
74
107
  if (json) {
75
- console.log(JSON.stringify({ kind: "spec", item: record }, null, 2));
108
+ const receipt = surface === "manifest"
109
+ ? { kind: "manifest", compatibility_kind: "spec", item: record }
110
+ : {
111
+ kind: "spec",
112
+ canonical_kind: "manifest",
113
+ legacy_alias: true,
114
+ deprecation: legacySpecDeprecation(),
115
+ item: record,
116
+ };
117
+ console.log(JSON.stringify(receipt, null, 2));
76
118
  return;
77
119
  }
120
+ if (surface === "spec") {
121
+ console.log(legacySpecDeprecation());
122
+ }
78
123
  console.log(`${record.qid} | ${record.visibility}`);
79
124
  console.log(`title: ${record.title}`);
125
+ console.log(`semantic_kind: ${record.manifest?.semantic_kind ?? "manifest"}`);
126
+ console.log(`compatibility: ${compatibilityLabel(record)}`);
80
127
  const specKind = stringValue(record.spec?.spec_kind);
81
128
  if (specKind) {
82
129
  console.log(`spec_kind: ${specKind}`);
@@ -88,14 +135,26 @@ function printSpec(record, json) {
88
135
  }
89
136
  }
90
137
  function runSpecListCommand(options) {
91
- printSpecList(loadSpecRecords(options), options.json);
138
+ const surface = commandSurface(options);
139
+ printSpecList(loadSpecRecords(options), options.json, surface);
92
140
  }
93
141
  function runSpecShowCommand(options) {
94
- printSpec(resolveSpecRecord(loadSpecRecords(options), options.id), options.json);
142
+ const surface = commandSurface(options);
143
+ printSpec(resolveSpecRecord(loadSpecRecords(options), options.id, surface), options.json, surface);
95
144
  }
96
145
  function runSpecValidateCommand(options) {
146
+ const surface = commandSurface(options);
97
147
  if (options.id) {
98
- resolveSpecRecord(loadSpecRecords(options), options.id);
148
+ resolveSpecRecord(loadSpecRecords(options), options.id, surface);
99
149
  }
100
150
  (0, validate_1.runValidateCommand)({ root: options.root, json: options.json });
101
151
  }
152
+ function runManifestListCommand(options) {
153
+ runSpecListCommand({ ...options, surface: "manifest" });
154
+ }
155
+ function runManifestShowCommand(options) {
156
+ runSpecShowCommand({ ...options, surface: "manifest" });
157
+ }
158
+ function runManifestValidateCommand(options) {
159
+ runSpecValidateCommand({ ...options, surface: "manifest" });
160
+ }
@@ -323,6 +323,9 @@ function runSubgraphVerifyCommand(options) {
323
323
  throw new errors_1.ValidationError("subgraph verify failed");
324
324
  }
325
325
  }
326
+ function dirtySourceGuidance(paths) {
327
+ return `source_path has dirty tracked changes: ${paths.join(", ")}; commit or stash the child repo first, then refresh the root-owned subgraph bundle from the clean accepted child commit`;
328
+ }
326
329
  function pushAuditCheck(receipt, check) {
327
330
  receipt.checks.push(check);
328
331
  if (!check.ok && check.severity === "error") {
@@ -349,7 +352,7 @@ function auditOneAlias(options) {
349
352
  source_repo: options.health.source_repo,
350
353
  capability_summary: {
351
354
  node_count: options.nodeTypes.length,
352
- spec_count: options.nodeTypes.filter((type) => type === "spec").length,
355
+ spec_count: options.nodeTypes.filter((type) => type === "manifest" || type === "spec").length,
353
356
  work_count: options.nodeTypes.filter((type) => type === "work").length,
354
357
  skill_count: options.nodeTypes.filter((type) => type === "skill").length,
355
358
  },
@@ -392,7 +395,7 @@ function auditOneAlias(options) {
392
395
  ok: !gitState.dirtyTracked,
393
396
  severity: "warning",
394
397
  message: gitState.dirtyTracked
395
- ? `source_path has dirty tracked changes: ${gitState.dirtyTrackedPaths.join(", ")}`
398
+ ? dirtySourceGuidance(gitState.dirtyTrackedPaths)
396
399
  : "source_path has no dirty tracked changes",
397
400
  path: options.subgraph.source_path,
398
401
  details: { dirty_tracked_paths: gitState.dirtyTrackedPaths },
@@ -539,7 +542,7 @@ function upgradePlanForAlias(options) {
539
542
  blockers.push(sourceGitError.message);
540
543
  }
541
544
  if (options.audit.dirty_tracked) {
542
- blockers.push(`source_path has dirty tracked changes: ${options.audit.dirty_tracked_paths?.join(", ")}`);
545
+ blockers.push(dirtySourceGuidance(options.audit.dirty_tracked_paths ?? []));
543
546
  }
544
547
  const rootOwnedErrors = options.audit.checks.filter((check) => check.id === "subgraph.bundle.root_owned" && !check.ok);
545
548
  blockers.push(...rootOwnedErrors.map((check) => check.message));
@@ -705,7 +708,7 @@ function inspectSourcePath(root, alias, subgraph, allowDirty) {
705
708
  ? status.stdout.split(/\r?\n/).map((line) => line.slice(3)).filter(Boolean).sort()
706
709
  : [];
707
710
  if (dirtyTrackedPaths.length > 0 && !allowDirty) {
708
- throw new errors_1.UsageError(`subgraph ${alias} source_path has dirty tracked changes: ${dirtyTrackedPaths.join(", ")}`);
711
+ throw new errors_1.UsageError(`subgraph ${alias} ${dirtySourceGuidance(dirtyTrackedPaths)}`);
709
712
  }
710
713
  return {
711
714
  sourceRoot,