mdkg 0.3.6 → 0.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +93 -0
- package/CLI_COMMAND_MATRIX.md +132 -24
- package/README.md +80 -25
- package/dist/cli.js +193 -36
- package/dist/command-contract.json +882 -122
- package/dist/commands/bundle.js +2 -0
- package/dist/commands/capability.js +1 -0
- package/dist/commands/checkpoint.js +139 -1
- package/dist/commands/db.js +8 -0
- package/dist/commands/doctor.js +7 -7
- package/dist/commands/format.js +155 -0
- package/dist/commands/goal.js +60 -0
- package/dist/commands/graph.js +148 -0
- package/dist/commands/handoff.js +301 -0
- package/dist/commands/mcp.js +9 -0
- package/dist/commands/new.js +12 -3
- package/dist/commands/pack.js +3 -1
- package/dist/commands/query_output.js +2 -0
- package/dist/commands/show.js +8 -0
- package/dist/commands/spec.js +76 -17
- package/dist/commands/status.js +1 -0
- package/dist/commands/subgraph.js +7 -4
- package/dist/commands/task.js +2 -0
- package/dist/commands/upgrade.js +128 -3
- package/dist/commands/validate.js +268 -6
- package/dist/commands/work.js +176 -5
- package/dist/core/project_db_queue_contract.js +101 -0
- package/dist/graph/agent_file_types.js +59 -20
- package/dist/graph/capabilities_indexer.js +45 -3
- package/dist/graph/edges.js +15 -0
- package/dist/graph/frontmatter.js +4 -1
- package/dist/graph/indexer.js +13 -0
- package/dist/graph/node.js +12 -1
- package/dist/graph/sqlite_index.js +2 -0
- package/dist/graph/subgraphs.js +2 -0
- package/dist/graph/template_schema.js +37 -17
- package/dist/graph/validate_graph.js +16 -5
- package/dist/graph/visibility.js +6 -0
- package/dist/init/AGENT_START.md +9 -6
- package/dist/init/CLI_COMMAND_MATRIX.md +50 -16
- package/dist/init/README.md +26 -9
- package/dist/init/init-manifest.json +67 -12
- package/dist/init/templates/default/bug.md +2 -0
- package/dist/init/templates/default/chk.md +3 -0
- package/dist/init/templates/default/epic.md +2 -0
- package/dist/init/templates/default/feat.md +2 -0
- package/dist/init/templates/default/goal.md +3 -0
- package/dist/init/templates/default/manifest.md +45 -0
- package/dist/init/templates/default/spike.md +2 -0
- package/dist/init/templates/default/task.md +2 -0
- package/dist/init/templates/default/test.md +2 -0
- package/dist/init/templates/specs/agent.MANIFEST.md +80 -0
- package/dist/init/templates/specs/api.MANIFEST.md +33 -0
- package/dist/init/templates/specs/base.MANIFEST.md +120 -0
- package/dist/init/templates/specs/capability.MANIFEST.md +45 -0
- package/dist/init/templates/specs/integration.MANIFEST.md +25 -0
- package/dist/init/templates/specs/model.MANIFEST.md +21 -0
- package/dist/init/templates/specs/project.MANIFEST.md +39 -0
- package/dist/init/templates/specs/runtime-agent.MANIFEST.md +49 -0
- package/dist/init/templates/specs/runtime-image.MANIFEST.md +21 -0
- package/dist/init/templates/specs/tool.MANIFEST.md +25 -0
- package/dist/pack/export_json.js +20 -8
- package/dist/pack/export_md.js +15 -4
- package/dist/pack/export_xml.js +9 -4
- package/dist/pack/metrics.js +12 -4
- package/dist/pack/pack.js +9 -1
- package/dist/util/argparse.js +3 -0
- package/package.json +21 -3
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
|
+
}
|
|
@@ -0,0 +1,301 @@
|
|
|
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.runHandoffCreateCommand = runHandoffCreateCommand;
|
|
7
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
8
|
+
const path_1 = __importDefault(require("path"));
|
|
9
|
+
const config_1 = require("../core/config");
|
|
10
|
+
const index_cache_1 = require("../graph/index_cache");
|
|
11
|
+
const pack_1 = require("../pack/pack");
|
|
12
|
+
const metrics_1 = require("../pack/metrics");
|
|
13
|
+
const atomic_1 = require("../util/atomic");
|
|
14
|
+
const errors_1 = require("../util/errors");
|
|
15
|
+
const qid_1 = require("../util/qid");
|
|
16
|
+
const version_1 = require("../core/version");
|
|
17
|
+
const RAW_CONTENT_MARKERS = [
|
|
18
|
+
{ id: "raw_prompt", pattern: /\bRAW_PROMPT_MARKER\b/i, description: "raw prompt marker" },
|
|
19
|
+
{ id: "raw_payload", pattern: /\bRAW_PAYLOAD_MARKER\b/i, description: "raw payload marker" },
|
|
20
|
+
{ id: "raw_secret", pattern: /\bRAW_SECRET_MARKER\b|BEGIN [A-Z ]*PRIVATE KEY|secret\s*=/i, description: "raw secret marker" },
|
|
21
|
+
];
|
|
22
|
+
function normalizeWorkspace(value) {
|
|
23
|
+
if (!value || value === "all") {
|
|
24
|
+
return undefined;
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
function outputPath(root, out) {
|
|
29
|
+
if (!out) {
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
const resolved = path_1.default.resolve(root, out);
|
|
33
|
+
const rootWithSep = root.endsWith(path_1.default.sep) ? root : `${root}${path_1.default.sep}`;
|
|
34
|
+
if (resolved !== root && !resolved.startsWith(rootWithSep)) {
|
|
35
|
+
throw new errors_1.UsageError("--out must stay within the repo root");
|
|
36
|
+
}
|
|
37
|
+
return resolved;
|
|
38
|
+
}
|
|
39
|
+
function sha256(value) {
|
|
40
|
+
return `sha256:${crypto_1.default.createHash("sha256").update(value).digest("hex")}`;
|
|
41
|
+
}
|
|
42
|
+
function listAttribute(node, key) {
|
|
43
|
+
const value = "attributes" in node ? node.attributes[key] : undefined;
|
|
44
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
45
|
+
}
|
|
46
|
+
function stringAttribute(node, key) {
|
|
47
|
+
const value = "attributes" in node ? node.attributes[key] : undefined;
|
|
48
|
+
return typeof value === "string" ? value : undefined;
|
|
49
|
+
}
|
|
50
|
+
function collectRawMarkerWarnings(pack) {
|
|
51
|
+
const warnings = [];
|
|
52
|
+
for (const node of pack.nodes) {
|
|
53
|
+
for (const marker of RAW_CONTENT_MARKERS) {
|
|
54
|
+
if (marker.pattern.test(node.body)) {
|
|
55
|
+
warnings.push({
|
|
56
|
+
qid: node.qid,
|
|
57
|
+
path: node.path,
|
|
58
|
+
marker_id: marker.id,
|
|
59
|
+
message: `${marker.description} detected; handoff omitted raw body content for this node`,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return warnings;
|
|
65
|
+
}
|
|
66
|
+
function nodeLine(node) {
|
|
67
|
+
const parts = [`${node.qid}`, node.type, node.title];
|
|
68
|
+
const state = [node.status, node.priority !== undefined ? `p${node.priority}` : undefined].filter(Boolean).join("/");
|
|
69
|
+
if (state) {
|
|
70
|
+
parts.push(state);
|
|
71
|
+
}
|
|
72
|
+
parts.push(node.path);
|
|
73
|
+
return `- ${parts.join(" | ")}`;
|
|
74
|
+
}
|
|
75
|
+
function renderList(values, empty = "- none") {
|
|
76
|
+
if (values.length === 0) {
|
|
77
|
+
return [empty];
|
|
78
|
+
}
|
|
79
|
+
return values.map((value) => `- ${value}`);
|
|
80
|
+
}
|
|
81
|
+
function renderNodeRefs(node) {
|
|
82
|
+
const lines = [];
|
|
83
|
+
if (node.refs.length > 0) {
|
|
84
|
+
lines.push(` refs: ${node.refs.join(", ")}`);
|
|
85
|
+
}
|
|
86
|
+
if (node.context_refs.length > 0) {
|
|
87
|
+
lines.push(` context_refs: ${node.context_refs.join(", ")}`);
|
|
88
|
+
}
|
|
89
|
+
if (node.evidence_refs.length > 0) {
|
|
90
|
+
lines.push(` evidence_refs: ${node.evidence_refs.join(", ")}`);
|
|
91
|
+
}
|
|
92
|
+
return lines;
|
|
93
|
+
}
|
|
94
|
+
function renderHandoff(params) {
|
|
95
|
+
const { rootNode, indexNode, pack, rawWarnings } = params;
|
|
96
|
+
const stats = (0, metrics_1.measurePack)(pack);
|
|
97
|
+
const lines = [];
|
|
98
|
+
const requiredChecks = listAttribute(indexNode, "required_checks");
|
|
99
|
+
const requiredSkills = listAttribute(indexNode, "required_skills");
|
|
100
|
+
const goalCondition = stringAttribute(indexNode, "goal_condition");
|
|
101
|
+
const activeNode = stringAttribute(indexNode, "active_node");
|
|
102
|
+
const lastActiveNode = stringAttribute(indexNode, "last_active_node");
|
|
103
|
+
const latestCheckpoint = pack.meta.latest_checkpoint_qid
|
|
104
|
+
? pack.nodes.find((node) => node.qid === pack.meta.latest_checkpoint_qid)
|
|
105
|
+
: undefined;
|
|
106
|
+
lines.push("# mdkg Agent Handoff");
|
|
107
|
+
lines.push("");
|
|
108
|
+
lines.push("Use this handoff as a sanitized graph summary. Inspect source files before mutating durable state.");
|
|
109
|
+
lines.push("");
|
|
110
|
+
lines.push("## Target");
|
|
111
|
+
lines.push(`- qid: ${rootNode.qid}`);
|
|
112
|
+
lines.push(`- type: ${rootNode.type}`);
|
|
113
|
+
lines.push(`- title: ${rootNode.title}`);
|
|
114
|
+
if (rootNode.status) {
|
|
115
|
+
lines.push(`- status: ${rootNode.status}`);
|
|
116
|
+
}
|
|
117
|
+
if (rootNode.priority !== undefined) {
|
|
118
|
+
lines.push(`- priority: ${rootNode.priority}`);
|
|
119
|
+
}
|
|
120
|
+
lines.push(`- path: ${rootNode.path}`);
|
|
121
|
+
if (goalCondition) {
|
|
122
|
+
lines.push(`- goal_condition: ${goalCondition}`);
|
|
123
|
+
}
|
|
124
|
+
if (activeNode) {
|
|
125
|
+
lines.push(`- active_node: ${activeNode}`);
|
|
126
|
+
}
|
|
127
|
+
if (lastActiveNode) {
|
|
128
|
+
lines.push(`- last_active_node: ${lastActiveNode}`);
|
|
129
|
+
}
|
|
130
|
+
lines.push("");
|
|
131
|
+
lines.push("## Boundaries");
|
|
132
|
+
lines.push("- mdkg is durable semantic memory and graph state, not raw execution trace storage.");
|
|
133
|
+
lines.push("- Do not include raw secrets, credentials, model prompts, provider payloads, cookies, tokens, or bulky runtime artifacts in mdkg nodes or handoffs.");
|
|
134
|
+
lines.push("- Use refs, hashes, redacted summaries, archive links, artifact links, and checkpoints for evidence.");
|
|
135
|
+
lines.push("- Treat subgraph nodes as read-only planning context unless you are operating in the owning repo.");
|
|
136
|
+
lines.push("- Run validation before closing work or handing execution to another agent.");
|
|
137
|
+
lines.push("");
|
|
138
|
+
lines.push("## Recommended Next Steps");
|
|
139
|
+
if (rootNode.type === "goal") {
|
|
140
|
+
lines.push(`- Run \`mdkg goal current --json\` and \`mdkg goal next ${rootNode.id} --json\` to confirm routing.`);
|
|
141
|
+
lines.push("- Claim one actionable local node before implementation with `mdkg goal claim <goal-id> <work-id> --json`.");
|
|
142
|
+
}
|
|
143
|
+
else {
|
|
144
|
+
lines.push(`- Run \`mdkg show ${rootNode.id} --json\` and \`mdkg pack ${rootNode.id}\` to refresh local context.`);
|
|
145
|
+
lines.push("- Use `mdkg task start|update|done` for lifecycle updates when the node is task-like.");
|
|
146
|
+
}
|
|
147
|
+
lines.push("- Keep detailed implementation notes in Markdown body sections, not CLI flags.");
|
|
148
|
+
lines.push("");
|
|
149
|
+
lines.push("## Required Checks");
|
|
150
|
+
lines.push(...renderList(requiredChecks));
|
|
151
|
+
lines.push("");
|
|
152
|
+
lines.push("## Required Skills");
|
|
153
|
+
lines.push(...renderList(requiredSkills));
|
|
154
|
+
lines.push("");
|
|
155
|
+
lines.push("## Latest Checkpoint");
|
|
156
|
+
if (latestCheckpoint) {
|
|
157
|
+
lines.push(nodeLine(latestCheckpoint));
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
lines.push("- none");
|
|
161
|
+
}
|
|
162
|
+
lines.push("");
|
|
163
|
+
lines.push("## Included Graph Context");
|
|
164
|
+
for (const node of pack.nodes) {
|
|
165
|
+
lines.push(nodeLine(node));
|
|
166
|
+
lines.push(...renderNodeRefs(node));
|
|
167
|
+
}
|
|
168
|
+
lines.push("");
|
|
169
|
+
lines.push("## Raw Content Warnings");
|
|
170
|
+
if (rawWarnings.length === 0) {
|
|
171
|
+
lines.push("- none");
|
|
172
|
+
}
|
|
173
|
+
else {
|
|
174
|
+
for (const warning of rawWarnings) {
|
|
175
|
+
lines.push(`- ${warning.qid} (${warning.path}): ${warning.marker_id} - ${warning.message}`);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
lines.push("");
|
|
179
|
+
lines.push("## Pack Summary");
|
|
180
|
+
lines.push(`- generated_at: ${pack.meta.generated_at}`);
|
|
181
|
+
lines.push(`- node_count: ${pack.nodes.length}`);
|
|
182
|
+
lines.push(`- tokens_estimate: ${stats.totals.tokens_estimate}`);
|
|
183
|
+
lines.push(`- truncated: max_nodes=${pack.meta.truncated.max_nodes} max_bytes=${pack.meta.truncated.max_bytes}`);
|
|
184
|
+
if (pack.meta.truncated.dropped.length > 0) {
|
|
185
|
+
lines.push(`- dropped: ${pack.meta.truncated.dropped.join(", ")}`);
|
|
186
|
+
}
|
|
187
|
+
lines.push("");
|
|
188
|
+
lines.push("## Handoff Prompt");
|
|
189
|
+
lines.push("Continue from the target above. Preserve the boundaries, verify current repo state, use mdkg commands for structured lifecycle changes, and record validation evidence before closeout.");
|
|
190
|
+
lines.push("");
|
|
191
|
+
return lines.join("\n");
|
|
192
|
+
}
|
|
193
|
+
function runHandoffCreateCommand(options) {
|
|
194
|
+
if (!options.id) {
|
|
195
|
+
throw new errors_1.UsageError("mdkg handoff create requires <id-or-qid>");
|
|
196
|
+
}
|
|
197
|
+
const config = (0, config_1.loadConfig)(options.root);
|
|
198
|
+
const ws = normalizeWorkspace(options.ws);
|
|
199
|
+
if (ws && !config.workspaces[ws] && !config.subgraphs[ws]) {
|
|
200
|
+
throw new errors_1.NotFoundError(`workspace not found: ${ws}`);
|
|
201
|
+
}
|
|
202
|
+
const { index, stale, rebuilt, warnings } = (0, index_cache_1.loadIndex)({
|
|
203
|
+
root: options.root,
|
|
204
|
+
config,
|
|
205
|
+
useCache: true,
|
|
206
|
+
allowReindex: true,
|
|
207
|
+
});
|
|
208
|
+
if (stale && !rebuilt) {
|
|
209
|
+
console.error("warning: index is stale; run mdkg index to refresh");
|
|
210
|
+
}
|
|
211
|
+
for (const warning of warnings) {
|
|
212
|
+
console.error(`warning: ${warning}`);
|
|
213
|
+
}
|
|
214
|
+
const resolved = (0, qid_1.resolveQid)(index, options.id, ws);
|
|
215
|
+
if (resolved.status !== "ok") {
|
|
216
|
+
throw new errors_1.NotFoundError((0, qid_1.formatResolveError)("id", options.id, resolved, ws));
|
|
217
|
+
}
|
|
218
|
+
const indexNode = index.nodes[resolved.qid];
|
|
219
|
+
if (!indexNode) {
|
|
220
|
+
throw new errors_1.NotFoundError(`node not found: ${resolved.qid}`);
|
|
221
|
+
}
|
|
222
|
+
const depth = options.depth ?? config.pack.default_depth;
|
|
223
|
+
if (!Number.isInteger(depth) || depth < 0) {
|
|
224
|
+
throw new errors_1.UsageError("--depth must be a non-negative integer");
|
|
225
|
+
}
|
|
226
|
+
const buildResult = (0, pack_1.buildPack)({
|
|
227
|
+
root: options.root,
|
|
228
|
+
index,
|
|
229
|
+
rootQid: resolved.qid,
|
|
230
|
+
depth,
|
|
231
|
+
edges: ["parent", "epic", "relates", "blocked_by", "blocks", "context_refs", "evidence_refs"],
|
|
232
|
+
verbose: false,
|
|
233
|
+
maxNodes: config.pack.limits.max_nodes,
|
|
234
|
+
verboseCoreListPath: path_1.default.resolve(options.root, config.pack.verbose_core_list_path),
|
|
235
|
+
wsHint: ws,
|
|
236
|
+
includeLatestCheckpoint: true,
|
|
237
|
+
});
|
|
238
|
+
for (const warning of buildResult.warnings) {
|
|
239
|
+
console.error(`warning: ${warning}`);
|
|
240
|
+
}
|
|
241
|
+
const rootNode = buildResult.pack.nodes[0];
|
|
242
|
+
if (!rootNode) {
|
|
243
|
+
throw new errors_1.NotFoundError(`node not found: ${resolved.qid}`);
|
|
244
|
+
}
|
|
245
|
+
const rawWarnings = collectRawMarkerWarnings(buildResult.pack);
|
|
246
|
+
const content = renderHandoff({
|
|
247
|
+
rootNode,
|
|
248
|
+
indexNode,
|
|
249
|
+
pack: buildResult.pack,
|
|
250
|
+
rawWarnings,
|
|
251
|
+
});
|
|
252
|
+
const contentHash = sha256(content);
|
|
253
|
+
const outPath = outputPath(options.root, options.out);
|
|
254
|
+
if (outPath) {
|
|
255
|
+
(0, atomic_1.atomicWriteFile)(outPath, content);
|
|
256
|
+
}
|
|
257
|
+
const receipt = {
|
|
258
|
+
action: "handoff-created",
|
|
259
|
+
ok: true,
|
|
260
|
+
mdkg_version: (0, version_1.readPackageVersion)(),
|
|
261
|
+
target: {
|
|
262
|
+
qid: rootNode.qid,
|
|
263
|
+
id: rootNode.id,
|
|
264
|
+
type: rootNode.type,
|
|
265
|
+
title: rootNode.title,
|
|
266
|
+
path: rootNode.path,
|
|
267
|
+
status: rootNode.status,
|
|
268
|
+
},
|
|
269
|
+
output_path: outPath ? path_1.default.relative(options.root, outPath).split(path_1.default.sep).join("/") : null,
|
|
270
|
+
content_sha256: contentHash,
|
|
271
|
+
warning_count: warnings.length + buildResult.warnings.length,
|
|
272
|
+
warnings: [...warnings, ...buildResult.warnings],
|
|
273
|
+
index_warning_count: warnings.length,
|
|
274
|
+
index_warnings: warnings,
|
|
275
|
+
pack_warning_count: buildResult.warnings.length,
|
|
276
|
+
pack_warnings: buildResult.warnings,
|
|
277
|
+
raw_marker_warning_count: rawWarnings.length,
|
|
278
|
+
raw_marker_warnings: rawWarnings,
|
|
279
|
+
latest_checkpoint_qid: buildResult.pack.meta.latest_checkpoint_qid ?? null,
|
|
280
|
+
included_qids: buildResult.pack.nodes.map((node) => node.qid),
|
|
281
|
+
pack: {
|
|
282
|
+
node_count: buildResult.pack.nodes.length,
|
|
283
|
+
generated_at: buildResult.pack.meta.generated_at,
|
|
284
|
+
truncated: buildResult.pack.meta.truncated,
|
|
285
|
+
},
|
|
286
|
+
content,
|
|
287
|
+
};
|
|
288
|
+
if (options.json) {
|
|
289
|
+
console.log(JSON.stringify(receipt, null, 2));
|
|
290
|
+
return;
|
|
291
|
+
}
|
|
292
|
+
if (outPath) {
|
|
293
|
+
console.log(`handoff written: ${outPath}`);
|
|
294
|
+
console.log(`sha256: ${contentHash}`);
|
|
295
|
+
if (rawWarnings.length > 0) {
|
|
296
|
+
console.log(`raw marker warnings: ${rawWarnings.length}`);
|
|
297
|
+
}
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
console.log(content);
|
|
301
|
+
}
|
package/dist/commands/mcp.js
CHANGED
|
@@ -464,6 +464,15 @@ function goalNextTool(root, args) {
|
|
|
464
464
|
warnings,
|
|
465
465
|
};
|
|
466
466
|
}
|
|
467
|
+
if (loaded.node.status === "done" || String(loaded.node.attributes.goal_state ?? "") === "achieved") {
|
|
468
|
+
return {
|
|
469
|
+
command: "mcp.goal_next",
|
|
470
|
+
goal_source: loaded.source,
|
|
471
|
+
goal: (0, query_output_1.toNodeSummaryJson)(loaded.node),
|
|
472
|
+
node: null,
|
|
473
|
+
warnings,
|
|
474
|
+
};
|
|
475
|
+
}
|
|
467
476
|
const statusPreference = config.work.next.status_preference.map((status) => status.toLowerCase());
|
|
468
477
|
const statusRanks = new Set(statusPreference);
|
|
469
478
|
const scope = (0, goal_scope_1.collectGoalScope)(index, loaded.node);
|
package/dist/commands/new.js
CHANGED
|
@@ -27,6 +27,7 @@ const DEC_STATUS = new Set(["proposed", "accepted", "rejected", "superseded"]);
|
|
|
27
27
|
const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
28
28
|
const CORE_TYPES = new Set(["rule"]);
|
|
29
29
|
const DESIGN_TYPES = new Set(["prd", "edd", "dec", "prop"]);
|
|
30
|
+
const LEGACY_NEW_SPEC_WARNING = "warning: mdkg new spec is legacy; use mdkg new manifest. This alias creates MANIFEST.md with type: manifest during the compatibility release.";
|
|
30
31
|
function parseCsvList(raw) {
|
|
31
32
|
if (!raw) {
|
|
32
33
|
return [];
|
|
@@ -162,6 +163,9 @@ function fileNameForType(type, id, slug) {
|
|
|
162
163
|
}
|
|
163
164
|
return `${id}-${slug}.md`;
|
|
164
165
|
}
|
|
166
|
+
function canonicalCreationType(requestedType) {
|
|
167
|
+
return requestedType === "spec" ? "manifest" : requestedType;
|
|
168
|
+
}
|
|
165
169
|
function ensureExists(index, value, ws, label) {
|
|
166
170
|
const resolved = (0, qid_1.resolveQid)(index, value, ws);
|
|
167
171
|
if (resolved.status !== "ok") {
|
|
@@ -177,13 +181,15 @@ function runNewCommandLocked(options) {
|
|
|
177
181
|
if (!title) {
|
|
178
182
|
throw new errors_1.UsageError("title cannot be empty");
|
|
179
183
|
}
|
|
180
|
-
const
|
|
181
|
-
if (
|
|
184
|
+
const requestedType = options.type.toLowerCase();
|
|
185
|
+
if (requestedType === "archive") {
|
|
182
186
|
throw new errors_1.UsageError("use `mdkg archive add <file>` to create archive sidecars");
|
|
183
187
|
}
|
|
184
|
-
if (!node_1.ALLOWED_TYPES.has(
|
|
188
|
+
if (!node_1.ALLOWED_TYPES.has(requestedType)) {
|
|
185
189
|
throw new errors_1.UsageError(`type must be one of ${Array.from(node_1.ALLOWED_TYPES).join(", ")}`);
|
|
186
190
|
}
|
|
191
|
+
const type = canonicalCreationType(requestedType);
|
|
192
|
+
const legacySpecAlias = requestedType === "spec";
|
|
187
193
|
const config = (0, config_1.loadConfig)(options.root);
|
|
188
194
|
const ws = normalizeWorkspace(options.ws);
|
|
189
195
|
if (!config.workspaces[ws]) {
|
|
@@ -389,6 +395,9 @@ function runNewCommandLocked(options) {
|
|
|
389
395
|
...(status ? { status } : {}),
|
|
390
396
|
...(priority !== undefined ? { priority } : {}),
|
|
391
397
|
};
|
|
398
|
+
if (legacySpecAlias) {
|
|
399
|
+
console.error(LEGACY_NEW_SPEC_WARNING);
|
|
400
|
+
}
|
|
392
401
|
if (options.json) {
|
|
393
402
|
console.log(JSON.stringify({
|
|
394
403
|
action: "created",
|
package/dist/commands/pack.js
CHANGED
|
@@ -25,7 +25,7 @@ const headings_1 = require("../templates/headings");
|
|
|
25
25
|
const errors_1 = require("../util/errors");
|
|
26
26
|
const output_1 = require("../util/output");
|
|
27
27
|
const qid_1 = require("../util/qid");
|
|
28
|
-
const EDGE_KEYS = new Set(["parent", "epic", "relates", "blocked_by", "blocks", "prev", "next"]);
|
|
28
|
+
const EDGE_KEYS = new Set(["parent", "epic", "relates", "blocked_by", "blocks", "prev", "next", "context_refs", "evidence_refs"]);
|
|
29
29
|
const FORMAT_KEYS = new Set(["md", "json", "toon", "xml"]);
|
|
30
30
|
const SKILL_SLUG_RE = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
31
31
|
function normalizeWorkspace(value) {
|
|
@@ -183,6 +183,8 @@ function appendSkillsToPack(pack, skillEntries, depth, root) {
|
|
|
183
183
|
links: skill.links,
|
|
184
184
|
artifacts: [],
|
|
185
185
|
refs: [],
|
|
186
|
+
context_refs: [],
|
|
187
|
+
evidence_refs: [],
|
|
186
188
|
aliases: [skill.slug, ...skill.tags],
|
|
187
189
|
attributes: {},
|
|
188
190
|
body: depth === "full" ? loadSkillFullBody(root, skill) : renderSkillMetaBody(skill),
|
|
@@ -35,6 +35,8 @@ function toNodeSummaryJson(node) {
|
|
|
35
35
|
relates: [...node.edges.relates],
|
|
36
36
|
blocked_by: [...node.edges.blocked_by],
|
|
37
37
|
blocks: [...node.edges.blocks],
|
|
38
|
+
context_refs: [...(node.edges.context_refs ?? [])],
|
|
39
|
+
evidence_refs: [...(node.edges.evidence_refs ?? [])],
|
|
38
40
|
},
|
|
39
41
|
...(node.source ? { source: node.source } : {}),
|
|
40
42
|
};
|
package/dist/commands/show.js
CHANGED
|
@@ -103,6 +103,14 @@ function runShowCommand(options) {
|
|
|
103
103
|
if (blocksLine) {
|
|
104
104
|
lines.push(blocksLine);
|
|
105
105
|
}
|
|
106
|
+
const contextRefsLine = maybeLine("context_refs", node.edges.context_refs ?? []);
|
|
107
|
+
if (contextRefsLine) {
|
|
108
|
+
lines.push(contextRefsLine);
|
|
109
|
+
}
|
|
110
|
+
const evidenceRefsLine = maybeLine("evidence_refs", node.edges.evidence_refs ?? []);
|
|
111
|
+
if (evidenceRefsLine) {
|
|
112
|
+
lines.push(evidenceRefsLine);
|
|
113
|
+
}
|
|
106
114
|
if (!options.metaOnly && body.length > 0) {
|
|
107
115
|
lines.push("");
|
|
108
116
|
lines.push(body);
|