@event4u/agent-config 9.26.0 → 9.28.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 +12 -11
- package/CHANGELOG.md +134 -207
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/conformance.md +43 -0
- package/dist/agent-src/commands/roadmap/next.md +65 -3
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +10 -3
- package/dist/agent-src/rules/active-remediation.md +2 -0
- package/dist/agent-src/rules/analysis-skill-routing.md +2 -0
- package/dist/agent-src/rules/architecture.md +2 -0
- package/dist/agent-src/rules/artifact-drafting-protocol.md +2 -0
- package/dist/agent-src/rules/artifact-engagement-recording.md +6 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +2 -0
- package/dist/agent-src/rules/autonomous-execution.md +3 -1
- package/dist/agent-src/rules/brand-consistency.md +2 -0
- package/dist/agent-src/rules/brand-source-of-truth.md +35 -6
- package/dist/agent-src/rules/broken-access-control.md +2 -0
- package/dist/agent-src/rules/cli-output-handling.md +2 -0
- package/dist/agent-src/rules/code-comment-discipline.md +2 -0
- package/dist/agent-src/rules/code-provenance.md +2 -0
- package/dist/agent-src/rules/command-suggestion-policy.md +2 -0
- package/dist/agent-src/rules/commit-conventions.md +2 -0
- package/dist/agent-src/rules/communication-through-line.md +2 -0
- package/dist/agent-src/rules/content-quoting-floor.md +2 -0
- package/dist/agent-src/rules/context-hygiene.md +13 -1
- package/dist/agent-src/rules/copilot-routing.md +2 -0
- package/dist/agent-src/rules/council-availability.md +93 -0
- package/dist/agent-src/rules/cross-source-consistency.md +5 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +2 -0
- package/dist/agent-src/rules/delegation-policy.md +31 -5
- package/dist/agent-src/rules/design-fidelity.md +5 -0
- package/dist/agent-src/rules/design-review-after-ui-write.md +4 -0
- package/dist/agent-src/rules/devcontainer-routing.md +2 -0
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +2 -0
- package/dist/agent-src/rules/docker-commands.md +2 -0
- package/dist/agent-src/rules/domain-adoption-policy.md +2 -0
- package/dist/agent-src/rules/domain-safety-disclaimer.md +2 -0
- package/dist/agent-src/rules/domain-safety-pii.md +2 -0
- package/dist/agent-src/rules/domain-safety-retention.md +2 -0
- package/dist/agent-src/rules/downstream-changes.md +2 -0
- package/dist/agent-src/rules/engineering-safety-floor.md +2 -0
- package/dist/agent-src/rules/evaluator-independence.md +14 -3
- package/dist/agent-src/rules/external-code-graph-interop.md +4 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -0
- package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -0
- package/dist/agent-src/rules/finance-safety-floor.md +3 -1
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +2 -0
- package/dist/agent-src/rules/git-history-discipline.md +3 -1
- package/dist/agent-src/rules/guidelines.md +2 -0
- package/dist/agent-src/rules/history-discipline.md +5 -1
- package/dist/agent-src/rules/icon-consistency.md +2 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +2 -0
- package/dist/agent-src/rules/improve-before-implement.md +2 -0
- package/dist/agent-src/rules/invite-challenge.md +2 -0
- package/dist/agent-src/rules/laravel-routing.md +2 -0
- package/dist/agent-src/rules/laravel-translations.md +2 -0
- package/dist/agent-src/rules/legal-safety-floor.md +3 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +2 -0
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +2 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +2 -0
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +2 -0
- package/dist/agent-src/rules/media-governance-routing.md +2 -0
- package/dist/agent-src/rules/media-sync-ground-truth.md +2 -0
- package/dist/agent-src/rules/minimal-safe-diff.md +2 -0
- package/dist/agent-src/rules/missing-tool-handling.md +2 -0
- package/dist/agent-src/rules/model-recommendation.md +2 -0
- package/dist/agent-src/rules/no-attribution-footers.md +2 -0
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +2 -0
- package/dist/agent-src/rules/no-pr-progress-comments.md +5 -1
- package/dist/agent-src/rules/no-roadmap-references.md +2 -0
- package/dist/agent-src/rules/notes-first-reasoning.md +2 -0
- package/dist/agent-src/rules/onboarding-gate.md +5 -1
- package/dist/agent-src/rules/output-discipline.md +2 -0
- package/dist/agent-src/rules/package-ci-checks.md +2 -0
- package/dist/agent-src/rules/persona-governance.md +2 -0
- package/dist/agent-src/rules/php-coding.md +2 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +2 -0
- package/dist/agent-src/rules/preservation-guard.md +2 -0
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +2 -0
- package/dist/agent-src/rules/question-not-instruction.md +2 -0
- package/dist/agent-src/rules/reviewer-awareness.md +2 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +6 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +2 -0
- package/dist/agent-src/rules/role-mode-adherence.md +3 -1
- package/dist/agent-src/rules/rule-type-governance.md +2 -0
- package/dist/agent-src/rules/runtime-safety.md +2 -0
- package/dist/agent-src/rules/scale-discipline.md +5 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +2 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +2 -0
- package/dist/agent-src/rules/self-repair-loop.md +103 -0
- package/dist/agent-src/rules/senior-engineering-discipline.md +2 -0
- package/dist/agent-src/rules/session-canary.md +62 -19
- package/dist/agent-src/rules/settings-ask-protocol.md +4 -0
- package/dist/agent-src/rules/size-enforcement.md +2 -0
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -0
- package/dist/agent-src/rules/skill-quality.md +2 -0
- package/dist/agent-src/rules/slash-command-routing-policy.md +2 -0
- package/dist/agent-src/rules/source-confidentiality.md +2 -0
- package/dist/agent-src/rules/source-discovery-gate.md +2 -0
- package/dist/agent-src/rules/source-of-truth.md +2 -0
- package/dist/agent-src/rules/spreadsheet-source-quality.md +2 -0
- package/dist/agent-src/rules/strategy-safety-floor.md +3 -1
- package/dist/agent-src/rules/symfony-routing.md +2 -0
- package/dist/agent-src/rules/think-before-action.md +2 -0
- package/dist/agent-src/rules/token-budget-discipline.md +5 -1
- package/dist/agent-src/rules/token-efficiency.md +2 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +2 -0
- package/dist/agent-src/rules/tool-safety.md +7 -1
- package/dist/agent-src/rules/ui-audit-gate.md +4 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
- package/dist/agent-src/rules/upstream-proposal.md +2 -0
- package/dist/agent-src/rules/user-interaction.md +2 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +2 -0
- package/dist/agent-src/skills/adversarial-review/SKILL.md +1 -1
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/performance-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-improvement/SKILL.md +1 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/sequential-thinking/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/templates/AGENTS.md +6 -10
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +9 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-22GCIJ2O.js +270 -0
- package/dist/cli-delegate/{chunk-BSLUHV2F.js → chunk-2BE5I2MN.js} +156 -141
- package/dist/cli-delegate/{chunk-AIIUU73N.js → chunk-7OMQCH4V.js} +1 -1
- package/dist/cli-delegate/{chunk-HJ6DAJHR.js → chunk-GB2FSUEE.js} +57 -7
- package/dist/cli-delegate/{chunk-WR6GTRHJ.js → chunk-JXARCGVN.js} +3 -3
- package/dist/cli-delegate/{chunk-SJQMJI7M.js → chunk-PQSAQKHW.js} +467 -305
- package/dist/cli-delegate/chunk-REKCMYOU.js +2991 -0
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +134 -0
- package/dist/cli-delegate/cmd_conformance.js +7 -7
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +4 -4
- package/dist/cli-delegate/cmd_explain.js +80 -330
- package/dist/cli-delegate/cmd_export.js +5 -5
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_mcp_available.js +433 -0
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +153 -0
- package/dist/cli-delegate/cmd_preflight.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +5 -5
- package/dist/cli-delegate/cmd_route_audit.js +8 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +272 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +5 -5
- package/dist/cli-delegate/cmd_settings_set.js +7 -2967
- package/dist/cli-delegate/cmd_sync.js +5 -5
- package/dist/cli-delegate/cmd_uninstall.js +5 -5
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +5 -5
- package/dist/cli-delegate/cmd_versions.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +184 -124
- 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 +7 -5
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +6 -4
- package/dist/hooks/dispatch.js +9080 -5701
- package/dist/install/install.mjs +698 -508
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +698 -437
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +80 -15
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/condense.js +33 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/io/yamlIO.js +85 -0
- package/dist/server/io/yamlIO.js.map +1 -1
- package/dist/server/routes/settings.js +1 -12
- package/dist/server/routes/settings.js.map +1 -1
- package/dist/server/routes/wizard.js +52 -2
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/shared/settingsCarveOut.js +118 -0
- package/dist/shared/settingsCarveOut.js.map +1 -0
- package/dist/shared/settingsSurface.js +7 -1
- package/dist/shared/settingsSurface.js.map +1 -1
- package/dist/ui/assets/index-DmR0mbk4.js.map +1 -1
- package/docs/contracts/settings-classes.md +29 -8
- package/docs/guidelines/agent-infra/memory-access.md +14 -3
- package/package.json +1 -1
- package/src/agent-src/templates/AGENTS.md +6 -10
- package/src/config/budgets.yml +29 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +26 -0
- package/src/scripts/_cli/cmd_brand_status.ts +174 -0
- package/src/scripts/_cli/cmd_doctor.ts +6 -21
- package/src/scripts/_cli/cmd_mcp_available.ts +266 -0
- package/src/scripts/_cli/cmd_packs_active.ts +225 -0
- package/src/scripts/_cli/cmd_settings_get.ts +324 -0
- package/src/scripts/_dispatch.bash +75 -0
- package/src/scripts/_lib/agent_settings.ts +82 -16
- package/src/scripts/_lib/carrier_divergence.ts +135 -0
- package/src/scripts/_lib/git_common_dir.ts +128 -0
- package/src/scripts/_lib/host_capability.ts +84 -1
- package/src/scripts/_lib/obligation_frequency.ts +391 -0
- package/src/scripts/_lib/prompt_shape.ts +88 -0
- package/src/scripts/_lib/review_skipped_record.ts +102 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +254 -0
- package/src/scripts/_lib/self_repair.ts +342 -0
- package/src/scripts/_lib/self_repair_store.ts +100 -0
- package/src/scripts/_lib/session_register.ts +361 -0
- package/src/scripts/_lib/transcript_entry.ts +80 -0
- package/src/scripts/check_branch_freshness.ts +255 -0
- package/src/scripts/check_council_config_location.ts +125 -1
- package/src/scripts/check_enforcement_coverage.ts +192 -7
- package/src/scripts/check_rule_projection_integrity.ts +364 -0
- package/src/scripts/check_standing_rule_delivery.ts +314 -0
- package/src/scripts/condense.ts +36 -2
- package/src/scripts/config/profiles.ts +13 -3
- package/src/scripts/conformance_scan.ts +307 -24
- package/src/scripts/council_availability_hook.ts +188 -0
- package/src/scripts/council_cli.ts +126 -7
- package/src/scripts/generate_settings_reference.ts +232 -0
- package/src/scripts/hook_manifest.yaml +155 -33
- package/src/scripts/hooks/block_no_verify.ts +95 -3
- package/src/scripts/hooks/block_unauthorized_git.ts +217 -2
- package/src/scripts/hooks/concern_registry.ts +12 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +450 -0
- package/src/scripts/hooks/end_review_nudge_hook.ts +726 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +342 -0
- package/src/scripts/install.ts +151 -0
- package/src/scripts/language_mirror_hook.ts +49 -6
- package/src/scripts/measure_scope_dedup.ts +19 -13
- package/src/scripts/onboarding_gate_hook.ts +40 -1
- package/src/scripts/orchestration_backfill.ts +354 -0
- package/src/scripts/orchestration_record.ts +4 -1
- package/src/scripts/preamble_byte_census.ts +9 -20
- package/src/scripts/release.ts +142 -36
- package/src/scripts/report_carrier_divergence.ts +364 -0
- package/src/scripts/report_skill_activation.ts +257 -0
- package/src/scripts/report_skill_obligation_violations.ts +579 -0
- package/src/scripts/routing_doctor.ts +77 -6
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/self_repair_cli.ts +240 -0
- package/src/scripts/self_repair_hook.ts +241 -0
- package/src/scripts/session_canary_hook.ts +35 -3
- package/src/scripts/session_register_hook.ts +288 -0
- package/src/scripts/sessions_cli.ts +140 -0
- package/src/scripts/validate_frontmatter.ts +68 -0
- package/src/server/io/yamlIO.ts +87 -0
- package/src/shared/settingsCarveOut.ts +143 -0
- package/src/shared/settingsSurface.ts +7 -1
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One definition of "these two carriers deliver the same rule" — shared by the
|
|
3
|
+
* C-3 reachability harness and the advisory divergence report.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS A LIBRARY AND NOT TWO COPIES. Two surfaces need the comparison
|
|
6
|
+
* and they ask different questions of it. `measure_scope_dedup` asks *how many*
|
|
7
|
+
* installed twins a byte-identity dedup could skip, anchored on the projection
|
|
8
|
+
* source. `report_carrier_divergence` asks *which* rules a reader must look at,
|
|
9
|
+
* anchored on the union of both carriers. The directory walks are legitimately
|
|
10
|
+
* different; the comparison is not, and a second copy of it is how "differs only
|
|
11
|
+
* in the ownership stamp" and "differs in body" drift into meaning two things.
|
|
12
|
+
*
|
|
13
|
+
* The comparison itself is the load-bearing part, so it is stated once:
|
|
14
|
+
*
|
|
15
|
+
* 1. bytes equal → `identical`
|
|
16
|
+
* 2. equal after removing the two
|
|
17
|
+
* installer ownership keys → `provenance-only`
|
|
18
|
+
* 3. otherwise → `body-diff`
|
|
19
|
+
*
|
|
20
|
+
* Class 2 exists because `install.ts` stamps `package:` and `source_path:` into
|
|
21
|
+
* every rule it installs while the in-repo projection stamps nothing, so a
|
|
22
|
+
* provenance-only difference survives even a perfect version alignment. Keeping
|
|
23
|
+
* it apart from class 3 is what turns a bare "0 identical twins" into an answer:
|
|
24
|
+
* `provenanceOnly == shared` means aligning versions would buy nothing, and
|
|
25
|
+
* `bodyDiff > 0` means the two carriers are delivering different text — the only
|
|
26
|
+
* class where a reader has to act.
|
|
27
|
+
*
|
|
28
|
+
* NOT A LICENCE TO DEDUP. Classifying a pair `provenance-only` says the bodies
|
|
29
|
+
* match; it does NOT say the rule is safe to drop from one carrier. Byte-identity
|
|
30
|
+
* remains the dedup predicate, deliberately — see
|
|
31
|
+
* `agents/settings/contexts/dedup-reachability-refusal.md`, which records that
|
|
32
|
+
* decision and its five reopen conditions. Nothing here relaxes it.
|
|
33
|
+
*/
|
|
34
|
+
import * as fs from 'node:fs';
|
|
35
|
+
import * as path from 'node:path';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The keys `install.ts` adds to every installed rule (`_set_key(fm_lines,
|
|
39
|
+
* 'package', …)` and `source_path` at install.ts:2733). Removing them is what
|
|
40
|
+
* separates an installer stamp from a content change.
|
|
41
|
+
*/
|
|
42
|
+
export const OWNERSHIP_KEYS = ['package', 'source_path'] as const;
|
|
43
|
+
|
|
44
|
+
export type PairVerdict = 'identical' | 'provenance-only' | 'body-diff';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Drop the ownership stamp lines, wherever they appear in the FILE.
|
|
48
|
+
*
|
|
49
|
+
* Not scoped to the frontmatter, and the docstring says so because the scope is
|
|
50
|
+
* wider than the intent. Two reasons it stays this way: `install.ts` writes the
|
|
51
|
+
* keys without any guarantee about fence position, and `scope_dedup.test.ts`
|
|
52
|
+
* deliberately pins the fence-less shape (a stamp with no `---` around it) as the
|
|
53
|
+
* install condition. Residual risk, stated rather than hidden: a rule body that
|
|
54
|
+
* contains a line literally starting `package:` or `source_path:` — a YAML
|
|
55
|
+
* example, an indent-free config snippet — has that line removed from both sides,
|
|
56
|
+
* so a genuine difference confined to such a line would read `provenance-only`
|
|
57
|
+
* instead of `body-diff`. Measured over the shipped corpus: no rule body carries
|
|
58
|
+
* one. Re-check that before treating the class as impossible rather than absent.
|
|
59
|
+
*/
|
|
60
|
+
export function stripOwnershipKeys(text: string): string {
|
|
61
|
+
return text
|
|
62
|
+
.split('\n')
|
|
63
|
+
.filter((line) => !OWNERSHIP_KEYS.some((k) => line.startsWith(`${k}:`)))
|
|
64
|
+
.join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Compare one rule's two copies.
|
|
69
|
+
*
|
|
70
|
+
* Takes buffers rather than paths: the callers already hold them, and a pure
|
|
71
|
+
* function is what lets both surfaces pin the same three-way split in tests
|
|
72
|
+
* without a filesystem.
|
|
73
|
+
*/
|
|
74
|
+
export function comparePair(a: Buffer, b: Buffer): PairVerdict {
|
|
75
|
+
if (a.equals(b)) {
|
|
76
|
+
return 'identical';
|
|
77
|
+
}
|
|
78
|
+
if (stripOwnershipKeys(a.toString('utf-8')) === stripOwnershipKeys(b.toString('utf-8'))) {
|
|
79
|
+
return 'provenance-only';
|
|
80
|
+
}
|
|
81
|
+
return 'body-diff';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RuleDirCensus {
|
|
85
|
+
files: number;
|
|
86
|
+
/**
|
|
87
|
+
* BYTES, not characters — `statSync().size`. The name is kept because every
|
|
88
|
+
* consumer of this figure (`preamble_byte_census`, `measure_scope_dedup`, the
|
|
89
|
+
* conformance scan) already divides it by 4 under that name, and one honest
|
|
90
|
+
* docstring beats a rename that would silently move three published baselines.
|
|
91
|
+
*
|
|
92
|
+
* The gap is measured, not assumed: over the 110-rule project tree, 406 502
|
|
93
|
+
* bytes against 402 823 characters — **0.91 %** overhead from the em-dashes,
|
|
94
|
+
* arrows and status glyphs the prose uses. So the published token estimate
|
|
95
|
+
* reads 101 626 where a character basis gives 100 706. Under 1 % and in the
|
|
96
|
+
* conservative direction for a payload figure, which is why it is documented
|
|
97
|
+
* rather than repaired; re-measure before quoting it as negligible on a corpus
|
|
98
|
+
* with more non-ASCII than this one.
|
|
99
|
+
*/
|
|
100
|
+
chars: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* How much rule text one carrier delivers.
|
|
105
|
+
*
|
|
106
|
+
* `statSync`, not `lstatSync`, on purpose: a project-scope entry is a symlink
|
|
107
|
+
* into `dist/`, and the bytes the HOST reads are the target's. Measuring the link
|
|
108
|
+
* would report ~50 bytes per rule and make the whole payload vanish.
|
|
109
|
+
*
|
|
110
|
+
* Moved here from `preamble_byte_census` when `conformance_scan` needed it:
|
|
111
|
+
* that module pulls `yaml` and the cold-start report, and importing a dependency
|
|
112
|
+
* chain into a bundled CLI path to reach fifteen lines of `statSync` is the wrong
|
|
113
|
+
* trade. `preamble_byte_census` re-exports this so its own callers are unchanged
|
|
114
|
+
* and there is still exactly one definition.
|
|
115
|
+
*/
|
|
116
|
+
export function censusRuleDir(dir: string): RuleDirCensus {
|
|
117
|
+
let files = 0;
|
|
118
|
+
let chars = 0;
|
|
119
|
+
let entries: string[];
|
|
120
|
+
try {
|
|
121
|
+
entries = fs.readdirSync(dir);
|
|
122
|
+
} catch {
|
|
123
|
+
return { files: 0, chars: 0 };
|
|
124
|
+
}
|
|
125
|
+
for (const name of entries) {
|
|
126
|
+
if (!name.endsWith('.md')) continue;
|
|
127
|
+
try {
|
|
128
|
+
chars += fs.statSync(path.join(dir, name)).size;
|
|
129
|
+
files += 1;
|
|
130
|
+
} catch {
|
|
131
|
+
// Unreadable entry — skip rather than fail the whole census.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { files, chars };
|
|
135
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resolve a repository's **common git directory** — the one directory every
|
|
3
|
+
* worktree of a repo shares.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `_cli/cmd_doctor.ts::_git_config_path`, which has resolved the
|
|
6
|
+
* `commondir` file this way since the git-identity check shipped. It is
|
|
7
|
+
* extracted rather than re-implemented on purpose: a repo with two different
|
|
8
|
+
* answers to "where is the common dir" is a bug waiting for a symlinked parent,
|
|
9
|
+
* and the worktree-cleanup work already hit that class once.
|
|
10
|
+
*
|
|
11
|
+
* ## Why this reads files instead of shelling out to git
|
|
12
|
+
*
|
|
13
|
+
* `git rev-parse --git-common-dir` is the obvious implementation and is wrong
|
|
14
|
+
* for the callers here, twice over:
|
|
15
|
+
*
|
|
16
|
+
* 1. **Inherited `GIT_DIR` overrides discovery.** Git hooks export `GIT_DIR`,
|
|
17
|
+
* and every child inherits it, so `execFileSync('git', …, { cwd })` silently
|
|
18
|
+
* resolves against the *hook's* repository rather than `cwd` — in a linked
|
|
19
|
+
* worktree those differ, and the symptom is wrong data, not an error. That
|
|
20
|
+
* is exactly what `_lib/git_env.ts` exists to strip. A file-based resolution
|
|
21
|
+
* has nothing to override and needs no such guard.
|
|
22
|
+
* 2. **The raw output is not comparable across checkouts.** Measured
|
|
23
|
+
* 2026-08-07: from a main checkout `git rev-parse --git-common-dir` prints
|
|
24
|
+
* the **relative** `.git`; from a linked worktree it prints an absolute
|
|
25
|
+
* path. A consumer comparing the raw strings concludes the two checkouts
|
|
26
|
+
* have different common dirs. Only the resolved realpath is a repo identity.
|
|
27
|
+
*
|
|
28
|
+
* ## Symlinked parents converge
|
|
29
|
+
*
|
|
30
|
+
* Measured on a throwaway repo reached both directly and through a symlinked
|
|
31
|
+
* ancestor: all four accesses (main / worktree × real / symlinked) resolve to
|
|
32
|
+
* the same directory, because the returned path is realpath-normalised here.
|
|
33
|
+
* This is the opposite of the worktree-cleanup finding that motivated the
|
|
34
|
+
* check — there realpath reporting caused a mis-classification; here it is the
|
|
35
|
+
* property that makes one shared register possible.
|
|
36
|
+
*
|
|
37
|
+
* Never throws: every caller treats "no common dir" as a reason to degrade,
|
|
38
|
+
* not as an error worth failing a hook over.
|
|
39
|
+
*/
|
|
40
|
+
|
|
41
|
+
import fs from 'node:fs';
|
|
42
|
+
import path from 'node:path';
|
|
43
|
+
|
|
44
|
+
/** `fs.realpathSync` that falls back to the input when the path is unreadable. */
|
|
45
|
+
function realpath_or_self(p: string): string {
|
|
46
|
+
try {
|
|
47
|
+
return fs.realpathSync(p);
|
|
48
|
+
} catch {
|
|
49
|
+
return p;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* The **per-worktree** git directory for `project_root` — `.git` in a plain
|
|
55
|
+
* checkout, the linked gitdir in a worktree. This is where `HEAD` lives, so it
|
|
56
|
+
* is the right anchor for "which branch is THIS checkout on", as opposed to
|
|
57
|
+
* `git_common_dir` which is deliberately shared across worktrees.
|
|
58
|
+
*/
|
|
59
|
+
export function git_dir(project_root: string): string | null {
|
|
60
|
+
const dot_git = path.join(project_root, '.git');
|
|
61
|
+
try {
|
|
62
|
+
const st = fs.statSync(dot_git);
|
|
63
|
+
if (st.isDirectory()) {
|
|
64
|
+
return realpath_or_self(dot_git);
|
|
65
|
+
}
|
|
66
|
+
const m = fs.readFileSync(dot_git, 'utf-8').match(/^gitdir:\s*(.+)\s*$/m);
|
|
67
|
+
if (m === null) {
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
return realpath_or_self(path.resolve(project_root, m[1]!.trim()));
|
|
71
|
+
} catch {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Current branch name for `project_root`, or `null` on a detached HEAD or
|
|
78
|
+
* outside a repo.
|
|
79
|
+
*
|
|
80
|
+
* Reads `<git-dir>/HEAD` directly rather than shelling out, for the same reason
|
|
81
|
+
* as everything else in this module: hooks export `GIT_DIR`, and an inherited
|
|
82
|
+
* one silently redirects `git` to the wrong repository.
|
|
83
|
+
*/
|
|
84
|
+
export function current_branch(project_root: string): string | null {
|
|
85
|
+
const gd = git_dir(project_root);
|
|
86
|
+
if (gd === null) return null;
|
|
87
|
+
try {
|
|
88
|
+
const head = fs.readFileSync(path.join(gd, 'HEAD'), 'utf-8').trim();
|
|
89
|
+
const m = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
90
|
+
return m === null ? null : m[1]!.trim();
|
|
91
|
+
} catch {
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The common git directory for `project_root`, realpath-normalised, or `null`
|
|
98
|
+
* when `project_root` is not inside a git repository.
|
|
99
|
+
*
|
|
100
|
+
* - Plain checkout → `<project_root>/.git`.
|
|
101
|
+
* - Linked worktree → the `commondir` target of the worktree's gitdir, i.e. the
|
|
102
|
+
* main checkout's `.git`, which is identical from every worktree of the repo.
|
|
103
|
+
* - Worktree gitdir without a `commondir` file → the gitdir itself (degenerate,
|
|
104
|
+
* but the historical behaviour and not this change's to alter).
|
|
105
|
+
*/
|
|
106
|
+
export function git_common_dir(project_root: string): string | null {
|
|
107
|
+
const dot_git = path.join(project_root, '.git');
|
|
108
|
+
try {
|
|
109
|
+
const st = fs.statSync(dot_git);
|
|
110
|
+
if (st.isDirectory()) {
|
|
111
|
+
return realpath_or_self(dot_git);
|
|
112
|
+
}
|
|
113
|
+
// Linked worktree: `.git` is a FILE containing "gitdir: <path>".
|
|
114
|
+
const m = fs.readFileSync(dot_git, 'utf-8').match(/^gitdir:\s*(.+)\s*$/m);
|
|
115
|
+
if (m === null) {
|
|
116
|
+
return null;
|
|
117
|
+
}
|
|
118
|
+
const gitdir = path.resolve(project_root, m[1]!.trim());
|
|
119
|
+
const commondir_file = path.join(gitdir, 'commondir');
|
|
120
|
+
if (fs.existsSync(commondir_file)) {
|
|
121
|
+
const common = path.resolve(gitdir, fs.readFileSync(commondir_file, 'utf-8').trim());
|
|
122
|
+
return realpath_or_self(common);
|
|
123
|
+
}
|
|
124
|
+
return realpath_or_self(gitdir);
|
|
125
|
+
} catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Host-capability manifest normalizer.
|
|
2
|
+
* Host-capability manifest normalizer + committed capability registry.
|
|
3
3
|
*
|
|
4
4
|
* Pure, no-I/O helper that takes a partial / unknown object (typically the
|
|
5
5
|
* `subagents.host_capabilities` override from `.agent-settings.yml`, or the
|
|
@@ -11,6 +11,18 @@
|
|
|
11
11
|
* have no subagent primitive.
|
|
12
12
|
* - A missing or non-boolean field resolves to `false`, never `true`.
|
|
13
13
|
* - `schema_version` is always forced to `1`.
|
|
14
|
+
*
|
|
15
|
+
* `resolveHostCapabilities` (F5, road-to-orchestrator-discipline-carriers)
|
|
16
|
+
* adds a resolution order in front of the safe default so a fresh install on
|
|
17
|
+
* a KNOWN host does not ship delegation dead by default:
|
|
18
|
+
*
|
|
19
|
+
* explicit `subagents.host_capabilities` override (wins, whole-object,
|
|
20
|
+
* unchanged semantics) → committed registry row for the detected host
|
|
21
|
+
* (`HOST_CAPABILITY_REGISTRY`) → `SAFE_DEFAULT` (all false).
|
|
22
|
+
*
|
|
23
|
+
* The registry lists only OBSERVED capabilities for a host this repo has
|
|
24
|
+
* actually measured (see the roadmap's transcript evidence) — never a
|
|
25
|
+
* speculative entry for a host nobody has verified.
|
|
14
26
|
*/
|
|
15
27
|
|
|
16
28
|
/** Resolved host-capability manifest. All booleans, `schema_version` fixed at 1. */
|
|
@@ -57,3 +69,74 @@ export function normalizeHostManifest(input: unknown): HostCapabilityManifest {
|
|
|
57
69
|
separate_quota_pool: asBool(src.separate_quota_pool),
|
|
58
70
|
};
|
|
59
71
|
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Committed registry — known host identifiers → OBSERVED capability sets.
|
|
75
|
+
*
|
|
76
|
+
* Keyed by the platform identifier this repo already uses for the same host
|
|
77
|
+
* across the hook/dispatch layer (`--platform claude`, `platforms['claude']`
|
|
78
|
+
* in `claude_settings_hooks.ts`, `TOOL_IDS` in `probe_skill_registration.ts`)
|
|
79
|
+
* — NOT the separate `toolDetection.ts` binary-detection id (`'claude-code'`),
|
|
80
|
+
* which names a different namespace (installed-CLI presence, not the running
|
|
81
|
+
* host identity a hook/dispatch envelope carries).
|
|
82
|
+
*
|
|
83
|
+
* Each row is partial: an omitted field stays at the `SAFE_DEFAULT` `false`
|
|
84
|
+
* when the row is applied. Claude Code is the one host this roadmap's
|
|
85
|
+
* transcript evidence measured spawning and running subagents concurrently
|
|
86
|
+
* (`Agent` tool dispatch, parallel tool-use blocks); polling and a separate
|
|
87
|
+
* quota pool were never observed, so they stay `false` — add a field only
|
|
88
|
+
* once it is itself observed, never by inference.
|
|
89
|
+
*/
|
|
90
|
+
const HOST_CAPABILITY_REGISTRY: Readonly<Record<string, Partial<HostCapabilityManifest>>> = {
|
|
91
|
+
claude: { subagent_spawn: true, parallel_spawn: true },
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Resolve the effective host-capability manifest for `hostId`.
|
|
96
|
+
*
|
|
97
|
+
* Resolution order (highest wins), per
|
|
98
|
+
* `host-capability-manifest.md § Resolution`:
|
|
99
|
+
*
|
|
100
|
+
* 1. `override` — the `subagents.host_capabilities` settings value, when
|
|
101
|
+
* present as a non-null object. Normalized exactly as {@link normalizeHostManifest}
|
|
102
|
+
* always has: a WHOLE-OBJECT override, each field independently coerced,
|
|
103
|
+
* a field absent from the override object resolves to `false`. This is
|
|
104
|
+
* the pre-existing, unchanged behavior — a present override still wins
|
|
105
|
+
* outright over the registry row, it does not merge field-by-field with it.
|
|
106
|
+
* 2. The committed {@link HOST_CAPABILITY_REGISTRY} row for `hostId`, when no
|
|
107
|
+
* override is present and the host is known.
|
|
108
|
+
* 3. `SAFE_DEFAULT` — unknown `hostId`, or no override and no registry row.
|
|
109
|
+
*
|
|
110
|
+
* `hostId` is a caller-supplied identifier (e.g. the same string a hook
|
|
111
|
+
* envelope's `platform` field carries) — this function does no environment
|
|
112
|
+
* probing of its own; see `host-capability-manifest.md` for why resolution
|
|
113
|
+
* stays a per-session, agent/caller-supplied fact rather than a live probe.
|
|
114
|
+
*
|
|
115
|
+
* An array override does NOT count as present (F8, review): `typeof [] ===
|
|
116
|
+
* 'object'` in JS, so a bare `typeof override === 'object'` check let a
|
|
117
|
+
* stray array reach `normalizeHostManifest`, which reads named properties
|
|
118
|
+
* off it (`.subagent_spawn`, …) that an array never has — every field
|
|
119
|
+
* silently coerces to `false`, exactly like an explicit empty-object
|
|
120
|
+
* override, even though nobody supplied one. An array is not a manifest
|
|
121
|
+
* shape at all, so it is treated the same as `null`/non-object: fall
|
|
122
|
+
* through to the registry row.
|
|
123
|
+
*/
|
|
124
|
+
export function resolveHostCapabilities(
|
|
125
|
+
hostId: string | null | undefined,
|
|
126
|
+
override?: unknown,
|
|
127
|
+
): HostCapabilityManifest {
|
|
128
|
+
if (
|
|
129
|
+
override !== undefined &&
|
|
130
|
+
override !== null &&
|
|
131
|
+
typeof override === 'object' &&
|
|
132
|
+
!Array.isArray(override)
|
|
133
|
+
) {
|
|
134
|
+
return normalizeHostManifest(override);
|
|
135
|
+
}
|
|
136
|
+
const row =
|
|
137
|
+
hostId !== null && hostId !== undefined ? HOST_CAPABILITY_REGISTRY[hostId] : undefined;
|
|
138
|
+
if (row === undefined) {
|
|
139
|
+
return { ...SAFE_DEFAULT };
|
|
140
|
+
}
|
|
141
|
+
return { ...SAFE_DEFAULT, ...row, schema_version: 1 };
|
|
142
|
+
}
|