@event4u/agent-config 14.9.0 → 14.10.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.
Files changed (50) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +49 -0
  3. package/dist/agent-src/skills/async-python-patterns/SKILL.md +1 -1
  4. package/dist/agent-src/skills/code-refactoring/SKILL.md +1 -1
  5. package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
  6. package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +1 -1
  7. package/dist/agent-src/skills/design-review/SKILL.md +1 -1
  8. package/dist/agent-src/skills/discovery-interview/SKILL.md +1 -1
  9. package/dist/agent-src/skills/doc-coauthoring/SKILL.md +1 -1
  10. package/dist/agent-src/skills/docx-authoring/SKILL.md +1 -1
  11. package/dist/agent-src/skills/editorial-calendar/SKILL.md +1 -1
  12. package/dist/agent-src/skills/github-ci/SKILL.md +1 -1
  13. package/dist/agent-src/skills/judge-artifact-completeness/SKILL.md +1 -1
  14. package/dist/agent-src/skills/judge-injection-defense/SKILL.md +1 -1
  15. package/dist/agent-src/skills/laravel-validation/SKILL.md +1 -1
  16. package/dist/agent-src/skills/legal-intake-triage/SKILL.md +1 -1
  17. package/dist/agent-src/skills/legal-practice-profile/SKILL.md +1 -1
  18. package/dist/agent-src/skills/messaging-architecture/SKILL.md +1 -1
  19. package/dist/agent-src/skills/migration-architect/SKILL.md +1 -1
  20. package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +1 -1
  21. package/dist/agent-src/skills/module-management/SKILL.md +1 -1
  22. package/dist/agent-src/skills/motion-choreographer/SKILL.md +1 -1
  23. package/dist/agent-src/skills/onboarding-program/SKILL.md +1 -1
  24. package/dist/agent-src/skills/perf-feedback-craft/SKILL.md +1 -1
  25. package/dist/agent-src/skills/prompt-optimizer/SKILL.md +1 -1
  26. package/dist/agent-src/skills/receiving-code-review/SKILL.md +1 -1
  27. package/dist/agent-src/skills/scene-expander/SKILL.md +1 -1
  28. package/dist/agent-src/skills/secrets-management/SKILL.md +1 -1
  29. package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
  30. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  31. package/dist/discovery/deprecation-report.md +1 -1
  32. package/dist/discovery/discovery-manifest.json +29 -29
  33. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  34. package/dist/discovery/discovery-manifest.summary.md +1 -1
  35. package/dist/discovery/dormancy-report.md +1 -1
  36. package/dist/discovery/orphan-report.md +1 -1
  37. package/dist/discovery/packs.json +2 -2
  38. package/dist/discovery/trust-report.md +1 -1
  39. package/dist/discovery/workspaces.json +2 -2
  40. package/dist/mcp/registry-manifest.json +1 -1
  41. package/dist/mcp/server.json +2 -2
  42. package/package.json +1 -1
  43. package/src/config/gate-coverage.yml +20 -0
  44. package/src/config/preamble-payload-budget.json +71 -67
  45. package/src/scripts/ai_council/seating.ts +211 -0
  46. package/src/scripts/check_pack_passport_reconciliation.ts +274 -0
  47. package/src/scripts/generate_pack_manifests.ts +78 -0
  48. package/src/scripts/schemas/pack.schema.json +62 -4
  49. package/src/scripts/schemas/skill.schema.json +2 -2
  50. package/src/scripts/tighten_skill_descriptions.ts +264 -0
