mdkg 0.0.7 → 0.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/CONTRIBUTING.md +124 -0
  3. package/README.md +33 -10
  4. package/dist/cli.js +80 -32
  5. package/dist/commands/checkpoint.js +19 -2
  6. package/dist/commands/event.js +12 -0
  7. package/dist/commands/new.js +50 -4
  8. package/dist/commands/pack.js +14 -0
  9. package/dist/commands/query_output.js +2 -0
  10. package/dist/commands/search.js +8 -0
  11. package/dist/commands/show.js +7 -0
  12. package/dist/commands/skill.js +80 -12
  13. package/dist/commands/task.js +41 -4
  14. package/dist/commands/validate.js +31 -3
  15. package/dist/commands/workspace.js +105 -13
  16. package/dist/core/config.js +217 -22
  17. package/dist/core/migrate.js +39 -5
  18. package/dist/core/workspace_path.js +41 -0
  19. package/dist/graph/agent_file_types.js +392 -0
  20. package/dist/graph/edges.js +13 -10
  21. package/dist/graph/frontmatter.js +33 -0
  22. package/dist/graph/indexer.js +1 -0
  23. package/dist/graph/node.js +21 -5
  24. package/dist/graph/skills_indexer.js +14 -1
  25. package/dist/graph/validate_graph.js +302 -2
  26. package/dist/init/AGENT_START.md +13 -0
  27. package/dist/init/CLI_COMMAND_MATRIX.md +43 -1
  28. package/dist/init/README.md +7 -0
  29. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +1 -1
  30. package/dist/init/templates/default/dispute.md +31 -0
  31. package/dist/init/templates/default/feedback.md +27 -0
  32. package/dist/init/templates/default/proposal.md +35 -0
  33. package/dist/init/templates/default/receipt.md +31 -0
  34. package/dist/init/templates/default/spec.md +43 -0
  35. package/dist/init/templates/default/work.md +44 -0
  36. package/dist/init/templates/default/work_order.md +32 -0
  37. package/dist/pack/export_json.js +3 -0
  38. package/dist/pack/export_md.js +9 -0
  39. package/dist/pack/export_xml.js +9 -0
  40. package/dist/pack/order.js +7 -0
  41. package/dist/pack/pack.js +1 -0
  42. package/dist/util/argparse.js +1 -0
  43. package/dist/util/id.js +19 -0
  44. package/package.json +9 -2
  45. 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,6 +4,7 @@ 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 agent_file_types_1 = require("./agent_file_types");
7
8
  const id_1 = require("../util/id");
8
9
  const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
9
10
  const DEC_ID_RE = /^dec-[0-9]+$/;
@@ -21,6 +22,7 @@ exports.ALLOWED_TYPES = new Set([
21
22
  "bug",
22
23
  "checkpoint",
23
24
  "test",
25
+ ...agent_file_types_1.AGENT_FILE_TYPES,
24
26
  ]);
25
27
  const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
26
28
  const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
@@ -83,6 +85,12 @@ function requireIdFormat(value, key, filePath) {
83
85
  }
84
86
  return value;
85
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
+ }
86
94
  function requireDate(value, key, filePath) {
87
95
  if (!DATE_RE.test(value)) {
88
96
  throw formatError(filePath, `${key} must be YYYY-MM-DD`);
@@ -99,12 +107,13 @@ function parsePriority(value, filePath, min, max) {
99
107
  }
100
108
  return parsed;
101
109
  }
102
- function normalizeIdList(values, key, filePath) {
110
+ function normalizeIdList(values, key, filePath, allowPortableIds = false) {
103
111
  return values.map((value) => {
104
112
  if (value !== value.toLowerCase()) {
105
113
  throw formatError(filePath, `${key} entries must be lowercase`);
106
114
  }
107
- if (!isValidId(value)) {
115
+ const valid = allowPortableIds ? (0, id_1.isPortableId)(value) : isValidId(value);
116
+ if (!valid) {
108
117
  throw formatError(filePath, `${key} entries must match <prefix>-<number> or reserved id`);
109
118
  }
110
119
  return value;
@@ -156,9 +165,14 @@ function parseNode(content, filePath, options) {
156
165
  if (!exports.ALLOWED_TYPES.has(type)) {
157
166
  throw formatError(filePath, `type must be one of ${Array.from(exports.ALLOWED_TYPES).join(", ")}`);
158
167
  }
168
+ const isAgentType = (0, agent_file_types_1.isAgentFileType)(type);
159
169
  const schema = requireTemplateSchema(type, options.templateSchemas, filePath);
160
170
  validateTemplateKeys(frontmatter, schema, filePath);
161
- 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);
162
176
  const title = expectString(frontmatter, "title", filePath);
163
177
  const created = requireDate(expectString(frontmatter, "created", filePath), "created", filePath);
164
178
  const updated = requireDate(expectString(frontmatter, "updated", filePath), "updated", filePath);
@@ -200,7 +214,7 @@ function parseNode(content, filePath, options) {
200
214
  const owners = requireLowercaseList(optionalList(frontmatter, "owners", filePath), "owners", filePath);
201
215
  const links = optionalList(frontmatter, "links", filePath);
202
216
  const artifacts = optionalList(frontmatter, "artifacts", filePath);
203
- const refs = normalizeIdList(optionalList(frontmatter, "refs", filePath), "refs", filePath);
217
+ const refs = normalizeIdList(optionalList(frontmatter, "refs", filePath), "refs", filePath, isAgentType);
204
218
  const aliases = requireLowercaseList(optionalList(frontmatter, "aliases", filePath), "aliases", filePath);
205
219
  const skillsRaw = optionalList(frontmatter, "skills", filePath);
206
220
  const skills = normalizeSkillList(skillsRaw, filePath);
@@ -218,7 +232,8 @@ function parseNode(content, filePath, options) {
218
232
  throw formatError(filePath, "supersedes must be a dec-# id");
219
233
  }
220
234
  }
221
- 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);
222
237
  return {
223
238
  id,
224
239
  type,
@@ -235,6 +250,7 @@ function parseNode(content, filePath, options) {
235
250
  aliases,
236
251
  skills,
237
252
  edges,
253
+ attributes,
238
254
  body,
239
255
  frontmatter,
240
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) {