mdkg 0.1.0 → 0.1.2

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 (68) hide show
  1. package/CHANGELOG.md +93 -0
  2. package/README.md +108 -15
  3. package/dist/cli.js +566 -15
  4. package/dist/commands/archive.js +474 -0
  5. package/dist/commands/bundle.js +743 -0
  6. package/dist/commands/bundle_import.js +243 -0
  7. package/dist/commands/capability.js +162 -0
  8. package/dist/commands/doctor.js +233 -2
  9. package/dist/commands/format.js +38 -9
  10. package/dist/commands/index.js +11 -0
  11. package/dist/commands/init.js +188 -63
  12. package/dist/commands/init_manifest.js +19 -6
  13. package/dist/commands/list.js +5 -2
  14. package/dist/commands/new.js +6 -0
  15. package/dist/commands/next.js +7 -0
  16. package/dist/commands/node_card.js +4 -1
  17. package/dist/commands/pack.js +62 -2
  18. package/dist/commands/query_output.js +1 -0
  19. package/dist/commands/search.js +5 -2
  20. package/dist/commands/show.js +7 -14
  21. package/dist/commands/skill_mirror.js +22 -0
  22. package/dist/commands/task.js +3 -0
  23. package/dist/commands/upgrade.js +151 -13
  24. package/dist/commands/validate.js +19 -2
  25. package/dist/commands/work.js +365 -0
  26. package/dist/commands/workspace.js +12 -2
  27. package/dist/core/config.js +100 -1
  28. package/dist/graph/agent_file_types.js +78 -5
  29. package/dist/graph/archive_file.js +125 -0
  30. package/dist/graph/archive_integrity.js +66 -0
  31. package/dist/graph/bundle_imports.js +418 -0
  32. package/dist/graph/capabilities_index_cache.js +103 -0
  33. package/dist/graph/capabilities_indexer.js +231 -0
  34. package/dist/graph/frontmatter.js +19 -0
  35. package/dist/graph/index_cache.js +21 -4
  36. package/dist/graph/indexer.js +4 -1
  37. package/dist/graph/node.js +23 -4
  38. package/dist/graph/node_body.js +37 -0
  39. package/dist/graph/skills_indexer.js +8 -3
  40. package/dist/graph/template_schema.js +33 -5
  41. package/dist/graph/validate_graph.js +83 -7
  42. package/dist/graph/visibility.js +214 -0
  43. package/dist/graph/workspace_files.js +22 -0
  44. package/dist/init/AGENT_START.md +21 -0
  45. package/dist/init/CLI_COMMAND_MATRIX.md +58 -3
  46. package/dist/init/README.md +60 -3
  47. package/dist/init/config.json +13 -1
  48. package/dist/init/core/guide.md +6 -2
  49. package/dist/init/core/rule-3-cli-contract.md +71 -4
  50. package/dist/init/core/rule-4-repo-safety-and-ignores.md +20 -0
  51. package/dist/init/core/rule-6-templates-and-schemas.md +10 -1
  52. package/dist/init/init-manifest.json +19 -14
  53. package/dist/init/skills/default/build-pack-and-execute-task/SKILL.md +2 -1
  54. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +26 -0
  55. package/dist/init/templates/default/archive.md +33 -0
  56. package/dist/init/templates/default/receipt.md +15 -1
  57. package/dist/init/templates/default/work.md +6 -1
  58. package/dist/init/templates/default/work_order.md +15 -1
  59. package/dist/pack/export_md.js +3 -0
  60. package/dist/pack/export_xml.js +3 -0
  61. package/dist/pack/order.js +1 -0
  62. package/dist/pack/pack.js +3 -13
  63. package/dist/templates/builtin.js +38 -0
  64. package/dist/templates/loader.js +9 -16
  65. package/dist/util/argparse.js +30 -0
  66. package/dist/util/refs.js +40 -0
  67. package/dist/util/zip.js +153 -0
  68. package/package.json +8 -2
