@event4u/agent-config 16.3.0 → 16.5.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 +178 -78
- package/README.md +2 -2
- package/agents/templates/.ai-council.yml.example +26 -8
- package/dist/agent-src/commands/analyze/inbox.md +10 -7
- 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/design.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 +1 -1
- package/dist/agent-src/commands/fix/ci.md +17 -3
- 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 +3 -3
- 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/next.md +10 -1
- package/dist/agent-src/commands/roadmap/process-full.md +52 -38
- package/dist/agent-src/commands/roadmap/resolve-blockers.md +64 -14
- 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/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 +1 -1
- 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 +67 -52
- 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 +4 -10
- 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/design-antipatterns.md +1 -1
- package/dist/agent-src/guidelines/php/git.md +15 -2
- package/dist/agent-src/rules/active-remediation.md +0 -1
- 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 +0 -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 +0 -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 +0 -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 +5 -5
- 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 +2 -3
- package/dist/agent-src/rules/delegation-policy.md +2 -3
- package/dist/agent-src/rules/design-fidelity.md +0 -1
- 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 +0 -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 +0 -1
- package/dist/agent-src/rules/domain-safety-pii.md +0 -1
- package/dist/agent-src/rules/domain-safety-retention.md +0 -1
- package/dist/agent-src/rules/downstream-changes.md +0 -1
- package/dist/agent-src/rules/engineering-safety-floor.md +0 -1
- 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 +1 -2
- package/dist/agent-src/rules/finance-safety-floor.md +0 -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 +1 -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 +0 -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 +0 -1
- package/dist/agent-src/rules/lethal-trifecta-guard.md +0 -1
- package/dist/agent-src/rules/linked-projects-onboarding-gate.md +0 -1
- package/dist/agent-src/rules/low-impact-corpus-privacy-floor.md +0 -1
- package/dist/agent-src/rules/markdown-safe-codeblocks.md +0 -1
- package/dist/agent-src/rules/media-governance-routing.md +1 -2
- 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 +2 -3
- 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 +2 -3
- package/dist/agent-src/rules/question-not-instruction.md +0 -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 +3 -6
- 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 +0 -1
- package/dist/agent-src/rules/scale-discipline.md +0 -1
- package/dist/agent-src/rules/secret-vcs-guard.md +0 -1
- package/dist/agent-src/rules/security-sensitive-stop.md +0 -1
- 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 +0 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +5 -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 +0 -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 +0 -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 +14 -48
- 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 +0 -1
- package/dist/agent-src/rules/ui-audit-gate.md +1 -2
- package/dist/agent-src/rules/untrusted-input-defense.md +0 -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 +0 -1
- package/dist/agent-src/scripts/gate_budget.ts +47 -23
- package/dist/agent-src/scripts/gate_execute.ts +12 -4
- package/dist/agent-src/skills/accessibility-auditor/data/aria-patterns.csv +2 -0
- package/dist/agent-src/skills/accessibility-auditor/data/manifest.json +3 -3
- 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/bug-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/canvas-design/SKILL.md +4 -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-intelligence/references/integration-mapping.md +13 -10
- package/dist/agent-src/skills/design-system-capture/SKILL.md +0 -3
- package/dist/agent-src/skills/estimate-ticket/SKILL.md +1 -1
- package/dist/agent-src/skills/fe-design/references/design-patterns.md +49 -2
- package/dist/agent-src/skills/feature-planning/SKILL.md +1 -1
- package/dist/agent-src/skills/frontend-render-security/SKILL.md +9 -0
- 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 +4 -2
- package/dist/agent-src/skills/image-provider-routing/SKILL.md +16 -5
- package/dist/agent-src/skills/jira-integration/SKILL.md +3 -3
- package/dist/agent-src/skills/logo-generation/SKILL.md +12 -8
- package/dist/agent-src/skills/merge-conflicts/SKILL.md +10 -4
- package/dist/agent-src/skills/po-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/refine-ticket/SKILL.md +4 -4
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +2 -2
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +4 -2
- package/dist/agent-src/skills/source-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/svg-authoring/SKILL.md +318 -0
- package/dist/agent-src/skills/tailwind-engineer/SKILL.md +3 -1
- package/dist/agent-src/skills/testing-anti-patterns/SKILL.md +1 -1
- 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/skills/wireframe/SKILL.md +5 -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/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/skill.md +1 -1
- package/dist/cli/commands/uiRender.js +201 -4
- package/dist/cli/commands/uiRenderAsset.js +1308 -0
- package/dist/cli/commands/uiRenderAssetCompare.js +752 -0
- package/dist/cli/commands/uiRenderAssetGrid.js +265 -0
- package/dist/cli/commands/uiRenderAssetGrounds.js +323 -0
- package/dist/cli/commands/uiRenderAssetIsolation.js +85 -0
- package/dist/cli/commands/uiRenderAssetTypes.js +145 -0
- package/dist/cli/main.js +47 -1
- package/dist/cli/registry.js +2 -1
- package/dist/cli-delegate/check_update_banner.js +195 -119
- package/dist/cli-delegate/{chunk-S4UMZNES.js → chunk-4DBG5K7M.js} +5 -5
- package/dist/cli-delegate/{chunk-TRI6DQZ6.js → chunk-5GPULQBE.js} +60 -42
- package/dist/cli-delegate/{chunk-JQVYKIZA.js → chunk-6SRJTYJS.js} +33 -177
- package/dist/cli-delegate/chunk-6V5FMA3P.js +186 -0
- 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-7HQ52DHU.js → chunk-AL3RZ22D.js} +1 -1
- package/dist/cli-delegate/{chunk-DBH4AHDK.js → chunk-ED7KCMLB.js} +9 -11
- package/dist/cli-delegate/{chunk-NXEILF2K.js → chunk-F3QDG2GT.js} +481 -72
- package/dist/cli-delegate/{chunk-CQWX336L.js → chunk-G5VVIZRC.js} +3 -3
- package/dist/cli-delegate/{chunk-JY2I2DXV.js → chunk-H76PHZBI.js} +137 -60
- package/dist/cli-delegate/chunk-IAYTIIQW.js +67 -0
- package/dist/cli-delegate/{chunk-LIYTLH6O.js → chunk-KXXWJTDO.js} +5 -10
- package/dist/cli-delegate/{chunk-T7L2QK3K.js → chunk-LQOCRFWT.js} +1 -1
- package/dist/cli-delegate/{chunk-S4YWNRKO.js → chunk-MDPPKQCQ.js} +34 -1
- package/dist/cli-delegate/chunk-OAFSJBHH.js +272 -0
- package/dist/cli-delegate/{chunk-GKE7ZVFA.js → chunk-PWX7CKMD.js} +1 -1
- package/dist/cli-delegate/{chunk-NDJLNC6Q.js → chunk-T6BP34WI.js} +1 -0
- package/dist/cli-delegate/{chunk-RQNNW7IF.js → chunk-T7XSJSUQ.js} +433 -632
- package/dist/cli-delegate/{chunk-NQ7WT43P.js → chunk-TMWYUOK4.js} +75 -12
- package/dist/cli-delegate/{chunk-EEHDXSYD.js → chunk-WQWXGIWB.js} +12 -273
- package/dist/cli-delegate/{chunk-QLDHIPWR.js → chunk-XQTPXKFA.js} +1 -1
- 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 +489 -238
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +15 -12
- package/dist/cli-delegate/cmd_doctor_neighbours.js +11 -4
- package/dist/cli-delegate/cmd_explain.js +43 -19
- package/dist/cli-delegate/cmd_export.js +10 -10
- package/dist/cli-delegate/cmd_git_convention.js +1851 -0
- package/dist/cli-delegate/cmd_handoff.js +40 -21
- package/dist/cli-delegate/cmd_mcp_available.js +3 -2
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +4 -3
- package/dist/cli-delegate/cmd_prune.js +10 -10
- package/dist/cli-delegate/cmd_route_audit.js +7 -7
- package/dist/cli-delegate/cmd_route_explain.js +5 -5
- package/dist/cli-delegate/cmd_session_recycle.js +9 -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 +10 -10
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +10 -10
- package/dist/cli-delegate/cmd_uninstall.js +10 -10
- package/dist/cli-delegate/cmd_update.js +7 -7
- package/dist/cli-delegate/cmd_upgrade.js +7 -7
- package/dist/cli-delegate/cmd_validate.js +10 -10
- package/dist/cli-delegate/cmd_versions.js +5 -5
- package/dist/cli-delegate/gate_budget.js +1 -1
- package/dist/cli-delegate/gate_execute.js +2 -2
- package/dist/cli-delegate/roadmap_gates.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +171 -107
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +7 -7
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +41 -22
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +16 -11
- package/dist/hooks/dispatch.js +2520 -1924
- package/dist/hooks/dispatch.sha256 +1 -1
- package/dist/hooks/svg_rules.chunk.cjs +1626 -0
- package/dist/hooks/svg_rules.chunk.cjs.sha256 +1 -0
- package/dist/install/install.mjs +462 -431
- package/dist/install/installThinLayer.js +40 -12
- package/dist/install/installedRuleLinks.js +105 -19
- 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 +3063 -2160
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +15 -120
- 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 +344 -18
- package/dist/scripts/_lib/package_root.js +68 -0
- package/dist/scripts/_lib/rule_law_section.js +1 -1
- package/dist/scripts/_lib/scan_scope.js +105 -0
- package/dist/scripts/_lib/settings_removed_keys.js +168 -0
- package/dist/scripts/_lib/svg_dom.js +284 -0
- package/dist/scripts/_lib/svg_geometry.js +477 -0
- package/dist/scripts/_lib/svg_motion.js +179 -0
- package/dist/scripts/_lib/svg_rules.js +1337 -0
- package/dist/scripts/_lib/svg_urls.js +67 -0
- package/dist/scripts/_lib/thin_rules.js +80 -3
- package/dist/scripts/lint_svg_asset.js +324 -0
- 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 +23 -81
- package/dist/shared/skillRanking.js +30 -4
- 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 +35 -65
- 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/design-antipatterns.md +1 -1
- package/docs/guidelines/php/git.md +15 -2
- package/package.json +4 -3
- package/src/agent-src/scripts/gate_budget.ts +47 -23
- package/src/agent-src/scripts/gate_execute.ts +12 -4
- 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/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/config/agent-settings.template.yml +79 -312
- 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 +3 -3
- package/src/config/discovery/packs.yml +17 -1
- package/src/config/evaluator-budgets.json +19 -6
- package/src/config/gate-coverage.yml +65 -23
- package/src/config/gate-violation-baselines.json +56 -16
- package/src/config/gitignore-block.txt +19 -0
- package/src/config/hook-bundle-budget.json +1 -1
- package/src/config/release-gate-locality.yml +2 -1
- package/src/config/routing-coverage-seed.json +55 -6
- package/src/config/rule-law-ceilings.json +12 -6
- package/src/config/rule-obligations.json +833 -0
- package/src/install/installThinLayer.ts +76 -10
- package/src/install/installedRuleLinks.ts +105 -17
- 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 +10 -10
- package/src/scripts/_cli/cmd_doctor_neighbours.ts +133 -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 +37 -2
- package/src/scripts/_lib/agent_settings.ts +15 -124
- package/src/scripts/_lib/blocked_by_marker.ts +1 -1
- package/src/scripts/_lib/compile_time_toggles.ts +12 -21
- package/src/scripts/_lib/dispatch_import_closure.ts +163 -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/global_deploy_inventory.ts +74 -0
- package/src/scripts/_lib/graph_feeder_record.ts +236 -13
- package/src/scripts/_lib/installed_layer.ts +448 -23
- package/src/scripts/_lib/knowledge_global.ts +14 -1
- package/src/scripts/_lib/module_reach.ts +456 -0
- package/src/scripts/_lib/neighbour_contradictions.ts +236 -0
- package/src/scripts/_lib/neighbour_tool_use.ts +8 -10
- 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/rule_law_section.ts +1 -1
- 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/svg_dom.ts +318 -0
- package/src/scripts/_lib/svg_geometry.ts +480 -0
- package/src/scripts/_lib/svg_motion.ts +208 -0
- package/src/scripts/_lib/svg_rules.ts +1453 -0
- package/src/scripts/_lib/svg_urls.ts +72 -0
- package/src/scripts/_lib/test_red_state.ts +5 -0
- package/src/scripts/_lib/thin_rules.ts +83 -3
- package/src/scripts/_lib/touched_file_quality.ts +142 -6
- package/src/scripts/_lib/verification_command.ts +59 -0
- package/src/scripts/_lib/verify_clause.ts +27 -17
- package/src/scripts/_lib/worker_budget.ts +1 -1
- package/src/scripts/adr/regenerate_index.ts +16 -1
- package/src/scripts/ai_council/config.ts +7 -7
- 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_team/config.ts +11 -16
- package/src/scripts/audit_user_type_axis.ts +20 -8
- 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/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_evaluator_budgets.ts +4 -0
- package/src/scripts/check_finding_dispositions.ts +88 -25
- 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 +107 -0
- package/src/scripts/check_kernel_edit_ratified.ts +86 -6
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_manifest_content.ts +189 -0
- package/src/scripts/check_standing_rule_delivery.ts +96 -0
- package/src/scripts/check_suppression_hygiene.ts +2 -5
- 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/command_suggester/settings.ts +8 -108
- package/src/scripts/command_suggester/types.ts +4 -5
- 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/explain_run.ts +8 -13
- package/src/scripts/generate_git_convention_grammar.ts +127 -0
- package/src/scripts/generate_host_cost_table.ts +20 -3
- package/src/scripts/handoff_context_hook.ts +86 -37
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +25 -6
- package/src/scripts/hooks/block_config_weakening.ts +48 -5
- package/src/scripts/hooks/block_plumbing_writes.ts +8 -4
- package/src/scripts/hooks/block_speaking_inbox_dir.ts +16 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +2 -3
- package/src/scripts/hooks/design_pass_hook.ts +157 -3
- package/src/scripts/hooks/evidence_independence.ts +12 -4
- package/src/scripts/hooks/mcp_usage_observation_hook.ts +121 -0
- package/src/scripts/hooks/push_settle_hook.ts +4 -3
- package/src/scripts/hooks/rule_inject_hook.ts +44 -3
- package/src/scripts/hooks/state_io.ts +8 -4
- package/src/scripts/hooks/svg_obligation.ts +497 -0
- package/src/scripts/hooks/svg_rules_loader.ts +133 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +7 -46
- package/src/scripts/hooks/turn_end_gate_hook.ts +6 -3
- package/src/scripts/install-hooks.sh +42 -14
- package/src/scripts/install.ts +7 -31
- package/src/scripts/lint_blocker_citations.ts +198 -0
- package/src/scripts/lint_consolidation_lineage.ts +6 -3
- package/src/scripts/lint_design_quality.ts +60 -12
- package/src/scripts/lint_evidence_artifacts.ts +9 -0
- package/src/scripts/lint_hook_concern_budget.ts +10 -21
- package/src/scripts/lint_motion_authority_drift.ts +27 -0
- package/src/scripts/lint_pack_boundaries.ts +406 -28
- 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 +151 -21
- package/src/scripts/lint_rule_skill_pack_reach.ts +17 -0
- package/src/scripts/lint_settings_classes.ts +1 -1
- package/src/scripts/lint_svg_asset.ts +365 -0
- package/src/scripts/lint_ticked_unmet_criteria.ts +165 -0
- package/src/scripts/lint_workflow_security.ts +12 -1
- package/src/scripts/measure_skill_ranker_baseline.ts +5 -0
- package/src/scripts/model_rule_injection.ts +3 -3
- 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/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_installed_rule_links.ts +118 -1
- package/src/scripts/report_module_reach.ts +212 -0
- package/src/scripts/report_obligation_mechanism.ts +16 -18
- package/src/scripts/report_parked_blockers.ts +222 -0
- package/src/scripts/report_required_checks_drift.ts +196 -0
- package/src/scripts/report_skill_ranker_confusion.ts +103 -2
- package/src/scripts/report_standing_payload_by_host.ts +1 -1
- package/src/scripts/routing_doctor.ts +2 -2
- package/src/scripts/schemas/agent-settings.schema.json +17 -31
- package/src/scripts/schemas/command.schema.json +1 -1
- package/src/scripts/schemas/skill.schema.json +1 -1
- package/src/scripts/self_review_gate.ts +39 -2
- package/src/scripts/skill_tools/score_skill_relevance.ts +28 -19
- package/src/scripts/sync_pr_branch.ts +403 -132
- package/src/scripts/trigger_eval_grandfather.json +0 -16
- package/src/server/schemas/settings.ts +33 -177
- package/src/shared/skillRanking.ts +53 -4
- package/dist/cli-delegate/chunk-NYX3L2PZ.js +0 -99
- 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
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How a browser reads the URLs and names in an SVG, for the safety rules in
|
|
3
|
+
* `svg_rules.ts`: references decoded, controls stripped, CSS escapes resolved,
|
|
4
|
+
* and an element prefix stripped only where it is bound to the SVG namespace.
|
|
5
|
+
* Its own module so `svg_rules.ts` stays under the source-size ceiling.
|
|
6
|
+
*/
|
|
7
|
+
/** Prefixes the document binds to the SVG namespace (`xmlns:svg="…/2000/svg"`). */
|
|
8
|
+
export function svgPrefixes(content) {
|
|
9
|
+
const out = new Set();
|
|
10
|
+
const re = /xmlns:([\w.-]+)\s*=\s*["']http:\/\/www\.w3\.org\/2000\/svg["']/gi;
|
|
11
|
+
let m;
|
|
12
|
+
while ((m = re.exec(content)) !== null)
|
|
13
|
+
out.add((m[1] ?? '').toLowerCase());
|
|
14
|
+
return out;
|
|
15
|
+
}
|
|
16
|
+
/** The SVG local name: `svg:script` is a `script` where `svg` is bound to SVG; `foo:script` is not. */
|
|
17
|
+
export function localName(name, prefixes) {
|
|
18
|
+
const colon = name.indexOf(':');
|
|
19
|
+
return colon !== -1 && prefixes.has(name.slice(0, colon)) ? name.slice(colon + 1) : name;
|
|
20
|
+
}
|
|
21
|
+
/** Attributes read from the raw tag, unquoted values included — HTML parses those. */
|
|
22
|
+
export function rawAttrs(raw) {
|
|
23
|
+
const out = {};
|
|
24
|
+
const re = /([\w:.-]+)\s*=\s*(?:"([^"]*)"|'([^']*)'|([^\s>"'`=]+))/g;
|
|
25
|
+
let m;
|
|
26
|
+
while ((m = re.exec(raw)) !== null)
|
|
27
|
+
out[(m[1] ?? '').toLowerCase()] = m[2] ?? m[3] ?? m[4] ?? '';
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
/** Attributes whose value is followed as a URL. */
|
|
31
|
+
export const URL_ATTRS = new Set(['href', 'xlink:href', 'src']);
|
|
32
|
+
/** A URL that executes or opens a document when followed. */
|
|
33
|
+
export const isActiveUrl = (raw) => /^(javascript:|data:text\/html)/i.test(resolvedUrl(raw));
|
|
34
|
+
const NAMED_URL_ENTITIES = {
|
|
35
|
+
colon: ':', sol: '/', tab: '\t', newline: '\n', period: '.', lpar: '(', rpar: ')', amp: '&',
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The URL a browser would actually resolve from an attribute value: character
|
|
39
|
+
* references decoded, leading/trailing C0 controls and spaces stripped, tab and
|
|
40
|
+
* newline removed anywhere. The trailing `;` of a reference is optional on
|
|
41
|
+
* purpose: an `.svg` parsed as XML needs it, but the same graphic inlined in an
|
|
42
|
+
* HTML host is parsed by the HTML tokenizer, which decodes `javascript:`
|
|
43
|
+
* too — over-reading XML here costs precision, under-reading HTML costs a miss.
|
|
44
|
+
* The raw value is attacker-chosen, so a scheme test
|
|
45
|
+
* against it is a test of one spelling — ` https:`, `ht\ttps:`, `javascript:`
|
|
46
|
+
* all load, and all passed.
|
|
47
|
+
*/
|
|
48
|
+
export function resolvedUrl(raw) {
|
|
49
|
+
const decoded = raw.replace(/&(?:#x([0-9a-f]+)|#(\d+)|([a-z]+));?/gi, (m, hex, dec, name) => {
|
|
50
|
+
const cp = hex ? parseInt(hex, 16) : dec ? parseInt(dec, 10) : NaN;
|
|
51
|
+
if (!Number.isNaN(cp))
|
|
52
|
+
return cp <= 0x10ffff ? String.fromCodePoint(cp) : m;
|
|
53
|
+
return NAMED_URL_ENTITIES[name.toLowerCase()] ?? m;
|
|
54
|
+
});
|
|
55
|
+
return decoded.replace(/^[\u0000- ]+|[\u0000- ]+$/g, '').replace(/[\t\n\r]/g, '');
|
|
56
|
+
}
|
|
57
|
+
/** CSS escapes decoded: `\\68 ` and `\\h` both read `h`. */
|
|
58
|
+
export function cssUnescape(raw) {
|
|
59
|
+
return raw.replace(/\\([0-9a-f]{1,6})\s?|\\(.)/gi, (m, hex, ch) => {
|
|
60
|
+
if (hex) {
|
|
61
|
+
const cp = parseInt(hex, 16);
|
|
62
|
+
return cp <= 0x10ffff ? String.fromCodePoint(cp) : m;
|
|
63
|
+
}
|
|
64
|
+
return ch;
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=svg_urls.js.map
|
|
@@ -255,13 +255,90 @@ function _title(s) {
|
|
|
255
255
|
* Exported so the WRITER and every DETECTOR share one definition. A gate that
|
|
256
256
|
* re-spelled this string would drift from the writer silently, and the failure
|
|
257
257
|
* would be invisible in exactly the direction that matters: a stub the gate
|
|
258
|
-
* fails to recognise reads as a complete rule body.
|
|
258
|
+
* fails to recognise reads as a complete rule body. Two gates had done exactly
|
|
259
|
+
* that until step 2.1 of `road-to-a-thinned-layer-measured-in-one-unit`; both
|
|
260
|
+
* import this now.
|
|
261
|
+
*
|
|
262
|
+
* SHORTENED 2026-10-06 from 47 characters to 27 (step 2.2). It is paid once per
|
|
263
|
+
* stub and there were 89 stubs on the measured layer, so the sentence cost
|
|
264
|
+
* 4,183 characters of a 97,496-character standing total; at 27 it costs 2,403,
|
|
265
|
+
* a saving of 1,780 = 20 x 89.
|
|
266
|
+
*
|
|
267
|
+
* WHAT THE SHORTENING WAS NOT ALLOWED TO DO: drop the instruction. This is the
|
|
268
|
+
* only standing text in the whole layer that says what a stub is FOR — "load
|
|
269
|
+
* the body" occurs in no kernel rule and in neither root instruction file — so
|
|
270
|
+
* a bare token like `> thin` would have saved more and left nothing that could
|
|
271
|
+
* be followed. The decision record (D2, K6) rejected that explicitly: nothing
|
|
272
|
+
* in this repository can check whether an instruction nobody states is still
|
|
273
|
+
* obeyed.
|
|
274
|
+
*
|
|
275
|
+
* WHY IT MUST STAY LONG ENOUGH TO BE UNIQUE: {@link is_thin_entry} is a
|
|
276
|
+
* substring test over the whole file, so a marker short or generic enough to
|
|
277
|
+
* occur inside ordinary rule prose would make that rule read as a stub — and
|
|
278
|
+
* nine rule files carry blockquote lines. `thin_marker_unique_in_corpus.test.ts`
|
|
279
|
+
* runs the constant against every rule body in `src/rules/` and against every
|
|
280
|
+
* entry `build_thin` emits, in both directions, which is the check that bounds
|
|
281
|
+
* any further shortening.
|
|
259
282
|
*/
|
|
260
|
-
export const THIN_ENTRY_MARKER = '>
|
|
283
|
+
export const THIN_ENTRY_MARKER = '> Load the body on a match.';
|
|
261
284
|
/** Is this projected entry a pointer stub rather than a rule body? */
|
|
262
285
|
export function is_thin_entry(text) {
|
|
263
286
|
return text.includes(THIN_ENTRY_MARKER);
|
|
264
287
|
}
|
|
288
|
+
/**
|
|
289
|
+
* What introduces a stub's body pointer. One writer, one reader.
|
|
290
|
+
*
|
|
291
|
+
* A BARE PATH SINCE 2026-10-06 (step 2.3 of
|
|
292
|
+
* `road-to-a-thinned-layer-measured-in-one-unit`). The pointer used to be a
|
|
293
|
+
* markdown link, ``Body: [`<id>`](<prefix><id>.md)``, whose link TEXT repeated
|
|
294
|
+
* the rule id the target already ends in. That cost `6 + len(id)` per stub over
|
|
295
|
+
* the bare form — 2,359 characters across the 89 stubs of the measured layer,
|
|
296
|
+
* 534 of it pure syntax and the rest the id written a second time.
|
|
297
|
+
*
|
|
298
|
+
* Nothing followed the link as a link. Under `delivery` the hook loads the body
|
|
299
|
+
* and never reads the pointer at all; under `thin` an agent reads the path. A
|
|
300
|
+
* markdown link renders in neither case, so the syntax bought rendering nobody
|
|
301
|
+
* was doing.
|
|
302
|
+
*/
|
|
303
|
+
export const THIN_BODY_POINTER_PREFIX = 'Body: ';
|
|
304
|
+
/**
|
|
305
|
+
* The pointer, as the WRITER emits it — exported so a detector cannot re-spell
|
|
306
|
+
* it, which is the same drift {@link THIN_ENTRY_MARKER} documents one screen up
|
|
307
|
+
* and which two gates had already committed there.
|
|
308
|
+
*
|
|
309
|
+
* WHAT IT CAPTURES is the whole tail of the line from `Body: ` to its end, and
|
|
310
|
+
* that is the intended semantic rather than an accident of greediness: the
|
|
311
|
+
* writer emits the pointer LAST on its line, so the tail IS the path. A review
|
|
312
|
+
* round proposed a lazy `(.+?\.md)` to stop at the first `.md`; measured, it
|
|
313
|
+
* changes nothing, because `\s*$` forces the match to reach end of line either
|
|
314
|
+
* way and a lazy quantifier simply backtracks forward to the same place. The
|
|
315
|
+
* greedy form is kept and the property is pinned by a test, so the next reader
|
|
316
|
+
* meets the measurement instead of repeating the proposal.
|
|
317
|
+
*
|
|
318
|
+
* ANCHORED TO END OF LINE, NOT `\S+`. The pointer is the last thing on its
|
|
319
|
+
* line, and a package root may contain spaces; `\S+` would silently stop at the
|
|
320
|
+
* first one and report a well-formed pointer as missing.
|
|
321
|
+
*
|
|
322
|
+
* COMPOSED FROM {@link THIN_BODY_POINTER_PREFIX} rather than re-spelling it.
|
|
323
|
+
* The constant exists so a reader cannot hold its own copy, and a pattern six
|
|
324
|
+
* lines below it holding a literal `Body: ` would have broken that invariant in
|
|
325
|
+
* the file that states it.
|
|
326
|
+
*/
|
|
327
|
+
export const THIN_BODY_POINTER_RE = (() => {
|
|
328
|
+
const lit = THIN_BODY_POINTER_PREFIX.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
329
|
+
// THE LAST PREFIX ON THE LINE, NOT THE FIRST. `thin_entry` puts the rule's
|
|
330
|
+
// own `description` on the SAME line ahead of the pointer, so a description
|
|
331
|
+
// containing the literal `Body: ` used to start the match inside the
|
|
332
|
+
// description and capture it as part of the path — `has_body_pointer` stayed
|
|
333
|
+
// true while the extraction was wrong, which is a wrong assertion rather
|
|
334
|
+
// than a visible failure. Forbidding a second prefix inside the capture
|
|
335
|
+
// makes the first viable start the last occurrence, which is the pointer.
|
|
336
|
+
return new RegExp(`${lit}((?:(?!${lit}).)+\\.md)\\s*$`, 'm');
|
|
337
|
+
})();
|
|
338
|
+
/** Does this entry carry a body pointer the writer would recognise? */
|
|
339
|
+
export function has_body_pointer(text) {
|
|
340
|
+
return THIN_BODY_POINTER_RE.test(text);
|
|
341
|
+
}
|
|
265
342
|
/**
|
|
266
343
|
* Where a stub tells a reader the body actually is.
|
|
267
344
|
*
|
|
@@ -318,7 +395,7 @@ export function thin_entry(rule_id, text, bodyLinkPrefix = BODY_LINK_PREFIX) {
|
|
|
318
395
|
const fires = hint ? ` Fires on: ${hint}.` : '';
|
|
319
396
|
return (`## ${title}\n` +
|
|
320
397
|
`${THIN_ENTRY_MARKER}${fires} ${desc} ` +
|
|
321
|
-
|
|
398
|
+
`${THIN_BODY_POINTER_PREFIX}${bodyLinkPrefix}${rule_id}.md\n`);
|
|
322
399
|
}
|
|
323
400
|
/**
|
|
324
401
|
* The marker that opens a stub's copied law block, and the one that closes it
|
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* lint_svg_asset — the command-line surface over the SVG rule set.
|
|
4
|
+
*
|
|
5
|
+
* The rules live in `_lib/svg_rules.ts`, because the design pass imports them
|
|
6
|
+
* into the hook bundle and must not drag a CLI in with them (see that file's
|
|
7
|
+
* header for the measurement). Everything this module adds is the part only a
|
|
8
|
+
* command needs: argument parsing, the directory walk, the project config, the
|
|
9
|
+
* scanned-nothing refusal, and the step 1.2 preservation readout.
|
|
10
|
+
*
|
|
11
|
+
* Inline-ignore (same shape as `lint_design_quality`, works in XML comments):
|
|
12
|
+
* svg-asset-disable <rule-id> [-- reason] (file scope)
|
|
13
|
+
* svg-asset-disable-next-line <rule-id> [-- reason]
|
|
14
|
+
* svg-asset-disable-line <rule-id> [-- reason]
|
|
15
|
+
*
|
|
16
|
+
* Per-project config (`.svg-asset.json` in the project root):
|
|
17
|
+
* { "ignoreRules": ["svg-a11y-generic-id"], "ignoreFiles": ["vendor/**"] }
|
|
18
|
+
*
|
|
19
|
+
* Usage:
|
|
20
|
+
* lint_svg_asset --dir <path> [--delivery inline|img|css-bg|document]
|
|
21
|
+
* lint_svg_asset --file <a.svg> [--file <b.svg>] [--delivery …]
|
|
22
|
+
* lint_svg_asset --file <after.svg> --baseline <before.svg> --allow id,id
|
|
23
|
+
*
|
|
24
|
+
* Exit codes: 0 = clean, 2 = findings, 1 = internal error (including the
|
|
25
|
+
* scanned-nothing refusal — a gate that scanned no file is not a clean gate).
|
|
26
|
+
*/
|
|
27
|
+
import fs from 'node:fs';
|
|
28
|
+
import path from 'node:path';
|
|
29
|
+
import { assertScanned, DeadScopeError } from './_lib/scan_scope.js';
|
|
30
|
+
import { DELIVERY_MODES, parseElements, scanHostHtml, scanSvg, } from './_lib/svg_rules.js';
|
|
31
|
+
export * from './_lib/svg_rules.js';
|
|
32
|
+
/**
|
|
33
|
+
* Canonicalised attribute map — number formatting, attribute order and
|
|
34
|
+
* whitespace normalised away, so a re-serialisation that changed nothing reads
|
|
35
|
+
* as nothing changed.
|
|
36
|
+
*/
|
|
37
|
+
export function canonicalAttrs(attrs) {
|
|
38
|
+
const out = {};
|
|
39
|
+
for (const key of Object.keys(attrs).sort()) {
|
|
40
|
+
const v = (attrs[key] ?? '')
|
|
41
|
+
.replace(/\s+/g, ' ')
|
|
42
|
+
.trim()
|
|
43
|
+
// Numbers only where a number is what it is. A hex color is a
|
|
44
|
+
// TOKEN, not a quantity: normalising inside `#00ff00` collapses it
|
|
45
|
+
// onto `#0ff0`, and the readout then reports no change for a color
|
|
46
|
+
// edit outside the allowed ids — a surgical-edit witness that
|
|
47
|
+
// cannot see a recolour.
|
|
48
|
+
.replace(/#[0-9a-fA-F]+|-?\d+\.?\d*(?:[eE][-+]?\d+)?/g, (tok) => {
|
|
49
|
+
if (tok.startsWith('#'))
|
|
50
|
+
return tok.toLowerCase();
|
|
51
|
+
const num = Number(tok);
|
|
52
|
+
return Number.isFinite(num) ? String(Number(num.toFixed(4))) : tok;
|
|
53
|
+
});
|
|
54
|
+
out[key] = v;
|
|
55
|
+
}
|
|
56
|
+
return out;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Every element outside the allowed set that changed between two revisions.
|
|
60
|
+
*
|
|
61
|
+
* NO PIXEL DIFF decides this. Anti-aliasing and font metrics would make a
|
|
62
|
+
* pixel comparison a per-machine verdict, and the discipline this witnesses —
|
|
63
|
+
* `design-fidelity-mechanics.md` § surgical edits — is about which elements
|
|
64
|
+
* the edit was allowed to touch, which is an attribute fact.
|
|
65
|
+
*/
|
|
66
|
+
export function preservationReadout(beforeContent, afterContent, allowedIds) {
|
|
67
|
+
const allow = new Set(allowedIds.map((s) => s.trim()).filter(Boolean));
|
|
68
|
+
/**
|
|
69
|
+
* A stable key per element, and the id chain above it.
|
|
70
|
+
*
|
|
71
|
+
* An element WITHOUT an id is keyed by its nearest id-bearing ancestor plus
|
|
72
|
+
* its tag and its ordinal INSIDE that ancestor — never by a document-wide
|
|
73
|
+
* index. A document-wide index shifts for every element after an allowed
|
|
74
|
+
* deletion, so removing one child of the allowed group reported every
|
|
75
|
+
* un-idded element after it as changed. Measured on the `grouped-transforms`
|
|
76
|
+
* triple: the correct edit reported `rect#7 (element)` and the readout could
|
|
77
|
+
* not tell a clean surgical edit from a dirty one.
|
|
78
|
+
*/
|
|
79
|
+
const index = (content) => {
|
|
80
|
+
const map = new Map();
|
|
81
|
+
const stack = [];
|
|
82
|
+
const ordinals = new Map();
|
|
83
|
+
for (const el of parseElements(content)) {
|
|
84
|
+
if (el.closing) {
|
|
85
|
+
if (stack[stack.length - 1]?.name === el.name)
|
|
86
|
+
stack.pop();
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
89
|
+
const chain = stack.map((f) => f.id).filter((x) => x !== null);
|
|
90
|
+
const ownerId = [...stack].reverse().find((f) => f.id !== null)?.id ?? '';
|
|
91
|
+
const own = el.attrs['id'] ?? null;
|
|
92
|
+
let key;
|
|
93
|
+
if (own) {
|
|
94
|
+
key = own;
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
const bucket = `${ownerId}/${el.name}`;
|
|
98
|
+
const n = (ordinals.get(bucket) ?? 0) + 1;
|
|
99
|
+
ordinals.set(bucket, n);
|
|
100
|
+
key = `${bucket}#${n}`;
|
|
101
|
+
}
|
|
102
|
+
map.set(key, { el, chain: own ? [...chain, own] : chain });
|
|
103
|
+
if (!el.selfClosing)
|
|
104
|
+
stack.push({ name: el.name, id: own });
|
|
105
|
+
}
|
|
106
|
+
return map;
|
|
107
|
+
};
|
|
108
|
+
const before = index(beforeContent);
|
|
109
|
+
const after = index(afterContent);
|
|
110
|
+
const changes = [];
|
|
111
|
+
/** An element inside an allowed subtree is itself allowed. */
|
|
112
|
+
const permitted = (entry) => Boolean(entry && entry.chain.some((id) => allow.has(id)));
|
|
113
|
+
for (const [key, beforeEntry] of before) {
|
|
114
|
+
if (permitted(beforeEntry))
|
|
115
|
+
continue;
|
|
116
|
+
const afterEntry = after.get(key);
|
|
117
|
+
if (!afterEntry) {
|
|
118
|
+
changes.push({
|
|
119
|
+
id: key,
|
|
120
|
+
element: beforeEntry.el.name,
|
|
121
|
+
attribute: '(element)',
|
|
122
|
+
before: 'present',
|
|
123
|
+
after: 'removed',
|
|
124
|
+
});
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
|
+
const a = canonicalAttrs(beforeEntry.el.attrs);
|
|
128
|
+
const b = canonicalAttrs(afterEntry.el.attrs);
|
|
129
|
+
for (const attr of new Set([...Object.keys(a), ...Object.keys(b)])) {
|
|
130
|
+
if ((a[attr] ?? '') === (b[attr] ?? ''))
|
|
131
|
+
continue;
|
|
132
|
+
changes.push({
|
|
133
|
+
id: key,
|
|
134
|
+
element: beforeEntry.el.name,
|
|
135
|
+
attribute: attr,
|
|
136
|
+
before: a[attr] ?? '(absent)',
|
|
137
|
+
after: b[attr] ?? '(absent)',
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const [key, afterEntry] of after) {
|
|
142
|
+
if (permitted(afterEntry) || before.has(key))
|
|
143
|
+
continue;
|
|
144
|
+
changes.push({
|
|
145
|
+
id: key,
|
|
146
|
+
element: afterEntry.el.name,
|
|
147
|
+
attribute: '(element)',
|
|
148
|
+
before: 'absent',
|
|
149
|
+
after: 'added',
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
return changes;
|
|
153
|
+
}
|
|
154
|
+
export function loadConfig(dir) {
|
|
155
|
+
const configPath = path.join(dir, '.svg-asset.json');
|
|
156
|
+
if (!fs.existsSync(configPath))
|
|
157
|
+
return {};
|
|
158
|
+
try {
|
|
159
|
+
return JSON.parse(fs.readFileSync(configPath, 'utf-8'));
|
|
160
|
+
}
|
|
161
|
+
catch {
|
|
162
|
+
return {};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function* walkDir(dir, ext) {
|
|
166
|
+
if (!fs.existsSync(dir))
|
|
167
|
+
return;
|
|
168
|
+
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
169
|
+
const full = path.join(dir, entry.name);
|
|
170
|
+
if (entry.name.startsWith('.') || entry.name === 'node_modules')
|
|
171
|
+
continue;
|
|
172
|
+
if (entry.isDirectory())
|
|
173
|
+
yield* walkDir(full, ext);
|
|
174
|
+
else if (ext.test(entry.name))
|
|
175
|
+
yield full;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
// ---------------------------------------------------------------------------
|
|
179
|
+
// CLI
|
|
180
|
+
// ---------------------------------------------------------------------------
|
|
181
|
+
function readFlag(args, name) {
|
|
182
|
+
const i = args.indexOf(name);
|
|
183
|
+
return i >= 0 ? args[i + 1] : undefined;
|
|
184
|
+
}
|
|
185
|
+
function readAll(args, name) {
|
|
186
|
+
const out = [];
|
|
187
|
+
for (let i = 0; i < args.length; i++)
|
|
188
|
+
if (args[i] === name && args[i + 1])
|
|
189
|
+
out.push(args[i + 1]);
|
|
190
|
+
return out;
|
|
191
|
+
}
|
|
192
|
+
export function main(argv = process.argv.slice(2)) {
|
|
193
|
+
const args = [...argv];
|
|
194
|
+
const jsonMode = args.includes('--json');
|
|
195
|
+
const quiet = args.includes('--quiet');
|
|
196
|
+
const deliveryRaw = readFlag(args, '--delivery');
|
|
197
|
+
if (deliveryRaw && !DELIVERY_MODES.includes(deliveryRaw)) {
|
|
198
|
+
process.stderr.write(`[lint_svg_asset] unknown --delivery "${deliveryRaw}"; expected one of ${DELIVERY_MODES.join(', ')}\n`);
|
|
199
|
+
return 1;
|
|
200
|
+
}
|
|
201
|
+
const delivery = deliveryRaw;
|
|
202
|
+
// --- step 1.2: preservation readout -------------------------------------
|
|
203
|
+
const baseline = readFlag(args, '--baseline');
|
|
204
|
+
if (baseline) {
|
|
205
|
+
const files = readAll(args, '--file');
|
|
206
|
+
const after = files[0];
|
|
207
|
+
if (!after) {
|
|
208
|
+
process.stderr.write('[lint_svg_asset] --baseline needs a --file <after.svg> to compare against\n');
|
|
209
|
+
return 1;
|
|
210
|
+
}
|
|
211
|
+
if (!fs.existsSync(baseline) || !fs.existsSync(after)) {
|
|
212
|
+
process.stderr.write('[lint_svg_asset] --baseline or --file path not found\n');
|
|
213
|
+
return 1;
|
|
214
|
+
}
|
|
215
|
+
const allow = (readFlag(args, '--allow') ?? '').split(',').map((s) => s.trim()).filter(Boolean);
|
|
216
|
+
const changes = preservationReadout(fs.readFileSync(baseline, 'utf-8'), fs.readFileSync(after, 'utf-8'), allow);
|
|
217
|
+
if (jsonMode) {
|
|
218
|
+
process.stdout.write(`${JSON.stringify(changes, null, 2)}\n`);
|
|
219
|
+
return changes.length ? 2 : 0;
|
|
220
|
+
}
|
|
221
|
+
if (!changes.length) {
|
|
222
|
+
if (!quiet) {
|
|
223
|
+
process.stdout.write(`✅ lint_svg_asset --baseline: nothing changed outside ${allow.length} allowed id(s).\n`);
|
|
224
|
+
}
|
|
225
|
+
return 0;
|
|
226
|
+
}
|
|
227
|
+
for (const c of changes) {
|
|
228
|
+
process.stdout.write(`❌ ${after} [svg-preserve-changed-outside-allowed] <${c.element} id="${c.id}"> ` +
|
|
229
|
+
`${c.attribute}: "${c.before}" → "${c.after}"\n`);
|
|
230
|
+
}
|
|
231
|
+
process.stdout.write(`\nlint_svg_asset: ${changes.length} element change(s) outside the allowed set. ` +
|
|
232
|
+
'A surgical edit touches only what it declared.\n');
|
|
233
|
+
return 2;
|
|
234
|
+
}
|
|
235
|
+
// --- scanning -----------------------------------------------------------
|
|
236
|
+
const explicit = readAll(args, '--file');
|
|
237
|
+
const dirArg = readFlag(args, '--dir');
|
|
238
|
+
const targets = [];
|
|
239
|
+
let roots = [];
|
|
240
|
+
if (explicit.length) {
|
|
241
|
+
targets.push(...explicit.map((f) => path.resolve(f)));
|
|
242
|
+
roots = targets;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
const scanDir = dirArg ? path.resolve(dirArg) : path.resolve(process.cwd(), 'src');
|
|
246
|
+
roots = [scanDir];
|
|
247
|
+
if (!fs.existsSync(scanDir)) {
|
|
248
|
+
// The two cases are NOT the same, and collapsing them into exit 0
|
|
249
|
+
// made a renamed or mistyped `--dir` report success in CI. An
|
|
250
|
+
// ABSENT DEFAULT root is the opt-in escape — a consumer with no
|
|
251
|
+
// `src/` has nothing to scan and must not red. An EXPLICIT `--dir`
|
|
252
|
+
// that does not resolve is a broken invocation, and this module's
|
|
253
|
+
// own header already says a gate that scanned no file is not a
|
|
254
|
+
// clean gate.
|
|
255
|
+
if (!quiet)
|
|
256
|
+
process.stderr.write(`[lint_svg_asset] scan directory not found: ${scanDir}\n`);
|
|
257
|
+
if (dirArg)
|
|
258
|
+
return 1;
|
|
259
|
+
process.stderr.write('This linter is opt-in. Run with --dir <path> or --file <a.svg> pointing at your assets.\n');
|
|
260
|
+
return 0;
|
|
261
|
+
}
|
|
262
|
+
for (const f of walkDir(scanDir, /\.(svg|html|htm)$/i))
|
|
263
|
+
targets.push(f);
|
|
264
|
+
}
|
|
265
|
+
const config = loadConfig(process.cwd());
|
|
266
|
+
const ignoreRules = new Set((config.ignoreRules ?? []).map((r) => r.toLowerCase()));
|
|
267
|
+
const ignoreGlobs = config.ignoreFiles ?? [];
|
|
268
|
+
const scanned = targets.filter((f) => !ignoreGlobs.some((g) => f.includes(g.replace('**', ''))));
|
|
269
|
+
try {
|
|
270
|
+
assertScanned({ gate: 'lint_svg_asset', scanned: scanned.length, units: 'SVG/HTML file(s)', roots });
|
|
271
|
+
}
|
|
272
|
+
catch (exc) {
|
|
273
|
+
if (exc instanceof DeadScopeError) {
|
|
274
|
+
process.stderr.write(`❌ ${exc.message}\n`);
|
|
275
|
+
return 1;
|
|
276
|
+
}
|
|
277
|
+
throw exc;
|
|
278
|
+
}
|
|
279
|
+
const all = [];
|
|
280
|
+
for (const abs of scanned) {
|
|
281
|
+
const rel = path.relative(process.cwd(), abs);
|
|
282
|
+
try {
|
|
283
|
+
const content = fs.readFileSync(abs, 'utf-8');
|
|
284
|
+
const findings = /\.svg$/i.test(abs)
|
|
285
|
+
? scanSvg(content, rel, { delivery })
|
|
286
|
+
: scanHostHtml(content, rel, { delivery });
|
|
287
|
+
all.push(...findings.filter((f) => !ignoreRules.has(f.rule.toLowerCase())));
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
if (!quiet)
|
|
291
|
+
process.stderr.write(`[lint_svg_asset] error: ${abs}: ${err}\n`);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
if (jsonMode) {
|
|
295
|
+
process.stdout.write(`${JSON.stringify(all, null, 2)}\n`);
|
|
296
|
+
return all.length > 0 ? 2 : 0;
|
|
297
|
+
}
|
|
298
|
+
if (!all.length) {
|
|
299
|
+
if (!quiet) {
|
|
300
|
+
process.stdout.write(`✅ lint_svg_asset: clean — ${scanned.length} file(s) checked, no violations.\n`);
|
|
301
|
+
}
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
const errors = all.filter((f) => f.severity === 'error');
|
|
305
|
+
const warnings = all.filter((f) => f.severity === 'warning');
|
|
306
|
+
const inert = all.filter((f) => f.severity === 'inert');
|
|
307
|
+
for (const f of all) {
|
|
308
|
+
const icon = f.severity === 'error' ? '❌' : f.severity === 'warning' ? '⚠️ ' : 'ℹ️ ';
|
|
309
|
+
process.stdout.write(`${icon} ${f.file}:${f.line} [${f.rule}] ${f.snippet}\n`);
|
|
310
|
+
}
|
|
311
|
+
process.stdout.write(`\nlint_svg_asset: ${errors.length} error(s), ${warnings.length} warning(s), ${inert.length} inert. ` +
|
|
312
|
+
'Use svg-asset-disable-next-line <rule> -- reason or .svg-asset.json to suppress; ' +
|
|
313
|
+
'svg-safety-* findings yield only to .svg-asset.json, never to a marker in the file. ' +
|
|
314
|
+
'Advisory: an "inert" row is not a clean row — the same file inlined tomorrow is an error.\n');
|
|
315
|
+
return 2;
|
|
316
|
+
}
|
|
317
|
+
/* c8 ignore start — entrypoint guard, exercised by the CLI not by the tests */
|
|
318
|
+
const invokedDirectly = typeof process !== 'undefined' &&
|
|
319
|
+
Array.isArray(process.argv) &&
|
|
320
|
+
/lint_svg_asset\.(ts|js)$/.test(process.argv[1] ?? '');
|
|
321
|
+
if (invokedDirectly)
|
|
322
|
+
process.exit(main());
|
|
323
|
+
/* c8 ignore stop */
|
|
324
|
+
//# sourceMappingURL=lint_svg_asset.js.map
|
package/dist/server/app.js
CHANGED
|
@@ -155,7 +155,7 @@ export async function createApp(opts) {
|
|
|
155
155
|
await app.register(pingRoute({ writeRoot, projectScopeRoot, mode, dryRun, projectSurface: opts.projectSurface === true }));
|
|
156
156
|
await app.register(discoveryRoute(opts.discoveryManifestPath ? { manifestPath: opts.discoveryManifestPath } : {}));
|
|
157
157
|
await app.register(schemaRoute());
|
|
158
|
-
await app.register(settingsRoute({ writeRoot, legacyReadRoot, packageRoot, dryRun, userGlobalReadRoot: userGlobalRead }));
|
|
158
|
+
await app.register(settingsRoute({ writeRoot, legacyReadRoot, packageRoot, dryRun, userGlobalReadRoot: userGlobalRead, userGlobalWrite: mode === 'global' }));
|
|
159
159
|
await app.register(settingsChangesRoute({ writeRoot, userGlobalReadRoot: userGlobalRead, dryRun }));
|
|
160
160
|
await app.register(userMdRoute({ writeRoot, legacyReadRoot, dryRun, userGlobalReadRoot: userGlobalRead }));
|
|
161
161
|
await app.register(installRoute(opts.installRouteOptions ?? {}));
|
|
@@ -167,6 +167,7 @@ export async function createApp(opts) {
|
|
|
167
167
|
dryRun,
|
|
168
168
|
userGlobalReadRoot: userGlobalRead,
|
|
169
169
|
extendedSteps: opts.extendedSteps === true,
|
|
170
|
+
userGlobalWrite: mode === 'global',
|
|
170
171
|
...(opts.initialStep !== undefined ? { initialStep: opts.initialStep } : {}),
|
|
171
172
|
...(opts.wizardMode !== undefined ? { wizardMode: opts.wizardMode } : {}),
|
|
172
173
|
}));
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The settings route's handling of the three `git.*` keys.
|
|
3
|
+
*
|
|
4
|
+
* In global mode the route writes the user-global file, and `load_agent_settings`
|
|
5
|
+
* discards `git.*` keys from that file, so a value saved there has no effect in
|
|
6
|
+
* any repository. The form is not offered those keys, and a non-default value is
|
|
7
|
+
* refused with the place a team declares them instead (ADR-283). A template
|
|
8
|
+
* default is accepted: the form round-trips it, and `upgrade` inserts it too.
|
|
9
|
+
*
|
|
10
|
+
* In every mode a value goes through the same check `git:convention show` and
|
|
11
|
+
* `settings:check` run, so a pattern that would reach a shell is refused here as
|
|
12
|
+
* well.
|
|
13
|
+
*/
|
|
14
|
+
import { promises as fs } from 'node:fs';
|
|
15
|
+
import { join } from 'node:path';
|
|
16
|
+
import { parseYaml } from './io/yamlIO.js';
|
|
17
|
+
import { GIT_CONVENTION_KEYS, invalidReason } from '../scripts/_lib/git_convention.js';
|
|
18
|
+
export const WITHHELD_GIT_KEYS = GIT_CONVENTION_KEYS.map((k) => `git.${k}`);
|
|
19
|
+
export const WITHHELD_REASON = 'This form writes your user-global settings file, which does not carry git.* keys. ' +
|
|
20
|
+
'A team declares them in .git-convention.yml at the repository root; one checkout can set them in its project settings file.';
|
|
21
|
+
function _gitSection(values) {
|
|
22
|
+
const git = values['git'];
|
|
23
|
+
return git !== null && typeof git === 'object' && !Array.isArray(git) ? git : null;
|
|
24
|
+
}
|
|
25
|
+
/** The values and the JSON schema with the `git` section removed. */
|
|
26
|
+
export function withholdGitKeys(values, schema) {
|
|
27
|
+
const { git: _git, ...rest } = values;
|
|
28
|
+
const properties = { ...(schema.properties ?? {}) };
|
|
29
|
+
delete properties['git'];
|
|
30
|
+
const next = { ...schema, properties };
|
|
31
|
+
if (Array.isArray(schema.required))
|
|
32
|
+
next.required = schema.required.filter((k) => k !== 'git');
|
|
33
|
+
return { values: rest, schema: next };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Field issues for the `git.*` values of a write. `defaults` is the template's
|
|
37
|
+
* `git` section; `userGlobal` is true when the write lands in the user-global file.
|
|
38
|
+
*/
|
|
39
|
+
export function gitKeyWriteIssues(values, defaults, userGlobal) {
|
|
40
|
+
const git = _gitSection(values);
|
|
41
|
+
if (git === null)
|
|
42
|
+
return [];
|
|
43
|
+
const fallback = _gitSection(defaults) ?? {};
|
|
44
|
+
const issues = [];
|
|
45
|
+
for (const key of GIT_CONVENTION_KEYS) {
|
|
46
|
+
const value = git[key];
|
|
47
|
+
if (typeof value !== 'string')
|
|
48
|
+
continue;
|
|
49
|
+
const why = invalidReason(key, value);
|
|
50
|
+
if (why !== null) {
|
|
51
|
+
issues.push({ path: `git.${key}`, message: why });
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
const def = fallback[key];
|
|
55
|
+
if (userGlobal && !(typeof def === 'string' && def === value)) {
|
|
56
|
+
issues.push({ path: `git.${key}`, message: WITHHELD_REASON });
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
return issues;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The write candidate with the `git` section the file already holds. In global
|
|
63
|
+
* mode the form never showed that section, and the schema's `.default({})`
|
|
64
|
+
* fills template defaults into whatever arrives, so a value the user-global
|
|
65
|
+
* file carries would otherwise read as a change the user made: a guarded-key
|
|
66
|
+
* confirmation for a field they never saw, then a silent reset.
|
|
67
|
+
*
|
|
68
|
+
* `fileValues` is that file alone, never the merged view: the merge carries
|
|
69
|
+
* the template defaults and any project layer, and copying a project value
|
|
70
|
+
* here would write it into the user-global file without passing the gate.
|
|
71
|
+
*/
|
|
72
|
+
export function keepWithheldGit(candidate, fileValues) {
|
|
73
|
+
const { git: _sent, ...rest } = candidate;
|
|
74
|
+
return 'git' in fileValues ? { ...rest, git: fileValues['git'] } : rest;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* The before-side of a global-mode diff: the merged view with its `git` section
|
|
78
|
+
* swapped for the file's own, or dropped when the file has none. The candidate
|
|
79
|
+
* from `keepWithheldGit` carries exactly that section, so comparing it against
|
|
80
|
+
* the merged view would report template defaults and project values as edits
|
|
81
|
+
* to a section the form never showed.
|
|
82
|
+
*/
|
|
83
|
+
export function gitDiffBase(merged, fileValues) {
|
|
84
|
+
return keepWithheldGit(merged, fileValues);
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* The wizard's finish write through the same gate: a 422 is sent and true
|
|
88
|
+
* returned when a `git.*` value is refused. The template's `git` section holds
|
|
89
|
+
* no install placeholder, so it is parsed without substitution.
|
|
90
|
+
*/
|
|
91
|
+
export async function refuseGitKeys(reply, values, packageRoot, userGlobal) {
|
|
92
|
+
let defaults = {};
|
|
93
|
+
try {
|
|
94
|
+
defaults = parseYaml(await fs.readFile(join(packageRoot, 'src', 'config', 'agent-settings.template.yml'), 'utf8'));
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
defaults = {};
|
|
98
|
+
}
|
|
99
|
+
const issues = gitKeyWriteIssues(values, defaults, userGlobal);
|
|
100
|
+
if (issues.length === 0)
|
|
101
|
+
return false;
|
|
102
|
+
await reply.code(422).send({ error: { code: 'VALIDATION', message: 'invalid settings', fields: issues } });
|
|
103
|
+
return true;
|
|
104
|
+
}
|
|
105
|
+
//# sourceMappingURL=gitKeysGate.js.map
|