@@ -0,0 +1,264 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Tighten skill descriptions toward a cap WITHOUT touching trigger vocabulary.
4
+ *
5
+ * The description is the routing surface. `lint_skill_descriptions` gates its
6
+ * shape, `audit_skill_descriptions` ranks it and `optimize_skill_description`
7
+ * tunes ONE skill against its own `evals/triggers.json`. None of them shortens
8
+ * a corpus, which is why this exists — and why it is deliberately timid.
9
+ *
10
+ * THE FAILURE IT IS BUILT AGAINST. A rewriter asked to cut N chars strips
11
+ * trigger vocabulary before it strips filler, because filler is the harder of
12
+ * the two to identify. A description that loses its trigger phrase costs more
13
+ * in missed routing than it ever saves in tokens, so this tool never rewrites:
14
+ * it applies a closed list of audited, meaning-preserving transformations and
15
+ * then REFUSES to write unless the informative-token set came out identical.
16
+ *
17
+ * "Informative token" is mechanical: lowercased, ≥4 chars, not in STOPWORDS.
18
+ * The set — not the count — must match, so a transformation that swapped one
19
+ * informative word for another of the same length is rejected too.
20
+ *
21
+ * A skill the transformations cannot bring under the cap is reported as a MISS
22
+ * with the residual filler budget beside it, and left alone. That number is
23
+ * what separates "a hand rewrite could still find filler here" from "the only
24
+ * chars left to cut are informative ones" — the second is a genuine
25
+ * trigger-loss wall and the honest reason to stop, and reporting it is the
26
+ * point rather than a consolation for having stopped.
27
+ *
28
+ * Usage:
29
+ * tighten_skill_descriptions --cap 189 # report only (default)
30
+ * tighten_skill_descriptions --cap 189 --apply # write the wins
31
+ * tighten_skill_descriptions --self-test
32
+ *
33
+ * Exit 0 on report and on apply; 1 only on a self-test failure or a usage
34
+ * error. This is an authoring tool, not a gate.
35
+ */
36
+ import * as fs from 'node:fs';
37
+ import * as path from 'node:path';
38
+
39
+ import { SRC_SKILLS } from './_lib/agent_src.js';
40
+
41
+ /** Words too short or too common to carry routing signal. */
42
+ const STOPWORDS = new Set([
43
+ 'when', 'with', 'from', 'that', 'this', 'they', 'them', 'then', 'than', 'into', 'over',
44
+ 'your', 'each', 'both', 'only', 'also', 'such', 'been', 'were', 'will', 'would', 'have',
45
+ 'here', 'what', 'which', 'while', 'their', 'there', 'these', 'those', 'about', 'after',
46
+ 'before', 'every', 'other', 'under', 'until', 'using', 'uses', 'used', 'more', 'most',
47
+ 'less', 'least', 'same', 'does', 'doing', 'done', 'make', 'makes', 'made',
48
+ ]);
49
+
50
+ export function informativeTokens(s: string): Set<string> {
51
+ const out = new Set<string>();
52
+ for (const raw of s.toLowerCase().split(/[^a-z0-9'’\-]+/)) {
53
+ const t = raw.replace(/^[-']+|[-']+$/g, '');
54
+ if (t.length < 4) continue;
55
+ if (STOPWORDS.has(t)) continue;
56
+ out.add(t);
57
+ }
58
+ return out;
59
+ }
60
+
61
+ /**
62
+ * The closed transformation list. Each is meaning-preserving in English and
63
+ * removes only connective or punctuation characters — never a content word.
64
+ * `why` is printed in the report so a reviewer can audit the list itself.
65
+ */
66
+ export const TRANSFORMS: readonly { why: string; apply: (s: string) => string }[] = [
67
+ { why: '"Use when X" → "When X" (the imperative adds nothing a reader needs)', apply: (s) => s.replace(/^Use when /, 'When ') },
68
+ { why: '"in order to" → "to"', apply: (s) => s.replace(/\bin order to\b/g, 'to') },
69
+ { why: 'Oxford comma removed (house style)', apply: (s) => s.replace(/,(\s+)and\b/g, '$1and') },
70
+ { why: 'collapsed runs of whitespace', apply: (s) => s.replace(/ +/g, ' ') },
71
+ ];
72
+
73
+ /**
74
+ * Filler still available AFTER the transforms — the chars a hand rewrite could
75
+ * remove without touching an informative token. Deliberately generous: it
76
+ * counts every stopword occurrence and every separator run, so a MISS reported
77
+ * with a small budget is a strong claim and one with a large budget is an
78
+ * invitation to rewrite by hand, not a wall.
79
+ */
80
+ export function fillerBudget(s: string): number {
81
+ let n = 0;
82
+ for (const raw of s.toLowerCase().split(/([^a-z0-9'’\-]+)/)) {
83
+ const t = raw.replace(/^[-']+|[-']+$/g, '');
84
+ if (t.length === 0) continue;
85
+ if (/^[^a-z0-9]+$/.test(raw) && raw.length > 1) n += raw.length - 1;
86
+ else if (t.length < 4 || STOPWORDS.has(t)) n += t.length + 1;
87
+ }
88
+ return n;
89
+ }
90
+
91
+ export type Outcome = 'already_under' | 'tightened' | 'miss';
92
+
93
+ export interface Result {
94
+ skill: string;
95
+ before: number;
96
+ after: number;
97
+ outcome: Outcome;
98
+ /** Chars of filler still removable by hand at `after`. Only set on a miss. */
99
+ filler_left: number;
100
+ applied: string[];
101
+ text: string | null;
102
+ }
103
+
104
+ export function tighten(skill: string, desc: string, cap: number): Result {
105
+ const before = desc.length;
106
+ if (before <= cap) {
107
+ return { skill, before, after: before, outcome: 'already_under', filler_left: 0, applied: [], text: null };
108
+ }
109
+ const want = informativeTokens(desc);
110
+ let cur = desc;
111
+ const applied: string[] = [];
112
+ for (const t of TRANSFORMS) {
113
+ const next = t.apply(cur).trim();
114
+ if (next === cur) continue;
115
+ // Reject any transformation that moved the informative set, even if it
116
+ // shortened the string. A same-length swap is still a routing change.
117
+ const got = informativeTokens(next);
118
+ if (got.size !== want.size || [...want].some((w) => !got.has(w))) continue;
119
+ cur = next;
120
+ applied.push(t.why);
121
+ }
122
+ if (cur.length <= cap) {
123
+ return { skill, before, after: cur.length, outcome: 'tightened', filler_left: 0, applied, text: cur };
124
+ }
125
+ return {
126
+ skill,
127
+ before,
128
+ after: cur.length,
129
+ outcome: 'miss',
130
+ filler_left: fillerBudget(cur),
131
+ applied,
132
+ text: null,
133
+ };
134
+ }
135
+
136
+ const DESC_RE = /^(description:[ \t]*)(.*?)[ \t]*$/m;
137
+
138
+ function unquote(v: string): { text: string; quote: string } {
139
+ const q = v.length > 1 && (v[0] === '"' || v[0] === "'") && v[v.length - 1] === v[0] ? String(v[0]) : '';
140
+ return { text: q ? v.slice(1, -1) : v, quote: q };
141
+ }
142
+
143
+ interface Skill {
144
+ name: string;
145
+ file: string;
146
+ raw: string;
147
+ desc: string;
148
+ quote: string;
149
+ }
150
+
151
+ function collect(root: string): Skill[] {
152
+ const out: Skill[] = [];
153
+ if (!fs.existsSync(root)) return out;
154
+ for (const d of fs.readdirSync(root, { withFileTypes: true })) {
155
+ if (!d.isDirectory()) continue;
156
+ const file = path.join(root, d.name, 'SKILL.md');
157
+ if (!fs.existsSync(file)) continue;
158
+ const raw = fs.readFileSync(file, 'utf-8');
159
+ const m = DESC_RE.exec(raw);
160
+ if (m === null) continue;
161
+ const { text, quote } = unquote(String(m[2]));
162
+ out.push({ name: d.name, file, raw, desc: text, quote });
163
+ }
164
+ return out;
165
+ }
166
+
167
+ function selfTest(): number {
168
+ const cases: { name: string; desc: string; cap: number; outcome: Outcome; after?: number }[] = [
169
+ { name: 'under cap is untouched', desc: 'Short one.', cap: 189, outcome: 'already_under' },
170
+ {
171
+ name: 'the Use-when prefix is the cheapest real win',
172
+ desc: 'Use when ' + 'x'.repeat(184),
173
+ cap: 189,
174
+ outcome: 'tightened',
175
+ after: 189,
176
+ },
177
+ {
178
+ name: 'nothing to remove is a miss, not a silent truncation',
179
+ desc: 'sqlinjection '.repeat(16),
180
+ cap: 100,
181
+ outcome: 'miss',
182
+ },
183
+ ];
184
+ let pass = 0;
185
+ let fail = 0;
186
+ for (const c of cases) {
187
+ const r = tighten(c.name, c.desc, c.cap);
188
+ const ok = r.outcome === c.outcome && (c.after === undefined || r.after === c.after);
189
+ if (ok) pass += 1;
190
+ else {
191
+ fail += 1;
192
+ console.error(`❌ ${c.name}: outcome=${r.outcome} after=${r.after}`);
193
+ }
194
+ }
195
+ // The load-bearing guard: a transformation that changed the informative
196
+ // set must be refused even though it shortens.
197
+ const before = informativeTokens('deploy kubernetes manifests');
198
+ const after = informativeTokens('deploy kubernetes manifest');
199
+ if (before.size === after.size && [...before].every((t) => after.has(t))) {
200
+ fail += 1;
201
+ console.error('❌ informativeTokens: singular/plural must NOT compare equal — the guard would let a word change through');
202
+ } else {
203
+ pass += 1;
204
+ }
205
+ process.stdout.write(`tighten_skill_descriptions --self-test: ${pass} pass, ${fail} fail\n`);
206
+ return fail === 0 ? 0 : 1;
207
+ }
208
+
209
+ function main(argv: string[]): number {
210
+ if (argv.includes('--self-test')) return selfTest();
211
+ const capIdx = argv.indexOf('--cap');
212
+ const cap = capIdx >= 0 && argv[capIdx + 1] !== undefined ? Number(argv[capIdx + 1]) : 189;
213
+ if (!Number.isInteger(cap) || cap < 40) {
214
+ process.stderr.write('--cap must be an integer ≥ 40\n');
215
+ return 1;
216
+ }
217
+ const apply = argv.includes('--apply');
218
+ const skills = collect(SRC_SKILLS());
219
+ if (skills.length === 0) {
220
+ process.stderr.write(`no skills found under ${SRC_SKILLS()} — refusing to report a zero\n`);
221
+ return 1;
222
+ }
223
+ const results = skills.map((s) => tighten(s.name, s.desc, cap));
224
+ const wins = results.filter((r) => r.outcome === 'tightened');
225
+ const misses = results.filter((r) => r.outcome === 'miss');
226
+
227
+ if (apply) {
228
+ for (const r of wins) {
229
+ const s = skills.find((x) => x.name === r.skill);
230
+ if (s === undefined || r.text === null) continue;
231
+ const replaced = s.raw.replace(DESC_RE, (_m, k: string) => `${k}${s.quote}${String(r.text)}${s.quote}`);
232
+ fs.writeFileSync(s.file, replaced);
233
+ }
234
+ }
235
+
236
+ const savedChars = wins.reduce((a, r) => a + (r.before - r.after), 0);
237
+ process.stdout.write(`cap ${cap} · ${skills.length} skill(s)\n`);
238
+ process.stdout.write(`tightened: ${wins.length} (${savedChars} chars, ≈${Math.round(savedChars / 4)} tok)${apply ? ' — WRITTEN' : ' — dry run'}\n`);
239
+ process.stdout.write(
240
+ `misses: ${misses.length} (left alone — the closed transformation list did not reach the cap)\n`,
241
+ );
242
+ if (misses.length > 0) {
243
+ const need = Math.max(...misses.map((m) => m.after - cap));
244
+ const minFiller = Math.min(...misses.map((m) => m.filler_left));
245
+ process.stdout.write(
246
+ minFiller > need
247
+ ? ` read the table: every miss still holds ≥ ${minFiller} chars of filler against a need of ≤ ${need}, ` +
248
+ 'so these are limits of THIS tool, not trigger-loss walls. A hand rewrite is available.\n'
249
+ : ' at least one miss is close to its filler floor — there a cut would start taking informative tokens.\n',
250
+ );
251
+ }
252
+ if (misses.length > 0) {
253
+ process.stdout.write('\n| skill | len | over | filler left |\n|---|---|---|---|\n');
254
+ for (const m of misses.sort((a, b) => b.after - a.after)) {
255
+ process.stdout.write(`| \`${m.skill}\` | ${m.after} | +${m.after - cap} | ${m.filler_left} |\n`);
256
+ }
257
+ }
258
+ process.stdout.write(`scanned: ${skills.length}\n`);
259
+ return 0;
260
+ }
261
+
262
+ if (process.argv[1] !== undefined && process.argv[1].includes('tighten_skill_descriptions')) {
263
+ process.exit(main(process.argv.slice(2)));
264
+ }