@@ -0,0 +1,231 @@
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.CAPABILITY_VISIBILITIES = exports.CAPABILITY_KINDS = exports.CAPABILITIES_INDEX_RELATIVE_PATH = void 0;
7
+ exports.resolveCapabilitiesIndexPath = resolveCapabilitiesIndexPath;
8
+ exports.buildCapabilitiesIndex = buildCapabilitiesIndex;
9
+ const crypto_1 = __importDefault(require("crypto"));
10
+ const fs_1 = __importDefault(require("fs"));
11
+ const path_1 = __importDefault(require("path"));
12
+ const indexer_1 = require("./indexer");
13
+ const skills_indexer_1 = require("./skills_indexer");
14
+ exports.CAPABILITIES_INDEX_RELATIVE_PATH = ".mdkg/index/capabilities.json";
15
+ exports.CAPABILITY_KINDS = ["skill", "spec", "work", "core", "design"];
16
+ exports.CAPABILITY_VISIBILITIES = ["private", "internal", "public"];
17
+ const CAPABILITY_CACHE_VERSION = 1;
18
+ function toPosixPath(value) {
19
+ return value.split(path_1.default.sep).join("/");
20
+ }
21
+ function sourceHash(content) {
22
+ return `sha256:${crypto_1.default.createHash("sha256").update(content).digest("hex")}`;
23
+ }
24
+ function extractHeadings(content) {
25
+ const headings = [];
26
+ for (const line of content.split(/\r?\n/)) {
27
+ const match = /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
28
+ if (!match) {
29
+ continue;
30
+ }
31
+ headings.push({ level: match[1].length, text: match[2].trim() });
32
+ }
33
+ return headings;
34
+ }
35
+ function workspaceVisibility(config, alias) {
36
+ return config.workspaces[alias]?.visibility ?? "private";
37
+ }
38
+ function workspaceMdkgPrefix(entry) {
39
+ const wsPath = entry.path === "." ? "" : `${toPosixPath(entry.path).replace(/\/+$/, "")}/`;
40
+ const mdkgDir = toPosixPath(entry.mdkg_dir).replace(/^\/+|\/+$/g, "");
41
+ return `${wsPath}${mdkgDir}/`;
42
+ }
43
+ function classifyNodeCapability(node, config) {
44
+ if (node.type === "spec") {
45
+ return "spec";
46
+ }
47
+ if (node.type === "work") {
48
+ return "work";
49
+ }
50
+ const workspace = config.workspaces[node.ws];
51
+ if (!workspace) {
52
+ return undefined;
53
+ }
54
+ const nodePath = toPosixPath(node.path);
55
+ const prefix = workspaceMdkgPrefix(workspace);
56
+ if (!nodePath.startsWith(prefix)) {
57
+ return undefined;
58
+ }
59
+ const relativeToMdkg = nodePath.slice(prefix.length);
60
+ if (relativeToMdkg.startsWith("core/")) {
61
+ return "core";
62
+ }
63
+ if (relativeToMdkg.startsWith("design/")) {
64
+ return "design";
65
+ }
66
+ return undefined;
67
+ }
68
+ function pickAttributes(attributes, keys) {
69
+ const picked = {};
70
+ for (const key of keys) {
71
+ if (attributes[key] !== undefined) {
72
+ picked[key] = attributes[key];
73
+ }
74
+ }
75
+ return picked;
76
+ }
77
+ function nodeCapabilityRecord(root, config, node, kind, indexedAt) {
78
+ const absolutePath = path_1.default.resolve(root, node.path);
79
+ const content = fs_1.default.readFileSync(absolutePath, "utf8");
80
+ const record = {
81
+ kind,
82
+ workspace: node.ws,
83
+ visibility: workspaceVisibility(config, node.ws),
84
+ id: node.id,
85
+ qid: node.qid,
86
+ path: toPosixPath(node.path),
87
+ title: node.title,
88
+ tags: [...node.tags],
89
+ refs: [...node.refs],
90
+ aliases: [...node.aliases],
91
+ links: [...node.links],
92
+ artifacts: [...node.artifacts],
93
+ updated: node.updated,
94
+ indexed_at: indexedAt,
95
+ source_hash: sourceHash(content),
96
+ headings: extractHeadings(content),
97
+ node_type: node.type,
98
+ };
99
+ if (kind === "spec") {
100
+ record.spec = pickAttributes(node.attributes, [
101
+ "version",
102
+ "role",
103
+ "runtime_mode",
104
+ "work_contracts",
105
+ "requested_capabilities",
106
+ "skill_refs",
107
+ "tool_refs",
108
+ "model_refs",
109
+ "wasm_component_refs",
110
+ "runtime_image_refs",
111
+ "subagent_refs",
112
+ "resource_profile",
113
+ "update_policy",
114
+ ]);
115
+ }
116
+ if (kind === "work") {
117
+ record.work = pickAttributes(node.attributes, [
118
+ "version",
119
+ "agent_id",
120
+ "kind",
121
+ "pricing_model",
122
+ "required_capabilities",
123
+ "skill_refs",
124
+ "tool_refs",
125
+ "model_refs",
126
+ "wasm_component_refs",
127
+ "runtime_image_refs",
128
+ "subagent_refs",
129
+ "inputs",
130
+ "outputs",
131
+ "receipt_required",
132
+ ]);
133
+ }
134
+ return record;
135
+ }
136
+ function skillCapabilityRecord(root, config, skill, indexedAt) {
137
+ const absolutePath = path_1.default.resolve(root, skill.path);
138
+ const content = fs_1.default.readFileSync(absolutePath, "utf8");
139
+ return {
140
+ kind: "skill",
141
+ workspace: skill.ws,
142
+ visibility: workspaceVisibility(config, skill.ws),
143
+ id: skill.id,
144
+ qid: skill.qid,
145
+ path: toPosixPath(skill.path),
146
+ title: skill.name,
147
+ name: skill.name,
148
+ description: skill.description,
149
+ slug: skill.slug,
150
+ tags: [...skill.tags],
151
+ refs: [],
152
+ aliases: [skill.slug, ...skill.tags],
153
+ links: [...skill.links],
154
+ artifacts: [],
155
+ indexed_at: indexedAt,
156
+ source_hash: sourceHash(content),
157
+ headings: extractHeadings(content),
158
+ skill: {
159
+ version: skill.version,
160
+ authors: [...skill.authors],
161
+ has_scripts: skill.has_scripts,
162
+ has_references: skill.has_references,
163
+ extensions: skill.extensions,
164
+ },
165
+ };
166
+ }
167
+ function workspaceSkillsRoot(root, entry) {
168
+ return path_1.default.resolve(root, entry.path, entry.mdkg_dir, "skills");
169
+ }
170
+ function buildWorkspaceSkillCapabilities(root, config, indexedAt) {
171
+ const records = [];
172
+ for (const alias of Object.keys(config.workspaces).sort()) {
173
+ const workspace = config.workspaces[alias];
174
+ if (!workspace.enabled) {
175
+ continue;
176
+ }
177
+ const skillsRoot = workspaceSkillsRoot(root, workspace);
178
+ for (const candidate of (0, skills_indexer_1.listSkillMarkdownFiles)(skillsRoot)) {
179
+ const skill = (0, skills_indexer_1.buildSkillIndexEntryForWorkspace)(root, alias, candidate.slug, candidate.filePath);
180
+ records.push(skillCapabilityRecord(root, config, skill, indexedAt));
181
+ }
182
+ }
183
+ return records;
184
+ }
185
+ function sortRecords(records) {
186
+ return [...records].sort((a, b) => {
187
+ for (const [left, right] of [
188
+ [a.workspace, b.workspace],
189
+ [a.kind, b.kind],
190
+ [a.id, b.id],
191
+ [a.path, b.path],
192
+ ]) {
193
+ const compared = left.localeCompare(right);
194
+ if (compared !== 0) {
195
+ return compared;
196
+ }
197
+ }
198
+ return 0;
199
+ });
200
+ }
201
+ function resolveCapabilitiesIndexPath(root, config) {
202
+ return path_1.default.resolve(root, config.capabilities.cache_path);
203
+ }
204
+ function buildCapabilitiesIndex(root, config, nodeIndex) {
205
+ const index = nodeIndex ?? (0, indexer_1.buildIndex)(root, config);
206
+ const records = [];
207
+ const generatedAt = new Date().toISOString();
208
+ for (const node of Object.values(index.nodes)) {
209
+ const kind = classifyNodeCapability(node, config);
210
+ if (!kind) {
211
+ continue;
212
+ }
213
+ records.push(nodeCapabilityRecord(root, config, node, kind, generatedAt));
214
+ }
215
+ records.push(...buildWorkspaceSkillCapabilities(root, config, generatedAt));
216
+ const sortedRecords = sortRecords(records);
217
+ return {
218
+ meta: {
219
+ tool: config.tool,
220
+ schema_version: config.schema_version,
221
+ cache_version: CAPABILITY_CACHE_VERSION,
222
+ generated_at: generatedAt,
223
+ root,
224
+ workspaces: Object.keys(config.workspaces)
225
+ .filter((alias) => config.workspaces[alias].enabled)
226
+ .sort(),
227
+ record_count: sortedRecords.length,
228
+ },
229
+ records: sortedRecords,
230
+ };
231
+ }
@@ -48,6 +48,25 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
48
48
  "proposal_status",
