@event4u/agent-config 1.15.0 → 1.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (244) hide show
  1. package/.agent-src/commands/bug-fix.md +1 -1
  2. package/.agent-src/commands/bug-investigate.md +2 -2
  3. package/.agent-src/commands/chat-history-checkpoint.md +1 -1
  4. package/.agent-src/commands/chat-history-clear.md +1 -1
  5. package/.agent-src/commands/chat-history.md +1 -1
  6. package/.agent-src/commands/check-current-md.md +1 -1
  7. package/.agent-src/commands/council-design.md +96 -0
  8. package/.agent-src/commands/council-optimize.md +115 -0
  9. package/.agent-src/commands/council-pr.md +123 -0
  10. package/.agent-src/commands/council.md +219 -0
  11. package/.agent-src/commands/create-pr.md +23 -0
  12. package/.agent-src/commands/do-and-judge.md +3 -3
  13. package/.agent-src/commands/do-in-steps.md +4 -4
  14. package/.agent-src/commands/e2e-heal.md +1 -1
  15. package/.agent-src/commands/e2e-plan.md +1 -1
  16. package/.agent-src/commands/feature-dev.md +8 -0
  17. package/.agent-src/commands/feature-explore.md +6 -1
  18. package/.agent-src/commands/feature-plan.md +33 -2
  19. package/.agent-src/commands/feature-refactor.md +5 -0
  20. package/.agent-src/commands/feature-roadmap.md +6 -1
  21. package/.agent-src/commands/feature.md +58 -0
  22. package/.agent-src/commands/fix-ci.md +5 -0
  23. package/.agent-src/commands/fix-portability.md +5 -0
  24. package/.agent-src/commands/fix-pr-bot-comments.md +5 -0
  25. package/.agent-src/commands/fix-pr-comments.md +5 -0
  26. package/.agent-src/commands/fix-pr-developer-comments.md +5 -0
  27. package/.agent-src/commands/fix-references.md +5 -0
  28. package/.agent-src/commands/fix-seeder.md +5 -0
  29. package/.agent-src/commands/fix.md +60 -0
  30. package/.agent-src/commands/jira-ticket.md +1 -1
  31. package/.agent-src/commands/judge.md +1 -1
  32. package/.agent-src/commands/memory-add.md +3 -3
  33. package/.agent-src/commands/memory-full.md +2 -2
  34. package/.agent-src/commands/memory-promote.md +2 -2
  35. package/.agent-src/commands/mode.md +5 -5
  36. package/.agent-src/commands/onboard.md +3 -3
  37. package/.agent-src/commands/optimize-agents.md +6 -1
  38. package/.agent-src/commands/optimize-augmentignore.md +5 -0
  39. package/.agent-src/commands/optimize-rtk-filters.md +5 -0
  40. package/.agent-src/commands/optimize-skills.md +6 -1
  41. package/.agent-src/commands/optimize.md +54 -0
  42. package/.agent-src/commands/propose-memory.md +2 -2
  43. package/.agent-src/commands/review-changes.md +26 -1
  44. package/.agent-src/commands/review-routing.md +1 -1
  45. package/.agent-src/commands/roadmap-create.md +29 -2
  46. package/.agent-src/commands/set-cost-profile.md +3 -3
  47. package/.agent-src/commands/sync-agent-settings.md +2 -2
  48. package/.agent-src/commands/tests-create.md +1 -1
  49. package/.agent-src/commands/upstream-contribute.md +1 -1
  50. package/.agent-src/contexts/authority/commit-mechanics.md +57 -0
  51. package/.agent-src/contexts/authority/destructive-mechanics.md +66 -0
  52. package/.agent-src/contexts/authority/scope-mechanics.md +87 -0
  53. package/.agent-src/contexts/execution/autonomy-detection.md +54 -0
  54. package/.agent-src/contexts/execution/autonomy-examples.md +90 -0
  55. package/.agent-src/contexts/execution/autonomy-mechanics.md +29 -0
  56. package/.agent-src/contexts/execution/verification-mechanics.md +80 -0
  57. package/.agent-src/personas/README.md +1 -1
  58. package/.agent-src/rules/agent-authority.md +24 -0
  59. package/.agent-src/rules/architecture.md +1 -1
  60. package/.agent-src/rules/artifact-drafting-protocol.md +1 -1
  61. package/.agent-src/rules/artifact-engagement-recording.md +1 -1
  62. package/.agent-src/rules/ask-when-uncertain.md +1 -1
  63. package/.agent-src/rules/autonomous-execution.md +78 -114
  64. package/.agent-src/rules/capture-learnings.md +1 -1
  65. package/.agent-src/rules/chat-history-cadence.md +3 -3
  66. package/.agent-src/rules/chat-history-ownership.md +3 -3
  67. package/.agent-src/rules/chat-history-visibility.md +3 -3
  68. package/.agent-src/rules/{command-suggestion.md → command-suggestion-policy.md} +7 -7
  69. package/.agent-src/rules/commit-conventions.md +1 -1
  70. package/.agent-src/rules/commit-policy.md +14 -42
  71. package/.agent-src/rules/context-hygiene.md +3 -3
  72. package/.agent-src/rules/direct-answers.md +1 -1
  73. package/.agent-src/rules/docs-sync.md +1 -1
  74. package/.agent-src/rules/e2e-testing.md +1 -1
  75. package/.agent-src/rules/guidelines.md +4 -4
  76. package/.agent-src/rules/improve-before-implement.md +2 -2
  77. package/.agent-src/rules/language-and-tone.md +37 -96
  78. package/.agent-src/rules/minimal-safe-diff.md +3 -3
  79. package/.agent-src/rules/model-recommendation.md +4 -4
  80. package/.agent-src/rules/no-cheap-questions.md +89 -0
  81. package/.agent-src/rules/non-destructive-by-default.md +15 -49
  82. package/.agent-src/rules/onboarding-gate.md +5 -5
  83. package/.agent-src/rules/review-routing-awareness.md +9 -9
  84. package/.agent-src/rules/roadmap-progress-sync.md +26 -33
  85. package/.agent-src/rules/role-mode-adherence.md +2 -2
  86. package/.agent-src/rules/scope-control.md +65 -46
  87. package/.agent-src/rules/security-sensitive-stop.md +2 -2
  88. package/.agent-src/rules/size-enforcement.md +1 -1
  89. package/.agent-src/rules/think-before-action.md +5 -5
  90. package/.agent-src/rules/token-efficiency.md +4 -4
  91. package/.agent-src/rules/{ui-audit-before-build.md → ui-audit-gate.md} +3 -3
  92. package/.agent-src/rules/user-interaction.md +3 -3
  93. package/.agent-src/rules/verify-before-complete.md +12 -67
  94. package/.agent-src/scripts/update_roadmap_progress.py +9 -4
  95. package/.agent-src/skills/ai-council/SKILL.md +333 -0
  96. package/.agent-src/skills/api-endpoint/SKILL.md +2 -2
  97. package/.agent-src/skills/blade-ui/SKILL.md +1 -1
  98. package/.agent-src/skills/blast-radius-analyzer/SKILL.md +1 -1
  99. package/.agent-src/skills/bug-analyzer/SKILL.md +1 -1
  100. package/.agent-src/skills/command-routing/SKILL.md +1 -1
  101. package/.agent-src/skills/command-writing/SKILL.md +1 -1
  102. package/.agent-src/skills/conventional-commits-writing/SKILL.md +1 -1
  103. package/.agent-src/skills/copilot-agents-optimization/SKILL.md +2 -2
  104. package/.agent-src/skills/developer-like-execution/SKILL.md +2 -2
  105. package/.agent-src/skills/flux/SKILL.md +1 -1
  106. package/.agent-src/skills/git-workflow/SKILL.md +1 -1
  107. package/.agent-src/skills/guideline-writing/SKILL.md +11 -11
  108. package/.agent-src/skills/learning-to-rule-or-skill/SKILL.md +4 -4
  109. package/.agent-src/skills/livewire/SKILL.md +1 -1
  110. package/.agent-src/skills/override-management/SKILL.md +2 -2
  111. package/.agent-src/skills/php-coder/SKILL.md +1 -1
  112. package/.agent-src/skills/playwright-testing/SKILL.md +2 -2
  113. package/.agent-src/skills/readme-reviewer/SKILL.md +1 -1
  114. package/.agent-src/skills/readme-writing/SKILL.md +1 -1
  115. package/.agent-src/skills/readme-writing-package/SKILL.md +1 -1
  116. package/.agent-src/skills/receiving-code-review/SKILL.md +1 -1
  117. package/.agent-src/skills/review-routing/SKILL.md +2 -2
  118. package/.agent-src/skills/rule-writing/SKILL.md +1 -1
  119. package/.agent-src/skills/skill-reviewer/SKILL.md +1 -1
  120. package/.agent-src/skills/skill-writing/SKILL.md +3 -3
  121. package/.agent-src/skills/subagent-orchestration/SKILL.md +1 -0
  122. package/.agent-src/skills/systematic-debugging/SKILL.md +1 -1
  123. package/.agent-src/skills/upstream-contribute/SKILL.md +1 -1
  124. package/.agent-src/skills/validate-feature-fit/SKILL.md +2 -2
  125. package/.agent-src/skills/{verify-before-complete → verify-completion-evidence}/SKILL.md +2 -2
  126. package/.agent-src/templates/agent-settings.md +8 -8
  127. package/.agent-src/templates/contexts/auth-model.md +1 -1
  128. package/.agent-src/templates/scripts/README.md +2 -2
  129. package/.agent-src/templates/scripts/telemetry/aggregator.py +16 -1
  130. package/.agent-src/templates/scripts/telemetry/engagement.py +59 -0
  131. package/.agent-src/templates/scripts/telemetry/report_renderer.py +28 -1
  132. package/.agent-src/templates/scripts/telemetry_record.py +14 -1
  133. package/.claude-plugin/marketplace.json +10 -2
  134. package/AGENTS.md +11 -9
  135. package/CHANGELOG.md +123 -1
  136. package/README.md +28 -30
  137. package/config/agent-settings.template.yml +58 -1
  138. package/config/gitignore-block.txt +3 -0
  139. package/docs/architecture.md +4 -4
  140. package/docs/catalog.md +331 -0
  141. package/docs/contracts/STABILITY.md +39 -0
  142. package/docs/contracts/adr-command-suggestion.md +3 -3
  143. package/docs/contracts/adr-product-ui-track.md +2 -2
  144. package/docs/contracts/agent-memory-contract.md +2 -2
  145. package/docs/contracts/artifact-engagement-flow.md +1 -1
  146. package/docs/contracts/command-clusters.md +2 -2
  147. package/docs/contracts/command-suggestion-flow.md +3 -3
  148. package/docs/contracts/implement-ticket-flow.md +2 -2
  149. package/docs/contracts/linear-ai-rules-inclusion.md +1 -1
  150. package/docs/contracts/load-context-schema.md +186 -0
  151. package/docs/contracts/rule-interactions.yml +96 -0
  152. package/docs/contracts/rule-priority-hierarchy.md +87 -0
  153. package/docs/contracts/ui-track-flow.md +1 -1
  154. package/docs/customization.md +14 -0
  155. package/docs/end-to-end-walkthroughs.md +165 -0
  156. package/docs/getting-started.md +26 -8
  157. package/docs/github-topics.md +12 -3
  158. package/docs/guidelines/agent-infra/language-and-tone-examples.md +79 -0
  159. package/{.agent-src → docs}/guidelines/docs/readme-size-and-splitting.md +26 -25
  160. package/docs/guidelines/php/git.md +164 -0
  161. package/docs/migrations/commands-1.15.0.md +1 -1
  162. package/docs/showcase.md +9 -4
  163. package/docs/skills-catalog.md +14 -8
  164. package/docs/ui-track-mental-model.md +2 -2
  165. package/llms.txt +13 -7
  166. package/package.json +1 -1
  167. package/scripts/agent-config +23 -0
  168. package/scripts/ai_council/__init__.py +39 -0
  169. package/scripts/ai_council/_default_prices.py +41 -0
  170. package/scripts/ai_council/_one_off_rebalancing_audit.py +149 -0
  171. package/scripts/ai_council/_one_off_roundtrip.py +106 -0
  172. package/scripts/ai_council/budget_guard.py +172 -0
  173. package/scripts/ai_council/bundler.py +261 -0
  174. package/scripts/ai_council/clients.py +381 -0
  175. package/scripts/ai_council/modes.py +127 -0
  176. package/scripts/ai_council/orchestrator.py +350 -0
  177. package/scripts/ai_council/pricing.py +213 -0
  178. package/scripts/ai_council/project_context.py +159 -0
  179. package/scripts/ai_council/prompts.py +232 -0
  180. package/scripts/ai_council/session.py +144 -0
  181. package/scripts/check_always_budget.py +126 -0
  182. package/scripts/check_augmentignore.py +69 -0
  183. package/scripts/check_command_count_messaging.py +120 -0
  184. package/scripts/check_portability.py +55 -0
  185. package/scripts/check_public_catalog_links.py +122 -0
  186. package/scripts/check_references.py +4 -1
  187. package/scripts/check_roadmap_trackable.py +111 -0
  188. package/scripts/command_suggester/cooldown.py +1 -1
  189. package/scripts/generate_index.py +266 -0
  190. package/scripts/install_anthropic_key.sh +5 -0
  191. package/scripts/install_openai_key.sh +106 -0
  192. package/scripts/lint_load_context.py +163 -0
  193. package/scripts/schemas/command.schema.json +20 -0
  194. package/scripts/schemas/rule.schema.json +10 -0
  195. package/scripts/skill_linter.py +12 -4
  196. package/scripts/sync_agent_settings.py +1 -1
  197. package/scripts/update_counts.py +9 -4
  198. package/scripts/update_prices.py +124 -0
  199. package/.agent-src/guidelines/php/git.md +0 -96
  200. /package/.agent-src/rules/{slash-commands.md → slash-command-routing-policy.md} +0 -0
  201. /package/{.agent-src → docs}/guidelines/agent-infra/agent-interaction-and-decision-quality.md +0 -0
  202. /package/{.agent-src → docs}/guidelines/agent-infra/break-glass-usage.md +0 -0
  203. /package/{.agent-src → docs}/guidelines/agent-infra/developer-judgment.md +0 -0
  204. /package/{.agent-src → docs}/guidelines/agent-infra/engineering-memory-data-format.md +0 -0
  205. /package/{.agent-src → docs}/guidelines/agent-infra/layered-settings.md +0 -0
  206. /package/{.agent-src → docs}/guidelines/agent-infra/memory-access.md +0 -0
  207. /package/{.agent-src → docs}/guidelines/agent-infra/naming.md +0 -0
  208. /package/{.agent-src → docs}/guidelines/agent-infra/output-patterns.md +0 -0
  209. /package/{.agent-src → docs}/guidelines/agent-infra/review-routing-data-format.md +0 -0
  210. /package/{.agent-src → docs}/guidelines/agent-infra/role-contracts.md +0 -0
  211. /package/{.agent-src → docs}/guidelines/agent-infra/role-mode-router.md +0 -0
  212. /package/{.agent-src → docs}/guidelines/agent-infra/runtime-layer.md +0 -0
  213. /package/{.agent-src → docs}/guidelines/agent-infra/self-improvement-pipeline.md +0 -0
  214. /package/{.agent-src → docs}/guidelines/agent-infra/size-and-scope.md +0 -0
  215. /package/{.agent-src → docs}/guidelines/agent-infra/tool-integration.md +0 -0
  216. /package/{.agent-src → docs}/guidelines/e2e/playwright.md +0 -0
  217. /package/{.agent-src → docs}/guidelines/php/api-design.md +0 -0
  218. /package/{.agent-src → docs}/guidelines/php/artisan-commands.md +0 -0
  219. /package/{.agent-src → docs}/guidelines/php/blade-ui.md +0 -0
  220. /package/{.agent-src → docs}/guidelines/php/controllers.md +0 -0
  221. /package/{.agent-src → docs}/guidelines/php/database.md +0 -0
  222. /package/{.agent-src → docs}/guidelines/php/eloquent.md +0 -0
  223. /package/{.agent-src → docs}/guidelines/php/flux.md +0 -0
  224. /package/{.agent-src → docs}/guidelines/php/general.md +0 -0
  225. /package/{.agent-src → docs}/guidelines/php/jobs.md +0 -0
  226. /package/{.agent-src → docs}/guidelines/php/livewire.md +0 -0
  227. /package/{.agent-src → docs}/guidelines/php/logging.md +0 -0
  228. /package/{.agent-src → docs}/guidelines/php/naming.md +0 -0
  229. /package/{.agent-src → docs}/guidelines/php/patterns/dependency-injection.md +0 -0
  230. /package/{.agent-src → docs}/guidelines/php/patterns/dtos.md +0 -0
  231. /package/{.agent-src → docs}/guidelines/php/patterns/events.md +0 -0
  232. /package/{.agent-src → docs}/guidelines/php/patterns/factory.md +0 -0
  233. /package/{.agent-src → docs}/guidelines/php/patterns/pipelines.md +0 -0
  234. /package/{.agent-src → docs}/guidelines/php/patterns/policies.md +0 -0
  235. /package/{.agent-src → docs}/guidelines/php/patterns/repositories.md +0 -0
  236. /package/{.agent-src → docs}/guidelines/php/patterns/service-layer.md +0 -0
  237. /package/{.agent-src → docs}/guidelines/php/patterns/strategy.md +0 -0
  238. /package/{.agent-src → docs}/guidelines/php/patterns.md +0 -0
  239. /package/{.agent-src → docs}/guidelines/php/performance.md +0 -0
  240. /package/{.agent-src → docs}/guidelines/php/resources.md +0 -0
  241. /package/{.agent-src → docs}/guidelines/php/security.md +0 -0
  242. /package/{.agent-src → docs}/guidelines/php/sql.md +0 -0
  243. /package/{.agent-src → docs}/guidelines/php/validations.md +0 -0
  244. /package/{.agent-src → docs}/guidelines/php/websocket.md +0 -0
