# ai-toolkit > Professional-grade Claude Code toolkit: skills, agents, machine-enforced constitution, quality hooks. ## Documentation - [README](README.md): Installation, usage, and feature overview - [CHANGELOG](CHANGELOG.md): Version history - [ARCHITECTURE](app/ARCHITECTURE.md): System design - [CONSTITUTION](app/constitution.md): Safety rules ## Knowledge Base - [Best Practices](kb/best-practices/README.md) - [No Hardcoded Counts in Secondary Docs](kb/best-practices/no-hardcoded-counts.md) - [Plan: Deep Coverage v3.0 — 100% Native Surface Utilization](kb/history/completed/deep-coverage-v3-20260423.md) - [Plan: Drop Cascade hooks after 2026-07-01 sunset](kb/history/completed/drop-cascade-hooks-after-sunset-20260710.md) - [Plan: Ecosystem Deep Sweep — All 12 Supported Tools](kb/history/completed/ecosystem-deep-sweep-20260423.md) - [Plan: Enterprise Config Inheritance — Multi-Repo Governance with `extends`](kb/history/completed/enterprise-config-inheritance-plan-20260412.md) - [Spike: F2 MCP Context Trim — Hook Feasibility & Path Decision](kb/history/completed/f2-mcp-trim-spike-20260504.md) - [Language Plugin Packs — REMOVED](kb/history/completed/language-packs-removed-20260727.md) - [PRD: MCP Context Trim v4.0 — ABANDONED](kb/history/completed/mcp-context-trim-v4-prd-obsoleted-20260727.md) - [Implementation Plan: Native Tool Output Filter](kb/history/completed/native-tool-output-filter-plan.md) - [Removed: Nine Plugin Packs That Installed Nothing](kb/history/completed/no-op-plugin-packs-removed-20260727.md) - [Plan: Offline-First SLM Profile — Lightweight Mode for Local Models](kb/history/completed/offline-slm-profile-plan-20260411.md) - [Retirement: Native Tool-Output Filter](kb/history/completed/output-filter-retirement-20260726.md) - [Plan: Output & Token Discipline](kb/history/completed/output-token-discipline-plan-20260504.md) - [rtk Pack Integration](kb/history/completed/rtk-pack-integration-20260726.md) - [Retirement: rtk-pack](kb/history/completed/rtk-pack-retirement-20260727.md) - [Closed: Tool-Output Token Reduction](kb/history/completed/tool-output-token-reduction-closed-20260727.md) - [How-To Guides](kb/howto/README.md) - [Plan: Cloud Security Pack — Multi-Cloud Audit](kb/planning/cloud-security-pack-plan.md) - [SOP: Ecosystem Sync](kb/procedures/ecosystem-sync-sop.md) - [SOP: AI Toolkit Maintenance](kb/procedures/maintenance-sop.md) - [SOP: Post-Release Testing](kb/procedures/post-release-testing-sop.md) - [SOP: Release Preparation](kb/procedures/release-preparation-sop.md) - [SOP: Release Verification](kb/procedures/release-verification-sop.md) - [Agents Catalog](kb/reference/agents-catalog.md) - [Anti-Pattern Registry Format](kb/reference/anti-pattern-registry-format.md) - [AI Toolkit Architecture](kb/reference/architecture-overview.md) - [Config Benchmark](kb/reference/benchmark-config.md) - [CI Integration](kb/reference/ci-integration.md) - [Claude Ecosystem Benchmark Snapshot](kb/reference/claude-ecosystem-benchmark-snapshot.md) - [Claude Ecosystem Expansion Foundations](kb/reference/claude-ecosystem-expansion-foundations.md) - [CLI Reference](kb/reference/cli-reference.md) - [AI Toolkit - Codex CLI Compatibility](kb/reference/codex-cli-compatibility.md) - [Ecosystem Comparison](kb/reference/comparison.md) - [Plan: Competitive Features — ai-toolkit](kb/reference/competitive-features-implementation.md) - [AI Toolkit - GitHub Copilot Compatibility](kb/reference/copilot-compatibility.md) - [Distribution Model](kb/reference/distribution-model.md) - [Enterprise Config Inheritance Guide](kb/reference/enterprise-config-guide.md) - [Extension API Reference](kb/reference/extension-api.md) - [Global Install Model](kb/reference/global-install-model.md) - [Hierarchical Override Pattern](kb/reference/hierarchical-override-pattern.md) - [Hooks Catalog](kb/reference/hooks-catalog.md) - [External Integrations](kb/reference/integrations.md) - [Language Rules System](kb/reference/language-rules.md) - [Licensing](kb/reference/licensing.md) - [Manifest-Driven Install System](kb/reference/manifest-install.md) - [MCP Editor Compatibility](kb/reference/mcp-editor-compatibility.md) - [MCP Server Templates](kb/reference/mcp-templates.md) - [Medplum Documentation Map](kb/reference/medplum-docs-map.md) - [Merge-Friendly Install Model](kb/reference/merge-friendly-install-model.md) - [AI Toolkit - opencode Compatibility](kb/reference/opencode-compatibility.md) - [Plugin Pack Conventions](kb/reference/plugin-pack-conventions.md) - [Quick Wins Implementation Summary](kb/reference/quick-wins-implementation-summary.md) - [Skill Templates](kb/reference/skill-templates.md) - [Skills Catalog](kb/reference/skills-catalog.md) - [Skills Unification Model](kb/reference/skills-unification.md) - [Usage Statistics](kb/reference/stats.md) - [Supported Tools Registry](kb/reference/supported-tools-registry.md) - [Config Sync](kb/reference/sync.md) - [Unique Features & Differentiators](kb/reference/unique-features.md) - [Windows Support](kb/reference/windows-support.md) - [Troubleshooting](kb/troubleshooting/README.md) ## Skills - **a11y-validate**: Accessibility validator: WCAG 2.1 AA, EN 301 549, EAA. Triggers: a11y, accessibility, WCAG, EAA, ARIA, contrast, keyboard, screen reader. - **agent-creator**: Creates new specialized agents with frontmatter, tools, delegation. Triggers: new agent, create agent, agent scaffold, specialized agent. - **analyze**: Analyzes code quality, complexity, patterns across codebase. Triggers: quality report, hotspot scan, code analysis, architecture signal. - **api-patterns**: REST/GraphQL API design: naming, versioning, pagination, idempotency, OpenAPI. Triggers: API design, REST, GraphQL, OpenAPI, Swagger, idempotency, rate limit. - **app-builder**: App scaffolding: Next.js, Vite, Nuxt, Astro, FastAPI, Django, Laravel, RN, Flutter. Triggers: scaffold, bootstrap, new project, starter, dashboard, mobile app. - **architecture-audit**: Audits codebase for architectural friction, shallow modules; proposes RFCs. Triggers: improve architecture, shallow modules, deepen modules, reduce coupling. - **architecture-decision**: Architecture decisions in ADR/RFC/RFD format: context, constraints, options, recommendation. Triggers: ADR, RFC, RFD, trade-offs, design choice, pick between, evaluate approach. - **biz-scan**: Scans codebase for revenue opportunities, KPIs, monetization gaps. Triggers: business metrics, KPI, analytics gaps, monetization, revenue. - **brand-voice**: Direct technical voice for docs, README, user-facing text. Concise/strict modes. Triggers: documentation, README, content, output-mode, voice, prose style. - **briefing**: Executive daily briefing aggregating reports from all agents into decision-focused summary. Triggers: briefing, daily summary, status across system, executive update. - **build**: Builds project with auto-detected toolchain (npm, poetry, cargo, go, flutter, Docker). Triggers: build, compile, bundle, produce artifacts. - **chaos**: Injects controlled faults for resilience testing on non-prod. Triggers: chaos, fault injection, latency injection, dependency kill, resilience test. - **ci**: Detect/generate/debug CI pipeline config (GitHub Actions, GitLab CI). Triggers: CI setup, build pipeline, GitHub Actions config, debug CI, GitLab CI. - **ci-cd-patterns**: CI/CD: GitHub Actions, GitLab CI, Jenkins, caching, blue-green, canary. Triggers: CI, CD, pipeline, GitHub Actions, workflow YAML, release, canary, rollout. - **clean-code**: Code quality: meaningful names, SRP, DRY, small functions, guard clauses, refactoring. Triggers: clean code, naming, code smell, SRP, DRY, long function, god class, dead code. - **command-creator**: Creates new Claude Code slash commands with frontmatter and validation. Triggers: new slash command, create command, command scaffold. - **commit**: Creates Conventional Commits with pre-commit validation. Triggers: commit, conventional commit, git commit, message. - **content-moderation-patterns**: Content moderation with Claude: pre-filter vs LLM-classify, categories, thresholds, HITL. Triggers: moderation, safety filter, policy enforcement, content classifier. - **council**: 4-perspective decision evaluation for architecture choices. Triggers: council, evaluate decision, pros cons, multi-angle, alternatives. - **cpp-rules**: C++ coding rules: style, patterns, security, testing. Triggers: .cpp, .cc, .cxx, .hpp, .h, CMakeLists.txt, Makefile, GoogleTest, clang-tidy. - **csharp-patterns**: C#/.NET: LINQ, async/await, DI, records, nullable refs, ASP.NET Core, EF Core, MediatR. Triggers: C#, .NET, dotnet, ASP.NET, EF Core, LINQ, record type, IServiceCollection. - **csharp-rules**: C#/.NET coding rules: style, patterns, security, testing. Triggers: .cs, .csproj, .sln, ASP.NET, ASP.NET Core, EF Core, LINQ, NUnit, xUnit, dotnet. - **cve-scan**: Scans deps for known CVEs via native audit (npm, pip, composer, cargo, go, bundler, dart). Triggers: CVE scan, vulnerability scan, npm audit, pip audit. - **dart-rules**: Dart/Flutter coding rules: style, patterns, security, testing. Triggers: .dart, pubspec.yaml, Flutter, Riverpod, Bloc, widget, StatelessWidget, StatefulWidget. - **database-patterns**: DB schema design and query tuning: normalization, indexing, N+1, transactions, EXPLAIN. Triggers: schema, index, slow query, N+1, PostgreSQL, MySQL, EXPLAIN, deadlock, query plan. - **debug**: Systematic debugging via logs, health checks, hypothesis-driven investigation. Triggers: debug, error, trace root cause, fix bug, reproduce symptom, investigation. - **deep-research**: Multi-source web research methodology: retrieve-vs-answer gate, complexity-scaled search budget, query craft, primary-source preference, source-conflict skepticism, adversarial verification, attribution-without-reproduction. Triggers: deep research, multi-source, web research, synthesize sources, cross-reference, fact synthesis, source verification. - **deploy**: Deploys with pre-flight checks and health verification. Triggers: deploy, deployment, ship, release, push to prod. - **design-an-interface**: Generates and compares parallel interface designs (Ousterhout 'Design It Twice'). Triggers: design API, interface options, compare modules, design it twice. - **design-engineering**: UI craftsmanship: animation rules, easing, micro-interactions, state polish. Triggers: animation, transition, ease-out, motion, micro-interaction, hover, loading state, UI polish. - **docker-devops**: Docker/K8s: Dockerfile, multi-stage, compose, manifests, Helm. Triggers: Docker, Dockerfile, container, Kubernetes, k8s, compose, Helm, pod. - **docs**: Generates/updates README, API docs, architecture notes. Triggers: docs, README, API docs, architecture note, documentation. - **documentation-standards**: KB conventions: YAML frontmatter, 5-category taxonomy (reference/howto/procedures/troubleshooting/best-practices). Triggers: kb/, SOP, runbook, howto, frontmatter, knowledge base. - **ecommerce-patterns**: E-commerce: cart, checkout, payments (Stripe/Adyen), order state, inventory, promos, tax. Triggers: cart, checkout, SKU, payment, Stripe, Shopify, Medusa, Magento, coupon, refund. - **evaluate**: Evaluates RAG retrieval and LLM-as-judge metrics (faithfulness, relevancy, context precision). Triggers: measure RAG quality, knowledge gap, RAG eval, golden dataset. - **evolve**: Analyzes agent/skill failures, drafts prompt/permission fixes. Triggers: improve agent, refine skill, system prompt, optimize agent. - **explain**: Explains code/architecture with Mermaid diagrams and sequence flows. Triggers: what does X do, how does Y work, explain code, sequence diagram. - **explore**: Explores codebase structure, stack, and architecture. Triggers: explore codebase, project structure, stack overview, architecture map. - **fix**: Applies targeted fix to known bug/lint error, verifies with same command that surfaced it. Triggers: fix, apply fix, fix bug, fix lint, targeted fix. - **flutter-patterns**: Flutter/Dart: widgets, state mgmt (Riverpod/Bloc), navigation, platform channels. Triggers: Flutter, Dart, widget, Riverpod, Bloc, pubspec, hot reload. - **git-mastery**: Advanced Git: rebase, bisect, reflog, cherry-pick, worktrees, LFS. Triggers: rebase, bisect, cherry-pick, reflog, force push, merge conflict, worktree. - **golang-rules**: Go coding rules: style, patterns, security, testing. Triggers: .go, go.mod, go.sum, Gin, Echo, Gorilla, testing, gofmt. - **grill-me**: Stress-tests a plan via Socratic questioning down each decision branch. Triggers: stress-test, grill me, validate assumptions, challenge plan, socratic review. - **health**: Service/infra health via liveness/readiness checks, resource usage, quick diagnostics. Triggers: health check, services up, system status, infra health, degraded service. - **hipaa-validate**: HIPAA validator: PHI exposure, audit logging, encryption, access control, BAA refs. Triggers: HIPAA, PHI, healthcare compliance, audit log, BAA. - **hook-creator**: Create new Claude Code lifecycle hook (PreToolUse/PostToolUse/Stop/SessionStart) with bash + hooks.json. Triggers: create hook, lifecycle hook, PreToolUse, PostToolUse, hook event. - **index**: Reindexes KB for semantic search via vector store (Qdrant). Triggers: reindex KB, rebuild index, vector reindex, refresh embeddings. - **instinct-review**: Reviews/promotes/removes instincts from `.claude/instincts/*.md`. Triggers: instinct review, curate instincts, manage instincts, promote instinct. - **introspect**: Agent self-debugging and recovery. Use when stuck in loops, making repeated errors, or quality degrades. Triggers: introspect, self-debug, stuck, loop, why failing. - **java-patterns**: Java: Spring Boot, CompletableFuture, records, sealed types, JPA/Hibernate, virtual threads. Triggers: Java, Spring, JPA, Hibernate, Maven, Gradle, virtual thread, sealed class. - **java-rules**: Java coding rules: style, patterns, security, testing. Triggers: .java, pom.xml, build.gradle, Spring, Spring Boot, JPA, Hibernate, JUnit, Maven, Gradle. - **json-mode-patterns**: Structured JSON output from Claude: tool-use-as-JSON, schema, parsing, partial recovery. Triggers: JSON mode, structured output, schema validation, JSON parsing. - **kotlin-patterns**: Kotlin: coroutines, Flow, sealed/data classes, null safety, Ktor, Compose, KMP. Triggers: Kotlin, coroutine, Flow, suspend, Ktor, Jetpack Compose, KMP, kotlinx. - **kotlin-rules**: Kotlin coding rules: style, patterns, security, testing. Triggers: .kt, .kts, build.gradle.kts, Ktor, Jetpack Compose, coroutines, kotlinx. - **lint**: Runs linter+typechecker with auto-detected toolchain (ruff/mypy, eslint/tsc, phpstan, golangci-lint, clippy). Triggers: lint, typecheck, static analysis. - **mcp-builder**: Builds production MCP servers via 4-phase methodology: research, implement, test, evaluate. Triggers: build MCP, new MCP, MCP integration, MCP server scaffold. - **mcp-patterns**: MCP server design: tool schemas, resources, stdio/SSE, capability negotiation. Triggers: MCP, Model Context Protocol, JSON-RPC, stdio, SSE, Claude Desktop. - **medplum-rules**: Medplum (FHIR healthcare) coding rules: style, patterns, security, testing. Triggers: medplum.config.mts, medplum.config.ts, FHIR, Medplum, Bot, Subscription, Questionnaire. - **mem-search**: Searches past coding sessions for observations, decisions, context. Triggers: mem-search, recall session, past work, prior decisions, session history. - **migrate**: Run/create DB migrations (Alembic, Prisma, Laravel, Django, Flyway, Drizzle); checks backup. Triggers: apply migration, rollback, generate migration. - **migration-patterns**: Zero-downtime DB migrations: expand-contract, double-write, backfill, blue-green. Triggers: migration, schema change, backfill, ALTER TABLE, online DDL. - **model-routing-patterns**: Multi-model pipelines (Haiku/Sonnet/Opus): cost routing, escalation, fallback chains. Triggers: model routing, Haiku, Sonnet, Opus, escalation, fallback chain. - **night-watch**: Autonomous maintenance (dep updates, dead code, small refactors) in isolated branch, off-hours. Triggers: night watch, autonomous maintenance, dep updates. - **observability-patterns**: Observability: structured logs, metrics (RED/USE), tracing, SLO/SLI. Triggers: logging, metrics, Prometheus, Grafana, OpenTelemetry, trace, monitoring. - **onboard**: Sets up ai-toolkit in a project: symlinks, CLAUDE.md, intent interview. Triggers: onboard, setup project, install ai-toolkit, migrate project. - **orchestrate**: Coordinates multiple specialized agents in parallel. Triggers: orchestrate, multi-agent, parallel agents, coordinate agents. - **panic**: Emergency kill switch — halts all agents via lockfile gate. Triggers: panic, stop everything, kill switch, halt agents, agents looping. - **performance-profiling**: Performance: golden signals, p50/p95/p99, flame graphs, load testing. Triggers: performance, slow, latency, p99, flame graph, bottleneck, memory leak. - **persona**: Switches engineering persona at runtime. Triggers: persona, switch role, backend-lead, frontend-lead, devops-eng, junior-dev. - **php-rules**: PHP coding rules: style, patterns, security, testing. Triggers: .php, composer.json, Laravel, Symfony, PHPUnit, PSR-12, Composer. - **plan**: Breaks features/goals into phased plans with task lists, agent assignments, dependencies. Triggers: plan feature, implementation roadmap, break down task, project phases. - **plugin-creator**: Creates opt-in plugin packs with manifests + module scaffolding for Claude/Codex. Triggers: new plugin, plugin pack, plugin scaffold. - **pr**: Creates GitHub PR after pre-flight checks (lint/typecheck/tests), structured summary from commits. Triggers: pr, pull request, create PR, ready to merge. - **prd-to-issues**: Splits a PRD into vertical-slice GitHub issues with HITL/AFK tagging and dependencies. Triggers: PRD to issues, create tickets, break down PRD, work items. - **prd-to-plan**: Converts PRD into phased plan via tracer-bullet vertical slices. Triggers: PRD to plan, break down PRD, implementation plan, tracer bullets, phased plan. - **predict**: Analyzes diffs for regression risk and blast radius, generates risk-scored impact report. Triggers: PR review, code change risk, breaking change, blast radius, regression check. - **prompt-caching-patterns**: Anthropic API prompt caching: TTL, breakpoints, stacking, invalidation, hit rate. Triggers: prompt caching, cache_control, cache breakpoint, cache TTL, hit rate. - **python-rules**: Python coding rules: style, patterns, security, testing. Triggers: .py, .pyi, pyproject.toml, requirements.txt, Pipfile, FastAPI, Django, Flask, pytest, SQLAlchemy, ruff, mypy. - **qa-session**: Interactive QA: user reports bugs conversationally, agent files GitHub issues. Triggers: QA session, report bug, file issue, conversational QA, bug intake. - **rag-patterns**: RAG: embeddings, chunking, hybrid search (BM25+vector), reranking, CRAG, multi-hop. Triggers: RAG, embedding, pgvector, Qdrant, Pinecone, Weaviate, reranker, semantic search. - **refactor**: Refactors code for quality and maintainability. Triggers: refactor, clean up, restructure, improve code, modernize. - **refactor-plan**: Creates detailed refactor plan with tiny commits via interview, files as GitHub RFC. Triggers: refactor plan, refactoring RFC, incremental refactor, safe steps. - **repeat**: Runs prompt/slash command on recurring interval until done or limit. Triggers: repeat, recurring task, poll status, run every N minutes, interval. - **research-mastery**: Hierarchical retrieval: KB → MCP/Context7 → web. Triggers: research, fact-check, verify, synthesize, cross-reference, multi-source, cite sources. - **review**: Reviews code for quality, security, correctness. Triggers: code review, quality review, security review, review PR, review branch. - **rollback**: Rolls back git commit, DB migration, or deploy to known-good with safety + health checks. Triggers: rollback, revert deploy, revert migration, rollback commit, git revert. - **ruby-patterns**: Ruby/Rails: blocks, metaprogramming, ActiveRecord, Sidekiq, RSpec, Sorbet, Hanami. Triggers: Ruby, Rails, ActiveRecord, Sidekiq, RSpec, Gemfile, bundler, Hanami, Sorbet. - **ruby-rules**: Ruby coding rules: style, patterns, security, testing. Triggers: .rb, Gemfile, .gemspec, Rails, ActiveRecord, Sidekiq, RSpec, Sorbet, rubocop. - **rust-patterns**: Rust: ownership, lifetimes, async (Tokio), Result/anyhow/thiserror, traits, unsafe. Triggers: Rust, borrow checker, lifetime, Tokio, cargo, trait, impl, Result, unsafe, clippy. - **rust-rules**: Rust coding rules: style, patterns, security, testing. Triggers: .rs, Cargo.toml, Cargo.lock, Tokio, Axum, Serde, clippy, cargo test. - **security-patterns**: App security: OWASP, authN/authZ, input validation, secrets, TLS, CSRF/XSS/SQLi, JWT, CSP, LLM prompt injection. Triggers: security, OWASP, auth, JWT, CSRF, XSS, SQL injection, secrets, TLS, CSP, CORS, prompt injection, LLM output trust, tool permissions. - **seo-validate**: SEO validator: meta/OG, Schema.org, hreflang, Core Web Vitals, crawlability. Triggers: SEO, meta tags, Schema.org, hreflang, LCP, INP, CLS, Core Web Vitals, sitemap, crawlability. - **skill-audit**: Scans skills/agents for security risks: dangerous patterns, secrets, excessive perms. Triggers: skill audit, security scan, agent audit, dangerous pattern. - **skill-creator**: Creates new skills from templates via guided workflow. Triggers: new skill, create skill, skill scaffold, skill template. - **subagent-development**: Executes plans via fresh subagents per task with two-stage review (spec → quality). Triggers: subagent execution, execute plan, fresh agent per task, spec compliance review. - **swarm**: Runs tasks via Map-Reduce, Consensus, or Relay swarms. Triggers: swarm, map-reduce, consensus swarm, relay swarm, parallel agents. - **swift-patterns**: Swift/iOS: SwiftUI, Combine, async/await, actors, SPM, Core Data, UIKit interop. Triggers: Swift, SwiftUI, Combine, iOS, Xcode, actor, Core Data, @MainActor, @State. - **swift-rules**: Swift coding rules: style, patterns, security, testing. Triggers: .swift, Package.swift, .xcodeproj, SwiftUI, Combine, async/await, XCTest. - **tdd**: TDD with red-green-refactor loop and vertical slices. Triggers: TDD, test-first, red-green-refactor, test driving development. - **test**: Runs project test suite with coverage, auto-detects framework (pytest, vitest, jest, flutter, go, cargo, phpunit). Triggers: run tests, test suite, coverage report. - **testing-patterns**: Testing strategy: pyramid, AAA, mocks/fakes/stubs, flaky tests, coverage. Triggers: test, fixture, mock, stub, e2e, TDD, Playwright, Cypress, flaky, coverage, property-based. - **triage-issue**: Bug triage: explores codebase for root cause, files GitHub issue with TDD fix plan. Triggers: triage, investigate bug, fix plan, root cause, file issue, bug report. - **typescript-patterns**: TypeScript types: generics, discriminated unions, Zod, satisfies, branded types. Triggers: TypeScript, TS, generics, Zod, satisfies, strict mode. - **typescript-rules**: TypeScript/JavaScript coding rules: style, patterns, security, testing. Triggers: .ts, .tsx, .js, .jsx, package.json, tsconfig.json, React, Next.js, Vue, Vite, Vitest, Jest, ESLint. - **ubiquitous-language**: Extracts DDD ubiquitous language glossary, flags ambiguities, saves to UBIQUITOUS_LANGUAGE.md. Triggers: define domain terms, build glossary, harden terminology, DDD, domain model. - **verification-before-completion**: Forces verification commands before success claims. Evidence before assertions. Triggers: complete, fixed, passing, done, ready, verified. - **workflow**: Starts and manages autonomous agent workflows. Triggers: workflow, start workflow, autonomous agents, agent pipeline. - **write-a-prd**: Creates PRD via interactive interview + codebase exploration + module design. Triggers: write PRD, product requirements, plan new feature, PRD interview. ## Agents - **ai-engineer**: AI/ML integration specialist. Use for LLM integration, vector databases, RAG pipelines, embeddings, AI agent orchestration, document indexing, semantic search, hybrid retrieval, and answer generation. Triggers: ai, ml, llm, embedding, vector, rag, agent, openai, anthropic, search, retrieval, indexing, chunking, reranking. - **backend-specialist**: Expert backend architect for Node.js, Python, PHP, and modern serverless systems. Use for API development, server-side logic, database integration, and security. Triggers: backend, server, api, endpoint, database, auth, fastapi, express, laravel. - **business-intelligence**: Opportunity Discovery agent. Scans data models and code to identify missing business metrics, KPIs, and opportunities for value creation. - **chaos-monkey**: Resilience testing agent. Use to inject faults, latency, and failures into the system to verify robustness and recovery mechanisms. - **chief-of-staff**: Executive Summary agent. Aggregates reports from all other agents to reduce noise and present a single, actionable daily briefing to the user. - **code-archaeologist**: Legacy code investigation and understanding specialist. Trigger words: legacy code, code archaeology, dead code, technical debt, dependency analysis, refactoring, code history - **code-reviewer**: Code review and security audit expert. Use for security reviews, Devil's Advocate analysis, quality audits, best practices validation. Triggers: review, security, audit, quality, best practices, vulnerability. - **command-expert**: CLI commands and shell scripting specialist. Trigger words: bash, shell, CLI, script, automation, command line, build script, deployment script - **data-analyst**: Data analysis and visualization expert. Use for SQL queries, data exploration, analytics, reporting, and insights. Triggers: data, analysis, sql, query, visualization, metrics, dashboard, pandas, report. - **data-scientist**: Statistical analysis and data insights specialist. Use for statistical analysis, data visualization, EDA, A/B testing, and predictive modeling. Triggers: statistics, visualization, eda, analysis, hypothesis testing, ab test. - **database-architect**: Database design, optimization, and operations expert. Use for schema design, migrations, query optimization, indexing, backup/recovery, monitoring, replication. Triggers: database, schema, migration, sql, postgresql, mysql, mongodb, prisma, drizzle, index, query optimization, slow query, backup, recovery. - **debugger**: Root cause analysis expert. Use for cryptic errors, stack traces, intermittent failures, silent bugs, and systematic debugging. Triggers: debug, error, exception, traceback, bug, failure, root cause. - **devops-implementer**: Infrastructure implementation expert. Use for writing Terraform, Ansible, Docker, and shell scripts based on approved architecture notes and implementation summaries. Triggers: terraform, ansible, docker, kubernetes, shell, infrastructure, deployment, configuration. - **documenter**: Documentation and KB expert. Use for architecture notes, runbooks, changelogs, KB updates, how-to guides, API docs, READMEs, tutorials, SOP creation, KB organization, content quality review. Triggers: document, documentation, architecture-note, runbook, changelog, howto, readme, kb, sop, technical writing. - **explorer-agent**: Codebase exploration and discovery agent. Use for mapping project structure, finding dependencies, understanding architecture, and research. Does NOT write code - only reads and analyzes. - **fact-checker**: Claim verification expert. Use for verifying facts, source validation, RAG result accuracy checking. Triggers: fact check, verify, accuracy, claim, source validation. - **frontend-specialist**: Senior Frontend Architect for React, Next.js, Vue, and modern web systems. Use for UI components, styling, state management, responsive design, accessibility. Triggers: component, react, vue, ui, ux, css, tailwind, responsive, nextjs. - **game-developer**: Game development across all platforms (PC, Web, Mobile, VR/AR). Use for Unity, Godot, Unreal, Phaser, Three.js. Covers game mechanics, multiplayer, optimization, 2D/3D graphics. - **incident-responder**: Production incident response expert. Use for P1-P4 incidents, outages, emergency fixes, and postmortem documentation. Triggers: incident, outage, production down, emergency, P1, alert, monitoring. - **infrastructure-architect**: System design expert. Use for architectural decisions, architecture notes, trade-off analysis, technology selection. Triggers: architecture, design, decision, trade-off, scalability, infrastructure planning. - **infrastructure-validator**: Deployment validation expert. Use for deployment verification, health checks, testing, rollback procedures. Triggers: validate, deploy, deployment, health check, smoke test, rollback. - **llm-ops-engineer**: LLM operations expert. Use for LLM caching, fallback strategies, cost optimization, observability, and reliability. Triggers: llm, language model, openai, ollama, caching, fallback, token, cost. - **mcp-specialist**: MCP server design, implementation, client configuration, and integration troubleshooting. Triggers: mcp, model context protocol, json-rpc, sse, stdio, mcp server, mcp config, mcp integration, mcp connection, claude desktop, mcp client. - **mcp-testing-engineer**: MCP protocol testing expert. Use for MCP server testing, protocol compliance, transport validation, integration testing. Triggers: mcp test, protocol compliance, mcp validation, transport testing. - **meta-architect**: Self-Optimization agent. Analyzes system performance and mistakes to update agent definitions and instructions. The only agent allowed to modify .claude/agents/*. - **ml-engineer**: Machine learning systems specialist. Use for model training, data pipelines, MLOps, and model deployment. Triggers: ml, machine learning, model training, mlops, tensorflow, pytorch, scikit-learn. - **mobile-developer**: Expert in React Native, Flutter, and native mobile development. Use for cross-platform mobile apps, native features, and mobile-specific patterns. Triggers: mobile, react native, flutter, ios, android, app store, expo, swift, kotlin. - **night-watchman**: Autonomous maintenance agent. Use for automated dependency updates, dead code removal, refactoring, and project hygiene tasks. Typically scheduled to run off-hours. - **nlp-engineer**: Natural Language Processing specialist. Use for text processing, NER, text classification, information extraction, and language model fine-tuning. Triggers: nlp, ner, tokenization, text classification, sentiment, spacy, transformers. - **orchestrator**: Multi-agent coordination and task orchestration. Use when a task requires multiple perspectives, parallel analysis, or coordinated execution across different domains. Invoke for complex tasks benefiting from security, backend, frontend, testing, and DevOps expertise combined. - **performance-optimizer**: Performance optimization expert. Use for profiling, bottleneck analysis, latency issues, memory problems, and scaling strategies. Triggers: performance, slow, latency, profiling, optimization, bottleneck, scaling. - **predictive-analyst**: Precognition agent. Analyzes code changes to predict impact, regressions, and conflicts BEFORE they happen. Uses dependency graphs and historical data. - **product-manager**: Product management and value maximization expert. Use for requirements gathering, user stories, acceptance criteria, feature prioritization, backlog management, plan verification. Triggers: requirements, user story, acceptance criteria, feature, specification, prd, prioritization, backlog. - **project-planner**: Smart project planning agent. Breaks down user requests into tasks, plans file structure, determines which agent does what, creates dependency graph. Use when starting new projects or planning major features. - **prompt-engineer**: LLM prompt design and optimization specialist. Trigger words: prompt, LLM, chain-of-thought, few-shot, system prompt, prompt engineering, token optimization - **qa-automation-engineer**: Test automation and QA specialist. Use for E2E testing, API testing, performance testing, and CI/CD test integration. Triggers: e2e, playwright, cypress, selenium, api test, performance test, automation. - **search-specialist**: Information retrieval and search optimization specialist. Trigger words: search, query, semantic search, information retrieval, relevance, ranking, search optimization - **security-architect**: Proactive security design expert. Use for Threat Modeling, architecture security reviews, and designing secure systems (AuthN/AuthZ, Crypto). - **security-auditor**: Security expert. Use for OWASP Top 10, CVE analysis, security audits, penetration testing, vulnerability assessment, hardening. Triggers: security, owasp, cve, vulnerability, audit, hardening, penetration, pentest, injection test, api security. - **seo-specialist**: Search engine + generative engine optimization specialist. Trigger words: SEO, GEO, AEO, search engine, meta tags, structured data, Core Web Vitals, sitemap, robots.txt, schema.org, llms.txt, ChatGPT visibility, Claude citation, Perplexity ranking, AI Overviews, topical authority, chunk architecture, semantic triples, query fan out - **system-governor**: The Guardian of the Constitution. Validates all evolutionary changes and enforces immutable rules. Has VETO power. - **tech-lead**: Technical authority for code quality, architecture patterns, and stack decisions. Use for code reviews, technological disputes, and standards enforcement. - **technical-researcher**: Deep technical investigation and multi-source research synthesis specialist. Trigger words: technical research, feasibility study, root cause analysis, API investigation, compatibility research, comparison matrix, synthesize, aggregate, report, executive summary, gap analysis, findings, multi-source, cross-reference - **test-engineer**: Testing expert. Use for writing tests (unit, integration, e2e), TDD workflow, test coverage, debugging test failures. Triggers: test, pytest, unittest, coverage, tdd, testing, mock, fixture. --- ## kb/best-practices/README.md --- title: "Best Practices" service: ai-toolkit category: best-practices tags: [best-practices, guidelines] last_updated: "2026-03-25" --- # Best Practices Guidelines and recommendations. Guides will be added here as they are created. --- ## kb/best-practices/no-hardcoded-counts.md --- title: "No Hardcoded Counts in Secondary Docs" category: best-practices service: ai-toolkit tags: [counts, documentation, maintenance, skills, agents, hooks, tests] created: "2026-04-07" last_updated: "2026-04-07" description: "Counts (skills, agents, hooks, tests) should only appear in README.md and manifest.json. All other docs must NOT contain hardcoded numbers to avoid drift." --- # No Hardcoded Counts in Secondary Docs ## Rule Hardcoded counts (skills, agents, hooks, tests, plugins) are allowed ONLY in: - **README.md** — badges, "What You Get" table, comparison table - **manifest.json** — module descriptions - **package.json** — description field All other files (CLAUDE.md, ARCHITECTURE.md, KB docs, plugin.json, llms.txt, AGENTS.md, copilot-instructions, rules, GEMINI.md) must NOT contain hardcoded counts like "90 skills" or "44 agents". ## Why Every time a skill, agent, or hook is added/removed, dozens of files need updating. This causes constant drift and stale counts that erode trust. Consolidating to 2-3 files makes maintenance tractable. ## How to Apply - In secondary docs, use relative language: "all agents", "the full skill set", "available hooks" - If a doc MUST reference scale, use: "see README.md for current counts" - `validate.py` checks counts only in README.md badges — that's sufficient - When adding skills/agents/hooks, update ONLY: README.md badges + manifest.json descriptions ## Anti-Pattern ```markdown # BAD — hardcoded count in ARCHITECTURE.md Shared AI development toolkit — 90 skills, 44 agents # GOOD — no count Shared AI development toolkit with multi-platform support ``` --- ## kb/history/completed/deep-coverage-v3-20260423.md --- title: "Plan: Deep Coverage v3.0 — 100% Native Surface Utilization Across 12 Tools" category: planning service: ai-toolkit doc_type: plan status: completed tags: [v3, deep-coverage, ecosystem, generators, hooks, skills, subagents, commands, profile-full] created: "2026-04-23" last_updated: "2026-04-23" completed: "2026-04-23" completion: "100%" description: "Ship v3.0.0 where every supported editor exposes the full ai-toolkit surface it is capable of hosting natively: hooks, subagents, custom commands, skill pointers. Introduce --profile full. Skip the 2.13.0 interim release and fold the completed deep sweep into 3.0.0." --- # Plan: Deep Coverage v3.0 — 100% Native Surface Utilization **Status:** :yellow_circle: IN PROGRESS **Invocation:** continuation of `ecosystem-deep-sweep-2026-04-23` — same orchestration model **Estimated effort:** 10-15h orchestrated (~3h wall-clock across 4 parallel buckets + consolidation) **Deliverable:** v3.0.0 release where every editor's native surface is fully utilized; `--profile full` available --- ## 1. Objective After the 2026-04-23 deep sweep closed the doc-drift gap, **v3.0.0 closes the capability-utilization gap**: each editor now exposes the full ai-toolkit surface it can host natively. Definition of "100% coverage" chosen: **each editor works at 100% of its native capability** (compat-read counts). No cargo-cult duplication. No writing to `~/.cursor/`, `~/.augment/rules/` etc. globally. --- ## 2. Policy decisions (immutable constraints for all buckets) | # | Decision | Rule | |---|----------|------| | 1 | Skill propagation | `.claude/skills/` canonical. Cursor/Windsurf/opencode → compat-read (nothing). Augment/Gemini/Antigravity → **pointer skill** (1 file per editor). Codex → native `.agents/skills/` mirror | | 2 | Global writes | Only `~/.claude/`. Cursor/Windsurf/opencode get global coverage via compat-read. Augment/Gemini/Roo require `--local` | | 3 | Surface activation | **`--profile full`** turns on every native surface. `standard` stays close to today's defaults but adds niepodważalne wypełnienia (Copilot wiring + Gemini hooks). `minimal` unchanged | | 4 | Default behavior | `--editors ` alone uses `standard`. Users who want the full stack pass `--profile full` | | 5 | Version | Skip 2.13. Ship everything (completed sweep + v3 work) as **3.0.0** with migration notes | --- ## 3. What's missing → what each bucket delivers ### Bucket 1 — Hooks generators (backend-specialist) **Owned files** - New: `scripts/generate_gemini_hooks.py` (writes `.gemini/settings.json` hooks merge) - New: `scripts/generate_cursor_hooks.py` (writes `.cursor/hooks.json`) - New: `scripts/generate_windsurf_hooks.py` (writes `.windsurf/hooks.json`) - New: `scripts/generate_augment_hooks.py` (writes `~/.augment/settings.json` hooks merge) - New: `tests/test_hooks_per_editor.bats` (≥20 tests covering all 4 generators) **Must-haves** - All generators reuse `~/.softspark/ai-toolkit/hooks/*.sh` scripts (no duplicate shell code). - Preserve user-authored hook entries; mark our entries with `_source: ai-toolkit`. - Idempotent on regeneration. - Event mapping informed by each editor's docs (Claude Code events ↔ target editor events). ### Bucket 2 — Native agents + custom commands (ai-engineer) **Owned files** - New: `scripts/generate_augment_agents.py` (`.augment/agents/*.md` with YAML frontmatter: name, description, model, color, tools, disabled_tools) - New: `scripts/generate_augment_commands.py` (`.augment/commands/*.md` from user-invocable skills) - New: `scripts/generate_cursor_agents.py` (`.cursor/agents/*.md` mirroring Claude Code agents) - New: `scripts/generate_gemini_commands.py` (`.gemini/commands/*.toml` custom slash commands) - New: `tests/test_native_surfaces.bats` (≥25 tests) **Must-haves** - Filter: only `user-invocable: true` skills become custom commands. - `ai-toolkit-*` prefix everywhere for install/uninstall sweep. - Do not touch files without our prefix. ### Bucket 3 — Skill pointers + Codex mirror (ai-engineer) **Owned files** - New: `scripts/generate_gemini_skills.py` (`.gemini/skills/ai-toolkit-skill-catalogue/SKILL.md` — pointer) - New: `scripts/generate_augment_skills.py` (`.augment/skills/ai-toolkit-skill-catalogue/SKILL.md` — pointer) - New: `scripts/generate_codex_skills.py` (Codex-native mirror to `.agents/skills//SKILL.md`) - New: `tests/test_skills_native.bats` (≥15 tests) **Must-haves** - Pointer pattern same as Antigravity: 1 file per editor referencing `~/.claude/skills/` and listing the catalogue. - Codex mirror respects `user-invocable: false` (knowledge skills stay, task skills stay — Codex reads them all). - Codex skills use the upstream `.agents/skills` discovery path. ### Bucket 4 — Install wiring + profile full + docs (devops-implementer) **Owned files** - `scripts/install_steps/ai_tools.py` — wire in all new generators from buckets 1-3 - `scripts/install.py` — parse `--profile full`, propagate to `_create_local_ai_tool_configs` - `scripts/config_validator.py` — ensure `full` profile is accepted (already present, verify) - `README.md` — "What's New in v3.0.0" + migration notes - `CHANGELOG.md` — v3.0.0 entry - `kb/reference/global-install-model.md` — document profile semantics - `kb/reference/supported-tools-registry.md` — per-tool "generators by profile" column - `kb/procedures/maintenance-sop.md` — profile table update - `package.json` version bump → `3.0.0` - `package-lock.json` sync - `tests/test_install_profiles.bats` (≥15 tests covering minimal/standard/strict/full × 3 editors) **Must-haves** - `standard` profile: Copilot directory mode ON, Gemini hooks ON (non-breaking additions). - `full` profile: everything from `standard` + all native surfaces from buckets 1-3. - Migration note: users on `standard` today get Copilot instructions/prompts and Gemini hooks automatically after upgrading (acceptable breaking for major bump, documented). --- ## 4. Success criteria - [ ] 13 new Python generators (6+4+3, minus wiring) - [ ] ≥75 new bats tests across buckets - [ ] `npm test` green - [ ] `python3 scripts/validate.py --strict` 0/0 - [ ] `python3 scripts/ecosystem_doctor.py --check` exit 0 - [ ] `ai-toolkit install --local --editors all --profile full` produces every native surface per editor - [ ] `ai-toolkit install --local --editors all --profile standard` is still minimal-invasive (no subagents, no hooks for non-Claude editors) **except** Copilot + Gemini hooks (both documented migration notes) - [ ] README test badge bumped - [ ] CHANGELOG v3.0.0 entry - [ ] All docs updated (registry, install model, maintenance SOP) - [ ] Single atomic commit - [ ] Tag `v3.0.0` ready (push held for user confirmation) --- ## 5. Safety rails - **Do not commit during bucket work.** Orchestrator consolidates. - **Do not touch files outside your bucket's ownership list.** - **Preserve user files** via `ai-toolkit-*` prefix on generated artifacts. - **Do not write to global editor paths** (`~/.cursor/`, `~/.augment/rules/`, etc.) — policy decision 2. - **Do not change `standard` profile in ways that break existing users**, beyond the two documented additions (Copilot directory mode + Gemini hooks). - **Test per bucket locally before reporting.** Bucket reports must include a "tests green" line. --- ## 6. Consolidation steps (orchestrator) 1. Merge all 4 bucket registry deltas into `scripts/ecosystem_tools.json` 2. Run `python3 scripts/ecosystem_doctor.py --update` 3. Run `npm run generate:all` 4. Bump `package.json` → `3.0.0`; sync `package-lock.json` 5. Update `README.md` badge + "What's New" 6. Update `CHANGELOG.md` with v3.0.0 entry (include migration notes) 7. Run `python3 scripts/validate.py --strict` (must pass 0/0) 8. Run `npm test` (all green) 9. Run `python3 scripts/ecosystem_doctor.py --check` (exit 0) 10. Move this plan doc to `kb/history/completed/deep-coverage-v3-20260423.md` 11. Single commit + tag `v3.0.0` 12. Hold push pending user confirmation --- ## 7. Related - `kb/history/completed/ecosystem-deep-sweep-20260423.md` — predecessor plan (doc-drift closure) - `kb/reference/global-install-model.md` — install scope semantics - `kb/reference/supported-tools-registry.md` — tool registry - `scripts/config_validator.py` — `VALID_PROFILES` already includes `full` --- ## kb/history/completed/drop-cascade-hooks-after-sunset-20260710.md --- title: "Plan: Drop Cascade hooks after 2026-07-01 sunset" category: planning service: ai-toolkit tags: - windsurf - devin - cascade - hooks - deprecation - cleanup doc_type: plan status: completed created: "2026-06-10" last_updated: "2026-07-10" completion: "100%" trigger_date: "2026-07-01" description: "Completed cleanup of the deprecated Windsurf Cascade hooks generator after the 2026-07-01 sunset; Devin CLI .devin/hooks.v1.json is now the sole live hook surface." --- # Plan: Drop Cascade hooks after 2026-07-01 sunset **Completed in v4.13.0 (2026-07-10).** The deprecated generator and install/test wiring were removed; the Devin hook generator remains. > **Filed to history 2026-07-27.** The plan was finished in v4.13.0 but sat in > `kb/planning/` for another two and a half weeks, where it read as open work. > Before moving it, every claim in the Verification section below was re-run > against the codebase: > > | Check | Result | > |---|---| > | `scripts/generate_windsurf_hooks.py` deleted | gone | > | `grep -rn generate_windsurf_hooks scripts/ tests/` | no source references (one stale `.pyc` under gitignored `__pycache__`) | > | `_HOOK_STEM_ALIAS = {"devin": "windsurf"}` retained in `validate.py` | present, line 882 | > | windsurf `our_generators` in `scripts/ecosystem_tools.json` | Cascade generator absent, `generate_devin_hooks.py` present | > | `kb/reference/supported-tools-registry.md` hooks-migration row | reads "Complete" in past tense | > | `validate.py --strict` | 0 errors, 0 warnings | > > The work was real and complete. Only the filing was wrong. > > **Process note.** This is the second document found misfiled in > `kb/planning/` on the same day; the other, > [`mcp-context-trim-v4-prd-obsoleted-20260727.md`](mcp-context-trim-v4-prd-obsoleted-20260727.md), > had gone obsolete because the platform shipped the fix first. A plan with a > `trigger_date` needs a matching move-to-history step in the release that acts > on it — otherwise `kb/planning/` accumulates work that is already done or no > longer worth doing, and the next reader cannot tell which is which. ## Why this exists Windsurf rebranded to Devin Desktop on 2026-06-02. The Cascade agent — and its `.windsurf/hooks.json` hook surface (`agent_action_name`/`tool_info` format) — is available **only through 2026-07-01**. Devin Local / Devin CLI do **not** read `.windsurf/hooks.json` as a fallback. v4.8.0 already shipped the replacement: `generate_devin_hooks.py` emits `.devin/hooks.v1.json` in the Claude-compatible format Devin CLI uses. During the transition **both** generators run at `profile=full` so pre-sunset Cascade users keep working. After 2026-07-01 the Cascade half is dead code and must be removed (Constitution Art. VI.1 — no dead code). ## Trigger First ai-toolkit release **on or after 2026-07-01**. Do NOT do this earlier — removing it before the sunset breaks Cascade users who are still on the old agent. ## Scope — remove the Cascade hooks surface 1. **Delete the generator:** `scripts/generate_windsurf_hooks.py`. 2. **Unwire the install step:** in `scripts/install_steps/ai_tools.py`, remove the `_try_generator("generate_windsurf_hooks", cwd)` call (keep `generate_devin_hooks`). Update the `profile=full` dry-run message to drop `.windsurf/hooks.json (Cascade, deprecated)`. 3. **Tests:** remove the windsurf-`.windsurf/hooks.json` cases from `tests/test_hooks_per_editor.bats` (output path, valid JSON, source tag, `$HOME` prefix, idempotence, user-preservation, `pre_write_code` coverage) and the `profile=full` assertion in `tests/test_install_profiles.bats` (`windsurf + full emits .windsurf/hooks.json`). Keep all `.devin/hooks.v1.json` tests. Adjust the README test-count badge to the new total. 4. **Registry:** in `scripts/ecosystem_tools.json` (windsurf entry), remove `scripts/generate_windsurf_hooks.py` from `our_generators`; keep `.windsurf/hooks.json` out of `config_paths` (it was never listed). Trim the `status_note` hooks-migration paragraph to past tense ("Cascade hooks removed in vX.Y.Z"). 5. **Docs:** in `kb/reference/supported-tools-registry.md` drop the `generate_windsurf_hooks.py` row and the "drop after 2026-07-01" note; in `kb/reference/hooks-catalog.md` remove the Cascade row from the Per-Editor Native Hooks table and the deprecation wording, leaving the Devin CLI section as the windsurf-family hook surface. 6. **validate.py:** the `_HOOK_STEM_ALIAS = {"devin": "windsurf"}` mapping STAYS — it is what keeps the `devin` hook generator counted as windsurf hooks in the README-honesty check after the Cascade generator is gone. 7. **CHANGELOG / version:** minor bump, `Removed` entry, regen artifacts, full release-preparation SOP gate. ## Verification - `python3 scripts/validate.py --strict` — 0/0 (editor-hooks-honesty must still report windsurf as hook-enabled via the `devin` generator alias). - `python3 scripts/ecosystem_doctor.py --offline --check` — exit 0. - `npm test` — 0 `not ok`; no test recreates the deleted `generate_windsurf_hooks.py` generator. - `grep -rn "generate_windsurf_hooks" scripts/ tests/` returns nothing (Art. VI.1 orphan check — the deleted generator is fully unwired). `.windsurf/hooks.json` intentionally remains referenced by the one-time migration/strip cleanup (`scripts/install_steps/ai_tools.py`) and its test, and in narrative docs (CHANGELOG, README, `kb/`, docstrings). ## Do NOT touch - `generate_devin_hooks.py` and `.devin/hooks.v1.json` — the live replacement. - The `.devin/`/`.windsurf/` rules + skills dual-emit (that fallback persists as long as Devin Desktop reads legacy `.windsurf/` paths; this plan is hooks-only). ## Related - `kb/reference/hooks-catalog.md` — Per-Editor Native Hooks + Devin CLI section - `kb/reference/supported-tools-registry.md` — windsurf entry, hooks-migration row - `kb/procedures/ecosystem-sync-sop.md` — class-D deprecation workflow - `scripts/ecosystem_tools.json` — windsurf `status_note` --- ## kb/history/completed/ecosystem-deep-sweep-20260423.md --- title: "Plan: Ecosystem Deep Sweep — All 12 Supported Tools" category: planning service: ai-toolkit doc_type: plan status: completed tags: [ecosystem, editors, generators, deep-sweep, orchestrate, drift, integration] created: "2026-04-23" last_updated: "2026-04-23" completed: "2026-04-23" completion: "100%" description: "Orchestrate-ready plan for a deep per-tool documentation sweep across all 12 supported tools (Claude Code + 11 editors). Each agent owns 2-3 tools: fetches docs, diffs against our generators, proposes minimal patches. Consolidation step collects results into a single changeset." --- # Plan: Ecosystem Deep Sweep — All 12 Supported Tools **Status:** :yellow_circle: PROPOSED **Invocation:** `/orchestrate deep ecosystem sweep per kb/planning/ecosystem-deep-sweep-2026-04-23.md` **Estimated effort:** 4-6 hours orchestrated (1-1.5 h per agent in parallel) **Deliverable:** Per-tool drift report + concrete generator/skill patches + updated registry --- ## 1. Objective For every supported tool in `scripts/ecosystem_tools.json`: 1. Read the current official documentation end-to-end (not just landing page) 2. Identify every feature that ai-toolkit could integrate with but does not currently 3. Classify each gap using the ecosystem-sync SOP taxonomy (class A-F) 4. Produce minimal, reviewable patches for class B/D/E/F gaps 5. Update the registry (`ecosystem_tools.json`) with new capability markers and config paths 6. Refresh the snapshot (`benchmarks/ecosystem-doctor-snapshot.json`) **Explicit non-goals:** complete feature parity, deep refactor of generators, adding new editors to the roster. --- ## 2. Parallelization Strategy 12 tools → **4 agents × 3 tools each** by affinity and complexity: | Agent | Role | Tools | Rationale | |-------|------|-------|-----------| | `backend-specialist` | Deep CLI / config analysis | `claude-code`, `codex-cli`, `opencode` | CLI + config.toml + agents/commands/plugins — backend integration depth | | `frontend-specialist` | Editor UI integrations | `cursor`, `windsurf`, `google-antigravity` | Editor-embedded AI, rule files, MCP-via-UI | | `devops-implementer` | Pipeline + rules tools | `github-copilot`, `cline`, `roo-code` | Rules directories, MCP JSON variants, mode configs | | `ai-engineer` | LLM-native tools | `gemini-cli`, `aider`, `augment` | Pure LLM workflows, minimal IDE coupling | Each agent works **in parallel**, independent file scopes (different generators). Cross-file coordination only at the registry update (single JSON file). --- ## 3. Per-Tool Task Template Every agent applies the **same 7-step protocol** per tool in their bucket: ### Step 1 — Baseline our current integration Read these files (read-only): - `scripts/generate__*.py` — every generator targeting this tool - `scripts/ecosystem_tools.json` — the tool's registry entry - `kb/reference/supported-tools-registry.md` — human docs section - `benchmarks/ecosystem-doctor-snapshot.json` — last-seen headings/markers/version Produce a 3-line summary: "we currently generate X, Y, Z for this tool". ### Step 2 — Fetch official docs Primary URL is in `ecosystem_tools.json::urls.docs`. Additionally fetch: - `urls.release_notes` — recent changes (last 6 months) - `urls.changelog` — if distinct from release notes - Any deep-link from the docs landing page that corresponds to an integration surface (rules, hooks, MCP, agents, commands, plugins, config schema) Use `WebFetch` (for general) or `gh api` (for GitHub-hosted docs like Codex CLI, opencode). ### Step 3 — Extract the feature surface For the current version of the tool, enumerate: - Config file paths (the tool's OWN paths, not ours) - Rule / instruction / prompt formats - Hook / lifecycle event names (if any) - MCP config target path (if supported) - Agent / custom-mode / preset concepts (if any) - Slash command / CLI subcommand surface - Supported model providers (note, do not integrate) - Authentication / API-key mechanisms Produce a structured markdown table: `Feature | Since version | Stable? | Our integration?` ### Step 4 — Diff against our output For each feature in the table, compare against: - What our `generate__*.py` produces - What fields are in our registry's `capability_markers` Mark each row with one of: - `✅ supported` — we already emit / track it - `⚠️ partial` — we emit a subset; specific sub-feature missing - `❌ missing` — we do not support at all - `➖ out of scope` — tool has it, but not applicable to ai-toolkit's mission ### Step 5 — Classify each gap For each `⚠️` / `❌` row, assign one of the SOP drift classes: | Class | Name | Action | |-------|------|--------| | A | Cosmetic | No code change; update snapshot only | | B | New feature — integrate | Patch generator(s), add tests | | C | New feature — not adopted | Note in registry, no code | | D | Deprecation | Migration warning in generator + CHANGELOG | | E | Feature promoted to default | Simplify generator; keep fallback comment | | F | Newly globally available | New generator / extended generator | ### Step 6 — Produce patches (class B/D/E/F only) For every class B/D/E/F gap: 1. Edit the relevant generator in `scripts/generate__*.py` 2. If a new capability marker emerges, add to `ecosystem_tools.json::capability_markers` 3. If a new config path emerges, add to `ecosystem_tools.json::config_paths` 4. If a hook event or skill frontmatter field emerges (for Claude Code), update: - `app/skills/hook-creator/SKILL.md` (hooks table) - `app/skills/skill-creator/SKILL.md` (frontmatter reference) - `scripts/validate.py` (allowlist) 5. Add a bats test under `tests/test_.bats` covering the new output 6. Update the tool's section in `kb/reference/supported-tools-registry.md` **Constraints on patches:** - One generator change per logical feature (no "big bang" commits) - Preserve existing output format for backward compatibility - New output opt-in via flag if it would change existing user-visible state - Every new capability marker must pass the doctor's probe on the live docs page ### Step 7 — Report Each agent emits a single markdown report with: - Feature matrix table (step 3+4+5 combined) - List of patches applied (files changed, bats tests added) - List of class B/D/E/F gaps NOT patched (with reason: "out of scope", "requires user decision", "blocker") - Registry diff (before/after for the tool's JSON entry) --- ## 4. Consolidation (after all agents finish) Run in order: 1. Merge registry entries — single edit to `ecosystem_tools.json` combining all 12 per-tool updates 2. Regenerate human registry doc: manually update `kb/reference/supported-tools-registry.md` from JSON 3. `python3 scripts/ecosystem_doctor.py --update` — baseline new capability markers 4. `python3 scripts/validate.py --strict` — must pass 5. `npm test` — must pass (includes the newly added bats tests per tool) 6. `python3 scripts/ecosystem_doctor.py --check` — exit 0 7. Regenerate downstream artifacts: ```bash npm run generate:all ``` 8. Collect all per-agent reports into `kb/learnings/ecosystem-sweep-2026-04-23.md` --- ## 5. Success Criteria - [ ] All 12 tools covered (no "skipped for time" items) - [ ] Every class B/D/E/F gap has either a patch OR a documented reason for deferral - [ ] Registry `capability_markers` list grew for at least 6 of 12 tools (signals real gap coverage) - [ ] `validate.py --strict`: 0 errors, 0 warnings - [ ] `npm test`: all green (including new per-tool bats tests) - [ ] `ecosystem_doctor.py --check`: exit 0 after snapshot refresh - [ ] Single consolidated commit per agent-bucket, plus one final consolidation commit --- ## 6. Known Traps (from prior ecosystem work) - **SPA docs** (Cursor, Antigravity, some Augment pages): `urllib` gets empty HTML skeleton. Agents should note this and do a **manual browser visit** or use a JS-aware fetcher. Do not treat "0 headings" as "nothing new". - **GitHub docs** rate-limit aggressively on repeated reads. Space out fetches or use `gh api`. - **Feature gates** vary by user plan. Copilot Business vs Individual vs Enterprise have different surface. Integrate with the OSS surface; document gated features as C (not adopted). - **Version skew** on config schemas. A setting that existed in v1.x may be deprecated in v2.x. When docs reference "available since v1.5" and we don't know what version users run, default to generating the newer form with a comment. - **Markdown vs MDX**: Cursor uses `.mdc`, Claude Code uses `.md`, Cline uses `.md` in `.clinerules/`, Roo uses `.md` in `.roo/rules/`. Don't assume one format fits all. --- ## 7. Orchestrate Invocation In a fresh Claude Code session (to avoid context rot from this session): ``` /orchestrate deep ecosystem sweep for ai-toolkit per kb/planning/ecosystem-deep-sweep-2026-04-23.md Spawn 4 agents in parallel: - backend-specialist: claude-code, codex-cli, opencode - frontend-specialist: cursor, windsurf, google-antigravity - devops-implementer: github-copilot, cline, roo-code - ai-engineer: gemini-cli, aider, augment Each agent follows the 7-step per-tool protocol in section 3. After all 4 report, run consolidation (section 4) and produce the sweep summary. ``` --- ## 8. Deliverables (per agent) Each agent's final output to orchestrator: 1. **Feature matrix** — one table per assigned tool (step 3+4+5) 2. **Patch log** — list of commits staged (not committed yet — orchestrator consolidates) 3. **Registry delta** — proposed JSON diff for `ecosystem_tools.json` 4. **Gaps not patched** — with rationale (out-of-scope, blocker, deferred) 5. **Test additions** — bats test file names + test count Orchestrator's final output: 1. Consolidated commit with message `feat(ecosystem): deep sweep 2026-04-23 — N class B/F integrations` 2. Version bump decision (minor if any class B/F, patch if only class A updates) 3. `kb/learnings/ecosystem-sweep-2026-04-23.md` — retrospective noting which tools needed most work (informs priority for next sweep) --- ## 9. Safety Rails - **Do not** silently upgrade default behavior — every user-visible change lands behind a flag OR goes through a minor version bump with CHANGELOG mention - **Do not** rewrite generators wholesale — incremental additions only - **Do not** commit during the sweep — orchestrator consolidates at the end - **Do not** modify files outside the tool's scope (e.g., backend-specialist touching frontend-specialist's files requires a handoff) - **Do** preserve existing symlinks and file-path expectations — the installer depends on them --- ## 10. Related - [Ecosystem Sync SOP](../procedures/ecosystem-sync-sop.md) — the process this plan instantiates - [Supported Tools Registry](../reference/supported-tools-registry.md) — source of truth for tool list - `scripts/ecosystem_doctor.py` — drift detector consumed by orchestrator consolidation - `scripts/ecosystem_tools.json` — registry file edited by every agent --- ## 11. Retrospective — 2026-04-23 ### Execution summary - 4 parallel agents, 3 tools each — full 12/12 coverage, one consolidation pass. - 161 new bats tests (679 → 840); validate.py 0 errors / 0 warnings; `ecosystem_doctor --check` exit 0. - 14 files modified, 12 new test files, 2 registry docs updated, 1 snapshot rebaselined. ### What worked - **Bucket-level file ownership** eliminated merge conflicts entirely. Agents that flagged cross-bucket edits (`ecosystem_tools.json`, registry markdown) correctly left them for the orchestrator. - **The 7-step protocol** caught high-impact bugs we would have shipped otherwise — Windsurf rules missing `trigger:` frontmatter (silent invisibility to Cascade), Aider's default `attribute-co-authored-by: true` violating our own git policy, Roo modes lacking `whenToUse` (invisible to Orchestrator). - **SPA-wall compensation patterns** (Antigravity bundle strings, Cursor/Windsurf llms.txt mirrors, GitHub release notes as fallback) were reusable across buckets. ### What surprised us - **Claude Code 2.1.x grew ~14 new hook events** and 3 new handler types since our last sync. Our validate.py allowlist was the bottleneck, not any generator. - **Cross-editor compat reads**: Cursor, Windsurf, and opencode now natively read `.claude/skills/` and `.claude/agents/` — we get skill/agent discovery in those editors "for free" without emitting duplicates. Saved ~300 generated files. - **Copilot tier-gating is heavy**: half of the upstream surface (custom agents, repo MCP, org instructions) is Business/Enterprise-only and was classified as C (documented non-integration). - **Test #755 regression** from the Codex `PermissionRequest` addition: the test counted `guard-destructive.sh` occurrences with `== 1`. Fixed by updating the expected count to 2 with a comment explaining why base hooks legitimately register it twice now. ### Open items flagged for future passes 1. **Native `.agents/skills/*/SKILL.md` emission** (class B) — writes the Codex skill catalog to the upstream discovery path. 2. **`.opencode/skills/` duplication** — deferred indefinitely; `.claude/skills/` fallback already works. 3. **New generators needed**: `generate_gemini_hooks.py`, `generate_augment_agents.py`, `generate_augment_commands.py`, `generate_augment_hooks.py`. 4. **Cross-editor hooks unification**: Cursor and Windsurf both shipped `.cursor/hooks.json` and `.windsurf/hooks.json` — worth a dedicated shared-schema pass rather than per-editor copies. 5. **Roo `.roomodes` YAML variant** — upstream-preferred; deferred until a YAML multi-line helper is added. 6. **Copilot install wiring**: new `.github/instructions/` and `.github/prompts/` directories are emitted when `generate_copilot.py` is called with a target dir, but `install_steps/ai_tools.py` doesn't invoke that path yet. Wire behind minor bump. ### Process refinements for next sweep - **Add a "class B/F deferred" register**: buckets produced these ad-hoc; a structured list in the plan would make prioritization for the next sweep trivial. - **Cross-bucket test impact**: adding per-tool bats tests inflates the test count and trips the README badge validator. Next time, bump the badge at the start of consolidation, not at the end. - **Search docs via llms.txt first** when the vendor publishes one — bypasses SPA walls with zero fallback logic. --- ## kb/history/completed/enterprise-config-inheritance-plan-20260412.md --- title: "Plan: Enterprise Config Inheritance — Multi-Repo Governance with extends" category: planning service: ai-toolkit tags: - enterprise - multi-repo - config-inheritance - extends - governance - team-management - monorepo doc_type: plan status: completed created: "2026-04-10" last_updated: "2026-04-11" completion: "100%" description: "Configuration inheritance system for ai-toolkit. Enables organizations to define a shared base config (agents, rules, hooks, profiles, constitution overrides) published as an npm package or local path, which individual projects extend via an `extends` field. Changes to the base config propagate automatically on `ai-toolkit update`. Targets enterprises managing 10-100+ repositories with uniform AI governance." --- # Plan: Enterprise Config Inheritance — Multi-Repo Governance with `extends` **Status:** Proposed **Completion:** 0% **Created:** 2026-04-10 **Origin:** Organizations adopting ai-toolkit across 10-100+ repositories face a config synchronization problem — updating a rule or policy requires touching every repository individually. The `extends` pattern (popularized by ESLint, TypeScript, Prettier) solves this by establishing a single source of truth that projects inherit from. **Estimated Effort:** 5-7 weeks (1 person) — MVP (core engine + install integration) shippable in ~3.5 weeks --- ## 1. Objective Create a configuration inheritance system where projects can extend a shared base config published as an npm package, a Git URL, or a local path. The base config defines organizational defaults (which agents to enable, which rules to enforce, which hooks to require, persona presets, and constitution amendments). Individual projects can override or supplement the base, creating a layered governance model. **Key design principles:** - **Familiar pattern** — mirrors ESLint's `extends`, TypeScript's `extends`, and Prettier's shared configs - **npm-first distribution** — base configs are regular npm packages (e.g., `@mycompany/ai-toolkit-config`). Resolver shells out to `npm pack` CLI (respects `.npmrc` auth) — no hand-rolled npm client, preserves stdlib-only constraint - **Single extends in v1** — `"extends": "string"` only. Multi-base merge (`"extends": [...]`) deferred to v2 to avoid merge-ordering complexity (ESLint's multi-extends is a known source of confusion) - **Layered merge** — base → project, with explicit override semantics (`override: true` required for safety-critical overrides) - **Constitution immutable** — base constitution articles cannot be modified by projects, period. Projects can only ADD new articles (article 6+). No weakening detection heuristics — absolute immutability is simpler and safer - **Offline-capable** — resolved at `install`/`update` time, not at runtime - **Backward-compatible** — projects without `extends` work exactly as today (no breaking changes) - **Audit trail** — `state.json` records which base config was resolved and what was overridden --- ## 1a. Functional Requirements | ID | Requirement | Priority | Success Metric | |----|-------------|----------|----------------| | FR1 | Resolve `extends` from npm package, git URL, local path | Must | 4 source types work | | FR2 | Deep merge base → project config with layered semantics | Must | Merge engine handles dict, list, scalar types | | FR3 | Constitution immutability — Articles I-V cannot be modified | Must | 100% block rate on modification attempts | | FR4 | Override validation with `override: true` + `justification` | Must | Missing justification → error | | FR5 | `enforce` block constraints (minHookProfile, requiredPlugins, forbidOverride, requiredAgents) | Must | All 4 constraint types enforced | | FR6 | Install/update integration — resolve extends during install | Must | `install --local` detects `.softspark-toolkit.json` | | FR7 | `config diff` command — show project vs base differences | Must | All merge layers visible | | FR8 | `config validate` command — schema + enforcement validation | Must | Exit 0/1 for pass/fail | | FR9 | `config init` — interactive project config setup | Should | Guided flow produces valid `.softspark-toolkit.json` | | FR10 | `config create-base` — scaffold npm base config package | Should | Ready-to-publish package with `package.json` | | FR11 | Lock file for reproducible installs | Should | Identical resolved config across team members | | FR12 | Audit trail in `state.json` | Should | Resolved version + overrides recorded | | FR13 | CI enforcement command (`config check`) | Could | Exit 0/1 for governance compliance | | FR14 | Multi-base extends (`"extends": [...]`) | Won't (v2) | Deferred — merge ordering complexity | --- ## 2. Architecture Overview ``` Organization Level (published once, consumed by all repos): ═══════════════════════════════════════════════════════════ @mycompany/ai-toolkit-config (npm package) ├── ai-toolkit.config.json ← base configuration ├── rules/ │ ├── code-review-policy.md ← company-specific rules │ └── deployment-checklist.md ├── agents/ │ └── compliance-auditor.md ← company-specific agent └── package.json Project Level (per-repository): ══════════════════════════════ my-service/ ├── .softspark-toolkit.json ← project config with "extends" ├── .claude/ │ ├── CLAUDE.md ← generated (base + project merged) │ └── settings.json ← generated (base hooks + project hooks merged) └── ... Merge Pipeline: ══════════════ @mycompany/ai-toolkit-config ← Layer 0: organizational defaults │ ▼ ai-toolkit defaults (manifest.json) ← Layer 1: toolkit defaults │ ▼ .softspark-toolkit.json ← Layer 2: project overrides │ ▼ Resolved Configuration ← Final: CLAUDE.md, settings.json, etc. ``` ### Config Resolution Order ``` 1. Load base config from "extends" (npm package, git URL, or local path) 2. Merge with ai-toolkit defaults (manifest.json profiles) 3. Apply project-level overrides from .softspark-toolkit.json 4. Validate merged config (constitution immutability, schema validation) 5. Generate output files (CLAUDE.md, settings.json, agent symlinks, etc.) ``` --- ## 3. Progress Tracking | # | Feature | Priority | Status | Est. Time | Notes | |---|---------|----------|--------|-----------|-------| | 1.1 | `.softspark-toolkit.json` schema definition | P0 | **Done** | 1d | `scripts/schemas/ai-toolkit-config.schema.json` | | 1.2 | Config resolver (npm, git, local path) | P0 | **Done** | 3d | `scripts/config_resolver.py` (~330 LOC) | | 1.3 | Merge engine (layered merge with override semantics) | P0 | **Done** | 3d | `scripts/config_merger.py` (~340 LOC) | | 1.4 | Constitution immutability guard | P0 | **Done** | 1d | In config_merger.py `_merge_constitution()` | | 2.1 | Install/update integration | P0 | **Done** | 2d | `install.py` + `ai_tools.py` — auto-detect, resolve, merge, inject | | 2.2 | `ai-toolkit config diff` command | P0 | **Done** | 1.5d | `scripts/config_cli.py` `cmd_diff()` | | 2.3 | `ai-toolkit config validate` command | P0 | **Done** | 1d | `scripts/config_cli.py` `cmd_validate()` | | 2.4 | `ai-toolkit config init` command | P1 | **Done** | 1.5d | Interactive + flag-driven, validates extends | | 2.5 | `ai-toolkit config create-base` command | P1 | **Done** | 2d | `scripts/config_scaffold.py` — full npm package scaffold | | 3.1 | Audit trail in state.json | P1 | **Done** | 1d | `install_state.py` extends field + `.softspark-toolkit-extends.json` | | 3.2 | Lock file (`.softspark-toolkit.lock.json`) | P1 | **Done** | 1.5d | `scripts/config_lock.py` — generate/consume/staleness check | | 3.3 | Base config scaffolder (npm package template) | P1 | **Done** | 1.5d | Part of `config_scaffold.py` `create_base_package()` | | 3.4 | CI enforcement (`ai-toolkit config check`) | P2 | **Done** | 1d | `config_cli.py` `cmd_check()` — JSON output, exit codes | | 4.1 | Tests | P1 | **Done** | 3d | 39 tests: resolver (7), merger (13), CLI (10), install integration (9) | | 4.2 | Documentation | P1 | **Done** | 3d | `kb/reference/enterprise-config-guide.md` — comprehensive guide | **Phasing (MVP-first):** - **MVP Phase 1 (week 1-2):** Core engine — schema (1.1), resolver (1.2), merge engine (1.3), constitution guard (1.4) - **MVP Phase 2 (week 2-3):** Integration + diff — install integration (2.1), `config diff` (2.2), `config validate` (2.3), tests for above (~3.5 weeks = shippable MVP) - **Phase 3 (week 4-5):** CLI polish — `config init` (2.4), `config create-base` (2.5), scaffolder (3.3) - **Phase 4 (week 5-6):** Enterprise — audit trail (3.1), lock file (3.2), CI enforcement (3.4) (**gate behind real enterprise feedback**) - **Phase 5 (week 6-7):** Tests + documentation (4.1, 4.2) (3d docs — all 9 docs per CLAUDE.md rules) > **Demand validation gate:** Ship MVP (Phases 1-2), announce, measure adoption. Only build Phase 4 (lock file, CI enforcement, audit trail) in response to confirmed enterprise demand. --- ## 4. Dependency Graph ``` MVP Phase 1: Core Engine (week 1-2) ==================================== Schema definition (1.1) ──────┐ ├──► Merge engine (1.3) Config resolver (1.2) ────────┤ └──► Constitution guard (1.4) MVP Phase 2: Integration + Diff (week 2-3) ============================================ Install integration (2.1) ──┐ ├──► config diff (2.2) └──► config validate (2.3) └──► MVP tests → SHIP ═══ DEMAND VALIDATION GATE ═══ Phase 3: CLI Polish (week 4-5) =============================== ├──► config init (2.4) └──► create-base (2.5) + scaffolder (3.3) Phase 4: Enterprise (week 5-6) ================================ Audit trail (3.1) ──┐ ├──► Lock file (3.2) └──► CI enforcement (3.4) Phase 5: Polish (week 6-7) =========================== └──► Full tests + docs (4.1, 4.2) ``` --- ## 5. Detailed Implementation ### Phase 1: Core Engine (week 1-2) #### 1.1 Configuration Schema (`.softspark-toolkit.json`) > **v1 scope:** The full schema below shows the target state. v1 implements only: `extends`, `profile`, `agents`, `rules`, `constitution`, and `enforce`. See section 6a for the v1/v2 field breakdown. **Project-level config file:** ```json { "$schema": "https://softspark.github.io/ai-toolkit/schemas/ai-toolkit-config.json", "extends": "@mycompany/ai-toolkit-config", "profile": "standard", "persona": "backend-lead", "hookProfile": "strict", "agents": { "enabled": ["backend-specialist", "test-engineer", "debugger"], "disabled": ["game-developer", "mobile-developer"], "custom": ["./agents/compliance-auditor.md"] }, "skills": { "disabled": ["/deploy", "/rollback"], "custom": ["./skills/internal-deploy/"] }, "rules": { "inject": ["./rules/code-review-policy.md"], "remove": [] }, "plugins": { "required": ["security-pack", "memory-pack"], "forbidden": [] }, "languages": ["typescript", "python"], "editors": ["cursor", "windsurf", "copilot"], "constitution": { "amendments": [ { "article": 6, "title": "Data Sovereignty", "text": "All code generation must comply with GDPR. No personal data in prompts. No PII in generated code comments." } ] }, "overrides": { "hooks": { "quality-check": { "override": true, "justification": "Company uses custom lint pipeline via Jenkins", "replacement": "skip" } } } } ``` **Base config (`ai-toolkit.config.json` in npm package):** ```json { "$schema": "https://softspark.github.io/ai-toolkit/schemas/ai-toolkit-base-config.json", "name": "@mycompany/ai-toolkit-config", "version": "2.1.0", "description": "MyCompany standard AI coding config", "extends": null, "profile": "strict", "persona": "backend-lead", "hookProfile": "strict", "agents": { "enabled": ["backend-specialist", "test-engineer", "code-reviewer", "security-auditor", "debugger", "documenter"], "disabled": ["game-developer"], "custom": ["./agents/compliance-auditor.md"] }, "rules": { "inject": [ "./rules/code-review-policy.md", "./rules/deployment-checklist.md", "./rules/data-handling-policy.md" ] }, "plugins": { "required": ["security-pack"] }, "languages": ["typescript"], "constitution": { "amendments": [ { "article": 6, "title": "Data Sovereignty", "text": "All code generation must comply with GDPR. No personal data in prompts." }, { "article": 7, "title": "Audit Compliance", "text": "All AI-generated code changes must be logged to the company audit system. The governance-capture hook must remain enabled." } ] }, "enforce": { "minHookProfile": "standard", "requiredPlugins": ["security-pack"], "forbidOverride": ["constitution", "guard-destructive", "guard-path"], "requiredAgents": ["security-auditor"] } } ``` **`enforce` section:** Base configs can define non-overridable constraints: - `minHookProfile` — projects cannot go below this profile - `requiredPlugins` — must be installed in all projects - `forbidOverride` — these components cannot be overridden - `requiredAgents` — must be enabled in all projects --- #### 1.2 Config Resolver **Resolution sources:** | Source | Syntax | Resolution | |--------|--------|------------| | npm package | `"extends": "@mycompany/ai-toolkit-config"` | `npm pack --pack-destination /tmp` + extract | | npm with version | `"extends": "@mycompany/ai-toolkit-config@^2.0.0"` | Version resolution via npm | | Git URL | `"extends": "git+https://github.com/myco/ai-config.git"` | `git clone --depth 1` to cache | | Local path | `"extends": "../shared-config"` | Resolve relative to project root | | ~~Multiple bases~~ | ~~`"extends": ["@mycompany/base", "@mycompany/typescript-extra"]`~~ | Deferred to v2 — multi-base merge ordering is a complexity trap | **Cache directory:** `~/.softspark/ai-toolkit/config-cache/` ``` ~/.softspark/ai-toolkit/config-cache/ @mycompany/ ai-toolkit-config/ 2.1.0/ ai-toolkit.config.json rules/ agents/ ``` **Resolution algorithm:** ```python def resolve_extends(extends_value: str, project_root: str) -> list[BaseConfig]: """Resolve extends chain into ordered list of base configs. v1: single string only. Multi-base (list) deferred to v2. """ configs = [] for source in [extends_value]: # v2: support list[str] if source.startswith('@') or source.startswith('npm:'): config = resolve_npm(source) elif source.startswith('git+'): config = resolve_git(source) elif source.startswith('.') or source.startswith('/'): config = resolve_local(source, project_root) else: raise ConfigError(f"Unknown extends source: {source}") # Recursive: base config may also have "extends" if config.extends: parent_configs = resolve_extends(config.extends, config.root) configs.extend(parent_configs) configs.append(config) return configs def resolve_extends(extends_value: str, project_root: str, _visited: set[str] | None = None) -> list[BaseConfig]: """Full signature with cycle detection via visited set.""" if _visited is None: _visited = set() if extends_value in _visited: raise ConfigError( f"Circular extends detected: {extends_value} already in chain " f"{' → '.join(_visited)}. Check your base config's 'extends' field." ) if len(_visited) >= 5: raise ConfigError( f"Extends chain too deep (max 5 levels). Chain: {' → '.join(_visited)}" ) _visited.add(extends_value) # ... resolution logic as above, passing _visited to recursive calls ``` **Max recursion depth:** 5 levels (prevent circular extends). Circular detection via visited set. **Offline handling:** If the npm/git source is unavailable: 1. Check cache (`~/.softspark/ai-toolkit/config-cache/`) 2. If cached version found → use with warning: "Using cached config v2.1.0 (offline)" 3. If not cached → error with instructions: "Run `ai-toolkit config update` when online" --- #### 1.3 Merge Engine **Layered deep merge with explicit override semantics:** ```python def merge_configs(base: dict, project: dict) -> dict: """Merge project config over base config with rules.""" merged = {} for key in set(base.keys()) | set(project.keys()): base_val = base.get(key) proj_val = project.get(key) if proj_val is None: merged[key] = base_val elif base_val is None: merged[key] = proj_val elif key == 'constitution': merged[key] = merge_constitution(base_val, proj_val) elif key == 'agents': merged[key] = merge_agents(base_val, proj_val) elif key == 'rules': merged[key] = merge_rules(base_val, proj_val) elif key == 'overrides': merged[key] = validate_overrides(base, proj_val) elif isinstance(base_val, dict) and isinstance(proj_val, dict): merged[key] = merge_configs(base_val, proj_val) elif isinstance(base_val, list) and isinstance(proj_val, list): merged[key] = list(set(base_val + proj_val)) # union else: merged[key] = proj_val # project wins for scalars return merged ``` **Agent merge rules:** ```python def merge_agents(base: dict, project: dict) -> dict: """Merge agent configs — project can enable/disable but not remove base-required.""" merged_enabled = set(base.get('enabled', [])) # Project can add agents merged_enabled.update(project.get('enabled', [])) # Project can disable agents (unless base enforces them) for agent in project.get('disabled', []): if agent in base.get('enforce', {}).get('requiredAgents', []): raise ConfigError( f"Cannot disable '{agent}' — required by base config '{base['name']}'. " f"Contact your team lead to request an exemption." ) merged_enabled.discard(agent) return { 'enabled': sorted(merged_enabled), 'custom': base.get('custom', []) + project.get('custom', []) } ``` **Override validation:** ```python def validate_overrides(base: dict, overrides: dict) -> dict: """Validate project overrides against base enforcement rules.""" forbidden = set(base.get('enforce', {}).get('forbidOverride', [])) for key, override in overrides.items(): if key in forbidden: raise ConfigError( f"Cannot override '{key}' — forbidden by base config '{base['name']}'.\n" f"Forbidden overrides: {', '.join(sorted(forbidden))}\n" f"Contact your team lead to request an exemption." ) if not override.get('override'): raise ConfigError( f"Override for '{key}' requires explicit 'override: true' + 'justification' field.\n" f"This ensures intentional deviation from organizational defaults." ) if not override.get('justification'): raise ConfigError( f"Override for '{key}' requires a 'justification' field explaining why.\n" f"Example: \"Company uses custom lint pipeline via Jenkins\"" ) return overrides ``` --- #### 1.4 Constitution Immutability Guard **Core rule:** Base constitution articles are absolutely immutable. Projects can only ADD new articles. No weakening-detection heuristic (character count, semantic analysis) — these produce false positives and are gameable. Instead, the rule is simple and absolute: if an article number exists in the base, it cannot be modified by the project. ```python def merge_constitution(base: dict, project: dict) -> dict: """Merge constitution — additions only, no modifications.""" base_amendments = {a['article']: a for a in base.get('amendments', [])} proj_amendments = {a['article']: a for a in project.get('amendments', [])} # Toolkit articles I-V are always immutable IMMUTABLE_ARTICLES = {1, 2, 3, 4, 5} merged = dict(base_amendments) for article_num, amendment in proj_amendments.items(): if article_num in IMMUTABLE_ARTICLES: raise ConfigError( f"Cannot modify Constitution Article {article_num} — immutable.\n" f"Articles I-V are defined by ai-toolkit and cannot be overridden.\n" f"You can ADD new articles (article 6+)." ) if article_num in base_amendments: # Base articles are immutable — projects cannot modify them raise ConfigError( f"Cannot modify Constitution Article {article_num} — " f"defined by base config '{base.get('name', 'unknown')}'.\n" f"Base articles are immutable. You can ADD new articles " f"with a higher article number." ) merged[article_num] = amendment return {'amendments': list(merged.values())} ``` --- ### MVP Phase 2: Integration + Diff (week 2-3) #### 2.1 Install/Update Integration **Modified `install.py` flow:** ```python # During install --local: # 1. Check for .softspark-toolkit.json in project root # 2. If found and has "extends": # a. Resolve base config(s) # b. Merge base → project # c. Validate merged config # d. Generate files from merged config # 3. If not found: proceed with current behavior (backwards compatible) ``` **CLI flags:** ```bash ai-toolkit install --local # auto-detect .softspark-toolkit.json ai-toolkit install --local --config ./custom.json # explicit config file ai-toolkit update --local # re-resolve extends + update ai-toolkit update --local --refresh-base # force re-fetch base config ``` --- #### 2.2 `ai-toolkit config diff` **Show differences between project config and base:** ```bash ai-toolkit config diff # Output: # Base: @mycompany/ai-toolkit-config@2.1.0 # # Profile: strict (base) → standard (project) ⚠ OVERRIDE # Persona: backend-lead (base) → frontend-lead (project) # Hook Profile: strict (base) → strict (inherited) # # Agents: # + frontend-specialist (project adds) # - game-developer (base disables) # = security-auditor (base requires, cannot disable) # # Rules: # + ./rules/api-standards.md (project adds) # = code-review-policy.md (inherited from base) # # Constitution: # = Articles I-V (immutable) # = Article 6: Data Sovereignty (inherited from base) # + Article 8: API Standards (project adds) # # Overrides: # quality-check: SKIP (justification: "Custom Jenkins pipeline") ``` --- #### 2.3 `ai-toolkit config validate` ```bash ai-toolkit config validate # Checks: # ✓ .softspark-toolkit.json schema valid # ✓ extends: @mycompany/ai-toolkit-config@2.1.0 resolved # ✓ No forbidden overrides # ✓ Required plugins installed: security-pack # ✓ Required agents enabled: security-auditor # ✓ Constitution articles I-V intact # ✓ Hook profile meets minimum: standard ≥ standard # ✓ All custom rule files exist # ✓ All custom agent files exist ``` --- ### Phase 3: CLI Polish (week 4-5) #### 2.4 `ai-toolkit config init` **Interactive project config setup:** ```bash ai-toolkit config init # Flow: # 1. "Does your organization have a shared ai-toolkit config? [y/n]" # → y: "npm package name or git URL:" → resolves + validates # → n: creates minimal .softspark-toolkit.json without extends # 2. "Which profile? [minimal/standard/strict]" → default from base or standard # 3. "Which persona? [none/backend-lead/frontend-lead/devops-eng/junior-dev]" # 4. Auto-detect languages from project # 5. Auto-detect editors from project files # 6. Write .softspark-toolkit.json # 7. Run ai-toolkit install --local ``` --- #### 2.5 `ai-toolkit config create-base` **Scaffold a base config package:** ```bash ai-toolkit config create-base @mycompany/ai-toolkit-config # Creates: # @mycompany-ai-toolkit-config/ # ├── package.json (name, version, files, peerDependencies) # ├── ai-toolkit.config.json (base config with sane defaults) # ├── rules/ (empty, ready for company rules) # ├── agents/ (empty, ready for company agents) # └── README.md (setup instructions) ``` **Generated `package.json`:** ```json { "name": "@mycompany/ai-toolkit-config", "version": "1.0.0", "description": "Shared ai-toolkit configuration for MyCompany", "main": "ai-toolkit.config.json", "files": ["ai-toolkit.config.json", "rules/", "agents/"], "peerDependencies": { "@softspark/ai-toolkit": ">=1.5.0" }, "keywords": ["ai-toolkit", "config", "shared"] } ``` --- ### Phase 4: Enterprise Features (week 5-6) #### 3.1 Audit Trail **`state.json` additions:** ```json { "extends": { "source": "@mycompany/ai-toolkit-config", "version": "2.1.0", "resolved_at": "2026-04-10T10:30:00Z", "hash": "sha256:abc123...", "overrides_applied": [ { "key": "hooks.quality-check", "action": "skip", "justification": "Custom Jenkins pipeline" } ] } } ``` --- #### 3.2 Lock File (`.softspark-toolkit.lock.json`) **Purpose:** Pin the exact resolved version of base configs for reproducible installs across team members and CI. ```json { "lockfileVersion": 1, "resolved": { "@mycompany/ai-toolkit-config": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/@mycompany/ai-toolkit-config/-/ai-toolkit-config-2.1.0.tgz", "integrity": "sha512-abc123...", "cached": "~/.softspark/ai-toolkit/config-cache/@mycompany/ai-toolkit-config/2.1.0/" } }, "generated_at": "2026-04-10T10:30:00Z", "ai_toolkit_version": "1.5.1" } ``` **Behavior:** - `ai-toolkit install --local` → uses lock file if present (like `npm ci`) - `ai-toolkit update --local` → re-resolves and updates lock file (like `npm install`) - `ai-toolkit update --local --refresh-base` → force re-fetch ignoring cache - `.softspark-toolkit.lock.json` should be committed to git (team synchronization) --- #### 3.4 CI Enforcement **`ai-toolkit config check` — for CI pipelines:** ```bash ai-toolkit config check # Exit codes: # 0 — project complies with base config # 1 — violations found (missing required plugins, forbidden overrides, etc.) # 2 — .softspark-toolkit.json not found ``` **GitHub Actions example:** ```yaml - name: AI Toolkit Governance Check run: | npx @softspark/ai-toolkit config check npx @softspark/ai-toolkit config validate --strict ``` **What it checks:** 1. Required plugins are installed 2. Required agents are enabled 3. No forbidden overrides applied without exemption 4. Hook profile meets minimum 5. Constitution articles intact 6. Lock file up-to-date (warn if stale) --- ## 6. File Summary | File | Action | LOC (est.) | Description | |------|--------|------------|-------------| | `scripts/config_resolver.py` | CREATE | ~400 | Resolve extends (npm, git, local path) | | `scripts/config_merger.py` | CREATE | ~350 | Layered merge engine | | `scripts/config_validator.py` | CREATE | ~200 | Schema + enforcement validation | | `scripts/config_scaffold.py` | CREATE | ~250 | create-base scaffolder | | `scripts/config_diff.py` | CREATE | ~200 | Diff viewer | | `scripts/config_check.py` | CREATE | ~150 | CI enforcement checker | | `scripts/install.py` | EDIT | +80 | Integrate extends resolution | | `bin/ai-toolkit.js` | EDIT | +40 | Register config subcommands | | `manifest.json` | EDIT | +10 | Schema references | | `kb/reference/enterprise-config-guide.md` | CREATE | ~300 | Enterprise setup guide | | `kb/reference/base-config-template/` | CREATE | ~200 | Scaffolded base config files | | `tests/test_config_resolver.bats` | CREATE | ~150 | Resolution tests | | `tests/test_config_merger.bats` | CREATE | ~200 | Merge + override tests | | `tests/test_config_immutability.bats` | CREATE | ~100 | Constitution guard tests | | `tests/test_config_cli.bats` | CREATE | ~150 | CLI command tests | | **Total** | | **~2780** | | --- ## 6a. Schema Scope (v1 vs v2) v1 ships with a minimal schema. Each additional field adds merge logic, validation, diff output, and test surface. Expand based on real usage, not speculation. | Field | v1 | v2 | Rationale | |-------|----|----|-----------| | `extends` | single string | array (multi-base) | Multi-base merge ordering is complex | | `profile` | yes | — | Core governance knob | | `agents` | yes | — | Most common customization | | `rules` | yes | — | Rule injection is existing feature | | `constitution` | yes | — | Key differentiator | | `enforce` | yes | — | Non-overridable constraints | | `skills` | — | yes | Less commonly customized at org level | | `plugins` | — | yes | Depends on plugin maturity | | `languages` | — | yes | Auto-detected, rarely org-level | | `editors` | — | yes | Auto-detected, rarely org-level | | `overrides` | — | yes | Complex, needs real-world feedback | | `hookProfile` / `persona` | — | yes | Low demand signal | --- ## 6b. Non-Functional Requirements | Category | Requirement | |----------|-------------| | **Performance** | `install --local` with extends resolution < 5s (cached), < 15s (first fetch). Config merge < 100ms. | | **Offline** | Cached configs used when registry unavailable, with clear warning. | | **Security** | No secret exposure in config files or audit trail. npm auth via `.npmrc` (user-managed). `execFile` for npm CLI (no shell injection). | | **Error messages** | Every validation error includes: what failed, which config layer caused it, and what to do (e.g., "Contact your team lead to request an exemption"). | | **Backward compatibility** | 100% — projects without `.softspark-toolkit.json` work exactly as today. Zero behavioral changes for existing users. | | **Maintainability** | Each new schema field requires: merge logic, validation, diff output, test. Budget 0.5d per new field. | | **Quality gates** | `ruff check scripts/config_*.py` (0 errors), `mypy --strict scripts/config_*.py` (0 errors). Run before every commit. | | **Type safety** | 100% public API type hints (all function signatures). >60% internal. Use `TypedDict` for config schemas, `dataclass` for resolved configs. | --- ## 7. Success Criteria (Overall) | Metric | Target | |--------|--------| | Extends sources (v1) | 4 (npm, npm+version, git URL, local path) — single string only | | Merge depth | 5 levels max (recursive extends) | | Config schema | JSON Schema validated | | Constitution protection | 100% (Articles I-V immutable) | | Override justification | Required for all overrides | | Enforce constraints | 4 types (minHookProfile, requiredPlugins, forbidOverride, requiredAgents) | | Backward compatibility | 100% (projects without .softspark-toolkit.json work as today) | | CI enforcement | Exit code 0/1 for governance compliance | | Lock file | Reproducible installs across team members | | Scaffold command | Ready-to-publish npm package template | | Tests | 30+ | | Offline resolution | Cached configs with warning | --- ## 8. Risks and Mitigation | Risk | Probability | Impact | Mitigation | |------|-------------|--------|------------| | npm registry unavailable during install | Low | Medium | Cache + offline fallback with warning | | Circular extends chain | Low | High | Max depth 5 + visited set for cycle detection | | Base config breaks project | Medium | High | Lock file pins exact version; `ai-toolkit config diff` shows changes before update | | Override abuse (teams bypass governance) | Medium | Medium | `enforce.forbidOverride` + CI check + justification requirement | | Config schema too restrictive | Medium | Medium | Start with minimal enforcement, expand based on enterprise feedback | | Multiple base configs conflict | — | — | Deferred to v2 (single extends only in v1) | | Private npm registry authentication | Medium | Low | Use existing npm auth (`.npmrc`), document setup | | Git URL resolution slow | Low | Low | `--depth 1` clone, cache aggressively | --- ## 9. Pre-Mortem 1. **"Config file fatigue"** — developers already have `.eslintrc`, `tsconfig.json`, `.prettierrc`. Another `.softspark-toolkit.json` may feel like bloat. Mitigation: file is optional, all features work without it. The DX gain (organizational governance without per-repo updates) justifies the file. 2. **"Base config never gets updated"** — team lead creates base config, nobody maintains it. Mitigation: `ai-toolkit config check` in CI catches drift; lock file staleness warnings. 3. **"Override justification is annoying"** — developers will write "needed" as justification. Mitigation: CI check can enforce minimum justification length (>20 chars); code review culture catches low-effort justifications. 4. **"Merge semantics are confusing"** — "does project override or extend the base agent list?" Mitigation: explicit semantics documented in schema; `ai-toolkit config diff` shows exactly what happened. 5. **"Enterprise teams want RBAC on overrides"** — who can approve overrides? Mitigation: v1 uses justification text + code review; v2 could integrate with GitHub CODEOWNERS for override approval. --- ## 10. Market Positioning **Target users:** 1. **Engineering managers** — enforce AI coding standards across 20+ repos without touching each one 2. **Security teams** — ensure constitution + security-auditor agent is always enabled 3. **Platform teams** — distribute company-specific agents, rules, and plugins via npm 4. **Compliance officers** — audit trail of what AI governance rules are active in each project **Competitive advantage:** No existing AI coding toolkit supports configuration inheritance. This is a unique enterprise feature that transforms ai-toolkit from a developer tool into an organizational governance platform. **Revenue potential:** Enterprise teams are the primary audience for paid support/consulting around ai-toolkit. Config inheritance is the feature that makes enterprise adoption manageable. --- ## 11. Next Actions **MVP (ship first, ~3.5 weeks):** 1. [x] Approve plan 2. [x] Define `.softspark-toolkit.json` JSON Schema — v1 scope only (1.1) 3. [x] Implement config resolver (npm, git, local) with caching (1.2) 4. [x] Implement merge engine with override validation (1.3) 5. [x] Implement constitution immutability guard (1.4) 6. [x] Integrate into install.py flow (2.1) 7. [x] Create `config diff` viewer (2.2) — primary debugging tool 8. [x] Create `config validate` checker (2.3) 9. [x] Tests for above (4.1 partial) 10. [x] **Ship MVP → announce → measure adoption** **Post-MVP:** 11. [x] Create `config init` interactive command (2.4) 12. [x] Create `config create-base` scaffolder (2.5) 13. [x] Add audit trail to state.json (3.1) 14. [x] Implement lock file generation + resolution (3.2) 15. [x] Create base config npm package template (3.3) 16. [x] Create CI enforcement command `config check` (3.4) 17. [x] Full tests + documentation (4.1, 4.2) **All 17 items completed — 2026-04-11.** --- ## 12. Future (v2) | Feature | Rationale | |---------|-----------| | Multi-base extends (`"extends": [...]`) | Needs real-world feedback on merge ordering UX | | v1 deferred schema fields (skills, plugins, languages, editors, overrides, hookProfile, persona) | Expand based on actual enterprise requests | | RBAC on overrides (GitHub CODEOWNERS integration) | v1 uses justification + code review | | Semantic constitution analysis | Character-count heuristics removed in v1; revisit only if absolute immutability proves too restrictive | | `ai-toolkit config audit` (full governance report) | Depends on audit trail maturity | --- ## 13. Cross-Plan Dependencies This plan shares modification targets with the Offline SLM plan: | Shared File | This Plan | Offline SLM Plan | |-------------|-----------|-----------------| | `scripts/install.py` | +80 LOC (extends resolution) | +30 LOC (offline-slm profile) | | `manifest.json` | +10 LOC (schema refs) | +5 LOC (offline-slm profile) | | `bin/ai-toolkit.js` | +40 LOC (config subcommands) | +10 LOC (compile-slm command) | **If implementing in parallel:** coordinate merge order for shared files. Recommended sequence: Offline SLM (smallest changes) → Enterprise Config. --- **Last Updated:** 2026-04-10 --- ## kb/history/completed/f2-mcp-trim-spike-20260504.md --- title: "Spike: F2 MCP Context Trim — Hook Feasibility & Path Decision" category: planning service: ai-toolkit tags: - mcp - hooks - claude-code - spike - feasibility doc_type: spike status: completed created: "2026-05-04" last_updated: "2026-05-04" completed: "2026-05-04" shipped_in: "v3.2.0 (decision only — implementation deferred to v4.0)" description: "Spike conclusion for Feature 2 of the output-token-discipline plan. Determines whether Claude Code hooks can modify MCP tool descriptions before they reach the LLM. Result: hooks operate per-call, not on tool list metadata. Full feature requires an MCP proxy server (multi-day scope). Outcome: F2 deferred to v4.0 with own dedicated PRD." --- # Spike: F2 MCP Context Trim — Hook Feasibility & Path Decision ## Question Can Claude Code's hook system modify MCP **tool descriptions** that get included in the model's system prompt, or do hooks only intercept individual tool **calls**? ## Method Reviewed local sources only (RAG MCP offline at spike time): 1. `app/skills/hook-creator/SKILL.md` — exhaustive list of supported hook events and their data shapes 2. `app/hooks/guard-destructive.sh`, `app/hooks/guard-path.sh` — actual examples reading `tool_input` from stdin 3. `app/skills/mcp-builder/SKILL.md` — MCP server-side conventions 4. `~/.claude/.mcp.json` — user's installed MCP servers (Context7, sequential-thinking, filesystem, rag-mcp, memory, jira-mcp) ## Findings ### Hook events that touch tool data | Event | Modifies tool list? | Modifies tool input? | Notes | |-------|--------------------|--------------------|-------| | `PreToolUse` | no | no (only block via exit 2) | Reads `tool_input.*`, decides allow/deny | | `PostToolUse` | no | no | Sees result for logging / feedback | | `PermissionRequest` | no | yes (`updatedInput`) | Can rewrite a single call's args | | `Elicitation` | no | n/a | Intercepts MCP UI prompts, not tool list | | `SessionStart` | no | n/a | Context injection only | | `InstructionsLoaded` | no | n/a | Verifies CLAUDE.md presence | **No event exposes the MCP `tools/list` response or the system-prompt tool catalog**. The tool catalog is materialized once per MCP server connection from the server's own `tools/list` reply. ### Why this matters The compression target was the bulk of MCP tool descriptions sitting in every model turn's system prompt. Examples from the user's installed servers: - `dart-mcp-server` — ~30 tools with multi-paragraph descriptions - `filesystem` — verbose paths and example sections - `pencil` — "IMPORTANT" stanzas repeated across tools - `jira-mcp` — long `Use this tool to...` boilerplate At ~100 tools across 7 servers in this user's config, easily 8–15k tokens of pure description text. Real waste, but Claude Code does not let a hook touch it. ### What would actually work To compress MCP tool descriptions before they reach the LLM, exactly two architectures are viable: 1. **Local MCP proxy server** between Claude Code and each target server. The proxy re-implements `tools/list` to rewrite descriptions on the fly while passing through `tools/call`. Requires JSON-RPC 2.0 over stdio + SSE per server, per-server config in `~/.claude/.mcp.json`, and a process supervisor for the proxies. Multi-day scope. Failure mode: a buggy proxy breaks all MCP-dependent skills. 2. **Source-side fork**: ship pre-trimmed copies of common MCP servers (`@softspark/jira-mcp-trim`, etc.) — high maintenance burden, doesn't help users with custom servers. Neither is a "minimal change" by the standards of this plan. ## Decision (final, 2026-05-04) **Drop F2 from v3.2.0 entirely. Defer the full MCP proxy approach to v4.0** with its own dedicated PRD and architecture spike. The spike originally surfaced a smaller "F2-lite observability tool" alternative (read-only inventory + suggestions). After review, the user chose to drop both options from v3.2.0: - v3.2.0 ships F1 + F3 + F3.5 only (output modes, token telemetry, default statusline) - F2 work — including any observability-first prototype — moves wholesale to v4.0 milestone - Reasoning: keep v3.2.0 release scope tight; v4.0 owns MCP-cost story end-to-end with proper proxy architecture ## Alternatives considered | Option | Pros | Cons | Verdict | |--------|------|------|---------| | Build full MCP proxy in v3.2.0 | Achieves original compression goal | Multi-day work, single-bug-breaks-all-MCP failure mode, would block release | Rejected — too big for current release | | Pre-install rewrite of `.mcp.json` | One-shot, no runtime cost | MCP spec sources descriptions from server, not config — wouldn't actually take effect | Rejected — does not work | | F2-lite observability tool in v3.2.0 | Low risk, gives users data | Not the original target; partial value; mixes two milestones | Rejected by user — keep v3.2.0 focused | | **Defer F2 entirely to v4.0** | Clean release boundaries; v4.0 owns MCP story end-to-end with full proxy scope | Token waste in MCP descriptions stays invisible to users until v4.0 | **Selected** | ## What was delivered in v3.2.0 (F1 + F3 + F3.5) The output-discipline goal is partially addressed by what shipped: - **Output modes** (F1) cut conversational response tokens 60–80% on the shipped fixture set - **Real token telemetry** (F3) lets users see actual cost per session — including the MCP description overhead, even if they cannot yet trim it - **Default statusline** (F3.5) surfaces that cost continuously Users now have visibility into the MCP-description waste this spike identified, even though automated compression has to wait for v4.0. ## What goes into v4.0 Tracked as an active PRD: [`kb/planning/mcp-context-trim-v4-prd.md`](../../planning/mcp-context-trim-v4-prd.md). It carries forward: 1. Compression heuristics from the original F2 design (migrated out of the archived plan into the live PRD) 2. Local MCP proxy server architecture: JSON-RPC 2.0 over stdio + SSE per server, process supervisor, per-server config in `~/.softspark/ai-toolkit/mcp-proxy/` 3. Rollback / opt-out story — a buggy proxy must not break MCP-dependent skills 4. Failure mode — proxy down → fall through to direct MCP server, with telemetry warning 5. Migration of existing user `.mcp.json` configs Estimate in the PRD: ~8 working days. ## Status | Date | Status | Author | |------|--------|--------| | 2026-05-04 | Spike completed | claude | | 2026-05-04 | User decision: defer F2 to v4.0 entirely (no F2-lite in v3.2.0) | lukasz.krzemien | | 2026-05-04 | Spike archived to `kb/history/completed/` alongside the parent plan | claude | --- ## kb/history/completed/language-packs-removed-20260727.md --- title: "AI Toolkit - Language Plugin Packs (removed)" category: planning service: ai-toolkit tags: [plugins, languages, rust, java, csharp, kotlin, swift, ruby, removed] doc_type: postmortem status: completed version: "1.1.0" created: "2026-03-29" last_updated: "2026-07-27" description: "REMOVED 2026-07-27 in v4.20.0. The six language packs wrapped skills that already ship in core, so installing one put zero files on disk. The skills themselves (rust-patterns, java-patterns, csharp-patterns, kotlin-patterns, swift-patterns, ruby-patterns) were never in the packs and are unaffected." --- # Language Plugin Packs — REMOVED > **Removed 2026-07-27 in v4.20.0. Nothing was lost.** > > The six packs described below each declared exactly one skill — > `rust-patterns`, `java-patterns`, `csharp-patterns`, `kotlin-patterns`, > `swift-patterns`, `ruby-patterns` — and every one of those skills lives in > `app/skills/`, is part of the core install, and **still does**. The packs > owned no files of their own beyond a `README.md`. > > Because `ai-toolkit install` links every core skill, installing a language > pack put **zero** files on disk. Measured on both runtimes and all three > profiles: `(0 file items)` every time. > > **If you used one of these packs, you lose nothing.** The skill it named is > already installed and triggers on the same file types it always did — open a > `.rs` file and `rust-patterns` still loads. > > Full measurement and the rule that now prevents a repeat: > [`no-op-plugin-packs-removed-20260727.md`](no-op-plugin-packs-removed-20260727.md). > > The document below is preserved as written on 2026-03-29. --- ## Overview Language packs are domain-scoped plugin packs that provide knowledge skills for specific programming languages. Each pack contains a single knowledge skill with idiomatic patterns, error handling, testing conventions, common frameworks, and performance tips. ## Available Packs | Pack | Skill | Language | Key Topics | |------|-------|----------|------------| | `rust-pack` | `rust-patterns` | Rust | Ownership, borrowing, Cargo, tokio, serde | | `java-pack` | `java-patterns` | Java | Records, sealed classes, Spring Boot, JUnit 5 | | `csharp-pack` | `csharp-patterns` | C# / .NET | Nullable refs, async/await, ASP.NET Core, EF Core | | `kotlin-pack` | `kotlin-patterns` | Kotlin | Coroutines, DSLs, sealed classes, Ktor, MockK | | `swift-pack` | `swift-patterns` | Swift / iOS | Protocol-oriented, SwiftUI, async/await, SPM | | `ruby-pack` | `ruby-patterns` | Ruby | Blocks, Rails conventions, RSpec, ActiveRecord | ## Skill Content Sections Each language skill follows a consistent structure: 1. **Project Structure** — standard directory layout and build tool configuration 2. **Idioms / Code Style** — language-specific patterns and conventions 3. **Error Handling** — error types, patterns, and best practices 4. **Testing Patterns** — test frameworks, assertion libraries, mocking 5. **Common Libraries / Frameworks** — ecosystem essentials 6. **Performance Tips** — optimization techniques and profiling 7. **Build / Package Management** — dependency management and CI ## How Knowledge Skills Work These skills have `user-invocable: false` in their frontmatter, meaning they are NOT slash commands. Instead, Claude loads them contextually when the conversation topic matches the skill's description trigger. For example, when a user asks "How do I handle errors in Rust?", Claude automatically loads `rust-patterns` to provide idiomatic Rust error handling guidance. ## Requesting New Language Packs File an issue with the `language-pack` label. Include: - Language name - Key topics to cover - Popular frameworks/libraries to include --- ## kb/history/completed/mcp-context-trim-v4-prd-obsoleted-20260727.md --- title: "PRD: MCP Context Trim v4.0 — Local Proxy with Description Compression" category: planning service: ai-toolkit tags: - mcp - proxy - tool-descriptions - jsonrpc - tokens - v4 doc_type: plan status: obsoleted created: "2026-05-04" last_updated: "2026-07-27" completion: "0% — never started, and no longer worth starting" target_milestone: "v4.0 (abandoned)" predecessor: - "kb/history/completed/output-token-discipline-plan-20260504.md" - "kb/history/completed/f2-mcp-trim-spike-20260504.md" description: "ABANDONED 2026-07-27. Local MCP proxy that would compress tool descriptions before they reach the model. Never built. Claude Code now defers MCP tool schemas by default — the catalog costs ~120 tokens of tool names, with schemas fetched on demand — so the 8-15k per-turn overhead this PRD was written to remove no longer exists. Kept as the record of a plan the platform solved first." --- # PRD: MCP Context Trim v4.0 — ABANDONED > **Abandoned 2026-07-27, never implemented.** > > **Why:** the premise expired. This PRD is built on one measured claim — that > MCP tool descriptions sit in every turn's system prompt, costing 8–15k tokens > per turn. Claude Code now **defers MCP tool schemas by default**: the model > receives roughly 120 tokens of tool *names*, and full schemas are fetched on > demand through tool search when a task actually needs one. > ([Claude Code docs — context window](https://code.claude.com/docs/en/context-window), > `ENABLE_TOOL_SEARCH=auto|false` controls the older eager behaviour.) > > The overhead this proxy was designed to remove is already gone. Building it > now would add a supervised local daemon, an `.mcp.json` rewrite, a rollback > path and five pre-mortem failure modes in order to compress a 120-token > catalog. > > **How it was caught:** during the 2026-07-27 token-reduction review, a > measurement of 1189 real sessions put the median startup context at 22,175 > tokens against ~7,850 in the documented reference shape. Attributing that gap > showed the excess was skill and agent descriptions and rule files — not MCP. > The MCP catalog was already deferred, exactly as the docs describe. Full > context: [`tool-output-token-reduction-closed-20260727.md`](tool-output-token-reduction-closed-20260727.md). > > **What survives:** nothing in the architecture below is reusable, because it > exists to solve a problem the host now solves. What survives is the process > note — this PRD sat in `kb/planning/` for nearly three months while the > platform shipped the fix, and nobody re-checked the premise. A plan blocked on > a host limitation should carry a re-validation date, not wait indefinitely. > > Everything below is the document as written on 2026-05-04. It is preserved > unedited so the reasoning stays auditable. --- **Status:** Proposed *(as of 2026-05-04; see abandonment note above)* **Target milestone:** v4.0 **Carved out of:** [`output-token-discipline-plan-20260504.md`](output-token-discipline-plan-20260504.md) (was Feature 2) **Spike basis:** [`f2-mcp-trim-spike-20260504.md`](f2-mcp-trim-spike-20260504.md) ## Problem MCP server tool descriptions are injected into every model turn's system prompt. With ~100 tools across 7 typical servers, descriptions consume 8–15k tokens per turn — pure overhead, paid every message. Examples observed in users' configs: - `dart-mcp-server` — ~30 tools with multi-paragraph descriptions - `filesystem` — verbose paths and example sections - `pencil` — "IMPORTANT" stanzas repeated across tools - `jira-mcp` — long `Use this tool to…` boilerplate The v3.2.0 output-discipline plan attempted to solve this with a hook-based trimmer. The spike conducted 2026-05-04 proved Claude Code hooks do not expose `tools/list` metadata or the system-prompt tool catalog. The only viable architecture is a local MCP proxy. ## Goal Reduce MCP-description overhead by ≥40% per server, with **zero** loss of parameter schemas, required fields, or discrimination signals (`not`, `never`, `only`, `except`, `unless`). ## Non-goals - Modifying tool **call** behavior (only descriptions) - Compressing user-facing prompts or completions - Replacing or rewriting upstream MCP servers - Touching MCP servers we do not control ## Architecture ### Proxy topology ``` Claude Code ──stdio──▶ ai-toolkit MCP proxy ──stdio/SSE──▶ upstream MCP server │ └─ rewrites tools/list response passes through tools/call unchanged ``` One proxy process per upstream server, supervised by `ai-toolkit mcp-trim daemon` (or equivalent). User's `~/.claude/.mcp.json` is rewritten by `ai-toolkit install` (opt-in) to point Claude Code at the proxy instead of upstream — proxy reads the original target from a sidecar config. ### Required components | Component | Purpose | |-----------|---------| | `scripts/mcp_proxy_server.py` | JSON-RPC 2.0 proxy. Reads stdin, forwards to upstream over stdio or SSE, intercepts `tools/list` response, rewrites descriptions. Stdlib-only. | | `scripts/mcp_description_trimmer.py` | Pure function library: `trim(description: str) → str`. Reused from heuristics below. Stdlib-only. | | `scripts/mcp_proxy_config.py` | Reads `~/.softspark/ai-toolkit/mcp-proxy/servers.json`, validates upstream targets, generates supervisord/launchd config. | | `app/hooks/mcp-proxy-health.sh` | SessionStart hook — verifies all configured proxies responsive; fall through (warn, do not block) if any down. | | `app/skills/mcp-trim/SKILL.md` | Knowledge skill: how to enable, opt out, audit savings. | | `bin/ai-toolkit-mcp-trim` | CLI: `enable`, `disable`, `status`, `audit` (per-server token savings report). | | `tests/test_mcp_proxy.bats` | Integration tests with mock upstream MCP servers. | | `tests/test_mcp_trimmer.bats` | Unit tests for description trim heuristics on captured fixtures. | ### Compression heuristics (from spike) Applied to each tool description in `tools/list` response: - Drop example sections >40 chars - Collapse `Use this server to…` / `Use this tool to…` boilerplate to minimum form preserving intent - Drop duplicate occurrences of tool name in its own description - **Preserve bytewise:** `inputSchema.properties[*].description`, `required`, `enum` values, URL/path identifiers - **Never strip:** the words `not`, `never`, `only`, `except`, `unless` — these carry "when NOT to use" signals - Target: ≥40% length reduction, 0% schema loss ### Failure modes & rollback | Scenario | Behavior | |----------|----------| | Proxy crashes mid-session | `mcp-proxy-health.sh` detects on next SessionStart, prints warning, suggests `ai-toolkit mcp-trim disable ` | | Upstream MCP server changes its tool catalog | Proxy passes through unchanged tools (no cached schema), warns once if a tool's description was previously trimmed | | Trimmer produces malformed JSON | Proxy falls through to upstream response unchanged, logs to `~/.softspark/ai-toolkit/mcp-proxy/error.log` | | User wants to bypass | `AI_TOOLKIT_MCP_TRIM_DISABLE=1` env var → proxies pass everything through unchanged | | User wants to fully uninstall | `ai-toolkit mcp-trim disable` reverts `~/.claude/.mcp.json` to original upstream targets | ### Migration of existing user `.mcp.json` `ai-toolkit mcp-trim enable` does: 1. Backup `~/.claude/.mcp.json` → `~/.softspark/ai-toolkit/mcp-proxy/.mcp.json.bak.` 2. Read each server entry, store in `~/.softspark/ai-toolkit/mcp-proxy/servers.json` 3. Rewrite each entry to point at the local proxy (with sidecar `target` field) 4. Spawn supervisor (per-OS: launchd on macOS, systemd on Linux, scheduled task on Windows) 5. Verify each upstream reachable via proxy, abort + restore backup on any failure ## Out-of-scope decisions (rejected mid-spike) | Option | Why rejected | |--------|--------------| | Pre-install rewrite of `.mcp.json` only | MCP spec sources descriptions from server runtime, not config — wouldn't take effect | | Source-side forks of MCP servers | Doesn't help users with custom servers; high maintenance | | F2-lite observability tool | User decision 2026-05-04: tracking token waste without trimming is half-value; do the full thing in v4.0 | | Hook-based interception | Spike proved hooks cannot reach `tools/list` | ## Success criteria - ≥40% description-length reduction per server on the captured fixture set (jira, filesystem, dart, pencil) - Deep-equal `inputSchema` between trimmed and upstream — zero schema regression - Proxy adds <50ms per `tools/list` call (one-time per session) - Proxy adds <5ms per `tools/call` (passthrough overhead) - Round-trip correctness: every tool callable via proxy returns byte-identical result vs direct call - Zero MCP-skill regressions in `npm test` after enabling proxy in CI - Rollback (`ai-toolkit mcp-trim disable`) restores byte-identical original `.mcp.json` ## Open questions 1. Process supervision per-OS — launchd / systemd / scheduled-task wrappers, or a built-in `ai-toolkit-mcp-trimd` daemon binary? 2. SSE-mode upstreams (e.g., rag-mcp at `http://localhost:8081/mcp/sse`) — proxy listens on SSE locally too, or stdio-only with internal SSE client? 3. Description rewrites — static dictionary of "boilerplate phrases to drop" (faster, deterministic) vs LLM-based summarizer (more aggressive, less predictable)? Recommend static for v4.0, LLM as v4.1 stretch. 4. Config path — `~/.softspark/ai-toolkit/mcp-proxy/` (matches existing convention) or `~/.claude/mcp-proxy/` (closer to MCP config)? Recommend the former. 5. Telemetry — does this become an opt-in metric in `/briefing --tokens` ("MCP descriptions: 12.3k → 7.2k, saved 5.1k per turn")? Recommend yes. ## Pre-mortem (failure scenarios to design against) 1. **Proxy gets out of sync with upstream** — upstream adds a new tool, proxy doesn't know how to compress it → passthrough that tool's description unchanged, log warning 2. **Compression breaks tool discriminability** — model picks wrong tool because trimmed description lost the "use only when X" qualifier → the `not/never/only/except/unless` blacklist must be exhaustive; add per-server allowlists for false positives 3. **Multi-process race on `.mcp.json` rewrite** — two `ai-toolkit install` invocations clobber each other → file lock during enable/disable 4. **Proxy supervisor fails to start on user's machine** — different distro / no systemd → ai-toolkit doctor must detect and report; degrade to "MCP proxy unavailable, falling through" with no functionality loss 5. **User has custom MCP server we don't recognize** — must work without per-server schema; default heuristics must be safe enough for arbitrary servers ## Estimate - Architecture spike + working proxy prototype: 2 days - Production proxy + supervisor + config + CLI: 3 days - Test suite + fixtures + CI integration: 2 days - Documentation + migration guide + release notes: 1 day **Total: ~8 working days** (1.5–2 weeks calendar time at typical pace) ## Status | Date | Status | Author | |------|--------|--------| | 2026-05-04 | PRD drafted from spike conclusions, carved out of v3.2.0 plan | claude | | 2026-07-27 | Abandoned without implementation. Claude Code began deferring MCP tool schemas by default, removing the per-turn overhead this proxy targeted. Moved from `kb/planning/` to `kb/history/completed/`. | claude | --- ## kb/history/completed/native-tool-output-filter-plan.md --- title: "Implementation Plan: Native Tool Output Filter" category: planning service: ai-toolkit tags: - output-filter - hooks - recovery - performance - claude-code doc_type: plan status: completed created: "2026-07-23" last_updated: "2026-07-23" completed: "2026-07-23" completion: "100% of approved Claude Code scope; other runtimes remain manual-only by capability decision" shipped_in: "Unreleased" description: "Approved implementation plan and completion evidence for the original dependency-free ai-toolkit tool-output filter, including conservative profiles, exact recovery, telemetry, runtime capability gates, and adjacent repairs." --- # Implementation Plan: Native Tool Output Filter ## Status Completed on 2026-07-23 after user approval. Completion evidence: - 69 focused Python tests pass, including the production-wrapper benchmark. - The 100-sample cold wrapper measures 63.565 ms p95 against the 75 ms gate. - The 8 MiB profile case measures 4.354 ms p95 and 8,792,932 peak traced bytes against the 150 ms and 41,943,040 byte gates. - The strict repository validator reports 44 agents, 108 skills, and 1477 tests with zero errors or warnings. - Ruff, mypy, ShellCheck, skill audit, generated artifacts, and the final repository test gate pass for the changed surface. ## Context `ai-toolkit` currently controls assistant response length and reports real Claude session tokens, but it does not transform live tool output before that output reaches the model. The requested feature is an original, MIT-licensed, dependency-free implementation inside `ai-toolkit`. RTK is research input only: no runtime dependency, vendoring, translated code, copied filters, fixtures, regex tables, CLI names, or configuration keys. The first native integration targets Claude Code because current Claude hooks support replacing successful tool output through `PostToolUse.hookSpecificOutput.updatedToolOutput`. The transformer remains strictly post-execution and cannot alter the command, arguments, environment, working directory, permission decision, exit status, or signal. This feature is separate from MCP `tools/list` description trimming. Hooks can transform an executed tool result, but they do not intercept MCP catalog metadata. [PATH: kb/history/completed/f2-mcp-trim-spike-20260504.md] [PATH: kb/planning/mcp-context-trim-v4-prd.md] Relevant local boundaries: - Fixed lifecycle enforcement belongs in hooks. [PATH: CLAUDE.md:10] - The current safety guards run in `PreToolUse`. [PATH: app/hooks.json:37] - Hook runtime Python helpers are deployed explicitly. [PATH: scripts/install_steps/hooks.py:71] - Runtime hook schemas differ by editor. [PATH: kb/reference/hooks-catalog.md:571] - Existing output/token work intentionally uses native mechanisms. [PATH: kb/history/completed/output-token-discipline-plan-20260504.md] - Claude hook contract: ## Scope ### Included - Python standard library implementation under `scripts/`. - Pure post-execution transformation of successful textual Bash output. - `off`, `observe`, and `safe` modes. - Claude Code integration through `PostToolUse.updatedToolOutput`. - Manual CLI for fixture inspection, status, recovery, and cleanup. - Two initial deterministic profiles: - `repeat-lines`: aggregate adjacent identical non-diagnostic lines and state their multiplicity. - `tap-success`: compact valid successful TAP while retaining plan, directives, comments, totals, duration, and all diagnostic material. - Byte and line savings, latency, outcome, profile ID, and profile version. - Exact, bounded, session-scoped raw recovery when `safe` mode is enabled. - Capability-gated adapters for additional runtimes after Phase 2. ### Excluded - RTK binaries, libraries, source code, filters, fixtures, configuration, or branding. - Command rewriting, `sh -c`, shell parsing, permission decisions, or command execution by the filter. - LLM-generated summaries or network calls. - Project-defined regex filters in the first release. - Failed commands, non-empty `stderr`, signals, TTY/streaming output, binary or invalid text, pipes, redirects, security scanners, dependency audits, deployment, migrations, permission failures, and destructive-command diagnostics. - `Read`, web results, arbitrary MCP results, and MCP `tools/list`. - Claims that byte reduction equals billed-token or whole-session savings. ## Architecture ```text PostToolUse payload | v runtime adapter validates the native payload and output shape | +-- unsupported, disabled, unsafe, failed, or malformed --> no hook output | v eligibility policy selects an explicit profile | v pure deterministic transformer | v invariant gate - mandatory facts preserved - no forbidden invention - deterministic and idempotent - at least 15% and 1 KiB smaller | +-- invariant failure --> exact raw passthrough + session circuit breaker | v exact raw response written to bounded ephemeral recovery store | +-- recovery unavailable --> exact raw passthrough | v hookSpecificOutput.updatedToolOutput ``` ### Module layout ```text scripts/ tool_output_filter/ __init__.py contracts.py engine.py hook_runtime.py input.py policy.py invariants.py recovery.py telemetry.py profiles/ __init__.py repeat_lines.py tap_success.py output_filter_hook.py output_filter_cli.py benchmark_output_filter.py app/ hooks/ filter-tool-output.sh session-end.sh hooks.json output-filter-policy.json tests/ fixtures/output-filter/ repeat-basic/ tap-basic/ tap-diagnostic/ ansi-adversarial/ test_tool_output_filter.py test_tool_output_filter_cli.py test_tool_output_filter_properties.py test_tool_output_filter_recovery.py test_tool_output_filter_benchmark.py test_output_filter_config.bats test_output_filter_hook.bats kb/reference/ tool-output-filter.md ``` ### Configuration contract ```json { "toolOutputFilter": { "mode": "off", "profiles": ["repeat-lines", "tap-success"], "maxInputBytes": 8388608, "minSavingsBytes": 1024, "minSavingsRatio": 0.15, "recovery": { "mode": "ephemeral", "ttlMinutes": 60, "maxSessionBytes": 33554432 } } } ``` - `off`: no Python invocation and no metrics. - `observe`: calculate an eligible result and metadata, but return no hook output, so the model receives the original bytes. - `safe`: replace only output that passes every invariant and has exact raw recovery available. - `AI_TOOLKIT_OUTPUT_FILTER_DISABLE=1`: global emergency bypass. - Three consecutive runtime or invariant failures disable filtering for the current session and emit one bounded warning. - All profiles remain explicit opt-in until GA evidence is reviewed. ### Recovery and privacy - Store the exact original tool-response object, not a redacted approximation. - Location: `~/.softspark/ai-toolkit/sessions//output-filter//`. - Directory mode `0700`, file mode `0600`, exclusive no-follow creation, opaque random identifiers, atomic publication, quota, and TTL. - Never store raw commands, paths, output, environment values, or session IDs in telemetry. - Telemetry contains only profile ID/version, input/output byte and line counts, latency, outcome, and fallback reason. - Session end, explicit clean, uninstall, and expired-TTL cleanup remove only ai-toolkit-owned recovery artifacts. - If the platform cannot provide the secure-store contract, `safe` degrades to raw passthrough. It must not silently become lossy. ## Success Criteria - [x] No new npm, pip, Cargo, system, or runtime dependency. - [x] Shadow/observe mode is byte-identical for 100% of fixtures and native hook-contract test payloads. - [x] Any exception, timeout, unsupported encoding, unknown profile, malformed payload, failed invariant, unavailable recovery store, non-zero exit, signal, or non-empty `stderr` returns the exact original output. - [x] Every omission is marked with profile/version, original and emitted line counts, and an opaque exact-recovery handle. - [x] Golden fixtures preserve 100% of declared mandatory facts and introduce zero non-marker facts. - [x] Filter output is deterministic, idempotent, and never larger than raw. - [x] Eligible fixtures achieve median reduction of at least 30%; replacement requires at least 15% and 1 KiB saved per call. - [x] Hard input cap is 8 MiB; larger payloads pass through without parsing. - [x] Algorithm p95 is at most 20 ms for 100 KiB and 150 ms for 8 MiB; cold end-to-end hook invocation p95 is at most 75 ms on CI reference runners. - [x] Peak traced Python allocation is at most three times input size plus 16 MiB. - [x] No regex has unbounded catastrophic backtracking. - [x] Claude Code `safe` mode is opt-in and can be disabled globally without reinstalling. - [x] Additional runtimes activate only after their native result-replacement contract has captured fixtures and passing integration tests. - [x] Full repository validation and test suite are green. ## Pre-Mortem | Risk | Probability | Impact | Mitigation | |---|---|---|---| | A profile removes a discriminating fact | High | High | Mandatory-fact fixtures, adversarial sentinels, raw recovery, one regression disables the profile | | A runtime changes its tool-output schema | Medium | High | Versioned adapters, strict shape validation, unknown shape returns no hook output | | Filtering hides security or failure diagnostics | Medium | High | Explicit exclusion list, successful stdout-only MVP, failures always raw | | Recovery leaks secrets or paths | Medium | High | Exact data only in opt-in ephemeral spool, `0700`/`0600`, no-follow, quota, TTL, complete cleanup | | Hook latency degrades normal tool use | Medium | Medium | Bash fast-path, size threshold, hard cap, benchmark gates, circuit breaker | | Command classification mishandles shell syntax | High | Medium | No command rewriting or execution, conservative allowlist, pipes/redirects bypass | | Regex or parser behavior is locale/version-specific | Medium | High | Independently captured multi-version fixtures; malformed/localized output passes raw | | Concurrent sessions corrupt metrics or recovery | Medium | Medium | Per-session directories, opaque IDs, atomic writes; do not reuse global `session-edits.json` | | Runtime adapters diverge in safety behavior | High | High | Capability matrix and one adapter at a time; no guessed compatibility | | Scope expands into MCP catalog proxying | Medium | Medium | Keep MCP trim as a separate PRD and release boundary | ## Tasks ### Phase 1: Observable vertical slice, no output mutation (M) Success criteria: - Claude PostToolUse payloads are parsed by a native Python stdlib engine. - `off` and `observe` modes cannot change model-visible output. - Independent fixture format and semantic oracle are operational. - No raw tool output or command is persisted. Tasks: - [x] Define contracts, independent fixture provenance ledger, conservative eligibility rules, and clean implementation boundary. Owner: `infrastructure-architect`. Files: `kb/reference/tool-output-filter.md`, `tests/fixtures/output-filter/README.md`. - [x] Implement pure engine, policy, invariants, `repeat-lines` candidate, and `tap-success` candidate in observe-only mode. Owner: `backend-specialist`. Files: `scripts/tool_output_filter/**`. - [x] Add manual `inspect`, `status`, recovery, and cleanup CLI. Owner: `backend-specialist`. Files: `scripts/output_filter_cli.py`, `bin/ai-toolkit.js`. - [x] Add Claude PostToolUse adapter last in hook order and deploy the Python runtime package with installed hooks. Owner: `command-expert`. Files: `app/hooks/filter-tool-output.sh`, `app/hooks.json`, `scripts/install_steps/hooks.py`. - [x] Add `toolOutputFilter` schema, merge, validation, and effective-policy materialization; fix config-lock source/version/integrity staleness and the Article VII reserved-number mismatch while those files are open. Owner: `backend-specialist`. Files: `scripts/schemas/ai-toolkit-config.schema.json`, `scripts/config_merger.py`, `scripts/config_validator.py`, `scripts/config_lock.py`, `app/output-filter-policy.json`. - [x] Build unit, property, hook-contract, malformed, binary, Unicode, ANSI/OSC, injection, huge-line, and concurrency tests. Owner: `test-engineer`. Files: `tests/test_tool_output_filter*.py`, `tests/test_tool_output_filter*.bats`, `tests/fixtures/output-filter/**`. Rollback/scope cut: - Remove the owned PostToolUse entry and deployed runtime package. - If native payload fixtures are unstable, ship only the manual inspect CLI and fixture oracle; do not enable the hook. ### Phase 2: Claude Code safe mode with exact recovery (L) Dependency: Phase 1 must pass all observe-mode and performance gates. Success criteria: - `safe` is explicit opt-in. - Only successful, stdout-only, allowlisted text is replaceable. - Exact raw recovery is available before replacement is emitted. - Every failure path is byte-identical passthrough. Tasks: - [x] Promote `repeat-lines` and `tap-success` only after mandatory-fact, idempotence, determinism, and minimum-savings gates pass. Owner: `backend-specialist`. Files: `scripts/tool_output_filter/profiles/**`, `scripts/tool_output_filter/invariants.py`. - [x] Implement bounded secure ephemeral recovery and exact `recover`/`clean` CLI operations. Owner: `security-architect`. Files: `scripts/tool_output_filter/recovery.py`, `scripts/output_filter_cli.py`. - [x] Add session circuit breaker, metadata-only telemetry, TTL/quota cleanup, and owned-artifact removal. Owner: `backend-specialist`. Files: `scripts/tool_output_filter/telemetry.py`, `app/hooks/session-end.sh`, `scripts/uninstall.py`. - [x] Verify safety precedence and that filtering cannot emit permission decisions, execute output, or change original tool input/status. Owner: `security-auditor`. Files: `tests/test_tool_output_filter.py`, `tests/test_tool_output_filter_recovery.py`, `tests/test_output_filter_hook.bats`. - [x] Add deterministic benchmark corpus and enforce latency, memory, maximum input, and savings thresholds. Owner: `performance-optimizer`. Files: `scripts/benchmark_output_filter.py`, `benchmarks/output-filter/**`. Rollback/scope cut: - Global disable returns the hook to observe/raw behavior immediately. - Per-profile disable preserves other validated profiles. - If exact secure recovery is unavailable on a platform, that platform stays in `observe`; no weaker recovery implementation is accepted. - If TAP cannot meet semantic gates, release only `repeat-lines`. ### Phase 3: Capability-gated runtime expansion (L) Dependency: Phase 2 must complete Claude safe-mode dogfooding without a confirmed semantic loss. Success criteria: - Each runtime has a documented capability: native replacement, observe-only, manual CLI, or unsupported. - No adapter uses command rewriting or `additionalContext` as fake replacement. - Each active adapter has captured native payload/output fixtures. Tasks: - [x] Build and document the capability matrix from current runtime contracts. Owner: `technical-researcher`. Files: `kb/reference/tool-output-filter.md`, `kb/reference/supported-tools-registry.md`. - [x] Classify adapters one by one and keep OpenCode, Cursor, and the remaining runtimes manual-only because no independently verified native result-replacement contract passed the release boundary. Owner: `command-expert`. Files: `scripts/generate_opencode_plugin.py`, `scripts/generate_cursor_hooks.py`, runtime-specific fixture files. - [x] Fix ignored OpenCode guard block propagation before adding its filter adapter. Owner: `backend-specialist`. Files: `scripts/generate_opencode_plugin.py`, `tests/test_opencode*.bats`. - [x] Fix Gemini invalid-settings overwrite and non-atomic write before any Gemini filter adapter is attempted. Owner: `backend-specialist`. Files: `scripts/generate_gemini_hooks.py`, `tests/test_gemini.bats`. - [x] Add per-runtime uninstall, rollback, user-hook preservation, failure, and schema-drift tests. Owner: `test-engineer`. Files: `tests/test_hooks_per_editor.bats`, `tests/test_*hooks*.bats`. Rollback/scope cut: - A runtime without safe native replacement remains manual CLI or observe-only. - Any runtime-specific regression removes only its owned adapter. - Cross-runtime uniformity is not a release requirement. ### Phase 4: Documentation and GA decision (M) Dependency: Phases 1 and 2 are mandatory; Phase 3 may remain partial. Success criteria: - Dogfood evidence distinguishes eligible-output byte savings from actual session token receipts. - Documentation lists exclusions, recovery sensitivity, and bypass procedure. - `safe` remains opt-in unless a separate user-approved promotion decision is made. Tasks: - [x] Review available shadow/safe evidence, false-positive and circuit-breaker paths, and latency. No production Claude JSONL receipt evidence was collected in this implementation run, so documentation makes no token-savings claim and `safe` remains opt-in. Owner: `data-analyst`. Files: `kb/reference/tool-output-filter.md`, `kb/reference/stats.md`. - [x] Update all hook counts, architecture references, CLI help, lifecycle catalog, README, changelog, generated instruction surfaces, and release notes. Owner: `documenter`. Files: `README.md`, `CLAUDE.md`, `ARCHITECTURE.md`, `app/ARCHITECTURE.md`, `kb/reference/architecture-overview.md`, `kb/reference/hooks-catalog.md`, `CHANGELOG.md`, `AGENTS.md`, `llms.txt`, `llms-full.txt`, `package.json`, `plugin.json`. - [x] Re-read the complete diff for orphaned references, dead code, missing behavior coverage, stale docs, and unexpected generated changes. Owner: `code-reviewer`. Files: all changed files. Rollback/scope cut: - Release `observe` and manual CLI only if active-mode evidence is insufficient. - Do not promote any profile to default without separate approval. ## Dependencies ```text Phase 1 → Phase 2 → Phase 3 → Phase 4 └────────→ Phase 4 if runtime expansion is deferred ``` There are no circular dependencies. Phase 1 is shippable as diagnostics, Phase 2 as Claude-only opt-in filtering, and Phase 3 as incremental runtime-by-runtime support. ## Verification Focused verification: 1. `python3 -m unittest discover -s tests -p 'test_tool_output_filter*.py'` 2. `bats tests/test_output_filter_config.bats tests/test_output_filter_hook.bats` 3. `python3 scripts/benchmark_output_filter.py` 4. `shellcheck --severity=warning app/hooks/*.sh` Repository quality gates: 1. `npm run generate:all` 2. `python3 scripts/validate.py --strict` 3. `python3 scripts/audit_skills.py --ci` 4. `npm test` 5. `git diff --check` 6. Re-read `git diff` and confirm no orphaned references, missing tests, stale docs, copied upstream expressions, or new external dependencies. Expected result: every command exits `0`, focused semantic fixtures report zero missing mandatory facts and zero forbidden inventions, active replacements meet the savings threshold, and all unsafe paths remain exact passthrough. ## Agent Assignments | Responsibility | Agent | Model | |---|---|---| | Architecture and contracts | `infrastructure-architect` | Assigned tier, no override | | Python engine and configuration | `backend-specialist` | Assigned tier, no override | | Hook and runtime adapters | `command-expert` | Assigned tier, no override | | Recovery threat model | `security-architect` | Assigned tier, no override | | Security verification | `security-auditor` | Assigned tier, no override | | Semantic and integration tests | `test-engineer` | Assigned tier, no override | | Performance gates | `performance-optimizer` | Assigned tier, no override | | Runtime contract research | `technical-researcher` | Assigned tier, no override | | Evidence analysis | `data-analyst` | Assigned tier, no override | | Documentation | `documenter` | Assigned tier, no override | | Final diff review | `code-reviewer` | Assigned tier, no override | ## Independent-Implementation Boundary - Implement from this plan, local ai-toolkit requirements, native runtime contracts, and independently captured command outputs. - Do not consult or copy RTK source while implementing filters. - Do not copy or transliterate identifiers, control flow, comments, regex tables, filter order, fixtures, messages, configuration keys, default values, benchmarks, or documentation wording. - Record the origin and rationale of each filter rule in the fixture provenance ledger. - If any upstream expression is intentionally adapted, stop and perform an Apache-2.0 attribution and NOTICE review before continuing. --- ## kb/history/completed/no-op-plugin-packs-removed-20260727.md --- title: "Removed: Nine Plugin Packs That Installed Nothing" category: planning service: ai-toolkit tags: - plugins - plugin-pack - measurement - postmortem - dead-code doc_type: postmortem status: completed created: "2026-07-27" last_updated: "2026-07-27" shipped_in: "v4.20.0 (removal)" description: "Nine of eleven plugin packs installed zero files, because every asset they declared already ships in the core install. Measured across both runtimes and all three profiles. Records what was removed, what survived, why the authoring guidance produced the problem, and the check that now prevents a repeat." --- # Removed: Nine Plugin Packs That Installed Nothing `csharp-pack`, `java-pack`, `kotlin-pack`, `ruby-pack`, `rust-pack`, `swift-pack`, `frontend-pack`, `research-pack`, `security-pack` — removed in v4.20.0. `memory-pack` and `enterprise-pack` stay. ## The measurement `ai-toolkit install` into a throwaway `HOME`, then `plugin install `, on each runtime. The number is what `plugin install` itself reports: | Pack | claude | codex | |---|---:|---:| | `csharp-pack` | 0 | 0 | | `java-pack` | 0 | 0 | | `kotlin-pack` | 0 | 0 | | `ruby-pack` | 0 | 0 | | `rust-pack` | 0 | 0 | | `swift-pack` | 0 | 0 | | `research-pack` | 0 | 0 | | `security-pack` | 0 | 0 | | `frontend-pack` | 0 | **1** | | **`memory-pack`** | **4** | **4** | | **`enterprise-pack`** | **2** | **2** | Cross-checked against the filesystem, not just the reported count: after core install (108 skills, 44 agents, 4742 characters of hook config), installing `rust-pack`, `security-pack`, `research-pack` or any language pack produced `+0` skills, `+0` agents, `+0` bytes of `settings.json` and `+0` files under `~/.softspark`. Profiles make no difference. `minimal`, `standard` and `strict` all install the same 108 skills and 44 agents, and packs add `+0` to each. ## Why Every one of the nine declared only assets that already ship in core: | Pack | Declared | All present in core? | |---|---|---| | `rust-pack` | `rust-patterns` | yes | | `security-pack` | `review`, `security-patterns`, `panic`, `security-auditor`, `security-architect`, `code-reviewer` | yes | | `research-pack` | `docs`, `research-mastery`, `plan`, `technical-researcher`, `fact-checker`, `search-specialist` | yes | Since `ai-toolkit install` links every core skill and agent, a manifest that names only core assets resolves to a set of things already installed. There is nothing left to do, so nothing is done. Eight of the nine owned no file but `README.md`. `security-pack`'s two hooks were core's `guard-destructive.sh` and `quality-gate.sh`; `research-pack`'s was core's `user-prompt-submit.sh`. ## The one that was not quite zero `frontend-pack` installed exactly one file, on codex only: `plugin-frontend-pack-post-tool-use.sh`. The pack owned no such file — it declared core's `post-tool-use.sh`, which core's codex surface does not install, so the pack copied it in under a pack-prefixed name. That is a generic hook wearing a domain label, not frontend functionality. It was removed with the pack. **If `post-tool-use` should run on codex, it belongs in the core codex hook set**, and adding it there is a separate, honest change — not a side effect of installing a pack named after a UI framework. ## The authoring rule that caused this `app/skills/plugin-creator/SKILL.md` told pack authors: > **MUST** reference existing toolkit assets before duplicating — packs extend, > they do not fork Read literally against a core install that ships everything, that instruction produces a no-op every time. It was correct about avoiding forks and silent about the pack needing to add anything. Nine packs followed it exactly. The rule now reads that a pack **must install something the core install does not**, with the verification spelled out, and the validation checklist carries a line item requiring a non-zero file count on every runtime the pack claims. ## What survived, and what users lose **Nothing.** Every skill and agent the nine packs named is a core asset, still present, still installed, still triggering on the same conditions. A Rust developer who had `rust-pack` installed keeps `rust-patterns`, because it was never in the pack. The two remaining packs are the two that own files: - `memory-pack` — two hooks, `init_db.py`, `strip_private.py`, and its own `mem-search` skill. Verified working the same day: driving `observation-capture.sh` with a real payload wrote a row to the SQLite store. - `enterprise-pack` — `status-line.sh` and `output-style.sh`, both its own. ## Process note The removal was nearly made on a wrong measurement. The first pass concluded "nine no-ops on every editor" after testing only four packs on the claude surface. `tests/test_plugin.bats` contradicted it by asserting that `frontend-pack` creates a file on codex — the test was right and the measurement was incomplete. The full 11 × 2 matrix was only then run. The existing test suite caught an error in a fresh measurement. That is worth remembering next time a test looks like it is merely in the way of a cleanup. ## Related - [Plugin Pack Conventions](../../reference/plugin-pack-conventions.md) — the rule this postmortem installed - [Language Packs (removed)](language-packs-removed-20260727.md) — the reference doc for six of the nine - [rtk-pack Retirement](rtk-pack-retirement-20260727.md) — the pack removed the day before, for a different reason --- ## kb/history/completed/offline-slm-profile-plan-20260411.md --- title: "Plan: Offline-First SLM Profile — Lightweight Mode for Local Models" category: planning service: ai-toolkit tags: - offline - slm - small-language-models - ollama - lm-studio - profile - context-optimization - privacy doc_type: plan status: completed created: "2026-04-10" last_updated: "2026-04-11" completion: "100%" completed: "2026-04-11" description: "Lightweight profile for ai-toolkit optimized for Small Language Models (SLMs) running locally via Ollama, LM Studio, or similar. Compiles a minimal instruction set that fits within 4K-8K system prompt budgets while preserving critical safety guardrails. Targets air-gapped, privacy-first, and cost-sensitive development workflows." --- # Plan: Offline-First SLM Profile — Lightweight Mode for Local Models **Status:** Completed **Completion:** 100% **Completed:** 2026-04-11 **Created:** 2026-04-10 **Origin:** Enterprise IP security requirements (air-gapped environments), cost-sensitive solo developers, and the growing adoption of local models (Ollama, LM Studio, llamafile). Current toolkit emits 20K+ token system prompts that exceed SLM context windows and degrade small model performance. **Estimated Effort:** 4-5 weeks (1 person) --- ## 1. Objective Create a `--profile offline-slm` install profile and a `scripts/compile_slm.py` compiler that produces a minimal, high-signal instruction set optimized for Small Language Models (8B-32B parameters). The compiled output preserves critical safety guardrails while stripping agent orchestration, multi-agent coordination, and complex skill routing that SLMs cannot handle. **Key design principles:** - **Token budget** — compiled output fits within 4K tokens (system prompt), with optional 8K mode for larger SLMs - **Safety-preserved** — Constitution Articles I-V always included (non-negotiable) - **Single-agent focus** — no multi-agent orchestration, no /swarm, no /teams - **Deterministic compilation** — same input → same output, no LLM involved in compilation - **Model-aware** — detects model size from Ollama API or manual flag and adjusts verbosity - **Platform-agnostic** — outputs plain markdown consumable by any local inference engine - **Hooks stripped** — SLM providers don't support lifecycle hooks; rules compile into system prompt --- ## 1a. Functional Requirements | ID | Requirement | Priority | Success Metric | |----|-------------|----------|----------------| | FR1 | Token counter (stdlib-only, ±10% accuracy target) | Must | Conservative estimate, no external deps | | FR2 | Component parser + scorer with safety-priority ranking | Must | Constitution=1.0, all components scored | | FR3 | Compression engine with 4 levels (ultra-light, light, standard, extended) | Must | Each level strips progressively less | | FR4 | Budget packer (greedy knapsack by score/size ratio) | Must | Output ≤ budget × 0.95 in all cases | | FR5 | Markdown emitter with safety-first structure | Must | Constitution always first in output | | FR6 | `--profile offline-slm` install integration | Must | `install.py` + `manifest.json` updated | | FR7 | `compile-slm` CLI command with flags | Must | `--budget`, `--model-size`, `--persona`, `--lang`, `--output`, `--format`, `--dry-run` | | FR8 | Constitution always included (non-negotiable) | Must | Compilation fails if constitution exceeds budget alone | | FR9 | Model size detection from Ollama API | Should | Auto-detect with graceful fallback to `14b` | | FR10 | Persona-aware compilation (boost relevant skills) | Should | Persona skills ranked higher | | FR11 | Language-aware compilation (include matching rules only) | Should | Non-matching language rules excluded | | FR12 | Integration guides for 4 platforms (Ollama, LM Studio, Aider, Continue.dev) | Should | Step-by-step setup per platform | | FR13 | Compile quality validator (post-compilation checks) | Should | FAIL on missing constitution, budget exceeded | | FR14 | 4 output formats (raw markdown, Ollama Modelfile, JSON string, Aider-compatible) | Should | Each format usable by target tool | | FR15 | `--dry-run` output showing included components + token counts | Should | Table: component, score, tokens, included? | --- ## 2. Architecture Overview ``` ai-toolkit install --profile offline-slm [--model-size 8b|14b|32b|70b] ai-toolkit compile-slm [--budget 4096] [--persona backend-lead] [--lang typescript] ┌──────────────────────────────────────────────────────────┐ │ offline-slm Profile │ │ │ │ Compiler: scripts/compile_slm.py │ │ Input: full toolkit (agents, skills, rules, constitution)│ │ Output: single compiled .md file for system prompt │ │ │ │ Token Budget Tiers: │ │ ultra-light (2K) — safety + persona only │ │ light (4K) — safety + persona + top skills + rules │ │ standard (8K) — safety + persona + full skills + rules │ │ extended (16K) — near-full toolkit (for 32B+ models) │ │ │ │ Output Files: │ │ ~/.softspark/ai-toolkit/compiled/slm-system-prompt.md │ │ ~/.softspark/ai-toolkit/compiled/slm-skills-reference.md │ │ CLAUDE.md (or equivalent) — auto-generated │ │ │ │ Integration Targets: │ │ Ollama (modelfile SYSTEM directive) │ │ LM Studio (system prompt field) │ │ llamafile (--system-prompt flag) │ │ Open WebUI (system prompt setting) │ │ Aider (--system-prompt-file flag) │ │ Continue.dev (system prompt in config) │ └──────────────────────────────────────────────────────────┘ ``` ### Compilation Pipeline ``` Full Toolkit (20K+ tokens) │ ▼ ┌─────────────────┐ │ 1. Parse Phase │ Read all agents, skills, rules, constitution └────────┬────────┘ ▼ ┌─────────────────┐ │ 2. Rank Phase │ Score components by: safety criticality × usage frequency × persona relevance └────────┬────────┘ ▼ ┌─────────────────┐ │ 3. Compress Phase│ Strip: examples, rationalization tables, related skills, verbose headers └────────┬────────┘ ▼ ┌─────────────────┐ │ 4. Budget Phase │ Pack highest-scoring components until token budget reached └────────┬────────┘ ▼ ┌─────────────────┐ │ 5. Emit Phase │ Write compiled .md + integration instructions └─────────────────┘ ``` --- ## 3. Progress Tracking | # | Feature | Priority | Status | Est. Time | Notes | |---|---------|----------|--------|-----------|-------| | 1.1 | Token counter (tiktoken-free, word-based estimator) | P0 | Proposed | 0.5d | ~0.75 tokens/word heuristic (stdlib only) | | 1.2 | Component parser + scorer | P0 | Proposed | 2d | Parse frontmatter, score by criticality/frequency/persona | | 1.3 | Compression engine | P0 | Proposed | 2d | Strip examples, rationalization tables, headers | | 1.4 | Budget packer | P0 | Proposed | 1d | Greedy knapsack by score/size ratio | | 1.5 | Emitter (markdown output) | P0 | Proposed | 1d | Clean compiled .md file | | 2.1 | Profile integration (`--profile offline-slm`) | P0 | Proposed | 1.5d | Install.py + manifest.json + state.json | | 2.2 | CLI command (`ai-toolkit compile-slm`) | P0 | Proposed | 1d | Standalone compilation with flags | | 2.3 | Model size detection (Ollama API) | P1 | Proposed | 1d | Auto-detect model params from `ollama list` | | 2.4 | Persona-aware compilation | P1 | Proposed | 1.5d | Boost persona-relevant skills in ranking | | 2.5 | Language-aware compilation | P1 | Proposed | 1d | Include only matching language rules | | 3.1 | Integration guides (Ollama, LM Studio, Aider, Continue) | P1 | Proposed | 1.5d | Step-by-step per platform | | 3.2 | Compile quality validator | P1 | Proposed | 1d | Verify output covers constitution, fits budget | | 3.3 | Tests | P1 | Proposed | 3d | Unit: compilation determinism, budget compliance, 4 compression levels × 4 output formats, constitution guard. Integration: `compile-slm --model-size 8b`, verify output fits 2048 tokens + constitution present end-to-end. Target: 40+ tests | | 3.4 | Documentation | P1 | Proposed | 2.5d | All 9 docs per CLAUDE.md: README, CLAUDE.md, ARCHITECTURE.md, package.json, llms.txt, llms-full.txt, AGENTS.md, skills-catalog.md, architecture-overview.md + integration guide | **Phasing:** - **Phase 1 (week 1-2):** Compiler — parser, scorer, compressor, packer, emitter - **Phase 2 (week 2-3):** Integration — profile, CLI, model detection, persona/language awareness - **Phase 3 (week 3-4):** Polish — integration guides, validator, tests, documentation > **Demand validation gate:** Ship Phase 1 + basic Phase 2 (compiler + profile + CLI with `--budget` and `--model-size` flags) as MVP. Test with 3 real models (8B, 14B, 32B). Only build persona/language-aware compilation and platform-specific integration guides if MVP validation confirms output quality. --- ## 4. Dependency Graph ``` Phase 1: Compiler (week 1-2) ============================ Token counter (1.1) ────┐ ├──► Compression engine (1.3) ──► Budget packer (1.4) ──► Emitter (1.5) Component parser (1.2) ──┘ Phase 2: Integration (week 2-3) ================================ Profile integration (2.1) ──┐ ├──► CLI command (2.2) Model detection (2.3) ──────┤ Persona-aware (2.4) ────────┤ Language-aware (2.5) ────────┘ Phase 3: Polish (week 3-4) =========================== ├──► Integration guides (3.1) ├──► Compile validator (3.2) └──► Tests + docs (3.3, 3.4) ``` --- ## 5. Detailed Implementation ### Phase 1: Compiler Engine (week 1-2) #### 1.1 Token Counter **Stdlib-only token estimation** — no tiktoken, no external dependencies. ```python def estimate_tokens(text: str) -> int: """Estimate token count from text without external dependencies. Uses two heuristics and returns the higher (conservative) estimate: 1. Word-based: ~0.75 tokens/word for English prose 2. Char-based: ~1 token per 4 chars (more accurate for code-heavy content) Accuracy target: ±10% vs tiktoken cl100k_base. To be validated on 50 toolkit files before shipping. """ word_est = int(len(text.split()) * 0.75) char_est = len(text) // 4 # Code blocks have higher token density — adjust code_blocks = text.count('```') code_penalty = code_blocks * 15 return max(word_est, char_est) + code_penalty ``` **Why not tiktoken:** tiktoken requires a C extension and network download of the BPE file. This violates the stdlib-only constraint and fails in air-gapped environments (which is literally the target audience for this feature). **Accuracy target:** ±10% vs tiktoken cl100k_base. Using `max(word, char)` gives a conservative estimate. We pack to budget × 0.95 (5% safety margin) to absorb estimation error. --- #### 1.2 Component Parser + Scorer **Parse all toolkit components into a unified scoring table:** ```python @dataclass class Component: name: str type: str # 'constitution', 'agent', 'skill', 'rule', 'hook-equivalent' source_file: str full_text: str compressed_text: str # after stripping (populated by compressor) tokens_full: int tokens_compressed: int score: float # 0.0 - 1.0 # Scoring factors safety_criticality: float # 0.0-1.0 (constitution=1.0, guard hooks=0.9) usage_frequency: float # 0.0-1.0 (from stats.json, normalized) persona_relevance: float # 0.0-1.0 (match against active persona) language_relevance: float # 0.0-1.0 (match against project language) ``` **Scoring formula:** ```python score = ( safety_criticality * 0.40 + # Safety always dominates — non-negotiable content gets priority usage_frequency * 0.25 + # Frequently used = valuable — from stats.json invocation counts persona_relevance * 0.20 + # Persona-matched = valuable — e.g. backend-lead boosts API skills language_relevance * 0.15 # Language-matched = contextual — include only relevant rules ) # Weight rationale: safety must dominate (0.40) to guarantee constitution + guard rules always fit. # Usage + persona (0.45 combined) ensure the most practical content fills remaining budget. # Language (0.15) is a tiebreaker — most projects use 1-2 languages. # Weights are compile-time constants in v1. If empirical testing (5 standard tasks across # 3 model sizes) shows suboptimal results, expose as --score-weights flag in v2. ``` **Fixed-score components (always included):** | Component | Score | Reason | |-----------|-------|--------| | Constitution (Articles I-V) | 1.0 | Non-negotiable safety | | Guard hooks (destructive, path) | 0.95 | Core safety rules (compiled as text, not hooks) | | Active persona definition | 0.90 | User-selected identity | | Active language rules | 0.85 | Project-specific quality gates | **Dynamic-score components:** | Component | Base Score | Adjusted By | |-----------|-----------|-------------| | Individual skills | 0.3-0.7 | Usage frequency + persona fit | | Agent definitions | 0.2-0.6 | Persona relevance (only 1 agent in SLM mode) | | Knowledge skills | 0.2-0.5 | Language match + persona match | | Iron Law rules | 0.7 | Always high (quality enforcement) | --- #### 1.3 Compression Engine **Strip low-signal content while preserving semantics:** | Strip Target | Savings (est.) | Example | |-------------|---------------|---------| | `## Common Rationalizations` tables | 200-400 tokens/skill | 15 skills have these tables | | `## Related Skills` sections | 50-100 tokens/skill | Routing not useful for SLMs | | `## Verification Checklist` (keep 1-liner summary) | 100-200 tokens/agent | Compress to "Verify: tests pass, no placeholders" | | Markdown headers (collapse hierarchy) | 20-50 tokens/file | `### 2.1.3 Sub-feature` → plain paragraph | | Example code blocks (keep first, strip rest) | 100-500 tokens/skill | Keep 1 example max | | Frontmatter (YAML) | 50-100 tokens/file | Strip entirely from compiled output | | Agent `## Allowed CLI Commands` lists | 200-400 tokens/agent | Not needed when agent won't execute them | | Multi-agent coordination instructions | 300-500 tokens | SLM = single agent, no /orchestrate | | Effort-based budgeting rules | 100 tokens | SLM doesn't manage budgets | **Compression levels:** ```python COMPRESSION_LEVELS = { 'ultra-light': { 'strip_examples': True, 'strip_rationalizations': True, 'strip_related_skills': True, 'strip_verification': True, 'strip_agent_commands': True, 'strip_multi_agent': True, 'max_skills': 5, # Only top 5 skills by score 'max_agents': 0, # No agent definitions (persona only) 'include_rules': False, }, 'light': { 'strip_examples': True, 'strip_rationalizations': True, 'strip_related_skills': True, 'strip_verification': 'summary', # 1-liner 'strip_agent_commands': True, 'strip_multi_agent': True, 'max_skills': 10, 'max_agents': 1, # Persona agent only 'include_rules': True, }, 'standard': { 'strip_examples': 'first-only', # Keep 1 example 'strip_rationalizations': True, 'strip_related_skills': True, 'strip_verification': 'summary', 'strip_agent_commands': True, 'strip_multi_agent': True, 'max_skills': 20, 'max_agents': 3, 'include_rules': True, }, 'extended': { 'strip_examples': 'first-only', 'strip_rationalizations': 'first-only', 'strip_related_skills': False, 'strip_verification': False, 'strip_agent_commands': False, 'strip_multi_agent': True, # Still stripped for SLMs 'max_skills': 40, 'max_agents': 5, 'include_rules': True, }, } ``` --- #### 1.4 Budget Packer **Greedy knapsack algorithm:** Sort components by `score / compressed_tokens` ratio (value density), pack until budget exhausted. ```python def pack_components(components: list[Component], budget: int) -> list[Component]: """Pack highest-value components into token budget.""" # Fixed components always included (constitution, persona, language rules) fixed = [c for c in components if c.score >= 0.85] remaining_budget = budget - sum(c.tokens_compressed for c in fixed) # Sort remaining by value density dynamic = sorted( [c for c in components if c.score < 0.85], key=lambda c: c.score / max(c.tokens_compressed, 1), reverse=True ) packed = list(fixed) for comp in dynamic: if comp.tokens_compressed <= remaining_budget: packed.append(comp) remaining_budget -= comp.tokens_compressed return packed ``` **Budget validation:** After packing, verify total tokens ≤ budget × 0.95 (5% safety margin for tokenizer estimation error). **Constitution budget guard:** Before packing dynamic components, verify that fixed components (constitution + persona + language rules) fit within the budget. If `sum(fixed.tokens_compressed) > budget`, fail with: `"Constitution + safety rules alone exceed {budget} token budget. Minimum safe budget: {required}. Use --budget {required} or higher."` This prevents silent omission of safety-critical content. --- #### 1.5 Emitter **Output:** Single markdown file structured for maximum SLM comprehension. ```markdown # AI Coding Assistant — System Instructions ## Safety Rules (MANDATORY) [Compiled constitution — always first, highest attention position] ## Your Identity [Compiled persona — who you are, what you focus on] ## Coding Standards [Compiled language rules — active language only] ## Key Skills [Top N skill summaries — compressed, actionable] ## Quality Checklist [Compiled from Iron Laws + verification — bullet points only] ``` **Why this structure:** - Safety first = maximum attention weight in transformer architecture - Identity second = establishes persona before task instructions - Standards = project-specific rules that shape code output - Skills at end = reference material, lower attention needed --- ### Phase 2: Integration (week 2-3) #### 2.1 Profile Integration **manifest.json addition:** ```json { "profiles": { "offline-slm": ["core"], "offline-slm-extended": ["core", "agents"] } } ``` **Install behavior:** ```bash ai-toolkit install --profile offline-slm # What happens: # 1. Standard install of core components # 2. Runs compile_slm.py with auto-detected settings # 3. Writes compiled output to ~/.softspark/ai-toolkit/compiled/ # 4. Generates integration instructions for detected local model tools # 5. state.json records profile as "offline-slm" ``` **No hooks installed:** SLM providers (Ollama, LM Studio) don't support lifecycle hooks. The critical hook behavior (destructive command guard, path guard) is compiled into the system prompt text as rules. --- #### 2.2 CLI Command ```bash ai-toolkit compile-slm # auto-detect model, default budget ai-toolkit compile-slm --budget 4096 # explicit token budget ai-toolkit compile-slm --budget 8192 --persona backend-lead # persona + budget ai-toolkit compile-slm --model-size 8b # auto-select budget for 8B model ai-toolkit compile-slm --model-size 32b # auto-select budget for 32B model ai-toolkit compile-slm --lang typescript,python # include specific language rules ai-toolkit compile-slm --output ./my-system-prompt.md # custom output path ai-toolkit compile-slm --dry-run # show what would be included + token counts (table format below) # --dry-run output format: # Budget: 4096 tokens | Level: light | Persona: backend-lead # ┌────────────────────────────┬──────────┬────────┬──────────┐ # │ Component │ Score │ Tokens │ Included │ # ├────────────────────────────┼──────────┼────────┼──────────┤ # │ Constitution (Articles I-V)│ 1.00 │ 420 │ YES │ # │ Persona: backend-lead │ 0.90 │ 180 │ YES │ # │ Rule: coding-style │ 0.85 │ 310 │ YES │ # │ Skill: /review │ 0.68 │ 290 │ YES │ # │ ... │ ... │ ... │ ... │ # │ Skill: /deploy │ 0.22 │ 350 │ NO (budget)│ # └────────────────────────────┴──────────┴────────┴──────────┘ # Total: 3,840 / 4,096 tokens (93.7% utilization) ai-toolkit compile-slm --format ollama # output as Ollama Modelfile SYSTEM block ai-toolkit compile-slm --format json-string # JSON-escaped string (for config files) ai-toolkit compile-slm --format raw # plain markdown (default) ``` **Model size → budget mapping:** Note: budget is about *effective instruction following capacity*, not context window. A 128K-context 8B model can *hold* 16K system prompt tokens, but cannot *follow* them reliably. Empirically, SLMs degrade when system prompt exceeds ~10-15% of their effective capacity. ```python MODEL_BUDGETS = { '7b': {'budget': 2048, 'level': 'ultra-light'}, # Llama 3.1 8B, Mistral 7B '8b': {'budget': 2048, 'level': 'ultra-light'}, '14b': {'budget': 4096, 'level': 'light'}, # Qwen 2.5 14B, Phi-3 14B '32b': {'budget': 8192, 'level': 'standard'}, # Qwen 2.5 32B, Mixtral 8x7B '70b': {'budget': 16384, 'level': 'extended'}, # Llama 3.1 70B } ``` --- #### 2.3 Model Size Detection **Auto-detect from Ollama:** ```python def detect_model_size() -> str | None: """Detect running model size from Ollama API.""" try: # curl http://localhost:11434/api/tags resp = urllib.request.urlopen('http://localhost:11434/api/tags', timeout=2) data = json.loads(resp.read()) models = data.get('models', []) if models: # Extract parameter count from model name: "llama3.1:8b" → "8b" latest = models[0]['name'] match = re.search(r'(\d+)[bB]', latest) if match: return match.group(0).lower() except (urllib.error.URLError, TimeoutError, json.JSONDecodeError): pass return None ``` **Fallback:** If no model detected, use `14b` defaults (4K budget, light compression). User can override with `--model-size`. --- ### Phase 3: Polish (week 3-4) #### 3.1 Integration Guides Per-platform setup instructions generated by the compiler. **Ollama:** ```bash # 1. Compile system prompt ai-toolkit compile-slm --format ollama --model-size 8b > Modelfile.ai-toolkit # 2. Create custom model ollama create my-coder -f Modelfile.ai-toolkit # 3. Use ollama run my-coder "implement the payment API" ``` **LM Studio:** ``` 1. ai-toolkit compile-slm --model-size 14b 2. Open LM Studio → Chat → System Prompt 3. Paste contents of ~/.softspark/ai-toolkit/compiled/slm-system-prompt.md ``` **Aider:** ```bash ai-toolkit compile-slm --output .aider.system-prompt.md --model-size 32b aider --model ollama/qwen2.5-coder:32b --system-prompt-file .aider.system-prompt.md ``` **Continue.dev:** ```bash # 1. Compile to a local file ai-toolkit compile-slm --model-size 14b # 2. In .continue/config.json, paste the compiled content into systemMessage # (Continue.dev does not support file references — content must be inline) # Use: ai-toolkit compile-slm --format json-string to get escaped output ``` --- #### 3.2 Compile Quality Validator Post-compilation checks: | Check | Severity | Description | |-------|----------|-------------| | Constitution present | FAIL | Articles I-V must be in output | | Budget exceeded | FAIL | Token count > budget | | Persona missing (when specified) | WARN | Persona definition not included | | No language rules included | WARN | Project language not detected | | Less than 3 skills included | WARN | Very minimal — may be too sparse | | Output empty | FAIL | Compilation produced no content | --- ## 6. File Summary | File | Action | LOC (est.) | Description | |------|--------|------------|-------------| | `scripts/compile_slm.py` | CREATE | ~500 | Main compiler — orchestrates pipeline: parse → score → compress → pack → emit. Contains `Component` dataclass, scorer, and budget packer | | `scripts/slm_token_counter.py` | CREATE | ~50 | Token estimation (stdlib only) — `estimate_tokens()` function used by compiler and validator | | `scripts/slm_compression.py` | CREATE | ~300 | Compression engine — strip/summarize functions per content type, compression level configs (`COMPRESSION_LEVELS` dict) | | `scripts/slm_integration.py` | CREATE | ~150 | Platform-specific output formatters — Ollama Modelfile, JSON-escaped string, raw markdown, Aider-compatible | | `bin/ai-toolkit.js` | EDIT | +10 | Register `compile-slm` command | | `scripts/install.py` | EDIT | +30 | Handle `--profile offline-slm` | | `manifest.json` | EDIT | +5 | Add offline-slm profile | | `kb/reference/offline-slm-guide.md` | CREATE | ~200 | Integration guides for all platforms | | `tests/test_compile_slm.bats` | CREATE | ~150 | Compilation tests | | `tests/test_slm_budgets.bats` | CREATE | ~80 | Budget compliance tests | | **Total** | | **~1575** | | --- ## 6a. Non-Functional Requirements | Category | Requirement | |----------|-------------| | **Performance** | Compilation < 2 seconds. No network calls during compilation (all data local). | | **Accuracy** | Token estimation ±10% vs tiktoken cl100k_base. Budget compliance: output ≤ budget × 0.95. | | **Determinism** | Same input (agents, skills, rules, persona, language, budget) → identical output. No randomness. | | **Security** | Constitution Articles I-V always present in output — compilation fails if they exceed budget alone. | | **Offline** | Zero network dependencies. Ollama auto-detection gracefully fails to manual fallback. | | **Portability** | Output is plain markdown — consumable by any tool accepting a system prompt string/file. | | **Quality gates** | `ruff check scripts/compile_slm.py scripts/slm_*.py` (0 errors), `mypy --strict scripts/compile_slm.py scripts/slm_*.py` (0 errors). Run before every commit. | | **Type safety** | 100% public API type hints (all function signatures). `Component` dataclass fully typed. Scoring functions use typed parameters, not bare `dict`. | --- ## 6b. Cache Invalidation & Recompile Triggers Compiled output (`~/.softspark/ai-toolkit/compiled/slm-system-prompt.md`) is a **derived artifact** — it must be recompiled when inputs change: | Trigger | Action | |---------|--------| | `ai-toolkit update` | Auto-recompile if profile is `offline-slm` | | `ai-toolkit install --profile offline-slm` | Always compile | | Agent/skill/rule files changed (detected via mtime) | Warn: "Compiled SLM prompt may be stale. Run `ai-toolkit compile-slm`" | | Manual `ai-toolkit compile-slm` | Always recompile | Compiled output includes a header comment: `` for staleness detection. --- ## 6b-bis. Rollback & Removal The offline-slm feature is purely additive — removing it is trivial: 1. Delete `scripts/compile_slm.py`, `scripts/slm_token_counter.py`, `scripts/slm_compression.py`, `scripts/slm_integration.py` 2. Remove `"offline-slm"` and `"offline-slm-extended"` from `manifest.json` profiles 3. Remove `compile-slm` from `SCRIPT_COMMANDS` in `bin/ai-toolkit.js` 4. Delete `~/.softspark/ai-toolkit/compiled/` directory (user-side) 5. No hooks, no state files, no config entries to clean up --- ## 6c. Quality Gate Degradation Notice **Important:** The `offline-slm` profile strips lifecycle hooks because SLM providers don't support them. This means: - No pre-commit quality check (ruff/tsc/mypy) - No destructive command interception (guard hooks) - No session context preservation Guard hook behavior is **compiled into the system prompt as text rules** — the SLM is *instructed* not to run destructive commands, but unlike hook-based enforcement, this is advisory, not blocking. Documentation must clearly state: **"SLM mode trades enforcement for guidance. Safety rules are present but not machine-enforced."** For teams needing enforcement, recommend `--profile offline-slm` combined with a Git pre-commit hook (`.git/hooks/pre-commit`) that runs lint/type-check independently of the AI tool. --- ## 7. Success Criteria (Overall) | Metric | Target | |--------|--------| | Budget tiers | 4 (ultra-light 2K, light 4K, standard 8K, extended 16K) | | Token budget compliance | 100% (output ≤ budget in all cases) | | Constitution inclusion | 100% (always present, all 5 articles) | | Compilation time | < 2 seconds | | Model size auto-detection | Ollama API (with graceful fallback) | | Output formats | 4 (raw markdown, Ollama Modelfile, JSON-escaped string, Aider-compatible) | | Integration guides | 4 platforms (Ollama, LM Studio, Aider, Continue.dev) | | Persona support | 4 personas (backend-lead, frontend-lead, devops-eng, junior-dev) | | Language rule support | 13 languages (all existing rules) | | External dependencies | 0 (stdlib Python only) | | Tests | 40+ | --- ## 8. Risks and Mitigation | Risk | Probability | Impact | Mitigation | |------|-------------|--------|------------| | Token estimation too inaccurate | Medium | Medium | Validate against tiktoken on 50 files, target ±10% | | Compiled prompt too compressed — loses meaning | Medium | High | Validate with 8B model on 5 standard tasks; if quality drops, increase minimum budget | | Ollama API changes | Low | Low | Graceful fallback to manual `--model-size` | | User expects full toolkit features with SLM | Medium | Medium | Clear documentation: "SLM mode = safety + coding standards, not multi-agent orchestration" | | Air-gapped environment can't run `ai-toolkit compile-slm` | Low | Medium | Pre-compile during `install` when network is available; compiled output is self-contained | | Constitution text changes break compiled cache | Low | Low | Recompile on every `ai-toolkit update` | --- ## 9. Pre-Mortem 1. **"Compiled prompt is too generic"** — without the full agent definitions, the SLM may produce generic code that doesn't match project conventions. Mitigation: Language rules and persona have highest priority after constitution — they provide project-specific context. 2. **"Users expect /review to work with 8B model"** — Skill invocations won't be available via SLM providers that lack hook support. Mitigation: Compiled output includes skill *knowledge* as rules, not invocable commands. Clear docs: "Skills are compiled as coding standards, not slash commands." 3. **"Model detects wrong size"** — Ollama model naming is inconsistent (`llama3.1:8b` vs `codellama:7b-instruct`). Mitigation: regex extracts any `\d+[bB]` pattern; fallback to `14b` if ambiguous. 4. **"Other local inference tools emerge"** — Jan.ai, GPT4All, Tabby, etc. Mitigation: Raw markdown output works with any tool that accepts a system prompt file. 5. **"Nobody uses the feature"** — SLM adoption among professional developers may be niche. Mitigation: Low effort (3-4 weeks), high signaling value ("we support air-gapped environments"), enterprise sales enabler. --- ## 10. Market Positioning **Target users:** 1. **Enterprise (air-gapped)** — financial services, defense, healthcare firms that cannot send code to cloud LLM APIs 2. **Privacy-conscious solo devs** — developers who don't want code leaving their machine 3. **Cost-sensitive teams** — startups that can't afford Anthropic/OpenAI API costs at scale 4. **Offline-first** — developers working on trains, planes, or in regions with poor connectivity **Competitive advantage:** No existing AI coding toolkit provides a compilation pipeline that adapts its instruction set to model capacity. This is a first-mover feature. --- ## 11. Next Actions 1. [ ] Approve plan 2. [ ] Implement token counter (1.1) 3. [ ] Implement component parser + scorer (1.2) 4. [ ] Implement compression engine (1.3) 5. [ ] Implement budget packer + emitter (1.4, 1.5) 6. [ ] Integrate profile into install.py + manifest.json (2.1) 7. [ ] Create CLI command `compile-slm` (2.2) 8. [ ] Add Ollama model detection (2.3) 9. [ ] Add persona + language aware compilation (2.4, 2.5) 10. [ ] Write integration guides for 4 platforms (3.1) 11. [ ] Compile quality validator (3.2) 12. [ ] Tests + documentation (3.3, 3.4) --- ## 12. Future | Feature | Rationale | |---------|-----------| | Automatic recompile on file changes (file watcher) | v1 uses manual recompile + staleness warning | | Quality benchmarks (run 5 standard tasks, measure output quality per budget tier) | Validate compilation quality empirically before shipping | | Plugin-aware compilation (include memory-pack prompts if installed) | Depends on plugin system maturity | | Model-specific prompt templates (different SLM families prefer different instruction styles) | Needs empirical testing across model families | | `--profile offline-slm --enforce-git-hooks` (install git pre-commit hook for quality gates) | Compensates for stripped lifecycle hooks | --- ## 13. Cross-Plan Dependencies This plan shares modification targets with the Enterprise Config plan: | Shared File | This Plan | Enterprise Config Plan | |-------------|-----------|----------------------| | `scripts/install.py` | +30 LOC (offline-slm profile) | +80 LOC (extends resolution) | | `manifest.json` | +5 LOC (offline-slm profile) | +10 LOC (schema refs) | | `bin/ai-toolkit.js` | +10 LOC (compile-slm command) | +40 LOC (config subcommands) | **If implementing in parallel:** this plan has the smallest changes to shared files — merge first to minimize conflicts. **Enterprise Config interaction:** If Enterprise Config ships, `compile-slm` should respect the `extends` chain — compile the merged config, not just local. Add a `--ignore-extends` flag for air-gapped environments without access to the base config. --- **Last Updated:** 2026-04-10 --- ## kb/history/completed/output-filter-retirement-20260726.md --- title: "Retirement: Native Tool-Output Filter — Measured 0% and Removed" category: planning service: ai-toolkit tags: - output-filter - token-reduction - postmortem - measurement - claude-code doc_type: postmortem status: completed created: "2026-07-26" last_updated: "2026-07-26" shipped_in: "v4.17.0 (removal)" description: "Why the native tool-output filter shipped in v4.16.0 was removed in v4.17.0: measured 0.0000% whole-session token saving on real traffic, because agent-issued commands are compound and the design accepted only simple registered shapes." --- # Retirement: Native Tool-Output Filter **Shipped:** v4.16.0 (2026-07-23). **Removed:** v4.17.0 (2026-07-26). ## The number Measured whole-session input-token saving: **0.0000%**. Real Bash results from local Claude Code transcripts were replayed through the shipped classifier and the full filter registry. The filters ran on the actual captured output; this is a measurement, not an estimate. | Scope | Value | |---|---:| | Transcripts replayed | 134, across 22 distinct projects | | Successful Bash results | 7600 | | Of those, parsed as a simple command shape | 145 (1.9%) | | Of those, matched a registered shape | 18 (0.24%) — 16 `git diff`, 2 `git show` | | Accepted by any filter | **0** | | Bytes saved | **0** | The classifier was verified working before the result was accepted: `git status`, `git log -n 20`, `pytest -v`, `bats --tap`, and `npm test` each produced exactly one candidate. The zero is real. ## Why: the premise, not the implementation Seventeen filters were correct. They cleared their byte floors on owned fixtures (44–95% reduction), stayed inside every latency budget at the 8 MiB engine cap, passed adversarial safety review, and never once compressed a failure. None of that mattered, because the commands they were built for are not the commands that get issued. 95% of successful Bash invocations are compound. The byte pool breaks down as: | Class | Share of compound bytes | Why the filter refused it | |---|---:|---| | `;` chain | 44.3% | multiple output producers, attribution ambiguous | | multiline script | 29.1% | rejected at the raw-string boundary | | pipeline | 12.7% | the pipe transformed the output | | `&&` chain with producing segments | 5.6% | multiple output producers | | redirect, substitution | 4.4% | rejected at the raw-string boundary | | heredoc | 3.5% | rejected at the raw-string boundary | Every one of those refusals was the correct safety decision in isolation. Together they excluded the entire population. The most-frequent single shape was `cd && …`, at 375 results and 480 KB. A bounded `cd`-prefix subset had already been designed, threat-modelled, and measured during Phase 3, and it was dropped because it covered 0.00% of the compound pool. The retirement measurement confirms why: of those 375 results, only 5 had a single simple second segment, and those 5 produced 0 bytes of output. ## What was already rejected on the way, and still stands - **Read-result coverage: rejected on the `Edit` exact-match hazard.** On 670.7 KB of real Read content, adjacent-duplicate collapse saves 0.00%, blank-run collapse 0.04%, trailing-whitespace 0.00%. Anything above noise requires elision, and 80.7% of `Edit` old-strings target a file read earlier in the same session, 63.4% of them multi-line byte-exact quotes. A Read result asserts what is on disk, so omission is a false claim rather than a summary. - **Compound-command subset: designed, measured at 0.00% coverage, dropped.** The pipeline-truncator shape failed on an inversion: a truncated document parses cleanly exactly where the shape would pay, and rejects exactly where truncation is detectable. ## The process lesson The plan validated its **design** exhaustively across five phases and its **premise** not at all until the fifth. Fixtures measured the filter; only real traffic measured the value, and the two disagreed by two orders of magnitude. The end-to-end replay that produced the 0% took under an hour and could have run on day one, before any filter existed. Any future plan of this shape must put premise validation in Phase 0, with a kill number published before the measurement rather than argued after it. ## Evaluated as a replacement: rtk `rtk` (https://github.com/rtk-ai/rtk, Apache-2.0) rewrites commands at `PreToolUse` rather than filtering output afterwards, which is the mechanism this project's own safety contract had excluded. Its rewrite pipeline was ported and validated against 197 of its own test assertions (197/197 exact agreement), then applied to the same traffic: - addresses **31.5%** of successful Bash bytes, **9.7%** of all tool-result bytes — genuinely non-zero, so the in-house 0% was a coverage failure rather than a law of nature; - projected saving is **0.32–0.48%** of session input tokens on rtk's own 60–90% claim, and **0.15–0.21%** once its filters' actual behaviour is modelled; - its two largest families here under-deliver: `rtk read` returns files verbatim at the default `--level none`, and `rtk grep` models at 12.3% against a claimed 75%; - custom TOML filters, the documented extension point, would reach 1.91% of Bash bytes. The large misses are structurally unreachable from config: `| head` and `| tail` (34.6%) are blocked by the pipeline-final rule, and `sed` (19.4%) sits in the hard-ignored prefix list. Not adopted. ## Where the tokens actually are The measurement points somewhere other than command output. In this traffic, `Read` is 53.8% of tool-result bytes, and within Bash the two largest buckets are `sed` used as a file reader (16.2%) and `| head` / `| tail` pipeline tails (34.6%). Those are file-reading patterns, not tool reports. Any future attempt at token reduction should start there, and should start by measuring. --- ## kb/history/completed/output-token-discipline-plan-20260504.md --- title: "Plan: Output & Token Discipline — Concise Modes, MCP Trim, Token Receipts" category: planning service: ai-toolkit tags: - brand-voice - output-style - mcp - statusline - token-tracking - hooks - briefing doc_type: plan status: completed created: "2026-05-04" last_updated: "2026-05-04" completed: "2026-05-04" completion: "100% of v3.2.0 scope (F2 deferred to v4.0 per spike conclusion)" shipped_in: "v3.2.0" description: "Three coordinated extensions to ai-toolkit that reduce token usage and surface real cost data: (1) brand-voice output modes for concise/strict Claude responses, (2) MCP description trimmer to compact tool listings before they reach the model, (3) token receipts in statusline reading session JSONL directly. Native extensions, no third-party skill names imported." --- # Plan: Output & Token Discipline **Status:** Completed (shipped in v3.2.0 on 2026-05-04) **Author:** lukasz.krzemien **Source of inspiration:** external Claude Code plugin observed 2026-05-04 (mechanism only, not naming or branding) **Spike companion:** [`kb/history/completed/f2-mcp-trim-spike-20260504.md`](f2-mcp-trim-spike-20260504.md) ## Cel Zmniejszyć realne zużycie tokenów w sesjach Claude Code i dać użytkownikowi widoczność tego zużycia w czasie rzeczywistym. Trzy mechanizmy działające razem, każdy jako natywne rozszerzenie istniejących komponentów ai-toolkit (`brand-voice`, `briefing`, `track-usage.sh`). Bez importowania obcych nazw — adaptacja idei jako własnych. ## Kontekst Obecnie ai-toolkit ma: - `brand-voice` skill — pilnuje stylu pisanego (docs, README, content) - `track-usage.sh` hook — liczy `/skill` invocations do `~/.softspark/ai-toolkit/stats.json` - `compile_slm.py` — kompresuje cały toolkit dla małych modeli (inny scope) - 113 skilli, pełen system hooków, doctor, eject Brakuje: - Trybu zwięzłego dla *odpowiedzi* Claude'a (brand-voice działa tylko dla pisanej zawartości) - Kompresji opisów MCP-tooli, które zżerają setki tokenów na każdym wywołaniu - Realnego pomiaru tokenów per sesja (mamy tylko licznik invocations, nie tokenów) ## Zakres Trzy features, zaplanowane w kolejności narastającego ryzyka. --- ## Feature 1 — `brand-voice` output modes ### Cel Rozszerzyć `brand-voice` o tryby zwięzłości stosowane do odpowiedzi konwersacyjnych Claude'a, nie tylko do generowanych dokumentów. ### Decyzje nazewnicze - **Wybrane:** zostaje `brand-voice` z wewnętrznymi trybami (`default`, `concise`, `strict`) - **Odrzucone:** - `concise` jako osobny skill — duplikuje brand-voice, niepotrzebny rozłam - `terse` — niejednoznaczne, kojarzy się z "rude" - `output-discipline` — zbyt biurokratyczne ### Pliki | Ścieżka | Akcja | Cel | |---------|-------|-----| | `app/skills/brand-voice/SKILL.md` | edit | Dodaj sekcję `## Output Modes` z opisem trzech trybów i sposobu aktywacji | | `app/skills/brand-voice/modes/concise.md` | new | Reguły: max 3 zdania per odpowiedź na pytanie zamknięte, brak preamble, brak "I'll now..." | | `app/skills/brand-voice/modes/strict.md` | new | Reguły: tylko fakty, zero filler adjectives, max 1 zdanie per fakt, listy zamiast prozy | | `app/skills/brand-voice/scripts/measure.py` | new | Eval przed/po na fixtures, raport oszczędności tokenów | | `tests/fixtures/output-modes/` | new | 10 par baseline/expected dla różnych typów zadań (debug, review, plan, eksploracja) | | `tests/skills_brand_voice.bats` | edit | Dodaj asercje dla modes (regex na zakazane filler, max-line-length) | ### Aktywacja Trzy mechanizmy: 1. Frontmatter w projekcie: `output-mode: concise` w `CLAUDE.md` lub `.claude/settings.json` 2. Slash: `/brand-voice concise` przełącza dla bieżącej sesji (przez `track-usage.sh` zapisuje do session state) 3. Auto-trigger: skill ładuje się także przy długich sesjach generowania (>30 min, heurystyka) ### Success criteria - Na zestawie 10 fixtures `concise` redukuje output >40% bez utraty kluczowych faktów - Test asercji: zachowane są wszystkie nazwy plików i symboli z baseline (regex match) - `validate.py --strict` przechodzi - `audit_skills.py --ci` zero HIGH ### Estymata 4–6h --- ## Feature 2 — MCP context trim — DEFERRED TO v4.0 **Status:** Deferred. Spike conducted before implementation, conclusion in [`f2-mcp-trim-spike-20260504.md`](f2-mcp-trim-spike-20260504.md). **Reason:** Claude Code hooks do not expose the MCP `tools/list` response or the system-prompt tool catalog. Hook events (`PreToolUse`, `PermissionRequest`, `Elicitation`) operate on individual tool calls only. Modifying tool descriptions before they reach the model requires a local MCP proxy server — multi-day scope, single-bug-breaks-all-MCP failure mode, out of scope for v3.2.0. **Resolution:** Full proxy-server approach moved to its own active planning doc: [`kb/planning/mcp-context-trim-v4-prd.md`](../../planning/mcp-context-trim-v4-prd.md). The mid-spike "F2-lite observability tool" alternative was also dropped per user decision (2026-05-04) — v3.2.0 ships F1+F3 only; v4.0 picks up the proxy-server work in full scope. The compression heuristics, file plan, and risk register from the original Feature 2 design were migrated into the v4.0 PRD. They are no longer duplicated in this archived doc. --- ## Feature 3 — Token receipts w statusline ### Cel Pokazać realne (nie estymowane) zużycie tokenów per-sesja w statusline Claude Code. Dane czytane z session JSONL, nie z heurystyk. ### Decyzje nazewnicze - **Wybrane:** rozszerzenie istniejącego skilla `briefing` + nowy hook `statusline-tokens.sh` + nowy skrypt `session_token_stats.py` - **Odrzucone:** - Nowy skill `stats` / `receipts` — duplikuje funkcjonalnie `briefing` - Modyfikacja istniejącego `track-usage.sh` jako jedynego punktu — za duża odpowiedzialność jednego pliku ### Pliki | Ścieżka | Akcja | Cel | |---------|-------|-----| | `scripts/session_token_stats.py` | new | Parser JSONL stdlib-only. Funkcje: `read_session()`, `aggregate_by_skill()`, `compare_baseline_vs_concise()` | | `app/hooks/statusline-tokens.sh` | new | Type `statusLine` w settings.json. Sumuje `usage.input_tokens` + `usage.output_tokens` z bieżącej sesji JSONL | | `app/hooks/track-usage.sh` | edit | Po wykryciu `/skill` zapisuj też `prompt_tokens` jeśli `transcript_path` dostępne | | `app/skills/briefing/SKILL.md` | edit | Nowa sekcja "Token receipts", komendy `/briefing --tokens --since 7d`, `/briefing --tokens --share` | | `scripts/merge-hooks.py` | edit | Statusline injection do `settings.json`, preserve user-customized entries (delivered as F3.5) | | `app/hooks/ai-toolkit-statusline.sh` | new | Comprehensive statusline (cwd + git + ctx + tokens + cost + model), default install (delivered as F3.5) | | `tests/session_token_stats.bats` | new | Fixture JSONL z 3 messages, asercje na sumę i breakdown | | `tests/statusline_tokens.bats` | new | Mock JSONL, weryfikacja outputu hooka (max 80 znaków, brak NaN, fallback gdy brak sesji) | ### Format statusline (proponowany) ``` [ai-toolkit] /concise · session: 24.7k · trend: ↓18% ``` Krótki tryb default, `--verbose` dodaje breakdown per skill. ### Ścieżka odczytu sesji Claude Code zapisuje JSONL do `~/.claude/projects//.jsonl`. Każda linia to message z polem `usage` (input_tokens, output_tokens, cache_*). Skrypt: 1. Identyfikuje aktualną sesję z env var `CLAUDE_SESSION_ID` (jeśli istnieje) lub najświeższy plik 2. Parsuje linie ignorując te bez `usage` 3. Sumuje + agreguje per skill (jeśli `track-usage.sh` zapisał skill mapping w sidecar pliku) ### Success criteria - Statusline pokazuje liczbę tokenów odczytaną z JSONL z dokładnością ±2% vs Anthropic API report (jeśli dostępny) - Brak crash gdy sesja jeszcze pusta - Brak crash gdy JSONL malformed - `validate.py --strict` przechodzi ### Estymata 6–8h --- ## Co nie wchodzi w plan | Pomysł | Powód odrzucenia | |--------|------------------| | Memory/file compressor (`/compress `) | `compile_slm.py` już kompresuje toolkit, brak konkretnego use-case dla per-file | | Compact `/commit`, `/review` modes | Powstaną automatycznie po Feature 1 (te skille będą używać reguł `concise` mode) | | "Caveman speak" / classical Chinese mode | Nie pasuje do tonu workmanlike, sprzeczne z brand-voice | | Single curl-installer | Już mamy `ai-toolkit install` z profilami | --- ## Kolejność realizacji i zależności ``` Feature 1 (brand-voice modes) ↓ dostarcza reguły zwięzłości Feature 3 (token receipts) ↓ dostarcza pomiar before/after dla F1 Feature 2 (mcp-trim) ← spike research najpierw, niezależne od F1/F3 ``` **F1 i F3 mogą iść parallel po dokończeniu F1 mode files.** **F2 ma osobną decyzję go/no-go po spike'u.** ## Estymata zbiorcza | Feature | Min | Max | |---------|-----|-----| | F1 | 4h | 6h | | F3 | 6h | 8h | | F2 spike | 1h | 1h | | F2 implementacja | 0h | 10h | | **Total** | **11h** | **25h** | ## Doc & test sweep (obowiązkowy po każdym feature) 1. `python3 scripts/validate.py --strict` 2. `python3 scripts/audit_skills.py --ci` 3. Regen `AGENTS.md`: `python3 scripts/generate_agents_md.py > AGENTS.md` 4. Regen `llms.txt`: `python3 scripts/generate_llms_txt.py > llms.txt` 5. Bump version w `package.json` + `plugin.json` 6. Update `skills-catalog.md` z nowymi/zmienionymi skillami 7. Update `README.md`, `CLAUDE.md`, `ARCHITECTURE.md`, `architecture-overview.md` jeśli zmiana behavior 8. Commit conventional: `feat(brand-voice): add output modes`, `feat(briefing): add token receipts`, `feat(mcp-trim): add description trimmer` ## Open questions — resolved 1. **Czy `PreToolUse` może modyfikować deklarację tool'a w MCP listingu?** — NIE. Spike potwierdził że żaden hook event nie wystawia `tools/list`. F2 wymaga MCP proxy. Odsunięte do v4.0. 2. **Czy Claude Code wystawia hookom `CLAUDE_SESSION_ID`?** — częściowo. `scripts/session_token_stats.py` używa fallback "newest JSONL w katalogu projektu" + opcjonalnie cwd → sanitize → match. Działa stabilnie na realnych sesjach (96.6k tokens parsed correctly w smoke tescie). 3. **Czy włączyć `concise` mode jako default?** — pozostaje opt-in. `brand-voice` z trybami auto-loaduje się tylko gdy projekt ustawi `output-mode: concise` w `CLAUDE.md` lub user wpisze `/brand-voice concise`. Pomiary z F3 dadzą dane do późniejszej decyzji. ## Final delivery (v3.2.0) ### Shipped | Feature | Outcome | Pliki | |---------|---------|-------| | **F1 — brand-voice output modes** | Done. Aggregate ratio na 3 fixtures: concise **21%**, strict **14%** (cel ≤60% / ≤40%). | `app/skills/brand-voice/SKILL.md`, `app/skills/brand-voice/modes/{concise,strict}.md`, `app/skills/brand-voice/scripts/measure.py`, `tests/fixtures/output-modes/{debug-explanation,plan-question,review-summary}/`, `tests/test_brand_voice.bats` (14 tests) | | **F3 — token receipts** | Done. Smoke-test na realnej sesji: 96.6k tokenów poprawnie sparsowane. | `scripts/session_token_stats.py`, `tests/fixtures/session-jsonl/{three-messages,malformed,empty}.jsonl`, `tests/test_session_token_stats.bats` (15 tests) | | **F3.5 — comprehensive default statusline** | Done. Pełny segment: cwd + git + ctx% + tokens + trend + model-aware cost + model. Installed by default via `merge-hooks.py`, user-custom statusLine preserved untouched. | `app/hooks/ai-toolkit-statusline.sh`, `app/hooks.json` (`statusLine` entry), `scripts/merge-hooks.py` (statusLine inject/strip), `tests/test_statusline_hook.bats` (14 tests), `tests/test_merge_hooks_statusline.bats` (8 tests) | | **briefing skill extension** | `/briefing --tokens` + wire-up docs + opt-out env vars. | `app/skills/briefing/SKILL.md` | ### Deviations from plan | Plan said | Shipped | Reason | |-----------|---------|--------| | 10 fixtures w F1 | 3 fixtures + `must_contain.txt` mechanism | Mniejszy zestaw + extensible konwencja wystarcza do walidacji budżetów; jakość > ilość | | F3 hook integracja jako follow-up (manual settings.json edit) | F3.5 dostarczył pełny default install via `merge-hooks.py` | User feedback w trakcie pracy: "niech ai-toolki instaluje go domyslnie od nowej wersji" | | F2 implementacja po spike'u | F2 deferred do v4.0 | Spike pokazał że Claude Code hooki nie wystawiają `tools/list` → wymaga proxy server, multi-day scope | ### Quality gates passed - `validate.py --strict`: 0 errors / 0 warnings - `audit_skills.py --ci`: 0 HIGH / 0 WARN / 13 INFO (pre-existing) - `audit_skills.py --sarif`: SARIF 2.1.0 valid, 5 rules - `npm test`: 1032 / 1032 passing (was 981 in v3.1.1) - Registry drift: clean - Provenance + checksum-pin: verified - Ecosystem doctor: 9 cosmetic drifts (class A) refreshed ### Skill classification change `brand-voice` przeszedł `user-invocable: false → true` (knowledge → hybrid): - **Hybrid**: 31 → 32 - **Knowledge**: 49 → 48 - **Task**: 32 (no change) Updated: `README.md`, `kb/reference/architecture-overview.md`, `kb/reference/skills-catalog.md`. ## Status & rewizje | Data | Zmiana | Autor | |------|--------|-------| | 2026-05-04 | Initial draft | lukasz.krzemien | | 2026-05-04 | F1 implementation done — brand-voice modes, measure.py, 3 fixtures, 14 bats tests. Aggregate ratio: concise 21%, strict 14%. | claude | | 2026-05-04 | F3 implementation done — `session_token_stats.py`, `statusline-tokens.sh`, briefing skill extension. 22 new bats tests. Smoke-tested on real session. | claude | | 2026-05-04 | F3.5 follow-up done — replaced focused tokens hook with comprehensive `ai-toolkit-statusline.sh`. Extended `merge-hooks.py` for safe statusLine injection. Version bumped 3.1.1 → 3.2.0. 22 new bats tests. CHANGELOG + README updated. | claude | | 2026-05-04 | F2 spike completed — see [`f2-mcp-trim-spike-20260504.md`](f2-mcp-trim-spike-20260504.md). Hooks cannot modify `tools/list`. F2 deferred to v4.0 with own PRD. | claude | | 2026-05-04 | Plan archived to `kb/history/completed/`. Shipped in v3.2.0. | claude | --- ## kb/history/completed/rtk-pack-integration-20260726.md --- title: "Plan: rtk Pack Integration" category: planning service: ai-toolkit tags: - rtk - plugin-pack - token-reduction - vendored-binaries - cross-build - upstream-sync doc_type: postmortem status: completed created: "2026-07-26" last_updated: "2026-07-26" completion: "100% — phases 0, 1, 2, 4, 5 delivered; phase 3 cut on the measurement" shipped_in: "v4.18.0" pinned_upstream: "v0.44.0" description: "Integrate rtk as an opt-in ai-toolkit plugin pack: binaries cross-built from source in our CI with telemetry disabled, hosted on our own GitHub Release, fetched and checksum-verified at pack install, auto-update on ai-toolkit update, and an SOP for tracking upstream releases. Custom filter presets were cut on the measurement (0.008% of input tokens). Phase 0 validated the premise against v0.44.0 on the full transcript corpus before any build work." --- # rtk Pack Integration **Completed 2026-07-26, shipped in v4.18.0.** Kept whole rather than summarised, because the value here is the measurements and the things that turned out to be false, not the plan structure. Live procedure: `kb/procedures/rtk-upstream-sync-sop.md`. Headline: rtk-pack ships opt-in, built from source with telemetry compiled out, and saves a **measured 0.0615% of input tokens** against a kill number of 0.05% published before the measurement. Phase 3 (our own filter presets) was cut at 0.008%. The projection this plan published for itself was about twice too optimistic, and section 10.1 says why. ## 1. Decision Ship `rtk` to ai-toolkit users as an **opt-in plugin pack**, with the binary supply chain owned end to end by us. Five choices, made and locked: | Question | Decision | |---|---| | Delivery | Plugin pack (`rtk-pack`), never a native feature | | Binary hosting | Our own GitHub Release, fetched and SHA-256 verified at pack install | | Binary provenance | Cross-built from upstream source in our CI, `RTK_TELEMETRY_URL` unset | | Pinned upstream tag | **v0.44.0** (2026-07-26). v0.43.0 was ruled out on evidence, see section 2 | | Upstream tracking | An SOP that rebuilds, verifies, and re-publishes when rtk ships a release | Upstream: https://github.com/rtk-ai/rtk, Apache-2.0, Rust 1.91 plus a C toolchain (rusqlite `bundled` compiles SQLite from source). ## 2. Phase 0: premise validated before building anything The retired output filter validated its design across five phases and its premise not at all until the fifth. This plan runs premise validation first. Everything below is measured, not estimated. ### 2.1 Why v0.43.0 was ruled out Two structural facts, read from the source of both tags: - **Custom TOML filters are not wired into the rewrite path in v0.43.0.** `src/discover/registry.rs` at v0.43.0 contains zero references to `toml_filter`. In v0.44.0 they appear at `registry.rs:1047,1055,1058`. *This reason no longer applies:* section 7 cut our own presets, so nothing we ship depends on that wiring. It is recorded because it was one of the two arguments made at pin time, and the pin now rests on the second alone. - **`pipeline_final_safe` does not exist in v0.43.0.** It arrives in v0.44.0 (`rules.rs:7,115,124`, `registry.rs:605,659,864,1069`). v0.43.0 rewrote the first pipeline stage; v0.44.0 rewrites the last. On this traffic 56.5% of `rtk grep` hits arrive through the pipeline-final path, so they do not exist at all on v0.43.0. ### 2.2 The port models v0.44.0, not v0.43.0 `rtk_port.py` is re-validated by extracting the `assert_eq!` assertions from each tag's `registry.rs` `#[cfg(test)]` block and replaying them: | Port checked against | Assertions | Exact-string agreement | |---|---:|---:| | **v0.44.0** | 203 | **203/203** | | v0.43.0 | 160 | 147/160 | The 13 v0.43.0 failures are all the same pipeline inversion. The port also passes the 15 assertions v0.44.0 added after the port was written. Pinning v0.44.0 makes the existing measurement correct rather than requiring rework. `registry.rs:590-592` returns `Some(unchanged)` for a simple already-`rtk` command; the port's equivalent bail must be mapped to that, not to `None`. ### 2.3 The recorded numbers came from an 11% sample `measure_rtk.py` sets `N_FILES = 134` and takes the most recently modified transcripts. The available pool is **1224 transcripts**. At n=134 the projected saving swings **8.8x** across windows (0.071% to 0.628%); at n>=408 it converges to within **1.05x**. The spread recorded in earlier revisions of this plan was sampling noise. ### 2.4 The port over-counted, and by how much Both real entry points call `contains_unattestable_construct` **before** `registry::rewrite_command`: `hook_cmd.rs:149-151` (Defer) and `rewrite_cmd.rs:54-56` (Passthrough). Any command carrying a command substitution, a process substitution, or a redirect with a file target is rejected whole. Upstream's rewrite tests call `rewrite_command` directly, so a port validated only against them passes while still over-counting. The gate is ported from `lexer.rs:295-347` and validated **35/35** against upstream's own assertions at `lexer.rs:1186-1276`. Applying it costs 3.1% relative coverage. ### 2.5 Corrected numbers Full corpus, 1224 transcripts, 552 MB, 106 MB of tool results, 26.6 MB of successful Bash, entry gate applied: | Metric | Earlier revision (n=134, ungated) | Phase 0 (n=1224, gated) | |---|---:|---:| | Coverage of successful Bash bytes | 31.5% | **35.17%** | | Coverage of all tool-result bytes | 9.69% | **8.77%** | | Ceiling: tool results as share of input volume | 5.52% | **4.54%** | | Projection at rtk's 60-90% claim | 0.32-0.48% | **0.239-0.359%** | | Projection, mechanism modelled | 0.15-0.21% | **0.117-0.164%** | | Reach of our own TOML presets (the number that cut section 7) | 1.91% of Bash | **1.08% of Bash** | | `Read` share of tool-result bytes | 53.8% | **62.8%** | Modelled effectiveness per family against the claim: `rtk grep` 75% claimed and **9.0%** modelled; `rtk read` 60% claimed and **0.0%** modelled; `rtk rg` 75% and 30%; `rtk make` 65% and 16%; `rtk jq` 74%. Largest unaddressed buckets, as a share of successful Bash bytes: pipeline not a rewritable final stage **39.78%**, ignored by rtk **10.72%**, entry gate **8.29%**, multiline script **4.71%**. ### 2.6 Open question 3 answered: no Patching `pipeline_final_safe` for `head` and `tail` unlocks 10.56 MB, 39.96% of Bash bytes. **88.3% of that (9.33 MB) routes to `rtk read`, measured at 0.00% saving** on this traffic because it returns files verbatim at the default `--level none`. The largest reachable gap is reachable and worthless. The argument for maintaining a fork does not survive its own measurement. ## 3. Why a pack rather than a native feature - **Opt-in is structural, not a policy.** Plugin packs must not be auto-installed [PATH: kb/reference/plugin-pack-conventions.md]. The retired output filter shipped `off` by default and still had to be wired into every install path, which is why v4.17.0 needed a bespoke migration cleanup. - **Removal is already solved.** `plugin remove` strips hook entries from `settings.json`, deletes owned `plugin--*` assets, and leaves core and user files alone. - **The core package stays lean.** ai-toolkit is 4.4 MB unpacked; the platform artifacts are ~19.5 MB compressed. They never enter the base package. - **There is a working precedent.** `memory-pack` ships hooks, a shared script directory, an install-time init script, its own data with retention, and a clean uninstall. `rtk-pack` is the same shape with a binary instead of a database. ## 4. Architecture ```text app/plugins/rtk-pack/ ├── plugin.json # pinned rtk version + per-platform SHA-256 ├── README.md ├── hooks/ │ └── rewrite.sh # PreToolUse; delegates to the fetched binary └── scripts/ ├── init.py # platform detect → fetch → verify → install └── status.py # reports binary, digest and hook wiring ``` Runtime layout on a user machine: ```text ~/.softspark/ai-toolkit/ ├── hooks/plugin-rtk-pack-rewrite.sh # installed hook └── plugin-scripts/rtk-pack/ ├── bin/rtk # verified binary, 0755 └── version.json # pinned version + recorded digest ``` Everything the pack writes lives under `plugin-scripts/rtk-pack/`, which `plugin remove` deletes wholesale (`plugin.py:500-503`). The pack owns no state outside the toolkit data directory: no `filters.toml`, no entry in rtk's trust store, nothing in an OS config directory. That is a consequence of cutting section 7 and it is what keeps removal complete. Binary supply chain: ```text upstream tag → our CI cross-build (RTK_TELEMETRY_URL unset) → our GitHub Release softspark-rtk-- → SHA-256 recorded in plugin.json → plugin install: detect platform, fetch, verify, chmod ``` ## 5. Phase 1: build pipeline and binary release **Outcome:** we can produce, from an upstream tag, a set of binaries that provably make no network calls of their own. **Targets: five, not six.** `x86_64-apple-darwin`, `aarch64-apple-darwin`, `x86_64-unknown-linux-musl`, `aarch64-unknown-linux-gnu`, `x86_64-pc-windows-msvc`. Earlier revisions added `x86_64-unknown-linux-gnu` on the reasoning that upstream does not ship it. It is dropped: upstream's own Homebrew formula routes Linux x86_64 to the **musl** tarball (`release.yml:314-316`), a static musl binary runs on glibc, and upstream has never validated a gnu x86_64 artifact, so we would be first to ship one and would own all its breakage plus an uncontrolled glibc floor inherited from the runner image. Build constraints, all read from the pinned checkout: - **Pin the toolchain.** Upstream uses `dtolnay/rust-toolchain@stable` (`release.yml:65,116`) with `warnings = "deny"` (`Cargo.toml:70-72`) and ships no `rust-toolchain` file. A future rustc lint turns our builds red with no change on either side. We pin an exact version at or above 1.91. - **Windows must build natively.** `build.rs:6-13` emits `cargo:rustc-link-arg=/STACK:8388608` under `#[cfg(windows)]`, which in a build script is a **host** predicate. Cross-building Windows from Linux silently drops the 8 MiB stack reservation that upstream's own comment says is what makes `rtk.exe --version`, `--help`, and hook entry points start reliably. Build on `windows-latest`, or pass the link-arg explicitly. - **Both darwin targets build on `macos-latest` (arm64).** `macos-13` is the last x86_64 macOS image and GitHub is winding it down: on runs 30212577757 and 30214341444 that job sat queued indefinitely while every other target finished. Upstream builds both on arm64 too, but never runs its x86_64 artifact. We do: the verifier detects Rosetta 2 and executes it, and reports `inconclusive` rather than `pass` if it cannot. - **Every target needs a target-capable C compiler.** rusqlite `bundled` (`Cargo.toml:26`) is not switchable off; there is no `[features]` table and no `cfg(feature` in `src/`. There is no pure-Rust escape route. - **aarch64-linux is the hard case, but upstream already solves it** with `cross: true` on `ubuntu-latest` (`release.yml:51-54`). Copy that. Upstream sets only the linker env var and lets the `cc` crate guess the C compiler; export `CC_aarch64_unknown_linux_gnu` and `AR_aarch64_unknown_linux_gnu` explicitly so a runner image change surfaces as a clear error. - **Run our own blocking `cargo audit`.** Upstream's is advisory: `ci.yml:88-96` swallows failures into a warning. Rebuilding from a tag inherits that lockfile, and on v0.44.0 it inherits four advisories across three of its 203 crates. The audit and the build jobs apply the same remediation, so we audit the dependency set we ship rather than a different one: | Crate | Advisory | Disposition | |---|---|---| | `anyhow` 1.0.102 | RUSTSEC-2026-0190 | `cargo update` to 1.0.103, in range | | `crossbeam-epoch` 0.9.18 | RUSTSEC-2026-0204 | `cargo update` to 0.9.20, in range | | `quick-xml` 0.37.5 | RUSTSEC-2026-0194, RUSTSEC-2026-0195 | ignored with reasons | The quick-xml fix lands only in 0.41.0 while rtk pins `"0.37"` as a direct dependency (`Cargo.toml:34`), so taking it means editing `Cargo.toml` and the calling code in `src/cmds/dotnet/dotnet_trx.rs`. That would break the promise that the only difference from an upstream build is the undefined telemetry endpoint. The reachable surface is narrow: quick-xml parses .NET TRX test output only, the input is a report produced locally by the user's own test run, and both advisories are availability-only (CVSS `C:N/I:N/A:H`). The SOP re-checks this on every sync and deletes the ignores once upstream moves to quick-xml 0.41 or later. - A target that will not build is dropped, not faked. ### 5.1 Proving the binary is silent The endpoint is compile-time only: `option_env!("RTK_TELEMETRY_URL")` at `telemetry.rs:16` and `telemetry_cmd.rs:176`, with the token at `telemetry.rs:17` and `telemetry_cmd.rs:189`. Nothing supplies a default: `build.rs` emits no `cargo:rustc-env`, `TelemetryConfig` has no URL field, and no runtime `std::env::var` resolves the endpoint. Upstream injects it only in `release.yml:85-86,124-125,151-152`. Building without it yields a binary whose telemetry destination cannot be re-enabled by any env var or config at runtime. **The acceptance criterion in earlier revisions was untestable and is replaced.** "No telemetry symbols in the binary" cannot be checked: the guard is a runtime `if TELEMETRY_URL.is_none()` on a const (`telemetry.rs:23-26`), not a `#[cfg]`, so the code compiles in and is only eliminated by LLVM as an optimisation; and `Cargo.toml:51` sets `strip = true`, which removes the symbols regardless of whether the code is present. A symbol check would pass for the wrong reason. What we assert instead: 0. **The artifact was actually started.** Every other assertion is worthless on a binary nobody executed, and two of the five targets are not native to their runner. `aarch64-unknown-linux-gnu` runs under `qemu-user` with the cross sysroot passed as `-L`, and `x86_64-apple-darwin` runs under Rosetta 2 on the arm64 runner. A target that cannot be started reports `inconclusive`, never `pass`. 1. **Build gate.** `RTK_TELEMETRY_URL` and `RTK_TELEMETRY_TOKEN` are unset in the build environment, asserted in CI before `cargo build`, with a clean target directory per build. 2. **Offline smoke run.** Each artifact runs its real command surface with no network route available and makes zero outbound connections. This is Linux-only: `unshare -rn` has no unprivileged equivalent on macOS or Windows runners, and Ubuntu 24.04's `kernel.apparmor_restrict_unprivileged_userns=1` means even there it needs `sudo`. On the first run this assertion silently degraded to a skip on every target while the verdict still read `pass`, so the manifest now records which isolator was used and a target that could not be started at all reports `inconclusive` rather than `pass`. 3. **Filesystem assertion.** No telemetry state is created under the resolved data directory. 4. **Drift detection.** Record artifact size and a string-allowlist hash per target and fail the build on unexplained drift, since whether `ureq`, `rustls`, `ring` and `webpki-roots` are actually eliminated is an LLVM outcome under `lto = true`, not a guarantee. Three findings that must reach the pack README: - **`rtk cc` shells out to `npx --yes ccusage`** when `ccusage` is not on PATH (`ccusage.rs:104-119`). That is a runtime npm fetch and third-party code execution. It is not telemetry and not automatic, but it is outbound network from a binary we tell users makes no network calls. - **`RTK_TELEMETRY_DISABLED` only accepts the exact string `1`** (`telemetry_cmd.rs:31-33`, locked by the test at `:222-229`). `true` and `yes` are silent no-ops. Never write anything else anywhere in the pack. - **`rtk telemetry forget` on an endpoint-free build prints a misleading failure** telling the user to email upstream to complete erasure (`telemetry_cmd.rs:158-169`), when nothing was ever sent. Apache-2.0 obligations ship alongside: upstream `LICENSE`, a `NOTICE` file (upstream has none), and a statement that the only build-time change is leaving the telemetry endpoint undefined. **Do not redistribute upstream `DISCLAIMER.md` verbatim**: `DISCLAIMER.md:25` states telemetry is collected by default, which the code contradicts and which a compliance reviewer would read as a reason to block the pack. `docs/TELEMETRY.md:180` calls the mechanism "compile-time gating" where "all telemetry code is dead", which is imprecise for the same reason our own criterion changed. **Success criteria:** five artifacts build from the pinned tag; each runs `rtk --version` on its target; the silence assertions pass or say plainly that they did not run; checksums published. ### 5.2 Result, run 30214882862 All five targets build and verify. Every artifact was actually started, none shipped on a skipped assertion. | Target | `runs` | `offline` | |---|---|---| | `aarch64-apple-darwin` | native | not applicable | | `x86_64-apple-darwin` | translated via Rosetta 2 | not applicable | | `x86_64-unknown-linux-musl` | native | passed under `sudo -n unshare -rn` | | `aarch64-unknown-linux-gnu` | emulated via `qemu-aarch64-static` | passed under `sudo -n unshare -rn` | | `x86_64-pc-windows-msvc` | native | not applicable | No TLS markers in any artifact, so `ureq`, `rustls`, `ring` and `webpki-roots` appear to be eliminated under LTO once the endpoint const is `None`. That is an optimisation outcome rather than a guarantee, which is why the fingerprint is recorded per target and drift fails the build. **Four of five targets are bit-reproducible.** Across independent runs with identical inputs, `aarch64-apple-darwin`, `x86_64-apple-darwin`, `aarch64-unknown-linux-gnu` and `x86_64-unknown-linux-musl` produced byte-identical binaries. The concern about unset `trim-paths` embedding registry paths does not materialise on these runners. `x86_64-pc-windows-msvc` did not, and the cause was bounded: **24 bytes out of 8,018,432 differed**, in five regions. Four were the same two-byte value at `0x100` and in the debug directory, which is the MSVC link timestamp; the fifth was a 16-byte CodeView GUID. The code was identical; only the link stamp varied. `-C link-arg=/Brepro` is now set for the Windows target, which makes the linker derive that timestamp from content rather than the clock. **The shipped `softspark-rtk-v0.44.0-1` artifacts predate that change**, so the Windows digest in `plugin.json` is from a non-deterministic link; the flag takes effect on the next rebuild, which will produce a different Windows digest for the same source. This matters for the sync SOP: a changed digest means changed input rather than build noise. That inference holds on four targets today and on all five from the next rebuild onward. Note that the **archives** are not reproducible even where the binaries are, because gzip records a timestamp. Compare extracted binaries, never the tarballs. Three defects the run surfaced, all now fixed and guarded: - The gnu target is dynamically linked, so `qemu-user` needs the cross sysroot. It is passed as `-L` rather than `QEMU_LD_PREFIX`, because the offline check runs under `sudo` and `env_reset` drops the variable. - The offline check compared a plain run against a namespaced one, so `sudo`'s environment handling was confounded with the network variable and a harness failure was reported as a behaviour difference in the binary. Both sides now run in a namespace and differ only in `-n`. - `7z` stored the full relative path, so the Windows zip held `rtk-src/target//release/rtk.exe` while every tar.gz held a bare `rtk`. The pack installer extracts all five the same way, so this was a silent install failure on one platform. A `collect` step now asserts every archive holds exactly one flat entry. ## 6. Phase 2: the pack **Outcome:** `ai-toolkit plugin install rtk-pack` produces a working setup and `plugin remove` leaves nothing behind. - `scripts/init.py` detects platform and libc, fetches the matching artifact, verifies its SHA-256 against `plugin.json`, and installs to `plugin-scripts/rtk-pack/bin/rtk`. A mismatch aborts and removes the partial download. No network at runtime, only at install. - Failure to fetch is not an install failure: the pack degrades to inert and says so, matching how the core behaves when `jq` is missing. - The hook is wired at `PreToolUse` through the existing pack hook mechanism, so `plugin remove` strips it via the `_source` marker. - `plugin status` dispatches to the pack's own `scripts/status.py`, which is generic rather than another hardcoded `if name == ...` branch. It reports the binary, the install record, the live version, and hook wiring, and announces a missing binary as inert rather than letting a green install imply it works. ### 6.1 No trust step, because the pack ships no filters Section 7 is cut, so the pack writes no `filters.toml` and takes no entry in rtk's trust store. That removes the single highest-ranked risk this plan carried: an untrusted or content-changed preset produces **zero output on the command path with no warning, no stderr line, and no non-zero exit** (`toml_filter.rs:220-221`, `:450-458`) — installed, apparently fine, doing nothing, which is exactly the shape of failure that retired the in-house filter. The pack instead reports whether it is actually working. `scripts/status.py` distinguishes installed from functioning: a missing binary is announced as `MISSING — the hook is inert and every command runs unchanged` rather than inferred from a green install. **If presets are ever revisited, these are the constraints, kept because they were expensive to establish:** - Trust is a byte-exact SHA-256 (`trust.rs:142-163`), so every write, upgrade, repair, trailing newline, or git CRLF normalisation invalidates it. - `rtk trust --yes` is indiscriminate: it trusts every gated file that exists, including a `.rtk/filters.toml` an attacker committed into the CWD (`trust.rs:262-305`, project path is CWD-relative at `:207`). - It exits 1 both when there is nothing to trust and when the file is invalid TOML (`trust.rs:307-312`), so `set -e` aborts on the benign case. - There is no `rtk doctor`, and `rtk trust --list` never re-hashes (`trust.rs:242-256`). Verification means reading the store and recomputing. - The global path is not `~/.config/rtk/filters.toml` everywhere: it comes from `dirs::config_dir()` (`trust.rs:206-216`), which on macOS is `~/Library/Application Support`. rtk's own error text hardcodes the Linux path and is wrong there. - One global file, no include mechanism, so injection would have to follow the merge-friendly install model [PATH: kb/reference/merge-friendly-install-model.md] with comment markers rather than owning the file. **Success criteria:** install, status, update, remove, and re-install are idempotent; a machine without the pack behaves exactly as today; uninstalling ai-toolkit removes every pack artifact. ## 7. Phase 3: cut **Cut on the measurement, 2026-07-26.** Custom TOML presets reach **1.08% of Bash bytes**, which is 0.27% of tool-result bytes. At a generous 60% reduction that is **0.008% of input tokens**, roughly one part in twelve thousand. Against that: presets would have required a trust step that fails silently, a doctor check that recomputes digests, marker injection into a file the user may also edit, re-trusting after every write, and a per-upstream-release review of a DSL whose `deny_unknown_fields` rejects the entire file on one unknown key. The maintenance surface is large, the payoff is not measurable, and every item on that list is a way for the pack to look installed while doing nothing. The pack therefore ships upstream rtk's built-in filters and nothing of our own. The 35.2% coverage quoted in section 2.5 is already what those built-ins deliver; it does not shrink as a result of this cut. Two consequences recorded so they are not rediscovered: - The pack owns no state outside `~/.softspark/ai-toolkit/plugin-scripts/rtk-pack/`, which is what makes `plugin remove` complete. - One of the two arguments for pinning v0.44.0 was that custom filters are only wired into the rewrite path there. That argument is now moot; the pin rests on `pipeline_final_safe`, which carries 56.5% of `rtk grep` hits on this traffic and does not exist in v0.43.0. See section 2.1. Reopening this is a plan change, not a task: it needs a workload where rtk's built-ins measurably under-perform and a preset that measurably closes the gap, demonstrated by replay before any code is written. ## 8. Phase 4: auto-update on `ai-toolkit update` **Outcome:** a user who installed the pack gets the new binary by running the update they already run. Verified gap: `handleUpdate` in `bin/ai-toolkit.js` reads installed modules from `state.json` and does not touch `plugins.json`. Nothing propagates to packs today, so this is new wiring, not a configuration change. **Done.** `ai-toolkit update` now calls `plugin update --editor all --all` after the core update. Two things had to change first, both generic rather than rtk-specific: - `update_pack` was an unconditional remove-then-reinstall, so wiring it into the core update would have refetched the binary on every run. `plugins.json` now records the pack version installed per editor, and an update whose manifest version matches is a silent no-op. `--force` overrides. - State written before versions were tracked has no `versions` map, so every pack reads as stale exactly once and is updated once. That is the intended migration, not a bug. `--local` leaves packs alone: they live in `~/.softspark/ai-toolkit` and are global, while `--local` is project-local config only. - After the core update completes, `ai-toolkit update` reads `plugins.json` and runs the equivalent of `plugin update` for every **currently installed** pack. Packs that are not installed stay untouched, which preserves adoption rule 1. - The rtk-pack update path is: compare the manifest's pinned version against the recorded pack version; if they differ, fetch and verify the new binary and re-install the hook. - `--dry-run` reports what would change per pack. - A pack update failure warns and continues; it never fails the core update. - Bats coverage in `tests/test_plugin_update.bats`: stale version replaced; current version untouched and silent; pack absent means no work; `--dry-run` reports without acting; legacy state with no version map updates once; a corrupt `plugins.json` does not crash the run. **Success criteria:** the wiring is generic across packs, not rtk-specific; `update` remains idempotent; a failed pack update never leaves a half-installed binary. ## 9. Phase 5: upstream sync SOP **Outcome:** a written procedure so tracking upstream is routine rather than a research project each time. **Written**, at `kb/procedures/rtk-upstream-sync-sop.md`, modelled on the existing ecosystem-sync SOP. The 0.43.0 to 0.44.0 bump is a worked example of why the review step exists: 200 commits, and every file the SOP names changed, including a semantic inversion in pipeline rewriting and the arrival of the trust gate. Steps the SOP must cover: 1. **Detect.** Check the upstream releases feed for a tag newer than the pinned one. Their stable cadence is roughly two to four weeks, behind a long release-candidate train. Cadence: on demand, plus a check folded into release preparation. 2. **Review before building.** Read the changelog and diff `src/discover/rules.rs`, `src/discover/registry.rs`, `src/discover/lexer.rs`, `IGNORED_PREFIXES`, the TOML DSL structs in `src/core/toml_filter.rs`, `src/hooks/trust.rs`, and anything touching telemetry. A change to the DSL or to trust handling is a stop-and-think, not a rebuild. 3. **Re-validate the port.** Re-extract the `rewrite_command` assertions from the new tag's `registry.rs` test block and the gate assertions from `lexer.rs`, and replay both. Anything short of full agreement invalidates every coverage number until the port is fixed. 4. **Rebuild** all five targets from the new tag with telemetry unset. 5. **Verify.** Binary runs on each target; the silence assertions pass; the pack's fixtures still produce the expected decisions. 6. **Publish** a new release in our namespace and record the new digests. 7. **Bump** `rtk-pack` version in `plugin.json` and note the upstream version it tracks. The bump is what makes `plugin update` fire at all, since a pack whose recorded version still matches is skipped silently. 8. **Ship** in the next ai-toolkit release; installed packs pick it up through Phase 4. 9. **Record** the licence position if upstream relicenses or adds a NOTICE. There is no upstream test asserting network silence, so that property can regress on any bump without turning their CI red. We own that test and re-run it every time. **Success criteria:** a maintainer who has never done it can follow the SOP end to end; the review step names specific files rather than saying "check for breaking changes". ## 10. Verification The pack must be able to answer "did this help?" with a number. **The before-and-after design is replaced.** Phase 0 measured its noise floor: on a 134-transcript window the projected saving varies by 8.8x while the mechanism is unchanged. A before-and-after comparison cannot detect an effect of 0.117% to 0.164% against that. Two changes make the measurement possible: - **Measure over the whole transcript pool, not a recency window.** At n>=408 the same metric converges to within 1.05x. - **Measure by replay, not by elapsed calendar time.** Run real captured tool output through the built binary and compare byte counts directly. That is the method that produced the honest 0% which retired the in-house filter, and it removes the confound of what work the user happened to do that week. The harness is `replay_rtk.py` in the measurement archive, not a script inside the pack. It pairs each successful Bash result with the command that produced it, keeps the ones the validated port says rtk would rewrite, and pipes the captured bytes through the shipped binary via `rtk pipe -f `. Two limits, stated because the number is meaningless without them: - `rtk pipe` runs the same filter code as the command path but knows less than it does: no result caps, no exit code, no file set. For search families the replay figure is a **lower bound** on what the command path would save. - Families with no pipe filter (`rtk read`, `rtk ls`, `rtk wc`) cannot be measured this way at all. They are reported as **unmeasurable**, never folded in as zero, because a zero that is really an absence is how the previous effort talked itself into shipping. Session-level token accounting, when it is wanted, comes from the session JSONL [PATH: scripts/session_token_stats.py], summing all four usage fields: in a cached session most context tokens land in the cache fields rather than `input_tokens`. **Kill number, published before the measurement rather than argued after it:** if replay on the full corpus shows the shipped binary saving less than 0.05% of input tokens, the pack is not worth its maintenance and supply-chain surface, and it is retired the way the output filter was. ### 10.1 Result: measured 0.0615%, kill number survived by 23% Full pool, 28.56 MB of successful Bash output, replayed through the published `softspark-rtk-v0.44.0-1` binary: | | | |---|---:| | Addressed by rtk | 9.99 MB, **35.00%** of Bash bytes | | Measurable through `rtk pipe` | 5.66 MB | | After filtering | 4.22 MB | | **Measured saving** | **1.44 MB = 360,529 tokens** | | As a share of tool-result bytes | 1.35% | | **As a share of input tokens** | **0.0615%** | Against a kill number of 0.05%, the pack survives by a factor of 1.23. That is a pass, not a vindication. **The projection published in section 2.5 was roughly twice too optimistic.** It said 0.117% to 0.164%; the measurement says 0.0615%. The error has a single identifiable cause: the model credited families it could not simulate with rtk's own claimed 60-90%. Replayed, the families that can be measured deliver **25.5%** in aggregate. Per family, measured against modelled and against upstream's claim: | Family | Measured | Modelled | Claimed | |---|---:|---:|---:| | `rtk grep` | **22.3%** | 9.0% | 75% | | `rtk git` | **33.1%** | — | 70% | | `rtk find` | **35.6%** | — | 70% | | `rtk rg` | **7.0%** | 30% | 75% | | `rtk ruff` | **0.0%** | — | — | The model **understated** grep by 2.5x and the total still came out high, so the two errors are unrelated: coverage modelling was sound, per-family effectiveness was guesswork wherever the source was not read closely. **What the replay does not settle.** 43.3% of addressed bytes (4.33 MB) have no `rtk pipe` filter and are unmeasurable, the largest being `rtk read` at 1.55 MB. That family is known from source to return files verbatim at the default `--level none`, so the headroom is smaller than 43.3% suggests. No extrapolation is applied: a zero that is really an absence of measurement is how the previous effort talked itself into shipping. For search families the pipe path also lacks the command path's result caps, making these figures a lower bound. **One independent confirmation.** The replay measured coverage at 35.00% of Bash bytes; the gated port measured 35.17% on the same pool by a different method. The port is right about *which* commands rtk touches even where it was wrong about how much each saves. Reproduce with `replay_rtk.py --pool 1300 --rtk ` in the measurement archive. ## 11. Licence and security obligations - **Apache-2.0.** Ship upstream `LICENSE` with the binaries, add a `NOTICE`, and state the build-time change. Never relabel any upstream file MIT. Do not ship `DISCLAIMER.md` verbatim, see section 5.1. - **Telemetry.** Proven absent per build by the four assertions in section 5.1, re-proven on every upstream sync. - **Supply chain.** Digests pinned in `plugin.json`, verified on fetch, re-verified by `doctor`. This mirrors the existing checksum-pin discipline for URL-sourced rules and hooks. Run a blocking `cargo audit` on the pinned lockfile, because upstream's is advisory. - **Trust boundary.** rtk rewrites commands before execution, which the retired in-house contract explicitly forbade for itself. Adopting it is a conscious reversal of that constraint and its own threat surface: what runs is not what the model asked for. The pack's README must say this plainly, and `plugin install` must not be silent about it. The README must also disclose the `rtk cc` npx path. The sharp edge is more specific than "the command changes". rtk evaluates the host's permission rules against the **original** command and applies the verdict to the **rewritten** one: `decide_hook_action(cmd, ...)` takes the command the model asked for, and `permissionDecision: allow` is then emitted for the substituted `rtk …` form (`hook_cmd.rs:405-436`). An allowlist entry for `git status` therefore authorises `rtk git status`, which the user never wrote a rule for. Upstream documents the absence case in its own test (`hook_cmd.rs:1272-1283`): with no matching allow rule, no decision is emitted and the normal prompt stands. Both halves belong in the README. ## 12. Pre-mortem | Rank | Failure mode | Probability | Impact | Mitigation | |---:|---|:---:|:---:|---| | — | ~~Presets install but are untrusted, so they silently do nothing~~ | — | — | **Eliminated**, not mitigated: section 7 is cut, so the pack ships no filters and takes no trust-store entry | | 2 | Cross-building with bundled SQLite is fragile in CI | Medium | Medium | Copy upstream's `cross` setup for aarch64-linux; five targets not six; a target that will not build is dropped, not faked | | 3 | Pre-execution rewriting changes command semantics | Medium | High | Opt-in pack, documented one-flag disable, upstream's own review process, our integration tests on real commands | | 4 | Windows binary cross-built without the 8 MiB stack reservation | Medium | High | Build natively on `windows-latest`, or pass the link-arg explicitly. `build.rs:6-13` | | 5 | Upstream velocity breaks the rewrite logic the coverage numbers model | Medium | Medium | Pinned version, SOP review step naming exact files, port re-validation as a gate, fork option preserved by Apache-2.0 | | 6 | Unpinned toolchain plus `warnings = "deny"` turns builds red with no change | Medium | Low | Pin an exact rustc version; treat bumps as deliberate | | 7 | A fetch failure leaves a half-installed pack | Medium | Medium | Verify-then-install, abort and clean on digest mismatch, `doctor` detects drift | | 8 | Live saving lands near the projection, not the claim | High | Medium | Section 10 measures it by replay either way, against a published kill number | | 9 | The auto-update wiring makes `update` slower or flakier | Low | Medium | Skip when versions match, warn-and-continue on failure, `--dry-run` coverage | | 10 | The pack drifts into being installed by default | Low | High | Adoption rule 1 plus a test asserting `install` never pulls it in | ## 13. Open questions 1. ~~Which upstream tag do we pin first?~~ **Answered: v0.44.0.** See section 2.1. 2. ~~User scope only, or a project-scoped preset variant too?~~ **Moot:** section 7 is cut, so the pack installs no presets at any scope. 3. ~~Is patching `pipeline_final_safe` for `head`/`tail` in scope later?~~ **Answered: no.** See section 2.6. 4. ~~Does Phase 3 survive its own measurement at 0.008% of input tokens?~~ **Answered: no, cut 2026-07-26.** See section 7. 5. ~~How does the pack share rtk's single global `filters.toml`?~~ **Moot:** it writes no filters at all. The marker-injection design and the trust constraints that would have applied are kept in section 6.1 in case presets are ever revisited. --- ## kb/history/completed/rtk-pack-retirement-20260727.md --- title: "Retirement: rtk-pack — Broken On Install, Removed One Day After Shipping" category: planning service: ai-toolkit tags: - rtk - plugin-pack - token-reduction - postmortem - measurement - release-process doc_type: postmortem status: completed created: "2026-07-27" last_updated: "2026-07-27" shipped_in: "v4.19.0 (removal)" description: "Why rtk-pack, shipped in v4.18.0, was removed in v4.19.0: the first real install proved every rewritten command failed with exit 127, the wrong architecture was fetched on Apple Silicon, and the pack's own status check reported both as green. Measured value was 0.0615% of input tokens, so neither defect was worth fixing." --- # Retirement: rtk-pack **Shipped:** v4.18.0 (2026-07-26). **Removed:** v4.19.0 (2026-07-27). The pack was installed on a maintainer's machine for the first time one day after release. It did not work, in the strongest sense available: it broke the shell. ## Defect 1: every rewritten command failed with exit 127 rtk emits its rewrite as a bare `rtk git status`. The pack installs its binary at `~/.softspark/ai-toolkit/plugin-scripts/rtk-pack/bin/rtk` and never puts that directory on `PATH` — deliberately, so a checksum-pinned binary cannot shadow anything system-wide. The two decisions are individually defensible and jointly fatal: the shell could not find `rtk`, so every command the hook touched died before running. Observed on the first three commands issued after install: | Command | Result | |---|---| | `cat ~/.softspark/ai-toolkit/plugins.json` | `command not found: rtk` | | `git --no-pager diff` | `command not found: rtk` | | `find ~/.softspark -path '*rtk*'` | `command not found: rtk` | The blast radius is every family in `rtk --help`: `git`, `ls`, `read`, `find`, `grep`, `rg`, `diff`, `docker`, `kubectl`, `npm`, `jest`, `tsc`. On the reference workload that is 35% of Bash bytes, which is the same 35% the pack was built to save. The mechanism that produced the benefit produced the outage. ## Defect 2: the Intel build on an Apple Silicon host `detect_platform()` trusted `platform.machine()`. The maintainer's `python3` is an Intel Homebrew build at `/usr/local/opt/python@3.14`, so it runs under Rosetta 2, where every architecture API inside the process reports `x86_64` — `platform.machine()`, `os.uname()` and `uname -m` alike. The pack fetched `rtk-x86_64-apple-darwin.tar.gz` onto an `arm64` machine and ran it emulated. `sysctl.proc_translated` answers the question that distinguishes the two cases and was not consulted. Note that CI had already met Rosetta on this project: commit `30614ca`, *"build x86_64-darwin on arm64 and verify it under Rosetta"*. The build pipeline knew. The install path did not. ## Defect 3: the pack's own health check called both of them green `plugin status` reported the binary present, the digest recorded, `runs: rtk 0.44.0`, the hook script present and the hook registered. All true, all useless. The pack's `status.py` was written specifically to *"distinguish installed from working"*, and it checked only the installed half. `kb/procedures/post-release-testing-sop.md`, written the day before the release, requires exactly the missing step: > Presence is not function. Drive the hook directly. The SOP was written and not run. Every defect above would have surfaced in its first five minutes. ## The number that made fixing it not worth it All three defects were fixed and tested before the removal decision: a `PATH` prefix on the emitted command, `sysctl.proc_translated` in the detector, and a status check that executes what the hook emits and looks for 127. Six tests, all passing, all failing against the previous code. The work was not hard. It was measured against this, from `rtk-pack-integration-20260726.md` §10.1: | | | |---|---:| | Measured saving | 1.44 MB = 360,529 tokens | | As a share of input tokens | **0.0615%** | | Kill number, published before the measurement | 0.05% | | Margin | ×1.23 | The integration plan's own verdict on that margin was *"a pass, not a vindication"*. A pack that survives its kill number by 23%, carries a supply-chain surface, an upstream-sync SOP, a cross-build workflow for five targets, and a hook that rewrites every command before it runs, is not worth three defect classes discovered on first contact. The cost side moved; the benefit side never did. ## Why the ceiling was always low, independent of any defect `Read` results are 62.8% of tool-result bytes on this traffic, and `rtk read` measures 0.0% — at its default `--level none` it returns files verbatim. Tool results are 4.54% of input token volume, so that is the arithmetic ceiling for any tool-output mechanism here, and rtk addresses 8.8% of it. Replayed against upstream's own claims, the families that can be measured deliver 25.5% in aggregate against a claimed 60–90%: | Family | Measured | Claimed | |---|---:|---:| | `rtk find` | 35.6% | 70% | | `rtk git` | 33.1% | 70% | | `rtk grep` | 22.3% | 75% | | `rtk rg` | 7.0% | 75% | | `rtk read` | 0.0% | 60% | Better engineering does not move any of this. The lever is in the wrong place. ## The process lesson The previous retirement in this series ([output-filter-retirement-20260726.md](output-filter-retirement-20260726.md)) concluded that premise validation must come first, with a kill number published before the measurement. rtk-pack did that, and did it well: Phase 0 ran on 1224 transcripts before any build work, the kill number was published in advance, and Phase 3 was cut on a measured 0.008%. It then shipped without anyone installing it. Measurement discipline and release discipline are different disciplines, and this project now has one clean failure of each. The first shipped a feature that worked and saved nothing. The second shipped a feature that would have saved something and did not work. The next plan of this shape needs both gates, and the second one is the cheap one: install the artifact, run the thing, look at what happens. ## What survives - **Multi-runtime pack hook wiring.** Packs write user-scope entries to `~/.cursor/hooks.json` and `~/.gemini/settings.json`, tagged per pack, with `plugin remove` taking only its own back out. Generic; no rtk in it. - **`supported_editors` in the manifest.** A pack declares the runtimes it works on instead of installing everywhere and silently doing nothing. - **Generic `plugin status` dispatch.** Any pack can ship `scripts/status.py`. The lesson attached: a status check must prove the working half by exercising it. - **Version-aware `plugin update`.** A pack whose manifest has not moved is a silent no-op. - **`audit_skills.py --ci` and the ShellCheck gate now cover `app/plugins/`.** - **[Post-Release Testing SOP](../../procedures/post-release-testing-sop.md).** Kept, and now carries the note that the one time it existed and was skipped, this happened. ## What was removed `app/plugins/rtk-pack/`, `.github/workflows/rtk-build.yml`, `scripts/verify_rtk_binary.py`, `tests/test_rtk_pack.bats`, `tests/test_verify_rtk_binary.bats`, `kb/procedures/rtk-upstream-sync-sop.md`, and the GitHub Release `softspark-rtk-v0.44.0-1` holding the five cross-built binaries. Anyone who installed the pack under v4.18.0 should run `ai-toolkit plugin remove rtk-pack`. With the release deleted, a fresh `plugin install rtk-pack` on v4.18.0 fails at the fetch and leaves the pack inert rather than half-installed, which is the degraded path the pack was designed for. --- ## kb/history/completed/tool-output-token-reduction-closed-20260727.md --- title: "Closed: Tool-Output Token Reduction — Three Attempts, One Ceiling" category: planning service: ai-toolkit tags: - token-reduction - measurement - postmortem - prompt-caching - context-window - closed-line-of-work doc_type: postmortem status: completed created: "2026-07-27" last_updated: "2026-07-27" description: "Closes the tool-output token-reduction line of work after a third measurement. Decomposes 1189 real sessions by cost: 84% is context being fed to the model, 14.7% is responses. Tool output is a small lever by construction and three independent attempts have now hit the same ceiling. Records what shipped (a 20.2% rag-mcp response trim, worth 0.49% of cost), what was killed by its own kill number, and the three measurement errors made on the way." --- # Closed: Tool-Output Token Reduction **Read this before proposing a fourth attempt.** Three independent efforts have tried to cut tokens by shrinking what tools return. All three were competently built. All three measured out near zero. | Attempt | Shipped | Measured saving | |---|---|---:| | [Native tool-output filter](output-filter-retirement-20260726.md) | v4.16.0, removed v4.17.0 | **0.0000%** | | [rtk-pack](rtk-pack-retirement-20260727.md) | v4.18.0, removed v4.19.0 | **0.0615%** | | This review's only clean win (rag-mcp response trim) | rag-mcp, 2026-07-27 | **0.49%** | This is not three unlucky implementations. It is one structural fact, measured three different ways. ## Where the money actually is 1189 sessions with traffic, 83,352 assistant turns, priced at Opus list rates: | Component | Share of cost | |---|---:| | `cache_read` | **63.1%** | | `cache_creation` | 20.9% | | `output` | 14.7% | | `input` (uncached) | 1.3% | **84% of cost is feeding context to the model.** Responses are 14.7%. That closes "make the model write less" as a serious lever — the whole `brand-voice` concise mode plays for a seventh of the bill. Cache hit rate is **97.4%**. Published guidance treats 80–95% as the achievable band, so there is nothing to win in cache tuning either. Session shape: median 29 turns (mean 70.1), median peak context 71,276 tokens (mean 114,712), median startup context 22,175 tokens. ## Why tool output cannot be the lever Decomposed by amplification — every tool result is re-read on every turn that follows it, so a result's true cost is its size times the turns remaining: | Category | Share of `cache_read` | |---|---:| | Fixed startup prelude | 12.4% | | `Read` results | 15.6% | | `Bash` results | 6.7% | | `rag-mcp` results | 3.8% | | Unattributed (assistant text, thinking, user messages, reminders) | ~61% | Every tool in the toolbox, amplified across every turn, is **26.1%** of `cache_read`, and it is not compressible without losing what it says. The original Phase 0 calculation reached the same place from the other direction: tool results are 4.54% of input-token volume, so 4.54% is the arithmetic ceiling for any mechanism operating on them. Raw tool-result bytes, 113 MB across 1308 session files: | Tool | Share of bytes | |---|---:| | `Read` | 62.2% | | `Bash` | 26.1% | | `rag-mcp` (3 tools) | 7.6% | | everything else | <2% each | ## What shipped **rag-mcp response compaction** — `compact_payload()` in `app/rag-mcp-server/routes/kb_search.py`, applied to `smart_query`, `hybrid_search_kb` and `get_document`, plus both sides of the smart_query cache so a hit and a miss return identical bytes. Verified by running the shipped function over 1356 real responses captured from session logs: **20.2% smaller, with no field the agent acts on removed.** That is 0.77% of `cache_read`, **0.49% of total cost**. It drops request echo (`use_hyde`/`use_crag`/`use_multi_hop`), null result columns, `_from_cache: false`, `total_documents_used` when it equals the result count, and the part of `source_documents_used` that merely repeats `results[].kb_id`. It deliberately keeps `file_path` (addresses Read/Edit, where `kb_id` addresses get_document), `routing` (the only signal for which pipeline ran), `score: 0.0`, and empty result sets. 16 unit tests, most of them asserting what must survive rather than what gets removed. ## What was killed, and by what A kill number was published before the work: *if the shipped changes do not cut `cache_read` by 3%, stop and do not proceed to the behavioural changes.* Result: **0.79%.** The threshold was not met, and the remaining items were not built. The kill number did its job — this is the first of the three attempts where it bound before code was written rather than after it shipped. ## Three measurement errors, and what they cost Recorded because each one nearly produced a wrong decision, and because two of them are the same class of error that produced the previous two failures. **1. Duplicate reads: 11.1% was actually 0.1%.** The first pass keyed duplicate detection on `file_path` alone, counting re-reads of *different ranges* of the same file as waste. Re-keyed on `(file_path, offset, limit)`: | Key | Duplicates | Bytes | Share of `Read` | |---|---:|---:|---:| | path only (wrong) | 1754 | 7,787,530 | 11.1% | | path + offset + limit (right) | **36** | **90,975** | **0.1%** | A whole planned deliverable — a session-scoped dedup hook, with a designed mitigation for the post-compaction re-read hazard — rested on that 11.1%. It does not exist. **2. A 58% "trim" that was deleting document content.** An aggressive variant of the rag-mcp compaction measured 58% smaller. It was whitelisting top-level keys and thereby dropping `content` — which for `get_document` *is* the document, 30.5% of all bytes those endpoints return. Not a trim; data loss that looked like a win. The honest figure is 20.2%. **3. "77.5% of the rag-mcp response is overhead."** Roughly half of that non-content mass is `kb_id`, `file_path`, `title` and `score` — fields the agent uses. Removable overhead is about 20%, not 77%. The common thread: **every one of these errors made the opportunity look bigger than it is, and every one was caught only by decomposing before building.** The filter retirement drew the same conclusion about premise validation; rtk-pack drew it about installing the artifact. This adds a third: decompose the metric before trusting its headline. ## What is left, and why it was not taken | Option | Value | Why not | |---|---:|---| | Model routing | tens of % | Ruled out by the maintainer — Opus 5 stays | | Trim the toolkit's own startup prelude | ~2.5% of cost | Ruled out — costs skill/agent discoverability | | `Read` with ranges instead of whole files | up to 10.4% of `cache_read` | The only remaining item of size, and **not quality-neutral**: it is a behavioural change whose effect on correctness cannot be measured automatically | | Unattributed ~61% of `cache_read` | unknown | Assistant text, thinking blocks, user messages, system reminders — no clean cut available | For the record, the startup prelude was measured rather than guessed. Median startup context is 22,175 tokens against ~7,850 in the documented reference shape, and the gap is the toolkit's own: | Component | Count | ~tokens | |---|---:|---:| | skill descriptions | 108 | 5,051 | | agent descriptions | 44 | 2,750 | | project rules | 5 | 3,802 | | global rules | 6 | 3,133 | | `CLAUDE.md` files | 3 | 2,338 | | **total** | | **17,076** | The tool built to reduce tokens is the single largest addition to every session's context. That is worth knowing, and it is still only 2.5% of cost, because the prelude is 12.4% of `cache_read` and only part of it is removable. ## The rule this leaves behind **Do not open a fourth tool-output token-reduction effort without first producing a measurement that beats 4.54%.** That is the ceiling on this workload and it has now been approached from three directions. Any proposal in this space must state, before any code, which share of *input token volume* it addresses — not which share of tool output, not which share of Bash bytes. If token cost genuinely needs to come down, the levers that are actually large are model selection and turn count. Both are policy decisions, not engineering projects, and neither is in this line of work. ## Related - [Output Filter Retirement](output-filter-retirement-20260726.md) — attempt one, 0.0000% - [rtk-pack Retirement](rtk-pack-retirement-20260727.md) — attempt two, 0.0615% - [rtk Pack Integration](rtk-pack-integration-20260726.md) — the Phase 0 ceiling calculation - [MCP Context Trim v4.0 — abandoned](mcp-context-trim-v4-prd-obsoleted-20260727.md) — a fourth idea in this space, killed by the platform shipping the fix first - [Output & Token Discipline](output-token-discipline-plan-20260504.md) — the plan all of this descends from --- ## kb/howto/README.md --- title: "How-To Guides" service: ai-toolkit category: howto tags: [howto, guides] last_updated: "2026-03-25" --- # How-To Guides Step-by-step guides for common tasks. Guides will be added here as they are created. --- ## kb/planning/cloud-security-pack-plan.md --- title: "Plan: Cloud Security Pack — Multi-Cloud Audit (GCP/AWS/Azure)" category: planning service: ai-toolkit tags: - cloud-security - gcp - aws - azure - firebase - plugin-pack - security-audit - credentials doc_type: plan status: proposed created: "2026-04-10" last_updated: "2026-04-10" completion: "0%" council_review: "2026-04-10 — conditional FOR, scope reduction recommended" description: "Plugin pack for deterministic, read-only security auditing of GCP (Firebase/Cloud Functions), AWS (S3/Lambda/IAM), and Azure (NSG/Functions/CosmosDB). Includes CLI credential management, static+live modes, false positive resolution, SARIF output, incremental scanning, and CI integration." --- # Plan: Cloud Security Pack — Multi-Cloud Audit **Status:** Proposed **Completion:** 0% **Created:** 2026-04-10 **Origin:** Firebase RTDB/Firestore rules audit, Cloud Functions public exposure, false positive resolution for App Check/Gateway patterns **Estimated Effort:** 5-6 weeks (council-revised from original 3-4 weeks) --- ## 1. Objective Create `cloud-security-pack` plugin pack that provides deterministic, read-only security auditing for three major cloud providers. All scripts are stdlib-only Python with zero external dependencies. The pack includes a dedicated agent, multiple scan scripts, CLI credential management, and CI pipeline integration. **Key design principles:** - **Read-only** — never modifies cloud resources, only reads state - **Deterministic** — reproducible results, no LLM-driven regex (same pattern as `hipaa_scan.py`) - **False positive aware** — context graph resolves "public endpoint behind gateway/App Check/WAF" - **CI-ready** — `--output json` + `--output sarif` (SARIF v2.1.0 for GitHub Advanced Security), exit code 1 on HIGH, 0 otherwise - **Credential isolation** — keys stored in `~/.softspark/ai-toolkit/credentials/`, accessible only by this pack's scripts - **Static-first** — static mode (no credentials) is the default, live mode is opt-in upgrade - **Incremental** — `--changed` flag scans only files modified since last commit (PR workflow) - **IaC via `terraform show -json`** — wraps Terraform's own JSON output instead of parsing HCL directly --- ## 2. Architecture Overview ``` ai-toolkit credentials add gcp --file ~/sa-viewer.json ai-toolkit credentials add aws --profile my-audit-profile ai-toolkit credentials add azure --subscription abc-123 ┌──────────────────────────────────────────────────────┐ │ cloud-security-pack │ │ │ │ Agent: cloud-security-auditor │ │ Tools: Read, Grep, Glob, Bash (read-only commands) │ │ │ │ Skills: │ │ /cloud-security-audit (orchestrator) │ │ /firebase-rules-audit (GCP: rules) │ │ /cloud-functions-audit (GCP: CF + IAM) │ │ /aws-security-audit (AWS: S3/Lambda) │ │ /azure-security-audit (Azure: NSG/Fn) │ │ │ │ Scripts (stdlib Python, zero deps): │ │ gcp_auth.py (credential helper)│ │ firebase_rules_scan.py (static parser) │ │ cloud_functions_audit.py (CF IAM + context) │ │ aws_security_scan.py (S3/Lambda/IAM) │ │ azure_security_scan.py (NSG/Fn/RBAC) │ │ false_positive_resolver.py (context graph) │ │ sarif_formatter.py (SARIF v2.1.0) │ │ incremental.py (git diff filter) │ │ │ │ Modes: │ │ --static (no credentials, parse IaC/source) │ │ --live (credentials, deployed state) │ │ --output json|sarif (CI pipeline) │ │ --changed (incremental, static only) │ │ --explain (remediation lookup) │ └──────────────────────────────────────────────────────┘ ### YAML Parsing Constraint (BLOCKER) Python stdlib has NO YAML parser. This affects AWS static mode: - CloudFormation templates (`.yaml`) — YAML - `serverless.yml` — YAML - SAM templates (`template.yaml`) — YAML **Decision: JSON-only for static IaC parsing.** Rationale: 1. CloudFormation supports both JSON and YAML — JSON variant parseable with `json` module 2. `terraform show -json` outputs JSON — the primary Terraform path 3. `serverless.yml` → recommend users run `sls print --format json` to convert 4. Adding `pyyaml` breaks the stdlib-only constraint for the entire toolkit **Practical impact:** ~30% of CloudFormation users use JSON, ~70% YAML. For YAML users, live mode (`--mode live`) still works (queries APIs directly, no file parsing needed). The `--explain` output will suggest `sls print --format json` conversion. **v2 option:** Ship a vendored minimal YAML subset parser (~150 LOC, handles flat key-value and simple nested maps — enough for security-relevant fields like `Principal`, `Effect`, `authLevel`). ``` --- ## 3. Progress Tracking | # | Feature | Priority | Status | Est. Time | Notes | |---|---------|----------|--------|-----------|-------| | 1.1 | CLI `credentials` command (add/list/remove/test) | P0 | Proposed | 2d | 0600 perms, allowlist wrapper | | 1.1b | `credentials init` interactive wizard | P1 | Proposed | 1.5d | **Deferred to Milestone 2** (orchestration-review: saves 1.5d in M1 critical path) | | 1.2 | `cloud-security-auditor` agent | P0 | Proposed | 1d | Agent definition | | 1.3 | SARIF + incremental scan infrastructure | P0 | Proposed | 2d | `--output sarif`, `--changed` flag | | 2.1 | `firebase-rules-audit` skill + script | P0 | Proposed | 4-5d | Recursive descent parser (orchestration-review: +1d vs regex) | | 2.2 | `cloud-functions-audit` skill + script | P0 | Proposed | 3-4d | CF IAM + App Check context | | 2.3 | False positive resolver (GCP context) | P0 | Proposed | 3-4d | Context graph engine — GCP only (~8 code paths). **+2d per provider** in later milestones (orchestration-review) | | 3.1 | `aws-security-audit` skill + script | P1 | Proposed | 4-5d | S3/Lambda/IAM/SG, `terraform show -json` | | 3.2 | `/cloud-security-audit` orchestrator + plugin.json | P1 | Proposed | 3d | Multi-provider orchestration + pack | | 4.1 | `azure-security-audit` skill + script | P1 | Proposed | 4-5d | NSG/Functions/RBAC | | 5.1 | Tests + CI integration docs | P1 | Proposed | 3d | Tests + SARIF + pipeline examples | | 5.2 | Documentation (kb/) | P2 | Proposed | 1d | Checklists, patterns, KB | **Phasing (full delivery, all 3 providers):** - **Phase 1 (week 1-2):** Foundation + GCP — credentials CLI, agent, SARIF/incremental infra, firebase-rules, cloud-functions, false positive resolver (GCP context) - **Phase 2 (week 3-4):** AWS + orchestrator — AWS security audit, `terraform show -json`, orchestrator skill, plugin pack, `credentials init` - **Phase 3 (week 5-6):** Azure + polish — Azure security audit, false positive resolver (Azure context), full test suite, documentation --- ## 4. Dependency Graph ``` Phase 1: Foundation + GCP (week 1-2) ==================================== credentials CLI (1.1) ─────┐ ├──► firebase-rules-audit (2.1) SARIF + incremental (1.3) ──┤ ├──► cloud-functions-audit (2.2) ──► false-positive-resolver (2.3) agent definition (1.2) ─────┘ Phase 2: AWS + Orchestrator (week 3-4) ====================================== credentials init (1.1b) ───┐ ├──► aws-security-audit (3.1) ─────► false-positive-resolver (+AWS context) └──► orchestrator skill + plugin.json (3.2) Phase 3: Azure + Polish (week 5-6) ================================== ├──► azure-security-audit (4.1) ───► false-positive-resolver (+Azure context) └──► tests + docs (5.1, 5.2) ``` **All 3 providers ship.** No conditional gates — full delivery in 6 weeks. --- ## 5. Detailed Implementation ### Faza 1: Foundation (tydzien 1) #### 1.1 CLI `credentials` Command **Purpose:** Secure credential storage for cloud provider API access. Credentials live outside any project directory and are only accessible by this pack's scripts. **CLI interface:** ```bash # GCP — Service Account JSON ai-toolkit credentials add gcp --file ~/sa-viewer.json ai-toolkit credentials add gcp --file ~/sa-viewer.json --project my-project-id # GCP — use existing gcloud session (no file needed) ai-toolkit credentials add gcp --gcloud --project my-project-id # AWS — named profile (reads from ~/.aws/credentials) ai-toolkit credentials add aws --profile audit-readonly ai-toolkit credentials add aws --profile audit-readonly --region eu-west-1 # AWS — explicit keys (interactive, never on CLI args) ai-toolkit credentials add aws --interactive # Azure — subscription ai-toolkit credentials add azure --subscription abc-123-def # Azure — use existing az login session ai-toolkit credentials add azure --az-cli # Interactive guided setup (reduces onboarding from 4 steps to 1) ai-toolkit credentials init # auto-detect provider, interactive wizard ai-toolkit credentials init --provider gcp # skip auto-detect, go straight to GCP setup # Management ai-toolkit credentials list ai-toolkit credentials remove gcp ai-toolkit credentials remove aws ai-toolkit credentials remove azure ai-toolkit credentials test gcp # verify read-only access works ai-toolkit credentials test aws ``` **Storage structure:** ``` ~/.softspark/ai-toolkit/ credentials/ gcp.json # SA key file (copied, chmod 0600) gcp.meta.json # { project_id, added_at, method: "file"|"gcloud" } aws.json # { profile, region, method: "profile"|"keys" } azure.json # { subscription_id, method: "subscription"|"az-cli" } ``` **`credentials init` interactive flow:** 1. Auto-detect providers from project files (`firebase.json` → GCP, `*.tf` with `provider "aws"` → AWS, etc.) 2. If multiple detected → ask user: "Found GCP and AWS markers. Which provider to configure first? [gcp/aws/both]" 3. Per provider: - GCP: "Do you have a Service Account JSON file? [y/n]" → if yes: ask path → if no: "Run `gcloud auth application-default login` and we'll use that" - AWS: "Do you have a named profile in ~/.aws/credentials? [y/n]" → if yes: ask profile name → if no: "Run `aws configure` first" - Azure: "Do you have an active `az login` session? [y/n]" → if yes: ask subscription ID → if no: "Run `az login` first" 4. Run `credentials test` automatically after setup 5. Generate `.cloud-security.json` scaffold with detected context **Security requirements:** - All credential files: `chmod 0600` (owner read/write only) - Never log credential contents to stdout/stderr - `credentials test` validates: - Connection works (GCP: `gcloud auth list`, AWS: `aws sts get-caller-identity`, Azure: `az account show`) - SA/role has **only read permissions** — **REFUSE to store** if write access detected (orchestration-review: warn-only is ignored by users). Override: `--force` flag with explicit acknowledgment - Project/subscription exists - `.gitignore`-proof — lives in `~/.softspark/ai-toolkit/`, never in project directory - Scripts access credentials via `gcp_auth.py` helper — single entry point, no direct file reads **Files to create/modify:** | File | Action | Description | |------|--------|-------------| | `scripts/credentials_cli.py` | CREATE | CLI: add/list/remove/test credentials | | `bin/ai-toolkit.js` | EDIT | Register `credentials` subcommand | | `tests/test_credentials.bats` | CREATE | Tests: add, remove, permissions, test | **Success Criteria:** - [ ] `credentials add gcp --file` copies and secures SA key - [ ] `credentials add aws --profile` stores profile reference - [ ] `credentials add azure --subscription` stores subscription - [ ] `credentials test` validates read access + warns on write perms - [ ] `credentials list` shows providers without exposing secrets - [ ] `credentials remove` cleans up securely - [ ] All files created with 0600 permissions - [ ] Tests: >= 8 --- #### 1.3 SARIF Output + Incremental Scanning (Council addition) **SARIF v2.1.0 output** — industry standard consumed by GitHub Advanced Security (inline PR annotations), VS Code SARIF Viewer, Azure DevOps, and SonarQube. ```bash /cloud-security-audit --output sarif > results.sarif # GitHub Actions: upload SARIF for inline PR annotations - uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif ``` **SARIF structure (per finding):** ```json { "$schema": "https://raw.githubusercontent.com/oasis-tcs/sarif-spec/main/sarif-2.1/schema/sarif-schema-2.1.0.json", "version": "2.1.0", "runs": [{ "tool": { "driver": { "name": "cloud-security-audit", "version": "1.0.0", "rules": [{ "id": "GCP-CF-001", "shortDescription": { "text": "Public Cloud Function invoker" }, "helpUri": "https://cloud.google.com/functions/docs/securing" }] } }, "results": [{ "ruleId": "GCP-CF-001", "level": "error", "message": { "text": "Cloud Function 'adminEndpoint' has allUsers invoker with no protection layer" }, "locations": [{ "physicalLocation": { "artifactLocation": { "uri": "functions/src/admin.ts" }, "region": { "startLine": 42 } } }], "properties": { "resolved_severity": "HIGH", "context_chain": [], "provider": "gcp" } }] }] } ``` **Incremental scan mode** — scan only changed files since last commit: ```bash /cloud-security-audit --changed # files changed vs HEAD~1 /cloud-security-audit --changed HEAD~5 # files changed in last 5 commits /cloud-security-audit --changed main # files changed vs main branch (PR workflow) ``` Implementation: use `git diff --name-only ` to get changed files, filter to relevant extensions (.rules, .tf, .json, .ts, .py, .bicep), scan only those. Falls back to full scan if no git repo detected. **`--changed` applies to static mode ONLY.** Live mode always scans all deployed resources (it queries cloud APIs, not files). If user passes `--changed` with `--mode live`, emit warning: "Incremental scan applies to static analysis only. Live mode will scan all resources." and proceed with full live scan. **`--explain` flag** — detailed remediation for a specific finding: ```bash /cloud-security-audit --explain GCP-CF-001 # Output: what the finding means, why it matters, exact fix steps, # links to GCP documentation, CIS Benchmark reference ``` **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/cloud-security-audit/scripts/sarif_formatter.py` | CREATE | SARIF v2.1.0 output | | `app/skills/cloud-security-audit/scripts/incremental.py` | CREATE | Git diff + file filter | | `app/skills/cloud-security-audit/reference/rule-explanations.json` | CREATE | Per-rule remediation guides | **Success Criteria:** - [ ] `--output sarif` produces valid SARIF v2.1.0 JSON with `driver.rules[]` array (orchestration-review: GitHub silently drops annotations without rule metadata) - [ ] SARIF upload to GitHub Advanced Security works (inline PR annotations) - [ ] SARIF `level` mapping: HIGH→`error`, WARN→`warning`, INFO→`note` - [ ] `--changed main` scans only PR-changed files - [ ] `--explain ` shows detailed remediation - [ ] Tests: >= 6 --- #### 1.2 Agent Definition: `cloud-security-auditor` **File:** `app/agents/cloud-security-auditor.md` ```markdown --- name: cloud-security-auditor description: "Multi-cloud security auditor (GCP/AWS/Azure). Read-only deterministic scans for IAM, network, storage, serverless, and compliance. False positive resolution via security context graph." model: opus color: red tools: Read, Grep, Glob, Bash skills: security-patterns, cloud-security-audit --- # Cloud Security Auditor Agent You are the **Cloud Security Auditor**. You perform read-only security assessments across GCP, AWS, and Azure. You never modify cloud resources. ## Core Philosophy **"Read everything, change nothing. Context before verdict."** ## Mandatory Protocol Before any audit: 1. Check credentials: `ai-toolkit credentials test ` 2. Determine mode: `--static` (IaC/source only) or `--live` (deployed state) 3. Run deterministic scripts first, then interpret results ## Responsibilities ### 1. Static Analysis (no credentials needed) - Parse IaC: Terraform (.tf), CloudFormation (.yaml/.json), Bicep (.bicep) - Parse Firebase rules: firestore.rules, database.rules.json - Parse source: Cloud Functions (onCall vs onRequest), Lambda handlers, Azure Functions - Parse configs: firebase.json, serverless.yml, sam-template.yaml ### 2. Live Analysis (credentials required, READ-ONLY) - GCP: `gcloud` CLI commands (list, describe, get-iam-policy) - AWS: `aws` CLI commands (s3api, lambda, iam, ec2 — get/list/describe only) - Azure: `az` CLI commands (network nsg, functionapp, cosmosdb — list/show only) ### 3. False Positive Resolution Build security context graph before rendering verdict: - Public endpoint → check: API Gateway? WAF? App Check? CDN? - Open port → check: behind Load Balancer? VPN? private subnet? - Broad IAM role → check: scoped to specific resource? temporary? ## Allowed CLI Commands (WHITELIST — read-only only) ### GCP - gcloud functions list / describe / get-iam-policy - gcloud projects get-iam-policy - gcloud app-check services list - gcloud firestore indexes list - gcloud compute firewall-rules list - gcloud run services list / describe - firebase apps:list ### AWS - aws s3api get-bucket-policy / get-bucket-acl / get-public-access-block - aws lambda get-policy / get-function-configuration / list-functions - aws iam list-roles / list-policies / get-role / get-policy-version - aws ec2 describe-security-groups / describe-network-acls - aws apigateway get-rest-apis / get-resources - aws elbv2 describe-load-balancers / describe-listeners ### Azure - az network nsg list / show / rule list - az functionapp list / show / config show - az cosmosdb list / show / keys list - az role assignment list - az webapp show / config show - az network application-gateway list ### NEVER ALLOWED - Any create/update/delete/put/set/deploy/push command - Any command that modifies state - `gcloud auth activate-service-account` (credential pivot) - `aws sts assume-role` (lateral movement) - `az login` (session hijack) - `terraform apply` / `terraform destroy` ## Output Format (see skill SKILL.md for detailed format) ``` **SECURITY: Programmatic Bash Allowlist (orchestration-review P0 BLOCKER)** The read-only whitelist above lives in the agent's system prompt. An LLM can be prompt-injected via malicious IaC files or source code comments. **The prompt-only approach is insufficient.** **Required:** A shell wrapper script (`scripts/cloud_security_allowlist.sh`) that validates every CLI invocation against a compiled allowlist regex BEFORE execution, independent of the LLM: ```bash #!/bin/bash # cloud_security_allowlist.sh — wraps Bash calls from cloud-security-auditor # Rejects any command not matching read-only patterns ALLOWED_PATTERNS=( '^gcloud (functions|projects|app-check|firestore|compute|run) (list|describe|get-iam-policy|indexes)' '^gcloud auth list$' '^firebase apps:list' '^aws (s3api|lambda|iam|ec2|apigateway|elbv2|sts|cloudfront) (get-|list-|describe-|generate-credential-report)' '^aws sts get-caller-identity$' '^az (network|functionapp|cosmosdb|role|webapp|storage) (list|show|rule list|config show|assignment list|account show)' '^az account show$' '^terraform show -json' '^git diff --name-only' '^python3 .*/scripts/.*\.(py)$' ) CMD="$*" for pattern in "${ALLOWED_PATTERNS[@]}"; do if [[ "$CMD" =~ $pattern ]]; then exec $CMD fi done echo "BLOCKED: Command not in read-only allowlist: $CMD" >&2 exit 1 ``` The agent's `allowed-tools` in SKILL.md references this wrapper instead of raw Bash. All cloud CLI calls go through the allowlist. **`terraform plan` risk (orchestration-review):** `terraform plan -json` executes providers and provisioners. A malicious `.tf` file with `local-exec` provisioner runs arbitrary code during plan. **Decision: use `terraform show -json` ONLY (reads existing state), NOT `terraform plan -json`.** Document this prominently. **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/cloud-security-audit/scripts/cloud_security_allowlist.sh` | CREATE | Bash allowlist wrapper | **Success Criteria:** - [ ] Agent file created in `app/agents/` - [ ] Read-only command whitelist documented - [ ] NEVER ALLOWED section explicit - [ ] Programmatic Bash allowlist enforced (not just prompt) - [ ] `terraform plan` excluded — only `terraform show -json` allowed - [ ] Allowlist tested: blocked commands return exit 1 --- ### Faza 2: GCP / Firebase (Phase 1, tydzien 1-2) #### 2.1 Skill: `firebase-rules-audit` **Purpose:** Static analysis of Firestore rules and RTDB rules. No credentials needed. **What it scans:** | Check | Severity | Description | |-------|----------|-------------| | `allow read, write: if true` | HIGH | World-readable/writable collection | | `allow read: if true` without `write` guard | WARN | Public read — may be intentional | | `allow write: if request.auth != null` without field validation | WARN | Authenticated but no field-level validation | | Missing `request.resource.data` validation on writes | WARN | No schema enforcement | | Wildcard collection `/{document=**}` with broad permissions | HIGH | Recursive wildcard + open access | | RTDB `.read: true` or `.write: true` at root | HIGH | Entire database public | | RTDB `.read: "auth != null"` without path scoping | WARN | All authenticated users can read everything | | Timestamp/TTL rules missing for sensitive collections | WARN | No data lifecycle enforcement | | `get()` / `exists()` cross-collection reads without auth check | WARN | Privilege escalation via rule chaining | | Rules file > 256KB (approaching Firebase limit) | WARN | May hit deployment limit | **Script:** `scripts/firebase_rules_scan.py` - Parses `firestore.rules` via **recursive descent parser** (not regex — orchestration-review P1) - Grammar: ~8 production rules (service, match, allow, function, condition) - Tracks: brace depth, current match path, accumulated allow blocks - Handles: nested `match` blocks, multi-line conditions with `&&`/`||`, custom `function` declarations - Estimated: 550-650 LOC for parser + check logic - Unsupported (documented): CEL ternary expressions, complex `get()`/`exists()` chains with computed paths - Parses `database.rules.json` (JSON rules — stdlib `json` module) - Outputs findings as JSON or text - Exit code 1 on HIGH, 0 otherwise - Supports `.cloud-security-ignore` for suppressions **Reference file:** `reference/firebase-rules-patterns.md` — safe/unsafe patterns with examples **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/firebase-rules-audit/SKILL.md` | CREATE | Skill definition | | `app/skills/firebase-rules-audit/scripts/firebase_rules_scan.py` | CREATE | Deterministic scanner | | `app/skills/firebase-rules-audit/reference/firebase-rules-patterns.md` | CREATE | Safe/unsafe patterns | **Success Criteria:** - [ ] Parses `firestore.rules` — detects 10+ check patterns - [ ] Parses `database.rules.json` — detects root-level open access - [ ] `--output json` for CI - [ ] `.cloud-security-ignore` support - [ ] Tests: >= 10 (one per check pattern + edge cases) --- #### 2.2 Skill: `cloud-functions-audit` **Purpose:** Audit Cloud Functions permissions and detect false positives. **Static mode (no credentials):** | Check | Severity | Description | |-------|----------|-------------| | `onRequest` handler without auth middleware | WARN | Potentially public — needs context | | `onCall` handler (inherently authenticated) | INFO | Informational — callable is auth'd | | Hardcoded API keys / secrets in source | HIGH | Secrets in code | | CORS `origin: '*'` in CF handler | WARN | Unrestricted CORS | | Missing rate limiting patterns | WARN | No throttling on public endpoint | | `functions.https.onRequest` + no `validateFirebaseIdToken` | WARN | HTTP function without Firebase Auth check | **Live mode (credentials required):** | Check | Severity | CLI Command | Description | |-------|----------|-------------|-------------| | `allUsers` invoker on CF | CONTEXT | `gcloud functions get-iam-policy` | Public — resolve with context graph | | `allAuthenticatedUsers` invoker | WARN | `gcloud functions get-iam-policy` | Any Google account can invoke | | App Check enforcement status | CONTEXT | `gcloud app-check services list` | Feeds into false positive resolution | | Deployed rules vs local rules diff | WARN | `gcloud firestore indexes` + local | Rules drift detection | | Cloud Run public ingress | CONTEXT | `gcloud run services describe` | Public — resolve with context graph | | Overly broad IAM roles on SA | HIGH | `gcloud projects get-iam-policy` | CF service account with editor/owner | **Script:** `scripts/cloud_functions_audit.py` **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/cloud-functions-audit/SKILL.md` | CREATE | Skill definition | | `app/skills/cloud-functions-audit/scripts/cloud_functions_audit.py` | CREATE | Scanner | | `app/skills/cloud-functions-audit/scripts/gcp_auth.py` | CREATE | Credential loader | | `app/skills/cloud-functions-audit/reference/false-positives-gcp.md` | CREATE | False positive patterns | **Success Criteria:** - [ ] Static: parses CF source for auth patterns - [ ] Live: checks IAM bindings via `gcloud` - [ ] False positive resolution for App Check + Gateway patterns - [ ] Tests: >= 8 --- #### 2.3 False Positive Resolver **Purpose:** Central engine that resolves "is this actually a problem?" by building a security context graph. **How it works:** ``` Input: Finding { resource, severity, type } Step 1: Gather context ├── Check API Gateway routes (firebase.json rewrites, API Gateway configs) ├── Check WAF/CDN (Cloudflare, CloudFront, Azure Front Door) ├── Check App Check / AppArmor / Shield ├── Check callable vs HTTP function type ├── Check VPC / private subnet placement └── Check Load Balancer + auth middleware Step 2: Apply resolution rules ├── Public CF + App Check ENFORCED → SUPPRESSED (protected) ├── Public CF + API Gateway route → SUPPRESSED (gateway handles auth) ├── Public CF + onCall() → SUPPRESSED (callable is auth'd by SDK) ├── Public S3 + CloudFront OAI → SUPPRESSED (not directly accessible) ├── Open SG port + ALB → SUPPRESSED (ALB handles TLS + auth) ├── Open NSG + Application Gateway → SUPPRESSED (WAF handles filtering) └── No context found → KEEP ORIGINAL SEVERITY Step 3: Output ├── Original severity ├── Resolved severity (SUPPRESSED / DOWNGRADED / CONFIRMED) ├── Context chain (what protections were found) └── Confidence (high if multiple protections, low if single) ``` **Output example:** ```json { "resource": "processPayment", "provider": "gcp", "type": "cloud-function-public-invoker", "original_severity": "HIGH", "resolved_severity": "SUPPRESSED", "confidence": "high", "context_chain": [ { "layer": "app_check", "status": "ENFORCED", "source": "gcloud app-check services list" }, { "layer": "function_type", "status": "onCall", "source": "source:index.ts:42" }, { "layer": "api_gateway", "status": "ROUTED", "source": "firebase.json:rewrites" } ], "verdict": "3/3 protection layers active. Suppressing finding." } ``` **Script:** `scripts/false_positive_resolver.py` **Resolution rules stored in:** `reference/resolution-rules.json` ```json { "rules": [ { "id": "gcp-cf-appcheck", "finding_type": "cloud-function-public-invoker", "provider": "gcp", "context_required": ["app_check:ENFORCED"], "action": "SUPPRESS", "reason": "App Check enforced — only verified app instances can invoke" }, { "id": "gcp-cf-callable", "finding_type": "cloud-function-public-invoker", "provider": "gcp", "context_required": ["function_type:onCall"], "action": "SUPPRESS", "reason": "onCall functions require Firebase Auth token from client SDK" }, { "id": "aws-s3-cloudfront-oai", "finding_type": "s3-bucket-public-access", "provider": "aws", "context_required": ["cloudfront_oai:ACTIVE"], "action": "SUPPRESS", "reason": "Bucket accessed only via CloudFront Origin Access Identity" }, { "id": "azure-func-apigw", "finding_type": "function-app-public", "provider": "azure", "context_required": ["application_gateway:ACTIVE"], "action": "SUPPRESS", "reason": "Function behind Application Gateway with WAF" } ] } ``` **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/cloud-security-audit/scripts/false_positive_resolver.py` | CREATE | Context graph engine | | `app/skills/cloud-security-audit/reference/resolution-rules.json` | CREATE | Configurable rules | **Success Criteria:** - [ ] Resolves GCP: App Check, Gateway, callable patterns - [ ] Resolves AWS: CloudFront OAI, ALB, WAF patterns - [ ] Resolves Azure: App Gateway, Front Door, VNET patterns - [ ] JSON output with context chain - [ ] User can add custom rules to `.cloud-security.json` `context` section - [ ] Tests: >= 12 (4 per provider) --- ### Faza 3: AWS (Phase 2, tydzien 3-4) #### 3.1 Skill: `aws-security-audit` **Static mode (IaC parsing):** | Check | Severity | Source | Description | |-------|----------|--------|-------------| | S3 bucket `"Effect": "Allow", "Principal": "*"` | HIGH | .tf / .json | Public bucket policy | | S3 `BlockPublicAccess` all false | HIGH | .tf / .json | Public access not blocked | | Lambda `resource-based policy` with `Principal: "*"` | HIGH | .tf / .json | Public Lambda | | Security Group `0.0.0.0/0` ingress on non-80/443 | HIGH | .tf / .json | Open port to world | | IAM policy with `Action: "*"` | HIGH | .tf / .json | God-mode IAM | | IAM policy with `Resource: "*"` + sensitive actions | WARN | .tf / .json | Broad resource scope | | Unencrypted RDS/DynamoDB | WARN | .tf / .json | Missing encryption at rest | | CloudTrail disabled | HIGH | .tf / .json | No audit logging | | Missing VPC Flow Logs | WARN | .tf / .json | No network monitoring | **Live mode:** | Check | CLI Command | Description | |-------|-------------|-------------| | S3 public buckets | `aws s3api get-public-access-block` | Per-bucket public access | | Lambda public policies | `aws lambda get-policy` | Resource-based policies | | Open Security Groups | `aws ec2 describe-security-groups` | Ingress from 0.0.0.0/0 | | Overly permissive IAM | `aws iam list-roles` + `get-role` | Roles with admin/broad access | | Unused IAM credentials | `aws iam generate-credential-report` | Stale access keys | | API Gateway without auth | `aws apigateway get-rest-apis` | Endpoints without authorizer | **Script:** `scripts/aws_security_scan.py` **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/aws-security-audit/SKILL.md` | CREATE | Skill definition | | `app/skills/aws-security-audit/scripts/aws_security_scan.py` | CREATE | Scanner | | `app/skills/aws-security-audit/scripts/aws_auth.py` | CREATE | Credential loader | | `app/skills/aws-security-audit/reference/aws-security-checklist.md` | CREATE | CIS Benchmark mapping | | `app/skills/aws-security-audit/reference/false-positives-aws.md` | CREATE | ALB/CloudFront/WAF patterns | **Terraform approach (council revision):** Do NOT parse HCL directly (stdlib-only Python cannot handle heredocs, variable interpolation, modules, `for_each`). Instead wrap `terraform show -json` / `terraform plan -json` which outputs clean JSON. Fallback to flat-resource regex for projects without `terraform` CLI. **Success Criteria:** - [ ] Static: `terraform show -json` wrapper + CloudFormation/SAM JSON parsing - [ ] Live: checks S3, Lambda, IAM, SG via `aws` CLI - [ ] False positive resolution for CloudFront/ALB/WAF - [ ] CIS Benchmark mapping in reference - [ ] Tests: >= 10 --- ### Faza 4: Azure (Phase 3, tydzien 5-6) #### 4.1 Skill: `azure-security-audit` **Static mode (IaC parsing):** | Check | Severity | Source | Description | |-------|----------|--------|-------------| | NSG rule `0.0.0.0/0` source on management ports | HIGH | .tf / .bicep | Open RDP/SSH to world | | Function App `authLevel: "anonymous"` | WARN | source / .tf | Public Azure Function | | Cosmos DB `publicNetworkAccess: enabled` | WARN | .tf / .bicep | Public database access | | Storage Account `allowBlobPublicAccess: true` | HIGH | .tf / .bicep | Public blob storage | | Missing Key Vault references (hardcoded secrets) | HIGH | source | Secrets not in Key Vault | | Missing RBAC (classic co-admin model) | WARN | .tf | Legacy access model | **Live mode:** | Check | CLI Command | Description | |-------|-------------|-------------| | Open NSG rules | `az network nsg rule list` | Broad inbound rules | | Function App auth | `az functionapp show` + `config` | Auth level and provider | | Cosmos DB access | `az cosmosdb show` | Network access settings | | RBAC assignments | `az role assignment list` | Owner/Contributor sprawl | | Storage public access | `az storage account show` | Blob public access | | App Service auth | `az webapp auth show` | Auth configuration | **Script:** `scripts/azure_security_scan.py` **Files:** | File | Action | Description | |------|--------|-------------| | `app/skills/azure-security-audit/SKILL.md` | CREATE | Skill definition | | `app/skills/azure-security-audit/scripts/azure_security_scan.py` | CREATE | Scanner | | `app/skills/azure-security-audit/scripts/azure_auth.py` | CREATE | Credential loader | | `app/skills/azure-security-audit/reference/azure-security-checklist.md` | CREATE | CIS Benchmark mapping | | `app/skills/azure-security-audit/reference/false-positives-azure.md` | CREATE | App Gateway/Front Door patterns | **Success Criteria:** - [ ] Static: parses Terraform, Bicep, ARM templates - [ ] Live: checks NSG, Functions, CosmosDB, RBAC via `az` CLI - [ ] False positive resolution for App Gateway/Front Door/VNET - [ ] Tests: >= 10 --- ### Faza 3 (cont.): Orchestration + Pack Integration (Phase 2, tydzien 3-4) #### 3.2 Orchestrator Skill: `/cloud-security-audit` **Purpose:** Single entry point that runs all provider audits detected in the project. **Behavior:** 1. Auto-detect providers from project files: - `firebase.json` / `firestore.rules` / `.firebaserc` → GCP - `serverless.yml` / `template.yaml` / `*.tf` with `provider "aws"` → AWS - `*.bicep` / `*.tf` with `provider "azurerm"` / `azure-pipelines.yml` → Azure 2. Check available credentials: `ai-toolkit credentials list` 3. Run detected provider scans in parallel 4. Merge results through false positive resolver 5. Output unified report **Skill frontmatter:** ```yaml --- name: cloud-security-audit description: "Multi-cloud security audit — auto-detects GCP/AWS/Azure and runs deterministic scans with false positive resolution" user-invocable: true effort: high disable-model-invocation: true context: fork agent: cloud-security-auditor argument-hint: "[path] [--provider gcp|aws|azure|auto] [--mode static|live] [--severity high|warn] [--output json]" allowed-tools: Read, Grep, Glob, Bash --- ``` **CLI usage:** ```bash /cloud-security-audit # auto-detect providers, static mode (default) /cloud-security-audit --provider gcp # GCP only /cloud-security-audit --provider aws,azure # AWS + Azure /cloud-security-audit --mode static # no credentials, IaC/source only (DEFAULT) /cloud-security-audit --mode live # deployed state (requires credentials) /cloud-security-audit --severity high # HIGH findings only /cloud-security-audit --output json # CI pipeline output /cloud-security-audit --output sarif # SARIF v2.1.0 for GitHub Advanced Security /cloud-security-audit --changed main # incremental: only files changed vs main /cloud-security-audit --explain GCP-CF-001 # detailed remediation for specific rule /cloud-security-audit src/functions/ # scan specific path ``` **Unified report format:** ```markdown ## Cloud Security Audit Report ### Summary | Metric | GCP | AWS | Azure | Total | |--------|-----|-----|-------|-------| | Mode | live | static | n/a | — | | Resources scanned | 12 | 8 | 0 | 20 | | HIGH | 2 | 1 | 0 | 3 | | WARN | 4 | 3 | 0 | 7 | | SUPPRESSED (false positive) | 3 | 1 | 0 | 4 | ### Findings (sorted by severity) #### [HIGH] GCP: Cloud Function "adminEndpoint" — public invoker, no protection ... #### [SUPPRESSED] GCP: Cloud Function "processPayment" — public invoker Context: App Check ENFORCED + onCall + API Gateway routed (3/3 layers) ... ``` --- #### 3.2.1 Plugin Pack Manifest **File:** `app/plugins/cloud-security-pack/plugin.json` ```json { "name": "cloud-security-pack", "description": "Multi-cloud security auditing for GCP, AWS, and Azure", "version": "1.0.0", "domain": "cloud-security", "type": "plugin-pack", "status": "experimental", "requires": [], "includes": { "agents": ["cloud-security-auditor"], "skills": [ "cloud-security-audit", "firebase-rules-audit", "cloud-functions-audit", "aws-security-audit", "azure-security-audit" ], "rules": [], "hooks": [] }, "credentials": { "supported_providers": ["gcp", "aws", "azure"], "setup_command": "ai-toolkit credentials add " } } ``` **Directory structure:** ``` app/plugins/cloud-security-pack/ ├── plugin.json ├── README.md └── (skills and agent live in core app/ dirs, referenced by name) ``` --- ### Faza 5: Tests + Documentation (ongoing, ships with each milestone) #### 5.1 Tests | Test file | Count | Description | |-----------|-------|-------------| | `tests/test_credentials.bats` | 8+ | CLI credentials management | | `tests/test_firebase_rules_scan.py` | 10+ | Firestore/RTDB rules patterns | | `tests/test_cloud_functions_audit.py` | 8+ | CF static + live checks | | `tests/test_aws_security_scan.py` | 10+ | S3/Lambda/IAM/SG checks | | `tests/test_azure_security_scan.py` | 10+ | NSG/Functions/CosmosDB checks | | `tests/test_false_positive_resolver.py` | 12+ | Context graph resolution | | `tests/test_sarif_formatter.py` | 4+ | SARIF v2.1.0 output validation | | `tests/test_incremental.py` | 4+ | Git diff filtering, fallback | | `tests/test_credentials_init.py` | 4+ | Interactive wizard, auto-detect | | **Total** | **70+** | | #### 5.2 Documentation | File | Description | |------|-------------| | `kb/planning/cloud-security-pack-plan.md` | This document | | `kb/reference/cloud-security-checklist.md` | Unified multi-cloud checklist (created when Milestone 1 ships) | | Skills `reference/` dirs | Per-provider patterns and false positives | --- ## 6. Configuration & Suppression (Unified) **Council revision:** merged `.cloud-security-ignore` + `.cloud-security-config` into a single `.cloud-security.json`. Two config files is one too many — developers expect one file. Scaffold interactively: `ai-toolkit credentials init` generates this file. **Suppression Governance (orchestration-review P0):** - **Wildcard ignores** (e.g., `GCP-CF-*`) REQUIRE a `justification` field — scanner refuses to suppress without one - **All ignore entries** require `justification` — enforced by schema validation - **CI diff detection:** When `.cloud-security.json` is modified in a PR, scanner emits a `SUPPRESSION_CHANGED` finding (severity: WARN) listing added/removed/modified ignore rules. This prevents silent suppression of vulnerabilities via committed config changes. - **In live mode:** context claims (e.g., `app_check_enforced: true`) are verified against actual cloud state. If claim doesn't match reality, emit `CONTEXT_MISMATCH` finding (severity: HIGH) ```json { "$schema": "https://softspark.github.io/ai-toolkit/schemas/cloud-security.json", "ignore": [ { "rule": "GCP-CF-001:processPayment", "justification": "Behind App Check + API Gateway, verified 2026-04-10" }, { "rule": "AWS-S3-001:static-assets", "justification": "Intentionally public static bucket, CloudFront OAI active" }, { "rule": "GCP-CF-*", "justification": "REQUIRED for wildcard suppression — reviewed by @jacek in PR #142" } ], "context": { "gcp": { "app_check_enforced": true, "api_gateway": "projects/my-proj/locations/us-central1/gateways/main", "known_public_functions": ["healthCheck", "webhookReceiver"] }, "aws": { "waf_enabled": true, "cloudfront_distributions": ["E1234567890"], "known_public_buckets": ["static-assets-prod"] }, "azure": { "front_door_enabled": true, "application_gateway": "my-app-gw", "known_public_functions": ["webhookHandler"] } } } ``` --- ## 7. CI Pipeline Integration ### Basic: JSON output + fail on HIGH ```yaml - name: Cloud Security Audit run: | python3 scripts/cloud_security_audit.py \ --mode static --output json --severity high \ > security-report.json # Exit code 1 = HIGH findings → fail pipeline ``` ### Recommended: SARIF + GitHub Advanced Security (inline PR annotations) ```yaml - name: Cloud Security Audit run: | python3 scripts/cloud_security_audit.py \ --mode static --output sarif --changed ${{ github.event.pull_request.base.sha }} \ > results.sarif continue-on-error: true - name: Upload SARIF uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif ``` ### Live mode with credentials (hardened — orchestration-review P0) ```yaml - name: Cloud Security Audit (Live) env: GCP_SA_KEY: ${{ secrets.GCP_SA_KEY }} run: | TMPFILE=$(mktemp -m 0600) trap 'rm -f "$TMPFILE"' EXIT echo "$GCP_SA_KEY" > "$TMPFILE" ai-toolkit credentials add gcp --file "$TMPFILE" python3 scripts/cloud_security_audit.py \ --mode live --output sarif --changed "${{ github.event.pull_request.base.sha }}" \ > results.sarif - name: Validate & Upload SARIF if: always() run: python3 -c "import json; d=json.load(open('results.sarif')); assert d.get('version')=='2.1.0', 'Invalid SARIF'" continue-on-error: false - name: Upload SARIF uses: github/codeql-action/upload-sarif@v3 with: sarif_file: results.sarif ``` **CI Security Notes (orchestration-review):** - `mktemp -m 0600` creates file with owner-only permissions (not world-readable `/tmp/sa.json`) - `trap 'rm -f' EXIT` ensures cleanup even on script failure - SARIF schema validated before upload to prevent injected/corrupted annotations - `${{ github.event.pull_request.base.sha }}` quoted to prevent shell injection via crafted refs - Consider GitHub OIDC workload identity federation instead of long-lived SA keys for production --- ## 8. Success Criteria (Overall) | Metric | Target | |--------|--------| | Providers supported | 3 (GCP, AWS, Azure) | | Check patterns (total) | 40+ (GCP: 16, AWS: 15, Azure: 12) | | False positive rules | 10+ | | Output formats | 3 (text, JSON, SARIF v2.1.0) | | CLI commands | 7 (add, list, remove, test, init per provider) | | Scripts (stdlib Python) | 9 (auth + scanners + resolver + sarif + incremental) | | Tests | 70+ | | External dependencies | 0 (stdlib only, CLI tools: gcloud/aws/az/terraform) | | CI exit codes | 0=clean, 1=HIGH findings, 2=credential error | | Incremental scan | `--changed` flag works with git refs | | GitHub integration | SARIF upload → inline PR annotations | --- ## 9. Fix Strategy **Same approach as HIPAA scanner v1: No auto-fix. Agent interprets and suggests.** The deterministic scripts produce findings. The `cloud-security-auditor` agent then: 1. **Reads** the flagged file/resource to understand actual context 2. **Suggests** a specific fix (not generic advice — concrete code/config change) 3. **Never** auto-applies changes — the user reviews and applies manually Examples: - Firestore rules finding → agent suggests the exact `allow read: if request.auth != null` rule change - Public CF finding → agent suggests adding `validateFirebaseIdToken` middleware with code snippet - Open S3 bucket → agent suggests the exact bucket policy JSON to add `BlockPublicAccess` - Broad IAM role → agent suggests the minimal policy document with only required permissions **Why no auto-fix in v1:** - Cloud security fixes require project-specific knowledge (which SA, which bucket, which auth flow) - Wrong auto-fix on IAM can lock out real users - Firestore rules changes can break client apps - The agent's context-aware suggestion is more valuable than a blind auto-fix **v2 option:** `--fix-mode suggest` generates a `.cloud-security-fixes.patch` file that users can review and apply with `git apply`. --- ## 10. Risks and Mitigation (updated) | Risk | Probability | Impact | Mitigation | |------|-------------|--------|------------| | CLI tools (gcloud/aws/az) not installed | Medium | Medium | Graceful fallback to static-only mode, clear error message | | Cloud APIs change breaking audit commands | Low | Medium | Version-pin CLI output format parsing, test with CI | | False positive rules too aggressive (suppress real issues) | Low | High | Default to WARN not SUPPRESS, require `.cloud-security-config` for suppression | | Credential leakage in logs | Low | Critical | Never log credentials, 0600 perms, `/tmp` cleanup in CI | | Scope creep (too many checks) | Medium | Medium | Start with top-10 per provider, expand based on feedback | | YAML IaC not parseable (stdlib-only) | High | Medium | JSON-only for static IaC; live mode unaffected; v2: vendored YAML subset parser | | `--changed` confused with live mode | Low | Low | Explicit warning: incremental applies to static only | --- ## 11. Pre-Mortem 1. **Firebase rules parser too simplistic** — Firestore rules use CEL-like syntax with nested `match` blocks. Parser needs proper recursive descent, not just regex. Mitigation: build minimal CEL parser or use line-by-line pattern matching with scope tracking. 2. **False positive resolver gives false confidence** — Users may trust SUPPRESSED status and miss real issues. Mitigation: always show context chain, require explicit `.cloud-security-config` for auto-suppression, default to WARN. 3. **AWS credential scope too broad** — User provides admin-level AWS profile. Mitigation: `credentials test` checks actual permissions, WARN if write access detected, suggest read-only IAM policy in docs. 4. **Three providers = 3x maintenance** — Each provider's CLI evolves independently. Mitigation: abstract provider interface, single test matrix, version tracking per provider. 5. **Terraform parsing incomplete** — HCL syntax is complex (modules, variables, conditionals). Mitigation: wrap `terraform show -json` instead of parsing HCL. Fallback to flat regex for projects without `terraform` CLI. 6. **SARIF adoption low** — Users may not know how to use SARIF with GitHub. Mitigation: provide copy-paste GitHub Actions workflow in docs and `--explain` for onboarding. --- ## 12. Council Review Summary (2026-04-10) **Verdict:** CONDITIONAL FOR — implement with scope reduction. **Confidence:** MEDIUM (weighted score: FOR 3.1 vs AGAINST 2.9) **Key insights applied to this plan:** - [x] Timeline revised from 3-4 → 5-6 weeks - [x] ~~Azure deferred to Milestone 3~~ → **reinstated: full 3-provider delivery** - [x] SARIF v2.1.0 output added — essential for GitHub Advanced Security integration - [x] Incremental scan mode added (`--changed`) — how developers actually use security tools - [x] `terraform show -json` wrapper instead of HCL parsing — realistic path - [x] Single config file `.cloud-security.json` (merged ignore + context) - [x] `credentials init` interactive wizard — reduce onboarding friction - [x] `--explain ` for on-demand remediation guidance - [x] Static mode as default — zero-setup first experience **Deferred to v2:** - Kubernetes/container security (separate pack candidate) - Secret scanning with entropy detection - Compliance framework mapping (SOC2, PCI-DSS, NIST 800-53) - Visual security dashboard in browser - GitHub PR comment integration beyond SARIF - Vendored YAML subset parser for CloudFormation YAML static scanning **Council strongest agreement:** False positive resolver is the killer feature and primary differentiator vs Checkov/Trivy/Prowler. No existing tool combines deterministic scanning with context-aware resolution. --- ## 13. Orchestration Review Summary (2026-04-10) **Agents:** tech-lead, security-architect, product-manager, code-reviewer (4 parallel) **Verdict:** Plan structurally complete (14/14 elements). Three P0 security blockers identified and resolved. **Applied changes:** | # | Action | Source | Priority | Applied? | |---|--------|--------|----------|----------| | 1 | Programmatic Bash allowlist | security-architect | P0 | Yes — allowlist.sh + agent integration | | 2 | CI hardening (mktemp/trap/SARIF validation) | security-architect | P0 | Yes — CI examples rewritten | | 3 | Suppression governance (justification + diff detection) | security-architect | P0 | Yes — schema + SUPPRESSION_CHANGED finding | | 4 | Recursive descent parser for Firestore | code-reviewer | P1 | Yes — replaced regex approach, +1d estimate | | 5 | SARIF `driver.rules[]` for GitHub annotations | code-reviewer | P1 | Yes — schema + success criteria | | 6 | Task 4.1→3.2 numbering fix | tech-lead | P1 | Yes — renumbered | | 7 | `terraform plan` execution risk documented | security-architect | P1 | Yes — only `show -json` allowed | | 8 | `credentials init` deferred to M2 | code-reviewer | P2 | Yes — saves 1.5d in M1 | | 9 | False positive resolver budgeted +2d/provider | code-reviewer | P2 | Yes — estimate updated | | 10 | `SUPPRESSION_CHANGED` finding type | security-architect | P2 | Yes — in governance section | **Market positioning (product-manager):** - Not competing with Checkov on check count (40 vs 3000) - Competing on: zero-noise (false positive resolver), zero-setup (static-first), IDE-native (10 platforms), AI interpretation - Target: developers in ai-toolkit ecosystem, not enterprise security teams - Value as ecosystem feature, not standalone product **Timeline revision (code-reviewer):** - 1 person: 6-7 weeks realistic (was 5-6) - 2 people: 4-5 weeks (parallel GCP + AWS tracks) - All 3 providers ship in 6 weeks — no conditional gates --- ## 14. Next Actions 1. [ ] Approve plan 2. [ ] Implement `credentials` CLI command (1.1) + Bash allowlist 3. [ ] Create `cloud-security-auditor` agent (1.2) 4. [ ] Implement SARIF formatter + incremental scan (1.3) 5. [ ] Implement `firebase-rules-audit` — recursive descent parser (2.1) 6. [ ] Implement `cloud-functions-audit` + false positive resolver GCP (2.2, 2.3) 7. [ ] Implement `aws-security-audit` + `terraform show -json` wrapper (3.1) 8. [ ] Implement orchestrator + plugin pack + `credentials init` (3.2, 1.1b) 9. [ ] Implement `azure-security-audit` + false positive resolver Azure (4.1) 10. [ ] Full test suite (70+) + documentation + release --- **Last Updated:** 2026-04-10 **Council Reviewed:** 2026-04-10 **Orchestration Reviewed:** 2026-04-10 (4 agents: tech-lead, security-architect, product-manager, code-reviewer) --- ## kb/procedures/ecosystem-sync-sop.md --- title: "SOP: Ecosystem Sync" category: procedures service: ai-toolkit tags: [sop, ecosystem, editors, generators, drift-detection, sync] version: "1.1.0" created: "2026-04-23" last_updated: "2026-07-10" description: "Quarterly (or event-triggered) sync procedure that detects documentation and capability drift in supported tools (Claude Code, Claude app, and 11 editors), analyses our generators and skills for missing features, and walks through the migration + generator-update workflow." --- # SOP: Ecosystem Sync Keeps ai-toolkit aligned with the tools it integrates with. When an editor adds a new hook lifecycle, makes a feature globally available, changes a config path, or deprecates a flag, this SOP surfaces it before it surprises users. **When to run:** - **Every quarter** as a baseline health check (calendar reminder) - **Before every minor release** of ai-toolkit (Phase 0 of release prep) - **Whenever an editor ships a major version** (subscribe to their changelogs) - **On demand** if a user reports "feature X exists but toolkit doesn't support it" **Time:** 30 minutes for drift review + variable for any generator updates --- ## Quick Reference ```bash # Full check (all 13 tools, online) python3 scripts/ecosystem_doctor.py --format text # Single tool python3 scripts/ecosystem_doctor.py --tool cursor --format text # First-ever run — baseline the snapshot python3 scripts/ecosystem_doctor.py --update > /dev/null # CI / gating mode python3 scripts/ecosystem_doctor.py --check # Offline (no network) — validates our side only python3 scripts/ecosystem_doctor.py --offline --format text ``` --- ## Inputs | File | Purpose | |------|---------| | `scripts/ecosystem_tools.json` | Authoritative registry: 13 tools with doc URLs, config paths, our generators, capability markers | | `benchmarks/ecosystem-doctor-snapshot.json` | Last-seen state (headings, content hash, markers, version) — updated via `--update` | | `scripts/ecosystem_doctor.py` | Drift detector | | `kb/reference/supported-tools-registry.md` | Human-readable view of the registry | --- ## Phase 1: Run the Doctor ```bash python3 scripts/ecosystem_doctor.py --format text > /tmp/eco-report.txt cat /tmp/eco-report.txt ``` The report classifies every tool into: - **Clean** — doc page, headings, and markers match the last snapshot; no action - **Drift** — something changed upstream. Each drift entry has a `kind`: - `headings_added` — the doc grew new sections (new features? reorg?) - `headings_removed` — a section disappeared (deprecation? renaming?) - `marker_flips` — an expected capability marker appeared (`+`) or vanished (`-`) - `content_changed_no_heading_delta` — prose edits, reorder, minor rewrites, OR HTML churn (timestamps, ads, CSRF nonces). Reported but **not** treated as drift by `--check` — too noisy on dynamic pages. - `version_changed` — the CLI version bumped (for tools that expose `--version`) - **Errored** — couldn't fetch docs (timeout, 404, auth wall). Doctor does not overwrite the snapshot for errored tools; the last-known-good state persists. --- ## Phase 2: Classify Each Drift For every drifting tool, read its docs URL and classify the change into exactly one bucket: | Drift class | What it means | Action owner | |-------------|---------------|--------------| | **A. Cosmetic reword** | Prose edited, same feature set | Update snapshot (`--update`), no code change | | **B. New feature — we should integrate** | New hook event, new config key, new CLI flag, new rule surface | Update the relevant generator in `scripts/generate__*.py`; extend `app/skills/*` or `app/agents/*` if the feature maps onto our skills; document in `kb/reference/supported-tools-registry.md` | | **C. New feature — not our concern** | Enterprise SSO, billing, proprietary UI-only features | Note in registry `capability_markers` as "not adopted"; update snapshot | | **D. Deprecation** | Flag or path removed / renamed | Open migration issue; coordinate with `ai-toolkit install` and generator output; add deprecation warning to CLAUDE.md rules if user-facing | | **E. Feature promoted to default** | Was behind a flag, now global | Remove the flag from generator output; simplify our installer | | **F. Global availability** | Was editor-only, now also available via CLI / hooks / settings.json | Map new config surface; may require a new generator or extending an existing one | Write one line per drift in `/tmp/eco-report.txt` with its class. Example: ``` cursor: headings_added [AGENTS.md support] -> class B (integrate: extend generate_cursor_mdc.py) aider: version_changed 0.70 -> 0.72 -> class A (cosmetic, --update) windsurf: marker_flips +Cascade -> class B (already supported, verify snapshot) ``` --- ## Phase 3: Execute Changes ### For class B (new feature — integrate) 1. Read the tool's docs section that introduced the feature. Note the exact config key / hook name / file path. 2. Open the relevant generator (`scripts/generate__*.py`) and add output for the new surface. 3. If the feature is a **hook event**, also update: - `app/hooks.json` (if Claude-Code-native) - `app/skills/hook-creator/SKILL.md` — add the event to the Supported Hook Events table - `scripts/inject_hook_cli.py` — if the hook target path differs 4. If the feature is a **skill/agent schema extension**: - Update `app/skills/skill-creator/SKILL.md` and `app/skills/command-creator/SKILL.md` templates - Update `scripts/validate.py` field allowlists - Update `kb/reference/agent-skills-spec.md` (if the change is an upstream spec change) 5. Add a bats test under `tests/test_.bats` covering the new output. 6. Regenerate artifacts: `npm run generate:all`. ### For class D (deprecation) 1. Open a migration issue in GitHub with "class: deprecation" and a link to the upstream changelog. 2. In the generator, mark the deprecated output path as emitting a comment: `# DEPRECATED: , removed in `. 3. If deprecation affects `ai-toolkit install --local --editors `, add a doctor check that warns when a user's repo still contains the deprecated file. ### For class E / F (feature promotion) 1. Simplify the generator to emit the new-default form; keep a fallback comment for users on older tool versions. 2. Update `kb/reference/supported-tools-registry.md` config-paths column. ### For class A / C (no code change) 1. Run `python3 scripts/ecosystem_doctor.py --update --tool ` to refresh that tool's snapshot. --- ## Phase 4: Update the Registry If new capability markers, config paths, or doc URLs emerged during Phase 3, edit `scripts/ecosystem_tools.json`: ```bash ${EDITOR:-nvim} scripts/ecosystem_tools.json ``` Fields to consider updating: - `urls.docs` — if the vendor moved their docs - `urls.release_notes` — if the changelog location changed - `config_paths` — if new files now ship in our install output - `our_generators` — if a new generator was added - `capability_markers` — if a new feature was adopted - `version_probe.command` — if the CLI binary was renamed After editing, increment `last_updated` in the registry and save the snapshot: ```bash python3 scripts/ecosystem_doctor.py --update ``` --- ## Phase 5: Validate ```bash python3 scripts/validate.py --strict python3 scripts/audit_skills.py --ci python3 scripts/ecosystem_doctor.py --check # exits 0 after --update npm test ``` All four must pass before committing generator / registry changes. --- ## Phase 6: Commit Use a structured commit per change class: ```bash git add scripts/ecosystem_tools.json benchmarks/ecosystem-doctor-snapshot.json git add scripts/generate__*.py # if class B/D/E/F git add app/skills//SKILL.md # if templates touched git add kb/reference/supported-tools-registry.md git commit -m "chore(ecosystem): sync " ``` Recommended commit messages by class: | Class | Template | |-------|----------| | A | `chore(ecosystem): refresh snapshot (cosmetic docs update)` | | B | `feat(): add support for ` | | C | `chore(ecosystem): note as not-adopted` | | D | `feat(): deprecation warning for ` | | E | `refactor(): remove flag for (now default)` | | F | `feat(): add generator` | --- ## Gotchas - **First run has no baseline.** On a machine where `benchmarks/ecosystem-doctor-snapshot.json` does not exist, every tool shows as clean (no prior state to diff against). Run `--update` once to seed, then run again to see real drift. - **Documentation sites use client-side rendering.** Aider, opencode, and Antigravity serve most content via JavaScript. `urllib` fetches the bare HTML skeleton — the doctor only sees a few headings. Combine the automated check with a manual visit to the docs on these tools. - **Release notes pages change structure more often than docs.** Cursor and Windsurf refactor their changelog layouts periodically; a heading delta from a changelog page is often a presentation change, not a feature change. Classify as A when in doubt. - **Version probes require the CLI to be installed locally.** `gemini --version`, `aider --version`, etc. are skipped silently when the binary isn't on `$PATH`. The snapshot therefore omits version drift for tools you haven't installed — that is intentional, not a bug. - **GitHub release pages have anti-scraping.** `github.com///releases` works via `urllib` but rate-limits aggressively. If the doctor errors on repeated runs, wait 10 minutes or manually review the release page. - **Marker list is intentionally small.** Capability markers are a "did we adopt this?" checklist, not a feature coverage map. Adding every sub-feature bloats the JSON and produces noisy flips — keep markers at the top-level-capability tier. - **`--check` only gates on structural drift.** Heading/marker/version changes and fetch errors exit `1`. Pure content-hash differences (`content_changed_no_heading_delta`) exit `0` — otherwise dynamic pages with timestamps or rotating ads would page you every run. If you want the strictest possible gate, grep for `Content changed` in the text report instead. --- ## Scheduling Recommended cadence: | Trigger | Action | |---------|--------| | Every Monday morning | `python3 scripts/ecosystem_doctor.py --format text` — scan during coffee | | Before a minor release | Full sync + clean snapshot before tagging | | After any drift report | Act within 1 week or record explicit "ignore, low priority" in the commit message | | New tool added to the registry | Baseline with `--update --tool ` | | Tool removed from support | Delete its entry from the registry AND from the snapshot JSON | An optional GitHub Action can run `--check` weekly and open an issue on drift. Template: ```yaml # .github/workflows/ecosystem-doctor.yml (proposed, not yet committed) on: schedule: - cron: '0 9 * * 1' # Mondays 09:00 UTC workflow_dispatch: {} jobs: doctor: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - run: python3 scripts/ecosystem_doctor.py --format text | tee /tmp/doctor.txt - run: python3 scripts/ecosystem_doctor.py --check ``` --- ## When NOT to Use - For **runtime** user support (user hit a bug with an editor) — use `/debug` or `/triage-issue` - For **picking** an editor to add — that is a product decision, not a sync; use `/architecture-decision` - For **one-off** testing of a specific tool's install flow — use the release-verification SOP - For **scaling up** the supported-tools list — add the new tool to the registry, then run the SOP to baseline it --- ## Related Documentation - [Supported Tools Registry](../reference/supported-tools-registry.md) — human-readable per-tool breakdown - [MCP Editor Compatibility](../reference/mcp-editor-compatibility.md) — MCP-specific adapter table - [Maintenance SOP](maintenance-sop.md) — general toolkit upkeep - [Release Preparation SOP](release-preparation-sop.md) — run the doctor before tagging --- ## kb/procedures/maintenance-sop.md --- title: "SOP: AI Toolkit Maintenance" category: procedures service: ai-toolkit tags: [sop, maintenance, agents, skills, install] version: "3.3.0" created: "2026-03-23" last_updated: "2026-07-14" description: "Standard operating procedures for installing, maintaining, and evolving the ai-toolkit." --- # SOP: AI Toolkit Maintenance ## Init Repository (New Project) Use this when starting a new project that should use the toolkit. **Prerequisites:** toolkit installed globally (`ai-toolkit install` already done once). ```bash cd /path/to/new-project ai-toolkit install --local ``` By default, `--local` installs Claude Code configs only: - `CLAUDE.md` — project-specific rules template (only if missing) - `.claude/settings.local.json` — MCP servers, env vars, permissions (only if missing, initialized with MCP defaults) - `.claude/constitution.md` — toolkit constitution **injected** via markers (preserves user content) To also install editor configs, use `--editors`: ```bash ai-toolkit install --local --editors all # all supported editors ai-toolkit install --local --editors cursor,aider # specific editors only ``` Supported editors: `cursor`, `windsurf`, `cline`, `roo`, `aider`, `augment`, `copilot`, `antigravity`, `codex`, `gemini`, `opencode`. To restrict which language rules are injected, use `--lang`: ```bash ai-toolkit install --local --lang python,typescript ai-toolkit install --local --lang python --editors all # language rules propagated to all editors ``` When `--editors` is combined with `--lang` (or auto-detected languages), language rules are propagated to all configured editors as `ai-toolkit-lang-` files — not just Claude's `CLAUDE.md`. Similarly, registered custom rules (`~/.softspark/ai-toolkit/rules/`) are propagated to directory-based editor configs as `ai-toolkit-custom-` files. **Note:** Claude Code hooks are global-only and merge into `~/.claude/settings.json`; any legacy `.claude/hooks.json` is removed. Editors with documented repository hook surfaces may receive local native files. Codex uses `.codex/hooks.json` plus `.codex/hooks/*`; Copilot profile `standard` and above uses `.github/hooks/ai-toolkit.json` plus its adjacent runtime. **Input validation (v1.4.2):** `--only`, `--skip`, `--editors`, and `--lang` are validated on input; an invalid value exits with a clear error before any changes are made. Then edit `CLAUDE.md`: ```markdown # My Project ## Overview What this project does. ## Tech Stack - Language: TypeScript - Framework: Next.js - Database: PostgreSQL ## Commands # Dev: npm run dev # Test: npm test # Build: npm run build ``` --- ## Install Toolkit Globally Run once per machine. Installs into `~/.claude/` — available in all projects. ```bash npm install -g @softspark/ai-toolkit # once per machine ai-toolkit install # sets up ~/.claude/ ``` What `install` and `update` do (merge-friendly — user content never overwritten): | Component | Strategy | User collision | |-----------|----------|---------------| | `agents/*.md` | Per-file symlinks into `~/.claude/agents/` | User file with same name preserved (toolkit skipped) | | `skills/*/` | Per-directory symlinks into `~/.claude/skills/` | User dir with same name preserved | | `settings.json` hooks | JSON merge via `merge-hooks.py` | User hooks + settings preserved, toolkit entries tagged `_source: ai-toolkit` | | `constitution.md` | Marker injection via `inject_section_cli.py` | User content outside `` markers untouched | | `ARCHITECTURE.md` | Marker injection via `inject_section_cli.py` | Same as above | | `CLAUDE.md` | Compact index for managed global rules | User content outside toolkit markers untouched | | `rules/ai-toolkit-*.md` | File-based Claude Code user-level rules from `app/rules/*.md` and registered rules | `ai-toolkit-*` prefix reserved for installer-managed files | Re-running updates only toolkit content. Old whole-directory symlinks are auto-upgraded to per-file on next run. ### Install in Claude Chat / Desktop / Cowork The Claude app does not read Claude Code's `~/.claude/rules/`, `CLAUDE.md`, agents, skills, or settings. Build the separate app-native plugin: ```bash ai-toolkit claude-app export --verify ``` 1. Open Claude and choose `Customize > Plugins > + > Upload plugin`. 2. Select `ai-toolkit-claude-app.zip`. 3. Paste `ai-toolkit-claude-app-global-instructions.md` into `Settings > Cowork > Global instructions`. The export includes registered rules by default; use `--no-custom-rules` for a shareable clean bundle. Re-export and re-upload after toolkit or registered-rule updates. Skills work in Chat and Cowork. Hooks and sub-agents run only in Cowork. ### Install Profiles (v3.0.0) | Profile | Claude Code core | Copilot | Codex | Other editor-native surfaces | Git hooks | |---------|:---------------:|---------|-------|------------------------------|:---------:| | `minimal` | yes | root instructions + agents + skills | instructions + agents + skills + hooks | pointer-only where required | no | | `standard` (default) | yes | minimal + scoped instructions + prompts + hooks | same native bundle | Gemini hooks + editor rules | no | | `strict` | yes | same as standard | same native bundle | same as standard | yes | | `full` | yes | same as standard | same native bundle | all supported hooks, agents, commands, and skill pointers | optional | Codex materializes the full skill catalog under `.agents/skills/` in every profile. `--codex-skills` remains only as an explicit refresh compatibility flag. See `kb/reference/global-install-model.md` for the full breakdown. --- ## Update Toolkit After a new npm release: ```bash npm install -g @softspark/ai-toolkit@latest ai-toolkit update ``` `update` is a semantic alias for `install` — use it for all re-apply flows. Supports the same flags: ```bash ai-toolkit update --only agents,hooks # re-apply only specific components ai-toolkit update --local # refresh project-local Claude Code configs; auto-detects editors from existing project files (no --editors needed) ai-toolkit update --local --editors cursor,windsurf # override auto-detection and target specific editors ai-toolkit update --list # dry-run: show what would change ``` When running `update --local`, the CLI inspects existing config files (e.g. `.cursor/rules`, `.aider.conf.yml`) to determine which editors are present and refreshes only those — no flags required. --- ## Register a Rule from Another Repo Third-party repos (jira-mcp, rag-mcp, etc.) can register their own rules globally: ```bash ai-toolkit add-rule ./my-project-rules.md # → copies to ~/.softspark/ai-toolkit/rules/my-project-rules.md ai-toolkit update # → syncs the rule into ~/.claude/rules/ai-toolkit-registered-*.md and all global editor configs ai-toolkit update --local # → also propagates as ai-toolkit-custom- to directory-based editors (Cursor, Windsurf, Cline, Roo, Augment, Antigravity) ``` To unregister (removes from registry **and** strips the block from CLAUDE.md): ```bash ai-toolkit remove-rule my-project-rules ``` Rule names derive from the filename (`my-project-rules.md` → marker `TOOLKIT:my-project-rules`). After adding or removing a registered rule, re-export the Claude app plugin if that runtime should receive the change. --- ## Adding a New Agent 1. Create `app/agents/.md` with YAML frontmatter: ```yaml --- name: agent-name description: "When to use this agent. Triggers: keyword1, keyword2." tools: Read, Write, Edit, Bash model: opus skills: skill-1, skill-2 --- ``` 2. Write agent instructions below frontmatter 3. Update `kb/reference/agents-catalog.md` 4. Update `app/ARCHITECTURE.md` counts 5. Run `scripts/validate.py` 6. Regenerate: `scripts/generate_agents_md.py > AGENTS.md` ## Adding a New Skill 1. Create `app/skills//SKILL.md` with frontmatter: ```yaml --- name: skill-name description: "Third-person description. Max 1024 chars." effort: medium disable-model-invocation: true # task skill user-invocable: false # knowledge skill --- ``` 2. Update `kb/reference/skills-catalog.md` and `app/ARCHITECTURE.md` 3. Run `scripts/validate.py` ## Adding a New Hook Preferred path: ```bash /hook-creator [event or hook description] ``` Manual path: 1. Create `app/hooks/.sh` 2. Register the hook under `app/hooks.json` 3. Run `scripts/validate.py` 4. Run `scripts/doctor.py` 5. Update `kb/reference/hooks-catalog.md`, `README.md`, and any affected architecture docs Use `PreToolUse` for blocking validations, `PostToolUse` for non-blocking feedback, `UserPromptSubmit` for prompt governance, and `PreCompact` / `SessionEnd` for context preservation and handoff. ## Troubleshooting Rule Enforcement in Claude Code Use this when Claude appears to ignore `CLAUDE.md`, `.claude/rules/*.md`, output styles, or search-first rules. 1. **Check current Claude docs first.** Confirm the live contract for memory, settings, output styles, and hooks: - `https://code.claude.com/docs/en/memory` - `https://code.claude.com/docs/en/settings` - `https://code.claude.com/docs/en/output-styles` - `https://code.claude.com/docs/en/hooks` 2. **Isolate with safe mode (v2.1.169+).** Start a session with `claude --safe-mode` (or `CLAUDE_CODE_SAFE_MODE=1`) — it disables hooks, skills, agents, and CLAUDE.md. If the problem persists in safe mode, the cause is upstream, not toolkit customization; if it disappears, bisect toolkit components with `ai-toolkit update --only `. 3. **Verify instruction loading.** Run `/memory` in Claude Code and confirm the expected `CLAUDE.md`, `CLAUDE.local.md`, and `.claude/rules/*.md` files are listed. Remember that Claude Code reads `CLAUDE.md`, not `AGENTS.md`, unless `CLAUDE.md` imports it. 4. **Verify the active output style.** Check `.claude/settings.local.json` or `/config`. Output style changes apply after `/clear` or a new session. 5. **Inspect installed hooks.** Ensure `~/.claude/settings.json` contains the ai-toolkit `UserPromptSubmit` and `Stop` entries. The governance hook must run with `AI_TOOLKIT_HOOK_QUIET=1 AI_TOOLKIT_HOOK_FORMAT=json` so it injects `additionalContext` without noisy transcript output. 6. **Reproduce the hook path directly.** ```bash printf '{"session_id":"debug","prompt":"debug this technical rule issue"}' \ | AI_TOOLKIT_SEARCH_FIRST=strict AI_TOOLKIT_HOOK_QUIET=1 AI_TOOLKIT_HOOK_FORMAT=json \ ~/.softspark/ai-toolkit/hooks/user-prompt-submit.sh ``` The output must be valid JSON with `hookSpecificOutput.additionalContext`. 7. **Check corrective enforcement.** If the assistant still skips required research, `stop-search-check.sh` should block Stop with the search-first message. If it does not, inspect `~/.softspark/ai-toolkit/state/search-required-*.flag` and the Codex/Claude transcript logs. 8. **Repair drift.** Run: ```bash ai-toolkit update --only hooks python3 scripts/ecosystem_doctor.py --tool claude-code --format text scripts/validate.py ``` ## Troubleshooting Rule Enforcement in Claude Chat / Cowork Do not use `/memory` or inspect `~/.claude/rules/` for the app runtime; those checks apply only to Claude Code. 1. Run `ai-toolkit claude-app verify` and fix any manifest/component error. 2. Re-export the ZIP and upload it from `Customize > Plugins`. 3. Confirm `ai-toolkit-rules` appears in the `/` or `+` skill picker. 4. Confirm the generated standing instructions are saved under `Settings > Cowork > Global instructions`. 5. In regular Chat, expect skills only. Hooks and sub-agents are intentionally unavailable there; test those in Cowork. 6. Start a new chat/task after updating the plugin so the new component catalog is loaded. ## Verification After changing rule-enforcement behavior, run at minimum: ```bash bats tests/test_hooks.bats tests/test_search_first_flow.bats bats tests/test_install.bats tests/test_codex.bats bats tests/test_claude_app.bats tests/test_hooks_per_editor.bats python3 scripts/claude_app.py verify python3 scripts/validate.py --strict ``` ## Managing Plugins ```bash ai-toolkit plugin list # show available packs ai-toolkit plugin install --editor claude # install for Claude Code global target ai-toolkit plugin install --editor codex # install for Codex global target ai-toolkit plugin install --editor all --all # install all 12 packs for both runtimes ai-toolkit plugin update --editor all --all # re-apply all installed packs after toolkit updates ai-toolkit plugin clean # prune data older than 90 days ai-toolkit plugin clean --days 30 # custom retention ai-toolkit plugin remove --editor codex # remove from one runtime only ai-toolkit plugin status --editor all # show installed packs with runtime details ``` Install copies runtime-owned hooks/scripts, ensures the required base skills are available, merges native hook configuration, and runs init scripts. Codex uses `$CODEX_HOME/AGENTS.md`, `$CODEX_HOME/hooks.json`, self-contained `$CODEX_HOME/ai-toolkit-hooks/*`, and shared `$HOME/.agents/skills/*`; it never uses `~/AGENTS.md` or `$HOME/.agents/rules/` as user configuration. Update removes and reinstalls the selected pack while preserving plugin data. Remove strips only exact pack-owned handlers, assets, and marker sections. Core agents/skills and user handlers remain untouched. Memory-pack auto-prunes observations older than 90 days on every session end (configurable via `MEMORY_RETENTION_DAYS`). State is tracked per runtime in `~/.softspark/ai-toolkit/plugins.json`. After every `ai-toolkit update`, also run `ai-toolkit plugin update --editor all --all` if plugin packs are installed. ## Adding a KB Document Follow the `documentation-standards` knowledge skill (`app/skills/documentation-standards/SKILL.md`) for full spec. Quick checklist: 1. **Choose category directory:** `kb/reference/`, `kb/howto/`, `kb/procedures/`, `kb/troubleshooting/`, `kb/best-practices/`, or `kb/planning/` 2. **Create file:** kebab-case name, no dates in filename 3. **Add frontmatter** with all 7 required fields: `title`, `category`, `service`, `tags`, `created`, `last_updated`, `description` 4. **Write in English** 5. **Validate:** `scripts/validate.py` (checks all `kb/**/*.md` frontmatter) **Documents without valid frontmatter will fail `validate.py` and block CI.** ## Adding Scripts to Skills 1. Create `app/skills//scripts/