@event4u/agent-config 14.20.0 → 14.21.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 (100) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +243 -0
  5. package/README.md +1 -1
  6. package/agents/templates/.ai-council.yml.example +10 -0
  7. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  8. package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
  9. package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
  10. package/dist/agent-src/commands/analyze.md +12 -7
  11. package/dist/agent-src/commands/feature/plan.md +55 -16
  12. package/dist/agent-src/commands/feature/refactor.md +8 -3
  13. package/dist/agent-src/commands/humanize.md +48 -4
  14. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
  15. package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
  16. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
  17. package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
  18. package/dist/agent-src/rules/evaluator-independence.md +4 -4
  19. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  20. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  21. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
  22. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
  23. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  24. package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
  25. package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
  26. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  27. package/dist/agent-src/templates/features.md +5 -0
  28. package/dist/agent-src/templates/roadmaps.md +15 -0
  29. package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
  30. package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
  31. package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-643Z7FIC.js} +1 -1
  32. package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
  33. package/dist/cli-delegate/cmd_conformance.js +17 -3
  34. package/dist/cli-delegate/cmd_doctor.js +3 -3
  35. package/dist/cli-delegate/cmd_export.js +2 -2
  36. package/dist/cli-delegate/cmd_handoff.js +1 -1
  37. package/dist/cli-delegate/cmd_prune.js +2 -2
  38. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  39. package/dist/cli-delegate/cmd_sync.js +2 -2
  40. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  41. package/dist/cli-delegate/cmd_validate.js +2 -2
  42. package/dist/discovery/deprecation-report.md +1 -1
  43. package/dist/discovery/discovery-manifest.json +56 -25
  44. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  45. package/dist/discovery/discovery-manifest.summary.md +3 -3
  46. package/dist/discovery/dormancy-report.md +1 -1
  47. package/dist/discovery/orphan-report.md +1 -1
  48. package/dist/discovery/packs.json +7 -6
  49. package/dist/discovery/trust-report.md +2 -2
  50. package/dist/discovery/workspaces.json +6 -5
  51. package/dist/hooks/dispatch.js +479 -241
  52. package/dist/install/install.mjs +4 -1
  53. package/dist/mcp/registry-manifest.json +2 -2
  54. package/dist/mcp/server.json +2 -2
  55. package/dist/mcp/server.mjs +313 -63
  56. package/dist/scripts/ai_council/content_ceiling.js +63 -0
  57. package/dist/scripts/ai_council/seat_policy.js +106 -0
  58. package/dist/scripts/ai_council/transport_resolver.js +21 -0
  59. package/llms.txt +14 -14
  60. package/package.json +1 -1
  61. package/src/config/gate-violation-baselines.json +3 -3
  62. package/src/config/release-gate-locality.yml +4 -3
  63. package/src/scripts/_lib/blocked_by_marker.ts +189 -0
  64. package/src/scripts/_lib/host_capability.ts +38 -1
  65. package/src/scripts/_lib/release_highlights.ts +19 -211
  66. package/src/scripts/_lib/release_material.ts +27 -77
  67. package/src/scripts/_lib/structured_ask.ts +117 -0
  68. package/src/scripts/_lib/tty_prompt.ts +76 -0
  69. package/src/scripts/ai_council/api_transport.ts +163 -0
  70. package/src/scripts/ai_council/clients.ts +58 -63
  71. package/src/scripts/ai_council/config.ts +13 -0
  72. package/src/scripts/ai_council/content_ceiling.ts +79 -0
  73. package/src/scripts/ai_council/jury_aggregate.ts +178 -0
  74. package/src/scripts/ai_council/seat_policy.ts +137 -0
  75. package/src/scripts/ai_council/status_surface.ts +79 -0
  76. package/src/scripts/ai_council/transport_resolver.ts +59 -1
  77. package/src/scripts/ai_tells_rules.ts +446 -5
  78. package/src/scripts/ask_block_census.ts +499 -0
  79. package/src/scripts/bench_humanizer_eval.ts +264 -40
  80. package/src/scripts/bench_prose_tells_fp.ts +198 -0
  81. package/src/scripts/check_branch_work_committed.ts +234 -0
  82. package/src/scripts/check_pack_size.ts +1 -1
  83. package/src/scripts/check_release_highlights.ts +0 -157
  84. package/src/scripts/council_cli.ts +18 -27
  85. package/src/scripts/detect_ai_tells.ts +142 -20
  86. package/src/scripts/harvest_reference_tokens.ts +698 -0
  87. package/src/scripts/hook_manifest.json +1 -1
  88. package/src/scripts/hook_manifest.yaml +31 -1
  89. package/src/scripts/hooks/concern_registry.ts +2 -0
  90. package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
  91. package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
  92. package/src/scripts/install-hooks.sh +52 -0
  93. package/src/scripts/lint_roadmap_blockers.ts +51 -1
  94. package/src/scripts/migrate_command_suggestions.ts +1 -1
  95. package/src/scripts/probe_unblocked_ask.ts +135 -2
  96. package/src/scripts/release.ts +26 -71
  97. package/src/scripts/release_drill.ts +6 -10
  98. package/src/scripts/release_env.ts +1 -0
  99. package/src/scripts/release_publication.ts +34 -29
  100. package/src/scripts/self_review_gate.ts +409 -15