package/llms.txt CHANGED
@@ -16,7 +16,7 @@ api-testing: Use when writing API endpoint tests — integration tests, contract
16
16
  artisan-commands: Use when creating or modifying Artisan commands. Covers clear signatures, safe execution flow, helpful output, and project conventions for console tooling.
17
17
  authz-review: Use when reviewing authorization end-to-end — route → gate → policy → query scope → response filter — before changes to permissions, tenants, ownership, or admin flows.
18
18
  aws-infrastructure: Use when working with AWS resources — ECS Fargate, ECR, EFS, Secrets Manager, gomplate templates, multi-env deployments — even when the user says 'deploy to staging' without naming AWS.
19
- blade-ui: Use when creating or editing Blade views, components, partials, layouts, or view logic even when the user says 'add a new page' or 'render this data' without naming Blade.
19
+ blade-ui: Stack-implementation skill for Laravel Blade dispatched by `directives/ui/apply.py` (and `review.py` / `polish.py`) when the project's frontend stack is Blade. Covers views, components, partials, layouts, and view logic.
20
20
  blast-radius-analyzer: Use BEFORE editing shared code — enumerates every call site, event consumer, queue worker, API client, migration, and test that a planned change will touch, with a file:line citation per dependency.
21
21
  bug-analyzer: Use when the user shares a Sentry error, Jira bug ticket, or error description and wants root cause analysis. Also for proactive bug hunting and code audits for hidden bugs.
