Appearance
Overview β
The Beans VS Code extension follows a layered architecture with clear separation of concerns. The extension runs in the workspace extension host to support remote development scenarios and integrates deeply with GitHub Copilot through MCP (Model Context Protocol) and chat participant APIs.
Architecture Principles β
- Remote-First Design: All operations work seamlessly in SSH, WSL, Dev Containers, and Codespaces
- Security-First: No shell injection, secure process execution, minimal permissions
- AI-Optimized: MCP server for tool access, chat participant for conversational workflows
- Testability: Clear module boundaries enable comprehensive unit and integration testing
- Extension Host Compatibility: Designed to run in workspace extension host (
extensionKind: workspace)
Module Boundaries β
Core Layers β
text
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Extension Entry β
β (src/extension.ts) β
β - Activation lifecycle β
β - Service initialization β
β - Command registration orchestration β
ββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββββββββ
β
ββββββββββββββββΌβββββββββββββββ
β β β
βΌ βΌ βΌ
βββββββββββ βββββββββββ ββββββββββββ
β UI Layerβ βAI Layer β βConfig β
β β β β βLayer β
βββββββββββ βββββββββββ ββββββββββββ
β β β
ββββββββββββββββΌβββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Service Layer β
β (BeansService) β
βββββββββββββββββββ
β
βΌ
βββββββββββββββββββ
β Beans CLI β
β (External) β
βββββββββββββββββββModule Structure β
1. Extension Entry (src/extension.ts) β
- Responsibility: Extension lifecycle management, initialization, and cleanup
- Dependencies: All major subsystems
- Key Functions:
activate(): Initialize services, register commands, set up tree viewsdeactivate(): Cleanup resources- Prompt handling for uninitialized workspaces
- AI feature toggle management
2. Service Layer (src/beans/service/) β
- Module:
BeansService - Responsibility: Type-safe wrapper around Beans CLI operations
- Security Features:
- Argument-based process execution (no shell injection)
- Timeout protection
- JSON parsing with error handling
- Key Operations:
listBeans(): Fetch all beans with optional filteringgetBean(): Get single bean detailscreateBean(): Create new bean with metadataupdateBean(): Update bean status, type, priority, relationshipsdeleteBean(): Delete draft/scrapped beans onlyinitWorkspace(): Initialize.beans.ymlin workspace
3. UI Layer (src/beans/tree/, src/beans/details/, src/beans/preview/) β
Tree Views (src/beans/tree/) β
- Providers:
ActiveBeansProvider: Beans with status=todo or in-progressCompletedBeansProvider: Beans with status=completedDraftBeansProvider: Beans with status=draftScrappedBeansProvider: Beans with status=scrapped
- Core Components:
BeansTreeDataProvider: Base class for tree rendering and hierarchical displayBeanTreeItem: VS Code TreeItem representation of a beanBeansFilterManager: Centralized filter state managementBeansDragAndDropController: Drag-and-drop for parent relationship management
- Features:
- Hierarchical tree with parent-child relationships
- Multi-select support for batch operations
- Customizable sorting (status/priority/type/title, updated, created, id)
- Search and tag filtering
- Context menu actions
Details View (src/beans/details/) β
- Module:
BeansDetailsViewProvider - Responsibility: WebView panel for displaying bean markdown content with metadata
- Features:
- Markdown rendering with syntax highlighting
- Action buttons for common operations
- Metadata display (status, type, priority, dates)
- Relationship visualization (parent, children, blocking)
Preview Provider (src/beans/preview/) β
- Module:
BeansPreviewProvider - Responsibility: Virtual document provider for bean markdown
- Use Case: Enable VS Code markdown preview features for bean files
Search View (src/beans/search/) β
- Module:
BeansSearchViewProvider - Responsibility: WebView for advanced search and filtering
- Features: Full-text search, multi-field filtering, saved searches
4. AI Layer (src/beans/chat/, src/beans/mcp/) β
Chat Integration (src/beans/chat/) β
- Module:
BeansChatIntegration - Responsibility: GitHub Copilot chat participant (
@beans) - Slash Commands:
/summary: Current status overview/priority: Top-priority active issues/stale: Stale issues by age/create: Issue creation guidance/search: Find issues by text/commit: Issue-related commit guidance
- Prompt Templates: Structured prompts in
prompts.ts - Tool Access: Reads bean data via BeansService
MCP Integration (src/beans/mcp/) β
- Modules:
BeansMcpIntegration: VS Code MCP provider registrationBeansMcpServer: Standalone stdio MCP server
- Responsibility: Expose Beans operations as MCP tools for any AI client
- Tools Exposed:
- Workspace:
init,refresh - Read:
view,list,search,filter,sort - Write:
create,edit,set_status,set_type,set_priority - Relationships:
set_parent,remove_parent,edit_blocking - Utilities:
copy_id,delete,open_config,show_output
- Workspace:
- Server Architecture:
- Standalone stdio server bundled as
dist/beans-mcp-server.js - Dynamically contributed via
mcp.mcpServersAPI - Uses remote Node.js (
process.execPath) for remote compatibility
- Standalone stdio server bundled as
5. Commands Layer (src/beans/commands/) β
- Module:
BeansCommands - Responsibility: Command palette and context menu command registration
- Command Categories:
- Workspace: Init, refresh
- Navigation: View, open preview, show in tree
- Creation: Create bean, create child bean
- Editing: Edit bean, set status/type/priority
- Relationships: Set/remove parent, edit blocking
- Utilities: Copy ID, delete, show output
- Filtering: Filter, clear filter, tag filter
- Integration: Delegates to BeansService and triggers UI refreshes
6. Configuration Layer (src/beans/config/) β
- Modules:
BeansConfigManager: Read/write.beans.ymlworkspace configCopilotInstructions: Generate.github/copilot-instructions.mdCopilotSkill: Generate.github/skills/beans/SKILL.md
- Responsibility: Manage workspace configuration and AI integration files
- Auto-Generation: When
beans.ai.enabled=true, generates skill and instructions - Auto-Cleanup: When
beans.ai.enabled=false, removes generated files
7. Model Layer (src/beans/model/) β
- Files:
Bean.ts: Core Bean type interfaceconfig.ts: BeansConfig type for.beans.ymlerrors.ts: Typed error classes
- Types:
Bean: id, title, status, type, priority, body, relationships, timestampsBeanStatus: todo, in-progress, draft, completed, scrappedBeanType: milestone, epic, feature, task, bugBeanPriority: critical, high, normal, low, deferred
- Error Types:
BeansCLINotFoundError: CLI not in PATHBeansJSONParseError: Invalid JSON from CLIBeansTimeoutError: CLI operation timeout
8. Logging Layer (src/beans/logging/) β
- Module:
BeansOutput - Responsibility: Centralized logging with output channel and file mirroring
- Features:
- Log levels: trace, debug, info, warn, error
- File mirroring to
.beans/.vscode/beans-output.logfor MCP tool access - Singleton pattern for global access
Data Flow β
Bean Read Flow β
text
User Action (Tree View, Command)
β
βΌ
BeansCommands.viewBean()
β
βΌ
BeansService.getBean(id)
β
βΌ
execAsync(['beans', 'show', '--json', id])
β
βΌ
Beans CLI (reads .beans/*.md)
β
βΌ
JSON Response
β
βΌ
BeansService parses & validates
β
βΌ
Returns Bean object
β
βΌ
BeansDetailsViewProvider.show(bean)
β
βΌ
WebView renders bean markdown + metadataBean Write Flow β
text
User Action (Command, Context Menu)
β
βΌ
BeansCommands.updateBeanStatus(id, status)
β
βΌ
BeansService.updateBean(id, { status })
β
βΌ
execAsync(['beans', 'update', '--json', id, '-s', status])
β
βΌ
Beans CLI (writes to .beans/*.md)
β
βΌ
JSON Response
β
βΌ
BeansService parses & validates
β
βΌ
Fire tree refresh event
β
βΌ
All tree providers re-fetch data
β
βΌ
Tree views update UIMCP Tool Call Flow β
text
Copilot Chat or MCP Client
β
βΌ
MCP Tool Request (e.g., beans_vscode_create)
β
βΌ
BeansMcpServer.handleToolCall()
β
βΌ
BeansService.createBean(params)
β
βΌ
execAsync(['beans', 'create', '--json', ...args])
β
βΌ
Beans CLI (creates .beans/*.md)
β
βΌ
JSON Response
β
βΌ
BeansMcpServer formats response
β
βΌ
MCP Tool Response to AI clientChat Participant Flow β
text
User: "@beans /summary"
β
βΌ
BeansChatIntegration.handleChatRequest()
β
βΌ
Parse slash command
β
βΌ
BeansService.listBeans({ excludeStatus: ['completed', 'scrapped'] })
β
βΌ
Beans CLI returns active beans JSON
β
βΌ
Format response with bean counts and priorities
β
βΌ
Return ChatResponseStream
β
βΌ
Copilot displays formatted summaryDesign Decisions β
1. Why Separate Tree Providers per Status? β
Decision: Four separate tree providers (Active, Completed, Draft, Scrapped) instead of one tree with grouping.
Rationale:
- Clear mental model: each pane has single responsibility
- User control: independently expandable/collapsible panes
- Performance: only fetch needed data for visible panes
- VS Code UX: matches native experience (Problems, Test Explorer)
Trade-off: More code, but better UX and flexibility.
2. Why Wrap Beans CLI Instead of Direct File Access? β
Decision: All operations go through BeansService β Beans CLI, not direct .beans/*.md file manipulation.
Rationale:
- Single source of truth: Beans CLI handles file format, locking, validation
- GraphQL access: Beans CLI provides efficient query engine
- Future-proof: CLI changes don't break extension
- Safety: CLI handles edge cases (conflicts, malformed files)
Trade-off: Dependency on external CLI, but gains reliability and maintainability.
3. Why Both MCP Server and Chat Participant? β
Decision: Implement both MCP tools and a dedicated chat participant.
Rationale:
- MCP tools: Atomic operations for any AI client (Claude Desktop, Cline, etc.)
- Chat participant: Conversational workflows specific to Copilot Chat
- Different use cases: MCP for programmatic access, chat for human interaction
- Complementary: MCP provides primitives, chat provides guided workflows
Trade-off: More surface area to maintain, but serves diverse user needs.
4. Why Workspace Extension Host? β
Decision: Set "extensionKind": ["workspace"] to run in remote extension host.
Rationale:
- Remote compatibility: Extension runs where files and CLI exist
- Security: No local-to-remote tunneling of file operations
- Simplicity: One execution model for local and remote
- Performance: Reduced latency for file operations
Trade-off: Can't run UI-only commands without workspace, but this is the right trade-off for a workspace-centric tool.
5. Why Drag-and-Drop for Parent Relationships? β
Decision: Implement drag-and-drop controller for setting parent relationships.
Rationale:
- Intuitive: Visual representation of hierarchy
- Efficient: No multi-step command palette workflow
- Discovery: Users naturally explore dragging beans
- VS Code native: Leverages TreeView drag-and-drop APIs
Trade-off: Additional complexity, but significant UX improvement for common operation.
6. Why Separate BeansFilterManager? β
Decision: Centralized filter state in BeansFilterManager instead of per-provider state.
Rationale:
- Consistency: Same filter applies across all tree views
- Simplicity: One source of truth for filter state
- Commands: Easy to expose "clear filter" command
- Future: Enables saved filters, filter history
Trade-off: Less flexibility for per-view filtering, but clearer UX.
7. Why Generate Copilot Skill File? β
Decision: Dynamically generate .github/skills/beans/SKILL.md when AI features enabled.
Rationale:
- Context injection: Provides Beans-specific guidance to Copilot
- Discoverability: Users see skill file, understand integration
- Maintainability: Extension keeps skill updated with capabilities
- Toggle: Clean removal when AI features disabled
Trade-off: Generated files in repo, but gitignore prevents commit noise.
8. Why Timeout Protection on CLI Calls? β
Decision: All CLI operations have 30-second timeout (configurable).
Rationale:
- Responsiveness: Don't hang VS Code on slow operations
- Error handling: Explicit timeout error vs indefinite wait
- Remote scenarios: Network issues shouldn't freeze UI
- Resource protection: Prevent runaway processes
Trade-off: Might timeout legitimately slow operations, but better than hanging.
Testing Strategy β
Unit Tests β
- Location:
src/test/beans/ - Coverage: Business logic, formatting utilities, config parsing
- Tools: Vitest, mocked VS Code API
- Examples:
prompts.test.ts: Chat participant prompt formattingsorting.test.ts: Bean sorting algorithmsCopilotSkill.test.ts: Skill generation logic
Integration Tests β
- Location:
src/test/integration/ - Coverage: Extension activation, command registration, tree population
- Tools:
@vscode/test-electron, Vitest - Examples:
extension-activation.test.ts: Full activation lifecycletree-population.test.ts: Tree provider data flowmcp-integration.test.ts: MCP server startup
AI Integration Tests β
- Location:
src/test/integration/ai/ - Coverage: MCP tool definitions, chat participant registration
- Examples:
mcp-integration.test.ts: MCP provider registrationchat-integration.test.ts: Chat participant lifecycleprompt-assembly.test.ts: Slash command routing
Remote Compatibility β
Key Remote Patterns β
- Path Resolution: Always use
workspaceFolder.uri.fsPath(remote-aware) - CLI Execution: Use
beans.cliPathconfig, default tobeansin remote PATH - Process Spawning: Use
process.execPathfor Node.js (resolves to remote Node) - File Access: Use
vscode.workspace.fsAPIs for remote filesystem compatibility
Remote Testing Checklist β
- [ ] SSH: Test with remote Linux/macOS machine
- [ ] WSL: Test with Ubuntu on Windows
- [ ] Dev Container: Test with
.devcontainer/devcontainer.json - [ ] Codespaces: Test with GitHub Codespaces
- [ ] Verify beans CLI installation in each scenario
- [ ] Verify MCP server starts with remote Node.js
- [ ] Verify tree views populate from remote filesystem
- [ ] Verify commands execute in remote context
Extension Points for Future Development β
Potential Additions β
- Archive View: Separate tree provider for archived beans
- Timeline View: Show bean history and changes over time
- Dependency Graph: Visual representation of blocking relationships
- Bulk Operations: Multi-select actions for batch status/type/priority changes
- Saved Filters: Persist user-defined filter configurations
- Bean Templates: Pre-filled templates for common bean types
- Rich Edit Mode: In-extension editor for bean markdown body
- Conflict Resolution: UI for handling merge conflicts in bean files
- Export/Import: Bulk export/import of beans in various formats
- Metrics Dashboard: Visualize bean lifecycle metrics (cycle time, throughput)
Architecture for Extensions β
- New Tree Providers: Extend
BeansTreeDataProviderbase class - New Commands: Register in
BeansCommands, delegate toBeansService - New MCP Tools: Add to
BeansMcpServertool definitions - New Chat Commands: Add to
BeansChatIntegrationslash command router - New Views: Implement
vscode.WebviewViewProviderorvscode.TreeDataProvider
Performance Considerations β
Current Optimizations β
- Lazy Loading: Tree items load children on expand
- Caching: BeansService doesn't cache (CLI is fast enough)
- Debouncing: File watcher debounces rapid changes
- Filtered Queries: Each tree provider only fetches its status filter
- JSON Parsing: Single-pass parsing with typed validation
Future Optimizations β
- Incremental Updates: Update tree items instead of full refresh
- Virtual Scrolling: For workspaces with 1000+ beans
- Background Indexing: Pre-build search index for faster filtering
- Cached GraphQL: Cache frequently-used GraphQL query results
- Parallel Fetches: Fetch multiple tree providers simultaneously
Troubleshooting Guide β
Common Issues β
"Beans CLI not found"
- Check
beans.cliPathsetting - Verify CLI in PATH:
which beans - Install beans:
brew install hmans/beans/beans
- Check
MCP tools not showing
- Check
beans.ai.enabledsetting - Run "Beans: MCP: Refresh Server Definitions"
- Check MCP logs: "Beans: MCP: Open Logs"
- Check
Tree view not populating
- Check workspace has
.beans.yml - Run "Beans: Initialize Beans in Workspace"
- Check output channel: "Beans" for errors
- Check workspace has
Remote extension not activating
- Verify beans CLI on remote machine
- Check remote extension host logs
- Reload window: "Developer: Reload Window"