mdkg 0.0.8 → 0.1.0

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 (54) hide show
  1. package/CHANGELOG.md +71 -0
  2. package/CONTRIBUTING.md +124 -0
  3. package/README.md +49 -14
  4. package/dist/cli.js +113 -32
  5. package/dist/commands/checkpoint.js +19 -2
  6. package/dist/commands/event.js +12 -0
  7. package/dist/commands/init.js +4 -0
  8. package/dist/commands/init_manifest.js +131 -0
  9. package/dist/commands/new.js +57 -21
  10. package/dist/commands/pack.js +14 -0
  11. package/dist/commands/query_output.js +2 -0
  12. package/dist/commands/search.js +8 -0
  13. package/dist/commands/show.js +7 -0
  14. package/dist/commands/skill.js +80 -12
  15. package/dist/commands/task.js +42 -12
  16. package/dist/commands/upgrade.js +286 -0
  17. package/dist/commands/validate.js +31 -3
  18. package/dist/commands/workspace.js +105 -13
  19. package/dist/core/config.js +217 -22
  20. package/dist/core/migrate.js +39 -5
  21. package/dist/core/version.js +31 -0
  22. package/dist/core/workspace_path.js +41 -0
  23. package/dist/graph/agent_file_types.js +392 -0
  24. package/dist/graph/edges.js +13 -10
  25. package/dist/graph/frontmatter.js +33 -0
  26. package/dist/graph/indexer.js +1 -0
  27. package/dist/graph/node.js +43 -16
  28. package/dist/graph/skills_indexer.js +14 -1
  29. package/dist/graph/template_schema.js +13 -126
  30. package/dist/graph/validate_graph.js +302 -2
  31. package/dist/init/AGENT_START.md +14 -2
  32. package/dist/init/CLI_COMMAND_MATRIX.md +49 -1
  33. package/dist/init/README.md +14 -0
  34. package/dist/init/core/rule-6-templates-and-schemas.md +1 -3
  35. package/dist/init/init-manifest.json +197 -0
  36. package/dist/init/legacy/v0.0.9-init-manifest.json +197 -0
  37. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +12 -11
  38. package/dist/init/templates/default/dispute.md +31 -0
  39. package/dist/init/templates/default/feedback.md +27 -0
  40. package/dist/init/templates/default/proposal.md +35 -0
  41. package/dist/init/templates/default/receipt.md +31 -0
  42. package/dist/init/templates/default/spec.md +43 -0
  43. package/dist/init/templates/default/work.md +44 -0
  44. package/dist/init/templates/default/work_order.md +32 -0
  45. package/dist/pack/export_json.js +3 -0
  46. package/dist/pack/export_md.js +9 -0
  47. package/dist/pack/export_xml.js +9 -0
  48. package/dist/pack/order.js +7 -0
  49. package/dist/pack/pack.js +1 -0
  50. package/dist/templates/loader.js +2 -2
  51. package/dist/util/argparse.js +2 -0
  52. package/dist/util/id.js +19 -0
  53. package/package.json +10 -2
  54. package/scripts/postinstall.js +89 -0
