@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
@@ -0,0 +1,137 @@
1
+ import {
2
+ classifyContentClass as _classifyContentClass,
3
+ DEFAULT_SEAT_CEILING as _DEFAULT_SEAT_CEILING,
4
+ type ContentClass,
5
+ } from './content_ceiling.js';
6
+
7
+ /**
8
+ * Seat policy — the vocabulary for refusing a route that IS reachable.
9
+ *
10
+ * road-to-admissible-council-seats 2.1. `AbsentReason` was exactly four values
11
+ * — `no_binary | no_auth | timeout | quota` — and every one of them means "the
12
+ * route is not reachable". A route that is reachable and **must not be used**
13
+ * had no name at all, so the resolver could not fail closed on terms, privacy,
14
+ * unproven cost, or a served model that is not the requested one. The absence
15
+ * of a word was the absence of the refusal: `resolveTransport`'s `api`/`cli`
16
+ * branches return `available: true` unconditionally, so anything the four
17
+ * reachability reasons could not express resolved OPEN.
18
+ *
19
+ * Fail closed on the unknown, which is the whole point.
20
+ * `classifyPolicyExclusion` maps anything it does not recognise to
21
+ * `policy_unknown`, and `policy_unknown` is an ABSENT reason like every other
22
+ * member of this union. An unrecognised policy token is the case where the
23
+ * config is trying to say something the code cannot understand; resolving it
24
+ * to "available" would make a typo an authorisation. This is the roadmap's own
25
+ * Risk 3 — "a new refusal vocabulary defaults open instead of closed".
26
+ *
27
+ * What this is NOT. It is not a secrets floor. Redaction (`skills/ai-council/references/
28
+ * cost-and-redaction.md`) still runs underneath and is not replaced. It is not
29
+ * an authorisation either: nothing here ever makes a seat available that was
30
+ * not already available — every value in the union only ever subtracts.
31
+ */
32
+
33
+ /** Reachable-but-refused reasons. Every value means ABSENT. */
34
+ export const POLICY_EXCLUSIONS = [
35
+ /** The provider's terms forbid this use of the route. */
36
+ 'policy_terms',
37
+ /** A privacy constraint forbids sending to this route. */
38
+ 'policy_privacy',
39
+ /** The route's cost property is asserted rather than proven. */
40
+ 'policy_unproven_cost',
41
+ /** The route serves a model other than the one requested. */
42
+ 'policy_model_mismatch',
43
+ /** 2.2 — the payload's content class exceeds the seat's ceiling. */
44
+ 'policy_content_ceiling',
45
+ /** The config named an exclusion this build does not understand. */
46
+ 'policy_unknown',
47
+ ] as const;
48
+
49
+ export type PolicyExclusion = (typeof POLICY_EXCLUSIONS)[number];
50
+
51
+ const _SET: ReadonlySet<string> = new Set<string>(POLICY_EXCLUSIONS);
52
+
53
+ /** True for a value this build can act on. `policy_unknown` included: it is a
54
+ * real, actionable verdict (refuse), not a parse failure. */
55
+ export function isPolicyExclusion(v: unknown): v is PolicyExclusion {
56
+ return typeof v === 'string' && _SET.has(v);
57
+ }
58
+
59
+ /**
60
+ * Any configured value → a verdict. Unrecognised input, including `null` used
61
+ * where a string was meant, becomes `policy_unknown` — never `null`, because a
62
+ * `null` return would put the caller back in the position of deciding what an
63
+ * unreadable policy means, which is where the open default came from.
64
+ *
65
+ * `undefined` and `null` are the two ways of saying "no exclusion configured"
66
+ * and are the ONLY inputs that return `null`. Everything else is a refusal.
67
+ */
68
+ export function classifyPolicyExclusion(raw: unknown): PolicyExclusion | null {
69
+ if (raw === undefined || raw === null) {
70
+ return null;
71
+ }
72
+ return isPolicyExclusion(raw) ? raw : 'policy_unknown';
73
+ }
74
+
75
+ /** One-line human reason for a refusal. `council:status` prints this. */
76
+ export function policyExclusionReason(p: PolicyExclusion, provider?: string): string {
77
+ const who = provider === undefined ? 'this seat' : provider;
78
+ switch (p) {
79
+ case 'policy_terms':
80
+ return `${who} is excluded by policy: the route's terms forbid this use`;
81
+ case 'policy_privacy':
82
+ return `${who} is excluded by policy: a privacy constraint forbids this route`;
83
+ case 'policy_unproven_cost':
84
+ return `${who} is excluded by policy: the route's cost property is asserted, not proven`;
85
+ case 'policy_model_mismatch':
86
+ return `${who} is excluded by policy: the route serves a model other than the one requested`;
87
+ case 'policy_content_ceiling':
88
+ return `${who} is excluded by policy: the payload exceeds the seat's content ceiling`;
89
+ case 'policy_unknown':
90
+ return `${who} is excluded by policy: an exclusion this build does not understand (failing closed)`;
91
+ }
92
+ }
93
+
94
+ /**
95
+ * Parsed together because they are one question — "what may this seat do, and
96
+ * if it may do nothing, why" — and because `config.ts` sits ~670 lines past
97
+ * the 1,500-line source ceiling, where every line costs one excess line.
98
+ *
99
+ * Returns an error MESSAGE rather than throwing so `config.ts` keeps raising
100
+ * its own `CouncilConfigError` (which its callers catch by type) without this
101
+ * module importing it and creating a cycle back into the loader.
102
+ */
103
+ export interface SeatFields {
104
+ readonly disabled_reason: string | null;
105
+ readonly policy_exclusion: PolicyExclusion | null;
106
+ readonly content_ceiling: ContentClass;
107
+ }
108
+
109
+ export function parseSeatFields(
110
+ name: string,
111
+ raw: Record<string, unknown>,
112
+ ): { fields: SeatFields } | { error: string } {
113
+ const dr = raw['disabled_reason'];
114
+ let disabled_reason: string | null = null;
115
+ if (dr !== undefined && dr !== null) {
116
+ if (typeof dr !== 'string' || dr.trim() === '') {
117
+ return {
118
+ error:
119
+ `members.${name}.disabled_reason must be a non-empty string when set ` +
120
+ `(got ${JSON.stringify(dr)}).`,
121
+ };
122
+ }
123
+ disabled_reason = dr.trim();
124
+ }
125
+ // Both of the next two fail CLOSED on an unreadable value: an unknown
126
+ // exclusion token becomes `policy_unknown` (which resolves ABSENT) and an
127
+ // unknown ceiling falls back to the RESTRICTIVE one. Throwing instead would
128
+ // be defensible, but a config typo would then take the whole council down
129
+ // rather than the one seat it names.
130
+ return {
131
+ fields: {
132
+ disabled_reason,
133
+ policy_exclusion: classifyPolicyExclusion(raw['policy_exclusion']),
134
+ content_ceiling: _classifyContentClass(raw['content_ceiling'], _DEFAULT_SEAT_CEILING),
135
+ },
136
+ };
137
+ }
@@ -0,0 +1,79 @@
1
+ /**
2
+ * The seat-status helpers `council:status` renders from.
3
+ *
4
+ * Extracted from `council_cli.ts` (road-to-admissible-council-seats 1.2/2.1):
5
+ * that file sits ~2,460 lines past the 1,500-line ceiling
6
+ * `check_source_size_budget` ratchets, so every line added there costs one
7
+ * excess line. These three surfaces are what 1.2 and 2.1 grew, and they are a
8
+ * coherent unit — "what does this package know about a seat before any call is
9
+ * made" — rather than an arbitrary slice taken to satisfy a gate.
10
+ *
11
+ * Nothing here authorizes anything. `disabledSeats` reports a note the config
12
+ * carries; `resolvedTransportFor` only ever forwards a REFUSAL input to the
13
+ * resolver.
14
+ */
15
+
16
+ import type { EnvironmentReport } from '../_lib/environment_detector.js';
17
+ import { detectEnvironment } from '../_lib/environment_detector.js';
18
+ import { DEFAULT_SEAT_CEILING, type ContentClass } from './content_ceiling.js';
19
+ import type { PolicyExclusion } from './seat_policy.js';
20
+ import { resolveMemberTransport } from './transport_resolver.js';
21
+
22
+ interface SeatLike {
23
+ readonly binary: string | null;
24
+ readonly api_key_ref: string | null;
25
+ readonly policy_exclusion?: PolicyExclusion | null;
26
+ readonly content_ceiling?: ContentClass;
27
+ }
28
+
29
+ interface ConfigLike {
30
+ readonly members: ReadonlyMap<string, { readonly enabled: boolean; readonly disabled_reason: string | null }>;
31
+ }
32
+
33
+ /**
34
+ * Resolve the concrete transport a member would use on THIS machine right now.
35
+ *
36
+ * `cmd_status` exists to answer "is the council reachable, and will it bill me".
37
+ * Since the transport-mode setting was removed, the second half is not a value
38
+ * anyone can look up in the config — it is a per-machine resolution. Printing a
39
+ * configured mode would be the same class of stale answer the removed setting
40
+ * produced. `mode` is pinned to `auto` because the loader no longer emits
41
+ * anything else.
42
+ */
43
+ export function resolvedTransportFor(
44
+ name: string,
45
+ member: SeatLike,
46
+ report?: EnvironmentReport,
47
+ contentClass?: ContentClass | null,
48
+ ): ReturnType<typeof resolveMemberTransport> {
49
+ return resolveMemberTransport({
50
+ provider: name,
51
+ report: report ?? detectEnvironment(),
52
+ invocationMode: null,
53
+ memberSettings: null,
54
+ globalMode: 'auto',
55
+ binaryOverride: member.binary,
56
+ policyExclusion: member.policy_exclusion ?? null,
57
+ seatCeiling: member.content_ceiling ?? DEFAULT_SEAT_CEILING,
58
+ contentClass: contentClass ?? null,
59
+ });
60
+ }
61
+
62
+ /**
63
+ * Disabled seats and the reason each records. `NO_DISABLED_REASON` is the
64
+ * honest rendering of an unset field: an `enabled: false` with nothing beside
65
+ * it is a fact about the config, and hiding it is what made the seat silent.
66
+ */
67
+ export const NO_DISABLED_REASON = 'disabled, no reason recorded';
68
+
69
+ export function disabledSeats(cfg: ConfigLike | null): Record<string, string> {
70
+ if (cfg === null) {
71
+ return {};
72
+ }
73
+ return Object.fromEntries(
74
+ [...cfg.members.entries()]
75
+ .filter(([, m]) => !m.enabled)
76
+ .map(([n, m]) => [n, m.disabled_reason ?? NO_DISABLED_REASON]),
77
+ );
78
+ }
79
+
@@ -49,6 +49,12 @@ import {
49
49
  type EnvironmentReport,
50
50
  } from '../_lib/environment_detector.js';
