mdkg 0.3.6 → 0.3.8
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 +93 -0
- package/CLI_COMMAND_MATRIX.md +132 -24
- package/README.md +80 -25
- package/dist/cli.js +193 -36
- package/dist/command-contract.json +882 -122
- package/dist/commands/bundle.js +2 -0
- package/dist/commands/capability.js +1 -0
- package/dist/commands/checkpoint.js +139 -1
- package/dist/commands/db.js +8 -0
- package/dist/commands/doctor.js +7 -7
- package/dist/commands/format.js +155 -0
- package/dist/commands/goal.js +60 -0
- package/dist/commands/graph.js +148 -0
- package/dist/commands/handoff.js +301 -0
- package/dist/commands/mcp.js +9 -0
- package/dist/commands/new.js +12 -3
- package/dist/commands/pack.js +3 -1
- package/dist/commands/query_output.js +2 -0
- package/dist/commands/show.js +8 -0
- package/dist/commands/spec.js +76 -17
- package/dist/commands/status.js +1 -0
- package/dist/commands/subgraph.js +7 -4
- package/dist/commands/task.js +2 -0
- package/dist/commands/upgrade.js +128 -3
- package/dist/commands/validate.js +268 -6
- package/dist/commands/work.js +176 -5
- package/dist/core/project_db_queue_contract.js +101 -0
- package/dist/graph/agent_file_types.js +59 -20
- package/dist/graph/capabilities_indexer.js +45 -3
- package/dist/graph/edges.js +15 -0
- package/dist/graph/frontmatter.js +4 -1
- package/dist/graph/indexer.js +13 -0
- package/dist/graph/node.js +12 -1
- package/dist/graph/sqlite_index.js +2 -0
- package/dist/graph/subgraphs.js +2 -0
- package/dist/graph/template_schema.js +37 -17
- package/dist/graph/validate_graph.js +16 -5
- package/dist/graph/visibility.js +6 -0
- package/dist/init/AGENT_START.md +9 -6
- package/dist/init/CLI_COMMAND_MATRIX.md +50 -16
- package/dist/init/README.md +26 -9
- package/dist/init/init-manifest.json +67 -12
- package/dist/init/templates/default/bug.md +2 -0
- package/dist/init/templates/default/chk.md +3 -0
- package/dist/init/templates/default/epic.md +2 -0
- package/dist/init/templates/default/feat.md +2 -0
- package/dist/init/templates/default/goal.md +3 -0
- package/dist/init/templates/default/manifest.md +45 -0
- package/dist/init/templates/default/spike.md +2 -0
- package/dist/init/templates/default/task.md +2 -0
- package/dist/init/templates/default/test.md +2 -0
- package/dist/init/templates/specs/agent.MANIFEST.md +80 -0
- package/dist/init/templates/specs/api.MANIFEST.md +33 -0
- package/dist/init/templates/specs/base.MANIFEST.md +120 -0
- package/dist/init/templates/specs/capability.MANIFEST.md +45 -0
- package/dist/init/templates/specs/integration.MANIFEST.md +25 -0
- package/dist/init/templates/specs/model.MANIFEST.md +21 -0
- package/dist/init/templates/specs/project.MANIFEST.md +39 -0
- package/dist/init/templates/specs/runtime-agent.MANIFEST.md +49 -0
- package/dist/init/templates/specs/runtime-image.MANIFEST.md +21 -0
- package/dist/init/templates/specs/tool.MANIFEST.md +25 -0
- package/dist/pack/export_json.js +20 -8
- package/dist/pack/export_md.js +15 -4
- package/dist/pack/export_xml.js +9 -4
- package/dist/pack/metrics.js +12 -4
- package/dist/pack/pack.js +9 -1
- package/dist/util/argparse.js +3 -0
- package/package.json +21 -3
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.projectDbQueueAdapterContract = projectDbQueueAdapterContract;
|
|
4
|
+
function projectDbQueueAdapterContract() {
|
|
5
|
+
return {
|
|
6
|
+
schema_version: 1,
|
|
7
|
+
contract_id: "mdkg.project_db.queue.adapter.v1",
|
|
8
|
+
stability: "public",
|
|
9
|
+
boundary: {
|
|
10
|
+
role: "durable local delivery state for mdkg project DB integrations",
|
|
11
|
+
canonical_history: "queue rows are not canonical event history or durable runtime transcripts",
|
|
12
|
+
raw_payload_policy: "store compact refs and redacted payloads; do not store raw secrets, prompts, provider payloads, or bulky runtime artifacts",
|
|
13
|
+
internal_surfaces: ["event", "receipt", "reducer", "writer_lease", "materializer"],
|
|
14
|
+
},
|
|
15
|
+
runtime: {
|
|
16
|
+
database: "node:sqlite",
|
|
17
|
+
transactions: "short BEGIN IMMEDIATE transactions for writes and claims",
|
|
18
|
+
external_dependencies: [],
|
|
19
|
+
},
|
|
20
|
+
commands: [
|
|
21
|
+
"mdkg db queue create <queue>",
|
|
22
|
+
"mdkg db queue pause <queue>",
|
|
23
|
+
"mdkg db queue resume <queue>",
|
|
24
|
+
"mdkg db queue enqueue <queue> <message-id>",
|
|
25
|
+
"mdkg db queue claim <queue>",
|
|
26
|
+
"mdkg db queue ack <queue> <message-id>",
|
|
27
|
+
"mdkg db queue fail <queue> <message-id>",
|
|
28
|
+
"mdkg db queue dead-letter <queue> <message-id>",
|
|
29
|
+
"mdkg db queue release-expired [queue]",
|
|
30
|
+
"mdkg db queue stats [queue]",
|
|
31
|
+
"mdkg db queue list <queue>",
|
|
32
|
+
"mdkg db queue show <queue> <message-id>",
|
|
33
|
+
"mdkg db queue contract",
|
|
34
|
+
],
|
|
35
|
+
queue_control: {
|
|
36
|
+
table: "project_queue",
|
|
37
|
+
states: ["active", "paused"],
|
|
38
|
+
paused_behavior: {
|
|
39
|
+
rejects: ["enqueue", "claim"],
|
|
40
|
+
allows: ["ack", "fail", "dead-letter", "release-expired", "stats", "list", "show"],
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
message: {
|
|
44
|
+
table: "project_queue_message",
|
|
45
|
+
states: ["ready", "leased", "acked", "dead_letter"],
|
|
46
|
+
fields: [
|
|
47
|
+
"queue_name",
|
|
48
|
+
"message_id",
|
|
49
|
+
"dedupe_key",
|
|
50
|
+
"payload_json",
|
|
51
|
+
"payload_hash",
|
|
52
|
+
"status",
|
|
53
|
+
"available_at_ms",
|
|
54
|
+
"attempt_count",
|
|
55
|
+
"max_attempts",
|
|
56
|
+
"lease_owner",
|
|
57
|
+
"lease_deadline_ms",
|
|
58
|
+
"created_at_ms",
|
|
59
|
+
"updated_at_ms",
|
|
60
|
+
"last_error",
|
|
61
|
+
],
|
|
62
|
+
terminal_states: ["acked", "dead_letter"],
|
|
63
|
+
},
|
|
64
|
+
payload_hash: {
|
|
65
|
+
algorithm: "sha256",
|
|
66
|
+
encoding: "sha256:<64 lowercase hex chars>",
|
|
67
|
+
canonicalization: "payload JSON is serialized deterministically with object keys sorted before hashing and storage",
|
|
68
|
+
},
|
|
69
|
+
dedupe: {
|
|
70
|
+
key: "queue_name + dedupe_key",
|
|
71
|
+
scope: "only non-null dedupe keys participate in the partial unique index",
|
|
72
|
+
duplicate_behavior: "enqueue with an existing dedupe key returns the existing message without replacing payload_json or payload_hash",
|
|
73
|
+
},
|
|
74
|
+
claim: {
|
|
75
|
+
selection: "oldest ready or expired leased message ordered by available_at_ms, created_at_ms, then message_id",
|
|
76
|
+
lease: "claim sets status=leased, lease_owner, and lease_deadline_ms; ack/fail/dead-letter must use the same lease owner",
|
|
77
|
+
transactional: true,
|
|
78
|
+
},
|
|
79
|
+
settlement: {
|
|
80
|
+
ack: "leased message becomes acked and clears lease owner/deadline",
|
|
81
|
+
fail: "leased message increments attempt_count; if attempts remain it becomes ready at now + retry_after_ms, otherwise it becomes dead_letter",
|
|
82
|
+
dead_letter: "leased message becomes dead_letter immediately and records last_error",
|
|
83
|
+
release_expired: "expired leased messages become ready and clear lease owner/deadline without changing attempt_count",
|
|
84
|
+
},
|
|
85
|
+
stats: {
|
|
86
|
+
counters: ["total", "ready", "leased", "acked", "dead_letter", "ready_available", "leased_expired"],
|
|
87
|
+
snapshot_summary: ["total", "ready", "leased", "acked", "dead_letter", "paused_ready", "active_ready"],
|
|
88
|
+
},
|
|
89
|
+
snapshot_policy: {
|
|
90
|
+
drain: "default seal policy; requires no ready or leased delivery work",
|
|
91
|
+
paused: "allows ready messages only when their queues are paused; leased messages are never allowed",
|
|
92
|
+
},
|
|
93
|
+
adapter_guidance: [
|
|
94
|
+
"create queues explicitly before enqueueing integration work",
|
|
95
|
+
"use dedupe keys for idempotent delivery",
|
|
96
|
+
"treat message payloads as refs and redacted envelopes, not canonical runtime state",
|
|
97
|
+
"settle or pause queues before committing sealed project DB state",
|
|
98
|
+
"use stats/list/show for operator review and avoid direct SQL coupling",
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
@@ -3,15 +3,20 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
-
exports.AGENT_ATTRIBUTE_KEY_ORDER = exports.AGENT_FILE_BASENAMES = exports.AGENT_FILE_TYPES = void 0;
|
|
6
|
+
exports.AGENT_ATTRIBUTE_KEY_ORDER = exports.AGENT_FILE_BASENAMES = exports.AGENT_FILE_TYPES = exports.LEGACY_SPEC_BASENAME = exports.CANONICAL_MANIFEST_BASENAME = void 0;
|
|
7
7
|
exports.isAgentFileType = isAgentFileType;
|
|
8
|
+
exports.isManifestSemanticType = isManifestSemanticType;
|
|
9
|
+
exports.collectManifestSiblingConflicts = collectManifestSiblingConflicts;
|
|
8
10
|
exports.validateAgentFileName = validateAgentFileName;
|
|
9
11
|
exports.validateAgentFrontmatter = validateAgentFrontmatter;
|
|
10
12
|
exports.extractAgentAttributes = extractAgentAttributes;
|
|
11
13
|
const path_1 = __importDefault(require("path"));
|
|
12
14
|
const id_1 = require("../util/id");
|
|
13
15
|
const refs_1 = require("../util/refs");
|
|
16
|
+
exports.CANONICAL_MANIFEST_BASENAME = "MANIFEST.md";
|
|
17
|
+
exports.LEGACY_SPEC_BASENAME = "SPEC.md";
|
|
14
18
|
exports.AGENT_FILE_TYPES = [
|
|
19
|
+
"manifest",
|
|
15
20
|
"spec",
|
|
16
21
|
"work",
|
|
17
22
|
"work_order",
|
|
@@ -21,7 +26,8 @@ exports.AGENT_FILE_TYPES = [
|
|
|
21
26
|
"proposal",
|
|
22
27
|
];
|
|
23
28
|
exports.AGENT_FILE_BASENAMES = {
|
|
24
|
-
|
|
29
|
+
manifest: exports.CANONICAL_MANIFEST_BASENAME,
|
|
30
|
+
spec: exports.LEGACY_SPEC_BASENAME,
|
|
25
31
|
work: "WORK.md",
|
|
26
32
|
work_order: "WORK_ORDER.md",
|
|
27
33
|
receipt: "RECEIPT.md",
|
|
@@ -29,23 +35,25 @@ exports.AGENT_FILE_BASENAMES = {
|
|
|
29
35
|
dispute: "DISPUTE.md",
|
|
30
36
|
proposal: "PROPOSAL.md",
|
|
31
37
|
};
|
|
38
|
+
const MANIFEST_ATTRIBUTE_KEYS = [
|
|
39
|
+
"version",
|
|
40
|
+
"spec_kind",
|
|
41
|
+
"role",
|
|
42
|
+
"runtime_mode",
|
|
43
|
+
"work_contracts",
|
|
44
|
+
"requested_capabilities",
|
|
45
|
+
"skill_refs",
|
|
46
|
+
"tool_refs",
|
|
47
|
+
"model_refs",
|
|
48
|
+
"wasm_component_refs",
|
|
49
|
+
"runtime_image_refs",
|
|
50
|
+
"subagent_refs",
|
|
51
|
+
"resource_profile",
|
|
52
|
+
"update_policy",
|
|
53
|
+
];
|
|
32
54
|
exports.AGENT_ATTRIBUTE_KEY_ORDER = {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"spec_kind",
|
|
36
|
-
"role",
|
|
37
|
-
"runtime_mode",
|
|
38
|
-
"work_contracts",
|
|
39
|
-
"requested_capabilities",
|
|
40
|
-
"skill_refs",
|
|
41
|
-
"tool_refs",
|
|
42
|
-
"model_refs",
|
|
43
|
-
"wasm_component_refs",
|
|
44
|
-
"runtime_image_refs",
|
|
45
|
-
"subagent_refs",
|
|
46
|
-
"resource_profile",
|
|
47
|
-
"update_policy",
|
|
48
|
-
],
|
|
55
|
+
manifest: MANIFEST_ATTRIBUTE_KEYS,
|
|
56
|
+
spec: MANIFEST_ATTRIBUTE_KEYS,
|
|
49
57
|
work: [
|
|
50
58
|
"version",
|
|
51
59
|
"agent_id",
|
|
@@ -207,9 +215,39 @@ function formatError(filePath, message) {
|
|
|
207
215
|
function isAgentFileType(type) {
|
|
208
216
|
return exports.AGENT_FILE_TYPES.includes(type);
|
|
209
217
|
}
|
|
218
|
+
function isManifestSemanticType(type) {
|
|
219
|
+
return type === "manifest" || type === "spec";
|
|
220
|
+
}
|
|
221
|
+
function collectManifestSiblingConflicts(filePaths, formatDir) {
|
|
222
|
+
const basenamesByDir = new Map();
|
|
223
|
+
for (const filePath of filePaths) {
|
|
224
|
+
const basename = path_1.default.basename(filePath);
|
|
225
|
+
if (basename !== exports.CANONICAL_MANIFEST_BASENAME && basename !== exports.LEGACY_SPEC_BASENAME) {
|
|
226
|
+
continue;
|
|
227
|
+
}
|
|
228
|
+
const dirPath = path_1.default.dirname(filePath);
|
|
229
|
+
const basenames = basenamesByDir.get(dirPath) ?? new Set();
|
|
230
|
+
basenames.add(basename);
|
|
231
|
+
basenamesByDir.set(dirPath, basenames);
|
|
232
|
+
}
|
|
233
|
+
const conflicts = [];
|
|
234
|
+
for (const [dirPath, basenames] of Array.from(basenamesByDir.entries()).sort()) {
|
|
235
|
+
if (basenames.has(exports.CANONICAL_MANIFEST_BASENAME) && basenames.has(exports.LEGACY_SPEC_BASENAME)) {
|
|
236
|
+
conflicts.push(`${formatDir(dirPath)}: MANIFEST.md and SPEC.md cannot both exist in the same logical Omni unit; keep MANIFEST.md and remove the legacy SPEC.md alias`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
return conflicts;
|
|
240
|
+
}
|
|
210
241
|
function validateAgentFileName(type, filePath) {
|
|
242
|
+
const basename = path_1.default.basename(filePath);
|
|
243
|
+
if (type === "spec" && basename === exports.CANONICAL_MANIFEST_BASENAME) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
211
246
|
const expected = exports.AGENT_FILE_BASENAMES[type];
|
|
212
|
-
if (
|
|
247
|
+
if (basename !== expected) {
|
|
248
|
+
if (isManifestSemanticType(type)) {
|
|
249
|
+
throw formatError(filePath, `manifest files must be named MANIFEST.md (canonical) or SPEC.md (legacy alias); ${type} files must be named ${expected}`);
|
|
250
|
+
}
|
|
213
251
|
throw formatError(filePath, `${type} files must be named ${expected}`);
|
|
214
252
|
}
|
|
215
253
|
}
|
|
@@ -303,7 +341,7 @@ function validateSpecKind(value, filePath) {
|
|
|
303
341
|
}
|
|
304
342
|
const route = DOCUMENTATION_ONLY_SPEC_KIND_ROUTES[value];
|
|
305
343
|
if (route) {
|
|
306
|
-
throw formatError(filePath, `spec_kind ${value} is documentation-only; ${route}.
|
|
344
|
+
throw formatError(filePath, `spec_kind ${value} is documentation-only; ${route}. MANIFEST.md must define a reusable invocable capability surface; legacy SPEC.md follows the same contract.`);
|
|
307
345
|
}
|
|
308
346
|
throw formatError(filePath, `spec_kind must be one of ${Array.from(SPEC_KIND_VALUES).join(", ")}; documentation-only records belong in normal mdkg nodes such as task, test, epic, goal, checkpoint, EDD, PRD, DEC, bug, feedback, dispute, or proposal.`);
|
|
309
347
|
}
|
|
@@ -398,6 +436,7 @@ function validateAgentFrontmatter(type, frontmatter, filePath) {
|
|
|
398
436
|
const version = expectString(frontmatter, "version", filePath);
|
|
399
437
|
requireSemver(version, "version", filePath);
|
|
400
438
|
switch (type) {
|
|
439
|
+
case "manifest":
|
|
401
440
|
case "spec": {
|
|
402
441
|
const specKind = optionalString(frontmatter, "spec_kind", filePath);
|
|
403
442
|
if (specKind) {
|
|
@@ -10,6 +10,7 @@ const crypto_1 = __importDefault(require("crypto"));
|
|
|
10
10
|
const fs_1 = __importDefault(require("fs"));
|
|
11
11
|
const path_1 = __importDefault(require("path"));
|
|
12
12
|
const indexer_1 = require("./indexer");
|
|
13
|
+
const agent_file_types_1 = require("./agent_file_types");
|
|
13
14
|
const skills_indexer_1 = require("./skills_indexer");
|
|
14
15
|
exports.CAPABILITIES_INDEX_RELATIVE_PATH = ".mdkg/index/capabilities.json";
|
|
15
16
|
exports.CAPABILITY_KINDS = ["skill", "spec", "work", "core", "design"];
|
|
@@ -41,7 +42,7 @@ function workspaceMdkgPrefix(entry) {
|
|
|
41
42
|
return `${wsPath}${mdkgDir}/`;
|
|
42
43
|
}
|
|
43
44
|
function classifyNodeCapability(node, config) {
|
|
44
|
-
if (node.type
|
|
45
|
+
if ((0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
45
46
|
return "spec";
|
|
46
47
|
}
|
|
47
48
|
if (node.type === "work") {
|
|
@@ -115,7 +116,7 @@ function resolveWorkSpecs(index, workNode) {
|
|
|
115
116
|
const candidates = new Map();
|
|
116
117
|
const workRefs = nodeRefSet(workNode);
|
|
117
118
|
for (const node of Object.values(index.nodes)) {
|
|
118
|
-
if (node.type
|
|
119
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type) || node.ws !== workNode.ws) {
|
|
119
120
|
continue;
|
|
120
121
|
}
|
|
121
122
|
const agentId = typeof workNode.attributes.agent_id === "string" ? workNode.attributes.agent_id : undefined;
|
|
@@ -159,9 +160,49 @@ function buildCapabilityLinkage(index, node, kind) {
|
|
|
159
160
|
receipt_qids: receipts.map((receiptNode) => receiptNode.qid),
|
|
160
161
|
};
|
|
161
162
|
}
|
|
163
|
+
function manifestCapabilityMetadata(node) {
|
|
164
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
165
|
+
return undefined;
|
|
166
|
+
}
|
|
167
|
+
const sourceBasename = path_1.default.posix.basename(node.path);
|
|
168
|
+
const compatibilityMode = sourceBasename === agent_file_types_1.LEGACY_SPEC_BASENAME
|
|
169
|
+
? "legacy"
|
|
170
|
+
: node.type === "spec"
|
|
171
|
+
? "transitional"
|
|
172
|
+
: "canonical";
|
|
173
|
+
return {
|
|
174
|
+
semantic_kind: "manifest",
|
|
175
|
+
source_basename: sourceBasename,
|
|
176
|
+
source_type: node.type,
|
|
177
|
+
canonical_basename: agent_file_types_1.CANONICAL_MANIFEST_BASENAME,
|
|
178
|
+
legacy_basename: agent_file_types_1.LEGACY_SPEC_BASENAME,
|
|
179
|
+
compatibility_mode: compatibilityMode,
|
|
180
|
+
legacy: compatibilityMode !== "canonical",
|
|
181
|
+
deprecated: compatibilityMode !== "canonical",
|
|
182
|
+
command_family: "manifest",
|
|
183
|
+
legacy_command_family: "spec",
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function manifestSearchAliases(metadata) {
|
|
187
|
+
if (!metadata) {
|
|
188
|
+
return [];
|
|
189
|
+
}
|
|
190
|
+
return [
|
|
191
|
+
"manifest",
|
|
192
|
+
"manifest.md",
|
|
193
|
+
"MANIFEST.md",
|
|
194
|
+
"manifest capability",
|
|
195
|
+
"MANIFEST.md legacy SPEC.md",
|
|
196
|
+
"spec.md compatibility alias",
|
|
197
|
+
"legacy spec.md",
|
|
198
|
+
metadata.compatibility_mode,
|
|
199
|
+
metadata.source_basename,
|
|
200
|
+
];
|
|
201
|
+
}
|
|
162
202
|
function nodeCapabilityRecord(root, config, index, node, kind, indexedAt) {
|
|
163
203
|
const absolutePath = path_1.default.resolve(root, node.path);
|
|
164
204
|
const content = fs_1.default.readFileSync(absolutePath, "utf8");
|
|
205
|
+
const manifest = kind === "spec" ? manifestCapabilityMetadata(node) : undefined;
|
|
165
206
|
const record = {
|
|
166
207
|
kind,
|
|
167
208
|
workspace: node.ws,
|
|
@@ -172,7 +213,7 @@ function nodeCapabilityRecord(root, config, index, node, kind, indexedAt) {
|
|
|
172
213
|
title: node.title,
|
|
173
214
|
tags: [...node.tags],
|
|
174
215
|
refs: [...node.refs],
|
|
175
|
-
aliases: [...node.aliases],
|
|
216
|
+
aliases: Array.from(new Set([...node.aliases, ...manifestSearchAliases(manifest)])),
|
|
176
217
|
links: [...node.links],
|
|
177
218
|
artifacts: [...node.artifacts],
|
|
178
219
|
updated: node.updated,
|
|
@@ -182,6 +223,7 @@ function nodeCapabilityRecord(root, config, index, node, kind, indexedAt) {
|
|
|
182
223
|
node_type: node.type,
|
|
183
224
|
};
|
|
184
225
|
if (kind === "spec") {
|
|
226
|
+
record.manifest = manifest;
|
|
185
227
|
record.spec = pickAttributes(node.attributes, [
|
|
186
228
|
"version",
|
|
187
229
|
"spec_kind",
|
package/dist/graph/edges.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.extractEdges = extractEdges;
|
|
4
4
|
const id_1 = require("../util/id");
|
|
5
|
+
const refs_1 = require("../util/refs");
|
|
5
6
|
function formatError(filePath, key, message) {
|
|
6
7
|
return new Error(`${filePath}: ${key} ${message}`);
|
|
7
8
|
}
|
|
@@ -18,6 +19,12 @@ function normalizeIdRef(value, filePath, key, options) {
|
|
|
18
19
|
}
|
|
19
20
|
return normalized;
|
|
20
21
|
}
|
|
22
|
+
function normalizeSemanticRef(value, filePath, key) {
|
|
23
|
+
if (!(0, refs_1.validatePortableOrUriRef)(value)) {
|
|
24
|
+
throw formatError(filePath, key, `invalid semantic reference: ${value}`);
|
|
25
|
+
}
|
|
26
|
+
return value.includes("://") ? value : value.toLowerCase();
|
|
27
|
+
}
|
|
21
28
|
function readString(fm, key) {
|
|
22
29
|
const value = fm[key];
|
|
23
30
|
if (value === undefined) {
|
|
@@ -46,10 +53,18 @@ function extractEdges(frontmatter, filePath, options = {}) {
|
|
|
46
53
|
const relates = readStringList(frontmatter, "relates") ?? [];
|
|
47
54
|
const blocked_by = readStringList(frontmatter, "blocked_by") ?? [];
|
|
48
55
|
const blocks = readStringList(frontmatter, "blocks") ?? [];
|
|
56
|
+
const context_refs = options.includeSemanticRefs
|
|
57
|
+
? readStringList(frontmatter, "context_refs") ?? []
|
|
58
|
+
: [];
|
|
59
|
+
const evidence_refs = options.includeSemanticRefs
|
|
60
|
+
? readStringList(frontmatter, "evidence_refs") ?? []
|
|
61
|
+
: [];
|
|
49
62
|
const edges = {
|
|
50
63
|
relates: relates.map((value) => normalizeIdRef(value, filePath, "relates", options)),
|
|
51
64
|
blocked_by: blocked_by.map((value) => normalizeIdRef(value, filePath, "blocked_by", options)),
|
|
52
65
|
blocks: blocks.map((value) => normalizeIdRef(value, filePath, "blocks", options)),
|
|
66
|
+
context_refs: context_refs.map((value) => normalizeSemanticRef(value, filePath, "context_refs")),
|
|
67
|
+
evidence_refs: evidence_refs.map((value) => normalizeSemanticRef(value, filePath, "evidence_refs")),
|
|
53
68
|
};
|
|
54
69
|
if (epic) {
|
|
55
70
|
edges.epic = normalizeIdRef(epic, filePath, "epic", options);
|
|
@@ -8,12 +8,14 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
|
|
|
8
8
|
"id",
|
|
9
9
|
"type",
|
|
10
10
|
"title",
|
|
11
|
+
"checkpoint_kind",
|
|
11
12
|
"status",
|
|
12
13
|
"priority",
|
|
13
14
|
"goal_state",
|
|
14
15
|
"goal_condition",
|
|
15
16
|
"scope_refs",
|
|
16
17
|
"active_node",
|
|
18
|
+
"last_active_node",
|
|
17
19
|
"required_skills",
|
|
18
20
|
"required_checks",
|
|
19
21
|
"max_iterations",
|
|
@@ -60,7 +62,6 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
|
|
|
60
62
|
"severity",
|
|
61
63
|
"proposal_status",
|
|
62
64
|
"proposal_kind",
|
|
63
|
-
"evidence_refs",
|
|
64
65
|
"archive_kind",
|
|
65
66
|
"source_path",
|
|
66
67
|
"stored_path",
|
|
@@ -89,6 +90,8 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
|
|
|
89
90
|
"blocked_by",
|
|
90
91
|
"blocks",
|
|
91
92
|
"refs",
|
|
93
|
+
"context_refs",
|
|
94
|
+
"evidence_refs",
|
|
92
95
|
"aliases",
|
|
93
96
|
"skills",
|
|
94
97
|
"cases",
|
package/dist/graph/indexer.js
CHANGED
|
@@ -10,6 +10,7 @@ const node_1 = require("./node");
|
|
|
10
10
|
const workspace_files_1 = require("./workspace_files");
|
|
11
11
|
const validate_graph_1 = require("./validate_graph");
|
|
12
12
|
const template_schema_1 = require("./template_schema");
|
|
13
|
+
const agent_file_types_1 = require("./agent_file_types");
|
|
13
14
|
function normalizeEdgeTarget(value, ws) {
|
|
14
15
|
if (value.includes(":")) {
|
|
15
16
|
return value;
|
|
@@ -25,6 +26,8 @@ function normalizeEdges(edges, ws) {
|
|
|
25
26
|
relates: edges.relates.map((value) => normalizeEdgeTarget(value, ws)),
|
|
26
27
|
blocked_by: edges.blocked_by.map((value) => normalizeEdgeTarget(value, ws)),
|
|
27
28
|
blocks: edges.blocks.map((value) => normalizeEdgeTarget(value, ws)),
|
|
29
|
+
context_refs: (edges.context_refs ?? []).map((value) => normalizeEdgeTarget(value, ws)),
|
|
30
|
+
evidence_refs: (edges.evidence_refs ?? []).map((value) => normalizeEdgeTarget(value, ws)),
|
|
28
31
|
};
|
|
29
32
|
}
|
|
30
33
|
function addReverseEdge(reverse, edgeKey, target, source) {
|
|
@@ -46,6 +49,10 @@ function buildIndex(root, config, options = {}) {
|
|
|
46
49
|
for (const alias of workspaceAliases) {
|
|
47
50
|
idsByWorkspace[alias] = new Set();
|
|
48
51
|
const files = docFilesByAlias[alias];
|
|
52
|
+
const manifestConflicts = (0, agent_file_types_1.collectManifestSiblingConflicts)(files, (dirPath) => path_1.default.relative(root, dirPath).split(path_1.default.sep).join("/") || ".");
|
|
53
|
+
if (manifestConflicts.length > 0 && !tolerant) {
|
|
54
|
+
throw new Error(manifestConflicts[0]);
|
|
55
|
+
}
|
|
49
56
|
for (const filePath of files) {
|
|
50
57
|
if (path_1.default.basename(filePath) === "core.md" && path_1.default.basename(path_1.default.dirname(filePath)) === "core") {
|
|
51
58
|
continue;
|
|
@@ -118,6 +125,12 @@ function buildIndex(root, config, options = {}) {
|
|
|
118
125
|
for (const target of edges.blocks) {
|
|
119
126
|
addReverseEdge(reverse_edges, "blocks", target, qid);
|
|
120
127
|
}
|
|
128
|
+
for (const target of edges.context_refs ?? []) {
|
|
129
|
+
addReverseEdge(reverse_edges, "context_refs", target, qid);
|
|
130
|
+
}
|
|
131
|
+
for (const target of edges.evidence_refs ?? []) {
|
|
132
|
+
addReverseEdge(reverse_edges, "evidence_refs", target, qid);
|
|
133
|
+
}
|
|
121
134
|
}
|
|
122
135
|
for (const edgeKey of Object.keys(reverse_edges)) {
|
|
123
136
|
for (const target of Object.keys(reverse_edges[edgeKey])) {
|
package/dist/graph/node.js
CHANGED
|
@@ -37,6 +37,7 @@ const GOAL_ATTRIBUTE_KEYS = [
|
|
|
37
37
|
"goal_condition",
|
|
38
38
|
"scope_refs",
|
|
39
39
|
"active_node",
|
|
40
|
+
"last_active_node",
|
|
40
41
|
"required_skills",
|
|
41
42
|
"required_checks",
|
|
42
43
|
"max_iterations",
|
|
@@ -180,6 +181,13 @@ function validateGoalFrontmatter(type, frontmatter, filePath) {
|
|
|
180
181
|
throw formatError(filePath, "active_node must be a local id or qid");
|
|
181
182
|
}
|
|
182
183
|
}
|
|
184
|
+
const lastActiveNode = optionalString(frontmatter, "last_active_node", filePath);
|
|
185
|
+
if (lastActiveNode !== undefined) {
|
|
186
|
+
const normalized = requireLowercase(lastActiveNode, "last_active_node", filePath);
|
|
187
|
+
if (!(0, id_1.isPortableIdRef)(normalized)) {
|
|
188
|
+
throw formatError(filePath, "last_active_node must be a local id or qid");
|
|
189
|
+
}
|
|
190
|
+
}
|
|
183
191
|
const scopeRefs = optionalList(frontmatter, "scope_refs", filePath);
|
|
184
192
|
for (const [index, value] of scopeRefs.entries()) {
|
|
185
193
|
if (typeof value !== "string" || value.trim().length === 0) {
|
|
@@ -334,7 +342,10 @@ function parseNode(content, filePath, options) {
|
|
|
334
342
|
throw formatError(filePath, "supersedes must be a dec-# id");
|
|
335
343
|
}
|
|
336
344
|
}
|
|
337
|
-
const edges = (0, edges_1.extractEdges)(frontmatter, filePath, {
|
|
345
|
+
const edges = (0, edges_1.extractEdges)(frontmatter, filePath, {
|
|
346
|
+
allowPortableRefs: true,
|
|
347
|
+
includeSemanticRefs: exports.WORK_TYPES.has(type),
|
|
348
|
+
});
|
|
338
349
|
const attributes = {
|
|
339
350
|
...extractGoalAttributes(type, frontmatter),
|
|
340
351
|
...(0, agent_file_types_1.extractAgentAttributes)(type, frontmatter),
|
|
@@ -205,6 +205,8 @@ function writeSqliteIndex(options) {
|
|
|
205
205
|
["relates", node.edges.relates],
|
|
206
206
|
["blocked_by", node.edges.blocked_by],
|
|
207
207
|
["blocks", node.edges.blocks],
|
|
208
|
+
["context_refs", node.edges.context_refs ?? []],
|
|
209
|
+
["evidence_refs", node.edges.evidence_refs ?? []],
|
|
208
210
|
]) {
|
|
209
211
|
for (const target of values) {
|
|
210
212
|
insertEdge.run(node.qid, kind, target);
|
package/dist/graph/subgraphs.js
CHANGED
|
@@ -278,6 +278,8 @@ function projectOneSubgraph(root, alias, subgraph) {
|
|
|
278
278
|
relates: remapTargets(node.edges.relates, qidMap),
|
|
279
279
|
blocked_by: remapTargets(node.edges.blocked_by, qidMap),
|
|
280
280
|
blocks: remapTargets(node.edges.blocks, qidMap),
|
|
281
|
+
context_refs: remapTargets(node.edges.context_refs ?? [], qidMap),
|
|
282
|
+
evidence_refs: remapTargets(node.edges.evidence_refs ?? [], qidMap),
|
|
281
283
|
},
|
|
282
284
|
source: {
|
|
283
285
|
imported: true,
|
|
@@ -9,6 +9,9 @@ const fs_1 = __importDefault(require("fs"));
|
|
|
9
9
|
const path_1 = __importDefault(require("path"));
|
|
10
10
|
const frontmatter_1 = require("./frontmatter");
|
|
11
11
|
const builtin_1 = require("../templates/builtin");
|
|
12
|
+
const TEMPLATE_SCHEMA_ALIASES = {
|
|
13
|
+
spec: "manifest",
|
|
14
|
+
};
|
|
12
15
|
function listMarkdownFiles(dir) {
|
|
13
16
|
if (!fs_1.default.existsSync(dir)) {
|
|
14
17
|
return [];
|
|
@@ -46,6 +49,34 @@ function addKeyToSchema(schema, key, kind, filePath) {
|
|
|
46
49
|
schema.listKeys.add(key);
|
|
47
50
|
}
|
|
48
51
|
}
|
|
52
|
+
function cloneSchema(schema, type) {
|
|
53
|
+
return {
|
|
54
|
+
type,
|
|
55
|
+
allowedKeys: new Set(schema.allowedKeys),
|
|
56
|
+
keyKinds: { ...schema.keyKinds },
|
|
57
|
+
listKeys: new Set(schema.listKeys),
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function loadBundledSchema(type) {
|
|
61
|
+
const bundledPath = (0, builtin_1.requireBundledTemplatePath)(type);
|
|
62
|
+
const content = fs_1.default.readFileSync(bundledPath, "utf8");
|
|
63
|
+
const { frontmatter } = (0, frontmatter_1.parseFrontmatter)(content, bundledPath);
|
|
64
|
+
const typeValue = frontmatter.type;
|
|
65
|
+
if (typeValue !== type) {
|
|
66
|
+
throw new Error(`bundled template fallback type mismatch for ${type}: ${bundledPath}`);
|
|
67
|
+
}
|
|
68
|
+
const schema = {
|
|
69
|
+
type,
|
|
70
|
+
allowedKeys: new Set(),
|
|
71
|
+
keyKinds: {},
|
|
72
|
+
listKeys: new Set(),
|
|
73
|
+
};
|
|
74
|
+
for (const [key, value] of Object.entries(frontmatter)) {
|
|
75
|
+
const kind = getValueKind(value);
|
|
76
|
+
addKeyToSchema(schema, key, kind, bundledPath);
|
|
77
|
+
}
|
|
78
|
+
return schema;
|
|
79
|
+
}
|
|
49
80
|
function loadTemplateSchemas(root, config, requiredTypes) {
|
|
50
81
|
return loadTemplateSchemasWithInfo(root, config, requiredTypes).schemas;
|
|
51
82
|
}
|
|
@@ -84,24 +115,13 @@ function loadTemplateSchemasWithInfo(root, config, requiredTypes) {
|
|
|
84
115
|
if (requiredTypes) {
|
|
85
116
|
const required = Array.from(requiredTypes, (value) => value.toLowerCase());
|
|
86
117
|
for (const missingType of required.filter((value) => !schemas[value])) {
|
|
87
|
-
const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
throw new Error(`bundled template fallback type mismatch for ${missingType}: ${bundledPath}`);
|
|
93
|
-
}
|
|
94
|
-
const schema = {
|
|
95
|
-
type: missingType,
|
|
96
|
-
allowedKeys: new Set(),
|
|
97
|
-
keyKinds: {},
|
|
98
|
-
listKeys: new Set(),
|
|
99
|
-
};
|
|
100
|
-
schemas[missingType] = schema;
|
|
101
|
-
for (const [key, value] of Object.entries(frontmatter)) {
|
|
102
|
-
const kind = getValueKind(value);
|
|
103
|
-
addKeyToSchema(schema, key, kind, bundledPath);
|
|
118
|
+
const aliasType = TEMPLATE_SCHEMA_ALIASES[missingType];
|
|
119
|
+
if (aliasType) {
|
|
120
|
+
const aliasSchema = schemas[aliasType] ?? loadBundledSchema(aliasType);
|
|
121
|
+
schemas[missingType] = cloneSchema(aliasSchema, missingType);
|
|
122
|
+
continue;
|
|
104
123
|
}
|
|
124
|
+
schemas[missingType] = loadBundledSchema(missingType);
|
|
105
125
|
fallbackTypes.push(missingType);
|
|
106
126
|
}
|
|
107
127
|
}
|
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.collectGraphErrors = collectGraphErrors;
|
|
4
4
|
exports.validateGraph = validateGraph;
|
|
5
|
+
const agent_file_types_1 = require("./agent_file_types");
|
|
5
6
|
const refs_1 = require("../util/refs");
|
|
7
|
+
const qid_1 = require("../util/qid");
|
|
6
8
|
const goal_scope_1 = require("./goal_scope");
|
|
7
9
|
function pushError(errors, message) {
|
|
8
10
|
if (errors) {
|
|
@@ -19,6 +21,8 @@ function validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorks
|
|
|
19
21
|
["relates", edges.relates],
|
|
20
22
|
["blocked_by", edges.blocked_by],
|
|
21
23
|
["blocks", edges.blocks],
|
|
24
|
+
["context_refs", edges.context_refs ?? []],
|
|
25
|
+
["evidence_refs", edges.evidence_refs ?? []],
|
|
22
26
|
];
|
|
23
27
|
if (edges.epic) {
|
|
24
28
|
edgeLists.push(["epic", [edges.epic]]);
|
|
@@ -34,6 +38,9 @@ function validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorks
|
|
|
34
38
|
}
|
|
35
39
|
for (const [edgeKey, values] of edgeLists) {
|
|
36
40
|
for (const value of values) {
|
|
41
|
+
if ((edgeKey === "context_refs" || edgeKey === "evidence_refs") && (0, refs_1.isUriRef)(value)) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
37
44
|
const targetNode = nodes[value];
|
|
38
45
|
if (targetNode &&
|
|
39
46
|
["epic", "parent", "prev", "next"].includes(edgeKey) &&
|
|
@@ -142,7 +149,7 @@ function validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors) {
|
|
|
142
149
|
});
|
|
143
150
|
}
|
|
144
151
|
for (const [qid, node] of Object.entries(index.nodes)) {
|
|
145
|
-
if (node.type
|
|
152
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
146
153
|
continue;
|
|
147
154
|
}
|
|
148
155
|
const workContracts = node.attributes.work_contracts;
|
|
@@ -228,7 +235,7 @@ function validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, external
|
|
|
228
235
|
function buildSpecRolesByWorkspace(index) {
|
|
229
236
|
const specRolesByWorkspace = {};
|
|
230
237
|
for (const node of Object.values(index.nodes)) {
|
|
231
|
-
if (node.type
|
|
238
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
232
239
|
continue;
|
|
233
240
|
}
|
|
234
241
|
if (!specRolesByWorkspace[node.ws]) {
|
|
@@ -242,8 +249,12 @@ function buildSpecRolesByWorkspace(index) {
|
|
|
242
249
|
return specRolesByWorkspace;
|
|
243
250
|
}
|
|
244
251
|
function resolveSpecRole(index, ws, value) {
|
|
245
|
-
const
|
|
246
|
-
if (
|
|
252
|
+
const resolved = (0, qid_1.resolveQid)(index, value, ws);
|
|
253
|
+
if (resolved.status !== "ok") {
|
|
254
|
+
return undefined;
|
|
255
|
+
}
|
|
256
|
+
const node = index.nodes[resolved.qid];
|
|
257
|
+
if (!node || !(0, agent_file_types_1.isManifestSemanticType)(node.type)) {
|
|
247
258
|
return undefined;
|
|
248
259
|
}
|
|
249
260
|
return {
|
|
@@ -254,7 +265,7 @@ function resolveSpecRole(index, ws, value) {
|
|
|
254
265
|
function validateAgentWorkflowSubagentRefs(index, allowMissing, externalWorkspaces, errors) {
|
|
255
266
|
const specRolesByWorkspace = buildSpecRolesByWorkspace(index);
|
|
256
267
|
for (const [qid, node] of Object.entries(index.nodes)) {
|
|
257
|
-
if (node.type
|
|
268
|
+
if (!(0, agent_file_types_1.isManifestSemanticType)(node.type) && node.type !== "work") {
|
|
258
269
|
continue;
|
|
259
270
|
}
|
|
260
271
|
const subagentRefs = node.attributes.subagent_refs;
|
package/dist/graph/visibility.js
CHANGED
|
@@ -88,6 +88,12 @@ function collectNodeStringReferences(node) {
|
|
|
88
88
|
for (const [index, value] of node.edges.blocks.entries()) {
|
|
89
89
|
values.push({ field: `blocks[${index}]`, value });
|
|
90
90
|
}
|
|
91
|
+
for (const [index, value] of (node.edges.context_refs ?? []).entries()) {
|
|
92
|
+
values.push({ field: `context_refs[${index}]`, value });
|
|
93
|
+
}
|
|
94
|
+
for (const [index, value] of (node.edges.evidence_refs ?? []).entries()) {
|
|
95
|
+
values.push({ field: `evidence_refs[${index}]`, value });
|
|
96
|
+
}
|
|
91
97
|
for (const [index, value] of node.links.entries()) {
|
|
92
98
|
values.push({ field: `links[${index}]`, value });
|
|
93
99
|
}
|