mdkg 0.1.2 → 0.1.4
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 +51 -0
- package/README.md +31 -15
- package/dist/cli.js +182 -85
- package/dist/commands/archive.js +20 -8
- package/dist/commands/bundle.js +7 -7
- package/dist/commands/capability.js +118 -4
- package/dist/commands/checkpoint.js +31 -5
- package/dist/commands/doctor.js +61 -24
- package/dist/commands/index.js +12 -23
- package/dist/commands/init.js +7 -1
- package/dist/commands/list.js +1 -1
- package/dist/commands/new.js +33 -7
- package/dist/commands/next.js +1 -1
- package/dist/commands/node_card.js +1 -1
- package/dist/commands/pack.js +1 -1
- package/dist/commands/search.js +1 -1
- package/dist/commands/show.js +1 -1
- package/dist/commands/subgraph.js +312 -0
- package/dist/commands/task.js +21 -7
- package/dist/commands/upgrade.js +51 -4
- package/dist/commands/validate.js +12 -6
- package/dist/commands/work.js +44 -12
- package/dist/core/config.js +110 -39
- package/dist/graph/capabilities_index_cache.js +2 -2
- package/dist/graph/index_cache.js +14 -14
- package/dist/graph/indexer.js +1 -1
- package/dist/graph/reindex.js +46 -0
- package/dist/graph/skills_index_cache.js +2 -2
- package/dist/graph/sqlite_index.js +293 -0
- package/dist/graph/{bundle_imports.js → subgraphs.js} +216 -142
- package/dist/graph/visibility.js +3 -3
- package/dist/init/AGENT_START.md +5 -1
- package/dist/init/CLI_COMMAND_MATRIX.md +21 -7
- package/dist/init/README.md +20 -10
- package/dist/init/config.json +6 -2
- package/dist/init/core/rule-1-mdkg-conventions.md +2 -1
- package/dist/init/core/rule-3-cli-contract.md +32 -24
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +28 -12
- package/dist/init/core/rule-5-release-and-versioning.md +4 -3
- package/dist/init/init-manifest.json +10 -10
- package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +1 -1
- package/dist/util/argparse.js +2 -0
- package/dist/util/atomic.js +44 -0
- package/dist/util/lock.js +72 -0
- package/package.json +13 -9
- package/dist/commands/bundle_import.js +0 -243
|
@@ -0,0 +1,312 @@
|
|
|
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.runSubgraphAddCommand = runSubgraphAddCommand;
|
|
7
|
+
exports.runSubgraphListCommand = runSubgraphListCommand;
|
|
8
|
+
exports.runSubgraphShowCommand = runSubgraphShowCommand;
|
|
9
|
+
exports.runSubgraphRemoveCommand = runSubgraphRemoveCommand;
|
|
10
|
+
exports.runSubgraphEnableCommand = runSubgraphEnableCommand;
|
|
11
|
+
exports.runSubgraphDisableCommand = runSubgraphDisableCommand;
|
|
12
|
+
exports.runSubgraphVerifyCommand = runSubgraphVerifyCommand;
|
|
13
|
+
exports.runSubgraphRefreshCommand = runSubgraphRefreshCommand;
|
|
14
|
+
const fs_1 = __importDefault(require("fs"));
|
|
15
|
+
const path_1 = __importDefault(require("path"));
|
|
16
|
+
const config_1 = require("../core/config");
|
|
17
|
+
const migrate_1 = require("../core/migrate");
|
|
18
|
+
const workspace_path_1 = require("../core/workspace_path");
|
|
19
|
+
const subgraphs_1 = require("../graph/subgraphs");
|
|
20
|
+
const reindex_1 = require("../graph/reindex");
|
|
21
|
+
const errors_1 = require("../util/errors");
|
|
22
|
+
const atomic_1 = require("../util/atomic");
|
|
23
|
+
const lock_1 = require("../util/lock");
|
|
24
|
+
const ALIAS_RE = /^[a-z][a-z0-9_]*$/;
|
|
25
|
+
function writeJson(value) {
|
|
26
|
+
console.log(JSON.stringify(value, null, 2));
|
|
27
|
+
}
|
|
28
|
+
function normalizeAlias(alias) {
|
|
29
|
+
if (alias === "all") {
|
|
30
|
+
throw new errors_1.UsageError("subgraph alias cannot be 'all'");
|
|
31
|
+
}
|
|
32
|
+
if (alias !== alias.toLowerCase() || !ALIAS_RE.test(alias)) {
|
|
33
|
+
throw new errors_1.UsageError("subgraph alias must be lowercase and use [a-z0-9_]");
|
|
34
|
+
}
|
|
35
|
+
return alias;
|
|
36
|
+
}
|
|
37
|
+
function normalizeVisibility(value) {
|
|
38
|
+
const normalized = (value ?? "private").toLowerCase();
|
|
39
|
+
if (normalized === "private" || normalized === "internal" || normalized === "public") {
|
|
40
|
+
return normalized;
|
|
41
|
+
}
|
|
42
|
+
throw new errors_1.UsageError("--visibility must be private, internal, or public");
|
|
43
|
+
}
|
|
44
|
+
function normalizeProfile(value) {
|
|
45
|
+
const normalized = (value ?? "private").toLowerCase();
|
|
46
|
+
if (normalized === "private" || normalized === "public") {
|
|
47
|
+
return normalized;
|
|
48
|
+
}
|
|
49
|
+
throw new errors_1.UsageError("--profile must be private or public");
|
|
50
|
+
}
|
|
51
|
+
function normalizeContained(value, label) {
|
|
52
|
+
try {
|
|
53
|
+
return (0, workspace_path_1.normalizeContainedWorkspacePath)(value, label);
|
|
54
|
+
}
|
|
55
|
+
catch (err) {
|
|
56
|
+
throw new errors_1.UsageError(err instanceof Error ? err.message : String(err));
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function readRawConfig(root) {
|
|
60
|
+
const configPath = path_1.default.join(root, ".mdkg", "config.json");
|
|
61
|
+
if (!fs_1.default.existsSync(configPath)) {
|
|
62
|
+
throw new errors_1.NotFoundError(`config not found at ${configPath}`);
|
|
63
|
+
}
|
|
64
|
+
let parsed;
|
|
65
|
+
try {
|
|
66
|
+
parsed = JSON.parse(fs_1.default.readFileSync(configPath, "utf8"));
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
throw new errors_1.UsageError(`failed to read config: ${err instanceof Error ? err.message : String(err)}`);
|
|
70
|
+
}
|
|
71
|
+
const migrated = (0, migrate_1.migrateConfig)(parsed).config;
|
|
72
|
+
(0, config_1.validateConfigSchema)(migrated);
|
|
73
|
+
if (typeof migrated !== "object" || migrated === null || Array.isArray(migrated)) {
|
|
74
|
+
throw new errors_1.UsageError("config must be a JSON object");
|
|
75
|
+
}
|
|
76
|
+
return { configPath, raw: migrated };
|
|
77
|
+
}
|
|
78
|
+
function writeRawConfig(configPath, raw) {
|
|
79
|
+
(0, atomic_1.atomicWriteFile)(configPath, `${JSON.stringify(raw, null, 2)}\n`);
|
|
80
|
+
}
|
|
81
|
+
function getSubgraphs(raw) {
|
|
82
|
+
if (raw.bundle_imports !== undefined) {
|
|
83
|
+
throw new errors_1.UsageError("config uses legacy bundle_imports; run `mdkg upgrade --apply` before editing subgraphs");
|
|
84
|
+
}
|
|
85
|
+
const subgraphs = raw.subgraphs;
|
|
86
|
+
if (subgraphs === undefined) {
|
|
87
|
+
raw.subgraphs = {};
|
|
88
|
+
return raw.subgraphs;
|
|
89
|
+
}
|
|
90
|
+
if (typeof subgraphs !== "object" || subgraphs === null || Array.isArray(subgraphs)) {
|
|
91
|
+
throw new errors_1.UsageError("config.subgraphs must be an object");
|
|
92
|
+
}
|
|
93
|
+
return subgraphs;
|
|
94
|
+
}
|
|
95
|
+
function receiptForHealth(action, health) {
|
|
96
|
+
return {
|
|
97
|
+
action,
|
|
98
|
+
subgraph: health,
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function healthByAlias(root, alias) {
|
|
102
|
+
const config = (0, config_1.loadConfig)(root);
|
|
103
|
+
const health = (0, subgraphs_1.buildSubgraphsIndex)(root, config).index.subgraphs.find((item) => item.alias === alias);
|
|
104
|
+
if (!health) {
|
|
105
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
106
|
+
}
|
|
107
|
+
return health;
|
|
108
|
+
}
|
|
109
|
+
function withSubgraphLock(root, fn) {
|
|
110
|
+
const config = (0, config_1.loadConfig)(root);
|
|
111
|
+
return (0, lock_1.withMutationLock)(root, config.index.lock_timeout_ms, fn);
|
|
112
|
+
}
|
|
113
|
+
function runSubgraphAddCommandLocked(options) {
|
|
114
|
+
const alias = normalizeAlias(options.alias);
|
|
115
|
+
const bundlePath = normalizeContained(options.bundlePath, "subgraph bundle path");
|
|
116
|
+
const visibility = normalizeVisibility(options.visibility);
|
|
117
|
+
const expected_profile = normalizeProfile(options.profile);
|
|
118
|
+
if (visibility !== "private" && expected_profile !== "public") {
|
|
119
|
+
throw new errors_1.UsageError("--profile public is required when --visibility is public or internal");
|
|
120
|
+
}
|
|
121
|
+
const source_path = options.sourcePath
|
|
122
|
+
? normalizeContained(options.sourcePath, "subgraph source path")
|
|
123
|
+
: undefined;
|
|
124
|
+
if (options.maxStaleSeconds !== undefined && (!Number.isInteger(options.maxStaleSeconds) || options.maxStaleSeconds <= 0)) {
|
|
125
|
+
throw new errors_1.UsageError("--max-stale-seconds must be a positive integer");
|
|
126
|
+
}
|
|
127
|
+
const { configPath, raw } = readRawConfig(options.root);
|
|
128
|
+
const subgraphs = getSubgraphs(raw);
|
|
129
|
+
if (subgraphs[alias]) {
|
|
130
|
+
throw new errors_1.UsageError(`subgraph already exists: ${alias}`);
|
|
131
|
+
}
|
|
132
|
+
const workspaces = raw.workspaces;
|
|
133
|
+
if (workspaces && workspaces[alias]) {
|
|
134
|
+
throw new errors_1.UsageError(`subgraph alias collides with workspace: ${alias}`);
|
|
135
|
+
}
|
|
136
|
+
subgraphs[alias] = {
|
|
137
|
+
enabled: true,
|
|
138
|
+
visibility,
|
|
139
|
+
permissions: ["read"],
|
|
140
|
+
max_stale_seconds: options.maxStaleSeconds ?? 3600,
|
|
141
|
+
...(source_path ? { source_path } : {}),
|
|
142
|
+
...(options.sourceRepo ? { source_repo: options.sourceRepo } : {}),
|
|
143
|
+
sources: [
|
|
144
|
+
{
|
|
145
|
+
path: bundlePath,
|
|
146
|
+
enabled: true,
|
|
147
|
+
expected_profile,
|
|
148
|
+
},
|
|
149
|
+
],
|
|
150
|
+
};
|
|
151
|
+
raw.subgraphs = subgraphs;
|
|
152
|
+
const validated = (0, config_1.validateConfigSchema)(raw);
|
|
153
|
+
const health = (0, subgraphs_1.buildSubgraphsIndex)(options.root, validated).index.subgraphs.find((item) => item.alias === alias);
|
|
154
|
+
if (!health) {
|
|
155
|
+
throw new errors_1.NotFoundError(`subgraph not found after validation: ${alias}`);
|
|
156
|
+
}
|
|
157
|
+
if (health.error_count > 0) {
|
|
158
|
+
throw new errors_1.ValidationError(`subgraph ${alias} is invalid:\n${health.errors.join("\n")}`);
|
|
159
|
+
}
|
|
160
|
+
writeRawConfig(configPath, raw);
|
|
161
|
+
const receipt = receiptForHealth("added", health);
|
|
162
|
+
if (options.json) {
|
|
163
|
+
writeJson(receipt);
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
console.log(`subgraph added: ${alias} (${bundlePath})`);
|
|
167
|
+
if (health.warning_count > 0) {
|
|
168
|
+
console.log(`warnings: ${health.warning_count}`);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
function runSubgraphAddCommand(options) {
|
|
172
|
+
return withSubgraphLock(options.root, () => runSubgraphAddCommandLocked(options));
|
|
173
|
+
}
|
|
174
|
+
function runSubgraphListCommand(options) {
|
|
175
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
176
|
+
const subgraphs = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index.subgraphs;
|
|
177
|
+
if (options.json) {
|
|
178
|
+
writeJson({ action: "list", count: subgraphs.length, subgraphs });
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (subgraphs.length === 0) {
|
|
182
|
+
console.log("no subgraphs configured");
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
for (const item of subgraphs) {
|
|
186
|
+
const status = item.enabled ? item.error_count > 0 ? "invalid" : item.stale ? "stale" : "ok" : "disabled";
|
|
187
|
+
const sourcePaths = item.sources.map((source) => source.path).join(",");
|
|
188
|
+
console.log(`${item.alias} | ${status} | ${item.visibility} | ${sourcePaths}`);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
function runSubgraphShowCommand(options) {
|
|
192
|
+
const alias = normalizeAlias(options.alias);
|
|
193
|
+
const health = healthByAlias(options.root, alias);
|
|
194
|
+
if (options.json) {
|
|
195
|
+
writeJson(receiptForHealth("show", health));
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
console.log(`${health.alias} | ${health.enabled ? "enabled" : "disabled"} | ${health.visibility}`);
|
|
199
|
+
console.log(`permissions: ${health.permissions.join(",")}`);
|
|
200
|
+
console.log(`max source count: ${health.sources.length}`);
|
|
201
|
+
for (const source of health.sources) {
|
|
202
|
+
const status = source.enabled ? source.error_count > 0 ? "invalid" : source.stale ? "stale" : "ok" : "disabled";
|
|
203
|
+
console.log(`source: ${source.path} | ${status} | ${source.expected_profile}`);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function runSubgraphRemoveCommandLocked(options) {
|
|
207
|
+
const alias = normalizeAlias(options.alias);
|
|
208
|
+
const { configPath, raw } = readRawConfig(options.root);
|
|
209
|
+
const subgraphs = getSubgraphs(raw);
|
|
210
|
+
const existing = subgraphs[alias];
|
|
211
|
+
if (!existing) {
|
|
212
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
213
|
+
}
|
|
214
|
+
delete subgraphs[alias];
|
|
215
|
+
raw.subgraphs = subgraphs;
|
|
216
|
+
(0, config_1.validateConfigSchema)(raw);
|
|
217
|
+
writeRawConfig(configPath, raw);
|
|
218
|
+
const receipt = { action: "removed", subgraph: { alias } };
|
|
219
|
+
if (options.json) {
|
|
220
|
+
writeJson(receipt);
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
console.log(`subgraph removed: ${alias}`);
|
|
224
|
+
}
|
|
225
|
+
function runSubgraphRemoveCommand(options) {
|
|
226
|
+
return withSubgraphLock(options.root, () => runSubgraphRemoveCommandLocked(options));
|
|
227
|
+
}
|
|
228
|
+
function setSubgraphEnabledLocked(options, enabled) {
|
|
229
|
+
const alias = normalizeAlias(options.alias);
|
|
230
|
+
const { configPath, raw } = readRawConfig(options.root);
|
|
231
|
+
const subgraphs = getSubgraphs(raw);
|
|
232
|
+
const existing = subgraphs[alias];
|
|
233
|
+
if (!existing || typeof existing !== "object" || Array.isArray(existing)) {
|
|
234
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
235
|
+
}
|
|
236
|
+
subgraphs[alias] = { ...existing, enabled };
|
|
237
|
+
raw.subgraphs = subgraphs;
|
|
238
|
+
(0, config_1.validateConfigSchema)(raw);
|
|
239
|
+
writeRawConfig(configPath, raw);
|
|
240
|
+
const health = healthByAlias(options.root, alias);
|
|
241
|
+
const receipt = receiptForHealth(enabled ? "enabled" : "disabled", health);
|
|
242
|
+
if (options.json) {
|
|
243
|
+
writeJson(receipt);
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
console.log(`subgraph ${enabled ? "enabled" : "disabled"}: ${alias}`);
|
|
247
|
+
}
|
|
248
|
+
function runSubgraphEnableCommand(options) {
|
|
249
|
+
withSubgraphLock(options.root, () => setSubgraphEnabledLocked(options, true));
|
|
250
|
+
}
|
|
251
|
+
function runSubgraphDisableCommand(options) {
|
|
252
|
+
withSubgraphLock(options.root, () => setSubgraphEnabledLocked(options, false));
|
|
253
|
+
}
|
|
254
|
+
function selectHealth(items, alias, all) {
|
|
255
|
+
const includeAll = all || !alias;
|
|
256
|
+
const selected = items.filter((item) => includeAll ? true : item.alias === alias);
|
|
257
|
+
if (!includeAll && selected.length === 0) {
|
|
258
|
+
throw new errors_1.NotFoundError(`subgraph not found: ${alias}`);
|
|
259
|
+
}
|
|
260
|
+
return selected;
|
|
261
|
+
}
|
|
262
|
+
function runSubgraphVerifyCommand(options) {
|
|
263
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
264
|
+
const subgraphs = selectHealth((0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index.subgraphs, options.alias, options.all);
|
|
265
|
+
const ok = subgraphs.every((item) => item.error_count === 0 && !item.stale);
|
|
266
|
+
const receipt = { action: "verified", ok, count: subgraphs.length, subgraphs };
|
|
267
|
+
if (options.json) {
|
|
268
|
+
writeJson(receipt);
|
|
269
|
+
}
|
|
270
|
+
else if (ok) {
|
|
271
|
+
console.log(`subgraphs verified: ${subgraphs.length}`);
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
console.log(`subgraph verify failed: ${subgraphs.length}`);
|
|
275
|
+
for (const item of subgraphs) {
|
|
276
|
+
for (const warning of item.warnings) {
|
|
277
|
+
console.log(`warning: ${item.alias}: ${warning}`);
|
|
278
|
+
}
|
|
279
|
+
for (const error of item.errors) {
|
|
280
|
+
console.log(`error: ${item.alias}: ${error}`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
if (!ok) {
|
|
285
|
+
throw new errors_1.ValidationError("subgraph verify failed");
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
function runSubgraphRefreshCommand(options) {
|
|
289
|
+
withSubgraphLock(options.root, () => {
|
|
290
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
291
|
+
const result = (0, reindex_1.writeDerivedIndexes)(options.root, config);
|
|
292
|
+
const subgraphs = selectHealth((0, subgraphs_1.buildSubgraphsIndex)(options.root, config).index.subgraphs, options.alias, options.all);
|
|
293
|
+
const ok = subgraphs.every((item) => item.error_count === 0);
|
|
294
|
+
const receipt = {
|
|
295
|
+
action: "refreshed",
|
|
296
|
+
ok,
|
|
297
|
+
count: subgraphs.length,
|
|
298
|
+
paths: result.paths,
|
|
299
|
+
subgraphs,
|
|
300
|
+
};
|
|
301
|
+
if (options.json) {
|
|
302
|
+
writeJson(receipt);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
console.log(`subgraphs refreshed: ${subgraphs.length}`);
|
|
306
|
+
console.log(`index: ${path_1.default.relative(options.root, result.paths.subgraphs)}`);
|
|
307
|
+
}
|
|
308
|
+
if (!ok) {
|
|
309
|
+
throw new errors_1.ValidationError("subgraph refresh failed");
|
|
310
|
+
}
|
|
311
|
+
});
|
|
312
|
+
}
|
package/dist/commands/task.js
CHANGED
|
@@ -11,12 +11,15 @@ const path_1 = __importDefault(require("path"));
|
|
|
11
11
|
const config_1 = require("../core/config");
|
|
12
12
|
const indexer_1 = require("../graph/indexer");
|
|
13
13
|
const index_cache_1 = require("../graph/index_cache");
|
|
14
|
+
const reindex_1 = require("../graph/reindex");
|
|
14
15
|
const frontmatter_1 = require("../graph/frontmatter");
|
|
15
16
|
const skills_indexer_1 = require("../graph/skills_indexer");
|
|
16
17
|
const date_1 = require("../util/date");
|
|
17
18
|
const errors_1 = require("../util/errors");
|
|
18
19
|
const qid_1 = require("../util/qid");
|
|
19
20
|
const id_1 = require("../util/id");
|
|
21
|
+
const atomic_1 = require("../util/atomic");
|
|
22
|
+
const lock_1 = require("../util/lock");
|
|
20
23
|
const event_support_1 = require("./event_support");
|
|
21
24
|
const checkpoint_1 = require("./checkpoint");
|
|
22
25
|
const MUTABLE_TASK_TYPES = new Set(["task", "bug", "test"]);
|
|
@@ -93,8 +96,7 @@ function maybeReindex(root, config) {
|
|
|
93
96
|
if (!config.index.auto_reindex) {
|
|
94
97
|
return;
|
|
95
98
|
}
|
|
96
|
-
|
|
97
|
-
(0, index_cache_1.writeIndex)(outputPath, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
99
|
+
(0, reindex_1.writeDerivedIndexes)(root, config, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
98
100
|
}
|
|
99
101
|
function loadMutableTaskNode(root, idOrQid, wsHint) {
|
|
100
102
|
const config = (0, config_1.loadConfig)(root);
|
|
@@ -108,7 +110,7 @@ function loadMutableTaskNode(root, idOrQid, wsHint) {
|
|
|
108
110
|
throw new errors_1.NotFoundError(`task not found: ${idOrQid}`);
|
|
109
111
|
}
|
|
110
112
|
if (node.source?.imported) {
|
|
111
|
-
throw new errors_1.UsageError(`cannot mutate read-only
|
|
113
|
+
throw new errors_1.UsageError(`cannot mutate read-only subgraph node ${node.qid}; update the source workspace for subgraph ${node.source.subgraph_alias}`);
|
|
112
114
|
}
|
|
113
115
|
if (!MUTABLE_TASK_TYPES.has(node.type)) {
|
|
114
116
|
throw new errors_1.UsageError(`mdkg task only supports task, bug, and test nodes; use markdown editing for ${node.type}:${node.id}`);
|
|
@@ -132,7 +134,7 @@ function writeNodeFile(root, filePath, frontmatter, body) {
|
|
|
132
134
|
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
133
135
|
const frontmatterBlock = ["---", ...lines, "---"].join("\n");
|
|
134
136
|
const content = body.length > 0 ? `${frontmatterBlock}\n${body}` : frontmatterBlock;
|
|
135
|
-
|
|
137
|
+
(0, atomic_1.atomicWriteFile)(filePath, content);
|
|
136
138
|
}
|
|
137
139
|
function ensureStatusAllowed(config, statusRaw, flag = "--status") {
|
|
138
140
|
const normalized = statusRaw.trim().toLowerCase();
|
|
@@ -202,7 +204,7 @@ function printTaskMutationReceipt(action, root, loaded, json, checkpoint) {
|
|
|
202
204
|
}
|
|
203
205
|
console.log(`task ${action}: ${loaded.qid}`);
|
|
204
206
|
}
|
|
205
|
-
function
|
|
207
|
+
function runTaskStartCommandLocked(options) {
|
|
206
208
|
const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
|
|
207
209
|
const now = options.now ?? new Date();
|
|
208
210
|
loaded.frontmatter.status = ensureStatusAllowed(loaded.config, "progress");
|
|
@@ -222,7 +224,7 @@ function runTaskStartCommand(options) {
|
|
|
222
224
|
maybeWarnEventsDisabled(options.root, loaded.config, loaded.ws);
|
|
223
225
|
printTaskMutationReceipt("started", options.root, loaded, options.json);
|
|
224
226
|
}
|
|
225
|
-
function
|
|
227
|
+
function runTaskUpdateCommandLocked(options) {
|
|
226
228
|
const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
|
|
227
229
|
const now = options.now ?? new Date();
|
|
228
230
|
if (options.status) {
|
|
@@ -264,7 +266,7 @@ function runTaskUpdateCommand(options) {
|
|
|
264
266
|
});
|
|
265
267
|
printTaskMutationReceipt("updated", options.root, loaded, options.json);
|
|
266
268
|
}
|
|
267
|
-
function
|
|
269
|
+
function runTaskDoneCommandLocked(options) {
|
|
268
270
|
const loaded = loadMutableTaskNode(options.root, options.id, options.ws);
|
|
269
271
|
const now = options.now ?? new Date();
|
|
270
272
|
const nextLinks = appendUnique(toStringList(loaded.frontmatter.links), parseCsvList(options.addLinks));
|
|
@@ -316,3 +318,15 @@ function runTaskDoneCommand(options) {
|
|
|
316
318
|
maybeWarnEventsDisabled(options.root, loaded.config, loaded.ws);
|
|
317
319
|
printTaskMutationReceipt("done", options.root, loaded, options.json, checkpoint);
|
|
318
320
|
}
|
|
321
|
+
function runTaskStartCommand(options) {
|
|
322
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
323
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskStartCommandLocked(options));
|
|
324
|
+
}
|
|
325
|
+
function runTaskUpdateCommand(options) {
|
|
326
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
327
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskUpdateCommandLocked(options));
|
|
328
|
+
}
|
|
329
|
+
function runTaskDoneCommand(options) {
|
|
330
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
331
|
+
return (0, lock_1.withMutationLock)(options.root, config.index.lock_timeout_ms, () => runTaskDoneCommandLocked(options));
|
|
332
|
+
}
|
package/dist/commands/upgrade.js
CHANGED
|
@@ -171,12 +171,57 @@ function planSeedFile(options) {
|
|
|
171
171
|
});
|
|
172
172
|
return false;
|
|
173
173
|
}
|
|
174
|
+
function migrateLegacyBundleImportsConfig(input) {
|
|
175
|
+
if (typeof input !== "object" || input === null || Array.isArray(input)) {
|
|
176
|
+
return { config: input, changed: false };
|
|
177
|
+
}
|
|
178
|
+
const raw = { ...input };
|
|
179
|
+
if (raw.bundle_imports === undefined) {
|
|
180
|
+
if (raw.subgraphs === undefined) {
|
|
181
|
+
raw.subgraphs = {};
|
|
182
|
+
return { config: raw, changed: true };
|
|
183
|
+
}
|
|
184
|
+
return { config: raw, changed: false };
|
|
185
|
+
}
|
|
186
|
+
if (raw.subgraphs !== undefined) {
|
|
187
|
+
return { config: raw, changed: false };
|
|
188
|
+
}
|
|
189
|
+
const subgraphs = {};
|
|
190
|
+
const legacy = raw.bundle_imports;
|
|
191
|
+
if (legacy && typeof legacy === "object" && !Array.isArray(legacy)) {
|
|
192
|
+
for (const [alias, value] of Object.entries(legacy)) {
|
|
193
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
194
|
+
continue;
|
|
195
|
+
}
|
|
196
|
+
const entry = value;
|
|
197
|
+
subgraphs[alias] = {
|
|
198
|
+
enabled: entry.enabled ?? true,
|
|
199
|
+
visibility: entry.visibility ?? "private",
|
|
200
|
+
permissions: ["read"],
|
|
201
|
+
max_stale_seconds: entry.max_stale_seconds ?? 3600,
|
|
202
|
+
...(entry.source_path ? { source_path: entry.source_path } : {}),
|
|
203
|
+
...(entry.source_repo ? { source_repo: entry.source_repo } : {}),
|
|
204
|
+
sources: [
|
|
205
|
+
{
|
|
206
|
+
path: entry.path,
|
|
207
|
+
enabled: true,
|
|
208
|
+
expected_profile: entry.expected_profile ?? "private",
|
|
209
|
+
},
|
|
210
|
+
],
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
raw.subgraphs = subgraphs;
|
|
215
|
+
delete raw.bundle_imports;
|
|
216
|
+
return { config: raw, changed: true };
|
|
217
|
+
}
|
|
174
218
|
function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
175
219
|
const cfgPath = (0, paths_1.configPath)(root);
|
|
176
220
|
const raw = JSON.parse(fs_1.default.readFileSync(cfgPath, "utf8"));
|
|
177
221
|
const migrated = (0, migrate_1.migrateConfig)(raw);
|
|
178
|
-
|
|
179
|
-
|
|
222
|
+
const nextConfig = migrateLegacyBundleImportsConfig(migrated.config);
|
|
223
|
+
(0, config_1.validateConfigSchema)(nextConfig.config);
|
|
224
|
+
if (migrated.from === migrated.to && !nextConfig.changed) {
|
|
180
225
|
summary.unchanged += 1;
|
|
181
226
|
return;
|
|
182
227
|
}
|
|
@@ -184,10 +229,12 @@ function migrateConfigIfNeeded(root, dryRun, summary, changes) {
|
|
|
184
229
|
path: ".mdkg/config.json",
|
|
185
230
|
category: "config",
|
|
186
231
|
action: "migrate",
|
|
187
|
-
reason:
|
|
232
|
+
reason: nextConfig.changed
|
|
233
|
+
? `config subgraphs migration${migrated.from === migrated.to ? "" : ` and schema_version ${migrated.from} -> ${migrated.to}`}`
|
|
234
|
+
: `config schema_version ${migrated.from} -> ${migrated.to}`,
|
|
188
235
|
});
|
|
189
236
|
if (!dryRun) {
|
|
190
|
-
writeFile(cfgPath, `${JSON.stringify(
|
|
237
|
+
writeFile(cfgPath, `${JSON.stringify(nextConfig.config, null, 2)}\n`);
|
|
191
238
|
}
|
|
192
239
|
}
|
|
193
240
|
function isIgnoredBySimpleGitignore(root, relativePath) {
|
|
@@ -12,8 +12,9 @@ const node_1 = require("../graph/node");
|
|
|
12
12
|
const skills_indexer_1 = require("../graph/skills_indexer");
|
|
13
13
|
const workspace_files_1 = require("../graph/workspace_files");
|
|
14
14
|
const validate_graph_1 = require("../graph/validate_graph");
|
|
15
|
-
const
|
|
15
|
+
const subgraphs_1 = require("../graph/subgraphs");
|
|
16
16
|
const visibility_1 = require("../graph/visibility");
|
|
17
|
+
const sqlite_index_1 = require("../graph/sqlite_index");
|
|
17
18
|
const errors_1 = require("../util/errors");
|
|
18
19
|
const skill_mirror_1 = require("./skill_mirror");
|
|
19
20
|
const RECOMMENDED_HEADINGS = {
|
|
@@ -207,6 +208,11 @@ function runValidateCommand(options) {
|
|
|
207
208
|
const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(options.root, config);
|
|
208
209
|
const errors = [];
|
|
209
210
|
const warnings = [];
|
|
211
|
+
if ((0, sqlite_index_1.isSqliteBackend)(config)) {
|
|
212
|
+
const health = (0, sqlite_index_1.sqliteHealth)(options.root, config);
|
|
213
|
+
warnings.push(...health.warnings);
|
|
214
|
+
errors.push(...health.errors);
|
|
215
|
+
}
|
|
210
216
|
if (templateSchemaInfo.fallbackTypes.length > 0) {
|
|
211
217
|
warnings.push(`using bundled template schema fallback for missing local type(s): ${templateSchemaInfo.fallbackTypes.join(", ")}; run \`mdkg upgrade --apply\` to vendor built-in templates`);
|
|
212
218
|
}
|
|
@@ -270,16 +276,16 @@ function runValidateCommand(options) {
|
|
|
270
276
|
nodes,
|
|
271
277
|
reverse_edges: {},
|
|
272
278
|
};
|
|
273
|
-
const
|
|
274
|
-
for (const item of
|
|
279
|
+
const subgraphProjection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config);
|
|
280
|
+
for (const item of subgraphProjection.index.subgraphs) {
|
|
275
281
|
for (const warning of item.warnings) {
|
|
276
|
-
warnings.push(`
|
|
282
|
+
warnings.push(`subgraph ${item.alias}: ${warning}`);
|
|
277
283
|
}
|
|
278
284
|
for (const error of item.errors) {
|
|
279
|
-
errors.push(`
|
|
285
|
+
errors.push(`subgraph ${item.alias}: ${error}`);
|
|
280
286
|
}
|
|
281
287
|
}
|
|
282
|
-
const validationIndex = (0,
|
|
288
|
+
const validationIndex = (0, subgraphs_1.mergeSubgraphsIntoIndex)(index, subgraphProjection);
|
|
283
289
|
let knownSkills = new Set();
|
|
284
290
|
try {
|
|
285
291
|
const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(options.root, config);
|
package/dist/commands/work.js
CHANGED
|
@@ -15,12 +15,15 @@ const config_1 = require("../core/config");
|
|
|
15
15
|
const frontmatter_1 = require("../graph/frontmatter");
|
|
16
16
|
const indexer_1 = require("../graph/indexer");
|
|
17
17
|
const index_cache_1 = require("../graph/index_cache");
|
|
18
|
+
const reindex_1 = require("../graph/reindex");
|
|
18
19
|
const agent_file_types_1 = require("../graph/agent_file_types");
|
|
19
20
|
const loader_1 = require("../templates/loader");
|
|
20
21
|
const date_1 = require("../util/date");
|
|
21
22
|
const errors_1 = require("../util/errors");
|
|
22
23
|
const id_1 = require("../util/id");
|
|
23
24
|
const qid_1 = require("../util/qid");
|
|
25
|
+
const atomic_1 = require("../util/atomic");
|
|
26
|
+
const lock_1 = require("../util/lock");
|
|
24
27
|
const event_support_1 = require("./event_support");
|
|
25
28
|
const archive_1 = require("./archive");
|
|
26
29
|
const PRICING_MODELS = new Set(["free", "included", "quoted", "fixed", "metered", "subscription"]);
|
|
@@ -90,8 +93,7 @@ function maybeReindex(root, config) {
|
|
|
90
93
|
if (!config.index.auto_reindex) {
|
|
91
94
|
return;
|
|
92
95
|
}
|
|
93
|
-
|
|
94
|
-
(0, index_cache_1.writeIndex)(outputPath, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
96
|
+
(0, reindex_1.writeDerivedIndexes)(root, config, (0, indexer_1.buildIndex)(root, config, { tolerant: config.index.tolerant }));
|
|
95
97
|
}
|
|
96
98
|
function findNodeById(index, ws, id, type) {
|
|
97
99
|
const node = index.nodes[`${ws}:${id}`];
|
|
@@ -123,7 +125,7 @@ function nodeReceipt(root, node) {
|
|
|
123
125
|
function writeFrontmatterFile(filePath, frontmatter, body) {
|
|
124
126
|
const lines = (0, frontmatter_1.formatFrontmatter)(frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER);
|
|
125
127
|
const content = ["---", ...lines, "---", body.trimStart()].join("\n");
|
|
126
|
-
|
|
128
|
+
(0, atomic_1.atomicWriteFile)(filePath, content.endsWith("\n") ? content : `${content}\n`);
|
|
127
129
|
}
|
|
128
130
|
function createAgentWorkflowNode(options) {
|
|
129
131
|
const config = (0, config_1.loadConfig)(options.root);
|
|
@@ -149,8 +151,16 @@ function createAgentWorkflowNode(options) {
|
|
|
149
151
|
updated: today,
|
|
150
152
|
...options.overrides,
|
|
151
153
|
});
|
|
152
|
-
|
|
153
|
-
|
|
154
|
+
try {
|
|
155
|
+
(0, atomic_1.writeFileExclusive)(filePath, content);
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
|
|
159
|
+
if (code === "EEXIST") {
|
|
160
|
+
throw new errors_1.UsageError(`node already exists: ${path_1.default.relative(options.root, filePath)}`);
|
|
161
|
+
}
|
|
162
|
+
throw err;
|
|
163
|
+
}
|
|
154
164
|
maybeReindex(options.root, config);
|
|
155
165
|
(0, event_support_1.appendAutomaticEvent)({
|
|
156
166
|
root: options.root,
|
|
@@ -180,7 +190,7 @@ function resolveWorkNode(index, idOrQid, ws, allowedTypes, label) {
|
|
|
180
190
|
throw new errors_1.NotFoundError(`${label} not found: ${idOrQid}`);
|
|
181
191
|
}
|
|
182
192
|
if (node.source?.imported) {
|
|
183
|
-
throw new errors_1.UsageError(`cannot mutate read-only
|
|
193
|
+
throw new errors_1.UsageError(`cannot mutate read-only subgraph node ${node.qid}; update the source workspace for subgraph ${node.source.subgraph_alias}`);
|
|
184
194
|
}
|
|
185
195
|
return node;
|
|
186
196
|
}
|
|
@@ -200,7 +210,7 @@ function printReceipt(action, receipt, json) {
|
|
|
200
210
|
}
|
|
201
211
|
console.log(`work ${action}: ${receipt.qid} (${receipt.path})`);
|
|
202
212
|
}
|
|
203
|
-
function
|
|
213
|
+
function runWorkContractNewCommandLocked(options) {
|
|
204
214
|
const config = (0, config_1.loadConfig)(options.root);
|
|
205
215
|
const ws = normalizeWorkspace(options.ws);
|
|
206
216
|
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
@@ -226,7 +236,7 @@ function runWorkContractNewCommand(options) {
|
|
|
226
236
|
});
|
|
227
237
|
printReceipt("contract created", receipt, options.json);
|
|
228
238
|
}
|
|
229
|
-
function
|
|
239
|
+
function runWorkOrderNewCommandLocked(options) {
|
|
230
240
|
const config = (0, config_1.loadConfig)(options.root);
|
|
231
241
|
const ws = normalizeWorkspace(options.ws);
|
|
232
242
|
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
@@ -255,7 +265,7 @@ function runWorkOrderNewCommand(options) {
|
|
|
255
265
|
});
|
|
256
266
|
printReceipt("order created", receipt, options.json);
|
|
257
267
|
}
|
|
258
|
-
function
|
|
268
|
+
function runWorkOrderUpdateCommandLocked(options) {
|
|
259
269
|
const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "work_order");
|
|
260
270
|
if (options.status) {
|
|
261
271
|
loaded.frontmatter.order_status = normalizeEnum(options.status, "--status", ORDER_STATUSES);
|
|
@@ -267,7 +277,7 @@ function runWorkOrderUpdateCommand(options) {
|
|
|
267
277
|
maybeReindex(options.root, loaded.config);
|
|
268
278
|
printReceipt("order updated", nodeReceipt(options.root, loaded.node), options.json);
|
|
269
279
|
}
|
|
270
|
-
function
|
|
280
|
+
function runWorkReceiptNewCommandLocked(options) {
|
|
271
281
|
const config = (0, config_1.loadConfig)(options.root);
|
|
272
282
|
const ws = normalizeWorkspace(options.ws);
|
|
273
283
|
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
@@ -295,7 +305,7 @@ function runWorkReceiptNewCommand(options) {
|
|
|
295
305
|
});
|
|
296
306
|
printReceipt("receipt created", receipt, options.json);
|
|
297
307
|
}
|
|
298
|
-
function
|
|
308
|
+
function runWorkReceiptUpdateCommandLocked(options) {
|
|
299
309
|
const loaded = loadMutableAgentNode(options.root, options.id, options.ws, "receipt");
|
|
300
310
|
if (options.receiptStatus) {
|
|
301
311
|
loaded.frontmatter.receipt_status = normalizeEnum(options.receiptStatus, "--receipt-status", RECEIPT_STATUSES);
|
|
@@ -308,7 +318,7 @@ function runWorkReceiptUpdateCommand(options) {
|
|
|
308
318
|
maybeReindex(options.root, loaded.config);
|
|
309
319
|
printReceipt("receipt updated", nodeReceipt(options.root, loaded.node), options.json);
|
|
310
320
|
}
|
|
311
|
-
function
|
|
321
|
+
function runWorkArtifactAddCommandLocked(options) {
|
|
312
322
|
const config = (0, config_1.loadConfig)(options.root);
|
|
313
323
|
const ws = normalizeWorkspace(options.ws);
|
|
314
324
|
const { index } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
@@ -363,3 +373,25 @@ function runWorkArtifactAddCommand(options) {
|
|
|
363
373
|
}
|
|
364
374
|
console.log(`work artifact registered: ${target.qid} -> ${archiveUri}`);
|
|
365
375
|
}
|
|
376
|
+
function withWorkLock(root, fn) {
|
|
377
|
+
const config = (0, config_1.loadConfig)(root);
|
|
378
|
+
return (0, lock_1.withMutationLock)(root, config.index.lock_timeout_ms, fn);
|
|
379
|
+
}
|
|
380
|
+
function runWorkContractNewCommand(options) {
|
|
381
|
+
return withWorkLock(options.root, () => runWorkContractNewCommandLocked(options));
|
|
382
|
+
}
|
|
383
|
+
function runWorkOrderNewCommand(options) {
|
|
384
|
+
return withWorkLock(options.root, () => runWorkOrderNewCommandLocked(options));
|
|
385
|
+
}
|
|
386
|
+
function runWorkOrderUpdateCommand(options) {
|
|
387
|
+
return withWorkLock(options.root, () => runWorkOrderUpdateCommandLocked(options));
|
|
388
|
+
}
|
|
389
|
+
function runWorkReceiptNewCommand(options) {
|
|
390
|
+
return withWorkLock(options.root, () => runWorkReceiptNewCommandLocked(options));
|
|
391
|
+
}
|
|
392
|
+
function runWorkReceiptUpdateCommand(options) {
|
|
393
|
+
return withWorkLock(options.root, () => runWorkReceiptUpdateCommandLocked(options));
|
|
394
|
+
}
|
|
395
|
+
function runWorkArtifactAddCommand(options) {
|
|
396
|
+
return withWorkLock(options.root, () => runWorkArtifactAddCommandLocked(options));
|
|
397
|
+
}
|