51
51
  import { resolve_mode } from './modes.js';
52
+ import {
53
+ admitsContent,
54
+ DEFAULT_SEAT_CEILING,
55
+ type ContentClass,
56
+ } from './content_ceiling.js';
57
+ import { policyExclusionReason, type PolicyExclusion } from './seat_policy.js';
52
58
 
53
59
  /** A concrete transport a member can actually run on. `auto` is not one. */
54
60
  export type Transport = 'api' | 'manual' | 'cli';
@@ -62,7 +68,17 @@ export type Transport = 'api' | 'manual' | 'cli';
62
68
  * `absentReasonFromCliFailure` below for the mapping FROM the mid-flight
63
69
  * `CliFailureClass` a caller already classifies its errors into.
64
70
  */
65
- export type AbsentReason = 'no_binary' | 'no_auth' | 'timeout' | 'quota';
71
+ /** The four REACHABILITY reasons. Each means "the route cannot be run". */
72
+ export type UnreachableReason = 'no_binary' | 'no_auth' | 'timeout' | 'quota';
73
+
74
+ /**
75
+ * Reachability OR policy. The policy half (2.1) names the case the four
76
+ * original values could not: a route that IS reachable and must not be used.
77
+ * Both halves mean the same thing to a caller — the member is absent — which
78
+ * is why they share one union rather than sitting in two fields a caller could
79
+ * forget to check.
80
+ */
81
+ export type AbsentReason = UnreachableReason | PolicyExclusion;
66
82
 
