mdkg 0.3.6 → 0.3.7
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 +69 -0
- package/CLI_COMMAND_MATRIX.md +56 -7
- package/README.md +33 -3
- package/dist/cli.js +132 -11
- package/dist/command-contract.json +431 -16
- package/dist/commands/bundle.js +2 -0
- package/dist/commands/checkpoint.js +139 -1
- package/dist/commands/db.js +8 -0
- package/dist/commands/format.js +116 -0
- package/dist/commands/goal.js +60 -0
- package/dist/commands/graph.js +148 -0
- package/dist/commands/handoff.js +295 -0
- package/dist/commands/mcp.js +9 -0
- package/dist/commands/pack.js +3 -1
- package/dist/commands/query_output.js +2 -0
- package/dist/commands/show.js +8 -0
- package/dist/commands/status.js +1 -0
- package/dist/commands/task.js +2 -0
- package/dist/commands/upgrade.js +61 -0
- package/dist/commands/validate.js +162 -3
- package/dist/commands/work.js +164 -0
- package/dist/core/project_db_queue_contract.js +101 -0
- package/dist/graph/edges.js +15 -0
- package/dist/graph/frontmatter.js +4 -1
- package/dist/graph/indexer.js +8 -0
- package/dist/graph/node.js +12 -1
- package/dist/graph/sqlite_index.js +2 -0
- package/dist/graph/subgraphs.js +2 -0
- package/dist/graph/validate_graph.js +5 -0
- package/dist/graph/visibility.js +6 -0
- package/dist/init/AGENT_START.md +4 -1
- package/dist/init/CLI_COMMAND_MATRIX.md +24 -3
- package/dist/init/README.md +17 -2
- package/dist/init/init-manifest.json +12 -12
- package/dist/init/templates/default/bug.md +2 -0
- package/dist/init/templates/default/chk.md +3 -0
- package/dist/init/templates/default/epic.md +2 -0
- package/dist/init/templates/default/feat.md +2 -0
- package/dist/init/templates/default/goal.md +3 -0
- package/dist/init/templates/default/spike.md +2 -0
- package/dist/init/templates/default/task.md +2 -0
- package/dist/init/templates/default/test.md +2 -0
- package/dist/pack/export_json.js +20 -8
- package/dist/pack/export_md.js +15 -4
- package/dist/pack/export_xml.js +9 -4
- package/dist/pack/metrics.js +12 -4
- package/dist/pack/pack.js +9 -1
- package/package.json +6 -2
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.CHECKPOINT_KINDS = void 0;
|
|
6
7
|
exports.createCheckpoint = createCheckpoint;
|
|
7
8
|
exports.runCheckpointNewCommand = runCheckpointNewCommand;
|
|
8
9
|
const fs_1 = __importDefault(require("fs"));
|
|
@@ -17,6 +18,20 @@ const atomic_1 = require("../util/atomic");
|
|
|
17
18
|
const lock_1 = require("../util/lock");
|
|
18
19
|
const sqlite_index_1 = require("../graph/sqlite_index");
|
|
19
20
|
const event_support_1 = require("./event_support");
|
|
21
|
+
exports.CHECKPOINT_KINDS = [
|
|
22
|
+
"implementation",
|
|
23
|
+
"test-proof",
|
|
24
|
+
"goal-closeout",
|
|
25
|
+
"audit",
|
|
26
|
+
"handoff",
|
|
27
|
+
];
|
|
28
|
+
function normalizeCheckpointKind(value) {
|
|
29
|
+
const normalized = (value ?? "implementation").toLowerCase();
|
|
30
|
+
if (exports.CHECKPOINT_KINDS.includes(normalized)) {
|
|
31
|
+
return normalized;
|
|
32
|
+
}
|
|
33
|
+
throw new errors_1.UsageError(`--kind must be one of ${exports.CHECKPOINT_KINDS.join(", ")}`);
|
|
34
|
+
}
|
|
20
35
|
function parseCsvList(raw) {
|
|
21
36
|
if (!raw) {
|
|
22
37
|
return [];
|
|
@@ -83,6 +98,125 @@ function normalizeWorkspace(value) {
|
|
|
83
98
|
}
|
|
84
99
|
return normalized;
|
|
85
100
|
}
|
|
101
|
+
function kindSpecificSection(kind) {
|
|
102
|
+
switch (kind) {
|
|
103
|
+
case "implementation":
|
|
104
|
+
return [
|
|
105
|
+
"# Implementation Details",
|
|
106
|
+
"",
|
|
107
|
+
"- Code or graph surfaces changed:",
|
|
108
|
+
"- Architecture or data-shape notes:",
|
|
109
|
+
"- Compatibility notes:",
|
|
110
|
+
];
|
|
111
|
+
case "test-proof":
|
|
112
|
+
return [
|
|
113
|
+
"# Test Proof",
|
|
114
|
+
"",
|
|
115
|
+
"- Test target:",
|
|
116
|
+
"- Fixtures or temp repos:",
|
|
117
|
+
"- Coverage gaps:",
|
|
118
|
+
];
|
|
119
|
+
case "goal-closeout":
|
|
120
|
+
return [
|
|
121
|
+
"# Goal Closeout",
|
|
122
|
+
"",
|
|
123
|
+
"- Goal condition result:",
|
|
124
|
+
"- Scoped nodes closed:",
|
|
125
|
+
"- Remaining deferred work:",
|
|
126
|
+
];
|
|
127
|
+
case "audit":
|
|
128
|
+
return [
|
|
129
|
+
"# Audit Findings",
|
|
130
|
+
"",
|
|
131
|
+
"- Reviewed surfaces:",
|
|
132
|
+
"- Findings:",
|
|
133
|
+
"- Residual risk:",
|
|
134
|
+
];
|
|
135
|
+
case "handoff":
|
|
136
|
+
return [
|
|
137
|
+
"# Handoff Summary",
|
|
138
|
+
"",
|
|
139
|
+
"- Recipient/context:",
|
|
140
|
+
"- Starting node or command:",
|
|
141
|
+
"- Explicit boundaries:",
|
|
142
|
+
];
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
function checkpointBody(kind) {
|
|
146
|
+
return [
|
|
147
|
+
"# Summary",
|
|
148
|
+
"",
|
|
149
|
+
"What was completed in this phase? What is now true?",
|
|
150
|
+
"",
|
|
151
|
+
"# Scope Covered",
|
|
152
|
+
"",
|
|
153
|
+
"Keep `scope` frontmatter updated when possible.",
|
|
154
|
+
"",
|
|
155
|
+
"## Changed Surfaces",
|
|
156
|
+
"",
|
|
157
|
+
"- files, commands, nodes, docs, or runtime surfaces changed",
|
|
158
|
+
"",
|
|
159
|
+
"## Boundaries",
|
|
160
|
+
"",
|
|
161
|
+
"- in scope:",
|
|
162
|
+
"- out of scope:",
|
|
163
|
+
"- raw secrets, raw prompts, raw payloads, and bulky execution traces excluded:",
|
|
164
|
+
"",
|
|
165
|
+
"# Decisions Captured",
|
|
166
|
+
"",
|
|
167
|
+
"Link the most important decision records.",
|
|
168
|
+
"",
|
|
169
|
+
"# Implementation Summary",
|
|
170
|
+
"",
|
|
171
|
+
"What changed? What patterns or architecture emerged?",
|
|
172
|
+
"",
|
|
173
|
+
...kindSpecificSection(kind),
|
|
174
|
+
"",
|
|
175
|
+
"# Verification / Testing",
|
|
176
|
+
"",
|
|
177
|
+
"## Command Evidence",
|
|
178
|
+
"",
|
|
179
|
+
"- command:",
|
|
180
|
+
"- result:",
|
|
181
|
+
"",
|
|
182
|
+
"## Pass / Fail Status",
|
|
183
|
+
"",
|
|
184
|
+
"- status:",
|
|
185
|
+
"",
|
|
186
|
+
"## Known Warnings",
|
|
187
|
+
"",
|
|
188
|
+
"- warning:",
|
|
189
|
+
"",
|
|
190
|
+
"# Known Issues / Follow-ups",
|
|
191
|
+
"",
|
|
192
|
+
"- issue 1",
|
|
193
|
+
"- issue 2",
|
|
194
|
+
"",
|
|
195
|
+
"## Follow-up Refs",
|
|
196
|
+
"",
|
|
197
|
+
"- task/test/goal refs:",
|
|
198
|
+
"",
|
|
199
|
+
"# Links / Artifacts",
|
|
200
|
+
"",
|
|
201
|
+
"- packs",
|
|
202
|
+
"- PRs/commits",
|
|
203
|
+
"- docs",
|
|
204
|
+
"- dashboards",
|
|
205
|
+
"",
|
|
206
|
+
"# Raw Content Safety",
|
|
207
|
+
"",
|
|
208
|
+
"- Summarize evidence and use refs, hashes, and artifact links instead of raw secrets, raw prompts, raw payloads, or bulky execution traces.",
|
|
209
|
+
"",
|
|
210
|
+
].join("\n");
|
|
211
|
+
}
|
|
212
|
+
function replaceRenderedBody(content, body) {
|
|
213
|
+
const marker = "\n---\n";
|
|
214
|
+
const start = content.indexOf(marker);
|
|
215
|
+
if (!content.startsWith("---\n") || start === -1) {
|
|
216
|
+
return content;
|
|
217
|
+
}
|
|
218
|
+
return `${content.slice(0, start + marker.length)}${body}`;
|
|
219
|
+
}
|
|
86
220
|
function createCheckpointLocked(options) {
|
|
87
221
|
const title = options.title.trim();
|
|
88
222
|
if (!title) {
|
|
@@ -130,12 +264,14 @@ function createCheckpointLocked(options) {
|
|
|
130
264
|
}
|
|
131
265
|
}
|
|
132
266
|
const scope = parseCsvList(options.scope).map((value) => normalizeId(value, "--scope"));
|
|
267
|
+
const kind = normalizeCheckpointKind(options.kind);
|
|
133
268
|
const now = options.now ?? new Date();
|
|
134
269
|
const today = (0, date_1.formatDate)(now);
|
|
135
270
|
const template = (0, loader_1.loadTemplate)(options.root, config, "checkpoint", options.template);
|
|
136
271
|
const content = (0, loader_1.renderTemplate)(template, {
|
|
137
272
|
id,
|
|
138
273
|
title,
|
|
274
|
+
checkpoint_kind: kind,
|
|
139
275
|
status,
|
|
140
276
|
priority,
|
|
141
277
|
created: today,
|
|
@@ -143,8 +279,9 @@ function createCheckpointLocked(options) {
|
|
|
143
279
|
relates,
|
|
144
280
|
scope,
|
|
145
281
|
});
|
|
282
|
+
const rendered = replaceRenderedBody(content, checkpointBody(kind));
|
|
146
283
|
try {
|
|
147
|
-
(0, atomic_1.writeFileExclusive)(filePath,
|
|
284
|
+
(0, atomic_1.writeFileExclusive)(filePath, rendered);
|
|
148
285
|
}
|
|
149
286
|
catch (err) {
|
|
150
287
|
const code = typeof err === "object" && err !== null && "code" in err ? String(err.code) : "";
|
|
@@ -168,6 +305,7 @@ function createCheckpointLocked(options) {
|
|
|
168
305
|
id,
|
|
169
306
|
qid: `${ws}:${id}`,
|
|
170
307
|
path: path_1.default.relative(options.root, filePath),
|
|
308
|
+
kind,
|
|
171
309
|
};
|
|
172
310
|
}
|
|
173
311
|
function createCheckpoint(options) {
|
package/dist/commands/db.js
CHANGED
|
@@ -9,6 +9,7 @@ exports.runDbMigrateCommand = runDbMigrateCommand;
|
|
|
9
9
|
exports.runDbVerifyCommand = runDbVerifyCommand;
|
|
10
10
|
exports.runDbStatsCommand = runDbStatsCommand;
|
|
11
11
|
exports.runDbQueueCreateCommand = runDbQueueCreateCommand;
|
|
12
|
+
exports.runDbQueueContractCommand = runDbQueueContractCommand;
|
|
12
13
|
exports.runDbQueuePauseCommand = runDbQueuePauseCommand;
|
|
13
14
|
exports.runDbQueueResumeCommand = runDbQueueResumeCommand;
|
|
14
15
|
exports.runDbQueueEnqueueCommand = runDbQueueEnqueueCommand;
|
|
@@ -36,6 +37,7 @@ const project_db_1 = require("../core/project_db");
|
|
|
36
37
|
const project_db_migrations_1 = require("../core/project_db_migrations");
|
|
37
38
|
const project_db_snapshot_1 = require("../core/project_db_snapshot");
|
|
38
39
|
const project_db_queue_1 = require("../core/project_db_queue");
|
|
40
|
+
const project_db_queue_contract_1 = require("../core/project_db_queue_contract");
|
|
39
41
|
const version_1 = require("../core/version");
|
|
40
42
|
const index_1 = require("./index");
|
|
41
43
|
const capabilities_indexer_1 = require("../graph/capabilities_indexer");
|
|
@@ -539,6 +541,12 @@ function runDbQueueCreateCommand(options) {
|
|
|
539
541
|
reason: options.reason,
|
|
540
542
|
}), "db-queue-create");
|
|
541
543
|
}
|
|
544
|
+
function runDbQueueContractCommand(options) {
|
|
545
|
+
writeQueueJsonOrText("db-queue-contract", {
|
|
546
|
+
mdkg_version: (0, version_1.readPackageVersion)(),
|
|
547
|
+
contract: (0, project_db_queue_contract_1.projectDbQueueAdapterContract)(),
|
|
548
|
+
}, options.json);
|
|
549
|
+
}
|
|
542
550
|
function runDbQueuePauseCommand(options) {
|
|
543
551
|
runQueueMutation(options, (databasePath) => ({ queue: (0, project_db_queue_1.pauseProjectQueue)(databasePath, { queue_name: requireQueueName(options), reason: options.reason }) }), "db-queue-pause");
|
|
544
552
|
}
|
package/dist/commands/format.js
CHANGED
|
@@ -19,6 +19,7 @@ const id_1 = require("../util/id");
|
|
|
19
19
|
const refs_1 = require("../util/refs");
|
|
20
20
|
const atomic_1 = require("../util/atomic");
|
|
21
21
|
const lock_1 = require("../util/lock");
|
|
22
|
+
const validate_1 = require("./validate");
|
|
22
23
|
const DEC_ID_RE = /^dec-[0-9]+$/;
|
|
23
24
|
const DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
|
|
24
25
|
const ID_LIST_KEYS = new Set(["refs", "scope"]);
|
|
@@ -46,6 +47,27 @@ function isValidId(value) {
|
|
|
46
47
|
function isCoreListFile(filePath) {
|
|
47
48
|
return path_1.default.basename(filePath) === "core.md" && path_1.default.basename(path_1.default.dirname(filePath)) === "core";
|
|
48
49
|
}
|
|
50
|
+
function normalizeHeading(value) {
|
|
51
|
+
return value.trim().toLowerCase();
|
|
52
|
+
}
|
|
53
|
+
function existingHeadings(body) {
|
|
54
|
+
const headings = new Set();
|
|
55
|
+
for (const line of body.split(/\r?\n/)) {
|
|
56
|
+
const match = /^#+\s+(.*)$/.exec(line);
|
|
57
|
+
if (match) {
|
|
58
|
+
headings.add(normalizeHeading(match[1] ?? ""));
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return headings;
|
|
62
|
+
}
|
|
63
|
+
function appendMissingHeadings(body, headings) {
|
|
64
|
+
if (headings.length === 0) {
|
|
65
|
+
return body;
|
|
66
|
+
}
|
|
67
|
+
const trimmed = body.replace(/\s+$/g, "");
|
|
68
|
+
const prefix = trimmed.length > 0 ? `${trimmed}\n\n` : "";
|
|
69
|
+
return `${prefix}${headings.map((heading) => `# ${heading}\n`).join("\n")}`;
|
|
70
|
+
}
|
|
49
71
|
function normalizeScalar(value) {
|
|
50
72
|
return value.trim();
|
|
51
73
|
}
|
|
@@ -250,7 +272,101 @@ function normalizeFrontmatter(frontmatter, schema, type, workStatusEnum, priorit
|
|
|
250
272
|
}
|
|
251
273
|
return { normalized, errors };
|
|
252
274
|
}
|
|
275
|
+
function runHeadingFormatCommandLocked(options) {
|
|
276
|
+
if (options.dryRun && options.apply) {
|
|
277
|
+
throw new errors_1.ValidationError("format --headings cannot use --dry-run and --apply together");
|
|
278
|
+
}
|
|
279
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
280
|
+
const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(options.root, config);
|
|
281
|
+
const errors = [];
|
|
282
|
+
const changes = [];
|
|
283
|
+
for (const files of Object.values(filesByAlias)) {
|
|
284
|
+
for (const filePath of files) {
|
|
285
|
+
if (isCoreListFile(filePath)) {
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
let content = "";
|
|
289
|
+
try {
|
|
290
|
+
content = fs_1.default.readFileSync(filePath, "utf8");
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
294
|
+
errors.push(`${filePath}: failed to read file: ${message}`);
|
|
295
|
+
continue;
|
|
296
|
+
}
|
|
297
|
+
let parsed;
|
|
298
|
+
try {
|
|
299
|
+
parsed = (0, frontmatter_1.parseFrontmatter)(content, filePath);
|
|
300
|
+
}
|
|
301
|
+
catch (err) {
|
|
302
|
+
const message = err instanceof Error ? err.message : "unknown error";
|
|
303
|
+
errors.push(message);
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
const typeValue = parsed.frontmatter.type;
|
|
307
|
+
if (typeof typeValue !== "string") {
|
|
308
|
+
errors.push(`${filePath}: type is required`);
|
|
309
|
+
continue;
|
|
310
|
+
}
|
|
311
|
+
const type = typeValue.toLowerCase();
|
|
312
|
+
const recommended = validate_1.RECOMMENDED_HEADINGS[type];
|
|
313
|
+
if (!recommended) {
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
if (!node_1.ALLOWED_TYPES.has(type)) {
|
|
317
|
+
errors.push(`${filePath}: type must be one of ${Array.from(node_1.ALLOWED_TYPES).join(", ")}`);
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const present = existingHeadings(parsed.body);
|
|
321
|
+
const addedHeadings = recommended.filter((heading) => !present.has(normalizeHeading(heading)));
|
|
322
|
+
if (addedHeadings.length === 0) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const frontmatterEnd = content.indexOf("---", 3);
|
|
326
|
+
const frontmatterBlock = frontmatterEnd >= 0 ? content.slice(0, frontmatterEnd + 3) : ["---", ...(0, frontmatter_1.formatFrontmatter)(parsed.frontmatter, frontmatter_1.DEFAULT_FRONTMATTER_KEY_ORDER), "---"].join("\n");
|
|
327
|
+
const nextBody = appendMissingHeadings(parsed.body, addedHeadings);
|
|
328
|
+
const nextContent = `${frontmatterBlock}\n${nextBody.endsWith("\n") ? nextBody : `${nextBody}\n`}`;
|
|
329
|
+
changes.push({
|
|
330
|
+
filePath,
|
|
331
|
+
path: path_1.default.relative(options.root, filePath).split(path_1.default.sep).join("/"),
|
|
332
|
+
id: typeof parsed.frontmatter.id === "string" ? parsed.frontmatter.id : undefined,
|
|
333
|
+
type,
|
|
334
|
+
added_headings: addedHeadings,
|
|
335
|
+
content: nextContent,
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
if (errors.length > 0) {
|
|
340
|
+
for (const error of errors) {
|
|
341
|
+
console.error(error);
|
|
342
|
+
}
|
|
343
|
+
throw new errors_1.ValidationError(`format --headings failed with ${errors.length} error(s)`);
|
|
344
|
+
}
|
|
345
|
+
const apply = options.apply === true;
|
|
346
|
+
if (apply) {
|
|
347
|
+
for (const change of changes) {
|
|
348
|
+
(0, atomic_1.atomicWriteFile)(change.filePath, change.content);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
const receipt = {
|
|
352
|
+
action: "format.headings",
|
|
353
|
+
ok: true,
|
|
354
|
+
dry_run: !apply,
|
|
355
|
+
applied: apply,
|
|
356
|
+
changed_count: changes.length,
|
|
357
|
+
changes: changes.map(({ filePath: _filePath, content: _content, ...change }) => change),
|
|
358
|
+
};
|
|
359
|
+
if (options.json) {
|
|
360
|
+
console.log(JSON.stringify(receipt, null, 2));
|
|
361
|
+
return;
|
|
362
|
+
}
|
|
363
|
+
console.log(`${apply ? "format headings updated" : "format headings dry-run"} ${changes.length} file(s)`);
|
|
364
|
+
}
|
|
253
365
|
function runFormatCommandLocked(options) {
|
|
366
|
+
if (options.headings) {
|
|
367
|
+
runHeadingFormatCommandLocked(options);
|
|
368
|
+
return;
|
|
369
|
+
}
|
|
254
370
|
const config = (0, config_1.loadConfig)(options.root);
|
|
255
371
|
const templateSchemas = (0, template_schema_1.loadTemplateSchemas)(options.root, config, node_1.ALLOWED_TYPES);
|
|
256
372
|
const filesByAlias = (0, workspace_files_1.listWorkspaceDocFilesByAlias)(options.root, config);
|
package/dist/commands/goal.js
CHANGED
|
@@ -227,6 +227,7 @@ function goalReceipt(root, loaded) {
|
|
|
227
227
|
goal_condition: String(fm.goal_condition ?? ""),
|
|
228
228
|
scope_refs: toStringList(fm.scope_refs),
|
|
229
229
|
active_node: optionalString(fm.active_node),
|
|
230
|
+
last_active_node: optionalString(fm.last_active_node),
|
|
230
231
|
required_skills: toStringList(fm.required_skills),
|
|
231
232
|
required_checks: toStringList(fm.required_checks),
|
|
232
233
|
max_iterations: optionalString(fm.max_iterations),
|
|
@@ -276,6 +277,11 @@ function isConcreteCandidate(node, statusRanks) {
|
|
|
276
277
|
}
|
|
277
278
|
return node.status !== "done";
|
|
278
279
|
}
|
|
280
|
+
function isAchievedGoal(node, frontmatter) {
|
|
281
|
+
const status = frontmatter ? String(frontmatter.status ?? "") : node.status;
|
|
282
|
+
const goalState = frontmatter ? String(frontmatter.goal_state ?? "") : String(node.attributes.goal_state ?? "");
|
|
283
|
+
return status === "done" || goalState === "achieved";
|
|
284
|
+
}
|
|
279
285
|
function resolveCandidate(index, idOrQid, ws) {
|
|
280
286
|
const resolved = (0, qid_1.resolveQid)(index, idOrQid, ws);
|
|
281
287
|
if (resolved.status !== "ok") {
|
|
@@ -283,6 +289,29 @@ function resolveCandidate(index, idOrQid, ws) {
|
|
|
283
289
|
}
|
|
284
290
|
return index.nodes[resolved.qid];
|
|
285
291
|
}
|
|
292
|
+
function readOnlySubgraphBlockerWarnings(index, qids) {
|
|
293
|
+
const warnings = [];
|
|
294
|
+
const seen = new Set();
|
|
295
|
+
for (const qid of [...qids].sort()) {
|
|
296
|
+
const node = index.nodes[qid];
|
|
297
|
+
if (!node || node.source?.imported || node.status === "done") {
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
300
|
+
for (const blockerQid of node.edges.blocked_by) {
|
|
301
|
+
const blocker = index.nodes[blockerQid];
|
|
302
|
+
if (!blocker?.source?.imported) {
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
const subgraph = blocker.source.subgraph_alias;
|
|
306
|
+
const warning = `${node.qid} is blocked by read-only subgraph node ${blocker.qid}; update the source workspace for subgraph ${subgraph} or refresh the subgraph bundle before claiming local work`;
|
|
307
|
+
if (!seen.has(warning)) {
|
|
308
|
+
seen.add(warning);
|
|
309
|
+
warnings.push(warning);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return warnings;
|
|
314
|
+
}
|
|
286
315
|
function runGoalShowCommand(options) {
|
|
287
316
|
const loaded = loadGoal(options.root, options.id, options.ws);
|
|
288
317
|
const receipt = { action: "showed", goal: goalReceipt(options.root, loaded) };
|
|
@@ -296,6 +325,9 @@ function runGoalShowCommand(options) {
|
|
|
296
325
|
if (loaded.frontmatter.active_node) {
|
|
297
326
|
console.log(`active_node: ${loaded.frontmatter.active_node}`);
|
|
298
327
|
}
|
|
328
|
+
if (loaded.frontmatter.last_active_node) {
|
|
329
|
+
console.log(`last_active_node: ${loaded.frontmatter.last_active_node}`);
|
|
330
|
+
}
|
|
299
331
|
const checks = toStringList(loaded.frontmatter.required_checks);
|
|
300
332
|
console.log(`required_checks: ${checks.length === 0 ? "none" : checks.join(", ")}`);
|
|
301
333
|
}
|
|
@@ -351,6 +383,23 @@ function runGoalNextCommand(options) {
|
|
|
351
383
|
console.error("no actionable local work found for goal");
|
|
352
384
|
return;
|
|
353
385
|
}
|
|
386
|
+
if (isAchievedGoal(loaded.node, loaded.frontmatter)) {
|
|
387
|
+
if (options.json) {
|
|
388
|
+
console.log(JSON.stringify({
|
|
389
|
+
action: "selected",
|
|
390
|
+
goal: goalReceipt(options.root, loaded),
|
|
391
|
+
goal_source: loaded.resolutionSource,
|
|
392
|
+
node: null,
|
|
393
|
+
warnings: loaded.warnings,
|
|
394
|
+
}, null, 2));
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
for (const warning of loaded.warnings) {
|
|
398
|
+
console.error(`warning: ${warning}`);
|
|
399
|
+
}
|
|
400
|
+
console.error("no actionable local work found for achieved goal");
|
|
401
|
+
return;
|
|
402
|
+
}
|
|
354
403
|
const statusPreference = loaded.config.work.next.status_preference.map((status) => status.toLowerCase());
|
|
355
404
|
const statusRanks = new Set(statusPreference);
|
|
356
405
|
const warnings = [...loaded.warnings];
|
|
@@ -362,6 +411,7 @@ function runGoalNextCommand(options) {
|
|
|
362
411
|
for (const invalid of scope.invalidRefs) {
|
|
363
412
|
warnings.push(`scope contains non-actionable or unsupported node: ${invalid}`);
|
|
364
413
|
}
|
|
414
|
+
warnings.push(...readOnlySubgraphBlockerWarnings(loaded.index, scope.actionableQids));
|
|
365
415
|
if (activeNode) {
|
|
366
416
|
const node = resolveCandidate(loaded.index, activeNode, loaded.node.ws);
|
|
367
417
|
if (node && scope.actionableQids.has(node.qid) && isConcreteCandidate(node, statusRanks)) {
|
|
@@ -549,6 +599,7 @@ function runGoalCurrentCommand(options) {
|
|
|
549
599
|
goal_condition: String(node.attributes.goal_condition ?? ""),
|
|
550
600
|
scope_refs: toStringList(node.attributes.scope_refs),
|
|
551
601
|
active_node: optionalString(node.attributes.active_node),
|
|
602
|
+
last_active_node: optionalString(node.attributes.last_active_node),
|
|
552
603
|
required_skills: toStringList(node.attributes.required_skills),
|
|
553
604
|
required_checks: toStringList(node.attributes.required_checks),
|
|
554
605
|
max_iterations: optionalString(node.attributes.max_iterations),
|
|
@@ -643,6 +694,15 @@ function runGoalStateMutationLocked(action, options) {
|
|
|
643
694
|
throw new errors_1.UsageError(`cannot ${action} archived goal ${loaded.node.qid}`);
|
|
644
695
|
}
|
|
645
696
|
const now = options.now ?? new Date();
|
|
697
|
+
if (action === "done" || action === "archive") {
|
|
698
|
+
const activeNode = optionalString(loaded.frontmatter.active_node);
|
|
699
|
+
if (activeNode) {
|
|
700
|
+
if (!loaded.frontmatter.last_active_node) {
|
|
701
|
+
loaded.frontmatter.last_active_node = activeNode;
|
|
702
|
+
}
|
|
703
|
+
delete loaded.frontmatter.active_node;
|
|
704
|
+
}
|
|
705
|
+
}
|
|
646
706
|
loaded.frontmatter.goal_state = GOAL_STATE_BY_ACTION[action];
|
|
647
707
|
loaded.frontmatter.status = ensureStatusAllowed(loaded.config, STATUS_BY_ACTION[action]);
|
|
648
708
|
writeGoalFile(loaded, now);
|
package/dist/commands/graph.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.runGraphCloneCommand = runGraphCloneCommand;
|
|
7
7
|
exports.runGraphForkCommand = runGraphForkCommand;
|
|
8
8
|
exports.runGraphImportTemplateCommand = runGraphImportTemplateCommand;
|
|
9
|
+
exports.runGraphRefsCommand = runGraphRefsCommand;
|
|
9
10
|
const fs_1 = __importDefault(require("fs"));
|
|
10
11
|
const path_1 = __importDefault(require("path"));
|
|
11
12
|
const bundle_1 = require("./bundle");
|
|
@@ -14,6 +15,7 @@ const validate_1 = require("./validate");
|
|
|
14
15
|
const config_1 = require("../core/config");
|
|
15
16
|
const workspace_path_1 = require("../core/workspace_path");
|
|
16
17
|
const indexer_1 = require("../graph/indexer");
|
|
18
|
+
const index_cache_1 = require("../graph/index_cache");
|
|
17
19
|
const frontmatter_1 = require("../graph/frontmatter");
|
|
18
20
|
const errors_1 = require("../util/errors");
|
|
19
21
|
const qid_1 = require("../util/qid");
|
|
@@ -21,9 +23,116 @@ const atomic_1 = require("../util/atomic");
|
|
|
21
23
|
const zip_1 = require("../util/zip");
|
|
22
24
|
const lock_1 = require("../util/lock");
|
|
23
25
|
const date_1 = require("../util/date");
|
|
26
|
+
const refs_1 = require("../util/refs");
|
|
24
27
|
function writeJson(value) {
|
|
25
28
|
console.log(JSON.stringify(value, null, 2));
|
|
26
29
|
}
|
|
30
|
+
function toStringList(value) {
|
|
31
|
+
if (!Array.isArray(value)) {
|
|
32
|
+
return [];
|
|
33
|
+
}
|
|
34
|
+
return value.filter((item) => typeof item === "string");
|
|
35
|
+
}
|
|
36
|
+
function summarizeNode(node) {
|
|
37
|
+
return {
|
|
38
|
+
qid: node.qid,
|
|
39
|
+
id: node.id,
|
|
40
|
+
workspace: node.ws,
|
|
41
|
+
type: node.type,
|
|
42
|
+
title: node.title,
|
|
43
|
+
status: node.status,
|
|
44
|
+
path: node.path,
|
|
45
|
+
read_only: Boolean(node.source?.read_only ?? node.source?.imported),
|
|
46
|
+
source: node.source,
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function summarizeRef(index, value, ws) {
|
|
50
|
+
if ((0, refs_1.isUriRef)(value)) {
|
|
51
|
+
return {
|
|
52
|
+
ref: value,
|
|
53
|
+
kind: "uri",
|
|
54
|
+
exists: true,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
const resolved = (0, qid_1.resolveQid)(index, value, ws);
|
|
58
|
+
if (resolved.status !== "ok") {
|
|
59
|
+
return {
|
|
60
|
+
ref: value,
|
|
61
|
+
kind: "missing",
|
|
62
|
+
exists: false,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
const node = index.nodes[resolved.qid];
|
|
66
|
+
if (!node) {
|
|
67
|
+
return {
|
|
68
|
+
ref: value,
|
|
69
|
+
kind: "missing",
|
|
70
|
+
exists: false,
|
|
71
|
+
qid: resolved.qid,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
ref: value,
|
|
76
|
+
kind: "node",
|
|
77
|
+
exists: true,
|
|
78
|
+
qid: node.qid,
|
|
79
|
+
node: summarizeNode(node),
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
function summarizeRefs(index, values, ws) {
|
|
83
|
+
return [...new Set(values)].sort().map((value) => summarizeRef(index, value, ws));
|
|
84
|
+
}
|
|
85
|
+
function compactOutgoing(node) {
|
|
86
|
+
return {
|
|
87
|
+
scope_refs: toStringList(node.attributes.scope_refs),
|
|
88
|
+
epic: node.edges.epic ? [node.edges.epic] : [],
|
|
89
|
+
parent: node.edges.parent ? [node.edges.parent] : [],
|
|
90
|
+
prev: node.edges.prev ? [node.edges.prev] : [],
|
|
91
|
+
next: node.edges.next ? [node.edges.next] : [],
|
|
92
|
+
relates: node.edges.relates,
|
|
93
|
+
blocked_by: node.edges.blocked_by,
|
|
94
|
+
blocks: node.edges.blocks,
|
|
95
|
+
context_refs: node.edges.context_refs ?? [],
|
|
96
|
+
evidence_refs: node.edges.evidence_refs ?? [],
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
function incomingScopeRefs(index, targetQid) {
|
|
100
|
+
const sources = [];
|
|
101
|
+
for (const node of Object.values(index.nodes)) {
|
|
102
|
+
for (const ref of toStringList(node.attributes.scope_refs)) {
|
|
103
|
+
const resolved = (0, qid_1.resolveQid)(index, ref, node.ws);
|
|
104
|
+
if (resolved.status === "ok" && resolved.qid === targetQid) {
|
|
105
|
+
sources.push(node.qid);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return sources.sort();
|
|
110
|
+
}
|
|
111
|
+
function buildGraphRefsReceipt(index, node, warnings) {
|
|
112
|
+
const outgoingRaw = compactOutgoing(node);
|
|
113
|
+
const incomingRaw = {
|
|
114
|
+
scope_refs: incomingScopeRefs(index, node.qid),
|
|
115
|
+
epic: index.reverse_edges.epic?.[node.qid] ?? [],
|
|
116
|
+
parent: index.reverse_edges.parent?.[node.qid] ?? [],
|
|
117
|
+
prev: index.reverse_edges.prev?.[node.qid] ?? [],
|
|
118
|
+
next: index.reverse_edges.next?.[node.qid] ?? [],
|
|
119
|
+
relates: index.reverse_edges.relates?.[node.qid] ?? [],
|
|
120
|
+
blocked_by: index.reverse_edges.blocked_by?.[node.qid] ?? [],
|
|
121
|
+
blocks: index.reverse_edges.blocks?.[node.qid] ?? [],
|
|
122
|
+
context_refs: index.reverse_edges.context_refs?.[node.qid] ?? [],
|
|
123
|
+
evidence_refs: index.reverse_edges.evidence_refs?.[node.qid] ?? [],
|
|
124
|
+
};
|
|
125
|
+
const outgoing = Object.fromEntries(Object.entries(outgoingRaw).map(([key, values]) => [key, summarizeRefs(index, values, node.ws)]));
|
|
126
|
+
const incoming = Object.fromEntries(Object.entries(incomingRaw).map(([key, values]) => [key, summarizeRefs(index, values, node.ws)]));
|
|
127
|
+
return {
|
|
128
|
+
action: "graph.refs",
|
|
129
|
+
ok: true,
|
|
130
|
+
target: summarizeNode(node),
|
|
131
|
+
outgoing,
|
|
132
|
+
incoming,
|
|
133
|
+
warnings,
|
|
134
|
+
};
|
|
135
|
+
}
|
|
27
136
|
function toPosixPath(value) {
|
|
28
137
|
return value.split(path_1.default.sep).join("/");
|
|
29
138
|
}
|
|
@@ -702,3 +811,42 @@ function runGraphImportTemplateCommand(options) {
|
|
|
702
811
|
console.log(`selected_goal: ${receipt.selected_goal.qid}${receipt.selected_goal.planned ? " (planned)" : ""}`);
|
|
703
812
|
}
|
|
704
813
|
}
|
|
814
|
+
function runGraphRefsCommand(options) {
|
|
815
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
816
|
+
const { index, warnings } = (0, index_cache_1.loadIndex)({ root: options.root, config });
|
|
817
|
+
const resolved = (0, qid_1.resolveQid)(index, options.id, options.ws);
|
|
818
|
+
if (resolved.status !== "ok") {
|
|
819
|
+
throw new errors_1.NotFoundError((0, qid_1.formatResolveError)("node", options.id, resolved, options.ws));
|
|
820
|
+
}
|
|
821
|
+
const node = index.nodes[resolved.qid];
|
|
822
|
+
if (!node) {
|
|
823
|
+
throw new errors_1.NotFoundError(`node not found: ${options.id}`);
|
|
824
|
+
}
|
|
825
|
+
const receipt = buildGraphRefsReceipt(index, node, warnings);
|
|
826
|
+
if (options.json) {
|
|
827
|
+
writeJson(receipt);
|
|
828
|
+
return;
|
|
829
|
+
}
|
|
830
|
+
console.log(`graph refs: ${node.qid}`);
|
|
831
|
+
if (node.source?.imported) {
|
|
832
|
+
console.log(`source: subgraph:${node.source.subgraph_alias} read-only`);
|
|
833
|
+
}
|
|
834
|
+
for (const [direction, lanes] of Object.entries({ outgoing: receipt.outgoing, incoming: receipt.incoming })) {
|
|
835
|
+
console.log(`${direction}:`);
|
|
836
|
+
for (const [lane, refs] of Object.entries(lanes)) {
|
|
837
|
+
if (refs.length === 0) {
|
|
838
|
+
continue;
|
|
839
|
+
}
|
|
840
|
+
console.log(` ${lane}:`);
|
|
841
|
+
for (const ref of refs) {
|
|
842
|
+
const target = ref.node
|
|
843
|
+
? `${ref.node.qid}${ref.node.read_only ? " (read-only)" : ""}`
|
|
844
|
+
: ref.ref;
|
|
845
|
+
console.log(` - ${target}`);
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
}
|
|
849
|
+
for (const warning of warnings) {
|
|
850
|
+
console.error(`warning: ${warning}`);
|
|
851
|
+
}
|
|
852
|
+
}
|