49
49
  "proposal_kind",
50
50
  "evidence_refs",
51
+ "archive_kind",
52
+ "source_path",
53
+ "stored_path",
54
+ "compressed_path",
55
+ "mime_type",
56
+ "byte_size",
57
+ "sha256",
58
+ "compressed_sha256",
59
+ "visibility",
60
+ "provenance",
61
+ "ingest_status",
62
+ "input_refs",
63
+ "requested_outputs",
64
+ "constraint_refs",
65
+ "artifact_policy",
66
+ "proof_refs",
67
+ "attestation_refs",
68
+ "input_hashes",
69
+ "output_hashes",
51
70
  "tags",
52
71
  "owners",
53
72
  "links",
@@ -10,6 +10,7 @@ const path_1 = __importDefault(require("path"));
10
10
  const sort_1 = require("../util/sort");
11
11
  const indexer_1 = require("./indexer");
12
12
  const staleness_1 = require("./staleness");
13
+ const bundle_imports_1 = require("./bundle_imports");
13
14
  function readIndex(indexPath) {
14
15
  try {
15
16
  const raw = fs_1.default.readFileSync(indexPath, "utf8");
@@ -29,22 +30,38 @@ function loadIndex(options) {
29
30
  const useCache = options.useCache ?? true;
30
31
  const allowReindex = options.allowReindex ?? options.config.index.auto_reindex;
31
32
  const tolerant = options.tolerant ?? options.config.index.tolerant;
33
+ const includeImports = options.includeImports ?? true;
32
34
  const indexPath = path_1.default.resolve(options.root, options.config.index.global_index_path);
35
+ const withImports = (index, rebuilt, stale) => {
36
+ if (!includeImports || Object.keys(options.config.bundle_imports).length === 0) {
37
+ return { index, rebuilt, stale, warnings: [] };
38
+ }
39
+ const imports = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, options.config);
40
+ if (allowReindex) {
41
+ (0, bundle_imports_1.writeBundleImportsIndex)((0, bundle_imports_1.resolveBundleImportsIndexPath)(options.root), imports.index);
42
+ }
43
+ return {
44
+ index: (0, bundle_imports_1.mergeBundleImportsIntoIndex)(index, imports),
45
+ rebuilt,
46
+ stale: stale || (0, bundle_imports_1.isBundleImportsIndexStale)(options.root, options.config),
47
+ warnings: (0, bundle_imports_1.importWarnings)(imports),
48
+ };
49
+ };
33
50
  if (!useCache) {
34
51
  const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
35
- return { index, rebuilt: true, stale: false };
52
+ return withImports(index, true, false);
36
53
  }
37
54
  const stale = (0, staleness_1.isIndexStale)(options.root, options.config);
38
55
  if (fs_1.default.existsSync(indexPath) && !stale) {
39
- return { index: readIndex(indexPath), rebuilt: false, stale: false };
56
+ return withImports(readIndex(indexPath), false, false);
40
57
  }
41
58
  if (allowReindex) {
42
59
  const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
43
60
  writeIndex(indexPath, index);
44
- return { index, rebuilt: true, stale };
61
+ return withImports(index, true, stale);
45
62
  }
46
63
  if (fs_1.default.existsSync(indexPath)) {
47
- return { index: readIndex(indexPath), rebuilt: false, stale: true };
64
+ return withImports(readIndex(indexPath), false, true);
48
65
  }
49
66
  throw new Error("index missing and auto-reindex is disabled");
50
67
  }
@@ -141,7 +141,10 @@ function buildIndex(root, config, options = {}) {
141
141
  nodes,
142
142
  reverse_edges,
143
143
  };
144
- (0, validate_graph_1.validateGraph)(index, { allowMissing: tolerant });
144
+ (0, validate_graph_1.validateGraph)(index, {
145
+ allowMissing: tolerant,
146
+ externalWorkspaces: new Set(Object.keys(config.bundle_imports ?? {})),
147
+ });
145
148
  const latestCheckpointByWorkspace = {};
146
149
  for (const alias of workspaceAliases) {
147
150
  const candidates = Object.values(nodes)
@@ -5,7 +5,9 @@ exports.parseNode = parseNode;
5
5
  const frontmatter_1 = require("./frontmatter");
6
6
  const edges_1 = require("./edges");
7
7
  const agent_file_types_1 = require("./agent_file_types");
8
+ const archive_file_1 = require("./archive_file");
8
9
  const id_1 = require("../util/id");
10
+ const refs_1 = require("../util/refs");
9
11
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
10
12
  const DEC_ID_RE = /^dec-[0-9]+$/;
11
13
  exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
@@ -22,6 +24,7 @@ exports.ALLOWED_TYPES = new Set([
22
24
  "bug",
23
25
  "checkpoint",
24
26
  "test",
27
+ "archive",
25
28
  ...agent_file_types_1.AGENT_FILE_TYPES,
26
29
  ]);
27
30
  const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
@@ -119,6 +122,17 @@ function normalizeIdList(values, key, filePath, allowPortableIds = false) {
119
122
  return value;
120
123
  });
121
124
  }
