@event4u/agent-config 16.2.0 → 16.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +337 -234
- package/README.md +15 -13
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +11 -8
- package/dist/agent-src/commands/analyze/roadmap-repos.md +1 -1
- package/dist/agent-src/commands/bug/fix.md +17 -2
- package/dist/agent-src/commands/bug/investigate.md +1 -1
- package/dist/agent-src/commands/commit/in-chunks.md +40 -31
- package/dist/agent-src/commands/commit.md +72 -33
- package/dist/agent-src/commands/condense.md +2 -8
- package/dist/agent-src/commands/council/debate.md +11 -7
- package/dist/agent-src/commands/council/default.md +10 -9
- package/dist/agent-src/commands/council.md +1 -1
- package/dist/agent-src/commands/estimate-ticket.md +1 -1
- package/dist/agent-src/commands/feature/explore.md +3 -3
- package/dist/agent-src/commands/feature/plan.md +2 -2
- package/dist/agent-src/commands/feature/roadmap.md +1 -1
- package/dist/agent-src/commands/fix/ci.md +18 -4
- package/dist/agent-src/commands/fix/commit-messages.md +48 -23
- package/dist/agent-src/commands/fix/pr-comments.md +3 -6
- package/dist/agent-src/commands/implement-ticket.md +4 -4
- package/dist/agent-src/commands/jira-ticket.md +1 -1
- package/dist/agent-src/commands/memory/add.md +4 -16
- package/dist/agent-src/commands/memory/load.md +5 -6
- package/dist/agent-src/commands/memory/promote.md +3 -16
- package/dist/agent-src/commands/mission/upgrade.md +3 -2
- package/dist/agent-src/commands/optimize/agents-dir.md +2 -2
- package/dist/agent-src/commands/optimize/deep.md +4 -2
- package/dist/agent-src/commands/optimize/rtk.md +4 -26
- package/dist/agent-src/commands/pr/create/description-only.md +18 -15
- package/dist/agent-src/commands/pr/create.md +42 -34
- package/dist/agent-src/commands/pr/merge.md +136 -9
- package/dist/agent-src/commands/prepare-for-review.md +13 -11
- package/dist/agent-src/commands/project/analyze.md +7 -35
- package/dist/agent-src/commands/refine-ticket.md +1 -1
- package/dist/agent-src/commands/review/changes.md +14 -19
- package/dist/agent-src/commands/roadmap/create.md +1 -1
- package/dist/agent-src/commands/roadmap/materialize.md +1 -1
- package/dist/agent-src/commands/roadmap/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +56 -42
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +411 -0
- package/dist/agent-src/commands/roadmap/triage-parked.md +325 -0
- package/dist/agent-src/commands/roadmap.md +18 -4
- package/dist/agent-src/commands/tests/create.md +17 -3
- package/dist/agent-src/commands/tests/execute.md +22 -5
- package/dist/agent-src/commands/worktree/create.md +7 -3
- package/dist/agent-src/commands/worktree/verify.md +2 -1
- package/dist/agent-src/contexts/authority/decision-revisit-gate-enforcement.md +43 -0
- package/dist/agent-src/contexts/communication/rules-auto/design-fidelity-enforcement.md +24 -0
- package/dist/agent-src/contexts/communication/rules-auto/think-before-action-mechanics.md +5 -3
- package/dist/agent-src/contexts/communication/rules-auto/token-efficiency-mechanics.md +7 -9
- package/dist/agent-src/contexts/contracts/command-suggestion-flow.md +8 -6
- package/dist/agent-src/contexts/contracts/frugality-charter.md +8 -5
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +7 -7
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +0 -1
- package/dist/agent-src/contexts/execution/evidence-discipline.md +2 -2
- package/dist/agent-src/contexts/execution/non-interactive-contract.md +5 -8
- package/dist/agent-src/contexts/execution/rdp-gate.md +4 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +9 -8
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +99 -54
- package/dist/agent-src/contexts/execution/session-canary-enforcement-history.md +67 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +8 -8
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -4
- package/dist/agent-src/contexts/execution/terminal-states.md +2 -2
- package/dist/agent-src/contexts/execution/toolchain-resolver.md +95 -7
- package/dist/agent-src/contexts/subagent-configuration.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/dist/agent-src/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/dist/agent-src/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/dist/agent-src/guidelines/agent-infra/layered-settings.md +13 -1
- package/dist/agent-src/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/dist/agent-src/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/dist/agent-src/guidelines/agent-infra/size-and-scope.md +36 -0
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +1 -2
- package/dist/agent-src/rules/analysis-skill-routing.md +0 -1
- package/dist/agent-src/rules/architecture.md +0 -1
- package/dist/agent-src/rules/artifact-drafting-protocol.md +0 -1
- package/dist/agent-src/rules/artifact-engagement-recording.md +0 -1
- package/dist/agent-src/rules/augment-edit-discipline.md +0 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-consistency.md +0 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +0 -1
- package/dist/agent-src/rules/broken-access-control.md +1 -1
- package/dist/agent-src/rules/cli-output-handling.md +0 -1
- package/dist/agent-src/rules/code-comment-discipline.md +0 -1
- package/dist/agent-src/rules/code-provenance.md +0 -1
- package/dist/agent-src/rules/command-suggestion-policy.md +1 -1
- package/dist/agent-src/rules/commit-conventions.md +1 -2
- package/dist/agent-src/rules/communication-through-line.md +1 -2
- package/dist/agent-src/rules/content-quoting-floor.md +1 -2
- package/dist/agent-src/rules/context-hygiene.md +6 -6
- package/dist/agent-src/rules/copilot-routing.md +1 -2
- package/dist/agent-src/rules/council-availability.md +4 -4
- package/dist/agent-src/rules/cross-source-consistency.md +4 -6
- package/dist/agent-src/rules/decision-revisit-gate.md +17 -35
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +12 -16
- package/dist/agent-src/rules/design-review-after-ui-write.md +3 -3
- package/dist/agent-src/rules/devcontainer-routing.md +1 -2
- package/dist/agent-src/rules/doc-screenshot-hygiene.md +1 -1
- package/dist/agent-src/rules/docker-commands.md +0 -1
- package/dist/agent-src/rules/domain-adoption-policy.md +0 -1
- package/dist/agent-src/rules/domain-safety-disclaimer.md +1 -1
- package/dist/agent-src/rules/domain-safety-pii.md +1 -1
- package/dist/agent-src/rules/domain-safety-retention.md +1 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +1 -1
- package/dist/agent-src/rules/evaluator-independence.md +3 -2
- package/dist/agent-src/rules/external-code-graph-interop.md +0 -1
- package/dist/agent-src/rules/external-reference-deep-dive.md +0 -1
- package/dist/agent-src/rules/fast-path-marker-visibility.md +2 -2
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/fix-what-you-see.md +6 -32
- package/dist/agent-src/rules/framework-neutrality-in-generic-skills.md +0 -1
- package/dist/agent-src/rules/git-history-discipline.md +2 -2
- package/dist/agent-src/rules/guidelines.md +0 -1
- package/dist/agent-src/rules/history-discipline.md +0 -1
- package/dist/agent-src/rules/icon-consistency.md +0 -1
- package/dist/agent-src/rules/image-likeness-and-rights.md +1 -1
- package/dist/agent-src/rules/improve-before-implement.md +0 -1
- package/dist/agent-src/rules/invite-challenge.md +0 -1
- package/dist/agent-src/rules/laravel-routing.md +1 -2
- package/dist/agent-src/rules/laravel-translations.md +0 -1
- package/dist/agent-src/rules/legal-safety-floor.md +3 -3
- package/dist/agent-src/rules/lethal-trifecta-guard.md +1 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +1 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +13 -17
- package/dist/agent-src/rules/media-sync-ground-truth.md +0 -1
- package/dist/agent-src/rules/minimal-safe-diff.md +0 -1
- package/dist/agent-src/rules/missing-skill-recovery.md +0 -1
- package/dist/agent-src/rules/missing-tool-handling.md +0 -1
- package/dist/agent-src/rules/model-recommendation.md +0 -1
- package/dist/agent-src/rules/neighbour-precedence.md +64 -0
- package/dist/agent-src/rules/no-attribution-footers.md +0 -1
- package/dist/agent-src/rules/no-decorative-emojis-in-git-surfaces.md +0 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +0 -1
- package/dist/agent-src/rules/no-roadmap-references.md +0 -1
- package/dist/agent-src/rules/notes-first-reasoning.md +0 -1
- package/dist/agent-src/rules/onboarding-gate.md +0 -1
- package/dist/agent-src/rules/output-discipline.md +0 -1
- package/dist/agent-src/rules/package-ci-checks.md +0 -1
- package/dist/agent-src/rules/persona-governance.md +4 -5
- package/dist/agent-src/rules/php-coding.md +0 -1
- package/dist/agent-src/rules/prefer-enums-over-literals.md +0 -1
- package/dist/agent-src/rules/preservation-guard.md +0 -1
- package/dist/agent-src/rules/provider-lifecycle-discipline.md +3 -4
- package/dist/agent-src/rules/question-not-instruction.md +1 -1
- package/dist/agent-src/rules/reviewer-awareness.md +0 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +0 -1
- package/dist/agent-src/rules/roadmap-progress-sync.md +4 -7
- package/dist/agent-src/rules/role-mode-adherence.md +0 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -1
- package/dist/agent-src/rules/runtime-safety.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +1 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +3 -3
- package/dist/agent-src/rules/self-repair-loop.md +0 -1
- package/dist/agent-src/rules/senior-engineering-discipline.md +0 -1
- package/dist/agent-src/rules/session-canary.md +7 -62
- package/dist/agent-src/rules/settings-ask-protocol.md +6 -6
- package/dist/agent-src/rules/size-enforcement.md +0 -1
- package/dist/agent-src/rules/skill-improvement-trigger.md +2 -3
- package/dist/agent-src/rules/skill-quality.md +0 -1
- package/dist/agent-src/rules/slash-command-routing-policy.md +0 -1
- package/dist/agent-src/rules/source-confidentiality.md +1 -1
- package/dist/agent-src/rules/source-discovery-gate.md +0 -1
- package/dist/agent-src/rules/source-of-truth.md +0 -1
- package/dist/agent-src/rules/spreadsheet-source-quality.md +1 -2
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/symfony-routing.md +1 -2
- package/dist/agent-src/rules/think-before-action.md +0 -1
- package/dist/agent-src/rules/token-budget-discipline.md +12 -46
- package/dist/agent-src/rules/token-efficiency.md +0 -1
- package/dist/agent-src/rules/token-optimizer-maintenance.md +0 -1
- package/dist/agent-src/rules/tool-safety.md +1 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +1 -1
- package/dist/agent-src/rules/upstream-proposal.md +0 -1
- package/dist/agent-src/rules/user-interaction.md +0 -1
- package/dist/agent-src/rules/user-interrupt-priority.md +1 -1
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/dist/agent-src/scripts/deferral_carry.ts +339 -0
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/dist/agent-src/skills/ai-council/SKILL.md +7 -4
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +21 -11
- package/dist/agent-src/skills/ai-council/references/procedure.md +12 -6
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/api-design/data/manifest.json +2 -2
- package/dist/agent-src/skills/api-testing/SKILL.md +17 -0
- package/dist/agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/code-intelligence/SKILL.md +14 -2
- package/dist/agent-src/skills/command-routing/SKILL.md +3 -4
- package/dist/agent-src/skills/conventional-commits-writing/SKILL.md +127 -139
- package/dist/agent-src/skills/corpus-grounding/ATTRIBUTION.md +39 -0
- package/dist/agent-src/skills/corpus-grounding/SKILL.md +8 -2
- package/dist/agent-src/skills/database/data/manifest.json +2 -2
- package/dist/agent-src/skills/database/data/query-tuning.csv +13 -13
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/design-system-capture/references/import-procedure.md +1 -1
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/existing-ui-audit/SKILL.md +2 -2
- package/dist/agent-src/skills/fe-design/SKILL.md +1 -1
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/git-workflow/SKILL.md +44 -44
- package/dist/agent-src/skills/git-workflow/references/branch-update.md +312 -0
- package/dist/agent-src/skills/git-workflow/references/commit-subject.md +205 -0
- package/dist/agent-src/skills/iconography/SKILL.md +1 -1
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/js-library-packaging/SKILL.md +2 -2
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/monorepo-workspace/SKILL.md +1 -1
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/quality-tools/SKILL.md +11 -1
- package/dist/agent-src/skills/quality-tools/references/go-tools.md +76 -0
- package/dist/agent-src/skills/quality-tools/references/python-tools.md +104 -0
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/review-routing/SKILL.md +2 -2
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +5 -3
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/storybook-workshop/SKILL.md +3 -3
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +4 -2
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +7 -0
- package/dist/agent-src/skills/test-driven-development/SKILL.md +17 -0
- package/dist/agent-src/skills/test-performance/SKILL.md +19 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- package/dist/agent-src/skills/ui-component-architect/SKILL.md +2 -2
- package/dist/agent-src/skills/using-git-worktrees/SKILL.md +1 -1
- package/dist/agent-src/skills/verify-repair-loop/SKILL.md +2 -2
- package/dist/agent-src/templates/AGENTS.md +1 -1
- package/dist/agent-src/templates/agent-settings.md +46 -129
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +3 -3
- package/dist/agent-src/templates/command.md +2 -2
- package/dist/agent-src/templates/roadmaps.md +6 -1
- package/dist/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/dist/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/dist/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/dist/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/dist/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/dist/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/dist/agent-src/templates/skill.md +1 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/archive_completed_roadmaps.js +92 -12
- package/dist/cli-delegate/check_update_banner.js +207 -120
- package/dist/cli-delegate/chunk-523HCFYO.js +90 -0
- package/dist/cli-delegate/{chunk-FP36SWJD.js → chunk-5GPULQBE.js} +2797 -338
- package/dist/cli-delegate/{chunk-IGD6ERX6.js → chunk-6SRJTYJS.js} +42 -176
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- package/dist/cli-delegate/{chunk-M2DPGFZ4.js → chunk-6V6TA25H.js} +22 -152
- package/dist/cli-delegate/chunk-6YQC2HLF.js +415 -0
- package/dist/cli-delegate/{chunk-E4XIZDIB.js → chunk-7HKRE54K.js} +1 -64
- package/dist/cli-delegate/chunk-7IQE3D5S.js +191 -0
- package/dist/cli-delegate/{chunk-YJ72UIAK.js → chunk-AED2VQGM.js} +1245 -249
- package/dist/cli-delegate/{chunk-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/chunk-DEIV7XP7.js +108 -0
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/chunk-F3QDG2GT.js +2569 -0
- package/dist/cli-delegate/{chunk-6KFEC3G7.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-FC75PGJE.js → chunk-H76PHZBI.js} +149 -61
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/chunk-JQOM7TTM.js +1201 -0
- package/dist/cli-delegate/chunk-KB5FZ655.js +208 -0
- package/dist/cli-delegate/{chunk-7RQP4UDH.js → chunk-KR6EM23A.js} +235 -1455
- package/dist/cli-delegate/{chunk-LTYVIY76.js → chunk-KXXWJTDO.js} +6 -11
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-6KM6ORRY.js → chunk-MDPPKQCQ.js} +58 -9
- package/dist/cli-delegate/{chunk-4XFP2Y3E.js → chunk-MSUL5NYP.js} +5 -5
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-4E2W63DA.js → chunk-OKJUIAPS.js} +62 -1
- package/dist/cli-delegate/{chunk-QAJS5YWY.js → chunk-PHD7OOLE.js} +2 -1
- package/dist/cli-delegate/{chunk-P6ZHP5W2.js → chunk-PWX7CKMD.js} +4 -4
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-7XGBX6ZH.js → chunk-T7XSJSUQ.js} +1144 -589
- package/dist/cli-delegate/chunk-ULQRUR23.js +208 -0
- package/dist/cli-delegate/chunk-WQWXGIWB.js +1779 -0
- package/dist/cli-delegate/{chunk-RGNIWTEA.js → chunk-XQTPXKFA.js} +1 -1
- package/dist/cli-delegate/{chunk-NUHLI2RX.js → chunk-YCU3TIXH.js} +2 -0
- package/dist/cli-delegate/chunk-ZBZV2G5R.js +210 -0
- package/dist/cli-delegate/{chunk-WGXTVKCQ.js → chunk-ZUZKZYCN.js} +5 -3
- package/dist/cli-delegate/cmd_analyze_session.js +223 -43
- package/dist/cli-delegate/cmd_brand_status.js +3 -2
- package/dist/cli-delegate/cmd_conformance.js +1252 -249
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +19 -9
- package/dist/cli-delegate/cmd_doctor_neighbours.js +30 -0
- package/dist/cli-delegate/cmd_explain.js +45 -21
- package/dist/cli-delegate/cmd_export.js +13 -10
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +44 -22
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_memory_get.js +3 -1
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +5 -4
- package/dist/cli-delegate/cmd_preflight.js +6 -6
- package/dist/cli-delegate/cmd_prune.js +14 -11
- package/dist/cli-delegate/cmd_refresh.js +2 -1
- package/dist/cli-delegate/cmd_route_audit.js +10 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_session_recycle.js +10 -7
- package/dist/cli-delegate/cmd_settings_check.js +54 -7
- package/dist/cli-delegate/cmd_settings_get.js +18 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +13 -10
- package/dist/cli-delegate/cmd_settings_set.js +4 -4
- package/dist/cli-delegate/cmd_sync.js +14 -11
- package/dist/cli-delegate/cmd_uninstall.js +15 -12
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +9 -8
- package/dist/cli-delegate/cmd_validate.js +15 -12
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/deferral_carry.js +16 -0
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +3 -3
- package/dist/cli-delegate/roadmap_gates.js +6 -6
- package/dist/cli-delegate/update_roadmap_progress.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +232 -141
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +26 -9
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +7 -4
- package/dist/hooks/dispatch.js +5655 -6623
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/install/claudePathsPlan.js +47 -6
- package/dist/install/claudeRuleRewrite.js +7 -0
- package/dist/install/emit_host_rules_cli.js +39 -36
- package/dist/install/globalRuleScope.js +87 -0
- package/dist/install/host_rule_ownership.js +160 -0
- package/dist/install/install.mjs +2336 -1249
- package/dist/install/installThinLayer.js +289 -0
- package/dist/install/installedRuleLinks.js +262 -0
- package/dist/install/wizard-plan.js +24 -0
- package/dist/mcp/awesome-mcp-servers.row.md +1 -1
- package/dist/mcp/mcp-cloudflare-catalogue.json +2 -2
- package/dist/mcp/registry-manifest.json +5 -5
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +36504 -30986
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +26 -120
- package/dist/scripts/_lib/claude_settings_hooks.js +10 -8
- package/dist/scripts/_lib/compile_time_toggles.js +12 -21
- package/dist/scripts/_lib/git_convention.js +232 -0
- package/dist/scripts/_lib/global_deploy_inventory.js +730 -0
- package/dist/scripts/_lib/installed_layer.js +704 -0
- package/dist/scripts/_lib/lean_projection_mode.js +218 -15
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_consequence_class.js +45 -0
- package/dist/scripts/_lib/rule_injection.js +67 -10
- package/dist/scripts/_lib/rule_law_section.js +122 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/thin_rules.js +535 -0
- package/dist/scripts/_lib/windsurf_render.js +60 -0
- package/dist/scripts/condense.js +14 -26
- package/dist/scripts/project_thin_rules.js +14 -294
- package/dist/server/app.js +2 -1
- package/dist/server/gitKeysGate.js +105 -0
- package/dist/server/routes/settings.js +67 -8
- package/dist/server/routes/wizard.js +4 -0
- package/dist/server/schemas/settings.js +29 -81
- package/dist/shared/skillRanking.js +103 -16
- package/dist/ui/assets/{index-Cwq4d0hX.js → index-C3djEJah.js} +2 -2
- package/dist/ui/index.html +1 -1
- package/docs/MIGRATION.md +72 -1
- package/docs/contracts/settings-classes.md +39 -67
- package/docs/guidelines/agent-infra/active-remediation-mechanics.md +39 -0
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/cross-source-consistency-mechanics.md +8 -10
- package/docs/guidelines/agent-infra/frontier-reasoning-operating-profile.md +3 -2
- package/docs/guidelines/agent-infra/layered-settings.md +13 -1
- package/docs/guidelines/agent-infra/roadmap-progress-mechanics.md +1 -1
- package/docs/guidelines/agent-infra/self-improvement-pipeline.md +10 -3
- package/docs/guidelines/agent-infra/size-and-scope.md +36 -0
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +1 -1
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +115 -20
- package/src/agent-src/scripts/deferral_carry.ts +339 -0
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- package/src/agent-src/scripts/update_roadmap_progress.ts +4 -1
- package/src/agent-src/templates/AGENTS.md +1 -1
- package/src/agent-src/templates/scripts/work_engine/_lib/agent_settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/directives/backend/_self_fix.ts +11 -7
- package/src/agent-src/templates/scripts/work_engine/directives/ui/apply.ts +48 -6
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/decision_trace.ts +3 -2
- package/src/agent-src/templates/scripts/work_engine/hooks/builtin/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/hooks/settings.ts +0 -6
- package/src/agent-src/templates/scripts/work_engine/scoring/decision_engine.ts +9 -12
- package/src/agent-src/templates/scripts/work_engine/scoring/memory_visibility.ts +2 -1
- package/src/agent-src/templates/scripts/work_engine/stack/runner.ts +774 -109
- package/src/config/agent-settings.template.yml +141 -334
- package/src/config/agents-paths.yml +16 -0
- package/src/config/assurance-capability-registry.json +7 -3
- package/src/config/ci-local-parity.yml +3 -0
- package/src/config/continuity-surface.json +10 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +23 -10
- package/src/config/gate-coverage.yml +138 -3
- package/src/config/gate-violation-baselines.json +64 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +25 -0
- package/src/config/hook-token-budget.json +6 -1
- package/src/config/host-instruction-limits.json +33 -0
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +67 -4
- package/src/config/rule-activation-census.json +7 -3
- package/src/config/rule-consequence-class.json +161 -0
- package/src/config/rule-law-ceilings.json +105 -0
- package/src/config/rule-obligations.json +833 -0
- package/src/install/claudePathsPlan.ts +46 -4
- package/src/install/claudeRuleRewrite.ts +10 -1
- package/src/install/emit_host_rules_cli.ts +43 -34
- package/src/install/globalRuleScope.ts +109 -0
- package/src/install/host_rule_ownership.ts +170 -0
- package/src/install/installThinLayer.ts +378 -0
- package/src/install/installedRuleLinks.ts +308 -0
- package/src/install/wizard-plan.ts +24 -0
- package/src/scripts/_cli/cmd_analyze_session.ts +9 -15
- package/src/scripts/_cli/cmd_conformance.ts +13 -2
- package/src/scripts/_cli/cmd_doctor.ts +33 -81
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +426 -0
- package/src/scripts/_cli/cmd_explain.ts +72 -14
- package/src/scripts/_cli/cmd_git_convention.ts +637 -0
- package/src/scripts/_cli/cmd_settings_check.ts +66 -8
- package/src/scripts/_cli/cmd_settings_get.ts +18 -3
- package/src/scripts/_cli/doctor_execution.ts +72 -1
- package/src/scripts/_dispatch.bash +53 -4
- package/src/scripts/_lib/agent_settings.ts +26 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/claude_settings_hooks.ts +10 -8
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -0
- package/src/scripts/_lib/estate_offsets.ts +241 -0
- package/src/scripts/_lib/exemption_shape.ts +143 -0
- package/src/scripts/_lib/fnv.ts +23 -0
- package/src/scripts/_lib/foreign_scan.ts +106 -0
- package/src/scripts/_lib/git_base_ref.ts +92 -0
- package/src/scripts/_lib/git_convention.ts +291 -0
- package/src/scripts/_lib/git_convention_carrier.ts +389 -0
- package/src/scripts/_lib/git_convention_grammar.ts +246 -0
- package/src/scripts/_lib/git_convention_measure.ts +344 -0
- package/src/scripts/_lib/gitignore_block.ts +314 -0
- package/src/scripts/_lib/global_deploy_inventory.ts +106 -0
- package/src/scripts/_lib/graph_feeder_record.ts +437 -0
- package/src/scripts/_lib/hook_settings.ts +73 -0
- package/src/scripts/_lib/host_capability.ts +40 -19
- package/src/scripts/_lib/host_hook_merge.ts +165 -0
- package/src/scripts/_lib/installed_layer.ts +963 -0
- package/src/scripts/_lib/json_merge.ts +82 -0
- package/src/scripts/_lib/json_pointers.ts +93 -11
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/lean_projection_mode.ts +275 -15
- package/src/scripts/_lib/machine_wake.ts +16 -3
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_census.ts +760 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_scan.ts +122 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +187 -0
- package/src/scripts/_lib/prompt_shape.ts +46 -0
- package/src/scripts/_lib/ratification_artifact.ts +220 -1
- package/src/scripts/_lib/ratification_subject.ts +92 -0
- package/src/scripts/_lib/release_findings_ingest.ts +15 -7
- package/src/scripts/_lib/release_highlights.ts +5 -0
- package/src/scripts/_lib/release_migration_gate.ts +19 -3
- package/src/scripts/_lib/reserved_name_sweep.ts +73 -0
- package/src/scripts/_lib/review_coverage.ts +134 -0
- package/src/scripts/_lib/rule_consequence_class.ts +71 -0
- package/src/scripts/_lib/rule_injection.ts +130 -10
- package/src/scripts/_lib/rule_law_section.ts +147 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +58 -0
- package/src/scripts/_lib/rule_obligations.ts +205 -0
- package/src/scripts/_lib/self_repair.ts +1 -1
- package/src/scripts/_lib/settings_removed_keys.ts +173 -0
- package/src/scripts/_lib/skill_origin.ts +254 -0
- package/src/scripts/_lib/structured_ask.ts +64 -21
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +620 -0
- package/src/scripts/_lib/touched_file_quality.ts +570 -0
- package/src/scripts/_lib/turn_end_refusals.ts +77 -2
- package/src/scripts/_lib/windsurf_render.ts +63 -0
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/cli_call_budget.ts +13 -5
- package/src/scripts/ai_council/clients.ts +26 -57
- package/src/scripts/ai_council/config.ts +8 -8
- package/src/scripts/ai_council/modification_review.ts +100 -0
- package/src/scripts/ai_council/modification_review_questions.ts +90 -0
- package/src/scripts/ai_council/orchestrator.ts +5 -5
- package/src/scripts/ai_council/spend_gate.ts +18 -7
- package/src/scripts/ai_council/vendor_defaults.ts +45 -0
- package/src/scripts/ai_team/config.ts +11 -16
- package/src/scripts/audit_skill_overlap.ts +261 -0
- package/src/scripts/audit_user_type_axis.ts +20 -8
- package/src/scripts/before_complete_hook.ts +243 -12
- package/src/scripts/bench_ab_clone.ts +1 -2
- package/src/scripts/bench_concern_spawn_path.ts +129 -0
- package/src/scripts/bench_graph_feeder_latency.ts +584 -0
- package/src/scripts/bench_touched_file_quality.ts +238 -0
- package/src/scripts/check_agent_artifact_location.ts +36 -0
- package/src/scripts/check_branch_freshness.ts +147 -24
- package/src/scripts/check_enforcement_coverage.ts +130 -0
- package/src/scripts/check_enforcement_matrix.ts +377 -20
- package/src/scripts/check_estate_count.ts +130 -153
- package/src/scripts/check_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +196 -21
- package/src/scripts/check_gate_coverage.ts +4 -3
- package/src/scripts/check_gate_reachability.ts +48 -1
- package/src/scripts/check_generator_sync.ts +48 -0
- package/src/scripts/check_hook_bundle_composition.ts +477 -0
- package/src/scripts/check_kernel_edit_ratified.ts +132 -14
- package/src/scripts/check_memory.ts +7 -1
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +10 -0
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_release_pr_shape.ts +38 -15
- package/src/scripts/check_standing_rule_delivery.ts +129 -13
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- package/src/scripts/check_verify_expectation_delta.ts +521 -0
- package/src/scripts/ci_settle.ts +42 -5
- package/src/scripts/classify_merge_risk.ts +320 -0
- package/src/scripts/closure_scan.ts +91 -1
- package/src/scripts/code_graph/detect.ts +127 -7
- package/src/scripts/code_graph/foreign.ts +194 -0
- package/src/scripts/code_graph/query.ts +217 -4
- package/src/scripts/code_graph/types.ts +26 -2
- package/src/scripts/code_graph/validate.ts +3 -1
- package/src/scripts/code_graph/verbs.ts +103 -0
- package/src/scripts/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- package/src/scripts/compile_hook_manifest.ts +33 -1
- package/src/scripts/condense.ts +14 -27
- package/src/scripts/consumer_matrix.ts +102 -0
- package/src/scripts/context_hygiene_hook.ts +13 -23
- package/src/scripts/cost/preflight.mjs +12 -1
- package/src/scripts/council_cli.ts +7 -7
- package/src/scripts/dispatch_r2_reviewer.ts +24 -11
- package/src/scripts/estate_exemption_shape.ts +202 -0
- package/src/scripts/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +21 -4
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +175 -15
- package/src/scripts/hooks/block_config_weakening.ts +115 -7
- package/src/scripts/hooks/block_plumbing_writes.ts +37 -5
- package/src/scripts/hooks/code_graph_context_hook.ts +252 -23
- package/src/scripts/hooks/concern_failure_policy.ts +280 -0
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/dispatch_hook.ts +52 -52
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/exit_codes.ts +2 -1
- package/src/scripts/hooks/host_lowering.json +1 -1
- package/src/scripts/hooks/host_lowering.ts +19 -1
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/one_question_per_ask_hook.ts +41 -25
- package/src/scripts/hooks/payload_stub.ts +10 -1
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +795 -60
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/table_fingerprint.ts +21 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +24 -7
- package/src/scripts/hooks/turn_end_gate_hook.ts +85 -0
- package/src/scripts/hooks_status.ts +112 -0
- package/src/scripts/injection_scan_hook.ts +7 -0
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +78 -215
- package/src/scripts/installed_layer_report.ts +103 -0
- package/src/scripts/language_mirror_hook.ts +15 -6
- package/src/scripts/learning_sidecar.ts +119 -8
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_hook_manifest.ts +53 -0
- package/src/scripts/lint_major_migration_sections.ts +156 -26
- package/src/scripts/lint_pack_boundaries.ts +465 -29
- package/src/scripts/lint_roadmap_blockers.ts +282 -29
- package/src/scripts/lint_roadmap_complexity.ts +4 -2
- package/src/scripts/lint_rule_law_section.ts +647 -0
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +173 -6
- package/src/scripts/mcp_server/consumer_tool_catalog.json +53 -0
- package/src/scripts/mcp_server/graph_tools.ts +72 -3
- package/src/scripts/measure_routing_signal.ts +54 -16
- package/src/scripts/measure_skill_ranker_baseline.ts +221 -8
- package/src/scripts/memory_learn_hook.ts +91 -6
- package/src/scripts/model_rule_injection.ts +55 -8
- package/src/scripts/onboarding_gate_hook.ts +7 -6
- package/src/scripts/print_required_checks.ts +15 -3
- package/src/scripts/probe_host_compliance.ts +33 -5
- package/src/scripts/probe_turnaround.ts +183 -3
- package/src/scripts/project_thin_rules.ts +34 -327
- package/src/scripts/ratification_header.ts +77 -0
- package/src/scripts/refusal_envelope.ts +138 -0
- package/src/scripts/release_publication.ts +29 -3
- package/src/scripts/report_dispatch_import_closure.ts +76 -0
- package/src/scripts/report_evidence_temperature.ts +657 -0
- package/src/scripts/report_installed_rule_links.ts +274 -0
- package/src/scripts/report_label_agreement.ts +233 -0
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +228 -0
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_single_token_triggers.ts +277 -0
- package/src/scripts/report_skill_ranker_confusion.ts +543 -0
- package/src/scripts/report_standing_payload_by_host.ts +188 -15
- package/src/scripts/report_verification_record_coverage.ts +306 -0
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/rule_activation_census.ts +5 -2
- package/src/scripts/rule_trigger_eval.ts +9 -9
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/review-findings.schema.json +3 -2
- package/src/scripts/schemas/rule.schema.json +5 -0
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +101 -9
- package/src/scripts/skill_tools/score_skill_relevance.ts +314 -40
- package/src/scripts/sweep_skill_ranker_signals.ts +129 -0
- package/src/scripts/sync_gitignore.ts +29 -235
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -21
- package/src/server/schemas/settings.ts +42 -176
- package/src/shared/skillRanking.ts +169 -14
- package/dist/cli-delegate/chunk-5R7HOLAF.js +0 -99
- package/dist/cli-delegate/chunk-JNMVLWLR.js +0 -4457
- package/dist/cli-delegate/chunk-YDPILCOL.js +0 -152
- package/src/scripts/_lib/config_chain.ts +0 -477
- package/src/scripts/_lib/conformance_report.ts +0 -403
- package/src/scripts/_lib/eval_discrimination.ts +0 -95
- package/src/scripts/_lib/evaluator_promotion.ts +0 -225
- package/src/scripts/_lib/file_slicer.ts +0 -117
- package/src/scripts/_lib/legacy_boundary_map.ts +0 -228
- package/src/scripts/_lib/minimality_tiebreak.ts +0 -168
- package/src/scripts/_lib/overbuild_lens_contract.ts +0 -196
- package/src/scripts/_lib/role_split.ts +0 -338
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
2
|
import {
|
|
3
|
-
|
|
4
|
-
} from "./chunk-
|
|
3
|
+
read_lockfile
|
|
4
|
+
} from "./chunk-LQOCRFWT.js";
|
|
5
5
|
import {
|
|
6
6
|
has_stable_session_id,
|
|
7
7
|
owns_session_state,
|
|
@@ -9,213 +9,13 @@ import {
|
|
|
9
9
|
prune_stale_session_states,
|
|
10
10
|
session_state_file,
|
|
11
11
|
update_json_under_lock
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-7HKRE54K.js";
|
|
13
13
|
import {
|
|
14
|
-
|
|
15
|
-
} from "./chunk-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
var BASE64_ENTROPY_THRESHOLD = 4.5;
|
|
20
|
-
var HEX_ENTROPY_THRESHOLD = 3;
|
|
21
|
-
var RULES = [
|
|
22
|
-
{ rule: "anthropic-api-key", kind: "anthropic-api-key", regex: /sk-ant-[A-Za-z0-9_-]{20,}/g },
|
|
23
|
-
{ rule: "anthropic-project-key", kind: "anthropic-api-key", regex: /sk-proj-[A-Za-z0-9_-]{20,}/g },
|
|
24
|
-
{ rule: "aws-access-key", kind: "aws-access-key", regex: /AKIA[0-9A-Z]{16}/g },
|
|
25
|
-
{ rule: "google-api-key", kind: "google-api-key", regex: /AIza[0-9A-Za-z_-]{35}/g },
|
|
26
|
-
{ rule: "github-pat", kind: "github-pat", regex: /ghp_[0-9A-Za-z]{36}/g },
|
|
27
|
-
// Fine-grained PATs (github_pat_…) were a measured detector gap: the
|
|
28
|
-
// 2026-07-28 adversarial corpus pass flagged them as missed positives.
|
|
29
|
-
{ rule: "github-fine-grained-pat", kind: "github-pat", regex: /github_pat_[0-9A-Za-z_]{22,}/g },
|
|
30
|
-
{
|
|
31
|
-
rule: "jwt",
|
|
32
|
-
kind: "jwt",
|
|
33
|
-
regex: /eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g
|
|
34
|
-
},
|
|
35
|
-
// Extensions.
|
|
36
|
-
{ rule: "stripe-secret-key", kind: "stripe-secret-key", regex: /sk_(?:live|test)_[0-9A-Za-z]{16,}/g },
|
|
37
|
-
{ rule: "slack-token", kind: "slack-token", regex: /xox[baprs]-[0-9A-Za-z-]{10,}/g },
|
|
38
|
-
{ rule: "pem-private-key", kind: "pem-private-key", regex: /-----BEGIN [A-Z ]*PRIVATE KEY-----/g },
|
|
39
|
-
{
|
|
40
|
-
rule: "db-connection-uri",
|
|
41
|
-
kind: "db-connection-uri",
|
|
42
|
-
regex: /\b[a-z][a-z0-9+.-]*:\/\/[^\s:/@]+:[^\s:/@]+@/g
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
rule: "generic-assignment",
|
|
46
|
-
kind: "generic-assignment",
|
|
47
|
-
regex: /(?:password|passwd|pwd|secret|api[_-]?key|access[_-]?token|auth[_-]?token)\s*[:=]\s*['"]?([^'"\s]{8,})['"]?/gi,
|
|
48
|
-
valueGroup: 1
|
|
49
|
-
}
|
|
50
|
-
];
|
|
51
|
-
var SECRET_ALLOW = /secret-allow/;
|
|
52
|
-
var PLACEHOLDER_PATTERNS = [
|
|
53
|
-
/x{4,}/i,
|
|
54
|
-
/example/i,
|
|
55
|
-
/changeme/i,
|
|
56
|
-
/your[_-].*here/i,
|
|
57
|
-
/<[^>]+>/,
|
|
58
|
-
/\.\.\./,
|
|
59
|
-
/^your[_-]/i,
|
|
60
|
-
/placeholder/i
|
|
61
|
-
];
|
|
62
|
-
var DATA_URI = /data:[\w.+-]+\/[\w.+-]+;base64,/i;
|
|
63
|
-
var UUID = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
|
|
64
|
-
var KEYWORD_CONTEXT = /password|secret|token|api[_-]?key|credential/i;
|
|
65
|
-
var ENTROPY_TOKEN = /[A-Za-z0-9+=_-]{20,}/g;
|
|
66
|
-
var IS_HEX = /^[0-9a-fA-F]+$/;
|
|
67
|
-
var HASH_HEX_LENGTHS = /* @__PURE__ */ new Set([32, 40, 64]);
|
|
68
|
-
function isAllSameChar(value) {
|
|
69
|
-
return value.length > 0 && /^(.)\1*$/.test(value);
|
|
70
|
-
}
|
|
71
|
-
var GENERIC_MIN_LENGTH = 16;
|
|
72
|
-
var GENERIC_MIN_ENTROPY = 3.3;
|
|
73
|
-
var CODE_EXPR = /[()$`{}<>[\];,]|->|=>|\.\w/;
|
|
74
|
-
function looksLikeSecretValue(value) {
|
|
75
|
-
if (value.length < GENERIC_MIN_LENGTH) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
if (CODE_EXPR.test(value)) {
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
const hasDigit = /[0-9]/.test(value);
|
|
82
|
-
const mixedCase = /[a-z]/.test(value) && /[A-Z]/.test(value);
|
|
83
|
-
if (!hasDigit && !mixedCase) {
|
|
84
|
-
return false;
|
|
85
|
-
}
|
|
86
|
-
return shannonEntropy(value) >= GENERIC_MIN_ENTROPY;
|
|
87
|
-
}
|
|
88
|
-
function isPlaceholderValue(value, pathHint) {
|
|
89
|
-
if (pathHint !== void 0) {
|
|
90
|
-
if (pathHint.endsWith(".example") || pathHint.endsWith(".sample") || pathHint.includes(".env.example")) {
|
|
91
|
-
return true;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
if (isAllSameChar(value)) {
|
|
95
|
-
return true;
|
|
96
|
-
}
|
|
97
|
-
return PLACEHOLDER_PATTERNS.some((p) => p.test(value));
|
|
98
|
-
}
|
|
99
|
-
function maskSecret(value) {
|
|
100
|
-
const front = value.slice(0, 4);
|
|
101
|
-
const back = value.length > 6 ? value.slice(-2) : "";
|
|
102
|
-
if (front.length + back.length >= value.length) {
|
|
103
|
-
return `${value.slice(0, 1)}\u2026`;
|
|
104
|
-
}
|
|
105
|
-
return `${front}\u2026${back}`;
|
|
106
|
-
}
|
|
107
|
-
function shannonEntropy(value) {
|
|
108
|
-
const freq = /* @__PURE__ */ new Map();
|
|
109
|
-
for (const ch of value) {
|
|
110
|
-
freq.set(ch, (freq.get(ch) ?? 0) + 1);
|
|
111
|
-
}
|
|
112
|
-
const n = value.length;
|
|
113
|
-
let entropy = 0;
|
|
114
|
-
for (const count of freq.values()) {
|
|
115
|
-
const p = count / n;
|
|
116
|
-
entropy -= p * Math.log2(p);
|
|
117
|
-
}
|
|
118
|
-
return entropy;
|
|
119
|
-
}
|
|
120
|
-
function overlaps(spans, start, end) {
|
|
121
|
-
return spans.some(([s, e]) => start < e && s < end);
|
|
122
|
-
}
|
|
123
|
-
function scanText(text, opts = {}) {
|
|
124
|
-
const pathHint = opts.path;
|
|
125
|
-
const findings = [];
|
|
126
|
-
const lines = text.split(/\r\n|\r|\n/);
|
|
127
|
-
for (let i = 0; i < lines.length; i++) {
|
|
128
|
-
const line = lines[i];
|
|
129
|
-
const lineNo = i + 1;
|
|
130
|
-
if (SECRET_ALLOW.test(line)) {
|
|
131
|
-
continue;
|
|
132
|
-
}
|
|
133
|
-
const covered = [];
|
|
134
|
-
for (const rule of RULES) {
|
|
135
|
-
rule.regex.lastIndex = 0;
|
|
136
|
-
let m;
|
|
137
|
-
while ((m = rule.regex.exec(line)) !== null) {
|
|
138
|
-
const value = rule.valueGroup !== void 0 ? m[rule.valueGroup] : m[0];
|
|
139
|
-
if (value === void 0) {
|
|
140
|
-
if (m.index === rule.regex.lastIndex) {
|
|
141
|
-
rule.regex.lastIndex += 1;
|
|
142
|
-
}
|
|
143
|
-
continue;
|
|
144
|
-
}
|
|
145
|
-
if (rule.rule === "generic-assignment" && !looksLikeSecretValue(value)) {
|
|
146
|
-
if (m.index === rule.regex.lastIndex) {
|
|
147
|
-
rule.regex.lastIndex += 1;
|
|
148
|
-
}
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
const valueStart = m.index + m[0].indexOf(value);
|
|
152
|
-
const confidence = isPlaceholderValue(value, pathHint) ? "low" : "high";
|
|
153
|
-
findings.push({
|
|
154
|
-
rule: rule.rule,
|
|
155
|
-
kind: rule.kind,
|
|
156
|
-
line: lineNo,
|
|
157
|
-
column: valueStart + 1,
|
|
158
|
-
masked: maskSecret(value),
|
|
159
|
-
confidence
|
|
160
|
-
});
|
|
161
|
-
covered.push([valueStart, valueStart + value.length]);
|
|
162
|
-
if (m.index === rule.regex.lastIndex) {
|
|
163
|
-
rule.regex.lastIndex += 1;
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
if (!DATA_URI.test(line)) {
|
|
168
|
-
const hasKeyword = KEYWORD_CONTEXT.test(line);
|
|
169
|
-
ENTROPY_TOKEN.lastIndex = 0;
|
|
170
|
-
let tm;
|
|
171
|
-
while ((tm = ENTROPY_TOKEN.exec(line)) !== null) {
|
|
172
|
-
const token = tm[0];
|
|
173
|
-
const start = tm.index;
|
|
174
|
-
const end = start + token.length;
|
|
175
|
-
if (token.length < ENTROPY_MIN_TOKEN_LENGTH) {
|
|
176
|
-
continue;
|
|
177
|
-
}
|
|
178
|
-
if (overlaps(covered, start, end)) {
|
|
179
|
-
continue;
|
|
180
|
-
}
|
|
181
|
-
if (UUID.test(token)) {
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
if (/^sha(?:256|384|512)-/.test(token)) {
|
|
185
|
-
continue;
|
|
186
|
-
}
|
|
187
|
-
const entropy = shannonEntropy(token);
|
|
188
|
-
const isHex = IS_HEX.test(token);
|
|
189
|
-
if (isHex && HASH_HEX_LENGTHS.has(token.length)) {
|
|
190
|
-
continue;
|
|
191
|
-
}
|
|
192
|
-
let confidence = null;
|
|
193
|
-
if (isHex) {
|
|
194
|
-
if (entropy >= HEX_ENTROPY_THRESHOLD && hasKeyword) {
|
|
195
|
-
confidence = "high";
|
|
196
|
-
}
|
|
197
|
-
} else if (entropy >= BASE64_ENTROPY_THRESHOLD) {
|
|
198
|
-
confidence = hasKeyword ? "high" : "medium";
|
|
199
|
-
}
|
|
200
|
-
if (confidence === null) {
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (isPlaceholderValue(token, pathHint)) {
|
|
204
|
-
confidence = "low";
|
|
205
|
-
}
|
|
206
|
-
findings.push({
|
|
207
|
-
rule: "entropy",
|
|
208
|
-
kind: "high-entropy-string",
|
|
209
|
-
line: lineNo,
|
|
210
|
-
column: start + 1,
|
|
211
|
-
masked: maskSecret(token),
|
|
212
|
-
confidence
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
}
|
|
216
|
-
}
|
|
217
|
-
return findings;
|
|
218
|
-
}
|
|
14
|
+
scanText
|
|
15
|
+
} from "./chunk-KB5FZ655.js";
|
|
16
|
+
import {
|
|
17
|
+
readHookStdin
|
|
18
|
+
} from "./chunk-F7DQWYR5.js";
|
|
219
19
|
|
|
220
20
|
// src/scripts/_lib/subagent_capsule.ts
|
|
221
21
|
var EPISTEMIC_STATES = ["verified", "assumed", "gap"];
|
|
@@ -561,10 +361,57 @@ function validateAssumption(input, label = "assumption") {
|
|
|
561
361
|
}
|
|
562
362
|
|
|
563
363
|
// src/scripts/before_complete_hook.ts
|
|
564
|
-
import
|
|
565
|
-
import
|
|
364
|
+
import fs4 from "node:fs";
|
|
365
|
+
import path4 from "node:path";
|
|
566
366
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
567
367
|
|
|
368
|
+
// src/scripts/_lib/hook_settings.ts
|
|
369
|
+
import * as fs from "node:fs";
|
|
370
|
+
import * as path from "node:path";
|
|
371
|
+
var SETTINGS_FILE = ".agent-settings.yml";
|
|
372
|
+
function hookSectionValue(root, section, key) {
|
|
373
|
+
const file = path.join(root, SETTINGS_FILE);
|
|
374
|
+
let text;
|
|
375
|
+
try {
|
|
376
|
+
if (!fs.statSync(file).isFile()) return "";
|
|
377
|
+
text = fs.readFileSync(file, "utf-8");
|
|
378
|
+
} catch {
|
|
379
|
+
return "";
|
|
380
|
+
}
|
|
381
|
+
const esc = (s) => s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
382
|
+
const sectionPattern = new RegExp(`^(\\s+)${esc(section)}\\s*:\\s*$`);
|
|
383
|
+
const keyPattern = new RegExp(`^\\s+${esc(key)}\\s*:\\s*(.+)$`);
|
|
384
|
+
let inHooks = false;
|
|
385
|
+
let sectionIndent = null;
|
|
386
|
+
const indentOf = (line) => line.length - line.replace(/^\s+/, "").length;
|
|
387
|
+
for (const raw of text.split(/\r\n|\r|\n/)) {
|
|
388
|
+
const line = raw.replace(/\s+$/, "");
|
|
389
|
+
if (!line || line.replace(/^\s+/, "").startsWith("#")) continue;
|
|
390
|
+
if (!(line.startsWith(" ") || line.startsWith(" "))) {
|
|
391
|
+
inHooks = /^hooks\s*:\s*$/.test(line);
|
|
392
|
+
sectionIndent = null;
|
|
393
|
+
continue;
|
|
394
|
+
}
|
|
395
|
+
if (inHooks) {
|
|
396
|
+
const opened = sectionPattern.exec(line);
|
|
397
|
+
if (opened) {
|
|
398
|
+
sectionIndent = opened[1].length;
|
|
399
|
+
continue;
|
|
400
|
+
}
|
|
401
|
+
if (sectionIndent !== null && indentOf(line) <= sectionIndent) {
|
|
402
|
+
sectionIndent = null;
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
if (sectionIndent !== null) {
|
|
406
|
+
const m = keyPattern.exec(line);
|
|
407
|
+
if (m) {
|
|
408
|
+
return (m[1] ?? "").replace(/\s+#.*$/, "").trim().replace(/^["']|["']$/g, "");
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return "";
|
|
413
|
+
}
|
|
414
|
+
|
|
568
415
|
// src/scripts/_lib/verification_command.ts
|
|
569
416
|
var VERIFY_BINARIES = /* @__PURE__ */ new Set([
|
|
570
417
|
"vitest",
|
|
@@ -733,6 +580,241 @@ function mightBeVerification(command) {
|
|
|
733
580
|
return _WIDE_CANDIDATE_RE.test(command);
|
|
734
581
|
}
|
|
735
582
|
|
|
583
|
+
// src/scripts/_lib/touched_file_quality.ts
|
|
584
|
+
import { spawnSync } from "node:child_process";
|
|
585
|
+
import * as fs2 from "node:fs";
|
|
586
|
+
import * as path2 from "node:path";
|
|
587
|
+
var OUTPUT_HEAD_LINES = 20;
|
|
588
|
+
var DEFAULT_TIMEOUT_MS = 3e4;
|
|
589
|
+
var ESLINT_IGNORED = /^\s+\d+:\d+\s+warning\s+File ignored because of a matching ignore pattern\b/;
|
|
590
|
+
var JS_EXTENSIONS = [".js", ".jsx", ".mjs", ".cjs", ".ts", ".tsx", ".mts", ".cts"];
|
|
591
|
+
var SCOPED_FORMS = /* @__PURE__ */ new Map([
|
|
592
|
+
[
|
|
593
|
+
"npx eslint .",
|
|
594
|
+
{
|
|
595
|
+
argv: ["npx", "eslint"],
|
|
596
|
+
extensions: JS_EXTENSIONS,
|
|
597
|
+
check_form: false,
|
|
598
|
+
ignored_signal: ESLINT_IGNORED
|
|
599
|
+
}
|
|
600
|
+
],
|
|
601
|
+
[
|
|
602
|
+
"vendor/bin/phpstan analyse",
|
|
603
|
+
{ argv: ["vendor/bin/phpstan", "analyse"], extensions: [".php"], check_form: false }
|
|
604
|
+
],
|
|
605
|
+
// `vendor/bin/pint` REWRITES source. `--test` is its check form: same tool,
|
|
606
|
+
// same rules, reports instead of writing. Risk 3 of the roadmap is that this
|
|
607
|
+
// module reformats the consumer's tree; this row plus the fixture that proves
|
|
608
|
+
// no file changed is the mitigation.
|
|
609
|
+
[
|
|
610
|
+
"vendor/bin/pint",
|
|
611
|
+
{ argv: ["vendor/bin/pint", "--test"], extensions: [".php"], check_form: true }
|
|
612
|
+
],
|
|
613
|
+
["ruff check", { argv: ["ruff", "check"], extensions: [".py"], check_form: false }],
|
|
614
|
+
["mypy .", { argv: ["mypy"], extensions: [".py"], check_form: false }]
|
|
615
|
+
]);
|
|
616
|
+
var MUTATING_WITHOUT_CHECK_FORM = /* @__PURE__ */ new Set([]);
|
|
617
|
+
var SCOPED_FORM_COMMANDS = [...SCOPED_FORMS.keys()];
|
|
618
|
+
var MUTATING_COMMANDS = [...MUTATING_WITHOUT_CHECK_FORM];
|
|
619
|
+
var TYPE_CHECK_COMMANDS = /* @__PURE__ */ new Set([
|
|
620
|
+
"npx tsc --noEmit",
|
|
621
|
+
"vendor/bin/phpstan analyse",
|
|
622
|
+
"mypy .",
|
|
623
|
+
"go vet ./...",
|
|
624
|
+
"cargo clippy"
|
|
625
|
+
]);
|
|
626
|
+
function extensionOf(file) {
|
|
627
|
+
const base = path2.basename(file);
|
|
628
|
+
const dot = base.lastIndexOf(".");
|
|
629
|
+
return dot <= 0 ? "" : base.slice(dot).toLowerCase();
|
|
630
|
+
}
|
|
631
|
+
function planQualityRuns(commands, files) {
|
|
632
|
+
return commands.map((source_command) => {
|
|
633
|
+
if (MUTATING_WITHOUT_CHECK_FORM.has(source_command)) {
|
|
634
|
+
return { source_command, argv: null, files: [], skipped: "mutating" };
|
|
635
|
+
}
|
|
636
|
+
const spec = SCOPED_FORMS.get(source_command);
|
|
637
|
+
if (spec === void 0) {
|
|
638
|
+
return { source_command, argv: null, files: [], skipped: "unscoped" };
|
|
639
|
+
}
|
|
640
|
+
const matched = spec.extensions.length === 0 ? [...files] : files.filter((f) => spec.extensions.includes(extensionOf(f)));
|
|
641
|
+
if (matched.length === 0) {
|
|
642
|
+
return { source_command, argv: null, files: [], skipped: "no_files" };
|
|
643
|
+
}
|
|
644
|
+
return { source_command, argv: [...spec.argv, ...matched], files: matched, skipped: null };
|
|
645
|
+
});
|
|
646
|
+
}
|
|
647
|
+
function renderCommand(argv) {
|
|
648
|
+
return argv.join(" ");
|
|
649
|
+
}
|
|
650
|
+
function headLines(text, limit) {
|
|
651
|
+
if (text === "") return [];
|
|
652
|
+
return text.split(/\r\n|\r|\n/).filter((line, i, all) => !(line === "" && i === all.length - 1)).slice(0, limit);
|
|
653
|
+
}
|
|
654
|
+
function now() {
|
|
655
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
|
|
656
|
+
}
|
|
657
|
+
function defaultSpawn(argv, cwd, timeout_ms) {
|
|
658
|
+
const [head, ...rest] = argv;
|
|
659
|
+
if (head === void 0) return { status: null, output: "", enoent: true };
|
|
660
|
+
const res = spawnSync(head, rest, {
|
|
661
|
+
cwd,
|
|
662
|
+
timeout: timeout_ms,
|
|
663
|
+
encoding: "utf-8",
|
|
664
|
+
// No shell. The argv comes from a committed table, and a shell would
|
|
665
|
+
// make a file path with a space into an injection surface.
|
|
666
|
+
shell: false,
|
|
667
|
+
maxBuffer: 4 * 1024 * 1024
|
|
668
|
+
});
|
|
669
|
+
const enoent = res.error !== void 0 && res.error.code === "ENOENT";
|
|
670
|
+
const output2 = `${res.stdout ?? ""}${res.stderr ?? ""}`;
|
|
671
|
+
return { status: res.status, output: output2, enoent };
|
|
672
|
+
}
|
|
673
|
+
function runTouchedFileQuality(opts) {
|
|
674
|
+
const timeout_ms = opts.timeout_ms ?? DEFAULT_TIMEOUT_MS;
|
|
675
|
+
const spawn = opts.spawn ?? defaultSpawn;
|
|
676
|
+
const out = [];
|
|
677
|
+
for (const plan of planQualityRuns(opts.commands, opts.files)) {
|
|
678
|
+
if (plan.argv === null) {
|
|
679
|
+
out.push({
|
|
680
|
+
command: plan.source_command,
|
|
681
|
+
source_command: plan.source_command,
|
|
682
|
+
exit_code: null,
|
|
683
|
+
output_head: [],
|
|
684
|
+
skipped: plan.skipped,
|
|
685
|
+
files: [],
|
|
686
|
+
ignored_files: [],
|
|
687
|
+
at: now()
|
|
688
|
+
});
|
|
689
|
+
continue;
|
|
690
|
+
}
|
|
691
|
+
let status = null;
|
|
692
|
+
let output2 = "";
|
|
693
|
+
let enoent = false;
|
|
694
|
+
try {
|
|
695
|
+
const res = spawn(plan.argv, opts.root, timeout_ms);
|
|
696
|
+
status = res.status;
|
|
697
|
+
output2 = res.output;
|
|
698
|
+
enoent = res.enoent;
|
|
699
|
+
} catch {
|
|
700
|
+
enoent = true;
|
|
701
|
+
}
|
|
702
|
+
const absent = enoent || status === 127;
|
|
703
|
+
const signal = SCOPED_FORMS.get(plan.source_command)?.ignored_signal;
|
|
704
|
+
const ignored = absent || signal === void 0 ? [] : ignoredFiles(output2, signal, plan.files);
|
|
705
|
+
const nothingLooked = ignored.length === plan.files.length && status === 0;
|
|
706
|
+
out.push({
|
|
707
|
+
command: renderCommand(plan.argv),
|
|
708
|
+
source_command: plan.source_command,
|
|
709
|
+
exit_code: absent || nothingLooked ? null : status,
|
|
710
|
+
output_head: absent ? [] : headLines(output2, OUTPUT_HEAD_LINES),
|
|
711
|
+
skipped: absent ? "absent" : null,
|
|
712
|
+
files: plan.files,
|
|
713
|
+
ignored_files: ignored,
|
|
714
|
+
at: now()
|
|
715
|
+
});
|
|
716
|
+
}
|
|
717
|
+
const typecheck_not_run = out.filter(
|
|
718
|
+
(r) => TYPE_CHECK_COMMANDS.has(r.source_command) && r.exit_code === null && r.skipped !== "no_files" && r.skipped !== null
|
|
719
|
+
).map((r) => r.source_command);
|
|
720
|
+
return out.map((r) => ({ ...r, typecheck_not_run: [...typecheck_not_run] }));
|
|
721
|
+
}
|
|
722
|
+
function ignoredFiles(output2, signal, files) {
|
|
723
|
+
const reported = [];
|
|
724
|
+
let unattributed = false;
|
|
725
|
+
let current = null;
|
|
726
|
+
for (const line of output2.split(/\r\n|\r|\n/)) {
|
|
727
|
+
if (signal.test(line)) {
|
|
728
|
+
if (current === null) unattributed = true;
|
|
729
|
+
else reported.push(normalizePath(current));
|
|
730
|
+
continue;
|
|
731
|
+
}
|
|
732
|
+
if (line !== "" && !/^\s/.test(line)) current = line.trim();
|
|
733
|
+
}
|
|
734
|
+
const hit = files.filter((f) => {
|
|
735
|
+
const want = normalizePath(f);
|
|
736
|
+
return reported.some((r) => r === want || r.endsWith(`/${want}`));
|
|
737
|
+
});
|
|
738
|
+
if (hit.length === 0 && unattributed && files.length === 1) return [...files];
|
|
739
|
+
return hit;
|
|
740
|
+
}
|
|
741
|
+
var WARN_LINE_MAX_BYTES = 200;
|
|
742
|
+
function advisoryLine(runs) {
|
|
743
|
+
const ignored = [...new Set(runs.flatMap((r) => r.ignored_files))];
|
|
744
|
+
const ignoredPart = ignored.length === 0 ? "" : `ignored ${ignored[0] ?? ""}${ignored.length > 1 ? ` (+${String(ignored.length - 1)} more)` : ""}`;
|
|
745
|
+
const failed = runs.find((r) => r.skipped === null && r.exit_code !== null && r.exit_code !== 0);
|
|
746
|
+
if (failed === void 0) {
|
|
747
|
+
if (ignored.length === 0) return null;
|
|
748
|
+
return truncateToBytes(
|
|
749
|
+
`touched-file quality: no verdict, ${ignoredPart}`,
|
|
750
|
+
WARN_LINE_MAX_BYTES
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
const file = failed.files[0] ?? "(no file)";
|
|
754
|
+
const more = failed.files.length > 1 ? ` (+${String(failed.files.length - 1)} more)` : "";
|
|
755
|
+
const tail = ignoredPart === "" ? "" : `; ${ignoredPart}`;
|
|
756
|
+
const line = `touched-file quality: ${failed.source_command} exited ${String(failed.exit_code)} on ${file}${more}${tail}`;
|
|
757
|
+
return truncateToBytes(line, WARN_LINE_MAX_BYTES);
|
|
758
|
+
}
|
|
759
|
+
function truncateToBytes(text, max) {
|
|
760
|
+
const buf = Buffer.from(text, "utf-8");
|
|
761
|
+
if (buf.length <= max) return text;
|
|
762
|
+
let end = max;
|
|
763
|
+
while (end > 0 && (buf[end] ?? 0) >= 128 && (buf[end] ?? 0) < 192) end -= 1;
|
|
764
|
+
return buf.subarray(0, end).toString("utf-8");
|
|
765
|
+
}
|
|
766
|
+
function touchedFilesAtStop(root, session_id, gitStatus = defaultGitStatus) {
|
|
767
|
+
let recorded;
|
|
768
|
+
try {
|
|
769
|
+
const raw = fs2.readFileSync(
|
|
770
|
+
path2.join(root, "agents", "state", "minimal-safe-diff.json"),
|
|
771
|
+
"utf-8"
|
|
772
|
+
);
|
|
773
|
+
const decoded = JSON.parse(raw);
|
|
774
|
+
if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) {
|
|
775
|
+
return { files: [], source: "none" };
|
|
776
|
+
}
|
|
777
|
+
const state = decoded;
|
|
778
|
+
if (state["session_id"] !== session_id || session_id === "") {
|
|
779
|
+
return { files: [], source: "none" };
|
|
780
|
+
}
|
|
781
|
+
const touched = state["files_touched_this_turn"];
|
|
782
|
+
if (!Array.isArray(touched)) return { files: [], source: "none" };
|
|
783
|
+
recorded = touched.filter((x) => typeof x === "string");
|
|
784
|
+
} catch {
|
|
785
|
+
return { files: [], source: "none" };
|
|
786
|
+
}
|
|
787
|
+
const changed = new Set(gitStatus(root));
|
|
788
|
+
const files = recorded.filter((f) => changed.has(normalizePath(f)));
|
|
789
|
+
return { files, source: "minimal-safe-diff\u2229worktree" };
|
|
790
|
+
}
|
|
791
|
+
function normalizePath(p) {
|
|
792
|
+
return p.replace(/\\/g, "/").replace(/^\.\//, "");
|
|
793
|
+
}
|
|
794
|
+
function defaultGitStatus(root) {
|
|
795
|
+
const res = spawnSync("git", ["status", "--porcelain", "-z"], {
|
|
796
|
+
cwd: root,
|
|
797
|
+
encoding: "utf-8",
|
|
798
|
+
timeout: 1e4,
|
|
799
|
+
shell: false,
|
|
800
|
+
maxBuffer: 4 * 1024 * 1024
|
|
801
|
+
});
|
|
802
|
+
if (res.status !== 0 || typeof res.stdout !== "string") return [];
|
|
803
|
+
return parsePorcelainZ(res.stdout);
|
|
804
|
+
}
|
|
805
|
+
function parsePorcelainZ(stdout) {
|
|
806
|
+
const out = [];
|
|
807
|
+
const parts = stdout.split("\0");
|
|
808
|
+
for (let i = 0; i < parts.length; i += 1) {
|
|
809
|
+
const entry = parts[i];
|
|
810
|
+
if (entry === void 0 || entry.length < 4) continue;
|
|
811
|
+
const status = entry.slice(0, 2);
|
|
812
|
+
out.push(normalizePath(entry.slice(3)));
|
|
813
|
+
if (status.startsWith("R") || status.startsWith("C")) i += 1;
|
|
814
|
+
}
|
|
815
|
+
return out;
|
|
816
|
+
}
|
|
817
|
+
|
|
736
818
|
// src/scripts/_lib/verification_evidence.ts
|
|
737
819
|
var INSTRUMENT_GAP_REASONS = /* @__PURE__ */ new Set([
|
|
738
820
|
"exit_code_unavailable",
|
|
@@ -1029,9 +1111,814 @@ function describeRecordFinding(failed, reasons) {
|
|
|
1029
1111
|
}
|
|
1030
1112
|
var NO_RED_EVIDENCE_REASON = "a completion claim over production code this turn changed, with a test file touched but `no_red_evidence` \u2014 no recorded run shows that test target failing and then passing after the last edit. A test never seen red has unknown sensitivity: run it against the unfixed code, watch it fail for the intended reason, then fix and re-run";
|
|
1031
1113
|
|
|
1114
|
+
// src/agent-src/templates/scripts/work_engine/stack/runner.ts
|
|
1115
|
+
import * as fs3 from "node:fs";
|
|
1116
|
+
import * as path3 from "node:path";
|
|
1117
|
+
var BEHAVIOR_UNKNOWN = "unknown";
|
|
1118
|
+
var SPEED_FAST = "fast";
|
|
1119
|
+
var SPEED_SLOW = "slow";
|
|
1120
|
+
var SPEED_E2E = "e2e";
|
|
1121
|
+
var HIGH = "HIGH";
|
|
1122
|
+
var MEDIUM = "MEDIUM";
|
|
1123
|
+
var LOW = "LOW";
|
|
1124
|
+
var _MANIFESTS = [
|
|
1125
|
+
"composer.json",
|
|
1126
|
+
"package.json",
|
|
1127
|
+
"pyproject.toml",
|
|
1128
|
+
"go.mod",
|
|
1129
|
+
"Cargo.toml",
|
|
1130
|
+
"Makefile",
|
|
1131
|
+
"Taskfile.yml",
|
|
1132
|
+
"Taskfile.yaml",
|
|
1133
|
+
"Gemfile",
|
|
1134
|
+
"pom.xml",
|
|
1135
|
+
"build.gradle",
|
|
1136
|
+
"build.gradle.kts",
|
|
1137
|
+
// `settings.gradle[.kts]` is a Gradle signal `_jvm_build` acts on, so it
|
|
1138
|
+
// must be in the key: a settings-only multi-project root emits a junit row
|
|
1139
|
+
// and would otherwise report the greenfield `mtime: 0` forever.
|
|
1140
|
+
"settings.gradle",
|
|
1141
|
+
"settings.gradle.kts",
|
|
1142
|
+
"global.json",
|
|
1143
|
+
"Directory.Build.props"
|
|
1144
|
+
];
|
|
1145
|
+
var _DOTNET_PROJECT_EXTS = [".sln", ".slnx", ".csproj", ".fsproj", ".vbproj"];
|
|
1146
|
+
var _DOTNET_TEST_STACK = /Microsoft\.NET\.Test\.Sdk|xunit|nunit|mstest/i;
|
|
1147
|
+
var _DOTNET_SCAN_DEPTH = 2;
|
|
1148
|
+
var _DOTNET_MAX_PROJECTS = 50;
|
|
1149
|
+
var _DOTNET_SOLUTION_EXTS = [".sln", ".slnx"];
|
|
1150
|
+
function _has_dotnet_solution(dir) {
|
|
1151
|
+
try {
|
|
1152
|
+
return fs3.readdirSync(dir, { withFileTypes: true }).some((e) => e.isFile() && _DOTNET_SOLUTION_EXTS.includes(path3.extname(e.name).toLowerCase()));
|
|
1153
|
+
} catch {
|
|
1154
|
+
return false;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
var _BEHAVIOR_MARKERS = [
|
|
1158
|
+
"pnpm-workspace.yaml",
|
|
1159
|
+
"requirements.txt",
|
|
1160
|
+
"gems.rb",
|
|
1161
|
+
".rspec",
|
|
1162
|
+
"behat.yml",
|
|
1163
|
+
"behat.yaml",
|
|
1164
|
+
"behat.yml.dist",
|
|
1165
|
+
"behat.dist.yml",
|
|
1166
|
+
"cucumber.js",
|
|
1167
|
+
"cucumber.cjs",
|
|
1168
|
+
"cucumber.mjs",
|
|
1169
|
+
"cucumber.json"
|
|
1170
|
+
];
|
|
1171
|
+
var RunnerResult = class {
|
|
1172
|
+
ecosystem;
|
|
1173
|
+
runner;
|
|
1174
|
+
command;
|
|
1175
|
+
speed;
|
|
1176
|
+
confidence;
|
|
1177
|
+
basis;
|
|
1178
|
+
constructor(ecosystem, runner, command, speed = SPEED_FAST, confidence = HIGH, basis = "") {
|
|
1179
|
+
this.ecosystem = ecosystem;
|
|
1180
|
+
this.runner = runner;
|
|
1181
|
+
this.command = command;
|
|
1182
|
+
this.speed = speed;
|
|
1183
|
+
this.confidence = confidence;
|
|
1184
|
+
this.basis = basis;
|
|
1185
|
+
}
|
|
1186
|
+
};
|
|
1187
|
+
var BehaviorRunnerResult = class {
|
|
1188
|
+
constructor(ecosystem, runner, command, scope_root, confidence = HIGH, basis = "", conflict = []) {
|
|
1189
|
+
this.ecosystem = ecosystem;
|
|
1190
|
+
this.runner = runner;
|
|
1191
|
+
this.command = command;
|
|
1192
|
+
this.scope_root = scope_root;
|
|
1193
|
+
this.confidence = confidence;
|
|
1194
|
+
this.basis = basis;
|
|
1195
|
+
this.conflict = conflict;
|
|
1196
|
+
}
|
|
1197
|
+
ecosystem;
|
|
1198
|
+
runner;
|
|
1199
|
+
command;
|
|
1200
|
+
scope_root;
|
|
1201
|
+
confidence;
|
|
1202
|
+
basis;
|
|
1203
|
+
conflict;
|
|
1204
|
+
};
|
|
1205
|
+
var ToolchainResult = class {
|
|
1206
|
+
ecosystems;
|
|
1207
|
+
runners;
|
|
1208
|
+
selected;
|
|
1209
|
+
quality;
|
|
1210
|
+
confidence;
|
|
1211
|
+
mtime;
|
|
1212
|
+
/**
|
|
1213
|
+
* Per-scope behavior inventory — detection only, deliberately NOT merged
|
|
1214
|
+
* into `runners` nor reachable from `selected`, so nothing is ever run
|
|
1215
|
+
* BECAUSE this axis detected it.
|
|
1216
|
+
*
|
|
1217
|
+
* That is a claim about reachability, not about execution, and the two
|
|
1218
|
+
* came apart in review: for four of the eight labels the behavior command
|
|
1219
|
+
* is byte-identical to a native command already in `selected` —
|
|
1220
|
+
* `pytest-bdd`/`pytest`, `cucumber-jvm`/`jvm.command`, and both
|
|
1221
|
+
* `reqnroll` and `specflow`/`dotnet test`. Those native rows are
|
|
1222
|
+
* SPEED_FAST, so the suite does run by default; it runs as the native
|
|
1223
|
+
* runner's work, which this axis neither caused nor can prevent. Stating
|
|
1224
|
+
* it as "reported, never run" overreached in exactly the half a reader
|
|
1225
|
+
* would act on.
|
|
1226
|
+
*/
|
|
1227
|
+
behavior_runners;
|
|
1228
|
+
constructor(args) {
|
|
1229
|
+
this.ecosystems = args.ecosystems;
|
|
1230
|
+
this.runners = args.runners;
|
|
1231
|
+
this.selected = args.selected;
|
|
1232
|
+
this.quality = args.quality;
|
|
1233
|
+
this.confidence = args.confidence;
|
|
1234
|
+
this.mtime = args.mtime;
|
|
1235
|
+
this.behavior_runners = args.behavior_runners ?? [];
|
|
1236
|
+
}
|
|
1237
|
+
/**
|
|
1238
|
+
* Serialise to the auto-generated project-config shape.
|
|
1239
|
+
*
|
|
1240
|
+
* Written to `agents/runtime/state/toolchain.json` by
|
|
1241
|
+
* {@link write_config} so the per-stack commands are captured once and
|
|
1242
|
+
* re-read cheaply (keyed on `mtime`) instead of re-detected every turn.
|
|
1243
|
+
*/
|
|
1244
|
+
to_config() {
|
|
1245
|
+
return {
|
|
1246
|
+
ecosystems: [...this.ecosystems],
|
|
1247
|
+
confidence: this.confidence,
|
|
1248
|
+
// `mtime` is a Python float; wrap so `write_config`'s serialiser
|
|
1249
|
+
// renders an integral value as `N.0` (Python float repr).
|
|
1250
|
+
mtime: pyFloat(this.mtime),
|
|
1251
|
+
runners: this.runners.map((r) => ({
|
|
1252
|
+
ecosystem: r.ecosystem,
|
|
1253
|
+
runner: r.runner,
|
|
1254
|
+
command: r.command,
|
|
1255
|
+
speed: r.speed,
|
|
1256
|
+
confidence: r.confidence,
|
|
1257
|
+
basis: r.basis
|
|
1258
|
+
})),
|
|
1259
|
+
selected: this.selected.map((r) => r.command),
|
|
1260
|
+
quality: [...this.quality],
|
|
1261
|
+
behavior_runners: this.behavior_runners.map((r) => ({
|
|
1262
|
+
ecosystem: r.ecosystem,
|
|
1263
|
+
runner: r.runner,
|
|
1264
|
+
command: r.command,
|
|
1265
|
+
scope_root: r.scope_root,
|
|
1266
|
+
confidence: r.confidence,
|
|
1267
|
+
basis: r.basis,
|
|
1268
|
+
conflict: [...r.conflict]
|
|
1269
|
+
}))
|
|
1270
|
+
};
|
|
1271
|
+
}
|
|
1272
|
+
};
|
|
1273
|
+
function resolve_toolchain(project_root, opts = {}) {
|
|
1274
|
+
const include_slow = opts.include_slow ?? false;
|
|
1275
|
+
const include_e2e = opts.include_e2e ?? false;
|
|
1276
|
+
const php_only = opts.php_only ?? false;
|
|
1277
|
+
const runners = [];
|
|
1278
|
+
const quality = [];
|
|
1279
|
+
const composer = _read_json(path3.join(project_root, "composer.json"));
|
|
1280
|
+
const pkg = _read_json(path3.join(project_root, "package.json"));
|
|
1281
|
+
const has_composer = _is_file(path3.join(project_root, "composer.json"));
|
|
1282
|
+
const has_package = _is_file(path3.join(project_root, "package.json"));
|
|
1283
|
+
const pyproject_text = _read_text(path3.join(project_root, "pyproject.toml"));
|
|
1284
|
+
const has_python = Boolean(pyproject_text) || _is_file(path3.join(project_root, "requirements.txt")) || _is_file(path3.join(project_root, "setup.cfg")) || _is_file(path3.join(project_root, "pytest.ini"));
|
|
1285
|
+
const has_go = _is_file(path3.join(project_root, "go.mod"));
|
|
1286
|
+
const has_cargo = _is_file(path3.join(project_root, "Cargo.toml"));
|
|
1287
|
+
const gemfile_text = _ruby_gemfile_text(project_root);
|
|
1288
|
+
const jvm = _jvm_build(project_root);
|
|
1289
|
+
const dotnet_basis = _dotnet_basis(project_root);
|
|
1290
|
+
const wrappers = _task_runner_wrappers(project_root, pkg);
|
|
1291
|
+
if (has_composer) {
|
|
1292
|
+
runners.push(..._php_runners(project_root, composer, wrappers));
|
|
1293
|
+
quality.push(..._php_quality(project_root, composer, wrappers));
|
|
1294
|
+
}
|
|
1295
|
+
if (has_package) {
|
|
1296
|
+
runners.push(..._js_runners(pkg, wrappers, _package_manager(project_root, pkg)));
|
|
1297
|
+
quality.push(..._js_quality(pkg, wrappers));
|
|
1298
|
+
}
|
|
1299
|
+
if (has_python) {
|
|
1300
|
+
runners.push(..._python_runners(pyproject_text));
|
|
1301
|
+
quality.push(..._python_quality(pyproject_text));
|
|
1302
|
+
}
|
|
1303
|
+
if (has_go) {
|
|
1304
|
+
runners.push(
|
|
1305
|
+
new RunnerResult("go", "go-test", "go test ./...", SPEED_FAST, HIGH, "go.mod present")
|
|
1306
|
+
);
|
|
1307
|
+
quality.push("go vet ./...");
|
|
1308
|
+
}
|
|
1309
|
+
if (has_cargo) {
|
|
1310
|
+
runners.push(
|
|
1311
|
+
new RunnerResult("rust", "cargo-test", "cargo test", SPEED_FAST, HIGH, "Cargo.toml present")
|
|
1312
|
+
);
|
|
1313
|
+
quality.push("cargo clippy");
|
|
1314
|
+
}
|
|
1315
|
+
runners.push(..._ruby_runners(project_root, gemfile_text));
|
|
1316
|
+
if (jvm !== null) {
|
|
1317
|
+
runners.push(..._jvm_runners(jvm));
|
|
1318
|
+
}
|
|
1319
|
+
if (dotnet_basis !== null) {
|
|
1320
|
+
runners.push(..._dotnet_runners(dotnet_basis));
|
|
1321
|
+
}
|
|
1322
|
+
const ecosystems = _dictFromKeys(runners.map((r) => r.ecosystem));
|
|
1323
|
+
const selected = _apply_guard(runners, { include_slow, include_e2e, php_only });
|
|
1324
|
+
const confidence = _overall_confidence(runners);
|
|
1325
|
+
const scopes = _behavior_scopes(project_root);
|
|
1326
|
+
return new ToolchainResult({
|
|
1327
|
+
ecosystems,
|
|
1328
|
+
runners: [...runners],
|
|
1329
|
+
selected: [...selected],
|
|
1330
|
+
quality: _dictFromKeys(quality),
|
|
1331
|
+
confidence,
|
|
1332
|
+
// Computed ONCE and threaded into both consumers: workspace discovery
|
|
1333
|
+
// reads a manifest and lists per glob parent, and both the cache probe
|
|
1334
|
+
// and the behavior axis need the same list.
|
|
1335
|
+
mtime: latest_manifest_mtime(project_root, scopes),
|
|
1336
|
+
behavior_runners: resolve_behavior_runners(project_root, scopes)
|
|
1337
|
+
});
|
|
1338
|
+
}
|
|
1339
|
+
function latest_manifest_mtime(project_root, scopes) {
|
|
1340
|
+
const mtimes = [];
|
|
1341
|
+
const names = [..._MANIFESTS, ..._BEHAVIOR_MARKERS];
|
|
1342
|
+
for (const scope of scopes ?? _behavior_scopes(project_root)) {
|
|
1343
|
+
const dir = scope === "." ? project_root : path3.join(project_root, scope);
|
|
1344
|
+
for (const name of names) {
|
|
1345
|
+
const p = path3.join(dir, name);
|
|
1346
|
+
if (_is_file(p)) {
|
|
1347
|
+
mtimes.push(_stat_mtime(p));
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
return mtimes.length > 0 ? Math.max(...mtimes) : 0;
|
|
1352
|
+
}
|
|
1353
|
+
function _php_runners(root, composer, wrappers) {
|
|
1354
|
+
const deps = _all_dependencies(composer, "require", "require-dev");
|
|
1355
|
+
if ("pestphp/pest" in deps || _is_file(path3.join(root, "vendor", "bin", "pest"))) {
|
|
1356
|
+
const cmd2 = wrappers["php-test"] || "vendor/bin/pest";
|
|
1357
|
+
const basis = "pestphp/pest" in deps ? "pestphp/pest in composer require" : "vendor/bin/pest present";
|
|
1358
|
+
return [new RunnerResult("php", "pest", cmd2, SPEED_FAST, HIGH, basis)];
|
|
1359
|
+
}
|
|
1360
|
+
if (_is_file(path3.join(root, "artisan"))) {
|
|
1361
|
+
const cmd2 = wrappers["php-test"] || "php artisan test";
|
|
1362
|
+
return [new RunnerResult("php", "phpunit", cmd2, SPEED_FAST, HIGH, "artisan present (Laravel)")];
|
|
1363
|
+
}
|
|
1364
|
+
if ("phpunit/phpunit" in deps || _is_file(path3.join(root, "vendor", "bin", "phpunit"))) {
|
|
1365
|
+
const cmd2 = wrappers["php-test"] || "vendor/bin/phpunit";
|
|
1366
|
+
return [new RunnerResult("php", "phpunit", cmd2, SPEED_FAST, HIGH, "phpunit/phpunit detected")];
|
|
1367
|
+
}
|
|
1368
|
+
const cmd = wrappers["php-test"] || "vendor/bin/phpunit";
|
|
1369
|
+
return [
|
|
1370
|
+
new RunnerResult(
|
|
1371
|
+
"php",
|
|
1372
|
+
"phpunit",
|
|
1373
|
+
cmd,
|
|
1374
|
+
SPEED_FAST,
|
|
1375
|
+
MEDIUM,
|
|
1376
|
+
"composer.json present, no explicit runner"
|
|
1377
|
+
)
|
|
1378
|
+
];
|
|
1379
|
+
}
|
|
1380
|
+
function _js_runners(pkg, wrappers, manager) {
|
|
1381
|
+
const deps = _all_dependencies(
|
|
1382
|
+
pkg,
|
|
1383
|
+
"dependencies",
|
|
1384
|
+
"devDependencies",
|
|
1385
|
+
"peerDependencies",
|
|
1386
|
+
"optionalDependencies"
|
|
1387
|
+
);
|
|
1388
|
+
const scripts = _isDict(pkg.scripts) ? pkg.scripts : {};
|
|
1389
|
+
const pm_test = wrappers["js-test"];
|
|
1390
|
+
const out = [];
|
|
1391
|
+
if ("vitest" in deps) {
|
|
1392
|
+
const cmd = pm_test && _script_uses(scripts, "test", "vitest") ? pm_test : "npx vitest run";
|
|
1393
|
+
out.push(new RunnerResult("js", "vitest", cmd, SPEED_FAST, HIGH, "vitest in package deps"));
|
|
1394
|
+
} else if ("jest" in deps) {
|
|
1395
|
+
const cmd = pm_test && _script_uses(scripts, "test", "jest") ? pm_test : "npx jest";
|
|
1396
|
+
out.push(new RunnerResult("js", "jest", cmd, SPEED_FAST, HIGH, "jest in package deps"));
|
|
1397
|
+
} else if (pm_test) {
|
|
1398
|
+
out.push(
|
|
1399
|
+
new RunnerResult(
|
|
1400
|
+
"js",
|
|
1401
|
+
"jest",
|
|
1402
|
+
pm_test,
|
|
1403
|
+
SPEED_FAST,
|
|
1404
|
+
MEDIUM,
|
|
1405
|
+
"package.json test script, runner unclear"
|
|
1406
|
+
)
|
|
1407
|
+
);
|
|
1408
|
+
}
|
|
1409
|
+
if ("@playwright/test" in deps || "playwright" in deps) {
|
|
1410
|
+
const cmd = _script_command(scripts, ["test:e2e", "e2e", "playwright"], manager) || "npx playwright test";
|
|
1411
|
+
out.push(
|
|
1412
|
+
new RunnerResult("js", "playwright", cmd, SPEED_E2E, HIGH, "@playwright/test in package deps")
|
|
1413
|
+
);
|
|
1414
|
+
} else if ("cypress" in deps) {
|
|
1415
|
+
const cmd = _script_command(scripts, ["test:e2e", "e2e", "cypress"], manager) || "npx cypress run";
|
|
1416
|
+
out.push(new RunnerResult("js", "cypress", cmd, SPEED_E2E, HIGH, "cypress in package deps"));
|
|
1417
|
+
}
|
|
1418
|
+
const slow_cmd = _script_command(scripts, ["test:slow", "test:integration"], manager);
|
|
1419
|
+
if (slow_cmd) {
|
|
1420
|
+
out.push(
|
|
1421
|
+
new RunnerResult(
|
|
1422
|
+
"js",
|
|
1423
|
+
"vitest" in deps ? "vitest" : "jest",
|
|
1424
|
+
slow_cmd,
|
|
1425
|
+
SPEED_SLOW,
|
|
1426
|
+
MEDIUM,
|
|
1427
|
+
"test:slow/integration script"
|
|
1428
|
+
)
|
|
1429
|
+
);
|
|
1430
|
+
}
|
|
1431
|
+
return out;
|
|
1432
|
+
}
|
|
1433
|
+
function _python_runners(pyproject_text) {
|
|
1434
|
+
if (pyproject_text.includes("pytest") || pyproject_text === "") {
|
|
1435
|
+
const conf = pyproject_text.includes("pytest") ? HIGH : MEDIUM;
|
|
1436
|
+
const basis = pyproject_text.includes("pytest") ? "pytest in pyproject" : "python project, no explicit runner";
|
|
1437
|
+
return [new RunnerResult("python", "pytest", "pytest", SPEED_FAST, conf, basis)];
|
|
1438
|
+
}
|
|
1439
|
+
return [
|
|
1440
|
+
new RunnerResult("python", "pytest", "pytest", SPEED_FAST, MEDIUM, "python project, no explicit runner")
|
|
1441
|
+
];
|
|
1442
|
+
}
|
|
1443
|
+
function _ruby_gemfile_text(dir) {
|
|
1444
|
+
let present = false;
|
|
1445
|
+
for (const name of ["Gemfile", "gems.rb"]) {
|
|
1446
|
+
const full = path3.join(dir, name);
|
|
1447
|
+
present = present || _is_file(full);
|
|
1448
|
+
const text = _read_text(full);
|
|
1449
|
+
if (text !== "") {
|
|
1450
|
+
return { text, file: name, present: true };
|
|
1451
|
+
}
|
|
1452
|
+
}
|
|
1453
|
+
return { text: "", file: "", present };
|
|
1454
|
+
}
|
|
1455
|
+
function _gem_declared(text, gem) {
|
|
1456
|
+
return new RegExp(`^[ \\t]*gem[ \\t]+["']${gem}(-[A-Za-z0-9_-]+)?["']`, "m").test(text);
|
|
1457
|
+
}
|
|
1458
|
+
function _ruby_runners(root, gemfile) {
|
|
1459
|
+
const dot_rspec = _is_file(path3.join(root, ".rspec"));
|
|
1460
|
+
const spec_helper = _is_file(path3.join(root, "spec", "spec_helper.rb"));
|
|
1461
|
+
const in_gemfile = _gem_declared(gemfile.text, "rspec");
|
|
1462
|
+
if (!in_gemfile && !dot_rspec && !spec_helper) {
|
|
1463
|
+
return [];
|
|
1464
|
+
}
|
|
1465
|
+
const basis = in_gemfile ? `rspec in ${gemfile.file}` : dot_rspec ? ".rspec present" : "spec/spec_helper.rb present";
|
|
1466
|
+
const cmd = gemfile.present ? "bundle exec rspec" : "rspec";
|
|
1467
|
+
return [new RunnerResult("ruby", "rspec", cmd, SPEED_FAST, HIGH, basis)];
|
|
1468
|
+
}
|
|
1469
|
+
function _jvm_build(root) {
|
|
1470
|
+
const pom = _read_text(path3.join(root, "pom.xml"));
|
|
1471
|
+
if (pom !== "") {
|
|
1472
|
+
const wrapper = _is_file(path3.join(root, "mvnw"));
|
|
1473
|
+
return { tool: "maven", text: pom, command: wrapper ? "./mvnw test" : "mvn test" };
|
|
1474
|
+
}
|
|
1475
|
+
const gradleFile = ["build.gradle", "build.gradle.kts", "settings.gradle", "settings.gradle.kts"].find(
|
|
1476
|
+
(n) => _is_file(path3.join(root, n))
|
|
1477
|
+
);
|
|
1478
|
+
if (gradleFile !== void 0) {
|
|
1479
|
+
const wrapper = _is_file(path3.join(root, "gradlew"));
|
|
1480
|
+
return {
|
|
1481
|
+
tool: "gradle",
|
|
1482
|
+
text: _read_text(path3.join(root, "build.gradle")) + "\n" + _read_text(path3.join(root, "build.gradle.kts")),
|
|
1483
|
+
command: wrapper ? "./gradlew test" : "gradle test"
|
|
1484
|
+
};
|
|
1485
|
+
}
|
|
1486
|
+
return null;
|
|
1487
|
+
}
|
|
1488
|
+
function _jvm_runners(build) {
|
|
1489
|
+
const named = /\bjunit\b/i.test(build.text);
|
|
1490
|
+
const conf = named ? HIGH : MEDIUM;
|
|
1491
|
+
const basis = named ? `junit in the ${build.tool} build file` : `${build.tool} build file present, no explicit runner`;
|
|
1492
|
+
return [new RunnerResult("jvm", "junit", build.command, SPEED_FAST, conf, basis)];
|
|
1493
|
+
}
|
|
1494
|
+
function _dotnet_basis(root) {
|
|
1495
|
+
let names;
|
|
1496
|
+
try {
|
|
1497
|
+
names = fs3.readdirSync(root, { withFileTypes: true }).filter((e) => e.isFile()).map((e) => e.name).sort();
|
|
1498
|
+
} catch {
|
|
1499
|
+
names = [];
|
|
1500
|
+
}
|
|
1501
|
+
const target = names.find((n) => _DOTNET_PROJECT_EXTS.includes(path3.extname(n).toLowerCase()));
|
|
1502
|
+
if (target === void 0) {
|
|
1503
|
+
return null;
|
|
1504
|
+
}
|
|
1505
|
+
return _DOTNET_TEST_STACK.test(_dotnet_project_text(root)) ? { basis: "test stack named in a .NET project file", confidence: HIGH } : { basis: `${target} present, no test stack named`, confidence: MEDIUM };
|
|
1506
|
+
}
|
|
1507
|
+
function _dotnet_runners(found) {
|
|
1508
|
+
return [
|
|
1509
|
+
new RunnerResult(
|
|
1510
|
+
"dotnet",
|
|
1511
|
+
"dotnet-test",
|
|
1512
|
+
"dotnet test",
|
|
1513
|
+
SPEED_FAST,
|
|
1514
|
+
found.confidence,
|
|
1515
|
+
found.basis
|
|
1516
|
+
)
|
|
1517
|
+
];
|
|
1518
|
+
}
|
|
1519
|
+
function resolve_behavior_runners(project_root, scopes) {
|
|
1520
|
+
const out = [];
|
|
1521
|
+
for (const scope of scopes ?? _behavior_scopes(project_root)) {
|
|
1522
|
+
const dir = scope === "." ? project_root : path3.join(project_root, scope);
|
|
1523
|
+
const rows = _behavior_runners_in_scope(dir, scope);
|
|
1524
|
+
for (const ecosystem of _dictFromKeys(rows.map((r) => r.ecosystem))) {
|
|
1525
|
+
const found = rows.filter((r) => r.ecosystem === ecosystem);
|
|
1526
|
+
const names = [...new Set(found.map((r) => r.runner))].sort();
|
|
1527
|
+
if (names.length <= 1) {
|
|
1528
|
+
out.push(found[0]);
|
|
1529
|
+
continue;
|
|
1530
|
+
}
|
|
1531
|
+
out.push(
|
|
1532
|
+
new BehaviorRunnerResult(
|
|
1533
|
+
ecosystem,
|
|
1534
|
+
BEHAVIOR_UNKNOWN,
|
|
1535
|
+
"",
|
|
1536
|
+
scope,
|
|
1537
|
+
LOW,
|
|
1538
|
+
`two ${ecosystem} behavior runners in one scope (${names.join(", ")}) \u2014 refusing to choose, as the frontend detector refuses between mutually exclusive workspaces`,
|
|
1539
|
+
names
|
|
1540
|
+
)
|
|
1541
|
+
);
|
|
1542
|
+
}
|
|
1543
|
+
}
|
|
1544
|
+
return out;
|
|
1545
|
+
}
|
|
1546
|
+
function _behavior_runners_in_scope(dir, scope) {
|
|
1547
|
+
const out = [];
|
|
1548
|
+
const at = (n) => path3.join(dir, n);
|
|
1549
|
+
const row = (ecosystem, runner, command, basis) => {
|
|
1550
|
+
out.push(new BehaviorRunnerResult(ecosystem, runner, command, scope, HIGH, basis));
|
|
1551
|
+
};
|
|
1552
|
+
const php_deps = _all_dependencies(_read_json(at("composer.json")), "require", "require-dev");
|
|
1553
|
+
const behat_config = ["behat.yml", "behat.yaml", "behat.yml.dist", "behat.dist.yml"].find(
|
|
1554
|
+
(n) => _is_file(at(n))
|
|
1555
|
+
);
|
|
1556
|
+
if ("behat/behat" in php_deps || behat_config !== void 0) {
|
|
1557
|
+
const declared = "behat/behat" in php_deps;
|
|
1558
|
+
const basis = declared ? "behat/behat in the composer manifest" : `${behat_config} present`;
|
|
1559
|
+
row("php", "behat", declared ? "vendor/bin/behat" : "behat", basis);
|
|
1560
|
+
}
|
|
1561
|
+
const js_deps = _all_dependencies(
|
|
1562
|
+
_read_json(at("package.json")),
|
|
1563
|
+
"dependencies",
|
|
1564
|
+
"devDependencies"
|
|
1565
|
+
);
|
|
1566
|
+
const cucumber_config = ["cucumber.js", "cucumber.cjs", "cucumber.mjs", "cucumber.json"].find(
|
|
1567
|
+
(n) => _is_file(at(n))
|
|
1568
|
+
);
|
|
1569
|
+
if ("@cucumber/cucumber" in js_deps || "cucumber" in js_deps || cucumber_config !== void 0) {
|
|
1570
|
+
const dep = "@cucumber/cucumber" in js_deps ? "@cucumber/cucumber" : "cucumber";
|
|
1571
|
+
const declared = dep in js_deps;
|
|
1572
|
+
const basis = declared ? `${dep} in package deps` : `${cucumber_config} present`;
|
|
1573
|
+
row("js", "cucumber-js", declared ? "npx cucumber-js" : "cucumber-js", basis);
|
|
1574
|
+
}
|
|
1575
|
+
const py_text = [_read_text(at("pyproject.toml")), _read_text(at("requirements.txt"))].join("\n");
|
|
1576
|
+
if (_PY_BEHAVE.test(py_text)) {
|
|
1577
|
+
row("python", "behave", "behave", "behave declared as a python dependency");
|
|
1578
|
+
}
|
|
1579
|
+
if (_PY_BDD.test(py_text)) {
|
|
1580
|
+
row("python", "pytest-bdd", "pytest", "pytest-bdd declared as a python dependency");
|
|
1581
|
+
}
|
|
1582
|
+
const gemfile = _ruby_gemfile_text(dir);
|
|
1583
|
+
const cucumber_gem = _gem_declared(gemfile.text, "cucumber");
|
|
1584
|
+
if (cucumber_gem || _is_file(path3.join(dir, "features", "support", "env.rb"))) {
|
|
1585
|
+
const basis = cucumber_gem ? `cucumber in ${gemfile.file}` : "features/support/env.rb present";
|
|
1586
|
+
const cmd = gemfile.present ? "bundle exec cucumber" : "cucumber";
|
|
1587
|
+
row("ruby", "cucumber-ruby", cmd, basis);
|
|
1588
|
+
}
|
|
1589
|
+
const jvm = _jvm_build(dir);
|
|
1590
|
+
if (jvm !== null && /io\.cucumber|cucumber-java|cucumber-junit/i.test(jvm.text)) {
|
|
1591
|
+
row("jvm", "cucumber-jvm", jvm.command, `cucumber in the ${jvm.tool} build file`);
|
|
1592
|
+
}
|
|
1593
|
+
const dotnet_text = _dotnet_project_text(dir);
|
|
1594
|
+
if (/\bReqnroll\b/i.test(dotnet_text)) {
|
|
1595
|
+
row("dotnet", "reqnroll", "dotnet test", "Reqnroll in a project file");
|
|
1596
|
+
}
|
|
1597
|
+
if (/\bSpecFlow\b/i.test(dotnet_text)) {
|
|
1598
|
+
row("dotnet", "specflow", "dotnet test", "SpecFlow in a project file");
|
|
1599
|
+
}
|
|
1600
|
+
return out;
|
|
1601
|
+
}
|
|
1602
|
+
function _dotnet_project_text(dir) {
|
|
1603
|
+
const texts = [];
|
|
1604
|
+
const solution = _has_dotnet_solution(dir);
|
|
1605
|
+
const walk = (at, depth) => {
|
|
1606
|
+
if (texts.length >= _DOTNET_MAX_PROJECTS) {
|
|
1607
|
+
return;
|
|
1608
|
+
}
|
|
1609
|
+
let entries;
|
|
1610
|
+
try {
|
|
1611
|
+
entries = fs3.readdirSync(at, { withFileTypes: true });
|
|
1612
|
+
} catch {
|
|
1613
|
+
return;
|
|
1614
|
+
}
|
|
1615
|
+
entries.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1616
|
+
let hit = false;
|
|
1617
|
+
for (const e of entries) {
|
|
1618
|
+
if (!e.isFile() || !_DOTNET_PROJECT_EXTS.includes(path3.extname(e.name).toLowerCase())) {
|
|
1619
|
+
continue;
|
|
1620
|
+
}
|
|
1621
|
+
if (_DOTNET_SOLUTION_EXTS.includes(path3.extname(e.name).toLowerCase())) {
|
|
1622
|
+
continue;
|
|
1623
|
+
}
|
|
1624
|
+
if (texts.length >= _DOTNET_MAX_PROJECTS) {
|
|
1625
|
+
return;
|
|
1626
|
+
}
|
|
1627
|
+
texts.push(_read_text(path3.join(at, e.name)));
|
|
1628
|
+
hit = true;
|
|
1629
|
+
}
|
|
1630
|
+
if (hit || !solution || depth >= _DOTNET_SCAN_DEPTH) {
|
|
1631
|
+
return;
|
|
1632
|
+
}
|
|
1633
|
+
for (const e of entries) {
|
|
1634
|
+
if (e.isDirectory() && !e.name.startsWith(".") && e.name !== "node_modules") {
|
|
1635
|
+
walk(path3.join(at, e.name), depth + 1);
|
|
1636
|
+
}
|
|
1637
|
+
}
|
|
1638
|
+
};
|
|
1639
|
+
walk(dir, 0);
|
|
1640
|
+
return texts.join("\n");
|
|
1641
|
+
}
|
|
1642
|
+
var _MAX_BEHAVIOR_SCOPES = 200;
|
|
1643
|
+
var _PY_BEHAVE = /^[ \t]*["']?behave["']?[ \t]*(?:$|[=<>~!,;[#])/m;
|
|
1644
|
+
var _PY_BDD = /^[ \t]*["']?pytest-bdd["']?[ \t]*(?:$|[=<>~!,;[#])/m;
|
|
1645
|
+
function _pnpm_packages(text) {
|
|
1646
|
+
const out = [];
|
|
1647
|
+
let inPackages = false;
|
|
1648
|
+
const push = (raw) => {
|
|
1649
|
+
const v = raw.replace(/\s+#.*$/, "").trim().replace(/^['"]|['"]$/g, "").trim();
|
|
1650
|
+
if (v !== "") out.push(v);
|
|
1651
|
+
};
|
|
1652
|
+
for (const line of text.split(/\r?\n/)) {
|
|
1653
|
+
if (line.trim() === "" || /^\s*#/.test(line)) {
|
|
1654
|
+
continue;
|
|
1655
|
+
}
|
|
1656
|
+
const key = /^([A-Za-z0-9_-]+)\s*:(.*)$/.exec(line);
|
|
1657
|
+
if (key) {
|
|
1658
|
+
inPackages = key[1] === "packages";
|
|
1659
|
+
const rest = (key[2] ?? "").trim();
|
|
1660
|
+
if (inPackages && rest.startsWith("[")) {
|
|
1661
|
+
for (const part of rest.replace(/^\[|\]$/g, "").split(",")) {
|
|
1662
|
+
push(part);
|
|
1663
|
+
}
|
|
1664
|
+
inPackages = false;
|
|
1665
|
+
}
|
|
1666
|
+
continue;
|
|
1667
|
+
}
|
|
1668
|
+
if (!inPackages) {
|
|
1669
|
+
continue;
|
|
1670
|
+
}
|
|
1671
|
+
const item = /^\s*-\s*(.+?)\s*$/.exec(line);
|
|
1672
|
+
if (item && item[1] !== void 0) {
|
|
1673
|
+
push(item[1]);
|
|
1674
|
+
}
|
|
1675
|
+
}
|
|
1676
|
+
return out;
|
|
1677
|
+
}
|
|
1678
|
+
function _excluded_scope(name) {
|
|
1679
|
+
return name.split("/").some((seg) => seg.startsWith(".") || seg === "node_modules");
|
|
1680
|
+
}
|
|
1681
|
+
function _behavior_scopes(project_root) {
|
|
1682
|
+
const globs = [];
|
|
1683
|
+
const pkg = _read_json(path3.join(project_root, "package.json"));
|
|
1684
|
+
const ws = pkg["workspaces"];
|
|
1685
|
+
if (Array.isArray(ws)) {
|
|
1686
|
+
globs.push(...ws.filter((g) => typeof g === "string"));
|
|
1687
|
+
} else if (_isDict(ws) && Array.isArray(ws["packages"])) {
|
|
1688
|
+
globs.push(...ws["packages"].filter((g) => typeof g === "string"));
|
|
1689
|
+
}
|
|
1690
|
+
globs.push(..._pnpm_packages(_read_text(path3.join(project_root, "pnpm-workspace.yaml"))));
|
|
1691
|
+
const scopes = ["."];
|
|
1692
|
+
for (const raw of globs) {
|
|
1693
|
+
if (scopes.length >= _MAX_BEHAVIOR_SCOPES) {
|
|
1694
|
+
break;
|
|
1695
|
+
}
|
|
1696
|
+
const g = raw.replace(/^\.\//, "").replace(/\/+$/, "");
|
|
1697
|
+
if (g === "" || g.startsWith("!") || g.startsWith("/") || g.includes("..")) {
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1700
|
+
if (_excluded_scope(g)) {
|
|
1701
|
+
continue;
|
|
1702
|
+
}
|
|
1703
|
+
if (g.endsWith("/*")) {
|
|
1704
|
+
const parent = g.slice(0, -2);
|
|
1705
|
+
let children;
|
|
1706
|
+
try {
|
|
1707
|
+
children = fs3.readdirSync(path3.join(project_root, parent), { withFileTypes: true });
|
|
1708
|
+
} catch {
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
children.sort((a, b) => a.name < b.name ? -1 : a.name > b.name ? 1 : 0);
|
|
1712
|
+
for (const c of children) {
|
|
1713
|
+
if (scopes.length >= _MAX_BEHAVIOR_SCOPES) {
|
|
1714
|
+
break;
|
|
1715
|
+
}
|
|
1716
|
+
if (c.isDirectory() && !_excluded_scope(c.name)) {
|
|
1717
|
+
scopes.push(`${parent}/${c.name}`);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
} else if (!g.includes("*")) {
|
|
1721
|
+
try {
|
|
1722
|
+
if (fs3.lstatSync(path3.join(project_root, g)).isDirectory()) {
|
|
1723
|
+
scopes.push(g);
|
|
1724
|
+
}
|
|
1725
|
+
} catch {
|
|
1726
|
+
}
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1729
|
+
return _dictFromKeys(scopes);
|
|
1730
|
+
}
|
|
1731
|
+
function _php_quality(root, composer, _wrappers) {
|
|
1732
|
+
const deps = _all_dependencies(composer, "require", "require-dev");
|
|
1733
|
+
const out = [];
|
|
1734
|
+
if ("phpstan/phpstan" in deps || _is_file(path3.join(root, "vendor", "bin", "phpstan"))) {
|
|
1735
|
+
out.push("vendor/bin/phpstan analyse");
|
|
1736
|
+
}
|
|
1737
|
+
if ("laravel/pint" in deps || _is_file(path3.join(root, "vendor", "bin", "pint"))) {
|
|
1738
|
+
out.push("vendor/bin/pint");
|
|
1739
|
+
}
|
|
1740
|
+
return out;
|
|
1741
|
+
}
|
|
1742
|
+
function _js_quality(pkg, _wrappers) {
|
|
1743
|
+
const deps = _all_dependencies(
|
|
1744
|
+
pkg,
|
|
1745
|
+
"dependencies",
|
|
1746
|
+
"devDependencies",
|
|
1747
|
+
"peerDependencies",
|
|
1748
|
+
"optionalDependencies"
|
|
1749
|
+
);
|
|
1750
|
+
const out = [];
|
|
1751
|
+
if ("typescript" in deps) {
|
|
1752
|
+
out.push("npx tsc --noEmit");
|
|
1753
|
+
}
|
|
1754
|
+
if ("eslint" in deps) {
|
|
1755
|
+
out.push("npx eslint .");
|
|
1756
|
+
}
|
|
1757
|
+
return out;
|
|
1758
|
+
}
|
|
1759
|
+
function _python_quality(pyproject_text) {
|
|
1760
|
+
const out = [];
|
|
1761
|
+
if (pyproject_text.includes("ruff")) {
|
|
1762
|
+
out.push("ruff check");
|
|
1763
|
+
}
|
|
1764
|
+
if (pyproject_text.includes("mypy")) {
|
|
1765
|
+
out.push("mypy .");
|
|
1766
|
+
}
|
|
1767
|
+
return out;
|
|
1768
|
+
}
|
|
1769
|
+
function _task_runner_wrappers(root, pkg) {
|
|
1770
|
+
const out = {};
|
|
1771
|
+
const makefile = _read_text(path3.join(root, "Makefile"));
|
|
1772
|
+
const taskfile = _read_text(path3.join(root, "Taskfile.yml")) || _read_text(path3.join(root, "Taskfile.yaml"));
|
|
1773
|
+
if (makefile && _reSearch(/^test\s*:/m, makefile)) {
|
|
1774
|
+
out["php-test"] = "make test";
|
|
1775
|
+
} else if (taskfile && _reSearch(/^\s*test\s*:/m, taskfile)) {
|
|
1776
|
+
out["php-test"] = "task test";
|
|
1777
|
+
}
|
|
1778
|
+
const scripts = _isDict(pkg.scripts) ? pkg.scripts : {};
|
|
1779
|
+
if (_isDict(scripts) && "test" in scripts) {
|
|
1780
|
+
out["js-test"] = `${_package_manager(root, pkg)} test`;
|
|
1781
|
+
}
|
|
1782
|
+
return out;
|
|
1783
|
+
}
|
|
1784
|
+
var LOCKFILE_MANAGERS = [
|
|
1785
|
+
["pnpm-lock.yaml", "pnpm"],
|
|
1786
|
+
["yarn.lock", "yarn"],
|
|
1787
|
+
["bun.lock", "bun"],
|
|
1788
|
+
["bun.lockb", "bun"],
|
|
1789
|
+
["package-lock.json", "npm"]
|
|
1790
|
+
];
|
|
1791
|
+
function _resolve_package_manager(root, pkg) {
|
|
1792
|
+
const declared = pkg === void 0 ? void 0 : pkg["packageManager"];
|
|
1793
|
+
if (typeof declared === "string" && declared.trim() !== "") {
|
|
1794
|
+
const name = declared.trim().split("@")[0];
|
|
1795
|
+
if (name !== "") {
|
|
1796
|
+
return { manager: name, via: "packageManager", finding: "" };
|
|
1797
|
+
}
|
|
1798
|
+
}
|
|
1799
|
+
const present = LOCKFILE_MANAGERS.filter(([file]) => _is_file(path3.join(root, file)));
|
|
1800
|
+
const distinct = [...new Set(present.map(([, mgr]) => mgr))];
|
|
1801
|
+
if (distinct.length > 1) {
|
|
1802
|
+
return {
|
|
1803
|
+
manager: null,
|
|
1804
|
+
via: "ambiguous",
|
|
1805
|
+
finding: `two package-manager lockfiles present (${present.map(([f]) => f).join(", ")}) and no \`packageManager\` declaration \u2014 report both and stop, per monorepo-workspace SKILL.md \xA7 1`
|
|
1806
|
+
};
|
|
1807
|
+
}
|
|
1808
|
+
if (distinct.length === 1) {
|
|
1809
|
+
const mgr = distinct[0];
|
|
1810
|
+
return { manager: mgr, via: mgr, finding: "" };
|
|
1811
|
+
}
|
|
1812
|
+
return { manager: "npm", via: "npm", finding: "" };
|
|
1813
|
+
}
|
|
1814
|
+
function _package_manager(root, pkg) {
|
|
1815
|
+
return _resolve_package_manager(root, pkg).manager ?? "npm";
|
|
1816
|
+
}
|
|
1817
|
+
function _apply_guard(runners, opts) {
|
|
1818
|
+
const out = [];
|
|
1819
|
+
for (const r of runners) {
|
|
1820
|
+
if (opts.php_only && r.ecosystem !== "php") {
|
|
1821
|
+
continue;
|
|
1822
|
+
}
|
|
1823
|
+
if (r.speed === SPEED_E2E && !opts.include_e2e) {
|
|
1824
|
+
continue;
|
|
1825
|
+
}
|
|
1826
|
+
if (r.speed === SPEED_SLOW && !opts.include_slow) {
|
|
1827
|
+
continue;
|
|
1828
|
+
}
|
|
1829
|
+
out.push(r);
|
|
1830
|
+
}
|
|
1831
|
+
return out;
|
|
1832
|
+
}
|
|
1833
|
+
function _overall_confidence(runners) {
|
|
1834
|
+
if (runners.length === 0) {
|
|
1835
|
+
return LOW;
|
|
1836
|
+
}
|
|
1837
|
+
if (runners.some((r) => r.confidence === HIGH)) {
|
|
1838
|
+
return HIGH;
|
|
1839
|
+
}
|
|
1840
|
+
return MEDIUM;
|
|
1841
|
+
}
|
|
1842
|
+
function _read_json(p) {
|
|
1843
|
+
if (!_is_file(p)) {
|
|
1844
|
+
return {};
|
|
1845
|
+
}
|
|
1846
|
+
let payload;
|
|
1847
|
+
try {
|
|
1848
|
+
payload = JSON.parse(fs3.readFileSync(p, { encoding: "utf-8" }));
|
|
1849
|
+
} catch {
|
|
1850
|
+
return {};
|
|
1851
|
+
}
|
|
1852
|
+
return _isDict(payload) ? payload : {};
|
|
1853
|
+
}
|
|
1854
|
+
function _read_text(p) {
|
|
1855
|
+
if (!_is_file(p)) {
|
|
1856
|
+
return "";
|
|
1857
|
+
}
|
|
1858
|
+
try {
|
|
1859
|
+
return fs3.readFileSync(p, { encoding: "utf-8" });
|
|
1860
|
+
} catch {
|
|
1861
|
+
return "";
|
|
1862
|
+
}
|
|
1863
|
+
}
|
|
1864
|
+
function _all_dependencies(manifest, ...keys) {
|
|
1865
|
+
const merged = {};
|
|
1866
|
+
for (const key of keys) {
|
|
1867
|
+
const section = manifest[key];
|
|
1868
|
+
if (_isDict(section)) {
|
|
1869
|
+
Object.assign(merged, section);
|
|
1870
|
+
}
|
|
1871
|
+
}
|
|
1872
|
+
return merged;
|
|
1873
|
+
}
|
|
1874
|
+
function _script_uses(scripts, name, tool) {
|
|
1875
|
+
const value = scripts[name];
|
|
1876
|
+
return typeof value === "string" && value.includes(tool);
|
|
1877
|
+
}
|
|
1878
|
+
function _script_command(scripts, names, manager) {
|
|
1879
|
+
for (const name of names) {
|
|
1880
|
+
if (typeof scripts[name] === "string") {
|
|
1881
|
+
return `${manager} run ${name}`;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
return "";
|
|
1885
|
+
}
|
|
1886
|
+
function _is_file(p) {
|
|
1887
|
+
try {
|
|
1888
|
+
return fs3.statSync(p).isFile();
|
|
1889
|
+
} catch {
|
|
1890
|
+
return false;
|
|
1891
|
+
}
|
|
1892
|
+
}
|
|
1893
|
+
function _stat_mtime(p) {
|
|
1894
|
+
try {
|
|
1895
|
+
return Number(fs3.statSync(p, { bigint: true }).mtimeNs) / 1e9;
|
|
1896
|
+
} catch {
|
|
1897
|
+
return 0;
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
function _isDict(v) {
|
|
1901
|
+
return typeof v === "object" && v !== null && !Array.isArray(v);
|
|
1902
|
+
}
|
|
1903
|
+
function _reSearch(pattern, text) {
|
|
1904
|
+
return pattern.test(text);
|
|
1905
|
+
}
|
|
1906
|
+
function _dictFromKeys(seq) {
|
|
1907
|
+
return [...new Set(seq)];
|
|
1908
|
+
}
|
|
1909
|
+
var _PyFloat = class {
|
|
1910
|
+
value;
|
|
1911
|
+
constructor(value) {
|
|
1912
|
+
this.value = value;
|
|
1913
|
+
}
|
|
1914
|
+
};
|
|
1915
|
+
function pyFloat(value) {
|
|
1916
|
+
return new _PyFloat(value);
|
|
1917
|
+
}
|
|
1918
|
+
|
|
1032
1919
|
// src/scripts/before_complete_hook.ts
|
|
1033
|
-
var STATE_FILE =
|
|
1034
|
-
var STATE_DIR =
|
|
1920
|
+
var STATE_FILE = path4.join("agents", "state", "verify-before-complete.json");
|
|
1921
|
+
var STATE_DIR = path4.join("agents", "state", "verify-before-complete");
|
|
1035
1922
|
var STATE_RETENTION_DAYS = 7;
|
|
1036
1923
|
function statePathFor(session_id) {
|
|
1037
1924
|
return session_state_file(STATE_DIR, session_id);
|
|
@@ -1080,9 +1967,55 @@ function _empty_state() {
|
|
|
1080
1967
|
// a reader must never have to distinguish "no runs yet" from "key absent".
|
|
1081
1968
|
verification_runs: [],
|
|
1082
1969
|
edits_this_turn: 0,
|
|
1970
|
+
// NOTE — `quality_runs` and `quality_files_source` are deliberately ABSENT
|
|
1971
|
+
// here, against the convention `verification_runs` above follows.
|
|
1972
|
+
//
|
|
1973
|
+
// That convention ("present in the empty state so a reader never has to tell
|
|
1974
|
+
// 'no runs yet' from 'key absent'") is the right default and it loses to a
|
|
1975
|
+
// stronger one: the roadmap's first acceptance criterion is that with
|
|
1976
|
+
// `touched_file_quality: off` the record is BYTE-IDENTICAL to the base ref.
|
|
1977
|
+
// Seeding two keys would break that for every install that never opted in,
|
|
1978
|
+
// and "the default-off feature changed my state file" is exactly the kind of
|
|
1979
|
+
// silent shape drift a consumer cannot audit. The convention's cost here is
|
|
1980
|
+
// zero, because the only reader of these keys is 2.1's analysis, which counts
|
|
1981
|
+
// stops — and a stop with no key is a stop where the pass did not run, which
|
|
1982
|
+
// is a distinction it needs rather than one it must not make.
|
|
1083
1983
|
checked_at: _now()
|
|
1084
1984
|
};
|
|
1085
1985
|
}
|
|
1986
|
+
function readTouchedFileQualityMode(consumer_root) {
|
|
1987
|
+
const raw = hookSectionValue(consumer_root, "verify_before_complete", "touched_file_quality");
|
|
1988
|
+
return raw === "shadow" || raw === "warn" ? raw : "off";
|
|
1989
|
+
}
|
|
1990
|
+
function collectTouchedFileQuality(consumer_root, session_id) {
|
|
1991
|
+
let touched;
|
|
1992
|
+
try {
|
|
1993
|
+
touched = touchedFilesAtStop(consumer_root, session_id);
|
|
1994
|
+
} catch {
|
|
1995
|
+
return null;
|
|
1996
|
+
}
|
|
1997
|
+
if (touched.files.length === 0) {
|
|
1998
|
+
return { runs: [], source: touched.source };
|
|
1999
|
+
}
|
|
2000
|
+
let commands = [];
|
|
2001
|
+
try {
|
|
2002
|
+
commands = resolve_toolchain(consumer_root).quality;
|
|
2003
|
+
} catch {
|
|
2004
|
+
commands = [];
|
|
2005
|
+
}
|
|
2006
|
+
try {
|
|
2007
|
+
return {
|
|
2008
|
+
runs: runTouchedFileQuality({
|
|
2009
|
+
root: consumer_root,
|
|
2010
|
+
commands,
|
|
2011
|
+
files: touched.files
|
|
2012
|
+
}),
|
|
2013
|
+
source: touched.source
|
|
2014
|
+
};
|
|
2015
|
+
} catch {
|
|
2016
|
+
return null;
|
|
2017
|
+
}
|
|
2018
|
+
}
|
|
1086
2019
|
function _extract_command(payload) {
|
|
1087
2020
|
const toolRaw = payload["tool_name"] || payload["toolName"] || payload["tool"];
|
|
1088
2021
|
if (!(typeof toolRaw === "string" && COMMAND_TOOLS.has(toolRaw))) {
|
|
@@ -1195,6 +2128,7 @@ function _cap_runs(runs) {
|
|
|
1195
2128
|
return [head, ...tail];
|
|
1196
2129
|
}
|
|
1197
2130
|
var _ERROR_EXIT_PREFIX = /^Error:\s*Exit code\s*(\d+)/i;
|
|
2131
|
+
var _BARE_EXIT_PREFIX = /^\s*Exit code\s*(\d+)/i;
|
|
1198
2132
|
var _NO_EXIT = { code: null, source: null, interrupted: false };
|
|
1199
2133
|
function _numeric_exit_field(obj) {
|
|
1200
2134
|
for (const key of ["exit_code", "exitCode", "returncode", "returnCode", "status_code"]) {
|
|
@@ -1206,26 +2140,46 @@ function _numeric_exit_field(obj) {
|
|
|
1206
2140
|
}
|
|
1207
2141
|
return null;
|
|
1208
2142
|
}
|
|
1209
|
-
|
|
2143
|
+
var FAILURE_EVENT_NAMES = /* @__PURE__ */ new Set(["PostToolUseFailure"]);
|
|
2144
|
+
function _is_failure_event(payload, envelope) {
|
|
2145
|
+
for (const v of [payload["hook_event_name"], envelope["native_event"]]) {
|
|
2146
|
+
if (typeof v !== "string" || v === "") continue;
|
|
2147
|
+
if (FAILURE_EVENT_NAMES.has(v)) return true;
|
|
2148
|
+
if (/failure$/i.test(v)) return true;
|
|
2149
|
+
}
|
|
2150
|
+
return false;
|
|
2151
|
+
}
|
|
2152
|
+
function _extract_exit_reading(payload, failure_event = false) {
|
|
2153
|
+
const seal = (r) => failure_event && r.code === 0 ? { code: null, source: null, interrupted: r.interrupted } : r;
|
|
1210
2154
|
const direct = _numeric_exit_field(payload);
|
|
1211
|
-
if (direct !== null) return { code: direct, source: "field", interrupted: false };
|
|
2155
|
+
if (direct !== null) return seal({ code: direct, source: "field", interrupted: false });
|
|
2156
|
+
const interrupted_top = payload["is_interrupt"] === true;
|
|
2157
|
+
const err = payload["error"];
|
|
2158
|
+
if (typeof err === "string") {
|
|
2159
|
+
if (interrupted_top) return { code: null, source: null, interrupted: true };
|
|
2160
|
+
const bare = _BARE_EXIT_PREFIX.exec(err) ?? _ERROR_EXIT_PREFIX.exec(err);
|
|
2161
|
+
if (bare?.[1] !== void 0) {
|
|
2162
|
+
return seal({ code: Number(bare[1]), source: "error_prefix", interrupted: false });
|
|
2163
|
+
}
|
|
2164
|
+
}
|
|
2165
|
+
if (interrupted_top) return { code: null, source: null, interrupted: true };
|
|
1212
2166
|
for (const key of ["tool_response", "toolResponse", "result", "output"]) {
|
|
1213
2167
|
const v = payload[key];
|
|
1214
2168
|
if (typeof v === "string") {
|
|
1215
2169
|
const m = _ERROR_EXIT_PREFIX.exec(v);
|
|
1216
2170
|
if (m?.[1] !== void 0) {
|
|
1217
|
-
return { code: Number(m[1]), source: "error_prefix", interrupted: false };
|
|
2171
|
+
return seal({ code: Number(m[1]), source: "error_prefix", interrupted: false });
|
|
1218
2172
|
}
|
|
1219
2173
|
continue;
|
|
1220
2174
|
}
|
|
1221
2175
|
if (v === null || typeof v !== "object" || Array.isArray(v)) continue;
|
|
1222
2176
|
const obj = v;
|
|
1223
2177
|
const nested = _numeric_exit_field(obj);
|
|
1224
|
-
if (nested !== null) return { code: nested, source: "field", interrupted: false };
|
|
2178
|
+
if (nested !== null) return seal({ code: nested, source: "field", interrupted: false });
|
|
1225
2179
|
if (obj["interrupted"] === true) return { code: null, source: null, interrupted: true };
|
|
1226
2180
|
const hasStream = typeof obj["stdout"] === "string" || typeof obj["stderr"] === "string";
|
|
1227
2181
|
if (obj["interrupted"] === false && hasStream) {
|
|
1228
|
-
return { code: 0, source: "response_shape", interrupted: false };
|
|
2182
|
+
return seal({ code: 0, source: "response_shape", interrupted: false });
|
|
1229
2183
|
}
|
|
1230
2184
|
}
|
|
1231
2185
|
return _NO_EXIT;
|
|
@@ -1247,10 +2201,15 @@ function _extract_run_streams(payload) {
|
|
|
1247
2201
|
}
|
|
1248
2202
|
const topOut = payload["stdout"] ?? payload["output"];
|
|
1249
2203
|
const topErr = payload["stderr"];
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
2204
|
+
if (typeof topOut === "string" || typeof topErr === "string") {
|
|
2205
|
+
return {
|
|
2206
|
+
stdout: typeof topOut === "string" ? topOut : "",
|
|
2207
|
+
stderr: typeof topErr === "string" ? topErr : ""
|
|
2208
|
+
};
|
|
2209
|
+
}
|
|
2210
|
+
const errText = payload["error"];
|
|
2211
|
+
if (typeof errText === "string") return { stdout: errText, stderr: "" };
|
|
2212
|
+
return { stdout: "", stderr: "" };
|
|
1254
2213
|
}
|
|
1255
2214
|
function _reset_turn(state, session_id) {
|
|
1256
2215
|
state["session_id"] = session_id || state["session_id"] || "";
|
|
@@ -1259,6 +2218,8 @@ function _reset_turn(state, session_id) {
|
|
|
1259
2218
|
state["verified_this_turn"] = false;
|
|
1260
2219
|
state["verification_runs"] = [];
|
|
1261
2220
|
state["edits_this_turn"] = 0;
|
|
2221
|
+
delete state["quality_runs"];
|
|
2222
|
+
delete state["quality_files_source"];
|
|
1262
2223
|
state["ci_saw_pending"] = false;
|
|
1263
2224
|
state["nonevidence_this_turn"] = 0;
|
|
1264
2225
|
return state;
|
|
@@ -1273,7 +2234,7 @@ function _asInt(v) {
|
|
|
1273
2234
|
const n = Number(v);
|
|
1274
2235
|
return Number.isFinite(n) ? Math.trunc(n) : 0;
|
|
1275
2236
|
}
|
|
1276
|
-
function _update(state, event, envelope) {
|
|
2237
|
+
function _update(state, event, envelope, quality = null) {
|
|
1277
2238
|
const session_id = envelope["session_id"] || state["session_id"] || "";
|
|
1278
2239
|
if (session_id && session_id !== state["session_id"]) {
|
|
1279
2240
|
state["session_id"] = session_id;
|
|
@@ -1281,6 +2242,8 @@ function _update(state, event, envelope) {
|
|
|
1281
2242
|
state["ci_last"] = null;
|
|
1282
2243
|
state = _reset_turn(state, session_id);
|
|
1283
2244
|
}
|
|
2245
|
+
const platform = envelope["platform"];
|
|
2246
|
+
if (typeof platform === "string" && platform) state["platform"] = platform;
|
|
1284
2247
|
let payload = envelope["payload"];
|
|
1285
2248
|
if (!(typeof payload === "object" && payload !== null && !Array.isArray(payload))) {
|
|
1286
2249
|
payload = {};
|
|
@@ -1332,7 +2295,7 @@ function _update(state, event, envelope) {
|
|
|
1332
2295
|
}
|
|
1333
2296
|
if (cmd && mightBeVerification(cmd)) {
|
|
1334
2297
|
const streams = _extract_run_streams(pl);
|
|
1335
|
-
const exit = _extract_exit_reading(pl);
|
|
2298
|
+
const exit = _extract_exit_reading(pl, _is_failure_event(pl, envelope));
|
|
1336
2299
|
const runs = Array.isArray(state["verification_runs"]) ? [...state["verification_runs"]] : [];
|
|
1337
2300
|
runs.push({
|
|
1338
2301
|
command: cmd.slice(0, 512),
|
|
@@ -1356,6 +2319,10 @@ function _update(state, event, envelope) {
|
|
|
1356
2319
|
}
|
|
1357
2320
|
} else if (event === "stop") {
|
|
1358
2321
|
state["last_stop_at"] = _now();
|
|
2322
|
+
if (quality !== null) {
|
|
2323
|
+
state["quality_runs"] = quality.runs;
|
|
2324
|
+
state["quality_files_source"] = quality.source;
|
|
2325
|
+
}
|
|
1359
2326
|
}
|
|
1360
2327
|
state["checked_at"] = _now();
|
|
1361
2328
|
return state;
|
|
@@ -1384,10 +2351,12 @@ function run(stdin_text, options) {
|
|
|
1384
2351
|
}
|
|
1385
2352
|
return 0;
|
|
1386
2353
|
}
|
|
1387
|
-
const target =
|
|
2354
|
+
const target = path4.join(consumer_root, statePathFor(session_id));
|
|
2355
|
+
const quality_mode = event === "stop" ? readTouchedFileQualityMode(consumer_root) : "off";
|
|
2356
|
+
const quality = quality_mode === "off" ? null : collectTouchedFileQuality(consumer_root, session_id);
|
|
1388
2357
|
let state = {};
|
|
1389
2358
|
const outcome = update_json_under_lock(target, (loaded) => {
|
|
1390
|
-
state = _update({ ..._empty_state(), ...loaded }, event, envelope);
|
|
2359
|
+
state = _update({ ..._empty_state(), ...loaded }, event, envelope, quality);
|
|
1391
2360
|
return state;
|
|
1392
2361
|
});
|
|
1393
2362
|
if (outcome === "failed") {
|
|
@@ -1397,13 +2366,20 @@ function run(stdin_text, options) {
|
|
|
1397
2366
|
return 0;
|
|
1398
2367
|
}
|
|
1399
2368
|
if (event === "session_start" || event === "user_prompt_submit") {
|
|
1400
|
-
prune_legacy_state_file(
|
|
2369
|
+
prune_legacy_state_file(path4.join(consumer_root, STATE_FILE));
|
|
1401
2370
|
prune_stale_session_states(
|
|
1402
|
-
|
|
2371
|
+
path4.join(consumer_root, STATE_DIR),
|
|
1403
2372
|
Date.now(),
|
|
1404
2373
|
STATE_RETENTION_DAYS
|
|
1405
2374
|
);
|
|
1406
2375
|
}
|
|
2376
|
+
if (quality_mode === "warn" && quality !== null) {
|
|
2377
|
+
const line = advisoryLine(quality.runs);
|
|
2378
|
+
if (line !== null) {
|
|
2379
|
+
process.stderr.write(`${line}
|
|
2380
|
+
`);
|
|
2381
|
+
}
|
|
2382
|
+
}
|
|
1407
2383
|
if (verbose) {
|
|
1408
2384
|
process.stderr.write(
|
|
1409
2385
|
`verify-before-complete-hook: event=${event} verified_this_turn=${pyRepr(state["verified_this_turn"])} verifications_this_turn=${pyRepr(state["verifications_this_turn"])}
|
|
@@ -1447,7 +2423,7 @@ function main(argv) {
|
|
|
1447
2423
|
function readTurnRunState(workspaceRoot, session_id) {
|
|
1448
2424
|
if (!has_stable_session_id(session_id)) return null;
|
|
1449
2425
|
try {
|
|
1450
|
-
const raw =
|
|
2426
|
+
const raw = fs4.readFileSync(path4.join(workspaceRoot, statePathFor(session_id)), "utf-8");
|
|
1451
2427
|
const decoded = JSON.parse(raw);
|
|
1452
2428
|
if (!owns_session_state(decoded, session_id)) return null;
|
|
1453
2429
|
if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return null;
|
|
@@ -1468,13 +2444,13 @@ function _isCliEntry() {
|
|
|
1468
2444
|
if (process.argv[1] === void 0) {
|
|
1469
2445
|
return false;
|
|
1470
2446
|
}
|
|
1471
|
-
const argvUrl = pathToFileURL(
|
|
2447
|
+
const argvUrl = pathToFileURL(path4.resolve(process.argv[1])).href;
|
|
1472
2448
|
if (import.meta.url === argvUrl) {
|
|
1473
2449
|
return true;
|
|
1474
2450
|
}
|
|
1475
2451
|
try {
|
|
1476
|
-
const here =
|
|
1477
|
-
const argv =
|
|
2452
|
+
const here = fs4.realpathSync(fileURLToPath(import.meta.url));
|
|
2453
|
+
const argv = fs4.realpathSync(path4.resolve(process.argv[1]));
|
|
1478
2454
|
return here === argv;
|
|
1479
2455
|
} catch {
|
|
1480
2456
|
return false;
|
|
@@ -1487,8 +2463,8 @@ if (isCliEntry) {
|
|
|
1487
2463
|
|
|
1488
2464
|
// src/scripts/_lib/turn_end_refusals.ts
|
|
1489
2465
|
import * as crypto from "node:crypto";
|
|
1490
|
-
import * as
|
|
1491
|
-
import * as
|
|
2466
|
+
import * as fs5 from "node:fs";
|
|
2467
|
+
import * as path5 from "node:path";
|
|
1492
2468
|
var DETECTOR_IDS = [
|
|
1493
2469
|
"promissory",
|
|
1494
2470
|
"language",
|
|
@@ -1497,19 +2473,19 @@ var DETECTOR_IDS = [
|
|
|
1497
2473
|
"pending-decision",
|
|
1498
2474
|
"untested"
|
|
1499
2475
|
];
|
|
1500
|
-
var REFUSAL_STATE_REL =
|
|
2476
|
+
var REFUSAL_STATE_REL = path5.join("agents", "runtime", "state", "turn-end-gate");
|
|
1501
2477
|
function refusalStateDir(workspaceRoot) {
|
|
1502
|
-
return
|
|
2478
|
+
return path5.join(workspaceRoot, REFUSAL_STATE_REL);
|
|
1503
2479
|
}
|
|
1504
2480
|
function deriveSessionKey(sessionId) {
|
|
1505
2481
|
return crypto.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
1506
2482
|
}
|
|
1507
2483
|
function sessionRefusalFile(workspaceRoot, sessionKey) {
|
|
1508
|
-
return
|
|
2484
|
+
return path5.join(refusalStateDir(workspaceRoot), `${sessionKey}.json`);
|
|
1509
2485
|
}
|
|
1510
2486
|
function readSessionCounts(workspaceRoot, sessionId) {
|
|
1511
2487
|
try {
|
|
1512
|
-
const raw =
|
|
2488
|
+
const raw = fs5.readFileSync(
|
|
1513
2489
|
sessionRefusalFile(workspaceRoot, deriveSessionKey(sessionId)),
|
|
1514
2490
|
"utf-8"
|
|
1515
2491
|
);
|
|
@@ -1620,7 +2596,7 @@ function emptyRetryCounts() {
|
|
|
1620
2596
|
var SHADOW_MAX_ROWS = 200;
|
|
1621
2597
|
var SHADOW_SUFFIX = ".shadow.json";
|
|
1622
2598
|
function sessionShadowFile(workspaceRoot, sessionKey) {
|
|
1623
|
-
return
|
|
2599
|
+
return path5.join(refusalStateDir(workspaceRoot), `${sessionKey}${SHADOW_SUFFIX}`);
|
|
1624
2600
|
}
|
|
1625
2601
|
function parseShadowRecord(raw) {
|
|
1626
2602
|
let decoded;
|
|
@@ -1645,7 +2621,9 @@ function parseShadowRecord(raw) {
|
|
|
1645
2621
|
if (typeof turn !== "number" || !Number.isFinite(turn)) continue;
|
|
1646
2622
|
if (typeof at !== "string") continue;
|
|
1647
2623
|
if (layer !== "stop_hook_active" && layer !== "refused_turn") continue;
|
|
1648
|
-
|
|
2624
|
+
const setByRaw = ro["set_by"];
|
|
2625
|
+
const set_by = setByRaw === "ours" || setByRaw === "foreign" || setByRaw === "unknown" ? setByRaw : "unknown";
|
|
2626
|
+
rows.push({ detector: det, turn, at, layer, set_by });
|
|
1649
2627
|
}
|
|
1650
2628
|
const num = (v) => typeof v === "number" && Number.isFinite(v) && v >= 0 ? v : 0;
|
|
1651
2629
|
const observed = o["retries_observed"];
|
|
@@ -1665,8 +2643,15 @@ function parseShadowRecord(raw) {
|
|
|
1665
2643
|
}
|
|
1666
2644
|
function foldShadow(prev, input) {
|
|
1667
2645
|
const rows = [...prev?.would_refuse_again ?? []];
|
|
2646
|
+
const set_by = input.setBy ?? "unknown";
|
|
1668
2647
|
for (const d of input.detectors) {
|
|
1669
|
-
rows.push({
|
|
2648
|
+
rows.push({
|
|
2649
|
+
detector: d,
|
|
2650
|
+
turn: input.turnOrdinal,
|
|
2651
|
+
at: input.at,
|
|
2652
|
+
layer: input.layer,
|
|
2653
|
+
set_by
|
|
2654
|
+
});
|
|
1670
2655
|
}
|
|
1671
2656
|
let dropped = prev?.dropped ?? 0;
|
|
1672
2657
|
if (rows.length > SHADOW_MAX_ROWS) {
|
|
@@ -1684,27 +2669,37 @@ function foldShadow(prev, input) {
|
|
|
1684
2669
|
last_at: input.at
|
|
1685
2670
|
};
|
|
1686
2671
|
}
|
|
2672
|
+
function shadowSetBy(workspaceRoot, sessionKey) {
|
|
2673
|
+
let raw;
|
|
2674
|
+
try {
|
|
2675
|
+
raw = fs5.readFileSync(sessionRefusalFile(workspaceRoot, sessionKey), "utf-8");
|
|
2676
|
+
} catch (err) {
|
|
2677
|
+
const code = err?.code;
|
|
2678
|
+
return code === "ENOENT" ? "foreign" : "unknown";
|
|
2679
|
+
}
|
|
2680
|
+
return parseRecord(raw) === null ? "unknown" : "ours";
|
|
2681
|
+
}
|
|
1687
2682
|
var REFUSAL_STATE_MAX_AGE_DAYS = 90;
|
|
1688
2683
|
function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
1689
2684
|
const maxAgeDays = opts.maxAgeDays ?? REFUSAL_STATE_MAX_AGE_DAYS;
|
|
1690
|
-
const
|
|
2685
|
+
const now2 = opts.now ?? /* @__PURE__ */ new Date();
|
|
1691
2686
|
const dir = refusalStateDir(workspaceRoot);
|
|
1692
2687
|
const result = { scanned: 0, pruned: 0, kept: 0 };
|
|
1693
2688
|
let entries;
|
|
1694
2689
|
try {
|
|
1695
|
-
entries =
|
|
2690
|
+
entries = fs5.readdirSync(dir);
|
|
1696
2691
|
} catch {
|
|
1697
2692
|
return result;
|
|
1698
2693
|
}
|
|
1699
|
-
const cutoffMs =
|
|
2694
|
+
const cutoffMs = now2.getTime() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
1700
2695
|
for (const name of entries) {
|
|
1701
2696
|
if (!name.endsWith(".json")) continue;
|
|
1702
2697
|
result.scanned += 1;
|
|
1703
|
-
const file =
|
|
2698
|
+
const file = path5.join(dir, name);
|
|
1704
2699
|
if (name.endsWith(SHADOW_SUFFIX)) {
|
|
1705
2700
|
let shadow = null;
|
|
1706
2701
|
try {
|
|
1707
|
-
shadow = parseShadowRecord(
|
|
2702
|
+
shadow = parseShadowRecord(fs5.readFileSync(file, "utf-8"));
|
|
1708
2703
|
} catch {
|
|
1709
2704
|
shadow = null;
|
|
1710
2705
|
}
|
|
@@ -1712,7 +2707,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
|
1712
2707
|
if (!Number.isFinite(shadowAt)) {
|
|
1713
2708
|
let mtime = NaN;
|
|
1714
2709
|
try {
|
|
1715
|
-
mtime =
|
|
2710
|
+
mtime = fs5.statSync(file).mtimeMs;
|
|
1716
2711
|
} catch {
|
|
1717
2712
|
mtime = NaN;
|
|
1718
2713
|
}
|
|
@@ -1725,7 +2720,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
|
1725
2720
|
continue;
|
|
1726
2721
|
}
|
|
1727
2722
|
try {
|
|
1728
|
-
|
|
2723
|
+
fs5.unlinkSync(file);
|
|
1729
2724
|
result.pruned += 1;
|
|
1730
2725
|
} catch {
|
|
1731
2726
|
result.kept += 1;
|
|
@@ -1734,7 +2729,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
|
1734
2729
|
}
|
|
1735
2730
|
let rec = null;
|
|
1736
2731
|
try {
|
|
1737
|
-
rec = parseRecord(
|
|
2732
|
+
rec = parseRecord(fs5.readFileSync(file, "utf-8"));
|
|
1738
2733
|
} catch {
|
|
1739
2734
|
rec = null;
|
|
1740
2735
|
}
|
|
@@ -1748,7 +2743,7 @@ function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
|
1748
2743
|
continue;
|
|
1749
2744
|
}
|
|
1750
2745
|
try {
|
|
1751
|
-
|
|
2746
|
+
fs5.unlinkSync(file);
|
|
1752
2747
|
result.pruned += 1;
|
|
1753
2748
|
} catch {
|
|
1754
2749
|
result.kept += 1;
|
|
@@ -1783,5 +2778,6 @@ export {
|
|
|
1783
2778
|
sessionShadowFile,
|
|
1784
2779
|
parseShadowRecord,
|
|
1785
2780
|
foldShadow,
|
|
2781
|
+
shadowSetBy,
|
|
1786
2782
|
pruneAgedRefusalState
|
|
1787
2783
|
};
|