agent-toolkit-cli 1.11.0__py3-none-win_amd64.whl

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 (438) hide show
  1. agent_toolkit/__init__.py +4 -0
  2. agent_toolkit/__main__.py +5 -0
  3. agent_toolkit/_paths.py +145 -0
  4. agent_toolkit/bin/.gitkeep +0 -0
  5. agent_toolkit/bin/agent-toolkit.exe +0 -0
  6. agent_toolkit/cli/__init__.py +0 -0
  7. agent_toolkit/cli/__main__.py +5 -0
  8. agent_toolkit/cli/build.py +278 -0
  9. agent_toolkit/cli/completion.py +297 -0
  10. agent_toolkit/cli/context_budget.py +455 -0
  11. agent_toolkit/cli/devcompanion.py +923 -0
  12. agent_toolkit/cli/devcompanion_queue.py +235 -0
  13. agent_toolkit/cli/diff.py +176 -0
  14. agent_toolkit/cli/doctor.py +1052 -0
  15. agent_toolkit/cli/insights.py +771 -0
  16. agent_toolkit/cli/install.py +706 -0
  17. agent_toolkit/cli/main.py +171 -0
  18. agent_toolkit/cli/mcp.py +537 -0
  19. agent_toolkit/cli/memory.py +739 -0
  20. agent_toolkit/cli/plugin.py +224 -0
  21. agent_toolkit/cli/project.py +459 -0
  22. agent_toolkit/cli/release.py +178 -0
  23. agent_toolkit/cli/skills.py +461 -0
  24. agent_toolkit/cli/uninstall.py +166 -0
  25. agent_toolkit/cli/update.py +298 -0
  26. agent_toolkit/cli/workspace.py +1713 -0
  27. agent_toolkit/compiler/hook_registry.py +110 -0
  28. agent_toolkit/compiler/loader.py +338 -0
  29. agent_toolkit/compiler/mcp_registry.py +87 -0
  30. agent_toolkit/compiler/model.py +182 -0
  31. agent_toolkit/compiler/provenance.py +140 -0
  32. agent_toolkit/compiler/registry_emit.py +263 -0
  33. agent_toolkit/compiler/target_registry.py +122 -0
  34. agent_toolkit/compiler/targets/__init__.py +1 -0
  35. agent_toolkit/compiler/targets/agent_plugins.py +160 -0
  36. agent_toolkit/compiler/targets/base.py +229 -0
  37. agent_toolkit/compiler/targets/claude_code.py +241 -0
  38. agent_toolkit/compiler/targets/codex.py +261 -0
  39. agent_toolkit/compiler/targets/copilot.py +275 -0
  40. agent_toolkit/compiler/targets/cursor.py +207 -0
  41. agent_toolkit/compiler/targets/gemini_cli.py +213 -0
  42. agent_toolkit/compiler/targets/muse_code.py +113 -0
  43. agent_toolkit/compiler/targets/opencode.py +204 -0
  44. agent_toolkit/compiler/targets/pi.py +263 -0
  45. agent_toolkit/compiler/targets/windsurf.py +267 -0
  46. agent_toolkit/compiler/tool_mapping.py +83 -0
  47. agent_toolkit/data/.gitignore +4 -0
  48. agent_toolkit/data/__init__.py +1 -0
  49. agent_toolkit/data/agents/agentic-security-reviewer/AGENT.md +60 -0
  50. agent_toolkit/data/agents/architect/AGENT.md +30 -0
  51. agent_toolkit/data/agents/assistant/AGENT.md +59 -0
  52. agent_toolkit/data/agents/build-error-resolver/AGENT.md +30 -0
  53. agent_toolkit/data/agents/client-workflow-bootstrap/AGENT.md +60 -0
  54. agent_toolkit/data/agents/code-reviewer/AGENT.md +49 -0
  55. agent_toolkit/data/agents/database-reviewer/AGENT.md +44 -0
  56. agent_toolkit/data/agents/docs-lookup/AGENT.md +28 -0
  57. agent_toolkit/data/agents/e2e-runner/AGENT.md +38 -0
  58. agent_toolkit/data/agents/performance-optimizer/AGENT.md +52 -0
  59. agent_toolkit/data/agents/planner/AGENT.md +41 -0
  60. agent_toolkit/data/agents/refactor-cleaner/AGENT.md +43 -0
  61. agent_toolkit/data/agents/reference-lookup/AGENT.md +33 -0
  62. agent_toolkit/data/agents/security-reviewer/AGENT.md +47 -0
  63. agent_toolkit/data/agents/tdd-guide/AGENT.md +58 -0
  64. agent_toolkit/data/agents/tech-assistant/AGENT.md +11 -0
  65. agent_toolkit/data/agents/typescript-reviewer/AGENT.md +61 -0
  66. agent_toolkit/data/capabilities/hooks/README.md +6 -0
  67. agent_toolkit/data/capabilities/hooks/pre-commit-validate.yaml +26 -0
  68. agent_toolkit/data/capabilities/hooks/scripts/pre-commit-validate.sh +41 -0
  69. agent_toolkit/data/capabilities/hooks/scripts/session-start-context.sh +11 -0
  70. agent_toolkit/data/capabilities/hooks/session-start-context.yaml +23 -0
  71. agent_toolkit/data/capabilities/targets/registry.yaml +90 -0
  72. agent_toolkit/data/capabilities/upstream.lock +118 -0
  73. agent_toolkit/data/catalogs/agent-catalog.yaml +81 -0
  74. agent_toolkit/data/catalogs/loop-catalog.yaml +55 -0
  75. agent_toolkit/data/catalogs/skill-catalog.yaml +520 -0
  76. agent_toolkit/data/catalogs/skills-layout.json +391 -0
  77. agent_toolkit/data/distributions/products.yaml +190 -0
  78. agent_toolkit/data/distributions/targets/claude-code.yaml +47 -0
  79. agent_toolkit/data/distributions/targets/cursor.yaml +42 -0
  80. agent_toolkit/data/distributions/targets/opencode.yaml +55 -0
  81. agent_toolkit/data/loops/changelog-drafter/loop.yaml +40 -0
  82. agent_toolkit/data/loops/ci-sweeper/loop.yaml +53 -0
  83. agent_toolkit/data/loops/daily-triage/loop.yaml +47 -0
  84. agent_toolkit/data/loops/dep-sweeper/loop.yaml +46 -0
  85. agent_toolkit/data/loops/issue-triage/loop.yaml +40 -0
  86. agent_toolkit/data/loops/oss-daily-briefing/loop.yaml +65 -0
  87. agent_toolkit/data/loops/oss-pr-monitor/loop.yaml +86 -0
  88. agent_toolkit/data/loops/oss-triage/loop.yaml +67 -0
  89. agent_toolkit/data/loops/post-merge-cleanup/loop.yaml +44 -0
  90. agent_toolkit/data/loops/pr-babysitter/loop.yaml +46 -0
  91. agent_toolkit/data/mcp/registry/chrome-devtools.yaml +61 -0
  92. agent_toolkit/data/mcp/registry/clickup.yaml +48 -0
  93. agent_toolkit/data/mcp/registry/figma.yaml +44 -0
  94. agent_toolkit/data/mcp/registry/github.yaml +52 -0
  95. agent_toolkit/data/mcp/registry/linear.yaml +47 -0
  96. agent_toolkit/data/mcp/registry/notion.yaml +48 -0
  97. agent_toolkit/data/mcp/registry/slack.yaml +46 -0
  98. agent_toolkit/data/mcp/templates/chrome-devtools/README.md +57 -0
  99. agent_toolkit/data/mcp/templates/chrome-devtools/config.template.json +6 -0
  100. agent_toolkit/data/mcp/templates/clickup/README.md +45 -0
  101. agent_toolkit/data/mcp/templates/clickup/config.template.json +8 -0
  102. agent_toolkit/data/mcp/templates/clickup/wrapper.sh +23 -0
  103. agent_toolkit/data/mcp/templates/figma/README.md +54 -0
  104. agent_toolkit/data/mcp/templates/figma/config.template.json +9 -0
  105. agent_toolkit/data/mcp/templates/github/README.md +16 -0
  106. agent_toolkit/data/mcp/templates/github/config.template.json +15 -0
  107. agent_toolkit/data/mcp/templates/github/wrapper.sh +6 -0
  108. agent_toolkit/data/mcp/templates/linear/README.md +49 -0
  109. agent_toolkit/data/mcp/templates/linear/config.template.json +10 -0
  110. agent_toolkit/data/mcp/templates/notion/README.md +7 -0
  111. agent_toolkit/data/mcp/templates/notion/config.template.json +8 -0
  112. agent_toolkit/data/mcp/templates/notion/wrapper.sh +23 -0
  113. agent_toolkit/data/mcp/templates/slack/README.md +8 -0
  114. agent_toolkit/data/mcp/templates/slack/config.template.json +9 -0
  115. agent_toolkit/data/mcp/templates/slack/wrapper.sh +23 -0
  116. agent_toolkit/data/packs/README.md +66 -0
  117. agent_toolkit/data/packs/agentic-security/README.md +19 -0
  118. agent_toolkit/data/packs/agentic-security/config.yaml +25 -0
  119. agent_toolkit/data/packs/architecture/README.md +22 -0
  120. agent_toolkit/data/packs/architecture/config.yaml +27 -0
  121. agent_toolkit/data/packs/code-quality/README.md +19 -0
  122. agent_toolkit/data/packs/code-quality/config.yaml +21 -0
  123. agent_toolkit/data/packs/delivery-discipline/README.md +19 -0
  124. agent_toolkit/data/packs/delivery-discipline/config.yaml +42 -0
  125. agent_toolkit/data/packs/design-engineering/README.md +19 -0
  126. agent_toolkit/data/packs/design-engineering/config.yaml +41 -0
  127. agent_toolkit/data/packs/engineering-workflow/README.md +21 -0
  128. agent_toolkit/data/packs/engineering-workflow/config.yaml +57 -0
  129. agent_toolkit/data/packs/oss-maintenance/README.md +245 -0
  130. agent_toolkit/data/packs/oss-maintenance/config.yaml +75 -0
  131. agent_toolkit/data/profiles/claude-code/CLAUDE.md +110 -0
  132. agent_toolkit/data/profiles/claude-code/agents/assistant.md +59 -0
  133. agent_toolkit/data/profiles/claude-code/agents/build-error-resolver.md +30 -0
  134. agent_toolkit/data/profiles/claude-code/agents/client-workflow-bootstrap.md +60 -0
  135. agent_toolkit/data/profiles/claude-code/agents/contribution-planner.md +39 -0
  136. agent_toolkit/data/profiles/claude-code/agents/database-reviewer.md +44 -0
  137. agent_toolkit/data/profiles/claude-code/agents/docs-lookup.md +28 -0
  138. agent_toolkit/data/profiles/claude-code/agents/e2e-runner.md +38 -0
  139. agent_toolkit/data/profiles/claude-code/agents/performance-optimizer.md +52 -0
  140. agent_toolkit/data/profiles/claude-code/agents/planner.md +41 -0
  141. agent_toolkit/data/profiles/claude-code/agents/refactor-cleaner.md +43 -0
  142. agent_toolkit/data/profiles/claude-code/agents/reference-lookup.md +33 -0
  143. agent_toolkit/data/profiles/claude-code/agents/security-reviewer.md +47 -0
  144. agent_toolkit/data/profiles/claude-code/agents/tdd-guide.md +58 -0
  145. agent_toolkit/data/profiles/claude-code/agents/typescript-reviewer.md +61 -0
  146. agent_toolkit/data/profiles/claude-code/settings.json +86 -0
  147. agent_toolkit/data/profiles/copilot/copilot-instructions.md +15 -0
  148. agent_toolkit/data/profiles/cursor/README.md +33 -0
  149. agent_toolkit/data/profiles/cursor/rules/architect.mdc +23 -0
  150. agent_toolkit/data/profiles/cursor/rules/assistant.mdc +61 -0
  151. agent_toolkit/data/profiles/cursor/rules/build-error-resolver.mdc +20 -0
  152. agent_toolkit/data/profiles/cursor/rules/code-reviewer.mdc +21 -0
  153. agent_toolkit/data/profiles/cursor/rules/database-reviewer.mdc +29 -0
  154. agent_toolkit/data/profiles/cursor/rules/docs-lookup.mdc +15 -0
  155. agent_toolkit/data/profiles/cursor/rules/e2e-runner.mdc +30 -0
  156. agent_toolkit/data/profiles/cursor/rules/performance-optimizer.mdc +27 -0
  157. agent_toolkit/data/profiles/cursor/rules/planner.mdc +28 -0
  158. agent_toolkit/data/profiles/cursor/rules/refactor-cleaner.mdc +26 -0
  159. agent_toolkit/data/profiles/cursor/rules/reference-lookup.mdc +20 -0
  160. agent_toolkit/data/profiles/cursor/rules/security-reviewer.mdc +20 -0
  161. agent_toolkit/data/profiles/cursor/rules/tdd-guide.mdc +26 -0
  162. agent_toolkit/data/profiles/cursor/rules/typescript-reviewer.mdc +28 -0
  163. agent_toolkit/data/profiles/muse-code/README.md +35 -0
  164. agent_toolkit/data/profiles/opencode/README.md +44 -0
  165. agent_toolkit/data/profiles/opencode/agents/architect.md +30 -0
  166. agent_toolkit/data/profiles/opencode/agents/assistant.md +96 -0
  167. agent_toolkit/data/profiles/opencode/agents/build-error-resolver.md +31 -0
  168. agent_toolkit/data/profiles/opencode/agents/client-workflow-bootstrap.md +66 -0
  169. agent_toolkit/data/profiles/opencode/agents/code-reviewer.md +47 -0
  170. agent_toolkit/data/profiles/opencode/agents/contribution-planner.md +44 -0
  171. agent_toolkit/data/profiles/opencode/agents/database-reviewer.md +43 -0
  172. agent_toolkit/data/profiles/opencode/agents/docs-lookup.md +28 -0
  173. agent_toolkit/data/profiles/opencode/agents/e2e-runner.md +33 -0
  174. agent_toolkit/data/profiles/opencode/agents/performance-optimizer.md +34 -0
  175. agent_toolkit/data/profiles/opencode/agents/planner.md +27 -0
  176. agent_toolkit/data/profiles/opencode/agents/refactor-cleaner.md +27 -0
  177. agent_toolkit/data/profiles/opencode/agents/reference-lookup.md +27 -0
  178. agent_toolkit/data/profiles/opencode/agents/security-reviewer.md +27 -0
  179. agent_toolkit/data/profiles/opencode/agents/tdd-guide.md +27 -0
  180. agent_toolkit/data/profiles/opencode/agents/typescript-reviewer.md +30 -0
  181. agent_toolkit/data/profiles/opencode/opencode.json +3 -0
  182. agent_toolkit/data/profiles/pi/skills/architect/skill.md +5 -0
  183. agent_toolkit/data/profiles/pi/skills/assistant/skill.md +3 -0
  184. agent_toolkit/data/profiles/pi/skills/code-reviewer/skill.md +5 -0
  185. agent_toolkit/data/profiles/pi/skills/planner/skill.md +3 -0
  186. agent_toolkit/data/profiles/pi/skills/security-reviewer/skill.md +3 -0
  187. agent_toolkit/data/profiles/windsurf/memories/global_rules.md +114 -0
  188. agent_toolkit/data/profiles/windsurf/rules/architect.mdc +23 -0
  189. agent_toolkit/data/profiles/windsurf/rules/assistant.mdc +61 -0
  190. agent_toolkit/data/profiles/windsurf/rules/build-error-resolver.mdc +20 -0
  191. agent_toolkit/data/profiles/windsurf/rules/code-reviewer.mdc +21 -0
  192. agent_toolkit/data/profiles/windsurf/rules/database-reviewer.mdc +29 -0
  193. agent_toolkit/data/profiles/windsurf/rules/docs-lookup.mdc +15 -0
  194. agent_toolkit/data/profiles/windsurf/rules/e2e-runner.mdc +30 -0
  195. agent_toolkit/data/profiles/windsurf/rules/performance-optimizer.mdc +27 -0
  196. agent_toolkit/data/profiles/windsurf/rules/planner.mdc +28 -0
  197. agent_toolkit/data/profiles/windsurf/rules/refactor-cleaner.mdc +26 -0
  198. agent_toolkit/data/profiles/windsurf/rules/reference-lookup.mdc +20 -0
  199. agent_toolkit/data/profiles/windsurf/rules/security-reviewer.mdc +20 -0
  200. agent_toolkit/data/profiles/windsurf/rules/tdd-guide.mdc +26 -0
  201. agent_toolkit/data/profiles/windsurf/rules/typescript-reviewer.mdc +28 -0
  202. agent_toolkit/data/skills/accessibility/review/SKILL.md +116 -0
  203. agent_toolkit/data/skills/accessibility/review/references/a11y-checklist.md +25 -0
  204. agent_toolkit/data/skills/accessibility/review/references/a11y-findings-template.md +56 -0
  205. agent_toolkit/data/skills/accessibility/review/references/research.md +47 -0
  206. agent_toolkit/data/skills/agentic-security/mcp-audit/SKILL.md +116 -0
  207. agent_toolkit/data/skills/agentic-security/mcp-audit/references/mcp-audit-template.md +51 -0
  208. agent_toolkit/data/skills/agentic-security/owasp-agentic-review/SKILL.md +73 -0
  209. agent_toolkit/data/skills/agentic-security/owasp-agentic-review/references/owasp-agentic-template.md +32 -0
  210. agent_toolkit/data/skills/agentic-security/supply-chain-audit/SKILL.md +142 -0
  211. agent_toolkit/data/skills/agentic-security/threat-modeling/SKILL.md +100 -0
  212. agent_toolkit/data/skills/agentic-security/threat-modeling/references/threat-model-template.md +55 -0
  213. agent_toolkit/data/skills/architecture/c4-model/SKILL.md +49 -0
  214. agent_toolkit/data/skills/cloud/aws-well-architected-review/SKILL.md +37 -0
  215. agent_toolkit/data/skills/cloud/cloud-design-patterns/SKILL.md +38 -0
  216. agent_toolkit/data/skills/core/assistant/SKILL.md +216 -0
  217. agent_toolkit/data/skills/core/assistant/references/AGENTS_TEMPLATE.md +0 -0
  218. agent_toolkit/data/skills/core/assistant/references/ORCHESTRATION.md +0 -0
  219. agent_toolkit/data/skills/core/assistant/references/REPO_INSPECTION.md +0 -0
  220. agent_toolkit/data/skills/core/dev-companion/SKILL.md +65 -0
  221. agent_toolkit/data/skills/core/dev-companion/references/LOOP_GUARDRAILS.md +0 -0
  222. agent_toolkit/data/skills/core/onboarding/SKILL.md +122 -0
  223. agent_toolkit/data/skills/core/output-handshake/SKILL.md +26 -0
  224. agent_toolkit/data/skills/core/pr-fallback/SKILL.md +35 -0
  225. agent_toolkit/data/skills/core/pr-fallback/references/pr-body-default.md +0 -0
  226. agent_toolkit/data/skills/core/project/SKILL.md +89 -0
  227. agent_toolkit/data/skills/core/workspace/SKILL.md +103 -0
  228. agent_toolkit/data/skills/core/workspace-knowledge-sync/SKILL.md +140 -0
  229. agent_toolkit/data/skills/data/dbt-validation/SKILL.md +41 -0
  230. agent_toolkit/data/skills/data/snowflake-validation/SKILL.md +38 -0
  231. agent_toolkit/data/skills/delivery/adr/SKILL.md +56 -0
  232. agent_toolkit/data/skills/delivery/adr/references/default-template.md +35 -0
  233. agent_toolkit/data/skills/delivery/adr/references/example-001-graphql-adoption.md +66 -0
  234. agent_toolkit/data/skills/delivery/agreement/SKILL.md +24 -0
  235. agent_toolkit/data/skills/delivery/agreement/references/default-template.md +17 -0
  236. agent_toolkit/data/skills/delivery/agreement/references/example-api-versioning.md +29 -0
  237. agent_toolkit/data/skills/delivery/bug/SKILL.md +26 -0
  238. agent_toolkit/data/skills/delivery/bug/references/default-template.md +26 -0
  239. agent_toolkit/data/skills/delivery/bug/references/example-search-filter-bug.md +58 -0
  240. agent_toolkit/data/skills/delivery/decision-log/SKILL.md +28 -0
  241. agent_toolkit/data/skills/delivery/decision-log/references/default-template.md +17 -0
  242. agent_toolkit/data/skills/delivery/decision-log/references/example-repository-pattern.md +27 -0
  243. agent_toolkit/data/skills/delivery/development-workflow/SKILL.md +34 -0
  244. agent_toolkit/data/skills/delivery/development-workflow/references/default-workflow-checklist.md +44 -0
  245. agent_toolkit/data/skills/delivery/epic/SKILL.md +22 -0
  246. agent_toolkit/data/skills/delivery/epic/references/default-template.md +26 -0
  247. agent_toolkit/data/skills/delivery/epic/references/example-unified-dashboard.md +40 -0
  248. agent_toolkit/data/skills/delivery/incident/SKILL.md +24 -0
  249. agent_toolkit/data/skills/delivery/incident/references/default-template.md +29 -0
  250. agent_toolkit/data/skills/delivery/incident/references/example-payment-outage.md +62 -0
  251. agent_toolkit/data/skills/delivery/management-unit-assessment/SKILL.md +104 -0
  252. agent_toolkit/data/skills/delivery/management-unit-assessment/references/default-template.md +24 -0
  253. agent_toolkit/data/skills/delivery/management-unit-assessment/references/example-team-assessment.md +126 -0
  254. agent_toolkit/data/skills/delivery/meeting-minutes/SKILL.md +25 -0
  255. agent_toolkit/data/skills/delivery/meeting-minutes/references/default-template.md +22 -0
  256. agent_toolkit/data/skills/delivery/meeting-minutes/references/example-weekly-sync.md +72 -0
  257. agent_toolkit/data/skills/delivery/planning/SKILL.md +31 -0
  258. agent_toolkit/data/skills/delivery/planning/references/default-template.md +25 -0
  259. agent_toolkit/data/skills/delivery/planning/references/example-sprint-planning.md +62 -0
  260. agent_toolkit/data/skills/delivery/prd/SKILL.md +46 -0
  261. agent_toolkit/data/skills/delivery/prd/references/default-template.md +31 -0
  262. agent_toolkit/data/skills/delivery/prd/references/example-unified-api.md +71 -0
  263. agent_toolkit/data/skills/delivery/project-assessment/SKILL.md +90 -0
  264. agent_toolkit/data/skills/delivery/project-assessment/references/default-template.md +26 -0
  265. agent_toolkit/data/skills/delivery/project-assessment/references/example-platform-assessment.md +122 -0
  266. agent_toolkit/data/skills/delivery/project-assessment-evidence/SKILL.md +88 -0
  267. agent_toolkit/data/skills/delivery/project-assessment-evidence/references/default-template.md +24 -0
  268. agent_toolkit/data/skills/delivery/project-assessment-evidence/references/example-evidence-map.md +46 -0
  269. agent_toolkit/data/skills/delivery/spike/SKILL.md +24 -0
  270. agent_toolkit/data/skills/delivery/spike/references/default-template.md +29 -0
  271. agent_toolkit/data/skills/delivery/spike/references/example-graphql-federation.md +79 -0
  272. agent_toolkit/data/skills/delivery/task/SKILL.md +21 -0
  273. agent_toolkit/data/skills/delivery/task/references/default-template.md +21 -0
  274. agent_toolkit/data/skills/delivery/task/references/example-nav-system.md +33 -0
  275. agent_toolkit/data/skills/delivery/technical-unit-assessment/SKILL.md +58 -0
  276. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/default-template.md +26 -0
  277. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/example-frontend-assessment.md +112 -0
  278. agent_toolkit/data/skills/delivery/technical-unit-assessment/references/indicator-groups.md +65 -0
  279. agent_toolkit/data/skills/delivery/trd/SKILL.md +49 -0
  280. agent_toolkit/data/skills/delivery/trd/references/default-template.md +31 -0
  281. agent_toolkit/data/skills/delivery/trd/references/example-api-migration.md +122 -0
  282. agent_toolkit/data/skills/delivery/user-story/SKILL.md +21 -0
  283. agent_toolkit/data/skills/delivery/user-story/references/default-template.md +21 -0
  284. agent_toolkit/data/skills/delivery/user-story/references/example-advanced-filtering.md +37 -0
  285. agent_toolkit/data/skills/delivery/work-item/SKILL.md +31 -0
  286. agent_toolkit/data/skills/delivery/work-item/references/default-template.md +21 -0
  287. agent_toolkit/data/skills/delivery/work-item/references/example-routing-dashboard.md +23 -0
  288. agent_toolkit/data/skills/delivery/workflow-client-bootstrap/SKILL.md +148 -0
  289. agent_toolkit/data/skills/delivery/workflow-client-bootstrap/questions.yaml +165 -0
  290. agent_toolkit/data/skills/delivery/workflow-generic-project/SKILL.md +79 -0
  291. agent_toolkit/data/skills/design/design-assessment/SKILL.md +150 -0
  292. agent_toolkit/data/skills/design/design-assessment/references/design-scorecard-template.md +78 -0
  293. agent_toolkit/data/skills/design/design-improvement/SKILL.md +167 -0
  294. agent_toolkit/data/skills/design/design-improvement/references/improvement-plan-template.md +113 -0
  295. agent_toolkit/data/skills/design/figma/LICENSE.txt +2 -0
  296. agent_toolkit/data/skills/design/figma/NOTICE.txt +25 -0
  297. agent_toolkit/data/skills/design/figma/SKILL.md +98 -0
  298. agent_toolkit/data/skills/design/figma/references/figma-mcp-config.md +82 -0
  299. agent_toolkit/data/skills/design/figma/references/figma-tools-and-prompts.md +34 -0
  300. agent_toolkit/data/skills/design/figma-code-connect-components/LICENSE.txt +2 -0
  301. agent_toolkit/data/skills/design/figma-code-connect-components/NOTICE.txt +18 -0
  302. agent_toolkit/data/skills/design/figma-code-connect-components/SKILL.md +354 -0
  303. agent_toolkit/data/skills/design/figma-code-connect-components/references/mapping-checklist.md +7 -0
  304. agent_toolkit/data/skills/design/figma-code-connect-components/scripts/normalize_node_id.py +25 -0
  305. agent_toolkit/data/skills/design/figma-create-design-system-rules/LICENSE.txt +2 -0
  306. agent_toolkit/data/skills/design/figma-create-design-system-rules/NOTICE.txt +19 -0
  307. agent_toolkit/data/skills/design/figma-create-design-system-rules/SKILL.md +554 -0
  308. agent_toolkit/data/skills/design/figma-create-design-system-rules/references/rule-template.md +15 -0
  309. agent_toolkit/data/skills/design/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -0
  310. agent_toolkit/data/skills/design/figma-create-new-file/LICENSE.txt +2 -0
  311. agent_toolkit/data/skills/design/figma-create-new-file/NOTICE.txt +17 -0
  312. agent_toolkit/data/skills/design/figma-create-new-file/SKILL.md +72 -0
  313. agent_toolkit/data/skills/design/figma-implement-design/LICENSE.txt +2 -0
  314. agent_toolkit/data/skills/design/figma-implement-design/NOTICE.txt +19 -0
  315. agent_toolkit/data/skills/design/figma-implement-design/SKILL.md +268 -0
  316. agent_toolkit/data/skills/design/frontend-design/LICENSE.txt +177 -0
  317. agent_toolkit/data/skills/design/frontend-design/SKILL.md +82 -0
  318. agent_toolkit/data/skills/design/frontend-design/UPSTREAM.md +14 -0
  319. agent_toolkit/data/skills/design/frontend-design-review/LICENSE +21 -0
  320. agent_toolkit/data/skills/design/frontend-design-review/SKILL.md +194 -0
  321. agent_toolkit/data/skills/design/frontend-design-review/references/pattern-examples.md +21 -0
  322. agent_toolkit/data/skills/design/frontend-design-review/references/quick-checklist.md +38 -0
  323. agent_toolkit/data/skills/design/frontend-design-review/references/review-output-format.md +68 -0
  324. agent_toolkit/data/skills/design/frontend-design-review/references/review-type-modifiers.md +31 -0
  325. agent_toolkit/data/skills/design/web-design-guidelines/SKILL.md +78 -0
  326. agent_toolkit/data/skills/design/web-design-guidelines/references/LICENSE +21 -0
  327. agent_toolkit/data/skills/design/web-design-guidelines/references/web-interface-guidelines.md +180 -0
  328. agent_toolkit/data/skills/forge/gh-address-comments/LICENSE.txt +202 -0
  329. agent_toolkit/data/skills/forge/gh-address-comments/NOTICE.txt +20 -0
  330. agent_toolkit/data/skills/forge/gh-address-comments/SKILL.md +72 -0
  331. agent_toolkit/data/skills/forge/gh-address-comments/scripts/fetch_comments.py +239 -0
  332. agent_toolkit/data/skills/forge/gh-contribution-planner/SKILL.md +160 -0
  333. agent_toolkit/data/skills/forge/gh-contribution-planner/scripts/inspect_contributions.py +818 -0
  334. agent_toolkit/data/skills/forge/gh-fix-ci/LICENSE.txt +201 -0
  335. agent_toolkit/data/skills/forge/gh-fix-ci/NOTICE.txt +20 -0
  336. agent_toolkit/data/skills/forge/gh-fix-ci/SKILL.md +74 -0
  337. agent_toolkit/data/skills/forge/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
  338. agent_toolkit/data/skills/forge/github-cli-workflow/SKILL.md +49 -0
  339. agent_toolkit/data/skills/forge/gitlab-cli-workflow/SKILL.md +44 -0
  340. agent_toolkit/data/skills/forge/workflow-client-bootstrap/SKILL.md +14 -0
  341. agent_toolkit/data/skills/forge/workflow-generic-project/SKILL.md +14 -0
  342. agent_toolkit/data/skills/forge/worktree/SKILL.md +113 -0
  343. agent_toolkit/data/skills/integrations/clickup-cli/SKILL.md +78 -0
  344. agent_toolkit/data/skills/integrations/clickup-cli/references/docs-management.md +57 -0
  345. agent_toolkit/data/skills/integrations/clickup-cli/references/task-management.md +131 -0
  346. agent_toolkit/data/skills/integrations/clickup-cli/references/time-tracking.md +38 -0
  347. agent_toolkit/data/skills/integrations/linear/LICENSE.txt +202 -0
  348. agent_toolkit/data/skills/integrations/linear/NOTICE.txt +23 -0
  349. agent_toolkit/data/skills/integrations/linear/SKILL.md +134 -0
  350. agent_toolkit/data/skills/integrations/mcp/SKILL.md +88 -0
  351. agent_toolkit/data/skills/integrations/slack-assistant/SKILL.md +138 -0
  352. agent_toolkit/data/skills/integrations/slack-cli/SKILL.md +109 -0
  353. agent_toolkit/data/skills/integrations/slack-cli/references/INSTALLATION.md +42 -0
  354. agent_toolkit/data/skills/loops/loop-runner/SKILL.md +92 -0
  355. agent_toolkit/data/skills/ops/docs-generator/SKILL.md +120 -0
  356. agent_toolkit/data/skills/ops/llm-cost-advisor/SKILL.md +87 -0
  357. agent_toolkit/data/skills/ops/swarm/SKILL.md +108 -0
  358. agent_toolkit/data/skills/ops/swarm-handoff/SKILL.md +112 -0
  359. agent_toolkit/data/skills/ops/swarm-observer/SKILL.md +102 -0
  360. agent_toolkit/data/skills/ops/triage/SKILL.md +119 -0
  361. agent_toolkit/data/skills/quality/codeql/SKILL.md +106 -0
  362. agent_toolkit/data/skills/quality/codeql/references/codeql-queries.md +43 -0
  363. agent_toolkit/data/skills/quality/megalinter/SKILL.md +162 -0
  364. agent_toolkit/data/skills/quality/megalinter/references/megalinter-images.md +36 -0
  365. agent_toolkit/data/skills/quality/megalinter/references/megalinter-license.md +41 -0
  366. agent_toolkit/data/skills/quality/megalinter/references/megalinter-targets.md +66 -0
  367. agent_toolkit/data/skills/tooling/chrome-devtools/SKILL.md +146 -0
  368. agent_toolkit/data/skills/tooling/herdr/SKILL.md +109 -0
  369. agent_toolkit/data/skills/tooling/inventory/SKILL.md +81 -0
  370. agent_toolkit/data/skills/tooling/jupyter-notebook/LICENSE.txt +201 -0
  371. agent_toolkit/data/skills/tooling/jupyter-notebook/NOTICE.txt +27 -0
  372. agent_toolkit/data/skills/tooling/jupyter-notebook/SKILL.md +145 -0
  373. agent_toolkit/data/skills/tooling/jupyter-notebook/assets/experiment-template.ipynb +117 -0
  374. agent_toolkit/data/skills/tooling/jupyter-notebook/assets/tutorial-template.ipynb +115 -0
  375. agent_toolkit/data/skills/tooling/jupyter-notebook/references/experiment-patterns.md +10 -0
  376. agent_toolkit/data/skills/tooling/jupyter-notebook/references/notebook-structure.md +17 -0
  377. agent_toolkit/data/skills/tooling/jupyter-notebook/references/quality-checklist.md +11 -0
  378. agent_toolkit/data/skills/tooling/jupyter-notebook/references/tutorial-patterns.md +9 -0
  379. agent_toolkit/data/skills/tooling/jupyter-notebook/scripts/new_notebook.py +132 -0
  380. agent_toolkit/data/skills/tooling/mermaid/SKILL.md +36 -0
  381. agent_toolkit/data/skills/tooling/playwright-cli/LICENSE.txt +201 -0
  382. agent_toolkit/data/skills/tooling/playwright-cli/NOTICE.txt +29 -0
  383. agent_toolkit/data/skills/tooling/playwright-cli/SKILL.md +154 -0
  384. agent_toolkit/data/skills/tooling/playwright-cli/references/cli.md +116 -0
  385. agent_toolkit/data/skills/tooling/playwright-cli/references/workflows.md +98 -0
  386. agent_toolkit/data/skills/tooling/playwright-cli/scripts/executable_playwright_cli.sh +25 -0
  387. agent_toolkit/data_cache.py +79 -0
  388. agent_toolkit/data_sync.py +144 -0
  389. agent_toolkit/installer/__init__.py +0 -0
  390. agent_toolkit/installer/merge.py +59 -0
  391. agent_toolkit/installer/receipt.py +89 -0
  392. agent_toolkit/installer/sources.py +92 -0
  393. agent_toolkit/installer/tracking.py +63 -0
  394. agent_toolkit/launcher.py +69 -0
  395. agent_toolkit/loop/__init__.py +0 -0
  396. agent_toolkit/loop/budget.py +107 -0
  397. agent_toolkit/loop/gh_gate.py +923 -0
  398. agent_toolkit/loop/loop-gh-gate +923 -0
  399. agent_toolkit/loop/pack.py +65 -0
  400. agent_toolkit/loop/runner.py +2722 -0
  401. agent_toolkit/runner/__init__.py +0 -0
  402. agent_toolkit/runner/dispatcher.py +163 -0
  403. agent_toolkit/runner/policy.py +356 -0
  404. agent_toolkit/runner/providers/__init__.py +21 -0
  405. agent_toolkit/runner/providers/anthropic_provider.py +109 -0
  406. agent_toolkit/runner/providers/base.py +53 -0
  407. agent_toolkit/runner/providers/ollama_provider.py +108 -0
  408. agent_toolkit/runner/providers/openai_provider.py +103 -0
  409. agent_toolkit/runner/providers/opencode_provider.py +127 -0
  410. agent_toolkit/swarm/__init__.py +5 -0
  411. agent_toolkit/swarm/approvals.py +121 -0
  412. agent_toolkit/swarm/backends/__init__.py +596 -0
  413. agent_toolkit/swarm/budget.py +83 -0
  414. agent_toolkit/swarm/cli.py +2448 -0
  415. agent_toolkit/swarm/config.py +351 -0
  416. agent_toolkit/swarm/handoff.py +174 -0
  417. agent_toolkit/swarm/models.py +230 -0
  418. agent_toolkit/swarm/prompts.py +177 -0
  419. agent_toolkit/swarm/recipes.py +304 -0
  420. agent_toolkit/swarm/runner.py +267 -0
  421. agent_toolkit/swarm/state.py +93 -0
  422. agent_toolkit/swarm/store.py +148 -0
  423. agent_toolkit/swarm/worktree.py +105 -0
  424. agent_toolkit/templates/workspace/.gitignore +19 -0
  425. agent_toolkit/templates/workspace/AGENTS.md +82 -0
  426. agent_toolkit/templates/workspace/knowledge/README.md +22 -0
  427. agent_toolkit/templates/workspace/knowledge/learnings/general.md +3 -0
  428. agent_toolkit/templates/workspace/knowledge/todos/pending.md +7 -0
  429. agent_toolkit/templates/workspace/packs/README.md +24 -0
  430. agent_toolkit/templates/workspace/personas/architect.md +12 -0
  431. agent_toolkit/templates/workspace/personas/implementer.md +12 -0
  432. agent_toolkit/templates/workspace/personas/researcher.md +12 -0
  433. agent_toolkit/templates/workspace/personas/reviewer.md +12 -0
  434. agent_toolkit_cli-1.11.0.dist-info/METADATA +363 -0
  435. agent_toolkit_cli-1.11.0.dist-info/RECORD +438 -0
  436. agent_toolkit_cli-1.11.0.dist-info/WHEEL +4 -0
  437. agent_toolkit_cli-1.11.0.dist-info/entry_points.txt +4 -0
  438. agent_toolkit_cli-1.11.0.dist-info/licenses/LICENSE +21 -0