@@ -30,11 +30,15 @@
30
30
  */
31
31
 
32
32
  import { readFileSync } from "node:fs";
33
+ import type { MatchResult, RawOccurrence } from "./ai_tells_rules.js";
33
34
  import {
34
35
  ALL_TELL_RULES,
35
36
  DEFAULT_MAX_CLUSTER_SCORE,
36
37
  DEFAULT_MAX_DASH_DENSITY,
37
38
  DEFAULT_MAX_HARD,
39
+ MIN_CONSISTENT_OCCURRENCES,
40
+ MIN_CONSISTENT_SPREAD,
41
+ MIN_DENSITY_WORDS,
38
42
  sniffLanguage,
39
43
  } from "./ai_tells_rules.js";
40
44
  import { _classify } from "./lint_hidden_unicode.js";
@@ -48,6 +52,15 @@ import { _classify } from "./lint_hidden_unicode.js";
48
52
  */
49
53
  export const MAX_SCAN_CHARS = 100_000;
50
54
 
55
+ /** Where one match sits, so a finding can be located rather than only counted. */
56
+ export interface Occurrence {
57
+ /** 1-based. */
58
+ line: number;
59
+ /** 1-based, in characters. */
60
+ column: number;
61
+ text: string;
62
+ }
63
+
51
64
  export interface RuleHit {
52
65
  id: string;
53
66
  group: string;
@@ -55,6 +68,18 @@ export interface RuleHit {
55
68
  count: number;
56
69
  weight: number;
57
70
  samples: string[];
71
+ /** Every match, located. `count` is `occurrences.length`. */
72
+ occurrences: Occurrence[];
73
+ /**
74
+ * `uniform` when the pattern is used at least
75
+ * `MIN_CONSISTENT_OCCURRENCES` times across at least
76
+ * `MIN_CONSISTENT_SPREAD` of the document: a habit the author carries
77
+ * through the whole piece, which is evidence of style rather than of N
78
+ * independent tells. A uniform cluster pattern contributes its weight ONCE.
79
+ */
80
+ consistency: "uniform" | "scattered";
81
+ /** What the cluster score actually charged for this rule. */
82
+ scored_count: number;
58
83
  }
59
84
 
60
85
  /**
@@ -77,9 +102,16 @@ export interface TellReport {
77
102
  cluster_hits: RuleHit[];
78
103
  hard_total: number;
79
104
  cluster_score: number;
80
- cluster_score_per_500: number;
105
+ /**
106
+ * `null` below `MIN_DENSITY_WORDS`: a per-500-words rate extrapolated from a
107
+ * handful of words is an artifact of the denominator, not a reading of the
108
+ * prose. `null` says "not evaluated" where a `0` would say "measured clean".
109
+ */
110
+ cluster_score_per_500: number | null;
81
111
  dash_count: number;
82
- dash_density_per_500: number;
112
+ dash_density_per_500: number | null;
113
+ /** False when the text is under the floor and neither density was applied. */
114
+ density_evaluated: boolean;
83
115
  per_pattern: Record<string, number>;
84
116
  truncated: boolean;
85
117
  hidden_unicode: HiddenUnicodeFinding[];
@@ -220,27 +252,82 @@ export function stripExempt(text: string): { base: string; forQuotes: string } {
220
252
  const base = t
221
253
  .replace(/"[^"\n]{1,300}"/g, " ")
222
254
  .replace(/“[^”\n]{1,300}”/g, " ")
223
- .replace(/'[^'\n]{1,120}'/g, (m) => (m.includes(" ") ? " " : m));
255
+ // A single-quoted SPAN, never a pair of contractions. The previous form
256
+ // matched any two apostrophes on a line with a space between them, so
257
+ // "In today's fast-paced world, let's be honest" was read as a quotation
258
+ // and stripped whole — which silently removed two tell families from the
259
+ // scan. An opening quote must start a token and a closing quote must end
260
+ // one; an apostrophe inside a word is never either.
261
+ .replace(
262
+ /(^|[\s([{"“])'([^'\n]{1,120})'(?=[\s)\]},.!?;:"”]|$)/g,
263
+ (m, lead: string, inner: string) => (inner.includes(" ") ? `${lead} ` : m),
264
+ );
224
265
  return { base, forQuotes };
225
266
  }
226
267
 
227
- function countMatches(text: string, patterns: RegExp[]): { count: number; samples: string[] } {
228
- let count = 0;
229
- const samples: string[] = [];
268
+ function countMatches(text: string, patterns: RegExp[]): MatchResult {
269
+ const occurrences: RawOccurrence[] = [];
230
270
  for (const p of patterns) {
231
271
  const flags = p.flags.includes("g") ? p.flags : p.flags + "g";
232
272
  const re = new RegExp(p.source, flags);
233
273
  for (const m of text.matchAll(re)) {
234
- count += 1;
235
- if (samples.length < 3) samples.push(m[0].slice(0, 60).trim());
274
+ occurrences.push({ index: m.index ?? 0, text: m[0] });
236
275
  }
237
276
  }
238
- return { count, samples };
277
+ occurrences.sort((a, b) => a.index - b.index);
278
+ return {
279
+ count: occurrences.length,
280
+ samples: occurrences.slice(0, 3).map((o) => o.text.slice(0, 60).trim()),
281
+ occurrences,
282
+ };
283
+ }
284
+
285
+ /** Offset → 1-based line and column, computed once per scan text. */
286
+ function lineIndex(text: string): (index: number) => { line: number; column: number } {
287
+ const starts = [0];
288
+ for (let i = 0; i < text.length; i += 1) if (text[i] === "\n") starts.push(i + 1);
289
+ return (index: number) => {
290
+ let lo = 0;
291
+ let hi = starts.length - 1;
292
+ while (lo < hi) {
293
+ const mid = (lo + hi + 1) >> 1;
294
+ if ((starts[mid] as number) <= index) lo = mid;
295
+ else hi = mid - 1;
296
+ }
297
+ return { line: lo + 1, column: index - (starts[lo] as number) + 1 };
298
+ };
299
+ }
300
+
301
+ /**
302
+ * Is this pattern a habit the author carries through the document, or a local
303
+ * repetition? Without a captured voice sample the skill falls back to defaults,
304
+ * so consistency is the only intent signal available from the text itself.
305
+ */
306
+ export function classifyConsistency(
307
+ occurrences: ReadonlyArray<RawOccurrence>,
308
+ textLength: number,
309
+ ): "uniform" | "scattered" {
310
+ if (occurrences.length < MIN_CONSISTENT_OCCURRENCES) return "scattered";
311
+ const first = occurrences[0]?.index ?? 0;
312
+ const last = occurrences[occurrences.length - 1]?.index ?? 0;
313
+ const spread = (last - first) / Math.max(textLength, 1);
314
+ return spread >= MIN_CONSISTENT_SPREAD ? "uniform" : "scattered";
315
+ }
316
+
317
+ export interface AnalyzeOptions {
318
+ /**
319
+ * Rule ids to leave out of the scan entirely — not merely out of the
320
+ * reported vector. Used by the paired bench to ask what a family was
321
+ * contributing: a metric that changes when the rule is removed is one the
322
+ * rule was carrying, and a metric that does not is one it was not.
323
+ */
324
+ exclude?: ReadonlySet<string>;
239
325
  }
240
326
 
241
327
  export function analyzeText(
242
328
  text: string,
243
329
  languageOpt: "en" | "de" | "auto" = "auto",
330
+ options: AnalyzeOptions = {},
244
331
  ): TellReport {
245
332
  // Scan the RAW text for hidden-instruction vectors before any stripping —
246
333
  // the smuggling layer is invisible and must be reported (untrusted-input).
@@ -253,16 +340,29 @@ export function analyzeText(
253
340
  const words = base.split(/\s+/).filter(Boolean).length || 1;
254
341
  const language = languageOpt === "auto" ? sniffLanguage(base) : languageOpt;
255
342
 
343
+ const locateBase = lineIndex(base);
344
+ const locateQuotes = lineIndex(forQuotes);
256
345
  const hardHits: RuleHit[] = [];
257
346
  const clusterHits: RuleHit[] = [];
258
347
  const perPattern: Record<string, number> = {};
259
348
 
260
349
  for (const rule of ALL_TELL_RULES) {
261
350
  if (rule.language !== "any" && rule.language !== language) continue;
351
+ if (options.exclude?.has(rule.id)) continue;
262
352
  const scanText = rule.id === "tell-curly-quotes" ? forQuotes : base;
263
- const { count, samples } = countMatches(scanText, rule.patterns);
353
+ const result: MatchResult = rule.match
354
+ ? rule.match(scanText, language)
355
+ : countMatches(scanText, rule.patterns);
356
+ const { count, samples, occurrences } = result;
264
357
  if (count === 0) continue;
265
358
  perPattern[rule.id] = count;
359
+ const locate = scanText === forQuotes ? locateQuotes : locateBase;
360
+ const consistency = classifyConsistency(occurrences, scanText.length);
361
+ // A uniform pattern is charged once. A HARD rule is never discounted: its
362
+ // members are chat artifacts and cutoff disclaimers, and repeating one
363
+ // through a document makes it more of a defect, not less of one.
364
+ const scoredCount =
365
+ rule.severity === "cluster" && consistency === "uniform" ? 1 : count;
266
366
  const hit: RuleHit = {
267
367
  id: rule.id,
268
368
  group: rule.group,
@@ -270,14 +370,19 @@ export function analyzeText(
270
370
  count,
271
371
  weight: rule.weight,
272
372
  samples,
373
+ occurrences: occurrences.map((o) => ({ ...locate(o.index), text: o.text.slice(0, 120) })),
374
+ consistency,
375
+ scored_count: scoredCount,
273
376
  };
274
377
  (rule.severity === "hard" ? hardHits : clusterHits).push(hit);
275
378
  }
276
379
 
277
380
  const hardTotal = hardHits.reduce((s, h) => s + h.count, 0);
278
- const clusterScore = clusterHits.reduce((s, h) => s + h.count * h.weight, 0);
381
+ const clusterScore = clusterHits.reduce((s, h) => s + h.scored_count * h.weight, 0);
279
382
  const dashCount = (forQuotes.match(/[—–]/g) ?? []).length;
280
- const per500 = (n: number) => Math.round((n / words) * 500 * 100) / 100;
383
+ const densityEvaluated = words >= MIN_DENSITY_WORDS;
384
+ const per500 = (n: number) =>
385
+ densityEvaluated ? Math.round((n / words) * 500 * 100) / 100 : null;
281
386
 
282
387
  return {
283
388
  words,
@@ -289,6 +394,7 @@ export function analyzeText(
289
394
  cluster_score_per_500: per500(clusterScore),
290
395
  dash_count: dashCount,
291
396
  dash_density_per_500: per500(dashCount),
397
+ density_evaluated: densityEvaluated,
292
398
  per_pattern: perPattern,
293
399
  truncated,
294
400
  hidden_unicode: hiddenUnicode,
@@ -299,9 +405,9 @@ export function exceedsThresholds(r: TellReport, t: Thresholds): string[] {
299
405
  const reasons: string[] = [];
300
406
  if (r.hard_total > t.maxHard)
301
407
  reasons.push(`hard hits ${r.hard_total} > ${t.maxHard}`);
302
- if (r.cluster_score_per_500 > t.maxScore)
408
+ if (r.cluster_score_per_500 !== null && r.cluster_score_per_500 > t.maxScore)
303
409
  reasons.push(`cluster score ${r.cluster_score_per_500}/500w > ${t.maxScore}`);
304
- if (r.dash_density_per_500 > t.maxDashDensity)
410
+ if (r.dash_density_per_500 !== null && r.dash_density_per_500 > t.maxDashDensity)
305
411
  reasons.push(`dash density ${r.dash_density_per_500}/500w > ${t.maxDashDensity}`);
306
412
  return reasons;
307
413
  }
@@ -309,15 +415,31 @@ export function exceedsThresholds(r: TellReport, t: Thresholds): string[] {
309
415
  function humanSummary(name: string, r: TellReport, reasons: string[]): string {
310
416
  const lines: string[] = [];
311
417
  const verdict = reasons.length === 0 ? "✅" : "❌";
418
+ const density = r.density_evaluated
419
+ ? `cluster ${r.cluster_score_per_500}/500w · dashes ${r.dash_density_per_500}/500w`
420
+ : `density not evaluated (${r.words} words < ${MIN_DENSITY_WORDS}-word floor; ` +
421
+ `${r.cluster_score} cluster weight, ${r.dash_count} dash(es) counted)`;
312
422
  lines.push(
313
- `${verdict} ${name} — ${r.words} words (${r.language}) · hard ${r.hard_total} · ` +
314
- `cluster ${r.cluster_score_per_500}/500w · dashes ${r.dash_density_per_500}/500w`,
423
+ `${verdict} ${name} — ${r.words} words (${r.language}) · hard ${r.hard_total} · ${density}`,
315
424
  );
316
425
  for (const h of [...r.hard_hits, ...r.cluster_hits]) {
317
- lines.push(
318
- ` ${h.severity === "hard" ? "‼" : "·"} ${h.id} ×${h.count}` +
319
- (h.samples.length ? ` (${h.samples.join(" | ")})` : ""),
320
- );
426
+ const where = h.occurrences
427
+ .slice(0, 3)
428
+ .map((o) => `${o.line}:${o.column}`)
429
+ .join(", ");
430
+ if (h.consistency === "uniform") {
431
+ lines.push(
432
+ ` ≡ ${h.id} — used consistently throughout (${h.count} occurrences, ` +
433
+ `${where}${h.occurrences.length > 3 ? ", …" : ""}); read as style, ` +
434
+ `charged once, not as ${h.count} independent tells`,
435
+ );
436
+ } else {
437
+ lines.push(
438
+ ` ${h.severity === "hard" ? "‼" : "·"} ${h.id} ×${h.count}` +
439
+ (where ? ` @ ${where}${h.occurrences.length > 3 ? ", …" : ""}` : "") +
440
+ (h.samples.length ? ` (${h.samples.join(" | ")})` : ""),
441
+ );
442
+ }
321
443
  }
322
444
  for (const reason of reasons) lines.push(` → over threshold: ${reason}`);
323
445
  for (const h of r.hidden_unicode) {