@@ -0,0 +1,392 @@
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.AGENT_ATTRIBUTE_KEY_ORDER = exports.AGENT_FILE_BASENAMES = exports.AGENT_FILE_TYPES = void 0;
7
+ exports.isAgentFileType = isAgentFileType;
8
+ exports.validateAgentFileName = validateAgentFileName;
9
+ exports.validateAgentFrontmatter = validateAgentFrontmatter;
10
+ exports.extractAgentAttributes = extractAgentAttributes;
11
+ const path_1 = __importDefault(require("path"));
12
+ const id_1 = require("../util/id");
13
+ exports.AGENT_FILE_TYPES = [
14
+ "spec",
15
+ "work",
16
+ "work_order",
17
+ "receipt",
18
+ "feedback",
19
+ "dispute",
20
+ "proposal",
21
+ ];
22
+ exports.AGENT_FILE_BASENAMES = {
23
+ spec: "SPEC.md",
24
+ work: "WORK.md",
25
+ work_order: "WORK_ORDER.md",
26
+ receipt: "RECEIPT.md",
27
+ feedback: "FEEDBACK.md",
28
+ dispute: "DISPUTE.md",
29
+ proposal: "PROPOSAL.md",
30
+ };
31
+ exports.AGENT_ATTRIBUTE_KEY_ORDER = {
32
+ spec: [
33
+ "version",
34
+ "role",
35
+ "runtime_mode",
36
+ "work_contracts",
37
+ "requested_capabilities",
38
+ "skill_refs",
39
+ "tool_refs",
40
+ "model_refs",
41
+ "wasm_component_refs",
42
+ "runtime_image_refs",
43
+ "subagent_refs",
44
+ "resource_profile",
45
+ "update_policy",
46
+ ],
47
+ work: [
48
+ "version",
49
+ "agent_id",
50
+ "kind",
51
+ "pricing_model",
52
+ "required_capabilities",
53
+ "skill_refs",
54
+ "tool_refs",
55
+ "model_refs",
56
+ "wasm_component_refs",
57
+ "runtime_image_refs",
58
+ "subagent_refs",
59
+ "inputs",
60
+ "outputs",
61
+ "receipt_required",
62
+ ],
63
+ work_order: [
64
+ "version",
65
+ "work_id",
66
+ "work_version",
67
+ "requester",
68
+ "order_status",
69
+ "request_ref",
70
+ ],
71
+ receipt: [
72
+ "version",
73
+ "work_order_id",
74
+ "receipt_status",
75
+ "outcome",
76
+ "cost_ref",
77
+ ],
78
+ feedback: [
79
+ "version",
80
+ "target_id",
81
+ "sentiment",
82
+ "feedback_status",
83
+ "source_ref",
84
+ ],
85
+ dispute: [
86
+ "version",
87
+ "work_order_id",
88
+ "receipt_id",
89
+ "dispute_status",
90
+ "severity",
91
+ ],
92
+ proposal: [
93
+ "version",
94
+ "target_id",
95
+ "proposal_status",
96
+ "proposal_kind",
97
+ "evidence_refs",
98
+ ],
99
+ };
100
+ const SEMVER_RE = /^\d+\.\d+\.\d+(?:[-+][a-z0-9.-]+)?$/;
101
+ const LOWER_TOKEN_RE = /^[a-z][a-z0-9_]*(?:-[a-z0-9_]+)*$/;
102
+ const FIELD_DESCRIPTOR_RE = /^[a-z][a-z0-9_]*:[a-z][a-z0-9_]*(?::(?:required|optional))?$/;
103
+ const ROLE_VALUES = new Set([
104
+ "orchestrator",
105
+ "subagent",
106
+ "standalone_agent",
107
+ "tool_service",
108
+ "remote_agent",
109
+ ]);
110
+ const RUNTIME_MODE_VALUES = new Set([
111
+ "room_orchestrated",
112
+ "standalone",
113
+ "tool_service",
114
+ "remote",
115
+ ]);
116
+ const UPDATE_POLICY_VALUES = new Set([
117
+ "manual",
118
+ "proposal_required",
119
+ "automatic",
120
+ "disabled",
121
+ ]);
122
+ const PRICING_MODEL_VALUES = new Set([
123
+ "free",
124
+ "included",
125
+ "quoted",
126
+ "fixed",
127
+ "metered",
128
+ "subscription",
129
+ ]);
130
+ const ORDER_STATUS_VALUES = new Set([
131
+ "submitted",
132
+ "accepted",
133
+ "running",
134
+ "completed",
135
+ "cancelled",
136
+ "failed",
137
+ ]);
138
+ const RECEIPT_STATUS_VALUES = new Set(["recorded", "verified", "rejected"]);
139
+ const OUTCOME_VALUES = new Set(["success", "partial", "failure"]);
140
+ const SENTIMENT_VALUES = new Set(["positive", "neutral", "negative", "mixed"]);
141
+ const FEEDBACK_STATUS_VALUES = new Set(["new", "triaged", "accepted", "rejected"]);
142
+ const DISPUTE_STATUS_VALUES = new Set(["open", "investigating", "resolved", "rejected"]);
143
+ const SEVERITY_VALUES = new Set(["low", "medium", "high", "critical"]);
144
+ const PROPOSAL_STATUS_VALUES = new Set([
145
+ "proposed",
146
+ "accepted",
147
+ "rejected",
148
+ "superseded",
149
+ "implemented",
150
+ ]);
151
+ const PROPOSAL_KIND_VALUES = new Set([
152
+ "spec_update",
153
+ "work_update",
154
+ "skill_update",
155
+ "policy_update",
156
+ "documentation",
157
+ ]);
158
+ function formatError(filePath, message) {
159
+ return new Error(`${filePath}: ${message}`);
160
+ }
161
+ function isAgentFileType(type) {
162
+ return exports.AGENT_FILE_TYPES.includes(type);
163
+ }
164
+ function validateAgentFileName(type, filePath) {
165
+ const expected = exports.AGENT_FILE_BASENAMES[type];
166
+ if (path_1.default.basename(filePath) !== expected) {
167
+ throw formatError(filePath, `${type} files must be named ${expected}`);
168
+ }
169
+ }
170
+ function expectString(frontmatter, key, filePath) {
171
+ const value = frontmatter[key];
172
+ if (typeof value !== "string" || value.trim().length === 0) {
173
+ throw formatError(filePath, `${key} is required and must be a non-empty string`);
174
+ }
175
+ if (value !== value.toLowerCase()) {
176
+ throw formatError(filePath, `${key} must be lowercase`);
177
+ }
178
+ return value;
179
+ }
180
+ function optionalString(frontmatter, key, filePath) {
181
+ const value = frontmatter[key];
182
+ if (value === undefined) {
183
+ return undefined;
184
+ }
185
+ if (typeof value !== "string" || value.trim().length === 0) {
186
+ throw formatError(filePath, `${key} must be a non-empty string`);
187
+ }
188
+ if (value !== value.toLowerCase()) {
189
+ throw formatError(filePath, `${key} must be lowercase`);
190
+ }
191
+ return value;
192
+ }
193
+ function expectBoolean(frontmatter, key, filePath) {
194
+ const value = frontmatter[key];
195
+ if (typeof value !== "boolean") {
196
+ throw formatError(filePath, `${key} is required and must be a boolean`);
197
+ }
198
+ return value;
199
+ }
200
+ function expectList(frontmatter, key, filePath) {
201
+ const value = frontmatter[key];
202
+ if (!Array.isArray(value)) {
203
+ throw formatError(filePath, `${key} is required and must be a list`);
204
+ }
205
+ return value;
206
+ }
207
+ function optionalList(frontmatter, key, filePath) {
208
+ const value = frontmatter[key];
209
+ if (value === undefined) {
210
+ return [];
211
+ }
212
+ if (!Array.isArray(value)) {
213
+ throw formatError(filePath, `${key} must be a list`);
214
+ }
215
+ return value;
216
+ }
217
+ function requirePortableId(value, key, filePath) {
218
+ if (!(0, id_1.isPortableId)(value)) {
219
+ throw formatError(filePath, `${key} must be a lowercase portable id`);
220
+ }
221
+ }
222
+ function requireSemver(value, key, filePath) {
223
+ if (!SEMVER_RE.test(value)) {
224
+ throw formatError(filePath, `${key} must be a semantic version like 1.0.0`);
225
+ }
226
+ }
227
+ function requireEnum(value, key, allowed, filePath) {
228
+ if (!allowed.has(value)) {
229
+ throw formatError(filePath, `${key} must be one of ${Array.from(allowed).join(", ")}`);
230
+ }
231
+ }
232
+ function requireLowerToken(value, key, filePath) {
233
+ if (!LOWER_TOKEN_RE.test(value)) {
234
+ throw formatError(filePath, `${key} must be lowercase snake/kebab style`);
235
+ }
236
+ }
237
+ function validateCapabilities(values, key, filePath) {
238
+ for (const [index, value] of values.entries()) {
239
+ if (value !== value.toLowerCase()) {
240
+ throw formatError(filePath, `${key}[${index}] must be lowercase`);
241
+ }
242
+ if (!(0, id_1.isPortableId)(value)) {
243
+ throw formatError(filePath, `${key}[${index}] must be a portable capability id`);
244
+ }
245
+ }
246
+ }
247
+ function validatePortableRefs(values, key, filePath) {
248
+ for (const [index, value] of values.entries()) {
249
+ if (value !== value.toLowerCase()) {
250
+ throw formatError(filePath, `${key}[${index}] must be lowercase`);
251
+ }
252
+ if (!(0, id_1.isPortableId)(value)) {
253
+ throw formatError(filePath, `${key}[${index}] must be a lowercase portable id`);
254
+ }
255
+ }
256
+ }
257
+ function validateRelativeMarkdownPaths(values, key, basename, filePath) {
258
+ for (const [index, value] of values.entries()) {
259
+ if (path_1.default.isAbsolute(value) || value.split(/[\\/]/).includes("..")) {
260
+ throw formatError(filePath, `${key}[${index}] must be a relative path`);
261
+ }
262
+ if (path_1.default.basename(value) !== basename) {
263
+ throw formatError(filePath, `${key}[${index}] must point to ${basename}`);
264
+ }
265
+ }
266
+ }
267
+ function validateFieldDescriptors(values, key, filePath) {
268
+ if (values.length === 0) {
269
+ throw formatError(filePath, `${key} must not be empty`);
270
+ }
271
+ for (const [index, value] of values.entries()) {
272
+ if (value !== value.toLowerCase() || !FIELD_DESCRIPTOR_RE.test(value)) {
273
+ throw formatError(filePath, `${key}[${index}] must use name:type or name:type:required|optional`);
274
+ }
275
+ }
276
+ }
277
+ function validateAgentFrontmatter(type, frontmatter, filePath) {
278
+ if (!isAgentFileType(type)) {
279
+ return;
280
+ }
281
+ validateAgentFileName(type, filePath);
282
+ const version = expectString(frontmatter, "version", filePath);
283
+ requireSemver(version, "version", filePath);
284
+ switch (type) {
285
+ case "spec": {
286
+ const role = expectString(frontmatter, "role", filePath);
287
+ requireEnum(role, "role", ROLE_VALUES, filePath);
288
+ const runtimeMode = expectString(frontmatter, "runtime_mode", filePath);
289
+ requireEnum(runtimeMode, "runtime_mode", RUNTIME_MODE_VALUES, filePath);
290
+ const updatePolicy = expectString(frontmatter, "update_policy", filePath);
291
+ requireEnum(updatePolicy, "update_policy", UPDATE_POLICY_VALUES, filePath);
292
+ validateRelativeMarkdownPaths(optionalList(frontmatter, "work_contracts", filePath), "work_contracts", "WORK.md", filePath);
293
+ validateCapabilities(optionalList(frontmatter, "requested_capabilities", filePath), "requested_capabilities", filePath);
294
+ validatePortableRefs(optionalList(frontmatter, "skill_refs", filePath), "skill_refs", filePath);
295
+ validatePortableRefs(optionalList(frontmatter, "tool_refs", filePath), "tool_refs", filePath);
296
+ validatePortableRefs(optionalList(frontmatter, "model_refs", filePath), "model_refs", filePath);
297
+ validatePortableRefs(optionalList(frontmatter, "wasm_component_refs", filePath), "wasm_component_refs", filePath);
298
+ validatePortableRefs(optionalList(frontmatter, "runtime_image_refs", filePath), "runtime_image_refs", filePath);
299
+ validatePortableRefs(optionalList(frontmatter, "subagent_refs", filePath), "subagent_refs", filePath);
300
+ const resourceProfile = optionalString(frontmatter, "resource_profile", filePath);
301
+ if (resourceProfile) {
302
+ requireLowerToken(resourceProfile, "resource_profile", filePath);
303
+ }
304
+ break;
305
+ }
306
+ case "work": {
307
+ const agentId = expectString(frontmatter, "agent_id", filePath);
308
+ requirePortableId(agentId, "agent_id", filePath);
309
+ const kind = expectString(frontmatter, "kind", filePath);
310
+ requireLowerToken(kind, "kind", filePath);
311
+ const pricingModel = expectString(frontmatter, "pricing_model", filePath);
312
+ requireEnum(pricingModel, "pricing_model", PRICING_MODEL_VALUES, filePath);
313
+ validateCapabilities(expectList(frontmatter, "required_capabilities", filePath), "required_capabilities", filePath);
314
+ validatePortableRefs(optionalList(frontmatter, "skill_refs", filePath), "skill_refs", filePath);
315
+ validatePortableRefs(optionalList(frontmatter, "tool_refs", filePath), "tool_refs", filePath);
316
+ validatePortableRefs(optionalList(frontmatter, "model_refs", filePath), "model_refs", filePath);
317
+ validatePortableRefs(optionalList(frontmatter, "wasm_component_refs", filePath), "wasm_component_refs", filePath);
318
+ validatePortableRefs(optionalList(frontmatter, "runtime_image_refs", filePath), "runtime_image_refs", filePath);
319
+ validatePortableRefs(optionalList(frontmatter, "subagent_refs", filePath), "subagent_refs", filePath);
320
+ validateFieldDescriptors(expectList(frontmatter, "inputs", filePath), "inputs", filePath);
321
+ validateFieldDescriptors(expectList(frontmatter, "outputs", filePath), "outputs", filePath);
322
+ expectBoolean(frontmatter, "receipt_required", filePath);
323
+ break;
324
+ }
325
+ case "work_order": {
326
+ const workId = expectString(frontmatter, "work_id", filePath);
327
+ requirePortableId(workId, "work_id", filePath);
328
+ const workVersion = expectString(frontmatter, "work_version", filePath);
329
+ requireSemver(workVersion, "work_version", filePath);
330
+ const requester = expectString(frontmatter, "requester", filePath);
331
+ requirePortableId(requester, "requester", filePath);
332
+ const orderStatus = expectString(frontmatter, "order_status", filePath);
333
+ requireEnum(orderStatus, "order_status", ORDER_STATUS_VALUES, filePath);
334
+ optionalString(frontmatter, "request_ref", filePath);
335
+ break;
336
+ }
337
+ case "receipt": {
338
+ const workOrderId = expectString(frontmatter, "work_order_id", filePath);
339
+ requirePortableId(workOrderId, "work_order_id", filePath);
340
+ const receiptStatus = expectString(frontmatter, "receipt_status", filePath);
341
+ requireEnum(receiptStatus, "receipt_status", RECEIPT_STATUS_VALUES, filePath);
342
+ const outcome = expectString(frontmatter, "outcome", filePath);
343
+ requireEnum(outcome, "outcome", OUTCOME_VALUES, filePath);
344
+ optionalString(frontmatter, "cost_ref", filePath);
345
+ break;
346
+ }
347
+ case "feedback": {
348
+ const targetId = expectString(frontmatter, "target_id", filePath);
349
+ requirePortableId(targetId, "target_id", filePath);
350
+ const sentiment = expectString(frontmatter, "sentiment", filePath);
351
+ requireEnum(sentiment, "sentiment", SENTIMENT_VALUES, filePath);
352
+ const feedbackStatus = expectString(frontmatter, "feedback_status", filePath);
353
+ requireEnum(feedbackStatus, "feedback_status", FEEDBACK_STATUS_VALUES, filePath);
354
+ optionalString(frontmatter, "source_ref", filePath);
355
+ break;
356
+ }
357
+ case "dispute": {
358
+ const workOrderId = expectString(frontmatter, "work_order_id", filePath);
359
+ requirePortableId(workOrderId, "work_order_id", filePath);
360
+ const receiptId = expectString(frontmatter, "receipt_id", filePath);
361
+ requirePortableId(receiptId, "receipt_id", filePath);
362
+ const disputeStatus = expectString(frontmatter, "dispute_status", filePath);
363
+ requireEnum(disputeStatus, "dispute_status", DISPUTE_STATUS_VALUES, filePath);
364
+ const severity = expectString(frontmatter, "severity", filePath);
365
+ requireEnum(severity, "severity", SEVERITY_VALUES, filePath);
366
+ break;
367
+ }
368
+ case "proposal": {
369
+ const targetId = expectString(frontmatter, "target_id", filePath);
370
+ requirePortableId(targetId, "target_id", filePath);
371
+ const proposalStatus = expectString(frontmatter, "proposal_status", filePath);
372
+ requireEnum(proposalStatus, "proposal_status", PROPOSAL_STATUS_VALUES, filePath);
373
+ const proposalKind = expectString(frontmatter, "proposal_kind", filePath);
374
+ requireEnum(proposalKind, "proposal_kind", PROPOSAL_KIND_VALUES, filePath);
375
+ validatePortableRefs(optionalList(frontmatter, "evidence_refs", filePath), "evidence_refs", filePath);
376
+ break;
377
+ }
378
+ }
379
+ }
380
+ function extractAgentAttributes(type, frontmatter) {
381
+ if (!isAgentFileType(type)) {
382
+ return {};
383
+ }
384
+ const attributes = {};
385
+ for (const key of exports.AGENT_ATTRIBUTE_KEY_ORDER[type]) {
386
+ const value = frontmatter[key];
387
+ if (value !== undefined) {
388
+ attributes[key] = value;
389
+ }
390
+ }
391
+ return attributes;
392
+ }
@@ -5,12 +5,15 @@ const id_1 = require("../util/id");
5
5
  function formatError(filePath, key, message) {
6
6
  return new Error(`${filePath}: ${key} ${message}`);
7
7
  }