File without changes
@@ -0,0 +1,163 @@
1
+ """LLM dispatcher for the dev-companion runner.
2
+
3
+ Selects the best available LLM provider, constrained by an :class:`LLMPolicy`
4
+ that captures client-driven privacy/billing requirements (allowlist, denylist,
5
+ pinned provider/model, fail-closed ``strict`` mode).
6
+
7
+ When no policy is supplied, behaviour is backwards-compatible: providers are
8
+ ordered by their hard-coded ``get_priority()`` and the first available one is
9
+ selected.
10
+ """
11
+
12
+ from __future__ import annotations
13
+
14
+ import logging
15
+ import os
16
+ import sys
17
+ from pathlib import Path
18
+ from pathlib import Path as _Path
19
+ from typing import Optional
20
+
21
+ sys.path.insert(0, str(_Path(__file__).parent))
22
+
23
+ from policy import LLMPolicy
24
+ from providers import (
25
+ AnthropicProvider,
26
+ LLMProvider,
27
+ LLMResponse,
28
+ OllamaProvider,
29
+ OpenAIProvider,
30
+ OpenCodeProvider,
31
+ )
32
+
33
+ logger = logging.getLogger(__name__)
34
+
35
+
36
+ class LLMDispatcher:
37
+ """Pick a provider that satisfies the active :class:`LLMPolicy`.
38
+
39
+ The dispatcher never silently falls back to a denied provider: if the
40
+ policy filters every provider out and ``strict`` is set, the caller gets
41
+ an explicit ``policy_no_provider_available`` error. Non-strict mode keeps
42
+ the previous behaviour (returns ``None``; caller can use a skeleton plan).
43
+ """
44
+
45
+ POLICY_VIOLATION = "policy_no_provider_available"
46
+
47
+ def __init__(self, policy: LLMPolicy | None = None) -> None:
48
+ self._policy: LLMPolicy = policy or LLMPolicy.empty()
49
+ self._providers: list[LLMProvider] = []
50
+ self._candidates: list[LLMProvider] = []
51
+ self._selected: Optional[LLMProvider] = None
52
+ self._initialize_providers()
53
+
54
+ @property
55
+ def policy(self) -> LLMPolicy:
56
+ return self._policy
57
+
58
+ def _initialize_providers(self) -> None:
59
+ all_providers: list[LLMProvider] = [
60
+ OpenCodeProvider(),
61
+ OllamaProvider(),
62
+ AnthropicProvider(),
63
+ OpenAIProvider(),
64
+ ]
65
+ # Stable hard-coded ordering for the unfiltered case.
66
+ all_providers.sort(key=lambda p: p.get_priority())
67
+ self._providers = all_providers
68
+ self._candidates = self._policy.filter(all_providers)
69
+
70
+ def list_candidates(self) -> list[dict]:
71
+ """Detailed status for every candidate after policy filtering, without
72
+ executing the model. Used by the ``llm-status`` diagnostic."""
73
+ return [
74
+ {
75
+ "name": p.name,
76
+ "model": p.get_model_name(),
77
+ "available": p.is_available(),
78
+ "is_local": getattr(p, "is_local", False),
79
+ "is_free": getattr(p, "is_free", False),
80
+ "priority": p.get_priority(),
81
+ }
82
+ for p in self._candidates
83
+ ]
84
+
85
+ def list_all_providers(self) -> list[dict]:
86
+ """Status for every known provider, regardless of policy. Useful for
87
+ operators who need to see what was filtered out and why."""
88
+ candidate_names = {p.name for p in self._candidates}
89
+ return [
90
+ {
91
+ "name": p.name,
92
+ "model": p.get_model_name(),
93
+ "available": p.is_available(),
94
+ "is_local": getattr(p, "is_local", False),
95
+ "is_free": getattr(p, "is_free", False),
96
+ "priority": p.get_priority(),
97
+ "in_policy": p.name in candidate_names,
98
+ }
99
+ for p in self._providers
100
+ ]
101
+
102
+ def get_available_provider(self) -> Optional[LLMProvider]:
103
+ if not self._candidates:
104
+ logger.warning(
105
+ "LLM policy filtered out every provider (allowlist=%s, deny=%s, pinned=%s)",
106
+ list(self._policy.allowlist) if self._policy.allowlist else None,
107
+ list(self._policy.denylist),
108
+ self._policy.pinned_provider,
109
+ )
110
+ return None
111
+ for provider in self._candidates:
112
+ if provider.is_available():
113
+ logger.info(
114
+ "Selected provider: %s (model: %s)",
115
+ provider.name,
116
+ provider.get_model_name(),
117
+ )
118
+ self._selected = provider
119
+ return provider
120
+ logger.warning(
121
+ "No allowed LLM provider is available (strict=%s)",
122
+ self._policy.strict,
123
+ )
124
+ return None
125
+
126
+ def get_selected(self) -> Optional[LLMProvider]:
127
+ return self._selected
128
+
129
+ def list_providers(self) -> list[dict]:
130
+ # Backwards-compatible alias used by ``dots_ai_devcompanion_runner``.
131
+ return self.list_candidates() or self.list_all_providers()
132
+
133
+ def generate(
134
+ self,
135
+ prompt: str,
136
+ system_prompt: Optional[str] = None,
137
+ repo_path: Optional[Path] = None,
138
+ timeout_sec: int = 300,
139
+ ) -> tuple[Optional[LLMResponse], Optional[str]]:
140
+ provider = self.get_selected()
141
+ if not provider:
142
+ if self._policy.strict:
143
+ return None, self.POLICY_VIOLATION
144
+ return None, "no_provider"
145
+
146
+ try:
147
+ response = provider.generate(
148
+ prompt=prompt,
149
+ system_prompt=system_prompt,
150
+ repo_path=repo_path,
151
+ timeout_sec=timeout_sec,
152
+ )
153
+ return response, None
154
+ except Exception as exc: # pragma: no cover - safety net
155
+ logger.error("Provider %s failed: %s", provider.name, exc)
156
+ return None, str(exc)
157
+
158
+
159
+ def create_dispatcher(policy: LLMPolicy | None = None) -> LLMDispatcher:
160
+ debug = os.environ.get("DOTS_WORKSTATION_LLM_DEBUG", "").lower() in ("1", "true", "yes", "on")
161
+ if debug:
162
+ logging.basicConfig(level=logging.DEBUG)
163
+ return LLMDispatcher(policy=policy)
@@ -0,0 +1,356 @@
1
+ """LLM execution policy for the agent-toolkit dev-companion runner.
2
+
3
+ Defines which LLM providers are allowed to run, in what order, and whether
4
+ the runner is required to fail closed when no allowed provider is available
5
+ (for client engagements that mandate a single billing/data identity).
6
+
7
+ Policy sources, in order of precedence — each layer can only RESTRICT the
8
+ prior one, never widen it:
9
+
10
+ 1. Global environment variables (machine baseline / engagement env file).
11
+ 2. Optional JSON config file at ``~/.config/agent-toolkit/devcompanion-llm.json``
12
+ (path overridable with ``DOTS_WORKSTATION_DEVCOMPANION_LLM_CONFIG``).
13
+ 3. Per-job ``llm`` object inside the ``.job`` JSON.
14
+
15
+ A job may further restrict (subset of ``allowlist``, additional ``denylist``
16
+ entries, ``strict: true``) but cannot expand the global allowlist or relax
17
+ strict mode that was set globally.
18
+ """
19
+
20
+ from __future__ import annotations
21
+
22
+ import json
23
+ import logging
24
+ import os
25
+ from dataclasses import dataclass, field
26
+ from pathlib import Path
27
+ from typing import Any, Iterable, Mapping
28
+
29
+ logger = logging.getLogger(__name__)
30
+
31
+ KNOWN_PROVIDERS: tuple[str, ...] = ("opencode", "ollama", "anthropic", "openai")
32
+ DEFAULT_CONFIG_PATH = Path.home() / ".config" / "agent-toolkit" / "devcompanion-llm.json"
33
+
34
+
35
+ class PolicyError(ValueError):
36
+ """Raised when policy values are inconsistent (for example a job tries to widen
37
+ the globally allowed set, or a pinned provider is denied)."""
38
+
39
+
40
+ def _truthy(value: Any) -> bool:
41
+ if isinstance(value, bool):
42
+ return value
43
+ if value is None:
44
+ return False
45
+ return str(value).strip().lower() in ("1", "true", "yes", "on")
46
+
47
+
48
+ def _split_list(value: Any) -> tuple[str, ...]:
49
+ if value is None:
50
+ return ()
51
+ if isinstance(value, str):
52
+ items: list[str] = [s.strip() for s in value.split(",")]
53
+ elif isinstance(value, Iterable) and not isinstance(value, (bytes, bytearray)):
54
+ items = [str(s).strip() for s in value]
55
+ else:
56
+ return ()
57
+ return tuple(item.lower() for item in items if item)
58
+
59
+
60
+ def _normalize_name(name: Any) -> str | None:
61
+ if name is None:
62
+ return None
63
+ text = str(name).strip().lower()
64
+ return text or None
65
+
66
+
67
+ @dataclass(frozen=True)
68
+ class LLMPolicy:
69
+ """Effective execution policy for the LLM dispatcher.
70
+
71
+ ``allowlist`` preserves order: when set, the dispatcher keeps providers in
72
+ that order rather than the implementation's hard-coded priority. This lets
73
+ operators express "prefer Anthropic first, then OpenAI" explicitly.
74
+
75
+ ``sources`` is for audit only; it lists which layers contributed to the
76
+ effective policy (``env``, ``file``, ``job``).
77
+ """
78
+
79
+ allowlist: tuple[str, ...] | None = None
80
+ denylist: tuple[str, ...] = ()
81
+ pinned_provider: str | None = None
82
+ pinned_model: str | None = None
83
+ strict: bool = False
84
+ sources: tuple[str, ...] = ()
85
+ warnings: tuple[str, ...] = field(default_factory=tuple)
86
+
87
+ # ── loaders ────────────────────────────────────────────────────────────
88
+ @classmethod
89
+ def empty(cls) -> "LLMPolicy":
90
+ return cls()
91
+
92
+ @classmethod
93
+ def from_env(cls, env: Mapping[str, str] | None = None) -> "LLMPolicy":
94
+ env = os.environ if env is None else env
95
+ allow = _split_list(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_ALLOWLIST"))
96
+ deny = _split_list(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_DENYLIST"))
97
+ pinned = _normalize_name(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_PINNED_PROVIDER"))
98
+ pinned_model_raw = env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_PINNED_MODEL")
99
+ pinned_model = (
100
+ pinned_model_raw.strip()
101
+ if isinstance(pinned_model_raw, str) and pinned_model_raw.strip()
102
+ else None
103
+ )
104
+ strict = _truthy(env.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_STRICT"))
105
+
106
+ warnings = list(_unknown_warnings("env", allow, deny, pinned))
107
+ sources = ("env",) if (allow or deny or pinned or pinned_model or strict) else ()
108
+ return cls(
109
+ allowlist=allow or None,
110
+ denylist=deny,
111
+ pinned_provider=pinned,
112
+ pinned_model=pinned_model,
113
+ strict=strict,
114
+ sources=sources,
115
+ warnings=tuple(warnings),
116
+ )
117
+
118
+ @classmethod
119
+ def from_file(cls, path: Path | str | None = None) -> "LLMPolicy":
120
+ env_override = os.environ.get("DOTS_WORKSTATION_DEVCOMPANION_LLM_CONFIG")
121
+ if path is None:
122
+ path = Path(env_override) if env_override else DEFAULT_CONFIG_PATH
123
+ path = Path(path).expanduser()
124
+ if not path.is_file():
125
+ return cls.empty()
126
+
127
+ try:
128
+ data = json.loads(path.read_text(encoding="utf-8"))
129
+ except Exception as exc:
130
+ return cls(
131
+ warnings=(f"failed to parse policy file {path}: {exc}",),
132
+ )
133
+
134
+ if not isinstance(data, Mapping):
135
+ return cls(
136
+ warnings=(f"policy file {path} must contain a JSON object",),
137
+ )
138
+
139
+ allow = _split_list(data.get("allowlist"))
140
+ deny = _split_list(data.get("denylist"))
141
+ pinned = _normalize_name(data.get("pinned_provider") or data.get("provider"))
142
+ pinned_model = data.get("pinned_model") or data.get("model")
143
+ if isinstance(pinned_model, str):
144
+ pinned_model = pinned_model.strip() or None
145
+ else:
146
+ pinned_model = None
147
+ strict = _truthy(data.get("strict"))
148
+ warnings = list(_unknown_warnings(f"file {path}", allow, deny, pinned))
149
+ sources = (f"file:{path}",) if (allow or deny or pinned or pinned_model or strict) else ()
150
+ return cls(
151
+ allowlist=allow or None,
152
+ denylist=deny,
153
+ pinned_provider=pinned,
154
+ pinned_model=pinned_model,
155
+ strict=strict,
156
+ sources=sources,
157
+ warnings=tuple(warnings),
158
+ )
159
+
160
+ @classmethod
161
+ def from_job(cls, job_llm: Any) -> "LLMPolicy":
162
+ if job_llm is None or isinstance(job_llm, bool):
163
+ return cls.empty()
164
+ if not isinstance(job_llm, Mapping):
165
+ return cls.empty()
166
+
167
+ allow = _split_list(job_llm.get("allowlist"))
168
+ deny = _split_list(job_llm.get("denylist"))
169
+ pinned = _normalize_name(job_llm.get("provider") or job_llm.get("pinned_provider"))
170
+ pinned_model = job_llm.get("model") or job_llm.get("pinned_model")
171
+ if isinstance(pinned_model, str):
172
+ pinned_model = pinned_model.strip() or None
173
+ else:
174
+ pinned_model = None
175
+ strict = _truthy(job_llm.get("strict"))
176
+ warnings = list(_unknown_warnings("job", allow, deny, pinned))
177
+ sources = ("job",) if (allow or deny or pinned or pinned_model or strict) else ()
178
+ return cls(
179
+ allowlist=allow or None,
180
+ denylist=deny,
181
+ pinned_provider=pinned,
182
+ pinned_model=pinned_model,
183
+ strict=strict,
184
+ sources=sources,
185
+ warnings=tuple(warnings),
186
+ )
187
+
188
+ @classmethod
189
+ def load(
190
+ cls,
191
+ *,
192
+ env: Mapping[str, str] | None = None,
193
+ config_path: Path | str | None = None,
194
+ job_llm: Any | None = None,
195
+ ) -> "LLMPolicy":
196
+ """Compose ``env`` → ``file`` → ``job`` and validate that later layers only
197
+ restrict (never widen) earlier ones. Raises :class:`PolicyError` when a job
198
+ tries to escape the global allowlist."""
199
+ base = merge_policies(cls.from_env(env), cls.from_file(config_path))
200
+ if job_llm is None:
201
+ return base
202
+ job = cls.from_job(job_llm)
203
+ return merge_policies(base, job, _validate_restriction=True)
204
+
205
+ # ── filtering ──────────────────────────────────────────────────────────
206
+ def filter(self, providers: Iterable[Any]) -> list[Any]:
207
+ """Return providers permitted by the policy, ordered by preference.
208
+
209
+ Mutates the ``model`` attribute on a pinned provider when ``pinned_model``
210
+ is set, so the caller can use the pinned model without changing the
211
+ provider classes.
212
+ """
213
+ by_name: dict[str, Any] = {}
214
+ for p in providers:
215
+ name = _provider_name(p)
216
+ if name:
217
+ by_name[name] = p
218
+
219
+ if self.pinned_provider:
220
+ if self.pinned_provider in self.denylist:
221
+ return []
222
+ provider = by_name.get(self.pinned_provider)
223
+ if provider is None:
224
+ return []
225
+ if self.pinned_model and hasattr(provider, "model"):
226
+ try:
227
+ setattr(provider, "model", self.pinned_model)
228
+ except Exception:
229
+ logger.debug("Could not set pinned model on provider %s", self.pinned_provider)
230
+ return [provider]
231
+
232
+ denied = set(self.denylist)
233
+ if self.allowlist:
234
+ ordered: list[Any] = []
235
+ for name in self.allowlist:
236
+ if name in denied:
237
+ continue
238
+ provider = by_name.get(name)
239
+ if provider is not None:
240
+ ordered.append(provider)
241
+ return ordered
242
+
243
+ sorted_providers = sorted(
244
+ by_name.values(), key=lambda p: getattr(p, "get_priority", lambda: 100)()
245
+ )
246
+ return [p for p in sorted_providers if _provider_name(p) not in denied]
247
+
248
+ # ── audit ──────────────────────────────────────────────────────────────
249
+ def to_audit(self) -> dict[str, Any]:
250
+ return {
251
+ "allowlist": list(self.allowlist) if self.allowlist else None,
252
+ "denylist": list(self.denylist),
253
+ "pinned_provider": self.pinned_provider,
254
+ "pinned_model": self.pinned_model,
255
+ "strict": self.strict,
256
+ "sources": list(self.sources),
257
+ "warnings": list(self.warnings),
258
+ }
259
+
260
+
261
+ def _provider_name(provider: Any) -> str:
262
+ return str(getattr(provider, "name", "")).strip().lower()
263
+
264
+
265
+ def _unknown_warnings(
266
+ layer: str,
267
+ allow: tuple[str, ...],
268
+ deny: tuple[str, ...],
269
+ pinned: str | None,
270
+ ) -> Iterable[str]:
271
+ for name in allow:
272
+ if name not in KNOWN_PROVIDERS:
273
+ yield f"{layer}: allowlist contains unknown provider '{name}'"
274
+ for name in deny:
275
+ if name not in KNOWN_PROVIDERS:
276
+ yield f"{layer}: denylist contains unknown provider '{name}'"
277
+ if pinned and pinned not in KNOWN_PROVIDERS:
278
+ yield f"{layer}: pinned provider '{pinned}' is unknown"
279
+
280
+
281
+ def merge_policies(
282
+ base: LLMPolicy,
283
+ overlay: LLMPolicy,
284
+ *,
285
+ _validate_restriction: bool = False,
286
+ ) -> LLMPolicy:
287
+ """Merge ``overlay`` on top of ``base``.
288
+
289
+ When ``_validate_restriction`` is true (job over global), ``overlay`` may only
290
+ restrict ``base``. Attempting to widen raises :class:`PolicyError`.
291
+ """
292
+ # Allowlist
293
+ if overlay.allowlist is None:
294
+ merged_allow = base.allowlist
295
+ elif base.allowlist is None:
296
+ merged_allow = overlay.allowlist
297
+ else:
298
+ if _validate_restriction:
299
+ extras = [name for name in overlay.allowlist if name not in base.allowlist]
300
+ if extras:
301
+ raise PolicyError(
302
+ "job allowlist tries to add provider(s) not allowed globally: "
303
+ + ", ".join(extras)
304
+ )
305
+ # Keep overlay order, but only entries also in base.
306
+ merged_allow = tuple(name for name in overlay.allowlist if name in base.allowlist)
307
+ if not merged_allow:
308
+ raise PolicyError("job allowlist is empty after intersecting with global allowlist")
309
+
310
+ # Denylist (always additive)
311
+ merged_deny = tuple(sorted(set(base.denylist) | set(overlay.denylist)))
312
+
313
+ # Pinned provider/model
314
+ merged_pin_provider = overlay.pinned_provider or base.pinned_provider
315
+ merged_pin_model = overlay.pinned_model or base.pinned_model
316
+
317
+ if _validate_restriction and overlay.pinned_provider:
318
+ if base.allowlist and overlay.pinned_provider not in base.allowlist:
319
+ raise PolicyError(
320
+ f"job pins provider '{overlay.pinned_provider}' but it is not in the global allowlist"
321
+ )
322
+ if overlay.pinned_provider in merged_deny:
323
+ raise PolicyError(
324
+ f"job pins provider '{overlay.pinned_provider}' but it is denied by policy"
325
+ )
326
+
327
+ # Strict can only escalate (False -> True). A job cannot lower a globally-strict policy.
328
+ merged_strict = base.strict or overlay.strict
329
+ if (
330
+ _validate_restriction
331
+ and base.strict
332
+ and overlay.strict is False
333
+ and "job" in overlay.sources
334
+ ):
335
+ # Job explicitly set strict=false but base is strict: warn, keep True.
336
+ warnings = (
337
+ list(base.warnings)
338
+ + list(overlay.warnings)
339
+ + [
340
+ "job tried to set strict=false while global policy is strict; keeping strict=true",
341
+ ]
342
+ )
343
+ else:
344
+ warnings = list(base.warnings) + list(overlay.warnings)
345
+
346
+ sources = tuple(dict.fromkeys(list(base.sources) + list(overlay.sources)))
347
+
348
+ return LLMPolicy(
349
+ allowlist=merged_allow,
350
+ denylist=merged_deny,
351
+ pinned_provider=merged_pin_provider,
352
+ pinned_model=merged_pin_model,
353
+ strict=merged_strict,
354
+ sources=sources,
355
+ warnings=tuple(warnings),
356
+ )
@@ -0,0 +1,21 @@
1
+ """
2
+ agent-toolkit dev-companion LLM providers.
3
+
4
+ Provider-agnostic abstraction layer for LLM generation.
5
+ Supports local (free) and cloud (paid) providers with automatic fallback.
6
+ """
7
+
8
+ from .anthropic_provider import AnthropicProvider
9
+ from .base import LLMProvider, LLMResponse
10
+ from .ollama_provider import OllamaProvider
11
+ from .openai_provider import OpenAIProvider
12
+ from .opencode_provider import OpenCodeProvider
13
+
14
+ __all__ = [
15
+ "LLMProvider",
16
+ "LLMResponse",
17
+ "OpenCodeProvider",
18
+ "OllamaProvider",
19
+ "AnthropicProvider",
20
+ "OpenAIProvider",
21
+ ]
@@ -0,0 +1,109 @@
1
+ """
2
+ Anthropic provider for dev-companion.
3
+
4
+ Uses Anthropic API (Claude) as optional paid provider.
5
+ Requires: ANTHROPIC_API_KEY environment variable.
6
+ """
7
+
8
+ import json
9
+ import os
10
+ import time
11
+ from pathlib import Path
12
+ from typing import Optional
13
+
14
+ from .base import LLMProvider, LLMResponse
15
+
16
+
17
+ class AnthropicProvider(LLMProvider):
18
+ name = "anthropic"
19
+ is_local = False
20
+ is_free = False
21
+
22
+ def __init__(self, model: str = "claude-3-5-haiku-20241022"):
23
+ self.model = model
24
+
25
+ def is_available(self) -> bool:
26
+ return bool(os.environ.get("ANTHROPIC_API_KEY"))
27
+
28
+ def supports_directory_context(self) -> bool:
29
+ return False
30
+
31
+ def get_model_name(self) -> str:
32
+ return self.model
33
+
34
+ def get_priority(self) -> int:
35
+ return 10
36
+
37
+ def generate(
38
+ self,
39
+ prompt: str,
40
+ system_prompt: Optional[str] = None,
41
+ repo_path: Optional[Path] = None,
42
+ timeout_sec: int = 300,
43
+ ) -> LLMResponse:
44
+ import urllib.error
45
+ import urllib.request
46
+
47
+ start_time = time.time()
48
+
49
+ headers = {
50
+ "Content-Type": "application/json",
51
+ "x-api-key": os.environ["ANTHROPIC_API_KEY"],
52
+ "anthropic-version": "2023-06-01",
53
+ }
54
+
55
+ messages = [{"role": "user", "content": prompt}]
56
+ if system_prompt:
57
+ messages[0] = {
58
+ "role": "user",
59
+ "content": f"{system_prompt}\n\n---\n\n{prompt}",
60
+ }
61
+
62
+ data = {
63
+ "model": self.model,
64
+ "messages": messages,
65
+ "max_tokens": 4096,
66
+ }
67
+
68
+ try:
69
+ req = urllib.request.Request(
70
+ "https://api.anthropic.com/v1/messages",
71
+ data=json.dumps(data).encode(),
72
+ headers=headers,
73
+ method="POST",
74
+ )
75
+
76
+ with urllib.request.urlopen(req, timeout=timeout_sec) as response:
77
+ result = json.loads(response.read().decode())
78
+
79
+ duration = time.time() - start_time
80
+
81
+ content = ""
82
+ for block in result.get("content", []):
83
+ if block.get("type") == "text":
84
+ content += block.get("text", "")
85
+
86
+ usage = result.get("usage", {})
87
+
88
+ return LLMResponse(
89
+ content=content,
90
+ model=self.model,
91
+ provider=self.name,
92
+ tokens_used=usage.get("output_tokens"),
93
+ duration_sec=duration,
94
+ )
95
+
96
+ except urllib.error.HTTPError as e:
97
+ return LLMResponse(
98
+ content=f"HTTP Error: {e.code} - {e.reason}",
99
+ model=self.model,
100
+ provider=self.name,
101
+ duration_sec=time.time() - start_time,
102
+ )
103
+ except Exception as e:
104
+ return LLMResponse(
105
+ content=f"Error: {str(e)}",
106
+ model=self.model,
107
+ provider=self.name,
108
+ duration_sec=time.time() - start_time,
109
+ )