mdkg 0.1.3 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +45 -1
  2. package/README.md +34 -10
  3. package/dist/cli.js +354 -87
  4. package/dist/commands/bundle.js +7 -7
  5. package/dist/commands/capability.js +118 -4
  6. package/dist/commands/doctor.js +15 -15
  7. package/dist/commands/goal.js +548 -0
  8. package/dist/commands/index.js +1 -1
  9. package/dist/commands/init.js +1 -0
  10. package/dist/commands/list.js +1 -1
  11. package/dist/commands/new.js +7 -1
  12. package/dist/commands/next.js +1 -1
  13. package/dist/commands/node_card.js +1 -1
  14. package/dist/commands/pack.js +1 -1
  15. package/dist/commands/search.js +1 -1
  16. package/dist/commands/show.js +1 -1
  17. package/dist/commands/subgraph.js +312 -0
  18. package/dist/commands/task.js +1 -1
  19. package/dist/commands/upgrade.js +54 -7
  20. package/dist/commands/validate.js +39 -7
  21. package/dist/commands/work.js +1 -1
  22. package/dist/core/config.js +95 -39
  23. package/dist/graph/frontmatter.js +8 -0
  24. package/dist/graph/goal_scope.js +127 -0
  25. package/dist/graph/index_cache.js +12 -12
  26. package/dist/graph/indexer.js +1 -1
  27. package/dist/graph/node.js +80 -1
  28. package/dist/graph/reindex.js +6 -6
  29. package/dist/graph/sqlite_index.js +6 -6
  30. package/dist/graph/{bundle_imports.js → subgraphs.js} +214 -140
  31. package/dist/graph/validate_graph.js +41 -0
  32. package/dist/graph/visibility.js +3 -3
  33. package/dist/init/AGENT_START.md +17 -1
  34. package/dist/init/CLI_COMMAND_MATRIX.md +43 -7
  35. package/dist/init/README.md +9 -8
  36. package/dist/init/config.json +1 -1
  37. package/dist/init/core/rule-3-cli-contract.md +56 -23
  38. package/dist/init/core/rule-4-repo-safety-and-ignores.md +7 -2
  39. package/dist/init/core/rule-6-templates-and-schemas.md +10 -1
  40. package/dist/init/init-manifest.json +20 -10
  41. package/dist/init/skills/default/pursue-mdkg-goal/SKILL.md +68 -0
  42. package/dist/init/skills/default/select-work-and-ground-context/SKILL.md +9 -7
  43. package/dist/init/skills/default/verify-close-and-checkpoint/SKILL.md +11 -10
  44. package/dist/init/templates/default/goal.md +91 -0
  45. package/dist/pack/order.js +2 -1
  46. package/dist/pack/pack.js +17 -0
  47. package/dist/util/argparse.js +2 -0
  48. package/package.json +8 -6
  49. package/dist/commands/bundle_import.js +0 -255
@@ -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
+ }
@@ -110,7 +110,7 @@ function loadMutableTaskNode(root, idOrQid, wsHint) {
110
110
  throw new errors_1.NotFoundError(`task not found: ${idOrQid}`);
111
111
  }
112
112
  if (node.source?.imported) {
113
- throw new errors_1.UsageError(`cannot mutate read-only imported node ${node.qid}; update the source workspace for bundle import ${node.source.import_alias}`);
113
+ throw new errors_1.UsageError(`cannot mutate read-only subgraph node ${node.qid}; update the source workspace for subgraph ${node.source.subgraph_alias}`);
114
114
  }