67
83
  /** The accepted values of the `mode` key, including the resolver-only `auto`. */
68
84
  export const VALID_TRANSPORT_MODES: ReadonlySet<string> = new Set([
@@ -110,6 +126,19 @@ export interface ResolveTransportOptions {
110
126
  * env-key auth record exists for this provider" from the report.
111
127
  */
112
128
  readonly apiKeyPresent?: boolean;
129
+ /**
130
+ * A configured policy exclusion for this seat (2.1). Non-null resolves
131
+ * ABSENT whatever the mode says — a reachable route that must not be used
132
+ * is not made usable by being reachable.
133
+ */
134
+ readonly policyExclusion?: PolicyExclusion | null;
135
+ /** The seat's content ceiling (2.2). Defaults to `project-content`. */
136
+ readonly seatCeiling?: ContentClass | null;
137
+ /**
138
+ * The class of what this pass would send. Omitted → no content check runs,
139
+ * which is what keeps every existing caller's behaviour identical.
140
+ */
141
+ readonly contentClass?: ContentClass | null;
113
142
  }
114
143
 
115
144
  /** Auth sources that mean "the provider CLI can authenticate". */
@@ -161,6 +190,28 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
161
190
  const authSource = auth?.source ?? null;
162
191
  const billing = classifyBilling(provider, authSource);
163
192
 
193
+ // Policy first (2.1). Before the mode dispatch, because `api` and `cli`
194
+ // below return `available: true` UNCONDITIONALLY — a refusal placed after
195
+ // them would be unreachable for exactly the seats it exists to stop.
196
+ const exclusion = opts.policyExclusion ?? null;
197
+ if (exclusion !== null) {
198
+ return unavailable(provider, billing, policyExclusionReason(exclusion, provider), exclusion);
199
+ }
200
+ // Content ceiling (2.2). Only runs when the caller declared what it would
201
+ // send; an undeclared pass is unchanged, not silently refused.
202
+ const contentClass = opts.contentClass ?? null;
203
+ if (contentClass !== null) {
204
+ const verdict = admitsContent(opts.seatCeiling ?? DEFAULT_SEAT_CEILING, contentClass);
205
+ if (!verdict.admitted) {
206
+ return unavailable(
207
+ provider,
208
+ billing,
209
+ `${provider}: ${verdict.reason ?? 'content ceiling refused the payload'}`,
210
+ 'policy_content_ceiling',
211
+ );
212
+ }
213
+ }
214
+
164
215
  if (mode === 'manual') {
165
216
  return {
166
217
  available: true,
@@ -246,6 +297,10 @@ export interface ResolveMemberTransportOptions {
246
297
  readonly globalMode?: string | null;
247
298
  readonly binaryOverride?: string | null;
248
299
  readonly apiKeyPresent?: boolean;
300
+ /** Forwarded to `resolveTransport` (2.1 / 2.2). */
301
+ readonly policyExclusion?: PolicyExclusion | null;
302
+ readonly seatCeiling?: ContentClass | null;
303
+ readonly contentClass?: ContentClass | null;
249
304
  }
250
305
 
251
306
  export interface ResolvedMemberTransport extends ResolvedTransport {
@@ -287,6 +342,9 @@ export function resolveMemberTransport(
287
342
  // optional-boolean field, so the key is omitted entirely rather than
288
343
  // forwarded as `apiKeyPresent: undefined`.
289
344
  ...(opts.apiKeyPresent !== undefined ? { apiKeyPresent: opts.apiKeyPresent } : {}),
345
+ policyExclusion: opts.policyExclusion ?? null,
346
+ seatCeiling: opts.seatCeiling ?? null,
347
+ contentClass: opts.contentClass ?? null,
290
348
  });
291
349
  return { ...resolved, configuredMode };
292
350
  }