mdkg 0.0.3 → 0.0.5

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 (52) hide show
  1. package/README.md +181 -169
  2. package/dist/cli.js +1002 -624
  3. package/dist/commands/checkpoint.js +17 -6
  4. package/dist/commands/event.js +46 -0
  5. package/dist/commands/event_support.js +146 -0
  6. package/dist/commands/format.js +6 -7
  7. package/dist/commands/index.js +10 -4
  8. package/dist/commands/init.js +224 -16
  9. package/dist/commands/list.js +18 -1
  10. package/dist/commands/new.js +30 -5
  11. package/dist/commands/pack.js +194 -2
  12. package/dist/commands/query_output.js +84 -0
  13. package/dist/commands/search.js +22 -5
  14. package/dist/commands/show.js +26 -11
  15. package/dist/commands/skill.js +374 -0
  16. package/dist/commands/skill_mirror.js +290 -0
  17. package/dist/commands/skill_support.js +122 -0
  18. package/dist/commands/task.js +278 -0
  19. package/dist/commands/validate.js +106 -7
  20. package/dist/graph/edges.js +2 -2
  21. package/dist/graph/frontmatter.js +1 -0
  22. package/dist/graph/indexer.js +21 -0
  23. package/dist/graph/node.js +20 -4
  24. package/dist/graph/skills_index_cache.js +94 -0
  25. package/dist/graph/skills_indexer.js +160 -0
  26. package/dist/init/AGENTS.md +6 -41
  27. package/dist/init/AGENT_START.md +46 -0
  28. package/dist/init/CLAUDE.md +6 -35
  29. package/dist/init/CLI_COMMAND_MATRIX.md +29 -0
  30. package/dist/init/README.md +6 -4
  31. package/dist/init/core/HUMAN.md +36 -0
  32. package/dist/init/core/SOUL.md +257 -0
  33. package/dist/init/core/core.md +3 -1
  34. package/dist/init/core/rule-1-mdkg-conventions.md +9 -2
  35. package/dist/init/core/rule-3-cli-contract.md +81 -14
  36. package/dist/init/core/rule-4-repo-safety-and-ignores.md +9 -3
  37. package/dist/init/core/rule-6-templates-and-schemas.md +6 -2
  38. package/dist/init/llms.txt +17 -0
  39. package/dist/init/skills/SKILL.md.example +41 -0
  40. package/dist/init/templates/default/bug.md +1 -0
  41. package/dist/init/templates/default/chk.md +1 -0
  42. package/dist/init/templates/default/epic.md +1 -0
  43. package/dist/init/templates/default/feat.md +1 -0
  44. package/dist/init/templates/default/task.md +1 -0
  45. package/dist/init/templates/default/test.md +1 -0
  46. package/dist/pack/export_md.js +6 -0
  47. package/dist/pack/export_xml.js +6 -0
  48. package/dist/pack/pack.js +35 -0
  49. package/dist/util/argparse.js +36 -5
  50. package/dist/util/filter.js +18 -0
  51. package/dist/util/id.js +23 -0
  52. package/package.json +5 -2
@@ -11,8 +11,8 @@ const index_cache_1 = require("../graph/index_cache");
11
11
  const loader_1 = require("../templates/loader");
12
12
  const date_1 = require("../util/date");
13
13
  const errors_1 = require("../util/errors");
