diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 603b14295..7d72f8bb1 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -17,9 +17,9 @@ "keywords": ["absinthe", "graphql", "elixir", "api", "subscriptions"] }, { - "name": "accessibility", + "name": "accessibility-engineering", "description": "Accessibility engineering agents providing expertise in WCAG, ARIA, and inclusive design", - "source": "./plugins/disciplines/accessibility", + "source": "./plugins/disciplines/accessibility-engineering", "category": "Discipline", "keywords": ["accessibility", "wcag", "inclusive-design", "agent"] }, @@ -90,9 +90,9 @@ ] }, { - "name": "api", + "name": "api-engineering", "description": "API engineering agents providing expertise in API design, contracts, and gateway patterns", - "source": "./plugins/disciplines/api", + "source": "./plugins/disciplines/api-engineering", "category": "Discipline", "keywords": ["api", "rest", "graphql", "agent"] }, @@ -104,9 +104,9 @@ "keywords": ["apollo", "graphql", "client", "server", "skill"] }, { - "name": "architecture", + "name": "system-architecture", "description": "Agents for system design and architectural thinking.", - "source": "./plugins/disciplines/architecture", + "source": "./plugins/disciplines/system-architecture", "category": "Discipline", "keywords": ["architecture", "design", "patterns"] }, @@ -131,9 +131,9 @@ ] }, { - "name": "backend", + "name": "backend-development", "description": "Agents specialized in backend development, API design, and system architecture. Focuses on scalable server-side solutions and data management.", - "source": "./plugins/disciplines/backend", + "source": "./plugins/disciplines/backend-development", "category": "Discipline", "keywords": ["backend", "api", "architecture", "data", "server", "agent"] }, @@ -165,9 +165,9 @@ ] }, { - "name": "blockchain", + "name": "blockchain-development", "description": "Blockchain development agents providing expertise in blockchain architecture, smart contracts, and Web3", - "source": "./plugins/disciplines/blockchain", + "source": "./plugins/disciplines/blockchain-development", "category": "Discipline", "keywords": ["blockchain", "web3", "smart-contracts", "agent"] }, @@ -290,16 +290,16 @@ ] }, { - "name": "compilers", + "name": "compiler-engineering", "description": "Compiler development agents providing expertise in lexing, parsing, code generation, and language design", - "source": "./plugins/disciplines/compilers", + "source": "./plugins/disciplines/compiler-engineering", "category": "Discipline", "keywords": ["compiler", "language-design", "parsing", "agent"] }, { - "name": "content", + "name": "content-creation", "description": "Specialized agents for creating engaging content across various formats and target audiences. Focuses on storytelling, audience engagement, and platform-optimized writing.", - "source": "./plugins/disciplines/content", + "source": "./plugins/disciplines/content-creation", "category": "Discipline", "keywords": [ "content", @@ -382,9 +382,9 @@ "keywords": ["data-engineering", "etl", "streaming", "agent"] }, { - "name": "databases", + "name": "database-engineering", "description": "Database engineering agents providing expertise in schema design, query optimization, and reliability", - "source": "./plugins/disciplines/databases", + "source": "./plugins/disciplines/database-engineering", "category": "Discipline", "keywords": ["database", "sql", "query-optimization", "agent"] }, @@ -410,9 +410,9 @@ "keywords": ["docker", "docker-compose", "containers", "devops"] }, { - "name": "documentation", + "name": "documentation-engineering", "description": "Agents specialized in technical documentation and knowledge management. Focuses on documentation architecture and user-focused writing.", - "source": "./plugins/disciplines/documentation", + "source": "./plugins/disciplines/documentation-engineering", "category": "Discipline", "keywords": ["documentation", "technical-writing", "knowledge", "agent"] }, @@ -444,9 +444,9 @@ "keywords": ["elixir", "otp", "ecto", "skill"] }, { - "name": "embedded", + "name": "embedded-development", "description": "Embedded development agents providing expertise in RTOS, firmware, and IoT", - "source": "./plugins/disciplines/embedded", + "source": "./plugins/disciplines/embedded-development", "category": "Discipline", "keywords": ["embedded-systems", "firmware", "iot", "agent"] }, @@ -525,9 +525,9 @@ ] }, { - "name": "frontend", + "name": "frontend-development", "description": "Agents specialized in frontend development, UI design, and presentation engineering. Focuses on visual design, user experience, and interaction patterns.", - "source": "./plugins/disciplines/frontend", + "source": "./plugins/disciplines/frontend-development", "category": "Discipline", "keywords": ["frontend", "ui", "ux", "design", "presentation", "agent"] }, @@ -544,9 +544,9 @@ ] }, { - "name": "games", + "name": "game-development", "description": "Game development engineering agents providing expertise in engine architecture, gameplay systems, and performance optimization", - "source": "./plugins/disciplines/games", + "source": "./plugins/disciplines/game-development", "category": "Discipline", "keywords": ["game-development", "game-engine", "gameplay", "agent"] }, @@ -563,6 +563,20 @@ "workflow" ] }, + { + "name": "git-worktrees", + "description": "Agent isolation via git worktrees - gives each subagent its own checkout to prevent interference during parallel work.", + "source": "./plugins/patterns/git-worktrees", + "category": "Pattern", + "keywords": [ + "git", + "worktree", + "isolation", + "subagent", + "parallel", + "multi-agent" + ] + }, { "name": "github", "description": "MCP server configuration for GitHub integration providing repository management, issues, pull requests, actions, and code search.", @@ -642,9 +656,9 @@ "keywords": ["go", "golang", "concurrency", "skill"] }, { - "name": "graphics", + "name": "graphics-engineering", "description": "Graphics engineering agents providing expertise in GPU programming, shaders, and rendering", - "source": "./plugins/disciplines/graphics", + "source": "./plugins/disciplines/graphics-engineering", "category": "Discipline", "keywords": ["graphics", "gpu", "shaders", "agent"] }, @@ -685,9 +699,9 @@ "keywords": ["helm", "kubernetes", "k8s", "charts", "devops"] }, { - "name": "infrastructure", + "name": "infrastructure-engineering", "description": "Agents for operational excellence and reliability engineering.", - "source": "./plugins/disciplines/infrastructure", + "source": "./plugins/disciplines/infrastructure-engineering", "category": "Discipline", "keywords": ["devops", "sre", "deployment", "operations"] }, @@ -841,9 +855,9 @@ ] }, { - "name": "mobile", + "name": "mobile-development", "description": "Agents specialized in mobile development for iOS, Android, and cross-platform applications. Focuses on native and cross-platform mobile solutions.", - "source": "./plugins/disciplines/mobile", + "source": "./plugins/disciplines/mobile-development", "category": "Discipline", "keywords": ["mobile", "ios", "android", "development", "agent"] }, @@ -869,9 +883,9 @@ "keywords": ["nestjs", "typescript", "nodejs", "skill"] }, { - "name": "networking", + "name": "network-engineering", "description": "Network engineering agents providing expertise in protocols, distributed systems, and service mesh", - "source": "./plugins/disciplines/networking", + "source": "./plugins/disciplines/network-engineering", "category": "Discipline", "keywords": ["network", "protocols", "distributed-systems", "agent"] }, @@ -938,9 +952,9 @@ "keywords": ["objective-c", "objc", "ios", "macos", "arc", "skill"] }, { - "name": "observability", + "name": "observability-engineering", "description": "Observability engineering agents providing expertise in tracing, monitoring, and logging", - "source": "./plugins/disciplines/observability", + "source": "./plugins/disciplines/observability-engineering", "category": "Discipline", "keywords": ["observability", "monitoring", "tracing", "agent"] }, @@ -951,6 +965,36 @@ "category": "Pattern", "keywords": ["oop", "object-oriented", "design-patterns", "encapsulation"] }, + { + "name": "antigravity", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, skills, disciplines) to work with Google Antigravity IDE via MCP server.", + "source": "./plugins/bridges/antigravity", + "category": "Integration", + "keywords": [ + "antigravity", + "google", + "bridge", + "mcp", + "hooks", + "validation", + "skills" + ] + }, + { + "name": "gemini-cli", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with Gemini CLI by translating Gemini CLI events into Han hook invocations.", + "source": "./plugins/bridges/gemini-cli", + "category": "Integration", + "keywords": [ + "gemini-cli", + "gemini", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, { "name": "opencode", "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with OpenCode by translating OpenCode events into Claude Code hook invocations.", @@ -966,9 +1010,39 @@ ] }, { - "name": "performance", + "name": "kiro", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with Kiro CLI by translating Kiro hook events into Han hook executions.", + "source": "./plugins/bridges/kiro", + "category": "Integration", + "keywords": [ + "kiro", + "kiro-cli", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, + { + "name": "codex", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with OpenAI Codex CLI by translating Codex events into Claude Code hook invocations.", + "source": "./plugins/bridges/codex", + "category": "Integration", + "keywords": [ + "codex", + "openai", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, + { + "name": "performance-engineering", "description": "Performance engineering agents providing expertise in profiling, optimization, and scalability", - "source": "./plugins/disciplines/performance", + "source": "./plugins/disciplines/performance-engineering", "category": "Discipline", "keywords": ["performance", "optimization", "scalability", "agent"] }, @@ -987,9 +1061,9 @@ "keywords": ["php", "composer", "security", "web", "skill"] }, { - "name": "platform", + "name": "platform-engineering", "description": "Platform engineering agents providing expertise in developer platforms, IaC, and tooling", - "source": "./plugins/disciplines/platform", + "source": "./plugins/disciplines/platform-engineering", "category": "Discipline", "keywords": ["platform-engineering", "infrastructure", "devex", "agent"] }, @@ -1023,7 +1097,7 @@ "keywords": ["mcp", "playwright", "testing", "automation", "server"] }, { - "name": "plugin-development", + "name": "claude-plugin-development", "description": "Specialized agents for developing, testing, and maintaining Claude Code plugins with quality enforcement hooks.", "source": "./plugins/disciplines/claude-plugin-development", "category": "Discipline", @@ -1043,9 +1117,9 @@ ] }, { - "name": "product", + "name": "product-management", "description": "Agents specialized in product vision and strategy. Focuses on product direction, market positioning, and strategic planning.", - "source": "./plugins/disciplines/product", + "source": "./plugins/disciplines/product-management", "category": "Discipline", "keywords": ["product", "strategy", "vision", "planning", "agent"] }, @@ -1057,9 +1131,9 @@ "keywords": ["project-management", "coordination", "workflow"] }, { - "name": "prompts", + "name": "prompt-engineering", "description": "Agents specialized in prompt engineering and AI interaction. Focuses on effective prompt design and AI model optimization.", - "source": "./plugins/disciplines/prompts", + "source": "./plugins/disciplines/prompt-engineering", "category": "Discipline", "keywords": ["prompt-engineering", "ai", "llm", "agent"] }, @@ -1092,9 +1166,9 @@ "keywords": ["python", "types", "async", "skill"] }, { - "name": "quality", + "name": "quality-engineering", "description": "Agents specialized in quality assurance, testing strategies, and test architecture. Focuses on ensuring code quality and reliability.", - "source": "./plugins/disciplines/quality", + "source": "./plugins/disciplines/quality-engineering", "category": "Discipline", "keywords": ["quality", "testing", "qa", "reliability", "agent"] }, @@ -1260,9 +1334,9 @@ "keywords": ["scratch", "workspace", "gitignore", "temporary", "workflow"] }, { - "name": "security", + "name": "security-engineering", "description": "Agents specialized in security engineering and threat mitigation. Focuses on secure architecture, vulnerability assessment, and compliance.", - "source": "./plugins/disciplines/security", + "source": "./plugins/disciplines/security-engineering", "category": "Discipline", "keywords": ["security", "compliance", "vulnerability", "threat", "agent"] }, @@ -1296,6 +1370,20 @@ "server" ] }, + { + "name": "session-code-review", + "description": "Backpressure code review that validates changes against REVIEW.md before commits and session completion.", + "source": "./plugins/validation/session-code-review", + "category": "Validation", + "keywords": [ + "code-review", + "review", + "backpressure", + "quality", + "validation", + "review.md" + ] + }, { "name": "shellcheck", "description": "Validation and quality enforcement for Bash and shell scripts using ShellCheck with portability and error handling skills.", @@ -1325,9 +1413,9 @@ "keywords": ["sip", "voip", "telecommunications", "real-time"] }, { - "name": "sre", + "name": "site-reliability-engineering", "description": "Site Reliability Engineering discipline agent for reliability, monitoring, and incident response", - "source": "./plugins/disciplines/sre", + "source": "./plugins/disciplines/site-reliability-engineering", "category": "Discipline", "keywords": [ "sre", @@ -1475,9 +1563,9 @@ ] }, { - "name": "voip", + "name": "voip-engineering", "description": "VoIP engineering discipline for telecommunications and real-time communications systems", - "source": "./plugins/disciplines/voip", + "source": "./plugins/disciplines/voip-engineering", "category": "Discipline", "keywords": ["voip", "telecommunications", "sip", "rtp", "engineering"] }, @@ -1519,63 +1607,63 @@ { "name": "do-accessibility", "description": "Accessibility engineering agents providing expertise in WCAG, ARIA, and inclusive design", - "source": "./plugins/disciplines/accessibility", + "source": "./plugins/disciplines/accessibility-engineering", "category": "Discipline", "keywords": ["accessibility", "wcag", "inclusive-design", "agent"] }, { "name": "do-accessibility-engineering", "description": "Accessibility engineering agents providing expertise in WCAG, ARIA, and inclusive design", - "source": "./plugins/disciplines/accessibility", + "source": "./plugins/disciplines/accessibility-engineering", "category": "Discipline", "keywords": ["accessibility", "wcag", "inclusive-design", "agent"] }, { "name": "do-api", "description": "API engineering agents providing expertise in API design, contracts, and gateway patterns", - "source": "./plugins/disciplines/api", + "source": "./plugins/disciplines/api-engineering", "category": "Discipline", "keywords": ["api", "rest", "graphql", "agent"] }, { "name": "do-api-engineering", "description": "API engineering agents providing expertise in API design, contracts, and gateway patterns", - "source": "./plugins/disciplines/api", + "source": "./plugins/disciplines/api-engineering", "category": "Discipline", "keywords": ["api", "rest", "graphql", "agent"] }, { "name": "do-architecture", "description": "Agents for system design and architectural thinking.", - "source": "./plugins/disciplines/architecture", + "source": "./plugins/disciplines/system-architecture", "category": "Discipline", "keywords": ["architecture", "design", "patterns"] }, { "name": "do-backend", "description": "Agents specialized in backend development, API design, and system architecture. Focuses on scalable server-side solutions and data management.", - "source": "./plugins/disciplines/backend", + "source": "./plugins/disciplines/backend-development", "category": "Discipline", "keywords": ["backend", "api", "architecture", "data", "server", "agent"] }, { "name": "do-backend-development", "description": "Agents specialized in backend development, API design, and system architecture. Focuses on scalable server-side solutions and data management.", - "source": "./plugins/disciplines/backend", + "source": "./plugins/disciplines/backend-development", "category": "Discipline", "keywords": ["backend", "api", "architecture", "data", "server", "agent"] }, { "name": "do-blockchain", "description": "Blockchain development agents providing expertise in blockchain architecture, smart contracts, and Web3", - "source": "./plugins/disciplines/blockchain", + "source": "./plugins/disciplines/blockchain-development", "category": "Discipline", "keywords": ["blockchain", "web3", "smart-contracts", "agent"] }, { "name": "do-blockchain-development", "description": "Blockchain development agents providing expertise in blockchain architecture, smart contracts, and Web3", - "source": "./plugins/disciplines/blockchain", + "source": "./plugins/disciplines/blockchain-development", "category": "Discipline", "keywords": ["blockchain", "web3", "smart-contracts", "agent"] }, @@ -1589,21 +1677,21 @@ { "name": "do-compiler-development", "description": "Compiler development agents providing expertise in lexing, parsing, code generation, and language design", - "source": "./plugins/disciplines/compilers", + "source": "./plugins/disciplines/compiler-engineering", "category": "Discipline", "keywords": ["compiler", "language-design", "parsing", "agent"] }, { "name": "do-compilers", "description": "Compiler development agents providing expertise in lexing, parsing, code generation, and language design", - "source": "./plugins/disciplines/compilers", + "source": "./plugins/disciplines/compiler-engineering", "category": "Discipline", "keywords": ["compiler", "language-design", "parsing", "agent"] }, { "name": "do-content", "description": "Specialized agents for creating engaging content across various formats and target audiences. Focuses on storytelling, audience engagement, and platform-optimized writing.", - "source": "./plugins/disciplines/content", + "source": "./plugins/disciplines/content-creation", "category": "Discipline", "keywords": [ "content", @@ -1617,7 +1705,7 @@ { "name": "do-content-creator", "description": "Specialized agents for creating engaging content across various formats and target audiences. Focuses on storytelling, audience engagement, and platform-optimized writing.", - "source": "./plugins/disciplines/content", + "source": "./plugins/disciplines/content-creation", "category": "Discipline", "keywords": [ "content", @@ -1638,84 +1726,84 @@ { "name": "do-database-engineering", "description": "Database engineering agents providing expertise in schema design, query optimization, and reliability", - "source": "./plugins/disciplines/databases", + "source": "./plugins/disciplines/database-engineering", "category": "Discipline", "keywords": ["database", "sql", "query-optimization", "agent"] }, { "name": "do-databases", "description": "Database engineering agents providing expertise in schema design, query optimization, and reliability", - "source": "./plugins/disciplines/databases", + "source": "./plugins/disciplines/database-engineering", "category": "Discipline", "keywords": ["database", "sql", "query-optimization", "agent"] }, { "name": "do-documentation", "description": "Agents specialized in technical documentation and knowledge management. Focuses on documentation architecture and user-focused writing.", - "source": "./plugins/disciplines/documentation", + "source": "./plugins/disciplines/documentation-engineering", "category": "Discipline", "keywords": ["documentation", "technical-writing", "knowledge", "agent"] }, { "name": "do-embedded", "description": "Embedded development agents providing expertise in RTOS, firmware, and IoT", - "source": "./plugins/disciplines/embedded", + "source": "./plugins/disciplines/embedded-development", "category": "Discipline", "keywords": ["embedded-systems", "firmware", "iot", "agent"] }, { "name": "do-embedded-development", "description": "Embedded development agents providing expertise in RTOS, firmware, and IoT", - "source": "./plugins/disciplines/embedded", + "source": "./plugins/disciplines/embedded-development", "category": "Discipline", "keywords": ["embedded-systems", "firmware", "iot", "agent"] }, { "name": "do-frontend", "description": "Agents specialized in frontend development, UI design, and presentation engineering. Focuses on visual design, user experience, and interaction patterns.", - "source": "./plugins/disciplines/frontend", + "source": "./plugins/disciplines/frontend-development", "category": "Discipline", "keywords": ["frontend", "ui", "ux", "design", "presentation", "agent"] }, { "name": "do-frontend-development", "description": "Agents specialized in frontend development, UI design, and presentation engineering. Focuses on visual design, user experience, and interaction patterns.", - "source": "./plugins/disciplines/frontend", + "source": "./plugins/disciplines/frontend-development", "category": "Discipline", "keywords": ["frontend", "ui", "ux", "design", "presentation", "agent"] }, { "name": "do-game-development", "description": "Game development engineering agents providing expertise in engine architecture, gameplay systems, and performance optimization", - "source": "./plugins/disciplines/games", + "source": "./plugins/disciplines/game-development", "category": "Discipline", "keywords": ["game-development", "game-engine", "gameplay", "agent"] }, { "name": "do-games", "description": "Game development engineering agents providing expertise in engine architecture, gameplay systems, and performance optimization", - "source": "./plugins/disciplines/games", + "source": "./plugins/disciplines/game-development", "category": "Discipline", "keywords": ["game-development", "game-engine", "gameplay", "agent"] }, { "name": "do-graphics", "description": "Graphics engineering agents providing expertise in GPU programming, shaders, and rendering", - "source": "./plugins/disciplines/graphics", + "source": "./plugins/disciplines/graphics-engineering", "category": "Discipline", "keywords": ["graphics", "gpu", "shaders", "agent"] }, { "name": "do-graphics-engineering", "description": "Graphics engineering agents providing expertise in GPU programming, shaders, and rendering", - "source": "./plugins/disciplines/graphics", + "source": "./plugins/disciplines/graphics-engineering", "category": "Discipline", "keywords": ["graphics", "gpu", "shaders", "agent"] }, { "name": "do-infrastructure", "description": "Agents for operational excellence and reliability engineering.", - "source": "./plugins/disciplines/infrastructure", + "source": "./plugins/disciplines/infrastructure-engineering", "category": "Discipline", "keywords": ["devops", "sre", "deployment", "operations"] }, @@ -1736,70 +1824,70 @@ { "name": "do-mobile", "description": "Agents specialized in mobile development for iOS, Android, and cross-platform applications. Focuses on native and cross-platform mobile solutions.", - "source": "./plugins/disciplines/mobile", + "source": "./plugins/disciplines/mobile-development", "category": "Discipline", "keywords": ["mobile", "ios", "android", "development", "agent"] }, { "name": "do-mobile-development", "description": "Agents specialized in mobile development for iOS, Android, and cross-platform applications. Focuses on native and cross-platform mobile solutions.", - "source": "./plugins/disciplines/mobile", + "source": "./plugins/disciplines/mobile-development", "category": "Discipline", "keywords": ["mobile", "ios", "android", "development", "agent"] }, { "name": "do-network-engineering", "description": "Network engineering agents providing expertise in protocols, distributed systems, and service mesh", - "source": "./plugins/disciplines/networking", + "source": "./plugins/disciplines/network-engineering", "category": "Discipline", "keywords": ["network", "protocols", "distributed-systems", "agent"] }, { "name": "do-networking", "description": "Network engineering agents providing expertise in protocols, distributed systems, and service mesh", - "source": "./plugins/disciplines/networking", + "source": "./plugins/disciplines/network-engineering", "category": "Discipline", "keywords": ["network", "protocols", "distributed-systems", "agent"] }, { "name": "do-observability", "description": "Observability engineering agents providing expertise in tracing, monitoring, and logging", - "source": "./plugins/disciplines/observability", + "source": "./plugins/disciplines/observability-engineering", "category": "Discipline", "keywords": ["observability", "monitoring", "tracing", "agent"] }, { "name": "do-observability-engineering", "description": "Observability engineering agents providing expertise in tracing, monitoring, and logging", - "source": "./plugins/disciplines/observability", + "source": "./plugins/disciplines/observability-engineering", "category": "Discipline", "keywords": ["observability", "monitoring", "tracing", "agent"] }, { "name": "do-performance", "description": "Performance engineering agents providing expertise in profiling, optimization, and scalability", - "source": "./plugins/disciplines/performance", + "source": "./plugins/disciplines/performance-engineering", "category": "Discipline", "keywords": ["performance", "optimization", "scalability", "agent"] }, { "name": "do-performance-engineering", "description": "Performance engineering agents providing expertise in profiling, optimization, and scalability", - "source": "./plugins/disciplines/performance", + "source": "./plugins/disciplines/performance-engineering", "category": "Discipline", "keywords": ["performance", "optimization", "scalability", "agent"] }, { "name": "do-platform", "description": "Platform engineering agents providing expertise in developer platforms, IaC, and tooling", - "source": "./plugins/disciplines/platform", + "source": "./plugins/disciplines/platform-engineering", "category": "Discipline", "keywords": ["platform-engineering", "infrastructure", "devex", "agent"] }, { "name": "do-platform-engineering", "description": "Platform engineering agents providing expertise in developer platforms, IaC, and tooling", - "source": "./plugins/disciplines/platform", + "source": "./plugins/disciplines/platform-engineering", "category": "Discipline", "keywords": ["platform-engineering", "infrastructure", "devex", "agent"] }, @@ -1813,14 +1901,14 @@ { "name": "do-product", "description": "Agents specialized in product vision and strategy. Focuses on product direction, market positioning, and strategic planning.", - "source": "./plugins/disciplines/product", + "source": "./plugins/disciplines/product-management", "category": "Discipline", "keywords": ["product", "strategy", "vision", "planning", "agent"] }, { "name": "do-product-management", "description": "Agents specialized in product vision and strategy. Focuses on product direction, market positioning, and strategic planning.", - "source": "./plugins/disciplines/product", + "source": "./plugins/disciplines/product-management", "category": "Discipline", "keywords": ["product", "strategy", "vision", "planning", "agent"] }, @@ -1834,49 +1922,49 @@ { "name": "do-prompt-engineering", "description": "Agents specialized in prompt engineering and AI interaction. Focuses on effective prompt design and AI model optimization.", - "source": "./plugins/disciplines/prompts", + "source": "./plugins/disciplines/prompt-engineering", "category": "Discipline", "keywords": ["prompt-engineering", "ai", "llm", "agent"] }, { "name": "do-prompts", "description": "Agents specialized in prompt engineering and AI interaction. Focuses on effective prompt design and AI model optimization.", - "source": "./plugins/disciplines/prompts", + "source": "./plugins/disciplines/prompt-engineering", "category": "Discipline", "keywords": ["prompt-engineering", "ai", "llm", "agent"] }, { "name": "do-quality", "description": "Agents specialized in quality assurance, testing strategies, and test architecture. Focuses on ensuring code quality and reliability.", - "source": "./plugins/disciplines/quality", + "source": "./plugins/disciplines/quality-engineering", "category": "Discipline", "keywords": ["quality", "testing", "qa", "reliability", "agent"] }, { "name": "do-quality-assurance", "description": "Agents specialized in quality assurance, testing strategies, and test architecture. Focuses on ensuring code quality and reliability.", - "source": "./plugins/disciplines/quality", + "source": "./plugins/disciplines/quality-engineering", "category": "Discipline", "keywords": ["quality", "testing", "qa", "reliability", "agent"] }, { "name": "do-security", "description": "Agents specialized in security engineering and threat mitigation. Focuses on secure architecture, vulnerability assessment, and compliance.", - "source": "./plugins/disciplines/security", + "source": "./plugins/disciplines/security-engineering", "category": "Discipline", "keywords": ["security", "compliance", "vulnerability", "threat", "agent"] }, { "name": "do-security-engineering", "description": "Agents specialized in security engineering and threat mitigation. Focuses on secure architecture, vulnerability assessment, and compliance.", - "source": "./plugins/disciplines/security", + "source": "./plugins/disciplines/security-engineering", "category": "Discipline", "keywords": ["security", "compliance", "vulnerability", "threat", "agent"] }, { "name": "do-site-reliability-engineering", "description": "Site Reliability Engineering discipline agent for reliability, monitoring, and incident response", - "source": "./plugins/disciplines/sre", + "source": "./plugins/disciplines/site-reliability-engineering", "category": "Discipline", "keywords": [ "sre", @@ -1890,7 +1978,7 @@ { "name": "do-sre", "description": "Site Reliability Engineering discipline agent for reliability, monitoring, and incident response", - "source": "./plugins/disciplines/sre", + "source": "./plugins/disciplines/site-reliability-engineering", "category": "Discipline", "keywords": [ "sre", @@ -1904,21 +1992,21 @@ { "name": "do-technical-documentation", "description": "Agents specialized in technical documentation and knowledge management. Focuses on documentation architecture and user-focused writing.", - "source": "./plugins/disciplines/documentation", + "source": "./plugins/disciplines/documentation-engineering", "category": "Discipline", "keywords": ["documentation", "technical-writing", "knowledge", "agent"] }, { "name": "do-voip", "description": "VoIP engineering discipline for telecommunications and real-time communications systems", - "source": "./plugins/disciplines/voip", + "source": "./plugins/disciplines/voip-engineering", "category": "Discipline", "keywords": ["voip", "telecommunications", "sip", "rtp", "engineering"] }, { "name": "do-voip-engineering", "description": "VoIP engineering discipline for telecommunications and real-time communications systems", - "source": "./plugins/disciplines/voip", + "source": "./plugins/disciplines/voip-engineering", "category": "Discipline", "keywords": ["voip", "telecommunications", "sip", "rtp", "engineering"] }, @@ -2484,6 +2572,36 @@ "server" ] }, + { + "name": "hashi-antigravity", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, skills, disciplines) to work with Google Antigravity IDE via MCP server.", + "source": "./plugins/bridges/antigravity", + "category": "Integration", + "keywords": [ + "antigravity", + "google", + "bridge", + "mcp", + "hooks", + "validation", + "skills" + ] + }, + { + "name": "hashi-gemini-cli", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with Gemini CLI by translating Gemini CLI events into Han hook invocations.", + "source": "./plugins/bridges/gemini-cli", + "category": "Integration", + "keywords": [ + "gemini-cli", + "gemini", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, { "name": "hashi-opencode", "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with OpenCode by translating OpenCode events into Claude Code hook invocations.", @@ -2498,6 +2616,36 @@ "validation" ] }, + { + "name": "hashi-kiro", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with Kiro CLI by translating Kiro hook events into Han hook executions.", + "source": "./plugins/bridges/kiro", + "category": "Integration", + "keywords": [ + "kiro", + "kiro-cli", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, + { + "name": "hashi-codex", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, context injection, orchestration) to work with OpenAI Codex CLI by translating Codex events into Claude Code hook invocations.", + "source": "./plugins/bridges/codex", + "category": "Integration", + "keywords": [ + "codex", + "openai", + "bridge", + "hooks", + "compatibility", + "claude-code", + "validation" + ] + }, { "name": "hashi-playwright-mcp", "description": "MCP server configuration for Playwright integration providing test automation, browser control, and test execution capabilities.", diff --git a/.claude/commands/create-blog-post.md b/.claude/commands/create-blog-post.md index 5d06f7e24..2ef8cc04f 100644 --- a/.claude/commands/create-blog-post.md +++ b/.claude/commands/create-blog-post.md @@ -14,7 +14,7 @@ If no topic provided, ask the user what angle they want to cover. ## MCP Server Required -This command requires the `hashi-reddit` MCP server to be installed and running. +This command requires the `reddit` MCP server to be installed and running. ## Phase 1: Research Reddit Discussions @@ -68,9 +68,9 @@ Analyze how Han's features address the discovered pain points: | Reddit Pain Point | Han Solution | |-------------------|--------------| -| Inconsistent code quality | Jutsu validation hooks | -| Lack of specialization | Do discipline agents | -| Poor tool integration | Hashi MCP bridges | +| Inconsistent code quality | Validation plugin hooks | +| Lack of specialization | Discipline plugin agents | +| Poor tool integration | Service plugin MCP integrations | | No memory across sessions | Memory system | | Uncalibrated confidence | Metrics tracking | | Missing documentation | Blueprints system | diff --git a/.claude/commands/create-plugin.md b/.claude/commands/create-plugin.md index 8824fc358..b2f07cce6 100644 --- a/.claude/commands/create-plugin.md +++ b/.claude/commands/create-plugin.md @@ -1,5 +1,5 @@ --- -description: Create a new Han plugin (jutsu, hashi, or do) +description: Create a new Han plugin (language, validation, service, tool, or discipline) --- # Create a Han Plugin @@ -10,19 +10,19 @@ Create a new plugin for: $ARGUMENTS First, determine what type of plugin to create based on the user's request: -### Jutsu (術 - Technique) +### Language/Validation Plugin **Use when**: The plugin provides validation hooks for a technology (linting, type-checking, testing, formatting) - Examples: biome, typescript, eslint, rust, playwright - Key feature: Runs validation commands on Stop events - Structure: `validation/{name}/` or `languages/{name}/` or `tools/{name}/` -### Hashi (橋 - Bridge) +### Service Plugin **Use when**: The plugin provides MCP server integration with an external service - Examples: github, gitlab, reddit, playwright-mcp, sentry - Key feature: Connects Claude Code to external APIs via MCP - Structure: `services/{name}/` -### Do (道 - The Way) +### Discipline Plugin **Use when**: The plugin provides specialized agents for a discipline - Examples: frontend-development, accessibility, content writing - Key feature: Contains agent definitions for domain expertise @@ -49,7 +49,7 @@ All plugins share this base structure: --- -## Jutsu Plugin Template +## Language/Validation Plugin Template For validation/quality enforcement plugins: @@ -109,7 +109,7 @@ memory: null --- -## Hashi Plugin Template +## Service Plugin Template For MCP server integrations: @@ -178,7 +178,7 @@ memory: null --- -## Do Plugin Template +## Discipline Plugin Template For discipline/agent plugins: @@ -259,19 +259,19 @@ Summon this agent when: - Include comprehensive README.md - Test before submitting -### Jutsu (Validation) +### Language/Validation - Use `--fail-fast` for quick feedback - Validate, don't auto-fix - Support monorepos with `dirs_with` - Use `if_changed` for caching -### Hashi (MCP) +### Service (MCP) - Prefer HTTP MCP when available (no install needed) - Document required environment variables - Include memory provider for team knowledge - Add commands for common workflows -### Do (Agents) +### Discipline (Agents) - Create 3-5 focused agents per discipline - Write detailed agent definitions (1000+ words) - Include when/when-not to invoke diff --git a/.claude/commands/research-new-features.md b/.claude/commands/research-new-features.md index b7e5dd932..154534ac9 100644 --- a/.claude/commands/research-new-features.md +++ b/.claude/commands/research-new-features.md @@ -8,7 +8,7 @@ Research feature requests, pain points, and community discussions about Claude A ## MCP Server Required -This command requires the `hashi-reddit` MCP server to be installed and running. +This command requires the `reddit` MCP server to be installed and running. ## Target Subreddits diff --git a/.claude/han.yml b/.claude/han.yml index 01f791214..0967ef424 100644 --- a/.claude/han.yml +++ b/.claude/han.yml @@ -1 +1 @@ -hanBinary: bun "$(git rev-parse --show-toplevel)/packages/han/lib/main.ts" +{} diff --git a/.claude/rules/blueprints/blueprints-index.md b/.claude/rules/blueprints/blueprints-index.md index c54dfb01d..f413d23fe 100644 --- a/.claude/rules/blueprints/blueprints-index.md +++ b/.claude/rules/blueprints/blueprints-index.md @@ -4,7 +4,7 @@ Technical documentation for this project's architecture and systems. ## When to Consult Blueprints -Before modifying system architecture, use `search_blueprints` and `read_blueprint` to understand: +Before modifying system architecture, use Glob and Read on the `blueprints/` directory to understand: - Current design decisions and rationale - Integration points and dependencies - Established patterns to follow @@ -21,7 +21,7 @@ Consult blueprints when working on: ## After Modifications -Update blueprints via `write_blueprint` when you: +Update blueprints using the Write tool on `blueprints/{name}.md` when you: - Add new systems or major features - Change architectural patterns - Discover undocumented conventions @@ -31,27 +31,26 @@ Update blueprints via `write_blueprint` when you: | Blueprint | Summary | |-----------|---------| -| blueprint-system | MCP-based blueprint management with frontmatter metadata | -| browse-architecture | Han browse command architecture - GraphQL + Vite unified server | -| build-deployment | CI/CD automation for releases and deployments | -| checkpoint-system | Session and agent checkpoints for scoped hook execution | +| blueprint-system | Skills-based blueprint management with frontmatter metadata | +| browse-architecture | Han browse command architecture - remote dashboard with local GraphQL coordinator | +| build-deployment | CI/CD with auto-versioning, cross-platform builds from Linux, npm OIDC publishing, and Railway deployment | | cli-architecture | Entry point, command structure, and CLI framework | -| cli-interface | Interactive CLI with AI-powered plugin discovery | -| coordinator-daemon | Coordinator daemon architecture with GraphQL server, lazy startup, and unified data access | -| coordinator-data-layer | Single-coordinator pattern for indexing JSONL transcripts to SQLite database | -| distribution-architecture | NPM wrapper + platform-specific Bun binaries distribution model | +| cli-interface | Interactive CLI with Commander.js, Ink UI, and AI-powered plugin discovery via Agent SDK | +| coordinator-daemon | Coordinator daemon with GraphQL server, lazy startup, file watching, and unified data access via han-native | +| coordinator-data-layer | JSONL transcript indexing to SQLite via han-native with FTS5 search and DataLoader-compatible batch queries | +| distribution-architecture | NPM wrapper with platform-specific Bun binaries, curl installer, and Homebrew distribution | | han-events-logging | Session-scoped logging of Han events (hooks, MCP calls) to JSONL files indexed into SQLite for Browse UI visibility | -| han-memory-system | Complete architecture and implementation of Han Memory - five-layer semantic memory with synthesis via Agent SDK, streaming output, and citation-backed answers | +| han-memory-system | Memory system with Agent SDK synthesis, multi-strategy search (FTS/Vector/Hybrid), plugin-discovered MCP providers, and read-only Memory Agent for autonomous research | | hook-result-parent-linkage | Hook result messages need parent_id linkage to hook run messages | -| hook-system | Complete hook lifecycle from definition to execution with centralized orchestration, checkpoint filtering, and cross-plugin dependencies | -| marketplace | Central plugin registry and distribution | +| hook-system | Direct plugin hook execution via Claude Code with no centralized orchestration | +| marketplace | Central plugin registry with canonical names, backward-compatible aliases, and category-based organization | | mcp-server | Model Context Protocol server exposing plugin tools | -| metrics-system | Self-reporting agent performance tracking with validation | -| native-module | High-performance Rust bindings for hook operations | -| plugin-directory | Filesystem organization and naming conventions | -| plugin-installation | Installation flow and marketplace integration | -| plugin-types | Bushido, Jutsu, Do, and Hashi plugin categories | -| rust-graphql-migration | Migration plan for tight DB-GraphQL coupling with Seaography, Relay connections, and sqlite3_update_hook subscriptions | +| metrics-system | Automatic task tracking via Claude Code native TaskCreate/TaskUpdate indexed from JSONL transcripts | +| native-module | Rust NAPI-RS bindings providing complete database layer, JSONL indexing, FTS search, and coordinator management | +| plugin-directory | Filesystem organization with category-based directories and short plugin identifiers | +| plugin-installation | Multi-scope plugin installation with auto-detection, three-tier UX fallback, and npm distribution | +| plugin-types | Plugin categories organized by function: core, languages, validation, services, tools, frameworks, disciplines | +| rust-graphql-migration | [PROPOSAL] Migration plan for tight DB-GraphQL coupling with Seaography, Relay connections, and sqlite3_update_hook subscriptions | | sdlc-coverage | AI-native engineering workflow alignment with OpenAI's framework | | settings-management | Multi-scope settings with precedence rules | | validation | Configuration validation and schema enforcement | diff --git a/.claude/rules/browse/always-local-flag.md b/.claude/rules/browse/always-local-flag.md new file mode 100644 index 000000000..e165b55a6 --- /dev/null +++ b/.claude/rules/browse/always-local-flag.md @@ -0,0 +1,39 @@ +# Always Use --local Flag for Browse (CRITICAL) + +## Rule + +When starting `han browse` for local development or verification, **ALWAYS** use the `--local` flag: + +```bash +cd packages/han && bun run lib/main.ts browse --local +``` + +## Why + +Without `--local`, the browse command opens the **remote** dashboard at `dashboard.local.han.guru` and immediately exits. This does NOT serve the local browse-client code — it just opens the deployed Railway site in the browser. + +The `--local` flag starts a **local Vite dev server** on port 41956 that: +- Serves the local browse-client source with HMR +- Starts relay-compiler in watch mode +- Reflects your uncommitted code changes immediately + +## NEVER Do This + +```bash +# WRONG — opens remote dashboard, doesn't serve local code +bun run lib/main.ts browse +``` + +## Verification After Changes + +```bash +# Kill existing +lsof -ti:41956 | xargs kill -9 2>/dev/null + +# Start LOCAL server +cd packages/han && bun run lib/main.ts browse --local & + +# Wait and verify +sleep 12 +curl -s -o /dev/null -w "%{http_code}" http://localhost:41956/ # Should return 200 +``` diff --git a/.claude/rules/browse/local-dashboard.md b/.claude/rules/browse/local-dashboard.md new file mode 100644 index 000000000..c4fc15e62 --- /dev/null +++ b/.claude/rules/browse/local-dashboard.md @@ -0,0 +1,50 @@ +# Local Dashboard Development + +## Rule: Always Open the Local Dashboard When Developing Locally + +When developing or testing browse-client or GraphQL changes locally, **always use the local dashboard** served by `han browse`, not the remote Railway deployment. + +## How to Start + +```bash +# From the han package directory +cd packages/han && bun run lib/main.ts browse +``` + +This starts the coordinator with: +- GraphQL API at `https://localhost:41957/graphql` +- Web UI at `http://localhost:41956/` + +## Verification After Changes + +After modifying GraphQL types, resolvers, or browse-client components: + +```bash +# Kill existing processes +lsof -ti:41956 | xargs kill -9 2>/dev/null +lsof -ti:41957 | xargs kill -9 2>/dev/null + +# Start fresh +cd packages/han && bun run lib/main.ts browse & +sleep 8 + +# Verify +curl -s -o /dev/null -w "%{http_code}" http://localhost:41956/ +curl -sk https://localhost:41957/graphql -X POST \ + -H "Content-Type: application/json" \ + -d '{"query":"{ __typename }"}' | grep -q "data" && echo "GraphQL OK" +``` + +## Why Local, Not Remote + +1. **Immediate feedback** — See changes without deploying to Railway +2. **Real data** — Local coordinator reads from your actual `~/.han/han.db` +3. **GraphQL iteration** — Test schema changes against the local server +4. **No deployment lag** — Railway builds add delays to the feedback loop + +## When to Use Remote + +Only use the remote dashboard (`dashboard.local.han.guru`) for: +- Final verification before merging +- Testing TLS/CORS behavior in production-like environment +- Demonstrating to others diff --git a/.claude/rules/cc-feature-support.md b/.claude/rules/cc-feature-support.md index 0de288f10..1f9e99955 100644 --- a/.claude/rules/cc-feature-support.md +++ b/.claude/rules/cc-feature-support.md @@ -4,11 +4,82 @@ Han maintains feature parity with Claude Code releases. This file tracks which v ## Current Support Level -**Supported up to:** Claude Code 2.1.10 +**Supported up to:** Claude Code 2.1.63 ## Features Tracked -### 2.1.10 (Current) +### 2.1.63 (Current) +- **Task tool renamed to Agent** - The `Task` tool (for spawning subagents) is now called `Agent`. PreToolUse/PostToolUse matchers should match both `Agent|Task` for backward compatibility. +- HTTP hooks (`type: "http"`) - POST JSON to a URL, receive JSON response instead of running shell commands +- `/simplify` and `/batch` bundled slash commands +- Project configs and auto memory shared across git worktrees of same repository +- `ENABLE_CLAUDEAI_MCP_SERVERS=false` env var to opt out of claude.ai MCP servers +- Fixed `/clear` not resetting cached skills +- Massive memory leak fix batch (git root detection, JSON parsing, MCP caches, WebSocket listeners, hooks config, bridge polling, etc.) + +### 2.1.59 +- Auto-memory: Claude automatically saves useful context, managed with `/memory` command +- `/copy` command with interactive picker for selecting individual code blocks +- Improved "always allow" prefix suggestions for compound bash commands (per-subcommand prefixes) +- `CLAUDE_CODE_DISABLE_AUTO_MEMORY=1` env var to disable auto memory + +### 2.1.51 +- `claude remote-control` subcommand for local environment serving from any device +- Custom npm registries and version pinning when installing plugins +- Managed settings via macOS plist or Windows Registry +- `CLAUDE_CODE_ACCOUNT_UUID`, `CLAUDE_CODE_USER_EMAIL`, `CLAUDE_CODE_ORGANIZATION_UUID` env vars for SDK +- `CLAUDE_CODE_PLUGIN_GIT_TIMEOUT_MS` env var (default now 120s, was 30s) +- Tool results > 50K chars persisted to disk (was 100K) +- BashTool skips login shell by default when shell snapshot available +- Security fix: `statusLine` and `fileSuggestion` hook commands require workspace trust + +### 2.1.50 +- WorktreeCreate hook event (fires on worktree creation, receives `name` slug, must print worktree path to stdout) +- WorktreeRemove hook event (fires on worktree removal, receives `worktree_path`, cannot block) +- LSP `startupTimeout` configuration support +- `claude agents` CLI command (list all configured agents) +- 1M context window for Opus 4.6 fast mode +- `CLAUDE_CODE_SIMPLE` mode strips down to minimal experience +- Fix: custom agents/skills now discovered when running from a git worktree + +### 2.1.49 +- ConfigChange hook event (fired when Claude Code configuration is modified, matcher on config source) +- Background agents (`background: true` in agent frontmatter) +- Worktree isolation (`isolation: worktree` in agent frontmatter) + +### 2.1.47 +- `last_assistant_message` field in Stop/SubagentStop hook inputs + +### 2.1.45 +- Sonnet 4.6 model support + +### 2.1.36 +- Fast mode for Opus 4.6 (same model, faster output) + +### 2.1.33 +- TeammateIdle hook event (fired when a teammate agent goes idle) +- TaskCompleted hook event (fired when a task is marked completed) +- Agent `memory` frontmatter field (`project`, `session`, etc.) +- `Agent(agent_type)` restriction for spawning specific agent types (was `Task(agent_type)` before 2.1.63) + +### 2.1.32 +- Agent Teams (TeamCreate, SendMessage, multi-agent coordination) +- Opus 4.6 model support +- Automatic memories (persistent auto memory directory) + +### 2.1.27 +- PR linkage (`--from-pr` flag, auto-link on `gh pr create`) +- PR-linked sessions for resuming work from pull requests + +### 2.1.20 +- Task deletion (`status: "deleted"` in TaskUpdate) + +### 2.1.16 +- Task system overhaul with dependencies (blocks/blockedBy) +- TaskCreate, TaskUpdate, TaskGet, TaskList tools +- Task status workflow: pending → in_progress → completed + +### 2.1.10 - Setup hook (`--init`, `--init-only`, `--maintenance` flags) - Session slug (human-readable session names like "snug-dreaming-knuth") - Token usage in messages (input_tokens, output_tokens, cache_read/creation_tokens) @@ -20,6 +91,37 @@ Han maintains feature parity with Claude Code releases. This file tracks which v - MCP tool calls - Session summaries and compaction +## Complete Hook Events Reference (as of 2.1.63) + +| Event | Matcher | Hook Types | Since | +|-------|---------|------------|-------| +| SessionStart | startup/resume/clear/compact | command only | 2.0.x | +| UserPromptSubmit | No | command, http, prompt, agent | 2.0.x | +| PreToolUse | tool name | command, http, prompt, agent | 2.0.x | +| PermissionRequest | tool name | command, http, prompt, agent | ~2.1.50 | +| PostToolUse | tool name | command, http, prompt, agent | 2.0.x | +| PostToolUseFailure | tool name | command, http, prompt, agent | ~2.1.50 | +| Notification | notification type | command only | 2.0.x | +| SubagentStart | agent type | command only | 2.0.x | +| SubagentStop | agent type | command, http, prompt, agent | 2.0.x | +| Stop | No | command, http, prompt, agent | 2.0.x | +| TeammateIdle | No | command only | 2.1.33 | +| TaskCompleted | No | command, http, prompt, agent | 2.1.33 | +| ConfigChange | config source | command only | 2.1.49 | +| WorktreeCreate | No | command only | 2.1.50 | +| WorktreeRemove | No | command only | 2.1.50 | +| PreCompact | manual/auto | command only | ~2.1.50 | +| SessionEnd | exit reason | command only | 2.0.x | + +## Hook Types (as of 2.1.63) + +| Type | Description | Since | +|------|-------------|-------| +| command | Execute shell command | 2.0.x | +| prompt | Return text to agent | 2.0.x | +| agent | Spawn agent hook | 2.1.x | +| http | POST JSON to URL, receive JSON response | 2.1.63 | + ## Notes - **Turn duration**: Calculated client-side, not in JSONL - no data to index diff --git a/.claude/rules/database.md b/.claude/rules/database.md new file mode 100644 index 000000000..af67107a5 --- /dev/null +++ b/.claude/rules/database.md @@ -0,0 +1,3 @@ +# Database Conventions +- Han Team Platform (#42) + - Han Team Platform (#42) diff --git a/.claude/rules/graphql/greenfairy-filter-pattern.md b/.claude/rules/graphql/greenfairy-filter-pattern.md new file mode 100644 index 000000000..80830cd81 --- /dev/null +++ b/.claude/rules/graphql/greenfairy-filter-pattern.md @@ -0,0 +1,184 @@ +# GreenFairy Filter Pattern (CRITICAL) + +## Overview + +Han uses a **GreenFairy-style** auto-generated filter system inspired by Hasura/Seaography. All GraphQL connections and list fields MUST use structured filter input types instead of ad-hoc arguments. + +## Core Rules + +### 1. NEVER Add Ad-Hoc Filter Arguments to Connection Fields + +```graphql +# WRONG — ad-hoc arguments +sessions(projectId: String, repoId: String, status: String): SessionConnection! + +# CORRECT — structured filter input +sessions(filter: SessionFilter, orderBy: SessionOrderBy): SessionConnection! +``` + +Ad-hoc arguments like `projectId`, `repoId`, `status`, `userId`, `worktreeName` on connection fields are **violations**. All filtering goes through the `filter` input type. + +### 2. Underscore-Prefixed Operators + +All filter operators MUST start with an underscore, following the Hasura/GreenFairy convention: + +| Operator | GraphQL Name | Rust Field | +|----------|-------------|------------| +| Equal | `_eq` | `eq` | +| Not equal | `_ne` | `ne` | +| Greater than | `_gt` | `gt` | +| Greater than or equal | `_gte` | `gte` | +| Less than | `_lt` | `lt` | +| Less than or equal | `_lte` | `lte` | +| Contains | `_contains` | `contains` | +| Starts with | `_startsWith` | `starts_with` | +| Ends with | `_endsWith` | `ends_with` | +| In list | `_in` | `in_list` | +| Not in list | `_notIn` | `not_in` | +| Is null | `_isNull` | `is_null` | +| Logical AND | `_and` | `and` | +| Logical OR | `_or` | `or` | +| Logical NOT | `_not` | `not` | + +### 3. Association Filtering + +Filters MUST support filtering through relationships (associations). This enables queries like: + +```graphql +# Filter sessions by their project's repo +sessions(filter: { + project: { + repo: { + id: { _eq: "some-repo-id" } + } + } +}) { + edges { node { id } } +} + +# Filter messages by their session's project +messages(filter: { + session: { + projectId: { _eq: "some-project-id" } + } +}) { + edges { node { id } } +} +``` + +Association fields on filters are **nested filter types** for the related entity. They translate to `EXISTS (SELECT 1 FROM related_table WHERE ...)` subqueries in SQL. + +### 4. Connection Field Signatures + +Connection fields should only accept: +- `first` / `after` / `last` / `before` — Relay pagination +- `filter` — The auto-generated `{Entity}Filter` input type +- `orderBy` — The auto-generated `{Entity}OrderBy` input type + +```graphql +type Query { + sessions( + first: Int + after: String + last: Int + before: String + filter: SessionFilter + orderBy: SessionOrderBy + ): SessionConnection! +} +``` + +No other arguments. + +### 5. Non-Connection List Fields + +Resolver fields that return computed data (not direct entity lists) like `metrics()`, `activity()`, `dashboardAnalytics()` may accept scope arguments — but prefer using a filter input when possible. + +## Implementation + +### EntityFilter Derive Macro + +The `#[derive(EntityFilter)]` macro generates filter and order-by types from a source struct: + +```rust +#[derive(han_graphql_derive::EntityFilter)] +#[entity_filter( + entity = "han_db::entities::sessions::Entity", + columns = "han_db::entities::sessions::Column", +)] +struct SessionFilterSource { + id: String, + project_id: Option, + status: Option, + slug: Option, +} +``` + +This generates: +- `SessionFilter` — InputObject with field-level filters + `_and`/`_or`/`_not` +- `SessionOrderBy` — InputObject with `OrderDirection` per field + +### Association Fields + +Association fields are declared as `()` marker fields with `#[entity_filter(assoc(...))]` on the source struct. The macro generates the nested filter field and `IN (SELECT ...)` subquery condition automatically: + +```rust +#[derive(han_graphql_derive::EntityFilter)] +#[entity_filter( + entity = "han_db::entities::sessions::Entity", + columns = "han_db::entities::sessions::Column", +)] +struct SessionFilterSource { + id: String, + project_id: Option, + status: Option, + + /// Association: sessions.project_id IN (SELECT id FROM projects WHERE ...) + #[entity_filter(assoc( + filter = "crate::types::project::ProjectFilter", + local_column = "ProjectId", + foreign_entity = "han_db::entities::projects::Entity", + foreign_column = "han_db::entities::projects::Column::Id", + ))] + project: (), +} +``` + +This generates a `SessionFilter` with a `project: Option` field that enables queries like: +```graphql +sessions(filter: { project: { repoId: { _eq: "some-repo-id" } } }) +``` + +**All filter types, including associations, MUST be macro-generated. Never hand-write filter structs.** See `graphql/macro-generated-filters.md`. + +### Filter Application + +Filters are applied via the `to_condition()` method which produces a SeaORM `Condition` tree: + +```rust +if let Some(ref filter) = filter { + let condition = filter.to_condition(); + query = query.filter(condition); +} +``` + +## Files + +| File | Purpose | +|------|---------| +| `han-rs/crates/han-api/src/filters/types.rs` | Scalar filter types (StringFilter, IntFilter, etc.) | +| `han-rs/crates/han-api/src/filters/apply.rs` | ApplyFilter trait implementations | +| `han-rs/crates/han-api/src/filters/ordering.rs` | OrderDirection enum | +| `han-rs/crates/han-graphql-derive/src/entity_filter.rs` | EntityFilter derive macro | +| `han-rs/crates/han-api/src/types/sessions/mod.rs` | SessionFilter definition | +| `han-rs/crates/han-api/src/types/messages/mod.rs` | MessageFilter definition | + +## Violations Checklist + +When auditing the schema, check for: + +1. Connection fields with ad-hoc filter arguments (anything besides `first`/`after`/`last`/`before`/`filter`/`orderBy`) +2. Filter operators without underscore prefix +3. Missing association filters on entity filters +4. `_filter` / `_order_by` prefixed parameters that are intentionally unused (dead code) +5. Scalar filter arguments on computed resolvers that should use entity filters instead diff --git a/.claude/rules/graphql/macro-generated-filters.md b/.claude/rules/graphql/macro-generated-filters.md new file mode 100644 index 000000000..54a77db6a --- /dev/null +++ b/.claude/rules/graphql/macro-generated-filters.md @@ -0,0 +1,107 @@ +# Macro-Generated Filters (CRITICAL) + +## Rule: ALL Filter Types MUST Be Macro-Generated + +Every GraphQL filter type (`{Entity}Filter`, `{Entity}OrderBy`) and its SQL condition logic MUST be generated by derive macros. **NEVER hand-write filter structs, `to_condition()` impls, or `apply()` methods.** + +## Why + +1. **Consistency** — Every entity filter follows the same pattern with zero drift +2. **Correctness** — The macro generates correct SQL conditions from type information +3. **Maintainability** — Adding a column to the entity automatically extends the filter +4. **Hasura/GreenFairy parity** — The macro IS the filter generation system, like Hasura's schema engine + +## Available Macros + +### `#[derive(EntityFilter)]` + +For entities where the GraphQL object type is separately defined (e.g., `SessionData` with `#[Object]`): + +```rust +#[derive(han_graphql_derive::EntityFilter)] +#[entity_filter( + entity = "han_db::entities::sessions::Entity", + columns = "han_db::entities::sessions::Column", +)] +pub struct SessionFilterSource { + pub id: String, + pub project_id: Option, + pub status: Option, + + // Association: nested filter through related entity + #[entity_filter(assoc( + filter = "crate::types::project::ProjectFilter", + local_column = "ProjectId", + foreign_entity = "han_db::entities::projects::Entity", + foreign_column = "han_db::entities::projects::Column::Id", + ))] + pub project: (), +} +``` + +### `#[derive(GraphQLEntity)]` + +For entities where everything is generated (SimpleObject + From + Filter + OrderBy): + +```rust +#[derive(GraphQLEntity)] +#[graphql_entity( + model = "projects::Model", + entity = "projects::Entity", + columns = "projects::Column", + type_name = "Project", +)] +pub struct Project { + pub slug: String, + pub name: String, + pub repo_id: Option, +} +``` + +## Association Filtering + +Associations are declared as `()` marker fields with `#[entity_filter(assoc(...))]`: + +```rust +#[entity_filter(assoc( + filter = "crate::types::project::ProjectFilter", // Related entity's filter type + local_column = "ProjectId", // Column on THIS entity + foreign_entity = "han_db::entities::projects::Entity", // Related SeaORM entity + foreign_column = "han_db::entities::projects::Column::Id", // Column to SELECT from related +))] +pub project: (), +``` + +This generates: +- A `project: Option` field on the filter struct +- An `IN (SELECT id FROM projects WHERE ...)` subquery condition in `to_condition()` + +## FORBIDDEN Patterns + +```rust +// NEVER hand-write a filter struct +pub struct SessionFilter { // BAD — must be macro-generated + pub id: Option, + pub project: Option, + // ... +} + +// NEVER hand-write to_condition() +impl SessionFilter { // BAD — must be macro-generated + pub fn to_condition(&self) -> Condition { ... } +} + +// NEVER hand-write OrderBy apply() +impl SessionOrderBy { // BAD — must be macro-generated + pub fn apply(&self, ...) { ... } +} +``` + +## When You Need New Filter Behavior + +1. **New column filter** → Add field to the `FilterSource` struct +2. **New association** → Add `()` marker field with `#[entity_filter(assoc(...))]` +3. **New scalar operator** → Extend `filters/types.rs` and `filters/apply.rs` +4. **New macro feature** → Extend `han-graphql-derive/src/entity_filter.rs` + +**Never work around the macro. Extend it.** diff --git a/.claude/rules/graphql/message-types.md b/.claude/rules/graphql/message-types.md index 4ba454585..8d6ed9f29 100644 --- a/.claude/rules/graphql/message-types.md +++ b/.claude/rules/graphql/message-types.md @@ -1,34 +1,49 @@ -# Message Types - Critical Rules +# Message Types - Timeline Display Rules -## NEVER Include Result Messages in Session.messages Connection +## All Messages Appear Chronologically in Timeline -**RULE: Result-type messages (messages that are follow-ups/responses to other messages) MUST NEVER appear in the `Session.messages` connection.** +All message types — including result messages — appear in the `Session.messages` connection in chronological order. This gives users a complete view of what happened during a session. Result messages include: -1. **ToolResultMessage** - Response to ToolUseBlock (linked via `toolCallId`) +1. **ToolResultUserMessage** - User messages containing tool_result content blocks 2. **McpToolResultMessage** - Response to McpToolCallMessage (linked via `callId`) 3. **ExposedToolResultMessage** - Response to ExposedToolCallMessage (linked via `callId`) 4. **HookResultMessage** - Response to HookRunMessage (linked via `hookRunId`) -5. **Any message whose purpose is to respond to a parent message** +5. **SentimentAnalysisMessage** - Sentiment analysis of user messages -These result types MUST be: -- Resolved as fields ON their parent type (e.g., `ToolUseBlock.result`, `HookRunMessage.result`) -- Loaded via DataLoader using their correlation ID -- NEVER shown as separate messages in the timeline -- NEVER returned from the messages connection +## Dual Access Pattern -## Correct Pattern +Result messages are accessible in two ways: + +1. **In the timeline** — shown chronologically with subway line connectors linking them visually to their parent messages +2. **As fields on parent types** — e.g., `ToolUseBlock.result`, `HookRunMessage.result`, `McpToolCallMessage.result` for convenient inline access without scrolling ```graphql type ToolUseBlock { toolCallId: String! name: String! input: String! - # Result resolved via DataLoader using toolCallId + # Also accessible inline on the parent result: ToolResultBlock } ``` +## Subway Line Visual Connectors + +Parent-child message pairs are connected with colored vertical lines (subway lines) on the left margin. The `parentId` field on messages identifies the relationship. Colors are consistent per group (hashed from parent ID). + +## Chat Alignment + +Messages use left/right alignment based on type: + +**Right-aligned (user input):** +- `RegularUserMessage` — real human input +- `CommandUserMessage` — slash commands +- `InterruptUserMessage` — user interruptions + +**Left-aligned (agent/system):** +- All other message types (assistant, system, hooks, tools, results, etc.) + ## Anti-Pattern (NEVER DO THIS) ```typescript @@ -39,6 +54,6 @@ type ToolUseBlock { ## Testing Requirements BDD tests MUST verify: -- Tool result messages don't appear in messages timeline -- Result counts match tool use counts +- Result messages appear in the timeline with subway lines - No "Unknown event" cards for known types +- User messages are right-aligned, agent messages are left-aligned diff --git a/.claude/rules/han-cli/exit-behavior.md b/.claude/rules/han-cli/exit-behavior.md new file mode 100644 index 000000000..b9f7fce0a --- /dev/null +++ b/.claude/rules/han-cli/exit-behavior.md @@ -0,0 +1,32 @@ +# CLI Exit Behavior + +## Problem: CLI commands hang after completion + +Bun/Node CLI commands using Commander.js may hang after the action handler completes due to: +- Open handles from async imports +- Event listeners that haven't been cleaned up +- Timer references +- Pending promises + +## Solution: Explicit process.exit(0) + +For CLI commands that should exit immediately after completion, add explicit `process.exit(0)` at the end of the action handler: + +```typescript +.action(async (args, options) => { + await doWork(); + process.exit(0); // Prevent hanging on open handles +}); +``` + +## When to apply + +- Hook dispatch commands +- Reference/utility commands +- Any command that outputs and exits (not interactive) + +## When NOT to apply + +- Interactive commands (using ink, prompts) +- Long-running daemons/servers +- Commands that need cleanup hooks to run diff --git a/.claude/rules/han-commands.md b/.claude/rules/han-commands.md index e4d9b994a..548de20a3 100644 --- a/.claude/rules/han-commands.md +++ b/.claude/rules/han-commands.md @@ -21,10 +21,10 @@ bun test han hook run # With caching (skip if no changes) -han hook run jutsu-biome lint --cached +han hook run biome lint --cached # Force re-run ignoring cache -han hook run jutsu-biome lint --cache=false +han hook run biome lint --cache=false ``` ## Plugin Config Format diff --git a/.claude/rules/han-settings.md b/.claude/rules/han-settings.md index a09bd4aec..c1ee71e72 100644 --- a/.claude/rules/han-settings.md +++ b/.claude/rules/han-settings.md @@ -26,7 +26,7 @@ metrics: enabled: true # Enable task metrics tracking plugins: - jutsu-biome: + biome: hooks: lint: enabled: true diff --git a/.claude/rules/hashi-blueprints/blueprints-index.md b/.claude/rules/hashi-blueprints/blueprints-index.md deleted file mode 100644 index c54dfb01d..000000000 --- a/.claude/rules/hashi-blueprints/blueprints-index.md +++ /dev/null @@ -1,58 +0,0 @@ -# Blueprints - -Technical documentation for this project's architecture and systems. - -## When to Consult Blueprints - -Before modifying system architecture, use `search_blueprints` and `read_blueprint` to understand: -- Current design decisions and rationale -- Integration points and dependencies -- Established patterns to follow - -## Key Triggers - -Consult blueprints when working on: -- GraphQL schema changes -- CLI command modifications -- MCP server integrations -- Plugin architecture changes -- Database schema updates -- Hook system modifications - -## After Modifications - -Update blueprints via `write_blueprint` when you: -- Add new systems or major features -- Change architectural patterns -- Discover undocumented conventions - -## Available Blueprints - - -| Blueprint | Summary | -|-----------|---------| -| blueprint-system | MCP-based blueprint management with frontmatter metadata | -| browse-architecture | Han browse command architecture - GraphQL + Vite unified server | -| build-deployment | CI/CD automation for releases and deployments | -| checkpoint-system | Session and agent checkpoints for scoped hook execution | -| cli-architecture | Entry point, command structure, and CLI framework | -| cli-interface | Interactive CLI with AI-powered plugin discovery | -| coordinator-daemon | Coordinator daemon architecture with GraphQL server, lazy startup, and unified data access | -| coordinator-data-layer | Single-coordinator pattern for indexing JSONL transcripts to SQLite database | -| distribution-architecture | NPM wrapper + platform-specific Bun binaries distribution model | -| han-events-logging | Session-scoped logging of Han events (hooks, MCP calls) to JSONL files indexed into SQLite for Browse UI visibility | -| han-memory-system | Complete architecture and implementation of Han Memory - five-layer semantic memory with synthesis via Agent SDK, streaming output, and citation-backed answers | -| hook-result-parent-linkage | Hook result messages need parent_id linkage to hook run messages | -| hook-system | Complete hook lifecycle from definition to execution with centralized orchestration, checkpoint filtering, and cross-plugin dependencies | -| marketplace | Central plugin registry and distribution | -| mcp-server | Model Context Protocol server exposing plugin tools | -| metrics-system | Self-reporting agent performance tracking with validation | -| native-module | High-performance Rust bindings for hook operations | -| plugin-directory | Filesystem organization and naming conventions | -| plugin-installation | Installation flow and marketplace integration | -| plugin-types | Bushido, Jutsu, Do, and Hashi plugin categories | -| rust-graphql-migration | Migration plan for tight DB-GraphQL coupling with Seaography, Relay connections, and sqlite3_update_hook subscriptions | -| sdlc-coverage | AI-native engineering workflow alignment with OpenAI's framework | -| settings-management | Multi-scope settings with precedence rules | -| validation | Configuration validation and schema enforcement | -| website | Static marketplace site with search and documentation | diff --git a/.claude/rules/hashi-plugins/mcp-transport.md b/.claude/rules/hashi-plugins/mcp-transport.md index 42f210062..c6a7cb372 100644 --- a/.claude/rules/hashi-plugins/mcp-transport.md +++ b/.claude/rules/hashi-plugins/mcp-transport.md @@ -1,8 +1,8 @@ -# Hashi Plugin MCP Transport Best Practices +# Service Plugin MCP Transport Best Practices ## Prefer HTTP Transport Over stdio -When creating hashi plugins, prefer HTTP-based MCP servers over stdio/Docker: +When creating service plugins, prefer HTTP-based MCP servers over stdio/Docker: ### HTTP Transport (Best) ```json diff --git a/.claude/rules/hooks.md b/.claude/rules/hooks.md index 8ec6c77d2..e7fb3d57e 100644 --- a/.claude/rules/hooks.md +++ b/.claude/rules/hooks.md @@ -8,5 +8,5 @@ ## Key Distinction -- `han hook dispatch` - Orchestrates hook execution, outputs text +- `han hook dispatch` - Dispatches hook execution, outputs text - `han hook run` - Executes individual hooks with caching/validation, should log events diff --git a/.claude/rules/hooks/architecture.md b/.claude/rules/hooks/architecture.md index 9ecdaefc7..9c5714588 100644 --- a/.claude/rules/hooks/architecture.md +++ b/.claude/rules/hooks/architecture.md @@ -42,20 +42,27 @@ Plugins register hooks in `hooks/hooks.json`: ## Hook Events -Claude Code provides these hook events: - -| Event | When Triggered | -|-------|----------------| -| `SessionStart` | When a Claude Code session begins | -| `SessionEnd` | When a session ends | -| `UserPromptSubmit` | When user submits a prompt | -| `Stop` | When Claude stops to allow validation | -| `PreToolUse` | Before a tool is executed | -| `PostToolUse` | After a tool is executed | -| `SubagentStart` | When a subagent (Task) starts | -| `SubagentStop` | When a subagent completes | -| `PreCompact` | Before context compaction | -| `Notification` | For notifications | +Claude Code provides these hook events (complete as of 2.1.63): + +| Event | When Triggered | Matcher | +|-------|----------------|---------| +| `SessionStart` | When a Claude Code session begins | startup/resume/clear/compact | +| `UserPromptSubmit` | When user submits a prompt | No | +| `PreToolUse` | Before a tool is executed | tool name | +| `PermissionRequest` | When permission dialog appears | tool name | +| `PostToolUse` | After a tool is executed | tool name | +| `PostToolUseFailure` | When a tool execution fails | tool name | +| `Notification` | For notifications | notification type | +| `SubagentStart` | When a subagent (Agent) starts | agent type | +| `SubagentStop` | When a subagent completes | agent type | +| `Stop` | When Claude stops to allow validation | No | +| `TeammateIdle` | When a teammate goes idle | No | +| `TaskCompleted` | When a task is marked completed | No | +| `ConfigChange` | When configuration is modified | config source | +| `WorktreeCreate` | When a worktree is created | No | +| `WorktreeRemove` | When a worktree is removed | No | +| `PreCompact` | Before context compaction | manual/auto | +| `SessionEnd` | When a session ends | exit reason | ## Hook Types @@ -82,6 +89,39 @@ Return text directly to the agent: } ``` +### Agent Hooks + +Spawn an agent to handle the event: + +```json +{ + "type": "agent", + "prompt": "Review the tool output for security issues. $ARGUMENTS" +} +``` + +### HTTP Hooks (2.1.63+) + +POST JSON to a URL and receive JSON back: + +```json +{ + "type": "http", + "url": "http://localhost:8080/hooks/stop", + "timeout": 30, + "headers": { + "Authorization": "Bearer $MY_TOKEN" + }, + "allowedEnvVars": ["MY_TOKEN"] +} +``` + +HTTP hooks send the event's JSON input as the POST body. Response handling: +- 2xx with empty body: success (exit code 0 equivalent) +- 2xx with plain text: success, text added as context +- 2xx with JSON: parsed using same schema as command hooks +- Non-2xx / connection failure: non-blocking error + ## Matchers Filter hooks by tool name (for PreToolUse/PostToolUse): @@ -99,7 +139,7 @@ The core plugin provides essential session hooks: - **SessionStart**: Ensures coordinator is running, registers config, outputs context - **UserPromptSubmit**: Outputs current datetime, references important rules -- **PreToolUse** (Task|Skill): Injects subagent context +- **PreToolUse** (Agent|Task|Skill): Injects subagent context ## Validation Plugin Hooks diff --git a/.claude/rules/hooks/pretooluse-updatedinput.md b/.claude/rules/hooks/pretooluse-updatedinput.md index 5ef95b369..eb5ebdbbb 100644 --- a/.claude/rules/hooks/pretooluse-updatedinput.md +++ b/.claude/rules/hooks/pretooluse-updatedinput.md @@ -2,7 +2,7 @@ When using `updatedInput` in PreToolUse hooks to modify tool parameters: -**DO NOT set `permissionDecision`** when using `updatedInput`. Setting `permissionDecision: "allow"` breaks the `updatedInput` functionality for the Task tool. +**DO NOT set `permissionDecision`** when using `updatedInput`. Setting `permissionDecision: "allow"` breaks the `updatedInput` functionality for the Agent tool (formerly Task). ## Correct Pattern diff --git a/.claude/rules/hooks/session-id.md b/.claude/rules/hooks/session-id.md index 0f01f83d1..1a8cdf57f 100644 --- a/.claude/rules/hooks/session-id.md +++ b/.claude/rules/hooks/session-id.md @@ -13,4 +13,4 @@ The env var fallbacks (HAN_SESSION_ID, CLAUDE_SESSION_ID) are only used when run // 5. Generated CLI session ID - final fallback ``` -When manually running `han hook orchestrate` from a Bash tool, there's no stdin payload, so it falls back to env vars - which may contain stale/different session IDs. +When manually running `han hook run` from a Bash tool, there's no stdin payload, so it falls back to env vars - which may contain stale/different session IDs. diff --git a/.claude/rules/hooks/subagent-prompt.md b/.claude/rules/hooks/subagent-prompt.md index 3e5681f1d..820c1d3d0 100644 --- a/.claude/rules/hooks/subagent-prompt.md +++ b/.claude/rules/hooks/subagent-prompt.md @@ -4,11 +4,12 @@ SubagentPrompt is a "virtual" hook type used by plugins to provide context that ## How It Works -1. Core Han defines a PreToolUse hook that intercepts Task and Skill tools -2. The hook runs `han hook orchestrate SubagentPrompt --tool-name ` -3. All SubagentPrompt hooks from enabled plugins are executed -4. Their combined output is wrapped in `` tags -5. This context is prepended to the tool's prompt/arguments +1. Core Han defines a PreToolUse hook that intercepts Agent (formerly Task) and Skill tools +2. The hook gathers context from SubagentPrompt hooks defined by plugins +3. Their combined output is wrapped in `` tags +4. This context is prepended to the tool's prompt/arguments + +**Note:** The orchestrate-based SubagentPrompt gathering has been removed. Context injection via `inject-subagent-context` is currently a no-op placeholder. ## Defining a SubagentPrompt Hook @@ -27,11 +28,12 @@ Use `tool_filter` to control which tools receive your context: ```yaml hooks: - # Only inject context for Task tool (subagents) - task-only-context: + # Only inject context for Agent tool (subagents) + # Note: Use both Agent and Task for backward compatibility with CC < 2.1.63 + agent-only-context: event: SubagentPrompt - command: echo "Task context here" - tool_filter: [Task] + command: echo "Agent context here" + tool_filter: [Agent, Task] # Only inject context for Skill tool skill-only-context: @@ -39,11 +41,11 @@ hooks: command: echo "Skill context here" tool_filter: [Skill] - # Both tools (default if tool_filter omitted) + # All tools (default if tool_filter omitted) all-context: event: SubagentPrompt - command: echo "Context for both" - tool_filter: [Task, Skill] + command: echo "Context for all" + tool_filter: [Agent, Task, Skill] ``` ## Output Format diff --git a/.claude/rules/jutsu-plugins/lsp-entrypoint-pattern.md b/.claude/rules/language-plugins/lsp-entrypoint-pattern.md similarity index 92% rename from .claude/rules/jutsu-plugins/lsp-entrypoint-pattern.md rename to .claude/rules/language-plugins/lsp-entrypoint-pattern.md index f4c97fbd2..4953c99f6 100644 --- a/.claude/rules/jutsu-plugins/lsp-entrypoint-pattern.md +++ b/.claude/rules/language-plugins/lsp-entrypoint-pattern.md @@ -18,7 +18,7 @@ project-root/ relay.config.json # Relay config here (not at root) ``` -The jutsu-relay LSP runs from project root and can't find `packages/browse-client/relay.config.json`, causing a crash. +The relay LSP runs from project root and can't find `packages/browse-client/relay.config.json`, causing a crash. ## Solution: Defensive Entrypoint Scripts @@ -71,7 +71,7 @@ For any jutsu plugin with LSP servers: ## Implemented In -- `jutsu-relay` - Checks for relay.config.json, relay.config.js, or package.json +- `relay` - Checks for relay.config.json, relay.config.js, or package.json ## Should Be Applied To diff --git a/.claude/rules/marketplace/plugin-aliases.md b/.claude/rules/marketplace/plugin-aliases.md index 63824c41e..71de8243d 100644 --- a/.claude/rules/marketplace/plugin-aliases.md +++ b/.claude/rules/marketplace/plugin-aliases.md @@ -64,9 +64,9 @@ The `packages/han/lib/plugin-aliases.ts` file defines the mapping of old names t |---------|-----------------|---------| | `jutsu-{name}` | `jutsu-typescript` | `languages/typescript` | | `hashi-{name}` | `hashi-gitlab` | `services/gitlab` | -| `do-{name}` | `do-api` | `disciplines/api` | -| `do-{name}-engineering` | `do-api-engineering` | `disciplines/api` | -| `do-{name}-development` | `do-backend-development` | `disciplines/backend` | +| `do-{name}` | `do-api` | `disciplines/api-engineering` | +| `do-{name}-engineering` | `do-api-engineering` | `disciplines/api-engineering` | +| `do-{name}-development` | `do-backend-development` | `disciplines/backend-development` | ### If You See "Plugin not found" Errors diff --git a/.claude/rules/mcp/tool-exposure.md b/.claude/rules/mcp/tool-exposure.md index bcaf405e3..b9913affc 100644 --- a/.claude/rules/mcp/tool-exposure.md +++ b/.claude/rules/mcp/tool-exposure.md @@ -1,6 +1,6 @@ # MCP Tool Access Patterns -## Two Ways to Access Hashi Plugin Tools +## Two Ways to Access Service/Tool Plugin Tools ### 1. Direct Exposure (`expose: true`) Tools are directly available as `{serverId}_{toolName}` in Claude Code: @@ -18,13 +18,13 @@ mcp: name: my-server # No expose flag - accessed via han_workflow ``` -**Used by:** All other hashi plugins (github, reddit, playwright, etc.) +**Used by:** All other service plugins (github, reddit, playwright, etc.) ## han_workflow Tool The orchestrator provides a single `han_workflow` tool that: 1. Analyzes user intent via `selectBackendsForIntent()` -2. Discovers available backends from installed hashi plugins +2. Discovers available backends from installed service plugins 3. Spawns Agent SDK agents with selected MCP servers 4. Runs workflows autonomously and returns results diff --git a/.claude/rules/plugin-naming.md b/.claude/rules/plugin-naming.md index 627eeffdb..fd26f31f3 100644 --- a/.claude/rules/plugin-naming.md +++ b/.claude/rules/plugin-naming.md @@ -25,6 +25,8 @@ Plugins are named by their short identifier only, matching their directory name: | Tools | `tools/vitest` | `vitest` | | Frameworks | `frameworks/relay` | `relay` | | Disciplines | `disciplines/frontend-development` | `frontend-development` | +| Disciplines | `disciplines/api-engineering` | `api-engineering` | +| Disciplines | `disciplines/security-engineering` | `security-engineering` | ## Installation Commands diff --git a/.claude/rules/target-market-hypothesis.md b/.claude/rules/target-market-hypothesis.md new file mode 100644 index 000000000..151f2cb59 --- /dev/null +++ b/.claude/rules/target-market-hypothesis.md @@ -0,0 +1,329 @@ +# Target Market Hypothesis + +## Context + +This document captures the working hypothesis for Han's target market, value propositions, data gaps, and go-to-market strategy. It is a living document intended to guide product decisions, not a final business plan. All assumptions should be validated against real user behavior and willingness to pay. + +## 1. Primary Market Segments + +### Segment A: Individual Power Users (Claude Code Max/Pro subscribers) + +**Who:** Software engineers spending $100-200/month on Claude Code subscriptions, working on multiple projects, using Han locally for personal productivity visibility. + +**Why they would pay:** They already pay for AI coding. Han's cost analysis card shows subscription utilization, value multiplier, and break-even calculations. These users want to know whether their Max subscription is justified. The dashboard answers "am I actually getting $200/month of value?" with real data. They also benefit from hook health monitoring, session effectiveness scoring, and memory search across sessions. + +**Current evidence:** The CostAnalysisCard already shows subscription tier comparisons, API-equivalent cost, and value multiplier. This is the data a power user opens the dashboard to see. + +**Willingness to pay:** Low to moderate as individuals. Most value here is as free-tier adoption that feeds bottom-up growth into teams. + +### Segment B: Tech Leads Managing 3-8 AI-Augmented Engineers + +**Who:** Senior engineers or tech leads responsible for a team's output quality. They adopted Claude Code, got their team on it, and now need to understand whether it is actually helping. + +**Why they would pay:** The team dashboard shows sessions by project, task completion rates, token usage aggregation, and activity timelines across contributors. But what they really need (and what currently has gaps) is the ability to answer: "Are my engineers using Claude Code effectively, or are they burning tokens on sessions that go nowhere?" + +**Current evidence:** TeamDashboardContent already shows total sessions, total tasks, success rate, estimated cost, sessions by project, task outcomes (success/partial/failure), and top contributors with success rates. The ExportButton enables data extraction for reporting. + +**Willingness to pay:** Moderate. This is the segment most likely to convert from free to paid. They need justification data for their engineering manager. + +### Segment C: Engineering Managers and Directors (Budget Holders) + +**Who:** People managing 20-100+ engineers, responsible for engineering budget and productivity metrics. They report to a VP/CTO on whether AI tooling investment is paying off. + +**Why they would pay:** They need to justify $200/seat/month * N engineers to finance. That is $2,400/engineer/year. For a 50-person team, that is $120K/year in AI tooling spend. They need data proving ROI, not just token counts. Han's existing metrics (cost per session, cost per completed task, cache hit rates, subscription utilization) are the raw ingredients, but these managers need them aggregated across teams with trend lines and exportable reports. + +**Willingness to pay:** High, if the data answers the ROI question convincingly. This is the enterprise buyer. + +### Segment D: DevOps/Platform Engineering Teams + +**Who:** Teams responsible for developer tooling and infrastructure. They manage Claude Code deployment across the organization, configure MCP servers, maintain hooks, and ensure the development environment is healthy. + +**Why they would pay:** Hook health monitoring (pass/fail rates, average durations), compaction health (context window management), slot coordination, and plugin management across scopes (user/project/local). They need operational health data for the AI development infrastructure. + +**Current evidence:** HookHealthCard shows per-hook pass rates, failure counts, and average durations. CompactionHealthCard tracks auto/manual compactions and sessions hitting context limits. Plugin stats show total/enabled/scoped plugin counts. + +**Willingness to pay:** Moderate, bundled with team/enterprise tier. + +## 2. User Personas by Role + +### Individual Developer + +**What they open the dashboard for:** +- "How much am I spending?" (CostAnalysisCard: daily/weekly trends, subscription utilization) +- "What did I work on this week?" (Activity heatmap, session list, task history) +- "Which sessions were productive?" (SessionEffectivenessCard: top/bottom sessions by score) +- "What tools am I using most?" (ToolUsageChart, SubagentUsageChart) +- "What time am I most productive?" (TimeOfDayChart) + +**Dashboard consumption pattern:** Glances at it once or twice a day. Cares about personal streaks, cost efficiency, and session history. Wants drill-down into specific sessions. + +**What they ignore:** Team metrics, organizational views, export buttons. + +### Tech Lead + +**What they open the dashboard for:** +- "Are my team members actually using Claude Code?" (Team dashboard: contributor activity) +- "Which projects are getting the most AI assistance?" (SessionsByProjectChart) +- "Are tasks being completed successfully?" (TaskOutcomesChart: success/partial/failure breakdown) +- "Where is the budget going?" (Cost per project breakdown via configDirBreakdowns) +- "Are hooks passing?" (HookHealthCard: CI validation hooks failing = bad code getting through) + +**Dashboard consumption pattern:** Weekly review. Compares team members' usage patterns. Exports data for sprint retrospectives. Drills into specific contributors or projects when something looks off. + +**What they need that is missing:** See Data Gaps section below. + +### Engineering Manager + +**What they open the dashboard for:** +- "What is our AI tooling ROI?" (Cost analysis across organization) +- "Are we getting faster?" (Trend lines over months, not just 30-day snapshots) +- "Which teams are adopting effectively?" (Cross-team comparison) +- "Should we increase/decrease seat count?" (Utilization data) +- Monthly/quarterly executive reports + +**Dashboard consumption pattern:** Monthly or quarterly. Wants pre-built reports, PDF exports, and trend comparisons. Does not want to navigate session-level details. + +**What they need that is missing:** See Data Gaps section below. + +### VP of Engineering / CTO + +**What they open the dashboard for:** +- "Is our AI investment paying off at the org level?" (Single number: ROI multiplier) +- "How does our usage compare to industry?" (Benchmarks) +- "What is the total cost of AI tooling?" (Aggregated across all teams) +- Board-ready slides showing AI adoption impact + +**Dashboard consumption pattern:** Quarterly. Receives a report, not a dashboard. Needs 3-5 metrics with clear directional arrows (up/down/flat). Does not log in to Han. + +**What they need that is missing:** See Data Gaps section below. + +### DevOps / Platform Engineer + +**What they open the dashboard for:** +- "Are hooks healthy across all projects?" (HookHealthCard) +- "Are any MCP servers failing?" (Currently not tracked) +- "How often are sessions hitting context limits?" (CompactionHealthCard) +- "What plugins are installed and where?" (Plugin stats by scope) +- "Is the coordinator daemon healthy?" (Live status) + +**Dashboard consumption pattern:** Operational monitoring. Wants alerts, not dashboards. Checks when something breaks. + +**What they need that is missing:** Alerting, MCP server health, coordinator uptime metrics. + +## 3. Value Propositions + +### Unique to Han (No Alternative Exists) + +1. **Claude Code session-level observability.** No other tool indexes Claude Code JSONL transcripts and provides session replay, message timelines, tool usage, and subagent tracking. This is Han's core moat. Anthropic's own console shows API usage, not session-level developer experience data. + +2. **Hook system health monitoring.** Han's hook architecture (PreToolUse, PostToolUse, Stop, SessionStart) is unique. The dashboard showing hook pass/fail rates with timing data is infrastructure observability that only applies to Han-managed Claude Code environments. + +3. **Cost analysis with subscription intelligence.** The CostAnalysisCard does not just show token costs. It calculates subscription utilization percentage, value multiplier (API-equivalent vs subscription price), break-even daily spend, and tier comparison recommendations. No other tool does this for Claude Code. + +4. **Plugin ecosystem management.** Visibility into which plugins are installed at which scope (user/project/local), with category breakdowns. This matters for organizations standardizing their Claude Code configuration. + +### Differentiated (Alternatives Exist but Are Weaker) + +1. **AI coding activity heatmaps.** GitHub Copilot has usage dashboards, but they show acceptance rates, not session-level productivity. Han's activity heatmap shows daily engagement patterns with token and code change breakdowns. + +2. **Task completion tracking.** LinearB and Jellyfish track PR cycle time and DORA metrics. Han tracks AI-assisted task completion with success/partial/failure outcomes and confidence calibration scores. Different signal, different granularity. + +3. **Model usage analysis.** The ModelUsageChart shows which models (Opus, Sonnet, Haiku) are being used across sessions with per-model token and cost breakdowns. Anthropic's console shows this at the API key level, not at the developer/project level. + +## 4. Data Gaps to Fill + +### Critical (Blocks Enterprise Sales) + +**4.1. PR/Commit Correlation** +- **Gap:** No connection between Claude Code sessions and actual Git outcomes (PRs merged, commits landed, review cycles). +- **Why it matters:** Engineering managers cannot prove AI ROI without tying sessions to shipped code. "We spent $10K on Claude Code last month" means nothing without "and it contributed to 45 merged PRs." +- **Data source:** Git log correlation via session worktree detection, `gh pr list` data, PR linkage (`--from-pr` is already supported in Claude Code 2.1.27+). +- **Who needs it:** Engineering managers, VPs, CTOs. + +**4.2. Time Savings Estimation** +- **Gap:** No metric for "time saved" or "time-to-completion comparison." +- **Why it matters:** The single most requested metric from budget holders. "Claude Code saves each engineer X hours per week" is the ROI story. +- **Approach:** Cannot measure counterfactual directly. Proxy metrics: (a) lines of code per session hour, (b) task completion rate trends over time, (c) session duration vs task complexity. Could also survey users for perceived time savings and aggregate. +- **Who needs it:** Engineering managers, VPs, CTOs. + +**4.3. Per-User Analytics in Team Dashboard** +- **Gap:** Team dashboard shows "top contributors" but lacks per-user drill-down with individual cost, session count, success rate, and usage patterns. +- **Why it matters:** Tech leads need to identify who needs help or coaching. Managers need per-user cost allocation. +- **Privacy consideration:** Must be opt-in and role-gated. Individual developers should not see each other's metrics. Only leads/managers see team member details. +- **Who needs it:** Tech leads, engineering managers. + +**4.4. Trend Lines and Period Comparisons** +- **Gap:** Dashboard shows 30-day snapshots. No week-over-week, month-over-month, or quarter-over-quarter trend comparisons. +- **Why it matters:** "Are we getting better?" requires trends, not snapshots. Executive reports need directional indicators. +- **Implementation:** The `activity(days: 730)` query already fetches up to 2 years of daily data. Need computed period comparisons and trend arrows. +- **Who needs it:** All personas above individual developer. + +### Important (Improves Retention and Expansion) + +**4.5. Session Quality Scoring Explanation** +- **Gap:** SessionEffectivenessCard shows a score, sentiment trend, focus score, and compaction count, but does not explain what makes a "good" session. Users see numbers without understanding what to change. +- **Why it matters:** Actionable insights drive engagement. "Your session scored 72 because you had 3 context compactions and low task completion" gives the user something to improve. +- **Who needs it:** Individual developers, tech leads. + +**4.6. MCP Server Health and Usage** +- **Gap:** No visibility into MCP server uptime, response times, error rates, or which MCP tools are being used most. +- **Why it matters:** Organizations deploying multiple MCP servers (GitHub, GitLab, Playwright, context7) need operational health data. +- **Data source:** McpToolCallMessage and McpToolResultMessage are already indexed. Need to extract timing, errors, and aggregate by server. +- **Who needs it:** DevOps/platform engineers. + +**4.7. Code Quality Correlation** +- **Gap:** No connection between AI-assisted sessions and downstream code quality (test pass rates, lint violations, PR review comments). +- **Why it matters:** If AI-generated code produces more bugs, the ROI story falls apart. Need to prove code quality is maintained or improved. +- **Data source:** Hook health (Stop hooks running linters/tests) partially covers this. Could correlate hook pass rates with specific sessions. +- **Who needs it:** Engineering managers, security leads. + +**4.8. Context Window Efficiency** +- **Gap:** CompactionHealthCard shows compaction counts but not context utilization efficiency. How much of the context window is being used productively vs filled with irrelevant content? +- **Why it matters:** Power users want to optimize their prompting strategy. High compaction rates may indicate inefficient context usage. +- **Who needs it:** Individual developers, tech leads. + +### Nice to Have (Differentiation and Delight) + +**4.9. Industry Benchmarks** +- **Gap:** No comparison to "how other teams use Claude Code." Users see their metrics in isolation. +- **Why it matters:** Engineering leaders want to know if their adoption is typical, behind, or ahead. "Your team uses 2.3x more sessions than the median team of your size" is powerful. +- **Requires:** Anonymous aggregate data from opt-in users. Significant user base needed. +- **Who needs it:** Engineering managers, VPs. + +**4.10. Custom Alerts and Thresholds** +- **Gap:** No alerting when hook failure rates spike, costs exceed budget, or session quality drops. +- **Why it matters:** DevOps teams need proactive monitoring, not reactive dashboard checking. +- **Who needs it:** DevOps/platform engineers, tech leads. + +**4.11. Integration with Existing BI Tools** +- **Gap:** ExportButton exists but only exports raw JSON. No integration with Datadog, Grafana, or existing engineering analytics platforms. +- **Why it matters:** Enterprise teams will not adopt another dashboard. They want Han data flowing into their existing observability stack. +- **Who needs it:** Enterprise platform teams. + +## 5. Pricing Hypothesis + +### Free Tier (Individual / Community) + +**Target:** Individual developers, open-source contributors. + +**Includes:** +- Local dashboard (runs on your machine via `han browse`) +- Personal session history, cost analysis, activity heatmaps +- Plugin marketplace and installation +- Hook system and validation +- Memory system (local vector search) +- Unlimited local data retention + +**Why free:** Bottom-up adoption. Every paying team starts with individual developers who fell in love with the tool for free. The local-first architecture means there is near-zero marginal cost per free user. + +### Team Tier ($15-25/user/month) + +**Target:** Tech leads and small teams (3-20 engineers). + +**Includes everything in Free, plus:** +- Team dashboard with cross-contributor views +- Per-project cost allocation and breakdowns +- Data sync to hosted coordinator (aggregated team data) +- Exportable reports (PDF, CSV) +- Trend comparisons (week-over-week, month-over-month) +- Session quality coaching (actionable improvement suggestions) +- PR/commit correlation (once built) +- Role-based access (admin/member/viewer roles already exist in auth types) +- Organization management (Org/TeamMember types already scaffolded) + +**Why this price:** Below the threshold that requires procurement approval at most companies. Comparable to LinearB starter pricing. At $20/user/month for a 10-person team, that is $200/month, which is the cost of a single Claude Code Max subscription. The value proposition: "Spend 1 extra seat-equivalent to understand whether the other 10 seats are worth it." + +### Enterprise Tier ($40-60/user/month, or custom) + +**Target:** Engineering organizations with 50+ Claude Code users. + +**Includes everything in Team, plus:** +- SSO/SAML integration +- Custom data retention policies +- Industry benchmarks (anonymized aggregate comparisons) +- Custom alerting and thresholds +- API access for BI tool integration (Datadog, Grafana, Tableau) +- Dedicated support +- Time savings estimation model +- Compliance and audit logging +- Multi-organization support +- On-premises coordinator option + +**Why this price:** At scale, AI tooling spend becomes a significant budget line. A 100-engineer team on Claude Code Max spends $240K/year. An enterprise Han license at $50/user/month ($60K/year) is 25% of the AI tool spend, which is reasonable if it provides the ROI data to justify (or optimize) the other $240K. + +## 6. Competitive Positioning + +### vs GitHub Copilot Metrics Dashboard + +GitHub Copilot's built-in analytics show acceptance rates, lines of code suggested, and active users. This is input-focused ("how much did the AI suggest?") not output-focused ("did the AI help ship working code?"). Han's session-level observability, task completion tracking, and cost analysis provide fundamentally different signal. Han also works with Claude Code specifically, which means it understands the agentic workflow (tool use, subagents, hooks, MCP calls) that Copilot's chat-only model does not have. + +**Positioning:** Han is to Claude Code what Datadog is to infrastructure. Copilot metrics is a simple counter; Han is observability. + +### vs LinearB / Jellyfish / Pluralsight Flow + +These tools focus on engineering productivity using Git data: PR cycle time, DORA metrics, sprint velocity. They do not understand AI coding sessions at all. They cannot tell you whether Claude Code helped or hurt. Han's data starts from the AI session and works outward toward Git outcomes. These tools start from Git and have no visibility into the AI interaction. + +**Positioning:** Complementary, not competitive. Han feeds data that these tools cannot access. The PR/commit correlation gap (section 4.1) is the bridge: once Han connects sessions to PRs, it becomes a data source for existing engineering analytics, not a replacement. + +### vs Anthropic Console / API Dashboard + +Anthropic's console shows API-level usage: tokens consumed, models used, costs by API key. This is infrastructure-level billing data, not developer-level productivity data. Han operates at the session level: which developer, which project, which task, what was the outcome, how efficient was the context window usage. + +**Positioning:** Anthropic's console is for the CTO who pays the API bill. Han is for the engineering manager who needs to know if the money is well spent. + +### vs Build-Your-Own (Querying JSONL Files Directly) + +Some teams will attempt to build their own analytics by parsing Claude Code's JSONL transcripts. This is exactly what Han's Rust-native indexer does, but with FTS5 search, proper message correlation (tool calls to results, hooks to outcomes), and a GraphQL API. Building this from scratch takes significant engineering effort and ongoing maintenance as Claude Code's message format evolves. + +**Positioning:** "We already built the hard part. You do not need a data engineering sprint to understand your Claude Code usage." + +## 7. Go-to-Market Strategy + +### Phase 1: Developer Adoption (Bottom-Up) + +**Tactic:** The free local dashboard. Developers install Han for the plugin marketplace, get the dashboard as a bonus, and start understanding their own Claude Code usage. The activity heatmap, cost analysis, and session effectiveness cards create habitual dashboard checking. + +**Distribution:** Already happening via `claude plugin install core@han` which installs the hooks and coordinator. The `han browse` command launches the dashboard. npm distribution (`npx -y @thebushidocollective/han browse`) means zero global install needed. + +**Conversion trigger:** Developer shows dashboard to their tech lead. Tech lead wants to see team-level data. Team-level data requires the Team tier. + +### Phase 2: Team Adoption (Tech Lead as Champion) + +**Tactic:** Tech lead enables team data sync, sees aggregate metrics, and starts using the team dashboard for sprint reviews. The export button creates shareable reports. + +**Conversion trigger:** Engineering manager asks "can we get this for the whole org?" or "can we track AI ROI for budget review?" This requires the Enterprise tier. + +### Phase 3: Enterprise Expansion (Manager as Buyer) + +**Tactic:** Engineering manager needs to justify AI tooling spend to VP/CTO. Han provides the data. The ROI report becomes the sales tool. + +**Conversion trigger:** VP asks for quarterly AI impact reports. Han's Enterprise tier provides automated reports with trend comparisons and benchmarks. + +### Content Marketing Focus + +1. **"Is your Claude Code Max subscription worth it?"** - Blog post showing the cost analysis dashboard, subscription utilization, and value multiplier. Targets individual developers considering the $200/month plan. + +2. **"How to measure AI coding ROI for your engineering team"** - Targets engineering managers. Shows what metrics matter and how Han provides them. + +3. **"The Claude Code observability gap"** - Targets DevOps/platform teams. Makes the case that AI coding tools need the same operational monitoring as any other production infrastructure. + +### Key Metrics to Track for Go-to-Market Validation + +- Free-to-team conversion rate (target: 5-10% of active free users within 6 months) +- Dashboard daily active users (leading indicator of habit formation) +- Export button usage (leading indicator of team sharing) +- Team dashboard page views vs individual dashboard (indicates team interest) +- Session count per user per week (engagement depth) +- Feature requests mentioning "team," "org," "manager," or "report" (demand signal for paid tiers) + +## 8. Risks and Open Questions + +1. **Anthropic builds this in.** If Claude Code ships native analytics that cover Han's dashboard capabilities, the observability value proposition weakens. Mitigation: Han's plugin ecosystem, hook system, and memory layer provide value beyond pure analytics. + +2. **Privacy concerns with team visibility.** Developers may resist their sessions being visible to managers. Mitigation: Role-based access, opt-in team sync, and clear data ownership policies. + +3. **Free tier too generous.** If the local dashboard solves 90% of the use case, teams may not convert. Mitigation: Team aggregation, trend comparisons, and PR correlation are genuinely multi-user features that do not work locally. + +4. **Market size uncertainty.** Claude Code's user base is growing but the total addressable market of "teams with 5+ Claude Code users who want analytics" is not yet proven at scale. + +5. **Data accuracy.** The dashboard shows estimated costs based on token counts and published pricing. If estimates diverge significantly from actual Anthropic bills, trust erodes. The `isEstimated` badge (already shown in CostAnalysisCard) partially addresses this, but accuracy improvements are needed. diff --git a/.claude/settings.json b/.claude/settings.json index bc88d2a58..e5ec60cf9 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,4 +1,7 @@ { + "env": { + "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" + }, "hooks": { "UserPromptSubmit": [ { @@ -23,13 +26,9 @@ } ] }, - "env": { - "CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1" - }, "enabledPlugins": { "accessibility@han": true, "act@han": true, - "ai-dlc@ai-dlc": true, "atomic-design@han": true, "biome@han": true, "blueprints@han": true, @@ -57,7 +56,9 @@ "relay@han": true, "rust@han": true, "shellcheck@han": true, - "typescript@han": true + "typescript@han": true, + "scratch@han": true, + "ai-dlc@ai-dlc": true }, "extraKnownMarketplaces": { "han": { diff --git a/.claude/skills/create-blog-post/SKILL.md b/.claude/skills/create-blog-post/SKILL.md index 5d06f7e24..2ef8cc04f 100644 --- a/.claude/skills/create-blog-post/SKILL.md +++ b/.claude/skills/create-blog-post/SKILL.md @@ -14,7 +14,7 @@ If no topic provided, ask the user what angle they want to cover. ## MCP Server Required -This command requires the `hashi-reddit` MCP server to be installed and running. +This command requires the `reddit` MCP server to be installed and running. ## Phase 1: Research Reddit Discussions @@ -68,9 +68,9 @@ Analyze how Han's features address the discovered pain points: | Reddit Pain Point | Han Solution | |-------------------|--------------| -| Inconsistent code quality | Jutsu validation hooks | -| Lack of specialization | Do discipline agents | -| Poor tool integration | Hashi MCP bridges | +| Inconsistent code quality | Validation plugin hooks | +| Lack of specialization | Discipline plugin agents | +| Poor tool integration | Service plugin MCP integrations | | No memory across sessions | Memory system | | Uncalibrated confidence | Metrics tracking | | Missing documentation | Blueprints system | diff --git a/.claude/skills/create-do/SKILL.md b/.claude/skills/create-do/SKILL.md index 385173c4d..c72c7483b 100644 --- a/.claude/skills/create-do/SKILL.md +++ b/.claude/skills/create-do/SKILL.md @@ -1,21 +1,21 @@ --- -description: Create a new do (discipline) plugin with specialized agents +description: Create a new discipline plugin with specialized agents --- -# Create a Dō (道 - The Way) Plugin +# Create a Discipline (道 - Do) Plugin -Create a new dō plugin for: $ARGUMENTS +Create a new discipline plugin for: $ARGUMENTS -## What is a Dō? +## What is a Discipline Plugin? -Dōs represent "the way" or path of mastery in the Han marketplace. They provide specialized agents that embody expertise in specific development disciplines (frontend, backend, security, performance, etc.). Each dō contains one or more agents with deep domain knowledge. +Discipline plugins represent "the way" or path of mastery in the Han marketplace. They provide specialized agents that embody expertise in specific development disciplines. Each discipline contains one or more agents with deep domain knowledge. ## Plugin Structure Create the following directory structure: ``` -do/do-{discipline-name}/ +plugins/disciplines/{discipline-name}/ ├── .claude-plugin/ │ └── plugin.json # Plugin metadata (ONLY plugin.json goes here) ├── agents/ @@ -34,6 +34,7 @@ do/do-{discipline-name}/ - Only `plugin.json` goes inside `.claude-plugin/` - `hooks.json` goes in the `hooks/` directory - `han-plugin.yml` stays at the plugin root (NOT in hooks/) +- Discipline names should be descriptive (e.g., `frontend-development`, `api-engineering`, `security-engineering`) ## Step 1: Create plugin.json @@ -41,7 +42,7 @@ Create `.claude-plugin/plugin.json`: ```json { - "name": "do-{discipline-name}", + "name": "{discipline-name}", "version": "1.0.0", "description": "Specialized agents for {discipline area} including {key responsibilities}.", "author": { @@ -55,8 +56,7 @@ Create `.claude-plugin/plugin.json`: "{discipline}", "{specialty}", "agent", - "{related-area}", - "{technology}" + "{related-area}" ] } ``` @@ -66,51 +66,22 @@ Create `.claude-plugin/plugin.json`: Create `han-plugin.yml` at the plugin root: ```yaml -# do-{discipline-name} plugin configuration +# {discipline-name} plugin configuration # This plugin provides specialized agents for {discipline area} # No hooks for agent-focused plugins (unless needed) hooks: {} -# No MCP server (unless this dō provides one) +# No MCP server (unless this discipline provides one) mcp: null -# Memory provider (optional - if agents should contribute to team memory) -memory: null -``` - -### Example han-plugin.yml with Hooks - -If your dō includes validation or quality enforcement: - -```yaml -# do-claude-plugin-development plugin configuration - -hooks: - lint: - command: "${CLAUDE_PLUGIN_ROOT}/hooks/lint.sh" - dirsWith: - - ".claude-plugin/plugin.json" - ifChanged: - - "**/*.json" - - "**/*.yml" - - "**/*.md" - - config-sync: - command: "cd packages/han && bun run lint:config-sync" - dirsWith: - - "packages/han/package.json" - ifChanged: - - "**/.claude-plugin/plugin.json" - - "**/han-plugin.yml" - -mcp: null +# Memory provider (optional) memory: null ``` ## Step 3: Create Agent Definitions -For each specialized role in your discipline, create an agent markdown file. +For each specialized role, create an agent markdown file. ### Agent File: `agents/{agent-name}.md` @@ -118,140 +89,34 @@ For each specialized role in your discipline, create an agent markdown file. --- name: {agent-name} description: | - {One-paragraph description of the agent's expertise, focus areas, and when to invoke them. Be specific about their domain and capabilities.} + {One-paragraph description of the agent's expertise, focus areas, and when to invoke them.} model: inherit color: {purple|blue|green|red|yellow|cyan|magenta} --- # {Agent Title} -{Compelling tagline describing the agent's mastery} - ## Role -{Detailed description of the agent's role, expertise level, and primary responsibilities. Set the context for what makes this agent uniquely qualified.} +{Detailed description of the agent's role and expertise} ## Core Responsibilities ### {Responsibility Area 1} - -{Detailed explanation of this responsibility} - -- {Specific capability} -- {Specific capability} -- {Specific capability} - -### {Responsibility Area 2} - -{Detailed explanation of this responsibility} - -- {Specific capability} -- {Specific capability} -- {Specific capability} - -### {Responsibility Area 3} - -{Detailed explanation of this responsibility} - -- {Specific capability} - {Specific capability} - {Specific capability} ## Approach -### {Methodology Name} - -{Explain the agent's methodology and decision-making process} - 1. **{Step 1}**: {What the agent does} 2. **{Step 2}**: {What the agent does} -3. **{Step 3}**: {What the agent does} -4. **{Step 4}**: {What the agent does} - -## Technical Expertise - -### {Expertise Area 1} - -{Deep dive into specific technical knowledge} - -- {Specific skill or tool} -- {Specific skill or tool} -- {Specific skill or tool} - -### {Expertise Area 2} - -{Deep dive into specific technical knowledge} - -- {Specific skill or tool} -- {Specific skill or tool} -- {Specific skill or tool} - -## Best Practices - -{List the key best practices this agent follows} - -1. **{Practice}**: {Why it matters} -2. **{Practice}**: {Why it matters} -3. **{Practice}**: {Why it matters} -4. **{Practice}**: {Why it matters} -5. **{Practice}**: {Why it matters} - -## Common Challenges - -### {Challenge Type 1} - -{How the agent approaches this challenge} - -### {Challenge Type 2} - -{How the agent approaches this challenge} - -### {Challenge Type 3} - -{How the agent approaches this challenge} - -## Deliverables - -When engaged, this agent provides: - -- **{Deliverable 1}**: {What this includes} -- **{Deliverable 2}**: {What this includes} -- **{Deliverable 3}**: {What this includes} -- **{Deliverable 4}**: {What this includes} - -## Integration Points - -{Describe how this agent works with other agents, tools, or processes} - -- **{Integration 1}**: {How it works} -- **{Integration 2}**: {How it works} -- **{Integration 3}**: {How it works} - -## Bushido Principles - -This agent embodies: - -- **{Virtue 1} ({Japanese} - {Romanization})**: {How this agent demonstrates this virtue} -- **{Virtue 2} ({Japanese} - {Romanization})**: {How this agent demonstrates this virtue} -- **{Virtue 3} ({Japanese} - {Romanization})**: {How this agent demonstrates this virtue} ## When to Invoke -Summon this agent when you need: - -- {Specific scenario} -- {Specific scenario} - {Specific scenario} - {Specific scenario} ``` -### Agent Frontmatter Guide - -- **name**: Kebab-case identifier (e.g., "frontend-architect", "security-engineer") -- **description**: Multi-line YAML string with detailed agent purpose. Use `|` for multi-line. -- **model**: Usually "inherit" to use the default model -- **color**: Visual identifier in Claude Code UI (purple, blue, green, red, yellow, cyan, magenta) - ### Agent Color Guidelines - **purple**: Design, UI/UX, presentation @@ -262,173 +127,37 @@ Summon this agent when you need: - **cyan**: Data, analytics, integration - **magenta**: Creative, experimental, research -## Step 4: Create Skills (Optional) - -Dō plugins can include supporting skills that agents can invoke: - -### Skill Directory Structure - -``` -skills/{skill-name}/ -└── SKILL.md -``` - -### SKILL.md Format +## Step 4: Write README.md ```markdown ---- -name: {discipline}-{skill-name} -description: Use when {specific scenario}. {What this skill provides}. -allowed-tools: - - Read - - Write - - Edit - - Bash - - Task ---- - -# {Discipline} - {Skill Name} - -{Overview of the skill} - -## When to Use +# {Discipline Name} -{Specific scenarios where this skill applies} +{Description of the discipline and what these agents provide} -## Methodology +## Specialized Agents -{Step-by-step approach} +### {Agent 1 Name} +{Description} | **Invoke when**: {Specific scenarios} -## Best Practices - -{List of best practices} - -## Examples - -{Practical examples} - -## Related Agents - -- **{agent-name}**: {How this skill relates to the agent} -``` - -## Step 5: Write README.md - -Create a comprehensive README: - -```markdown -# Dō: {Discipline Name} - -{Compelling description of the discipline and what these agents provide} - -## What This Dō Provides - -### Specialized Agents - -This dō provides the following agents: - -#### {Agent 1 Name} - -{One-paragraph description of agent specialization} - -**Invoke when**: {Specific scenarios} - -#### {Agent 2 Name} - -{One-paragraph description of agent specialization} - -**Invoke when**: {Specific scenarios} - -#### {Agent 3 Name} - -{One-paragraph description of agent specialization} - -**Invoke when**: {Specific scenarios} - -### Supporting Skills - -{If applicable, list supporting skills} - -- **{skill-1}**: {brief description} -- **{skill-2}**: {brief description} +### {Agent 2 Name} +{Description} | **Invoke when**: {Specific scenarios} ## Installation -Install via the Han marketplace: - -\`\`\`bash -han plugin install do-{discipline-name} -\`\`\` - -Or install manually: - \`\`\`bash -claude plugin marketplace add thebushidocollective/han -claude plugin install do-{discipline-name}@han -\`\`\` - -## Usage - -### Invoking Agents - -In Claude Code, invoke agents using the Task tool or natural language: - +han plugin install {discipline-name} \`\`\` -"I need a {agent-name} to {task description}" -\`\`\` - -Or explicitly: - -\`\`\` -Please spawn a {agent-name} agent to help with {specific task} -\`\`\` - -### Example Workflows - -#### {Workflow 1} - -1. {Step 1} -2. {Step 2} -3. {Step 3} - -#### {Workflow 2} - -1. {Step 1} -2. {Step 2} -3. {Step 3} - -## Learning Path - -### Shu (守) - Follow - -{Beginner practices for this discipline} - -### Ha (破) - Break - -{Intermediate practices for this discipline} - -### Ri (離) - Transcend - -{Advanced practices for this discipline} - -## Contributing - -See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines. - -## License - -MIT License - See [LICENSE](../../LICENSE) for details. ``` -## Step 6: Register in Marketplace +## Step 5: Register in Marketplace Add your plugin to `.claude-plugin/marketplace.json`: ```json { - "name": "do-{discipline-name}", + "name": "{discipline-name}", "description": "Specialized agents for {discipline area} including {key responsibilities}.", - "source": "./do/do-{discipline-name}", + "source": "./plugins/disciplines/{discipline-name}", "category": "Discipline", "keywords": [ "{discipline}", @@ -438,123 +167,24 @@ Add your plugin to `.claude-plugin/marketplace.json`: } ``` -## Best Practices - -### DO - -✅ Create agents with deep, specific expertise (not generic generalists) -✅ Write detailed, comprehensive agent definitions (1000+ words) -✅ Include specific methodologies and decision-making frameworks -✅ Provide clear "when to invoke" guidance -✅ Link agents to Bushido virtues -✅ Include practical examples and scenarios -✅ Consider how agents work together -✅ Make agents opinionated based on best practices -✅ Include technical depth appropriate to the discipline -✅ Test agents with realistic scenarios - -### DON'T - -❌ Don't create vague or generic agents -❌ Don't duplicate functionality of existing agents -❌ Don't write shallow agent definitions (<500 words) -❌ Don't forget to specify when NOT to use an agent -❌ Don't ignore integration with other tools/agents -❌ Don't create too many agents (3-5 is ideal per dō) -❌ Don't use placeholder content -❌ Don't skip the philosophy and principles sections - -## Agent Design Guidelines - -### Agent Scope - -Each agent should have: - -- **Clear specialty**: One specific area of deep expertise -- **Distinct perspective**: Unique approach or methodology -- **Defined boundaries**: Know what they DON'T handle -- **Complementary relationships**: Work well with other agents - -### Agent Personality - -Agents should be: - -- **Authoritative**: Demonstrate deep knowledge -- **Practical**: Focus on real-world application -- **Principled**: Follow best practices and patterns -- **Collaborative**: Work well in teams - -### Common Agent Archetypes - -1. **Architect**: High-level design and system structure -2. **Engineer**: Hands-on implementation and problem-solving -3. **Specialist**: Deep expertise in narrow domain -4. **Reviewer**: Quality assessment and improvement -5. **Consultant**: Advisory and strategic guidance - -## Discipline Categories - -### Development Disciplines - -- Frontend Development -- Backend Development -- Mobile Development -- Full-Stack Development -- Game Development - -### Engineering Disciplines - -- Security Engineering -- Performance Engineering -- Platform Engineering -- Database Engineering -- Infrastructure Engineering -- Network Engineering - -### Practice Disciplines - -- Architecture -- Quality Assurance -- Site Reliability Engineering -- DevOps/Platform -- Technical Documentation - -### Specialized Disciplines - -- Accessibility Engineering -- Data Engineering -- Machine Learning Engineering -- Blockchain Development -- Embedded Development -- Graphics Engineering -- Compiler Development - -## Examples of Well-Structured Dōs - -Reference these examples: - -- **do-frontend-development**: Excellent agent definitions with clear roles -- **do-security-engineering**: Strong technical depth -- **do-architecture**: Good balance of strategy and implementation - -## Testing Your Dō - -1. Install locally: - - ```bash - han plugin install /path/to/do-{discipline} - ``` +## Naming Guidelines -2. Invoke agents in various scenarios: +Discipline names should be descriptive and specific: - ``` - "I need help with [scenario] - can a [agent-name] help?" - ``` +| Good | Too Generic | +|------|-------------| +| `frontend-development` | `frontend` | +| `api-engineering` | `api` | +| `security-engineering` | `security` | +| `database-engineering` | `databases` | +| `site-reliability-engineering` | `sre` | -3. Verify agents provide appropriate depth and expertise +## Examples of Well-Structured Discipline Plugins -4. Test agent collaboration in complex scenarios +- **frontend-development**: Excellent agent definitions with clear roles +- **security-engineering**: Strong technical depth +- **system-architecture**: Good balance of strategy and implementation ## Questions? -See the [Han documentation](https://thebushidocollective.github.io/han) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). +See the [Han documentation](https://han.guru) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). diff --git a/.claude/skills/create-hashi/SKILL.md b/.claude/skills/create-hashi/SKILL.md index da39b6b13..aeaf28315 100644 --- a/.claude/skills/create-hashi/SKILL.md +++ b/.claude/skills/create-hashi/SKILL.md @@ -1,36 +1,40 @@ --- -description: Create a new hashi (teacher) plugin for MCP server integration +description: Create a new bridge plugin for MCP server integration --- -# Create a Hashi (橋 - Bridge) Plugin +# Create a Bridge (橋 - Hashi) Plugin -Create a new hashi plugin for: $ARGUMENTS +Create a new bridge plugin for: $ARGUMENTS -## What is a Hashi? +## What is a Bridge Plugin? -Hashis are "bridges" in the Han marketplace - they provide access to external knowledge and capabilities through MCP servers. A hashi connects Claude Code to external services, APIs, databases, or specialized tools that extend Claude's capabilities. +Bridge plugins provide access to external knowledge and capabilities through MCP servers. A bridge connects Claude Code to external services, APIs, databases, or specialized tools that extend Claude's capabilities. ## Plugin Structure Create the following directory structure: ``` -hashi/hashi-{service-name}/ +plugins/services/{service-name}/ ├── .claude-plugin/ │ └── plugin.json # Plugin metadata (ONLY plugin.json goes here) ├── han-plugin.yml # MCP server config + hooks + memory (at plugin root) -├── commands/ # Slash commands (optional) -│ └── {command-name}.md └── README.md # Plugin documentation ``` +### Categories + +| Category | For | Examples | +|----------|-----|---------| +| `services/` | External service integrations | github, gitlab, sentry, reddit | +| `bridges/` | AI tool bridges | gemini-cli, opencode, kiro | + **IMPORTANT**: - Only `plugin.json` goes inside `.claude-plugin/` (NO mcpServers here) - MCP server config goes in `han-plugin.yml` at plugin root - `han-plugin.yml` is the single source of truth for MCP config - -Note: Hashi plugins typically don't include skills or agents - they provide tools through MCP servers. +- Bridge plugins typically don't include skills or agents ## Step 1: Create plugin.json @@ -38,7 +42,7 @@ Create `.claude-plugin/plugin.json` (metadata only, NO mcpServers): ```json { - "name": "hashi-{service-name}", + "name": "{service-name}", "version": "1.0.0", "description": "MCP server configuration for {Service Name} integration providing {key capabilities}.", "author": { @@ -52,259 +56,89 @@ Create `.claude-plugin/plugin.json` (metadata only, NO mcpServers): "mcp", "{service-type}", "{capability}", - "server", - "{category}" + "server" ] } ``` -**Note**: Do NOT include `mcpServers` in plugin.json - it goes in `han-plugin.yml`. - ## Step 2: Create han-plugin.yml -Create `han-plugin.yml` at the plugin root with the MCP server configuration: +Create `han-plugin.yml` at the plugin root with the MCP server configuration. -```yaml -# hashi-{service-name} plugin configuration -# This plugin provides {Service Name} integration via MCP server +### Preferred: HTTP Transport -# MCP server definition (managed by Han MCP orchestrator) +```yaml mcp: name: {server-name} description: {Brief description of capabilities} - command: {command} - args: - - {arg1} - - {arg2} - env: - {ENV_VAR}: ${PLACEHOLDER} - capabilities: - - category: {Category} - summary: {What this server enables} - examples: - - {Example use case 1} - - {Example use case 2} - - {Example use case 3} - -# No hooks - MCP server plugins don't have validation hooks -hooks: {} - -# Memory provider for team memory extraction (optional) -memory: - allowed_tools: - - mcp__{server-name}__{tool_name_1} - - mcp__{server-name}__{tool_name_2} - system_prompt: | - {Instructions for how to use this MCP server for memory queries} -``` - -### MCP Configuration Types - -#### Stdio MCP Server (most common) - -```yaml -mcp: - name: service-name - description: Service integration - command: uvx - args: - - package-name - env: {} + type: http + url: https://mcp.service.com/mcp ``` -#### NPM Package MCP Server +### Alternative: NPX Package ```yaml mcp: - name: service-name - description: Service integration + name: {server-name} + description: {Brief description of capabilities} command: npx args: - -y - "@scope/package-name" ``` -#### HTTP/Remote MCP Server - -```yaml -mcp: - name: service-name - description: Service integration - type: http - url: https://mcp.service.com/mcp -``` - -#### With Environment Variables +### With Environment Variables ```yaml mcp: - name: service-name - description: Service integration + name: {server-name} + description: {Brief description of capabilities} command: npx args: - -y - "@scope/package" env: API_KEY: ${SERVICE_API_KEY} - API_URL: https://api.service.com ``` -### Environment Variable Patterns - -- **Required API Keys**: Use `${SERVICE_NAME_API_KEY}` format -- **Optional Configuration**: Provide sensible defaults -- **Shell Commands**: Use `$(command)` for dynamic values - ## Step 3: Write README.md -Create a comprehensive README: - ```markdown -# Hashi: {Service Name} +# {Service Name} -{Compelling description of what this MCP server provides and why it's valuable} +{Description of what this MCP server provides} -## What This Hashi Provides +## What This Plugin Provides ### MCP Server: {server-name} -{Detailed description of the MCP server's capabilities} - -This hashi connects Claude Code to {service} and provides: - - **{Capability 1}**: {Description} - **{Capability 2}**: {Description} -- **{Capability 3}**: {Description} -- **{Capability 4}**: {Description} - -### Available Tools - -Once installed, Claude Code gains access to these tools: - -- \`{tool-name-1}\`: {What it does} -- \`{tool-name-2}\`: {What it does} -- \`{tool-name-3}\`: {What it does} -- \`{tool-name-4}\`: {What it does} ## Installation -### Prerequisites - -{List any prerequisites} - -- {Requirement 1} -- {Requirement 2} -- {API key or authentication requirements} - -### Via Han Marketplace - -\`\`\`bash -han plugin install hashi-{service-name} -\`\`\` - -Or install manually: - \`\`\`bash -claude plugin marketplace add thebushidocollective/han -claude plugin install hashi-{service-name}@han +han plugin install {service-name} \`\`\` -### Configuration - -{If environment variables are required:} +## Configuration -1. Set required environment variables: +Set required environment variables: \`\`\`bash export {ENV_VAR}="your-value-here" \`\`\` - -Or add to your shell profile (\`~/.zshrc\`, \`~/.bashrc\`): - -\`\`\`bash -echo 'export {ENV_VAR}="your-value-here"' >> ~/.zshrc -source ~/.zshrc -\`\`\` - -## Usage - -### Example 1: {Use Case} - -{Practical example of using the MCP server} - -### Example 2: {Use Case} - -{Another practical example} - -## Tool Reference - -### \`{tool-name-1}\` - -**Purpose**: {What this tool does} - -**Parameters**: -- \`{param1}\` (required): {description} -- \`{param2}\` (optional): {description} - -## Limitations - -{Known limitations of the MCP server or service} - -- {Limitation 1} -- {Limitation 2} - -## Troubleshooting - -### Issue: {Common Problem} - -**Solution**: {How to resolve} - -## Related Plugins - -{List related hashis or jutsus} - -- **hashi-{related}**: {What it provides} - -## License - -MIT License - See [LICENSE](../../LICENSE) for details. - -## Links - -- [{Service} Documentation]({url}) -- [{MCP Server Package}]({npm or pypi url}) -- [MCP Protocol Specification](https://modelcontextprotocol.io) -``` - -## Step 4: Create Commands (Optional) - -If your hashi provides useful workflows, create commands in the `commands/` directory: - -```markdown ---- -description: Brief description of what this command does ---- - -# Command Title - -{Instructions for Claude on how to use the MCP tools for this workflow} - -## Steps - -1. {Step 1 using MCP tools} -2. {Step 2 using MCP tools} -3. {Step 3 using MCP tools} ``` -## Step 5: Register in Marketplace +## Step 4: Register in Marketplace Add your plugin to `.claude-plugin/marketplace.json`: ```json { - "name": "hashi-{service-name}", + "name": "{service-name}", "description": "MCP server for {Service Name} integration providing {key capabilities}.", - "source": "./hashi/hashi-{service-name}", + "source": "./plugins/services/{service-name}", "category": "Bridge", "keywords": [ "mcp", @@ -319,82 +153,23 @@ Add your plugin to `.claude-plugin/marketplace.json`: ### DO -✅ Put MCP config in `han-plugin.yml`, NOT in `plugin.json` -✅ Use well-maintained MCP server packages -✅ Document all environment variables clearly -✅ Provide practical usage examples -✅ Include security considerations -✅ Document tool parameters and return values -✅ Add commands for common workflows -✅ Include memory provider config for team memory integration +- Put MCP config in `han-plugin.yml`, NOT in `plugin.json` +- Prefer HTTP transport over stdio when available +- Use well-maintained MCP server packages +- Document all environment variables clearly ### DON'T -❌ Don't put mcpServers in plugin.json (use han-plugin.yml) -❌ Don't hardcode API keys or secrets -❌ Don't skip documentation of required setup -❌ Don't forget to test with actual API credentials -❌ Don't ignore rate limits and quotas - -## MCP Server Categories - -### Knowledge & Documentation - -- Context7: Up-to-date library documentation -- Web search and crawling -- Documentation aggregators - -### Development Tools - -- Git operations -- GitHub/GitLab integration -- Playwright browser automation -- Database clients - -### External Services - -- Cloud providers (AWS, GCP, Azure) -- Communication (Slack, Discord) -- Project management (Jira, Linear) -- Social media (Reddit, Twitter) - -## Testing Your Hashi - -1. Install locally: - - ```bash - han plugin install /path/to/hashi-{name} - ``` - -2. Verify MCP server connects: - - ```bash - # Check Claude Code logs for connection errors - tail -f ~/.claude/logs/claude.log - ``` - -3. Test tools are available: - - ``` - # In Claude Code, ask to use specific tools - "Can you use the {tool-name} tool to {action}?" - ``` - -## Examples of Well-Structured Hashis - -Reference these examples: - -- **hashi-github**: Excellent han-plugin.yml with memory provider -- **hashi-sentry**: HTTP-based MCP server example -- **hashi-reddit**: Simple uvx-based MCP server +- Don't put mcpServers in plugin.json (use han-plugin.yml) +- Don't hardcode API keys or secrets +- Don't use Docker unless no HTTP or npx option exists -## MCP Resources +## Examples of Well-Structured Bridge Plugins -- [MCP Specification](https://modelcontextprotocol.io) -- [MCP SDK (TypeScript)](https://github.com/modelcontextprotocol/typescript-sdk) -- [MCP SDK (Python)](https://github.com/modelcontextprotocol/python-sdk) -- [MCP Servers List](https://github.com/modelcontextprotocol/servers) +- **github**: HTTP transport with OAuth +- **sentry**: HTTP-based MCP server +- **reddit**: Simple uvx-based MCP server ## Questions? -See the [Han documentation](https://thebushidocollective.github.io/han) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). +See the [Han documentation](https://han.guru) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). diff --git a/.claude/skills/create-jutsu/SKILL.md b/.claude/skills/create-jutsu/SKILL.md index 9ec8a36a8..4df1458e0 100644 --- a/.claude/skills/create-jutsu/SKILL.md +++ b/.claude/skills/create-jutsu/SKILL.md @@ -1,21 +1,21 @@ --- -description: Create a new jutsu (weapon) plugin for a technology +description: Create a new technique plugin for a technology (language, tool, framework, or validation) --- -# Create a Jutsu (術 - Technique) Plugin +# Create a Technique (術 - Jutsu) Plugin -Create a new jutsu plugin for: $ARGUMENTS +Create a new technique plugin for: $ARGUMENTS -## What is a Jutsu? +## What is a Technique Plugin? -Jutsus are "techniques" in the Han marketplace - they provide validation hooks that automatically enforce quality standards for specific technologies. A jutsu watches for tool usage and validates the results. +Technique plugins provide validation hooks that automatically enforce quality standards for specific technologies. They watch for tool usage and validate the results. They live under `plugins/` organized by category. ## Plugin Structure -Create the following directory structure: +Create the following directory structure under the appropriate category: ``` -jutsu/jutsu-{name}/ +plugins/{category}/{name}/ ├── .claude-plugin/ │ └── plugin.json # Plugin metadata (ONLY plugin.json goes here) ├── han-plugin.yml # Han hook configurations (at plugin root) @@ -27,6 +27,16 @@ jutsu/jutsu-{name}/ └── README.md # Plugin documentation ``` +### Categories + +| Category | For | Examples | +|----------|-----|---------| +| `languages/` | Programming languages | typescript, python, rust | +| `frameworks/` | Frameworks | react, nextjs, relay | +| `tools/` | Build/dev tools | bun, playwright, mise | +| `validation/` | Linters/formatters | biome, eslint, prettier | +| `patterns/` | Development patterns | tdd, bdd, git-storytelling | + **IMPORTANT**: - Only `plugin.json` goes inside `.claude-plugin/` @@ -39,7 +49,7 @@ Create `.claude-plugin/plugin.json`: ```json { - "name": "jutsu-{technology-name}", + "name": "{technology-name}", "version": "1.0.0", "description": "Validation and quality enforcement for {Technology Name} projects.", "author": { @@ -64,7 +74,7 @@ Create `.claude-plugin/plugin.json`: Create `han-plugin.yml` at the plugin root with hook definitions: ```yaml -# jutsu-{technology-name} plugin configuration +# {technology-name} plugin configuration # This plugin provides validation hooks for {Technology Name} projects # Hook definitions (managed by Han orchestrator) @@ -76,25 +86,17 @@ hooks: ifChanged: - "{glob-patterns}" -# No MCP server for jutsu plugins (they use hooks, not MCP) +# No MCP server for technique plugins (they use hooks, not MCP) mcp: null # Memory provider (optional) memory: null ``` -### Hook Configuration Guide - -- **hooks**: Object containing named hook definitions -- **{hook-name}**: Descriptive name for the hook (e.g., "lint", "test", "typecheck", "build") -- **command**: The validation command to run (e.g., "npm run lint", "cargo check") -- **dirsWith**: Array of marker files to detect relevant directories (e.g., ["package.json"]) -- **ifChanged**: Array of glob patterns to watch for changes (optional, enables caching) - ### Example han-plugin.yml ```yaml -# jutsu-typescript plugin configuration +# typescript plugin configuration # This plugin provides TypeScript validation hooks hooks: @@ -114,7 +116,7 @@ memory: null ### Multi-Hook Example ```yaml -# jutsu-rust plugin configuration +# rust plugin configuration hooks: check: @@ -157,7 +159,7 @@ Create `hooks/hooks.json` to register the hook with Claude Code events: "hooks": [ { "type": "command", - "command": "han hook run jutsu-{technology-name} {hook-name} --fail-fast --cached", + "command": "han hook run {technology-name} {hook-name} --fail-fast --cached", "timeout": 120 } ] @@ -167,16 +169,6 @@ Create `hooks/hooks.json` to register the hook with Claude Code events: } ``` -### hooks.json Configuration Guide - -- **hooks**: Top-level object containing event hooks -- **Stop**: Hooks that run when conversation stops -- **type**: Always "command" for hook execution -- **command**: Uses `han hook run {plugin-name} {hook-name}` format -- **--fail-fast**: Stop on first error for quick feedback -- **--cached**: Enable smart caching based on ifChanged patterns -- **timeout**: Max execution time in milliseconds (120000 = 2 minutes) - ### Marker Files by Technology - JavaScript/TypeScript: `package.json` @@ -188,27 +180,10 @@ Create `hooks/hooks.json` to register the hook with Claude Code events: - C#: `*.csproj` - Elixir: `mix.exs` -### Common Validation Commands - -- **TypeScript**: `npx -y --package typescript tsc` -- **ESLint**: `npx eslint . --max-warnings 0` -- **Biome**: `npx @biomejs/biome check .` -- **Pytest**: `pytest` -- **RSpec**: `bundle exec rspec` -- **Cargo**: `cargo check && cargo clippy` -- **Go**: `go vet ./... && go test ./...` - ## Step 4: Create Skills For each major concept/feature of the technology, create a skill: -### Skill Directory Structure - -``` -skills/{skill-name}/ -└── SKILL.md -``` - ### SKILL.md Format ```markdown @@ -229,107 +204,35 @@ allowed-tools: {Brief overview of this skill and when to use it} ## Key Concepts - -{Explain the main concepts this skill covers} - ## Best Practices - -{List the best practices for this feature} - ## Examples - -{Provide practical code examples} - ## Common Patterns - -{Show common usage patterns} - ## Anti-Patterns - -{Explain what to avoid} - -## Related Skills - -- {Link to related skills} ``` -### Recommended Skill Categories - -1. **Core Language/Framework Features** (3-5 skills) - - Type system, syntax, fundamental patterns - -2. **Testing Patterns** (1-2 skills) - - Unit testing, integration testing, mocking - -3. **Configuration** (1 skill) - - Project setup, configuration files, build settings - -4. **Advanced Features** (2-4 skills) - - Performance optimization, async patterns, metaprogramming - -5. **Integration Patterns** (1-2 skills) - - Working with other tools, CI/CD, deployment - ## Step 5: Write README.md -Create a comprehensive README: - ```markdown -# Jutsu: {Technology Name} +# {Technology Name} -{Brief description of what this jutsu validates} +{Brief description of what this plugin validates} -## What This Jutsu Provides +## What This Plugin Provides ### Validation Hooks - **{Technology} Validation**: Runs {validation tool} to ensure code quality -- **Type Checking**: Validates type safety (if applicable) -- **Linting**: Enforces code style standards ### Skills -This jutsu provides the following skills: - - **{skill-1}**: {brief description} - **{skill-2}**: {brief description} -- **{skill-3}**: {brief description} ## Installation -Install via the Han marketplace: - -\`\`\`bash -han plugin install jutsu-{technology-name} -\`\`\` - -Or install manually: - \`\`\`bash -claude plugin marketplace add thebushidocollective/han -claude plugin install jutsu-{technology-name}@han +han plugin install {technology-name} \`\`\` - -## Usage - -Once installed, this jutsu automatically validates your {technology} code: - -- When you finish a conversation with Claude Code -- When Claude Code agents complete their work -- Before commits (when combined with git hooks) - -## Requirements - -- {Technology} {minimum version} -- {Any required tools or dependencies} - -## Contributing - -See [CONTRIBUTING.md](../../CONTRIBUTING.md) for guidelines. - -## License - -MIT License - See [LICENSE](../../LICENSE) for details. ``` ## Step 6: Register in Marketplace @@ -338,9 +241,9 @@ Add your plugin to `.claude-plugin/marketplace.json`: ```json { - "name": "jutsu-{technology-name}", + "name": "{technology-name}", "description": "Validation and quality enforcement for {Technology Name} projects.", - "source": "./jutsu/jutsu-{technology-name}", + "source": "./plugins/{category}/{technology-name}", "category": "Technique", "keywords": [ "{technology}", @@ -351,57 +254,12 @@ Add your plugin to `.claude-plugin/marketplace.json`: } ``` -## Best Practices - -### DO - -✅ Focus on read-only validation (check, lint, test) -✅ Use `--fail-fast` for quick feedback -✅ Use `--dirs-with` to invoke hooks in multiple directories -✅ Provide clear error messages with `showOutput: "on-error"` -✅ Include skills for modern (2024-2025) features -✅ Test your hooks in both single-project and monorepo scenarios -✅ Document required versions and dependencies -✅ Include practical examples in skills - -### DON'T - -❌ Don't auto-fix code in hooks (hooks should only validate) -❌ Don't include placeholder or template content in skills -❌ Don't copy examples from other languages into your skills -❌ Don't skip testing patterns and CI/CD integration skills -❌ Don't forget security and performance patterns -❌ Don't use outdated framework versions in examples - -## Testing Your Jutsu - -1. Install locally: - - ```bash - han plugin install /path/to/jutsu-{name} - ``` - -2. Test validation hooks: - - ```bash - # Intentionally break code to trigger validation - # Then run Claude Code to verify hooks execute - ``` - -3. Verify skills are accessible: - - ```bash - # In Claude Code, invoke a skill and verify it provides correct guidance - ``` - -## Examples of Well-Structured Jutsus - -Reference these examples: +## Examples of Well-Structured Technique Plugins -- **jutsu-biome**: Excellent hook configuration and skill organization -- **jutsu-typescript**: Clean validation patterns -- **jutsu-playwright**: Comprehensive testing coverage +- **biome**: Excellent hook configuration and skill organization +- **typescript**: Clean validation patterns +- **playwright**: Comprehensive testing coverage ## Questions? -See the [Han documentation](https://thebushidocollective.github.io/han) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). +See the [Han documentation](https://han.guru) or ask in [GitHub Discussions](https://github.com/thebushidocollective/han/discussions). diff --git a/.claude/skills/research-new-features/SKILL.md b/.claude/skills/research-new-features/SKILL.md index b7e5dd932..154534ac9 100644 --- a/.claude/skills/research-new-features/SKILL.md +++ b/.claude/skills/research-new-features/SKILL.md @@ -8,7 +8,7 @@ Research feature requests, pain points, and community discussions about Claude A ## MCP Server Required -This command requires the `hashi-reddit` MCP server to be installed and running. +This command requires the `reddit` MCP server to be installed and running. ## Target Subreddits diff --git a/.mcp.json b/.mcp.json index 038380453..2f24a4bcf 100644 --- a/.mcp.json +++ b/.mcp.json @@ -3,8 +3,19 @@ "Railway": { "type": "stdio", "command": "npx", - "args": ["@railway/mcp-server"], + "args": [ + "@railway/mcp-server" + ], + "env": {} + }, + "playwright": { + "type": "stdio", + "command": "npx", + "args": [ + "@playwright/mcp@latest", + "--headless" + ], "env": {} } } -} +} \ No newline at end of file diff --git a/packages/han/lib/plugin-aliases.ts b/packages/han/lib/plugin-aliases.ts index ceb7b9533..a1615a2b9 100644 --- a/packages/han/lib/plugin-aliases.ts +++ b/packages/han/lib/plugin-aliases.ts @@ -131,7 +131,11 @@ export const PLUGIN_ALIASES: Record = { blueprints: 'tools/blueprints', // bridges + 'hashi-antigravity': 'bridges/antigravity', + 'hashi-gemini-cli': 'bridges/gemini-cli', 'hashi-opencode': 'bridges/opencode', + 'hashi-kiro': 'bridges/kiro', + 'hashi-codex': 'bridges/codex', // services 'hashi-agent-sop': 'services/agent-sop', diff --git a/plugins/bridges/antigravity/.claude-plugin/plugin.json b/plugins/bridges/antigravity/.claude-plugin/plugin.json new file mode 100644 index 000000000..bc4ab031c --- /dev/null +++ b/plugins/bridges/antigravity/.claude-plugin/plugin.json @@ -0,0 +1,21 @@ +{ + "name": "antigravity", + "version": "0.1.0", + "description": "Bridge plugin that enables Han's hook ecosystem (validation, skills, disciplines) to work with Google Antigravity IDE via MCP server.", + "author": { + "name": "The Bushido Collective", + "url": "https://thebushido.co" + }, + "homepage": "https://github.com/thebushidocollective/han", + "repository": "https://github.com/thebushidocollective/han", + "license": "Apache-2.0", + "keywords": [ + "antigravity", + "google", + "bridge", + "mcp", + "hooks", + "validation", + "skills" + ] +} diff --git a/plugins/bridges/antigravity/README.md b/plugins/bridges/antigravity/README.md new file mode 100644 index 000000000..4e8731b8e --- /dev/null +++ b/plugins/bridges/antigravity/README.md @@ -0,0 +1,188 @@ +# Han Bridge for Google Antigravity + +MCP server that brings Han's full plugin ecosystem to [Google Antigravity](https://antigravity.google/) IDE -- 400+ skills, 25 agent disciplines, and on-demand validation hooks. + +## What This Does + +Han plugins define validation hooks, specialized skills, and agent disciplines that run during Claude Code sessions. This bridge makes the entire ecosystem work in Antigravity via MCP: + +1. **Skills** -- 400+ coding skills loadable on demand via `han_skills` tool +2. **Disciplines** -- 25 agent personas (frontend, backend, SRE, security, etc.) +3. **Validation** -- On-demand linting, type checking, and formatting via `han_validate` +4. **Sync** -- Copy skills and rules to `.agent/` for native Antigravity discovery +5. **Context** -- Current time and session state via `han_context` +6. **Events** -- Unified JSONL logging with `provider: "antigravity"` for Browse UI + +## How It Works + +Unlike the OpenCode bridge (which hooks into JS plugin events), Antigravity doesn't have lifecycle hooks. Instead, this bridge runs as an MCP server and exposes Han's capabilities as tools: + +| Tool | Purpose | Replaces | +|------|---------|----------| +| `han_skills` | Browse/load 400+ skills | Native skill discovery | +| `han_discipline` | Activate agent personas | System prompt injection | +| `han_validate` | Run validation hooks | PostToolUse/Stop hooks | +| `han_sync` | Sync skills/rules to .agent/ | SessionStart setup | +| `han_context` | Get time + active discipline | UserPromptSubmit context | + +## Validation Modes + +### Per-File Validation (After Edits) + +``` +Agent: han_validate({ mode: "file", files: ["src/app.ts"], tool: "Edit" }) +→ Runs matching PostToolUse hooks (biome, eslint, tsc) +→ Returns structured results with pass/fail per hook +``` + +### Project-Wide Validation (Before Finishing) + +``` +Agent: han_validate({ mode: "project" }) +→ Runs all Stop hooks (full project lint, typecheck, tests) +→ Returns comprehensive validation summary +``` + +## Setup + +### Prerequisites + +Han plugins must be installed: + +```bash +curl -fsSL https://han.guru/install.sh | bash +han plugin install --auto +``` + +### Add MCP Server + +Add to `~/.gemini/antigravity/mcp_config.json`: + +```json +{ + "mcpServers": { + "han": { + "command": "npx", + "args": ["-y", "antigravity-han-mcp"] + } + } +} +``` + +### Optional: Sync Skills Natively + +After setup, ask the agent to sync Han skills for native Antigravity discovery: + +``` +> Run han_sync to set up Han skills +``` + +This copies skills to `.agent/skills/` and creates `.agent/rules/han-guidelines.md`. + +## Architecture + +``` +Google Antigravity IDE + | + |-- mcp_config.json ─────────────────────> Han MCP Server + | (stdio transport) | + | | + |-- han_skills ──────────────────────────> Skill discovery + | (LLM-callable tool) (list/search/load 400+ skills) + | | + |-- han_discipline ──────────────────────> Agent disciplines + | (LLM-callable tool) (activate/deactivate/list) + | | + |-- han_validate ────────────────────────> Hook executor + | (LLM-callable tool) | + | mode="file" ──> PostToolUse hooks | + | mode="project" ──> Stop hooks | + | | + |-- han_sync ────────────────────────────> Filesystem sync + | (LLM-callable tool) | + | skills → .agent/skills/ | + | rules → .agent/rules/ | + | | + |-- han_context ─────────────────────────> Session state + | (LLM-callable tool) (time, discipline, stats) + | | + |-- .agent/rules/han-guidelines.md ──────> Core guidelines + | (native Antigravity rules) (synced by han_sync) + | | + |-- .agent/skills/han-*/SKILL.md ────────> Native skills + | (native Antigravity skills) (synced by han_sync) + | | + |-- JSONL logger ────────────────────────> Event logging + (hook_run, hook_result) (Browse UI visibility) + +Bridge Internals: + index.ts MCP server entry point (JSON-RPC over stdio) + discovery.ts Read settings + marketplace + resolve plugin paths + skills.ts Discover SKILL.md files from installed plugins + disciplines.ts Discover discipline plugins, context builder + context.ts Core guidelines + datetime for rules/context + sync.ts Copy skills/rules to .agent/ directory + matcher.ts Filter hooks by tool name, file globs, dirsWith + executor.ts Spawn hook commands as parallel promises + formatter.ts Structure results for MCP tool responses + events.ts JSONL event logger (provider="antigravity") + cache.ts Content-hash caching (SHA-256) +``` + +## Comparison with OpenCode Bridge + +| Feature | OpenCode Bridge | Antigravity Bridge | +|---------|----------------|-------------------| +| Integration | JS plugin API (events) | MCP server (tools) | +| Validation trigger | Automatic (tool.execute.after) | On-demand (han_validate) | +| Context injection | experimental.chat.system.transform | .agent/rules/ + han_context | +| Skill discovery | han_skills tool | han_skills tool + .agent/skills/ sync | +| Stop validation | Automatic (session.idle/stop) | On-demand (han_validate mode=project) | +| Event logging | provider: "opencode" | provider: "antigravity" | + +## Remaining Gaps + +These are genuine platform limitations in Antigravity (as of Feb 2026): + +- **Automatic validation**: No lifecycle hooks, so validation requires explicit han_validate calls +- **Permission denial**: Cannot block tool execution (no PreToolUse equivalent) +- **Subagent hooks**: No SubagentStart/SubagentStop equivalent +- **Session checkpoints**: Not available +- **MCP tool events**: Cannot intercept MCP tool calls for validation + +## Skills + +The bridge gives the agent access to Han's full skill library (400+ skills across 95+ plugins). Two modes: + +- **MCP tool**: Agent calls `han_skills` to list/load skills on demand +- **Native sync**: `han_sync` copies skills to `.agent/skills/` for Antigravity's native semantic matching + +## Disciplines + +25 agent personas available via `han_discipline`: + +- frontend, backend, api, architecture, mobile, database, security +- infrastructure, sre, performance, accessibility, quality, documentation +- and more + +When activated, the discipline's context is returned with each `han_context` call. + +## Event Logging + +Events are logged to `~/.han/antigravity/projects/{slug}/{sessionId}-han.jsonl` with `provider: "antigravity"`. The Han coordinator indexes these files and serves them through the Browse UI alongside Claude Code and OpenCode sessions. + +## Development + +```bash +# From the han repo root +cd plugins/bridges/antigravity + +# Run the MCP server directly +bun src/index.ts --project-dir /path/to/project + +# The server reads JSON-RPC from stdin and writes to stdout +``` + +## License + +Apache-2.0 diff --git a/plugins/bridges/antigravity/han-plugin.yml b/plugins/bridges/antigravity/han-plugin.yml new file mode 100644 index 000000000..e76b57116 --- /dev/null +++ b/plugins/bridges/antigravity/han-plugin.yml @@ -0,0 +1,18 @@ +# Han plugin configuration for the Antigravity bridge. +# +# This plugin has no hooks of its own - it IS the bridge that enables +# other Han plugins' hooks to run inside Google Antigravity IDE via MCP. +# +# Installation: +# 1. Install Han plugins as usual: han plugin install --auto +# 2. Add MCP server to ~/.gemini/antigravity/mcp_config.json: +# { "mcpServers": { "han": { "command": "npx", "args": ["-y", "antigravity-han-mcp"] } } } +# 3. Han skills, disciplines, and validation are now available in Antigravity. + +hooks: {} + +learn_patterns: + - "antigravity" + - "anti.?gravity" + - "gemini" + - "agy" diff --git a/plugins/bridges/antigravity/package.json b/plugins/bridges/antigravity/package.json new file mode 100644 index 000000000..22ea40e56 --- /dev/null +++ b/plugins/bridges/antigravity/package.json @@ -0,0 +1,42 @@ +{ + "name": "antigravity-han-mcp", + "version": "0.1.0", + "description": "Han bridge for Google Antigravity - MCP server enabling Han's validation hooks, skills, and disciplines in Antigravity IDE", + "main": "src/index.ts", + "bin": "src/index.ts", + "type": "module", + "exports": { + ".": "./src/index.ts" + }, + "files": [ + "src/" + ], + "keywords": [ + "antigravity", + "google-antigravity", + "mcp", + "mcp-server", + "han", + "claude-code", + "hooks", + "validation", + "linting", + "bridge", + "skills" + ], + "author": { + "name": "The Bushido Collective", + "url": "https://thebushido.co" + }, + "license": "Apache-2.0", + "repository": { + "type": "git", + "url": "https://github.com/thebushidocollective/han", + "directory": "plugins/bridges/antigravity" + }, + "homepage": "https://github.com/thebushidocollective/han/tree/main/plugins/bridges/antigravity", + "engines": { + "bun": ">=1.0.0", + "node": ">=18.0.0" + } +} diff --git a/plugins/bridges/antigravity/src/cache.ts b/plugins/bridges/antigravity/src/cache.ts new file mode 100644 index 000000000..a6f3d839c --- /dev/null +++ b/plugins/bridges/antigravity/src/cache.ts @@ -0,0 +1,86 @@ +/** + * Content-hash cache for hook execution. + * + * Tracks file content hashes per hook. If a file's hash hasn't changed + * since the last successful run of a hook, that hook is skipped. + * + * Cache key: `${pluginName}:${hookName}:${filePath}` + * Cache value: content hash after last successful hook run + */ + +import { readFileSync } from "node:fs" +import { createHash } from "node:crypto" + +/** Map of cache keys to content hashes from the last successful run */ +const hashCache = new Map() + +/** + * Compute SHA-256 hash of a file's contents. + */ +function hashFile(filePath: string): string | null { + try { + const content = readFileSync(filePath) + return createHash("sha256").update(content).digest("hex") + } catch { + return null + } +} + +function cacheKey( + pluginName: string, + hookName: string, + filePath: string, +): string { + return `${pluginName}:${hookName}:${filePath}` +} + +/** + * Check if a hook can be skipped for the given file. + */ +export function shouldSkipHook( + pluginName: string, + hookName: string, + filePath: string, +): boolean { + const key = cacheKey(pluginName, hookName, filePath) + const cachedHash = hashCache.get(key) + if (!cachedHash) return false + + const currentHash = hashFile(filePath) + if (!currentHash) return false + + return cachedHash === currentHash +} + +/** + * Record a successful hook run. + */ +export function recordSuccess( + pluginName: string, + hookName: string, + filePath: string, +): void { + const hash = hashFile(filePath) + if (hash) { + const key = cacheKey(pluginName, hookName, filePath) + hashCache.set(key, hash) + } +} + +/** + * Invalidate cache for a file across all hooks. + */ +export function invalidateFile(filePath: string): void { + for (const [key] of hashCache) { + if (key.endsWith(`:${filePath}`)) { + hashCache.delete(key) + } + } +} + +/** + * Clear the entire cache. + */ +export function clearCache(): void { + hashCache.clear() +} diff --git a/plugins/bridges/antigravity/src/context.ts b/plugins/bridges/antigravity/src/context.ts new file mode 100644 index 000000000..fb72e5c5b --- /dev/null +++ b/plugins/bridges/antigravity/src/context.ts @@ -0,0 +1,92 @@ +/** + * Context and guidelines for Antigravity sessions. + * + * Generates rules content that can be synced to .agent/rules/ or + * returned via MCP tools. These guidelines are LLM-universal and + * improve agent quality regardless of provider. + */ + +/** + * Core guidelines injected into the agent's context. + * These are written to .agent/rules/han-guidelines.md by the sync tool. + */ +export const CORE_GUIDELINES = `# Han Guidelines + +## Professional Honesty - Epistemic Rigor + +When a user makes claims about code behavior, bugs, system state, performance, or architecture: +- **VERIFY BEFORE PROCEEDING** - Read code, search codebase, run tests +- **NEVER** say "You're absolutely right" or accept claims without evidence +- **ALWAYS** start with "Let me verify..." or "I'll check the current implementation..." +- Evidence required: Read files, search with tools, run commands + +When user knowledge IS trusted (no verification needed): +- User preferences, project decisions, new feature requirements, styling choices + +## No Time Estimates + +- NEVER provide time estimates (hours, days, weeks, months) +- NEVER use temporal planning language ("Week 1-2", "By month 2") +- INSTEAD use: Phase numbers, priority order, dependency-based sequencing + +## No Excuses Policy + +- If you encounter issues, fix them - pre-existing or not +- NEVER categorize failures as "pre-existing" or "not caused by our changes" +- You own every issue you see (Boy Scout Rule) +- Test failures are not acceptable - investigate and fix all of them + +## Date Handling + +- Use the current date for temporal assertions +- NEVER hardcode future dates in tests (use relative dates or mock clocks) +- Use ISO 8601 format for machine-readable timestamps +- Store dates in UTC, convert to local only for display + +## Skill Selection + +Review available skills BEFORE starting work. Use han_skills to: +1. Search for relevant skills matching your task +2. Load skill content for specialized guidance +3. Announce which skills you're applying and why +` + +/** + * Build rules file content for .agent/rules/han-guidelines.md. + */ +export function buildRulesContent( + skillCount: number, + disciplineCount: number, +): string { + const lines: string[] = [CORE_GUIDELINES] + + lines.push(`## Han Capabilities\n`) + lines.push( + `Han bridge active with ${skillCount} skills and ${disciplineCount} disciplines available.`, + ) + lines.push( + `MCP tools: han_skills (browse/load skills), han_discipline (activate agent personas), han_validate (run validation hooks), han_sync (sync skills/rules)`, + ) + lines.push("") + + return lines.join("\n") +} + +/** + * Build per-prompt context with current datetime. + */ +export function buildPromptContext(): string { + const now = new Date() + const dateStr = now.toLocaleString("en-US", { + weekday: "short", + year: "numeric", + month: "short", + day: "numeric", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + timeZoneName: "short", + }) + + return `Current local time: ${dateStr}` +} diff --git a/plugins/bridges/antigravity/src/disciplines.ts b/plugins/bridges/antigravity/src/disciplines.ts new file mode 100644 index 000000000..15711d743 --- /dev/null +++ b/plugins/bridges/antigravity/src/disciplines.ts @@ -0,0 +1,136 @@ +/** + * Discipline discovery and context building for Antigravity. + * + * Han's discipline plugins define specialized agent personas (frontend, + * backend, SRE, security, etc.) with curated skill sets. This module + * discovers available disciplines and builds context for injection into + * Antigravity's rules or GEMINI.md. + */ + +import { readFileSync, existsSync } from "node:fs" +import { join } from "node:path" +import type { SkillInfo } from "./skills" + +/** + * Discipline metadata parsed from a discipline plugin. + */ +export interface DisciplineInfo { + /** Discipline name (e.g. "frontend", "sre", "security") */ + name: string + /** Description from plugin.json */ + description: string + /** Plugin root directory */ + pluginRoot: string + /** Skills provided by this discipline */ + skills: SkillInfo[] +} + +/** + * Parse plugin.json for discipline metadata. + */ +function parsePluginJson( + pluginRoot: string, +): { name: string; description: string } | null { + const pluginJsonPath = join(pluginRoot, ".claude-plugin", "plugin.json") + if (!existsSync(pluginJsonPath)) return null + + try { + const content = readFileSync(pluginJsonPath, "utf-8") + const json = JSON.parse(content) + return { + name: json.name ?? "", + description: json.description ?? "", + } + } catch { + return null + } +} + +/** + * Check if a plugin is a discipline plugin by its directory structure. + */ +function isDisciplinePlugin(pluginRoot: string): boolean { + return pluginRoot.includes("/disciplines/") || pluginRoot.includes("\\disciplines\\") +} + +/** + * Discover all discipline plugins from resolved plugin paths. + */ +export function discoverDisciplines( + resolvedPlugins: Map, + allSkills: SkillInfo[], +): DisciplineInfo[] { + const disciplines: DisciplineInfo[] = [] + + for (const [pluginName, pluginRoot] of resolvedPlugins) { + if (!isDisciplinePlugin(pluginRoot)) continue + + const meta = parsePluginJson(pluginRoot) + if (!meta) continue + + const disciplineSkills = allSkills.filter( + (s) => s.pluginName === pluginName, + ) + + disciplines.push({ + name: pluginName, + description: meta.description, + pluginRoot, + skills: disciplineSkills, + }) + } + + return disciplines +} + +/** + * Format discipline list for display. + */ +export function formatDisciplineList(disciplines: DisciplineInfo[]): string { + if (disciplines.length === 0) { + return "No discipline plugins discovered. Install with: han plugin install --auto" + } + + const lines: string[] = [`Available disciplines (${disciplines.length}):\n`] + + for (const d of disciplines) { + const skillCount = d.skills.length + lines.push( + `- **${d.name}**: ${d.description || "(no description)"}` + + (skillCount > 0 ? ` (${skillCount} skills)` : ""), + ) + } + + lines.push( + `\nUse han_discipline with action="activate" and discipline="" to activate a discipline.`, + ) + + return lines.join("\n") +} + +/** + * Build context for an active discipline. + * Injected via .agent/rules/ or returned as tool output. + */ +export function buildDisciplineContext(discipline: DisciplineInfo): string { + const lines: string[] = [ + ``, + `You are operating as a **${discipline.name}** specialist.`, + discipline.description ? `\n${discipline.description}` : "", + ] + + if (discipline.skills.length > 0) { + lines.push("\n## Available Skills\n") + lines.push( + "The following specialized skills are available for this discipline. " + + "Use han_skills to load any skill's full content when needed:\n", + ) + for (const skill of discipline.skills) { + lines.push(`- **${skill.name}**: ${skill.description || "(no description)"}`) + } + } + + lines.push("") + + return lines.join("\n") +} diff --git a/plugins/bridges/antigravity/src/discovery.ts b/plugins/bridges/antigravity/src/discovery.ts new file mode 100644 index 000000000..e41adc381 --- /dev/null +++ b/plugins/bridges/antigravity/src/discovery.ts @@ -0,0 +1,355 @@ +/** + * Plugin and hook discovery. + * + * Reads Claude Code settings files to find installed Han plugins, + * resolves their paths via the marketplace, and parses han-plugin.yml + * to extract hook definitions. + * + * Shared logic with the OpenCode bridge - reads the same settings + * and marketplace files since Han plugins are provider-agnostic. + */ + +import { readFileSync, existsSync } from "node:fs" +import { join, resolve, dirname } from "node:path" +import { homedir } from "node:os" +import type { HookDefinition } from "./types" + +// ─── Settings Discovery ───────────────────────────────────────────────────── + +interface PluginEntry { + enabled?: boolean +} + +interface ClaudeSettings { + plugins?: Record +} + +interface MarketplacePlugin { + name: string + source: string +} + +interface Marketplace { + plugins: MarketplacePlugin[] +} + +/** + * Find all enabled Han plugins by merging user, project, and local settings. + */ +function getEnabledPlugins(projectDir: string): string[] { + const pluginNames = new Set() + + const settingsPaths = [ + join(homedir(), ".claude", "settings.json"), // user scope + join(projectDir, ".claude", "settings.json"), // project scope + join(projectDir, ".claude", "settings.local.json"), // local scope + ] + + for (const path of settingsPaths) { + if (!existsSync(path)) continue + try { + const content = readFileSync(path, "utf-8") + const settings: ClaudeSettings = JSON.parse(content) + if (!settings.plugins) continue + + for (const [name, entry] of Object.entries(settings.plugins)) { + if (entry.enabled !== false) { + // Strip @han suffix if present: "biome@han" -> "biome" + const cleanName = name.replace(/@han$/, "") + pluginNames.add(cleanName) + } + } + } catch { + // Skip unreadable settings files + } + } + + return Array.from(pluginNames) +} + +// ─── Marketplace Resolution ────────────────────────────────────────────────── + +/** + * Find the marketplace.json file. Searches up from projectDir to find + * the han repository root, or falls back to the npm-installed marketplace. + */ +function findMarketplace(projectDir: string): Marketplace | null { + const candidates = [ + join(projectDir, ".claude-plugin", "marketplace.json"), + join(homedir(), ".claude", "marketplace.json"), + ] + + // Walk up directories looking for .claude-plugin/marketplace.json + let dir = projectDir + for (let i = 0; i < 10; i++) { + const candidate = join(dir, ".claude-plugin", "marketplace.json") + if (existsSync(candidate) && !candidates.includes(candidate)) { + candidates.unshift(candidate) + } + const parent = dirname(dir) + if (parent === dir) break + dir = parent + } + + for (const path of candidates) { + if (!existsSync(path)) continue + try { + const content = readFileSync(path, "utf-8") + return JSON.parse(content) as Marketplace + } catch { + continue + } + } + + return null +} + +/** + * Resolve a plugin name to its filesystem path using the marketplace. + */ +function resolvePluginPath( + pluginName: string, + marketplace: Marketplace, + marketplaceDir: string, +): string | null { + const entry = marketplace.plugins.find((p) => p.name === pluginName) + if (!entry) return null + + const pluginPath = resolve(marketplaceDir, entry.source) + return existsSync(pluginPath) ? pluginPath : null +} + +// ─── han-plugin.yml Parsing ────────────────────────────────────────────────── + +interface RawHookDef { + event?: string | string[] + command?: string + tool_filter?: string[] + file_filter?: string[] + dirs_with?: string[] + dir_test?: string + timeout?: number +} + +interface RawPluginConfig { + hooks?: Record +} + +/** + * Minimal YAML parser for han-plugin.yml. + * Handles the subset of YAML used by Han plugin configs. + */ +function parseSimpleYaml(content: string): RawPluginConfig { + const result: RawPluginConfig = { hooks: {} } + const lines = content.split("\n") + + let currentSection: string | null = null + let currentHook: string | null = null + let currentField: string | null = null + + for (const line of lines) { + const trimmed = line.trimEnd() + + // Top-level section + if (/^hooks:\s*$/.test(trimmed)) { + currentSection = "hooks" + continue + } + + if (currentSection !== "hooks") continue + + // Hook name (2-space indent) + const hookMatch = trimmed.match(/^ (\S+):\s*$/) + if (hookMatch) { + currentHook = hookMatch[1] + result.hooks![currentHook] = {} + currentField = null + continue + } + + if (!currentHook) continue + const hook = result.hooks![currentHook] + + // Simple key-value (4-space indent) + const kvMatch = trimmed.match(/^ (\S+):\s*(.+)$/) + if (kvMatch) { + const [, key, value] = kvMatch + const cleanValue = value.replace(/^["']|["']$/g, "") + currentField = null + + if (key === "event") { + if (cleanValue.startsWith("[")) { + hook.event = cleanValue + .replace(/^\[|\]$/g, "") + .split(",") + .map((s) => s.trim()) + } else { + hook.event = cleanValue + } + } else if (key === "command") { + hook.command = cleanValue + } else if (key === "dir_test") { + hook.dir_test = cleanValue + } else if (key === "timeout") { + hook.timeout = parseInt(cleanValue, 10) + } else if ( + key === "tool_filter" || + key === "file_filter" || + key === "dirs_with" + ) { + if (cleanValue.startsWith("[")) { + ;(hook as any)[key] = cleanValue + .replace(/^\[|\]$/g, "") + .split(",") + .map((s) => s.trim().replace(/^["']|["']$/g, "")) + } else { + currentField = key + } + } + continue + } + + // Array item (6-space indent with -) + const arrayMatch = trimmed.match(/^ - (.+)$/) + if (arrayMatch && currentField) { + const value = arrayMatch[1].replace(/^["']|["']$/g, "") + if (!(hook as any)[currentField]) { + ;(hook as any)[currentField] = [] + } + ;(hook as any)[currentField].push(value) + continue + } + + // Key with no value starts an array + const arrayKeyMatch = trimmed.match(/^ (\S+):\s*$/) + if (arrayKeyMatch) { + currentField = arrayKeyMatch[1] + continue + } + } + + return result +} + +/** + * Read and parse a plugin's han-plugin.yml, extracting hook definitions. + */ +function parsePluginHooks( + pluginName: string, + pluginRoot: string, +): HookDefinition[] { + const ymlPath = join(pluginRoot, "han-plugin.yml") + if (!existsSync(ymlPath)) return [] + + try { + const content = readFileSync(ymlPath, "utf-8") + const config = parseSimpleYaml(content) + if (!config.hooks) return [] + + const hooks: HookDefinition[] = [] + + for (const [name, def] of Object.entries(config.hooks)) { + if (!def.command) continue + + hooks.push({ + name, + pluginName, + pluginRoot, + event: def.event ?? "Stop", + command: def.command, + toolFilter: def.tool_filter, + fileFilter: def.file_filter, + dirsWith: def.dirs_with, + dirTest: def.dir_test, + timeout: def.timeout, + }) + } + + return hooks + } catch { + return [] + } +} + +// ─── Public API ────────────────────────────────────────────────────────────── + +/** + * Resolve enabled plugins to their filesystem paths. + * Returns a map of plugin name -> absolute plugin root path. + */ +export function resolvePluginPaths(projectDir: string): Map { + const resolved = new Map() + const enabledPlugins = getEnabledPlugins(projectDir) + if (enabledPlugins.length === 0) return resolved + + const marketplace = findMarketplace(projectDir) + if (!marketplace) return resolved + + const marketplaceDir = findMarketplaceDir(projectDir) + if (!marketplaceDir) return resolved + + for (const pluginName of enabledPlugins) { + const pluginPath = resolvePluginPath(pluginName, marketplace, marketplaceDir) + if (pluginPath) { + resolved.set(pluginName, pluginPath) + } + } + + return resolved +} + +/** + * Find the directory containing marketplace.json. + */ +function findMarketplaceDir(projectDir: string): string | null { + const candidates = [ + join(projectDir, ".claude-plugin"), + join(homedir(), ".claude"), + ] + + let dir = projectDir + for (let i = 0; i < 10; i++) { + const candidate = join(dir, ".claude-plugin") + if ( + existsSync(join(candidate, "marketplace.json")) && + !candidates.includes(candidate) + ) { + candidates.unshift(candidate) + } + const parent = dirname(dir) + if (parent === dir) break + dir = parent + } + + return candidates.find((d) => existsSync(join(d, "marketplace.json"))) ?? null +} + +/** + * Discover all hook definitions from installed Han plugins. + */ +export function discoverHooks(projectDir: string): HookDefinition[] { + const resolved = resolvePluginPaths(projectDir) + if (resolved.size === 0) return [] + + const allHooks: HookDefinition[] = [] + + for (const [pluginName, pluginPath] of resolved) { + const hooks = parsePluginHooks(pluginName, pluginPath) + allHooks.push(...hooks) + } + + return allHooks +} + +/** + * Filter hooks to only those matching a specific event type. + */ +export function getHooksByEvent( + hooks: HookDefinition[], + event: string, +): HookDefinition[] { + return hooks.filter((h) => { + if (Array.isArray(h.event)) return h.event.includes(event) + return h.event === event + }) +} diff --git a/plugins/bridges/antigravity/src/events.ts b/plugins/bridges/antigravity/src/events.ts new file mode 100644 index 000000000..c43569e36 --- /dev/null +++ b/plugins/bridges/antigravity/src/events.ts @@ -0,0 +1,194 @@ +/** + * Event logger for the Antigravity bridge. + * + * Writes Han-format JSONL events with provider="antigravity" so the + * coordinator can index them alongside Claude Code and OpenCode sessions. + * + * Path: ~/.han/antigravity/{project-slug}/{sessionId}-han.jsonl + */ + +import { randomUUID } from "node:crypto" +import { appendFileSync, mkdirSync } from "node:fs" +import { dirname, join } from "node:path" +import type { HookDefinition, HookResult, HanProvider } from "./types" + +const MAX_OUTPUT_LENGTH = 10_000 + +/** + * Convert a filesystem path to a slug. + */ +function pathToSlug(fsPath: string): string { + return fsPath.replace(/^\//, "-").replace(/[/.]/g, "-") +} + +/** + * Get the han data root for Antigravity. + */ +function getHanAntigravityRoot(): string { + const home = process.env.HOME ?? process.env.USERPROFILE ?? "/tmp" + return join(home, ".han", "antigravity") +} + +/** + * Get the JSONL events file path for an Antigravity session. + */ +function getEventsFilePath(projectDir: string, sessionId: string): string { + const slug = pathToSlug(projectDir) + return join(getHanAntigravityRoot(), "projects", slug, `${sessionId}-han.jsonl`) +} + +interface BaseEventMeta { + uuid: string + sessionId: string + type: string + timestamp: string + provider: HanProvider + cwd?: string +} + +function truncateOutput(output: string): string { + if (output.length <= MAX_OUTPUT_LENGTH) return output + return `${output.slice(0, MAX_OUTPUT_LENGTH)}\n... [truncated, ${output.length - MAX_OUTPUT_LENGTH} more bytes]` +} + +/** + * Event logger for Antigravity bridge sessions. + */ +export class BridgeEventLogger { + private logPath: string + private buffer: string[] = [] + private flushTimer: ReturnType | null = null + private readonly sessionId: string + private readonly provider: HanProvider = "antigravity" + private readonly cwd: string + + constructor(sessionId: string, projectDir: string) { + this.sessionId = sessionId + this.cwd = projectDir + this.logPath = getEventsFilePath(projectDir, sessionId) + + try { + mkdirSync(dirname(this.logPath), { recursive: true }) + } catch (err) { + if ((err as NodeJS.ErrnoException).code !== "EEXIST") { + throw err + } + } + + console.error( + `[han] Event logger initialized: ${this.logPath}`, + ) + } + + private createBase(type: string): BaseEventMeta { + return { + uuid: randomUUID(), + sessionId: this.sessionId, + type, + timestamp: new Date().toISOString(), + provider: this.provider, + cwd: this.cwd, + } + } + + private writeEvent(event: Record): void { + const line = `${JSON.stringify(event)}\n` + this.buffer.push(line) + + const type = event.type as string + if (type.endsWith("_result")) { + this.flush() + } else { + this.scheduleFlush() + } + } + + private scheduleFlush(): void { + if (this.flushTimer) return + this.flushTimer = setTimeout(() => { + this.flush() + }, 100) + } + + flush(): void { + if (this.flushTimer) { + clearTimeout(this.flushTimer) + this.flushTimer = null + } + if (this.buffer.length === 0) return + + try { + appendFileSync(this.logPath, this.buffer.join("")) + this.buffer = [] + } catch (err) { + console.error( + `[han] Failed to write events:`, + err instanceof Error ? err.message : err, + ) + } + } + + /** + * Log hook_run event. Returns UUID for correlating with hook_result. + */ + logHookRun(hook: HookDefinition, hookType: string): string { + const base = this.createBase("hook_run") + this.writeEvent({ + ...base, + data: { + plugin: hook.pluginName, + hook: hook.name, + hook_type: hookType, + directory: this.cwd, + cached: false, + command: hook.command, + }, + }) + return base.uuid + } + + /** + * Log hook_result event. + */ + logHookResult(result: HookResult, hookType: string, hookRunId: string): void { + this.writeEvent({ + ...this.createBase("hook_result"), + hookRunId, + data: { + plugin: result.hook.pluginName, + hook: result.hook.name, + hook_type: hookType, + directory: this.cwd, + cached: result.skipped, + duration_ms: result.durationMs, + exit_code: result.exitCode, + success: result.exitCode === 0, + output: result.stdout ? truncateOutput(result.stdout) : undefined, + error: result.stderr || undefined, + command: result.hook.command, + }, + }) + } + + /** + * Log hook_file_change event. + */ + logFileChange(toolName: string, filePath: string): void { + this.writeEvent({ + ...this.createBase("hook_file_change"), + data: { + session_id: this.sessionId, + tool_name: toolName, + file_path: filePath, + }, + }) + } + + getLogPath(): string { + return this.logPath + } + + getWatchDir(): string { + return join(getHanAntigravityRoot(), "projects") + } +} diff --git a/plugins/bridges/antigravity/src/executor.ts b/plugins/bridges/antigravity/src/executor.ts new file mode 100644 index 000000000..9b0d1ab35 --- /dev/null +++ b/plugins/bridges/antigravity/src/executor.ts @@ -0,0 +1,184 @@ +/** + * Promise-based hook executor. + * + * Runs hook commands as child processes, capturing stdout/stderr/exit code. + * Results are collected and returned for formatting into MCP tool responses. + */ + +import { spawn } from "node:child_process" +import type { HookDefinition, HookResult } from "./types" +import { shouldSkipHook, recordSuccess } from "./cache" +import type { BridgeEventLogger } from "./events" + +const DEFAULT_TIMEOUT = 60_000 + +/** + * Execute a single hook command as a promise. + */ +export function executeHook( + hook: HookDefinition, + filePaths: string[], + options: { + cwd: string + sessionId: string + timeout?: number + eventLogger?: BridgeEventLogger + hookType?: string + }, +): Promise { + const startTime = Date.now() + + // Check cache + if (filePaths.length > 0) { + const allCached = filePaths.every((fp) => + shouldSkipHook(hook.pluginName, hook.name, fp), + ) + if (allCached) { + return Promise.resolve({ + hook, + exitCode: 0, + stdout: "", + stderr: "", + durationMs: 0, + skipped: true, + }) + } + } + + // Substitute variables + const filesArg = filePaths.join(" ") + let command = hook.command.replace(/\$\{HAN_FILES\}/g, filesArg) + command = command.replace(/\$\{CLAUDE_PLUGIN_ROOT\}/g, hook.pluginRoot) + + if (hook.command.includes("${HAN_FILES}") && filePaths.length === 0) { + return Promise.resolve({ + hook, + exitCode: 0, + stdout: "", + stderr: "", + durationMs: 0, + skipped: true, + }) + } + + // Log hook_run event + const hookRunId = options.eventLogger?.logHookRun( + hook, + options.hookType ?? "Stop", + ) + + return new Promise((resolve) => { + const timeout = hook.timeout ?? options.timeout ?? DEFAULT_TIMEOUT + + const child = spawn(command, { + cwd: options.cwd, + shell: "/bin/bash", + stdio: ["ignore", "pipe", "pipe"], + env: { + ...process.env, + CLAUDE_PLUGIN_ROOT: hook.pluginRoot, + CLAUDE_PROJECT_DIR: options.cwd, + HAN_SESSION_ID: options.sessionId, + HAN_PROVIDER: "antigravity", + HAN_FILES: filesArg, + HAN_FILE: filePaths[0] ?? "", + }, + timeout, + }) + + let stdout = "" + let stderr = "" + + child.stdout.on("data", (data: Buffer) => { + stdout += data.toString() + }) + + child.stderr.on("data", (data: Buffer) => { + stderr += data.toString() + }) + + child.on("close", (code) => { + const exitCode = code ?? 1 + + if (exitCode === 0) { + for (const fp of filePaths) { + recordSuccess(hook.pluginName, hook.name, fp) + } + } + + const result: HookResult = { + hook, + exitCode, + stdout, + stderr, + durationMs: Date.now() - startTime, + skipped: false, + } + + if (options.eventLogger && hookRunId) { + options.eventLogger.logHookResult( + result, + options.hookType ?? "Stop", + hookRunId, + ) + } + + resolve(result) + }) + + child.on("error", (err) => { + const result: HookResult = { + hook, + exitCode: 127, + stdout: "", + stderr: `Failed to execute: ${err.message}`, + durationMs: Date.now() - startTime, + skipped: false, + } + + if (options.eventLogger && hookRunId) { + options.eventLogger.logHookResult( + result, + options.hookType ?? "Stop", + hookRunId, + ) + } + + resolve(result) + }) + }) +} + +/** + * Execute multiple hooks in parallel and collect all results. + */ +export async function executeHooksParallel( + hooks: HookDefinition[], + filePaths: string[], + options: { + cwd: string + sessionId: string + timeout?: number + eventLogger?: BridgeEventLogger + hookType?: string + }, +): Promise { + if (hooks.length === 0) return [] + + const results = await Promise.allSettled( + hooks.map((hook) => executeHook(hook, filePaths, options)), + ) + + return results.map((r) => + r.status === "fulfilled" + ? r.value + : { + hook: hooks[0], + exitCode: 1, + stdout: "", + stderr: r.reason?.message ?? "Unknown error", + durationMs: 0, + skipped: false, + }, + ) +} diff --git a/plugins/bridges/antigravity/src/formatter.ts b/plugins/bridges/antigravity/src/formatter.ts new file mode 100644 index 000000000..e27dee0fc --- /dev/null +++ b/plugins/bridges/antigravity/src/formatter.ts @@ -0,0 +1,104 @@ +/** + * Result formatting: turns raw hook results into structured messages + * for MCP tool responses. + * + * Uses XML-like tags for clear structure, matching the OpenCode bridge's + * format so results are consistent across providers. + */ + +import type { HookResult } from "./types" + +/** + * Format a single hook result as a structured validation block. + */ +function formatSingleResult(result: HookResult): string { + const { hook, exitCode, stdout, stderr } = result + const status = exitCode === 0 ? "passed" : "failed" + const output = (stdout || stderr).trim() + + if (!output) return "" + + return [ + ``, + output, + ``, + ].join("\n") +} + +/** + * Format validation results for MCP tool response. + * + * Returns all results (passed and failed) with clear status indicators + * since MCP tools return complete responses. + */ +export function formatValidationResults( + results: HookResult[], + filePaths?: string[], +): string { + const failures = results.filter((r) => !r.skipped && r.exitCode !== 0) + const successes = results.filter((r) => !r.skipped && r.exitCode === 0) + const skipped = results.filter((r) => r.skipped) + + if (failures.length === 0 && successes.length === 0 && skipped.length === 0) { + return "No validation hooks matched. Ensure Han plugins are installed: han plugin install --auto" + } + + const lines: string[] = [] + + if (filePaths && filePaths.length > 0) { + lines.push(`Files: ${filePaths.join(", ")}\n`) + } + + if (failures.length > 0) { + lines.push( + `**${failures.length} validation issue${failures.length > 1 ? "s" : ""} found:**\n`, + ) + for (const result of failures) { + const block = formatSingleResult(result) + if (block) lines.push(block) + } + } + + if (successes.length > 0) { + lines.push( + `\n**${successes.length} check${successes.length > 1 ? "s" : ""} passed:**`, + ) + for (const result of successes) { + lines.push(`- ${result.hook.pluginName}/${result.hook.name} (${result.durationMs}ms)`) + } + } + + if (skipped.length > 0) { + lines.push(`\n*${skipped.length} hook${skipped.length > 1 ? "s" : ""} skipped (cached/no matching files)*`) + } + + if (failures.length === 0) { + lines.push("\nAll validation checks passed.") + } else { + lines.push("\nPlease fix the issues above before continuing.") + } + + return lines.join("\n") +} + +/** + * Format Stop hook results (project-wide validation). + */ +export function formatStopResults(results: HookResult[]): string | null { + const failures = results.filter( + (r) => !r.skipped && r.exitCode !== 0, + ) + + if (failures.length === 0) return null + + const blocks = failures.map(formatSingleResult).filter(Boolean) + if (blocks.length === 0) return null + + return [ + "", + "Han validation hooks found issues that need to be fixed:", + "", + ...blocks, + "", + ].join("\n") +} diff --git a/plugins/bridges/antigravity/src/index.ts b/plugins/bridges/antigravity/src/index.ts new file mode 100644 index 000000000..a41cde994 --- /dev/null +++ b/plugins/bridges/antigravity/src/index.ts @@ -0,0 +1,611 @@ +/** + * Han Bridge for Antigravity (Google) + * + * MCP server that brings Han's plugin ecosystem to Antigravity IDE. + * + * Architecture: + * + * Antigravity doesn't have lifecycle hooks like Claude Code or OpenCode. + * Instead, the bridge integrates via MCP server, exposing Han's + * capabilities as tools the agent can call: + * + * han_skills (MCP tool) + * -> skills.ts discovers SKILL.md from installed plugins + * -> Agent can list and load 400+ skills on demand + * + * han_discipline (MCP tool) + * -> disciplines.ts discovers agent personas + * -> Agent activates/deactivates specialized disciplines + * + * han_validate (MCP tool) + * -> executor.ts runs matching validation hooks + * -> Returns structured results for the agent to act on + * -> Supports file-specific (PostToolUse) and project-wide (Stop) validation + * + * han_sync (MCP tool) + * -> sync.ts copies skills to .agent/skills/ for native discovery + * -> sync.ts generates .agent/rules/han-guidelines.md + * -> Antigravity discovers Han skills without MCP calls + * + * Key difference from OpenCode bridge: Antigravity lacks event hooks, + * so validation is on-demand (agent calls han_validate) rather than + * automatic (triggered by tool.execute.after). The agent is instructed + * via rules to call han_validate after edits. + */ + +import { discoverHooks, resolvePluginPaths, getHooksByEvent } from "./discovery" +import { matchPostToolUseHooks, matchStopHooks } from "./matcher" +import { executeHooksParallel } from "./executor" +import { invalidateFile } from "./cache" +import { formatValidationResults } from "./formatter" +import { mapToolName } from "./types" +import { BridgeEventLogger } from "./events" +import { + discoverAllSkills, + loadSkillContent, + formatSkillList, + type SkillInfo, +} from "./skills" +import { + discoverDisciplines, + formatDisciplineList, + buildDisciplineContext, + type DisciplineInfo, +} from "./disciplines" +import { buildPromptContext } from "./context" +import { syncAll } from "./sync" + +const PREFIX = "[han]" + +/** + * Start the Han coordinator daemon in the background. + */ +function startCoordinator(watchDir: string): void { + try { + const { spawn } = require("node:child_process") as typeof import("node:child_process") + + const child = spawn( + "han", + ["coordinator", "ensure", "--background", "--watch-path", watchDir], + { + stdio: "ignore", + detached: true, + env: { + ...process.env, + HAN_PROVIDER: "antigravity", + }, + }, + ) + + child.unref() + console.error(`${PREFIX} Coordinator ensure started (watch: ${watchDir})`) + } catch { + console.error( + `${PREFIX} Could not start coordinator (han CLI not found). ` + + `Browse UI won't show Antigravity sessions.`, + ) + } +} + +/** + * Create and start the Han MCP server for Antigravity. + * + * This is the main entry point. It discovers plugins, sets up tools, + * and returns an MCP server configuration that Antigravity can use. + */ +export async function createHanMcpServer(projectDir: string) { + // ─── Plugin Discovery ─────────────────────────────────────────────────── + const resolvedPlugins = resolvePluginPaths(projectDir) + + // ─── Hook Discovery ────────────────────────────────────────────────────── + const allHooks = discoverHooks(projectDir) + const postToolUseHooks = getHooksByEvent(allHooks, "PostToolUse") + const stopHooks = getHooksByEvent(allHooks, "Stop") + + // ─── Skill Discovery ────────────────────────────────────────────────────── + const allSkills = discoverAllSkills(resolvedPlugins) + const skillsByName = new Map() + for (const skill of allSkills) { + skillsByName.set(skill.name, skill) + } + + // ─── Discipline Discovery ────────────────────────────────────────────────── + const allDisciplines = discoverDisciplines(resolvedPlugins, allSkills) + const disciplinesByName = new Map() + for (const d of allDisciplines) { + disciplinesByName.set(d.name, d) + } + + let activeDiscipline: DisciplineInfo | null = null + + // ─── Logging ────────────────────────────────────────────────────────────── + const pluginCount = resolvedPlugins.size + const skillCount = allSkills.length + const disciplineCount = allDisciplines.length + + if (pluginCount === 0) { + console.error( + `${PREFIX} No Han plugins found. ` + + `Install plugins: han plugin install --auto`, + ) + } else { + console.error( + `${PREFIX} Discovered ${pluginCount} plugins: ` + + `${postToolUseHooks.length} PostToolUse, ` + + `${stopHooks.length} Stop hooks, ` + + `${skillCount} skills, ` + + `${disciplineCount} disciplines`, + ) + } + + // ─── Session State ─────────────────────────────────────────────────────── + const sessionId = crypto.randomUUID() + process.env.HAN_PROVIDER = "antigravity" + process.env.HAN_SESSION_ID = sessionId + + // ─── Event Logger ────────────────────────────────────────────────────── + const eventLogger = new BridgeEventLogger(sessionId, projectDir) + + // ─── Coordinator ─────────────────────────────────────────────────────── + startCoordinator(eventLogger.getWatchDir()) + + // ─── MCP Tool Definitions ───────────────────────────────────────────── + return { + name: "han", + version: "0.1.0", + tools: { + /** + * han_skills - Browse and load Han's skill library. + */ + han_skills: { + description: + "Browse and load Han skills (400+ specialized coding skills). " + + 'Use action="list" to search available skills, ' + + 'action="load" with skill name to get full skill content.', + inputSchema: { + type: "object" as const, + properties: { + action: { + type: "string" as const, + enum: ["list", "load"], + description: 'Action: "list" to search, "load" to get content', + }, + skill: { + type: "string" as const, + description: "Skill name to load (required for action=load)", + }, + filter: { + type: "string" as const, + description: "Search filter (optional for action=list)", + }, + }, + required: ["action"], + }, + async execute(args: { action: string; skill?: string; filter?: string }) { + if (args.action === "load") { + if (!args.skill) { + return { content: [{ type: "text" as const, text: "Error: skill parameter required for action=load" }] } + } + + const skill = skillsByName.get(args.skill) + if (!skill) { + const matches = allSkills.filter((s) => + s.name.toLowerCase().includes(args.skill!.toLowerCase()), + ) + if (matches.length === 1) { + return { content: [{ type: "text" as const, text: loadSkillContent(matches[0]) }] } + } + if (matches.length > 1) { + return { + content: [{ + type: "text" as const, + text: + `Multiple skills match "${args.skill}":\n` + + matches.map((s) => `- ${s.name}`).join("\n") + + "\n\nBe more specific.", + }], + } + } + return { content: [{ type: "text" as const, text: `Skill "${args.skill}" not found. Use action="list" to see available skills.` }] } + } + + return { content: [{ type: "text" as const, text: loadSkillContent(skill) }] } + } + + return { content: [{ type: "text" as const, text: formatSkillList(allSkills, args.filter) }] } + }, + }, + + /** + * han_discipline - Activate specialized agent disciplines. + */ + han_discipline: { + description: + "Activate a Han discipline (specialized agent persona). " + + 'Use action="list" to see available disciplines, ' + + 'action="activate" to switch, action="deactivate" to clear.', + inputSchema: { + type: "object" as const, + properties: { + action: { + type: "string" as const, + enum: ["list", "activate", "deactivate"], + description: 'Action: "list", "activate", or "deactivate"', + }, + discipline: { + type: "string" as const, + description: "Discipline name (required for activate)", + }, + }, + required: ["action"], + }, + async execute(args: { action: string; discipline?: string }) { + if (args.action === "activate") { + if (!args.discipline) { + return { content: [{ type: "text" as const, text: "Error: discipline parameter required for activate" }] } + } + + const d = disciplinesByName.get(args.discipline) + if (!d) { + return { + content: [{ + type: "text" as const, + text: + `Discipline "${args.discipline}" not found.\n\n` + + formatDisciplineList(allDisciplines), + }], + } + } + + activeDiscipline = d + return { + content: [{ + type: "text" as const, + text: + `Activated discipline: **${d.name}**\n\n` + + `${d.description}\n\n` + + (d.skills.length > 0 + ? `${d.skills.length} specialized skills available. ` + + `Use han_skills to load any of them.\n\n` + + buildDisciplineContext(d) + : ""), + }], + } + } + + if (args.action === "deactivate") { + const prev = activeDiscipline?.name + activeDiscipline = null + return { + content: [{ + type: "text" as const, + text: prev + ? `Deactivated discipline: ${prev}` + : "No discipline was active.", + }], + } + } + + return { content: [{ type: "text" as const, text: formatDisciplineList(allDisciplines) }] } + }, + }, + + /** + * han_validate - Run validation hooks on demand. + * + * Since Antigravity lacks lifecycle hooks, the agent calls this + * tool explicitly after making edits or before finishing a task. + * + * Two modes: + * - file: Run PostToolUse hooks for specific files (after edits) + * - project: Run Stop hooks for full project validation (before finishing) + */ + han_validate: { + description: + "Run Han validation hooks (linting, type checking, formatting). " + + 'Use mode="file" with file paths after editing files, or ' + + 'mode="project" for full project validation before completing a task.', + inputSchema: { + type: "object" as const, + properties: { + mode: { + type: "string" as const, + enum: ["file", "project"], + description: '"file" for per-file validation, "project" for full project check', + }, + files: { + type: "array" as const, + items: { type: "string" as const }, + description: "File paths to validate (required for mode=file)", + }, + tool: { + type: "string" as const, + description: 'Tool that edited the files (e.g. "Edit", "Write"). Defaults to "Edit".', + }, + }, + required: ["mode"], + }, + async execute(args: { mode: string; files?: string[]; tool?: string }) { + if (args.mode === "file") { + if (!args.files || args.files.length === 0) { + return { content: [{ type: "text" as const, text: "Error: files parameter required for mode=file" }] } + } + + const claudeToolName = args.tool + ? mapToolName(args.tool) + : "Edit" + + // Invalidate cache for edited files + for (const fp of args.files) { + invalidateFile(fp) + } + + // Find and run matching PostToolUse hooks for the first file + const matching = matchPostToolUseHooks( + postToolUseHooks, + claudeToolName, + args.files[0], + projectDir, + ) + + if (matching.length === 0) { + return { + content: [{ + type: "text" as const, + text: `No validation hooks matched for ${args.files.join(", ")}. ` + + `(${postToolUseHooks.length} PostToolUse hooks registered)`, + }], + } + } + + const results = await executeHooksParallel(matching, args.files, { + cwd: projectDir, + sessionId, + eventLogger, + hookType: "PostToolUse", + }) + + return { + content: [{ + type: "text" as const, + text: formatValidationResults(results, args.files), + }], + } + } + + if (args.mode === "project") { + const matching = matchStopHooks(stopHooks, projectDir) + + if (matching.length === 0) { + return { + content: [{ + type: "text" as const, + text: `No project validation hooks matched. ` + + `(${stopHooks.length} Stop hooks registered)`, + }], + } + } + + const results = await executeHooksParallel(matching, [], { + cwd: projectDir, + sessionId, + timeout: 120_000, + eventLogger, + hookType: "Stop", + }) + + eventLogger.flush() + + return { + content: [{ + type: "text" as const, + text: formatValidationResults(results), + }], + } + } + + return { + content: [{ + type: "text" as const, + text: 'Error: mode must be "file" or "project"', + }], + isError: true, + } + }, + }, + + /** + * han_sync - Sync Han assets to Antigravity's native directories. + * + * Copies skills to .agent/skills/ and generates rules in .agent/rules/ + * so Antigravity discovers them natively without MCP tool calls. + */ + han_sync: { + description: + "Sync Han skills and rules to Antigravity's native directories " + + "(.agent/skills/ and .agent/rules/). This lets Antigravity discover " + + "Han's skills natively. Run once after installing new plugins.", + inputSchema: { + type: "object" as const, + properties: {}, + required: [], + }, + async execute() { + const result = syncAll(projectDir, allSkills, allDisciplines) + return { content: [{ type: "text" as const, text: result }] } + }, + }, + + /** + * han_context - Get current session context. + * + * Returns current time, active discipline, and capabilities summary. + * Useful since Antigravity doesn't have automatic context injection hooks. + */ + han_context: { + description: + "Get current Han session context including time, active discipline, " + + "and capabilities summary.", + inputSchema: { + type: "object" as const, + properties: {}, + required: [], + }, + async execute() { + const lines: string[] = [] + + lines.push(buildPromptContext()) + lines.push("") + + if (activeDiscipline) { + lines.push(`Active discipline: **${activeDiscipline.name}**`) + lines.push(buildDisciplineContext(activeDiscipline)) + } else { + lines.push("No discipline active. Use han_discipline to activate one.") + } + + lines.push("") + lines.push(`Available: ${skillCount} skills, ${disciplineCount} disciplines`) + lines.push(`Validation: ${postToolUseHooks.length} PostToolUse hooks, ${stopHooks.length} Stop hooks`) + + return { content: [{ type: "text" as const, text: lines.join("\n") }] } + }, + }, + }, + } +} + +/** + * CLI entry point for running the MCP server via stdio. + * + * Usage in mcp_config.json: + * { + * "mcpServers": { + * "han": { + * "command": "npx", + * "args": ["-y", "@thebushidocollective/antigravity-han-mcp"] + * } + * } + * } + */ +async function main() { + const projectDir = process.argv.includes("--project-dir") + ? process.argv[process.argv.indexOf("--project-dir") + 1] + : process.cwd() + + console.error(`${PREFIX} Starting Han MCP server for Antigravity`) + console.error(`${PREFIX} Project: ${projectDir}`) + + const server = await createHanMcpServer(projectDir) + + // MCP stdio transport: read JSON-RPC from stdin, write to stdout + const { createInterface } = await import("node:readline") + const rl = createInterface({ input: process.stdin }) + + const tools = server.tools + + rl.on("line", async (line) => { + try { + const request = JSON.parse(line) + + if (request.method === "initialize") { + const response = { + jsonrpc: "2.0", + id: request.id, + result: { + protocolVersion: "2024-11-05", + serverInfo: { name: server.name, version: server.version }, + capabilities: { + tools: {}, + }, + }, + } + process.stdout.write(JSON.stringify(response) + "\n") + return + } + + if (request.method === "tools/list") { + const toolList = Object.entries(tools).map(([name, tool]) => ({ + name, + description: tool.description, + inputSchema: tool.inputSchema, + })) + const response = { + jsonrpc: "2.0", + id: request.id, + result: { tools: toolList }, + } + process.stdout.write(JSON.stringify(response) + "\n") + return + } + + if (request.method === "tools/call") { + const { name, arguments: args } = request.params + const tool = tools[name as keyof typeof tools] + if (!tool) { + const response = { + jsonrpc: "2.0", + id: request.id, + error: { code: -32601, message: `Unknown tool: ${name}` }, + } + process.stdout.write(JSON.stringify(response) + "\n") + return + } + + try { + const result = await tool.execute(args ?? {}) + const response = { + jsonrpc: "2.0", + id: request.id, + result, + } + process.stdout.write(JSON.stringify(response) + "\n") + } catch (err) { + const response = { + jsonrpc: "2.0", + id: request.id, + result: { + content: [{ + type: "text", + text: `Error: ${err instanceof Error ? err.message : String(err)}`, + }], + isError: true, + }, + } + process.stdout.write(JSON.stringify(response) + "\n") + } + return + } + + if (request.method === "notifications/initialized") { + // No response needed for notifications + return + } + + // Unknown method + const response = { + jsonrpc: "2.0", + id: request.id, + error: { code: -32601, message: `Method not found: ${request.method}` }, + } + process.stdout.write(JSON.stringify(response) + "\n") + } catch (err) { + console.error(`${PREFIX} Parse error:`, err) + } + }) + + rl.on("close", () => { + console.error(`${PREFIX} MCP server shutting down`) + process.exit(0) + }) +} + +// Run if invoked directly +if (require.main === module || process.argv[1]?.endsWith("index.ts")) { + main().catch((err) => { + console.error(`${PREFIX} Fatal error:`, err) + process.exit(1) + }) +} + +export default createHanMcpServer diff --git a/plugins/bridges/antigravity/src/matcher.ts b/plugins/bridges/antigravity/src/matcher.ts new file mode 100644 index 000000000..56b7b205a --- /dev/null +++ b/plugins/bridges/antigravity/src/matcher.ts @@ -0,0 +1,99 @@ +/** + * Hook matching: determines which hooks should fire for a given context. + * + * Checks tool name against toolFilter and file path against fileFilter + * to find hooks that apply to a specific validation request. + */ + +import { existsSync } from "node:fs" +import { join, relative } from "node:path" +import type { HookDefinition } from "./types" + +/** + * Test if a file path matches a glob-like pattern. + */ +function matchGlob(pattern: string, filePath: string): boolean { + let regexStr = pattern + .replace(/\./g, "\\.") + .replace(/\{([^}]+)\}/g, (_match, group: string) => { + const alts = group.split(",").map((s: string) => s.trim()) + return `(${alts.join("|")})` + }) + .replace(/\*\*/g, "<<>>") + .replace(/\*/g, "[^/]*") + .replace(/<<>>/g, ".*") + + regexStr = `^${regexStr}$` + + try { + return new RegExp(regexStr).test(filePath) + } catch { + return false + } +} + +/** + * Check if a hook's dirsWith requirement is met. + */ +function checkDirsWith( + hook: HookDefinition, + projectDir: string, +): boolean { + if (!hook.dirsWith || hook.dirsWith.length === 0) return true + return hook.dirsWith.some((requiredFile) => + existsSync(join(projectDir, requiredFile)), + ) +} + +/** + * Check if a file matches the hook's fileFilter patterns. + */ +function checkFileFilter( + hook: HookDefinition, + filePath: string, + projectDir: string, +): boolean { + if (!hook.fileFilter || hook.fileFilter.length === 0) return true + const relPath = relative(projectDir, filePath) + return hook.fileFilter.some( + (pattern) => matchGlob(pattern, relPath) || matchGlob(pattern, filePath), + ) +} + +/** + * Check if a tool name matches the hook's toolFilter. + */ +function checkToolFilter( + hook: HookDefinition, + claudeToolName: string, +): boolean { + if (!hook.toolFilter || hook.toolFilter.length === 0) return true + return hook.toolFilter.includes(claudeToolName) +} + +/** + * Find PostToolUse hooks matching a given tool + file combination. + */ +export function matchPostToolUseHooks( + hooks: HookDefinition[], + claudeToolName: string, + filePath: string, + projectDir: string, +): HookDefinition[] { + return hooks.filter((hook) => { + if (!checkToolFilter(hook, claudeToolName)) return false + if (!checkDirsWith(hook, projectDir)) return false + if (!checkFileFilter(hook, filePath, projectDir)) return false + return true + }) +} + +/** + * Find Stop hooks that apply to the current project. + */ +export function matchStopHooks( + hooks: HookDefinition[], + projectDir: string, +): HookDefinition[] { + return hooks.filter((hook) => checkDirsWith(hook, projectDir)) +} diff --git a/plugins/bridges/antigravity/src/skills.ts b/plugins/bridges/antigravity/src/skills.ts new file mode 100644 index 000000000..b6c9a797d --- /dev/null +++ b/plugins/bridges/antigravity/src/skills.ts @@ -0,0 +1,204 @@ +/** + * Skill discovery and registration for Antigravity. + * + * Scans installed Han plugins for skills/ directories containing SKILL.md + * files. Exposes via MCP tools so the agent can list and load skills. + * + * Antigravity has its own skill system (.agent/skills/) using the same + * SKILL.md format. The han_sync tool can optionally copy skills there + * for native discovery. This module provides on-demand access without + * filesystem duplication. + */ + +import { readFileSync, existsSync, readdirSync } from "node:fs" +import { join } from "node:path" + +/** + * Parsed skill metadata from SKILL.md frontmatter. + */ +export interface SkillInfo { + /** Skill name (from frontmatter or directory name) */ + name: string + /** Human-readable description */ + description: string + /** Parent plugin name */ + pluginName: string + /** Allowed tools (empty = all) */ + allowedTools: string[] + /** Full path to SKILL.md */ + filePath: string +} + +/** + * Parse YAML frontmatter from a SKILL.md file. + */ +function parseFrontmatter(content: string): { + meta: Record + body: string +} { + const match = content.match(/^---\n([\s\S]*?)\n---\n?([\s\S]*)$/) + if (!match) return { meta: {}, body: content } + + const meta: Record = {} + const lines = match[1].split("\n") + let currentKey: string | null = null + + for (const line of lines) { + const kvMatch = line.match(/^(\S+):\s*(.+)$/) + if (kvMatch) { + const [, key, value] = kvMatch + meta[key] = value.replace(/^["']|["']$/g, "") + currentKey = null + continue + } + + const keyMatch = line.match(/^(\S+):\s*$/) + if (keyMatch) { + currentKey = keyMatch[1] + meta[currentKey] = [] + continue + } + + const inlineArrayMatch = line.match(/^(\S+):\s*\[([^\]]*)\]$/) + if (inlineArrayMatch) { + const [, key, items] = inlineArrayMatch + meta[key] = items + .split(",") + .map((s) => s.trim().replace(/^["']|["']$/g, "")) + .filter(Boolean) + currentKey = null + continue + } + + const arrayMatch = line.match(/^\s*-\s+(.+)$/) + if (arrayMatch && currentKey && Array.isArray(meta[currentKey])) { + ;(meta[currentKey] as string[]).push( + arrayMatch[1].replace(/^["']|["']$/g, ""), + ) + } + } + + return { meta, body: match[2] } +} + +/** + * Discover all skills from a plugin directory. + */ +function discoverPluginSkills( + pluginName: string, + pluginRoot: string, +): SkillInfo[] { + const skillsDir = join(pluginRoot, "skills") + if (!existsSync(skillsDir)) return [] + + const skills: SkillInfo[] = [] + + try { + const entries = readdirSync(skillsDir, { withFileTypes: true }) + + for (const entry of entries) { + if (!entry.isDirectory()) continue + + const skillMd = join(skillsDir, entry.name, "SKILL.md") + if (!existsSync(skillMd)) continue + + try { + const content = readFileSync(skillMd, "utf-8") + const { meta } = parseFrontmatter(content) + + skills.push({ + name: (meta.name as string) || entry.name, + description: (meta.description as string) || "", + pluginName, + allowedTools: Array.isArray(meta["allowed-tools"]) + ? (meta["allowed-tools"] as string[]) + : [], + filePath: skillMd, + }) + } catch { + // Skip unreadable skills + } + } + } catch { + // Skills directory not readable + } + + return skills +} + +/** + * Discover all skills from all resolved plugin paths. + */ +export function discoverAllSkills( + resolvedPlugins: Map, +): SkillInfo[] { + const allSkills: SkillInfo[] = [] + + for (const [pluginName, pluginRoot] of resolvedPlugins) { + const skills = discoverPluginSkills(pluginName, pluginRoot) + allSkills.push(...skills) + } + + return allSkills +} + +/** + * Load the full content of a skill's SKILL.md. + */ +export function loadSkillContent(skill: SkillInfo): string { + try { + return readFileSync(skill.filePath, "utf-8") + } catch { + return `Error: Could not read skill file at ${skill.filePath}` + } +} + +/** + * Format a skill list for display. + */ +export function formatSkillList( + skills: SkillInfo[], + filter?: string, +): string { + let filtered = skills + + if (filter) { + const lower = filter.toLowerCase() + filtered = skills.filter( + (s) => + s.name.toLowerCase().includes(lower) || + s.description.toLowerCase().includes(lower) || + s.pluginName.toLowerCase().includes(lower), + ) + } + + if (filtered.length === 0) { + return filter + ? `No skills matching "${filter}". Try a broader search term.` + : "No Han skills discovered. Install plugins: han plugin install --auto" + } + + // Group by plugin + const byPlugin = new Map() + for (const skill of filtered) { + const existing = byPlugin.get(skill.pluginName) || [] + existing.push(skill) + byPlugin.set(skill.pluginName, existing) + } + + const lines: string[] = [`Found ${filtered.length} skills:\n`] + + for (const [plugin, pluginSkills] of byPlugin) { + lines.push(`## ${plugin}`) + for (const skill of pluginSkills) { + lines.push(`- **${skill.name}**: ${skill.description || "(no description)"}`) + } + lines.push("") + } + + lines.push( + `\nUse han_skills with action="load" and skill="" to load a skill's full content.`, + ) + + return lines.join("\n") +} diff --git a/plugins/bridges/antigravity/src/sync.ts b/plugins/bridges/antigravity/src/sync.ts new file mode 100644 index 000000000..f41e072ac --- /dev/null +++ b/plugins/bridges/antigravity/src/sync.ts @@ -0,0 +1,197 @@ +/** + * Sync Han assets to Antigravity's native directories. + * + * Antigravity discovers skills and rules from: + * - .agent/skills//SKILL.md (per-project skills) + * - .agent/rules/.md (per-project rules) + * - ~/.gemini/antigravity/skills/ (global skills) + * + * This module copies Han's skills and generates rules files so + * Antigravity can discover them natively without MCP tool calls. + * This is optional - the MCP tools (han_skills, han_discipline) + * work without syncing. + */ + +import { + readFileSync, + writeFileSync, + mkdirSync, + existsSync, + readdirSync, + rmSync, +} from "node:fs" +import { join, basename } from "node:path" +import type { SkillInfo } from "./skills" +import type { DisciplineInfo } from "./disciplines" +import { buildRulesContent } from "./context" + +const HAN_SYNC_MARKER = "" + +/** + * Sync skills to .agent/skills/ directory. + * + * Copies SKILL.md files from installed Han plugins into Antigravity's + * workspace skills directory. Antigravity will discover them natively. + * + * @returns Number of skills synced + */ +export function syncSkills( + projectDir: string, + skills: SkillInfo[], +): { synced: number; removed: number } { + const agentSkillsDir = join(projectDir, ".agent", "skills") + mkdirSync(agentSkillsDir, { recursive: true }) + + // Track which skill directories we manage + const managedSkills = new Set() + let synced = 0 + + for (const skill of skills) { + const skillDir = join(agentSkillsDir, `han-${skill.pluginName}-${skill.name}`) + managedSkills.add(basename(skillDir)) + + mkdirSync(skillDir, { recursive: true }) + + // Read source SKILL.md + let content: string + try { + content = readFileSync(skill.filePath, "utf-8") + } catch { + continue + } + + // Add managed marker to content + const markedContent = `${HAN_SYNC_MARKER}\n${content}` + + const targetPath = join(skillDir, "SKILL.md") + + // Only write if content changed + if (existsSync(targetPath)) { + try { + const existing = readFileSync(targetPath, "utf-8") + if (existing === markedContent) continue + } catch { + // Re-write on read error + } + } + + writeFileSync(targetPath, markedContent) + synced++ + } + + // Remove skills we previously synced but are no longer active + let removed = 0 + try { + const entries = readdirSync(agentSkillsDir, { withFileTypes: true }) + for (const entry of entries) { + if (!entry.isDirectory()) continue + if (!entry.name.startsWith("han-")) continue + if (managedSkills.has(entry.name)) continue + + // Check if it has our marker before removing + const skillMd = join(agentSkillsDir, entry.name, "SKILL.md") + if (existsSync(skillMd)) { + try { + const content = readFileSync(skillMd, "utf-8") + if (content.startsWith(HAN_SYNC_MARKER)) { + rmSync(join(agentSkillsDir, entry.name), { recursive: true }) + removed++ + } + } catch { + // Skip if can't read + } + } + } + } catch { + // Directory might not exist yet + } + + return { synced, removed } +} + +/** + * Sync Han guidelines to .agent/rules/ directory. + * + * Generates a rules file with Han's core guidelines so Antigravity + * injects them into the agent's context natively. + * + * @returns Whether the rules file was updated + */ +export function syncRules( + projectDir: string, + skillCount: number, + disciplineCount: number, +): boolean { + const rulesDir = join(projectDir, ".agent", "rules") + mkdirSync(rulesDir, { recursive: true }) + + const rulesPath = join(rulesDir, "han-guidelines.md") + const content = `${HAN_SYNC_MARKER}\n${buildRulesContent(skillCount, disciplineCount)}` + + // Only write if changed + if (existsSync(rulesPath)) { + try { + const existing = readFileSync(rulesPath, "utf-8") + if (existing === content) return false + } catch { + // Re-write on read error + } + } + + writeFileSync(rulesPath, content) + return true +} + +/** + * Generate MCP config entry for Han in Antigravity's mcp_config.json. + * + * Returns the JSON config that should be added to + * ~/.gemini/antigravity/mcp_config.json. + */ +export function generateMcpConfig(projectDir: string): string { + const config = { + mcpServers: { + han: { + command: "npx", + args: [ + "-y", + "@thebushidocollective/antigravity-han-mcp", + "--project-dir", + projectDir, + ], + }, + }, + } + + return JSON.stringify(config, null, 2) +} + +/** + * Full sync: skills + rules + report. + */ +export function syncAll( + projectDir: string, + skills: SkillInfo[], + disciplines: DisciplineInfo[], +): string { + const skillResult = syncSkills(projectDir, skills) + const rulesUpdated = syncRules(projectDir, skills.length, disciplines.length) + + const lines: string[] = ["Han sync complete:\n"] + + lines.push(`- Skills: ${skillResult.synced} synced to .agent/skills/`) + if (skillResult.removed > 0) { + lines.push(` (${skillResult.removed} stale skills removed)`) + } + + lines.push(`- Rules: ${rulesUpdated ? "updated" : "up to date"} at .agent/rules/han-guidelines.md`) + lines.push(`\nAntigravity will now discover ${skills.length} skills natively.`) + + if (disciplines.length > 0) { + lines.push( + `\n${disciplines.length} disciplines available via han_discipline tool.`, + ) + } + + return lines.join("\n") +} diff --git a/plugins/bridges/antigravity/src/types.ts b/plugins/bridges/antigravity/src/types.ts new file mode 100644 index 000000000..b888091bb --- /dev/null +++ b/plugins/bridges/antigravity/src/types.ts @@ -0,0 +1,154 @@ +/** + * Shared type definitions for the Han-Antigravity bridge. + * + * Antigravity (Google's agent-first IDE) doesn't have a lifecycle hooks + * system like OpenCode. Instead, the bridge integrates via MCP server, + * exposing skills, disciplines, and on-demand validation as tools. + * + * Configuration: + * - Global: ~/.gemini/antigravity/mcp_config.json + * - Rules: .agent/rules/ + * - Skills: .agent/skills/ + * - Workflows: .agent/workflows/ + */ + +// ─── Antigravity Configuration Types ───────────────────────────────────────── + +/** + * Antigravity MCP server configuration in mcp_config.json. + */ +export interface AntigravityMcpConfig { + mcpServers: Record< + string, + { + command: string + args?: string[] + env?: Record + } + > +} + +/** + * Antigravity settings from .gemini/settings.json. + */ +export interface AntigravitySettings { + "tools.autoAccept"?: string[] + [key: string]: unknown +} + +// ─── MCP Tool Types ────────────────────────────────────────────────────────── + +/** + * MCP tool definition shape for the bridge's tools. + */ +export interface McpToolDefinition { + name: string + description: string + inputSchema: { + type: "object" + properties: Record + required?: string[] + } +} + +export interface McpToolProperty { + type: string + description: string + enum?: string[] +} + +/** + * MCP tool call result. + */ +export interface McpToolResult { + content: Array<{ + type: "text" + text: string + }> + isError?: boolean +} + +// ─── Han Hook Types ────────────────────────────────────────────────────────── + +/** + * A hook definition parsed from a plugin's han-plugin.yml. + */ +export interface HookDefinition { + /** Hook name (key in the hooks map, e.g. "lint-async") */ + name: string + /** Plugin short name (e.g. "biome") */ + pluginName: string + /** Absolute path to the plugin directory */ + pluginRoot: string + /** The hook event type(s) */ + event: string | string[] + /** Shell command to execute (may contain ${HAN_FILES}) */ + command: string + /** Which tools trigger this hook (e.g. ["Edit", "Write", "NotebookEdit"]) */ + toolFilter?: string[] + /** File glob patterns that trigger this hook */ + fileFilter?: string[] + /** Directories required to exist (e.g. ["biome.json"]) */ + dirsWith?: string[] + /** Bash expression to test if hook applies to this directory */ + dirTest?: string + /** Timeout in milliseconds */ + timeout?: number +} + +/** + * Result of executing a single hook command. + */ +export interface HookResult { + /** The hook that was executed */ + hook: HookDefinition + /** Process exit code (0 = success) */ + exitCode: number + /** Combined stdout output */ + stdout: string + /** Combined stderr output */ + stderr: string + /** Execution duration in milliseconds */ + durationMs: number + /** Whether the hook was skipped (e.g. no matching files) */ + skipped: boolean +} + +// ─── Provider ──────────────────────────────────────────────────────────────── + +/** + * Han provider identifies which AI coding tool is running the session. + */ +export type HanProvider = "antigravity" | "opencode" | "claude-code" + +export function getProvider(): HanProvider { + const env = process.env.HAN_PROVIDER + if (env === "antigravity") return "antigravity" + if (env === "opencode") return "opencode" + return "claude-code" +} + +// ─── Antigravity -> Claude Code Tool Name Mapping ──────────────────────────── + +/** + * Map Antigravity tool names to Claude Code tool names. + * Antigravity uses similar naming to VS Code extensions and Gemini tools. + */ +export const TOOL_NAME_MAP: Record = { + edit_file: "Edit", + write_file: "Write", + read_file: "Read", + run_terminal_command: "Bash", + search_files: "Grep", + list_files: "Glob", + edit: "Edit", + write: "Write", + bash: "Bash", + read: "Read", + glob: "Glob", + grep: "Grep", +} + +export function mapToolName(antigravityTool: string): string { + return TOOL_NAME_MAP[antigravityTool.toLowerCase()] ?? antigravityTool +} diff --git a/website/content/docs/_nav.json b/website/content/docs/_nav.json index 5c41cf594..0841516da 100644 --- a/website/content/docs/_nav.json +++ b/website/content/docs/_nav.json @@ -14,7 +14,9 @@ { "title": "Installation Methods", "slug": "installation" }, { "title": "Installing Plugins", "slug": "installation/plugins" }, { "title": "Installation Scopes", "slug": "installation/scopes" }, - { "title": "Using Han with OpenCode", "slug": "installation/opencode" } + { "title": "Using Han with OpenCode", "slug": "installation/opencode" }, + { "title": "Using Han with Antigravity", "slug": "installation/antigravity" }, + { "title": "Using Han with Gemini CLI", "slug": "installation/gemini-cli" } ] }, { diff --git a/website/content/docs/installation/antigravity.md b/website/content/docs/installation/antigravity.md new file mode 100644 index 000000000..db23f8428 --- /dev/null +++ b/website/content/docs/installation/antigravity.md @@ -0,0 +1,179 @@ +--- +title: "Using Han with Google Antigravity" +description: "How to use Han's validation pipeline, skills, and disciplines with Google Antigravity IDE via MCP server." +--- + +Han works with [Google Antigravity](https://antigravity.google/) through an MCP server bridge that exposes Han's skills, disciplines, and validation hooks as tools the agent can call. + +## How It Works + +Antigravity doesn't have lifecycle hooks like Claude Code or OpenCode. Instead, Han integrates via MCP server, exposing capabilities as tools: + +1. **MCP tools** → **Skills, disciplines, validation** (agent calls on demand) +2. **Native sync** → **`.agent/skills/` and `.agent/rules/`** (Antigravity discovers natively) +3. **Event logging** → **Browse UI** (sessions visible alongside Claude Code sessions) + +```text +Agent finishes editing src/app.ts + -> Agent calls han_validate({ mode: "file", files: ["src/app.ts"] }) + -> Bridge matches PostToolUse hooks (biome, eslint, tsc) + -> Runs hooks in parallel, returns structured results + -> Agent fixes validation errors +``` + +## Setup + +### 1. Install Han and plugins + +```bash +curl -fsSL https://han.guru/install.sh | bash +han plugin install --auto +``` + +### 2. Add the MCP server to Antigravity + +Add to `~/.gemini/antigravity/mcp_config.json`: + +```json +{ + "mcpServers": { + "han": { + "command": "npx", + "args": ["-y", "antigravity-han-mcp"] + } + } +} +``` + +### 3. Optional: Sync skills natively + +Ask the agent to run `han_sync` to copy skills to `.agent/skills/` and generate `.agent/rules/han-guidelines.md`. This lets Antigravity discover Han's skills via its native semantic matching without MCP tool calls. + +## Available Tools + +The bridge exposes five MCP tools: + +| Tool | Purpose | Usage | +|------|---------|-------| +| `han_skills` | Browse/load 400+ coding skills | `han_skills({ action: "list", filter: "react" })` | +| `han_discipline` | Activate agent personas (25 disciplines) | `han_discipline({ action: "activate", discipline: "frontend" })` | +| `han_validate` | Run validation hooks on demand | `han_validate({ mode: "file", files: ["src/app.ts"] })` | +| `han_sync` | Sync skills/rules to `.agent/` directory | `han_sync({})` | +| `han_context` | Get time, active discipline, stats | `han_context({})` | + +## Validation + +Since Antigravity doesn't have automatic tool lifecycle hooks, validation is on-demand. The agent calls `han_validate` after edits or before finishing. + +### Per-File Validation (After Edits) + +```text +han_validate({ mode: "file", files: ["src/app.ts"], tool: "Edit" }) +``` + +Runs matching PostToolUse hooks: + +| Plugin | What It Does | +|--------|-------------| +| `biome` | Lint and format JavaScript/TypeScript | +| `eslint` | JavaScript/TypeScript linting | +| `prettier` | Code formatting | +| `typescript` | Type checking | +| `clippy` | Rust linting | +| `pylint` | Python linting | + +### Project-Wide Validation (Before Finishing) + +```text +han_validate({ mode: "project" }) +``` + +Runs all Stop hooks for comprehensive project validation: + +- Full project linting +- Type checking across the codebase +- Test suite execution + +## Skills (400+) + +The `han_skills` tool gives the agent access to Han's full skill library without duplicating files: + +```text +han_skills({ action: "list", filter: "typescript" }) +-> Lists all TypeScript-related skills + +han_skills({ action: "load", skill: "typescript-type-system" }) +-> Loads full skill content into context +``` + +Alternatively, run `han_sync` to copy skills to `.agent/skills/` for native Antigravity discovery via semantic matching. + +## Disciplines (25 Agent Personas) + +Activate specialized agent expertise via `han_discipline`: + +```text +han_discipline({ action: "list" }) +-> Available: frontend, backend, sre, security, mobile, database... + +han_discipline({ action: "activate", discipline: "frontend" }) +-> Agent now operates with frontend specialist context +``` + +Available disciplines: frontend, backend, api, architecture, mobile, database, security, infrastructure, sre, performance, accessibility, quality, documentation, project-management, product, data-engineering, machine-learning, and more. + +## Native Integration via Sync + +Running `han_sync` copies Han's assets to Antigravity's native directories: + +- **Skills** → `.agent/skills/han-*/SKILL.md` (native semantic discovery) +- **Rules** → `.agent/rules/han-guidelines.md` (auto-injected guidelines) + +This means Antigravity discovers Han's skills via its built-in semantic matching system, without needing to call MCP tools. The rules file provides core guidelines (professional honesty, no time estimates, skill selection) that are injected into every agent context. + +## Comparison with Claude Code and OpenCode + +| Feature | Claude Code | OpenCode | Antigravity | +|---------|-------------|----------|-------------| +| Integration type | Native hooks | JS plugin API | MCP server | +| Validation trigger | Automatic (PostToolUse) | Automatic (tool.execute.after) | On-demand (han_validate) | +| Context injection | SessionStart hook | system.transform | .agent/rules/ + han_context | +| Skill access | han_skills tool | han_skills tool | han_skills + .agent/skills/ sync | +| Discipline support | do-* plugins | han_discipline tool | han_discipline tool | + +## Remaining Gaps + +These are platform limitations in Antigravity (as of Feb 2026): + +- **Automatic validation**: No lifecycle hooks, so validation requires explicit `han_validate` calls. You can add a rule in `.agent/rules/` instructing the agent to validate after edits. +- **Permission denial**: Cannot block tool execution (no PreToolUse equivalent). +- **Subagent hooks**: No SubagentStart/SubagentStop equivalent. +- **MCP tool events**: Cannot intercept MCP tool calls for validation. +- **Session checkpoints**: Not available. + +## How Plugins Stay Compatible + +Han plugins don't need modification to work with Antigravity. The bridge reads the same `han-plugin.yml` files that Claude Code and OpenCode use: + +```yaml +# This config works in Claude Code, OpenCode, AND Antigravity +hooks: + lint-async: + event: PostToolUse + command: "npx -y @biomejs/biome check --write ${HAN_FILES}" + tool_filter: [Edit, Write, NotebookEdit] + file_filter: ["**/*.{js,jsx,ts,tsx}"] + dirs_with: ["biome.json"] +``` + +Same hook definition. Same validation. Different runtime. + +## Event Logging + +The bridge writes JSONL events to `~/.han/antigravity/projects/`. Each event includes `provider: "antigravity"`. The Han coordinator indexes these and serves them through the Browse UI alongside Claude Code and OpenCode sessions. + +## Next Steps + +- [Install Han plugins](/docs/installation/plugins) for your project's languages and tools +- Read about [hook configuration](/docs/plugin-development/hooks) to understand what hooks do +- Check the [bridge source code](https://github.com/TheBushidoCollective/han/tree/main/plugins/bridges/antigravity) for implementation details diff --git a/website/content/docs/installation/index.md b/website/content/docs/installation/index.md index 29ea8a343..dd5712558 100644 --- a/website/content/docs/installation/index.md +++ b/website/content/docs/installation/index.md @@ -1,9 +1,9 @@ --- title: "Installation Methods" -description: "Install Han for Claude Code, OpenCode, or standalone use via curl, Homebrew, or plugin install." +description: "Install Han for Claude Code, OpenCode, Antigravity, or standalone use via curl, Homebrew, or plugin install." --- -Han works with Claude Code, OpenCode, and as a standalone CLI. Choose the method that fits your workflow. +Han works with Claude Code, OpenCode, Google Antigravity, and as a standalone CLI. Choose the method that fits your workflow. ## Quick Install (Recommended) @@ -76,6 +76,27 @@ Han works with [OpenCode](https://opencode.ai) through a bridge plugin that tran Your Han validation hooks (biome, eslint, typescript, etc.) now run in OpenCode. See the [full OpenCode guide](/docs/installation/opencode) for details. +## Google Antigravity + +Han works with [Google Antigravity](https://antigravity.google/) through an MCP server bridge that exposes skills, disciplines, and validation as tools. + +1. Install Han using any method above +2. Install Han plugins: `han plugin install --auto` +3. Add the MCP server to `~/.gemini/antigravity/mcp_config.json`: + +```json +{ + "mcpServers": { + "han": { + "command": "npx", + "args": ["-y", "antigravity-han-mcp"] + } + } +} +``` + +Han's 400+ skills, 25 disciplines, and validation hooks are now available as MCP tools in Antigravity. See the [full Antigravity guide](/docs/installation/antigravity) for details. + ## Next Steps After installing Han, you'll want to: