@forwardimpact/libcoaligned 0.1.5 → 0.1.6
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 +4 -3
- package/package.json +1 -1
- package/src/instructions.js +30 -10
package/README.md
CHANGED
|
@@ -11,7 +11,7 @@ invariants across the repo.
|
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
13
|
npx coaligned # run every check (instructions + jtbd)
|
|
14
|
-
npx coaligned instructions # enforce L1–
|
|
14
|
+
npx coaligned instructions # enforce L1–L7 length and checklist caps
|
|
15
15
|
npx coaligned jtbd # validate JTBD entries against package.json
|
|
16
16
|
npx coaligned jtbd --fix # regenerate catalog and job blocks in place
|
|
17
17
|
```
|
|
@@ -20,8 +20,9 @@ The two subcommands implement the contract described in
|
|
|
20
20
|
[COALIGNED.md](https://github.com/forwardimpact/monorepo/blob/main/COALIGNED.md):
|
|
21
21
|
|
|
22
22
|
- `instructions` — every layer (L1 CLAUDE.md, L2 CONTRIBUTING.md / JTBD.md,
|
|
23
|
-
L3 agent profile, L4
|
|
24
|
-
a line cap **and** a word cap. Either breach
|
|
23
|
+
L3 agent profile, L4 agent reference, L5 SKILL.md, L6 skill reference,
|
|
24
|
+
L7 checklist block) is gated by a line cap **and** a word cap. Either breach
|
|
25
|
+
fails.
|
|
25
26
|
- `jtbd` — each `package.json .jobs` entry is validated against the JTBD
|
|
26
27
|
schema; with `--fix`, marker-delimited blocks in `<dir>/README.md`,
|
|
27
28
|
`<dir>/<pkg>/README.md`, and root `JTBD.md` are regenerated.
|
package/package.json
CHANGED
package/src/instructions.js
CHANGED
|
@@ -14,8 +14,8 @@ const SKIP_DIRS = new Set([
|
|
|
14
14
|
"worktrees",
|
|
15
15
|
]);
|
|
16
16
|
|
|
17
|
-
const
|
|
18
|
-
const
|
|
17
|
+
const L7_MAX_ITEMS = 9;
|
|
18
|
+
const L7_MAX_WORDS_PER_ITEM = 32;
|
|
19
19
|
|
|
20
20
|
const CHECKLIST_RE =
|
|
21
21
|
/<(read_do_checklist|do_confirm_checklist)\b[^>]*>([\s\S]*?)<\/\1>/g;
|
|
@@ -78,6 +78,19 @@ async function findAgentProfiles(root, claudeDirs) {
|
|
|
78
78
|
return out;
|
|
79
79
|
}
|
|
80
80
|
|
|
81
|
+
async function findAgentReferences(root, claudeDirs) {
|
|
82
|
+
const out = [];
|
|
83
|
+
for (const d of claudeDirs) {
|
|
84
|
+
const files = await listFiles(
|
|
85
|
+
root,
|
|
86
|
+
`${d}/agents/references`,
|
|
87
|
+
(e) => e.isFile() && e.name.endsWith(".md"),
|
|
88
|
+
);
|
|
89
|
+
out.push(...files);
|
|
90
|
+
}
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
|
|
81
94
|
async function findSkillDirs(root, claudeDirs) {
|
|
82
95
|
const out = [];
|
|
83
96
|
for (const d of claudeDirs) {
|
|
@@ -147,13 +160,20 @@ async function buildLayers(root) {
|
|
|
147
160
|
},
|
|
148
161
|
{
|
|
149
162
|
id: "L4",
|
|
163
|
+
name: "agent reference",
|
|
164
|
+
maxLines: 192,
|
|
165
|
+
maxWords: 1280,
|
|
166
|
+
files: await findAgentReferences(root, claudeDirs),
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: "L5",
|
|
150
170
|
name: "skill procedure",
|
|
151
171
|
maxLines: 192,
|
|
152
172
|
maxWords: 1280,
|
|
153
173
|
files: skillDirs.map((d) => `${d}/SKILL.md`),
|
|
154
174
|
},
|
|
155
175
|
{
|
|
156
|
-
id: "
|
|
176
|
+
id: "L6",
|
|
157
177
|
name: "skill reference",
|
|
158
178
|
maxLines: 128,
|
|
159
179
|
maxWords: 768,
|
|
@@ -241,29 +261,29 @@ export const INSTRUCTION_RULES = [
|
|
|
241
261
|
hint: HINT_LAYER_BUDGET,
|
|
242
262
|
},
|
|
243
263
|
{
|
|
244
|
-
id: "
|
|
264
|
+
id: "L7.too-many-items",
|
|
245
265
|
scope: "checklist-block",
|
|
246
266
|
severity: "fail",
|
|
247
267
|
check: (s) =>
|
|
248
|
-
s.items.length >
|
|
249
|
-
? { count: s.items.length, max:
|
|
268
|
+
s.items.length > L7_MAX_ITEMS
|
|
269
|
+
? { count: s.items.length, max: L7_MAX_ITEMS }
|
|
250
270
|
: null,
|
|
251
271
|
message: (s, r) =>
|
|
252
272
|
`checklist #${s.blockIndex} (${s.type}) has ${r.count} items (max ${r.max})`,
|
|
253
273
|
hint: "split the checklist into multiple sections, or remove items not load-bearing for the goal",
|
|
254
274
|
},
|
|
255
275
|
{
|
|
256
|
-
id: "
|
|
276
|
+
id: "L7.item-too-many-words",
|
|
257
277
|
scope: "checklist-block",
|
|
258
278
|
severity: "fail",
|
|
259
279
|
check: (s) => {
|
|
260
280
|
const offenders = [];
|
|
261
281
|
s.items.forEach((item, i) => {
|
|
262
|
-
if (item.words >
|
|
282
|
+
if (item.words > L7_MAX_WORDS_PER_ITEM) {
|
|
263
283
|
offenders.push({
|
|
264
284
|
itemIndex: i + 1,
|
|
265
285
|
words: item.words,
|
|
266
|
-
max:
|
|
286
|
+
max: L7_MAX_WORDS_PER_ITEM,
|
|
267
287
|
});
|
|
268
288
|
}
|
|
269
289
|
});
|
|
@@ -278,7 +298,7 @@ export const INSTRUCTION_RULES = [
|
|
|
278
298
|
// -- Public entry --------------------------------------------------------
|
|
279
299
|
|
|
280
300
|
/**
|
|
281
|
-
* Walk the repo rooted at `root`, applying the L1–
|
|
301
|
+
* Walk the repo rooted at `root`, applying the L1–L7 caps from COALIGNED.md.
|
|
282
302
|
* Each layer is gated by a line cap AND a word cap; either breach fails.
|
|
283
303
|
*
|
|
284
304
|
* @param {{ root: string }} options
|