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.
- agent_toolkit/__init__.py +4 -0
- agent_toolkit/__main__.py +5 -0
- agent_toolkit/_paths.py +145 -0
- agent_toolkit/bin/.gitkeep +0 -0
- agent_toolkit/bin/agent-toolkit.exe +0 -0
- agent_toolkit/cli/__init__.py +0 -0
- agent_toolkit/cli/__main__.py +5 -0
- agent_toolkit/cli/build.py +278 -0
- agent_toolkit/cli/completion.py +297 -0
- agent_toolkit/cli/context_budget.py +455 -0
- agent_toolkit/cli/devcompanion.py +923 -0
- agent_toolkit/cli/devcompanion_queue.py +235 -0
- agent_toolkit/cli/diff.py +176 -0
- agent_toolkit/cli/doctor.py +1052 -0
- agent_toolkit/cli/insights.py +771 -0
- agent_toolkit/cli/install.py +706 -0
- agent_toolkit/cli/main.py +171 -0
- agent_toolkit/cli/mcp.py +537 -0
- agent_toolkit/cli/memory.py +739 -0
- agent_toolkit/cli/plugin.py +224 -0
- agent_toolkit/cli/project.py +459 -0
- agent_toolkit/cli/release.py +178 -0
- agent_toolkit/cli/skills.py +461 -0
- agent_toolkit/cli/uninstall.py +166 -0
- agent_toolkit/cli/update.py +298 -0
- agent_toolkit/cli/workspace.py +1713 -0
- agent_toolkit/compiler/hook_registry.py +110 -0
- agent_toolkit/compiler/loader.py +338 -0
- agent_toolkit/compiler/mcp_registry.py +87 -0
- agent_toolkit/compiler/model.py +182 -0
- agent_toolkit/compiler/provenance.py +140 -0
- agent_toolkit/compiler/registry_emit.py +263 -0
- agent_toolkit/compiler/target_registry.py +122 -0
- agent_toolkit/compiler/targets/__init__.py +1 -0
- agent_toolkit/compiler/targets/agent_plugins.py +160 -0
- agent_toolkit/compiler/targets/base.py +229 -0
- agent_toolkit/compiler/targets/claude_code.py +241 -0
- agent_toolkit/compiler/targets/codex.py +261 -0
- agent_toolkit/compiler/targets/copilot.py +275 -0
- agent_toolkit/compiler/targets/cursor.py +207 -0
- agent_toolkit/compiler/targets/gemini_cli.py +213 -0
- agent_toolkit/compiler/targets/muse_code.py +113 -0
- agent_toolkit/compiler/targets/opencode.py +204 -0
- agent_toolkit/compiler/targets/pi.py +263 -0
- agent_toolkit/compiler/targets/windsurf.py +267 -0
- agent_toolkit/compiler/tool_mapping.py +83 -0
- agent_toolkit/data/.gitignore +4 -0
- agent_toolkit/data/__init__.py +1 -0
- agent_toolkit/data/agents/agentic-security-reviewer/AGENT.md +60 -0
- agent_toolkit/data/agents/architect/AGENT.md +30 -0
- agent_toolkit/data/agents/assistant/AGENT.md +59 -0
- agent_toolkit/data/agents/build-error-resolver/AGENT.md +30 -0
- agent_toolkit/data/agents/client-workflow-bootstrap/AGENT.md +60 -0
- agent_toolkit/data/agents/code-reviewer/AGENT.md +49 -0
- agent_toolkit/data/agents/database-reviewer/AGENT.md +44 -0
- agent_toolkit/data/agents/docs-lookup/AGENT.md +28 -0
- agent_toolkit/data/agents/e2e-runner/AGENT.md +38 -0
- agent_toolkit/data/agents/performance-optimizer/AGENT.md +52 -0
- agent_toolkit/data/agents/planner/AGENT.md +41 -0
- agent_toolkit/data/agents/refactor-cleaner/AGENT.md +43 -0
- agent_toolkit/data/agents/reference-lookup/AGENT.md +33 -0
- agent_toolkit/data/agents/security-reviewer/AGENT.md +47 -0
- agent_toolkit/data/agents/tdd-guide/AGENT.md +58 -0
- agent_toolkit/data/agents/tech-assistant/AGENT.md +11 -0
- agent_toolkit/data/agents/typescript-reviewer/AGENT.md +61 -0
- agent_toolkit/data/capabilities/hooks/README.md +6 -0
- agent_toolkit/data/capabilities/hooks/pre-commit-validate.yaml +26 -0
- agent_toolkit/data/capabilities/hooks/scripts/pre-commit-validate.sh +41 -0
- agent_toolkit/data/capabilities/hooks/scripts/session-start-context.sh +11 -0
- agent_toolkit/data/capabilities/hooks/session-start-context.yaml +23 -0
- agent_toolkit/data/capabilities/targets/registry.yaml +90 -0
- agent_toolkit/data/capabilities/upstream.lock +118 -0
- agent_toolkit/data/catalogs/agent-catalog.yaml +81 -0
- agent_toolkit/data/catalogs/loop-catalog.yaml +55 -0
- agent_toolkit/data/catalogs/skill-catalog.yaml +520 -0
- agent_toolkit/data/catalogs/skills-layout.json +391 -0
- agent_toolkit/data/distributions/products.yaml +190 -0
- agent_toolkit/data/distributions/targets/claude-code.yaml +47 -0
- agent_toolkit/data/distributions/targets/cursor.yaml +42 -0
- agent_toolkit/data/distributions/targets/opencode.yaml +55 -0
- agent_toolkit/data/loops/changelog-drafter/loop.yaml +40 -0
- agent_toolkit/data/loops/ci-sweeper/loop.yaml +53 -0
- agent_toolkit/data/loops/daily-triage/loop.yaml +47 -0
- agent_toolkit/data/loops/dep-sweeper/loop.yaml +46 -0
- agent_toolkit/data/loops/issue-triage/loop.yaml +40 -0
- agent_toolkit/data/loops/oss-daily-briefing/loop.yaml +65 -0
- agent_toolkit/data/loops/oss-pr-monitor/loop.yaml +86 -0
- agent_toolkit/data/loops/oss-triage/loop.yaml +67 -0
- agent_toolkit/data/loops/post-merge-cleanup/loop.yaml +44 -0
- agent_toolkit/data/loops/pr-babysitter/loop.yaml +46 -0
- agent_toolkit/data/mcp/registry/chrome-devtools.yaml +61 -0
- agent_toolkit/data/mcp/registry/clickup.yaml +48 -0
- agent_toolkit/data/mcp/registry/figma.yaml +44 -0
- agent_toolkit/data/mcp/registry/github.yaml +52 -0
- agent_toolkit/data/mcp/registry/linear.yaml +47 -0
- agent_toolkit/data/mcp/registry/notion.yaml +48 -0
- agent_toolkit/data/mcp/registry/slack.yaml +46 -0
- agent_toolkit/data/mcp/templates/chrome-devtools/README.md +57 -0
- agent_toolkit/data/mcp/templates/chrome-devtools/config.template.json +6 -0
- agent_toolkit/data/mcp/templates/clickup/README.md +45 -0
- agent_toolkit/data/mcp/templates/clickup/config.template.json +8 -0
- agent_toolkit/data/mcp/templates/clickup/wrapper.sh +23 -0
- agent_toolkit/data/mcp/templates/figma/README.md +54 -0
- agent_toolkit/data/mcp/templates/figma/config.template.json +9 -0
- agent_toolkit/data/mcp/templates/github/README.md +16 -0
- agent_toolkit/data/mcp/templates/github/config.template.json +15 -0
- agent_toolkit/data/mcp/templates/github/wrapper.sh +6 -0
- agent_toolkit/data/mcp/templates/linear/README.md +49 -0
- agent_toolkit/data/mcp/templates/linear/config.template.json +10 -0
- agent_toolkit/data/mcp/templates/notion/README.md +7 -0
- agent_toolkit/data/mcp/templates/notion/config.template.json +8 -0
- agent_toolkit/data/mcp/templates/notion/wrapper.sh +23 -0
- agent_toolkit/data/mcp/templates/slack/README.md +8 -0
- agent_toolkit/data/mcp/templates/slack/config.template.json +9 -0
- agent_toolkit/data/mcp/templates/slack/wrapper.sh +23 -0
- agent_toolkit/data/packs/README.md +66 -0
- agent_toolkit/data/packs/agentic-security/README.md +19 -0
- agent_toolkit/data/packs/agentic-security/config.yaml +25 -0
- agent_toolkit/data/packs/architecture/README.md +22 -0
- agent_toolkit/data/packs/architecture/config.yaml +27 -0
- agent_toolkit/data/packs/code-quality/README.md +19 -0
- agent_toolkit/data/packs/code-quality/config.yaml +21 -0
- agent_toolkit/data/packs/delivery-discipline/README.md +19 -0
- agent_toolkit/data/packs/delivery-discipline/config.yaml +42 -0
- agent_toolkit/data/packs/design-engineering/README.md +19 -0
- agent_toolkit/data/packs/design-engineering/config.yaml +41 -0
- agent_toolkit/data/packs/engineering-workflow/README.md +21 -0
- agent_toolkit/data/packs/engineering-workflow/config.yaml +57 -0
- agent_toolkit/data/packs/oss-maintenance/README.md +245 -0
- agent_toolkit/data/packs/oss-maintenance/config.yaml +75 -0
- agent_toolkit/data/profiles/claude-code/CLAUDE.md +110 -0
- agent_toolkit/data/profiles/claude-code/agents/assistant.md +59 -0
- agent_toolkit/data/profiles/claude-code/agents/build-error-resolver.md +30 -0
- agent_toolkit/data/profiles/claude-code/agents/client-workflow-bootstrap.md +60 -0
- agent_toolkit/data/profiles/claude-code/agents/contribution-planner.md +39 -0
- agent_toolkit/data/profiles/claude-code/agents/database-reviewer.md +44 -0
- agent_toolkit/data/profiles/claude-code/agents/docs-lookup.md +28 -0
- agent_toolkit/data/profiles/claude-code/agents/e2e-runner.md +38 -0
- agent_toolkit/data/profiles/claude-code/agents/performance-optimizer.md +52 -0
- agent_toolkit/data/profiles/claude-code/agents/planner.md +41 -0
- agent_toolkit/data/profiles/claude-code/agents/refactor-cleaner.md +43 -0
- agent_toolkit/data/profiles/claude-code/agents/reference-lookup.md +33 -0
- agent_toolkit/data/profiles/claude-code/agents/security-reviewer.md +47 -0
- agent_toolkit/data/profiles/claude-code/agents/tdd-guide.md +58 -0
- agent_toolkit/data/profiles/claude-code/agents/typescript-reviewer.md +61 -0
- agent_toolkit/data/profiles/claude-code/settings.json +86 -0
- agent_toolkit/data/profiles/copilot/copilot-instructions.md +15 -0
- agent_toolkit/data/profiles/cursor/README.md +33 -0
- agent_toolkit/data/profiles/cursor/rules/architect.mdc +23 -0
- agent_toolkit/data/profiles/cursor/rules/assistant.mdc +61 -0
- agent_toolkit/data/profiles/cursor/rules/build-error-resolver.mdc +20 -0
- agent_toolkit/data/profiles/cursor/rules/code-reviewer.mdc +21 -0
- agent_toolkit/data/profiles/cursor/rules/database-reviewer.mdc +29 -0
- agent_toolkit/data/profiles/cursor/rules/docs-lookup.mdc +15 -0
- agent_toolkit/data/profiles/cursor/rules/e2e-runner.mdc +30 -0
- agent_toolkit/data/profiles/cursor/rules/performance-optimizer.mdc +27 -0
- agent_toolkit/data/profiles/cursor/rules/planner.mdc +28 -0
- agent_toolkit/data/profiles/cursor/rules/refactor-cleaner.mdc +26 -0
- agent_toolkit/data/profiles/cursor/rules/reference-lookup.mdc +20 -0
- agent_toolkit/data/profiles/cursor/rules/security-reviewer.mdc +20 -0
- agent_toolkit/data/profiles/cursor/rules/tdd-guide.mdc +26 -0
- agent_toolkit/data/profiles/cursor/rules/typescript-reviewer.mdc +28 -0
- agent_toolkit/data/profiles/muse-code/README.md +35 -0
- agent_toolkit/data/profiles/opencode/README.md +44 -0
- agent_toolkit/data/profiles/opencode/agents/architect.md +30 -0
- agent_toolkit/data/profiles/opencode/agents/assistant.md +96 -0
- agent_toolkit/data/profiles/opencode/agents/build-error-resolver.md +31 -0
- agent_toolkit/data/profiles/opencode/agents/client-workflow-bootstrap.md +66 -0
- agent_toolkit/data/profiles/opencode/agents/code-reviewer.md +47 -0
- agent_toolkit/data/profiles/opencode/agents/contribution-planner.md +44 -0
- agent_toolkit/data/profiles/opencode/agents/database-reviewer.md +43 -0
- agent_toolkit/data/profiles/opencode/agents/docs-lookup.md +28 -0
- agent_toolkit/data/profiles/opencode/agents/e2e-runner.md +33 -0
- agent_toolkit/data/profiles/opencode/agents/performance-optimizer.md +34 -0
- agent_toolkit/data/profiles/opencode/agents/planner.md +27 -0
- agent_toolkit/data/profiles/opencode/agents/refactor-cleaner.md +27 -0
- agent_toolkit/data/profiles/opencode/agents/reference-lookup.md +27 -0
- agent_toolkit/data/profiles/opencode/agents/security-reviewer.md +27 -0
- agent_toolkit/data/profiles/opencode/agents/tdd-guide.md +27 -0
- agent_toolkit/data/profiles/opencode/agents/typescript-reviewer.md +30 -0
- agent_toolkit/data/profiles/opencode/opencode.json +3 -0
- agent_toolkit/data/profiles/pi/skills/architect/skill.md +5 -0
- agent_toolkit/data/profiles/pi/skills/assistant/skill.md +3 -0
- agent_toolkit/data/profiles/pi/skills/code-reviewer/skill.md +5 -0
- agent_toolkit/data/profiles/pi/skills/planner/skill.md +3 -0
- agent_toolkit/data/profiles/pi/skills/security-reviewer/skill.md +3 -0
- agent_toolkit/data/profiles/windsurf/memories/global_rules.md +114 -0
- agent_toolkit/data/profiles/windsurf/rules/architect.mdc +23 -0
- agent_toolkit/data/profiles/windsurf/rules/assistant.mdc +61 -0
- agent_toolkit/data/profiles/windsurf/rules/build-error-resolver.mdc +20 -0
- agent_toolkit/data/profiles/windsurf/rules/code-reviewer.mdc +21 -0
- agent_toolkit/data/profiles/windsurf/rules/database-reviewer.mdc +29 -0
- agent_toolkit/data/profiles/windsurf/rules/docs-lookup.mdc +15 -0
- agent_toolkit/data/profiles/windsurf/rules/e2e-runner.mdc +30 -0
- agent_toolkit/data/profiles/windsurf/rules/performance-optimizer.mdc +27 -0
- agent_toolkit/data/profiles/windsurf/rules/planner.mdc +28 -0
- agent_toolkit/data/profiles/windsurf/rules/refactor-cleaner.mdc +26 -0
- agent_toolkit/data/profiles/windsurf/rules/reference-lookup.mdc +20 -0
- agent_toolkit/data/profiles/windsurf/rules/security-reviewer.mdc +20 -0
- agent_toolkit/data/profiles/windsurf/rules/tdd-guide.mdc +26 -0
- agent_toolkit/data/profiles/windsurf/rules/typescript-reviewer.mdc +28 -0
- agent_toolkit/data/skills/accessibility/review/SKILL.md +116 -0
- agent_toolkit/data/skills/accessibility/review/references/a11y-checklist.md +25 -0
- agent_toolkit/data/skills/accessibility/review/references/a11y-findings-template.md +56 -0
- agent_toolkit/data/skills/accessibility/review/references/research.md +47 -0
- agent_toolkit/data/skills/agentic-security/mcp-audit/SKILL.md +116 -0
- agent_toolkit/data/skills/agentic-security/mcp-audit/references/mcp-audit-template.md +51 -0
- agent_toolkit/data/skills/agentic-security/owasp-agentic-review/SKILL.md +73 -0
- agent_toolkit/data/skills/agentic-security/owasp-agentic-review/references/owasp-agentic-template.md +32 -0
- agent_toolkit/data/skills/agentic-security/supply-chain-audit/SKILL.md +142 -0
- agent_toolkit/data/skills/agentic-security/threat-modeling/SKILL.md +100 -0
- agent_toolkit/data/skills/agentic-security/threat-modeling/references/threat-model-template.md +55 -0
- agent_toolkit/data/skills/architecture/c4-model/SKILL.md +49 -0
- agent_toolkit/data/skills/cloud/aws-well-architected-review/SKILL.md +37 -0
- agent_toolkit/data/skills/cloud/cloud-design-patterns/SKILL.md +38 -0
- agent_toolkit/data/skills/core/assistant/SKILL.md +216 -0
- agent_toolkit/data/skills/core/assistant/references/AGENTS_TEMPLATE.md +0 -0
- agent_toolkit/data/skills/core/assistant/references/ORCHESTRATION.md +0 -0
- agent_toolkit/data/skills/core/assistant/references/REPO_INSPECTION.md +0 -0
- agent_toolkit/data/skills/core/dev-companion/SKILL.md +65 -0
- agent_toolkit/data/skills/core/dev-companion/references/LOOP_GUARDRAILS.md +0 -0
- agent_toolkit/data/skills/core/onboarding/SKILL.md +122 -0
- agent_toolkit/data/skills/core/output-handshake/SKILL.md +26 -0
- agent_toolkit/data/skills/core/pr-fallback/SKILL.md +35 -0
- agent_toolkit/data/skills/core/pr-fallback/references/pr-body-default.md +0 -0
- agent_toolkit/data/skills/core/project/SKILL.md +89 -0
- agent_toolkit/data/skills/core/workspace/SKILL.md +103 -0
- agent_toolkit/data/skills/core/workspace-knowledge-sync/SKILL.md +140 -0
- agent_toolkit/data/skills/data/dbt-validation/SKILL.md +41 -0
- agent_toolkit/data/skills/data/snowflake-validation/SKILL.md +38 -0
- agent_toolkit/data/skills/delivery/adr/SKILL.md +56 -0
- agent_toolkit/data/skills/delivery/adr/references/default-template.md +35 -0
- agent_toolkit/data/skills/delivery/adr/references/example-001-graphql-adoption.md +66 -0
- agent_toolkit/data/skills/delivery/agreement/SKILL.md +24 -0
- agent_toolkit/data/skills/delivery/agreement/references/default-template.md +17 -0
- agent_toolkit/data/skills/delivery/agreement/references/example-api-versioning.md +29 -0
- agent_toolkit/data/skills/delivery/bug/SKILL.md +26 -0
- agent_toolkit/data/skills/delivery/bug/references/default-template.md +26 -0
- agent_toolkit/data/skills/delivery/bug/references/example-search-filter-bug.md +58 -0
- agent_toolkit/data/skills/delivery/decision-log/SKILL.md +28 -0
- agent_toolkit/data/skills/delivery/decision-log/references/default-template.md +17 -0
- agent_toolkit/data/skills/delivery/decision-log/references/example-repository-pattern.md +27 -0
- agent_toolkit/data/skills/delivery/development-workflow/SKILL.md +34 -0
- agent_toolkit/data/skills/delivery/development-workflow/references/default-workflow-checklist.md +44 -0
- agent_toolkit/data/skills/delivery/epic/SKILL.md +22 -0
- agent_toolkit/data/skills/delivery/epic/references/default-template.md +26 -0
- agent_toolkit/data/skills/delivery/epic/references/example-unified-dashboard.md +40 -0
- agent_toolkit/data/skills/delivery/incident/SKILL.md +24 -0
- agent_toolkit/data/skills/delivery/incident/references/default-template.md +29 -0
- agent_toolkit/data/skills/delivery/incident/references/example-payment-outage.md +62 -0
- agent_toolkit/data/skills/delivery/management-unit-assessment/SKILL.md +104 -0
- agent_toolkit/data/skills/delivery/management-unit-assessment/references/default-template.md +24 -0
- agent_toolkit/data/skills/delivery/management-unit-assessment/references/example-team-assessment.md +126 -0
- agent_toolkit/data/skills/delivery/meeting-minutes/SKILL.md +25 -0
- agent_toolkit/data/skills/delivery/meeting-minutes/references/default-template.md +22 -0
- agent_toolkit/data/skills/delivery/meeting-minutes/references/example-weekly-sync.md +72 -0
- agent_toolkit/data/skills/delivery/planning/SKILL.md +31 -0
- agent_toolkit/data/skills/delivery/planning/references/default-template.md +25 -0
- agent_toolkit/data/skills/delivery/planning/references/example-sprint-planning.md +62 -0
- agent_toolkit/data/skills/delivery/prd/SKILL.md +46 -0
- agent_toolkit/data/skills/delivery/prd/references/default-template.md +31 -0
- agent_toolkit/data/skills/delivery/prd/references/example-unified-api.md +71 -0
- agent_toolkit/data/skills/delivery/project-assessment/SKILL.md +90 -0
- agent_toolkit/data/skills/delivery/project-assessment/references/default-template.md +26 -0
- agent_toolkit/data/skills/delivery/project-assessment/references/example-platform-assessment.md +122 -0
- agent_toolkit/data/skills/delivery/project-assessment-evidence/SKILL.md +88 -0
- agent_toolkit/data/skills/delivery/project-assessment-evidence/references/default-template.md +24 -0
- agent_toolkit/data/skills/delivery/project-assessment-evidence/references/example-evidence-map.md +46 -0
- agent_toolkit/data/skills/delivery/spike/SKILL.md +24 -0
- agent_toolkit/data/skills/delivery/spike/references/default-template.md +29 -0
- agent_toolkit/data/skills/delivery/spike/references/example-graphql-federation.md +79 -0
- agent_toolkit/data/skills/delivery/task/SKILL.md +21 -0
- agent_toolkit/data/skills/delivery/task/references/default-template.md +21 -0
- agent_toolkit/data/skills/delivery/task/references/example-nav-system.md +33 -0
- agent_toolkit/data/skills/delivery/technical-unit-assessment/SKILL.md +58 -0
- agent_toolkit/data/skills/delivery/technical-unit-assessment/references/default-template.md +26 -0
- agent_toolkit/data/skills/delivery/technical-unit-assessment/references/example-frontend-assessment.md +112 -0
- agent_toolkit/data/skills/delivery/technical-unit-assessment/references/indicator-groups.md +65 -0
- agent_toolkit/data/skills/delivery/trd/SKILL.md +49 -0
- agent_toolkit/data/skills/delivery/trd/references/default-template.md +31 -0
- agent_toolkit/data/skills/delivery/trd/references/example-api-migration.md +122 -0
- agent_toolkit/data/skills/delivery/user-story/SKILL.md +21 -0
- agent_toolkit/data/skills/delivery/user-story/references/default-template.md +21 -0
- agent_toolkit/data/skills/delivery/user-story/references/example-advanced-filtering.md +37 -0
- agent_toolkit/data/skills/delivery/work-item/SKILL.md +31 -0
- agent_toolkit/data/skills/delivery/work-item/references/default-template.md +21 -0
- agent_toolkit/data/skills/delivery/work-item/references/example-routing-dashboard.md +23 -0
- agent_toolkit/data/skills/delivery/workflow-client-bootstrap/SKILL.md +148 -0
- agent_toolkit/data/skills/delivery/workflow-client-bootstrap/questions.yaml +165 -0
- agent_toolkit/data/skills/delivery/workflow-generic-project/SKILL.md +79 -0
- agent_toolkit/data/skills/design/design-assessment/SKILL.md +150 -0
- agent_toolkit/data/skills/design/design-assessment/references/design-scorecard-template.md +78 -0
- agent_toolkit/data/skills/design/design-improvement/SKILL.md +167 -0
- agent_toolkit/data/skills/design/design-improvement/references/improvement-plan-template.md +113 -0
- agent_toolkit/data/skills/design/figma/LICENSE.txt +2 -0
- agent_toolkit/data/skills/design/figma/NOTICE.txt +25 -0
- agent_toolkit/data/skills/design/figma/SKILL.md +98 -0
- agent_toolkit/data/skills/design/figma/references/figma-mcp-config.md +82 -0
- agent_toolkit/data/skills/design/figma/references/figma-tools-and-prompts.md +34 -0
- agent_toolkit/data/skills/design/figma-code-connect-components/LICENSE.txt +2 -0
- agent_toolkit/data/skills/design/figma-code-connect-components/NOTICE.txt +18 -0
- agent_toolkit/data/skills/design/figma-code-connect-components/SKILL.md +354 -0
- agent_toolkit/data/skills/design/figma-code-connect-components/references/mapping-checklist.md +7 -0
- agent_toolkit/data/skills/design/figma-code-connect-components/scripts/normalize_node_id.py +25 -0
- agent_toolkit/data/skills/design/figma-create-design-system-rules/LICENSE.txt +2 -0
- agent_toolkit/data/skills/design/figma-create-design-system-rules/NOTICE.txt +19 -0
- agent_toolkit/data/skills/design/figma-create-design-system-rules/SKILL.md +554 -0
- agent_toolkit/data/skills/design/figma-create-design-system-rules/references/rule-template.md +15 -0
- agent_toolkit/data/skills/design/figma-create-design-system-rules/scripts/check_agents_md.sh +9 -0
- agent_toolkit/data/skills/design/figma-create-new-file/LICENSE.txt +2 -0
- agent_toolkit/data/skills/design/figma-create-new-file/NOTICE.txt +17 -0
- agent_toolkit/data/skills/design/figma-create-new-file/SKILL.md +72 -0
- agent_toolkit/data/skills/design/figma-implement-design/LICENSE.txt +2 -0
- agent_toolkit/data/skills/design/figma-implement-design/NOTICE.txt +19 -0
- agent_toolkit/data/skills/design/figma-implement-design/SKILL.md +268 -0
- agent_toolkit/data/skills/design/frontend-design/LICENSE.txt +177 -0
- agent_toolkit/data/skills/design/frontend-design/SKILL.md +82 -0
- agent_toolkit/data/skills/design/frontend-design/UPSTREAM.md +14 -0
- agent_toolkit/data/skills/design/frontend-design-review/LICENSE +21 -0
- agent_toolkit/data/skills/design/frontend-design-review/SKILL.md +194 -0
- agent_toolkit/data/skills/design/frontend-design-review/references/pattern-examples.md +21 -0
- agent_toolkit/data/skills/design/frontend-design-review/references/quick-checklist.md +38 -0
- agent_toolkit/data/skills/design/frontend-design-review/references/review-output-format.md +68 -0
- agent_toolkit/data/skills/design/frontend-design-review/references/review-type-modifiers.md +31 -0
- agent_toolkit/data/skills/design/web-design-guidelines/SKILL.md +78 -0
- agent_toolkit/data/skills/design/web-design-guidelines/references/LICENSE +21 -0
- agent_toolkit/data/skills/design/web-design-guidelines/references/web-interface-guidelines.md +180 -0
- agent_toolkit/data/skills/forge/gh-address-comments/LICENSE.txt +202 -0
- agent_toolkit/data/skills/forge/gh-address-comments/NOTICE.txt +20 -0
- agent_toolkit/data/skills/forge/gh-address-comments/SKILL.md +72 -0
- agent_toolkit/data/skills/forge/gh-address-comments/scripts/fetch_comments.py +239 -0
- agent_toolkit/data/skills/forge/gh-contribution-planner/SKILL.md +160 -0
- agent_toolkit/data/skills/forge/gh-contribution-planner/scripts/inspect_contributions.py +818 -0
- agent_toolkit/data/skills/forge/gh-fix-ci/LICENSE.txt +201 -0
- agent_toolkit/data/skills/forge/gh-fix-ci/NOTICE.txt +20 -0
- agent_toolkit/data/skills/forge/gh-fix-ci/SKILL.md +74 -0
- agent_toolkit/data/skills/forge/gh-fix-ci/scripts/inspect_pr_checks.py +509 -0
- agent_toolkit/data/skills/forge/github-cli-workflow/SKILL.md +49 -0
- agent_toolkit/data/skills/forge/gitlab-cli-workflow/SKILL.md +44 -0
- agent_toolkit/data/skills/forge/workflow-client-bootstrap/SKILL.md +14 -0
- agent_toolkit/data/skills/forge/workflow-generic-project/SKILL.md +14 -0
- agent_toolkit/data/skills/forge/worktree/SKILL.md +113 -0
- agent_toolkit/data/skills/integrations/clickup-cli/SKILL.md +78 -0
- agent_toolkit/data/skills/integrations/clickup-cli/references/docs-management.md +57 -0
- agent_toolkit/data/skills/integrations/clickup-cli/references/task-management.md +131 -0
- agent_toolkit/data/skills/integrations/clickup-cli/references/time-tracking.md +38 -0
- agent_toolkit/data/skills/integrations/linear/LICENSE.txt +202 -0
- agent_toolkit/data/skills/integrations/linear/NOTICE.txt +23 -0
- agent_toolkit/data/skills/integrations/linear/SKILL.md +134 -0
- agent_toolkit/data/skills/integrations/mcp/SKILL.md +88 -0
- agent_toolkit/data/skills/integrations/slack-assistant/SKILL.md +138 -0
- agent_toolkit/data/skills/integrations/slack-cli/SKILL.md +109 -0
- agent_toolkit/data/skills/integrations/slack-cli/references/INSTALLATION.md +42 -0
- agent_toolkit/data/skills/loops/loop-runner/SKILL.md +92 -0
- agent_toolkit/data/skills/ops/docs-generator/SKILL.md +120 -0
- agent_toolkit/data/skills/ops/llm-cost-advisor/SKILL.md +87 -0
- agent_toolkit/data/skills/ops/swarm/SKILL.md +108 -0
- agent_toolkit/data/skills/ops/swarm-handoff/SKILL.md +112 -0
- agent_toolkit/data/skills/ops/swarm-observer/SKILL.md +102 -0
- agent_toolkit/data/skills/ops/triage/SKILL.md +119 -0
- agent_toolkit/data/skills/quality/codeql/SKILL.md +106 -0
- agent_toolkit/data/skills/quality/codeql/references/codeql-queries.md +43 -0
- agent_toolkit/data/skills/quality/megalinter/SKILL.md +162 -0
- agent_toolkit/data/skills/quality/megalinter/references/megalinter-images.md +36 -0
- agent_toolkit/data/skills/quality/megalinter/references/megalinter-license.md +41 -0
- agent_toolkit/data/skills/quality/megalinter/references/megalinter-targets.md +66 -0
- agent_toolkit/data/skills/tooling/chrome-devtools/SKILL.md +146 -0
- agent_toolkit/data/skills/tooling/herdr/SKILL.md +109 -0
- agent_toolkit/data/skills/tooling/inventory/SKILL.md +81 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/LICENSE.txt +201 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/NOTICE.txt +27 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/SKILL.md +145 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/assets/experiment-template.ipynb +117 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/assets/tutorial-template.ipynb +115 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/references/experiment-patterns.md +10 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/references/notebook-structure.md +17 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/references/quality-checklist.md +11 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/references/tutorial-patterns.md +9 -0
- agent_toolkit/data/skills/tooling/jupyter-notebook/scripts/new_notebook.py +132 -0
- agent_toolkit/data/skills/tooling/mermaid/SKILL.md +36 -0
- agent_toolkit/data/skills/tooling/playwright-cli/LICENSE.txt +201 -0
- agent_toolkit/data/skills/tooling/playwright-cli/NOTICE.txt +29 -0
- agent_toolkit/data/skills/tooling/playwright-cli/SKILL.md +154 -0
- agent_toolkit/data/skills/tooling/playwright-cli/references/cli.md +116 -0
- agent_toolkit/data/skills/tooling/playwright-cli/references/workflows.md +98 -0
- agent_toolkit/data/skills/tooling/playwright-cli/scripts/executable_playwright_cli.sh +25 -0
- agent_toolkit/data_cache.py +79 -0
- agent_toolkit/data_sync.py +144 -0
- agent_toolkit/installer/__init__.py +0 -0
- agent_toolkit/installer/merge.py +59 -0
- agent_toolkit/installer/receipt.py +89 -0
- agent_toolkit/installer/sources.py +92 -0
- agent_toolkit/installer/tracking.py +63 -0
- agent_toolkit/launcher.py +69 -0
- agent_toolkit/loop/__init__.py +0 -0
- agent_toolkit/loop/budget.py +107 -0
- agent_toolkit/loop/gh_gate.py +923 -0
- agent_toolkit/loop/loop-gh-gate +923 -0
- agent_toolkit/loop/pack.py +65 -0
- agent_toolkit/loop/runner.py +2722 -0
- agent_toolkit/runner/__init__.py +0 -0
- agent_toolkit/runner/dispatcher.py +163 -0
- agent_toolkit/runner/policy.py +356 -0
- agent_toolkit/runner/providers/__init__.py +21 -0
- agent_toolkit/runner/providers/anthropic_provider.py +109 -0
- agent_toolkit/runner/providers/base.py +53 -0
- agent_toolkit/runner/providers/ollama_provider.py +108 -0
- agent_toolkit/runner/providers/openai_provider.py +103 -0
- agent_toolkit/runner/providers/opencode_provider.py +127 -0
- agent_toolkit/swarm/__init__.py +5 -0
- agent_toolkit/swarm/approvals.py +121 -0
- agent_toolkit/swarm/backends/__init__.py +596 -0
- agent_toolkit/swarm/budget.py +83 -0
- agent_toolkit/swarm/cli.py +2448 -0
- agent_toolkit/swarm/config.py +351 -0
- agent_toolkit/swarm/handoff.py +174 -0
- agent_toolkit/swarm/models.py +230 -0
- agent_toolkit/swarm/prompts.py +177 -0
- agent_toolkit/swarm/recipes.py +304 -0
- agent_toolkit/swarm/runner.py +267 -0
- agent_toolkit/swarm/state.py +93 -0
- agent_toolkit/swarm/store.py +148 -0
- agent_toolkit/swarm/worktree.py +105 -0
- agent_toolkit/templates/workspace/.gitignore +19 -0
- agent_toolkit/templates/workspace/AGENTS.md +82 -0
- agent_toolkit/templates/workspace/knowledge/README.md +22 -0
- agent_toolkit/templates/workspace/knowledge/learnings/general.md +3 -0
- agent_toolkit/templates/workspace/knowledge/todos/pending.md +7 -0
- agent_toolkit/templates/workspace/packs/README.md +24 -0
- agent_toolkit/templates/workspace/personas/architect.md +12 -0
- agent_toolkit/templates/workspace/personas/implementer.md +12 -0
- agent_toolkit/templates/workspace/personas/researcher.md +12 -0
- agent_toolkit/templates/workspace/personas/reviewer.md +12 -0
- agent_toolkit_cli-1.11.0.dist-info/METADATA +363 -0
- agent_toolkit_cli-1.11.0.dist-info/RECORD +438 -0
- agent_toolkit_cli-1.11.0.dist-info/WHEEL +4 -0
- agent_toolkit_cli-1.11.0.dist-info/entry_points.txt +4 -0
- agent_toolkit_cli-1.11.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,1052 @@
|
|
|
1
|
+
"""
|
|
2
|
+
doctor — Check system health, AI tool availability, and Swarm prerequisites.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
agent-toolkit doctor [options]
|
|
6
|
+
|
|
7
|
+
Options:
|
|
8
|
+
--json Output results as JSON
|
|
9
|
+
--fix Attempt to auto-fix issues (installs missing profiles)
|
|
10
|
+
--provenance Show full provenance report (SHA/commit immutability, expiry >90d)
|
|
11
|
+
--help Show this help message
|
|
12
|
+
|
|
13
|
+
Exit codes:
|
|
14
|
+
0 — no errors detected
|
|
15
|
+
1 — one or more errors detected
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
from __future__ import annotations
|
|
19
|
+
|
|
20
|
+
import json
|
|
21
|
+
import os
|
|
22
|
+
import pathlib
|
|
23
|
+
import platform
|
|
24
|
+
import shutil
|
|
25
|
+
import subprocess
|
|
26
|
+
import sys
|
|
27
|
+
import urllib.request
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
|
|
30
|
+
from agent_toolkit._paths import toolkit_root
|
|
31
|
+
|
|
32
|
+
# Windows: force UTF-8 output so emoji chars (✓ ✗ ⚠) don't crash
|
|
33
|
+
if sys.platform == "win32":
|
|
34
|
+
try:
|
|
35
|
+
sys.stdout.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
|
|
36
|
+
sys.stderr.reconfigure(encoding="utf-8", errors="replace") # type: ignore[attr-defined]
|
|
37
|
+
except Exception:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
# ---------------------------------------------------------------------------
|
|
42
|
+
# Result model
|
|
43
|
+
# ---------------------------------------------------------------------------
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class CheckResult:
|
|
47
|
+
"""A single health-check item."""
|
|
48
|
+
|
|
49
|
+
STATUS_OK = "ok"
|
|
50
|
+
STATUS_WARN = "warn"
|
|
51
|
+
STATUS_ERR = "error"
|
|
52
|
+
|
|
53
|
+
def __init__(self, category: str, name: str, status: str, detail: str = "") -> None:
|
|
54
|
+
self.category = category
|
|
55
|
+
self.name = name
|
|
56
|
+
self.status = status
|
|
57
|
+
self.detail = detail
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def icon(self) -> str:
|
|
61
|
+
return {"ok": "✓", "warn": "⚠", "error": "✗"}.get(self.status, "?")
|
|
62
|
+
|
|
63
|
+
def to_dict(self) -> dict:
|
|
64
|
+
return {
|
|
65
|
+
"category": self.category,
|
|
66
|
+
"name": self.name,
|
|
67
|
+
"status": self.status,
|
|
68
|
+
"detail": self.detail,
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
# Individual checks
|
|
74
|
+
# ---------------------------------------------------------------------------
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _check_python_version() -> CheckResult:
|
|
78
|
+
vi = sys.version_info
|
|
79
|
+
version_str = f"{vi.major}.{vi.minor}.{vi.micro}"
|
|
80
|
+
if (vi.major, vi.minor) >= (3, 10):
|
|
81
|
+
return CheckResult("system", "python >= 3.10", CheckResult.STATUS_OK, version_str)
|
|
82
|
+
return CheckResult(
|
|
83
|
+
"system", "python >= 3.10", CheckResult.STATUS_ERR, f"Found {version_str} — need 3.10+"
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
def _check_command(category: str, name: str, cmd: str) -> CheckResult:
|
|
88
|
+
path = shutil.which(cmd)
|
|
89
|
+
if path:
|
|
90
|
+
# Try to get version
|
|
91
|
+
try:
|
|
92
|
+
result = subprocess.run([cmd, "--version"], capture_output=True, text=True, timeout=5)
|
|
93
|
+
version = (result.stdout or result.stderr or "").strip().splitlines()[0]
|
|
94
|
+
except Exception:
|
|
95
|
+
version = path
|
|
96
|
+
return CheckResult(category, name, CheckResult.STATUS_OK, version)
|
|
97
|
+
return CheckResult(category, name, CheckResult.STATUS_ERR, f"'{cmd}' not found in PATH")
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
def _check_gh_auth() -> CheckResult:
|
|
101
|
+
if shutil.which("gh") is None:
|
|
102
|
+
return CheckResult("system", "gh auth", CheckResult.STATUS_ERR, "'gh' not found in PATH")
|
|
103
|
+
try:
|
|
104
|
+
result = subprocess.run(
|
|
105
|
+
["gh", "auth", "status"], capture_output=True, text=True, timeout=10
|
|
106
|
+
)
|
|
107
|
+
if result.returncode == 0:
|
|
108
|
+
return CheckResult("system", "gh auth", CheckResult.STATUS_OK, "authenticated")
|
|
109
|
+
return CheckResult(
|
|
110
|
+
"system",
|
|
111
|
+
"gh auth",
|
|
112
|
+
CheckResult.STATUS_WARN,
|
|
113
|
+
"gh installed but not authenticated — run: gh auth login",
|
|
114
|
+
)
|
|
115
|
+
except Exception as exc:
|
|
116
|
+
return CheckResult("system", "gh auth", CheckResult.STATUS_WARN, str(exc))
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _check_ai_tool(name: str, cmd: str) -> CheckResult:
|
|
120
|
+
path = shutil.which(cmd)
|
|
121
|
+
if path:
|
|
122
|
+
return CheckResult("ai_tools", name, CheckResult.STATUS_OK, path)
|
|
123
|
+
return CheckResult(
|
|
124
|
+
"ai_tools", name, CheckResult.STATUS_WARN, f"'{cmd}' not found — tool may not be installed"
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _profile_installed(category: str, name: str, path: Path) -> CheckResult:
|
|
129
|
+
if path.exists():
|
|
130
|
+
return CheckResult(category, name, CheckResult.STATUS_OK, str(path))
|
|
131
|
+
return CheckResult(category, name, CheckResult.STATUS_WARN, f"Not installed: {path}")
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
def _check_profiles() -> list[CheckResult]:
|
|
135
|
+
"""Check whether profiles are installed for each detected tool."""
|
|
136
|
+
results: list[CheckResult] = []
|
|
137
|
+
home = Path.home()
|
|
138
|
+
|
|
139
|
+
# claude-code
|
|
140
|
+
if shutil.which("claude") or (home / ".claude").is_dir():
|
|
141
|
+
results.append(
|
|
142
|
+
_profile_installed("profiles", "claude-code CLAUDE.md", home / ".claude" / "CLAUDE.md")
|
|
143
|
+
)
|
|
144
|
+
# settings.json is user-owned and must not be managed by the installer.
|
|
145
|
+
results.append(
|
|
146
|
+
_profile_installed("profiles", "claude-code agents/", home / ".claude" / "agents")
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# cursor
|
|
150
|
+
if shutil.which("cursor") or (home / ".cursor").is_dir():
|
|
151
|
+
results.append(_profile_installed("profiles", "cursor rules/", home / ".cursor" / "rules"))
|
|
152
|
+
|
|
153
|
+
# opencode
|
|
154
|
+
if shutil.which("opencode") or (home / ".config" / "opencode").is_dir():
|
|
155
|
+
results.append(
|
|
156
|
+
_profile_installed(
|
|
157
|
+
"profiles", "opencode agents/", home / ".config" / "opencode" / "agents"
|
|
158
|
+
)
|
|
159
|
+
)
|
|
160
|
+
results.append(
|
|
161
|
+
_profile_installed(
|
|
162
|
+
"profiles",
|
|
163
|
+
"opencode opencode.json",
|
|
164
|
+
home / ".config" / "opencode" / "opencode.json",
|
|
165
|
+
)
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
# windsurf
|
|
169
|
+
windsurf_dir: Path | None = None
|
|
170
|
+
if (home / ".codeium" / "windsurf").is_dir():
|
|
171
|
+
windsurf_dir = home / ".codeium" / "windsurf"
|
|
172
|
+
elif (home / ".windsurf").is_dir():
|
|
173
|
+
windsurf_dir = home / ".windsurf"
|
|
174
|
+
elif shutil.which("windsurf"):
|
|
175
|
+
windsurf_dir = home / ".codeium" / "windsurf"
|
|
176
|
+
|
|
177
|
+
if windsurf_dir is not None:
|
|
178
|
+
results.append(_profile_installed("profiles", "windsurf rules/", windsurf_dir / "rules"))
|
|
179
|
+
results.append(
|
|
180
|
+
_profile_installed("profiles", "windsurf memories/", windsurf_dir / "memories")
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
# pi
|
|
184
|
+
if shutil.which("pi") or (home / ".pi").is_dir():
|
|
185
|
+
results.append(
|
|
186
|
+
_profile_installed("profiles", "pi skills/", home / ".pi" / "agent" / "skills")
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
# muse (Meta) — ~/.config/muse/skills and project .agents/skills
|
|
190
|
+
if shutil.which("muse") or (home / ".config" / "muse").is_dir() or (home / ".agents").is_dir():
|
|
191
|
+
results.append(
|
|
192
|
+
_profile_installed(
|
|
193
|
+
"profiles", "muse skills (user)", home / ".config" / "muse" / "skills"
|
|
194
|
+
)
|
|
195
|
+
)
|
|
196
|
+
# project scope: .agents/skills in CWD or workspace root
|
|
197
|
+
# check CWD .agents/skills and toolkit-relative if available
|
|
198
|
+
cwd_agents = Path.cwd() / ".agents" / "skills"
|
|
199
|
+
if cwd_agents.is_dir() or (home / ".agents").is_dir():
|
|
200
|
+
# use CWD if present, else home fallback for display
|
|
201
|
+
agents_path = cwd_agents if cwd_agents.is_dir() else home / ".agents" / "skills"
|
|
202
|
+
results.append(_profile_installed("profiles", "muse skills (project)", agents_path))
|
|
203
|
+
else:
|
|
204
|
+
results.append(
|
|
205
|
+
CheckResult(
|
|
206
|
+
"profiles",
|
|
207
|
+
"muse skills (project)",
|
|
208
|
+
CheckResult.STATUS_WARN,
|
|
209
|
+
"Not found: .agents/skills (project scope — optional)",
|
|
210
|
+
)
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
return results
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def _check_loop_runtime(toolkit_dir: Path | None) -> list[CheckResult]:
|
|
217
|
+
results: list[CheckResult] = []
|
|
218
|
+
|
|
219
|
+
# Check if `agent-toolkit loop` resolves (basic import test)
|
|
220
|
+
try:
|
|
221
|
+
subprocess.run(
|
|
222
|
+
[sys.executable, "-m", "agent_toolkit.cli", "loop", "--help"],
|
|
223
|
+
capture_output=True,
|
|
224
|
+
timeout=10,
|
|
225
|
+
)
|
|
226
|
+
results.append(
|
|
227
|
+
CheckResult(
|
|
228
|
+
"loops",
|
|
229
|
+
"loop subcommand",
|
|
230
|
+
CheckResult.STATUS_OK,
|
|
231
|
+
"agent-toolkit loop --help succeeded",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
except Exception as exc:
|
|
235
|
+
results.append(
|
|
236
|
+
CheckResult(
|
|
237
|
+
"loops",
|
|
238
|
+
"loop subcommand",
|
|
239
|
+
CheckResult.STATUS_WARN,
|
|
240
|
+
f"Could not run loop command: {exc}",
|
|
241
|
+
)
|
|
242
|
+
)
|
|
243
|
+
|
|
244
|
+
# Check loops/ templates
|
|
245
|
+
if toolkit_dir:
|
|
246
|
+
loops_dir = toolkit_dir / "loops"
|
|
247
|
+
if loops_dir.is_dir():
|
|
248
|
+
count = sum(1 for d in loops_dir.iterdir() if d.is_dir())
|
|
249
|
+
results.append(
|
|
250
|
+
CheckResult(
|
|
251
|
+
"loops",
|
|
252
|
+
"loop templates",
|
|
253
|
+
CheckResult.STATUS_OK,
|
|
254
|
+
f"{count} template(s) found in {loops_dir}",
|
|
255
|
+
)
|
|
256
|
+
)
|
|
257
|
+
else:
|
|
258
|
+
results.append(
|
|
259
|
+
CheckResult(
|
|
260
|
+
"loops",
|
|
261
|
+
"loop templates",
|
|
262
|
+
CheckResult.STATUS_WARN,
|
|
263
|
+
f"loops/ directory not found at {loops_dir}",
|
|
264
|
+
)
|
|
265
|
+
)
|
|
266
|
+
else:
|
|
267
|
+
results.append(
|
|
268
|
+
CheckResult(
|
|
269
|
+
"loops",
|
|
270
|
+
"loop templates",
|
|
271
|
+
CheckResult.STATUS_WARN,
|
|
272
|
+
"Toolkit directory not found — cannot check loop templates",
|
|
273
|
+
)
|
|
274
|
+
)
|
|
275
|
+
|
|
276
|
+
return results
|
|
277
|
+
|
|
278
|
+
|
|
279
|
+
def _check_llm_providers() -> list[CheckResult]:
|
|
280
|
+
results: list[CheckResult] = []
|
|
281
|
+
|
|
282
|
+
# API keys
|
|
283
|
+
for var in ("ANTHROPIC_API_KEY", "OPENAI_API_KEY"):
|
|
284
|
+
val = os.environ.get(var, "")
|
|
285
|
+
if val:
|
|
286
|
+
masked = val[:8] + "..." if len(val) > 8 else "***"
|
|
287
|
+
results.append(CheckResult("llm", var, CheckResult.STATUS_OK, f"set ({masked})"))
|
|
288
|
+
else:
|
|
289
|
+
results.append(CheckResult("llm", var, CheckResult.STATUS_WARN, "not set"))
|
|
290
|
+
|
|
291
|
+
# Ollama
|
|
292
|
+
try:
|
|
293
|
+
req = urllib.request.Request("http://localhost:11434", method="HEAD")
|
|
294
|
+
req.add_header("User-Agent", "agent-toolkit-doctor/1")
|
|
295
|
+
with urllib.request.urlopen(req, timeout=3):
|
|
296
|
+
pass
|
|
297
|
+
results.append(
|
|
298
|
+
CheckResult("llm", "ollama", CheckResult.STATUS_OK, "running at localhost:11434")
|
|
299
|
+
)
|
|
300
|
+
except Exception:
|
|
301
|
+
results.append(
|
|
302
|
+
CheckResult(
|
|
303
|
+
"llm", "ollama", CheckResult.STATUS_WARN, "not reachable at localhost:11434"
|
|
304
|
+
)
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
# opencode socket (Linux/macOS only — os.getuid() doesn't exist on Windows)
|
|
308
|
+
try:
|
|
309
|
+
uid = os.getuid() # type: ignore[attr-defined]
|
|
310
|
+
except AttributeError:
|
|
311
|
+
uid = 0
|
|
312
|
+
xdg_runtime = os.environ.get("XDG_RUNTIME_DIR", f"/run/user/{uid}")
|
|
313
|
+
socket_paths = [
|
|
314
|
+
Path(xdg_runtime) / "opencode.sock",
|
|
315
|
+
Path.home() / ".config" / "opencode" / "opencode.sock",
|
|
316
|
+
]
|
|
317
|
+
for sp in socket_paths:
|
|
318
|
+
if sp.exists():
|
|
319
|
+
results.append(CheckResult("llm", "opencode socket", CheckResult.STATUS_OK, str(sp)))
|
|
320
|
+
break
|
|
321
|
+
else:
|
|
322
|
+
results.append(
|
|
323
|
+
CheckResult(
|
|
324
|
+
"llm",
|
|
325
|
+
"opencode socket",
|
|
326
|
+
CheckResult.STATUS_WARN,
|
|
327
|
+
"socket not found (opencode may not be running)",
|
|
328
|
+
)
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
return results
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
def _check_mcp() -> list[CheckResult]:
|
|
335
|
+
results: list[CheckResult] = []
|
|
336
|
+
config_path = Path.home() / ".config" / "agent-toolkit" / "mcp-config.json"
|
|
337
|
+
|
|
338
|
+
if not config_path.exists():
|
|
339
|
+
results.append(
|
|
340
|
+
CheckResult(
|
|
341
|
+
"mcp",
|
|
342
|
+
"mcp-config.json",
|
|
343
|
+
CheckResult.STATUS_WARN,
|
|
344
|
+
f"Not found: {config_path} (run: agent-toolkit mcp setup <provider>)",
|
|
345
|
+
)
|
|
346
|
+
)
|
|
347
|
+
return results
|
|
348
|
+
|
|
349
|
+
try:
|
|
350
|
+
data = json.loads(config_path.read_text())
|
|
351
|
+
providers = data.get("providers", {})
|
|
352
|
+
if not providers:
|
|
353
|
+
results.append(
|
|
354
|
+
CheckResult(
|
|
355
|
+
"mcp",
|
|
356
|
+
"mcp providers",
|
|
357
|
+
CheckResult.STATUS_WARN,
|
|
358
|
+
"Config exists but no providers configured",
|
|
359
|
+
)
|
|
360
|
+
)
|
|
361
|
+
return results
|
|
362
|
+
for name, cfg in providers.items():
|
|
363
|
+
enabled = cfg.get("enabled", False)
|
|
364
|
+
validated = cfg.get("validated_at", "never")
|
|
365
|
+
status = CheckResult.STATUS_OK if enabled else CheckResult.STATUS_WARN
|
|
366
|
+
results.append(
|
|
367
|
+
CheckResult(
|
|
368
|
+
"mcp",
|
|
369
|
+
f"provider:{name}",
|
|
370
|
+
status,
|
|
371
|
+
f"enabled={enabled}, validated_at={validated}",
|
|
372
|
+
)
|
|
373
|
+
)
|
|
374
|
+
except (json.JSONDecodeError, OSError) as exc:
|
|
375
|
+
results.append(
|
|
376
|
+
CheckResult(
|
|
377
|
+
"mcp", "mcp-config.json", CheckResult.STATUS_ERR, f"Cannot read config: {exc}"
|
|
378
|
+
)
|
|
379
|
+
)
|
|
380
|
+
|
|
381
|
+
return results
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
def _check_scheduled_loops() -> list[CheckResult]:
|
|
385
|
+
results: list[CheckResult] = []
|
|
386
|
+
system = platform.system()
|
|
387
|
+
|
|
388
|
+
if system == "Linux":
|
|
389
|
+
timer_dir = Path.home() / ".config" / "systemd" / "user"
|
|
390
|
+
if timer_dir.is_dir():
|
|
391
|
+
timers = list(timer_dir.glob("agent-toolkit-*.timer"))
|
|
392
|
+
if timers:
|
|
393
|
+
for t in sorted(timers):
|
|
394
|
+
results.append(CheckResult("scheduled", t.name, CheckResult.STATUS_OK, str(t)))
|
|
395
|
+
else:
|
|
396
|
+
results.append(
|
|
397
|
+
CheckResult(
|
|
398
|
+
"scheduled",
|
|
399
|
+
"systemd timers",
|
|
400
|
+
CheckResult.STATUS_WARN,
|
|
401
|
+
"No agent-toolkit-*.timer files found",
|
|
402
|
+
)
|
|
403
|
+
)
|
|
404
|
+
else:
|
|
405
|
+
results.append(
|
|
406
|
+
CheckResult(
|
|
407
|
+
"scheduled",
|
|
408
|
+
"systemd user dir",
|
|
409
|
+
CheckResult.STATUS_WARN,
|
|
410
|
+
f"Not found: {timer_dir}",
|
|
411
|
+
)
|
|
412
|
+
)
|
|
413
|
+
elif system == "Darwin":
|
|
414
|
+
launchd_dir = Path.home() / "Library" / "LaunchAgents"
|
|
415
|
+
if launchd_dir.is_dir():
|
|
416
|
+
plists = list(launchd_dir.glob("com.agent-toolkit.*.plist"))
|
|
417
|
+
if plists:
|
|
418
|
+
for p in sorted(plists):
|
|
419
|
+
results.append(CheckResult("scheduled", p.name, CheckResult.STATUS_OK, str(p)))
|
|
420
|
+
else:
|
|
421
|
+
results.append(
|
|
422
|
+
CheckResult(
|
|
423
|
+
"scheduled",
|
|
424
|
+
"launchd plists",
|
|
425
|
+
CheckResult.STATUS_WARN,
|
|
426
|
+
"No com.agent-toolkit.*.plist files found",
|
|
427
|
+
)
|
|
428
|
+
)
|
|
429
|
+
else:
|
|
430
|
+
results.append(
|
|
431
|
+
CheckResult(
|
|
432
|
+
"scheduled",
|
|
433
|
+
"LaunchAgents dir",
|
|
434
|
+
CheckResult.STATUS_WARN,
|
|
435
|
+
f"Not found: {launchd_dir}",
|
|
436
|
+
)
|
|
437
|
+
)
|
|
438
|
+
else:
|
|
439
|
+
results.append(
|
|
440
|
+
CheckResult(
|
|
441
|
+
"scheduled",
|
|
442
|
+
"scheduled loops",
|
|
443
|
+
CheckResult.STATUS_WARN,
|
|
444
|
+
f"Unsupported platform: {system}",
|
|
445
|
+
)
|
|
446
|
+
)
|
|
447
|
+
|
|
448
|
+
return results
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _check_swarm() -> list[CheckResult]:
|
|
452
|
+
results: list[CheckResult] = []
|
|
453
|
+
|
|
454
|
+
tmux_path = shutil.which("tmux")
|
|
455
|
+
if tmux_path:
|
|
456
|
+
try:
|
|
457
|
+
result = subprocess.run(["tmux", "-V"], capture_output=True, text=True, timeout=5)
|
|
458
|
+
version = result.stdout.strip()
|
|
459
|
+
results.append(CheckResult("swarm", "tmux", CheckResult.STATUS_OK, version))
|
|
460
|
+
except Exception:
|
|
461
|
+
results.append(CheckResult("swarm", "tmux", CheckResult.STATUS_OK, str(tmux_path)))
|
|
462
|
+
else:
|
|
463
|
+
results.append(
|
|
464
|
+
CheckResult(
|
|
465
|
+
"swarm",
|
|
466
|
+
"tmux",
|
|
467
|
+
CheckResult.STATUS_WARN,
|
|
468
|
+
"not found — install with: brew install tmux / apt install tmux",
|
|
469
|
+
)
|
|
470
|
+
)
|
|
471
|
+
|
|
472
|
+
herdr_path = shutil.which("herdr")
|
|
473
|
+
if herdr_path:
|
|
474
|
+
try:
|
|
475
|
+
result = subprocess.run(
|
|
476
|
+
["herdr", "--version"], capture_output=True, text=True, timeout=5
|
|
477
|
+
)
|
|
478
|
+
version = result.stdout.strip() or result.stderr.strip()
|
|
479
|
+
results.append(CheckResult("swarm", "herdr", CheckResult.STATUS_OK, version))
|
|
480
|
+
except Exception:
|
|
481
|
+
results.append(CheckResult("swarm", "herdr", CheckResult.STATUS_OK, str(herdr_path)))
|
|
482
|
+
else:
|
|
483
|
+
results.append(
|
|
484
|
+
CheckResult(
|
|
485
|
+
"swarm",
|
|
486
|
+
"herdr",
|
|
487
|
+
CheckResult.STATUS_WARN,
|
|
488
|
+
"not found — install from https://herdr.dev/docs/install/",
|
|
489
|
+
)
|
|
490
|
+
)
|
|
491
|
+
|
|
492
|
+
try:
|
|
493
|
+
plan_result = subprocess.run(
|
|
494
|
+
[
|
|
495
|
+
sys.executable,
|
|
496
|
+
"-m",
|
|
497
|
+
"agent_toolkit",
|
|
498
|
+
"swarm",
|
|
499
|
+
"plan",
|
|
500
|
+
"--runner",
|
|
501
|
+
"skeleton",
|
|
502
|
+
"--ui",
|
|
503
|
+
"tmux",
|
|
504
|
+
"doctor check",
|
|
505
|
+
],
|
|
506
|
+
capture_output=True,
|
|
507
|
+
text=True,
|
|
508
|
+
timeout=15,
|
|
509
|
+
)
|
|
510
|
+
if plan_result.returncode == 0:
|
|
511
|
+
results.append(
|
|
512
|
+
CheckResult(
|
|
513
|
+
"swarm",
|
|
514
|
+
"swarm plan (offline)",
|
|
515
|
+
CheckResult.STATUS_OK,
|
|
516
|
+
"skeleton runner plan succeeded",
|
|
517
|
+
)
|
|
518
|
+
)
|
|
519
|
+
else:
|
|
520
|
+
stderr = (plan_result.stderr or "").strip().splitlines()
|
|
521
|
+
detail = stderr[-1] if stderr else f"plan exited {plan_result.returncode}"
|
|
522
|
+
results.append(
|
|
523
|
+
CheckResult(
|
|
524
|
+
"swarm",
|
|
525
|
+
"swarm plan (offline)",
|
|
526
|
+
CheckResult.STATUS_WARN,
|
|
527
|
+
detail,
|
|
528
|
+
)
|
|
529
|
+
)
|
|
530
|
+
except Exception as exc:
|
|
531
|
+
results.append(
|
|
532
|
+
CheckResult(
|
|
533
|
+
"swarm",
|
|
534
|
+
"swarm plan (offline)",
|
|
535
|
+
CheckResult.STATUS_WARN,
|
|
536
|
+
f"could not run plan: {exc}",
|
|
537
|
+
)
|
|
538
|
+
)
|
|
539
|
+
|
|
540
|
+
return results
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
# ---------------------------------------------------------------------------
|
|
544
|
+
# Formatting
|
|
545
|
+
# ---------------------------------------------------------------------------
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
def _print_section(title: str) -> None:
|
|
549
|
+
print(f"\n── {title} ──")
|
|
550
|
+
|
|
551
|
+
|
|
552
|
+
def _check_provenance(toolkit_dir: Path | None = None) -> list[CheckResult]:
|
|
553
|
+
"""Provenance checks per §52: SHA/commit immutability, expiry/staleness >90d, UNKNOWN/mutable ref."""
|
|
554
|
+
results: list[CheckResult] = []
|
|
555
|
+
# Try multiple roots: toolkit_root() (data), its parent (repo), cwd, and file's repo root
|
|
556
|
+
candidates = []
|
|
557
|
+
tr = toolkit_dir or toolkit_root()
|
|
558
|
+
candidates.append(tr / "capabilities" / "upstream.lock")
|
|
559
|
+
candidates.append(tr.parent / "capabilities" / "upstream.lock")
|
|
560
|
+
candidates.append(Path.cwd() / "capabilities" / "upstream.lock")
|
|
561
|
+
# also try toolkit_root().parents[2] if data is deep
|
|
562
|
+
for anc in tr.parents:
|
|
563
|
+
candidates.append(anc / "capabilities" / "upstream.lock")
|
|
564
|
+
# Also try repo root via _paths toolkit_root parent chain
|
|
565
|
+
import pathlib as _P
|
|
566
|
+
|
|
567
|
+
for anc in _P.Path(__file__).resolve().parents:
|
|
568
|
+
candidates.append(anc / "capabilities" / "upstream.lock")
|
|
569
|
+
if (anc / "capabilities" / "upstream.lock").exists():
|
|
570
|
+
break
|
|
571
|
+
lock_path = next((c for c in candidates if c.exists()), candidates[0])
|
|
572
|
+
if not lock_path.exists():
|
|
573
|
+
results.append(
|
|
574
|
+
CheckResult(
|
|
575
|
+
"provenance",
|
|
576
|
+
"upstream.lock exists",
|
|
577
|
+
CheckResult.STATUS_ERR,
|
|
578
|
+
f"Missing: {lock_path}",
|
|
579
|
+
)
|
|
580
|
+
)
|
|
581
|
+
return results
|
|
582
|
+
try:
|
|
583
|
+
try:
|
|
584
|
+
import yaml as _yaml
|
|
585
|
+
|
|
586
|
+
has_yaml = True
|
|
587
|
+
except ImportError:
|
|
588
|
+
_yaml = None
|
|
589
|
+
has_yaml = False
|
|
590
|
+
import json as _json
|
|
591
|
+
|
|
592
|
+
raw = lock_path.read_text()
|
|
593
|
+
if has_yaml:
|
|
594
|
+
try:
|
|
595
|
+
data = _yaml.safe_load(raw) or {}
|
|
596
|
+
except Exception:
|
|
597
|
+
data = _json.loads(raw)
|
|
598
|
+
else:
|
|
599
|
+
# yaml not available in this env (e.g., minimal install) — try json, else warn
|
|
600
|
+
try:
|
|
601
|
+
data = _json.loads(raw)
|
|
602
|
+
except Exception:
|
|
603
|
+
results.append(
|
|
604
|
+
CheckResult(
|
|
605
|
+
"provenance",
|
|
606
|
+
"upstream.lock parse",
|
|
607
|
+
CheckResult.STATUS_WARN,
|
|
608
|
+
"yaml not installed — install pyyaml to validate provenance (pip install pyyaml)",
|
|
609
|
+
)
|
|
610
|
+
)
|
|
611
|
+
return results
|
|
612
|
+
# check version and upstreams
|
|
613
|
+
if data.get("version") != 1:
|
|
614
|
+
results.append(
|
|
615
|
+
CheckResult(
|
|
616
|
+
"provenance",
|
|
617
|
+
"lock version",
|
|
618
|
+
CheckResult.STATUS_WARN,
|
|
619
|
+
f"version={data.get('version')} expected 1",
|
|
620
|
+
)
|
|
621
|
+
)
|
|
622
|
+
else:
|
|
623
|
+
results.append(
|
|
624
|
+
CheckResult("provenance", "lock version", CheckResult.STATUS_OK, "version 1")
|
|
625
|
+
)
|
|
626
|
+
caps = data.get("capabilities", data.get("upstreams", []))
|
|
627
|
+
if not caps:
|
|
628
|
+
# fallback: count files
|
|
629
|
+
results.append(
|
|
630
|
+
CheckResult(
|
|
631
|
+
"provenance",
|
|
632
|
+
"lock has entries",
|
|
633
|
+
CheckResult.STATUS_WARN,
|
|
634
|
+
"no capabilities/upstreams",
|
|
635
|
+
)
|
|
636
|
+
)
|
|
637
|
+
else:
|
|
638
|
+
results.append(
|
|
639
|
+
CheckResult(
|
|
640
|
+
"provenance", "lock entries", CheckResult.STATUS_OK, f"{len(caps)} entries"
|
|
641
|
+
)
|
|
642
|
+
)
|
|
643
|
+
# Check each source for SHA40 and staleness >90d
|
|
644
|
+
import datetime as _dt
|
|
645
|
+
|
|
646
|
+
now = _dt.datetime.now(_dt.timezone.utc)
|
|
647
|
+
for cap in caps if isinstance(caps, list) else []:
|
|
648
|
+
# caps may be dict with sources
|
|
649
|
+
sources = cap.get("sources", []) if isinstance(cap, dict) else []
|
|
650
|
+
for src in sources:
|
|
651
|
+
ref = src.get("ref", "")
|
|
652
|
+
commit = src.get("commit", "")
|
|
653
|
+
# mutable ref check: tag without commit or branch name
|
|
654
|
+
if ref and not commit:
|
|
655
|
+
results.append(
|
|
656
|
+
CheckResult(
|
|
657
|
+
"provenance",
|
|
658
|
+
f"provenance:{cap.get('id', '?')}:{src.get('id', '?')} immutable",
|
|
659
|
+
CheckResult.STATUS_ERR,
|
|
660
|
+
f"ref={ref} without commit — mutable",
|
|
661
|
+
)
|
|
662
|
+
)
|
|
663
|
+
elif commit and len(commit) != 40:
|
|
664
|
+
results.append(
|
|
665
|
+
CheckResult(
|
|
666
|
+
"provenance",
|
|
667
|
+
f"provenance:{cap.get('id', '?')} SHA",
|
|
668
|
+
CheckResult.STATUS_ERR,
|
|
669
|
+
f"commit {commit[:8]} not SHA40",
|
|
670
|
+
)
|
|
671
|
+
)
|
|
672
|
+
# staleness: check last_checked or reviewed_at
|
|
673
|
+
for date_key in ("last_checked", "reviewed_at", "last_activity"):
|
|
674
|
+
val = src.get(date_key) or cap.get(date_key)
|
|
675
|
+
if val:
|
|
676
|
+
try:
|
|
677
|
+
dt = _dt.datetime.fromisoformat(val.replace("Z", "+00:00"))
|
|
678
|
+
age = (now - dt).days
|
|
679
|
+
if age > 90:
|
|
680
|
+
results.append(
|
|
681
|
+
CheckResult(
|
|
682
|
+
"provenance",
|
|
683
|
+
f"provenance:{cap.get('id', '?')} freshness",
|
|
684
|
+
CheckResult.STATUS_WARN,
|
|
685
|
+
f"{date_key} {age}d ago (>90d) — consider update",
|
|
686
|
+
)
|
|
687
|
+
)
|
|
688
|
+
break
|
|
689
|
+
except Exception:
|
|
690
|
+
pass
|
|
691
|
+
# Also check SKILL.md declarations have origin
|
|
692
|
+
# (light — ensure file exists)
|
|
693
|
+
results.append(
|
|
694
|
+
CheckResult(
|
|
695
|
+
"provenance",
|
|
696
|
+
"provenance: doctor --provenance",
|
|
697
|
+
CheckResult.STATUS_OK,
|
|
698
|
+
"run with --provenance for full SHA/commit + expiry report",
|
|
699
|
+
)
|
|
700
|
+
)
|
|
701
|
+
except Exception as exc:
|
|
702
|
+
results.append(
|
|
703
|
+
CheckResult("provenance", "upstream.lock parse", CheckResult.STATUS_ERR, str(exc))
|
|
704
|
+
)
|
|
705
|
+
return results
|
|
706
|
+
|
|
707
|
+
|
|
708
|
+
def _check_products_and_packs(toolkit_dir: Path | None = None) -> list[CheckResult]:
|
|
709
|
+
"""Pack consistency + product catalog per #387."""
|
|
710
|
+
results: list[CheckResult] = []
|
|
711
|
+
# Locate repo root (not data dir) for distributions, skills, packs
|
|
712
|
+
candidates = []
|
|
713
|
+
tr = toolkit_dir or toolkit_root()
|
|
714
|
+
candidates.append(tr)
|
|
715
|
+
candidates.append(tr.parent)
|
|
716
|
+
candidates.append(Path.cwd())
|
|
717
|
+
for anc in tr.parents:
|
|
718
|
+
candidates.append(anc)
|
|
719
|
+
for anc in pathlib.Path(__file__).resolve().parents:
|
|
720
|
+
candidates.append(anc)
|
|
721
|
+
root = next((c for c in candidates if (c / "distributions" / "products.yaml").exists()), tr)
|
|
722
|
+
prod_path = root / "distributions" / "products.yaml"
|
|
723
|
+
if not prod_path.exists():
|
|
724
|
+
results.append(
|
|
725
|
+
CheckResult("packs", "products.yaml exists", CheckResult.STATUS_ERR, str(prod_path))
|
|
726
|
+
)
|
|
727
|
+
else:
|
|
728
|
+
results.append(
|
|
729
|
+
CheckResult("packs", "products.yaml exists", CheckResult.STATUS_OK, str(prod_path))
|
|
730
|
+
)
|
|
731
|
+
# check complete covers all skills via catalog
|
|
732
|
+
try:
|
|
733
|
+
try:
|
|
734
|
+
import yaml as _yaml
|
|
735
|
+
except ImportError:
|
|
736
|
+
results.append(
|
|
737
|
+
CheckResult(
|
|
738
|
+
"packs",
|
|
739
|
+
"products.yaml parse",
|
|
740
|
+
CheckResult.STATUS_WARN,
|
|
741
|
+
"yaml not installed — install pyyaml to validate packs",
|
|
742
|
+
)
|
|
743
|
+
)
|
|
744
|
+
return results
|
|
745
|
+
prod_data = _yaml.safe_load(prod_path.read_text())
|
|
746
|
+
complete = next(
|
|
747
|
+
(
|
|
748
|
+
p
|
|
749
|
+
for p in prod_data.get("products", [])
|
|
750
|
+
if p.get("id") == "agent-toolkit-complete"
|
|
751
|
+
),
|
|
752
|
+
None,
|
|
753
|
+
)
|
|
754
|
+
if complete:
|
|
755
|
+
included = set(complete.get("includes", {}).get("skills", []))
|
|
756
|
+
# count skills on disk
|
|
757
|
+
skills = list((root / "skills").rglob("SKILL.md"))
|
|
758
|
+
skill_ids = {
|
|
759
|
+
f"{p.parts[-3]}/{p.parts[-2]}" if len(p.parts) >= 3 else p.parent.name
|
|
760
|
+
for p in skills
|
|
761
|
+
}
|
|
762
|
+
# also handle skills/*/* flat?
|
|
763
|
+
missing = skill_ids - included
|
|
764
|
+
if missing:
|
|
765
|
+
results.append(
|
|
766
|
+
CheckResult(
|
|
767
|
+
"packs",
|
|
768
|
+
"complete covers all skills",
|
|
769
|
+
CheckResult.STATUS_ERR,
|
|
770
|
+
f"missing {sorted(missing)[:5]}",
|
|
771
|
+
)
|
|
772
|
+
)
|
|
773
|
+
else:
|
|
774
|
+
results.append(
|
|
775
|
+
CheckResult(
|
|
776
|
+
"packs",
|
|
777
|
+
"complete covers all skills",
|
|
778
|
+
CheckResult.STATUS_OK,
|
|
779
|
+
f"{len(included)} skills",
|
|
780
|
+
)
|
|
781
|
+
)
|
|
782
|
+
except Exception as exc:
|
|
783
|
+
results.append(
|
|
784
|
+
CheckResult("packs", "products.yaml parse", CheckResult.STATUS_WARN, str(exc))
|
|
785
|
+
)
|
|
786
|
+
# packs/* config.yaml
|
|
787
|
+
packs_dir = root / "packs"
|
|
788
|
+
if packs_dir.is_dir():
|
|
789
|
+
for pack_dir in sorted(packs_dir.iterdir()):
|
|
790
|
+
if pack_dir.is_dir():
|
|
791
|
+
cfg = pack_dir / "config.yaml"
|
|
792
|
+
if not cfg.exists():
|
|
793
|
+
results.append(
|
|
794
|
+
CheckResult(
|
|
795
|
+
"packs",
|
|
796
|
+
f"pack:{pack_dir.name} config",
|
|
797
|
+
CheckResult.STATUS_WARN,
|
|
798
|
+
"missing config.yaml",
|
|
799
|
+
)
|
|
800
|
+
)
|
|
801
|
+
else:
|
|
802
|
+
results.append(
|
|
803
|
+
CheckResult(
|
|
804
|
+
"packs", f"pack:{pack_dir.name} config", CheckResult.STATUS_OK, str(cfg)
|
|
805
|
+
)
|
|
806
|
+
)
|
|
807
|
+
# loops check
|
|
808
|
+
try:
|
|
809
|
+
try:
|
|
810
|
+
import yaml as _yaml2
|
|
811
|
+
except ImportError:
|
|
812
|
+
continue
|
|
813
|
+
cfg_data = _yaml2.safe_load(cfg.read_text()) or {}
|
|
814
|
+
for loop_name in cfg_data.get("loops", {}).keys():
|
|
815
|
+
if not (root / "loops" / loop_name).is_dir():
|
|
816
|
+
results.append(
|
|
817
|
+
CheckResult(
|
|
818
|
+
"packs",
|
|
819
|
+
f"pack:{pack_dir.name} loop:{loop_name}",
|
|
820
|
+
CheckResult.STATUS_WARN,
|
|
821
|
+
"loop dir missing",
|
|
822
|
+
)
|
|
823
|
+
)
|
|
824
|
+
except Exception:
|
|
825
|
+
pass
|
|
826
|
+
return results
|
|
827
|
+
|
|
828
|
+
|
|
829
|
+
def _check_mcp_registry(toolkit_dir: Path | None = None) -> list[CheckResult]:
|
|
830
|
+
"""MCP registry vs installed runtime per #387."""
|
|
831
|
+
results: list[CheckResult] = []
|
|
832
|
+
root = toolkit_dir or toolkit_root()
|
|
833
|
+
reg_dir = root / "mcp" / "registry"
|
|
834
|
+
if not reg_dir.is_dir():
|
|
835
|
+
results.append(
|
|
836
|
+
CheckResult("mcp", "mcp/registry dir", CheckResult.STATUS_WARN, str(reg_dir))
|
|
837
|
+
)
|
|
838
|
+
return results
|
|
839
|
+
yaml_files = list(reg_dir.glob("*.yaml"))
|
|
840
|
+
results.append(
|
|
841
|
+
CheckResult(
|
|
842
|
+
"mcp", "mcp/registry count", CheckResult.STATUS_OK, f"{len(yaml_files)} providers"
|
|
843
|
+
)
|
|
844
|
+
)
|
|
845
|
+
for yf in sorted(yaml_files):
|
|
846
|
+
try:
|
|
847
|
+
try:
|
|
848
|
+
import yaml as _yaml3
|
|
849
|
+
except ImportError:
|
|
850
|
+
results.append(
|
|
851
|
+
CheckResult(
|
|
852
|
+
"mcp",
|
|
853
|
+
"mcp/registry count",
|
|
854
|
+
CheckResult.STATUS_WARN,
|
|
855
|
+
"yaml not installed — install pyyaml to validate mcp",
|
|
856
|
+
)
|
|
857
|
+
)
|
|
858
|
+
break
|
|
859
|
+
data = _yaml3.safe_load(yf.read_text()) or {}
|
|
860
|
+
# require id, tools or implementation
|
|
861
|
+
if not data.get("id"):
|
|
862
|
+
results.append(
|
|
863
|
+
CheckResult("mcp", f"mcp:{yf.stem} id", CheckResult.STATUS_ERR, "missing id")
|
|
864
|
+
)
|
|
865
|
+
# check healthcheck or transport
|
|
866
|
+
if "transport" not in data and "implementation" not in data:
|
|
867
|
+
results.append(
|
|
868
|
+
CheckResult(
|
|
869
|
+
"mcp",
|
|
870
|
+
f"mcp:{yf.stem} transport",
|
|
871
|
+
CheckResult.STATUS_WARN,
|
|
872
|
+
"no transport/implementation",
|
|
873
|
+
)
|
|
874
|
+
)
|
|
875
|
+
except Exception as exc:
|
|
876
|
+
results.append(
|
|
877
|
+
CheckResult("mcp", f"mcp:{yf.stem} parse", CheckResult.STATUS_ERR, str(exc))
|
|
878
|
+
)
|
|
879
|
+
return results
|
|
880
|
+
|
|
881
|
+
|
|
882
|
+
def _print_result(r: CheckResult) -> None:
|
|
883
|
+
detail = f" ({r.detail})" if r.detail else ""
|
|
884
|
+
print(f" {r.icon} {r.name}{detail}")
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
# ---------------------------------------------------------------------------
|
|
888
|
+
# Argument parsing
|
|
889
|
+
# ---------------------------------------------------------------------------
|
|
890
|
+
|
|
891
|
+
_PARSE_HELP = object()
|
|
892
|
+
_PARSE_ERROR = object()
|
|
893
|
+
|
|
894
|
+
|
|
895
|
+
def _parse_args(args: list[str]):
|
|
896
|
+
"""Return (json_output, fix), _PARSE_HELP, or _PARSE_ERROR."""
|
|
897
|
+
json_output = False
|
|
898
|
+
fix = False
|
|
899
|
+
|
|
900
|
+
for arg in args:
|
|
901
|
+
if arg in ("-h", "--help"):
|
|
902
|
+
print(__doc__)
|
|
903
|
+
return _PARSE_HELP
|
|
904
|
+
elif arg == "--json":
|
|
905
|
+
json_output = True
|
|
906
|
+
elif arg == "--fix":
|
|
907
|
+
fix = True
|
|
908
|
+
elif arg == "--provenance":
|
|
909
|
+
print(
|
|
910
|
+
" ℹ --provenance: full SHA/commit + expiry report (provenance checks are always run; use --json for machine-readable)",
|
|
911
|
+
file=sys.stderr,
|
|
912
|
+
)
|
|
913
|
+
# explicit flag — no separate mode, just acknowledge
|
|
914
|
+
pass
|
|
915
|
+
else:
|
|
916
|
+
print(
|
|
917
|
+
f" ✗ Unknown option: {arg} (run 'agent-toolkit doctor --help')", file=sys.stderr
|
|
918
|
+
)
|
|
919
|
+
return _PARSE_ERROR
|
|
920
|
+
|
|
921
|
+
return json_output, fix
|
|
922
|
+
|
|
923
|
+
|
|
924
|
+
# ---------------------------------------------------------------------------
|
|
925
|
+
# Main entry point
|
|
926
|
+
# ---------------------------------------------------------------------------
|
|
927
|
+
|
|
928
|
+
|
|
929
|
+
def cmd_doctor(args: list[str]) -> int:
|
|
930
|
+
"""Run health checks for the agent-toolkit installation.
|
|
931
|
+
|
|
932
|
+
Returns 0 if no errors, 1 if any check errors, 2 on usage/parse errors.
|
|
933
|
+
"""
|
|
934
|
+
result = _parse_args(args)
|
|
935
|
+
if result is _PARSE_HELP:
|
|
936
|
+
return 0
|
|
937
|
+
if result is _PARSE_ERROR:
|
|
938
|
+
return 2
|
|
939
|
+
|
|
940
|
+
json_output, fix = result
|
|
941
|
+
toolkit_dir = toolkit_root()
|
|
942
|
+
|
|
943
|
+
# Run all checks
|
|
944
|
+
all_results: list[CheckResult] = []
|
|
945
|
+
|
|
946
|
+
# 1. System baseline
|
|
947
|
+
all_results.append(_check_python_version())
|
|
948
|
+
all_results.append(_check_command("system", "git", "git"))
|
|
949
|
+
all_results.append(_check_command("system", "gh (GitHub CLI)", "gh"))
|
|
950
|
+
all_results.append(_check_gh_auth())
|
|
951
|
+
|
|
952
|
+
# 2. AI tools
|
|
953
|
+
for tool_name, cmd in [
|
|
954
|
+
("claude", "claude"),
|
|
955
|
+
("cursor", "cursor"),
|
|
956
|
+
("opencode", "opencode"),
|
|
957
|
+
("windsurf", "windsurf"),
|
|
958
|
+
("muse", "muse"),
|
|
959
|
+
]:
|
|
960
|
+
all_results.append(_check_ai_tool(tool_name, cmd))
|
|
961
|
+
|
|
962
|
+
# 3. Profiles
|
|
963
|
+
all_results.extend(_check_profiles())
|
|
964
|
+
|
|
965
|
+
# 4. Loop runtime
|
|
966
|
+
all_results.extend(_check_loop_runtime(toolkit_dir))
|
|
967
|
+
|
|
968
|
+
# 5. LLM providers
|
|
969
|
+
all_results.extend(_check_llm_providers())
|
|
970
|
+
|
|
971
|
+
# 6. MCP
|
|
972
|
+
all_results.extend(_check_mcp())
|
|
973
|
+
|
|
974
|
+
# 7. Scheduled loops
|
|
975
|
+
all_results.extend(_check_scheduled_loops())
|
|
976
|
+
|
|
977
|
+
# 8. Swarm tooling
|
|
978
|
+
all_results.extend(_check_swarm())
|
|
979
|
+
|
|
980
|
+
# 9. Provenance (per §52: inventory warnings, doctor --provenance validating SHA/commit immutability, expiry >90d)
|
|
981
|
+
all_results.extend(_check_provenance(toolkit_dir))
|
|
982
|
+
|
|
983
|
+
# 10. Products / Packs (per #387: pack consistency, complete coverage)
|
|
984
|
+
all_results.extend(_check_products_and_packs(toolkit_dir))
|
|
985
|
+
|
|
986
|
+
# 11. MCP registry (per #387: mcp/registry vs installed runtime)
|
|
987
|
+
all_results.extend(_check_mcp_registry(toolkit_dir))
|
|
988
|
+
|
|
989
|
+
# Output
|
|
990
|
+
if json_output:
|
|
991
|
+
print(json.dumps({"checks": [r.to_dict() for r in all_results]}, indent=2))
|
|
992
|
+
else:
|
|
993
|
+
print()
|
|
994
|
+
print("agent-toolkit doctor")
|
|
995
|
+
|
|
996
|
+
categories_seen: list[str] = []
|
|
997
|
+
category_labels = {
|
|
998
|
+
"system": "System baseline",
|
|
999
|
+
"ai_tools": "AI tools",
|
|
1000
|
+
"profiles": "Profiles",
|
|
1001
|
+
"loops": "Loop runtime",
|
|
1002
|
+
"llm": "LLM providers",
|
|
1003
|
+
"mcp": "MCP",
|
|
1004
|
+
"scheduled": "Scheduled loops",
|
|
1005
|
+
"swarm": "Swarm tooling",
|
|
1006
|
+
"provenance": "Provenance (SHA/commit, expiry)",
|
|
1007
|
+
"packs": "Products / Packs",
|
|
1008
|
+
}
|
|
1009
|
+
for r in all_results:
|
|
1010
|
+
if r.category not in categories_seen:
|
|
1011
|
+
categories_seen.append(r.category)
|
|
1012
|
+
label = category_labels.get(r.category, r.category)
|
|
1013
|
+
_print_section(label)
|
|
1014
|
+
_print_result(r)
|
|
1015
|
+
|
|
1016
|
+
# Summary
|
|
1017
|
+
n_ok = sum(1 for r in all_results if r.status == CheckResult.STATUS_OK)
|
|
1018
|
+
n_warn = sum(1 for r in all_results if r.status == CheckResult.STATUS_WARN)
|
|
1019
|
+
n_err = sum(1 for r in all_results if r.status == CheckResult.STATUS_ERR)
|
|
1020
|
+
|
|
1021
|
+
print()
|
|
1022
|
+
print("── Summary ──")
|
|
1023
|
+
print(f" ✓ {n_ok} ok ⚠ {n_warn} warnings ✗ {n_err} errors")
|
|
1024
|
+
print()
|
|
1025
|
+
|
|
1026
|
+
# --fix: call install for warnings/errors related to profiles
|
|
1027
|
+
install_rc = 0
|
|
1028
|
+
if fix:
|
|
1029
|
+
has_profile_issues = any(
|
|
1030
|
+
r.category == "profiles" and r.status != CheckResult.STATUS_OK for r in all_results
|
|
1031
|
+
)
|
|
1032
|
+
if has_profile_issues:
|
|
1033
|
+
if not json_output:
|
|
1034
|
+
print("── Auto-fix: running install ──")
|
|
1035
|
+
from agent_toolkit.cli.install import cmd_install
|
|
1036
|
+
|
|
1037
|
+
if json_output:
|
|
1038
|
+
import contextlib
|
|
1039
|
+
import io
|
|
1040
|
+
|
|
1041
|
+
# Keep stdout JSON-pure: swallow install chatter.
|
|
1042
|
+
with contextlib.redirect_stdout(io.StringIO()):
|
|
1043
|
+
install_rc = cmd_install([])
|
|
1044
|
+
else:
|
|
1045
|
+
install_rc = cmd_install([])
|
|
1046
|
+
|
|
1047
|
+
errors = [r for r in all_results if r.status == CheckResult.STATUS_ERR]
|
|
1048
|
+
if errors:
|
|
1049
|
+
return 1
|
|
1050
|
+
if install_rc:
|
|
1051
|
+
return install_rc
|
|
1052
|
+
return 0
|