cans-spec 0.3.0 → 0.5.0
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 +15 -6
- package/bin/cans.js +87 -14
- package/bin/ts-loader.mjs +9 -2
- package/package.json +1 -1
- package/src/cli.ts +6 -1
- package/src/commands/budget.ts +87 -16
- package/src/commands/check.ts +313 -58
- package/src/commands/import.ts +216 -6
- package/src/core/fs.ts +31 -11
- package/src/core/outline.ts +17 -1
- package/src/core/output.ts +202 -57
- package/src/core/overflow.ts +4 -0
- package/src/core/redundancy.ts +5 -0
- package/src/core/refs.ts +197 -25
- package/src/core/report.ts +575 -0
- package/src/core/structure.ts +10 -0
- package/src/core/style.ts +2 -0
- package/src/core/token-budget.ts +34 -3
- package/src/types.ts +17 -0
package/src/core/token-budget.ts
CHANGED
|
@@ -108,9 +108,22 @@ export function buildReadPlan(
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
const backRefFiles = new Set<string>();
|
|
111
|
+
// §26 step 3 forward-ref tier (QA-17 F28): the files the canonical home
|
|
112
|
+
// POINTS TO — targets of see: refs made from the home file — connect at 40.
|
|
113
|
+
// (Back-refs, the files that point AT the home, stay 60; a file that is
|
|
114
|
+
// both keeps the higher tier.)
|
|
115
|
+
const forwardFiles = new Set<string>();
|
|
111
116
|
if (home !== null) {
|
|
112
117
|
for (const bp of backPointers) {
|
|
113
118
|
if (targetMatchesKey(bp.toFile, home.file)) backRefFiles.add(bp.fromFile);
|
|
119
|
+
if (bp.fromFile === home.file) {
|
|
120
|
+
for (const key of allFiles.keys()) {
|
|
121
|
+
if (targetMatchesKey(bp.toFile, key)) {
|
|
122
|
+
forwardFiles.add(key);
|
|
123
|
+
break;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
114
127
|
}
|
|
115
128
|
}
|
|
116
129
|
for (const key of allFiles.keys()) {
|
|
@@ -119,6 +132,10 @@ export function buildReadPlan(
|
|
|
119
132
|
items.set(key, { file: key, anchor: null, reason: 'see: back-ref', score: 60, estTokens: tokens(key), rank: 2 });
|
|
120
133
|
continue;
|
|
121
134
|
}
|
|
135
|
+
if (forwardFiles.has(key)) {
|
|
136
|
+
items.set(key, { file: key, anchor: null, reason: 'forward ref', score: 40, estTokens: tokens(key), rank: 3 });
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
122
139
|
const mentions = flattenNodes(allFiles.get(key)!).some(n => n.text.toLowerCase().includes(lc));
|
|
123
140
|
if (mentions) {
|
|
124
141
|
items.set(key, { file: key, anchor: null, reason: 'mentions concept', score: 20, estTokens: tokens(key), rank: 3 });
|
|
@@ -182,10 +199,14 @@ export function buildReadPlan(
|
|
|
182
199
|
const plan: BudgetReadPlanItem[] = [];
|
|
183
200
|
const skipped: string[] = [];
|
|
184
201
|
let totalTokens = 0;
|
|
185
|
-
|
|
202
|
+
// §26 step 4 (issue #16): best-effort greedy packing. Items are walked in
|
|
203
|
+
// score order and each item that fits under the remaining budget is
|
|
204
|
+
// planned. An item that does not fit is skipped (listed in `skipped`) but
|
|
205
|
+
// does NOT cut the walk — cheaper lower-scored items that still fit are
|
|
206
|
+
// considered, so a limit below the canonical home (score 100) can still
|
|
207
|
+
// afford a cheaper back-ref (score 60) instead of yielding plan: [].
|
|
186
208
|
for (const item of sorted) {
|
|
187
|
-
if (
|
|
188
|
-
cut = true;
|
|
209
|
+
if (totalTokens + item.estTokens > budgetLimit) {
|
|
189
210
|
skipped.push(item.file);
|
|
190
211
|
continue;
|
|
191
212
|
}
|
|
@@ -198,6 +219,16 @@ export function buildReadPlan(
|
|
|
198
219
|
for (const key of allFiles.keys()) {
|
|
199
220
|
if (!items.has(key)) skipped.push(key);
|
|
200
221
|
}
|
|
222
|
+
// §26 step 4 (QA-17 F25): skipped lists EVERY file not in the plan — active
|
|
223
|
+
// task files are in budget scope (§22), so a task file with no connection
|
|
224
|
+
// to the concept (never scored into `items`) is listed too, never
|
|
225
|
+
// invisible. Planned-but-unaffordable task files already landed in skipped
|
|
226
|
+
// via the packing loop above.
|
|
227
|
+
if (activeTaskPaths !== undefined) {
|
|
228
|
+
for (const taskPath of activeTaskPaths) {
|
|
229
|
+
if (!items.has(taskPath)) skipped.push(taskPath);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
201
232
|
skipped.sort();
|
|
202
233
|
|
|
203
234
|
const usagePercent = budgetLimit > 0
|
package/src/types.ts
CHANGED
|
@@ -33,6 +33,12 @@ export interface BackPointer {
|
|
|
33
33
|
fromFile: string;
|
|
34
34
|
fromLine: number;
|
|
35
35
|
toFile: string;
|
|
36
|
+
/** The anchor side of the back-pointer. For graph-built back-pointers
|
|
37
|
+
* (buildRefGraph): the ref's raw anchor token, null for file-level refs.
|
|
38
|
+
* For extracted `<!-- ref-by: ... -->` comments (extractBackPointers,
|
|
39
|
+
* issue #19): the text of the node whose bullet line carries the comment
|
|
40
|
+
* (INLINE form — a node mark), or null when the comment stands on its own
|
|
41
|
+
* line (STANDALONE form — a file-level mark). */
|
|
36
42
|
toAnchor: string | null;
|
|
37
43
|
}
|
|
38
44
|
|
|
@@ -48,6 +54,10 @@ export interface Issue {
|
|
|
48
54
|
category: IssueCategory;
|
|
49
55
|
message: string;
|
|
50
56
|
suggestion?: string;
|
|
57
|
+
/** issue #41: machine-readable dotted rule key (e.g. "refs.broken.file",
|
|
58
|
+
* "structure.node_length.max") — stable vocabulary for report grouping and
|
|
59
|
+
* agent consumption. Optional so non-engine Issue constructors stay legal. */
|
|
60
|
+
rule?: string;
|
|
51
61
|
}
|
|
52
62
|
|
|
53
63
|
// ── Rules ──
|
|
@@ -160,6 +170,13 @@ export interface CheckResult extends CommandResult {
|
|
|
160
170
|
errorCount: number;
|
|
161
171
|
warningCount: number;
|
|
162
172
|
backPointersUpdated: number;
|
|
173
|
+
/** Issue #11: spec-relative paths of the files --fix actually rewrote
|
|
174
|
+
* (sorted). Empty without --fix or when nothing needed a write; with a
|
|
175
|
+
* [file] filter only matching files can ever appear here. */
|
|
176
|
+
backPointersUpdatedFiles: string[];
|
|
177
|
+
/** issue #41: wall-clock duration of the whole checkWorkspace run,
|
|
178
|
+
* rounded to whole ms (0 for the static checkFail paths). */
|
|
179
|
+
elapsedMs: number;
|
|
163
180
|
/** §22/§36: human-facing one-line summary of the active _rules.yaml limits (QA-02 F17). */
|
|
164
181
|
rulesSummary?: string;
|
|
165
182
|
}
|