22
22
  check-refs: Use when verifying cross-references between skills, rules, commands, guidelines, and context documents are not broken after edits, renames, or deletions.
@@ -41,15 +41,17 @@ developer-like-execution: Use when implementing, debugging, refactoring, or revi
41
41
  docker: Use when working with Docker — Dockerfile edits, docker-compose services, containers, or the dual-container (fast + Xdebug) setup — even when the user just says 'my container won't start'.
42
42
  dto-creator: Use when the user says "create a DTO", "new data transfer object", or needs to convert request/response data into a typed PHP class. Creates DTOs with SimpleDto base class and attribute mapping.
43
43
  eloquent: Use when writing Eloquent models, relationships, scopes, or queries via Model:: — 'fetch users with their orders'. NOT for PHPStan output, non-Eloquent services, or raw SQL questions.
44
- fe-design: Use when designing frontend interfaces component architecture, layout patterns, form design, table patterns, responsive strategies, and UX principles for Blade/Livewire/Flux/Tailwind.
44
+ "estimate-ticket": Estimate a Jira/Linear ticket'estimate PROJ-123', 'wie groß ist das?', 'should we split this?' size + risk + split + uncertainty, sibling of /refine-ticket, close-prompt.
45
+ existing-ui-audit: Use BEFORE writing or editing any non-trivial UI — inventories components, design tokens, shadcn primitives, and reusable patterns into state.ui_audit. Hard gate for the ui directive set.
46
+ fe-design: Reference for frontend-design heuristics — component architecture, layout patterns, form/table design, responsive strategy, a11y, UX principles. Stack-agnostic; cited by directives/ui/design.py.
45
47
  feature-planning: Use when the user says "plan a feature", "brainstorm", "explore this idea", or wants to go from idea to structured plan and roadmap.
