ai-kit-python 1.4.2__py3-none-any.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.
- ai_kit_python-1.4.2.dist-info/METADATA +132 -0
- ai_kit_python-1.4.2.dist-info/RECORD +278 -0
- ai_kit_python-1.4.2.dist-info/WHEEL +5 -0
- ai_kit_python-1.4.2.dist-info/entry_points.txt +2 -0
- ai_kit_python-1.4.2.dist-info/licenses/LICENSE +21 -0
- ai_kit_python-1.4.2.dist-info/licenses/template/ai-kit/vendor/security-audit/LICENSE +21 -0
- ai_kit_python-1.4.2.dist-info/licenses/template/ai-kit/vendor/strix/LICENSE +201 -0
- ai_kit_python-1.4.2.dist-info/top_level.txt +1 -0
- aikit/.agents/skills/go-work/SKILL.md +8 -0
- aikit/.agents/skills/laravel-work/SKILL.md +8 -0
- aikit/.agents/skills/moodle-work/SKILL.md +8 -0
- aikit/.agents/skills/project-continuity/SKILL.md +34 -0
- aikit/.agents/skills/python-work/SKILL.md +8 -0
- aikit/.dockerignore +90 -0
- aikit/.gitattributes +3 -0
- aikit/.github/workflows/check-kit.yml +70 -0
- aikit/.github/workflows/publish-pypi.yml +108 -0
- aikit/.gitignore +60 -0
- aikit/BOOTSTRAP_PROMPT.md +18 -0
- aikit/CHANGELOG.md +85 -0
- aikit/CLAUDE.md +3 -0
- aikit/CONTRIBUTING.md +46 -0
- aikit/Dockerfile +44 -0
- aikit/KIMI.md +3 -0
- aikit/LICENSE +21 -0
- aikit/MANIFEST.in +22 -0
- aikit/MANUS.md +3 -0
- aikit/Makefile +70 -0
- aikit/README.md +1161 -0
- aikit/SECURITY.md +8 -0
- aikit/SKILL.md +105 -0
- aikit/VERSION +1 -0
- aikit/__init__.py +1 -0
- aikit/ai-kit/ADAPTERS.md +5 -0
- aikit/ai-kit/CONTAINERS.md +5 -0
- aikit/ai-kit/ENGINEERING.md +5 -0
- aikit/ai-kit/INTENT.md +5 -0
- aikit/ai-kit/MODELS.md +5 -0
- aikit/ai-kit/SKILLS.md +5 -0
- aikit/ai-kit/stacks/GO.md +5 -0
- aikit/ai-kit/stacks/LARAVEL.md +5 -0
- aikit/ai-kit/stacks/MOODLE.md +5 -0
- aikit/ai-kit/stacks/PYTHON.md +5 -0
- aikit/aikit_cli.py +59 -0
- aikit/deploy/k8s/install-preview-job.yaml +56 -0
- aikit/docker/build-requirements.txt +4 -0
- aikit/docker/check_version.py +29 -0
- aikit/docker/pypi-requirements.txt +3 -0
- aikit/docs/ADAPTER_PATH_FIX.md +23 -0
- aikit/docs/BENCHMARK.md +106 -0
- aikit/docs/CONTEXT7.md +33 -0
- aikit/docs/DECISIONS.md +22 -0
- aikit/docs/DEVELOPMENT_PLAN.md +16 -0
- aikit/docs/DIALOGUE_MEMORY.md +70 -0
- aikit/docs/DOCKER.md +74 -0
- aikit/docs/DOCKER_HUB_OVERVIEW.md +311 -0
- aikit/docs/EVALUATION.md +30 -0
- aikit/docs/IMPLEMENTATION.md +134 -0
- aikit/docs/MANUS_REVIEW.md +46 -0
- aikit/docs/PYPI.md +87 -0
- aikit/docs/PYPI_DESCRIPTION.md +103 -0
- aikit/docs/SECURITY_AUDIT.md +26 -0
- aikit/docs/STRIX.md +28 -0
- aikit/docs/V0_5_0.md +27 -0
- aikit/docs/V0_6_4.md +25 -0
- aikit/docs/V1_4_0.md +73 -0
- aikit/docs/V1_4_1.md +55 -0
- aikit/docs/V1_4_2.md +33 -0
- aikit/docs/WEB_DESIGN.md +72 -0
- aikit/docs/WINDOWS_CI_FIX.md +39 -0
- aikit/docs/adr/0000-template.md +24 -0
- aikit/docs/adr/0001-adaptive-model-routing.md +30 -0
- aikit/docs/adr/0002-template-separation.md +24 -0
- aikit/docs/adr/0003-agent-registry-and-router.md +24 -0
- aikit/docs/adr/0004-onboarding-and-project-facts.md +24 -0
- aikit/docs/adr/0005-client-settings-ownership.md +30 -0
- aikit/docs/adr/0006-routing-evidence-and-local-measurements.md +26 -0
- aikit/docs/adr/0007-generated-client-configuration-and-presets.md +34 -0
- aikit/docs/adr/0008-unified-cli-and-fact-drift.md +20 -0
- aikit/docs/adr/0009-provider-review-doctor-fix-and-ci-self-install.md +20 -0
- aikit/docs/adr/0010-metrics-analysis-and-gemini-verification.md +20 -0
- aikit/docs/adr/0011-evidence-lifecycle-and-evaluation.md +20 -0
- aikit/docs/adr/0012-measurement-led-routing.md +31 -0
- aikit/docs/adr/0013-docker-cli-image.md +24 -0
- aikit/docs/adr/0014-local-dialogue-memory.md +22 -0
- aikit/docs/history/original-bundle-checks.md +11 -0
- aikit/docs/history/v0.1-changelog.txt +37 -0
- aikit/docs/history/v0.10-changelog.md +12 -0
- aikit/docs/history/v0.2-changelog.md +35 -0
- aikit/docs/history/v0.3-changelog.md +25 -0
- aikit/docs/history/v0.4-changelog.md +31 -0
- aikit/docs/history/v0.5-changelog.md +14 -0
- aikit/docs/history/v0.6.4-changelog.md +12 -0
- aikit/docs/history/v0.6.5-v0.9-changelog.md +35 -0
- aikit/docs/history/v1.1.0-changelog.md +12 -0
- aikit/docs/history/v1.2.0-changelog.md +11 -0
- aikit/docs/history/verification-before-measurements.md +53 -0
- aikit/integrations/CONVENTIONS.md +3 -0
- aikit/integrations/README.md +7 -0
- aikit/integrations/agents.json +136 -0
- aikit/integrations/ai-kit-check.yml +66 -0
- aikit/integrations/ai-kit-cline.md +4 -0
- aikit/integrations/ai-kit-roo.md +4 -0
- aikit/integrations/ai-kit-windsurf.md +4 -0
- aikit/integrations/ai-kit.mdc +6 -0
- aikit/integrations/claude-settings.data-ask.json +23 -0
- aikit/integrations/claude-settings.git-ask.json +10 -0
- aikit/integrations/copilot-instructions.md +3 -0
- aikit/integrations/security-audit.json +135 -0
- aikit/integrations/strix.json +69 -0
- aikit/pyproject.toml +60 -0
- aikit/scripts/__init__.py +1 -0
- aikit/scripts/adapters.py +184 -0
- aikit/scripts/adaptive.py +167 -0
- aikit/scripts/adr.py +61 -0
- aikit/scripts/benchmark.py +543 -0
- aikit/scripts/changelog.py +55 -0
- aikit/scripts/check_distribution.py +178 -0
- aikit/scripts/check_kit.py +406 -0
- aikit/scripts/context.py +184 -0
- aikit/scripts/docker_test_usage.py +55 -0
- aikit/scripts/doctor.py +313 -0
- aikit/scripts/evaluate.py +125 -0
- aikit/scripts/install.py +1584 -0
- aikit/scripts/learning.py +260 -0
- aikit/scripts/memory.py +344 -0
- aikit/scripts/memory_import.py +177 -0
- aikit/scripts/memory_store.py +125 -0
- aikit/scripts/metrics.py +351 -0
- aikit/scripts/router.py +399 -0
- aikit/template/.agents/hooks/session-start.md +3 -0
- aikit/template/.agents/hooks/session-start.sh +21 -0
- aikit/template/.agents/skills/ai-kit-bootstrap/SKILL.md +14 -0
- aikit/template/.agents/skills/ai-kit-review/SKILL.md +11 -0
- aikit/template/.agents/skills/ai-kit-sync-context/SKILL.md +12 -0
- aikit/template/.agents/skills/context7-docs/SKILL.md +11 -0
- aikit/template/.agents/skills/design-extract/SKILL.md +9 -0
- aikit/template/.agents/skills/dotnet-work/SKILL.md +9 -0
- aikit/template/.agents/skills/filament-work/SKILL.md +9 -0
- aikit/template/.agents/skills/frontend-work/SKILL.md +9 -0
- aikit/template/.agents/skills/go-work/SKILL.md +9 -0
- aikit/template/.agents/skills/infra-work/SKILL.md +9 -0
- aikit/template/.agents/skills/java-work/SKILL.md +9 -0
- aikit/template/.agents/skills/laravel-blade-ui/SKILL.md +9 -0
- aikit/template/.agents/skills/laravel-work/SKILL.md +10 -0
- aikit/template/.agents/skills/library-work/SKILL.md +9 -0
- aikit/template/.agents/skills/moodle-work/SKILL.md +9 -0
- aikit/template/.agents/skills/php-work/SKILL.md +9 -0
- aikit/template/.agents/skills/project-continuity/SKILL.md +21 -0
- aikit/template/.agents/skills/python-work/SKILL.md +9 -0
- aikit/template/.agents/skills/rust-work/SKILL.md +9 -0
- aikit/template/.agents/skills/security-audit/SKILL.md +16 -0
- aikit/template/.agents/skills/security-audit/agents/openai.yaml +2 -0
- aikit/template/.agents/skills/strix/SKILL.md +16 -0
- aikit/template/.agents/skills/strix/agents/openai.yaml +2 -0
- aikit/template/.agents/skills/ui-review/SKILL.md +9 -0
- aikit/template/.agents/skills/web-design/SKILL.md +9 -0
- aikit/template/AGENTS.md +16 -0
- aikit/template/CHANGELOG.md +6 -0
- aikit/template/CLAUDE.md +5 -0
- aikit/template/GEMINI.md +4 -0
- aikit/template/KIMI.md +4 -0
- aikit/template/MANUS.md +4 -0
- aikit/template/PROJECT_CONTEXT.md +35 -0
- aikit/template/README.md +6 -0
- aikit/template/WIKI.md +37 -0
- aikit/template/ai-kit/ADAPTERS.md +100 -0
- aikit/template/ai-kit/BOOTSTRAP.md +15 -0
- aikit/template/ai-kit/CONTAINERS.md +63 -0
- aikit/template/ai-kit/CONTEXT7.md +96 -0
- aikit/template/ai-kit/CORE.md +23 -0
- aikit/template/ai-kit/ENGINEERING.md +34 -0
- aikit/template/ai-kit/MCP.md +17 -0
- aikit/template/ai-kit/METRICS.md +83 -0
- aikit/template/ai-kit/SECURITY-AUDIT.md +21 -0
- aikit/template/ai-kit/SECURITY.md +34 -0
- aikit/template/ai-kit/SKILLS.md +37 -0
- aikit/template/ai-kit/STRIX.md +44 -0
- aikit/template/ai-kit/UPSTREAM.md +38 -0
- aikit/template/ai-kit/WEB-DESIGN.md +37 -0
- aikit/template/ai-kit/profiles/OWNER.md +19 -0
- aikit/template/ai-kit/project.json +14 -0
- aikit/template/ai-kit/router/POLICY.md +79 -0
- aikit/template/ai-kit/router/providers/anthropic.json +44 -0
- aikit/template/ai-kit/router/providers/anthropic.md +14 -0
- aikit/template/ai-kit/router/providers/gemini.json +13 -0
- aikit/template/ai-kit/router/providers/gemini.md +10 -0
- aikit/template/ai-kit/router/providers/kimi.json +26 -0
- aikit/template/ai-kit/router/providers/kimi.md +13 -0
- aikit/template/ai-kit/router/providers/local.json +18 -0
- aikit/template/ai-kit/router/providers/local.md +10 -0
- aikit/template/ai-kit/router/providers/openai.json +48 -0
- aikit/template/ai-kit/router/providers/openai.md +14 -0
- aikit/template/ai-kit/router/routing.json +11 -0
- aikit/template/ai-kit/router/selection.json +14 -0
- aikit/template/ai-kit/settings.json +11 -0
- aikit/template/ai-kit/stacks/DOTNET.md +13 -0
- aikit/template/ai-kit/stacks/FILAMENT.md +18 -0
- aikit/template/ai-kit/stacks/FRONTEND.md +9 -0
- aikit/template/ai-kit/stacks/GO.md +19 -0
- aikit/template/ai-kit/stacks/INFRA.md +12 -0
- aikit/template/ai-kit/stacks/JAVA.md +13 -0
- aikit/template/ai-kit/stacks/LARAVEL.md +15 -0
- aikit/template/ai-kit/stacks/LIBRARY.md +9 -0
- aikit/template/ai-kit/stacks/MOODLE.md +12 -0
- aikit/template/ai-kit/stacks/PHP.md +11 -0
- aikit/template/ai-kit/stacks/PYTHON.md +11 -0
- aikit/template/ai-kit/stacks/RUST.md +18 -0
- aikit/template/ai-kit/vendor/security-audit/AI-AND-LLM.md +83 -0
- aikit/template/ai-kit/vendor/security-audit/ATTACK-CLASSES.md +130 -0
- aikit/template/ai-kit/vendor/security-audit/CLIENT-SIDE.md +83 -0
- aikit/template/ai-kit/vendor/security-audit/CLOUD-AND-DEPLOYMENT.md +86 -0
- aikit/template/ai-kit/vendor/security-audit/DATA-ISOLATION-AND-LIFECYCLE.md +84 -0
- aikit/template/ai-kit/vendor/security-audit/DESKTOP-MOBILE-AND-LOCAL-IPC.md +89 -0
- aikit/template/ai-kit/vendor/security-audit/HUNTING.md +251 -0
- aikit/template/ai-kit/vendor/security-audit/LICENSE +21 -0
- aikit/template/ai-kit/vendor/security-audit/MEMORY-SAFETY-AND-BINARY.md +101 -0
- aikit/template/ai-kit/vendor/security-audit/PROTOCOLS-RPC-AND-MESSAGING.md +81 -0
- aikit/template/ai-kit/vendor/security-audit/RECONNAISSANCE.md +156 -0
- aikit/template/ai-kit/vendor/security-audit/RESOURCE-EXHAUSTION-AND-AVAILABILITY.md +78 -0
- aikit/template/ai-kit/vendor/security-audit/SKILL.md +192 -0
- aikit/template/ai-kit/vendor/security-audit/SUPPLY-CHAIN-AND-RELEASE.md +73 -0
- aikit/template/ai-kit/vendor/security-audit/VALIDATION-AND-REPORTING.md +186 -0
- aikit/template/ai-kit/vendor/security-audit/WEB-PROTOCOL-AND-AUTH.md +105 -0
- aikit/template/ai-kit/vendor/security-audit/report-schema.json +461 -0
- aikit/template/ai-kit/vendor/security-audit/validate-coverage-ledger.cjs +872 -0
- aikit/template/ai-kit/vendor/security-audit/validate-coverage-ledger.test.cjs +740 -0
- aikit/template/ai-kit/vendor/security-audit/validate-findings.cjs +773 -0
- aikit/template/ai-kit/vendor/security-audit/validate-findings.test.cjs +652 -0
- aikit/template/ai-kit/vendor/strix/LICENSE +201 -0
- aikit/template/ai-kit/vendor/strix/api-security-testing/SKILL.md +61 -0
- aikit/template/ai-kit/vendor/strix/application-security-testing/SKILL.md +66 -0
- aikit/template/ai-kit/vendor/strix/ci-security-scanning-with-strix/SKILL.md +149 -0
- aikit/template/ai-kit/vendor/strix/find-security-vulnerabilities-in-code/SKILL.md +62 -0
- aikit/template/ai-kit/vendor/strix/fix-security-vulnerabilities-with-strix/SKILL.md +77 -0
- aikit/template/ai-kit/vendor/strix/managed-pentesting-with-strix/SKILL.md +321 -0
- aikit/template/ai-kit/vendor/strix/owasp-top-10-testing/SKILL.md +64 -0
- aikit/template/ai-kit/vendor/strix/penetration-testing-with-strix/SKILL.md +162 -0
- aikit/template/ai-kit/vendor/strix/web-app-penetration-testing/SKILL.md +54 -0
- aikit/template/ai-kit/web-design/LARAVEL.md +51 -0
- aikit/template/ai-kit/web-design/LIBRARIES.md +39 -0
- aikit/template/ai-kit/web-design/REVIEW.md +37 -0
- aikit/template/ai-kit/web-design/TOOLS.md +43 -0
- aikit/template/docs/DECISIONS.md +7 -0
- aikit/template/docs/adr/0000-template.md +20 -0
- aikit/template/docs/design/ACCEPTANCE.md +24 -0
- aikit/template/docs/design/BRIEF.md +26 -0
- aikit/template/docs/design/SYSTEM.md +30 -0
- aikit/templates/agent-secrets.ignore +19 -0
- aikit/templates/gitignore.private +160 -0
- aikit/templates/gitignore.team +139 -0
- aikit/tests/build_distribution.py +73 -0
- aikit/tests/docker_smoke.py +172 -0
- aikit/tests/fixtures/create-junction.ps1 +18 -0
- aikit/tests/fixtures/golden/claude-settings-local.json +58 -0
- aikit/tests/test_adapters.py +105 -0
- aikit/tests/test_aikit.py +79 -0
- aikit/tests/test_benchmark.py +241 -0
- aikit/tests/test_check_kit.py +203 -0
- aikit/tests/test_context.py +76 -0
- aikit/tests/test_context7.py +153 -0
- aikit/tests/test_context_selection.py +70 -0
- aikit/tests/test_distribution.py +181 -0
- aikit/tests/test_docker_image.py +63 -0
- aikit/tests/test_docker_test_usage.py +54 -0
- aikit/tests/test_doctor.py +148 -0
- aikit/tests/test_evaluate.py +66 -0
- aikit/tests/test_install.py +939 -0
- aikit/tests/test_learning.py +247 -0
- aikit/tests/test_memory.py +371 -0
- aikit/tests/test_metrics.py +209 -0
- aikit/tests/test_native.py +342 -0
- aikit/tests/test_onboarding.py +301 -0
- aikit/tests/test_router.py +336 -0
- aikit/tests/test_security_audit.py +237 -0
- aikit/tests/test_strix.py +245 -0
- aikit/tests/test_web_design.py +219 -0
- aikit/tests/wheel_smoke.py +104 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"ask": [
|
|
4
|
+
"Bash(rm -rf *)",
|
|
5
|
+
"Bash(rm -fr *)",
|
|
6
|
+
"Bash(git reset --hard*)",
|
|
7
|
+
"Bash(git clean *)",
|
|
8
|
+
"Bash(docker system prune*)",
|
|
9
|
+
"Bash(docker volume rm *)",
|
|
10
|
+
"Bash(docker volume prune*)"
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"stacks": {
|
|
14
|
+
"LARAVEL": [
|
|
15
|
+
"Bash(php artisan migrate*)",
|
|
16
|
+
"Bash(php artisan db:wipe*)",
|
|
17
|
+
"Bash(php artisan db:seed*)"
|
|
18
|
+
],
|
|
19
|
+
"MOODLE": [
|
|
20
|
+
"Bash(php admin/cli/upgrade.php*)"
|
|
21
|
+
]
|
|
22
|
+
}
|
|
23
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"repository": "https://github.com/cloudflare/security-audit-skill",
|
|
4
|
+
"revision": "c1c8a8c1471069fb0e188eeaff69b8e8db6564a8",
|
|
5
|
+
"source_directory": "skills/security-audit",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"files": {
|
|
8
|
+
"LICENSE": {
|
|
9
|
+
"sha256": "e598e694aa506650c7192d5ea3be0e50aca0d356ea00551c53429f0b01eb6391",
|
|
10
|
+
"git_blob": "6dbc9ecb3a5b9080e95b962869e8c7ab16cfdc20",
|
|
11
|
+
"git_mode": "100644",
|
|
12
|
+
"size": 1078
|
|
13
|
+
},
|
|
14
|
+
"AI-AND-LLM.md": {
|
|
15
|
+
"sha256": "7335a95c43554eadefcf03ece639291bb0e038c398a179299e3887ffe06bd2ef",
|
|
16
|
+
"git_blob": "02ba9039eca4cec811fefc77bce0d7349b379aae",
|
|
17
|
+
"git_mode": "100644",
|
|
18
|
+
"size": 10617
|
|
19
|
+
},
|
|
20
|
+
"ATTACK-CLASSES.md": {
|
|
21
|
+
"sha256": "3ee4f00c6e8c9dcf6dd02dd5ac2a4ceb322e2085f3e9fbe79cf0c6330bc7ef83",
|
|
22
|
+
"git_blob": "18a2beb4dbb554fb52b0a0293b8bd9108207f8fa",
|
|
23
|
+
"git_mode": "100644",
|
|
24
|
+
"size": 15695
|
|
25
|
+
},
|
|
26
|
+
"CLIENT-SIDE.md": {
|
|
27
|
+
"sha256": "613f6b98f86083e045525c5b60154ffdab122bac35f1e05eb17488e6e453370c",
|
|
28
|
+
"git_blob": "8ba673e9e7e3d02f5518aa0219bcbeec7957531b",
|
|
29
|
+
"git_mode": "100644",
|
|
30
|
+
"size": 8594
|
|
31
|
+
},
|
|
32
|
+
"CLOUD-AND-DEPLOYMENT.md": {
|
|
33
|
+
"sha256": "3c80f3088be22345e07b057c056d6ed99fe2b0304f794398964f7ccd5a496ca3",
|
|
34
|
+
"git_blob": "5569f76c7740744aa3f60aaa89eaefe275f2e6c9",
|
|
35
|
+
"git_mode": "100644",
|
|
36
|
+
"size": 8291
|
|
37
|
+
},
|
|
38
|
+
"DATA-ISOLATION-AND-LIFECYCLE.md": {
|
|
39
|
+
"sha256": "307dcb3e24d292715c4a041190e7bb135646a37eb6187619ae01d000a634a4ad",
|
|
40
|
+
"git_blob": "0cba1fa463b62c9b0caba710a201e29c1eecd0ec",
|
|
41
|
+
"git_mode": "100644",
|
|
42
|
+
"size": 7798
|
|
43
|
+
},
|
|
44
|
+
"DESKTOP-MOBILE-AND-LOCAL-IPC.md": {
|
|
45
|
+
"sha256": "58eae8ee0ca6a46611c501ec5a3ab47fdff88dd7db0d8d74789bbf8bb19d8cb2",
|
|
46
|
+
"git_blob": "d6230098651bff6348d3868c11f566bfb1306e7f",
|
|
47
|
+
"git_mode": "100644",
|
|
48
|
+
"size": 8739
|
|
49
|
+
},
|
|
50
|
+
"HUNTING.md": {
|
|
51
|
+
"sha256": "71c121decea1322c47c718f6e110f1a892980bb4266bd7ac55ef043a213ade09",
|
|
52
|
+
"git_blob": "377d4dcfbe88a049379448cd7892717185401597",
|
|
53
|
+
"git_mode": "100644",
|
|
54
|
+
"size": 22446
|
|
55
|
+
},
|
|
56
|
+
"MEMORY-SAFETY-AND-BINARY.md": {
|
|
57
|
+
"sha256": "7c8b0dccfb35315cca2455509674d49632b2fe37e1f3aac16c1a496a7e9924a7",
|
|
58
|
+
"git_blob": "86ff4920752661fc4a31a35ca4f962ace5dd5f48",
|
|
59
|
+
"git_mode": "100644",
|
|
60
|
+
"size": 10513
|
|
61
|
+
},
|
|
62
|
+
"PROTOCOLS-RPC-AND-MESSAGING.md": {
|
|
63
|
+
"sha256": "8b73816fa5b3697c9c40977f0c836b40f01d6e81929ff2720fe83ce089e9a619",
|
|
64
|
+
"git_blob": "b04ab88f852224838fdc9683d9fcccd858b0778a",
|
|
65
|
+
"git_mode": "100644",
|
|
66
|
+
"size": 7483
|
|
67
|
+
},
|
|
68
|
+
"RECONNAISSANCE.md": {
|
|
69
|
+
"sha256": "02ca887aca84de8ae137f1891bd2f0a54f28d3fa6da92a27f54734382b3381a2",
|
|
70
|
+
"git_blob": "1a170a93ab76b93dc5ac268a5b690beb423a8aa9",
|
|
71
|
+
"git_mode": "100644",
|
|
72
|
+
"size": 15902
|
|
73
|
+
},
|
|
74
|
+
"report-schema.json": {
|
|
75
|
+
"sha256": "6575c9de4a62255699ea052ebf9a49ebfb373a32891bcd89af6f55cc84b35519",
|
|
76
|
+
"git_blob": "55815a3687a45fff63d8ebf20068611bd6ad6d9a",
|
|
77
|
+
"git_mode": "100644",
|
|
78
|
+
"size": 14494
|
|
79
|
+
},
|
|
80
|
+
"RESOURCE-EXHAUSTION-AND-AVAILABILITY.md": {
|
|
81
|
+
"sha256": "4326fab5c92a0b4c420a70196254040105cd794026562130b59bf1a6db3e4baa",
|
|
82
|
+
"git_blob": "6cbbd1e2cfa482068c8279c2f7da9db43d3f54b8",
|
|
83
|
+
"git_mode": "100644",
|
|
84
|
+
"size": 7476
|
|
85
|
+
},
|
|
86
|
+
"SKILL.md": {
|
|
87
|
+
"sha256": "5e3e96a1e438d8f35fef0a1e38f02d4f00e2bdf910401b7dfe00e6779c6dac85",
|
|
88
|
+
"git_blob": "92178dad304d3f63e37582a297026a7874e12c62",
|
|
89
|
+
"git_mode": "100644",
|
|
90
|
+
"size": 22026
|
|
91
|
+
},
|
|
92
|
+
"SUPPLY-CHAIN-AND-RELEASE.md": {
|
|
93
|
+
"sha256": "f372e919940e335468ba1ae09f99026c3986a345f7fe26f8068f132e199f52a5",
|
|
94
|
+
"git_blob": "bf96aed0803bc07471483ab129dea86759c3e305",
|
|
95
|
+
"git_mode": "100644",
|
|
96
|
+
"size": 7258
|
|
97
|
+
},
|
|
98
|
+
"validate-coverage-ledger.cjs": {
|
|
99
|
+
"sha256": "2eb280bd2e33c002916f86db9b29f9edfedb6454e5db973c766290465b868ac8",
|
|
100
|
+
"git_blob": "0ef58657bef18b26a92321df6c8e3d31bc8e6659",
|
|
101
|
+
"git_mode": "100755",
|
|
102
|
+
"size": 33522
|
|
103
|
+
},
|
|
104
|
+
"validate-coverage-ledger.test.cjs": {
|
|
105
|
+
"sha256": "cfb9df26b03469a4279a70e7664a2a5434dd6f8c2c204e91f9b0aeb9dd71e648",
|
|
106
|
+
"git_blob": "b1444f57e1b021a61175733b8c356171861c8701",
|
|
107
|
+
"git_mode": "100644",
|
|
108
|
+
"size": 28483
|
|
109
|
+
},
|
|
110
|
+
"validate-findings.cjs": {
|
|
111
|
+
"sha256": "e85f232e36bfac0f866f244da50376692d6b45c690beadcb00608b0e6c8b5283",
|
|
112
|
+
"git_blob": "2843feceddb7e30bf10d6e4f7bbe6640e4c57799",
|
|
113
|
+
"git_mode": "100755",
|
|
114
|
+
"size": 28806
|
|
115
|
+
},
|
|
116
|
+
"validate-findings.test.cjs": {
|
|
117
|
+
"sha256": "b92001117a92c4161d43777a0ba2fa182a8a4d97f2304cc8c0cee5331497b73b",
|
|
118
|
+
"git_blob": "8d245b226bbe2fdf28b46b3203b99c2824039d2d",
|
|
119
|
+
"git_mode": "100644",
|
|
120
|
+
"size": 26384
|
|
121
|
+
},
|
|
122
|
+
"VALIDATION-AND-REPORTING.md": {
|
|
123
|
+
"sha256": "b892470cb89bb31f33403e050d4bdfbe2cdaef940ade1496fa65e32b2e129449",
|
|
124
|
+
"git_blob": "5e200d7387e665e53e0e4190fa4a5814c334c507",
|
|
125
|
+
"git_mode": "100644",
|
|
126
|
+
"size": 17843
|
|
127
|
+
},
|
|
128
|
+
"WEB-PROTOCOL-AND-AUTH.md": {
|
|
129
|
+
"sha256": "4f3066dffa23c5635859187be9b96c32f2aff29a688186b91ff0c63e47d62b5c",
|
|
130
|
+
"git_blob": "1a099fa575cf52a61e9a627f91b61015120fa517",
|
|
131
|
+
"git_mode": "100644",
|
|
132
|
+
"size": 12300
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schema": 1,
|
|
3
|
+
"repository": "https://github.com/usestrix/strix",
|
|
4
|
+
"revision": "f7a32ae62d94872631bc8415073868d02a6ffdaf",
|
|
5
|
+
"source_directory": "skills",
|
|
6
|
+
"license": "Apache-2.0",
|
|
7
|
+
"files": {
|
|
8
|
+
"LICENSE": {
|
|
9
|
+
"sha256": "7db9697134251e314bd8d39257fe170c98b3e2ad9b2ed67e97acaf9613e7b9e3",
|
|
10
|
+
"git_blob": "65c4e8f58a00552cc29f6bd1dcaf1c4ef10012b8",
|
|
11
|
+
"git_mode": "100644",
|
|
12
|
+
"size": 11345
|
|
13
|
+
},
|
|
14
|
+
"api-security-testing/SKILL.md": {
|
|
15
|
+
"sha256": "bd04c90d87be2353ebefee33363af0818746345e25c80ed36f63fcdd2b25509c",
|
|
16
|
+
"git_blob": "d21a25da6167f79c3caff745abc142b2118c2691",
|
|
17
|
+
"git_mode": "100644",
|
|
18
|
+
"size": 6331
|
|
19
|
+
},
|
|
20
|
+
"application-security-testing/SKILL.md": {
|
|
21
|
+
"sha256": "b3041c800a9a1d34e04164c5f700b6d12bbc237f098d760c09ec076c92d2a654",
|
|
22
|
+
"git_blob": "0c27bd5df4a346abfc1e81efc62f49b066637f52",
|
|
23
|
+
"git_mode": "100644",
|
|
24
|
+
"size": 4557
|
|
25
|
+
},
|
|
26
|
+
"ci-security-scanning-with-strix/SKILL.md": {
|
|
27
|
+
"sha256": "f8e1dd61721dc302bd64155f80a2a3b16a5bf5bca598ae23d98aad8fefebea91",
|
|
28
|
+
"git_blob": "69ae726776b8c1dbc667f18a9f972a66e4656c4e",
|
|
29
|
+
"git_mode": "100644",
|
|
30
|
+
"size": 9019
|
|
31
|
+
},
|
|
32
|
+
"find-security-vulnerabilities-in-code/SKILL.md": {
|
|
33
|
+
"sha256": "e7c25f1a0aea437d011d96dd306736b644597b07aa5a6dbc5588eefaab248e0a",
|
|
34
|
+
"git_blob": "b1829e9cba9db38d6d76e9fa274da3caa188ac75",
|
|
35
|
+
"git_mode": "100644",
|
|
36
|
+
"size": 4386
|
|
37
|
+
},
|
|
38
|
+
"fix-security-vulnerabilities-with-strix/SKILL.md": {
|
|
39
|
+
"sha256": "bb1b351fd60c91e4875207f3fe4f35774ff5194261a00aea9f4752e459f24a9f",
|
|
40
|
+
"git_blob": "6912cb542b3e5c46e99fa196cd29039de9a8e574",
|
|
41
|
+
"git_mode": "100644",
|
|
42
|
+
"size": 5997
|
|
43
|
+
},
|
|
44
|
+
"managed-pentesting-with-strix/SKILL.md": {
|
|
45
|
+
"sha256": "45e1432ea897e59e67a5102513a4b9aac7372a5666d68d620ee867bbfa8b290b",
|
|
46
|
+
"git_blob": "246aa882580962a89aaef2ad89d5d3c6e150a793",
|
|
47
|
+
"git_mode": "100644",
|
|
48
|
+
"size": 23639
|
|
49
|
+
},
|
|
50
|
+
"owasp-top-10-testing/SKILL.md": {
|
|
51
|
+
"sha256": "02b1cbe60d40c44493309418c7824f1f681d9f32bf0299ca246892c46cb130af",
|
|
52
|
+
"git_blob": "328e217f7e0ab0fe43f448de49c1b2fb2897a6a7",
|
|
53
|
+
"git_mode": "100644",
|
|
54
|
+
"size": 6245
|
|
55
|
+
},
|
|
56
|
+
"penetration-testing-with-strix/SKILL.md": {
|
|
57
|
+
"sha256": "8051d93f1ccc96c7268ca7b3d3d589c56997c620164773617ceb53af8832692c",
|
|
58
|
+
"git_blob": "afd56cea3920da1f5092f90244c3db7519b8b0ae",
|
|
59
|
+
"git_mode": "100644",
|
|
60
|
+
"size": 10131
|
|
61
|
+
},
|
|
62
|
+
"web-app-penetration-testing/SKILL.md": {
|
|
63
|
+
"sha256": "2e8df0b9233971c496d0c0c7660808fc717c08d4165455a0423229045389608a",
|
|
64
|
+
"git_blob": "99331fa8bb83315a3bce4449592bb77504421d30",
|
|
65
|
+
"git_mode": "100644",
|
|
66
|
+
"size": 4320
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
aikit/pyproject.toml
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=77.0.3", "wheel"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "ai-kit-python"
|
|
7
|
+
description = "Offline installer and portable instructions that give AI coding agents project memory, stack rules, guardrails, and adaptive model routing."
|
|
8
|
+
readme = {file = "docs/PYPI_DESCRIPTION.md", content-type = "text/markdown"}
|
|
9
|
+
requires-python = ">=3.10"
|
|
10
|
+
authors = [{name = "Igor Sazonov"}]
|
|
11
|
+
license = "MIT AND Apache-2.0"
|
|
12
|
+
license-files = [
|
|
13
|
+
"LICENSE",
|
|
14
|
+
"template/ai-kit/vendor/security-audit/LICENSE",
|
|
15
|
+
"template/ai-kit/vendor/strix/LICENSE",
|
|
16
|
+
]
|
|
17
|
+
keywords = ["ai-agents", "coding-assistant", "context-engineering", "agent-skills", "project-memory", "developer-tools"]
|
|
18
|
+
classifiers = [
|
|
19
|
+
"Environment :: Console",
|
|
20
|
+
"Intended Audience :: Developers",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3 :: Only",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Programming Language :: Python :: 3.13",
|
|
27
|
+
"Programming Language :: Python :: 3.14",
|
|
28
|
+
"Topic :: Software Development",
|
|
29
|
+
]
|
|
30
|
+
dynamic = ["version"]
|
|
31
|
+
|
|
32
|
+
[project.urls]
|
|
33
|
+
Homepage = "https://github.com/tigusigalpa/ai-kit"
|
|
34
|
+
Documentation = "https://github.com/tigusigalpa/ai-kit#readme"
|
|
35
|
+
Source = "https://github.com/tigusigalpa/ai-kit"
|
|
36
|
+
Issues = "https://github.com/tigusigalpa/ai-kit/issues"
|
|
37
|
+
Changelog = "https://github.com/tigusigalpa/ai-kit/blob/main/CHANGELOG.md"
|
|
38
|
+
|
|
39
|
+
[project.scripts]
|
|
40
|
+
aikit = "aikit.aikit_cli:main"
|
|
41
|
+
|
|
42
|
+
[tool.setuptools]
|
|
43
|
+
packages = ["aikit", "aikit.scripts"]
|
|
44
|
+
package-dir = {aikit = ".", "aikit.scripts" = "scripts"}
|
|
45
|
+
include-package-data = true
|
|
46
|
+
|
|
47
|
+
[tool.setuptools.package-data]
|
|
48
|
+
aikit = ["VERSION", "LICENSE", "MANIFEST.in", "pyproject.toml", "Makefile", "Dockerfile", ".dockerignore", ".gitignore", ".gitattributes", "*.md", "*.json", "ai-kit/*.md", "ai-kit/router/*.md", "ai-kit/router/providers/*.md", "ai-kit/stacks/*.md", "ai-kit/profiles/*.md", "docs/**/*", "integrations/**/*", "template/**/*", "template/.agents/**/*", "templates/**/*", ".github/**/*", ".agents/**/*", "docker/**/*", "deploy/**/*", "tests/*.py", "tests/fixtures/**/*"]
|
|
49
|
+
|
|
50
|
+
[tool.setuptools.dynamic]
|
|
51
|
+
version = {file = ["VERSION"]}
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.exclude-package-data]
|
|
54
|
+
aikit = [
|
|
55
|
+
"AGENTS.md", "PROJECT_CONTEXT.md", "WIKI.md", "auth.json", ".netrc", ".pypirc", ".env", ".env.*",
|
|
56
|
+
"ai-kit/.metrics/*", "ai-kit/.memory/*", "ai-kit/.upstream-cache/*", "ai-kit/.design/*", "docs/.private/*",
|
|
57
|
+
"**/.env", "**/.env.*", "**/auth.json", "**/.netrc", "**/.pypirc",
|
|
58
|
+
"*.pem", "*.p12", "*.pfx", "id_rsa*", "id_ed25519*",
|
|
59
|
+
"**/*.pem", "**/*.p12", "**/*.pfx", "**/id_rsa*", "**/id_ed25519*",
|
|
60
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""AI-KIT command implementations."""
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
"""Review client adapters and retire only unchanged AI-KIT-owned adapter files."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import argparse
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
10
|
+
import install
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
STATE_PATH = "ai-kit/.install-state.json"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def target_root(target: Path) -> Path:
|
|
17
|
+
target = target.absolute()
|
|
18
|
+
if install.is_link(target):
|
|
19
|
+
raise ValueError("Refusing a linked project root")
|
|
20
|
+
target = target.resolve()
|
|
21
|
+
if not target.is_dir():
|
|
22
|
+
raise ValueError("Project must be an existing directory")
|
|
23
|
+
return target
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def installed_state(target: Path, registry: dict) -> dict:
|
|
27
|
+
path = install.safe_path(target, STATE_PATH)
|
|
28
|
+
if not path.is_file():
|
|
29
|
+
raise ValueError("No installer state found; is AI-KIT installed in this project?")
|
|
30
|
+
state = install.read_json(path, {})
|
|
31
|
+
# Read-only audits must reach their obstruction report; lexical/link boundaries stay strict.
|
|
32
|
+
install.validate_state(state, target, registry["names"], allow_obstructions=True)
|
|
33
|
+
return state
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def report(*, root: Path = install.ROOT, project: Path | None = None) -> dict:
|
|
37
|
+
registry = install.load_agent_registry(root)
|
|
38
|
+
state = None
|
|
39
|
+
detected: dict[str, list[str]] = {}
|
|
40
|
+
target = None
|
|
41
|
+
if project is not None:
|
|
42
|
+
target = target_root(project)
|
|
43
|
+
state = installed_state(target, registry)
|
|
44
|
+
detected = install.detect_agents(target, registry)
|
|
45
|
+
clients = []
|
|
46
|
+
for name in sorted(registry["names"]):
|
|
47
|
+
metadata = registry["metadata"][name]
|
|
48
|
+
managed = []
|
|
49
|
+
blocked = []
|
|
50
|
+
selected = None
|
|
51
|
+
if state is not None:
|
|
52
|
+
selected = name in state["agents"]
|
|
53
|
+
for relative in sorted(state["files"]):
|
|
54
|
+
if install.adapter_owner(relative, registry) != name:
|
|
55
|
+
continue
|
|
56
|
+
try:
|
|
57
|
+
exists = install.safe_path(target, relative).is_file()
|
|
58
|
+
except (ValueError, NotADirectoryError):
|
|
59
|
+
blocked.append(relative)
|
|
60
|
+
continue
|
|
61
|
+
if exists:
|
|
62
|
+
managed.append(relative)
|
|
63
|
+
clients.append({"client": name, "documentation_status": metadata["status"],
|
|
64
|
+
"verified_documentation_date": metadata["verified_documentation_date"],
|
|
65
|
+
"sources": metadata["sources"], "activation": metadata["activation"],
|
|
66
|
+
"selected": selected, "detected": detected.get(name, []), "managed_paths": managed,
|
|
67
|
+
"blocked_paths": blocked})
|
|
68
|
+
return {"project": str(target) if target is not None else None, "clients": clients,
|
|
69
|
+
"note": "Documentation status does not prove native client activation."}
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def retirement_plan(project: Path, agent: str, *, root: Path = install.ROOT) -> dict:
|
|
73
|
+
target = target_root(project)
|
|
74
|
+
registry = install.load_agent_registry(root)
|
|
75
|
+
if agent not in registry["names"]:
|
|
76
|
+
raise ValueError("Unknown client: " + agent)
|
|
77
|
+
state = installed_state(target, registry)
|
|
78
|
+
if agent in state["agents"] and len(state["agents"]) == 1:
|
|
79
|
+
raise ValueError("Cannot retire the last selected client; install a replacement selection first")
|
|
80
|
+
records = state["files"]
|
|
81
|
+
removals, conflicts, absent = [], [], []
|
|
82
|
+
for relative in sorted(records):
|
|
83
|
+
if install.adapter_owner(relative, registry) != agent:
|
|
84
|
+
continue
|
|
85
|
+
try:
|
|
86
|
+
path = install.safe_path(target, relative)
|
|
87
|
+
except (ValueError, NotADirectoryError):
|
|
88
|
+
conflicts.append(relative)
|
|
89
|
+
continue
|
|
90
|
+
if not path.exists():
|
|
91
|
+
absent.append(relative)
|
|
92
|
+
continue
|
|
93
|
+
if not path.is_file():
|
|
94
|
+
conflicts.append(relative)
|
|
95
|
+
continue
|
|
96
|
+
data = path.read_bytes()
|
|
97
|
+
record = records[relative]
|
|
98
|
+
if record.get("local_adaptation") or install.digest(data) != record.get("installed_hash"):
|
|
99
|
+
conflicts.append(relative)
|
|
100
|
+
continue
|
|
101
|
+
removals.append({"path": relative, "old": data,
|
|
102
|
+
"backup": "ai-kit/.upstream-cache/retirements/" + install.digest(data) + "/" + relative})
|
|
103
|
+
next_state = dict(state)
|
|
104
|
+
next_files = dict(records)
|
|
105
|
+
for item in removals:
|
|
106
|
+
next_files.pop(item["path"], None)
|
|
107
|
+
for relative in absent:
|
|
108
|
+
next_files.pop(relative, None)
|
|
109
|
+
next_state["files"] = next_files
|
|
110
|
+
next_state["agents"] = [name for name in state["agents"] if name != agent]
|
|
111
|
+
state_path = install.safe_path(target, STATE_PATH)
|
|
112
|
+
old_state = state_path.read_bytes()
|
|
113
|
+
state_data = (json.dumps(next_state, indent=2, sort_keys=True) + "\n").encode("utf-8")
|
|
114
|
+
return {"target": str(target), "agent": agent, "removals": removals, "conflicts": conflicts,
|
|
115
|
+
"absent": absent, "state_path": STATE_PATH, "state_old": old_state, "state_data": state_data,
|
|
116
|
+
"warnings": ((["Selected client will be deselected only after this explicit retirement."]
|
|
117
|
+
if agent in state["agents"] else []) +
|
|
118
|
+
(["Modified or non-file adapter paths require manual retirement."] if conflicts else []))}
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
def apply_retirement(plan: dict) -> bool:
|
|
122
|
+
if plan["conflicts"]:
|
|
123
|
+
return False
|
|
124
|
+
target = Path(plan["target"])
|
|
125
|
+
state_path = install.safe_path(target, plan["state_path"])
|
|
126
|
+
if state_path.read_bytes() != plan["state_old"]:
|
|
127
|
+
raise ValueError("Installer state changed after preview")
|
|
128
|
+
for item in plan["removals"]:
|
|
129
|
+
path = install.safe_path(target, item["path"])
|
|
130
|
+
actual = path.read_bytes() if path.exists() and path.is_file() else None
|
|
131
|
+
if actual != item["old"]:
|
|
132
|
+
raise ValueError("Adapter changed after preview: " + item["path"])
|
|
133
|
+
completed = []
|
|
134
|
+
try:
|
|
135
|
+
for item in plan["removals"]:
|
|
136
|
+
path = install.safe_path(target, item["path"])
|
|
137
|
+
backup = install.safe_path(target, item["backup"])
|
|
138
|
+
if backup.exists():
|
|
139
|
+
if backup.read_bytes() != item["old"]:
|
|
140
|
+
raise ValueError("Existing retirement backup differs: " + item["backup"])
|
|
141
|
+
else:
|
|
142
|
+
install.atomic_write(backup, item["old"])
|
|
143
|
+
path.unlink()
|
|
144
|
+
completed.append(item)
|
|
145
|
+
install.atomic_write(state_path, plan["state_data"])
|
|
146
|
+
except (OSError, ValueError):
|
|
147
|
+
for item in reversed(completed):
|
|
148
|
+
path = install.safe_path(target, item["path"])
|
|
149
|
+
if not path.exists():
|
|
150
|
+
install.atomic_write(path, item["old"])
|
|
151
|
+
raise
|
|
152
|
+
return True
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def main(argv: list[str] | None = None) -> int:
|
|
156
|
+
parser = argparse.ArgumentParser(description=__doc__)
|
|
157
|
+
sub = parser.add_subparsers(dest="command", required=True)
|
|
158
|
+
report_parser = sub.add_parser("report", help="List client documentation status and installed adapter evidence")
|
|
159
|
+
report_parser.add_argument("--project", type=Path)
|
|
160
|
+
retire = sub.add_parser("retire", help="Preview or remove unchanged tracked files and explicitly deselect one client")
|
|
161
|
+
retire.add_argument("project", type=Path)
|
|
162
|
+
retire.add_argument("--agent", required=True)
|
|
163
|
+
retire.add_argument("--apply", action="store_true")
|
|
164
|
+
args = parser.parse_args(argv)
|
|
165
|
+
try:
|
|
166
|
+
if args.command == "report":
|
|
167
|
+
print(json.dumps(report(project=args.project), indent=2))
|
|
168
|
+
return 0
|
|
169
|
+
plan = retirement_plan(args.project, args.agent)
|
|
170
|
+
print(json.dumps({"target": plan["target"], "agent": plan["agent"], "preview": not args.apply,
|
|
171
|
+
"removals": [item["path"] for item in plan["removals"]],
|
|
172
|
+
"backups": [item["backup"] for item in plan["removals"]],
|
|
173
|
+
"conflicts": plan["conflicts"], "absent": plan["absent"],
|
|
174
|
+
"warnings": plan["warnings"]}, indent=2))
|
|
175
|
+
if args.apply:
|
|
176
|
+
apply_retirement(plan)
|
|
177
|
+
return 2 if plan["conflicts"] else 0
|
|
178
|
+
except (OSError, ValueError, UnicodeError, json.JSONDecodeError) as exc:
|
|
179
|
+
print(f"Adapter operation refused: {exc}", file=sys.stderr)
|
|
180
|
+
return 1
|
|
181
|
+
|
|
182
|
+
|
|
183
|
+
if __name__ == "__main__":
|
|
184
|
+
raise SystemExit(main())
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""Optional routing modes, pins and measured recommendations, without a switching runtime."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import copy
|
|
5
|
+
import json
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
10
|
+
import install
|
|
11
|
+
import learning
|
|
12
|
+
import metrics
|
|
13
|
+
import router
|
|
14
|
+
|
|
15
|
+
MODES = ("manual", "fixed", "recommended", "adaptive")
|
|
16
|
+
PROFILES = ("economy", "balanced", "quality")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def defaults() -> dict:
|
|
20
|
+
return {"schema": 1, "mode": "recommended", "profile": "balanced", "project_pin": None,
|
|
21
|
+
"task_pins": {}, "minimum_cases": 20, "quality_floor": 0.8,
|
|
22
|
+
"context_bytes": {"economy": 16000, "balanced": 32000, "quality": 64000},
|
|
23
|
+
"orchestration": {"planner": "work", "executor": "work", "reviewer": "work", "escalation": "escalation"}}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_pin(value: object) -> dict:
|
|
27
|
+
if not isinstance(value, dict) or set(value) != {"provider", "model", "effort"}:
|
|
28
|
+
raise ValueError("A pin needs provider, model and effort (null when unknown/not controlled)")
|
|
29
|
+
for key in ("provider", "model"):
|
|
30
|
+
metrics._identifier(value[key], key)
|
|
31
|
+
if value["effort"] is not None:
|
|
32
|
+
metrics._identifier(value["effort"], "effort")
|
|
33
|
+
return value
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def validate_config(value: object) -> dict:
|
|
37
|
+
base = defaults()
|
|
38
|
+
if not isinstance(value, dict) or set(value) - set(base):
|
|
39
|
+
raise ValueError("Invalid routing configuration or unknown fields")
|
|
40
|
+
base.update(copy.deepcopy(value))
|
|
41
|
+
if type(base["schema"]) is not int or base["schema"] != 1:
|
|
42
|
+
raise ValueError("Expected routing schema 1")
|
|
43
|
+
if not isinstance(base["mode"], str) or not isinstance(base["profile"], str) or base["mode"] not in MODES or base["profile"] not in PROFILES:
|
|
44
|
+
raise ValueError("Unknown routing mode/profile")
|
|
45
|
+
if base["project_pin"] is not None:
|
|
46
|
+
validate_pin(base["project_pin"])
|
|
47
|
+
if not isinstance(base["task_pins"], dict):
|
|
48
|
+
raise ValueError("task_pins must be an object")
|
|
49
|
+
for key, pin in base["task_pins"].items():
|
|
50
|
+
metrics._identifier(key, "task pin ID")
|
|
51
|
+
validate_pin(pin)
|
|
52
|
+
if type(base["minimum_cases"]) is not int or base["minimum_cases"] < 5:
|
|
53
|
+
raise ValueError("Minimum sample must be at least five distinct cases")
|
|
54
|
+
if type(base["quality_floor"]) not in {int, float} or not 0.5 <= base["quality_floor"] <= 1:
|
|
55
|
+
raise ValueError("Quality floor must be 0.5-1")
|
|
56
|
+
if (not isinstance(base["context_bytes"], dict) or set(base["context_bytes"]) != set(PROFILES) or
|
|
57
|
+
any(type(v) is not int or v < 1 for v in base["context_bytes"].values())):
|
|
58
|
+
raise ValueError("Every profile needs a positive context byte budget")
|
|
59
|
+
roles = base["orchestration"]
|
|
60
|
+
if (not isinstance(roles, dict) or set(roles) != {"planner", "executor", "reviewer", "escalation"} or
|
|
61
|
+
any(v not in router.ROLES for v in roles.values())):
|
|
62
|
+
raise ValueError("Orchestration roles must map to configured routing roles")
|
|
63
|
+
return base
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
def load_config(project: Path | None = None, root: Path = router.ROOT) -> dict:
|
|
67
|
+
path = (install.safe_path(metrics._target(project), "ai-kit/router/routing.json") if project else
|
|
68
|
+
root / "template/ai-kit/router/routing.json")
|
|
69
|
+
return validate_config(json.loads(path.read_text(encoding="utf-8")) if path.exists() else {})
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def recommend_route(text: str, selection: dict, providers: dict, *, config: dict | None = None,
|
|
73
|
+
records: list[dict] | None = None, mode: str | None = None, profile: str | None = None,
|
|
74
|
+
task_id: str | None = None, stack: str = "unknown", capabilities: list[str] | None = None,
|
|
75
|
+
**overrides) -> dict:
|
|
76
|
+
config = validate_config(config or {})
|
|
77
|
+
mode, profile = mode or config["mode"], profile or config["profile"]
|
|
78
|
+
if mode not in MODES or profile not in PROFILES:
|
|
79
|
+
raise ValueError("Unknown routing mode/profile")
|
|
80
|
+
if stack not in metrics.STACKS:
|
|
81
|
+
raise ValueError("Unknown stack")
|
|
82
|
+
if task_id is not None:
|
|
83
|
+
metrics._identifier(task_id, "task_id")
|
|
84
|
+
capabilities = capabilities or []
|
|
85
|
+
for capability in capabilities:
|
|
86
|
+
metrics._identifier(capability, "capability")
|
|
87
|
+
pin = config["task_pins"].get(task_id, config["project_pin"])
|
|
88
|
+
source = "task pin" if task_id in config["task_pins"] else "project pin"
|
|
89
|
+
explicit = any(overrides.get(k) is not None for k in ("provider", "model", "effort"))
|
|
90
|
+
call = dict(overrides)
|
|
91
|
+
effective_pin = copy.deepcopy(pin)
|
|
92
|
+
if pin:
|
|
93
|
+
if overrides.get("provider") is not None and overrides["provider"] != pin["provider"]:
|
|
94
|
+
effective_pin.pop("model", None)
|
|
95
|
+
effective_pin.pop("effort", None)
|
|
96
|
+
elif overrides.get("model") is not None and overrides["model"] != pin["model"]:
|
|
97
|
+
effective_pin.pop("effort", None)
|
|
98
|
+
call = {**effective_pin, **{k: v for k, v in overrides.items() if v is not None}}
|
|
99
|
+
selection = copy.deepcopy(selection)
|
|
100
|
+
for name in router.ROLES:
|
|
101
|
+
spec = selection["roles"].get(name)
|
|
102
|
+
if isinstance(spec, dict):
|
|
103
|
+
spec.pop("effort", None)
|
|
104
|
+
result = router.route_result(text, selection, providers, **call)
|
|
105
|
+
if mode in {"manual", "fixed"} and not pin and overrides.get("model") is None:
|
|
106
|
+
raise ValueError("Manual/fixed mode requires a project/task pin or explicit model")
|
|
107
|
+
if mode in {"manual", "fixed"} and not result["resolved"]:
|
|
108
|
+
raise ValueError("Pinned model is unresolved")
|
|
109
|
+
if pin and "effort" in effective_pin and overrides.get("effort") is None:
|
|
110
|
+
# Null pin effort explicitly means no effort control; the ladder cannot override it.
|
|
111
|
+
result["effort"] = effective_pin["effort"]
|
|
112
|
+
result["effort_source"] = source
|
|
113
|
+
configured_effort = router._role_selection(result["role"], selection).get("effort") is not None
|
|
114
|
+
if not pin and not explicit and not configured_effort:
|
|
115
|
+
supported = router._supported_efforts(providers.get(result["provider"], {}), result["model"])
|
|
116
|
+
ladder = ("none", "minimal", "low", "medium", "high", "xhigh", "max")
|
|
117
|
+
if profile == "quality" and result["effort"] in ladder and "high" in supported and ladder.index(result["effort"]) < ladder.index("high"):
|
|
118
|
+
result["effort"], result["effort_source"] = "high", "quality profile"
|
|
119
|
+
elif profile == "economy" and result["level"] < 3 and result["effort"] == "medium" and "low" in supported:
|
|
120
|
+
result["effort"], result["effort_source"] = "low", "economy profile"
|
|
121
|
+
learned = None
|
|
122
|
+
if mode == "adaptive" and not pin and not explicit and not configured_effort:
|
|
123
|
+
classified = router.classify_task(text, operation=overrides.get("operation"),
|
|
124
|
+
risks=overrides.get("risks", ()), components=overrides.get("components"))
|
|
125
|
+
task_class = overrides.get("operation") or classified["signals"][0].split(": ")[-1]
|
|
126
|
+
available = {selection.get("default_provider")}
|
|
127
|
+
available.update(router._role_selection(name, selection).get("provider") for name in router.ROLES)
|
|
128
|
+
if any(selection["local_models"].values()):
|
|
129
|
+
available.update(name for name, catalogue in providers.items() if catalogue.get("user_supplied_models"))
|
|
130
|
+
available.discard(None)
|
|
131
|
+
learned = learning.recommend(records or [], result, stack=stack, task_class=task_class,
|
|
132
|
+
level=result["level"], capabilities=capabilities,
|
|
133
|
+
minimum=config["minimum_cases"],
|
|
134
|
+
quality_floor=max(config["quality_floor"], 0.95 if profile == "quality" else 0.5),
|
|
135
|
+
profile=profile, allowed_providers=available)
|
|
136
|
+
if learned:
|
|
137
|
+
catalogue = providers.get(learned["provider"], {})
|
|
138
|
+
declared = list(catalogue.get("roles", {}).values())
|
|
139
|
+
supported = set(next((s.get("capabilities", []) for s in declared if s.get("model") == learned["model"]), []))
|
|
140
|
+
known_models = {s.get("model") for s in declared} | (set(selection["local_models"].values()) if catalogue.get("user_supplied_models") else set())
|
|
141
|
+
efforts = router._supported_efforts(catalogue, learned["model"])
|
|
142
|
+
if (catalogue and catalogue.get("verification_status") != "pending" and learned["model"] in known_models and
|
|
143
|
+
(learned["effort"] is None or learned["effort"] in efforts) and set(capabilities) <= supported):
|
|
144
|
+
proposed = router.route_result(text, selection, providers, **{
|
|
145
|
+
**overrides, **{k: learned[k] for k in ("provider", "model", "effort")}})
|
|
146
|
+
result = proposed
|
|
147
|
+
result["effort"] = learned["effort"]
|
|
148
|
+
result["effort_source"] = "matched measurements"
|
|
149
|
+
else:
|
|
150
|
+
learned = None
|
|
151
|
+
result.update({"routing_mode": mode, "optimization_profile": profile,
|
|
152
|
+
"context_budget_bytes": config["context_bytes"][profile],
|
|
153
|
+
"independent_review": profile == "quality" or result["level"] >= 4,
|
|
154
|
+
"escalation_policy": "diagnosed failure; independent review before acceptance" if profile == "quality" else
|
|
155
|
+
"bounded rework before diagnosed escalation; preserve quality floor" if profile == "economy" else
|
|
156
|
+
"diagnosed capability shortfall; include recovery cost",
|
|
157
|
+
"recommendation_source": "explicit" if explicit else source if pin else
|
|
158
|
+
"matched measurements" if learned else "capability ladder",
|
|
159
|
+
"measurement_evidence": learned, "execution_confirmed": False})
|
|
160
|
+
declared = providers.get(result["provider"], {}).get("roles", {}).values()
|
|
161
|
+
known = set(next((s.get("capabilities", []) for s in declared if s.get("model") == result["model"]), []))
|
|
162
|
+
result["capabilities_verified"] = set(capabilities) <= known
|
|
163
|
+
if not result["capabilities_verified"]:
|
|
164
|
+
result["adjustments"].append("Required capabilities are not declared for this model; verify before execution.")
|
|
165
|
+
if mode == "adaptive" and not learned and not pin and not explicit:
|
|
166
|
+
result["adjustments"].append("Insufficient comparable measured evidence; retain the capability recommendation.")
|
|
167
|
+
return result
|