@event4u/agent-config 1.15.0 → 1.17.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/.agent-src/commands/{agents-audit.md → agents/audit.md} +4 -3
- package/.agent-src/commands/{agents-cleanup.md → agents/cleanup.md} +12 -6
- package/.agent-src/commands/{agents-prepare.md → agents/prepare.md} +4 -3
- package/.agent-src/commands/agents.md +46 -0
- package/.agent-src/commands/bug-fix.md +1 -1
- package/.agent-src/commands/bug-investigate.md +2 -2
- package/.agent-src/commands/{chat-history-checkpoint.md → chat-history/checkpoint.md} +5 -5
- package/.agent-src/commands/{chat-history-clear.md → chat-history/clear.md} +5 -5
- package/.agent-src/commands/{chat-history-resume.md → chat-history/resume.md} +4 -4
- package/.agent-src/commands/chat-history/show.md +107 -0
- package/.agent-src/commands/chat-history.md +33 -89
- package/.agent-src/commands/check-current-md.md +1 -1
- package/.agent-src/commands/{commit-in-chunks.md → commit/in-chunks.md} +15 -13
- package/.agent-src/commands/commit.md +22 -2
- package/.agent-src/commands/{context-create.md → context/create.md} +4 -3
- package/.agent-src/commands/{context-refactor.md → context/refactor.md} +4 -3
- package/.agent-src/commands/context.md +44 -0
- package/.agent-src/commands/{copilot-agents-init.md → copilot-agents/init.md} +4 -3
- package/.agent-src/commands/{copilot-agents-optimize.md → copilot-agents/optimize.md} +4 -3
- package/.agent-src/commands/copilot-agents.md +44 -0
- package/.agent-src/commands/council/default.md +221 -0
- package/.agent-src/commands/council/design.md +97 -0
- package/.agent-src/commands/council/optimize.md +116 -0
- package/.agent-src/commands/council/pr.md +124 -0
- package/.agent-src/commands/council.md +54 -0
- package/.agent-src/commands/{create-pr-description.md → create-pr/description-only.md} +4 -2
- package/.agent-src/commands/create-pr.md +49 -5
- package/.agent-src/commands/e2e-heal.md +1 -1
- package/.agent-src/commands/e2e-plan.md +1 -1
- package/.agent-src/commands/{feature-dev.md → feature/dev.md} +6 -3
- package/.agent-src/commands/{feature-explore.md → feature/explore.md} +5 -4
- package/.agent-src/commands/{feature-plan.md → feature/plan.md} +32 -5
- package/.agent-src/commands/{feature-refactor.md → feature/refactor.md} +4 -3
- package/.agent-src/commands/{feature-roadmap.md → feature/roadmap.md} +7 -6
- package/.agent-src/commands/feature.md +52 -0
- package/.agent-src/commands/{fix-ci.md → fix/ci.md} +4 -3
- package/.agent-src/commands/{fix-portability.md → fix/portability.md} +4 -3
- package/.agent-src/commands/{fix-pr-bot-comments.md → fix/pr-bots.md} +4 -3
- package/.agent-src/commands/{fix-pr-developer-comments.md → fix/pr-developers.md} +4 -3
- package/.agent-src/commands/{fix-pr-comments.md → fix/pr.md} +7 -6
- package/.agent-src/commands/{fix-references.md → fix/refs.md} +4 -3
- package/.agent-src/commands/{fix-seeder.md → fix/seeder.md} +4 -3
- package/.agent-src/commands/fix.md +54 -0
- package/.agent-src/commands/jira-ticket.md +1 -1
- package/.agent-src/commands/{do-and-judge.md → judge/on-diff.md} +7 -6
- package/.agent-src/commands/judge/solo.md +90 -0
- package/.agent-src/commands/{do-in-steps.md → judge/steps.md} +8 -7
- package/.agent-src/commands/judge.md +35 -70
- package/.agent-src/commands/{memory-add.md → memory/add.md} +7 -6
- package/.agent-src/commands/{memory-full.md → memory/load.md} +6 -5
- package/.agent-src/commands/{memory-promote.md → memory/promote.md} +6 -5
- package/.agent-src/commands/{propose-memory.md → memory/propose.md} +6 -5
- package/.agent-src/commands/memory.md +48 -0
- package/.agent-src/commands/mode.md +5 -5
- package/.agent-src/commands/{module-create.md → module/create.md} +4 -3
- package/.agent-src/commands/{module-explore.md → module/explore.md} +4 -3
- package/.agent-src/commands/module.md +44 -0
- package/.agent-src/commands/onboard.md +3 -3
- package/.agent-src/commands/{optimize-agents.md → optimize/agents.md} +5 -4
- package/.agent-src/commands/{optimize-augmentignore.md → optimize/augmentignore.md} +4 -4
- package/.agent-src/commands/{optimize-rtk-filters.md → optimize/rtk.md} +4 -3
- package/.agent-src/commands/{optimize-skills.md → optimize/skills.md} +5 -4
- package/.agent-src/commands/optimize.md +48 -0
- package/.agent-src/commands/{override-create.md → override/create.md} +4 -3
- package/.agent-src/commands/{override-manage.md → override/manage.md} +4 -3
- package/.agent-src/commands/override.md +44 -0
- package/.agent-src/commands/review-changes.md +26 -1
- package/.agent-src/commands/review-routing.md +1 -1
- package/.agent-src/commands/{roadmap-create.md → roadmap/create.md} +33 -5
- package/.agent-src/commands/{roadmap-execute.md → roadmap/execute.md} +4 -3
- package/.agent-src/commands/roadmap.md +44 -0
- package/.agent-src/commands/set-cost-profile.md +3 -3
- package/.agent-src/commands/sync-agent-settings.md +2 -2
- package/.agent-src/commands/{tests-create.md → tests/create.md} +5 -4
- package/.agent-src/commands/{tests-execute.md → tests/execute.md} +4 -3
- package/.agent-src/commands/tests.md +44 -0
- package/.agent-src/commands/upstream-contribute.md +1 -1
- package/.agent-src/contexts/authority/commit-mechanics.md +57 -0
- package/.agent-src/contexts/authority/destructive-mechanics.md +66 -0
- package/.agent-src/contexts/authority/scope-mechanics.md +87 -0
- package/.agent-src/contexts/communication/rules-auto/artifact-engagement-recording-mechanics.md +72 -0
- package/.agent-src/contexts/communication/rules-auto/augment-portability-mechanics.md +79 -0
- package/.agent-src/contexts/communication/rules-auto/augment-source-of-truth-mechanics.md +98 -0
- package/.agent-src/contexts/communication/rules-auto/cli-output-handling-mechanics.md +87 -0
- package/.agent-src/contexts/communication/rules-auto/command-suggestion-policy-mechanics.md +62 -0
- package/.agent-src/contexts/communication/rules-auto/docs-sync-mechanics.md +78 -0
- package/.agent-src/contexts/communication/rules-auto/package-ci-checks-mechanics.md +85 -0
- package/.agent-src/contexts/communication/rules-auto/review-routing-awareness-mechanics.md +65 -0
- package/.agent-src/contexts/communication/rules-auto/roadmap-progress-sync-mechanics.md +78 -0
- package/.agent-src/contexts/communication/rules-auto/skill-quality-mechanics.md +62 -0
- package/.agent-src/contexts/communication/rules-auto/slash-command-routing-policy-mechanics.md +55 -0
- package/.agent-src/contexts/communication/rules-auto/ui-audit-gate-mechanics.md +53 -0
- package/.agent-src/contexts/communication/rules-auto/user-interaction-mechanics.md +77 -0
- package/.agent-src/contexts/execution/autonomy-detection.md +54 -0
- package/.agent-src/contexts/execution/autonomy-examples.md +90 -0
- package/.agent-src/contexts/execution/autonomy-mechanics.md +29 -0
- package/.agent-src/contexts/execution/verification-mechanics.md +80 -0
- package/.agent-src/contexts/judges/no-consolidate-rationale.md +102 -0
- package/.agent-src/contexts/judges/persona-voice-rubric.md +140 -0
- package/.agent-src/personas/README.md +1 -1
- package/.agent-src/rules/agent-authority.md +24 -0
- package/.agent-src/rules/architecture.md +1 -1
- package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
- package/.agent-src/rules/artifact-engagement-recording.md +14 -70
- package/.agent-src/rules/ask-when-uncertain.md +28 -43
- package/.agent-src/rules/augment-portability.md +15 -61
- package/.agent-src/rules/augment-source-of-truth.md +27 -93
- package/.agent-src/rules/autonomous-execution.md +78 -114
- package/.agent-src/rules/capture-learnings.md +1 -1
- package/.agent-src/rules/chat-history-cadence.md +3 -3
- package/.agent-src/rules/chat-history-ownership.md +3 -3
- package/.agent-src/rules/chat-history-visibility.md +3 -3
- package/.agent-src/rules/cli-output-handling.md +10 -76
- package/.agent-src/rules/command-suggestion-policy.md +93 -0
- package/.agent-src/rules/commit-conventions.md +17 -14
- package/.agent-src/rules/commit-policy.md +14 -42
- package/.agent-src/rules/context-hygiene.md +3 -3
- package/.agent-src/rules/direct-answers.md +34 -49
- package/.agent-src/rules/docker-commands.md +5 -5
- package/.agent-src/rules/docs-sync.md +16 -70
- package/.agent-src/rules/e2e-testing.md +1 -1
- package/.agent-src/rules/guidelines.md +4 -4
- package/.agent-src/rules/improve-before-implement.md +2 -2
- package/.agent-src/rules/language-and-tone.md +50 -133
- package/.agent-src/rules/minimal-safe-diff.md +3 -3
- package/.agent-src/rules/missing-tool-handling.md +28 -22
- package/.agent-src/rules/model-recommendation.md +4 -4
- package/.agent-src/rules/no-cheap-questions.md +82 -0
- package/.agent-src/rules/no-roadmap-references.md +73 -0
- package/.agent-src/rules/non-destructive-by-default.md +15 -49
- package/.agent-src/rules/onboarding-gate.md +5 -5
- package/.agent-src/rules/package-ci-checks.md +21 -61
- package/.agent-src/rules/preservation-guard.md +64 -29
- package/.agent-src/rules/review-routing-awareness.md +26 -45
- package/.agent-src/rules/roadmap-progress-sync.md +28 -96
- package/.agent-src/rules/role-mode-adherence.md +2 -2
- package/.agent-src/rules/scope-control.md +65 -46
- package/.agent-src/rules/security-sensitive-stop.md +9 -9
- package/.agent-src/rules/size-enforcement.md +1 -1
- package/.agent-src/rules/skill-quality.md +16 -48
- package/.agent-src/rules/{slash-commands.md → slash-command-routing-policy.md} +7 -4
- package/.agent-src/rules/think-before-action.md +55 -45
- package/.agent-src/rules/token-efficiency.md +4 -4
- package/.agent-src/rules/tool-safety.md +19 -16
- package/.agent-src/rules/{ui-audit-before-build.md → ui-audit-gate.md} +27 -41
- package/.agent-src/rules/user-interaction.md +16 -71
- package/.agent-src/rules/verify-before-complete.md +12 -67
- package/.agent-src/scripts/update_roadmap_progress.py +9 -4
- package/.agent-src/skills/ai-council/SKILL.md +335 -0
- package/.agent-src/skills/api-endpoint/SKILL.md +2 -2
- package/.agent-src/skills/api-testing/SKILL.md +1 -1
- package/.agent-src/skills/blade-ui/SKILL.md +1 -1
- package/.agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
- package/.agent-src/skills/bug-analyzer/SKILL.md +1 -1
- package/.agent-src/skills/check-refs/SKILL.md +59 -40
- package/.agent-src/skills/command-routing/SKILL.md +1 -1
- package/.agent-src/skills/command-writing/SKILL.md +1 -1
- package/.agent-src/skills/conventional-commits-writing/SKILL.md +86 -28
- package/.agent-src/skills/copilot-agents-optimization/SKILL.md +7 -7
- package/.agent-src/skills/developer-like-execution/SKILL.md +6 -6
- package/.agent-src/skills/finishing-a-development-branch/SKILL.md +101 -65
- package/.agent-src/skills/flux/SKILL.md +31 -11
- package/.agent-src/skills/git-workflow/SKILL.md +1 -1
- package/.agent-src/skills/github-ci/SKILL.md +2 -2
- package/.agent-src/skills/guideline-writing/SKILL.md +11 -11
- package/.agent-src/skills/judge-code-quality/SKILL.md +7 -8
- package/.agent-src/skills/judge-security-auditor/SKILL.md +4 -5
- package/.agent-src/skills/judge-test-coverage/SKILL.md +3 -4
- package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +4 -4
- package/.agent-src/skills/lint-skills/SKILL.md +57 -39
- package/.agent-src/skills/livewire/SKILL.md +1 -1
- package/.agent-src/skills/md-language-check/SKILL.md +61 -39
- package/.agent-src/skills/override-management/SKILL.md +7 -7
- package/.agent-src/skills/php-coder/SKILL.md +1 -1
- package/.agent-src/skills/playwright-testing/SKILL.md +2 -2
- package/.agent-src/skills/quality-tools/SKILL.md +2 -2
- package/.agent-src/skills/react-shadcn-ui/SKILL.md +116 -43
- package/.agent-src/skills/readme-reviewer/SKILL.md +31 -30
- package/.agent-src/skills/readme-writing/SKILL.md +79 -54
- package/.agent-src/skills/readme-writing-package/SKILL.md +51 -48
- package/.agent-src/skills/receiving-code-review/SKILL.md +53 -48
- package/.agent-src/skills/refine-prompt/SKILL.md +0 -1
- package/.agent-src/skills/requesting-code-review/SKILL.md +35 -30
- package/.agent-src/skills/review-routing/SKILL.md +2 -2
- package/.agent-src/skills/rule-writing/SKILL.md +1 -1
- package/.agent-src/skills/security/SKILL.md +7 -2
- package/.agent-src/skills/security-audit/SKILL.md +7 -3
- package/.agent-src/skills/skill-reviewer/SKILL.md +1 -1
- package/.agent-src/skills/skill-writing/SKILL.md +3 -3
- package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -0
- package/.agent-src/skills/systematic-debugging/SKILL.md +69 -61
- package/.agent-src/skills/test-driven-development/SKILL.md +59 -57
- package/.agent-src/skills/test-performance/SKILL.md +0 -1
- package/.agent-src/skills/traefik/SKILL.md +4 -4
- package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
- package/.agent-src/skills/validate-feature-fit/SKILL.md +2 -2
- package/.agent-src/skills/{verify-before-complete → verify-completion-evidence}/SKILL.md +30 -28
- package/.agent-src/templates/agent-settings.md +8 -8
- package/.agent-src/templates/contexts/auth-model.md +1 -1
- package/.agent-src/templates/scripts/README.md +2 -2
- package/.agent-src/templates/scripts/telemetry/aggregator.py +16 -1
- package/.agent-src/templates/scripts/telemetry/engagement.py +59 -0
- package/.agent-src/templates/scripts/telemetry/report_renderer.py +28 -1
- package/.agent-src/templates/scripts/telemetry_record.py +14 -1
- package/.claude-plugin/marketplace.json +31 -12
- package/AGENTS.md +11 -9
- package/CHANGELOG.md +213 -2
- package/README.md +43 -44
- package/config/agent-settings.template.yml +58 -1
- package/config/gitignore-block.txt +3 -0
- package/docs/architecture.md +5 -7
- package/docs/catalog.md +359 -0
- package/docs/contracts/STABILITY.md +46 -1
- package/docs/contracts/adr-chat-history-split.md +1 -3
- package/docs/contracts/adr-command-suggestion.md +3 -5
- package/docs/contracts/adr-implement-ticket-runtime.md +1 -2
- package/docs/contracts/adr-product-ui-track.md +5 -8
- package/docs/contracts/adr-prompt-driven-execution.md +3 -4
- package/docs/contracts/agent-memory-contract.md +8 -13
- package/docs/contracts/artifact-engagement-flow.md +7 -10
- package/docs/contracts/command-clusters.md +56 -46
- package/docs/contracts/command-suggestion-flow.md +4 -6
- package/docs/contracts/context-paths.md +99 -0
- package/docs/contracts/file-ownership-matrix.json +6722 -0
- package/docs/contracts/file-ownership-matrix.md +134 -0
- package/docs/contracts/implement-ticket-flow.md +8 -11
- package/docs/contracts/linear-ai-rules-inclusion.md +1 -2
- package/docs/contracts/linear-ai-three-layers.md +0 -2
- package/docs/contracts/load-context-budget-model.md +178 -0
- package/docs/contracts/load-context-schema.md +184 -0
- package/docs/contracts/rule-interactions.md +0 -1
- package/docs/contracts/rule-interactions.yml +96 -0
- package/docs/contracts/rule-priority-hierarchy.md +87 -0
- package/docs/contracts/ui-track-flow.md +8 -18
- package/docs/customization.md +16 -0
- package/docs/end-to-end-walkthroughs.md +165 -0
- package/docs/getting-started.md +29 -10
- package/docs/github-topics.md +12 -3
- package/docs/guidelines/agent-infra/asking-and-brevity-examples.md +100 -0
- package/docs/guidelines/agent-infra/language-and-tone-examples.md +79 -0
- package/{.agent-src → docs}/guidelines/docs/readme-size-and-splitting.md +26 -25
- package/docs/guidelines/php/git.md +164 -0
- package/docs/migrations/commands-1.15.0.md +1 -1
- package/docs/showcase.md +9 -4
- package/docs/skills-catalog.md +14 -8
- package/docs/ui-track-mental-model.md +2 -2
- package/llms.txt +13 -7
- package/package.json +1 -1
- package/scripts/_one_off_phase4_dispatch_latency.py +108 -0
- package/scripts/_one_off_phase6_trigger_jaccard.py +92 -0
- package/scripts/_phase2_shim_helper.py +109 -0
- package/scripts/agent-config +33 -0
- package/scripts/ai_council/__init__.py +39 -0
- package/scripts/ai_council/_default_prices.py +41 -0
- package/scripts/ai_council/_one_off_2a4_acceptance.py +208 -0
- package/scripts/ai_council/_one_off_context_layer_v1_estimate.py +67 -0
- package/scripts/ai_council/_one_off_context_layer_v1_review.py +292 -0
- package/scripts/ai_council/_one_off_followups_review.py +259 -0
- package/scripts/ai_council/_one_off_nondestructive_inline_audit.py +209 -0
- package/scripts/ai_council/_one_off_phase_2a_budget_rebalance.py +257 -0
- package/scripts/ai_council/_one_off_phase_2a_post_revert.py +197 -0
- package/scripts/ai_council/_one_off_rebalancing_audit.py +149 -0
- package/scripts/ai_council/_one_off_roundtrip.py +106 -0
- package/scripts/ai_council/_one_off_rule_hardening_v1.py +251 -0
- package/scripts/ai_council/_one_off_structural_open_questions.py +232 -0
- package/scripts/ai_council/_one_off_structural_optimization.py +144 -0
- package/scripts/ai_council/_one_off_structural_v3_gaps.py +252 -0
- package/scripts/ai_council/_one_off_structural_v3_review.py +240 -0
- package/scripts/ai_council/budget_guard.py +172 -0
- package/scripts/ai_council/bundler.py +261 -0
- package/scripts/ai_council/clients.py +381 -0
- package/scripts/ai_council/modes.py +127 -0
- package/scripts/ai_council/orchestrator.py +350 -0
- package/scripts/ai_council/pricing.py +213 -0
- package/scripts/ai_council/project_context.py +159 -0
- package/scripts/ai_council/prompts.py +232 -0
- package/scripts/ai_council/session.py +144 -0
- package/scripts/check_always_budget.py +444 -0
- package/scripts/check_augmentignore.py +69 -0
- package/scripts/check_cluster_patterns.py +159 -0
- package/scripts/check_command_count_messaging.py +127 -0
- package/scripts/check_context_paths.py +201 -0
- package/scripts/check_no_roadmap_refs.py +155 -0
- package/scripts/check_phase_coupling.py +148 -0
- package/scripts/check_portability.py +57 -0
- package/scripts/check_public_catalog_links.py +122 -0
- package/scripts/check_references.py +33 -3
- package/scripts/check_roadmap_trackable.py +111 -0
- package/scripts/check_safety_floor_untouched.py +125 -0
- package/scripts/command_suggester/cooldown.py +1 -1
- package/scripts/command_suggester/loader.py +4 -1
- package/scripts/compress.py +59 -13
- package/scripts/generate_index.py +270 -0
- package/scripts/generate_ownership_matrix.py +323 -0
- package/scripts/hooks/augment-roadmap-progress.sh +57 -0
- package/scripts/install.py +49 -28
- package/scripts/install_anthropic_key.sh +5 -0
- package/scripts/install_openai_key.sh +106 -0
- package/scripts/lint_load_context.py +163 -0
- package/scripts/lint_no_new_atomic_commands.py +12 -11
- package/scripts/requirements-evals.txt +1 -0
- package/scripts/roadmap_progress_hook.py +159 -0
- package/scripts/schemas/command.schema.json +22 -1
- package/scripts/schemas/rule.schema.json +10 -0
- package/scripts/skill_linter.py +13 -4
- package/scripts/sync_agent_settings.py +26 -3
- package/scripts/update_counts.py +16 -4
- package/scripts/update_prices.py +124 -0
- package/.agent-src/guidelines/php/git.md +0 -96
- package/.agent-src/rules/command-suggestion.md +0 -134
- /package/{.agent-src → docs}/guidelines/agent-infra/agent-interaction-and-decision-quality.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/break-glass-usage.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/developer-judgment.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/engineering-memory-data-format.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/layered-settings.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/memory-access.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/naming.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/output-patterns.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/review-routing-data-format.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/role-contracts.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/role-mode-router.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/runtime-layer.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/self-improvement-pipeline.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/size-and-scope.md +0 -0
- /package/{.agent-src → docs}/guidelines/agent-infra/tool-integration.md +0 -0
- /package/{.agent-src → docs}/guidelines/e2e/playwright.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/api-design.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/artisan-commands.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/blade-ui.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/controllers.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/database.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/eloquent.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/flux.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/general.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/jobs.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/livewire.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/logging.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/naming.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/dependency-injection.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/dtos.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/events.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/factory.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/pipelines.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/policies.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/repositories.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/service-layer.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns/strategy.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/patterns.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/performance.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/resources.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/security.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/sql.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/validations.md +0 -0
- /package/{.agent-src → docs}/guidelines/php/websocket.md +0 -0
package/scripts/install.py
CHANGED
|
@@ -464,44 +464,65 @@ def ensure_augment_bridge(project_root: Path, force: bool) -> None:
|
|
|
464
464
|
# .augment/settings.json is plugin enablement, not hooks.
|
|
465
465
|
AUGMENT_USER_DIR = Path.home() / ".augment"
|
|
466
466
|
AUGMENT_USER_HOOKS_DIR = AUGMENT_USER_DIR / "hooks"
|
|
467
|
-
|
|
468
|
-
|
|
467
|
+
AUGMENT_CHAT_HISTORY_TRAMPOLINE = "augment-chat-history.sh"
|
|
468
|
+
AUGMENT_ROADMAP_PROGRESS_TRAMPOLINE = "augment-roadmap-progress.sh"
|
|
469
|
+
# (trampoline name, list of events it should fire on). Each trampoline
|
|
470
|
+
# is a self-contained workspace router; mapping them per-event keeps the
|
|
471
|
+
# wiring explicit and lets a future hook bind to a different surface
|
|
472
|
+
# without touching the chat-history one.
|
|
473
|
+
AUGMENT_HOOK_BINDINGS = (
|
|
474
|
+
(AUGMENT_CHAT_HISTORY_TRAMPOLINE,
|
|
475
|
+
("SessionStart", "SessionEnd", "Stop", "PostToolUse")),
|
|
476
|
+
(AUGMENT_ROADMAP_PROGRESS_TRAMPOLINE,
|
|
477
|
+
("PostToolUse",)),
|
|
478
|
+
)
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
def _deploy_augment_trampoline(package_root: Path, name: str, force: bool) -> Path | None:
|
|
482
|
+
src = package_root / "scripts" / "hooks" / name
|
|
483
|
+
if not src.exists():
|
|
484
|
+
skip(f"augment trampoline missing in package: {src}")
|
|
485
|
+
return None
|
|
486
|
+
AUGMENT_USER_HOOKS_DIR.mkdir(parents=True, exist_ok=True)
|
|
487
|
+
dst = AUGMENT_USER_HOOKS_DIR / name
|
|
488
|
+
src_text = src.read_text(encoding="utf-8")
|
|
489
|
+
if dst.exists() and dst.read_text(encoding="utf-8") == src_text and not force:
|
|
490
|
+
skip(f"~/.augment/hooks/{name} already up to date")
|
|
491
|
+
else:
|
|
492
|
+
dst.write_text(src_text, encoding="utf-8")
|
|
493
|
+
dst.chmod(0o755)
|
|
494
|
+
success(f"~/.augment/hooks/{name} installed")
|
|
495
|
+
return dst
|
|
469
496
|
|
|
470
497
|
|
|
471
498
|
def ensure_augment_user_hooks(package_root: Path, force: bool) -> None:
|
|
472
|
-
"""Deploy the Augment lifecycle-hook
|
|
499
|
+
"""Deploy the Augment lifecycle-hook trampolines at user scope.
|
|
473
500
|
|
|
474
501
|
Augment hook scripts must use the .sh extension and be referenced by
|
|
475
502
|
absolute path; user scope is the only surface that fires for both the
|
|
476
503
|
CLI and the IDE plugins. This installs once per developer (not per
|
|
477
|
-
project) —
|
|
478
|
-
and dispatches into whichever project is active at hook-fire
|
|
504
|
+
project) — each trampoline reads workspace_roots from the event
|
|
505
|
+
payload and dispatches into whichever project is active at hook-fire
|
|
506
|
+
time.
|
|
507
|
+
|
|
508
|
+
Two trampolines are deployed:
|
|
509
|
+
- augment-chat-history.sh → SessionStart/SessionEnd/Stop/PostToolUse
|
|
510
|
+
- augment-roadmap-progress.sh → PostToolUse (path-filtered to
|
|
511
|
+
agents/roadmaps/ — see scripts/roadmap_progress_hook.py)
|
|
479
512
|
"""
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
513
|
+
per_event: dict[str, list] = {}
|
|
514
|
+
for name, events in AUGMENT_HOOK_BINDINGS:
|
|
515
|
+
dst = _deploy_augment_trampoline(package_root, name, force)
|
|
516
|
+
if dst is None:
|
|
517
|
+
continue
|
|
518
|
+
entry = {"hooks": [{"type": "command", "command": str(dst)}]}
|
|
519
|
+
for event in events:
|
|
520
|
+
per_event.setdefault(event, []).append(entry)
|
|
487
521
|
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
skip(f"~/.augment/hooks/{AUGMENT_TRAMPOLINE_NAME} already up to date")
|
|
491
|
-
else:
|
|
492
|
-
dst.write_text(src_text, encoding="utf-8")
|
|
493
|
-
dst.chmod(0o755)
|
|
494
|
-
success(f"~/.augment/hooks/{AUGMENT_TRAMPOLINE_NAME} installed")
|
|
522
|
+
if not per_event:
|
|
523
|
+
return
|
|
495
524
|
|
|
496
|
-
|
|
497
|
-
"hooks": [
|
|
498
|
-
{
|
|
499
|
-
"type": "command",
|
|
500
|
-
"command": str(dst),
|
|
501
|
-
},
|
|
502
|
-
],
|
|
503
|
-
}
|
|
504
|
-
settings_patch: dict = {"hooks": {event: [hook_entry] for event in AUGMENT_HOOK_EVENTS}}
|
|
525
|
+
settings_patch: dict = {"hooks": per_event}
|
|
505
526
|
merge_json_file(
|
|
506
527
|
AUGMENT_USER_DIR / "settings.json",
|
|
507
528
|
settings_patch,
|
|
@@ -99,3 +99,8 @@ echo "✅ Key installed: ${TARGET_FILE} (mode 0600)."
|
|
|
99
99
|
echo " Verify: ls -la ${TARGET_FILE}"
|
|
100
100
|
echo " Rotate: rerun this script (you'll be prompted to overwrite)."
|
|
101
101
|
echo " Remove: rm ${TARGET_FILE}"
|
|
102
|
+
echo
|
|
103
|
+
echo "ℹ️ Key install ≠ enable. To use this provider in /council:"
|
|
104
|
+
echo " set ai_council.enabled: true and ai_council.members.anthropic.enabled: true"
|
|
105
|
+
echo " in .agent-settings.yml. Council is a 'full' cost_profile feature; under"
|
|
106
|
+
echo " 'minimal' / 'balanced' the runtime hooks stay inactive."
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Interactive OpenAI-API-key installer for scripts/ai_council/clients.py.
|
|
3
|
+
#
|
|
4
|
+
# Reads the key with `read -s` so it never echoes to the terminal and
|
|
5
|
+
# never lands in shell history or scrollback. Writes atomically to
|
|
6
|
+
# ~/.config/agent-config/openai.key with mode 0600.
|
|
7
|
+
#
|
|
8
|
+
# Contract — companion to scripts/ai_council/clients.py:
|
|
9
|
+
# - File path: $HOME/.config/agent-config/openai.key
|
|
10
|
+
# - File mode: 0600 (owner read/write only)
|
|
11
|
+
# - Key format: must start with `sk-`
|
|
12
|
+
# - No --force, no --yes, no env-var bypass. Piped stdin is rejected.
|
|
13
|
+
#
|
|
14
|
+
# The runner re-checks all of the above at every live invocation and
|
|
15
|
+
# refuses to run if the file drifts from this contract.
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
TARGET_DIR="${HOME}/.config/agent-config"
|
|
20
|
+
TARGET_FILE="${TARGET_DIR}/openai.key"
|
|
21
|
+
|
|
22
|
+
# ── controlling-terminal requirement ─────────────────────────────────────
|
|
23
|
+
# We read from /dev/tty directly (fd 3), not from stdin. This is the
|
|
24
|
+
# stricter and more portable contract:
|
|
25
|
+
# - works under `task`, `script`, `sudo`, anything that reattaches stdin
|
|
26
|
+
# - forces every character to come from the user's real keyboard, so a
|
|
27
|
+
# pipe or redirected file cannot smuggle the key into the process
|
|
28
|
+
# - exits cleanly if there is no controlling terminal at all (e.g. CI,
|
|
29
|
+
# cron, agent automation)
|
|
30
|
+
if ! exec 3</dev/tty 2>/dev/null; then
|
|
31
|
+
echo "❌ install_openai_key.sh requires a controlling terminal." >&2
|
|
32
|
+
echo " /dev/tty not available — refusing to run under automation." >&2
|
|
33
|
+
exit 2
|
|
34
|
+
fi
|
|
35
|
+
|
|
36
|
+
# ── overwrite confirmation ───────────────────────────────────────────────
|
|
37
|
+
if [[ -e "${TARGET_FILE}" ]]; then
|
|
38
|
+
echo "⚠️ ${TARGET_FILE} already exists."
|
|
39
|
+
printf "Overwrite? [type 'yes' to replace, anything else aborts]: "
|
|
40
|
+
read -r -u 3 answer
|
|
41
|
+
if [[ "${answer}" != "yes" ]]; then
|
|
42
|
+
echo "Aborted. Existing key untouched."
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
fi
|
|
46
|
+
|
|
47
|
+
# ── read key (no echo, no history) ───────────────────────────────────────
|
|
48
|
+
echo "Paste your OpenAI API key (input is hidden, no echo)."
|
|
49
|
+
echo "The key should start with 'sk-'."
|
|
50
|
+
printf "Key: "
|
|
51
|
+
# -s = silent (no echo), read from fd 3 = /dev/tty, not stdin.
|
|
52
|
+
read -r -s -u 3 API_KEY
|
|
53
|
+
echo
|
|
54
|
+
|
|
55
|
+
if [[ -z "${API_KEY}" ]]; then
|
|
56
|
+
echo "❌ Empty input — no file written." >&2
|
|
57
|
+
exit 2
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
if [[ "${API_KEY}" != sk-* ]]; then
|
|
61
|
+
echo "❌ Input does not look like an OpenAI key (missing 'sk-' prefix)." >&2
|
|
62
|
+
echo " No file written." >&2
|
|
63
|
+
exit 2
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# ── create config dir with 0700, atomic write with 0600 ──────────────────
|
|
67
|
+
mkdir -p "${TARGET_DIR}"
|
|
68
|
+
chmod 0700 "${TARGET_DIR}"
|
|
69
|
+
|
|
70
|
+
TMP_FILE="$(mktemp "${TARGET_DIR}/.openai.key.XXXXXX")"
|
|
71
|
+
cleanup() { rm -f "${TMP_FILE}"; }
|
|
72
|
+
trap cleanup EXIT
|
|
73
|
+
|
|
74
|
+
# chmod the tmpfile BEFORE writing the key, so there is no window where
|
|
75
|
+
# the key sits on disk with group/other-readable permissions.
|
|
76
|
+
chmod 0600 "${TMP_FILE}"
|
|
77
|
+
printf '%s\n' "${API_KEY}" > "${TMP_FILE}"
|
|
78
|
+
mv "${TMP_FILE}" "${TARGET_FILE}"
|
|
79
|
+
trap - EXIT
|
|
80
|
+
|
|
81
|
+
# Clear the variable and the `mv` positional argument — defence in depth
|
|
82
|
+
# against a crash handler that dumps the process environment.
|
|
83
|
+
API_KEY=""
|
|
84
|
+
|
|
85
|
+
# ── verify mode post-write (portable stat: BSD on macOS, GNU on Linux) ───
|
|
86
|
+
if ACTUAL_MODE=$(stat -f '%Lp' "${TARGET_FILE}" 2>/dev/null); then
|
|
87
|
+
: # macOS / BSD
|
|
88
|
+
else
|
|
89
|
+
ACTUAL_MODE=$(stat -c '%a' "${TARGET_FILE}")
|
|
90
|
+
fi
|
|
91
|
+
|
|
92
|
+
if [[ "${ACTUAL_MODE}" != "600" ]]; then
|
|
93
|
+
echo "❌ Permissions verification failed: ${TARGET_FILE} has mode ${ACTUAL_MODE}, expected 600." >&2
|
|
94
|
+
echo " Delete and reinstall: rm ${TARGET_FILE} && $0" >&2
|
|
95
|
+
exit 3
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
echo "✅ Key installed: ${TARGET_FILE} (mode 0600)."
|
|
99
|
+
echo " Verify: ls -la ${TARGET_FILE}"
|
|
100
|
+
echo " Rotate: rerun this script (you'll be prompted to overwrite)."
|
|
101
|
+
echo " Remove: rm ${TARGET_FILE}"
|
|
102
|
+
echo
|
|
103
|
+
echo "ℹ️ Key install ≠ enable. To use this provider in /council:"
|
|
104
|
+
echo " set ai_council.enabled: true and ai_council.members.openai.enabled: true"
|
|
105
|
+
echo " in .agent-settings.yml. Council is a 'full' cost_profile feature; under"
|
|
106
|
+
echo " 'minimal' / 'balanced' the runtime hooks stay inactive."
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Lint the `load_context:` / `load_context_eager:` frontmatter schema.
|
|
3
|
+
|
|
4
|
+
Validates per docs/contracts/load-context-schema.md:
|
|
5
|
+
- Paths exist and are .md
|
|
6
|
+
- Allowed roots only (.agent-src*/contexts/, agents/contexts/)
|
|
7
|
+
- No public→project-local leak (warn)
|
|
8
|
+
- No circular refs across lazy + eager edges
|
|
9
|
+
- Combined char-budget for eager edges (rule + eager targets ≤ cap)
|
|
10
|
+
|
|
11
|
+
Exits non-zero on error; warnings are reported but do not fail.
|
|
12
|
+
Used in CI via `task lint-load-context`.
|
|
13
|
+
"""
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import sys
|
|
17
|
+
from pathlib import Path
|
|
18
|
+
from typing import Iterable
|
|
19
|
+
|
|
20
|
+
import yaml
|
|
21
|
+
|
|
22
|
+
ROOT = Path(__file__).resolve().parent.parent
|
|
23
|
+
|
|
24
|
+
SCAN_DIRS = [
|
|
25
|
+
ROOT / ".agent-src.uncompressed" / "rules",
|
|
26
|
+
ROOT / ".agent-src.uncompressed" / "contexts",
|
|
27
|
+
ROOT / "agents" / "contexts",
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
ALLOWED_PREFIXES = (
|
|
31
|
+
".agent-src.uncompressed/contexts/",
|
|
32
|
+
".agent-src/contexts/",
|
|
33
|
+
"agents/contexts/",
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
PUBLIC_RULE_PREFIX = ".agent-src.uncompressed/rules/"
|
|
37
|
+
PROJECT_LOCAL_PREFIX = "agents/contexts/"
|
|
38
|
+
|
|
39
|
+
HARD_FLOOR_RULES = {"non-destructive-by-default", "security-sensitive-stop"}
|
|
40
|
+
|
|
41
|
+
CAP_ALWAYS = 2_500
|
|
42
|
+
CAP_AUTO = 4_000
|
|
43
|
+
CAP_SAFETY = 5_000
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def parse_frontmatter(path: Path) -> dict:
|
|
47
|
+
text = path.read_text(encoding="utf-8")
|
|
48
|
+
if not text.startswith("---\n"):
|
|
49
|
+
return {}
|
|
50
|
+
end = text.find("\n---\n", 4)
|
|
51
|
+
if end == -1:
|
|
52
|
+
return {}
|
|
53
|
+
try:
|
|
54
|
+
data = yaml.safe_load(text[4:end])
|
|
55
|
+
except yaml.YAMLError:
|
|
56
|
+
return {}
|
|
57
|
+
return data if isinstance(data, dict) else {}
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def collect_files() -> Iterable[Path]:
|
|
61
|
+
for d in SCAN_DIRS:
|
|
62
|
+
if d.exists():
|
|
63
|
+
yield from d.rglob("*.md")
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def rel(p: Path) -> str:
|
|
67
|
+
return p.relative_to(ROOT).as_posix()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def cap_for(rule_path: Path, fm: dict) -> int:
|
|
71
|
+
if rule_path.stem in HARD_FLOOR_RULES:
|
|
72
|
+
return CAP_SAFETY
|
|
73
|
+
rtype = (fm.get("type") or "").strip('"').strip("'")
|
|
74
|
+
if rtype == "always":
|
|
75
|
+
return CAP_ALWAYS
|
|
76
|
+
if rtype == "auto":
|
|
77
|
+
return CAP_AUTO
|
|
78
|
+
return CAP_AUTO # default for non-rule contexts cited in eager (won't trigger)
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def find_cycles(graph: dict[str, list[str]]) -> list[list[str]]:
|
|
82
|
+
cycles: list[list[str]] = []
|
|
83
|
+
visiting: set[str] = set()
|
|
84
|
+
visited: set[str] = set()
|
|
85
|
+
stack: list[str] = []
|
|
86
|
+
|
|
87
|
+
def dfs(node: str) -> None:
|
|
88
|
+
if node in visiting:
|
|
89
|
+
i = stack.index(node)
|
|
90
|
+
cycles.append(stack[i:] + [node])
|
|
91
|
+
return
|
|
92
|
+
if node in visited:
|
|
93
|
+
return
|
|
94
|
+
visiting.add(node)
|
|
95
|
+
stack.append(node)
|
|
96
|
+
for nxt in graph.get(node, []):
|
|
97
|
+
dfs(nxt)
|
|
98
|
+
stack.pop()
|
|
99
|
+
visiting.discard(node)
|
|
100
|
+
visited.add(node)
|
|
101
|
+
|
|
102
|
+
for n in graph:
|
|
103
|
+
dfs(n)
|
|
104
|
+
return cycles
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def main() -> int:
|
|
108
|
+
errors: list[str] = []
|
|
109
|
+
warnings: list[str] = []
|
|
110
|
+
graph: dict[str, list[str]] = {}
|
|
111
|
+
|
|
112
|
+
for f in collect_files():
|
|
113
|
+
fm = parse_frontmatter(f)
|
|
114
|
+
lazy = fm.get("load_context") or []
|
|
115
|
+
eager = fm.get("load_context_eager") or []
|
|
116
|
+
if not (lazy or eager):
|
|
117
|
+
continue
|
|
118
|
+
if not isinstance(lazy, list) or not isinstance(eager, list):
|
|
119
|
+
errors.append(f"{rel(f)}: load_context* must be a list")
|
|
120
|
+
continue
|
|
121
|
+
|
|
122
|
+
edges = list(lazy) + list(eager)
|
|
123
|
+
graph[rel(f)] = edges
|
|
124
|
+
|
|
125
|
+
for entry in edges:
|
|
126
|
+
if not isinstance(entry, str) or not entry.endswith(".md"):
|
|
127
|
+
errors.append(f"{rel(f)}: entry not str ending in .md → {entry!r}")
|
|
128
|
+
continue
|
|
129
|
+
if not entry.startswith(ALLOWED_PREFIXES):
|
|
130
|
+
errors.append(f"{rel(f)}: disallowed root → {entry}")
|
|
131
|
+
continue
|
|
132
|
+
target = ROOT / entry
|
|
133
|
+
if not target.exists():
|
|
134
|
+
errors.append(f"{rel(f)}: target missing → {entry}")
|
|
135
|
+
continue
|
|
136
|
+
if rel(f).startswith(PUBLIC_RULE_PREFIX) and entry.startswith(PROJECT_LOCAL_PREFIX):
|
|
137
|
+
warnings.append(f"{rel(f)}: public rule references project-local context → {entry}")
|
|
138
|
+
|
|
139
|
+
if eager:
|
|
140
|
+
cap = cap_for(f, fm)
|
|
141
|
+
total = len(f.read_text(encoding="utf-8"))
|
|
142
|
+
for entry in eager:
|
|
143
|
+
tgt = ROOT / entry
|
|
144
|
+
if tgt.exists():
|
|
145
|
+
total += len(tgt.read_text(encoding="utf-8"))
|
|
146
|
+
if total > cap:
|
|
147
|
+
errors.append(f"{rel(f)}: eager-load combined chars {total} > cap {cap}")
|
|
148
|
+
|
|
149
|
+
for cycle in find_cycles(graph):
|
|
150
|
+
errors.append("circular load_context: " + " → ".join(cycle))
|
|
151
|
+
|
|
152
|
+
for w in warnings:
|
|
153
|
+
print(f"⚠️ {w}")
|
|
154
|
+
for e in errors:
|
|
155
|
+
print(f"❌ {e}")
|
|
156
|
+
if errors:
|
|
157
|
+
return 1
|
|
158
|
+
print(f"✅ load_context schema clean ({len(graph)} declarer(s))")
|
|
159
|
+
return 0
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
if __name__ == "__main__":
|
|
163
|
+
sys.exit(main())
|
|
@@ -43,24 +43,24 @@ class Violation:
|
|
|
43
43
|
|
|
44
44
|
|
|
45
45
|
def load_locked_clusters() -> set[str]:
|
|
46
|
-
"""Parse the
|
|
46
|
+
"""Parse the locked cluster table from the contract."""
|
|
47
47
|
text = (ROOT / CLUSTER_CONTRACT).read_text(encoding="utf-8")
|
|
48
|
-
# Locate the
|
|
49
|
-
|
|
48
|
+
# Locate the locked-clusters table; cluster names sit in backticks in column 1.
|
|
49
|
+
in_table = False
|
|
50
50
|
clusters: set[str] = set()
|
|
51
51
|
for line in text.splitlines():
|
|
52
|
-
if line.startswith("##
|
|
53
|
-
|
|
52
|
+
if line.startswith("## Locked clusters"):
|
|
53
|
+
in_table = True
|
|
54
54
|
continue
|
|
55
|
-
if
|
|
55
|
+
if in_table and line.startswith("## "):
|
|
56
56
|
break
|
|
57
|
-
if
|
|
57
|
+
if in_table:
|
|
58
58
|
m = re.match(r"\|\s*`([a-z][a-z0-9-]*)`\s*\|", line)
|
|
59
59
|
if m:
|
|
60
60
|
clusters.add(m.group(1))
|
|
61
61
|
if not clusters:
|
|
62
62
|
print(
|
|
63
|
-
f"❌ Could not parse
|
|
63
|
+
f"❌ Could not parse locked-clusters table from {CLUSTER_CONTRACT}",
|
|
64
64
|
file=sys.stderr,
|
|
65
65
|
)
|
|
66
66
|
sys.exit(3)
|
|
@@ -83,7 +83,7 @@ def added_command_files(baseline: str) -> list[Path]:
|
|
|
83
83
|
file=sys.stderr)
|
|
84
84
|
sys.exit(3)
|
|
85
85
|
files = [Path(p) for p in result.stdout.splitlines()
|
|
86
|
-
if p.endswith(".md") and p != ""]
|
|
86
|
+
if p.endswith(".md") and p != "" and Path(p).name != "AGENTS.md"]
|
|
87
87
|
# Also include untracked (newly added, uncommitted) files.
|
|
88
88
|
try:
|
|
89
89
|
wt = subprocess.run(
|
|
@@ -97,7 +97,7 @@ def added_command_files(baseline: str) -> list[Path]:
|
|
|
97
97
|
if status.strip() not in ("A", "??", "AM"):
|
|
98
98
|
continue
|
|
99
99
|
path = line[3:].strip().split(" -> ")[-1]
|
|
100
|
-
if path.endswith(".md"):
|
|
100
|
+
if path.endswith(".md") and Path(path).name != "AGENTS.md":
|
|
101
101
|
p = Path(path)
|
|
102
102
|
if p not in files:
|
|
103
103
|
files.append(p)
|
|
@@ -107,7 +107,8 @@ def added_command_files(baseline: str) -> list[Path]:
|
|
|
107
107
|
|
|
108
108
|
|
|
109
109
|
def all_command_files() -> list[Path]:
|
|
110
|
-
return sorted((ROOT / COMMANDS_DIR).
|
|
110
|
+
return sorted(p for p in (ROOT / COMMANDS_DIR).rglob("*.md")
|
|
111
|
+
if p.name != "AGENTS.md")
|
|
111
112
|
|
|
112
113
|
|
|
113
114
|
def parse_frontmatter(path: Path) -> dict[str, str]:
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""Platform-agnostic PostToolUse hook for the `roadmap-progress-sync` rule.
|
|
3
|
+
|
|
4
|
+
Reads a JSON event from stdin (Augment / Claude / Cursor / Cline /
|
|
5
|
+
Windsurf / Gemini PostToolUse-shaped envelopes), decides whether the
|
|
6
|
+
tool call wrote to a roadmap file under `agents/roadmaps/`, and — when
|
|
7
|
+
it did — re-runs `update_roadmap_progress.py` so the dashboard stays
|
|
8
|
+
in sync without depending on agent self-discipline.
|
|
9
|
+
|
|
10
|
+
Exit code is **always 0**. Hooks must never block the agent loop; the
|
|
11
|
+
worst-case is a no-op when stdin is malformed or the regenerator is
|
|
12
|
+
missing.
|
|
13
|
+
|
|
14
|
+
Output discipline:
|
|
15
|
+
- stdout: nothing (Augment would surface stdout to the user)
|
|
16
|
+
- stderr: one short line in --verbose mode, otherwise silent
|
|
17
|
+
|
|
18
|
+
CLI:
|
|
19
|
+
python3 scripts/roadmap_progress_hook.py [--platform NAME] [--verbose]
|
|
20
|
+
|
|
21
|
+
The `--platform` flag is informational only — the filter logic reads
|
|
22
|
+
the same field names across platforms (tool_name, tool_input.path,
|
|
23
|
+
file_changes[].path).
|
|
24
|
+
"""
|
|
25
|
+
from __future__ import annotations
|
|
26
|
+
|
|
27
|
+
import argparse
|
|
28
|
+
import json
|
|
29
|
+
import subprocess
|
|
30
|
+
import sys
|
|
31
|
+
from pathlib import Path
|
|
32
|
+
|
|
33
|
+
# Tools whose successful execution can write to a roadmap file. We keep
|
|
34
|
+
# the list explicit so an unknown tool name (e.g. a new MCP tool that
|
|
35
|
+
# happens to mention a roadmap path in its input) does not trigger a
|
|
36
|
+
# spurious regeneration.
|
|
37
|
+
WRITE_TOOLS = frozenset({
|
|
38
|
+
"str-replace-editor",
|
|
39
|
+
"save-file",
|
|
40
|
+
"remove-files",
|
|
41
|
+
# Claude Code / Cursor naming variants — kept for cross-platform
|
|
42
|
+
# parity if this hook is ever wired beyond Augment.
|
|
43
|
+
"Edit",
|
|
44
|
+
"Write",
|
|
45
|
+
"MultiEdit",
|
|
46
|
+
})
|
|
47
|
+
|
|
48
|
+
ROADMAP_PREFIX = "agents/roadmaps/"
|
|
49
|
+
# Paths under these subtrees are tracked but not part of the open list
|
|
50
|
+
# the dashboard summarises — regenerating on every archived edit would
|
|
51
|
+
# be wasteful. The check still fires on the parent dir itself.
|
|
52
|
+
ROADMAP_EXCLUDED_PARTS = frozenset({"archive", "skipped"})
|
|
53
|
+
DASHBOARD_PATH = "agents/roadmaps-progress.md"
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
def _candidate_paths(payload: dict) -> list[str]:
|
|
57
|
+
"""Pull every plausible file path out of a PostToolUse payload."""
|
|
58
|
+
out: list[str] = []
|
|
59
|
+
fc = payload.get("file_changes")
|
|
60
|
+
if isinstance(fc, list):
|
|
61
|
+
for entry in fc:
|
|
62
|
+
if isinstance(entry, dict):
|
|
63
|
+
p = entry.get("path")
|
|
64
|
+
if isinstance(p, str) and p:
|
|
65
|
+
out.append(p)
|
|
66
|
+
ti = payload.get("tool_input")
|
|
67
|
+
if isinstance(ti, dict):
|
|
68
|
+
for key in ("path", "file_path", "target_file"):
|
|
69
|
+
v = ti.get(key)
|
|
70
|
+
if isinstance(v, str) and v:
|
|
71
|
+
out.append(v)
|
|
72
|
+
return out
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def _is_roadmap_touch(path: str) -> bool:
|
|
76
|
+
"""Return True if `path` is a roadmap file we should react to."""
|
|
77
|
+
norm = path.lstrip("./").replace("\\", "/")
|
|
78
|
+
if not norm.startswith(ROADMAP_PREFIX):
|
|
79
|
+
return False
|
|
80
|
+
if norm == DASHBOARD_PATH:
|
|
81
|
+
# Defensive — the dashboard sits at agents/roadmaps-progress.md,
|
|
82
|
+
# NOT inside agents/roadmaps/. The prefix check above already
|
|
83
|
+
# excludes it, but keep this explicit so a future relocation
|
|
84
|
+
# cannot turn the hook into an infinite loop.
|
|
85
|
+
return False
|
|
86
|
+
rest = norm[len(ROADMAP_PREFIX):]
|
|
87
|
+
parts = rest.split("/")
|
|
88
|
+
if len(parts) >= 2 and parts[0] in ROADMAP_EXCLUDED_PARTS:
|
|
89
|
+
return False
|
|
90
|
+
if not norm.endswith(".md"):
|
|
91
|
+
return False
|
|
92
|
+
return True
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _resolve_regenerator(consumer_root: Path) -> Path | None:
|
|
96
|
+
"""Find the regenerator script — package-shipped or installed copy."""
|
|
97
|
+
for candidate in (
|
|
98
|
+
consumer_root / ".augment" / "scripts" / "update_roadmap_progress.py",
|
|
99
|
+
consumer_root / ".agent-src" / "scripts" / "update_roadmap_progress.py",
|
|
100
|
+
consumer_root / ".agent-src.uncompressed" / "scripts" / "update_roadmap_progress.py",
|
|
101
|
+
):
|
|
102
|
+
if candidate.is_file():
|
|
103
|
+
return candidate
|
|
104
|
+
return None
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
def run(stdin_text: str, *, consumer_root: Path, verbose: bool = False) -> int:
|
|
108
|
+
payload: dict = {}
|
|
109
|
+
if stdin_text.strip():
|
|
110
|
+
try:
|
|
111
|
+
decoded = json.loads(stdin_text)
|
|
112
|
+
if isinstance(decoded, dict):
|
|
113
|
+
payload = decoded
|
|
114
|
+
except json.JSONDecodeError:
|
|
115
|
+
return 0 # malformed stdin → silent no-op, never block
|
|
116
|
+
|
|
117
|
+
tool = payload.get("tool_name") or payload.get("toolName") or payload.get("tool")
|
|
118
|
+
if not isinstance(tool, str) or tool not in WRITE_TOOLS:
|
|
119
|
+
return 0
|
|
120
|
+
|
|
121
|
+
paths = _candidate_paths(payload)
|
|
122
|
+
if not any(_is_roadmap_touch(p) for p in paths):
|
|
123
|
+
return 0
|
|
124
|
+
|
|
125
|
+
script = _resolve_regenerator(consumer_root)
|
|
126
|
+
if script is None:
|
|
127
|
+
if verbose:
|
|
128
|
+
print("roadmap-progress-hook: regenerator not found, skipping",
|
|
129
|
+
file=sys.stderr)
|
|
130
|
+
return 0
|
|
131
|
+
|
|
132
|
+
try:
|
|
133
|
+
subprocess.run(
|
|
134
|
+
[sys.executable, str(script)],
|
|
135
|
+
cwd=consumer_root, check=False,
|
|
136
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
137
|
+
timeout=30,
|
|
138
|
+
)
|
|
139
|
+
except (OSError, subprocess.SubprocessError):
|
|
140
|
+
pass # never propagate regenerator failures into the agent loop
|
|
141
|
+
|
|
142
|
+
if verbose:
|
|
143
|
+
print(f"roadmap-progress-hook: regenerated for tool={tool}",
|
|
144
|
+
file=sys.stderr)
|
|
145
|
+
return 0
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def main(argv: list[str] | None = None) -> int:
|
|
149
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
150
|
+
parser.add_argument("--platform", default="generic",
|
|
151
|
+
help="informational platform tag (augment/claude/...)")
|
|
152
|
+
parser.add_argument("--verbose", action="store_true",
|
|
153
|
+
help="emit one stderr line per invocation")
|
|
154
|
+
args = parser.parse_args(argv)
|
|
155
|
+
return run(sys.stdin.read(), consumer_root=Path.cwd(), verbose=args.verbose)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
if __name__ == "__main__": # pragma: no cover
|
|
159
|
+
sys.exit(main())
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"properties": {
|
|
10
10
|
"name": {
|
|
11
11
|
"type": "string",
|
|
12
|
-
"pattern": "^[a-z][a-z0-9-]
|
|
12
|
+
"pattern": "^[a-z][a-z0-9-]*(:[a-z][a-z0-9-]*)?$",
|
|
13
|
+
"$comment": "Top-level commands use the bare slug (`commit`). Nested cluster commands under `commands/<cluster>/<sub>.md` use the colon form (`council:default`) to mirror Claude Code's `/cluster:sub` rendering. Directory slug for `.claude/skills/` is the hyphenated form (`council-default`), generated by compress.py."
|
|
13
14
|
},
|
|
14
15
|
"description": {
|
|
15
16
|
"type": "string",
|
|
@@ -28,6 +29,26 @@
|
|
|
28
29
|
"pattern": "^[a-z][a-z0-9-]*$"
|
|
29
30
|
}
|
|
30
31
|
},
|
|
32
|
+
"cluster": {
|
|
33
|
+
"type": "string",
|
|
34
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
35
|
+
"description": "Locked verb cluster this command belongs to. See docs/contracts/command-clusters.md."
|
|
36
|
+
},
|
|
37
|
+
"sub": {
|
|
38
|
+
"type": "string",
|
|
39
|
+
"pattern": "^[a-z][a-z0-9-]*$",
|
|
40
|
+
"description": "Sub-command identifier within the cluster (e.g. `ci` for `/fix ci`)."
|
|
41
|
+
},
|
|
42
|
+
"superseded_by": {
|
|
43
|
+
"type": "string",
|
|
44
|
+
"pattern": "^[a-z][a-z0-9-]*( (--[a-z][a-z0-9-]*|[a-z][a-z0-9-]*))?$",
|
|
45
|
+
"description": "Set on deprecation shims. Format: '<cluster> <sub>' (e.g. 'fix ci') or '<cluster> --<flag>' for flag-clusters (e.g. 'commit --in-chunks'). See docs/contracts/command-clusters.md § Deprecation shim contract."
|
|
46
|
+
},
|
|
47
|
+
"deprecated_in": {
|
|
48
|
+
"type": "string",
|
|
49
|
+
"pattern": "^[0-9]+\\.[0-9]+\\.[0-9]+$",
|
|
50
|
+
"description": "Semver release in which this command became a shim (e.g. '1.15.0')."
|
|
51
|
+
},
|
|
31
52
|
"suggestion": {
|
|
32
53
|
"type": "object",
|
|
33
54
|
"additionalProperties": false,
|
|
@@ -23,6 +23,16 @@
|
|
|
23
23
|
"alwaysApply": {
|
|
24
24
|
"type": "boolean",
|
|
25
25
|
"description": "Optional sidecar for Cursor/Cline; by convention true when type=always, false when type=auto."
|
|
26
|
+
},
|
|
27
|
+
"load_context": {
|
|
28
|
+
"type": "array",
|
|
29
|
+
"items": {"type": "string", "pattern": "\\.md$"},
|
|
30
|
+
"description": "Lazy on-demand context references. Path rules and budget caps enforced by scripts/lint_load_context.py. Contract: docs/contracts/load-context-schema.md."
|
|
31
|
+
},
|
|
32
|
+
"load_context_eager": {
|
|
33
|
+
"type": "array",
|
|
34
|
+
"items": {"type": "string", "pattern": "\\.md$"},
|
|
35
|
+
"description": "Eager auto-loaded context references. Counts against the per-rule char budget; enforced by scripts/lint_load_context.py."
|
|
26
36
|
}
|
|
27
37
|
}
|
|
28
38
|
}
|