mdkg 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +79 -0
- package/README.md +105 -14
- package/dist/cli.js +564 -15
- package/dist/commands/archive.js +474 -0
- package/dist/commands/bundle.js +743 -0
- package/dist/commands/bundle_import.js +243 -0
- package/dist/commands/capability.js +162 -0
- package/dist/commands/doctor.js +223 -0
- package/dist/commands/format.js +38 -9
- package/dist/commands/index.js +11 -0
- package/dist/commands/init.js +188 -63
- package/dist/commands/init_manifest.js +19 -6
- package/dist/commands/list.js +5 -2
- package/dist/commands/new.js +3 -0
- package/dist/commands/next.js +7 -0
- package/dist/commands/node_card.js +4 -1
- package/dist/commands/pack.js +62 -2
- package/dist/commands/query_output.js +1 -0
- package/dist/commands/search.js +5 -2
- package/dist/commands/show.js +7 -14
- package/dist/commands/skill_mirror.js +22 -0
- package/dist/commands/task.js +3 -0
- package/dist/commands/upgrade.js +24 -1
- package/dist/commands/validate.js +14 -1
- package/dist/commands/work.js +365 -0
- package/dist/commands/workspace.js +12 -2
- package/dist/core/config.js +100 -1
- package/dist/graph/agent_file_types.js +78 -5
- package/dist/graph/archive_file.js +125 -0
- package/dist/graph/archive_integrity.js +66 -0
- package/dist/graph/bundle_imports.js +418 -0
- package/dist/graph/capabilities_index_cache.js +103 -0
- package/dist/graph/capabilities_indexer.js +231 -0
- package/dist/graph/frontmatter.js +19 -0
- package/dist/graph/index_cache.js +21 -4
- package/dist/graph/indexer.js +4 -1
- package/dist/graph/node.js +23 -4
- package/dist/graph/node_body.js +37 -0
- package/dist/graph/skills_indexer.js +8 -3
- package/dist/graph/validate_graph.js +83 -7
- package/dist/graph/visibility.js +214 -0
- package/dist/graph/workspace_files.js +22 -0
- package/dist/init/AGENT_START.md +21 -0
- package/dist/init/CLI_COMMAND_MATRIX.md +56 -3
- package/dist/init/README.md +59 -2
- package/dist/init/config.json +13 -1
- package/dist/init/core/guide.md +6 -2
- package/dist/init/core/rule-3-cli-contract.md +71 -4
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +20 -0
- package/dist/init/core/rule-6-templates-and-schemas.md +7 -0
- package/dist/init/init-manifest.json +19 -14
- package/dist/init/skills/default/build-pack-and-execute-task/SKILL.md +2 -1
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +26 -0
- package/dist/init/templates/default/archive.md +33 -0
- package/dist/init/templates/default/receipt.md +15 -1
- package/dist/init/templates/default/work.md +6 -1
- package/dist/init/templates/default/work_order.md +15 -1
- package/dist/pack/export_md.js +3 -0
- package/dist/pack/export_xml.js +3 -0
- package/dist/pack/order.js +1 -0
- package/dist/pack/pack.js +3 -13
- package/dist/util/argparse.js +30 -0
- package/dist/util/refs.js +40 -0
- package/dist/util/zip.js +153 -0
- package/package.json +8 -2
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CAPABILITY_VISIBILITIES = exports.CAPABILITY_KINDS = exports.CAPABILITIES_INDEX_RELATIVE_PATH = void 0;
|
|
7
|
+
exports.resolveCapabilitiesIndexPath = resolveCapabilitiesIndexPath;
|
|
8
|
+
exports.buildCapabilitiesIndex = buildCapabilitiesIndex;
|
|
9
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
10
|
+
const fs_1 = __importDefault(require("fs"));
|
|
11
|
+
const path_1 = __importDefault(require("path"));
|
|
12
|
+
const indexer_1 = require("./indexer");
|
|
13
|
+
const skills_indexer_1 = require("./skills_indexer");
|
|
14
|
+
exports.CAPABILITIES_INDEX_RELATIVE_PATH = ".mdkg/index/capabilities.json";
|
|
15
|
+
exports.CAPABILITY_KINDS = ["skill", "spec", "work", "core", "design"];
|
|
16
|
+
exports.CAPABILITY_VISIBILITIES = ["private", "internal", "public"];
|
|
17
|
+
const CAPABILITY_CACHE_VERSION = 1;
|
|
18
|
+
function toPosixPath(value) {
|
|
19
|
+
return value.split(path_1.default.sep).join("/");
|
|
20
|
+
}
|
|
21
|
+
function sourceHash(content) {
|
|
22
|
+
return `sha256:${crypto_1.default.createHash("sha256").update(content).digest("hex")}`;
|
|
23
|
+
}
|
|
24
|
+
function extractHeadings(content) {
|
|
25
|
+
const headings = [];
|
|
26
|
+
for (const line of content.split(/\r?\n/)) {
|
|
27
|
+
const match = /^(#{1,6})\s+(.+?)\s*#*\s*$/.exec(line);
|
|
28
|
+
if (!match) {
|
|
29
|
+
continue;
|
|
30
|
+
}
|
|
31
|
+
headings.push({ level: match[1].length, text: match[2].trim() });
|
|
32
|
+
}
|
|
33
|
+
return headings;
|
|
34
|
+
}
|
|
35
|
+
function workspaceVisibility(config, alias) {
|
|
36
|
+
return config.workspaces[alias]?.visibility ?? "private";
|
|
37
|
+
}
|
|
38
|
+
function workspaceMdkgPrefix(entry) {
|
|
39
|
+
const wsPath = entry.path === "." ? "" : `${toPosixPath(entry.path).replace(/\/+$/, "")}/`;
|
|
40
|
+
const mdkgDir = toPosixPath(entry.mdkg_dir).replace(/^\/+|\/+$/g, "");
|
|
41
|
+
return `${wsPath}${mdkgDir}/`;
|
|
42
|
+
}
|
|
43
|
+
function classifyNodeCapability(node, config) {
|
|
44
|
+
if (node.type === "spec") {
|
|
45
|
+
return "spec";
|
|
46
|
+
}
|
|
47
|
+
if (node.type === "work") {
|
|
48
|
+
return "work";
|
|
49
|
+
}
|
|
50
|
+
const workspace = config.workspaces[node.ws];
|
|
51
|
+
if (!workspace) {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
const nodePath = toPosixPath(node.path);
|
|
55
|
+
const prefix = workspaceMdkgPrefix(workspace);
|
|
56
|
+
if (!nodePath.startsWith(prefix)) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
const relativeToMdkg = nodePath.slice(prefix.length);
|
|
60
|
+
if (relativeToMdkg.startsWith("core/")) {
|
|
61
|
+
return "core";
|
|
62
|
+
}
|
|
63
|
+
if (relativeToMdkg.startsWith("design/")) {
|
|
64
|
+
return "design";
|
|
65
|
+
}
|
|
66
|
+
return undefined;
|
|
67
|
+
}
|
|
68
|
+
function pickAttributes(attributes, keys) {
|
|
69
|
+
const picked = {};
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
if (attributes[key] !== undefined) {
|
|
72
|
+
picked[key] = attributes[key];
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return picked;
|
|
76
|
+
}
|
|
77
|
+
function nodeCapabilityRecord(root, config, node, kind, indexedAt) {
|
|
78
|
+
const absolutePath = path_1.default.resolve(root, node.path);
|
|
79
|
+
const content = fs_1.default.readFileSync(absolutePath, "utf8");
|
|
80
|
+
const record = {
|
|
81
|
+
kind,
|
|
82
|
+
workspace: node.ws,
|
|
83
|
+
visibility: workspaceVisibility(config, node.ws),
|
|
84
|
+
id: node.id,
|
|
85
|
+
qid: node.qid,
|
|
86
|
+
path: toPosixPath(node.path),
|
|
87
|
+
title: node.title,
|
|
88
|
+
tags: [...node.tags],
|
|
89
|
+
refs: [...node.refs],
|
|
90
|
+
aliases: [...node.aliases],
|
|
91
|
+
links: [...node.links],
|
|
92
|
+
artifacts: [...node.artifacts],
|
|
93
|
+
updated: node.updated,
|
|
94
|
+
indexed_at: indexedAt,
|
|
95
|
+
source_hash: sourceHash(content),
|
|
96
|
+
headings: extractHeadings(content),
|
|
97
|
+
node_type: node.type,
|
|
98
|
+
};
|
|
99
|
+
if (kind === "spec") {
|
|
100
|
+
record.spec = pickAttributes(node.attributes, [
|
|
101
|
+
"version",
|
|
102
|
+
"role",
|
|
103
|
+
"runtime_mode",
|
|
104
|
+
"work_contracts",
|
|
105
|
+
"requested_capabilities",
|
|
106
|
+
"skill_refs",
|
|
107
|
+
"tool_refs",
|
|
108
|
+
"model_refs",
|
|
109
|
+
"wasm_component_refs",
|
|
110
|
+
"runtime_image_refs",
|
|
111
|
+
"subagent_refs",
|
|
112
|
+
"resource_profile",
|
|
113
|
+
"update_policy",
|
|
114
|
+
]);
|
|
115
|
+
}
|
|
116
|
+
if (kind === "work") {
|
|
117
|
+
record.work = pickAttributes(node.attributes, [
|
|
118
|
+
"version",
|
|
119
|
+
"agent_id",
|
|
120
|
+
"kind",
|
|
121
|
+
"pricing_model",
|
|
122
|
+
"required_capabilities",
|
|
123
|
+
"skill_refs",
|
|
124
|
+
"tool_refs",
|
|
125
|
+
"model_refs",
|
|
126
|
+
"wasm_component_refs",
|
|
127
|
+
"runtime_image_refs",
|
|
128
|
+
"subagent_refs",
|
|
129
|
+
"inputs",
|
|
130
|
+
"outputs",
|
|
131
|
+
"receipt_required",
|
|
132
|
+
]);
|
|
133
|
+
}
|
|
134
|
+
return record;
|
|
135
|
+
}
|
|
136
|
+
function skillCapabilityRecord(root, config, skill, indexedAt) {
|
|
137
|
+
const absolutePath = path_1.default.resolve(root, skill.path);
|
|
138
|
+
const content = fs_1.default.readFileSync(absolutePath, "utf8");
|
|
139
|
+
return {
|
|
140
|
+
kind: "skill",
|
|
141
|
+
workspace: skill.ws,
|
|
142
|
+
visibility: workspaceVisibility(config, skill.ws),
|
|
143
|
+
id: skill.id,
|
|
144
|
+
qid: skill.qid,
|
|
145
|
+
path: toPosixPath(skill.path),
|
|
146
|
+
title: skill.name,
|
|
147
|
+
name: skill.name,
|
|
148
|
+
description: skill.description,
|
|
149
|
+
slug: skill.slug,
|
|
150
|
+
tags: [...skill.tags],
|
|
151
|
+
refs: [],
|
|
152
|
+
aliases: [skill.slug, ...skill.tags],
|
|
153
|
+
links: [...skill.links],
|
|
154
|
+
artifacts: [],
|
|
155
|
+
indexed_at: indexedAt,
|
|
156
|
+
source_hash: sourceHash(content),
|
|
157
|
+
headings: extractHeadings(content),
|
|
158
|
+
skill: {
|
|
159
|
+
version: skill.version,
|
|
160
|
+
authors: [...skill.authors],
|
|
161
|
+
has_scripts: skill.has_scripts,
|
|
162
|
+
has_references: skill.has_references,
|
|
163
|
+
extensions: skill.extensions,
|
|
164
|
+
},
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function workspaceSkillsRoot(root, entry) {
|
|
168
|
+
return path_1.default.resolve(root, entry.path, entry.mdkg_dir, "skills");
|
|
169
|
+
}
|
|
170
|
+
function buildWorkspaceSkillCapabilities(root, config, indexedAt) {
|
|
171
|
+
const records = [];
|
|
172
|
+
for (const alias of Object.keys(config.workspaces).sort()) {
|
|
173
|
+
const workspace = config.workspaces[alias];
|
|
174
|
+
if (!workspace.enabled) {
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const skillsRoot = workspaceSkillsRoot(root, workspace);
|
|
178
|
+
for (const candidate of (0, skills_indexer_1.listSkillMarkdownFiles)(skillsRoot)) {
|
|
179
|
+
const skill = (0, skills_indexer_1.buildSkillIndexEntryForWorkspace)(root, alias, candidate.slug, candidate.filePath);
|
|
180
|
+
records.push(skillCapabilityRecord(root, config, skill, indexedAt));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return records;
|
|
184
|
+
}
|
|
185
|
+
function sortRecords(records) {
|
|
186
|
+
return [...records].sort((a, b) => {
|
|
187
|
+
for (const [left, right] of [
|
|
188
|
+
[a.workspace, b.workspace],
|
|
189
|
+
[a.kind, b.kind],
|
|
190
|
+
[a.id, b.id],
|
|
191
|
+
[a.path, b.path],
|
|
192
|
+
]) {
|
|
193
|
+
const compared = left.localeCompare(right);
|
|
194
|
+
if (compared !== 0) {
|
|
195
|
+
return compared;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return 0;
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function resolveCapabilitiesIndexPath(root, config) {
|
|
202
|
+
return path_1.default.resolve(root, config.capabilities.cache_path);
|
|
203
|
+
}
|
|
204
|
+
function buildCapabilitiesIndex(root, config, nodeIndex) {
|
|
205
|
+
const index = nodeIndex ?? (0, indexer_1.buildIndex)(root, config);
|
|
206
|
+
const records = [];
|
|
207
|
+
const generatedAt = new Date().toISOString();
|
|
208
|
+
for (const node of Object.values(index.nodes)) {
|
|
209
|
+
const kind = classifyNodeCapability(node, config);
|
|
210
|
+
if (!kind) {
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
records.push(nodeCapabilityRecord(root, config, node, kind, generatedAt));
|
|
214
|
+
}
|
|
215
|
+
records.push(...buildWorkspaceSkillCapabilities(root, config, generatedAt));
|
|
216
|
+
const sortedRecords = sortRecords(records);
|
|
217
|
+
return {
|
|
218
|
+
meta: {
|
|
219
|
+
tool: config.tool,
|
|
220
|
+
schema_version: config.schema_version,
|
|
221
|
+
cache_version: CAPABILITY_CACHE_VERSION,
|
|
222
|
+
generated_at: generatedAt,
|
|
223
|
+
root,
|
|
224
|
+
workspaces: Object.keys(config.workspaces)
|
|
225
|
+
.filter((alias) => config.workspaces[alias].enabled)
|
|
226
|
+
.sort(),
|
|
227
|
+
record_count: sortedRecords.length,
|
|
228
|
+
},
|
|
229
|
+
records: sortedRecords,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
@@ -48,6 +48,25 @@ exports.DEFAULT_FRONTMATTER_KEY_ORDER = [
|
|
|
48
48
|
"proposal_status",
|
|
49
49
|
"proposal_kind",
|
|
50
50
|
"evidence_refs",
|
|
51
|
+
"archive_kind",
|
|
52
|
+
"source_path",
|
|
53
|
+
"stored_path",
|
|
54
|
+
"compressed_path",
|
|
55
|
+
"mime_type",
|
|
56
|
+
"byte_size",
|
|
57
|
+
"sha256",
|
|
58
|
+
"compressed_sha256",
|
|
59
|
+
"visibility",
|
|
60
|
+
"provenance",
|
|
61
|
+
"ingest_status",
|
|
62
|
+
"input_refs",
|
|
63
|
+
"requested_outputs",
|
|
64
|
+
"constraint_refs",
|
|
65
|
+
"artifact_policy",
|
|
66
|
+
"proof_refs",
|
|
67
|
+
"attestation_refs",
|
|
68
|
+
"input_hashes",
|
|
69
|
+
"output_hashes",
|
|
51
70
|
"tags",
|
|
52
71
|
"owners",
|
|
53
72
|
"links",
|
|
@@ -10,6 +10,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
10
10
|
const sort_1 = require("../util/sort");
|
|
11
11
|
const indexer_1 = require("./indexer");
|
|
12
12
|
const staleness_1 = require("./staleness");
|
|
13
|
+
const bundle_imports_1 = require("./bundle_imports");
|
|
13
14
|
function readIndex(indexPath) {
|
|
14
15
|
try {
|
|
15
16
|
const raw = fs_1.default.readFileSync(indexPath, "utf8");
|
|
@@ -29,22 +30,38 @@ function loadIndex(options) {
|
|
|
29
30
|
const useCache = options.useCache ?? true;
|
|
30
31
|
const allowReindex = options.allowReindex ?? options.config.index.auto_reindex;
|
|
31
32
|
const tolerant = options.tolerant ?? options.config.index.tolerant;
|
|
33
|
+
const includeImports = options.includeImports ?? true;
|
|
32
34
|
const indexPath = path_1.default.resolve(options.root, options.config.index.global_index_path);
|
|
35
|
+
const withImports = (index, rebuilt, stale) => {
|
|
36
|
+
if (!includeImports || Object.keys(options.config.bundle_imports).length === 0) {
|
|
37
|
+
return { index, rebuilt, stale, warnings: [] };
|
|
38
|
+
}
|
|
39
|
+
const imports = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, options.config);
|
|
40
|
+
if (allowReindex) {
|
|
41
|
+
(0, bundle_imports_1.writeBundleImportsIndex)((0, bundle_imports_1.resolveBundleImportsIndexPath)(options.root), imports.index);
|
|
42
|
+
}
|
|
43
|
+
return {
|
|
44
|
+
index: (0, bundle_imports_1.mergeBundleImportsIntoIndex)(index, imports),
|
|
45
|
+
rebuilt,
|
|
46
|
+
stale: stale || (0, bundle_imports_1.isBundleImportsIndexStale)(options.root, options.config),
|
|
47
|
+
warnings: (0, bundle_imports_1.importWarnings)(imports),
|
|
48
|
+
};
|
|
49
|
+
};
|
|
33
50
|
if (!useCache) {
|
|
34
51
|
const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
|
|
35
|
-
return
|
|
52
|
+
return withImports(index, true, false);
|
|
36
53
|
}
|
|
37
54
|
const stale = (0, staleness_1.isIndexStale)(options.root, options.config);
|
|
38
55
|
if (fs_1.default.existsSync(indexPath) && !stale) {
|
|
39
|
-
return
|
|
56
|
+
return withImports(readIndex(indexPath), false, false);
|
|
40
57
|
}
|
|
41
58
|
if (allowReindex) {
|
|
42
59
|
const index = (0, indexer_1.buildIndex)(options.root, options.config, { tolerant });
|
|
43
60
|
writeIndex(indexPath, index);
|
|
44
|
-
return
|
|
61
|
+
return withImports(index, true, stale);
|
|
45
62
|
}
|
|
46
63
|
if (fs_1.default.existsSync(indexPath)) {
|
|
47
|
-
return
|
|
64
|
+
return withImports(readIndex(indexPath), false, true);
|
|
48
65
|
}
|
|
49
66
|
throw new Error("index missing and auto-reindex is disabled");
|
|
50
67
|
}
|
package/dist/graph/indexer.js
CHANGED
|
@@ -141,7 +141,10 @@ function buildIndex(root, config, options = {}) {
|
|
|
141
141
|
nodes,
|
|
142
142
|
reverse_edges,
|
|
143
143
|
};
|
|
144
|
-
(0, validate_graph_1.validateGraph)(index, {
|
|
144
|
+
(0, validate_graph_1.validateGraph)(index, {
|
|
145
|
+
allowMissing: tolerant,
|
|
146
|
+
externalWorkspaces: new Set(Object.keys(config.bundle_imports ?? {})),
|
|
147
|
+
});
|
|
145
148
|
const latestCheckpointByWorkspace = {};
|
|
146
149
|
for (const alias of workspaceAliases) {
|
|
147
150
|
const candidates = Object.values(nodes)
|
package/dist/graph/node.js
CHANGED
|
@@ -5,7 +5,9 @@ exports.parseNode = parseNode;
|
|
|
5
5
|
const frontmatter_1 = require("./frontmatter");
|
|
6
6
|
const edges_1 = require("./edges");
|
|
7
7
|
const agent_file_types_1 = require("./agent_file_types");
|
|
8
|
+
const archive_file_1 = require("./archive_file");
|
|
8
9
|
const id_1 = require("../util/id");
|
|
10
|
+
const refs_1 = require("../util/refs");
|
|
9
11
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
10
12
|
const DEC_ID_RE = /^dec-[0-9]+$/;
|
|
11
13
|
exports.WORK_TYPES = new Set(["epic", "feat", "task", "bug", "checkpoint", "test"]);
|
|
@@ -22,6 +24,7 @@ exports.ALLOWED_TYPES = new Set([
|
|
|
22
24
|
"bug",
|
|
23
25
|
"checkpoint",
|
|
24
26
|
"test",
|
|
27
|
+
"archive",
|
|
25
28
|
...agent_file_types_1.AGENT_FILE_TYPES,
|
|
26
29
|
]);
|
|
27
30
|
const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
|
|
@@ -119,6 +122,17 @@ function normalizeIdList(values, key, filePath, allowPortableIds = false) {
|
|
|
119
122
|
return value;
|
|
120
123
|
});
|
|
121
124
|
}
|
|
125
|
+
function normalizeRefsList(values, key, filePath, allowPortableOrUriRefs = false) {
|
|
126
|
+
if (!allowPortableOrUriRefs) {
|
|
127
|
+
return normalizeIdList(values, key, filePath);
|
|
128
|
+
}
|
|
129
|
+
return values.map((value) => {
|
|
130
|
+
if (!(0, refs_1.validatePortableOrUriRef)(value)) {
|
|
131
|
+
throw formatError(filePath, `${key} entries must be portable ids or URI refs`);
|
|
132
|
+
}
|
|
133
|
+
return value;
|
|
134
|
+
});
|
|
135
|
+
}
|
|
122
136
|
function normalizeSkillList(values, filePath) {
|
|
123
137
|
return values.map((value, index) => {
|
|
124
138
|
const normalized = value.toLowerCase();
|
|
@@ -166,11 +180,13 @@ function parseNode(content, filePath, options) {
|
|
|
166
180
|
throw formatError(filePath, `type must be one of ${Array.from(exports.ALLOWED_TYPES).join(", ")}`);
|
|
167
181
|
}
|
|
168
182
|
const isAgentType = (0, agent_file_types_1.isAgentFileType)(type);
|
|
183
|
+
const isPortableType = isAgentType || (0, archive_file_1.isArchiveType)(type);
|
|
169
184
|
const schema = requireTemplateSchema(type, options.templateSchemas, filePath);
|
|
170
185
|
validateTemplateKeys(frontmatter, schema, filePath);
|
|
171
186
|
(0, agent_file_types_1.validateAgentFrontmatter)(type, frontmatter, filePath);
|
|
187
|
+
(0, archive_file_1.validateArchiveFrontmatter)(type, frontmatter, filePath);
|
|
172
188
|
const idValue = requireLowercase(expectString(frontmatter, "id", filePath), "id", filePath);
|
|
173
|
-
const id =
|
|
189
|
+
const id = isPortableType
|
|
174
190
|
? requirePortableIdFormat(idValue, "id", filePath)
|
|
175
191
|
: requireIdFormat(idValue, "id", filePath);
|
|
176
192
|
const title = expectString(frontmatter, "title", filePath);
|
|
@@ -214,7 +230,7 @@ function parseNode(content, filePath, options) {
|
|
|
214
230
|
const owners = requireLowercaseList(optionalList(frontmatter, "owners", filePath), "owners", filePath);
|
|
215
231
|
const links = optionalList(frontmatter, "links", filePath);
|
|
216
232
|
const artifacts = optionalList(frontmatter, "artifacts", filePath);
|
|
217
|
-
const refs =
|
|
233
|
+
const refs = normalizeRefsList(optionalList(frontmatter, "refs", filePath), "refs", filePath, isPortableType);
|
|
218
234
|
const aliases = requireLowercaseList(optionalList(frontmatter, "aliases", filePath), "aliases", filePath);
|
|
219
235
|
const skillsRaw = optionalList(frontmatter, "skills", filePath);
|
|
220
236
|
const skills = normalizeSkillList(skillsRaw, filePath);
|
|
@@ -232,8 +248,11 @@ function parseNode(content, filePath, options) {
|
|
|
232
248
|
throw formatError(filePath, "supersedes must be a dec-# id");
|
|
233
249
|
}
|
|
234
250
|
}
|
|
235
|
-
const edges = (0, edges_1.extractEdges)(frontmatter, filePath, { allowPortableRefs:
|
|
236
|
-
const attributes =
|
|
251
|
+
const edges = (0, edges_1.extractEdges)(frontmatter, filePath, { allowPortableRefs: isPortableType });
|
|
252
|
+
const attributes = {
|
|
253
|
+
...(0, agent_file_types_1.extractAgentAttributes)(type, frontmatter),
|
|
254
|
+
...(0, archive_file_1.extractArchiveAttributes)(type, frontmatter),
|
|
255
|
+
};
|
|
237
256
|
return {
|
|
238
257
|
id,
|
|
239
258
|
type,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.readNodeBody = readNodeBody;
|
|
7
|
+
const fs_1 = __importDefault(require("fs"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const frontmatter_1 = require("./frontmatter");
|
|
10
|
+
const zip_1 = require("../util/zip");
|
|
11
|
+
const errors_1 = require("../util/errors");
|
|
12
|
+
function readImportedBody(root, node) {
|
|
13
|
+
const source = node.source;
|
|
14
|
+
if (!source?.imported) {
|
|
15
|
+
throw new Error("node is not imported");
|
|
16
|
+
}
|
|
17
|
+
const bundlePath = path_1.default.resolve(root, source.bundle_path);
|
|
18
|
+
if (!fs_1.default.existsSync(bundlePath)) {
|
|
19
|
+
throw new errors_1.NotFoundError(`bundle not found for ${node.qid}: ${source.bundle_path}`);
|
|
20
|
+
}
|
|
21
|
+
const entry = (0, zip_1.readZipEntries)(fs_1.default.readFileSync(bundlePath)).find((candidate) => candidate.name === source.original_path);
|
|
22
|
+
if (!entry) {
|
|
23
|
+
throw new errors_1.NotFoundError(`bundle entry not found for ${node.qid}: ${source.original_path}`);
|
|
24
|
+
}
|
|
25
|
+
return (0, frontmatter_1.parseFrontmatter)(entry.data.toString("utf8"), source.original_path).body.trimEnd();
|
|
26
|
+
}
|
|
27
|
+
function readNodeBody(root, node) {
|
|
28
|
+
if (node.source?.imported) {
|
|
29
|
+
return readImportedBody(root, node);
|
|
30
|
+
}
|
|
31
|
+
const filePath = path_1.default.resolve(root, node.path);
|
|
32
|
+
if (!fs_1.default.existsSync(filePath)) {
|
|
33
|
+
throw new errors_1.NotFoundError(`file not found for ${node.qid}: ${node.path}`);
|
|
34
|
+
}
|
|
35
|
+
const content = fs_1.default.readFileSync(filePath, "utf8");
|
|
36
|
+
return (0, frontmatter_1.parseFrontmatter)(content, filePath).body.trimEnd();
|
|
37
|
+
}
|
|
@@ -4,8 +4,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
6
|
exports.SKILL_SLUG_RE = exports.SKILLS_INDEX_RELATIVE_PATH = void 0;
|
|
7
|
+
exports.listSkillMarkdownFiles = listSkillMarkdownFiles;
|
|
7
8
|
exports.resolveSkillsRoot = resolveSkillsRoot;
|
|
8
9
|
exports.resolveSkillsIndexPath = resolveSkillsIndexPath;
|
|
10
|
+
exports.buildSkillIndexEntryForWorkspace = buildSkillIndexEntryForWorkspace;
|
|
9
11
|
exports.buildSkillIndexEntry = buildSkillIndexEntry;
|
|
10
12
|
exports.buildSkillsIndex = buildSkillsIndex;
|
|
11
13
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -107,7 +109,7 @@ function resolveSkillsRoot(root, config) {
|
|
|
107
109
|
function resolveSkillsIndexPath(root) {
|
|
108
110
|
return path_1.default.resolve(root, exports.SKILLS_INDEX_RELATIVE_PATH);
|
|
109
111
|
}
|
|
110
|
-
function
|
|
112
|
+
function buildSkillIndexEntryForWorkspace(root, workspace, slug, filePath) {
|
|
111
113
|
if (!exports.SKILL_SLUG_RE.test(slug)) {
|
|
112
114
|
throw new Error(`${filePath}: skill slug must be kebab-case`);
|
|
113
115
|
}
|
|
@@ -128,8 +130,8 @@ function buildSkillIndexEntry(root, slug, filePath) {
|
|
|
128
130
|
return {
|
|
129
131
|
slug,
|
|
130
132
|
id: `skill:${slug}`,
|
|
131
|
-
qid:
|
|
132
|
-
ws:
|
|
133
|
+
qid: `${workspace}:skill:${slug}`,
|
|
134
|
+
ws: workspace,
|
|
133
135
|
type: "skill",
|
|
134
136
|
name,
|
|
135
137
|
description,
|
|
@@ -144,6 +146,9 @@ function buildSkillIndexEntry(root, slug, filePath) {
|
|
|
144
146
|
ochatr,
|
|
145
147
|
};
|
|
146
148
|
}
|
|
149
|
+
function buildSkillIndexEntry(root, slug, filePath) {
|
|
150
|
+
return buildSkillIndexEntryForWorkspace(root, "root", slug, filePath);
|
|
151
|
+
}
|
|
147
152
|
function buildSkillsIndex(root, config) {
|
|
148
153
|
const skillsRoot = resolveSkillsRoot(root, config);
|
|
149
154
|
const files = listSkillMarkdownFiles(skillsRoot);
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.collectGraphErrors = collectGraphErrors;
|
|
4
4
|
exports.validateGraph = validateGraph;
|
|
5
|
+
const refs_1 = require("../util/refs");
|
|
5
6
|
function pushError(errors, message) {
|
|
6
7
|
if (errors) {
|
|
7
8
|
errors.push(message);
|
|
@@ -9,7 +10,7 @@ function pushError(errors, message) {
|
|
|
9
10
|
}
|
|
10
11
|
throw new Error(message);
|
|
11
12
|
}
|
|
12
|
-
function validateEdgeTargets(index, allowMissing, knownSkillSlugs, errors) {
|
|
13
|
+
function validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors) {
|
|
13
14
|
const nodes = index.nodes;
|
|
14
15
|
for (const [qid, node] of Object.entries(nodes)) {
|
|
15
16
|
const edges = node.edges;
|
|
@@ -33,6 +34,10 @@ function validateEdgeTargets(index, allowMissing, knownSkillSlugs, errors) {
|
|
|
33
34
|
for (const [edgeKey, values] of edgeLists) {
|
|
34
35
|
for (const value of values) {
|
|
35
36
|
if (!nodes[value]) {
|
|
37
|
+
const [workspace] = value.split(":");
|
|
38
|
+
if (workspace && externalWorkspaces?.has(workspace)) {
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
36
41
|
if (edgeKey === "relates" &&
|
|
37
42
|
node.type === "proposal" &&
|
|
38
43
|
node.attributes.proposal_kind === "skill_update" &&
|
|
@@ -320,7 +325,11 @@ function buildNodeIdsByWorkspace(index) {
|
|
|
320
325
|
}
|
|
321
326
|
return nodeIdsByWorkspace;
|
|
322
327
|
}
|
|
323
|
-
function validateAgentWorkflowNodeIdRef(qid, ws, field, value, nodeIdsByWorkspace, allowSkillRef, knownSkillSlugs, allowMissing, errors) {
|
|
328
|
+
function validateAgentWorkflowNodeIdRef(qid, ws, field, value, nodeIdsByWorkspace, allowSkillRef, knownSkillSlugs, externalWorkspaces, allowMissing, errors) {
|
|
329
|
+
const [workspace] = value.split(":");
|
|
330
|
+
if (workspace && value.includes(":") && externalWorkspaces?.has(workspace)) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
324
333
|
if (nodeIdsByWorkspace[ws]?.has(value)) {
|
|
325
334
|
return;
|
|
326
335
|
}
|
|
@@ -333,7 +342,7 @@ function validateAgentWorkflowNodeIdRef(qid, ws, field, value, nodeIdsByWorkspac
|
|
|
333
342
|
}
|
|
334
343
|
pushError(errors, `${qid}: ${field} references missing node ${value}`);
|
|
335
344
|
}
|
|
336
|
-
function validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, errors) {
|
|
345
|
+
function validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors) {
|
|
337
346
|
const nodeIdsByWorkspace = buildNodeIdsByWorkspace(index);
|
|
338
347
|
for (const [qid, node] of Object.entries(index.nodes)) {
|
|
339
348
|
if (node.type !== "feedback" && node.type !== "proposal") {
|
|
@@ -342,7 +351,7 @@ function validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSki
|
|
|
342
351
|
const targetId = node.attributes.target_id;
|
|
343
352
|
if (typeof targetId === "string") {
|
|
344
353
|
const allowSkillTarget = node.type === "proposal" && node.attributes.proposal_kind === "skill_update";
|
|
345
|
-
validateAgentWorkflowNodeIdRef(qid, node.ws, "target_id", targetId, nodeIdsByWorkspace, allowSkillTarget, knownSkillSlugs, allowMissing, errors);
|
|
354
|
+
validateAgentWorkflowNodeIdRef(qid, node.ws, "target_id", targetId, nodeIdsByWorkspace, allowSkillTarget, knownSkillSlugs, externalWorkspaces, allowMissing, errors);
|
|
346
355
|
}
|
|
347
356
|
if (node.type !== "proposal") {
|
|
348
357
|
continue;
|
|
@@ -355,7 +364,72 @@ function validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSki
|
|
|
355
364
|
if (typeof value !== "string") {
|
|
356
365
|
continue;
|
|
357
366
|
}
|
|
358
|
-
validateAgentWorkflowNodeIdRef(qid, node.ws, `evidence_refs[${indexValue}]`, value, nodeIdsByWorkspace, true, knownSkillSlugs, allowMissing, errors);
|
|
367
|
+
validateAgentWorkflowNodeIdRef(qid, node.ws, `evidence_refs[${indexValue}]`, value, nodeIdsByWorkspace, true, knownSkillSlugs, externalWorkspaces, allowMissing, errors);
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
function buildArchiveIdsByWorkspace(index) {
|
|
372
|
+
const archiveIdsByWorkspace = {};
|
|
373
|
+
for (const node of Object.values(index.nodes)) {
|
|
374
|
+
if (node.type !== "archive") {
|
|
375
|
+
continue;
|
|
376
|
+
}
|
|
377
|
+
if (!archiveIdsByWorkspace[node.ws]) {
|
|
378
|
+
archiveIdsByWorkspace[node.ws] = new Set();
|
|
379
|
+
}
|
|
380
|
+
archiveIdsByWorkspace[node.ws].add(node.id);
|
|
381
|
+
}
|
|
382
|
+
return archiveIdsByWorkspace;
|
|
383
|
+
}
|
|
384
|
+
function validateArchiveUriValue(qid, ws, field, value, archiveIdsByWorkspace, allowMissing, errors) {
|
|
385
|
+
if (!value.startsWith("archive://")) {
|
|
386
|
+
return;
|
|
387
|
+
}
|
|
388
|
+
const archiveId = (0, refs_1.archiveIdFromUri)(value);
|
|
389
|
+
if (!archiveId) {
|
|
390
|
+
pushError(errors, `${qid}: ${field} has malformed archive ref ${value}`);
|
|
391
|
+
return;
|
|
392
|
+
}
|
|
393
|
+
if (archiveIdsByWorkspace[ws]?.has(archiveId)) {
|
|
394
|
+
return;
|
|
395
|
+
}
|
|
396
|
+
if (allowMissing) {
|
|
397
|
+
return;
|
|
398
|
+
}
|
|
399
|
+
pushError(errors, `${qid}: ${field} references missing archive ${value}`);
|
|
400
|
+
}
|
|
401
|
+
function validateArchiveUriRefs(index, allowMissing, errors) {
|
|
402
|
+
const archiveIdsByWorkspace = buildArchiveIdsByWorkspace(index);
|
|
403
|
+
const attributeListFields = [
|
|
404
|
+
"input_refs",
|
|
405
|
+
"constraint_refs",
|
|
406
|
+
"proof_refs",
|
|
407
|
+
"attestation_refs",
|
|
408
|
+
];
|
|
409
|
+
const attributeScalarFields = ["request_ref", "cost_ref"];
|
|
410
|
+
for (const [qid, node] of Object.entries(index.nodes)) {
|
|
411
|
+
for (const [indexValue, value] of node.artifacts.entries()) {
|
|
412
|
+
validateArchiveUriValue(qid, node.ws, `artifacts[${indexValue}]`, value, archiveIdsByWorkspace, allowMissing, errors);
|
|
413
|
+
}
|
|
414
|
+
const attributes = node.attributes ?? {};
|
|
415
|
+
for (const field of attributeListFields) {
|
|
416
|
+
const values = attributes[field];
|
|
417
|
+
if (!Array.isArray(values)) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
for (const [indexValue, value] of values.entries()) {
|
|
421
|
+
if (typeof value !== "string") {
|
|
422
|
+
continue;
|
|
423
|
+
}
|
|
424
|
+
validateArchiveUriValue(qid, node.ws, `${field}[${indexValue}]`, value, archiveIdsByWorkspace, allowMissing, errors);
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
for (const field of attributeScalarFields) {
|
|
428
|
+
const value = attributes[field];
|
|
429
|
+
if (typeof value !== "string") {
|
|
430
|
+
continue;
|
|
431
|
+
}
|
|
432
|
+
validateArchiveUriValue(qid, node.ws, field, value, archiveIdsByWorkspace, allowMissing, errors);
|
|
359
433
|
}
|
|
360
434
|
}
|
|
361
435
|
}
|
|
@@ -396,14 +470,16 @@ function collectGraphErrors(index, options = {}) {
|
|
|
396
470
|
const errors = [];
|
|
397
471
|
const allowMissing = options.allowMissing ?? false;
|
|
398
472
|
const knownSkillSlugs = options.knownSkillSlugs;
|
|
399
|
-
|
|
473
|
+
const externalWorkspaces = options.externalWorkspaces;
|
|
474
|
+
validateEdgeTargets(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors);
|
|
400
475
|
validatePrevNextSymmetry(index, allowMissing, errors);
|
|
401
476
|
validateAgentWorkflowSpecWorkContracts(index, allowMissing, errors);
|
|
402
477
|
validateAgentWorkflowWorkOrderWorkRefs(index, allowMissing, errors);
|
|
403
478
|
validateAgentWorkflowReceiptWorkOrderRefs(index, allowMissing, errors);
|
|
404
479
|
validateAgentWorkflowSubagentRefs(index, allowMissing, errors);
|
|
405
480
|
validateAgentWorkflowDisputeRefs(index, allowMissing, errors);
|
|
406
|
-
validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, errors);
|
|
481
|
+
validateAgentWorkflowFeedbackProposalRefs(index, allowMissing, knownSkillSlugs, externalWorkspaces, errors);
|
|
482
|
+
validateArchiveUriRefs(index, allowMissing, errors);
|
|
407
483
|
detectPrevNextCycles(index, errors);
|
|
408
484
|
return errors;
|
|
409
485
|
}
|