125
+ function normalizeRefsList(values, key, filePath, allowPortableOrUriRefs = false) {
126
+ if (!allowPortableOrUriRefs) {
127
+ return normalizeIdList(values, key, filePath);
128
+ }
129
+ return values.map((value) => {
130
+ if (!(0, refs_1.validatePortableOrUriRef)(value)) {
131
+ throw formatError(filePath, `${key} entries must be portable ids or URI refs`);
132
+ }
133
+ return value;
134
+ });
135
+ }
122
136
  function normalizeSkillList(values, filePath) {
123
137
  return values.map((value, index) => {
124
138
  const normalized = value.toLowerCase();
@@ -166,11 +180,13 @@ function parseNode(content, filePath, options) {
166
180
  throw formatError(filePath, `type must be one of ${Array.from(exports.ALLOWED_TYPES).join(", ")}`);
167
181
  }
168
182
  const isAgentType = (0, agent_file_types_1.isAgentFileType)(type);
183
+ const isPortableType = isAgentType || (0, archive_file_1.isArchiveType)(type);
169
184
  const schema = requireTemplateSchema(type, options.templateSchemas, filePath);
170
185
  validateTemplateKeys(frontmatter, schema, filePath);
171
186
  (0, agent_file_types_1.validateAgentFrontmatter)(type, frontmatter, filePath);
187
+ (0, archive_file_1.validateArchiveFrontmatter)(type, frontmatter, filePath);
172
188
  const idValue = requireLowercase(expectString(frontmatter, "id", filePath), "id", filePath);
173
- const id = isAgentType
189
+ const id = isPortableType
174
190
  ? requirePortableIdFormat(idValue, "id", filePath)
175
191
  : requireIdFormat(idValue, "id", filePath);
176
192
  const title = expectString(frontmatter, "title", filePath);
@@ -214,7 +230,7 @@ function parseNode(content, filePath, options) {
214
230
  const owners = requireLowercaseList(optionalList(frontmatter, "owners", filePath), "owners", filePath);
215
231
  const links = optionalList(frontmatter, "links", filePath);
216
232
  const artifacts = optionalList(frontmatter, "artifacts", filePath);
217
- const refs = normalizeIdList(optionalList(frontmatter, "refs", filePath), "refs", filePath, isAgentType);
233
+ const refs = normalizeRefsList(optionalList(frontmatter, "refs", filePath), "refs", filePath, isPortableType);
218
234
  const aliases = requireLowercaseList(optionalList(frontmatter, "aliases", filePath), "aliases", filePath);
219
235
  const skillsRaw = optionalList(frontmatter, "skills", filePath);
220
236
  const skills = normalizeSkillList(skillsRaw, filePath);
@@ -232,8 +248,11 @@ function parseNode(content, filePath, options) {
232
248
  throw formatError(filePath, "supersedes must be a dec-# id");
233
249
  }
234
250
  }
235
- const edges = (0, edges_1.extractEdges)(frontmatter, filePath, { allowPortableRefs: isAgentType });
236
- const attributes = (0, agent_file_types_1.extractAgentAttributes)(type, frontmatter);
251
+ const edges = (0, edges_1.extractEdges)(frontmatter, filePath, { allowPortableRefs: isPortableType });
252
+ const attributes = {
253
+ ...(0, agent_file_types_1.extractAgentAttributes)(type, frontmatter),
254
+ ...(0, archive_file_1.extractArchiveAttributes)(type, frontmatter),
255
+ };
237
256
  return {
238
257
  id,
239
258
  type,
@@ -0,0 +1,37 @@
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.readNodeBody = readNodeBody;
7
+ const fs_1 = __importDefault(require("fs"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const frontmatter_1 = require("./frontmatter");
10
+ const zip_1 = require("../util/zip");
11
+ const errors_1 = require("../util/errors");
12
+ function readImportedBody(root, node) {
13
+ const source = node.source;
14
+ if (!source?.imported) {
15
+ throw new Error("node is not imported");
16
+ }
17
+ const bundlePath = path_1.default.resolve(root, source.bundle_path);
18
+ if (!fs_1.default.existsSync(bundlePath)) {
19
+ throw new errors_1.NotFoundError(`bundle not found for ${node.qid}: ${source.bundle_path}`);
20
+ }
21
+ const entry = (0, zip_1.readZipEntries)(fs_1.default.readFileSync(bundlePath)).find((candidate) => candidate.name === source.original_path);
22
+ if (!entry) {
23
+ throw new errors_1.NotFoundError(`bundle entry not found for ${node.qid}: ${source.original_path}`);
24
+ }
25
+ return (0, frontmatter_1.parseFrontmatter)(entry.data.toString("utf8"), source.original_path).body.trimEnd();
26
+ }
27
+ function readNodeBody(root, node) {
28
+ if (node.source?.imported) {
29
+ return readImportedBody(root, node);
30
+ }
31
+ const filePath = path_1.default.resolve(root, node.path);
32
+ if (!fs_1.default.existsSync(filePath)) {
33
+ throw new errors_1.NotFoundError(`file not found for ${node.qid}: ${node.path}`);
34
+ }
35
+ const content = fs_1.default.readFileSync(filePath, "utf8");
36
+ return (0, frontmatter_1.parseFrontmatter)(content, filePath).body.trimEnd();
37
+ }
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.SKILL_SLUG_RE = exports.SKILLS_INDEX_RELATIVE_PATH = void 0;
7
+ exports.listSkillMarkdownFiles = listSkillMarkdownFiles;
7
8
  exports.resolveSkillsRoot = resolveSkillsRoot;
8
9
  exports.resolveSkillsIndexPath = resolveSkillsIndexPath;
10
+ exports.buildSkillIndexEntryForWorkspace = buildSkillIndexEntryForWorkspace;
9
11
  exports.buildSkillIndexEntry = buildSkillIndexEntry;
10
12
  exports.buildSkillsIndex = buildSkillsIndex;
11
13
  const fs_1 = __importDefault(require("fs"));
@@ -107,7 +109,7 @@ function resolveSkillsRoot(root, config) {
107
109
  function resolveSkillsIndexPath(root) {
108
110
  return path_1.default.resolve(root, exports.SKILLS_INDEX_RELATIVE_PATH);
109
111
  }
110
- function buildSkillIndexEntry(root, slug, filePath) {
112
+ function buildSkillIndexEntryForWorkspace(root, workspace, slug, filePath) {
111
113
  if (!exports.SKILL_SLUG_RE.test(slug)) {
112
114
  throw new Error(`${filePath}: skill slug must be kebab-case`);
113
115
  }
@@ -128,8 +130,8 @@ function buildSkillIndexEntry(root, slug, filePath) {
128
130
  return {
129
131
  slug,
130
132
  id: `skill:${slug}`,
131
- qid: `root:skill:${slug}`,
132
- ws: "root",
133
+ qid: `${workspace}:skill:${slug}`,
134
+ ws: workspace,
133
135
  type: "skill",
134
136
  name,
135
137
  description,
@@ -144,6 +146,9 @@ function buildSkillIndexEntry(root, slug, filePath) {
144
146
  ochatr,
145
147
  };
146
148
  }
149
+ function buildSkillIndexEntry(root, slug, filePath) {
150
+ return buildSkillIndexEntryForWorkspace(root, "root", slug, filePath);
151
+ }
147
152
  function buildSkillsIndex(root, config) {
148
153
  const skillsRoot = resolveSkillsRoot(root, config);
149
154
  const files = listSkillMarkdownFiles(skillsRoot);
@@ -4,9 +4,11 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.loadTemplateSchemas = loadTemplateSchemas;
7
+ exports.loadTemplateSchemasWithInfo = loadTemplateSchemasWithInfo;
7
8
  const fs_1 = __importDefault(require("fs"));
8
9
  const path_1 = __importDefault(require("path"));
9
10
  const frontmatter_1 = require("./frontmatter");
11
+ const builtin_1 = require("../templates/builtin");
10
12
  function listMarkdownFiles(dir) {
11
13
  if (!fs_1.default.existsSync(dir)) {
12
14
  return [];
@@ -45,9 +47,12 @@ function addKeyToSchema(schema, key, kind, filePath) {
45
47
  }
46
48
  }
47
49
  function loadTemplateSchemas(root, config, requiredTypes) {
50
+ return loadTemplateSchemasWithInfo(root, config, requiredTypes).schemas;
51
+ }
52
+ function loadTemplateSchemasWithInfo(root, config, requiredTypes) {
48
53
  const templateRoot = path_1.default.resolve(root, config.templates.root_path, config.templates.default_set);
49
54
  const files = listMarkdownFiles(templateRoot);
50
- if (files.length === 0) {
55
+ if (files.length === 0 && !requiredTypes) {
51
56
  throw new Error(`no templates found at ${templateRoot}`);
52
57
  }
53
58
  const schemas = {};
@@ -75,12 +80,35 @@ function loadTemplateSchemas(root, config, requiredTypes) {
75
80
  addKeyToSchema(schema, key, kind, filePath);
76
81
  }
77
82
  }
83
+ const fallbackTypes = [];
78
84
  if (requiredTypes) {
79
85
  const required = Array.from(requiredTypes, (value) => value.toLowerCase());
80
- const missing = required.filter((value) => !schemas[value]);
81
- if (missing.length > 0) {
82
- throw new Error(`template schema missing for type(s): ${missing.join(", ")}`);
86
+ for (const missingType of required.filter((value) => !schemas[value])) {
87
+ const bundledPath = (0, builtin_1.requireBundledTemplatePath)(missingType);
88
+ const content = fs_1.default.readFileSync(bundledPath, "utf8");
89
+ const { frontmatter } = (0, frontmatter_1.parseFrontmatter)(content, bundledPath);
90
+ const typeValue = frontmatter.type;
91
+ if (typeValue !== missingType) {
92
+ throw new Error(`bundled template fallback type mismatch for ${missingType}: ${bundledPath}`);
93
+ }
94
+ const schema = {
95
+ type: missingType,
96
+ allowedKeys: new Set(),
97
+ keyKinds: {},
98
+ listKeys: new Set(),
99
+ };
100
+ schemas[missingType] = schema;
101
+ for (const [key, value] of Object.entries(frontmatter)) {
102
+ const kind = getValueKind(value);
103
+ addKeyToSchema(schema, key, kind, bundledPath);
104
+ }
105
+ fallbackTypes.push(missingType);
83
106
  }
84
107
  }
85
- return schemas;
108
+ return {
109
+ schemas,
110
+ templateRoot,
111
+ bundledTemplateRoot: (0, builtin_1.resolveBundledTemplateRoot)(),
112
+ fallbackTypes,
113
+ };
86
114
  }