46
48
  file-editor: Use when opening edited files in the user's IDE. Reads settings from .agent-settings.yml to determine IDE and whether auto-open is enabled.
47
49
  finishing-a-development-branch: Use when the feature is implementation-complete and the next step is 'ship it' — verifies, cleans up, and routes to merge/PR/park/discard — even when the user just says 'I'm done, what now?'.
48
- flux: Use when writing Laravel Flux UI components the official Livewire component library by the Laravel team. Covers components, slots, and variants.
50
+ flux: Stack-implementation skill for Laravel Flux dispatched by `directives/ui/apply.py` (and `review.py` / `polish.py`) when the project uses `livewire/flux`. Covers Flux components, slots, variants, and form primitives.
49
51
  git-workflow: Use when working with Git — branch naming, commit messages, PR creation, rebasing, or the code review process — even when the user says 'push this' or 'merge the branch' without naming Git.
50
52
  github-ci: Use when working with GitHub Actions — workflow YAML, quality gates, test matrices, deployment triggers, reusable workflows — even when the user just says 'my CI is failing' or 'add a check'.
51
53
  grafana: Use when working with Grafana — dashboards, Loki LogQL queries, alerting rules, monitoring panels — even when the user just says 'build me a dashboard' or 'query the logs' without naming Grafana.
52
- guideline-writing: Use when creating or editing a guideline in .agent-src.uncompressed/guidelines/ — reference material cited by skills, no auto-triggers — even when the user just says 'write up our naming conventions'.
54
+ guideline-writing: Use when creating or editing a guideline in docs/guidelines/ — reference material cited by skills, no auto-triggers — even when the user just says 'write up our naming conventions'.
53
55
  jira-integration: Use when the user says "check Jira", "create ticket", "update issue", or needs JQL queries, ticket transitions, or branch-to-ticket linking.
54
56
  jobs-events: Use when creating Laravel jobs, queued workflows, events, or listeners. Covers clear responsibilities, safe serialization, and retry/failure handling.
55
57
  judge-bug-hunter: Use when a diff needs correctness review — null-safety, edge cases, off-by-one, races, error handling — dispatched by /review-changes, /do-and-judge, /judge, even without 'judge'.
@@ -68,9 +70,10 @@ laravel-scheduling: Use when configuring Laravel task scheduling — cron expres
68
70
  laravel-validation: Use when writing validation — Form Requests, rules, custom rule objects, request-boundary design — even when the user just says 'validate this input' or 'check the request' without naming it.
69
71
  learning-to-rule-or-skill: Use when a repeated learning, mistake, or successful pattern should be turned into a new rule or skill. Also use after completing a task to capture learnings from the work.
70
72
  lint-skills: Use when running the package's skill linter against all skills and rules to validate frontmatter, required sections, and execution metadata.
71
- livewire: Use when writing Livewire components reactive state, events, lifecycle hooks, and clean separation between component logic and Blade templates.
73
+ livewire: Stack-implementation skill for Livewire — dispatched by `directives/ui/apply.py` (and `review.py` / `polish.py`) when the project's frontend stack is Livewire. Covers reactive state, events, lifecycle hooks, and component/view separation.
72
74
  logging-monitoring: Use when working with logging or monitoring — Sentry error tracking, Grafana/Loki log aggregation, structured logging channels, or monitoring helpers.
73
75
  mcp: Use when working with MCP (Model Context Protocol) servers — their tools, capabilities, and best practices for effective agent workflows.
76
+ md-language-check: Use BEFORE saving any .md under .augment/, .agent-src*/, or agents/ — scans umlauts, German function words, and quoted German phrases outside DE:/EN: anchor blocks. Hard gate per language-and-tone.
74
77
  merge-conflicts: Use when the user has merge conflicts or says "resolve conflicts". Understands conflict markers, resolution strategies, and verification workflow.
75
78
  migration-creator: Use when the user says "create migration", "add column", or "new table". Creates migrations with correct table prefixes, column naming, and multi-tenant awareness.
76
79
  module-management: Use when the user says "create module", "explore module", or works within app/Modules/. Understands module structure, auto-loading, route registration, and namespace conventions.
@@ -95,10 +98,13 @@ project-analysis-zend-laminas: Use for deep Zend Framework or Laminas project an
95
98
  project-analyzer: ONLY when user explicitly requests: full project analysis, tech stack detection, or structured analysis documents for agents/analysis/. NOT for regular feature work.
96
99
  project-docs: Use when looking for project-specific documentation. Knows which docs exist in agents/docs/ and agents/contexts/ and maps work areas to relevant docs.
97
100
  quality-tools: Use when PHPStan, Rector, or ECS output appears — \"phpstan says mixed\", type errors, \"fix code style\", \"run rector\" — even when Eloquent/Laravel/model code is also mentioned.
101
+ react-shadcn-ui: Use when building React UI on shadcn/ui primitives + Tailwind — the apply/review/polish skill dispatched by `directives/ui/*` for the `react-shadcn` stack.
98
102
  readme-reviewer: Use when reviewing a README for accuracy, usability, and alignment with the actual repository. Detects invented content, broken setup steps, and structural issues.
99
103
  readme-writing: Use when creating, rewriting, or significantly improving a README based on the actual repository structure, commands, and intended audience.
100
104
  readme-writing-package: Use when creating or rewriting a README for a reusable package or library. Focus on installability, minimal usage example, compatibility, and developer onboarding.
101
105
  receiving-code-review: Use when processing code review feedback (bot or human) before changing anything — triages, verifies, and pushes back with technical reasoning — even when the user just says 'fix the comments'.
106
+ "refine-prompt": Reconstruct a free-form prompt into actionable AC + assumptions + confidence band before the engine plans — '/work \"…\"', 'baue X', 'ist der Prompt klar genug für die Engine?'.
107
+ "refine-ticket": Refine a Jira/Linear ticket before planning — 'refine ticket', 'tighten AC on PROJ-123', 'ist das Ticket klar?' — rewritten ticket, Top-5 risks, persona voices, sub-skills orchestrated, close-prompt.
102
108
  requesting-code-review: Use when asking for a review or creating a PR — self-review first, frame the right context, test plan included — even when the user just says 'open a PR' or 'ready to merge'.
103
109
  review-routing: Use when preparing a PR description, suggesting reviewers, or flagging risk — produces owner-mapped roles plus historical bug-pattern matches from project-local YAML.
104
110
  roadmap-management: Use when the user says "create roadmap", "show roadmap", or "execute roadmap". Creates, reads, and manages roadmap files with phase tracking.
@@ -113,7 +119,7 @@ skill-management: Use when compressing, decompressing, refactoring, or improving
113
119
  skill-reviewer: Use when reviewing, auditing, or optimizing skills — validates against the 7 Skill Killers checklist and produces fix recommendations.
114
120
  skill-writing: Use when deciding 'should this be a skill or a rule?', creating/improving/reviewing agent skills, SKILL.md frontmatter, or procedure sections — even without saying 'skill-writing'.
