@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.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +243 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +15 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
- package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-643Z7FIC.js} +1 -1
- package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
- package/dist/cli-delegate/cmd_conformance.js +17 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +56 -25
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +479 -241
- package/dist/install/install.mjs +4 -1
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +313 -63
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/llms.txt +14 -14
- package/package.json +1 -1
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/release-gate-locality.yml +4 -3
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/host_capability.ts +38 -1
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +163 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/council_cli.ts +18 -27
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +31 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/install-hooks.sh +52 -0
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/release.ts +26 -71
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_publication.ts +34 -29
- 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
|
-
|
|
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
|
-
|
|
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[]):
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
|
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.
|
|
381
|
+
const clusterScore = clusterHits.reduce((s, h) => s + h.scored_count * h.weight, 0);
|
|
279
382
|
const dashCount = (forQuotes.match(/[—–]/g) ?? []).length;
|
|
280
|
-
const
|
|
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
|
-
|
|
318
|
-
|
|
319
|
-
|
|
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) {
|