8
- function normalizeIdRef(value, filePath, key) {
8
+ function normalizeIdRef(value, filePath, key, options) {
9
9
  const normalized = value.toLowerCase();
10
10
  if (normalized !== value) {
11
11
  throw formatError(filePath, key, "must be lowercase");
12
12
  }
13
- if (!(0, id_1.isCanonicalIdRef)(normalized)) {
13
+ const valid = options.allowPortableRefs
14
+ ? (0, id_1.isPortableIdRef)(normalized)
15
+ : (0, id_1.isCanonicalIdRef)(normalized);
16
+ if (!valid) {
14
17
  throw formatError(filePath, key, `invalid id reference: ${value}`);
15
18
  }
16
19
  return normalized;
@@ -35,7 +38,7 @@ function readStringList(fm, key) {
35
38
  }
36
39
  return value;
37
40
  }
38
- function extractEdges(frontmatter, filePath) {
41
+ function extractEdges(frontmatter, filePath, options = {}) {
39
42
  const epic = readString(frontmatter, "epic");
40
43
  const parent = readString(frontmatter, "parent");
41
44
  const prev = readString(frontmatter, "prev");
@@ -44,21 +47,21 @@ function extractEdges(frontmatter, filePath) {
44
47
  const blocked_by = readStringList(frontmatter, "blocked_by") ?? [];
45
48
  const blocks = readStringList(frontmatter, "blocks") ?? [];
46
49
  const edges = {
47
- relates: relates.map((value) => normalizeIdRef(value, filePath, "relates")),
48
- blocked_by: blocked_by.map((value) => normalizeIdRef(value, filePath, "blocked_by")),
49
- blocks: blocks.map((value) => normalizeIdRef(value, filePath, "blocks")),
50
+ relates: relates.map((value) => normalizeIdRef(value, filePath, "relates", options)),
51
+ blocked_by: blocked_by.map((value) => normalizeIdRef(value, filePath, "blocked_by", options)),
52
+ blocks: blocks.map((value) => normalizeIdRef(value, filePath, "blocks", options)),
50
53
  };
51
54
  if (epic) {
52
- edges.epic = normalizeIdRef(epic, filePath, "epic");
55
+ edges.epic = normalizeIdRef(epic, filePath, "epic", options);
53
56
  }
54
57
  if (parent) {
55
- edges.parent = normalizeIdRef(parent, filePath, "parent");
58
+ edges.parent = normalizeIdRef(parent, filePath, "parent", options);
56
59
  }
57
60
  if (prev) {
58
- edges.prev = normalizeIdRef(prev, filePath, "prev");
61
+ edges.prev = normalizeIdRef(prev, filePath, "prev", options);
59
62
  }
60
63
  if (next) {
61
- edges.next = normalizeIdRef(next, filePath, "next");
64
+ edges.next = normalizeIdRef(next, filePath, "next", options);
62
65
  }
63
66
  return edges;
64
67
  }
@@ -15,6 +15,39 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
15
15
  "prev",
16
16
  "next",
17
17
  "supersedes",
18
+ "version",
19
+ "role",
20
+ "runtime_mode",
21
+ "work_contracts",
22
+ "requested_capabilities",
23
+ "resource_profile",
24
+ "update_policy",
25
+ "agent_id",
26
+ "kind",
27
+ "pricing_model",
28
+ "required_capabilities",
29
+ "inputs",
30
+ "outputs",
31
+ "receipt_required",
32
+ "work_id",
33
+ "work_version",
34
+ "requester",
35
+ "order_status",
36
+ "request_ref",
37
+ "work_order_id",
38
+ "receipt_status",
39
+ "outcome",
40
+ "cost_ref",
41
+ "target_id",
42
+ "sentiment",
43
+ "feedback_status",
44
+ "source_ref",
45
+ "receipt_id",
46
+ "dispute_status",
47
+ "severity",
48
+ "proposal_status",
49
+ "proposal_kind",
50
+ "evidence_refs",
18
51
  "tags",
19
52
  "owners",
20
53
  "links",
@@ -82,6 +82,7 @@ function buildIndex(root, config, options = {}) {
82
82
  refs: node.refs,
83
83
  aliases: node.aliases,
84
84
  skills: node.skills,
85
+ attributes: node.attributes,
85
86
  path: relPath,
86
87
  edges: normalizedEdges,
87
88
  };
@@ -4,13 +4,26 @@ 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
+ const agent_file_types_1 = require("./agent_file_types");
11
8
  const id_1 = require("../util/id");
12
9
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
13
10
  const DEC_ID_RE = /^dec-[0-9]+$/;
11
+ exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
12
+ exports.DEC_TYPES = new Set(["dec"]);
13
+ exports.ALLOWED_TYPES = new Set([
14
+ "rule",
15
+ "prd",
16
+ "edd",
17
+ "dec",
18
+ "prop",
19
+ "epic",
20
+ "feat",
21
+ "task",
22
+ "bug",
23
+ "checkpoint",
24
+ "test",
25
+ ...agent_file_types_1.AGENT_FILE_TYPES,
26
+ ]);
14
27
  const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
15
28
  const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
16
29
  function formatError(filePath, message) {
@@ -72,6 +85,12 @@ function requireIdFormat(value, key, filePath) {
72
85
  }
73
86
  return value;
74
87
  }
88
+ function requirePortableIdFormat(value, key, filePath) {
89
+ if (!(0, id_1.isPortableId)(value)) {
90
+ throw formatError(filePath, `${key} must be a lowercase portable id`);
91
+ }
92
+ return value;
93
+ }
75
94
  function requireDate(value, key, filePath) {
76
95
  if (!DATE_RE.test(value)) {
77
96
  throw formatError(filePath, `${key} must be YYYY-MM-DD`);
@@ -88,12 +107,13 @@ function parsePriority(value, filePath, min, max) {
88
107
  }
89
108
  return parsed;
90
109
  }
91
- function normalizeIdList(values, key, filePath) {
110
+ function normalizeIdList(values, key, filePath, allowPortableIds = false) {
92
111
  return values.map((value) => {
93
112
  if (value !== value.toLowerCase()) {
94
113
  throw formatError(filePath, `${key} entries must be lowercase`);
95
114
  }
96
- if (!isValidId(value)) {
115
+ const valid = allowPortableIds ? (0, id_1.isPortableId)(value) : isValidId(value);
116
+ if (!valid) {
97
117
  throw formatError(filePath, `${key} entries must match <prefix>-<number> or reserved id`);
98
118
  }
99
119
  return value;
@@ -142,19 +162,24 @@ function validateTemplateKeys(frontmatter, schema, filePath) {
142
162
  function parseNode(content, filePath, options) {
143
163
  const { frontmatter, body } = (0, frontmatter_1.parseFrontmatter)(content, filePath);
144
164
  const type = requireLowercase(expectString(frontmatter, "type", filePath), "type", filePath);
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(", ")}`);
165
+ if (!exports.ALLOWED_TYPES.has(type)) {
166
+ throw formatError(filePath, `type must be one of ${Array.from(exports.ALLOWED_TYPES).join(", ")}`);
147
167
  }
168
+ const isAgentType = (0, agent_file_types_1.isAgentFileType)(type);
148
169
  const schema = requireTemplateSchema(type, options.templateSchemas, filePath);
149
170
  validateTemplateKeys(frontmatter, schema, filePath);
150
- const id = requireIdFormat(requireLowercase(expectString(frontmatter, "id", filePath), "id", filePath), "id", filePath);
171
+ (0, agent_file_types_1.validateAgentFrontmatter)(type, frontmatter, filePath);
172
+ const idValue = requireLowercase(expectString(frontmatter, "id", filePath), "id", filePath);
173
+ const id = isAgentType
174
+ ? requirePortableIdFormat(idValue, "id", filePath)
175
+ : requireIdFormat(idValue, "id", filePath);
151
176
  const title = expectString(frontmatter, "title", filePath);
152
177
  const created = requireDate(expectString(frontmatter, "created", filePath), "created", filePath);
153
178
  const updated = requireDate(expectString(frontmatter, "updated", filePath), "updated", filePath);
154
179
  const statusValue = optionalString(frontmatter, "status", filePath);
155
180
  let status = undefined;
156
181
  const workStatus = new Set(options.workStatusEnum.map((value) => value.toLowerCase()));
157
- if (template_schema_1.WORK_TYPES.has(type)) {
182
+ if (exports.WORK_TYPES.has(type)) {
158
183
  if (!statusValue) {
159
184
  throw formatError(filePath, "status is required for work items");
160
185
  }
@@ -164,7 +189,7 @@ function parseNode(content, filePath, options) {
164
189
  }
165
190
  status = normalized;
166
191
  }
167
- else if (template_schema_1.DEC_TYPES.has(type)) {
192
+ else if (exports.DEC_TYPES.has(type)) {
168
193
  if (!statusValue) {
169
194
  throw formatError(filePath, "status is required for decision records");
170
195
  }
@@ -180,7 +205,7 @@ function parseNode(content, filePath, options) {
180
205
  const priorityValue = optionalString(frontmatter, "priority", filePath);
181
206
  let priority = undefined;
182
207
  if (priorityValue !== undefined) {
183
- if (!template_schema_1.WORK_TYPES.has(type)) {
208
+ if (!exports.WORK_TYPES.has(type)) {
184
209
  throw formatError(filePath, "priority is only allowed for work items");
185
210
  }
186
211
  priority = parsePriority(priorityValue, filePath, options.priorityMin, options.priorityMax);
@@ -189,17 +214,17 @@ function parseNode(content, filePath, options) {
189
214
  const owners = requireLowercaseList(optionalList(frontmatter, "owners", filePath), "owners", filePath);
190
215
  const links = optionalList(frontmatter, "links", filePath);
191
216
  const artifacts = optionalList(frontmatter, "artifacts", filePath);
192
- const refs = normalizeIdList(optionalList(frontmatter, "refs", filePath), "refs", filePath);
217
+ const refs = normalizeIdList(optionalList(frontmatter, "refs", filePath), "refs", filePath, isAgentType);
193
218
  const aliases = requireLowercaseList(optionalList(frontmatter, "aliases", filePath), "aliases", filePath);
194
219
  const skillsRaw = optionalList(frontmatter, "skills", filePath);
195
220
  const skills = normalizeSkillList(skillsRaw, filePath);
196
- if (skills.length > 0 && !template_schema_1.WORK_TYPES.has(type)) {
221
+ if (skills.length > 0 && !exports.WORK_TYPES.has(type)) {
197
222
  throw formatError(filePath, "skills is only allowed for work items");
198
223
  }
199
224
  normalizeIdList(optionalList(frontmatter, "scope", filePath), "scope", filePath);
200
225
  const supersedesValue = optionalString(frontmatter, "supersedes", filePath);
201
226
  if (supersedesValue !== undefined) {
202
- if (!template_schema_1.DEC_TYPES.has(type)) {
227
+ if (!exports.DEC_TYPES.has(type)) {
203
228
  throw formatError(filePath, "supersedes is only allowed for decision records");
204
229
  }
205
230
  const normalized = requireLowercase(supersedesValue, "supersedes", filePath);
@@ -207,7 +232,8 @@ function parseNode(content, filePath, options) {
207
232
  throw formatError(filePath, "supersedes must be a dec-# id");
208
233
  }
209
234
  }
210
- const edges = (0, edges_1.extractEdges)(frontmatter, filePath);
235
+ const edges = (0, edges_1.extractEdges)(frontmatter, filePath, { allowPortableRefs: isAgentType });
236
+ const attributes = (0, agent_file_types_1.extractAgentAttributes)(type, frontmatter);
211
237
  return {
212
238
  id,
213
239
  type,
@@ -224,6 +250,7 @@ function parseNode(content, filePath, options) {
224
250
  aliases,
225
251
  skills,
226
252
  edges,
253
+ attributes,
227
254
  body,
228
255
  frontmatter,
229
256
  };
@@ -81,6 +81,13 @@ function extractOchatr(frontmatter) {
81
81
  }
82
82
  return extracted;
83
83
  }
84
+ function stripPrefix(values, prefix) {
85
+ const stripped = {};
86
+ for (const [key, value] of Object.entries(values)) {
87
+ stripped[key.slice(prefix.length)] = value;
88
+ }
89
+ return stripped;
90
+ }
84
91
  function hasDirectory(dirPath) {
85
92
  if (!fs_1.default.existsSync(dirPath)) {
86
93
  return false;
@@ -113,6 +120,11 @@ function buildSkillIndexEntry(root, slug, filePath) {
113
120
  const authors = toLowercaseList(optionalList(frontmatter, "authors", filePath));
114
121
  const links = optionalList(frontmatter, "links", filePath);
115
122
  const skillDir = path_1.default.dirname(filePath);
123
+ const ochatr = extractOchatr(frontmatter);
124
+ const extensions = {};
125
+ if (Object.keys(ochatr).length > 0) {
126
+ extensions.ochatr = stripPrefix(ochatr, "ochatr_");
127
+ }
116
128
  return {
117
129
  slug,
118
130
  id: `skill:${slug}`,
@@ -128,7 +140,8 @@ function buildSkillIndexEntry(root, slug, filePath) {
128
140
  path: path_1.default.relative(root, filePath),
129
141
  has_scripts: hasDirectory(path_1.default.join(skillDir, "scripts")),
130
142
  has_references: hasDirectory(path_1.default.join(skillDir, "references")),
131
- ochatr: extractOchatr(frontmatter),
143
+ extensions,
144
+ ochatr,
132
145
  };
133
146
  }
134
147
  function buildSkillsIndex(root, config) {