115
121
  sql-writing: Use when writing raw SQL — MariaDB/MySQL syntax, parameterization, raw migrations, seeders with `DB::statement` — even when the user just pastes a query and asks 'why is this slow' without naming SQL.
116
- subagent-orchestration: Use when orchestrating implementer/judge subagents — five modes (do-and-judge, do-in-steps, do-in-parallel, do-competitively, judge-with-debate) — model pairing and parallelism from .agent-settings.yml.
122
+ subagent-orchestration: Use when orchestrating implementer/judge subagents — five modes (do-and-judge, do-in-steps, do-in-parallel, do-competitively, judge-with-debate) — models from .agent-settings.yml.
117
123
  systematic-debugging: Use when hitting a bug, test failure, crash, or unexpected behavior — enforces reproduce → isolate → hypothesize → verify before any fix — even when the user just says 'this is broken' or 'quick fix'.
118
124
  technical-specification: Use when the user says "write a spec", "create RFC", or "document this decision". Writes technical specifications, RFCs, and ADRs with clear structure.
119
125
  terraform: Use when writing Terraform — AWS modules, resources, variables, outputs, remote state — even when the user just says 'provision this infra' or 'add an S3 bucket' without naming Terraform.
@@ -126,5 +132,5 @@ universal-project-analysis: ONLY when user explicitly requests: full project ana
126
132
  upstream-contribute: Use when a learning, new skill, rule improvement, or bug fix from a consumer project should be contributed back to the shared agent-config package.
127
133
  using-git-worktrees: Use when starting parallel work in isolation from the current branch — spawn a git worktree with ignore-safety checks and a clean test baseline — even when the user says 'try this on the side'.
128
134
  "validate-feature-fit": Validate whether a feature request fits the existing codebase — check for duplicates, contradictions, scope creep, and architectural misfit
129
- verify-before-complete: Use when claiming 'done', suggesting a commit, push, or PR — runs the evidence gate so completion claims come from fresh output in this message, not memory or earlier runs.
135
+ verify-completion-evidence: Use when claiming 'done', suggesting a commit, push, or PR — runs the evidence gate so completion claims come from fresh output in this message, not memory or earlier runs.
130
136
  websocket: Use when building real-time features — WebSocket broadcasting, live updates, presence channels, connection state — even when the user just says 'push this to the client live'.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@event4u/agent-config",
3
- "version": "1.15.0",
3
+ "version": "1.16.0",
4
4
  "description": "Shared agent configuration \u2014 skills, rules, commands, guidelines, and templates for AI coding tools",
5
5
  "license": "MIT",
6
6
  "private": false,
@@ -48,6 +48,10 @@ Commands:
48
48
  roadmap:progress-check Fail if agents/roadmaps-progress.md is stale (for CI)
49
49
  hooks:install Install the pre-commit roadmap-progress hook
50
50
  (use --print to dump it, --force to overwrite an existing hook)
51
+ keys:install-anthropic Install the Anthropic API key for the AI Council
52
+ (interactive, /dev/tty only, writes ~/.config/agent-config/anthropic.key 0600)
53
+ keys:install-openai Install the OpenAI API key for the AI Council
54
+ (interactive, /dev/tty only, writes ~/.config/agent-config/openai.key 0600)
51
55
  first-run Guided first-run setup — cost profile, settings, tooling
52
56
  implement-ticket Drive the work_engine Python engine on a ticket envelope
53
57
  (Option-A loop; called by the /implement-ticket command)
@@ -78,6 +82,8 @@ Examples:
78
82
  ./agent-config mcp:check
79
83
  ./agent-config roadmap:progress
80
84
  ./agent-config hooks:install
85
+ ./agent-config keys:install-anthropic
86
+ ./agent-config keys:install-openai
81
87
  ./agent-config first-run
82
88
  ./agent-config implement-ticket --state-file .work-state.json
83
89
  ./agent-config work --state-file .work-state.json --prompt-file prompt.txt
@@ -390,6 +396,21 @@ HELP
390
396
  echo " To uninstall: rm $target"
391
397
  }
392
398
 
399
+ # Wrap the interactive key installers under a stable CLI entry. The shell
400
+ # scripts themselves enforce /dev/tty, 0600, and atomic write — this is
401
+ # pure routing so consumers never have to know the package layout.
402
+ cmd_keys_install_anthropic() {
403
+ local script
404
+ script="$(resolve_script "scripts/install_anthropic_key.sh")" || return 1
405
+ exec bash "$script" "$@"
406
+ }
407
+
408
+ cmd_keys_install_openai() {
409
+ local script
410
+ script="$(resolve_script "scripts/install_openai_key.sh")" || return 1
411
+ exec bash "$script" "$@"
412
+ }
413
+
393
414
  main() {
394
415
  local cmd="${1-}"
395
416
  [[ $# -gt 0 ]] && shift || true
@@ -400,6 +421,8 @@ main() {
400
421
  roadmap:progress) cmd_roadmap_progress "$@" ;;
401
422
  roadmap:progress-check) cmd_roadmap_progress_check "$@" ;;
402
423
  hooks:install) cmd_hooks_install "$@" ;;
424
+ keys:install-anthropic) cmd_keys_install_anthropic "$@" ;;
425
+ keys:install-openai) cmd_keys_install_openai "$@" ;;
403
426
  first-run) cmd_first_run "$@" ;;
404
427
  implement-ticket) cmd_implement_ticket "$@" ;;
405
428
  work) cmd_work "$@" ;;
