@event4u/agent-config 8.11.0 → 8.13.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/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +64 -266
- package/README.md +2 -2
- package/dist/agent-src/commands/pr/create.md +26 -0
- package/dist/agent-src/commands/team/adversarial.md +96 -0
- package/dist/agent-src/commands/team/delegate.md +109 -0
- package/dist/agent-src/commands/team/review.md +84 -0
- package/dist/agent-src/commands/team/status.md +92 -0
- package/dist/agent-src/commands/team.md +147 -0
- package/dist/agent-src/contexts/execution/roadmap-ci-steps-mechanics.md +117 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +106 -94
- package/dist/agent-src/rules/architecture.md +6 -51
- package/dist/agent-src/rules/code-comment-discipline.md +10 -94
- package/dist/agent-src/rules/decision-revisit-gate.md +9 -77
- package/dist/agent-src/rules/improve-before-implement.md +8 -65
- package/dist/agent-src/rules/no-roadmap-references.md +13 -77
- package/dist/agent-src/rules/persona-governance.md +5 -47
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +6 -32
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +21 -104
- package/dist/agent-src/rules/untrusted-input-defense.md +3 -75
- package/dist/agent-src/skills/agent-docs-writing/SKILL.md +83 -0
- package/dist/agent-src/skills/decision-review/SKILL.md +86 -0
- package/dist/agent-src/skills/module-detect-on-the-fly/SKILL.md +58 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/contexts/knowledge-card.md +12 -0
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +159 -21
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +11 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +10 -5
- package/dist/install/install.mjs +38 -0
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/router.json +1 -1
- package/dist/server/schemas/settings.js +7 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/CLAIMS.md +31 -3
- package/docs/architecture.md +2 -2
- package/docs/archive/CHANGELOG-pre-8.12.0.md +304 -0
- package/docs/benchmark.md +39 -0
- package/docs/catalog.md +10 -4
- package/docs/command-flows.md +6 -1
- package/docs/contracts/ai-council-config.md +9 -4
- package/docs/contracts/ai-team-config.md +191 -0
- package/docs/contracts/command-clusters.md +1 -0
- package/docs/contracts/persona-schema.md +49 -0
- package/docs/contracts/provider-lifecycle.md +29 -4
- package/docs/decisions/ADR-120-council-chairman-mode.md +70 -0
- package/docs/decisions/ADR-121-knowledge-sensitivity-classes.md +90 -0
- package/docs/decisions/INDEX.md +2 -0
- package/docs/design/council-vs-solo-baseline.md +92 -0
- package/docs/design/utilization-window-criteria.md +51 -0
- package/docs/featured-skills.md +1 -1
- package/docs/getting-started-by-role.md +3 -1
- package/docs/getting-started.md +31 -1
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +68 -0
- package/docs/guidelines/agent-infra/rule-body-migration-inventory.md +241 -0
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +80 -3
- package/docs/guidelines/code-clarity.md +86 -1
- package/docs/maintainers/branch-protection.md +105 -0
- package/docs/maintainers/system-map.md +85 -0
- package/docs/proof.md +15 -4
- package/package.json +1 -1
- package/src/config/agent-settings.template.yml +49 -0
- package/src/scripts/_cli/cmd_doctor.ts +291 -1
- package/src/scripts/_lib/claude_plugin.ts +39 -4
- package/src/scripts/_lib/knowledge_global.ts +44 -0
- package/src/scripts/_lib/knowledge_global_promote.ts +150 -0
- package/src/scripts/ai_council/orchestrator.ts +188 -1
- package/src/scripts/ai_team/config.ts +162 -0
- package/src/scripts/bench_persona_placebo.ts +308 -0
- package/src/scripts/bench_quality_rerun.ts +357 -0
- package/src/scripts/build_proof.ts +9 -0
- package/src/scripts/check_artefact_count_messaging.ts +56 -0
- package/src/scripts/check_knowledge_cards.ts +33 -4
- package/src/scripts/check_portability.ts +1 -0
- package/src/scripts/complexity_report.ts +776 -0
- package/src/scripts/consumer_matrix.ts +251 -12
- package/src/scripts/council_cli.ts +56 -1
- package/src/scripts/explain_run.ts +593 -0
- package/src/scripts/install.ts +39 -0
- package/src/scripts/knowledge_global_cli.ts +234 -45
- package/src/scripts/rule_backlinks.ts +350 -0
- package/src/server/schemas/settings.ts +17 -0
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
#!/usr/bin/env tsx
|
|
2
|
+
/**
|
|
3
|
+
* Rule → skill/guideline backlink report (feedback-8.11 Phase 6).
|
|
4
|
+
*
|
|
5
|
+
* The P4 pattern moves rule bodies into skills/guidelines with the rule
|
|
6
|
+
* keeping a thin routing stub. The 2026-07-12 council REJECTED a
|
|
7
|
+
* `routed_from_rules` skill-frontmatter key (backwards coupling) — the
|
|
8
|
+
* linkage stays derivable from the RULE side only. This report is that
|
|
9
|
+
* derivation: per target (skill / guideline / context / contract), which
|
|
10
|
+
* rules route to it, so a skill author sees inbound routes without any
|
|
11
|
+
* schema cost.
|
|
12
|
+
*
|
|
13
|
+
* Sources per rule file (src/rules/*.md):
|
|
14
|
+
* 1. frontmatter `routes_to:` list items ("skill:x", "guideline:y", paths)
|
|
15
|
+
* 2. body prose `Body migrated to <target>` lines (backtick or bare
|
|
16
|
+
* `skill:x` / `guideline:y` tokens, and explicit context/contract paths)
|
|
17
|
+
*
|
|
18
|
+
* Correctness slice (council-adjudicated: correctness NOW, semantic metrics
|
|
19
|
+
* PARKED): every derived target is resolved against the tree — a stub
|
|
20
|
+
* pointing at a deleted target is a broken promise. Resolution map:
|
|
21
|
+
* skill:<id> → src/skills/<id>/SKILL.md
|
|
22
|
+
* guideline:<path> → docs/guidelines/<path>.md
|
|
23
|
+
* context:<path> → src/agent-src/contexts/<path>.md
|
|
24
|
+
* contract:<id> → docs/contracts/<id>.md OR
|
|
25
|
+
* src/agent-src/contexts/contracts/<id>.md
|
|
26
|
+
* contexts/<p>.md → src/agent-src/contexts/<p>.md
|
|
27
|
+
* docs/… · src/… → repo-root-relative path
|
|
28
|
+
* Anything else is reported honestly as "unknown-shape" — listed
|
|
29
|
+
* separately, never counted as an orphan.
|
|
30
|
+
*
|
|
31
|
+
* Modes:
|
|
32
|
+
* default — write internal/reports/rule-backlinks.md, ALWAYS exit 0
|
|
33
|
+
* (report-only; regenerate after each migration batch).
|
|
34
|
+
* --check — same report, exit 1 iff orphan stubs exist.
|
|
35
|
+
*
|
|
36
|
+
* Design notes — gated future work (round 4). Later-hard-gates list,
|
|
37
|
+
* verbatim: invalid backlinks, cyclic ownership, kernel-budget breach,
|
|
38
|
+
* new setting without profile default, new runtime-state surface without
|
|
39
|
+
* owner, stub without valid target. Precondition: only precise, non-proxy
|
|
40
|
+
* metrics may ever hard-gate. Cycles / competing-owners metrics are PARKED
|
|
41
|
+
* per the complexity report's kill-criterion discipline.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
import * as fs from 'node:fs';
|
|
45
|
+
import * as path from 'node:path';
|
|
46
|
+
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
47
|
+
|
|
48
|
+
const _HERE = fileURLToPath(import.meta.url);
|
|
49
|
+
export const ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
50
|
+
|
|
51
|
+
export interface Backlink {
|
|
52
|
+
target: string; // normalized target id, e.g. "skill:laravel" or a repo path
|
|
53
|
+
rule: string; // rule stem, e.g. "docker-commands"
|
|
54
|
+
source: 'frontmatter' | 'prose';
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type Resolution =
|
|
58
|
+
| { status: 'resolved'; path: string }
|
|
59
|
+
| { status: 'orphan'; tried: string[] }
|
|
60
|
+
| { status: 'unknown-shape' };
|
|
61
|
+
|
|
62
|
+
export interface OrphanEntry {
|
|
63
|
+
target: string;
|
|
64
|
+
rules: string[];
|
|
65
|
+
tried: string[]; // repo-relative candidate paths that were checked
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface UnknownShapeEntry {
|
|
69
|
+
target: string;
|
|
70
|
+
rules: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface ValidationResult {
|
|
74
|
+
orphans: OrphanEntry[];
|
|
75
|
+
unknownShapes: UnknownShapeEntry[];
|
|
76
|
+
resolvedCount: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
const _TARGET_TOKEN_RE = /(?:`)?((?:skill|guideline|context):[a-z0-9][a-z0-9/_.-]*)(?:`)?/gi;
|
|
80
|
+
const _PATH_TOKEN_RE = /`((?:contexts|docs|src)\/[a-zA-Z0-9/_.-]+\.md)`/g;
|
|
81
|
+
|
|
82
|
+
export function extract_backlinks(ruleStem: string, text: string): Backlink[] {
|
|
83
|
+
const out: Backlink[] = [];
|
|
84
|
+
const fmMatch = text.match(/^---[ \t]*\n([\s\S]*?)\n---[ \t]*\n/);
|
|
85
|
+
if (fmMatch !== null) {
|
|
86
|
+
const fm = fmMatch[1]!;
|
|
87
|
+
const rt = fm.match(/^routes_to:\s*\n((?:\s+-\s+.*\n?)+)/m);
|
|
88
|
+
if (rt !== null) {
|
|
89
|
+
for (const line of rt[1]!.split('\n')) {
|
|
90
|
+
const item = line.match(/^\s+-\s+["']?([^"'\s]+)["']?/);
|
|
91
|
+
if (item !== null) {
|
|
92
|
+
out.push({ target: item[1]!, rule: ruleStem, source: 'frontmatter' });
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
const body = fmMatch === null ? text : text.slice(fmMatch[0].length);
|
|
98
|
+
for (const line of body.split('\n')) {
|
|
99
|
+
if (!/body migrated to/i.test(line)) {
|
|
100
|
+
continue;
|
|
101
|
+
}
|
|
102
|
+
for (const m of line.matchAll(new RegExp(_TARGET_TOKEN_RE.source, 'gi'))) {
|
|
103
|
+
out.push({ target: m[1]!.toLowerCase(), rule: ruleStem, source: 'prose' });
|
|
104
|
+
}
|
|
105
|
+
for (const m of line.matchAll(new RegExp(_PATH_TOKEN_RE.source, 'g'))) {
|
|
106
|
+
out.push({ target: m[1]!, rule: ruleStem, source: 'prose' });
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
return out;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function collect(rulesDir: string): Map<string, Backlink[]> {
|
|
113
|
+
const byTarget = new Map<string, Backlink[]>();
|
|
114
|
+
if (!fs.existsSync(rulesDir)) {
|
|
115
|
+
return byTarget;
|
|
116
|
+
}
|
|
117
|
+
for (const f of fs.readdirSync(rulesDir).sort()) {
|
|
118
|
+
if (!f.endsWith('.md')) {
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
const stem = f.replace(/\.md$/, '');
|
|
122
|
+
const links = extract_backlinks(stem, fs.readFileSync(path.join(rulesDir, f), 'utf-8'));
|
|
123
|
+
const seen = new Set<string>();
|
|
124
|
+
for (const l of links) {
|
|
125
|
+
const key = `${l.target} ${l.rule}`;
|
|
126
|
+
if (seen.has(key)) {
|
|
127
|
+
continue; // frontmatter + prose naming the same target = one backlink
|
|
128
|
+
}
|
|
129
|
+
seen.add(key);
|
|
130
|
+
(byTarget.get(l.target) ?? byTarget.set(l.target, []).get(l.target)!).push(l);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return byTarget;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Map a derived target id to candidate repo-relative paths. Returns the
|
|
138
|
+
* candidate list for a known shape, or null when the shape is not one we
|
|
139
|
+
* know how to place (→ "unknown-shape", honestly listed, never an orphan).
|
|
140
|
+
*/
|
|
141
|
+
export function candidate_paths(target: string): string[] | null {
|
|
142
|
+
const skill = target.match(/^skill:([a-z0-9][a-z0-9_.-]*)$/i);
|
|
143
|
+
if (skill !== null) {
|
|
144
|
+
return [path.join('src', 'skills', skill[1]!, 'SKILL.md')];
|
|
145
|
+
}
|
|
146
|
+
const guideline = target.match(/^guideline:([a-z0-9][a-z0-9/_.-]*)$/i);
|
|
147
|
+
if (guideline !== null) {
|
|
148
|
+
return [path.join('docs', 'guidelines', `${guideline[1]!.replace(/\.md$/, '')}.md`)];
|
|
149
|
+
}
|
|
150
|
+
const context = target.match(/^context:([a-z0-9][a-z0-9/_.-]*)$/i);
|
|
151
|
+
if (context !== null) {
|
|
152
|
+
return [path.join('src', 'agent-src', 'contexts', `${context[1]!.replace(/\.md$/, '')}.md`)];
|
|
153
|
+
}
|
|
154
|
+
const contract = target.match(/^contract:([a-z0-9][a-z0-9_.-]*)$/i);
|
|
155
|
+
if (contract !== null) {
|
|
156
|
+
const id = contract[1]!.replace(/\.md$/, '');
|
|
157
|
+
return [
|
|
158
|
+
path.join('docs', 'contracts', `${id}.md`),
|
|
159
|
+
path.join('src', 'agent-src', 'contexts', 'contracts', `${id}.md`),
|
|
160
|
+
];
|
|
161
|
+
}
|
|
162
|
+
if (/^contexts\/[a-zA-Z0-9/_.-]+\.md$/.test(target)) {
|
|
163
|
+
return [path.join('src', 'agent-src', target)];
|
|
164
|
+
}
|
|
165
|
+
if (/^(?:docs|src)\/[a-zA-Z0-9/_.-]+\.md$/.test(target)) {
|
|
166
|
+
return [target];
|
|
167
|
+
}
|
|
168
|
+
return null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function resolve_target(target: string, root: string = ROOT): Resolution {
|
|
172
|
+
const candidates = candidate_paths(target);
|
|
173
|
+
if (candidates === null) {
|
|
174
|
+
return { status: 'unknown-shape' };
|
|
175
|
+
}
|
|
176
|
+
for (const rel of candidates) {
|
|
177
|
+
if (fs.existsSync(path.join(root, rel))) {
|
|
178
|
+
return { status: 'resolved', path: rel };
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
return { status: 'orphan', tried: candidates };
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
export function validate_targets(
|
|
185
|
+
byTarget: Map<string, Backlink[]>,
|
|
186
|
+
root: string = ROOT,
|
|
187
|
+
): ValidationResult {
|
|
188
|
+
const orphans: OrphanEntry[] = [];
|
|
189
|
+
const unknownShapes: UnknownShapeEntry[] = [];
|
|
190
|
+
let resolvedCount = 0;
|
|
191
|
+
for (const target of [...byTarget.keys()].sort()) {
|
|
192
|
+
const rules = [...new Set(byTarget.get(target)!.map((l) => l.rule))].sort();
|
|
193
|
+
const res = resolve_target(target, root);
|
|
194
|
+
if (res.status === 'resolved') {
|
|
195
|
+
resolvedCount += 1;
|
|
196
|
+
} else if (res.status === 'orphan') {
|
|
197
|
+
orphans.push({ target, rules, tried: res.tried });
|
|
198
|
+
} else {
|
|
199
|
+
unknownShapes.push({ target, rules });
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
return { orphans, unknownShapes, resolvedCount };
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
interface FanOut {
|
|
206
|
+
perRule: Map<string, number>; // rule stem → distinct backlink targets
|
|
207
|
+
max: number;
|
|
208
|
+
highFanOut: { rule: string; targets: number }[]; // >2 targets — info, NOT failures
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function _fan_out(byTarget: Map<string, Backlink[]>): FanOut {
|
|
212
|
+
const perRule = new Map<string, number>();
|
|
213
|
+
for (const links of byTarget.values()) {
|
|
214
|
+
for (const l of links) {
|
|
215
|
+
perRule.set(l.rule, (perRule.get(l.rule) ?? 0) + 1);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const max = perRule.size === 0 ? 0 : Math.max(...perRule.values());
|
|
219
|
+
const highFanOut = [...perRule.entries()]
|
|
220
|
+
.filter(([, n]) => n > 2)
|
|
221
|
+
.map(([rule, targets]) => ({ rule, targets }))
|
|
222
|
+
.sort((a, b) => b.targets - a.targets || a.rule.localeCompare(b.rule));
|
|
223
|
+
return { perRule, max, highFanOut };
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
export function render(
|
|
227
|
+
byTarget: Map<string, Backlink[]>,
|
|
228
|
+
validation: ValidationResult = validate_targets(byTarget),
|
|
229
|
+
): string {
|
|
230
|
+
const L: string[] = [];
|
|
231
|
+
L.push('<!-- GENERATED by src/scripts/rule_backlinks.ts — do NOT hand-edit.');
|
|
232
|
+
L.push(' Regenerate after each P4 migration batch. Report-only. -->');
|
|
233
|
+
L.push('');
|
|
234
|
+
L.push('# Rule backlinks — which rules route here');
|
|
235
|
+
L.push('');
|
|
236
|
+
L.push('Derived from rule frontmatter `routes_to:` + "Body migrated to" prose.');
|
|
237
|
+
L.push('No skill frontmatter carries this (council 2026-07-12: routed_from_rules');
|
|
238
|
+
L.push('REJECTED — linkage stays rule-side and derivable).');
|
|
239
|
+
L.push('');
|
|
240
|
+
const targets = [...byTarget.keys()].sort();
|
|
241
|
+
L.push(`${targets.length} routing targets · ${[...byTarget.values()].reduce((a, v) => a + v.length, 0)} backlinks`);
|
|
242
|
+
L.push('');
|
|
243
|
+
L.push('## Orphan stubs');
|
|
244
|
+
L.push('');
|
|
245
|
+
L.push('A stub pointing at a deleted target is a broken promise. `--check` exits 1');
|
|
246
|
+
L.push('iff this section is non-empty; report mode always exits 0.');
|
|
247
|
+
L.push('');
|
|
248
|
+
if (validation.orphans.length === 0) {
|
|
249
|
+
L.push(`None — all ${validation.resolvedCount} resolvable targets resolve.`);
|
|
250
|
+
L.push('');
|
|
251
|
+
} else {
|
|
252
|
+
L.push('| Target | Routing rules | Candidate paths checked |');
|
|
253
|
+
L.push('|---|---|---|');
|
|
254
|
+
for (const o of validation.orphans) {
|
|
255
|
+
L.push(
|
|
256
|
+
`| \`${o.target}\` | ${o.rules.map((r) => `\`${r}\``).join(', ')} | ${o.tried.map((t) => `\`${t}\``).join(', ')} |`,
|
|
257
|
+
);
|
|
258
|
+
}
|
|
259
|
+
L.push('');
|
|
260
|
+
}
|
|
261
|
+
if (validation.unknownShapes.length > 0) {
|
|
262
|
+
L.push('## Unknown-shape targets');
|
|
263
|
+
L.push('');
|
|
264
|
+
L.push('Shapes the resolution map cannot place — listed honestly, NOT counted');
|
|
265
|
+
L.push('as orphans. Extend `candidate_paths()` when a shape becomes real.');
|
|
266
|
+
L.push('');
|
|
267
|
+
for (const u of validation.unknownShapes) {
|
|
268
|
+
L.push(`- \`${u.target}\` — ${u.rules.map((r) => `\`${r}\``).join(', ')}`);
|
|
269
|
+
}
|
|
270
|
+
L.push('');
|
|
271
|
+
}
|
|
272
|
+
const fanOut = _fan_out(byTarget);
|
|
273
|
+
L.push('## Fan-out (info)');
|
|
274
|
+
L.push('');
|
|
275
|
+
L.push(`Backlink targets per rule. ${fanOut.perRule.size} routing rules · max fan-out ${fanOut.max}.`);
|
|
276
|
+
L.push('Rules with >2 targets are listed as info, NOT failures.');
|
|
277
|
+
L.push('');
|
|
278
|
+
if (fanOut.highFanOut.length === 0) {
|
|
279
|
+
L.push('No rule routes to more than 2 targets.');
|
|
280
|
+
L.push('');
|
|
281
|
+
} else {
|
|
282
|
+
L.push('| Rule | Targets |');
|
|
283
|
+
L.push('|---|---|');
|
|
284
|
+
for (const h of fanOut.highFanOut) {
|
|
285
|
+
L.push(`| \`${h.rule}\` | ${h.targets} |`);
|
|
286
|
+
}
|
|
287
|
+
L.push('');
|
|
288
|
+
}
|
|
289
|
+
for (const t of targets) {
|
|
290
|
+
L.push(`## ${t}`);
|
|
291
|
+
L.push('');
|
|
292
|
+
for (const l of byTarget.get(t)!.sort((a, b) => a.rule.localeCompare(b.rule))) {
|
|
293
|
+
L.push(`- \`${l.rule}\` (${l.source})`);
|
|
294
|
+
}
|
|
295
|
+
L.push('');
|
|
296
|
+
}
|
|
297
|
+
L.push('## Design notes — gated future work');
|
|
298
|
+
L.push('');
|
|
299
|
+
L.push('Round-4 later-hard-gates list, verbatim: invalid backlinks, cyclic');
|
|
300
|
+
L.push('ownership, kernel-budget breach, new setting without profile default,');
|
|
301
|
+
L.push('new runtime-state surface without owner, stub without valid target.');
|
|
302
|
+
L.push('Precondition: only precise, non-proxy metrics may ever hard-gate.');
|
|
303
|
+
L.push("Cycles / competing-owners metrics are PARKED per the complexity report's");
|
|
304
|
+
L.push('kill-criterion discipline.');
|
|
305
|
+
L.push('');
|
|
306
|
+
return `${L.join('\n')}\n`;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
export function main(argv: readonly string[] = process.argv.slice(2), root: string = ROOT): number {
|
|
310
|
+
const rulesDir = path.join(root, 'src', 'rules');
|
|
311
|
+
const outPath = path.join(root, 'internal', 'reports', 'rule-backlinks.md');
|
|
312
|
+
const byTarget = collect(rulesDir);
|
|
313
|
+
const validation = validate_targets(byTarget, root);
|
|
314
|
+
fs.mkdirSync(path.dirname(outPath), { recursive: true });
|
|
315
|
+
fs.writeFileSync(outPath, render(byTarget, validation));
|
|
316
|
+
if (!argv.includes('--quiet')) {
|
|
317
|
+
process.stdout.write(
|
|
318
|
+
`rule_backlinks: wrote ${path.relative(root, outPath)} — ${byTarget.size} targets, ` +
|
|
319
|
+
`${validation.orphans.length} orphans, ${validation.unknownShapes.length} unknown-shape\n`,
|
|
320
|
+
);
|
|
321
|
+
for (const o of validation.orphans) {
|
|
322
|
+
process.stdout.write(` ORPHAN: ${o.target} ← ${o.rules.join(', ')}\n`);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
if (argv.includes('--check') && validation.orphans.length > 0) {
|
|
326
|
+
return 1;
|
|
327
|
+
}
|
|
328
|
+
return 0;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function _isCliEntry(): boolean {
|
|
332
|
+
if (process.argv[1] === undefined) {
|
|
333
|
+
return false;
|
|
334
|
+
}
|
|
335
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
336
|
+
if (import.meta.url === argvUrl) {
|
|
337
|
+
return true;
|
|
338
|
+
}
|
|
339
|
+
try {
|
|
340
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
341
|
+
const argv1 = fs.realpathSync(path.resolve(process.argv[1]));
|
|
342
|
+
return here === argv1;
|
|
343
|
+
} catch {
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (_isCliEntry() || process.argv[1] === _HERE) {
|
|
349
|
+
process.exit(main(process.argv.slice(2)));
|
|
350
|
+
}
|
|
@@ -288,6 +288,23 @@ export const settingsSchema = z.object({
|
|
|
288
288
|
'When the agent considers a parallel `git worktree` for risky / large work. ask (default) = surface a numbered option and wait. on = spawn worktrees autonomously. off = never use worktrees, edit in place.',
|
|
289
289
|
),
|
|
290
290
|
}),
|
|
291
|
+
ai_team: z.object({
|
|
292
|
+
enabled: z.boolean().default(false).describe(
|
|
293
|
+
'Master switch for the /team cross-model review family (docs/contracts/ai-team-config.md). false (default) = commands are never suggested and every invocation refuses with an enable pointer. true = the family is live; individual gates (allow_delegate) still apply.',
|
|
294
|
+
),
|
|
295
|
+
model: z.string().default('auto').describe(
|
|
296
|
+
"Model handed to the codex CLI. 'auto' (default) = pass no --model flag so the CLI's own default applies — tracks the subscription's current strongest model instead of pinning a stale ID. Any other value passes through verbatim as `--model <value>`.",
|
|
297
|
+
),
|
|
298
|
+
allow_delegate: z.boolean().default(false).describe(
|
|
299
|
+
'Second opt-in for the only wrapper that delegates write access (/team:delegate). false (default) = delegate refuses even when ai_team.enabled is true. Both keys must be true before delegation is reachable.',
|
|
300
|
+
),
|
|
301
|
+
max_calls_per_day: z.number().int().min(0).default(50).describe(
|
|
302
|
+
'Per-day cap on team calls, read against the EXISTING cli_call_budget openai bucket (~/.event4u/agent-config/cli-calls.json, daily UTC reset) — one subscription, one counter, never a parallel count. 0 = block all team calls.',
|
|
303
|
+
),
|
|
304
|
+
suppress_setup_hint: z.boolean().default(false).describe(
|
|
305
|
+
'Suppress the one-line wizard/init recommendation to set up the codex plugin on Claude-Code hosts. Cosmetic only — never changes behavior.',
|
|
306
|
+
),
|
|
307
|
+
}).default({ enabled: false, model: 'auto', allow_delegate: false, max_calls_per_day: 50, suppress_setup_hint: false }),
|
|
291
308
|
onboarding: z.object({
|
|
292
309
|
onboarded: z.boolean().default(false).describe(
|
|
293
310
|
'Set to true once the developer has completed `agent-config setup`. The onboarding-gate rule blocks the first turn of every chat until this is true. Toggle back to false to re-trigger the wizard.',
|