mdkg 0.0.6 → 0.0.8

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/README.md CHANGED
@@ -94,6 +94,8 @@ mdkg task update task-1 --add-artifacts tests://unit.txt --add-tags release
94
94
  mdkg task done task-1 --checkpoint "release readiness milestone"
95
95
  ```
96
96
 
97
+ `mdkg task ...` remains limited to `task`, `bug`, and `test`, but `skills: [...]` is valid metadata on all work items (`epic`, `feat`, `task`, `bug`, `checkpoint`, `test`). mdkg-generated work items should validate and round-trip through the task mutators without manual frontmatter repair.
98
+
97
99
  Ensure and append baseline event memory:
98
100
 
99
101
  ```bash
@@ -176,11 +178,15 @@ Current source behavior:
176
178
  - `mdkg skill show <slug>`
177
179
  - `mdkg skill validate [<slug>]`
178
180
  - `mdkg skill sync`
179
- - machine-readable skill discovery is available through:
181
+ - machine-readable skill discovery and inspection is available through:
180
182
  - `mdkg skill list --json`
181
183
  - `mdkg skill search "<query>" --json`
182
184
  - `mdkg skill show <slug> --json`
185
+ - `mdkg skill list --xml|--toon|--md`
186
+ - `mdkg skill search "<query>" --xml|--toon|--md`
187
+ - `mdkg skill show <slug> --xml|--toon|--md`
183
188
  - work items may reference `skills: [slug,...]`
189
+ - `skills` is valid on all work items (`epic`, `feat`, `task`, `bug`, `checkpoint`, `test`)
184
190
  - packs may include skills with `--skills` and `--skills-depth`
185
191
  - mdkg indexes and discovers skills but does not execute skill scripts
186
192
  - `SKILL.md` is canonical
@@ -201,11 +207,13 @@ This repo now dogfoods three internal skills:
201
207
  This release includes:
202
208
  - `init --agent`
203
209
  - default ignore updates with `--no-update-ignores` for `.mdkg/index/` and `.mdkg/pack/`
210
+ - root-only published init seed config
204
211
  - skills indexing and search/show/list support
205
212
  - optional `skills: [...]` on work items
206
213
  - pack-time skill inclusion
207
214
  - latest-checkpoint resolver + index hint
208
215
  - events JSONL validation
216
+ - XML / TOON / Markdown output for node and skill list/search/show
209
217
  - product-specific skill mirrors for Codex/OpenAI and Claude
210
218
  - shared `AGENT_START.md` startup guidance
211
219
 
@@ -214,6 +222,7 @@ Current direction:
214
222
  - use `init --agent` for deeper AI-agent bootstrap
215
223
  - keep `pack <id>` at the center of the human/agent loop
216
224
  - use `mdkg task ...` for structured state changes and markdown edits for narrative/body content
225
+ - keep validation strict and fix schema drift instead of papering over mdkg-generated inconsistencies
217
226
  - make event logging guided instead of purely manual
218
227
  - dogfood real skills inside the repo
219
228
  - make skill authoring first-class through `mdkg skill`
package/dist/cli.js CHANGED
@@ -134,7 +134,7 @@ function printIndexHelp(log) {
134
134
  }