@@ -0,0 +1,39 @@
1
+ """ai_council — external-AI consultation module.
2
+
3
+ The host agent uses this to poll independent models (OpenAI, Anthropic)
4
+ for second opinions on roadmaps, diffs, free-form prompts, or file sets.
5
+ Council members never see the host agent's reasoning — only the artefact
6
+ plus a neutral system prompt asking for an independent critique.
7
+
8
+ Architecture:
9
+ clients.py — ExternalAIClient ABC + concrete OpenAI/Anthropic
10
+ impls + 0600 key loaders (no env-var fallback).
11
+ bundler.py — Context bundling with redaction + size guard.
12
+ orchestrator.py — Parallel fan-out, error normalisation, cost cap.
13
+ prompts.py — Neutrality system-prompt templates per input mode.
14
+
15
+ Trust boundary: this module makes networked, paid calls. Tokens come
16
+ exclusively from ~/.config/agent-config/<provider>.key (mode 0600). The
17
+ module never edits files, never opens PRs, never merges — output is
18
+ text only, advisory.
19
+ """
20
+
21
+ from scripts.ai_council.clients import (
22
+ AnthropicClient,
23
+ CouncilResponse,
24
+ ExternalAIClient,
25
+ KeyGateError,
26
+ OpenAIClient,
27
+ load_anthropic_key,
28
+ load_openai_key,
29
+ )
30
+
31
+ __all__ = [
32
+ "AnthropicClient",
33
+ "CouncilResponse",
34
+ "ExternalAIClient",
35
+ "KeyGateError",
36
+ "OpenAIClient",
37
+ "load_anthropic_key",
38
+ "load_openai_key",
39
+ ]
@@ -0,0 +1,41 @@
1
+ """Shipped baseline prices for the AI Council.
2
+
3
+ This file is the bootstrap source for `.agent-prices.md` when the
4
+ runtime file is missing. It is also the network-fallback source for
5
+ `scripts/update_prices.py` when the upstream feed (LiteLLM) is
6
+ unreachable.
7
+
8
+ Prices are USD per **1 000 000** tokens. Models are identified by the
9
+ exact `model:` string the user puts into `.agent-settings.yml`.
10
+
11
+ Numbers below are a hand-curated snapshot — they will drift. The
12
+ runtime never reads them directly once `.agent-prices.md` exists; the
13
+ weekly refresh and user edits are the live source of truth.
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ # YYYY-MM-DD of when this table was last hand-edited. Keep in sync with
19
+ # the test_default_prices freshness assertion if you bump this.
20
+ LAST_UPDATED = "2026-04-29"
21
+
22
+ # (provider, model) -> (input_per_1m_usd, output_per_1m_usd)
23
+ DEFAULT_PRICES: dict[tuple[str, str], tuple[float, float]] = {
24
+ # ── Anthropic ────────────────────────────────────────────────────
25
+ ("anthropic", "claude-sonnet-4-5"): (3.00, 15.00),
26
+ ("anthropic", "claude-opus-4-1"): (15.00, 75.00),
27
+ ("anthropic", "claude-haiku-4-5"): (1.00, 5.00),
28
+ # ── OpenAI ───────────────────────────────────────────────────────
29
+ ("openai", "gpt-4o"): (2.50, 10.00),
30
+ ("openai", "gpt-4o-mini"): (0.15, 0.60),
31
+ ("openai", "o1"): (15.00, 60.00),
32
+ ("openai", "o3-mini"): (1.10, 4.40),
33
+ }
34
+
35
+
36
+ def as_rows() -> list[tuple[str, str, float, float]]:
37
+ """Return the table sorted (provider, model) for stable Markdown output."""
38
+ return [
39
+ (provider, model, prices[0], prices[1])
40
+ for (provider, model), prices in sorted(DEFAULT_PRICES.items())
41
+ ]
@@ -0,0 +1,149 @@
1
+ """One-off council consultation — Phase 0 audit findings on rebalancing roadmap.
2
+
3
+ Validates whether the rebalancing roadmap premise still holds against the
4
+ actual PR #34 diff. Transient script; can be deleted after the consult runs.
5
+
6
+ Invocation:
7
+ .venv/bin/python -m scripts.ai_council._one_off_rebalancing_audit
8
+ """
9
+ from __future__ import annotations
10
+
11
+ import sys
12
+ from pathlib import Path
13
+
14
+ from scripts.ai_council.clients import AnthropicClient, load_anthropic_key
15
+ from scripts.ai_council.orchestrator import CostBudget, CouncilQuestion, consult, estimate
16
+ from scripts.ai_council.pricing import estimate_cost, load_prices
17
+ from scripts.ai_council.project_context import detect_project_context
18
+
19
+ REPO_ROOT = Path(__file__).resolve().parents[2]
20
+
21
+ ORIGINAL_ASK = (
22
+ "Phase 0 audit of the rebalancing roadmap. The roadmap was written based on "
23
+ "five rounds of external review claiming PR #34 over-deleted implicit "
24
+ "expertise. Validate whether the premise still holds against the actual diff. "
25
+ "Three concrete questions: (1) is the deletion narrative supported by the "
26
+ "numbers? (2) which of the 6 phases are already done or moot? "
27
+ "(3) what is the actual minimum-viable scope still worth executing?"
28
+ )
29
+
30
+ ARTEFACT = """# Phase 0 audit findings - road-to-rebalancing.md
31
+
32
+ ## Premise from the roadmap
33
+
34
+ The risk surface is whether implicit expertise (edge cases, decision forks,
35
+ failure modes, anti-patterns) was trimmed alongside the redundancy.
36
+ Rebalancing means restoring intelligence without re-inflating Always-rules.
37
+
38
+ ## Actual numbers from the PR #34 diff
39
+
40
+ Scope: git diff origin/main...HEAD, path .agent-src.uncompressed/rules/
41
+
42
+ - 35 files changed: 202 insertions, 204 deletions => net -2 lines total.
43
+ - Largest delta: language-and-tone.md 37 ins / 96 del. The 96 lines were
44
+ EXTRACTED to docs/guidelines/language-and-tone-examples.md (79 lines),
45
+ not deleted. Net knowledge loss: ~17 lines of duplicated phrasing.
46
+ - Second-largest: roadmap-progress-sync.md 26 ins / 33 del - minor.
47
+ - All other rules: <=8 line changes each, mostly renames.
48
+ - ZERO rule files deleted outright.
49
+
50
+ ## Phase-by-phase realities
51
+
52
+ ### Phase 0 - Removed-Knowledge Audit
53
+ This document IS Phase 0. Findings: 80% redundancy, examples extracted to
54
+ safe layer, zero decision-logic deletions. Phase 0 is now COMPLETE.
55
+
56
+ ### Phase 1 - Pilot Context Split (3 rules)
57
+ Candidates: autonomous-execution, minimal-safe-diff, scope-control.
58
+ - autonomous-execution: 8 line delta in PR, no extraction yet.
59
+ - minimal-safe-diff: already auto-trigger; 6 line delta.
60
+ - scope-control: 40 line ADDITION (not deletion) in this PR. Pilot value low.
61
+
62
+ ### Phase 2 - load_context: convention + linter
63
+ 0 rules use load_context: today. Convention does not exist.
64
+ Genuinely net-new work.
65
+
66
+ ### Phase 3 - Guidelines domain folders
67
+ Already done. 47 guidelines, 46 already in domain folders
68
+ (agent-infra/, docs/, e2e/, php/). Only language-and-tone-examples.md
69
+ is flat at root. Phase 3 reduces to deciding where the one flat file goes.
70
+
71
+ ### Phase 4 - Golden-Transcript-backed demos under examples/flows/
72
+ Partially shipped. docs/end-to-end-walkthroughs.md (built last cycle)
73
+ already does this with 4 traces anchored to GT-1, GT-P1, GT-U2, GT-2.
74
+ Required cases per roadmap: implement-ticket-demo, work-freeform-demo,
75
+ ui-track-demo, blocked-path-demo (all covered) plus mixed-flow-demo
76
+ (NOT covered yet).
77
+ Net-new: 1 mixed-flow demo + folder move from docs/ to examples/flows/.
78
+
79
+ ### Phase 5 - Rule priority hierarchy + interaction matrix
80
+ Partially shipped. docs/contracts/rule-interactions.yml and
81
+ rule-interactions.md exist (13 pairs across 9 rules).
82
+ rule-priority-hierarchy.md does NOT exist.
83
+
84
+ ### Phase 6 - Senior-agent behavior tests
85
+ Not started. Net-new work, but only valuable if Phase 1 actually runs
86
+ and produces something to validate.
87
+
88
+ ## Question to council
89
+
90
+ Given:
91
+ - The deletion narrative is empirically thin (-2 net lines after extraction).
92
+ - 3 of 6 phases are already done or near-done.
93
+ - Phase 1 pilot value is questionable on the 3 named candidates.
94
+
95
+ Should this roadmap:
96
+
97
+ (A) Close out as substantially-already-done. Execute only the small delta
98
+ (1 mixed-flow demo, move walkthroughs to examples/flows/, add the
99
+ priority-hierarchy doc, add load_context: convention if cheap),
100
+ then archive.
101
+
102
+ (B) Drop Phase 1 pilot - the three named candidates don't show evidence of
103
+ over-deletion. Execute Phase 2 + Phase 4 (1 missing demo + restructure)
104
+ + Phase 5 (priority-hierarchy doc) only.
105
+
106
+ (C) Original full scope - assume the audit missed something subtle and run
107
+ all 6 phases as written.
108
+
109
+ (D) Other framing - propose a tighter scope based on what the audit shows.
110
+
111
+ Identify blind spots: did the diff miss content moves between branches?
112
+ Are there rules whose implicit expertise lives in non-line-count signal?
113
+ Recommend (A/B/C/D) with rationale.
114
+ """
115
+
116
+
117
+ def main() -> int:
118
+ api_key = load_anthropic_key()
119
+ client = AnthropicClient(api_key=api_key)
120
+ project = detect_project_context(REPO_ROOT)
121
+ table = load_prices()
122
+
123
+ question = CouncilQuestion(mode="roadmap", user_prompt=ARTEFACT, max_tokens=2048)
124
+ estimates = estimate(question, [client], table, project=project, original_ask=ORIGINAL_ASK)
125
+ print(f"[estimate] ~{estimates[0].input_tokens} in + {estimates[0].output_tokens} out = ${estimates[0].total_usd:.4f}")
126
+
127
+ budget = CostBudget(
128
+ max_input_tokens=50_000, max_output_tokens=20_000,
129
+ max_calls=5, max_total_usd=0.50,
130
+ )
131
+
132
+ print(f"[consult] calling {client.name}/{client.model} ...")
133
+ responses = consult([client], question, budget, table=table, project=project, original_ask=ORIGINAL_ASK)
134
+ if not responses or responses[0].error:
135
+ err = responses[0].error if responses else "no response"
136
+ print(f"[error] {err}", file=sys.stderr)
137
+ return 1
138
+
139
+ r = responses[0]
140
+ actual = estimate_cost(r.provider, r.model, r.input_tokens, r.output_tokens, table)
141
+ print(f"[done] {r.input_tokens} in / {r.output_tokens} out, {r.latency_ms} ms, actual ${actual.total_usd:.4f}")
142
+ print("=" * 72)
143
+ print(r.text)
144
+ print("=" * 72)
145
+ return 0
146
+
147
+
148
+ if __name__ == "__main__":
149
+ raise SystemExit(main())
@@ -0,0 +1,106 @@
1
+ """One-off Phase-1 round-trip runner.
2
+
3
+ Used exactly once to generate the evidence artefact required to lift
4
+ the capture-only fence on `road-to-ai-council.md` Phase 2+ and the
5
+ end-to-end verification on `road-to-council-modes.md` Phase 2a.
6
+
7
+ Not part of the public CLI surface — `/council` remains the supported
8
+ entry point. This script is committed under `scripts/ai_council/` so
9
+ the evidence is reproducible from the git history alone.
10
+
11
+ Invocation:
12
+ .venv/bin/python -m scripts.ai_council._one_off_roundtrip
13
+ """
14
+ from __future__ import annotations
15
+
16
+ import sys
17
+ from pathlib import Path
18
+
19
+ from scripts.ai_council.bundler import bundle_roadmap
20
+ from scripts.ai_council.clients import AnthropicClient, load_anthropic_key
21
+ from scripts.ai_council.orchestrator import (
22
+ CostBudget,
23
+ CouncilQuestion,
24
+ consult,
25
+ estimate,
26
+ )
27
+ from scripts.ai_council.pricing import estimate_cost, load_prices
28
+ from scripts.ai_council.project_context import detect_project_context
29
+ from scripts.ai_council.session import SessionManifest, save as save_session
30
+
31
+ REPO_ROOT = Path(__file__).resolve().parents[2]
32
+ ROADMAP_PATH = REPO_ROOT / "agents/roadmaps/road-to-council-modes.md"
33
+
34
+ ORIGINAL_ASK = (
35
+ "Bitte review die folgende Roadmap (council-modes Phase 2c "
36
+ "Playwright). Die Maintainer-Recommendations für Q1-Q5 sind im "
37
+ "Block 'Decisions Required' bereits hinterlegt. Frage: sollten "
38
+ "wir die Recommendations annehmen wie sie sind, oder gibt es "
39
+ "blinde Flecken die wir vor dem Lift der capture-only fence "
40
+ "kläeren sollten?"
41
+ )
42
+
43
+
44
+ def main() -> int:
45
+ api_key = load_anthropic_key()
46
+ client = AnthropicClient(api_key=api_key)
47
+
48
+ context = bundle_roadmap(ROADMAP_PATH)
49
+ project = detect_project_context(REPO_ROOT)
50
+ table = load_prices()
51
+
52
+ question = CouncilQuestion(
53
+ mode="roadmap",
54
+ user_prompt=context.text,
55
+ max_tokens=2048,
56
+ )
57
+
58
+ estimates = estimate(
59
+ question, [client], table,
60
+ project=project, original_ask=ORIGINAL_ASK,
61
+ )
62
+ print(f"[estimate] {client.name}/{client.model}: "
63
+ f"~{estimates[0].input_tokens} in + {estimates[0].output_tokens} out "
64
+ f"= ${estimates[0].total_usd:.4f}")
65
+
66
+ budget = CostBudget(
67
+ max_input_tokens=50_000,
68
+ max_output_tokens=20_000,
69
+ max_calls=10,
70
+ max_total_usd=0.50,
71
+ )
72
+
73
+ print(f"[consult] calling {client.name}/{client.model} ...")
74
+ responses = consult(
75
+ [client], question, budget,
76
+ table=table, project=project, original_ask=ORIGINAL_ASK,
77
+ )
78
+
79
+ if not responses or responses[0].error:
80
+ err = responses[0].error if responses else "no response"
81
+ print(f"[error] {err}", file=sys.stderr)
82
+ return 1
83
+
84
+ r = responses[0]
85
+ actual = estimate_cost(r.provider, r.model, r.input_tokens, r.output_tokens, table)
86
+ actual_usd = actual.total_usd
87
+ print(f"[done] tokens: {r.input_tokens} in / {r.output_tokens} out · "
88
+ f"latency: {r.latency_ms} ms · actual ${actual_usd:.4f}")
89
+
90
+ manifest = SessionManifest(
91
+ mode="roadmap",
92
+ artefact=str(ROADMAP_PATH.relative_to(REPO_ROOT)),
93
+ original_ask=ORIGINAL_ASK,
94
+ members=[f"{r.provider}/{r.model}"],
95
+ rounds=1,
96
+ cost_usd_estimated=estimates[0].total_usd,
97
+ cost_usd_actual=actual_usd,
98
+ extra={"purpose": "Phase 1 ai-council round-trip + Phase 2a council-modes E2E evidence"},
99
+ )
100
+ session_dir = save_session(manifest=manifest, responses=responses)
101
+ print(f"[saved] {session_dir.relative_to(REPO_ROOT)}/")
102
+ return 0
103
+
104
+
105
+ if __name__ == "__main__":
106
+ raise SystemExit(main())
@@ -0,0 +1,172 @@
1
+ """Per-day rolling cost-budget guard for the council (D3).
2
+
3
+ Adds a 24h-rolling-window USD limit on top of the per-session caps in
4
+ `orchestrator.CostBudget`. Persists a small JSONL ledger in
5
+ ``~/.config/agent-config/council-spend.jsonl`` (mode 0600, same
6
+ permission discipline as the API keys).
7
+
8
+ Contract
9
+ - The ledger is **append-only**. Each line is ``{"ts": ISO-8601 UTC,
10
+ "usd": float, "provider": str, "model": str}``.
11
+ - ``today_spend_usd()`` sums entries within the last 24h from "now"
12
+ (true rolling window — not midnight UTC, never resets at boundary
13
+ surprise).
14
+ - ``would_exceed(limit_usd, next_call_usd)`` returns True iff the next
15
+ call would push the rolling window past the limit.
16
+ - ``record_spend(usd, provider, model)`` appends a single entry; never
17
+ raises on disk failure (logs to stderr, returns False).
18
+
19
+ The guard is **advisory** to the orchestrator: it provides a check
20
+ function the host agent can call before each council member; the
21
+ orchestrator's per-session cost gate stays the primary defence.
22
+ """
23
+
24
+ from __future__ import annotations
25
+
26
+ import datetime as _dt
27
+ import json
28
+ import os
29
+ import stat
30
+ import sys
31
+ from dataclasses import dataclass
32
+ from pathlib import Path
33
+
34
+ LEDGER_PATH = Path.home() / ".config" / "agent-config" / "council-spend.jsonl"
35
+ ROLLING_WINDOW_HOURS = 24
36
+
37
+
38
+ @dataclass
39
+ class SpendEntry:
40
+ ts: _dt.datetime # UTC, tz-aware
41
+ usd: float
42
+ provider: str
43
+ model: str
44
+
45
+
46
+ def _now_utc() -> _dt.datetime:
47
+ return _dt.datetime.now(_dt.timezone.utc)
48
+
49
+
50
+ def _ensure_ledger_dir(path: Path) -> bool:
51
+ """Create the ledger's parent directory mode 0700 if missing."""
52
+ try:
53
+ path.parent.mkdir(parents=True, exist_ok=True)
54
+ if (path.parent.stat().st_mode & 0o777) != 0o700:
55
+ try:
56
+ os.chmod(path.parent, 0o700)
57
+ except OSError:
58
+ # On macOS ~/.config may inherit umask perms; do not block.
59
+ pass
60
+ return True
61
+ except OSError as exc: # noqa: BLE001 - never block the orchestrator
62
+ print(f"[council:budget_guard] mkdir failed: {exc}", file=sys.stderr)
63
+ return False
64
+
65
+
66
+ def _ensure_ledger_file_mode(path: Path) -> None:
67
+ """Make sure an existing ledger file is mode 0600. Best-effort."""
68
+ if not path.exists():
69
+ return
70
+ current = path.stat().st_mode & 0o777
71
+ if current != 0o600:
72
+ try:
73
+ os.chmod(path, 0o600)
74
+ except OSError:
75
+ pass
76
+
77
+
78
+ def _parse_iso(ts: str) -> _dt.datetime | None:
79
+ try:
80
+ # `fromisoformat` accepts "+00:00"; we always write with "+00:00".
81
+ return _dt.datetime.fromisoformat(ts)
82
+ except ValueError:
83
+ return None
84
+
85
+
86
+ def read_entries(path: Path | None = None) -> list[SpendEntry]:
87
+ """Read every well-formed entry from the ledger.
88
+
89
+ Malformed lines are skipped silently. Empty/missing ledger → [].
90
+ """
91
+ p = path or LEDGER_PATH
92
+ if not p.exists():
93
+ return []
94
+ out: list[SpendEntry] = []
95
+ for line in p.read_text(encoding="utf-8").splitlines():
96
+ line = line.strip()
97
+ if not line:
98
+ continue
99
+ try:
100
+ obj = json.loads(line)
101
+ except json.JSONDecodeError:
102
+ continue
103
+ ts = _parse_iso(str(obj.get("ts", "")))
104
+ if ts is None:
105
+ continue
106
+ try:
107
+ usd = float(obj.get("usd", 0))
108
+ except (TypeError, ValueError):
109
+ continue
110
+ out.append(SpendEntry(
111
+ ts=ts, usd=usd,
112
+ provider=str(obj.get("provider", "")),
113
+ model=str(obj.get("model", "")),
114
+ ))
115
+ return out
116
+
117
+
118
+ def today_spend_usd(
119
+ *,
120
+ path: Path | None = None,
121
+ now: _dt.datetime | None = None,
122
+ window_hours: int = ROLLING_WINDOW_HOURS,
123
+ ) -> float:
124
+ """Sum of USD spent in the last `window_hours` (rolling window)."""
125
+ cutoff = (now or _now_utc()) - _dt.timedelta(hours=window_hours)
126
+ return sum(e.usd for e in read_entries(path) if e.ts >= cutoff)
127
+
128
+
129
+ def would_exceed(
130
+ limit_usd: float,
131
+ next_call_usd: float,
132
+ *,
133
+ path: Path | None = None,
134
+ now: _dt.datetime | None = None,
135
+ window_hours: int = ROLLING_WINDOW_HOURS,
136
+ ) -> bool:
137
+ """True iff appending `next_call_usd` would push the window past `limit_usd`.
138
+
139
+ `limit_usd <= 0` disables the guard (returns False). Mirrors the
140
+ `CostBudget.max_total_usd` convention.
141
+ """
142
+ if limit_usd <= 0:
143
+ return False
144
+ spent = today_spend_usd(path=path, now=now, window_hours=window_hours)
145
+ return (spent + next_call_usd) > limit_usd
146
+
147
+
148
+ def record_spend(
149
+ usd: float,
150
+ provider: str,
151
+ model: str,
152
+ *,
153
+ path: Path | None = None,
154
+ now: _dt.datetime | None = None,
155
+ ) -> bool:
156
+ """Append one entry to the ledger. Returns True on success."""
157
+ if usd <= 0:
158
+ return True # zero-cost calls (manual mode) skip the ledger
159
+ p = path or LEDGER_PATH
160
+ if not _ensure_ledger_dir(p):
161
+ return False
162
+ ts = (now or _now_utc()).isoformat()
163
+ entry = json.dumps({"ts": ts, "usd": round(usd, 6),
164
+ "provider": provider, "model": model}) + "\n"
165
+ try:
166
+ with p.open("a", encoding="utf-8") as fh:
167
+ fh.write(entry)
168
+ except OSError as exc: # noqa: BLE001 - never block the orchestrator
169
+ print(f"[council:budget_guard] write failed: {exc}", file=sys.stderr)
170
+ return False
171
+ _ensure_ledger_file_mode(p)
172
+ return True