115
115
  if (!MUTABLE_TASK_TYPES.has(node.type)) {
116
116
  throw new errors_1.UsageError(`mdkg task only supports task, bug, and test nodes; use markdown editing for ${node.type}:${node.id}`);
@@ -18,7 +18,7 @@ const skill_mirror_1 = require("./skill_mirror");
18
18
  const DEFAULT_SEED_SUBDIR = path_1.default.resolve(__dirname, "..", "init");
19
19
  const PROTECTED_CORE_DOCS = new Set([".mdkg/core/SOUL.md", ".mdkg/core/HUMAN.md"]);
20
20
  const CREATE_ONLY_PRESERVED = new Set([".mdkg/core/core.md"]);
21
- const ARCHIVE_RAW_IGNORE_ENTRIES = [".mdkg/archive/**/source/"];
21
+ const LOCAL_STATE_IGNORE_ENTRIES = [".mdkg/state/", ".mdkg/archive/**/source/"];
22
22
  function seededInitEvent(nowIso) {
23
23
  const event = {
24
24
  ts: nowIso,
@@ -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
- (0, config_1.validateConfigSchema)(migrated.config);
179
- if (migrated.from === migrated.to) {
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: `config schema_version ${migrated.from} -> ${migrated.to}`,
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(migrated.config, null, 2)}\n`);
237
+ writeFile(cfgPath, `${JSON.stringify(nextConfig.config, null, 2)}\n`);
191
238
  }
192
239
  }
193
240
  function isIgnoredBySimpleGitignore(root, relativePath) {
@@ -260,7 +307,7 @@ function ensureArchiveIgnorePolicy(root, dryRun, summary, changes) {
260
307
  const raw = fs_1.default.existsSync(ignorePath) ? fs_1.default.readFileSync(ignorePath, "utf8") : "";
261
308
  const lines = raw.split(/\r?\n/);
262
309
  const existing = new Set(lines.map((line) => line.trim()).filter(Boolean));
263
- const additions = ARCHIVE_RAW_IGNORE_ENTRIES.filter((entry) => !existing.has(entry));
310
+ const additions = LOCAL_STATE_IGNORE_ENTRIES.filter((entry) => !existing.has(entry));
264
311
  if (additions.length === 0) {
265
312
  summary.unchanged += 1;
266
313
  return;
@@ -269,7 +316,7 @@ function ensureArchiveIgnorePolicy(root, dryRun, summary, changes) {
269
316
  path: ".gitignore",
270
317
  category: "ignore_policy",
271
318
  action: fs_1.default.existsSync(ignorePath) ? "update" : "create",
272
- reason: "ignore raw local archive source copies while keeping sidecars and zip caches commit-eligible",
319
+ reason: "ignore local mdkg state and raw archive source copies while keeping authored graph records commit-eligible",
273
320
  });
274
321
  if (!dryRun) {
275
322
  const suffix = raw.length === 0 || raw.endsWith("\n") ? "" : "\n";
@@ -12,7 +12,7 @@ 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 bundle_imports_1 = require("../graph/bundle_imports");
15
+ const subgraphs_1 = require("../graph/subgraphs");
16
16
  const visibility_1 = require("../graph/visibility");
17
17
  const sqlite_index_1 = require("../graph/sqlite_index");
18
18
  const errors_1 = require("../util/errors");
@@ -142,6 +142,38 @@ function buildWorkspaceMap(config) {
142
142
  }
143
143
  return workspaces;
144
144
  }
145
+ function addReverseEdge(reverse, edgeKey, target, source) {
146
+ if (!target) {
147
+ return;
148
+ }
149
+ reverse[edgeKey] = reverse[edgeKey] ?? {};
150
+ reverse[edgeKey][target] = reverse[edgeKey][target] ?? [];
151
+ reverse[edgeKey][target].push(source);
152
+ }
153
+ function buildReverseEdges(nodes) {
154
+ const reverse = {};
155
+ for (const [qid, node] of Object.entries(nodes)) {
156
+ addReverseEdge(reverse, "epic", node.edges.epic, qid);
157
+ addReverseEdge(reverse, "parent", node.edges.parent, qid);
158
+ addReverseEdge(reverse, "prev", node.edges.prev, qid);
159
+ addReverseEdge(reverse, "next", node.edges.next, qid);
160
+ for (const target of node.edges.relates) {
161
+ addReverseEdge(reverse, "relates", target, qid);
162
+ }
163
+ for (const target of node.edges.blocked_by) {
164
+ addReverseEdge(reverse, "blocked_by", target, qid);
165
+ }
166
+ for (const target of node.edges.blocks) {
167
+ addReverseEdge(reverse, "blocks", target, qid);
168
+ }
169
+ }
170
+ for (const targets of Object.values(reverse)) {
171
+ for (const sources of Object.values(targets)) {
172
+ sources.sort();
173
+ }
174
+ }
175
+ return reverse;
176
+ }
145
177
  function listDirectories(dirPath) {
146
178
  if (!fs_1.default.existsSync(dirPath)) {
147
179
  return [];
@@ -274,18 +306,18 @@ function runValidateCommand(options) {
274
306
  },
275
307
  workspaces: buildWorkspaceMap(config),
276
308
  nodes,
277
- reverse_edges: {},
309
+ reverse_edges: buildReverseEdges(nodes),
278
310
  };
279
- const importProjection = (0, bundle_imports_1.buildBundleImportsIndex)(options.root, config);
280
- for (const item of importProjection.index.imports) {
311
+ const subgraphProjection = (0, subgraphs_1.buildSubgraphsIndex)(options.root, config);
312
+ for (const item of subgraphProjection.index.subgraphs) {
281
313
  for (const warning of item.warnings) {
282
- warnings.push(`bundle import ${item.alias}: ${warning}`);
314
+ warnings.push(`subgraph ${item.alias}: ${warning}`);
283
315
  }
284
316
  for (const error of item.errors) {
285
- errors.push(`bundle import ${item.alias}: ${error}`);
317
+ errors.push(`subgraph ${item.alias}: ${error}`);
286
318
  }
287
319
  }
288
- const validationIndex = (0, bundle_imports_1.mergeBundleImportsIntoIndex)(index, importProjection);
320
+ const validationIndex = (0, subgraphs_1.mergeSubgraphsIntoIndex)(index, subgraphProjection);
289
321
  let knownSkills = new Set();
290
322
  try {
291
323
  const skillsIndex = (0, skills_indexer_1.buildSkillsIndex)(options.root, config);
@@ -190,7 +190,7 @@ function resolveWorkNode(index, idOrQid, ws, allowedTypes, label) {
190
190
  throw new errors_1.NotFoundError(`${label} not found: ${idOrQid}`);
191
191
  }
192
192
  if (node.source?.imported) {
193
- throw new errors_1.UsageError(`cannot mutate read-only imported node ${node.qid}; update the source workspace for bundle import ${node.source.import_alias}`);
193
+ throw new errors_1.UsageError(`cannot mutate read-only subgraph node ${node.qid}; update the source workspace for subgraph ${node.source.subgraph_alias}`);
194
194
  }
195
195
  return node;
196
196
  }