@event4u/agent-config 9.14.0 → 9.16.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/CHANGELOG.md +118 -99
- package/README.md +8 -0
- package/dist/agent-src/commands/agents/audit.md +7 -7
- package/dist/agent-src/commands/agents/optimize.md +1 -1
- package/dist/agent-src/commands/analyze/reference-repo.md +1 -1
- package/dist/agent-src/commands/chat-history.md +1 -0
- package/dist/agent-src/commands/context.md +1 -0
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/ghostwriter.md +1 -1
- package/dist/agent-src/commands/memory.md +2 -1
- package/dist/agent-src/commands/module.md +1 -0
- package/dist/agent-src/commands/optimize/agents-dir.md +1 -1
- package/dist/agent-src/commands/optimize/deep.md +5 -2
- package/dist/agent-src/commands/optimize/skills.md +5 -5
- package/dist/agent-src/commands/override.md +1 -0
- package/dist/agent-src/commands/prediction-pool.md +4 -4
- package/dist/agent-src/commands/research/deep.md +1 -1
- package/dist/agent-src/commands/roadmap.md +2 -1
- package/dist/agent-src/commands/rule-compliance-audit.md +1 -1
- package/dist/agent-src/commands/tdd.md +1 -0
- package/dist/agent-src/commands/tests.md +1 -0
- package/dist/agent-src/commands/upstream-contribute.md +5 -5
- package/dist/agent-src/commands/video/from-script.md +1 -1
- package/dist/agent-src/commands/video/from-song.md +1 -1
- package/dist/agent-src/commands/video/scene.md +1 -1
- package/dist/agent-src/commands/video/stitch.md +1 -1
- package/dist/agent-src/commands/video/storyboard.md +1 -1
- package/dist/agent-src/commands/worktree/create.md +2 -1
- package/dist/agent-src/contexts/augment-infrastructure.md +1 -1
- package/dist/agent-src/contexts/contracts/artifact-engagement-flow.md +5 -5
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +3 -3
- package/dist/agent-src/contexts/contracts/emergency-triage-block.md +2 -2
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +4 -3
- package/dist/agent-src/contexts/execution/subagent-routing.md +19 -0
- package/dist/agent-src/contexts/override-system.md +1 -1
- package/dist/agent-src/ghostwriter/fictional-fixture-v1.md +1 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +1 -1
- package/dist/agent-src/rules/autonomous-execution.md +2 -1
- package/dist/agent-src/rules/brand-consistency.md +1 -0
- package/dist/agent-src/rules/brand-source-of-truth.md +1 -0
- package/dist/agent-src/rules/code-comment-discipline.md +2 -0
- package/dist/agent-src/rules/communication-through-line.md +1 -0
- package/dist/agent-src/rules/content-quoting-floor.md +1 -0
- package/dist/agent-src/rules/cross-source-consistency.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +1 -0
- package/dist/agent-src/rules/external-code-graph-interop.md +1 -0
- package/dist/agent-src/rules/external-reference-deep-dive.md +1 -0
- package/dist/agent-src/rules/fast-path-marker-visibility.md +1 -0
- package/dist/agent-src/rules/finance-safety-floor.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +1 -0
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -0
- package/dist/agent-src/rules/improve-before-implement.md +1 -1
- package/dist/agent-src/rules/invite-challenge.md +1 -0
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -0
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -0
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +1 -0
- package/dist/agent-src/rules/media-governance-routing.md +4 -3
- package/dist/agent-src/rules/media-sync-ground-truth.md +1 -0
- package/dist/agent-src/rules/no-attribution-footers.md +1 -0
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +1 -0
- package/dist/agent-src/rules/no-pr-progress-comments.md +1 -0
- package/dist/agent-src/rules/onboarding-gate.md +1 -0
- package/dist/agent-src/rules/output-discipline.md +1 -0
- package/dist/agent-src/rules/prefer-enums-over-literals.md +1 -0
- package/dist/agent-src/rules/preservation-guard.md +1 -0
- package/dist/agent-src/rules/question-not-instruction.md +1 -0
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +1 -0
- package/dist/agent-src/rules/role-mode-adherence.md +1 -0
- package/dist/agent-src/rules/runtime-safety.md +1 -0
- package/dist/agent-src/rules/security-sensitive-stop.md +1 -0
- package/dist/agent-src/rules/session-canary.md +9 -2
- package/dist/agent-src/rules/source-confidentiality.md +1 -0
- package/dist/agent-src/rules/source-of-truth.md +1 -0
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -0
- package/dist/agent-src/rules/think-before-action.md +1 -0
- package/dist/agent-src/rules/token-budget-discipline.md +1 -0
- package/dist/agent-src/rules/token-efficiency.md +1 -0
- package/dist/agent-src/rules/token-optimizer-maintenance.md +5 -5
- package/dist/agent-src/rules/tool-safety.md +1 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +2 -0
- package/dist/agent-src/rules/user-interaction.md +1 -0
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -0
- package/dist/agent-src/skills/adr-create/SKILL.md +1 -1
- package/dist/agent-src/skills/check-refs/SKILL.md +1 -1
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +1 -1
- package/dist/agent-src/skills/condense-memory/SKILL.md +1 -1
- package/dist/agent-src/skills/context-authoring/SKILL.md +1 -1
- package/dist/agent-src/skills/copilot-agents-optimization/SKILL.md +1 -1
- package/dist/agent-src/skills/description-assist/SKILL.md +1 -1
- package/dist/agent-src/skills/github-ci/SKILL.md +1 -0
- package/dist/agent-src/skills/guideline-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/lint-skills/SKILL.md +1 -1
- package/dist/agent-src/skills/readme-reviewer/SKILL.md +1 -1
- package/dist/agent-src/skills/roadmap-management/SKILL.md +8 -1
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-writing/SKILL.md +4 -4
- package/dist/agent-src/skills/token-optimizer/SKILL.md +4 -4
- package/dist/agent-src/skills/upstream-contribute/SKILL.md +6 -6
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +25 -0
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/command.md +1 -1
- package/dist/agent-src/templates/copilot-instructions.md +3 -3
- package/dist/agent-src/templates/rule.md +2 -2
- package/dist/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/dist/agent-src/templates/skill-archive-note.md +5 -5
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/agent-config.js +66 -19
- package/dist/cli/agent-config.js.map +1 -1
- package/dist/cli/main.js +9 -4
- package/dist/cli/main.js.map +1 -1
- package/dist/cli/mcp/content.js +1 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/cli/python/workspace_drive.js +2 -0
- package/dist/cli/python/workspace_drive.js.map +1 -1
- package/dist/cli/python/workspace_drive_health.js +2 -0
- package/dist/cli/python/workspace_drive_health.js.map +1 -1
- package/dist/cli/python/workspace_hosts.js +2 -0
- package/dist/cli/python/workspace_hosts.js.map +1 -1
- package/dist/cli/python/workspace_sessions.js +2 -0
- package/dist/cli/python/workspace_sessions.js.map +1 -1
- package/dist/cli/registry.js +1 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-A3HJ6NTI.js → chunk-63WSP6YZ.js} +2 -1
- package/dist/cli-delegate/{chunk-EJYIGZ3T.js → chunk-BKO2AZ7Z.js} +1 -1
- package/dist/cli-delegate/{chunk-GCVFLBP2.js → chunk-H574KOUH.js} +338 -494
- package/dist/cli-delegate/chunk-NXFWFAYV.js +339 -0
- package/dist/cli-delegate/{chunk-TEGJU2JX.js → chunk-ZXXJLL5K.js} +2 -2
- package/dist/cli-delegate/cmd_conformance.js +29 -23
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +127 -95
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +729 -430
- package/dist/install/install.mjs +52 -11
- package/dist/install/rule_scope.js +18 -2
- package/dist/install/rule_scope.js.map +1 -1
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +518 -411
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/claude_settings_hooks.js +30 -5
- package/dist/scripts/_lib/claude_settings_hooks.js.map +1 -1
- package/dist/scripts/_lib/scoped_projection.js +223 -0
- package/dist/scripts/_lib/scoped_projection.js.map +1 -0
- package/dist/scripts/_lib/surface_tiers.js +125 -0
- package/dist/scripts/_lib/surface_tiers.js.map +1 -0
- package/dist/scripts/_lib/token_count.js +166 -0
- package/dist/scripts/_lib/token_count.js.map +1 -0
- package/dist/scripts/_lib/trigger_eval_floors.js +8 -0
- package/dist/scripts/_lib/trigger_eval_floors.js.map +1 -1
- package/dist/scripts/condense.js +139 -9
- package/dist/scripts/condense.js.map +1 -1
- package/dist/scripts/project_thin_rules.js +450 -0
- package/dist/scripts/project_thin_rules.js.map +1 -0
- package/dist/server/routes/workspace.js +3 -1
- package/dist/server/routes/workspace.js.map +1 -1
- package/dist/server/schemas/settings.js +9 -3
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/userMd/utils.js +1 -1
- package/dist/shared/userMd/utils.js.map +1 -1
- package/dist/ui/assets/index-DmR0mbk4.js +26 -0
- package/dist/ui/assets/index-DmR0mbk4.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/package.json +14 -14
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/orchestration.ts +1 -1
- package/src/config/agent-settings.template.yml +61 -11
- package/src/config/budget-routing.json +7 -0
- package/src/config/ci-local-parity.yml +8 -0
- package/src/config/discovery/unassigned-artefacts.yml +23 -25
- package/src/config/evaluator-budgets.json +35 -5
- package/src/config/gate-coverage.yml +147 -0
- package/src/config/gate-violation-baselines.json +25 -0
- package/src/install/rule_scope.ts +27 -2
- package/src/scripts/_cli/cmd_conformance.ts +13 -24
- package/src/scripts/_dispatch.bash +10 -0
- package/src/scripts/_lib/claude_settings_hooks.ts +30 -4
- package/src/scripts/_lib/gate_baseline.ts +213 -0
- package/src/scripts/_lib/scoped_projection.ts +50 -0
- package/src/scripts/_lib/tier_budget_routing.ts +360 -0
- package/src/scripts/_lib/tool_adapter_registry.ts +8 -1
- package/src/scripts/_lib/trigger_eval_floors.ts +9 -0
- package/src/scripts/_lib/trigger_routers.ts +17 -5
- package/src/scripts/adr/regenerate_index.ts +4 -3
- package/src/scripts/ai-video/lib/parse-blueprint.sh +1 -1
- package/src/scripts/ai_council/compile_corpus.ts +1 -1
- package/src/scripts/anchor_scoring_run.ts +1 -1
- package/src/scripts/audit_cloud_compatibility.ts +37 -2
- package/src/scripts/audit_initial_context.ts +53 -19
- package/src/scripts/audit_likelihood.ts +31 -2
- package/src/scripts/audit_user_type_axis.ts +58 -11
- package/src/scripts/bench_hook_latency.ts +172 -41
- package/src/scripts/bench_persona_placebo.ts +1 -1
- package/src/scripts/bench_quality_run.ts +1 -1
- package/src/scripts/build_mcp_catalog.ts +12 -1
- package/src/scripts/check_always_budget.ts +122 -11
- package/src/scripts/check_augment_description_cap.ts +24 -3
- package/src/scripts/check_ci_strict_superset.ts +150 -0
- package/src/scripts/check_cli_registry_budget_sync.ts +168 -0
- package/src/scripts/check_cluster_patterns.ts +100 -0
- package/src/scripts/check_consumer_scope_flip.ts +26 -4
- package/src/scripts/check_evaluator_budgets.ts +198 -19
- package/src/scripts/check_gate_coverage.ts +338 -2
- package/src/scripts/check_generator_output_coverage.ts +2 -1
- package/src/scripts/check_iron_law_prominence.ts +1 -1
- package/src/scripts/check_kernel_rule_bundle.ts +1 -1
- package/src/scripts/check_no_new_legacy_path.ts +167 -3
- package/src/scripts/check_pack_size.ts +18 -4
- package/src/scripts/check_portability.ts +37 -7
- package/src/scripts/check_references.ts +19 -3
- package/src/scripts/check_release_pr_shape.ts +13 -0
- package/src/scripts/check_token_quality_golden.ts +1 -1
- package/src/scripts/check_token_regression.ts +9 -2
- package/src/scripts/condense.ts +155 -12
- package/src/scripts/cost/budget.mjs +77 -2
- package/src/scripts/evaluator_umbrella.sh +34 -1
- package/src/scripts/generate_capability_matrix.ts +8 -0
- package/src/scripts/generate_router_coverage_from_matrix.ts +155 -0
- package/src/scripts/hook_manifest.yaml +17 -3
- package/src/scripts/hooks/block_config_weakening.ts +337 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/probe_throttle.ts +75 -0
- package/src/scripts/install-hooks.sh +24 -2
- package/src/scripts/install.ts +2 -2
- package/src/scripts/lint_agents_md.ts +9 -3
- package/src/scripts/lint_archived_skills.ts +1 -1
- package/src/scripts/lint_artefact_frontmatter.ts +185 -37
- package/src/scripts/lint_command_routing.ts +21 -52
- package/src/scripts/lint_command_verbs.ts +47 -28
- package/src/scripts/lint_context_spine_usage.ts +38 -12
- package/src/scripts/lint_framework_leakage.ts +25 -6
- package/src/scripts/lint_handoffs.ts +49 -2
- package/src/scripts/lint_media_policy_linkage.ts +62 -30
- package/src/scripts/lint_namespace.ts +92 -7
- package/src/scripts/lint_new_skill_gate.ts +39 -31
- package/src/scripts/lint_originality_shingles.ts +7 -1
- package/src/scripts/lint_override_kernel_guard.ts +21 -3
- package/src/scripts/lint_pack_boundaries.ts +461 -91
- package/src/scripts/lint_pack_dependencies.ts +24 -57
- package/src/scripts/lint_persona_governance.ts +1 -1
- package/src/scripts/lint_role_experiences.ts +34 -5
- package/src/scripts/lint_trigger_precision.ts +142 -0
- package/src/scripts/mcp_server/consumer_tool_catalog.json +12 -201
- package/src/scripts/mcp_server/tools.ts +1 -1
- package/src/scripts/prepack-check.mjs +40 -1
- package/src/scripts/prepack_router_targets.d.mts +7 -0
- package/src/scripts/prepack_router_targets.mjs +93 -0
- package/src/scripts/print_required_checks.ts +72 -18
- package/src/scripts/profile_staleness_hook.ts +12 -0
- package/src/scripts/record_evaluator_measurements.mjs +105 -0
- package/src/scripts/recruit_preflight.sh +1 -1
- package/src/scripts/release.ts +214 -7
- package/src/scripts/router_target_paths.d.mts +4 -0
- package/src/scripts/router_target_paths.mjs +69 -0
- package/src/scripts/router_telemetry.ts +52 -1
- package/src/scripts/routing_doctor.ts +560 -0
- package/src/scripts/rule_trigger_eval.ts +403 -0
- package/src/scripts/schemas/rule.schema.json +5 -1
- package/src/scripts/session_canary_hook.ts +96 -5
- package/src/scripts/skill_linter.ts +9 -28
- package/src/scripts/smoke/skills.sh +1 -1
- package/src/scripts/sweep_dead_scan_roots.ts +356 -10
- package/src/scripts/trigger_coverage.ts +13 -2
- package/src/scripts/update_counts.ts +1 -1
- package/src/scripts/validate_telegraph_carveouts.ts +1 -1
- package/src/scripts/wrapper_freshness_hook.ts +14 -0
- package/src/server/schemas/settings.ts +19 -5
- package/src/shared/userMd/utils.ts +1 -1
- package/dist/cli-delegate/chunk-TJIAM5FW.js +0 -133
- package/dist/ui/assets/index-BpSQt5vm.js +0 -40
- package/dist/ui/assets/index-BpSQt5vm.js.map +0 -1
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* same finding text, same scan scope and file-walk order. No behaviour
|
|
9
9
|
* changes; latent bugs are replicated and flagged as divergence candidates.
|
|
10
10
|
*
|
|
11
|
-
* Scans
|
|
11
|
+
* Scans src/ and dist/agent-src/ for project-specific
|
|
12
12
|
* references that violate package portability (the package must work in ANY
|
|
13
13
|
* project).
|
|
14
14
|
*
|
|
@@ -271,10 +271,25 @@ const ALLOWLIST: string[] = [
|
|
|
271
271
|
'package repository', // "package repository" concept
|
|
272
272
|
'src/scripts/mcp_server/', // MCP server module path (road-to-mcp-server.md Phase 1)
|
|
273
273
|
String.raw`scripts\.mcp_server`, // MCP server Python module entrypoint
|
|
274
|
+
// ── Revealed 2026-08-02 when `src/` entered SCAN_DIRS (dead-root repair,
|
|
275
|
+
// road-to-renewal-foundation Phase 1). Both live under `src/templates/`,
|
|
276
|
+
// which is never projected into `dist/agent-src/`, so this gate had never
|
|
277
|
+
// seen them. Neither is a CONSUMER-project leak — they are the package's
|
|
278
|
+
// own published identifiers, which a consumer must type verbatim:
|
|
279
|
+
String.raw`plugin install agent-conf@`, // real marketplace install command
|
|
280
|
+
String.raw`\.event4u-bridge\.yml`, // real shipped anchor filename (src/scripts/install.ts, lint_agents_layout.ts)
|
|
274
281
|
];
|
|
275
282
|
|
|
276
|
-
// Directories to scan (only package files, not project-specific agents/)
|
|
277
|
-
|
|
283
|
+
// Directories to scan (only package files, not project-specific agents/).
|
|
284
|
+
//
|
|
285
|
+
// DEAD-ROOT REPAIR (road-to-renewal-foundation Phase 1): the second entry was
|
|
286
|
+
// `.agent-src.uncondensed`, deleted by ADR-051. The loops below `continue` on a
|
|
287
|
+
// missing directory, so half this gate's declared corpus — the whole authoring
|
|
288
|
+
// tree — was silently skipped. A project identifier authored in `src/` was only
|
|
289
|
+
// ever caught once a condense run projected it into `dist/agent-src/`, i.e. one
|
|
290
|
+
// step later than the gate claims to catch it. `src/` is the source of truth
|
|
291
|
+
// now and is scanned directly.
|
|
292
|
+
const SCAN_DIRS = ['src', 'dist/agent-src'];
|
|
278
293
|
|
|
279
294
|
// Additional root-level files shipped by the package that must also stay
|
|
280
295
|
// portable. These are read by agents working on the package itself and —
|
|
@@ -608,11 +623,20 @@ function check_cli_invocations(filepath: string): Violation[] {
|
|
|
608
623
|
return violations;
|
|
609
624
|
}
|
|
610
625
|
|
|
611
|
-
function scan_all(root: string): {
|
|
626
|
+
function scan_all(root: string): {
|
|
627
|
+
violations: Violation[];
|
|
628
|
+
detected: string[];
|
|
629
|
+
scanned: number;
|
|
630
|
+
} {
|
|
612
631
|
const { patterns, detected } = _compile_patterns(root);
|
|
613
632
|
const forbidden = _compile_forbidden_patterns();
|
|
614
633
|
const allowlist = _compile_allowlist();
|
|
615
634
|
const violations: Violation[] = [];
|
|
635
|
+
// Distinct files actually read, for the gate-coverage contract
|
|
636
|
+
// (src/config/gate-coverage.yml): a gate that inspected nothing has not
|
|
637
|
+
// passed. Counted as a set because the layer-3/4 passes revisit files the
|
|
638
|
+
// layer-1 pass already read.
|
|
639
|
+
const scannedFiles = new Set<string>();
|
|
616
640
|
|
|
617
641
|
// Layer 1 + 2: full package content
|
|
618
642
|
for (const scanDir of SCAN_DIRS) {
|
|
@@ -621,6 +645,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
621
645
|
continue;
|
|
622
646
|
}
|
|
623
647
|
for (const f of _rglobMdSorted(d)) {
|
|
648
|
+
scannedFiles.add(f);
|
|
624
649
|
violations.push(...check_file(f, [...patterns, ...forbidden], allowlist));
|
|
625
650
|
}
|
|
626
651
|
}
|
|
@@ -629,6 +654,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
629
654
|
for (const rel of SCAN_ROOT_FILES) {
|
|
630
655
|
const f = path.join(root, rel);
|
|
631
656
|
if (_isFile(f)) {
|
|
657
|
+
scannedFiles.add(f);
|
|
632
658
|
violations.push(...check_file(f, forbidden, allowlist));
|
|
633
659
|
}
|
|
634
660
|
}
|
|
@@ -664,7 +690,7 @@ function scan_all(root: string): { violations: Violation[]; detected: string[] }
|
|
|
664
690
|
}
|
|
665
691
|
}
|
|
666
692
|
|
|
667
|
-
return { violations, detected };
|
|
693
|
+
return { violations, detected, scanned: scannedFiles.size };
|
|
668
694
|
}
|
|
669
695
|
|
|
670
696
|
function format_text(violations: Violation[], detected: string[]): string {
|
|
@@ -816,7 +842,7 @@ function parseArgs(argv: readonly string[]): ParsedArgs {
|
|
|
816
842
|
function main(): number {
|
|
817
843
|
const args = parseArgs(process.argv.slice(2));
|
|
818
844
|
|
|
819
|
-
let result: { violations: Violation[]; detected: string[] };
|
|
845
|
+
let result: { violations: Violation[]; detected: string[]; scanned: number };
|
|
820
846
|
try {
|
|
821
847
|
result = scan_all(args.root);
|
|
822
848
|
} catch (e) {
|
|
@@ -825,7 +851,11 @@ function main(): number {
|
|
|
825
851
|
return 3;
|
|
826
852
|
}
|
|
827
853
|
|
|
828
|
-
const { violations, detected } = result;
|
|
854
|
+
const { violations, detected, scanned } = result;
|
|
855
|
+
// gate-coverage contract (src/config/gate-coverage.yml): one machine-readable
|
|
856
|
+
// count on stderr so `check_gate_coverage` can assert this gate read a real
|
|
857
|
+
// corpus. It scanned a deleted root for months and still exited 0.
|
|
858
|
+
process.stderr.write(`scanned: ${String(scanned)}\n`);
|
|
829
859
|
if (args.format === 'json') {
|
|
830
860
|
const payload = { detected, violations };
|
|
831
861
|
process.stdout.write(`${JSON.stringify(payload, null, 2)}\n`);
|
|
@@ -452,9 +452,12 @@ function _extract_personas_frontmatter(text: string): Array<[number, string]> {
|
|
|
452
452
|
|
|
453
453
|
function _find_suggestion(p: string, root: string): string {
|
|
454
454
|
const name = path.basename(p);
|
|
455
|
+
// DEAD-ROOT REPAIR (road-to-renewal-foundation Phase 1): the middle entry
|
|
456
|
+
// was `.agent-src.uncondensed`, deleted by ADR-051. `src/` is the source of
|
|
457
|
+
// truth, so a file that exists only there produced no suggestion at all.
|
|
455
458
|
for (const d of [
|
|
456
459
|
path.join(root, 'dist/agent-src'),
|
|
457
|
-
path.join(root, '
|
|
460
|
+
path.join(root, 'src'),
|
|
458
461
|
path.join(root, 'agents'),
|
|
459
462
|
]) {
|
|
460
463
|
if (_exists(d)) {
|
|
@@ -586,10 +589,15 @@ function check_file(filepath: string, artifacts: Artifacts, root: string): Broke
|
|
|
586
589
|
} else {
|
|
587
590
|
// Strip leading ./ and try with prefixes
|
|
588
591
|
const ref = rawRef.replace(/^[./]+/, '');
|
|
592
|
+
// DEAD-ROOT REPAIR: `.agent-src.uncondensed` (ADR-051) never
|
|
593
|
+
// resolved, so a reference that only exists under the source of
|
|
594
|
+
// truth was reported BROKEN. `src/` and its category roots
|
|
595
|
+
// replace it.
|
|
589
596
|
for (const prefix of [
|
|
590
597
|
root,
|
|
591
598
|
path.join(root, 'dist/agent-src'),
|
|
592
|
-
path.join(root, '
|
|
599
|
+
path.join(root, 'src'),
|
|
600
|
+
path.join(root, 'src/agent-src'),
|
|
593
601
|
]) {
|
|
594
602
|
if (_exists(path.join(prefix, ref))) {
|
|
595
603
|
resolved = true;
|
|
@@ -605,7 +613,7 @@ function check_file(filepath: string, artifacts: Artifacts, root: string): Broke
|
|
|
605
613
|
const rel = rawRef.slice('.augment/'.length);
|
|
606
614
|
for (const prefix of [
|
|
607
615
|
path.join(root, 'dist/agent-src'),
|
|
608
|
-
path.join(root, '
|
|
616
|
+
path.join(root, 'src/agent-src'),
|
|
609
617
|
]) {
|
|
610
618
|
if (_exists(path.join(prefix, rel))) {
|
|
611
619
|
resolved = true;
|
|
@@ -774,6 +782,10 @@ function check_memory_yaml(filepath: string, artifacts: Artifacts, root: string)
|
|
|
774
782
|
function scan_all(root: string): BrokenRef[] {
|
|
775
783
|
const artifacts = collect_artifacts(root);
|
|
776
784
|
const broken: BrokenRef[] = [];
|
|
785
|
+
// gate-coverage contract (src/config/gate-coverage.yml): a machine-readable
|
|
786
|
+
// count of files actually read, so `check_gate_coverage` can assert this
|
|
787
|
+
// gate saw a real corpus rather than a moved root.
|
|
788
|
+
let scannedFiles = 0;
|
|
777
789
|
for (const scanDir of SCAN_DIRS) {
|
|
778
790
|
const d = path.join(root, scanDir);
|
|
779
791
|
if (!_exists(d)) {
|
|
@@ -785,18 +797,22 @@ function scan_all(root: string): BrokenRef[] {
|
|
|
785
797
|
if (SKIP_DIRS.some((skip) => f.startsWith(path.join(root, skip)))) {
|
|
786
798
|
continue;
|
|
787
799
|
}
|
|
800
|
+
scannedFiles += 1;
|
|
788
801
|
broken.push(...check_file(f, artifacts, root));
|
|
789
802
|
}
|
|
790
803
|
}
|
|
791
804
|
const memoryDir = path.join(root, MEMORY_YAML_ROOT);
|
|
792
805
|
if (_isDir(memoryDir)) {
|
|
793
806
|
for (const f of _rglob(memoryDir, '.yml').sort()) {
|
|
807
|
+
scannedFiles += 1;
|
|
794
808
|
broken.push(...check_memory_yaml(f, artifacts, root));
|
|
795
809
|
}
|
|
796
810
|
for (const f of _rglob(memoryDir, '.yaml').sort()) {
|
|
811
|
+
scannedFiles += 1;
|
|
797
812
|
broken.push(...check_memory_yaml(f, artifacts, root));
|
|
798
813
|
}
|
|
799
814
|
}
|
|
815
|
+
process.stderr.write(`scanned: ${String(scannedFiles)}\n`);
|
|
800
816
|
return broken;
|
|
801
817
|
}
|
|
802
818
|
|
|
@@ -24,6 +24,9 @@ const _HERE = fileURLToPath(import.meta.url);
|
|
|
24
24
|
|
|
25
25
|
const ALLOWLIST_GLOBS = [
|
|
26
26
|
'package.json',
|
|
27
|
+
// Lockfile version fields — bumped by release.ts set_lockfile_version in
|
|
28
|
+
// lockstep with package.json (road-to-gates-that-can-fail Phase 5).
|
|
29
|
+
'package-lock.json',
|
|
27
30
|
'CHANGELOG.md',
|
|
28
31
|
'.claude-plugin/marketplace.json',
|
|
29
32
|
'src/packs/*/pack.yaml',
|
|
@@ -131,6 +134,16 @@ function check(files: readonly string[]): number {
|
|
|
131
134
|
for (const f of bad) {
|
|
132
135
|
process.stdout.write(`OUT-OF-SHAPE: ${f}\n`);
|
|
133
136
|
}
|
|
137
|
+
process.stdout.write(
|
|
138
|
+
'\n' +
|
|
139
|
+
'A release PR may only contain the version-bump allowlist — the heavy\n' +
|
|
140
|
+
'test matrix skips on release/* heads, so code must not ride a release PR\n' +
|
|
141
|
+
'(docs/contracts/release-pr-gating.md § Mid-release fixes).\n' +
|
|
142
|
+
'Fix: land the files above on main via their own PR, then\n' +
|
|
143
|
+
' git checkout release/X.Y.Z && git merge origin/main && git push\n' +
|
|
144
|
+
'— their release-PR diff becomes empty and this check goes green.\n' +
|
|
145
|
+
'Then resume with: task release -- --resume --yes\n',
|
|
146
|
+
);
|
|
134
147
|
return 1;
|
|
135
148
|
}
|
|
136
149
|
process.stdout.write(
|
|
@@ -41,7 +41,7 @@ import * as path from 'node:path';
|
|
|
41
41
|
import process from 'node:process';
|
|
42
42
|
import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
43
43
|
|
|
44
|
-
import yaml from 'js-yaml';
|
|
44
|
+
import * as yaml from 'js-yaml';
|
|
45
45
|
|
|
46
46
|
const _HERE = fileURLToPath(import.meta.url);
|
|
47
47
|
const REPO_ROOT = path.resolve(path.dirname(_HERE), '..', '..');
|
|
@@ -17,7 +17,9 @@
|
|
|
17
17
|
* - thin_rule_load — thin_projection.thin_gpt
|
|
18
18
|
* - skill_descriptions — description_catalog.skills_core_source.tokens_gpt
|
|
19
19
|
* - command_descriptions — description_catalog.commands_core_source.tokens_gpt
|
|
20
|
-
* - mcp_schemas —
|
|
20
|
+
* - mcp_schemas — max mcp_tool_schemas[*].tokens_gpt (per-transport;
|
|
21
|
+
* a client connects over ONE transport, so Σ would
|
|
22
|
+
* double-bill the tools both transports serve)
|
|
21
23
|
*
|
|
22
24
|
* A baseline captured under a different `token_method` (e.g. proxy → exact)
|
|
23
25
|
* is a legitimate reset, not a regression — the gate WARNS and asks for a
|
|
@@ -74,10 +76,15 @@ function _num(obj: unknown, ...keys: string[]): number {
|
|
|
74
76
|
|
|
75
77
|
/** Extract the flat gated-metric map from a projection-cost report. */
|
|
76
78
|
function extract_metrics(report: Json): Metrics {
|
|
79
|
+
// MAX, not Σ. The report keys one entry per SERVER-TRANSPORT pair, and a
|
|
80
|
+
// client connects over exactly one transport of a given server — summing
|
|
81
|
+
// `agent-config (stdio)` and `agent-config (worker)` would bill the 19
|
|
82
|
+
// shared tools twice and read as a regression the moment the transports were
|
|
83
|
+
// split apart. The gated metric is what the worst-case single client loads.
|
|
77
84
|
const mcp = (report['mcp_tool_schemas'] as Json) ?? {};
|
|
78
85
|
let mcp_total = 0;
|
|
79
86
|
for (const server of Object.values(mcp)) {
|
|
80
|
-
mcp_total
|
|
87
|
+
mcp_total = Math.max(mcp_total, _num(server, 'tokens_gpt'));
|
|
81
88
|
}
|
|
82
89
|
return {
|
|
83
90
|
token_method: String(report['token_method'] ?? 'unknown'),
|
package/src/scripts/condense.ts
CHANGED
|
@@ -35,15 +35,23 @@ import { fileURLToPath, pathToFileURL } from 'node:url';
|
|
|
35
35
|
import * as YAML from 'yaml';
|
|
36
36
|
|
|
37
37
|
import {
|
|
38
|
+
_domains_command_logical,
|
|
38
39
|
artefact_roots as _agent_src_artefact_roots,
|
|
39
40
|
iter_all_sources as _agent_src_iter_all_sources,
|
|
40
41
|
resolve_logical as _agent_src_resolve_logical,
|
|
41
42
|
strip_source_prefix,
|
|
42
43
|
} from './_lib/agent_src.js';
|
|
44
|
+
// Import-safety note: `project_thin_rules` guards its CLI entry
|
|
45
|
+
// (`_isCliEntry()` before `process.exit(main())`), so importing it here is
|
|
46
|
+
// side-effect-free. condense.ts is bundled into the installer, where a bare
|
|
47
|
+
// top-level `process.exit` would fire at consumer runtime — the documented
|
|
48
|
+
// bundled-CLI-entry-guard landmine. Verified before wiring, not assumed.
|
|
49
|
+
import { build_thin } from './project_thin_rules.js';
|
|
43
50
|
import { build_claude_hook_matrix } from './_lib/claude_settings_hooks.js';
|
|
44
51
|
import { is_claude_builtin_name } from './_lib/claude_builtin_names.js';
|
|
45
52
|
import { project_settings_path, load_agent_settings } from './_lib/agent_settings.js';
|
|
46
53
|
import { rule_is_compile_enabled } from './_lib/compile_time_toggles.js';
|
|
54
|
+
import { resolve_rule_pack_scope } from './_lib/scoped_projection.js';
|
|
47
55
|
import { info, success, flush_summary, resolve_level } from './_lib/script_output.js';
|
|
48
56
|
import {
|
|
49
57
|
TIER_TO_CLAUDE_MODEL as _TIER_TO_CLAUDE_MODEL,
|
|
@@ -987,13 +995,16 @@ export { _HRR_BANNER_MARKER };
|
|
|
987
995
|
// (rule-router contract § Schema v2). Untagged rules fail safe: they ship.
|
|
988
996
|
// ADR-040: projection-time filtering only, no runtime resolver.
|
|
989
997
|
|
|
990
|
-
function
|
|
998
|
+
function _read_projection_raw(key: string): unknown {
|
|
991
999
|
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
992
1000
|
const proj = data['projection'];
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
1001
|
+
return typeof proj === 'object' && proj !== null && !Array.isArray(proj)
|
|
1002
|
+
? (proj as Record<string, unknown>)[key]
|
|
1003
|
+
: null;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
function _read_projection_list(key: string): string[] | null {
|
|
1007
|
+
const value = _read_projection_raw(key);
|
|
997
1008
|
if (Array.isArray(value) && value.length > 0) {
|
|
998
1009
|
return value.map((v) => String(v));
|
|
999
1010
|
}
|
|
@@ -1004,8 +1015,28 @@ function _read_rule_workspaces(): string[] | null {
|
|
|
1004
1015
|
return _read_projection_list('rule_workspaces');
|
|
1005
1016
|
}
|
|
1006
1017
|
|
|
1018
|
+
/** `runtime.active_packs` overlay, empty when unset (fresh-install default). */
|
|
1019
|
+
function _read_runtime_active_packs(): string[] {
|
|
1020
|
+
const data = load_agent_settings({ project_path: MODULE_STATE.SETTINGS_FILE });
|
|
1021
|
+
const rt = data['runtime'];
|
|
1022
|
+
const value =
|
|
1023
|
+
typeof rt === 'object' && rt !== null && !Array.isArray(rt)
|
|
1024
|
+
? (rt as Record<string, unknown>)['active_packs']
|
|
1025
|
+
: null;
|
|
1026
|
+
return Array.isArray(value) ? value.map((v) => String(v)) : [];
|
|
1027
|
+
}
|
|
1028
|
+
|
|
1029
|
+
/**
|
|
1030
|
+
* Pack scope for the rule layer. `rule_packs: auto` derives the active-pack
|
|
1031
|
+
* set (the same one the skill/command prune uses) instead of a hand-typed id
|
|
1032
|
+
* list; anything else keeps the historical list-or-inactive semantics.
|
|
1033
|
+
*/
|
|
1007
1034
|
function _read_rule_packs(): string[] | null {
|
|
1008
|
-
return
|
|
1035
|
+
return resolve_rule_pack_scope(
|
|
1036
|
+
_read_projection_raw('rule_packs'),
|
|
1037
|
+
MODULE_STATE.PROJECT_ROOT,
|
|
1038
|
+
_read_runtime_active_packs(),
|
|
1039
|
+
);
|
|
1009
1040
|
}
|
|
1010
1041
|
|
|
1011
1042
|
/**
|
|
@@ -1044,11 +1075,19 @@ export function generate_rule_symlinks(): number {
|
|
|
1044
1075
|
|
|
1045
1076
|
let thin_files: Record<string, string> | null = null;
|
|
1046
1077
|
if (_lean_projection_mode() === 'thin') {
|
|
1047
|
-
//
|
|
1048
|
-
//
|
|
1049
|
-
//
|
|
1050
|
-
|
|
1051
|
-
|
|
1078
|
+
// DEAD-SWITCH REPAIR (road-to-renewal-foundation Phase 2). This branch
|
|
1079
|
+
// used to THROW: the port was skipped as "out of scope, not exercised by
|
|
1080
|
+
// golden parity", which left a documented, settings-selectable mode that
|
|
1081
|
+
// could only ever crash. `build_thin` was ported and has been present the
|
|
1082
|
+
// whole time — the wiring was the only thing missing.
|
|
1083
|
+
//
|
|
1084
|
+
// Scope note: this repairs the SWITCH only. The default stays
|
|
1085
|
+
// `eager-all`. Flipping it is parked behind the thin-projection honest
|
|
1086
|
+
// null (thin win-rate 36.2% < the 48% pre-registered threshold), and
|
|
1087
|
+
// nothing here disturbs that verdict — but a mode that throws cannot
|
|
1088
|
+
// even be re-measured, which is why the repair earns its place alone.
|
|
1089
|
+
thin_files = Object.fromEntries(
|
|
1090
|
+
build_thin(MODULE_STATE.RULES_SOURCE, _read_rule_workspaces()),
|
|
1052
1091
|
);
|
|
1053
1092
|
}
|
|
1054
1093
|
|
|
@@ -1146,6 +1185,9 @@ function _WINDSURF_WORKFLOWS_DIR(): string {
|
|
|
1146
1185
|
function _CURSOR_COMMANDS_DIR(): string {
|
|
1147
1186
|
return path.join(MODULE_STATE.PROJECT_ROOT, '.cursor', 'commands');
|
|
1148
1187
|
}
|
|
1188
|
+
function _CLAUDE_COMMANDS_DIR(): string {
|
|
1189
|
+
return path.join(MODULE_STATE.PROJECT_ROOT, '.claude', 'commands');
|
|
1190
|
+
}
|
|
1149
1191
|
|
|
1150
1192
|
function _parse_frontmatter(content: string): [Record<string, unknown>, string] {
|
|
1151
1193
|
if (!content.startsWith('---')) {
|
|
@@ -1566,6 +1608,92 @@ export function extract_description_from_md(content: string): string {
|
|
|
1566
1608
|
return '';
|
|
1567
1609
|
}
|
|
1568
1610
|
|
|
1611
|
+
/**
|
|
1612
|
+
* The `commands/<sub>` path of a command source file, or `null` when it is a
|
|
1613
|
+
* FLAT command (`commands/<name>.md`).
|
|
1614
|
+
*
|
|
1615
|
+
* A nested path is what Claude Code renders as `/cluster:sub`. Flat command
|
|
1616
|
+
* FILES are not registered by Claude Code (probed ≤ 2.1.204 — see the
|
|
1617
|
+
* flat-command mitigation in `install.ts`), which is why flat commands keep a
|
|
1618
|
+
* skill wrapper and nested ones do not need one.
|
|
1619
|
+
*/
|
|
1620
|
+
function _nested_command_subpath(source_file: string): string | null {
|
|
1621
|
+
const rel = _relativeToPosix(source_file, MODULE_STATE.PROJECT_ROOT);
|
|
1622
|
+
const logical = _domains_command_logical(rel);
|
|
1623
|
+
if (logical === null || !logical.startsWith('commands/')) {
|
|
1624
|
+
return null;
|
|
1625
|
+
}
|
|
1626
|
+
const sub = logical.slice('commands/'.length).replace(/\.md$/, '');
|
|
1627
|
+
return sub.includes('/') ? sub : null;
|
|
1628
|
+
}
|
|
1629
|
+
|
|
1630
|
+
/**
|
|
1631
|
+
* Project-scope `.claude/commands/<cluster>/<sub>.md` — the colon form.
|
|
1632
|
+
*
|
|
1633
|
+
* Before this, the package repo's own `.claude/` carried NO commands
|
|
1634
|
+
* directory: every command reached Claude Code as a hyphen-named skill
|
|
1635
|
+
* wrapper in `.claude/skills/`, while the colon form existed only in the
|
|
1636
|
+
* user-global tree written by `install.ts`. Every clustered command was
|
|
1637
|
+
* therefore listed TWICE for anyone with a global install
|
|
1638
|
+
* (`roadmap-process-full` AND `roadmap:process-full`), costing 4,214 GPT tok
|
|
1639
|
+
* of always-loaded catalog for zero added reach — measured 2026-08-02.
|
|
1640
|
+
*
|
|
1641
|
+
* Emitting the nested form here lets `generate_claude_commands` stop wrapping
|
|
1642
|
+
* the clustered commands: one listing, same reachability, and reachability no
|
|
1643
|
+
* longer depends on a global deploy having run. Claude Code dedupes project
|
|
1644
|
+
* and user scope by name, so the two copies of `/cluster:sub` collapse.
|
|
1645
|
+
*
|
|
1646
|
+
* ADR-003 (colon canonical for clusters) and ADR-044 (flat commands stay
|
|
1647
|
+
* hyphenated) both hold: flat commands are untouched and keep their wrapper.
|
|
1648
|
+
*/
|
|
1649
|
+
export function generate_claude_project_commands(
|
|
1650
|
+
active_command_slugs: ReadonlySet<string> | null = null,
|
|
1651
|
+
): number {
|
|
1652
|
+
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1653
|
+
return 0;
|
|
1654
|
+
}
|
|
1655
|
+
const nested: Array<[string, string]> = [];
|
|
1656
|
+
for (const [source_file, slug] of iterCommands()) {
|
|
1657
|
+
if (active_command_slugs !== null && !active_command_slugs.has(slug)) {
|
|
1658
|
+
continue;
|
|
1659
|
+
}
|
|
1660
|
+
const sub = _nested_command_subpath(source_file);
|
|
1661
|
+
if (sub !== null) {
|
|
1662
|
+
nested.push([source_file, sub]);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
const target_root = _CLAUDE_COMMANDS_DIR();
|
|
1667
|
+
const valid = new Set(nested.map(([, sub]) => `${sub}.md`));
|
|
1668
|
+
// Sweep stale links first: a renamed or deleted command must not linger as
|
|
1669
|
+
// a dangling `/cluster:sub`.
|
|
1670
|
+
if (_exists(target_root)) {
|
|
1671
|
+
for (const item of _rglobSorted(target_root, '*.md')) {
|
|
1672
|
+
const rel = _relativeToPosix(item, target_root);
|
|
1673
|
+
if (!valid.has(rel)) {
|
|
1674
|
+
fs.unlinkSync(item);
|
|
1675
|
+
}
|
|
1676
|
+
}
|
|
1677
|
+
}
|
|
1678
|
+
|
|
1679
|
+
let count = 0;
|
|
1680
|
+
for (const [source_file, sub] of nested) {
|
|
1681
|
+
const link = path.join(target_root, `${sub}.md`);
|
|
1682
|
+
_mkdirp(path.dirname(link));
|
|
1683
|
+
if (_existsOrSymlink(link)) {
|
|
1684
|
+
fs.unlinkSync(link);
|
|
1685
|
+
}
|
|
1686
|
+
// path.relative, not a hand-counted `../` run: the nesting depth
|
|
1687
|
+
// varies (2 and 3 segments both occur) and an off-by-one here writes
|
|
1688
|
+
// a dangling link that still LOOKS right in `ls -l`.
|
|
1689
|
+
const rel_target = path.relative(path.dirname(link), source_file);
|
|
1690
|
+
fs.symlinkSync(rel_target, link);
|
|
1691
|
+
count += 1;
|
|
1692
|
+
}
|
|
1693
|
+
info(` ✅ Linked ${count} \`.claude/commands/**/*.md\` files (colon form)`);
|
|
1694
|
+
return count;
|
|
1695
|
+
}
|
|
1696
|
+
|
|
1569
1697
|
export function generate_claude_commands(active_command_slugs: ReadonlySet<string> | null = null): number {
|
|
1570
1698
|
if (!_isDir(path.join(MODULE_STATE.PROJECT_ROOT, 'src', 'domains'))) {
|
|
1571
1699
|
process.stderr.write(' ⚠️ src/domains/ not found — skipping commands\n');
|
|
@@ -1597,6 +1725,15 @@ export function generate_claude_commands(active_command_slugs: ReadonlySet<strin
|
|
|
1597
1725
|
skipped += 1;
|
|
1598
1726
|
continue;
|
|
1599
1727
|
}
|
|
1728
|
+
// A clustered command already reaches Claude Code as `/cluster:sub`
|
|
1729
|
+
// from `.claude/commands/` (generate_claude_project_commands). A
|
|
1730
|
+
// hyphen wrapper on top would be a second listing of the same command
|
|
1731
|
+
// — the double-listing this de-duplicates. Flat commands still need
|
|
1732
|
+
// the wrapper: Claude Code does not register flat command FILES.
|
|
1733
|
+
if (_nested_command_subpath(source_file) !== null) {
|
|
1734
|
+
skipped += 1;
|
|
1735
|
+
continue;
|
|
1736
|
+
}
|
|
1600
1737
|
// Never claim a `/name` that is a Claude Code built-in command or
|
|
1601
1738
|
// bundled skill — a same-name entry shadows the built-in (e.g. a
|
|
1602
1739
|
// `review` entry hides Claude Code's own /review). Nested
|
|
@@ -2144,6 +2281,11 @@ function _generate_tools_inner(
|
|
|
2144
2281
|
generate_gemini_md();
|
|
2145
2282
|
}
|
|
2146
2283
|
const skills = _tool_active('claude-code') ? generate_claude_skills(skill_names) : 0;
|
|
2284
|
+
// Colon form first: `generate_claude_commands` skips every command this
|
|
2285
|
+
// one emitted, so the ordering is what keeps the two from double-listing.
|
|
2286
|
+
const claude_cmds = _tool_active('claude-code')
|
|
2287
|
+
? generate_claude_project_commands(cmd_slugs)
|
|
2288
|
+
: 0;
|
|
2147
2289
|
const commands = _tool_active('claude-code') ? generate_claude_commands(cmd_slugs) : 0;
|
|
2148
2290
|
const subagents = _tool_active('claude-code') ? generate_claude_subagents() : 0;
|
|
2149
2291
|
const subagent_contexts = generate_subagent_host_contexts();
|
|
@@ -2157,7 +2299,8 @@ function _generate_tools_inner(
|
|
|
2157
2299
|
const windsurf_wf = _tool_active('windsurf') ? generate_windsurf_workflows(cmd_slugs) : 0;
|
|
2158
2300
|
const summary =
|
|
2159
2301
|
`✅ generate-tools — rules=${rules} skills=${skills} ` +
|
|
2160
|
-
`
|
|
2302
|
+
`claude_commands=${claude_cmds} command_skills=${commands} ` +
|
|
2303
|
+
`subagents=${subagents} subagent_contexts=${subagent_contexts} plugin_cmd_skills=${plugin_cmd_skills} ` +
|
|
2161
2304
|
`plugin_hooks=${plugin_hooks} ` +
|
|
2162
2305
|
`personas=${personas} user_types=${user_types} ` +
|
|
2163
2306
|
`cursor_mdc=${cursor_mdc} windsurf_rules=${windsurf_modern} ` +
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
// Env: BUDGET_STORE, BUDGET_CONFIG, BUDGET_PERIOD={today|week|month|all}, BUDGET_QUIET=1
|
|
11
11
|
|
|
12
12
|
import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
13
|
-
import { dirname } from 'node:path';
|
|
13
|
+
import { dirname, join } from 'node:path';
|
|
14
14
|
|
|
15
15
|
const STORE = process.env.BUDGET_STORE || 'agents/cost-tracking/sessions.jsonl';
|
|
16
16
|
const CONFIG = process.env.BUDGET_CONFIG || 'agents/cost-tracking/budget.json';
|
|
@@ -198,14 +198,89 @@ function cmdCheck() {
|
|
|
198
198
|
if (hardStop) process.exit(1);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
+
// --- Per-tier budget state (docs/contracts/budget-routing.md) -------------
|
|
202
|
+
// v1 window for per-tier ceilings: rolling 24h for SPEND (ledger entries
|
|
203
|
+
// tagged `tier:`). Pending reserves from tier-reserves.jsonl count only
|
|
204
|
+
// within the shared reserve TTL — reserves are race protection, not spend
|
|
205
|
+
// accounting. The TTL comes from src/config/budget-routing.json, the SAME
|
|
206
|
+
// source acquireBudgetPermit reads, so the two readers cannot diverge
|
|
207
|
+
// (external review 2026-08-03, Finding 1b).
|
|
208
|
+
|
|
209
|
+
function loadReserveTtlMs() {
|
|
210
|
+
try {
|
|
211
|
+
const cfgPath = join(dirname(new URL(import.meta.url).pathname), '..', '..', 'config', 'budget-routing.json');
|
|
212
|
+
const raw = JSON.parse(readFileSync(cfgPath, 'utf-8'));
|
|
213
|
+
if (Number.isFinite(raw.reserve_ttl_ms)) return raw.reserve_ttl_ms;
|
|
214
|
+
} catch { /* fall through to the safe default below */ }
|
|
215
|
+
return 600000;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
function loadPerTierCeilings() {
|
|
219
|
+
const out = { cheap: null, medium: null, strong: null };
|
|
220
|
+
if (!existsSync(SETTINGS)) return out;
|
|
221
|
+
let inPerTier = false;
|
|
222
|
+
for (const line of readFileSync(SETTINGS, 'utf-8').split('\n')) {
|
|
223
|
+
if (/^ {4}per_tier:/.test(line)) { inPerTier = true; continue; }
|
|
224
|
+
if (inPerTier) {
|
|
225
|
+
const m = line.match(/^ {6}(cheap|medium|strong):\s*([0-9.]+|null)\s*(#.*)?$/);
|
|
226
|
+
if (m) {
|
|
227
|
+
out[m[1]] = m[2] === 'null' ? null : Number(m[2]);
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
if (/^ {0,5}\S/.test(line)) inPerTier = false;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
return out;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
function cmdTier(rest) {
|
|
237
|
+
const tier = rest[0];
|
|
238
|
+
if (!['cheap', 'medium', 'strong'].includes(tier)) {
|
|
239
|
+
console.error('usage: budget.mjs tier {cheap|medium|strong}');
|
|
240
|
+
process.exit(2);
|
|
241
|
+
}
|
|
242
|
+
const dayMs = 24 * 3600 * 1000;
|
|
243
|
+
const now = Date.now();
|
|
244
|
+
const inWindow = (ts) => ts && (now - new Date(ts).getTime()) < dayMs;
|
|
245
|
+
const spent = loadSessions()
|
|
246
|
+
.filter((s) => s.tier === tier && inWindow(s.capturedAt || s.endedAt || s.ts))
|
|
247
|
+
.reduce((acc, s) => acc + (Number(s.total_cost_usd ?? s.cost_usd ?? 0) || 0), 0);
|
|
248
|
+
let reserved = 0;
|
|
249
|
+
const reserveTtl = loadReserveTtlMs();
|
|
250
|
+
const reservePath = join(dirname(STORE), 'tier-reserves.jsonl');
|
|
251
|
+
if (existsSync(reservePath)) {
|
|
252
|
+
for (const line of readFileSync(reservePath, 'utf-8').split('\n')) {
|
|
253
|
+
if (!line.trim()) continue;
|
|
254
|
+
try {
|
|
255
|
+
const e = JSON.parse(line);
|
|
256
|
+
if (e.tier === tier && e.status === 'pending' && (now - e.ts_ms) < reserveTtl) {
|
|
257
|
+
reserved += Number(e.est_usd || 0) || 0;
|
|
258
|
+
}
|
|
259
|
+
} catch { /* skip malformed line */ }
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
const ceiling = loadPerTierCeilings()[tier];
|
|
263
|
+
const available = ceiling === null ? true : spent + reserved < ceiling;
|
|
264
|
+
console.log(JSON.stringify({
|
|
265
|
+
tier,
|
|
266
|
+
window: 'rolling-24h',
|
|
267
|
+
reserve_ttl_ms: reserveTtl,
|
|
268
|
+
ceiling_usd: ceiling,
|
|
269
|
+
spent_usd: spent,
|
|
270
|
+
reserved_usd: reserved,
|
|
271
|
+
available,
|
|
272
|
+
}));
|
|
273
|
+
}
|
|
274
|
+
|
|
201
275
|
function main() {
|
|
202
276
|
const [cmd, ...rest] = process.argv.slice(2);
|
|
203
277
|
switch (cmd) {
|
|
204
278
|
case 'set': return cmdSet(rest);
|
|
205
279
|
case 'get': return cmdGet();
|
|
206
280
|
case 'check': return cmdCheck();
|
|
281
|
+
case 'tier': return cmdTier(rest);
|
|
207
282
|
default:
|
|
208
|
-
console.error('usage: budget.mjs {set <usd>|get|check}');
|
|
283
|
+
console.error('usage: budget.mjs {set <usd>|get|check|tier <cheap|medium|strong>}');
|
|
209
284
|
process.exit(2);
|
|
210
285
|
}
|
|
211
286
|
}
|
|
@@ -23,8 +23,12 @@
|
|
|
23
23
|
# 9. npx smoke first-touch paths (--version, --help, mcp initialize)
|
|
24
24
|
# 6-8 feed src/scripts/check_evaluator_budgets.ts against
|
|
25
25
|
# src/config/evaluator-budgets.json (owner + review date lint-enforced).
|
|
26
|
+
# 8b. record publish with UMBRELLA_RECORD=1, writes the measurement set to
|
|
27
|
+
# agents/evidence/metrics/evaluator-measurements.json so
|
|
28
|
+
# drift is visible in a diff the day it lands
|
|
26
29
|
#
|
|
27
30
|
# Usage: bash src/scripts/evaluator_umbrella.sh [--skip-pack <tarball>]
|
|
31
|
+
# UMBRELLA_RECORD=1 bash src/scripts/evaluator_umbrella.sh # nightly
|
|
28
32
|
set -euo pipefail
|
|
29
33
|
|
|
30
34
|
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
@@ -166,9 +170,38 @@ console.log(JSON.stringify(m, null, 1));' \
|
|
|
166
170
|
"$MEASURE_TMP" "$UNPACKED_BYTES" "$NM_MB" "$DEP_COUNT" "$CLI_MS" "$MCP_MS" "$CLI_COUNT" "$MCP_TOOLS" \
|
|
167
171
|
"$COLD_START_MS" "$COLD_START_CONDITIONS"
|
|
168
172
|
|
|
169
|
-
|
|
173
|
+
# The budget gate runs BEFORE the record is refreshed, so the comparison is
|
|
174
|
+
# fresh-vs-committed. Refreshing first would make every drift check trivially
|
|
175
|
+
# green (road-to-gates-that-can-fail Phase 5).
|
|
176
|
+
step "budget gate (absolute + >regression_pct creep + drift vs the committed record)"
|
|
170
177
|
./scripts-run src/scripts/check_evaluator_budgets --measurements "$MEASURE_TMP"
|
|
171
178
|
|
|
179
|
+
# ------------------------------------------------- 8b. publish the record
|
|
180
|
+
# With UMBRELLA_RECORD=1 (the nightly sets it) the measurement set is written
|
|
181
|
+
# to a COMMITTED artifact, so budget drift is visible the day it lands instead
|
|
182
|
+
# of at the next release — `cli_help_command_count` drifted 74 → 80 against a
|
|
183
|
+
# value frozen at 79 with nobody seeing it. It lives under agents/evidence/,
|
|
184
|
+
# the repo's committed-evidence tree, and NOT under src/config/, which ships:
|
|
185
|
+
# this is maintainer evidence with nightly churn, not consumer runtime config,
|
|
186
|
+
# and keeping it out of the tarball also keeps evaluator-budgets.json (the
|
|
187
|
+
# policy) reviewable on its own. Sorted keys + fixed indent so a moved number
|
|
188
|
+
# is a one-line diff.
|
|
189
|
+
if [[ "${UMBRELLA_RECORD:-0}" == "1" ]]; then
|
|
190
|
+
step "publish the measurement record (agents/evidence/metrics/)"
|
|
191
|
+
RECORD_PATH="$ROOT/agents/evidence/metrics/evaluator-measurements.json"
|
|
192
|
+
mkdir -p "$(dirname "$RECORD_PATH")"
|
|
193
|
+
RUN_URL=""
|
|
194
|
+
if [[ -n "${GITHUB_REPOSITORY:-}" && -n "${GITHUB_RUN_ID:-}" ]]; then
|
|
195
|
+
RUN_URL="${GITHUB_SERVER_URL:-https://github.com}/$GITHUB_REPOSITORY/actions/runs/$GITHUB_RUN_ID"
|
|
196
|
+
fi
|
|
197
|
+
node src/scripts/record_evaluator_measurements.mjs \
|
|
198
|
+
--measurements "$MEASURE_TMP" \
|
|
199
|
+
--out "$RECORD_PATH" \
|
|
200
|
+
--sha "$(git rev-parse HEAD 2>/dev/null || echo '')" \
|
|
201
|
+
--run-url "$RUN_URL" \
|
|
202
|
+
--conditions "$COLD_START_CONDITIONS"
|
|
203
|
+
fi
|
|
204
|
+
|
|
172
205
|
# ------------------------------------------------------------- 9. npx smoke
|
|
173
206
|
step "first-touch smoke (installed bin)"
|
|
174
207
|
"$CLI_BIN" --version >/dev/null
|
|
@@ -109,6 +109,14 @@ const _FN_SPEC: Record<string, FnSpec> = {
|
|
|
109
109
|
artifact: 'commands',
|
|
110
110
|
cells: { 'claude-code': 'native', augment: 'native' },
|
|
111
111
|
},
|
|
112
|
+
// The colon form (`/cluster:sub`) in the project tree. Same artifact and
|
|
113
|
+
// the same native cell as the hyphen wrapper above — the two split the
|
|
114
|
+
// command population between them (nested here, flat there); this is not
|
|
115
|
+
// an added surface.
|
|
116
|
+
generate_claude_project_commands: {
|
|
117
|
+
artifact: 'commands',
|
|
118
|
+
cells: { 'claude-code': 'native' },
|
|
119
|
+
},
|
|
112
120
|
generate_claude_subagents: {
|
|
113
121
|
artifact: 'subagents',
|
|
114
122
|
cells: { 'claude-code': 'native' },
|