135
135
  function printShowHelp(log) {
136
136
  log("Usage:");
137
- log(" mdkg show <id-or-qid> [--ws <alias>] [--meta] [--json]");
137
+ log(" mdkg show <id-or-qid> [--ws <alias>] [--meta] [--json|--xml|--toon|--md]");
138
138
  log("\nWhen to use:");
139
139
  log(" Inspect one mdkg node exactly. Use `mdkg skill show <slug>` for skills.");
140
140
  log("\nDefault behavior:");
@@ -144,7 +144,8 @@ function printShowHelp(log) {
144
144
  function printListHelp(log) {
145
145
  log("Usage:");
146
146
  log(" mdkg list [--type <type>] [--status <status>] [--ws <alias>] [--epic <id>]");
147
- log(" [--priority <n>] [--blocked] [--tags <tag,tag,...>] [--tags-mode any|all] [--json]");
147
+ log(" [--priority <n>] [--blocked] [--tags <tag,tag,...>] [--tags-mode any|all]");
148
+ log(" [--json|--xml|--toon|--md]");
148
149
  log("\nWhen to use:");
149
150
  log(" List mdkg nodes. Use `mdkg skill list` for skills.");
150
151
  printGlobalOptions(log);
@@ -152,7 +153,7 @@ function printListHelp(log) {
152
153
  function printSearchHelp(log) {
153
154
  log("Usage:");
154
155
  log(' mdkg search "<query>" [--type <type>] [--status <status>] [--ws <alias>]');
155
- log(" [--tags <tag,tag,...>] [--tags-mode any|all] [--json]");
156
+ log(" [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]");
156
157
  log("\nWhen to use:");
157
158
  log(" Search mdkg nodes by metadata. Use `mdkg skill search` for skills.");
158
159
  printGlobalOptions(log);
@@ -210,21 +211,21 @@ function printSkillHelp(log, subcommand) {
210
211
  return;
211
212
  case "list":
212
213
  log("Usage:");
213
- log(" mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json]");
214
+ log(" mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]");
214
215
  log("\nWhen to use:");
215
216
  log(" Discover skills directly, including stage-tagged orchestrator lookups.");
216
217
  printGlobalOptions(log);
217
218
  return;
218
219
  case "show":
219
220
  log("Usage:");
220
- log(" mdkg skill show <slug> [--meta] [--json]");
221
+ log(" mdkg skill show <slug> [--meta] [--json|--xml|--toon|--md]");
221
222
  log("\nWhen to use:");
222
223
  log(" Inspect one skill body or metadata after discovery.");
223
224
  printGlobalOptions(log);
224
225
  return;
225
226
  case "search":
226
227
  log("Usage:");
227
- log(' mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json]');
228
+ log(' mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]');
228
229
  log("\nWhen to use:");
229
230
  log(" Search skills by trigger text, tags, and stage conventions like `stage:plan`.");
230
231
  printGlobalOptions(log);
@@ -244,9 +245,9 @@ function printSkillHelp(log, subcommand) {
244
245
  default:
245
246
  log("Usage:");
246
247
  log(' mdkg skill new <slug> "<name>" --description "<description>" [options]');
247
- log(" mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json]");
248
- log(" mdkg skill show <slug> [--meta] [--json]");
249
- log(' mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json]');
248
+ log(" mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]");
249
+ log(" mdkg skill show <slug> [--meta] [--json|--xml|--toon|--md]");
250
+ log(' mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]');
250
251
  log(" mdkg skill validate [<slug>]");
251
252
  log(" mdkg skill sync [--force]");
252
253
  log("\nNotes:");
@@ -528,6 +529,25 @@ function parseTagsModeFlag(value) {
528
529
  }
529
530
  throw new errors_1.UsageError("--tags-mode must be any or all");
530
531
  }
532
+ function parseQueryOutputFormat(parsed) {
533
+ const enabled = [];
534
+ if (parseBooleanFlag("--json", parsed.flags["--json"])) {
535
+ enabled.push("json");
536
+ }
537
+ if (parseBooleanFlag("--xml", parsed.flags["--xml"])) {
538
+ enabled.push("xml");
539
+ }
540
+ if (parseBooleanFlag("--toon", parsed.flags["--toon"])) {
541
+ enabled.push("toon");
542
+ }
543
+ if (parseBooleanFlag("--md", parsed.flags["--md"])) {
544
+ enabled.push("md");
545
+ }
546
+ if (enabled.length > 1) {
547
+ throw new errors_1.UsageError("choose at most one output flag: --json, --xml, --toon, or --md");
548
+ }
549
+ return enabled[0];
550
+ }
531
551
  function handleCommandError(err, command, runtime) {
532
552
  const message = err instanceof Error ? err.message : String(err);
533
553
  if (err instanceof errors_1.UsageError) {
@@ -622,12 +642,12 @@ function runSkillSubcommand(parsed, root) {
622
642
  const tagsMode = parseTagsModeFlag(parsed.flags["--tags-mode"]);
623
643
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
624
644
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
625
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
645
+ const format = parseQueryOutputFormat(parsed);
626
646
  (0, skill_1.runSkillListCommand)({
627
647
  root,
628
648
  tags,
629
649
  tagsMode,
630
- json,
650
+ format,
631
651
  noCache,
632
652
  noReindex,
633
653
  });
@@ -639,14 +659,14 @@ function runSkillSubcommand(parsed, root) {
639
659
  throw new errors_1.UsageError("skill show requires <slug>");
640
660
  }
641
661
  const metaOnly = parseBooleanFlag("--meta", parsed.flags["--meta"]);
642
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
662
+ const format = parseQueryOutputFormat(parsed);
643
663
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
644
664
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
645
665
  (0, skill_1.runSkillShowCommand)({
646
666
  root,
647
667
  slug,
648
668
  metaOnly,
649
- json,
669
+ format,
650
670
  noCache,
651
671
  noReindex,
652
672
  });
@@ -659,7 +679,7 @@ function runSkillSubcommand(parsed, root) {
659
679
  const query = parsed.positionals.slice(2).join(" ");
660
680
  const tags = parseCsvFlag("--tags", parsed.flags["--tags"]);
661
681
  const tagsMode = parseTagsModeFlag(parsed.flags["--tags-mode"]);
662
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
682
+ const format = parseQueryOutputFormat(parsed);
663
683
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
664
684
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
665
685
  (0, skill_1.runSkillSearchCommand)({
@@ -667,7 +687,7 @@ function runSkillSubcommand(parsed, root) {
667
687
  query,
668
688
  tags,
669
689
  tagsMode,
670
- json,
690
+ format,
671
691
  noCache,
672
692
  noReindex,
673
693
  });
@@ -930,7 +950,7 @@ function runCommand(parsed, root, runtime) {
930
950
  }
931
951
  const ws = requireFlagValue("--ws", parsed.flags["--ws"]);
932
952
  const metaOnly = parseBooleanFlag("--meta", parsed.flags["--meta"]);
933
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
953
+ const format = parseQueryOutputFormat(parsed);
934
954
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
935
955
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
936
956
  (0, show_1.runShowCommand)({
@@ -938,7 +958,7 @@ function runCommand(parsed, root, runtime) {
938
958
  id,
939
959
  ws,
940
960
  metaOnly,
941
- json,
961
+ format,
942
962
  noCache,
943
963
  noReindex,
944
964
  });
@@ -956,7 +976,7 @@ function runCommand(parsed, root, runtime) {
956
976
  const blocked = parseBooleanFlag("--blocked", parsed.flags["--blocked"]);
957
977
  const tags = parseCsvFlag("--tags", parsed.flags["--tags"]);
958
978
  const tagsMode = parseTagsModeFlag(parsed.flags["--tags-mode"]);
959
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
979
+ const format = parseQueryOutputFormat(parsed);
960
980
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
961
981
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
962
982
  (0, list_1.runListCommand)({
@@ -969,7 +989,7 @@ function runCommand(parsed, root, runtime) {
969
989
  blocked,
970
990
  tags,
971
991
  tagsMode,
972
- json,
992
+ format,
973
993
  noCache,
974
994
  noReindex,
975
995
  });
@@ -985,7 +1005,7 @@ function runCommand(parsed, root, runtime) {
985
1005
  const status = requireFlagValue("--status", parsed.flags["--status"]);
986
1006
  const tags = parseCsvFlag("--tags", parsed.flags["--tags"]);
987
1007
  const tagsMode = parseTagsModeFlag(parsed.flags["--tags-mode"]);
988
- const json = parseBooleanFlag("--json", parsed.flags["--json"]);
1008
+ const format = parseQueryOutputFormat(parsed);
989
1009
  const noCache = parseBooleanFlag("--no-cache", parsed.flags["--no-cache"]);
990
1010
  const noReindex = parseBooleanFlag("--no-reindex", parsed.flags["--no-reindex"]);
991
1011
  (0, search_1.runSearchCommand)({
@@ -996,7 +1016,7 @@ function runCommand(parsed, root, runtime) {
996
1016
  status,
997
1017
  tags,
998
1018
  tagsMode,
999
- json,
1019
+ format,
1000
1020
  noCache,
1001
1021
  noReindex,
1002
1022
  });
@@ -53,13 +53,14 @@ function runListCommand(options) {
53
53
  tagsMode: options.tagsMode,
54
54
  });
55
55
  const sorted = (0, sort_1.sortNodesByQid)(filtered);
56
- if (options.json) {
57
- (0, query_output_1.writeJson)({
56
+ const format = options.format ?? (options.json ? "json" : undefined);
57
+ if (format) {
58
+ (0, query_output_1.writeStructuredOutput)({
58
59
  command: "list",
59
60
  kind: "node",
60
61
  count: sorted.length,
61
62
  items: sorted.map(query_output_1.toNodeSummaryJson),
62
- });
63
+ }, format);
63
64
  return;
64
65
  }
65
66
  (0, query_output_1.writeCount)(sorted.length, sorted.length === 0 ? "no nodes matched current filters" : undefined);
@@ -10,6 +10,7 @@ const config_1 = require("../core/config");
10
10
  const index_cache_1 = require("../graph/index_cache");
11
11
  const node_1 = require("../graph/node");
12
12
  const indexer_1 = require("../graph/indexer");
13
+ const template_schema_1 = require("../graph/template_schema");
13
14
  const loader_1 = require("../templates/loader");
14
15
  const date_1 = require("../util/date");
15
16
  const errors_1 = require("../util/errors");
@@ -172,7 +173,7 @@ function runNewCommand(options) {
172
173
  throw new errors_1.UsageError(`--status must be one of ${Array.from(allowed).join(", ")}`);
173
174
  }
174
175
  }
175
- else if (type === "dec") {
176
+ else if (node_1.DEC_TYPES.has(type)) {
176
177
  status = statusInput ?? "proposed";
177
178
  if (!DEC_STATUS.has(status)) {
178
179
  throw new errors_1.UsageError(`--status must be one of ${Array.from(DEC_STATUS).join(", ")}`);
@@ -196,12 +197,20 @@ function runNewCommand(options) {
196
197
  else if (options.priority !== undefined) {
197
198
  throw new errors_1.UsageError("--priority is only valid for work items");
198
199
  }
199
- if (!node_1.WORK_TYPES.has(type)) {
200
- if (options.epic || options.parent || options.prev || options.next || options.blockedBy || options.blocks) {
201
- throw new errors_1.UsageError("epic/parent/prev/next/blocked-by/blocks are only valid for work items");
200
+ const graphFields = [
201
+ ["epic", options.epic],
202
+ ["parent", options.parent],
203
+ ["prev", options.prev],
204
+ ["next", options.next],
205
+ ["blocked_by", options.blockedBy],
206
+ ["blocks", options.blocks],
207
+ ];
208
+ for (const [key, raw] of graphFields) {
209
+ if (raw && !(0, template_schema_1.schemaAllowsKey)(type, key)) {
210
+ throw new errors_1.UsageError(`--${key.replace("_", "-")} is not valid for ${type} nodes`);
202
211
  }
203
212
  }
204
- if (options.cases && type !== "test") {
213
+ if (options.cases && !(0, template_schema_1.schemaAllowsKey)(type, "cases")) {
205
214
  throw new errors_1.UsageError("--cases is only valid for test nodes");
206
215
  }
207
216
  const epic = options.epic ? normalizeIdRef(options.epic, "--epic") : undefined;
@@ -219,7 +228,7 @@ function runNewCommand(options) {
219
228
  const skills = normalizeSkillList(options.skills);
220
229
  const links = normalizeList(options.links);
221
230
  const artifacts = normalizeList(options.artifacts);
222
- if (skills.length > 0 && !node_1.WORK_TYPES.has(type)) {
231
+ if (skills.length > 0 && !(0, template_schema_1.schemaAllowsKey)(type, "skills")) {
223
232
  throw new errors_1.UsageError("--skills is only valid for work items");
224
233
  }
225
234
  if (type === "dec" && options.supersedes) {
@@ -258,6 +267,7 @@ function runNewCommand(options) {
258
267
  }
259
268
  const today = (0, date_1.formatDate)(options.now ?? new Date());
260
269
  const template = (0, loader_1.loadTemplate)(options.root, config, type, options.template);
270
+ const schema = (0, template_schema_1.getNodeSchema)(type);
261
271
  const content = (0, loader_1.renderTemplate)(template, {
262
272
  id,
263
273
  type,
@@ -282,7 +292,7 @@ function runNewCommand(options) {
282
292
  supersedes: options.supersedes ? options.supersedes.toLowerCase() : undefined,
283
293
  created: today,
284
294
  updated: today,
285
- });
295
+ }, schema?.allowedKeys);
286
296
  fs_1.default.mkdirSync(targetDir, { recursive: true });
287
297
  fs_1.default.writeFileSync(filePath, content, "utf8");
288
298
  if (config.index.auto_reindex && !noReindex) {
@@ -5,6 +5,7 @@ exports.toNodeDetailJson = toNodeDetailJson;
5
5
  exports.toSkillSummaryJson = toSkillSummaryJson;
6
6
  exports.toSkillDetailJson = toSkillDetailJson;
7
7
  exports.writeJson = writeJson;
8
+ exports.writeStructuredOutput = writeStructuredOutput;
8
9
  exports.writeCount = writeCount;
9
10
  function toNodeSummaryJson(node) {
10
11
  return {
@@ -76,6 +77,113 @@ function toSkillDetailJson(skill, body) {
76
77
  function writeJson(payload) {
77
78
  console.log(JSON.stringify(payload, null, 2));
78
79
  }
80
+ function escapeXml(value) {
81
+ return value
82
+ .replace(/&/g, "&amp;")
83
+ .replace(/</g, "&lt;")
84
+ .replace(/>/g, "&gt;")
85
+ .replace(/"/g, "&quot;")
86
+ .replace(/'/g, "&apos;");
87
+ }
88
+ function isPrimitive(value) {
89
+ return (value === null ||
90
+ typeof value === "string" ||
91
+ typeof value === "number" ||
92
+ typeof value === "boolean");
93
+ }
94
+ function objectToXml(tag, value, indent = "") {
95
+ if (value === undefined) {
96
+ return [];
97
+ }
98
+ if (Array.isArray(value)) {
99
+ const lines = [`${indent}<${tag}>`];
100
+ for (const item of value) {
101
+ lines.push(...objectToXml("item", item, `${indent} `));
102
+ }
103
+ lines.push(`${indent}</${tag}>`);
104
+ return lines;
105
+ }
106
+ if (isPrimitive(value)) {
107
+ return [`${indent}<${tag}>${escapeXml(value === null ? "" : String(value))}</${tag}>`];
108
+ }
109
+ const lines = [`${indent}<${tag}>`];
110
+ for (const [key, child] of Object.entries(value)) {
111
+ lines.push(...objectToXml(key, child, `${indent} `));
112
+ }
113
+ lines.push(`${indent}</${tag}>`);
114
+ return lines;
115
+ }
116
+ function formatMarkdownScalar(value) {
117
+ return value === null ? "null" : String(value);
118
+ }
119
+ function objectToMarkdown(value, lines, indent = "", key) {
120
+ if (value === undefined) {
121
+ return;
122
+ }
123
+ if (Array.isArray(value)) {
124
+ if (key) {
125
+ lines.push(`${indent}- ${key}:`);
126
+ }
127
+ const childIndent = key ? `${indent} ` : indent;
128
+ if (value.length === 0) {
129
+ lines.push(`${childIndent}- []`);
130
+ return;
131
+ }
132
+ for (const item of value) {
133
+ if (isPrimitive(item)) {
134
+ lines.push(`${childIndent}- ${formatMarkdownScalar(item)}`);
135
+ continue;
136
+ }
137
+ lines.push(`${childIndent}-`);
138
+ objectToMarkdown(item, lines, `${childIndent} `);
139
+ }
140
+ return;
141
+ }
142
+ if (isPrimitive(value)) {
143
+ if (!key) {
144
+ lines.push(`${indent}- ${formatMarkdownScalar(value)}`);
145
+ return;
146
+ }
147
+ lines.push(`${indent}- ${key}: ${formatMarkdownScalar(value)}`);
148
+ return;
149
+ }
150
+ if (key) {
151
+ lines.push(`${indent}- ${key}:`);
152
+ }
153
+ const childIndent = key ? `${indent} ` : indent;
154
+ for (const [childKey, childValue] of Object.entries(value)) {
155
+ objectToMarkdown(childValue, lines, childIndent, childKey);
156
+ }
157
+ }
158
+ function writeXml(payload) {
159
+ const lines = ['<?xml version="1.0" encoding="UTF-8"?>'];
160
+ lines.push(...objectToXml("response", payload));
161
+ console.log(lines.join("\n"));
162
+ }
163
+ function writeToon(payload) {
164
+ console.log(JSON.stringify(payload, null, 2));
165
+ }
166
+ function writeMarkdown(payload) {
167
+ const lines = ["# mdkg response"];
168
+ objectToMarkdown(payload, lines);
169
+ console.log(lines.join("\n"));
170
+ }
171
+ function writeStructuredOutput(payload, format) {
172
+ switch (format) {
173
+ case "json":
174
+ writeJson(payload);
175
+ return;
176
+ case "xml":
177
+ writeXml(payload);
178
+ return;
179
+ case "toon":
180
+ writeToon(payload);
181
+ return;
182
+ case "md":
183
+ writeMarkdown(payload);
184
+ return;
185
+ }
186
+ }
79
187
  function writeCount(count, note) {
80
188
  console.error(`count: ${count}`);
81
189
  if (note) {
@@ -71,13 +71,14 @@ function runSearchCommand(options) {
71
71
  tagsMode: options.tagsMode,
72
72
  }).filter((node) => matchesQuery(node, terms));
73
73
  const sorted = (0, sort_1.sortNodesByQid)(nodeResults);
74
- if (options.json) {
75
- (0, query_output_1.writeJson)({
74
+ const format = options.format ?? (options.json ? "json" : undefined);
75
+ if (format) {
76
+ (0, query_output_1.writeStructuredOutput)({
76
77
  command: "search",
77
78
  kind: "node",
78
79
  count: sorted.length,
79
80
  items: sorted.map(query_output_1.toNodeSummaryJson),
80
- });
81
+ }, format);
81
82
  return;
82
83
  }
83
84
  (0, query_output_1.writeCount)(sorted.length, sorted.length === 0 ? `no nodes matched query "${query}"` : undefined);
@@ -58,12 +58,13 @@ function runShowCommand(options) {
58
58
  const content = fs_1.default.readFileSync(filePath, "utf8");
59
59
  body = (0, frontmatter_1.parseFrontmatter)(content, filePath).body.trimEnd();
60
60
  }
61
- if (options.json) {
62
- (0, query_output_1.writeJson)({
61
+ const format = options.format ?? (options.json ? "json" : undefined);
62
+ if (format) {
63
+ (0, query_output_1.writeStructuredOutput)({
63
64
  command: "show",
64
65
  kind: "node",
65
66
  item: (0, query_output_1.toNodeDetailJson)(node, options.metaOnly ? undefined : body),
66
- });
67
+ }, format);
67
68
  return;
68
69
  }
69
70
  const lines = [];
@@ -205,13 +205,14 @@ function runSkillListCommand(options) {
205
205
  console.error("warning: skills index is stale; run mdkg index to refresh");
206
206
  }
207
207
  const skills = filterSkills(Object.values(index.skills), options.tags, options.tagsMode ?? "any").sort((a, b) => a.qid.localeCompare(b.qid));
208
- if (options.json) {
209
- (0, query_output_1.writeJson)({
208
+ const format = options.format ?? (options.json ? "json" : undefined);
209
+ if (format) {
210
+ (0, query_output_1.writeStructuredOutput)({
210
211
  command: "list",
211
212
  kind: "skill",
212
213
  count: skills.length,
213
214
  items: skills.map(query_output_1.toSkillSummaryJson),
214
- });
215
+ }, format);
215
216
  return;
216
217
  }
217
218
  (0, query_output_1.writeCount)(skills.length, skills.length === 0 ? "no skills matched current filters" : undefined);
@@ -243,12 +244,13 @@ function runSkillShowCommand(options) {
243
244
  }
244
245
  body = fs_1.default.readFileSync(skillPath, "utf8").trimEnd();
245
246
  }
246
- if (options.json) {
247
- (0, query_output_1.writeJson)({
247
+ const format = options.format ?? (options.json ? "json" : undefined);
248
+ if (format) {
249
+ (0, query_output_1.writeStructuredOutput)({
248
250
  command: "show",
249
251
  kind: "skill",
250
252
  item: (0, query_output_1.toSkillDetailJson)(skill, options.metaOnly ? undefined : body),
251
- });
253
+ }, format);
252
254
  return;
253
255
  }
254
256
  if (options.metaOnly) {
@@ -307,13 +309,14 @@ function runSkillSearchCommand(options) {
307
309
  const skills = filterSkills(Object.values(index.skills), options.tags, options.tagsMode ?? "any")
308
310
  .filter((skill) => matchesSkillQuery(skill, terms))
309
311
  .sort((a, b) => a.qid.localeCompare(b.qid));
310
- if (options.json) {
311
- (0, query_output_1.writeJson)({
312
+ const format = options.format ?? (options.json ? "json" : undefined);
313
+ if (format) {
314
+ (0, query_output_1.writeStructuredOutput)({
312
315
  command: "search",
313
316
  kind: "skill",
314
317
  count: skills.length,
315
318
  items: skills.map(query_output_1.toSkillSummaryJson),
316
- });
319
+ }, format);
317
320
  return;
318
321
  }
319
322
  (0, query_output_1.writeCount)(skills.length, skills.length === 0 ? `no skills matched query "${query}"` : undefined);
@@ -9,10 +9,12 @@ exports.runTaskDoneCommand = runTaskDoneCommand;
9
9
  const fs_1 = __importDefault(require("fs"));
10
10
  const path_1 = __importDefault(require("path"));
11
11
  const config_1 = require("../core/config");
12
+ const node_1 = require("../graph/node");
12
13
  const indexer_1 = require("../graph/indexer");
13
14
  const index_cache_1 = require("../graph/index_cache");
14
15
  const frontmatter_1 = require("../graph/frontmatter");
15
16
  const skills_indexer_1 = require("../graph/skills_indexer");
17
+ const template_schema_1 = require("../graph/template_schema");
16
18
  const date_1 = require("../util/date");
17
19
  const errors_1 = require("../util/errors");
18
20
  const qid_1 = require("../util/qid");
@@ -112,7 +114,12 @@ function loadMutableTaskNode(root, idOrQid, wsHint) {
112
114
  }
113
115
  const filePath = path_1.default.resolve(root, node.path);
114
116
  const content = fs_1.default.readFileSync(filePath, "utf8");
115
- const parsed = (0, frontmatter_1.parseFrontmatter)(content, filePath);
117
+ const parsed = (0, node_1.parseNode)(content, filePath, {
118
+ workStatusEnum: config.work.status_enum,
119
+ priorityMin: config.work.priority_min,
120
+ priorityMax: config.work.priority_max,
121
+ templateSchemas: (0, template_schema_1.loadTemplateSchemas)(root, config, node_1.ALLOWED_TYPES),
122
+ });
116
123
  return {
117
124
  config,
118
125
  index,
@@ -4,24 +4,13 @@ exports.ALLOWED_TYPES = exports.DEC_TYPES = exports.WORK_TYPES = void 0;
4
4
  exports.parseNode = parseNode;
5
5
  const frontmatter_1 = require("./frontmatter");
6
6
  const edges_1 = require("./edges");
7
+ const template_schema_1 = require("./template_schema");
8
+ Object.defineProperty(exports, "ALLOWED_TYPES", { enumerable: true, get: function () { return template_schema_1.ALLOWED_TYPES; } });
9
+ Object.defineProperty(exports, "DEC_TYPES", { enumerable: true, get: function () { return template_schema_1.DEC_TYPES; } });
10
+ Object.defineProperty(exports, "WORK_TYPES", { enumerable: true, get: function () { return template_schema_1.WORK_TYPES; } });
7
11
  const id_1 = require("../util/id");
8
12
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
9
13
  const DEC_ID_RE = /^dec-[0-9]+$/;
10
- exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
11
- exports.DEC_TYPES = new Set(["dec"]);
12
- exports.ALLOWED_TYPES = new Set([
13
- "rule",
14
- "prd",
15
- "edd",
16
- "dec",
17
- "prop",
18
- "epic",
19
- "feat",
20
- "task",
21
- "bug",
22
- "checkpoint",
23
- "test",
24
- ]);
25
14
  const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
26
15
  const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
27
16
  function formatError(filePath, message) {
@@ -153,8 +142,8 @@ function validateTemplateKeys(frontmatter, schema, filePath) {
153
142
  function parseNode(content, filePath, options) {
154
143
  const { frontmatter, body } = (0, frontmatter_1.parseFrontmatter)(content, filePath);
155
144
  const type = requireLowercase(expectString(frontmatter, "type", filePath), "type", filePath);
156
- if (!exports.ALLOWED_TYPES.has(type)) {
157
- throw formatError(filePath, `type must be one of ${Array.from(exports.ALLOWED_TYPES).join(", ")}`);
145
+ if (!template_schema_1.ALLOWED_TYPES.has(type)) {
146
+ throw formatError(filePath, `type must be one of ${Array.from(template_schema_1.ALLOWED_TYPES).join(", ")}`);
158
147
  }
159
148
  const schema = requireTemplateSchema(type, options.templateSchemas, filePath);
160
149
  validateTemplateKeys(frontmatter, schema, filePath);
@@ -165,7 +154,7 @@ function parseNode(content, filePath, options) {
165
154
  const statusValue = optionalString(frontmatter, "status", filePath);
166
155
  let status = undefined;
167
156
  const workStatus = new Set(options.workStatusEnum.map((value) => value.toLowerCase()));
168
- if (exports.WORK_TYPES.has(type)) {
157
+ if (template_schema_1.WORK_TYPES.has(type)) {
169
158
  if (!statusValue) {
170
159
  throw formatError(filePath, "status is required for work items");
171
160
  }
@@ -175,7 +164,7 @@ function parseNode(content, filePath, options) {
175
164
  }
176
165
  status = normalized;
177
166
  }
178
- else if (exports.DEC_TYPES.has(type)) {
167
+ else if (template_schema_1.DEC_TYPES.has(type)) {
179
168
  if (!statusValue) {
180
169
  throw formatError(filePath, "status is required for decision records");
181
170
  }
@@ -191,7 +180,7 @@ function parseNode(content, filePath, options) {
191
180
  const priorityValue = optionalString(frontmatter, "priority", filePath);
192
181
  let priority = undefined;
193
182
  if (priorityValue !== undefined) {
194
- if (!exports.WORK_TYPES.has(type)) {
183
+ if (!template_schema_1.WORK_TYPES.has(type)) {
195
184
  throw formatError(filePath, "priority is only allowed for work items");
196
185
  }
197
186
  priority = parsePriority(priorityValue, filePath, options.priorityMin, options.priorityMax);
@@ -204,13 +193,13 @@ function parseNode(content, filePath, options) {
204
193
  const aliases = requireLowercaseList(optionalList(frontmatter, "aliases", filePath), "aliases", filePath);
205
194
  const skillsRaw = optionalList(frontmatter, "skills", filePath);
206
195
  const skills = normalizeSkillList(skillsRaw, filePath);
207
- if (skills.length > 0 && !exports.WORK_TYPES.has(type)) {
196
+ if (skills.length > 0 && !template_schema_1.WORK_TYPES.has(type)) {
208
197
  throw formatError(filePath, "skills is only allowed for work items");
209
198
  }
210
199
  normalizeIdList(optionalList(frontmatter, "scope", filePath), "scope", filePath);
211
200
  const supersedesValue = optionalString(frontmatter, "supersedes", filePath);
212
201
  if (supersedesValue !== undefined) {
213
- if (!exports.DEC_TYPES.has(type)) {
202
+ if (!template_schema_1.DEC_TYPES.has(type)) {
214
203
  throw formatError(filePath, "supersedes is only allowed for decision records");
215
204
  }
216
205
  const normalized = requireLowercase(supersedesValue, "supersedes", filePath);
@@ -3,10 +3,77 @@ 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.ALLOWED_TYPES = exports.DEC_TYPES = exports.WORK_TYPES = void 0;
7
+ exports.buildNodeSchemas = buildNodeSchemas;
8
+ exports.getNodeSchema = getNodeSchema;
9
+ exports.schemaAllowsKey = schemaAllowsKey;
6
10
  exports.loadTemplateSchemas = loadTemplateSchemas;
7
11
  const fs_1 = __importDefault(require("fs"));
8
12
  const path_1 = __importDefault(require("path"));
9
13
  const frontmatter_1 = require("./frontmatter");
14
+ exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
15
+ exports.DEC_TYPES = new Set(["dec"]);
16
+ exports.ALLOWED_TYPES = new Set([
17
+ "rule",
18
+ "prd",
19
+ "edd",
20
+ "dec",
21
+ "prop",
22
+ "epic",
23
+ "feat",
24
+ "task",
25
+ "bug",
26
+ "checkpoint",
27
+ "test",
28
+ ]);
29
+ const COMMON_SCALAR_KEYS = ["id", "type", "title", "created", "updated"];
30
+ const COMMON_LIST_KEYS = ["tags", "owners", "links", "artifacts", "relates", "refs", "aliases"];
31
+ const NODE_SCHEMA_DEFS = {
32
+ rule: {
33
+ scalars: [...COMMON_SCALAR_KEYS],
34
+ lists: [...COMMON_LIST_KEYS],
35
+ },
36
+ prd: {
37
+ scalars: [...COMMON_SCALAR_KEYS],
38
+ lists: [...COMMON_LIST_KEYS],
39
+ },
40
+ edd: {
41
+ scalars: [...COMMON_SCALAR_KEYS],
42
+ lists: [...COMMON_LIST_KEYS],
43
+ },
44
+ prop: {
45
+ scalars: [...COMMON_SCALAR_KEYS],
46
+ lists: [...COMMON_LIST_KEYS],
47
+ },
48
+ dec: {
49
+ scalars: [...COMMON_SCALAR_KEYS, "status", "supersedes"],
50
+ lists: [...COMMON_LIST_KEYS],
51
+ },
52
+ epic: {
53
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority"],
54
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
55
+ },
56
+ feat: {
57
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
58
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
59
+ },
60
+ task: {
61
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
62
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
63
+ },
64
+ bug: {
65
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
66
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills"],
67
+ },
68
+ checkpoint: {
69
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
70
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills", "scope"],
71
+ },
72
+ test: {
73
+ scalars: [...COMMON_SCALAR_KEYS, "status", "priority", "epic", "parent", "prev", "next"],
74
+ lists: [...COMMON_LIST_KEYS, "blocked_by", "blocks", "skills", "cases"],
75
+ },
76
+ };
10
77
  function listMarkdownFiles(dir) {
11
78
  if (!fs_1.default.existsSync(dir)) {
12
79
  return [];
@@ -44,13 +111,65 @@ function addKeyToSchema(schema, key, kind, filePath) {
44
111
  schema.listKeys.add(key);
45
112
  }
46
113
  }
114
+ function buildSchema(type, definition) {
115
+ const schema = {
116
+ type,
117
+ allowedKeys: new Set(),
118
+ keyKinds: {},
119
+ listKeys: new Set(),
120
+ };
121
+ for (const key of definition.scalars) {
122
+ addKeyToSchema(schema, key, "scalar", type);
123
+ }
124
+ for (const key of definition.lists ?? []) {
125
+ addKeyToSchema(schema, key, "list", type);
126
+ }
127
+ for (const key of definition.booleans ?? []) {
128
+ addKeyToSchema(schema, key, "boolean", type);
129
+ }
130
+ return schema;
131
+ }
132
+ function buildNodeSchemas(requiredTypes) {
133
+ const schemas = {};
134
+ for (const [type, definition] of Object.entries(NODE_SCHEMA_DEFS)) {
135
+ schemas[type] = buildSchema(type, definition);
136
+ }
137
+ if (requiredTypes) {
138
+ const required = Array.from(requiredTypes, (value) => value.toLowerCase());
139
+ const missing = required.filter((value) => !schemas[value]);
140
+ if (missing.length > 0) {
141
+ throw new Error(`template schema missing for type(s): ${missing.join(", ")}`);
142
+ }
143
+ }
144
+ return schemas;
145
+ }
146
+ function getNodeSchema(type) {
147
+ return buildNodeSchemas()[type.toLowerCase()];
148
+ }
149
+ function schemaAllowsKey(type, key) {
150
+ const schema = getNodeSchema(type);
151
+ return Boolean(schema?.allowedKeys.has(key));
152
+ }
153
+ function validateTemplateAgainstSchema(frontmatter, schema, filePath) {
154
+ for (const [key, value] of Object.entries(frontmatter)) {
155
+ const expected = schema.keyKinds[key];
156
+ if (!expected) {
157
+ throw new Error(`template key not allowed for ${schema.type}: ${key} (${filePath})`);
158
+ }
159
+ const kind = getValueKind(value);
160
+ if (expected !== kind) {
161
+ throw new Error(`template schema mismatch for ${schema.type}.${key}: expected ${expected} but found ${kind} (${filePath})`);
162
+ }
163
+ }
164
+ }
47
165
  function loadTemplateSchemas(root, config, requiredTypes) {
166
+ const schemas = buildNodeSchemas(requiredTypes);
48
167
  const templateRoot = path_1.default.resolve(root, config.templates.root_path, config.templates.default_set);
49
168
  const files = listMarkdownFiles(templateRoot);
50
169
  if (files.length === 0) {
51
170
  throw new Error(`no templates found at ${templateRoot}`);
52
171
  }
53
- const schemas = {};
172
+ const discoveredTypes = new Set();
54
173
  for (const filePath of files) {
55
174
  const content = fs_1.default.readFileSync(filePath, "utf8");
56
175
  const { frontmatter } = (0, frontmatter_1.parseFrontmatter)(content, filePath);
@@ -62,22 +181,16 @@ function loadTemplateSchemas(root, config, requiredTypes) {
62
181
  if (normalizedType !== typeValue) {
63
182
  throw new Error(`template type must be lowercase in ${filePath}`);
64
183
  }
65
- const schema = schemas[normalizedType] ??
66
- {
67
- type: normalizedType,
68
- allowedKeys: new Set(),
69
- keyKinds: {},
70
- listKeys: new Set(),
71
- };
72
- schemas[normalizedType] = schema;
73
- for (const [key, value] of Object.entries(frontmatter)) {
74
- const kind = getValueKind(value);
75
- addKeyToSchema(schema, key, kind, filePath);
184
+ const schema = schemas[normalizedType];
185
+ if (!schema) {
186
+ throw new Error(`template schema missing for type ${normalizedType}`);
76
187
  }
188
+ validateTemplateAgainstSchema(frontmatter, schema, filePath);
189
+ discoveredTypes.add(normalizedType);
77
190
  }
78
191
  if (requiredTypes) {
79
192
  const required = Array.from(requiredTypes, (value) => value.toLowerCase());
80
- const missing = required.filter((value) => !schemas[value]);
193
+ const missing = required.filter((value) => !discoveredTypes.has(value));
81
194
  if (missing.length > 0) {
82
195
  throw new Error(`template schema missing for type(s): ${missing.join(", ")}`);
83
196
  }
@@ -41,7 +41,15 @@ Conventions:
41
41
  - mdkg does not execute skill scripts; runtimes decide when and whether to do that.
42
42
  - Prefer packs over ad-hoc file lists.
43
43
  - Prefer task/event commands for structured work-state changes and use markdown edits for narrative/body updates.
44
+ - `skills` is valid metadata on all work items (`epic`, `feat`, `task`, `bug`, `checkpoint`, `test`), but `mdkg task ...` still mutates only `task`, `bug`, and `test`.
45
+ - Treat mdkg-generated work items as schema-consistent by default: `mdkg new`, `mdkg validate`, and `mdkg task ...` should not require manual frontmatter repair.
44
46
  - Use `mdkg task done <id> --checkpoint "<title>"` for milestone compression, not every routine task completion.
45
47
  - Prefer checkpoints for feat/epic closeout summaries; parent status edits remain manual.
46
48
  - If `events.jsonl` is missing, `mdkg task start` and `mdkg task done` will remind you how to recreate it.
47
49
  - Files outside mdkg-managed skill mirrors, such as local tool permission files, are not managed by mdkg unless documented explicitly.
50
+
51
+ Structured discovery/show exports:
52
+ - `--json`
53
+ - `--xml`
54
+ - `--toon`
55
+ - `--md`
@@ -21,9 +21,16 @@ Agent bootstrap:
21
21
  - `mdkg init --llm`
22
22
  - `mdkg init --agent`
23
23
  - `mdkg init --llm --agent`
24
+ - published bootstrap config is root-only by default
24
25
 
25
26
  Skill discovery:
26
27
  - `mdkg skill list --tags stage:plan --json`
27
28
  - `mdkg skill search "<query>" --json`
28
29
  - `mdkg skill show <slug> --json`
29
30
  - `mdkg skill sync`
31
+
32
+ Discovery/show export flags:
33
+ - `--json`
34
+ - `--xml`
35
+ - `--toon`
36
+ - `--md`
@@ -52,16 +52,6 @@
52
52
  "path": ".",
53
53
  "enabled": true,
54
54
  "mdkg_dir": ".mdkg"
55
- },
56
- "polish": {
57
- "path": "polish",
58
- "enabled": true,
59
- "mdkg_dir": ".mdkg"
60
- },
61
- "smoke": {
62
- "path": "smoke",
63
- "enabled": true,
64
- "mdkg_dir": ".mdkg"
65
55
  }
66
56
  }
67
- }
57
+ }
@@ -88,7 +88,7 @@ If a user provides an unqualified ID and it is ambiguous globally:
88
88
  - Commands should be script-friendly:
89
89
  - concise outputs for single items
90
90
  - predictable formatting
91
- - `--json` output for supported discovery/show commands
91
+ - `--json`, `--xml`, `--toon`, and `--md` output for supported discovery/show commands
92
92
  - when printing node summaries (e.g., `show`/list results), outputs SHOULD surface key searchable frontmatter fields such as `links` and `artifacts`
93
93
 
94
94
  ## Command set (v1 target)
@@ -164,18 +164,19 @@ Common flags:
164
164
  - `--template <set>` (default from config)
165
165
 
166
166
  ### Read/search
167
- - `mdkg show <id-or-qid> [--meta] [--json]`
167
+ - `mdkg show <id-or-qid> [--meta] [--json|--xml|--toon|--md]`
168
168
  - default behavior shows the full node body
169
169
  - `--meta` is the compact card-only view
170
- - `mdkg search "<query>" [--type <type>] [--status <status>] [--ws <alias>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json]`
170
+ - `mdkg search "<query>" [--type <type>] [--status <status>] [--ws <alias>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
171
171
  - search SHOULD match on IDs, titles, tags, path tokens, and searchable frontmatter lists (`links`, `artifacts`, `refs`, `aliases`)
172
- - `mdkg list [--type <type>] [--status <status>] [--ws <alias>] [--epic <id>] [--blocked] [--priority <n>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json]`
172
+ - `mdkg list [--type <type>] [--status <status>] [--ws <alias>] [--epic <id>] [--blocked] [--priority <n>] [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
173
173
  - skills are first-class under `mdkg skill ...` only:
174
- - `mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json]`
175
- - `mdkg skill show <slug> [--meta] [--json]`
176
- - `mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json]`
174
+ - `mdkg skill list [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
175
+ - `mdkg skill show <slug> [--meta] [--json|--xml|--toon|--md]`
176
+ - `mdkg skill search "<query>" [--tags <tag,tag,...>] [--tags-mode any|all] [--json|--xml|--toon|--md]`
177
177
  - `mdkg skill validate [<slug>]`
178
178
  - `mdkg skill sync [--force]`
179
+ - discovery/show output flags are mutually exclusive; text mode remains the default when none are supplied
179
180
 
180
181
  ### Task lifecycle mutation
181
182
  - `mdkg task start <id-or-qid> [--ws <alias>] [--run-id <id>] [--note "<text>"]`
@@ -95,7 +95,9 @@ Work items (`epic/feat/task/bug/checkpoint/test`):
95
95
  - `status` (enum)
96
96
  - optional `priority` (0..9)
97
97
  - optional `skills: [slug, ...]` (kebab-case skill slugs)
98
- - optional graph edges: `epic`, `parent`, `relates`, `blocked_by`, `blocks`, `prev`, `next`
98
+ - optional graph fields are type-specific by the shared schema/templates
99
+ - `relates`, `blocked_by`, and `blocks` are common work-item list fields
100
+ - `epic`, `parent`, `prev`, and `next` are only valid on the work-item types whose templates/schema include them
99
101
 
100
102
  Decision records (`dec-*`):
101
103
  - `status` (enum: `proposed`, `accepted`, `rejected`, `superseded`)
@@ -15,3 +15,5 @@ First commands:
15
15
  - `mdkg pack <id>`
16
16
  - `mdkg skill list --tags stage:plan --json`
17
17
  - `mdkg validate`
18
+
19
+ Discovery and show commands also support `--xml`, `--toon`, and `--md` when JSON is not the desired interchange format.
@@ -48,8 +48,8 @@ function renderTokenValue(value) {
48
48
  }
49
49
  return value;
50
50
  }
51
- function renderTemplate(template, context) {
52
- const allowedKeys = new Set(Object.keys(template.frontmatter));
51
+ function renderTemplate(template, context, allowedKeysInput) {
52
+ const allowedKeys = new Set(allowedKeysInput ?? Object.keys(template.frontmatter));
53
53
  const rendered = {};
54
54
  for (const [key, value] of Object.entries(template.frontmatter)) {
55
55
  if (isTokenPlaceholder(value)) {
@@ -79,6 +79,9 @@ const BOOLEAN_FLAGS = new Set([
79
79
  "--version",
80
80
  "--dry-run",
81
81
  "--json",
82
+ "--xml",
83
+ "--toon",
84
+ "--md",
82
85
  "--list-profiles",
83
86
  "--with-scripts",
84
87
  "--clear-blocked-by",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mdkg",
3
- "version": "0.0.6",
3
+ "version": "0.0.8",
4
4
  "description": "Markdown Knowledge Graph",
5
5
  "license": "MIT",
6
6
  "bin": {