mdkg 0.0.2 → 0.0.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/README.md +171 -151
- package/dist/cli.js +920 -422
- package/dist/commands/checkpoint.js +17 -6
- package/dist/commands/doctor.js +156 -0
- package/dist/commands/event.js +46 -0
- package/dist/commands/event_support.js +146 -0
- package/dist/commands/format.js +6 -7
- package/dist/commands/index.js +10 -4
- package/dist/commands/init.js +202 -11
- package/dist/commands/list.js +18 -1
- package/dist/commands/new.js +30 -5
- package/dist/commands/pack.js +332 -10
- package/dist/commands/query_output.js +84 -0
- package/dist/commands/search.js +22 -5
- package/dist/commands/show.js +26 -11
- package/dist/commands/skill.js +359 -0
- package/dist/commands/skill_support.js +121 -0
- package/dist/commands/task.js +270 -0
- package/dist/commands/validate.js +104 -7
- package/dist/graph/edges.js +2 -2
- package/dist/graph/frontmatter.js +1 -0
- package/dist/graph/indexer.js +21 -0
- package/dist/graph/node.js +20 -4
- package/dist/graph/skills_index_cache.js +94 -0
- package/dist/graph/skills_indexer.js +160 -0
- package/dist/init/README.md +43 -0
- package/dist/init/core/rule-1-mdkg-conventions.md +9 -2
- package/dist/init/core/rule-3-cli-contract.md +73 -14
- package/dist/init/core/rule-4-repo-safety-and-ignores.md +9 -3
- package/dist/init/core/rule-6-templates-and-schemas.md +6 -2
- package/dist/init/skills/SKILL.md.example +41 -0
- package/dist/init/templates/default/bug.md +1 -0
- package/dist/init/templates/default/chk.md +1 -0
- package/dist/init/templates/default/epic.md +1 -0
- package/dist/init/templates/default/feat.md +1 -0
- package/dist/init/templates/default/task.md +1 -0
- package/dist/init/templates/default/test.md +1 -0
- package/dist/pack/budget.js +186 -0
- package/dist/pack/export_md.js +17 -1
- package/dist/pack/export_xml.js +15 -0
- package/dist/pack/metrics.js +66 -0
- package/dist/pack/pack.js +35 -0
- package/dist/pack/profile.js +222 -0
- package/dist/pack/stats.js +37 -0
- package/dist/templates/headings.js +34 -0
- package/dist/util/argparse.js +47 -1
- package/dist/util/filter.js +18 -0
- package/dist/util/id.js +23 -0
- package/dist/util/output.js +2 -2
- package/package.json +6 -2
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.applyPackBudgets = applyPackBudgets;
|
|
4
|
+
const metrics_1 = require("./metrics");
|
|
5
|
+
function normalizeLimit(value, label) {
|
|
6
|
+
if (value === undefined) {
|
|
7
|
+
return undefined;
|
|
8
|
+
}
|
|
9
|
+
if (!Number.isInteger(value) || value < 0) {
|
|
10
|
+
throw new Error(`${label} must be a non-negative integer`);
|
|
11
|
+
}
|
|
12
|
+
if (value === 0) {
|
|
13
|
+
return undefined;
|
|
14
|
+
}
|
|
15
|
+
return value;
|
|
16
|
+
}
|
|
17
|
+
function exceeds(limits, totals) {
|
|
18
|
+
if (limits.maxChars !== undefined && totals.chars > limits.maxChars) {
|
|
19
|
+
return true;
|
|
20
|
+
}
|
|
21
|
+
if (limits.maxLines !== undefined && totals.lines > limits.maxLines) {
|
|
22
|
+
return true;
|
|
23
|
+
}
|
|
24
|
+
if (limits.maxTokens !== undefined && totals.tokens_estimate > limits.maxTokens) {
|
|
25
|
+
return true;
|
|
26
|
+
}
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
function exceededKinds(limits, totals) {
|
|
30
|
+
const kinds = [];
|
|
31
|
+
if (limits.maxChars !== undefined && totals.chars > limits.maxChars) {
|
|
32
|
+
kinds.push("chars");
|
|
33
|
+
}
|
|
34
|
+
if (limits.maxLines !== undefined && totals.lines > limits.maxLines) {
|
|
35
|
+
kinds.push("lines");
|
|
36
|
+
}
|
|
37
|
+
if (limits.maxTokens !== undefined && totals.tokens_estimate > limits.maxTokens) {
|
|
38
|
+
kinds.push("tokens");
|
|
39
|
+
}
|
|
40
|
+
return kinds;
|
|
41
|
+
}
|
|
42
|
+
function truncateBodyToFit(pack, limits) {
|
|
43
|
+
if (pack.nodes.length === 0) {
|
|
44
|
+
return pack;
|
|
45
|
+
}
|
|
46
|
+
const root = pack.nodes[0];
|
|
47
|
+
const sourceLines = root.body.split(/\r?\n/);
|
|
48
|
+
if (sourceLines.length === 0) {
|
|
49
|
+
return pack;
|
|
50
|
+
}
|
|
51
|
+
let low = 0;
|
|
52
|
+
let high = sourceLines.length;
|
|
53
|
+
let best = 0;
|
|
54
|
+
while (low <= high) {
|
|
55
|
+
const mid = Math.floor((low + high) / 2);
|
|
56
|
+
const candidateBody = sourceLines.slice(0, mid).join("\n").trimEnd();
|
|
57
|
+
const candidatePack = {
|
|
58
|
+
...pack,
|
|
59
|
+
nodes: [{ ...root, body: candidateBody }],
|
|
60
|
+
};
|
|
61
|
+
const stats = (0, metrics_1.measurePack)(candidatePack);
|
|
62
|
+
if (!exceeds(limits, stats.totals)) {
|
|
63
|
+
best = mid;
|
|
64
|
+
low = mid + 1;
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
high = mid - 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const trimmedBody = sourceLines.slice(0, best).join("\n").trimEnd();
|
|
71
|
+
return {
|
|
72
|
+
...pack,
|
|
73
|
+
nodes: [{ ...root, body: trimmedBody }],
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function applyPackBudgets(pack, limitsInput, profile) {
|
|
77
|
+
const limits = {
|
|
78
|
+
maxChars: normalizeLimit(limitsInput.maxChars, "--max-chars"),
|
|
79
|
+
maxLines: normalizeLimit(limitsInput.maxLines, "--max-lines"),
|
|
80
|
+
maxTokens: normalizeLimit(limitsInput.maxTokens, "--max-tokens"),
|
|
81
|
+
};
|
|
82
|
+
const beforeStats = (0, metrics_1.measurePack)(pack);
|
|
83
|
+
let working = {
|
|
84
|
+
meta: {
|
|
85
|
+
...pack.meta,
|
|
86
|
+
truncated: {
|
|
87
|
+
...pack.meta.truncated,
|
|
88
|
+
max_chars: pack.meta.truncated.max_chars ?? false,
|
|
89
|
+
max_lines: pack.meta.truncated.max_lines ?? false,
|
|
90
|
+
max_tokens: pack.meta.truncated.max_tokens ?? false,
|
|
91
|
+
body_truncated: pack.meta.truncated.body_truncated
|
|
92
|
+
? [...pack.meta.truncated.body_truncated]
|
|
93
|
+
: [],
|
|
94
|
+
},
|
|
95
|
+
},
|
|
96
|
+
nodes: [...pack.nodes],
|
|
97
|
+
};
|
|
98
|
+
const droppedNodes = [];
|
|
99
|
+
const bodyTruncatedNodes = [];
|
|
100
|
+
let stats = (0, metrics_1.measurePack)(working);
|
|
101
|
+
while (working.nodes.length > 1 && exceeds(limits, stats.totals)) {
|
|
102
|
+
const kinds = exceededKinds(limits, stats.totals);
|
|
103
|
+
for (const kind of kinds) {
|
|
104
|
+
if (kind === "chars") {
|
|
105
|
+
working.meta.truncated.max_chars = true;
|
|
106
|
+
}
|
|
107
|
+
else if (kind === "lines") {
|
|
108
|
+
working.meta.truncated.max_lines = true;
|
|
109
|
+
}
|
|
110
|
+
else if (kind === "tokens") {
|
|
111
|
+
working.meta.truncated.max_tokens = true;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
const dropped = working.nodes[working.nodes.length - 1];
|
|
115
|
+
working = {
|
|
116
|
+
...working,
|
|
117
|
+
nodes: working.nodes.slice(0, -1),
|
|
118
|
+
};
|
|
119
|
+
if (dropped) {
|
|
120
|
+
droppedNodes.push(dropped.qid);
|
|
121
|
+
}
|
|
122
|
+
stats = (0, metrics_1.measurePack)(working);
|
|
123
|
+
}
|
|
124
|
+
if (exceeds(limits, stats.totals) && working.nodes.length > 0) {
|
|
125
|
+
const beforeBody = working.nodes[0]?.body ?? "";
|
|
126
|
+
const truncated = truncateBodyToFit(working, limits);
|
|
127
|
+
const afterBody = truncated.nodes[0]?.body ?? "";
|
|
128
|
+
if (afterBody !== beforeBody) {
|
|
129
|
+
bodyTruncatedNodes.push(truncated.nodes[0].qid);
|
|
130
|
+
truncated.meta.truncated.body_truncated = [
|
|
131
|
+
...(truncated.meta.truncated.body_truncated ?? []),
|
|
132
|
+
truncated.nodes[0].qid,
|
|
133
|
+
];
|
|
134
|
+
}
|
|
135
|
+
const kinds = exceededKinds(limits, stats.totals);
|
|
136
|
+
for (const kind of kinds) {
|
|
137
|
+
if (kind === "chars") {
|
|
138
|
+
truncated.meta.truncated.max_chars = true;
|
|
139
|
+
}
|
|
140
|
+
else if (kind === "lines") {
|
|
141
|
+
truncated.meta.truncated.max_lines = true;
|
|
142
|
+
}
|
|
143
|
+
else if (kind === "tokens") {
|
|
144
|
+
truncated.meta.truncated.max_tokens = true;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
working = truncated;
|
|
148
|
+
stats = (0, metrics_1.measurePack)(working);
|
|
149
|
+
}
|
|
150
|
+
if (droppedNodes.length > 0) {
|
|
151
|
+
working.meta.truncated.dropped.push(...droppedNodes);
|
|
152
|
+
}
|
|
153
|
+
if (bodyTruncatedNodes.length > 0) {
|
|
154
|
+
working.meta.truncated.body_truncated = [
|
|
155
|
+
...(working.meta.truncated.body_truncated ?? []),
|
|
156
|
+
...bodyTruncatedNodes,
|
|
157
|
+
];
|
|
158
|
+
}
|
|
159
|
+
working.meta.node_count = working.nodes.length;
|
|
160
|
+
const report = {
|
|
161
|
+
root: pack.meta.root,
|
|
162
|
+
profile: pack.meta.profile ?? profile,
|
|
163
|
+
limits: {
|
|
164
|
+
max_chars: limits.maxChars,
|
|
165
|
+
max_lines: limits.maxLines,
|
|
166
|
+
max_tokens: limits.maxTokens,
|
|
167
|
+
},
|
|
168
|
+
before: {
|
|
169
|
+
node_count: pack.nodes.length,
|
|
170
|
+
chars: beforeStats.totals.chars,
|
|
171
|
+
lines: beforeStats.totals.lines,
|
|
172
|
+
bytes: beforeStats.totals.bytes,
|
|
173
|
+
tokens_estimate: beforeStats.totals.tokens_estimate,
|
|
174
|
+
},
|
|
175
|
+
after: {
|
|
176
|
+
node_count: working.nodes.length,
|
|
177
|
+
chars: stats.totals.chars,
|
|
178
|
+
lines: stats.totals.lines,
|
|
179
|
+
bytes: stats.totals.bytes,
|
|
180
|
+
tokens_estimate: stats.totals.tokens_estimate,
|
|
181
|
+
},
|
|
182
|
+
dropped_nodes: droppedNodes,
|
|
183
|
+
body_truncated_nodes: bodyTruncatedNodes,
|
|
184
|
+
};
|
|
185
|
+
return { pack: working, report };
|
|
186
|
+
}
|
package/dist/pack/export_md.js
CHANGED
|
@@ -13,8 +13,20 @@ function renderHeader(meta, nodes) {
|
|
|
13
13
|
lines.push(`root: ${meta.root}`);
|
|
14
14
|
lines.push(`depth: ${meta.depth}`);
|
|
15
15
|
lines.push(`verbose: ${meta.verbose}`);
|
|
16
|
+
if (meta.profile) {
|
|
17
|
+
lines.push(`profile: ${meta.profile}`);
|
|
18
|
+
}
|
|
19
|
+
if (meta.body_mode) {
|
|
20
|
+
lines.push(`body_mode: ${meta.body_mode}`);
|
|
21
|
+
}
|
|
22
|
+
if (meta.latest_checkpoint_qid) {
|
|
23
|
+
lines.push(`latest_checkpoint_qid: ${meta.latest_checkpoint_qid}`);
|
|
24
|
+
}
|
|
25
|
+
if (meta.latest_checkpoint_qid_hint) {
|
|
26
|
+
lines.push(`latest_checkpoint_qid_hint: ${meta.latest_checkpoint_qid_hint}`);
|
|
27
|
+
}
|
|
16
28
|
lines.push(`nodes: ${nodes.length}`);
|
|
17
|
-
lines.push(`truncated: max_nodes=${meta.truncated.max_nodes} max_bytes=${meta.truncated.max_bytes}`);
|
|
29
|
+
lines.push(`truncated: max_nodes=${meta.truncated.max_nodes} max_bytes=${meta.truncated.max_bytes} max_chars=${Boolean(meta.truncated.max_chars)} max_lines=${Boolean(meta.truncated.max_lines)} max_tokens=${Boolean(meta.truncated.max_tokens)}`);
|
|
18
30
|
if (meta.truncated.dropped.length > 0) {
|
|
19
31
|
lines.push(`dropped: ${meta.truncated.dropped.join(", ")}`);
|
|
20
32
|
}
|
|
@@ -55,6 +67,10 @@ function cloneTruncation(truncation) {
|
|
|
55
67
|
max_nodes: truncation.max_nodes,
|
|
56
68
|
max_bytes: truncation.max_bytes,
|
|
57
69
|
dropped: [...truncation.dropped],
|
|
70
|
+
max_chars: truncation.max_chars,
|
|
71
|
+
max_lines: truncation.max_lines,
|
|
72
|
+
max_tokens: truncation.max_tokens,
|
|
73
|
+
body_truncated: truncation.body_truncated ? [...truncation.body_truncated] : [],
|
|
58
74
|
};
|
|
59
75
|
}
|
|
60
76
|
function buildMeta(meta, nodes) {
|
package/dist/pack/export_xml.js
CHANGED
|
@@ -29,11 +29,26 @@ function exportXml(pack) {
|
|
|
29
29
|
lines.push(` <root>${escapeXml(pack.meta.root)}</root>`);
|
|
30
30
|
lines.push(` <depth>${pack.meta.depth}</depth>`);
|
|
31
31
|
lines.push(` <verbose>${pack.meta.verbose}</verbose>`);
|
|
32
|
+
if (pack.meta.profile) {
|
|
33
|
+
lines.push(` <profile>${escapeXml(pack.meta.profile)}</profile>`);
|
|
34
|
+
}
|
|
35
|
+
if (pack.meta.body_mode) {
|
|
36
|
+
lines.push(` <body_mode>${escapeXml(pack.meta.body_mode)}</body_mode>`);
|
|
37
|
+
}
|
|
38
|
+
if (pack.meta.latest_checkpoint_qid) {
|
|
39
|
+
lines.push(` <latest_checkpoint_qid>${escapeXml(pack.meta.latest_checkpoint_qid)}</latest_checkpoint_qid>`);
|
|
40
|
+
}
|
|
41
|
+
if (pack.meta.latest_checkpoint_qid_hint) {
|
|
42
|
+
lines.push(` <latest_checkpoint_qid_hint>${escapeXml(pack.meta.latest_checkpoint_qid_hint)}</latest_checkpoint_qid_hint>`);
|
|
43
|
+
}
|
|
32
44
|
lines.push(` <generated_at>${escapeXml(pack.meta.generated_at)}</generated_at>`);
|
|
33
45
|
lines.push(` <node_count>${pack.meta.node_count}</node_count>`);
|
|
34
46
|
lines.push(" <truncated>");
|
|
35
47
|
lines.push(` <max_nodes>${pack.meta.truncated.max_nodes}</max_nodes>`);
|
|
36
48
|
lines.push(` <max_bytes>${pack.meta.truncated.max_bytes}</max_bytes>`);
|
|
49
|
+
lines.push(` <max_chars>${Boolean(pack.meta.truncated.max_chars)}</max_chars>`);
|
|
50
|
+
lines.push(` <max_lines>${Boolean(pack.meta.truncated.max_lines)}</max_lines>`);
|
|
51
|
+
lines.push(` <max_tokens>${Boolean(pack.meta.truncated.max_tokens)}</max_tokens>`);
|
|
37
52
|
if (pack.meta.truncated.dropped.length > 0) {
|
|
38
53
|
lines.push(" <dropped>");
|
|
39
54
|
for (const qid of pack.meta.truncated.dropped) {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.countLines = countLines;
|
|
4
|
+
exports.estimateTokensFromChars = estimateTokensFromChars;
|
|
5
|
+
exports.renderNodeMetricsText = renderNodeMetricsText;
|
|
6
|
+
exports.measureNode = measureNode;
|
|
7
|
+
exports.measurePack = measurePack;
|
|
8
|
+
function countLines(text) {
|
|
9
|
+
if (text.length === 0) {
|
|
10
|
+
return 0;
|
|
11
|
+
}
|
|
12
|
+
return text.split(/\r?\n/).length;
|
|
13
|
+
}
|
|
14
|
+
function estimateTokensFromChars(chars) {
|
|
15
|
+
if (chars <= 0) {
|
|
16
|
+
return 0;
|
|
17
|
+
}
|
|
18
|
+
return Math.ceil(chars / 4);
|
|
19
|
+
}
|
|
20
|
+
function renderNodeMetricsText(node) {
|
|
21
|
+
const lines = [];
|
|
22
|
+
lines.push(`qid: ${node.qid}`);
|
|
23
|
+
lines.push(`id: ${node.id}`);
|
|
24
|
+
lines.push(`workspace: ${node.workspace}`);
|
|
25
|
+
lines.push(`type: ${node.type}`);
|
|
26
|
+
lines.push(`title: ${node.title}`);
|
|
27
|
+
if (node.status) {
|
|
28
|
+
lines.push(`status: ${node.status}`);
|
|
29
|
+
}
|
|
30
|
+
if (node.priority !== undefined) {
|
|
31
|
+
lines.push(`priority: ${node.priority}`);
|
|
32
|
+
}
|
|
33
|
+
lines.push(`path: ${node.path}`);
|
|
34
|
+
lines.push(`links: ${node.links.join(",")}`);
|
|
35
|
+
lines.push(`artifacts: ${node.artifacts.join(",")}`);
|
|
36
|
+
lines.push(`refs: ${node.refs.join(",")}`);
|
|
37
|
+
lines.push(`aliases: ${node.aliases.join(",")}`);
|
|
38
|
+
if (node.body.length > 0) {
|
|
39
|
+
lines.push("");
|
|
40
|
+
lines.push(node.body);
|
|
41
|
+
}
|
|
42
|
+
return lines.join("\n");
|
|
43
|
+
}
|
|
44
|
+
function measureNode(node) {
|
|
45
|
+
const rendered = renderNodeMetricsText(node);
|
|
46
|
+
const chars = rendered.length;
|
|
47
|
+
const lines = countLines(rendered);
|
|
48
|
+
const bytes = Buffer.byteLength(rendered, "utf8");
|
|
49
|
+
return {
|
|
50
|
+
qid: node.qid,
|
|
51
|
+
chars,
|
|
52
|
+
lines,
|
|
53
|
+
bytes,
|
|
54
|
+
tokens_estimate: estimateTokensFromChars(chars),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
function measurePack(pack) {
|
|
58
|
+
const nodes = pack.nodes.map((node) => measureNode(node));
|
|
59
|
+
const totals = nodes.reduce((acc, node) => ({
|
|
60
|
+
chars: acc.chars + node.chars,
|
|
61
|
+
lines: acc.lines + node.lines,
|
|
62
|
+
bytes: acc.bytes + node.bytes,
|
|
63
|
+
tokens_estimate: acc.tokens_estimate + node.tokens_estimate,
|
|
64
|
+
}), { chars: 0, lines: 0, bytes: 0, tokens_estimate: 0 });
|
|
65
|
+
return { nodes, totals };
|
|
66
|
+
}
|
package/dist/pack/pack.js
CHANGED
|
@@ -130,6 +130,24 @@ function buildPackNode(root, index, qid) {
|
|
|
130
130
|
function mergeWarnings(warnings, message) {
|
|
131
131
|
warnings.push(message);
|
|
132
132
|
}
|
|
133
|
+
function checkpointWorkspaceFromQid(qid) {
|
|
134
|
+
const [workspace] = qid.split(":");
|
|
135
|
+
return workspace ?? "root";
|
|
136
|
+
}
|
|
137
|
+
function resolveLatestCheckpointQid(index, workspace) {
|
|
138
|
+
const candidates = Object.values(index.nodes)
|
|
139
|
+
.filter((node) => node.ws === workspace && node.type === "checkpoint")
|
|
140
|
+
.sort((a, b) => {
|
|
141
|
+
if (a.updated !== b.updated) {
|
|
142
|
+
return b.updated.localeCompare(a.updated);
|
|
143
|
+
}
|
|
144
|
+
if (a.created !== b.created) {
|
|
145
|
+
return b.created.localeCompare(a.created);
|
|
146
|
+
}
|
|
147
|
+
return b.qid.localeCompare(a.qid);
|
|
148
|
+
});
|
|
149
|
+
return candidates[0]?.qid;
|
|
150
|
+
}
|
|
133
151
|
function applyMaxNodes(orderedQids, maxNodes, truncation) {
|
|
134
152
|
if (maxNodes <= 0 || orderedQids.length <= maxNodes) {
|
|
135
153
|
return { included: orderedQids, dropped: [] };
|
|
@@ -142,7 +160,22 @@ function applyMaxNodes(orderedQids, maxNodes, truncation) {
|
|
|
142
160
|
}
|
|
143
161
|
function buildPack(options) {
|
|
144
162
|
const warnings = [];
|
|
163
|
+
const includeLatestCheckpoint = options.includeLatestCheckpoint ?? true;
|
|
145
164
|
const { qids, depths } = collectNodes(options.index, options.rootQid, options.depth, options.edges);
|
|
165
|
+
const workspace = checkpointWorkspaceFromQid(options.rootQid);
|
|
166
|
+
const latestCheckpointHint = options.index.meta.latest_checkpoint_qid?.[workspace];
|
|
167
|
+
const latestCheckpointResolved = includeLatestCheckpoint
|
|
168
|
+
? resolveLatestCheckpointQid(options.index, workspace)
|
|
169
|
+
: undefined;
|
|
170
|
+
if (latestCheckpointResolved && latestCheckpointResolved !== options.rootQid) {
|
|
171
|
+
qids.add(latestCheckpointResolved);
|
|
172
|
+
}
|
|
173
|
+
if (includeLatestCheckpoint &&
|
|
174
|
+
latestCheckpointHint &&
|
|
175
|
+
latestCheckpointResolved &&
|
|
176
|
+
latestCheckpointHint !== latestCheckpointResolved) {
|
|
177
|
+
mergeWarnings(warnings, `latest_checkpoint_qid hint mismatch for ${workspace}: ${latestCheckpointHint} -> ${latestCheckpointResolved}`);
|
|
178
|
+
}
|
|
146
179
|
if (options.verbose) {
|
|
147
180
|
const coreIds = (0, verbose_core_1.readVerboseCoreList)(options.verboseCoreListPath);
|
|
148
181
|
for (const id of coreIds) {
|
|
@@ -173,6 +206,8 @@ function buildPack(options) {
|
|
|
173
206
|
verbose: options.verbose,
|
|
174
207
|
generated_at: new Date().toISOString(),
|
|
175
208
|
node_count: nodes.length,
|
|
209
|
+
latest_checkpoint_qid: latestCheckpointResolved,
|
|
210
|
+
latest_checkpoint_qid_hint: latestCheckpointHint,
|
|
176
211
|
truncated: truncation,
|
|
177
212
|
},
|
|
178
213
|
nodes,
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.listPackProfiles = listPackProfiles;
|
|
4
|
+
exports.resolvePackProfile = resolvePackProfile;
|
|
5
|
+
exports.shapePackBodies = shapePackBodies;
|
|
6
|
+
const PACK_PROFILE_CATALOG = [
|
|
7
|
+
{
|
|
8
|
+
profile: "standard",
|
|
9
|
+
bodyMode: "full",
|
|
10
|
+
description: "Keep full node bodies (current default behavior).",
|
|
11
|
+
defaults: [],
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
profile: "concise",
|
|
15
|
+
bodyMode: "summary",
|
|
16
|
+
description: "Use summary-first excerpts for each node body.",
|
|
17
|
+
defaults: ["strip_code=true"],
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
profile: "headers",
|
|
21
|
+
bodyMode: "none",
|
|
22
|
+
description: "Frontmatter/metadata only; remove all node body content.",
|
|
23
|
+
defaults: [],
|
|
24
|
+
},
|
|
25
|
+
];
|
|
26
|
+
const CODE_FENCE_RE = /^(```|~~~)/;
|
|
27
|
+
function normalizeHeading(value) {
|
|
28
|
+
return value.trim().toLowerCase();
|
|
29
|
+
}
|
|
30
|
+
function removeCodeBlocks(body) {
|
|
31
|
+
const lines = body.split(/\r?\n/);
|
|
32
|
+
const result = [];
|
|
33
|
+
let inFence = false;
|
|
34
|
+
for (const line of lines) {
|
|
35
|
+
if (CODE_FENCE_RE.test(line.trimStart())) {
|
|
36
|
+
inFence = !inFence;
|
|
37
|
+
if (!inFence) {
|
|
38
|
+
result.push("[code block omitted]");
|
|
39
|
+
}
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
if (!inFence) {
|
|
43
|
+
result.push(line);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return result.join("\n");
|
|
47
|
+
}
|
|
48
|
+
function capCodeBlocks(body, maxCodeLines) {
|
|
49
|
+
const lines = body.split(/\r?\n/);
|
|
50
|
+
const result = [];
|
|
51
|
+
let inFence = false;
|
|
52
|
+
let kept = 0;
|
|
53
|
+
let truncated = false;
|
|
54
|
+
for (const line of lines) {
|
|
55
|
+
if (CODE_FENCE_RE.test(line.trimStart())) {
|
|
56
|
+
if (inFence && truncated) {
|
|
57
|
+
result.push("[code lines truncated]");
|
|
58
|
+
}
|
|
59
|
+
inFence = !inFence;
|
|
60
|
+
kept = 0;
|
|
61
|
+
truncated = false;
|
|
62
|
+
result.push(line);
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
if (!inFence) {
|
|
66
|
+
result.push(line);
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
if (kept < maxCodeLines) {
|
|
70
|
+
result.push(line);
|
|
71
|
+
kept += 1;
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
truncated = true;
|
|
75
|
+
}
|
|
76
|
+
if (inFence && truncated) {
|
|
77
|
+
result.push("[code lines truncated]");
|
|
78
|
+
}
|
|
79
|
+
return result.join("\n");
|
|
80
|
+
}
|
|
81
|
+
function applyCodeTransform(body, stripCode, maxCodeLines) {
|
|
82
|
+
if (body.length === 0) {
|
|
83
|
+
return body;
|
|
84
|
+
}
|
|
85
|
+
if (stripCode) {
|
|
86
|
+
return removeCodeBlocks(body);
|
|
87
|
+
}
|
|
88
|
+
if (maxCodeLines !== undefined) {
|
|
89
|
+
return capCodeBlocks(body, maxCodeLines);
|
|
90
|
+
}
|
|
91
|
+
return body;
|
|
92
|
+
}
|
|
93
|
+
function splitSections(body) {
|
|
94
|
+
const lines = body.split(/\r?\n/);
|
|
95
|
+
const sections = [{ lines: [] }];
|
|
96
|
+
let current = sections[0];
|
|
97
|
+
let inFence = false;
|
|
98
|
+
for (const line of lines) {
|
|
99
|
+
const trimmed = line.trimStart();
|
|
100
|
+
if (CODE_FENCE_RE.test(trimmed)) {
|
|
101
|
+
inFence = !inFence;
|
|
102
|
+
current.lines.push(line);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
if (!inFence) {
|
|
106
|
+
const match = /^#+\s+(.*)$/.exec(line);
|
|
107
|
+
if (match) {
|
|
108
|
+
current = {
|
|
109
|
+
heading: normalizeHeading(match[1] ?? ""),
|
|
110
|
+
headingLine: line,
|
|
111
|
+
lines: [],
|
|
112
|
+
};
|
|
113
|
+
sections.push(current);
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
current.lines.push(line);
|
|
118
|
+
}
|
|
119
|
+
return sections;
|
|
120
|
+
}
|
|
121
|
+
function firstNonEmptyLines(lines, maxLines) {
|
|
122
|
+
const result = [];
|
|
123
|
+
for (const line of lines) {
|
|
124
|
+
if (line.trim().length === 0) {
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
result.push(line);
|
|
128
|
+
if (result.length >= maxLines) {
|
|
129
|
+
break;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
function extractSummary(body, preferredHeadings, summaryMaxLines) {
|
|
135
|
+
if (body.trim().length === 0) {
|
|
136
|
+
return "";
|
|
137
|
+
}
|
|
138
|
+
const sections = splitSections(body);
|
|
139
|
+
for (const heading of preferredHeadings) {
|
|
140
|
+
const section = sections.find((candidate) => candidate.heading === heading);
|
|
141
|
+
if (!section) {
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
const excerpt = firstNonEmptyLines(section.lines, summaryMaxLines);
|
|
145
|
+
if (excerpt.length === 0) {
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const summaryLines = section.headingLine ? [section.headingLine, ...excerpt] : excerpt;
|
|
149
|
+
return summaryLines.join("\n").trimEnd();
|
|
150
|
+
}
|
|
151
|
+
const fallback = firstNonEmptyLines(body.split(/\r?\n/), summaryMaxLines);
|
|
152
|
+
return fallback.join("\n").trimEnd();
|
|
153
|
+
}
|
|
154
|
+
function shapeNodeBody(node, options) {
|
|
155
|
+
const { resolved, templateHeadingMap } = options;
|
|
156
|
+
let shaped = node.body;
|
|
157
|
+
if (resolved.bodyMode === "none") {
|
|
158
|
+
shaped = "";
|
|
159
|
+
}
|
|
160
|
+
else if (resolved.bodyMode === "summary") {
|
|
161
|
+
const preferred = templateHeadingMap[node.type] ?? [];
|
|
162
|
+
shaped = extractSummary(node.body, preferred, resolved.summaryMaxLines);
|
|
163
|
+
}
|
|
164
|
+
shaped = applyCodeTransform(shaped, resolved.stripCode, resolved.maxCodeLines);
|
|
165
|
+
return shaped.trimEnd();
|
|
166
|
+
}
|
|
167
|
+
function listPackProfiles() {
|
|
168
|
+
return PACK_PROFILE_CATALOG.map((entry) => ({
|
|
169
|
+
profile: entry.profile,
|
|
170
|
+
bodyMode: entry.bodyMode,
|
|
171
|
+
description: entry.description,
|
|
172
|
+
defaults: [...entry.defaults],
|
|
173
|
+
}));
|
|
174
|
+
}
|
|
175
|
+
function resolvePackProfile(input) {
|
|
176
|
+
if (input.maxCodeLines !== undefined && (!Number.isInteger(input.maxCodeLines) || input.maxCodeLines < 0)) {
|
|
177
|
+
throw new Error("--max-code-lines must be a non-negative integer");
|
|
178
|
+
}
|
|
179
|
+
let profileRaw = input.profile;
|
|
180
|
+
if (input.concise) {
|
|
181
|
+
if (profileRaw && profileRaw.toLowerCase() !== "concise") {
|
|
182
|
+
throw new Error("--concise conflicts with --pack-profile");
|
|
183
|
+
}
|
|
184
|
+
profileRaw = "concise";
|
|
185
|
+
}
|
|
186
|
+
const normalized = (profileRaw ?? "standard").toLowerCase();
|
|
187
|
+
if (normalized !== "standard" && normalized !== "concise" && normalized !== "headers") {
|
|
188
|
+
throw new Error("--pack-profile must be one of standard, concise, headers");
|
|
189
|
+
}
|
|
190
|
+
const profile = normalized;
|
|
191
|
+
let bodyMode = "full";
|
|
192
|
+
let defaultStripCode = false;
|
|
193
|
+
if (profile === "concise") {
|
|
194
|
+
bodyMode = "summary";
|
|
195
|
+
defaultStripCode = true;
|
|
196
|
+
}
|
|
197
|
+
else if (profile === "headers") {
|
|
198
|
+
bodyMode = "none";
|
|
199
|
+
}
|
|
200
|
+
return {
|
|
201
|
+
profile,
|
|
202
|
+
bodyMode,
|
|
203
|
+
stripCode: input.stripCode || defaultStripCode,
|
|
204
|
+
maxCodeLines: input.maxCodeLines,
|
|
205
|
+
summaryMaxLines: 12,
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
function shapePackBodies(pack, options) {
|
|
209
|
+
const nodes = pack.nodes.map((node) => ({
|
|
210
|
+
...node,
|
|
211
|
+
body: shapeNodeBody(node, options),
|
|
212
|
+
}));
|
|
213
|
+
return {
|
|
214
|
+
meta: {
|
|
215
|
+
...pack.meta,
|
|
216
|
+
node_count: nodes.length,
|
|
217
|
+
profile: options.resolved.profile,
|
|
218
|
+
body_mode: options.resolved.bodyMode,
|
|
219
|
+
},
|
|
220
|
+
nodes,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.renderPackStats = renderPackStats;
|
|
4
|
+
function pad(value, width) {
|
|
5
|
+
if (value.length >= width) {
|
|
6
|
+
return value;
|
|
7
|
+
}
|
|
8
|
+
return `${value}${" ".repeat(width - value.length)}`;
|
|
9
|
+
}
|
|
10
|
+
function padLeft(value, width) {
|
|
11
|
+
if (value.length >= width) {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
return `${" ".repeat(width - value.length)}${value}`;
|
|
15
|
+
}
|
|
16
|
+
function renderPackStats(stats) {
|
|
17
|
+
const rows = stats.nodes.map((node) => ({
|
|
18
|
+
qid: node.qid,
|
|
19
|
+
chars: String(node.chars),
|
|
20
|
+
lines: String(node.lines),
|
|
21
|
+
bytes: String(node.bytes),
|
|
22
|
+
tokens: String(node.tokens_estimate),
|
|
23
|
+
}));
|
|
24
|
+
const qidWidth = Math.max(3, ...rows.map((row) => row.qid.length), 5);
|
|
25
|
+
const charsWidth = Math.max(5, ...rows.map((row) => row.chars.length), String(stats.totals.chars).length);
|
|
26
|
+
const linesWidth = Math.max(5, ...rows.map((row) => row.lines.length), String(stats.totals.lines).length);
|
|
27
|
+
const bytesWidth = Math.max(5, ...rows.map((row) => row.bytes.length), String(stats.totals.bytes).length);
|
|
28
|
+
const tokensWidth = Math.max(6, ...rows.map((row) => row.tokens.length), String(stats.totals.tokens_estimate).length);
|
|
29
|
+
const lines = [];
|
|
30
|
+
lines.push(`${pad("qid", qidWidth)} ${padLeft("chars", charsWidth)} ${padLeft("lines", linesWidth)} ${padLeft("bytes", bytesWidth)} ${padLeft("tokens", tokensWidth)}`);
|
|
31
|
+
lines.push(`${"-".repeat(qidWidth)} ${"-".repeat(charsWidth)} ${"-".repeat(linesWidth)} ${"-".repeat(bytesWidth)} ${"-".repeat(tokensWidth)}`);
|
|
32
|
+
for (const row of rows) {
|
|
33
|
+
lines.push(`${pad(row.qid, qidWidth)} ${padLeft(row.chars, charsWidth)} ${padLeft(row.lines, linesWidth)} ${padLeft(row.bytes, bytesWidth)} ${padLeft(row.tokens, tokensWidth)}`);
|
|
34
|
+
}
|
|
35
|
+
lines.push(`${pad("TOTAL", qidWidth)} ${padLeft(String(stats.totals.chars), charsWidth)} ${padLeft(String(stats.totals.lines), linesWidth)} ${padLeft(String(stats.totals.bytes), bytesWidth)} ${padLeft(String(stats.totals.tokens_estimate), tokensWidth)}`);
|
|
36
|
+
return lines.join("\n");
|
|
37
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.extractMarkdownHeadings = extractMarkdownHeadings;
|
|
4
|
+
exports.loadTemplateHeadingMap = loadTemplateHeadingMap;
|
|
5
|
+
const loader_1 = require("./loader");
|
|
6
|
+
function normalizeHeading(value) {
|
|
7
|
+
return value.trim().toLowerCase();
|
|
8
|
+
}
|
|
9
|
+
function extractMarkdownHeadings(body) {
|
|
10
|
+
const lines = body.split(/\r?\n/);
|
|
11
|
+
const headings = [];
|
|
12
|
+
const seen = new Set();
|
|
13
|
+
for (const line of lines) {
|
|
14
|
+
const match = /^#+\s+(.*)$/.exec(line);
|
|
15
|
+
if (!match) {
|
|
16
|
+
continue;
|
|
17
|
+
}
|
|
18
|
+
const heading = normalizeHeading(match[1] ?? "");
|
|
19
|
+
if (!heading || seen.has(heading)) {
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
22
|
+
headings.push(heading);
|
|
23
|
+
seen.add(heading);
|
|
24
|
+
}
|
|
25
|
+
return headings;
|
|
26
|
+
}
|
|
27
|
+
function loadTemplateHeadingMap(root, config, types) {
|
|
28
|
+
const map = {};
|
|
29
|
+
for (const type of types) {
|
|
30
|
+
const template = (0, loader_1.loadTemplate)(root, config, type);
|
|
31
|
+
map[type] = extractMarkdownHeadings(template.body);
|
|
32
|
+
}
|
|
33
|
+
return map;
|
|
34
|
+
}
|