14
- const ID_RE = /^[a-z]+-[0-9]+$/;
15
- const ID_REF_RE = /^([a-z][a-z0-9_]*:)?[a-z]+-[0-9]+$/;
14
+ const id_1 = require("../util/id");
15
+ const event_support_1 = require("./event_support");
16
16
  function parseCsvList(raw) {
17
17
  if (!raw) {
18
18
  return [];
@@ -24,14 +24,14 @@ function parseCsvList(raw) {
24
24
  }
25
25
  function normalizeId(value, key) {
26
26
  const normalized = value.toLowerCase();
27
- if (!ID_RE.test(normalized) && normalized !== "rule-guide") {
28
- throw new errors_1.UsageError(`${key} entries must match <prefix>-<number>: ${value}`);
27
+ if (!(0, id_1.isCanonicalId)(normalized)) {
28
+ throw new errors_1.UsageError(`${key} entries must match <prefix>-<number> or reserved id: ${value}`);
29
29
  }
30
30
  return normalized;
31
31
  }
32
32
  function normalizeIdRef(value, key) {
33
33
  const normalized = value.toLowerCase();
34
- if (!ID_REF_RE.test(normalized)) {
34
+ if (!(0, id_1.isCanonicalIdRef)(normalized)) {
35
35
  throw new errors_1.UsageError(`${key} entries must match <id> or <ws>:<id>: ${value}`);
36
36
  }
37
37
  return normalized;
@@ -115,7 +115,8 @@ function runCheckpointNewCommand(options) {
115
115
  }
116
116
  }
117
117
  const scope = parseCsvList(options.scope).map((value) => normalizeId(value, "--scope"));
118
- const today = (0, date_1.formatDate)(new Date());
118
+ const now = options.now ?? new Date();
119
+ const today = (0, date_1.formatDate)(now);
119
120
  const template = (0, loader_1.loadTemplate)(options.root, config, "checkpoint", options.template);
120
121
  const content = (0, loader_1.renderTemplate)(template, {
121
122
  id,
@@ -129,5 +130,15 @@ function runCheckpointNewCommand(options) {
129
130
  });
130
131
  fs_1.default.mkdirSync(workDir, { recursive: true });
131
132
  fs_1.default.writeFileSync(filePath, content, "utf8");
133
+ (0, event_support_1.appendAutomaticEvent)({
134
+ root: options.root,
135
+ ws,
136
+ kind: "CHECKPOINT_CREATED",
137
+ status: "ok",
138
+ refs: [id],
139
+ notes: options.note ?? `checkpoint created via mdkg checkpoint new`,
140
+ runId: options.runId,
141
+ now,
142
+ });
132
143
  console.log(`checkpoint created: ${ws}:${id} (${path_1.default.relative(options.root, filePath)})`);
133
144
  }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.runEventEnableCommand = runEventEnableCommand;
4
+ exports.runEventAppendCommand = runEventAppendCommand;
5
+ const event_support_1 = require("./event_support");
6
+ const errors_1 = require("../util/errors");
7
+ function runEventEnableCommand(options) {
8
+ const result = (0, event_support_1.ensureEventsEnabled)(options);
9
+ const createdLabel = result.created ? "created" : "already present";
10
+ const ignoreLabel = result.gitignoreUpdated ? "updated .gitignore" : "left .gitignore unchanged";
11
+ console.log(`event logging enabled: ${result.ws} (${createdLabel}; ${ignoreLabel})`);
12
+ }
13
+ function normalizeEventStatus(value) {
14
+ const normalized = value.trim().toLowerCase();
15
+ if (normalized === "ok" ||
16
+ normalized === "error" ||
17
+ normalized === "retry" ||
18
+ normalized === "skipped") {
19
+ return normalized;
20
+ }
21
+ throw new errors_1.UsageError("--status must be one of ok, error, retry, skipped");
22
+ }
23
+ function runEventAppendCommand(options) {
24
+ const kind = options.kind.trim();
25
+ if (!kind) {
26
+ throw new errors_1.UsageError("--kind is required");
27
+ }
28
+ const refs = (0, event_support_1.normalizeEventRefList)(options.refs);
29
+ if (refs.length === 0) {
30
+ throw new errors_1.UsageError("--refs requires at least one id or qid");
31
+ }
32
+ const record = (0, event_support_1.appendEvent)({
33
+ root: options.root,
34
+ ws: options.ws,
35
+ kind,
36
+ status: normalizeEventStatus(options.status),
37
+ refs,
38
+ artifacts: (0, event_support_1.normalizeEventStringList)(options.artifacts),
39
+ notes: options.notes,
40
+ runId: options.runId,
41
+ agent: options.agent,
42
+ skill: options.skill,
43
+ tool: options.tool,
44
+ });
45
+ console.log(`event appended: ${record.workspace}:${record.kind} (${record.run_id})`);
46
+ }
@@ -0,0 +1,146 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.normalizeWorkspaceForEvents = normalizeWorkspaceForEvents;
7
+ exports.resolveEventsPath = resolveEventsPath;
8
+ exports.createLocalRunId = createLocalRunId;
9
+ exports.isEventLoggingEnabled = isEventLoggingEnabled;
10
+ exports.ensureEventsEnabled = ensureEventsEnabled;
11
+ exports.normalizeEventRefList = normalizeEventRefList;
12
+ exports.normalizeEventStringList = normalizeEventStringList;
13
+ exports.appendEvent = appendEvent;
14
+ exports.appendAutomaticEvent = appendAutomaticEvent;
15
+ const fs_1 = __importDefault(require("fs"));
16
+ const path_1 = __importDefault(require("path"));
17
+ const config_1 = require("../core/config");
18
+ const errors_1 = require("../util/errors");
19
+ function appendIgnoreEntries(filePath, entries) {
20
+ const normalizedEntries = entries.map((entry) => entry.trim()).filter(Boolean);
21
+ if (normalizedEntries.length === 0) {
22
+ return false;
23
+ }
24
+ let existing = "";
25
+ if (fs_1.default.existsSync(filePath)) {
26
+ existing = fs_1.default.readFileSync(filePath, "utf8");
27
+ }
28
+ const existingLines = new Set(existing
29
+ .split(/\r?\n/)
30
+ .map((line) => line.trim())
31
+ .filter(Boolean));
32
+ const additions = normalizedEntries.filter((entry) => !existingLines.has(entry));
33
+ if (additions.length === 0) {
34
+ return false;
35
+ }
36
+ const next = existing.length === 0 ? `${additions.join("\n")}\n` : `${existing.replace(/\s*$/, "\n")}${additions.join("\n")}\n`;
37
+ fs_1.default.writeFileSync(filePath, next, "utf8");
38
+ return true;
39
+ }
40
+ function normalizeWorkspaceForEvents(config, raw) {
41
+ const normalized = (raw ?? "root").toLowerCase();
42
+ if (normalized === "all") {
43
+ throw new errors_1.UsageError("--ws all is not valid here");
44
+ }
45
+ if (!config.workspaces[normalized]) {
46
+ throw new errors_1.NotFoundError(`workspace not found: ${normalized}`);
47
+ }
48
+ return normalized;
49
+ }
50
+ function resolveEventsPath(root, config, ws = "root") {
51
+ const entry = config.workspaces[ws];
52
+ if (!entry) {
53
+ throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
54
+ }
55
+ return path_1.default.resolve(root, entry.path, entry.mdkg_dir, "work", "events", "events.jsonl");
56
+ }
57
+ function formatRunIdTimestamp(now) {
58
+ const iso = now.toISOString().replace(/[-:]/g, "").replace(/\./g, "").replace("Z", "Z");
59
+ return iso;
60
+ }
61
+ function createLocalRunId(kind, now = new Date()) {
62
+ const normalizedKind = kind.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
63
+ return `run_mdkg_cli_${normalizedKind}_${formatRunIdTimestamp(now)}`;
64
+ }
65
+ function isEventLoggingEnabled(root, config, ws) {
66
+ const normalizedWs = normalizeWorkspaceForEvents(config, ws);
67
+ return fs_1.default.existsSync(resolveEventsPath(root, config, normalizedWs));
68
+ }
69
+ function ensureEventsEnabled(options) {
70
+ const config = (0, config_1.loadConfig)(options.root);
71
+ const ws = normalizeWorkspaceForEvents(config, options.ws);
72
+ const eventsPath = resolveEventsPath(options.root, config, ws);
73
+ const updateGitignore = options.updateGitignore !== false;
74
+ let created = false;
75
+ if (!fs_1.default.existsSync(eventsPath)) {
76
+ fs_1.default.mkdirSync(path_1.default.dirname(eventsPath), { recursive: true });
77
+ fs_1.default.writeFileSync(eventsPath, "", "utf8");
78
+ created = true;
79
+ }
80
+ let gitignoreUpdated = false;
81
+ if (updateGitignore) {
82
+ gitignoreUpdated = appendIgnoreEntries(path_1.default.join(options.root, ".gitignore"), [
83
+ ".mdkg/work/events/*.jsonl",
84
+ ]);
85
+ }
86
+ return { ws, eventsPath, created, gitignoreUpdated };
87
+ }
88
+ function normalizeIdOrIdRef(value) {
89
+ return value.trim().toLowerCase();
90
+ }
91
+ function normalizeEventRefList(raw) {
92
+ if (!raw) {
93
+ return [];
94
+ }
95
+ return raw
96
+ .split(",")
97
+ .map((value) => normalizeIdOrIdRef(value))
98
+ .filter(Boolean);
99
+ }
100
+ function normalizeEventStringList(raw) {
101
+ if (!raw) {
102
+ return [];
103
+ }
104
+ return raw
105
+ .split(",")
106
+ .map((value) => value.trim())
107
+ .filter(Boolean);
108
+ }
109
+ function buildEventRecord(config, options) {
110
+ const now = options.now ?? new Date();
111
+ const ws = normalizeWorkspaceForEvents(config, options.ws);
112
+ return {
113
+ ts: now.toISOString(),
114
+ run_id: options.runId?.trim() || createLocalRunId(options.kind, now),
115
+ workspace: ws,
116
+ agent: options.agent?.trim() || "mdkg-cli",
117
+ kind: options.kind,
118
+ status: options.status,
119
+ refs: options.refs.map(normalizeIdOrIdRef).filter(Boolean),
120
+ artifacts: (options.artifacts ?? []).map((value) => value.trim()).filter(Boolean),
121
+ notes: options.notes?.trim() ?? "",
122
+ ...(options.skill ? { skill: options.skill.trim().toLowerCase() } : {}),
123
+ ...(options.tool ? { tool: options.tool.trim() } : {}),
124
+ };
125
+ }
126
+ function appendEvent(options) {
127
+ const config = (0, config_1.loadConfig)(options.root);
128
+ const record = buildEventRecord(config, options);
129
+ if (record.refs.length === 0) {
130
+ throw new errors_1.UsageError("--refs requires at least one id or qid");
131
+ }
132
+ const eventsPath = resolveEventsPath(options.root, config, record.workspace);
133
+ if (!fs_1.default.existsSync(eventsPath)) {
134
+ throw new errors_1.NotFoundError(`event logging is not enabled for workspace ${record.workspace}; run \`mdkg event enable --ws ${record.workspace}\``);
135
+ }
136
+ fs_1.default.appendFileSync(eventsPath, `${JSON.stringify(record)}\n`, "utf8");
137
+ return record;
138
+ }
139
+ function appendAutomaticEvent(options) {
140
+ const config = (0, config_1.loadConfig)(options.root);
141
+ const ws = normalizeWorkspaceForEvents(config, options.ws);
142
+ if (!isEventLoggingEnabled(options.root, config, ws)) {
143
+ return undefined;
144
+ }
145
+ return appendEvent({ ...options, ws });
146
+ }
@@ -13,8 +13,7 @@ const node_1 = require("../graph/node");
13
13
  const workspace_files_1 = require("../graph/workspace_files");
14
14
  const errors_1 = require("../util/errors");
15
15
  const date_1 = require("../util/date");
16
- const ID_RE = /^[a-z]+-[0-9]+$/;
17
- const ID_REF_RE = /^([a-z][a-z0-9_]*:)?[a-z]+-[0-9]+$/;
16
+ const id_1 = require("../util/id");
18
17
  const DEC_ID_RE = /^dec-[0-9]+$/;
19
18
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
20
19
  const ID_LIST_KEYS = new Set(["refs", "scope"]);
@@ -22,7 +21,7 @@ const ID_REF_LIST_KEYS = new Set(["relates", "blocked_by", "blocks"]);
22
21
  const ID_REF_SCALAR_KEYS = new Set(["epic", "parent", "prev", "next"]);
23
22
  const PRESERVE_CASE_LIST_KEYS = new Set(["links", "artifacts"]);
24
23
  function isValidId(value) {
25
- return ID_RE.test(value) || value === "rule-guide";
24
+ return (0, id_1.isCanonicalId)(value);
26
25
  }
27
26
  function isCoreListFile(filePath) {
28
27
  return path_1.default.basename(filePath) === "core.md" && path_1.default.basename(path_1.default.dirname(filePath)) === "core";
@@ -59,9 +58,9 @@ function normalizeList(values, key, errors, filePath) {
59
58
  continue;
60
59
  }
61
60
  if (ID_LIST_KEYS.has(key) && !isValidId(entry)) {
62
- errors.push(`${filePath}: ${key} entries must match <prefix>-<number>`);
61
+ errors.push(`${filePath}: ${key} entries must match <prefix>-<number> or reserved id`);
63
62
  }
64
- if (ID_REF_LIST_KEYS.has(key) && !ID_REF_RE.test(entry)) {
63
+ if (ID_REF_LIST_KEYS.has(key) && !(0, id_1.isCanonicalIdRef)(entry)) {
65
64
  errors.push(`${filePath}: ${key} entries must be valid id references`);
66
65
  }
67
66
  }
@@ -69,7 +68,7 @@ function normalizeList(values, key, errors, filePath) {
69
68
  }
70
69
  function normalizeIdRef(value, key, errors, filePath) {
71
70
  const normalized = normalizeScalar(value).toLowerCase();
72
- if (!ID_REF_RE.test(normalized)) {
71
+ if (!(0, id_1.isCanonicalIdRef)(normalized)) {
73
72
  errors.push(`${filePath}: ${key} must be a valid id reference`);
74
73
  }
75
74
  return normalized;
@@ -124,7 +123,7 @@ function normalizeFrontmatter(frontmatter, schema, type, workStatusEnum, priorit
124
123
  else {
125
124
  const normalizedId = idValue.toLowerCase();
126
125
  if (!isValidId(normalizedId)) {
127
- errors.push(`${filePath}: id must match <prefix>-<number>`);
126
+ errors.push(`${filePath}: id must match <prefix>-<number> or reserved id`);
128
127
  }
129
128
  normalized.id = normalizedId;
130
129
  }
@@ -8,10 +8,16 @@ const path_1 = __importDefault(require("path"));
8
8
  const config_1 = require("../core/config");
9
9
  const indexer_1 = require("../graph/indexer");
10
10
  const index_cache_1 = require("../graph/index_cache");
11
+ const skills_index_cache_1 = require("../graph/skills_index_cache");
12
+ const skills_indexer_1 = require("../graph/skills_indexer");
11
13
  function runIndexCommand(options) {
12
14
  const config = (0, config_1.loadConfig)(options.root);
13
- const index = (0, indexer_1.buildIndex)(options.root, config, { tolerant: options.tolerant });
14
- const outputPath = path_1.default.resolve(options.root, config.index.global_index_path);
15
- (0, index_cache_1.writeIndex)(outputPath, index);
16
- console.log(`index written: ${path_1.default.relative(options.root, outputPath)}`);
15
+ const nodeIndex = (0, indexer_1.buildIndex)(options.root, config, { tolerant: options.tolerant });
16
+ const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(options.root, config);
17
+ const nodesOutputPath = path_1.default.resolve(options.root, config.index.global_index_path);
18
+ const skillsOutputPath = (0, skills_indexer_1.resolveSkillsIndexPath)(options.root);
19
+ (0, index_cache_1.writeIndex)(nodesOutputPath, nodeIndex);
20
+ (0, skills_index_cache_1.writeSkillsIndex)(skillsOutputPath, skillsIndex);
21
+ console.log(`index written: ${path_1.default.relative(options.root, nodesOutputPath)}`);
22
+ console.log(`skills index written: ${path_1.default.relative(options.root, skillsOutputPath)}`);
17
23
  }
@@ -6,8 +6,20 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.runInitCommand = runInitCommand;
7
7
  const fs_1 = __importDefault(require("fs"));
8
8
  const path_1 = __importDefault(require("path"));
9
+ const config_1 = require("../core/config");
9
10
  const errors_1 = require("../util/errors");
11
+ const date_1 = require("../util/date");
12
+ const skill_support_1 = require("./skill_support");
13
+ const skill_mirror_1 = require("./skill_mirror");
10
14
  const DEFAULT_SEED_SUBDIR = path_1.default.resolve(__dirname, "..", "init");
15
+ const SOUL_PIN_ID = "rule-soul";
16
+ const HUMAN_PIN_ID = "rule-human";
17
+ const DEFAULT_CORE_LIST_HEADER = [
18
+ "# mdkg verbose core list",
19
+ "",
20
+ "# One node ID per line. Lines starting with # are comments.",
21
+ "# This list is included by `mdkg pack --verbose`.",
22
+ ];
11
23
  function listFiles(dir) {
12
24
  if (!fs_1.default.existsSync(dir)) {
13
25
  return [];
@@ -59,16 +71,169 @@ function appendIgnoreEntries(filePath, entries) {
59
71
  fs_1.default.writeFileSync(filePath, updated, "utf8");
60
72
  return true;
61
73
  }
74
+ function writeFileIfMissing(filePath, content, force, stats) {
75
+ if (fs_1.default.existsSync(filePath) && !force) {
76
+ stats.skipped += 1;
77
+ return;
78
+ }
79
+ fs_1.default.mkdirSync(path_1.default.dirname(filePath), { recursive: true });
80
+ fs_1.default.writeFileSync(filePath, content, "utf8");
81
+ stats.created += 1;
82
+ }
83
+ function soulTemplate(created) {
84
+ return [
85
+ "---",
86
+ `id: ${SOUL_PIN_ID}`,
87
+ "type: rule",
88
+ "title: agent soul and execution contract",
89
+ "tags: [agent, constraints]",
90
+ "owners: []",
91
+ "links: []",
92
+ "artifacts: []",
93
+ "relates: []",
94
+ "refs: []",
95
+ "aliases: [soul]",
96
+ `created: ${created}`,
97
+ `updated: ${created}`,
98
+ "---",
99
+ "",
100
+ "# Purpose",
101
+ "",
102
+ "Define the canonical agent execution boundaries and memory contract for this repository.",
103
+ "",
104
+ "# Scope",
105
+ "",
106
+ "Applies to all orchestrators and coding-agent executions using mdkg in this repo.",
107
+ "",
108
+ "# Requirements",
109
+ "",
110
+ "- Ask for approval before destructive operations or policy-sensitive actions.",
111
+ "- Prefer deterministic mdkg packs over ad-hoc context assembly.",
112
+ "- Follow single-writer commit discipline and event-driven batching.",
113
+ "- Never place secrets in mdkg docs or generated packs.",
114
+ "",
115
+ "# Notes",
116
+ "",
117
+ "Customize this file to encode repo-specific constraints, approval boundaries, and memory cadence.",
118
+ "",
119
+ ].join("\n");
120
+ }
121
+ function humanTemplate(created) {
122
+ return [
123
+ "---",
124
+ `id: ${HUMAN_PIN_ID}`,
125
+ "type: rule",
126
+ "title: human working profile and collaboration preferences",
127
+ "tags: [human, collaboration, preferences]",
128
+ "owners: []",
129
+ "links: []",
130
+ "artifacts: []",
131
+ "relates: []",
132
+ "refs: []",
133
+ "aliases: [human]",
134
+ `created: ${created}`,
135
+ `updated: ${created}`,
136
+ "---",
137
+ "",
138
+ "# Purpose",
139
+ "",
140
+ "Capture stable collaboration preferences and boundaries so agents can work with less ambiguity.",
141
+ "",
142
+ "# Scope",
143
+ "",
144
+ "Applies to planning, implementation, and review interactions in this repository.",
145
+ "",
146
+ "# Requirements",
147
+ "",
148
+ "- Keep top goals, boundaries, and style preferences current.",
149
+ "- Include ask-before-doing constraints for risky or high-impact actions.",
150
+ "- Record preferred environment assumptions and validation commands.",
151
+ "",
152
+ "# Notes",
153
+ "",
154
+ "Suggested prompts:",
155
+ "- What are your top 3 goals in this repo right now?",
156
+ "- What should never happen without confirmation?",
157
+ "- What coding/review style should the agent prefer?",
158
+ "- What OS/runtime/test commands should be assumed?",
159
+ "",
160
+ ].join("\n");
161
+ }
162
+ function seededInitEvent(nowIso) {
163
+ const event = {
164
+ ts: nowIso,
165
+ run_id: `init-${nowIso.replace(/[^0-9]/g, "").slice(0, 14)}`,
166
+ workspace: "root",
167
+ agent: "mdkg",
168
+ kind: "RUN_STARTED",
169
+ status: "ok",
170
+ refs: ["edd-4"],
171
+ artifacts: [],
172
+ notes: "init agent scaffold target initialized",
173
+ redacted: true,
174
+ };
175
+ return `${JSON.stringify(event)}\n`;
176
+ }
177
+ function parseCoreList(raw) {
178
+ const lines = raw.split(/\r?\n/);
179
+ const header = [];
180
+ const ids = [];
181
+ let seenFirstId = false;
182
+ for (const line of lines) {
183
+ const trimmed = line.trim();
184
+ const isComment = trimmed.startsWith("#");
185
+ if (!seenFirstId && (trimmed.length === 0 || isComment)) {
186
+ header.push(line);
187
+ continue;
188
+ }
189
+ if (trimmed.length === 0 || isComment) {
190
+ seenFirstId = true;
191
+ continue;
192
+ }
193
+ seenFirstId = true;
194
+ ids.push(trimmed.toLowerCase());
195
+ }
196
+ return { header, ids };
197
+ }
198
+ function ensureCorePins(coreListPath, requiredPins) {
199
+ const raw = fs_1.default.existsSync(coreListPath) ? fs_1.default.readFileSync(coreListPath, "utf8") : "";
200
+ const { header, ids } = parseCoreList(raw);
201
+ const seen = new Set();
202
+ const dedupedExisting = [];
203
+ for (const id of ids) {
204
+ if (seen.has(id)) {
205
+ continue;
206
+ }
207
+ seen.add(id);
208
+ dedupedExisting.push(id);
209
+ }
210
+ const required = requiredPins.map((value) => value.toLowerCase());
211
+ const filteredExisting = dedupedExisting.filter((value) => !required.includes(value));
212
+ const finalIds = [...required, ...filteredExisting];
213
+ const headerLines = header.length > 0 ? header : DEFAULT_CORE_LIST_HEADER;
214
+ const normalizedHeader = headerLines.slice();
215
+ while (normalizedHeader.length > 0 && normalizedHeader[normalizedHeader.length - 1].trim() === "") {
216
+ normalizedHeader.pop();
217
+ }
218
+ const output = [...normalizedHeader, "", ...finalIds, ""].join("\n");
219
+ fs_1.default.mkdirSync(path_1.default.dirname(coreListPath), { recursive: true });
220
+ fs_1.default.writeFileSync(coreListPath, output, "utf8");
221
+ }
62
222
  function runInitCommand(options) {
63
223
  const root = path_1.default.resolve(options.root);
64
224
  const seedRoot = options.seedRoot ? path_1.default.resolve(options.seedRoot) : DEFAULT_SEED_SUBDIR;
65
225
  const createAgents = Boolean(options.createAgents || options.createLlm);
66
226
  const createClaude = Boolean(options.createClaude || options.createLlm);
227
+ const createStartupDocs = Boolean(options.createLlm || options.agent);
228
+ const force = Boolean(options.force);
67
229
  const seedConfig = path_1.default.join(seedRoot, "config.json");
68
230
  const seedCore = path_1.default.join(seedRoot, "core");
69
231
  const seedTemplates = path_1.default.join(seedRoot, "templates");
70
232
  const seedAgents = path_1.default.join(seedRoot, "AGENTS.md");
71
233
  const seedClaude = path_1.default.join(seedRoot, "CLAUDE.md");
234
+ const seedLlms = path_1.default.join(seedRoot, "llms.txt");
235
+ const seedAgentStart = path_1.default.join(seedRoot, "AGENT_START.md");
236
+ const seedCliMatrix = path_1.default.join(seedRoot, "CLI_COMMAND_MATRIX.md");
72
237
  const seedReadme = path_1.default.join(seedRoot, "README.md");
73
238
  if (!fs_1.default.existsSync(seedConfig) || !fs_1.default.existsSync(seedCore) || !fs_1.default.existsSync(seedTemplates)) {
74
239
  throw new errors_1.NotFoundError(`init assets not found at ${seedRoot} (try reinstalling mdkg)`);
@@ -79,6 +244,15 @@ function runInitCommand(options) {
79
244
  if (createClaude && !fs_1.default.existsSync(seedClaude)) {
80
245
  throw new errors_1.NotFoundError(`init assets missing CLAUDE.md at ${seedRoot}`);
81
246
  }
247
+ if (createStartupDocs && !fs_1.default.existsSync(seedLlms)) {
248
+ throw new errors_1.NotFoundError(`init assets missing llms.txt at ${seedRoot}`);
249
+ }
250
+ if (createStartupDocs && !fs_1.default.existsSync(seedAgentStart)) {
251
+ throw new errors_1.NotFoundError(`init assets missing AGENT_START.md at ${seedRoot}`);
252
+ }
253
+ if (createStartupDocs && !fs_1.default.existsSync(seedCliMatrix)) {
254
+ throw new errors_1.NotFoundError(`init assets missing CLI_COMMAND_MATRIX.md at ${seedRoot}`);
255
+ }
82
256
  if (!fs_1.default.existsSync(seedReadme)) {
83
257
  throw new errors_1.NotFoundError(`init assets missing README.md at ${seedRoot}`);
84
258
  }
@@ -87,34 +261,68 @@ function runInitCommand(options) {
87
261
  fs_1.default.mkdirSync(path_1.default.join(mdkgDir, "work"), { recursive: true });
88
262
  fs_1.default.mkdirSync(path_1.default.join(mdkgDir, "design"), { recursive: true });
89
263
  const stats = { created: 0, skipped: 0 };
90
- copySeedFile(seedConfig, path_1.default.join(mdkgDir, "config.json"), Boolean(options.force), stats);
91
- copySeedFile(seedReadme, path_1.default.join(mdkgDir, "README.md"), Boolean(options.force), stats);
92
- copySeedDir(seedCore, path_1.default.join(mdkgDir, "core"), Boolean(options.force), stats);
93
- copySeedDir(seedTemplates, path_1.default.join(mdkgDir, "templates"), Boolean(options.force), stats);
264
+ copySeedFile(seedConfig, path_1.default.join(mdkgDir, "config.json"), force, stats);
265
+ copySeedFile(seedReadme, path_1.default.join(mdkgDir, "README.md"), force, stats);
266
+ copySeedDir(seedCore, path_1.default.join(mdkgDir, "core"), force, stats);
267
+ copySeedDir(seedTemplates, path_1.default.join(mdkgDir, "templates"), force, stats);
94
268
  if (createAgents) {
95
- copySeedFile(seedAgents, path_1.default.join(root, "AGENTS.md"), Boolean(options.force), stats);
269
+ copySeedFile(seedAgents, path_1.default.join(root, "AGENTS.md"), force, stats);
96
270
  }
97
271
  if (createClaude) {
98
- copySeedFile(seedClaude, path_1.default.join(root, "CLAUDE.md"), Boolean(options.force), stats);
272
+ copySeedFile(seedClaude, path_1.default.join(root, "CLAUDE.md"), force, stats);
273
+ }
274
+ if (createStartupDocs) {
275
+ copySeedFile(seedLlms, path_1.default.join(root, "llms.txt"), force, stats);
276
+ copySeedFile(seedAgentStart, path_1.default.join(root, "AGENT_START.md"), force, stats);
277
+ copySeedFile(seedCliMatrix, path_1.default.join(root, "CLI_COMMAND_MATRIX.md"), force, stats);
278
+ }
279
+ if (options.agent) {
280
+ const today = (0, date_1.formatDate)(new Date());
281
+ const soulPath = path_1.default.join(mdkgDir, "core", "SOUL.md");
282
+ const humanPath = path_1.default.join(mdkgDir, "core", "HUMAN.md");
283
+ const skillsDir = path_1.default.join(mdkgDir, "skills");
284
+ const registryPath = path_1.default.join(skillsDir, "registry.md");
285
+ const eventsDir = path_1.default.join(mdkgDir, "work", "events");
286
+ const eventsPath = path_1.default.join(eventsDir, "events.jsonl");
287
+ fs_1.default.mkdirSync(skillsDir, { recursive: true });
288
+ fs_1.default.mkdirSync(eventsDir, { recursive: true });
289
+ writeFileIfMissing(soulPath, soulTemplate(today), force, stats);
290
+ writeFileIfMissing(humanPath, humanTemplate(today), force, stats);
291
+ writeFileIfMissing(registryPath, (0, skill_support_1.registryTemplate)(), force, stats);
292
+ if (!fs_1.default.existsSync(eventsPath) || force) {
293
+ writeFileIfMissing(eventsPath, seededInitEvent(new Date().toISOString()), force, stats);
294
+ }
295
+ const coreListPath = path_1.default.join(mdkgDir, "core", "core.md");
296
+ ensureCorePins(coreListPath, [SOUL_PIN_ID, HUMAN_PIN_ID]);
297
+ (0, skill_mirror_1.scaffoldMirrorRoots)(root);
298
+ const config = (0, config_1.loadConfig)(root);
299
+ (0, skill_mirror_1.syncSkillMirrors)({ root, config, createRoots: true, force });
99
300
  }
100
- if (options.updateGitignore) {
101
- appendIgnoreEntries(path_1.default.join(root, ".gitignore"), [".mdkg/index/", ".mdkg/pack/"]);
301
+ const noUpdateIgnores = Boolean(options.noUpdateIgnores);
302
+ const shouldUpdateGitignore = Boolean(options.updateGitignore || !noUpdateIgnores);
303
+ const shouldUpdateNpmignore = Boolean(options.updateNpmignore || !noUpdateIgnores);
304
+ if (shouldUpdateGitignore) {
305
+ appendIgnoreEntries(path_1.default.join(root, ".gitignore"), [
306
+ ".mdkg/index/",
307
+ ".mdkg/pack/",
308
+ ".mdkg/work/events/*.jsonl",
309
+ ]);
102
310
  }
103
- if (options.updateNpmignore) {
311
+ if (shouldUpdateNpmignore) {
104
312
  appendIgnoreEntries(path_1.default.join(root, ".npmignore"), [".mdkg/", ".mdkg/index/", ".mdkg/pack/"]);
105
313
  }
106
314
  if (options.updateDockerignore) {
107
315
  appendIgnoreEntries(path_1.default.join(root, ".dockerignore"), [".mdkg/"]);
108
316
  }
109
- if (!options.updateGitignore && !options.updateNpmignore && !options.updateDockerignore) {
110
- console.error("warning: ignore files were not updated");
111
- console.error("hint: rerun `mdkg init --update-gitignore --update-npmignore` to avoid committing cache/pack artifacts");
112
- }
113
317
  console.log(`mdkg init complete: ${stats.created} file(s) created, ${stats.skipped} skipped`);
114
318
  console.log("next:");
115
- console.log(" mdkg index");
319
+ if (createStartupDocs) {
320
+ console.log(" read AGENT_START.md");
321
+ }
116
322
  console.log(' mdkg new task "..." --status todo --priority 1');
117
- console.log(" mdkg list --status todo");
118
- console.log(" mdkg pack <id> --verbose");
323
+ console.log(' mdkg search "..."');
324
+ console.log(" mdkg show <id>");
325
+ console.log(" mdkg next");
326
+ console.log(" mdkg pack <id>");
119
327
  console.log(" mdkg validate");
120
328
  }
@@ -8,6 +8,7 @@ const errors_1 = require("../util/errors");
8
8
  const qid_1 = require("../util/qid");
9
9
  const sort_1 = require("../util/sort");
10
10
  const node_card_1 = require("./node_card");
11
+ const query_output_1 = require("./query_output");
11
12
  function normalizeWorkspace(value) {
12
13
  if (!value || value === "all") {
13
14
  return undefined;
@@ -20,6 +21,10 @@ function runListCommand(options) {
20
21
  if (ws && !config.workspaces[ws]) {
21
22
  throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
22
23
  }
24
+ const normalizedType = options.type?.toLowerCase();
25
+ if (normalizedType === "skill") {
26
+ throw new errors_1.UsageError("--type skill is no longer supported here; use `mdkg skill list`");
27
+ }
23
28
  const { index, rebuilt, stale } = (0, index_cache_1.loadIndex)({
24
29
  root: options.root,
25
30
  config,
@@ -39,13 +44,25 @@ function runListCommand(options) {
39
44
  }
40
45
  const filtered = (0, filter_1.filterNodes)(Object.values(index.nodes), {
41
46
  ws,
42
- type: options.type,
47
+ type: normalizedType,
43
48
  status: options.status,
44
49
  epic: epicQid,
45
50
  priority: options.priority,
46
51
  blocked: options.blocked,
52
+ tags: options.tags,
53
+ tagsMode: options.tagsMode,
47
54
  });
48
55
  const sorted = (0, sort_1.sortNodesByQid)(filtered);
56
+ if (options.json) {
57
+ (0, query_output_1.writeJson)({
58
+ command: "list",
59
+ kind: "node",
60
+ count: sorted.length,
61
+ items: sorted.map(query_output_1.toNodeSummaryJson),
62
+ });
63
+ return;
64
+ }
65
+ (0, query_output_1.writeCount)(sorted.length, sorted.length === 0 ? "no nodes matched current filters" : undefined);
49
66
  for (const node of sorted) {
50
67
  console.log((0, node_card_1.formatNodeCard)(node));
51
68
  }