mdkg 0.0.8 → 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.
- package/CHANGELOG.md +56 -0
- package/CONTRIBUTING.md +124 -0
- package/README.md +33 -14
- package/dist/cli.js +80 -32
- package/dist/commands/checkpoint.js +19 -2
- package/dist/commands/event.js +12 -0
- package/dist/commands/new.js +57 -21
- package/dist/commands/pack.js +14 -0
- package/dist/commands/query_output.js +2 -0
- package/dist/commands/search.js +8 -0
- package/dist/commands/show.js +7 -0
- package/dist/commands/skill.js +80 -12
- package/dist/commands/task.js +42 -12
- package/dist/commands/validate.js +31 -3
- package/dist/commands/workspace.js +105 -13
- package/dist/core/config.js +217 -22
- package/dist/core/migrate.js +39 -5
- package/dist/core/workspace_path.js +41 -0
- package/dist/graph/agent_file_types.js +392 -0
- package/dist/graph/edges.js +13 -10
- package/dist/graph/frontmatter.js +33 -0
- package/dist/graph/indexer.js +1 -0
- package/dist/graph/node.js +43 -16
- package/dist/graph/skills_indexer.js +14 -1
- package/dist/graph/template_schema.js +13 -126
- package/dist/graph/validate_graph.js +302 -2
- package/dist/init/AGENT_START.md +13 -2
- package/dist/init/CLI_COMMAND_MATRIX.md +43 -1
- package/dist/init/README.md +7 -0
- package/dist/init/core/rule-6-templates-and-schemas.md +1 -3
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +1 -1
- package/dist/init/templates/default/dispute.md +31 -0
- package/dist/init/templates/default/feedback.md +27 -0
- package/dist/init/templates/default/proposal.md +35 -0
- package/dist/init/templates/default/receipt.md +31 -0
- package/dist/init/templates/default/spec.md +43 -0
- package/dist/init/templates/default/work.md +44 -0
- package/dist/init/templates/default/work_order.md +32 -0
- package/dist/pack/export_json.js +3 -0
- package/dist/pack/export_md.js +9 -0
- package/dist/pack/export_xml.js +9 -0
- package/dist/pack/order.js +7 -0
- package/dist/pack/pack.js +1 -0
- package/dist/templates/loader.js +2 -2
- package/dist/util/argparse.js +1 -0
- package/dist/util/id.js +19 -0
- package/package.json +9 -2
- 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
|
+
}
|
package/dist/graph/edges.js
CHANGED
|
@@ -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
|
-
|
|
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",
|
package/dist/graph/indexer.js
CHANGED
package/dist/graph/node.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
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 (!
|
|
146
|
-
throw formatError(filePath, `type must be one of ${Array.from(
|
|
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
|
-
|
|
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 (
|
|
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 (
|
|
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 (!
|
|
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 && !
|
|
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 (!
|
|
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
|
-
|
|
143
|
+
extensions,
|
|
144
|
+
ochatr,
|
|
132
145
|
};
|
|
133
146
|
}
|
|
134
147
|
function buildSkillsIndex(root, config) {
|