From 5aaf8a38e4596bd11fc43b41a56fb5c44c5ae0ae Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Fri, 6 Feb 2026 21:15:26 +0000 Subject: [PATCH 01/20] Add springboard-mcp analysis document MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Analyzes svelte-mcp architecture patterns for creating a similar MCP server for springboard. Documents key patterns including: - Tool orchestration (list-sections → get-documentation → autofixer) - Use cases as keywords for smart doc selection - Iterative validation with AST visitors - Context-efficient workflow design Proposes springboard-mcp design with validator patterns for: - State mutation detection - Missing cleanup handlers - Route conflicts - Module interface merging Co-Authored-By: Claude Opus 4.5 --- .planning/springboard-mcp-analysis.md | 310 ++++++++++++++++++++++++++ 1 file changed, 310 insertions(+) create mode 100644 .planning/springboard-mcp-analysis.md diff --git a/.planning/springboard-mcp-analysis.md b/.planning/springboard-mcp-analysis.md new file mode 100644 index 00000000..52f84a71 --- /dev/null +++ b/.planning/springboard-mcp-analysis.md @@ -0,0 +1,310 @@ +# Springboard MCP Server Analysis + +This document analyzes the svelte-mcp architecture and patterns to guide the creation of a springboard-mcp server. + +## Overview: svelte-mcp Architecture + +The svelte-mcp server is a well-designed MCP server that helps AI coding agents work efficiently with Svelte by providing: + +1. **Documentation discovery and retrieval** - Agents find relevant docs without fetching everything +2. **Code validation/autofixing** - Iterative code analysis catches errors before runtime +3. **Playground integration** - Shareable code previews +4. **Context-efficient workflow** - "Try knowledge first, validate, then fetch docs" + +## Key Patterns from svelte-mcp + +### 1. Tool Orchestration Pattern + +``` +list-sections → analyze use_cases → get-documentation +→ svelte-autofixer (iterative) → playground-link +``` + +**Why this matters:** Agents don't need to fetch all documentation upfront. They: +1. Get a lightweight list of what's available with use-case keywords +2. Selectively fetch only relevant sections +3. Validate code iteratively +4. Share results via playground links + +### 2. Use Cases as Keywords + +`svelte-mcp` pre-generates "use_cases" metadata for each doc section using Claude: + +```json +{ + "docs/$state": "always, any svelte project, core reactivity, state management, counters...", + "docs/logic-blocks": "conditional rendering, each loops, iteration, key blocks..." +} +``` + +This allows agents to make smart doc selection without semantic search or reading full docs. + +### 3. Iterative Autofixer Pattern + +``` +Generate code → autofixer → Has issues? → Fix → autofixer again → Until clean +``` + +The `svelte-autofixer` tool returns: +- `issues[]` - Compilation/linting errors +- `suggestions[]` - Improvements (not errors) +- `require_another_tool_call_after_fixing` - Flag for iteration + +**Three-layer validation:** +1. **Compilation** - Svelte compiler warnings/errors +2. **Custom visitors** - Domain-specific pattern detection via AST walking +3. **ESLint** - Standard linting rules + +### 4. Prompt-Based Context Loading + +The `svelte-task` prompt pre-loads: +- Full list of available documentation sections +- Instructions for using tools efficiently +- Workflow guidance ("try autofixer first, then docs") + +This avoids repeated `list-sections` calls and guides agent behavior. + +### 5. Multi-Transport Design + +Same core server supports: +- **HTTP** (Vercel deployment) - Stateless +- **STDIO** (CLI) - Direct invocation via `npx @sveltejs/mcp` + +--- + +## Springboard MCP Server Design + +### Core Components to Build + +#### 1. Documentation System + +**Source files to expose:** +- `/doks/content/docs/springboard/` - Core springboard docs +- `/doks/content/docs/jamtools/` - Jamtools extension docs +- `/packages/springboard/cli/docs-out/CLI_DOCS_sb.md` - CLI reference + +**Use cases to generate:** +```json +{ + "springboard/module-development": "creating modules, registerModule, feature modules, utility modules, initializer modules...", + "springboard/state-management": "shared state, persistent state, user agent state, createSharedState, useState...", + "springboard/guides/registering-ui-routes": "routing, routes, registerRoute, hideApplicationShell, documentMeta..." +} +``` + +#### 2. Autofixer / Validator Tool + +**Springboard-specific patterns to detect:** + +| Pattern | Detection | Suggestion | +|---------|-----------|------------| +| Direct state mutation | `state.value = x` outside setState | Use `state.setState()` or `state.setStateImmer()` | +| Missing module interface | registerModule without return type | Add interface merge to `AllModules` | +| Shared vs UserAgent state confusion | userAgent state used for cross-device data | Use `createSharedState` or `createPersistentState` | +| Missing cleanup | Subscriptions without `onDestroy` | Add `moduleAPI.onDestroy()` callback | +| Route conflicts | Duplicate route paths | Make route paths unique | +| getModule before ready | getModule in synchronous code | Move to async initialization | +| Missing error handling | Actions without try/catch | Use `coreDeps.showError()` | +| Platform directive issues | Mismatched @platform tags | Close @platform directives properly | + +**Validation layers:** +1. **TypeScript compilation** - Type errors in module code +2. **Custom visitors** - Springboard-specific pattern detection +3. **ESLint** - Standard React/TypeScript rules + +#### 3. Tools to Implement + +| Tool | Purpose | Input | Output | +|------|---------|-------|--------| +| `list-sections` | Discover available docs | - | Section list with use_cases | +| `get-documentation` | Fetch doc content | Section name(s) | Markdown content | +| `springboard-validator` | Analyze module code | Code string, options | `{issues[], suggestions[], needsRerun}` | +| `scaffold-module` | Generate module template | Module type, name | Code template | +| `module-types` | Get TypeScript definitions | - | Core type definitions | + +#### 4. Resources + +| Resource | URI Pattern | Description | +|----------|-------------|-------------| +| `springboard-doc` | `springboard://{slug}.md` | Documentation sections | +| `module-api-reference` | `springboard://api/module-api` | ModuleAPI interface | +| `type-definitions` | `springboard://types/{type}` | Core TypeScript types | + +#### 5. Prompts + +**`springboard-task`** - Main context prompt: +``` +You are working on a Springboard application. Springboard is a full-stack +JavaScript framework built on React, Hono, JSON-RPC, and WebSockets. + +Available documentation sections: +{sections_list} + +Workflow: +1. Use your knowledge of React and TypeScript first +2. Run `springboard-validator` to check your code +3. Only fetch documentation when validator issues reference unknown APIs +4. For new modules, use `scaffold-module` to start with correct structure + +Key concepts: +- Modules are registered with `springboard.registerModule()` +- State: shared (cross-device), persistent (DB), userAgent (local) +- Actions are automatically RPC-enabled +- Routes use React Router under the hood +``` + +--- + +## Implementation Plan + +### Phase 1: Core Server Setup + +``` +packages/ + springboard-mcp/ + src/ + mcp/ + index.ts # McpServer initialization + handlers/ + tools/ + list-sections.ts + get-documentation.ts + springboard-validator.ts + scaffold-module.ts + resources/ + doc-section.ts + prompts/ + springboard-task.ts + parse/ + parse.ts # TypeScript/React AST parsing + validators/ + visitors/ # Pattern detection visitors + state-mutation.ts + missing-cleanup.ts + route-conflicts.ts + module-interface.ts + lib/ + schemas.ts # Valibot schemas + use_cases.json # Pre-generated use cases + package.json +``` + +### Phase 2: Validator Visitors + +Key AST visitors to implement: + +1. **`state-mutation.ts`** - Detect direct state mutations +2. **`missing-cleanup.ts`** - Find subscriptions without onDestroy +3. **`module-interface.ts`** - Check AllModules type merging +4. **`action-patterns.ts`** - Validate action definitions +5. **`route-patterns.ts`** - Check route registration +6. **`platform-directives.ts`** - Validate @platform tags + +### Phase 3: Documentation Pipeline + +1. Convert existing doks content to LLM-optimized format +2. Generate use_cases.json via Claude batch API +3. Implement documentation fetching (local files or hosted) + +### Phase 4: Transport & Distribution + +- STDIO transport for CLI usage +- HTTP transport for hosted deployment +- npm package: `@springboard/mcp` + +--- + +## API Surface: ModuleAPI Reference + +For the MCP server to be useful, it needs to understand and expose the ModuleAPI: + +```typescript +interface ModuleAPI { + // Lifecycle + onDestroy(callback: () => void): void + + // State Management + statesAPI: { + createSharedState(name: string, initial: T): StateSupervisor + createPersistentState(name: string, initial: T): StateSupervisor + createUserAgentState(name: string, initial: T): StateSupervisor + } + + // Actions + createActions(actions: T): Actions + + // Routing + registerRoute( + path: string, + options: { hideApplicationShell?: boolean, documentMeta?: DocumentMeta }, + component: React.ComponentType + ): void + + // Module Interaction + getModule(id: K): AllModules[K] + + // Core Dependencies + coreDependencies: CoreDependencies +} + +interface StateSupervisor { + getState(): T + setState(newState: T): void + setStateImmer(callback: (draft: T) => void): void + useState(): T // React hook + subject: Subject // RxJS observable +} + +interface CoreDependencies { + log: (...args: any[]) => void + showError: (error: string) => void + files: { saveFile: (name: string, content: string) => Promise } + storage: { + remote: KVStore + userAgent: KVStore + } + rpc: { + remote: Rpc + local?: Rpc + } + isMaestro: () => boolean +} +``` + +--- + +## Key Differences from svelte-mcp + +| Aspect | svelte-mcp | springboard-mcp | +|--------|------------|-----------------| +| Compilation | Svelte compiler | TypeScript + React | +| AST Parser | svelte-eslint-parser | TypeScript parser | +| Domain | Component framework | Full-stack app framework | +| State | Runes ($state, $derived) | StateSupervisor pattern | +| Focus | Component code validation | Module architecture validation | +| Playground | svelte.dev/playground | (future: stackblitz embed?) | + +--- + +## Files to Reference in svelte-mcp + +When implementing, refer to these key files: + +| svelte-mcp File | Purpose | +|-----------------|---------| +| `packages/mcp-server/src/mcp/index.ts` | Server setup pattern | +| `packages/mcp-server/src/mcp/handlers/tools/svelte-autofixer.ts` | Validation tool structure | +| `packages/mcp-server/src/mcp/autofixers/visitors/` | Visitor pattern examples | +| `packages/mcp-server/src/parse/parse.ts` | Parser setup | +| `packages/mcp-server/src/use_cases.json` | Use cases format | +| `packages/mcp-server/src/mcp/handlers/prompts/svelte-task.ts` | Prompt structure | + +--- + +## Next Steps + +1. **Decide on hosting**: Fork svelte-mcp or build from scratch? +2. **Generate use_cases.json**: Run Claude batch on springboard docs +3. **Implement core validators**: Start with most common error patterns +4. **Set up documentation pipeline**: Local or hosted doc serving +5. **Create npm package**: `@springboard/mcp` or similar From c0a17bf2746cd82bfae16e2990bf66c52392aa6a Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Fri, 6 Feb 2026 21:32:48 +0000 Subject: [PATCH 02/20] Pivot from MCP server to CLI tool design Redesign springboard-ai as a CLI tool instead of MCP server for: - Simpler integration (any AI agent can run shell commands) - No protocol overhead (direct stdin/stdout) - Easier testing (run commands manually) - Portability (works with any AI tool) CLI commands: - sb-ai list-sections: Discover docs with use_cases - sb-ai get-docs: Fetch documentation - sb-ai validate: Validate module code (issues/suggestions) - sb-ai scaffold: Generate module templates - sb-ai context: Output full agent context prompt - sb-ai types: Output TypeScript definitions Co-Authored-By: Claude Opus 4.5 --- .planning/springboard-mcp-analysis.md | 427 ++++++++++++++------------ 1 file changed, 234 insertions(+), 193 deletions(-) diff --git a/.planning/springboard-mcp-analysis.md b/.planning/springboard-mcp-analysis.md index 52f84a71..f81322cc 100644 --- a/.planning/springboard-mcp-analysis.md +++ b/.planning/springboard-mcp-analysis.md @@ -1,89 +1,43 @@ -# Springboard MCP Server Analysis +# Springboard CLI for AI Agents -This document analyzes the svelte-mcp architecture and patterns to guide the creation of a springboard-mcp server. +This document analyzes the svelte-mcp patterns and adapts them into a CLI tool that AI coding agents can use when building springboard applications. -## Overview: svelte-mcp Architecture +## Why CLI Instead of MCP? -The svelte-mcp server is a well-designed MCP server that helps AI coding agents work efficiently with Svelte by providing: +- **Simpler integration** - Any AI agent can run shell commands +- **No protocol overhead** - Direct stdin/stdout communication +- **Easier testing** - Run commands manually to verify behavior +- **Portable** - Works with any AI tool, not just MCP-compatible ones -1. **Documentation discovery and retrieval** - Agents find relevant docs without fetching everything -2. **Code validation/autofixing** - Iterative code analysis catches errors before runtime -3. **Playground integration** - Shareable code previews -4. **Context-efficient workflow** - "Try knowledge first, validate, then fetch docs" +## Key Patterns from svelte-mcp (Adapted for CLI) -## Key Patterns from svelte-mcp +### 1. Command Structure -### 1. Tool Orchestration Pattern +svelte-mcp exposes these via MCP tools - we expose them as CLI subcommands: -``` -list-sections → analyze use_cases → get-documentation -→ svelte-autofixer (iterative) → playground-link -``` - -**Why this matters:** Agents don't need to fetch all documentation upfront. They: -1. Get a lightweight list of what's available with use-case keywords -2. Selectively fetch only relevant sections -3. Validate code iteratively -4. Share results via playground links - -### 2. Use Cases as Keywords - -`svelte-mcp` pre-generates "use_cases" metadata for each doc section using Claude: - -```json -{ - "docs/$state": "always, any svelte project, core reactivity, state management, counters...", - "docs/logic-blocks": "conditional rendering, each loops, iteration, key blocks..." -} -``` +```bash +# Documentation discovery +sb-ai list-sections # List available docs with use_cases +sb-ai get-docs # Fetch specific documentation -This allows agents to make smart doc selection without semantic search or reading full docs. +# Code validation +sb-ai validate # Validate a module file +sb-ai validate --stdin # Validate code from stdin -### 3. Iterative Autofixer Pattern +# Scaffolding +sb-ai scaffold module # Generate module template +sb-ai scaffold feature # Generate feature module +sb-ai scaffold utility # Generate utility module +# Context for agents +sb-ai context # Output full context prompt for AI agents +sb-ai types # Output core TypeScript definitions ``` -Generate code → autofixer → Has issues? → Fix → autofixer again → Until clean -``` - -The `svelte-autofixer` tool returns: -- `issues[]` - Compilation/linting errors -- `suggestions[]` - Improvements (not errors) -- `require_another_tool_call_after_fixing` - Flag for iteration - -**Three-layer validation:** -1. **Compilation** - Svelte compiler warnings/errors -2. **Custom visitors** - Domain-specific pattern detection via AST walking -3. **ESLint** - Standard linting rules - -### 4. Prompt-Based Context Loading - -The `svelte-task` prompt pre-loads: -- Full list of available documentation sections -- Instructions for using tools efficiently -- Workflow guidance ("try autofixer first, then docs") - -This avoids repeated `list-sections` calls and guides agent behavior. - -### 5. Multi-Transport Design - -Same core server supports: -- **HTTP** (Vercel deployment) - Stateless -- **STDIO** (CLI) - Direct invocation via `npx @sveltejs/mcp` - ---- - -## Springboard MCP Server Design -### Core Components to Build - -#### 1. Documentation System +### 2. Use Cases as Keywords -**Source files to expose:** -- `/doks/content/docs/springboard/` - Core springboard docs -- `/doks/content/docs/jamtools/` - Jamtools extension docs -- `/packages/springboard/cli/docs-out/CLI_DOCS_sb.md` - CLI reference +Pre-generated metadata lets agents select docs without semantic search: -**Use cases to generate:** ```json { "springboard/module-development": "creating modules, registerModule, feature modules, utility modules, initializer modules...", @@ -92,131 +46,212 @@ Same core server supports: } ``` -#### 2. Autofixer / Validator Tool +**CLI output format:** +```bash +$ sb-ai list-sections +springboard/module-development + Use cases: creating modules, registerModule, feature modules, utility modules... -**Springboard-specific patterns to detect:** - -| Pattern | Detection | Suggestion | -|---------|-----------|------------| -| Direct state mutation | `state.value = x` outside setState | Use `state.setState()` or `state.setStateImmer()` | -| Missing module interface | registerModule without return type | Add interface merge to `AllModules` | -| Shared vs UserAgent state confusion | userAgent state used for cross-device data | Use `createSharedState` or `createPersistentState` | -| Missing cleanup | Subscriptions without `onDestroy` | Add `moduleAPI.onDestroy()` callback | -| Route conflicts | Duplicate route paths | Make route paths unique | -| getModule before ready | getModule in synchronous code | Move to async initialization | -| Missing error handling | Actions without try/catch | Use `coreDeps.showError()` | -| Platform directive issues | Mismatched @platform tags | Close @platform directives properly | - -**Validation layers:** -1. **TypeScript compilation** - Type errors in module code -2. **Custom visitors** - Springboard-specific pattern detection -3. **ESLint** - Standard React/TypeScript rules - -#### 3. Tools to Implement +springboard/state-management + Use cases: shared state, persistent state, user agent state, createSharedState... +``` -| Tool | Purpose | Input | Output | -|------|---------|-------|--------| -| `list-sections` | Discover available docs | - | Section list with use_cases | -| `get-documentation` | Fetch doc content | Section name(s) | Markdown content | -| `springboard-validator` | Analyze module code | Code string, options | `{issues[], suggestions[], needsRerun}` | -| `scaffold-module` | Generate module template | Module type, name | Code template | -| `module-types` | Get TypeScript definitions | - | Core type definitions | +### 3. Iterative Validator Pattern -#### 4. Resources +```bash +$ sb-ai validate src/modules/my-module.ts +{ + "issues": [ + "Line 15: Direct state mutation detected. Use state.setState() or state.setStateImmer()" + ], + "suggestions": [ + "Line 8: Consider adding onDestroy() cleanup for the subscription on line 12" + ], + "hasErrors": true +} +``` -| Resource | URI Pattern | Description | -|----------|-------------|-------------| -| `springboard-doc` | `springboard://{slug}.md` | Documentation sections | -| `module-api-reference` | `springboard://api/module-api` | ModuleAPI interface | -| `type-definitions` | `springboard://types/{type}` | Core TypeScript types | +Agents can iterate: +``` +Generate code → sb-ai validate → Fix issues → sb-ai validate → Until clean +``` -#### 5. Prompts +### 4. Context Prompt -**`springboard-task`** - Main context prompt: -``` +```bash +$ sb-ai context You are working on a Springboard application. Springboard is a full-stack JavaScript framework built on React, Hono, JSON-RPC, and WebSockets. Available documentation sections: -{sections_list} +- springboard/module-development (creating modules, registerModule...) +- springboard/state-management (shared state, persistent state...) +- springboard/guides/registering-ui-routes (routing, routes...) +... Workflow: 1. Use your knowledge of React and TypeScript first -2. Run `springboard-validator` to check your code -3. Only fetch documentation when validator issues reference unknown APIs -4. For new modules, use `scaffold-module` to start with correct structure +2. Run `sb-ai validate ` to check your code +3. Only fetch docs with `sb-ai get-docs
` when needed +4. For new modules, use `sb-ai scaffold module ` Key concepts: - Modules are registered with `springboard.registerModule()` -- State: shared (cross-device), persistent (DB), userAgent (local) +- State types: shared (cross-device), persistent (DB), userAgent (local) - Actions are automatically RPC-enabled - Routes use React Router under the hood ``` --- -## Implementation Plan +## CLI Design + +### Commands + +| Command | Purpose | Output | +|---------|---------|--------| +| `sb-ai list-sections` | List docs with use_cases | Text or JSON (`--json`) | +| `sb-ai get-docs ` | Fetch documentation | Markdown content | +| `sb-ai validate ` | Validate module code | JSON `{issues, suggestions, hasErrors}` | +| `sb-ai validate --stdin` | Validate from stdin | JSON `{issues, suggestions, hasErrors}` | +| `sb-ai scaffold ` | Generate templates | File path created | +| `sb-ai context` | Full agent context | Text prompt | +| `sb-ai types` | Core type definitions | TypeScript definitions | + +### Output Formats + +```bash +# Default: human-readable +$ sb-ai list-sections + +# JSON for programmatic use +$ sb-ai list-sections --json + +# Quiet mode (errors only) +$ sb-ai validate src/module.ts --quiet +``` + +--- + +## Validator Patterns to Detect + +| Pattern | Detection | Message | +|---------|-----------|---------| +| Direct state mutation | `state.value = x` | Use `state.setState()` or `state.setStateImmer()` | +| Missing module interface | registerModule without AllModules merge | Add interface merge for type-safe `getModule()` | +| Wrong state type | userAgent state for cross-device data | Use `createSharedState` or `createPersistentState` | +| Missing cleanup | Subscriptions without `onDestroy` | Add `moduleAPI.onDestroy()` callback | +| Route conflicts | Duplicate route paths | Make route paths unique | +| Sync getModule | getModule in synchronous code | Move to async initialization | +| Missing error handling | Actions without error handling | Use `coreDeps.showError()` | +| Platform directive issues | Unclosed @platform tags | Close `@platform` with `@platform end` | -### Phase 1: Core Server Setup +### Validation Layers + +1. **TypeScript** - Type errors via tsc +2. **Custom AST visitors** - Springboard-specific patterns +3. **ESLint** - React/TypeScript best practices + +--- + +## Implementation Structure ``` packages/ - springboard-mcp/ + springboard-ai-cli/ src/ - mcp/ - index.ts # McpServer initialization - handlers/ - tools/ - list-sections.ts - get-documentation.ts - springboard-validator.ts - scaffold-module.ts - resources/ - doc-section.ts - prompts/ - springboard-task.ts - parse/ - parse.ts # TypeScript/React AST parsing + cli.ts # Main entry point (commander/yargs) + commands/ + list-sections.ts + get-docs.ts + validate.ts + scaffold.ts + context.ts + types.ts validators/ - visitors/ # Pattern detection visitors + index.ts # Orchestrates validation layers + visitors/ state-mutation.ts missing-cleanup.ts route-conflicts.ts module-interface.ts - lib/ - schemas.ts # Valibot schemas - use_cases.json # Pre-generated use cases + platform-directives.ts + docs/ + sections.json # Doc metadata with use_cases + content/ # LLM-optimized doc content + templates/ + module.ts.template + feature.ts.template + utility.ts.template package.json + tsconfig.json +``` + +### Key Dependencies + +```json +{ + "dependencies": { + "commander": "^12.0.0", + "typescript": "^5.0.0", + "@typescript-eslint/parser": "^7.0.0", + "@typescript-eslint/typescript-estree": "^7.0.0" + } +} ``` -### Phase 2: Validator Visitors +--- -Key AST visitors to implement: +## Documentation Pipeline -1. **`state-mutation.ts`** - Detect direct state mutations -2. **`missing-cleanup.ts`** - Find subscriptions without onDestroy -3. **`module-interface.ts`** - Check AllModules type merging -4. **`action-patterns.ts`** - Validate action definitions -5. **`route-patterns.ts`** - Check route registration -6. **`platform-directives.ts`** - Validate @platform tags +### Source Files -### Phase 3: Documentation Pipeline +- `/doks/content/docs/springboard/` - Core docs +- `/doks/content/docs/jamtools/` - Jamtools extension +- `/packages/springboard/cli/docs-out/CLI_DOCS_sb.md` - CLI reference -1. Convert existing doks content to LLM-optimized format -2. Generate use_cases.json via Claude batch API -3. Implement documentation fetching (local files or hosted) +### LLM-Optimized Format -### Phase 4: Transport & Distribution +Convert markdown docs to condensed format: -- STDIO transport for CLI usage -- HTTP transport for hosted deployment -- npm package: `@springboard/mcp` +```markdown +# State Management ---- +## createSharedState(name, initialValue) +Creates state synchronized across all connected devices. +- Returns: StateSupervisor +- Use when: multiplayer features, real-time collaboration + +## createPersistentState(name, initialValue) +Creates state persisted to database. +- Returns: StateSupervisor +- Use when: user preferences, saved data + +## createUserAgentState(name, initialValue) +Creates state stored locally (localStorage). +- Returns: StateSupervisor +- Use when: UI state, device-specific settings +``` + +### Use Cases Generation -## API Surface: ModuleAPI Reference +Run Claude batch API on docs to generate `sections.json`: -For the MCP server to be useful, it needs to understand and expose the ModuleAPI: +```json +{ + "sections": [ + { + "slug": "springboard/module-development", + "title": "Module Development", + "use_cases": "creating modules, registerModule, feature modules, utility modules, initializer modules, module lifecycle, module dependencies" + } + ] +} +``` + +--- + +## API Reference (for context command) ```typescript interface ModuleAPI { @@ -231,14 +266,10 @@ interface ModuleAPI { } // Actions - createActions(actions: T): Actions + createActions(actions: T): Actions // Routing - registerRoute( - path: string, - options: { hideApplicationShell?: boolean, documentMeta?: DocumentMeta }, - component: React.ComponentType - ): void + registerRoute(path: string, options: RouteOptions, component: Component): void // Module Interaction getModule(id: K): AllModules[K] @@ -258,53 +289,63 @@ interface StateSupervisor { interface CoreDependencies { log: (...args: any[]) => void showError: (error: string) => void - files: { saveFile: (name: string, content: string) => Promise } - storage: { - remote: KVStore - userAgent: KVStore - } - rpc: { - remote: Rpc - local?: Rpc - } + files: { saveFile(name: string, content: string): Promise } + storage: { remote: KVStore; userAgent: KVStore } + rpc: { remote: Rpc; local?: Rpc } isMaestro: () => boolean } ``` --- -## Key Differences from svelte-mcp +## Usage Examples -| Aspect | svelte-mcp | springboard-mcp | -|--------|------------|-----------------| -| Compilation | Svelte compiler | TypeScript + React | -| AST Parser | svelte-eslint-parser | TypeScript parser | -| Domain | Component framework | Full-stack app framework | -| State | Runes ($state, $derived) | StateSupervisor pattern | -| Focus | Component code validation | Module architecture validation | -| Playground | svelte.dev/playground | (future: stackblitz embed?) | +### Agent Workflow ---- +```bash +# 1. Get context at start of session +sb-ai context > /tmp/springboard-context.md + +# 2. Find relevant docs for task +sb-ai list-sections | grep -i "state" + +# 3. Fetch specific documentation +sb-ai get-docs springboard/state-management -## Files to Reference in svelte-mcp +# 4. Generate module scaffold +sb-ai scaffold feature user-profile + +# 5. Validate after writing code +sb-ai validate src/modules/user-profile.ts + +# 6. Fix issues and re-validate until clean +``` -When implementing, refer to these key files: +### Integration with AI Tools -| svelte-mcp File | Purpose | -|-----------------|---------| -| `packages/mcp-server/src/mcp/index.ts` | Server setup pattern | -| `packages/mcp-server/src/mcp/handlers/tools/svelte-autofixer.ts` | Validation tool structure | -| `packages/mcp-server/src/mcp/autofixers/visitors/` | Visitor pattern examples | -| `packages/mcp-server/src/parse/parse.ts` | Parser setup | -| `packages/mcp-server/src/use_cases.json` | Use cases format | -| `packages/mcp-server/src/mcp/handlers/prompts/svelte-task.ts` | Prompt structure | +**Claude Code CLAUDE.md:** +```markdown +## Springboard Development + +When working on springboard modules: +1. Run `sb-ai context` to understand the framework +2. Use `sb-ai list-sections` to find relevant docs +3. Validate code with `sb-ai validate ` before finishing +4. Use `sb-ai scaffold` for new modules +``` + +**Cursor rules:** +``` +When creating springboard modules, always run `sb-ai validate` on the file. +``` --- ## Next Steps -1. **Decide on hosting**: Fork svelte-mcp or build from scratch? -2. **Generate use_cases.json**: Run Claude batch on springboard docs -3. **Implement core validators**: Start with most common error patterns -4. **Set up documentation pipeline**: Local or hosted doc serving -5. **Create npm package**: `@springboard/mcp` or similar +1. **Set up package structure** - Create `packages/springboard-ai-cli` +2. **Implement list-sections** - Parse docs, generate use_cases +3. **Implement validate** - TypeScript parsing + custom visitors +4. **Implement scaffold** - Module templates +5. **Generate use_cases.json** - Run Claude batch on docs +6. **Publish** - `npx @springboard/ai` or `npx sb-ai` From d11e44742df9cfaafac0eb2de48f54eda6bfe4b5 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 19:09:29 +0000 Subject: [PATCH 03/20] Rename CLI commands to `sb docs` subcommand Integrate with existing `sb` CLI rather than separate tool: - sb docs list - List docs with use_cases - sb docs get - Fetch documentation - sb docs validate - Validate module code - sb docs scaffold - Generate templates - sb docs context - Agent context prompt - sb docs types - TypeScript definitions Implementation extends /packages/springboard/cli/ instead of new package. Co-Authored-By: Claude Opus 4.5 --- .planning/springboard-mcp-analysis.md | 144 +++++++++++++------------- 1 file changed, 72 insertions(+), 72 deletions(-) diff --git a/.planning/springboard-mcp-analysis.md b/.planning/springboard-mcp-analysis.md index f81322cc..6537b274 100644 --- a/.planning/springboard-mcp-analysis.md +++ b/.planning/springboard-mcp-analysis.md @@ -1,6 +1,6 @@ -# Springboard CLI for AI Agents +# Springboard `sb docs` CLI for AI Agents -This document analyzes the svelte-mcp patterns and adapts them into a CLI tool that AI coding agents can use when building springboard applications. +This document analyzes the svelte-mcp patterns and adapts them into a `sb docs` subcommand that AI coding agents can use when building springboard applications. ## Why CLI Instead of MCP? @@ -8,30 +8,31 @@ This document analyzes the svelte-mcp patterns and adapts them into a CLI tool t - **No protocol overhead** - Direct stdin/stdout communication - **Easier testing** - Run commands manually to verify behavior - **Portable** - Works with any AI tool, not just MCP-compatible ones +- **Unified CLI** - Extends existing `sb` command rather than adding new tool ## Key Patterns from svelte-mcp (Adapted for CLI) ### 1. Command Structure -svelte-mcp exposes these via MCP tools - we expose them as CLI subcommands: +svelte-mcp exposes these via MCP tools - we expose them as `sb docs` subcommands: ```bash # Documentation discovery -sb-ai list-sections # List available docs with use_cases -sb-ai get-docs # Fetch specific documentation +sb docs list # List available docs with use_cases +sb docs get # Fetch specific documentation # Code validation -sb-ai validate # Validate a module file -sb-ai validate --stdin # Validate code from stdin +sb docs validate # Validate a module file +sb docs validate --stdin # Validate code from stdin # Scaffolding -sb-ai scaffold module # Generate module template -sb-ai scaffold feature # Generate feature module -sb-ai scaffold utility # Generate utility module +sb docs scaffold module # Generate module template +sb docs scaffold feature # Generate feature module +sb docs scaffold utility # Generate utility module # Context for agents -sb-ai context # Output full context prompt for AI agents -sb-ai types # Output core TypeScript definitions +sb docs context # Output full context prompt for AI agents +sb docs types # Output core TypeScript definitions ``` ### 2. Use Cases as Keywords @@ -48,7 +49,7 @@ Pre-generated metadata lets agents select docs without semantic search: **CLI output format:** ```bash -$ sb-ai list-sections +$ sb docs list springboard/module-development Use cases: creating modules, registerModule, feature modules, utility modules... @@ -59,7 +60,7 @@ springboard/state-management ### 3. Iterative Validator Pattern ```bash -$ sb-ai validate src/modules/my-module.ts +$ sb docs validate src/modules/my-module.ts { "issues": [ "Line 15: Direct state mutation detected. Use state.setState() or state.setStateImmer()" @@ -73,13 +74,13 @@ $ sb-ai validate src/modules/my-module.ts Agents can iterate: ``` -Generate code → sb-ai validate → Fix issues → sb-ai validate → Until clean +Generate code → sb docs validate → Fix issues → sb docs validate → Until clean ``` ### 4. Context Prompt ```bash -$ sb-ai context +$ sb docs context You are working on a Springboard application. Springboard is a full-stack JavaScript framework built on React, Hono, JSON-RPC, and WebSockets. @@ -91,9 +92,9 @@ Available documentation sections: Workflow: 1. Use your knowledge of React and TypeScript first -2. Run `sb-ai validate ` to check your code -3. Only fetch docs with `sb-ai get-docs
` when needed -4. For new modules, use `sb-ai scaffold module ` +2. Run `sb docs validate ` to check your code +3. Only fetch docs with `sb docs get
` when needed +4. For new modules, use `sb docs scaffold module ` Key concepts: - Modules are registered with `springboard.registerModule()` @@ -110,25 +111,25 @@ Key concepts: | Command | Purpose | Output | |---------|---------|--------| -| `sb-ai list-sections` | List docs with use_cases | Text or JSON (`--json`) | -| `sb-ai get-docs ` | Fetch documentation | Markdown content | -| `sb-ai validate ` | Validate module code | JSON `{issues, suggestions, hasErrors}` | -| `sb-ai validate --stdin` | Validate from stdin | JSON `{issues, suggestions, hasErrors}` | -| `sb-ai scaffold ` | Generate templates | File path created | -| `sb-ai context` | Full agent context | Text prompt | -| `sb-ai types` | Core type definitions | TypeScript definitions | +| `sb docs list` | List docs with use_cases | Text or JSON (`--json`) | +| `sb docs get ` | Fetch documentation | Markdown content | +| `sb docs validate ` | Validate module code | JSON `{issues, suggestions, hasErrors}` | +| `sb docs validate --stdin` | Validate from stdin | JSON `{issues, suggestions, hasErrors}` | +| `sb docs scaffold ` | Generate templates | File path created | +| `sb docs context` | Full agent context | Text prompt | +| `sb docs types` | Core type definitions | TypeScript definitions | ### Output Formats ```bash # Default: human-readable -$ sb-ai list-sections +$ sb docs list # JSON for programmatic use -$ sb-ai list-sections --json +$ sb docs list --json # Quiet mode (errors only) -$ sb-ai validate src/module.ts --quiet +$ sb docs validate src/module.ts --quiet ``` --- @@ -156,44 +157,42 @@ $ sb-ai validate src/module.ts --quiet ## Implementation Structure +Extends existing `sb` CLI in `/packages/springboard/cli/`: + ``` -packages/ - springboard-ai-cli/ - src/ - cli.ts # Main entry point (commander/yargs) - commands/ - list-sections.ts - get-docs.ts +packages/springboard/cli/ + src/ + commands/ + docs/ # New `sb docs` subcommand + index.ts # Register docs subcommands + list.ts + get.ts validate.ts scaffold.ts context.ts types.ts - validators/ - index.ts # Orchestrates validation layers - visitors/ - state-mutation.ts - missing-cleanup.ts - route-conflicts.ts - module-interface.ts - platform-directives.ts - docs/ - sections.json # Doc metadata with use_cases - content/ # LLM-optimized doc content - templates/ - module.ts.template - feature.ts.template - utility.ts.template - package.json - tsconfig.json + validators/ + index.ts # Orchestrates validation layers + visitors/ + state-mutation.ts + missing-cleanup.ts + route-conflicts.ts + module-interface.ts + platform-directives.ts + docs-data/ + sections.json # Doc metadata with use_cases + content/ # LLM-optimized doc content (or fetch from doks) + templates/ + module.ts.template + feature.ts.template + utility.ts.template ``` -### Key Dependencies +### Key Dependencies to Add ```json { "dependencies": { - "commander": "^12.0.0", - "typescript": "^5.0.0", "@typescript-eslint/parser": "^7.0.0", "@typescript-eslint/typescript-estree": "^7.0.0" } @@ -304,19 +303,19 @@ interface CoreDependencies { ```bash # 1. Get context at start of session -sb-ai context > /tmp/springboard-context.md +sb docs context > /tmp/springboard-context.md # 2. Find relevant docs for task -sb-ai list-sections | grep -i "state" +sb docs list | grep -i "state" # 3. Fetch specific documentation -sb-ai get-docs springboard/state-management +sb docs get springboard/state-management # 4. Generate module scaffold -sb-ai scaffold feature user-profile +sb docs scaffold feature user-profile # 5. Validate after writing code -sb-ai validate src/modules/user-profile.ts +sb docs validate src/modules/user-profile.ts # 6. Fix issues and re-validate until clean ``` @@ -328,24 +327,25 @@ sb-ai validate src/modules/user-profile.ts ## Springboard Development When working on springboard modules: -1. Run `sb-ai context` to understand the framework -2. Use `sb-ai list-sections` to find relevant docs -3. Validate code with `sb-ai validate ` before finishing -4. Use `sb-ai scaffold` for new modules +1. Run `sb docs context` to understand the framework +2. Use `sb docs list` to find relevant docs +3. Validate code with `sb docs validate ` before finishing +4. Use `sb docs scaffold` for new modules ``` **Cursor rules:** ``` -When creating springboard modules, always run `sb-ai validate` on the file. +When creating springboard modules, always run `sb docs validate` on the file. ``` --- ## Next Steps -1. **Set up package structure** - Create `packages/springboard-ai-cli` -2. **Implement list-sections** - Parse docs, generate use_cases -3. **Implement validate** - TypeScript parsing + custom visitors -4. **Implement scaffold** - Module templates -5. **Generate use_cases.json** - Run Claude batch on docs -6. **Publish** - `npx @springboard/ai` or `npx sb-ai` +1. **Add `docs` subcommand to `sb` CLI** - Extend `/packages/springboard/cli/` +2. **Implement `sb docs list`** - Parse doks content, generate use_cases +3. **Implement `sb docs get`** - Fetch doc content by section +4. **Implement `sb docs context`** - Output agent context prompt +5. **Implement `sb docs validate`** - TypeScript parsing + custom visitors +6. **Implement `sb docs scaffold`** - Module templates +7. **Generate use_cases.json** - Run Claude batch on docs for keyword metadata From b8ba68ffe8a02caff9ccffd411e2dbe7a614447b Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 19:13:24 +0000 Subject: [PATCH 04/20] Add `sb docs` command structure for AI agent support Creates new `sb docs` subcommand with placeholder implementations for: - sb docs list: List documentation sections with use_cases - sb docs get: Fetch specific documentation - sb docs validate: Validate module code - sb docs scaffold: Generate module templates (module/feature/utility) - sb docs context: Output agent context prompt - sb docs types: Output TypeScript definitions All commands return TODO messages and will be implemented in follow-up commits. Co-Authored-By: Claude Opus 4.5 --- packages/springboard/cli/src/cli.ts | 3 + packages/springboard/cli/src/docs_command.ts | 90 ++++++++++++++++++++ 2 files changed, 93 insertions(+) create mode 100644 packages/springboard/cli/src/docs_command.ts diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index d3aa54f9..ea37c502 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -8,6 +8,7 @@ import packageJSON from '../package.json'; import {buildApplication, buildServer, platformBrowserBuildConfig, platformNodeBuildConfig, platformOfflineBrowserBuildConfig, platformPartykitBrowserBuildConfig, platformPartykitServerBuildConfig, platformTauriMaestroBuildConfig, platformTauriWebviewBuildConfig, Plugin, SpringboardPlatform} from './build'; import {esbuildPluginTransformAwaitImportToRequire} from './esbuild_plugins/esbuild_plugin_transform_await_import'; +import {createDocsCommand} from './docs_command'; function resolveEntrypoint(entrypoint: string): string { let applicationEntrypoint = entrypoint; @@ -317,6 +318,8 @@ program // await generateReactNativeProject(); // }); +// Register docs command +program.addCommand(createDocsCommand()); if (!(globalThis as any).AVOID_PROGRAM_PARSE) { program.parse(); diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts new file mode 100644 index 00000000..dd1885b3 --- /dev/null +++ b/packages/springboard/cli/src/docs_command.ts @@ -0,0 +1,90 @@ +import { Command } from 'commander'; + +/** + * Creates the `sb docs` command with all subcommands for AI agent support. + * + * Provides documentation discovery, code validation, scaffolding, and context + * for AI coding agents working with Springboard applications. + */ +export function createDocsCommand(): Command { + const docs = new Command('docs') + .description('Documentation and AI agent support tools'); + + // sb docs list + docs.command('list') + .description('List available documentation sections with use cases') + .option('--json', 'Output as JSON') + .action(async (options: { json?: boolean }) => { + console.log('TODO: Implement list command'); + // Will list all documentation sections with their use_cases keywords + }); + + // sb docs get + docs.command('get') + .description('Fetch documentation for specific sections') + .argument('', 'Documentation section(s) to fetch') + .action(async (sections: string[]) => { + console.log('TODO: Implement get command for sections:', sections); + // Will fetch and output documentation content + }); + + // sb docs validate + docs.command('validate') + .description('Validate a module file for common issues') + .argument('[file]', 'Module file to validate') + .option('--stdin', 'Read code from stdin instead of file') + .option('--json', 'Output as JSON (default)') + .option('--quiet', 'Only output errors') + .action(async (file: string | undefined, options: { stdin?: boolean; json?: boolean; quiet?: boolean }) => { + console.log('TODO: Implement validate command'); + // Will run TypeScript compilation + custom AST visitors + ESLint + // Output: { issues: [], suggestions: [], hasErrors: boolean } + }); + + // sb docs scaffold + const scaffold = docs.command('scaffold') + .description('Generate module templates'); + + scaffold.command('module') + .description('Generate a basic module') + .argument('', 'Module name') + .action(async (name: string) => { + console.log('TODO: Implement scaffold module for:', name); + }); + + scaffold.command('feature') + .description('Generate a feature module') + .argument('', 'Module name') + .action(async (name: string) => { + console.log('TODO: Implement scaffold feature for:', name); + }); + + scaffold.command('utility') + .description('Generate a utility module') + .argument('', 'Module name') + .action(async (name: string) => { + console.log('TODO: Implement scaffold utility for:', name); + }); + + // sb docs context + docs.command('context') + .description('Output full context prompt for AI agents') + .action(async () => { + console.log('TODO: Implement context command'); + // Will output a comprehensive prompt with: + // - Framework overview + // - Available documentation sections + // - Workflow instructions + // - Key concepts + }); + + // sb docs types + docs.command('types') + .description('Output core TypeScript type definitions') + .action(async () => { + console.log('TODO: Implement types command'); + // Will output ModuleAPI, StateSupervisor, CoreDependencies, etc. + }); + + return docs; +} From 05dcace90c1bda5f338803f8e7c704c67ffd32b3 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 19:15:44 +0000 Subject: [PATCH 05/20] Add `sb docs` help and create CLAUDE.md/AGENTS.md files Changes: 1. Make `sb docs` (without subcommand) show help output 2. Add CLAUDE.md creation in create-springboard-app with: - Instructions to run `npx sb docs --help` before coding - Key commands and workflow for Claude Code agents - Emphasis on using docs tools to ensure correct code 3. Add AGENTS.md creation in create-springboard-app with: - Similar instructions for other AI coding assistants - Clear workflow recommendations - Guidance to lean on `sb docs` commands Both files are created automatically when running create-springboard-app, ensuring AI agents have immediate context about available documentation tools. Co-Authored-By: Claude Opus 4.5 --- packages/springboard/cli/src/docs_command.ts | 6 +- .../create-springboard-app/src/cli.ts | 70 +++++++++++++++++++ 2 files changed, 75 insertions(+), 1 deletion(-) diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts index dd1885b3..5ce5acb2 100644 --- a/packages/springboard/cli/src/docs_command.ts +++ b/packages/springboard/cli/src/docs_command.ts @@ -8,7 +8,11 @@ import { Command } from 'commander'; */ export function createDocsCommand(): Command { const docs = new Command('docs') - .description('Documentation and AI agent support tools'); + .description('Documentation and AI agent support tools') + .action(() => { + // When `sb docs` is called without subcommand, show help + docs.help(); + }); // sb docs list docs.command('list') diff --git a/packages/springboard/create-springboard-app/src/cli.ts b/packages/springboard/create-springboard-app/src/cli.ts index d1d366d8..0c5a18f1 100644 --- a/packages/springboard/create-springboard-app/src/cli.ts +++ b/packages/springboard/create-springboard-app/src/cli.ts @@ -69,6 +69,76 @@ program writeFileSync(`${process.cwd()}/src/index.tsx`, exampleString); console.log('Created application entrypoint src/index.tsx'); + // Create CLAUDE.md for Claude Code agents + const claudeMdContent = `# Springboard Development Guide + +This application is built with the **Springboard framework**. + +Before writing any code, run: + +\`\`\`bash +npx sb docs --help +\`\`\` + +This will show you available documentation commands that help ensure your code is correct and uses the framework as intended. + +## Key Commands + +- \`sb docs list\` - List available documentation sections +- \`sb docs get
\` - Fetch specific documentation +- \`sb docs validate \` - Validate your module code +- \`sb docs scaffold \` - Generate module templates +- \`sb docs context\` - Get full framework context + +## Workflow + +1. Run \`sb docs context\` at the start of your session +2. Use \`sb docs list\` to find relevant documentation +3. Always run \`sb docs validate \` before finishing work +4. Use \`sb docs scaffold\` to generate new modules + +This ensures your code follows Springboard patterns and avoids common mistakes. +`; + writeFileSync(`${process.cwd()}/CLAUDE.md`, claudeMdContent); + console.log('Created CLAUDE.md with AI agent instructions'); + + // Create AGENTS.md for other AI coding assistants + const agentsMdContent = `# AI Agent Instructions + +This application is created with the **Springboard framework**. + +## Before You Start + +Run this command to learn about available documentation tools: + +\`\`\`bash +npx sb docs --help +\`\`\` + +## Important + +Please lean on the \`sb docs\` commands to make sure the code you're writing is correct and uses the framework as intended. + +### Available Commands + +- \`sb docs list\` - Discover what documentation is available +- \`sb docs get
\` - Fetch documentation for a specific topic +- \`sb docs validate \` - Check if your code follows Springboard patterns +- \`sb docs scaffold \` - Generate boilerplate for new modules +- \`sb docs context\` - Get comprehensive framework information + +### Recommended Workflow + +1. Start by running \`sb docs context\` to understand the framework +2. Use \`sb docs list\` to find relevant documentation for your task +3. Validate your code with \`sb docs validate \` before completing +4. Generate new modules with \`sb docs scaffold\` for consistent structure + +These tools help prevent common mistakes and ensure your code aligns with Springboard's architecture. +`; + writeFileSync(`${process.cwd()}/AGENTS.md`, agentsMdContent); + console.log('Created AGENTS.md with AI agent instructions'); + const packageJsonPath = `${process.cwd()}/package.json`; const packageJson = JSON.parse(readFileSync(packageJsonPath).toString()); packageJson.scripts = { From 9ad16fb21dc08a625db42750120435444cfaee54 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 19:19:53 +0000 Subject: [PATCH 06/20] Improve `sb docs` onboarding following svelte-mcp patterns MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Changes based on analysis of svelte-mcp's approach: 1. Add helpful text to `sb docs --help`: - Guides AI agents to run `sb docs context` first - Explains that context includes full docs list - Shows recommended workflow: context → validate → get 2. Update CLAUDE.md and AGENTS.md: - Emphasize `sb docs context` as the single starting point - Explain that context includes everything (framework info + docs list) - Clarify that `list` is redundant if you've run `context` - Simplify workflow to match svelte-mcp pattern Following svelte-mcp's pattern where the prompt pre-loads all available docs and explicitly tells agents "you do not need to call list-sections again." Co-Authored-By: Claude Opus 4.5 --- packages/springboard/cli/src/docs_command.ts | 14 ++++ .../create-springboard-app/src/cli.ts | 68 +++++++++++-------- 2 files changed, 54 insertions(+), 28 deletions(-) diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts index 5ce5acb2..243e3fb9 100644 --- a/packages/springboard/cli/src/docs_command.ts +++ b/packages/springboard/cli/src/docs_command.ts @@ -9,6 +9,20 @@ import { Command } from 'commander'; export function createDocsCommand(): Command { const docs = new Command('docs') .description('Documentation and AI agent support tools') + .addHelpText('after', ` +Getting Started: + For AI agents: Run 'sb docs context' first to get comprehensive framework + information and available documentation sections. This provides everything + you need to start working with Springboard. + + The 'context' command includes the full list of available docs, so you + don't need to run 'list' separately. + +Workflow: + 1. sb docs context # Get full framework context (run this first) + 2. sb docs validate # Check your code follows Springboard patterns + 3. sb docs get
# Fetch specific docs only when needed +`) .action(() => { // When `sb docs` is called without subcommand, show help docs.help(); diff --git a/packages/springboard/create-springboard-app/src/cli.ts b/packages/springboard/create-springboard-app/src/cli.ts index 0c5a18f1..8c638e67 100644 --- a/packages/springboard/create-springboard-app/src/cli.ts +++ b/packages/springboard/create-springboard-app/src/cli.ts @@ -74,30 +74,38 @@ program This application is built with the **Springboard framework**. -Before writing any code, run: +## Getting Started + +**Before writing any code, run:** \`\`\`bash -npx sb docs --help +npx sb docs context \`\`\` -This will show you available documentation commands that help ensure your code is correct and uses the framework as intended. +This command outputs comprehensive framework information including all available +documentation sections. It's the single command you need to start working efficiently. + +## Recommended Workflow + +1. **Run \`sb docs context\`** at the start of your session + - This gives you everything: framework overview, available docs, and workflow guidance + - You don't need to run \`sb docs list\` separately + +2. **Write code** using your knowledge + the context from step 1 -## Key Commands +3. **Run \`sb docs validate \`** to check your code + - Fix any issues reported + - Re-run validate until clean -- \`sb docs list\` - List available documentation sections -- \`sb docs get
\` - Fetch specific documentation -- \`sb docs validate \` - Validate your module code -- \`sb docs scaffold \` - Generate module templates -- \`sb docs context\` - Get full framework context +4. **Fetch specific docs** only when needed: + - \`sb docs get
\` - Use section names from the context output -## Workflow +## Other Useful Commands -1. Run \`sb docs context\` at the start of your session -2. Use \`sb docs list\` to find relevant documentation -3. Always run \`sb docs validate \` before finishing work -4. Use \`sb docs scaffold\` to generate new modules +- \`sb docs --help\` - See all available commands +- \`sb docs types\` - Get TypeScript type definitions -This ensures your code follows Springboard patterns and avoids common mistakes. +This approach ensures your code follows Springboard patterns and avoids common mistakes. `; writeFileSync(`${process.cwd()}/CLAUDE.md`, claudeMdContent); console.log('Created CLAUDE.md with AI agent instructions'); @@ -109,30 +117,34 @@ This application is created with the **Springboard framework**. ## Before You Start -Run this command to learn about available documentation tools: +Run this command to get comprehensive framework information: \`\`\`bash -npx sb docs --help +npx sb docs context \`\`\` +This single command provides: +- Framework overview and key concepts +- Full list of available documentation sections with use cases +- Recommended workflow for AI agents +- Everything you need to start coding + ## Important Please lean on the \`sb docs\` commands to make sure the code you're writing is correct and uses the framework as intended. -### Available Commands +### Recommended Workflow -- \`sb docs list\` - Discover what documentation is available -- \`sb docs get
\` - Fetch documentation for a specific topic -- \`sb docs validate \` - Check if your code follows Springboard patterns -- \`sb docs scaffold \` - Generate boilerplate for new modules -- \`sb docs context\` - Get comprehensive framework information +1. **Start with context**: Run \`sb docs context\` (includes full docs list) +2. **Write code**: Use your knowledge + the context from step 1 +3. **Validate often**: Run \`sb docs validate \` to catch issues early +4. **Fetch docs when needed**: Use \`sb docs get
\` for specific topics -### Recommended Workflow +### Other Commands -1. Start by running \`sb docs context\` to understand the framework -2. Use \`sb docs list\` to find relevant documentation for your task -3. Validate your code with \`sb docs validate \` before completing -4. Generate new modules with \`sb docs scaffold\` for consistent structure +- \`sb docs --help\` - See all available commands +- \`sb docs types\` - Get TypeScript type definitions +- \`sb docs list\` - Just the docs list (context includes this already) These tools help prevent common mistakes and ensure your code aligns with Springboard's architecture. `; From f71ae82d0dd322cae9b38f7e12a3097d3025b894 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 19:57:35 +0000 Subject: [PATCH 07/20] Add example modules system and comparison document Creates bundled examples system similar to svelte-mcp's approach: 1. Add `sb docs examples` commands: - `sb docs examples list` - List all available examples - `sb docs examples show ` - Display full code for an example 2. Create three example modules: - basic-feature-module: Shared state + actions + routes - persistent-state-module: Database-backed state - user-agent-state-module: localStorage-backed UI state 3. Examples are stored as .txt files and bundled in npm package - Copied to dist/examples/ during build - Read at runtime via fs.readFileSync - Categorized by type (state, actions, routing, patterns) - Tagged for discoverability 4. Add comparison document (.planning/sb-docs-vs-svelte-mcp.md): - Documents what svelte-mcp has vs what we have - Key differences: MCP vs CLI, playground-link vs examples - Missing features: live docs fetching, use_cases metadata - Architecture decisions needed This follows svelte-mcp's pattern of providing concrete examples, though via bundled files instead of playground links (no playground equivalent for Springboard yet). Co-Authored-By: Claude Opus 4.5 --- .planning/sb-docs-vs-svelte-mcp.md | 85 +++++++++++ packages/springboard/cli/package.json | 3 +- packages/springboard/cli/src/docs_command.ts | 47 ++++++ .../cli/src/examples/basic-feature-module.txt | 95 +++++++++++++ .../springboard/cli/src/examples/index.ts | 75 ++++++++++ .../src/examples/persistent-state-module.txt | 124 ++++++++++++++++ .../src/examples/user-agent-state-module.txt | 134 ++++++++++++++++++ 7 files changed, 562 insertions(+), 1 deletion(-) create mode 100644 .planning/sb-docs-vs-svelte-mcp.md create mode 100644 packages/springboard/cli/src/examples/basic-feature-module.txt create mode 100644 packages/springboard/cli/src/examples/index.ts create mode 100644 packages/springboard/cli/src/examples/persistent-state-module.txt create mode 100644 packages/springboard/cli/src/examples/user-agent-state-module.txt diff --git a/.planning/sb-docs-vs-svelte-mcp.md b/.planning/sb-docs-vs-svelte-mcp.md new file mode 100644 index 00000000..03bce577 --- /dev/null +++ b/.planning/sb-docs-vs-svelte-mcp.md @@ -0,0 +1,85 @@ +# sb docs vs svelte-mcp Command Comparison + +## What svelte-mcp Has (MCP Tools) + +| Tool | Purpose | Status in sb docs | +|------|---------|-------------------| +| `list-sections` | List docs with use_cases | ✅ `sb docs list` | +| `get-documentation` | Fetch specific docs | ✅ `sb docs get` | +| `svelte-autofixer` | Validate component code | ✅ `sb docs validate` | +| `playground-link` | Generate svelte.dev playground URL | ❌ Not implemented | +| **Prompt: `svelte-task`** | Pre-load context + workflow | ✅ `sb docs context` | + +## What sb docs Has (CLI Commands) + +| Command | Purpose | Status in svelte-mcp | +|---------|---------|---------------------| +| `sb docs list` | List docs with use_cases | ✅ `list-sections` tool | +| `sb docs get` | Fetch specific docs | ✅ `get-documentation` tool | +| `sb docs validate` | Validate module code | ✅ `svelte-autofixer` tool | +| `sb docs context` | Full context prompt | ✅ `svelte-task` prompt | +| `sb docs types` | Output TypeScript types | ❌ Not in svelte-mcp | +| `sb docs scaffold` | Generate templates | ❌ Not in svelte-mcp | +| `sb docs --help` | Onboarding/workflow | ❌ Not in svelte-mcp (implicit in prompts) | + +## Key Differences + +### 1. Delivery Mechanism +- **svelte-mcp**: MCP protocol (tools + prompts) +- **sb docs**: CLI commands (shell execution) + +### 2. Scaffold/Templates +- **svelte-mcp**: No scaffolding - uses `playground-link` to share code +- **sb docs**: Has `scaffold` subcommands (diverges from svelte-mcp) + +### 3. Type Definitions +- **svelte-mcp**: No dedicated types command +- **sb docs**: `types` command to output ModuleAPI, StateSupervisor, etc. + +### 4. Onboarding +- **svelte-mcp**: Implicit in prompts (agents get workflow via `svelte-task` prompt) +- **sb docs**: Explicit via `--help` text + CLAUDE.md/AGENTS.md files + +### 5. Playground/Examples +- **svelte-mcp**: `playground-link` generates URLs to svelte.dev/playground + - Compresses code with gzip + base64 + - Embeds in MCP UI resources + - Requires `App.svelte` entry point +- **sb docs**: ❌ Not implemented yet + +## Missing from sb docs + +1. **Playground/Examples System** ❌ + - svelte-mcp has `playground-link` tool + - We should add example bundling in npm package + +2. **Live Documentation Fetching** ❌ + - svelte-mcp fetches from svelte.dev at runtime + - We need to decide: bundle in package or fetch from doks site + +3. **Use Cases Metadata** ❌ + - svelte-mcp has pre-computed `use_cases.json` + - We need to generate this from springboard docs + +4. **Actual Implementation** ❌ + - All commands currently return "TODO" + - Need to implement each command + +## Recommendations + +### Must Have (Match svelte-mcp) +1. ✅ Remove `scaffold` - doesn't match svelte-mcp pattern +2. ❌ Add examples system (bundled in npm package) +3. ❌ Implement `list` with use_cases metadata +4. ❌ Implement `get` to fetch/bundle docs +5. ❌ Implement `validate` with AST visitors +6. ❌ Implement `context` with pre-loaded docs list + +### Optional (Extensions) +- Keep `types` command (useful, not in svelte-mcp) +- Keep `scaffold` if valuable for Springboard (diverges) + +### Architecture Decisions Needed +1. **Docs source**: Bundle in package or fetch at runtime? +2. **Examples**: In-package files or external URLs? +3. **Scaffold**: Remove or keep as Springboard-specific feature? diff --git a/packages/springboard/cli/package.json b/packages/springboard/cli/package.json index 4f1cf57d..72eb7549 100644 --- a/packages/springboard/cli/package.json +++ b/packages/springboard/cli/package.json @@ -12,7 +12,8 @@ ], "scripts": { "build": "npm run clean && npm run build-cli", - "build-cli": "tsc && npm run add-header", + "build-cli": "tsc && npm run copy-examples && npm run add-header", + "copy-examples": "mkdir -p dist/examples && cp src/examples/*.txt dist/examples/", "prepublishOnly": "npm run build", "dev:setup": "[ -f ./dist/cli.js ] || npm run build", "build-saas": "DISABLE_IO=true npx tsx src/cli.ts build ../../../apps/jamtools/modules/index.ts", diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts index 243e3fb9..21bc3204 100644 --- a/packages/springboard/cli/src/docs_command.ts +++ b/packages/springboard/cli/src/docs_command.ts @@ -104,5 +104,52 @@ Workflow: // Will output ModuleAPI, StateSupervisor, CoreDependencies, etc. }); + // sb docs examples + const examplesCmd = docs.command('examples') + .description('View example modules'); + + examplesCmd.command('list') + .description('List all available examples') + .option('--json', 'Output as JSON') + .action(async (options: { json?: boolean }) => { + const { listExamples } = await import('./examples/index.js'); + const examplesList = listExamples(); + + if (options.json) { + console.log(JSON.stringify(examplesList, null, 2)); + } else { + console.log('Available Springboard Examples:\n'); + for (const ex of examplesList) { + console.log(`${ex.name}`); + console.log(` ${ex.title}`); + console.log(` ${ex.description}`); + console.log(` Category: ${ex.category}`); + console.log(` Tags: ${ex.tags.join(', ')}`); + console.log(); + } + } + }); + + examplesCmd.command('show') + .description('Show code for a specific example') + .argument('', 'Example name') + .action(async (name: string) => { + const { getExample } = await import('./examples/index.js'); + const example = getExample(name); + + if (!example) { + console.error(`Example "${name}" not found. Run 'sb docs examples list' to see available examples.`); + process.exit(1); + } + + console.log(`# ${example.title}\n`); + console.log(`${example.description}\n`); + console.log(`Category: ${example.category}`); + console.log(`Tags: ${example.tags.join(', ')}\n`); + console.log('```tsx'); + console.log(example.code); + console.log('```'); + }); + return docs; } diff --git a/packages/springboard/cli/src/examples/basic-feature-module.txt b/packages/springboard/cli/src/examples/basic-feature-module.txt new file mode 100644 index 00000000..e4d7fb24 --- /dev/null +++ b/packages/springboard/cli/src/examples/basic-feature-module.txt @@ -0,0 +1,95 @@ +/** + * Example: Basic Feature Module + * + * This example demonstrates a simple feature module with: + * - Shared state (synced across devices) + * - Actions (RPC-enabled functions) + * - Routes (UI pages) + * - Lifecycle cleanup + */ + +import springboard from 'springboard'; + +springboard.registerModule( + 'BasicFeatureExample', + {}, + async (moduleAPI) => { + // 1. Create shared state (synced across all connected devices) + const counterState = await moduleAPI.statesAPI.createSharedState( + 'counter', + 0 + ); + + // 2. Create actions (automatically RPC-enabled) + const actions = moduleAPI.createActions({ + increment: async () => { + const current = counterState.getState(); + counterState.setState(current + 1); + return { newValue: current + 1 }; + }, + + decrement: async () => { + const current = counterState.getState(); + counterState.setState(current - 1); + return { newValue: current - 1 }; + }, + + reset: async () => { + counterState.setState(0); + return { newValue: 0 }; + }, + }); + + // 3. Register routes + moduleAPI.registerRoute( + '/', + {}, + () => { + const counter = counterState.useState(); + + return ( +
+

Basic Feature Example

+

Counter: {counter}

+ + + +
+ ); + } + ); + + // 4. Lifecycle cleanup (if needed) + moduleAPI.onDestroy(() => { + console.log('BasicFeatureExample module destroyed'); + }); + + // 5. Return public API for other modules + return { + states: { counter: counterState }, + actions, + }; + } +); + +// Type-safe module registry +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + BasicFeatureExample: { + states: { + counter: ReturnType>; + }; + actions: { + increment: () => Promise<{ newValue: number }>; + decrement: () => Promise<{ newValue: number }>; + reset: () => Promise<{ newValue: number }>; + }; + }; + } +} diff --git a/packages/springboard/cli/src/examples/index.ts b/packages/springboard/cli/src/examples/index.ts new file mode 100644 index 00000000..2c07b6c1 --- /dev/null +++ b/packages/springboard/cli/src/examples/index.ts @@ -0,0 +1,75 @@ +/** + * Springboard Example Modules + * + * These examples are bundled with the CLI package to help AI agents + * understand common patterns in Springboard development. + */ + +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export interface Example { + name: string; + title: string; + description: string; + category: 'state' | 'actions' | 'routing' | 'patterns'; + tags: string[]; + code: string; +} + +const examplesDir = __dirname; + +export const examples: Example[] = [ + { + name: 'basic-feature-module', + title: 'Basic Feature Module', + description: 'Simple feature module with shared state, actions, and routes. Good starting point for most features.', + category: 'patterns', + tags: ['shared-state', 'actions', 'routes', 'beginner'], + code: readFileSync(join(examplesDir, 'basic-feature-module.txt'), 'utf-8'), + }, + { + name: 'persistent-state-module', + title: 'Persistent State Module', + description: 'Module using persistent state stored in database. Data survives app restarts and syncs across devices.', + category: 'state', + tags: ['persistent-state', 'database', 'settings'], + code: readFileSync(join(examplesDir, 'persistent-state-module.txt'), 'utf-8'), + }, + { + name: 'user-agent-state-module', + title: 'User Agent State Module', + description: 'Module using localStorage-backed state. Perfect for device-specific UI preferences.', + category: 'state', + tags: ['user-agent-state', 'localStorage', 'ui-state'], + code: readFileSync(join(examplesDir, 'user-agent-state-module.txt'), 'utf-8'), + }, +]; + +/** + * Get an example by name + */ +export function getExample(name: string): Example | undefined { + return examples.find((ex) => ex.name === name); +} + +/** + * Get examples by category + */ +export function getExamplesByCategory(category: Example['category']): Example[] { + return examples.filter((ex) => ex.category === category); +} + +/** + * Get examples by tag + */ +export function getExamplesByTag(tag: string): Example[] { + return examples.filter((ex) => ex.tags.includes(tag)); +} + +/** + * List all examples with metadata (no code) + */ +export function listExamples(): Omit[] { + return examples.map(({ code, ...metadata }) => metadata); +} diff --git a/packages/springboard/cli/src/examples/persistent-state-module.txt b/packages/springboard/cli/src/examples/persistent-state-module.txt new file mode 100644 index 00000000..98729ff6 --- /dev/null +++ b/packages/springboard/cli/src/examples/persistent-state-module.txt @@ -0,0 +1,124 @@ +/** + * Example: Persistent State Module + * + * This example demonstrates using persistent state that is: + * - Stored in the database + * - Survives app restarts + * - Synced across devices + */ + +import springboard from 'springboard'; + +interface UserSettings { + theme: 'light' | 'dark'; + notifications: boolean; + language: string; +} + +springboard.registerModule( + 'PersistentStateExample', + {}, + async (moduleAPI) => { + // Persistent state - stored in database + const settingsState = await moduleAPI.statesAPI.createPersistentState( + 'userSettings', + { + theme: 'light', + notifications: true, + language: 'en', + } + ); + + // Actions to modify settings + const actions = moduleAPI.createActions({ + toggleTheme: async () => { + settingsState.setStateImmer((draft) => { + draft.theme = draft.theme === 'light' ? 'dark' : 'light'; + }); + }, + + toggleNotifications: async () => { + settingsState.setStateImmer((draft) => { + draft.notifications = !draft.notifications; + }); + }, + + setLanguage: async (args: { language: string }) => { + settingsState.setStateImmer((draft) => { + draft.language = args.language; + }); + }, + }); + + // Register UI + moduleAPI.registerRoute( + '/settings', + {}, + () => { + const settings = settingsState.useState(); + + return ( +
+

User Settings

+ +
+ +
+ +
+ +
+ +
+ +
+
+ ); + } + ); + + return { + states: { settings: settingsState }, + actions, + }; + } +); + +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + PersistentStateExample: { + states: { + settings: ReturnType>; + }; + actions: { + toggleTheme: () => Promise; + toggleNotifications: () => Promise; + setLanguage: (args: { language: string }) => Promise; + }; + }; + } +} diff --git a/packages/springboard/cli/src/examples/user-agent-state-module.txt b/packages/springboard/cli/src/examples/user-agent-state-module.txt new file mode 100644 index 00000000..afe0b009 --- /dev/null +++ b/packages/springboard/cli/src/examples/user-agent-state-module.txt @@ -0,0 +1,134 @@ +/** + * Example: User Agent State Module + * + * This example demonstrates local state that is: + * - Stored only in localStorage (not synced) + * - Device-specific + * - Perfect for UI preferences, local-only data + */ + +import springboard from 'springboard'; + +interface LocalUIState { + sidebarCollapsed: boolean; + lastViewedPage: string; + recentSearches: string[]; +} + +springboard.registerModule( + 'UserAgentStateExample', + {}, + async (moduleAPI) => { + // User agent state - stored locally in localStorage + const uiState = await moduleAPI.statesAPI.createUserAgentState( + 'localUIState', + { + sidebarCollapsed: false, + lastViewedPage: '/', + recentSearches: [], + } + ); + + // Actions for local UI state + const actions = moduleAPI.createActions({ + toggleSidebar: async () => { + uiState.setStateImmer((draft) => { + draft.sidebarCollapsed = !draft.sidebarCollapsed; + }); + }, + + recordPageView: async (args: { page: string }) => { + uiState.setStateImmer((draft) => { + draft.lastViewedPage = args.page; + }); + }, + + addSearch: async (args: { query: string }) => { + uiState.setStateImmer((draft) => { + // Keep only last 10 searches + draft.recentSearches = [ + args.query, + ...draft.recentSearches.filter((q) => q !== args.query), + ].slice(0, 10); + }); + }, + + clearSearchHistory: async () => { + uiState.setStateImmer((draft) => { + draft.recentSearches = []; + }); + }, + }); + + // Register UI + moduleAPI.registerRoute( + '/local-ui', + {}, + () => { + const ui = uiState.useState(); + + return ( +
+ {/* Sidebar */} + {!ui.sidebarCollapsed && ( + + )} + + {/* Main content */} +
+

User Agent State Example

+

Last viewed: {ui.lastViewedPage}

+ + +
+ { + if (e.key === 'Enter' && e.currentTarget.value) { + actions.addSearch({ query: e.currentTarget.value }); + e.currentTarget.value = ''; + } + }} + /> +
+
+
+ ); + } + ); + + return { + states: { ui: uiState }, + actions, + }; + } +); + +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + UserAgentStateExample: { + states: { + ui: ReturnType>; + }; + actions: { + toggleSidebar: () => Promise; + recordPageView: (args: { page: string }) => Promise; + addSearch: (args: { query: string }) => Promise; + clearSearchHistory: () => Promise; + }; + }; + } +} From 7d9659ffed55f7ae12571894cf357f0b9458e254 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Sat, 7 Feb 2026 21:55:05 +0000 Subject: [PATCH 08/20] Add questionnaire for sb docs command implementation Created comprehensive questionnaire based on deep research of: - Springboard core APIs (ModuleAPI, StatesAPI, Actions, Routing, etc.) - Real-world usage in songdrive (19+ modules, patterns, anti-patterns) Questionnaire covers 25 questions across: - General architecture (doc source, format, use_cases metadata) - Command-specific decisions for each sb docs command - Validation patterns to detect (from songdrive analysis) - Implementation priority and MVP scope - Integration with CLAUDE.md/AGENTS.md Key decisions needed: - Documentation source: Bundle vs Runtime vs Hosted - Use cases generation: Claude Batch vs Manual vs Skip - Validation scope: Which patterns to detect - Scaffold: Keep vs Remove vs Replace with examples - MVP: Which commands are minimum viable Co-Authored-By: Claude Opus 4.5 --- .planning/sb-docs-questionnaire.md | 487 +++++++++++++++++++++++++++++ 1 file changed, 487 insertions(+) create mode 100644 .planning/sb-docs-questionnaire.md diff --git a/.planning/sb-docs-questionnaire.md b/.planning/sb-docs-questionnaire.md new file mode 100644 index 00000000..bce0b9dc --- /dev/null +++ b/.planning/sb-docs-questionnaire.md @@ -0,0 +1,487 @@ +# Springboard `sb docs` Implementation Questionnaire + +This document contains questions that need answers to implement each `sb docs` command. Please fill in the answers to guide implementation. + +--- + +## General Architecture Questions + +### Q1: Documentation Source Location + +Where should the documentation content come from? + +**Options:** +- [ ] A. Bundle markdown files directly in the CLI npm package (like examples) +- [ ] B. Read from `/doks/content/docs/` at runtime (requires doks to be present) +- [ ] C. Host documentation at a URL (e.g., `docs.springboard.dev/llms.txt`) and fetch at runtime +- [ ] D. Other: _______________ + +**Your Answer:** + +--- + +### Q2: Documentation Format + +What format should docs be in for AI consumption? + +**Options:** +- [ ] A. Raw markdown files from doks (as-is) +- [ ] B. LLM-optimized condensed format (like svelte-mcp's `/llms.txt`) +- [ ] C. Both - raw for humans, condensed for `sb docs get` +- [ ] D. Other: _______________ + +**Your Answer:** + +--- + +### Q3: Use Cases Metadata + +svelte-mcp pre-generates "use_cases" keywords for each doc section using Claude Batch API. Should we do the same? + +**Options:** +- [ ] A. Yes, generate `use_cases.json` via Claude Batch API +- [ ] B. Manually write use cases for each section +- [ ] C. Skip use cases, just use doc titles +- [ ] D. Use categories/tags instead of free-form keywords + +**Your Answer:** + +--- + +## Command-Specific Questions + +--- + +## `sb docs list` Command + +### Q4: What documentation sections should be listed? + +Based on the doks content, I found these potential sections: + +**Springboard Core:** +- [ ] `springboard/overview` - Framework introduction +- [ ] `springboard/module-development` - Module types (feature/utility/initializer) +- [ ] `springboard/state-management` - createSharedState, createPersistentState, createUserAgentState +- [ ] `springboard/actions` - createAction, createActions, RPC behavior +- [ ] `springboard/routing` - registerRoute, documentMeta, hideApplicationShell +- [ ] `springboard/lifecycle` - onDestroy, initialization order +- [ ] `springboard/module-communication` - getModule, interface merging +- [ ] `springboard/core-dependencies` - log, showError, files, storage, rpc, isMaestro +- [ ] `springboard/platforms` - browser, node, react-native, desktop, partykit +- [ ] `springboard/conditional-compilation` - @platform directives +- [ ] `springboard/server-modules` - serverRegistry, Hono routes, RPC middleware + +**Guides:** +- [ ] `guides/registering-ui-routes` - Route registration patterns +- [ ] `guides/creating-feature-module` - Feature module walkthrough +- [ ] `guides/creating-utility-module` - Utility module walkthrough +- [ ] `guides/multi-module-apps` - Module dependencies and communication +- [ ] `guides/offline-first` - Offline mode patterns +- [ ] `guides/multi-workspace` - Workspace context patterns + +**CLI:** +- [ ] `cli/commands` - sb dev, sb build, sb start +- [ ] `cli/plugins` - Plugin system + +**JamTools (if applicable):** +- [ ] `jamtools/overview` - MIDI/IO extensions +- [ ] `jamtools/macros` - Macro system + +**Which sections should be included?** (Check all that apply or add more) + +**Your Answer:** + +--- + +### Q5: Section Metadata Format + +What metadata should each section have in `sb docs list`? + +``` +Option A (svelte-mcp style): +springboard/state-management + Use cases: shared state, persistent state, user agent state, createSharedState, cross-device sync... + +Option B (Category-based): +springboard/state-management + Category: core + Tags: state, sync, persistence + +Option C (Minimal): +springboard/state-management - State management with createSharedState, createPersistentState, createUserAgentState +``` + +**Your Answer:** + +--- + +## `sb docs get` Command + +### Q6: Documentation Fetching Behavior + +When `sb docs get springboard/state-management` is called: + +**Options:** +- [ ] A. Output raw markdown file content +- [ ] B. Output condensed LLM-optimized version +- [ ] C. Output with examples included inline +- [ ] D. Other: _______________ + +**Your Answer:** + +--- + +### Q7: Multiple Section Fetching + +Should `sb docs get` support fetching multiple sections at once? + +```bash +# Option A: Multiple arguments +sb docs get springboard/state-management springboard/actions + +# Option B: Single section only +sb docs get springboard/state-management + +# Option C: Category/tag based +sb docs get --category=core +``` + +**Your Answer:** + +--- + +## `sb docs context` Command + +### Q8: Context Prompt Content + +What should `sb docs context` include? + +**Options (check all that apply):** +- [ ] Framework overview (what is Springboard) +- [ ] Full list of available doc sections with use cases +- [ ] Key APIs summary (ModuleAPI methods) +- [ ] Common patterns and anti-patterns +- [ ] Workflow instructions (when to use each command) +- [ ] TypeScript type definitions +- [ ] Example code snippets +- [ ] Link to examples command + +**Your Answer:** + +--- + +### Q9: Context Length + +How long should the context output be? + +**Options:** +- [ ] A. Minimal (~500 tokens) - just workflow + doc list +- [ ] B. Medium (~2000 tokens) - workflow + doc list + key APIs +- [ ] C. Comprehensive (~5000 tokens) - everything including examples +- [ ] D. Configurable via flag (e.g., `--full`, `--minimal`) + +**Your Answer:** + +--- + +## `sb docs validate` Command + +### Q10: Validation Scope + +What should `sb docs validate` check for? + +**From songdrive analysis, common patterns/issues:** + +**Module Structure:** +- [ ] Module has return type matching AllModules interface declaration +- [ ] Module uses proper state creation methods +- [ ] Module registers cleanup via onDestroy when needed + +**State Patterns:** +- [ ] Using setState/setStateImmer correctly (not direct mutation) +- [ ] Correct state type chosen (shared vs persistent vs userAgent) +- [ ] State namespacing follows conventions + +**Actions:** +- [ ] Actions are async functions +- [ ] Actions properly handle errors +- [ ] Actions have proper TypeScript types + +**Routing:** +- [ ] Routes don't conflict +- [ ] Routes have proper components + +**Module Communication:** +- [ ] getModule called inside routes/actions (not at module level) +- [ ] Optional chaining used for potentially undefined modules + +**Anti-patterns:** +- [ ] Computed values stored in state (should use useMemo) +- [ ] Race conditions in initialization + +**Which checks should be implemented?** (Prioritize) + +**Your Answer:** + +--- + +### Q11: Validation Output Format + +How should validation results be formatted? + +**Options:** +- [ ] A. JSON only (for programmatic use) +- [ ] B. Human-readable by default, JSON with `--json` flag +- [ ] C. ESLint-style output (file:line:col message) + +**Your Answer:** + +--- + +### Q12: Validation Strictness + +Should validation have severity levels? + +``` +Option A: Binary (issues = errors, suggestions = warnings) +{ + "issues": ["Line 15: getModule called at module level"], + "suggestions": ["Line 8: Consider using setStateImmer for complex updates"], + "hasErrors": true +} + +Option B: Severity levels +{ + "errors": [...], + "warnings": [...], + "info": [...], + "hasErrors": true +} +``` + +**Your Answer:** + +--- + +## `sb docs types` Command + +### Q13: Which Types to Output + +What TypeScript definitions should `sb docs types` output? + +**Options (check all that apply):** +- [ ] ModuleAPI interface +- [ ] StatesAPI interface +- [ ] StateSupervisor interface +- [ ] CoreDependencies interface +- [ ] ModuleDependencies interface +- [ ] RegisterRouteOptions type +- [ ] ActionCallOptions type +- [ ] KVStore interface +- [ ] Rpc interface +- [ ] AllModules declaration pattern +- [ ] ServerModuleAPI interface + +**Your Answer:** + +--- + +### Q14: Types Format + +How should types be output? + +**Options:** +- [ ] A. Single concatenated TypeScript file +- [ ] B. Structured with headers/comments explaining each type +- [ ] C. Just the interface definitions (no explanations) +- [ ] D. Interactive (show list, pick which to output) + +**Your Answer:** + +--- + +## `sb docs scaffold` Command + +### Q15: Should Scaffold Remain? + +svelte-mcp does NOT have scaffolding. Should we keep it? + +**Options:** +- [ ] A. Remove scaffold - follow svelte-mcp pattern exactly +- [ ] B. Keep scaffold - useful for Springboard's module structure +- [ ] C. Replace with examples only - agents copy from examples +- [ ] D. Keep scaffold but simplify to one command + +**Your Answer:** + +--- + +### Q16: Scaffold Templates (if keeping) + +If keeping scaffold, what templates should be available? + +**Options (check all that apply):** +- [ ] `sb docs scaffold module ` - Basic module +- [ ] `sb docs scaffold feature ` - Feature module with routes +- [ ] `sb docs scaffold utility ` - Utility module with exports +- [ ] `sb docs scaffold initializer ` - Initializer module +- [ ] `sb docs scaffold server ` - Server module + +**Your Answer:** + +--- + +## `sb docs examples` Command + +### Q17: Example Categories + +What example categories are most useful? + +**Current examples:** +- [x] basic-feature-module (shared state + actions + routes) +- [x] persistent-state-module (database-backed state) +- [x] user-agent-state-module (localStorage state) + +**Additional examples to add:** +- [ ] utility-module (exports for other modules) +- [ ] server-module (Hono routes + RPC middleware) +- [ ] module-communication (getModule patterns) +- [ ] modal-integration (using modals module) +- [ ] navigation-patterns (programmatic navigation) +- [ ] offline-first-module (offline mode handling) +- [ ] workspace-aware-module (multi-tenant patterns) +- [ ] platform-specific-module (@platform directives) + +**Which examples should be included?** + +**Your Answer:** + +--- + +## Implementation Priority + +### Q18: Command Priority + +In what order should commands be implemented? + +**Rank 1-7 (1 = highest priority):** +- [ ] `sb docs context` - Primary entry point for agents +- [ ] `sb docs list` - Documentation discovery +- [ ] `sb docs get` - Documentation fetching +- [ ] `sb docs validate` - Code validation +- [ ] `sb docs types` - TypeScript definitions +- [ ] `sb docs examples` - (Already implemented) +- [ ] `sb docs scaffold` - Template generation + +**Your Answer:** + +--- + +### Q19: MVP vs Full Implementation + +What's the minimum viable implementation? + +**Options:** +- [ ] A. Just `context` + `examples` (agents use examples as templates) +- [ ] B. `context` + `list` + `get` (documentation access) +- [ ] C. `context` + `list` + `get` + `validate` (full workflow) +- [ ] D. Everything (all commands fully implemented) + +**Your Answer:** + +--- + +## Integration Questions + +### Q20: CLAUDE.md / AGENTS.md Updates + +Should the generated CLAUDE.md/AGENTS.md files be updated to match what commands are actually implemented? + +**Current content tells agents to:** +1. Run `sb docs context` +2. Use `sb docs validate` +3. Use `sb docs get` +4. Use `sb docs scaffold` + +**Should we update these files as we implement each command?** + +**Your Answer:** + +--- + +### Q21: Documentation Hosting + +If documentation is bundled in the npm package: + +**Questions:** +1. Maximum acceptable package size? _____ MB +2. Should docs be compressed? Yes / No +3. Should docs auto-update somehow? Yes / No / How: _____ + +**Your Answer:** + +--- + +### Q22: Versioning Strategy + +How should documentation versioning work? + +**Options:** +- [ ] A. Docs version matches CLI version (bundled) +- [ ] B. Docs fetched from latest (always current) +- [ ] C. Docs version configurable via flag +- [ ] D. Not concerned about versioning + +**Your Answer:** + +--- + +## Additional Questions + +### Q23: Anything Missing? + +Are there other commands or features that should be added? + +**Suggestions from songdrive patterns:** +- `sb docs patterns` - Common patterns (navigation reasons, modal system) +- `sb docs anti-patterns` - Things to avoid +- `sb docs migrate` - Migration helpers between versions +- `sb docs debug` - Debugging tips + +**Your Answer:** + +--- + +### Q24: CLI Name Preference + +Is `sb docs` the right namespace? + +**Options:** +- [ ] A. `sb docs` (current) +- [ ] B. `sb ai` (explicit AI agent focus) +- [ ] C. `sb help` (general help system) +- [ ] D. `sb llm` (explicit LLM focus) +- [ ] E. Other: _______________ + +**Your Answer:** + +--- + +### Q25: Additional Context + +Anything else I should know about how you want this to work? + +**Your Answer:** + +--- + +## Summary of Key Decisions Needed + +| Question | Topic | Options | +|----------|-------|---------| +| Q1 | Doc source | Bundle / Runtime / Hosted | +| Q3 | Use cases | Claude Batch / Manual / Skip | +| Q10 | Validation checks | Which patterns to detect | +| Q15 | Scaffold | Keep / Remove / Replace | +| Q18 | Priority | Implementation order | +| Q19 | MVP scope | Minimum viable set | From c925f813002f04293a5a9cf245d719afabb26523 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 9 Feb 2026 03:57:25 +0000 Subject: [PATCH 09/20] Implement sb docs commands with bundled documentation Add full implementation of sb docs CLI commands for AI agent support: - sb docs list: Lists 13 documentation sections with use_cases keywords - sb docs get
: Fetches specific documentation content - sb docs context: Outputs comprehensive context prompt for AI agents - sb docs types: Shows TypeScript type definitions - sb docs validate: Placeholder for future AST-based validation - sb docs scaffold: Redirects to examples system Create LLM-optimized documentation covering: - Module API, state management, actions, routing - Module types and communication patterns - Core dependencies and lifecycle - Platform support and server modules - Common patterns and anti-patterns Add build scripts to copy docs to dist during npm build. Co-Authored-By: Claude Opus 4.5 --- packages/springboard/cli/package.json | 3 +- .../src/docs/content/springboard-actions.md | 174 ++++++++++ .../docs/content/springboard-anti-patterns.md | 250 +++++++++++++++ .../content/springboard-core-dependencies.md | 170 ++++++++++ .../src/docs/content/springboard-lifecycle.md | 188 +++++++++++ .../docs/content/springboard-module-api.md | 132 ++++++++ .../springboard-module-communication.md | 163 ++++++++++ .../docs/content/springboard-module-types.md | 195 ++++++++++++ .../src/docs/content/springboard-overview.md | 62 ++++ .../src/docs/content/springboard-patterns.md | 300 ++++++++++++++++++ .../src/docs/content/springboard-platforms.md | 217 +++++++++++++ .../src/docs/content/springboard-routing.md | 191 +++++++++++ .../content/springboard-server-modules.md | 241 ++++++++++++++ .../content/springboard-state-management.md | 143 +++++++++ packages/springboard/cli/src/docs/index.ts | 80 +++++ .../springboard/cli/src/docs/sections.json | 69 ++++ packages/springboard/cli/src/docs_command.ts | 293 ++++++++++++++--- 17 files changed, 2830 insertions(+), 41 deletions(-) create mode 100644 packages/springboard/cli/src/docs/content/springboard-actions.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-anti-patterns.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-core-dependencies.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-lifecycle.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-module-api.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-module-communication.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-module-types.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-overview.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-patterns.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-platforms.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-routing.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-server-modules.md create mode 100644 packages/springboard/cli/src/docs/content/springboard-state-management.md create mode 100644 packages/springboard/cli/src/docs/index.ts create mode 100644 packages/springboard/cli/src/docs/sections.json diff --git a/packages/springboard/cli/package.json b/packages/springboard/cli/package.json index 72eb7549..be6fe26f 100644 --- a/packages/springboard/cli/package.json +++ b/packages/springboard/cli/package.json @@ -12,8 +12,9 @@ ], "scripts": { "build": "npm run clean && npm run build-cli", - "build-cli": "tsc && npm run copy-examples && npm run add-header", + "build-cli": "tsc && npm run copy-examples && npm run copy-docs && npm run add-header", "copy-examples": "mkdir -p dist/examples && cp src/examples/*.txt dist/examples/", + "copy-docs": "mkdir -p dist/docs/content && cp src/docs/sections.json dist/docs/ && cp src/docs/content/*.md dist/docs/content/", "prepublishOnly": "npm run build", "dev:setup": "[ -f ./dist/cli.js ] || npm run build", "build-saas": "DISABLE_IO=true npx tsx src/cli.ts build ../../../apps/jamtools/modules/index.ts", diff --git a/packages/springboard/cli/src/docs/content/springboard-actions.md b/packages/springboard/cli/src/docs/content/springboard-actions.md new file mode 100644 index 00000000..b25ccb2e --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-actions.md @@ -0,0 +1,174 @@ +# Actions & RPC + +Actions are async functions that automatically work across client/server via RPC. + +## Creating Actions + +### createActions (recommended) +```typescript +const actions = moduleAPI.createActions({ + increment: async () => { + const current = counter.getState(); + counter.setState(current + 1); + return { newValue: current + 1 }; + }, + + addItem: async (args: { text: string }) => { + items.setStateImmer(draft => { + draft.push({ id: uuid(), text: args.text }); + }); + }, + + fetchData: async (args: { id: string }) => { + const response = await fetch(`/api/data/${args.id}`); + return response.json(); + } +}); +``` + +### createAction (single action) +```typescript +const doSomething = moduleAPI.createAction( + 'doSomething', + {}, + async (args: { value: number }) => { + return args.value * 2; + } +); +``` + +## How RPC Works + +1. **Client calls action** → Serialized and sent via WebSocket +2. **Server receives** → Executes action callback +3. **Result returned** → Sent back to client + +```typescript +// On browser (client) +const result = await actions.increment(); +// ↓ RPC call to server +// ↓ Server executes increment() +// ↓ Result returned +console.log(result.newValue); // From server +``` + +## Execution Modes + +### Default: Remote (RPC) +```typescript +await actions.doSomething(args); // Uses RPC +``` + +### Force Local Execution +```typescript +await actions.doSomething(args, { mode: 'local' }); +``` +Runs directly without RPC. Use for: +- Actions that only affect local state +- Performance-critical operations +- Offline mode + +### Force Remote Execution +```typescript +await actions.doSomething(args, { mode: 'remote' }); +``` + +### Module-Level RPC Mode +```typescript +// All actions in this module run locally +moduleAPI.setRpcMode('local'); +``` + +## isMaestro Check + +The "maestro" is the authoritative device (usually server). + +```typescript +const actions = moduleAPI.createActions({ + syncData: async () => { + if (moduleAPI.deps.core.isMaestro()) { + // Running on server - do authoritative work + return await fetchFromDatabase(); + } else { + // Running on client - this shouldn't happen with default RPC + } + } +}); +``` + +## Error Handling + +```typescript +const actions = moduleAPI.createActions({ + riskyAction: async (args) => { + try { + return await doRiskyThing(args); + } catch (error) { + moduleAPI.deps.core.showError(`Failed: ${error.message}`); + throw error; // Re-throw to inform caller + } + } +}); +``` + +## Action Patterns + +### Optimistic Updates +```typescript +const actions = moduleAPI.createActions({ + addTodo: async (args: { text: string }) => { + const tempId = `temp-${Date.now()}`; + + // Optimistic update + todos.setStateImmer(draft => { + draft.push({ id: tempId, text: args.text, pending: true }); + }); + + try { + const realId = await saveTodoToServer(args.text); + // Replace temp with real + todos.setStateImmer(draft => { + const item = draft.find(t => t.id === tempId); + if (item) { + item.id = realId; + item.pending = false; + } + }); + } catch (error) { + // Rollback + todos.setStateImmer(draft => { + const idx = draft.findIndex(t => t.id === tempId); + if (idx >= 0) draft.splice(idx, 1); + }); + throw error; + } + } +}); +``` + +### Debounced Actions +```typescript +let saveTimeout: NodeJS.Timeout; + +const actions = moduleAPI.createActions({ + saveDocument: async (args: { content: string }) => { + clearTimeout(saveTimeout); + saveTimeout = setTimeout(async () => { + await persistToDatabase(args.content); + }, 1000); + } +}); +``` + +## Calling Actions from Components + +```typescript +function MyComponent() { + const handleClick = async () => { + const result = await actions.doSomething({ value: 42 }); + console.log(result); + }; + + return ; +} +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-anti-patterns.md b/packages/springboard/cli/src/docs/content/springboard-anti-patterns.md new file mode 100644 index 00000000..79b58067 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-anti-patterns.md @@ -0,0 +1,250 @@ +# Anti-Patterns to Avoid + +Common mistakes when developing Springboard modules. + +## Module Level getModule + +### ❌ Problem +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // DON'T: Called during registration, other module might not exist yet + const auth = moduleAPI.getModule('auth'); + const user = auth.userState.getState(); // May crash or return undefined + + moduleAPI.registerRoute('/', {}, () =>
{user?.name}
); +}); +``` + +### ✅ Solution +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + moduleAPI.registerRoute('/', {}, () => { + // DO: Called at render time, all modules registered + const auth = moduleAPI.getModule('auth'); + const user = auth.userState.useState(); + + return
{user?.name}
; + }); +}); +``` + +## Missing Optional Chaining + +### ❌ Problem +```typescript +const maybeModule = moduleAPI.getModule('optionalFeature'); +maybeModule.actions.doSomething(); // Crashes if module doesn't exist +``` + +### ✅ Solution +```typescript +const maybeModule = moduleAPI.getModule('optionalFeature'); +maybeModule?.actions?.doSomething(); // Safe + +// Or with explicit check +if (maybeModule) { + await maybeModule.actions.doSomething(); +} +``` + +## Direct State Mutation + +### ❌ Problem +```typescript +const items = itemsState.getState(); +items.push(newItem); // Mutating directly - won't trigger updates +itemsState.setState(items); // Same reference, may not trigger re-render +``` + +### ✅ Solution +```typescript +// Option 1: Immutable update +itemsState.setState([...items, newItem]); + +// Option 2: setStateImmer (recommended) +itemsState.setStateImmer(draft => { + draft.push(newItem); +}); +``` + +## Computed Values in State + +### ❌ Problem +```typescript +const [items, setItems] = useState([]); +const [filteredItems, setFilteredItems] = useState([]); // Derived state + +useEffect(() => { + setFilteredItems(items.filter(i => i.active)); // Redundant state +}, [items]); +``` + +### ✅ Solution +```typescript +const items = itemsState.useState(); + +// Compute on each render, memoize if expensive +const filteredItems = useMemo( + () => items.filter(i => i.active), + [items] +); +``` + +## Race Conditions in Initialization + +### ❌ Problem +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // These run in parallel - state2 might use state1 before it's ready + const state1Promise = moduleAPI.statesAPI.createPersistentState('a', {}); + const state2Promise = moduleAPI.statesAPI.createPersistentState('b', { + aRef: state1Promise.getState() // state1 not resolved yet! + }); +}); +``` + +### ✅ Solution +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // Await in sequence if there are dependencies + const state1 = await moduleAPI.statesAPI.createPersistentState('a', {}); + const state2 = await moduleAPI.statesAPI.createPersistentState('b', { + aRef: state1.getState() // Safe - state1 is resolved + }); + + // Or use Promise.all if truly independent + const [stateA, stateB] = await Promise.all([ + moduleAPI.statesAPI.createPersistentState('a', {}), + moduleAPI.statesAPI.createPersistentState('b', {}) + ]); +}); +``` + +## Missing Cleanup + +### ❌ Problem +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + const subscription = someObservable.subscribe(handleChange); + // Subscription never cleaned up - memory leak! + + setInterval(pollData, 5000); + // Interval never cleared - runs forever! +}); +``` + +### ✅ Solution +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + const subscription = someObservable.subscribe(handleChange); + moduleAPI.onDestroy(() => subscription.unsubscribe()); + + const intervalId = setInterval(pollData, 5000); + moduleAPI.onDestroy(() => clearInterval(intervalId)); +}); +``` + +## Wrong State Type Choice + +### ❌ Problem +```typescript +// Using SharedState for data that should persist +const userPrefs = await moduleAPI.statesAPI.createSharedState('prefs', { + theme: 'light' +}); +// Lost on server restart! + +// Using PersistentState for ephemeral UI state +const isModalOpen = await moduleAPI.statesAPI.createPersistentState('modal', false); +// Unnecessary database writes! +``` + +### ✅ Solution +```typescript +// Persistent for data that should survive restarts +const userPrefs = await moduleAPI.statesAPI.createPersistentState('prefs', { + theme: 'light' +}); + +// UserAgent for local UI state +const isModalOpen = await moduleAPI.statesAPI.createUserAgentState('modal', false); + +// Shared for real-time sync without persistence +const cursorPosition = await moduleAPI.statesAPI.createSharedState('cursor', null); +``` + +## Blocking Actions + +### ❌ Problem +```typescript +const actions = moduleAPI.createActions({ + processLargeData: async (args: { data: BigData }) => { + // Blocks UI for entire processing time + for (const item of args.data.items) { + await heavyProcessing(item); + } + } +}); +``` + +### ✅ Solution +```typescript +const actions = moduleAPI.createActions({ + processLargeData: async (args: { data: BigData }) => { + // Process in chunks, yield to UI + const chunks = chunkArray(args.data.items, 100); + + for (const chunk of chunks) { + await Promise.all(chunk.map(heavyProcessing)); + // Allow UI to update + await new Promise(r => setTimeout(r, 0)); + progressState.setStateImmer(d => { d.processed += chunk.length; }); + } + } +}); +``` + +## Sync getModule in Render + +### ❌ Problem +```typescript +function MyComponent() { + // Called on every render - inefficient + const otherModule = moduleAPI.getModule('other'); + const data = otherModule.state.useState(); + + return
{data}
; +} +``` + +### ✅ Solution +```typescript +// Get module reference once +const otherModule = moduleAPI.getModule('other'); + +function MyComponent() { + // Just use the state hook + const data = otherModule.state.useState(); + return
{data}
; +} + +// Or if conditional: +function MyComponent() { + const otherModule = useMemo(() => moduleAPI.getModule('other'), []); + const data = otherModule?.state?.useState() ?? defaultValue; + return
{data}
; +} +``` + +## Summary Checklist + +Before completing a module: + +- [ ] No `getModule` calls at module registration level +- [ ] All optional modules accessed with `?.` +- [ ] State updated via `setState` or `setStateImmer`, never mutated directly +- [ ] No derived state stored - use `useMemo` instead +- [ ] All subscriptions cleaned up in `onDestroy` +- [ ] All timers/intervals cleared in `onDestroy` +- [ ] Correct state type chosen (Shared/Persistent/UserAgent) +- [ ] Heavy operations chunked or run in background diff --git a/packages/springboard/cli/src/docs/content/springboard-core-dependencies.md b/packages/springboard/cli/src/docs/content/springboard-core-dependencies.md new file mode 100644 index 00000000..ae3e7837 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-core-dependencies.md @@ -0,0 +1,170 @@ +# Core Dependencies + +Every module has access to `CoreDependencies` via `moduleAPI.deps.core`. + +## Overview + +```typescript +type CoreDependencies = { + log: (...args: any[]) => void; + showError: (error: string) => void; + files: { + saveFile: (name: string, content: string) => Promise; + }; + storage: { + remote: KVStore; // Server/shared storage + userAgent: KVStore; // Local device storage + }; + rpc: { + remote: Rpc; // Server communication + local?: Rpc; // Local RPC (on maestro) + }; + isMaestro: () => boolean; +}; +``` + +## Logging + +### log +```typescript +moduleAPI.deps.core.log('Message', { data: 'value' }); +``` +Logs to console with module prefix. Use for debugging. + +### showError +```typescript +moduleAPI.deps.core.showError('Something went wrong'); +``` +Shows error to user (toast notification). Use for user-facing errors. + +```typescript +const actions = moduleAPI.createActions({ + saveDocument: async (args) => { + try { + await save(args); + } catch (error) { + moduleAPI.deps.core.showError(`Save failed: ${error.message}`); + throw error; + } + } +}); +``` + +## File Operations + +### saveFile +```typescript +await moduleAPI.deps.core.files.saveFile('export.json', JSON.stringify(data)); +``` +Triggers file download in browser, writes to disk in Node. + +## Storage (KVStore) + +Low-level key-value storage. Prefer `createPersistentState` for most cases. + +### KVStore Interface +```typescript +type KVStore = { + get: (key: string) => Promise; + set: (key: string, value: T) => Promise; + getAll: () => Promise | null>; +}; +``` + +### Remote Storage +```typescript +const { remote } = moduleAPI.deps.core.storage; + +// Stored on server, shared across devices +await remote.set('settings', { theme: 'dark' }); +const settings = await remote.get('settings'); +``` + +### UserAgent Storage +```typescript +const { userAgent } = moduleAPI.deps.core.storage; + +// Stored locally (localStorage in browser) +await userAgent.set('recentSearches', ['query1', 'query2']); +const searches = await userAgent.get('recentSearches'); +``` + +## RPC (Remote Procedure Call) + +Low-level RPC access. Usually not needed (use `createActions` instead). + +### Rpc Interface +```typescript +type Rpc = { + callRpc: (name: string, args: Args) => Promise; + broadcastRpc: (name: string, args: Args) => Promise; + registerRpc: (name: string, cb: (args: Args) => Promise) => void; + role: 'server' | 'client'; +}; +``` + +### Example: Custom RPC +```typescript +const { rpc } = moduleAPI.deps.core; + +// Register handler (on server) +rpc.remote.registerRpc('custom:ping', async (args: { message: string }) => { + return { pong: args.message }; +}); + +// Call from client +const result = await rpc.remote.callRpc('custom:ping', { message: 'hello' }); +console.log(result.pong); // 'hello' +``` + +## isMaestro + +Checks if running on the authoritative device (server). + +```typescript +if (moduleAPI.deps.core.isMaestro()) { + // Running on server - do authoritative work + await syncWithExternalAPI(); +} else { + // Running on client +} +``` + +**Use cases:** +- Server-only initialization +- Authoritative game logic +- Scheduled tasks + +## Module Dependencies (modDeps) + +Access via `moduleAPI.deps.module`: + +```typescript +type ModuleDependencies = { + moduleRegistry: ModuleRegistry; + toast: (options: ToastOptions) => void; + rpc: { remote: Rpc; local?: Rpc }; + services: { + remoteSharedStateService: SharedStateService; + localSharedStateService: SharedStateService; + }; +}; +``` + +### Toast Notifications +```typescript +moduleAPI.deps.module.toast({ + target: 'all', // 'all' | 'self' | 'others' + message: 'Hello!', + variant: 'info', // 'info' | 'success' | 'warning' | 'error' + flash: false, // Brief highlight + persistent: false // Stays until dismissed +}); +``` + +## Best Practices + +1. **Prefer high-level APIs**: Use `createActions` over raw RPC +2. **Use showError for users**: Don't just log errors silently +3. **Check isMaestro for server logic**: Keep authoritative code on server +4. **Use state APIs over storage**: `createPersistentState` handles sync automatically diff --git a/packages/springboard/cli/src/docs/content/springboard-lifecycle.md b/packages/springboard/cli/src/docs/content/springboard-lifecycle.md new file mode 100644 index 00000000..f69d2909 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-lifecycle.md @@ -0,0 +1,188 @@ +# Module Lifecycle + +Understanding when modules initialize and how to clean up resources. + +## Initialization Order + +``` +1. Springboard Engine created +2. RPC connections established +3. SharedStateServices initialized +4. Modules registered (in import order): + a. Module object created + b. ModuleAPI instantiated + c. registerModule callback executed + d. Module added to registry +5. React app renders +6. Module Providers stacked +7. Routes become active +``` + +## Registration Phase + +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // This runs during registration (step 4c) + // - RPC is available + // - Other modules may not be registered yet + + const state = await moduleAPI.statesAPI.createSharedState('data', {}); + + moduleAPI.registerRoute('/', {}, () => { + // This runs during render (step 7) + // - All modules are registered + // - Safe to call getModule + return
Hello
; + }); + + return { state }; +}); +``` + +## onDestroy - Cleanup + +Register cleanup callbacks with `moduleAPI.onDestroy()`: + +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // Create subscription + const subscription = someObservable.subscribe(handleChange); + + // Create interval + const intervalId = setInterval(checkUpdates, 5000); + + // Register cleanup + moduleAPI.onDestroy(() => { + subscription.unsubscribe(); + clearInterval(intervalId); + }); +}); +``` + +### When onDestroy Runs + +- Module is explicitly destroyed +- App is unmounting +- Hot module replacement (development) + +### Multiple Cleanup Callbacks + +```typescript +moduleAPI.onDestroy(() => { + console.log('Cleanup 1'); +}); + +moduleAPI.onDestroy(() => { + console.log('Cleanup 2'); +}); +// Both will be called +``` + +## Common Cleanup Scenarios + +### RxJS Subscriptions +```typescript +const sub = state.subject.subscribe(handleChange); +moduleAPI.onDestroy(() => sub.unsubscribe()); +``` + +### Event Listeners +```typescript +window.addEventListener('resize', handleResize); +moduleAPI.onDestroy(() => { + window.removeEventListener('resize', handleResize); +}); +``` + +### Timers +```typescript +const timerId = setInterval(poll, 1000); +moduleAPI.onDestroy(() => clearInterval(timerId)); +``` + +### WebSocket Connections +```typescript +const ws = new WebSocket(url); +moduleAPI.onDestroy(() => ws.close()); +``` + +### Third-Party Library Cleanup +```typescript +const chart = new Chart(canvas, config); +moduleAPI.onDestroy(() => chart.destroy()); +``` + +## Provider Pattern + +Each module can have a React Provider: + +```typescript +springboard.registerClassModule(async (coreDeps, modDeps) => { + const MyContext = createContext(null); + + return { + moduleId: 'myModule', + Provider: ({ children }) => { + const [state, setState] = useState(initialState); + return ( + + {children} + + ); + } + }; +}); +``` + +Providers are stacked in registration order: +``` + + + + + + + +``` + +## Splash Screen + +Register a loading screen shown during initialization: + +```typescript +springboard.registerSplashScreen(() => ( +
+ +

Loading...

+
+)); +``` + +Shown until all modules are registered and initial state is loaded. + +## Async Initialization + +Module registration callbacks can be async: + +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // Await initial data + const initialData = await fetchInitialData(); + + const state = await moduleAPI.statesAPI.createPersistentState( + 'data', + initialData + ); + + // Module isn't "ready" until this callback completes + return { state }; +}); +``` + +## Best Practices + +1. **Always clean up subscriptions**: Use `onDestroy` for every subscription +2. **Don't call getModule during registration**: Wait for routes/actions +3. **Keep registration fast**: Defer heavy work to actions or effects +4. **Use splash screen**: Show loading state during async init +5. **Import order matters**: Dependencies before dependents diff --git a/packages/springboard/cli/src/docs/content/springboard-module-api.md b/packages/springboard/cli/src/docs/content/springboard-module-api.md new file mode 100644 index 00000000..ad414089 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-module-api.md @@ -0,0 +1,132 @@ +# ModuleAPI Reference + +The `ModuleAPI` is the primary interface for module development. Received as parameter in `registerModule` callback. + +## Properties + +```typescript +moduleAPI.moduleId // string - Unique module identifier +moduleAPI.fullPrefix // string - Namespaced prefix +moduleAPI.statesAPI // StatesAPI - State management +moduleAPI.deps.core // CoreDependencies +moduleAPI.deps.module // ModuleDependencies +``` + +## State Management + +### createSharedState +```typescript +const state = await moduleAPI.statesAPI.createSharedState(name: string, initial: T) +``` +Cross-device synchronized state. Use for real-time features. + +### createPersistentState +```typescript +const state = await moduleAPI.statesAPI.createPersistentState(name: string, initial: T) +``` +Database-backed state. Survives restarts, syncs across devices. + +### createUserAgentState +```typescript +const state = await moduleAPI.statesAPI.createUserAgentState(name: string, initial: T) +``` +Local-only state (localStorage). Device-specific preferences. + +### StateSupervisor Methods +```typescript +state.getState() // Get current value +state.setState(newValue) // Set new value +state.setState(prev => newValue) // Set with callback +state.setStateImmer(draft => {}) // Mutate with Immer +state.useState() // React hook +state.subject // RxJS Subject +``` + +## Actions + +### createActions +```typescript +const actions = moduleAPI.createActions({ + actionName: async (args: Args) => { + // Automatically RPC-enabled + return result; + } +}); +``` + +### createAction (single) +```typescript +const action = moduleAPI.createAction('name', {}, async (args) => result); +``` + +### Action Options +```typescript +// Force local execution (skip RPC) +await actions.doSomething(args, { mode: 'local' }); + +// Force remote execution +await actions.doSomething(args, { mode: 'remote' }); +``` + +## Routing + +### registerRoute +```typescript +moduleAPI.registerRoute(path, options, Component); + +// Examples: +moduleAPI.registerRoute('/', {}, HomePage); // Absolute: / +moduleAPI.registerRoute('items/:id', {}, ItemPage); // Relative: /modules/MyModule/items/:id +moduleAPI.registerRoute('/admin', { // Absolute with options + hideApplicationShell: true, + documentMeta: { title: 'Admin' } +}, AdminPage); +``` + +### Route Options +```typescript +type RegisterRouteOptions = { + hideApplicationShell?: boolean; // Hide app shell + documentMeta?: { + title?: string; + description?: string; + 'og:image'?: string; + // ... SEO metadata + }; +} +``` + +### registerApplicationShell +```typescript +moduleAPI.registerApplicationShell(({ children, modules }) => ( + {children} +)); +``` + +## Module Communication + +### getModule +```typescript +const otherModule = moduleAPI.getModule('OtherModuleId'); +await otherModule.actions.doSomething(); +``` + +**Important**: Call inside routes/actions, not at module level. + +## Lifecycle + +### onDestroy +```typescript +moduleAPI.onDestroy(() => { + // Cleanup subscriptions, timers, etc. + subscription.unsubscribe(); +}); +``` + +## RPC Mode + +### setRpcMode +```typescript +moduleAPI.setRpcMode('local'); // All actions run locally +moduleAPI.setRpcMode('remote'); // All actions use RPC (default) +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-module-communication.md b/packages/springboard/cli/src/docs/content/springboard-module-communication.md new file mode 100644 index 00000000..fb2167e1 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-module-communication.md @@ -0,0 +1,163 @@ +# Module Communication + +Modules can access other modules' APIs using `getModule()` with type-safe interface merging. + +## Basic Usage + +```typescript +// In a route or action (NOT at module level) +const otherModule = moduleAPI.getModule('otherModuleId'); +await otherModule.actions.doSomething(); +``` + +## Interface Merging Pattern + +For type-safe `getModule()`, declare your module's exports: + +```typescript +// In auth module +springboard.registerModule('auth', {}, async (moduleAPI) => { + const userState = await moduleAPI.statesAPI.createPersistentState('user', null as User | null); + + const actions = moduleAPI.createActions({ + login: async (args: { email: string; password: string }) => { /* ... */ }, + logout: async () => { /* ... */ } + }); + + return { userState, actions }; +}); + +// Type declaration (usually at bottom of file or in types.ts) +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + auth: { + userState: StateSupervisor; + actions: { + login: (args: { email: string; password: string }) => Promise; + logout: () => Promise; + }; + }; + } +} +``` + +Now in other modules: +```typescript +const auth = moduleAPI.getModule('auth'); // Type-safe! +const user = auth.userState.useState(); // TypeScript knows this is User | null +await auth.actions.login({ email, password }); // Autocomplete works +``` + +## Important: Where to Call getModule + +### ❌ Wrong: At Module Level +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // DON'T DO THIS - module might not be registered yet + const auth = moduleAPI.getModule('auth'); + + // ... +}); +``` + +### ✅ Correct: Inside Routes or Actions +```typescript +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + moduleAPI.registerRoute('/', {}, () => { + // ✓ Safe - called after all modules registered + const auth = moduleAPI.getModule('auth'); + const user = auth.userState.useState(); + + return
Hello {user?.name}
; + }); + + const actions = moduleAPI.createActions({ + doSomething: async () => { + // ✓ Safe - called at runtime + const auth = moduleAPI.getModule('auth'); + const user = auth.userState.getState(); + // ... + } + }); +}); +``` + +## Optional Module Access + +If a module might not be registered: + +```typescript +const maybeModule = moduleAPI.getModule('optionalModule'); + +// Use optional chaining +maybeModule?.actions?.doSomething(); + +// Or check existence +if (maybeModule) { + await maybeModule.actions.doSomething(); +} +``` + +## Common Patterns + +### Shared Notification System +```typescript +// In feature module +const handleError = (error: Error) => { + const notifications = moduleAPI.getModule('notifications'); + notifications.actions.show({ + message: error.message, + type: 'error' + }); +}; +``` + +### Auth-Protected Actions +```typescript +const actions = moduleAPI.createActions({ + saveDocument: async (args: { content: string }) => { + const auth = moduleAPI.getModule('auth'); + const user = auth.userState.getState(); + + if (!user) { + throw new Error('Must be logged in'); + } + + await saveToDatabase(user.id, args.content); + } +}); +``` + +### Cross-Module State Subscription +```typescript +springboard.registerModule('Dashboard', {}, async (moduleAPI) => { + moduleAPI.registerRoute('/', {}, () => { + const todos = moduleAPI.getModule('todos'); + const calendar = moduleAPI.getModule('calendar'); + + // Subscribe to multiple modules' state + const todoItems = todos.state.useState(); + const events = calendar.eventsState.useState(); + + return ( +
+ + +
+ ); + }); +}); +``` + +## Module Dependencies + +Modules are initialized in registration order. If module B depends on module A: + +```typescript +// index.tsx - register in dependency order +import './modules/utilities/auth'; // First +import './modules/utilities/notifications'; +import './modules/features/dashboard'; // Last (depends on above) +``` + +For circular dependencies, use lazy access via `getModule()` inside routes/actions. diff --git a/packages/springboard/cli/src/docs/content/springboard-module-types.md b/packages/springboard/cli/src/docs/content/springboard-module-types.md new file mode 100644 index 00000000..38924148 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-module-types.md @@ -0,0 +1,195 @@ +# Module Types + +Springboard has three module types, each with a specific purpose. + +## Feature Module + +**Purpose:** Implements user-facing features with UI. + +**Characteristics:** +- Registers routes +- Has visual components +- May use other modules +- Most common type + +**Example:** +```typescript +springboard.registerModule('TodoList', {}, async (moduleAPI) => { + const todosState = await moduleAPI.statesAPI.createPersistentState('todos', []); + + const actions = moduleAPI.createActions({ + addTodo: async (args: { text: string }) => { + todosState.setStateImmer(draft => { + draft.push({ id: Date.now(), text: args.text, done: false }); + }); + }, + toggleTodo: async (args: { id: number }) => { + todosState.setStateImmer(draft => { + const todo = draft.find(t => t.id === args.id); + if (todo) todo.done = !todo.done; + }); + } + }); + + moduleAPI.registerRoute('/', {}, () => { + const todos = todosState.useState(); + return ( +
+

Todos

+ {todos.map(todo => ( +
actions.toggleTodo({ id: todo.id })}> + {todo.done ? '✓' : '○'} {todo.text} +
+ ))} +
+ ); + }); + + return { state: todosState, actions }; +}); +``` + +## Utility Module + +**Purpose:** Provides shared functionality for other modules. + +**Characteristics:** +- No routes or UI +- Exports APIs for other modules +- Uses interface merging for type-safety +- Examples: auth, notifications, analytics + +**Example:** +```typescript +springboard.registerModule('notifications', {}, async (moduleAPI) => { + const notificationsState = await moduleAPI.statesAPI.createSharedState('notifications', [] as Notification[]); + + const actions = moduleAPI.createActions({ + show: async (args: { message: string; type: 'info' | 'error' | 'success' }) => { + const id = Date.now(); + notificationsState.setStateImmer(draft => { + draft.push({ id, ...args }); + }); + + // Auto-dismiss after 5s + setTimeout(() => { + notificationsState.setStateImmer(draft => { + const idx = draft.findIndex(n => n.id === id); + if (idx >= 0) draft.splice(idx, 1); + }); + }, 5000); + }, + + dismiss: async (args: { id: number }) => { + notificationsState.setStateImmer(draft => { + const idx = draft.findIndex(n => n.id === args.id); + if (idx >= 0) draft.splice(idx, 1); + }); + } + }); + + // Expose for other modules + return { state: notificationsState, actions }; +}); + +// Type declaration for getModule +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + notifications: { + state: StateSupervisor; + actions: { + show: (args: { message: string; type: 'info' | 'error' | 'success' }) => Promise; + dismiss: (args: { id: number }) => Promise; + }; + }; + } +} +``` + +**Using in another module:** +```typescript +springboard.registerModule('MyFeature', {}, async (moduleAPI) => { + moduleAPI.registerRoute('/', {}, () => { + const notifications = moduleAPI.getModule('notifications'); + + const handleSave = async () => { + try { + await saveData(); + notifications.actions.show({ message: 'Saved!', type: 'success' }); + } catch (e) { + notifications.actions.show({ message: 'Save failed', type: 'error' }); + } + }; + + return ; + }); +}); +``` + +## Initializer Module + +**Purpose:** Performs setup during app initialization. + +**Characteristics:** +- Runs before other modules +- No UI or routes +- Platform-specific initialization +- Examples: theme setup, analytics init, feature flags + +**Example:** +```typescript +springboard.registerModule('ThemeInitializer', {}, async (moduleAPI) => { + const themeState = await moduleAPI.statesAPI.createUserAgentState('theme', { + mode: 'light' as 'light' | 'dark' + }); + + // Apply theme on load + const theme = themeState.getState(); + document.documentElement.setAttribute('data-theme', theme.mode); + + // Watch for changes + themeState.subject.subscribe(({ mode }) => { + document.documentElement.setAttribute('data-theme', mode); + }); + + return { + state: themeState, + setTheme: (mode: 'light' | 'dark') => { + themeState.setState({ mode }); + } + }; +}); +``` + +## When to Use Each Type + +| Scenario | Module Type | +|----------|-------------| +| User-facing feature with pages | Feature | +| Shared service (auth, notifications) | Utility | +| App-wide setup (themes, analytics) | Initializer | +| API integration | Utility | +| Dashboard with routes | Feature | +| State shared across features | Utility | + +## Module Organization Pattern + +``` +src/ + modules/ + features/ + todos/ + index.tsx # Feature module + components/ # UI components + dashboard/ + index.tsx + utilities/ + notifications/ + index.tsx # Utility module + auth/ + index.tsx + initializers/ + theme.tsx # Initializer module + analytics.tsx + index.tsx # Register all modules +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-overview.md b/packages/springboard/cli/src/docs/content/springboard-overview.md new file mode 100644 index 00000000..5a0cb70c --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-overview.md @@ -0,0 +1,62 @@ +# Springboard Overview + +Springboard is a full-stack JavaScript framework for building real-time, multi-device applications. Built on React, Hono, JSON-RPC, and WebSockets. + +## Core Philosophy + +"Your codebase should only be feature-level code" - Springboard abstracts infrastructure so you focus on features. + +## Key Features + +- **Multi-platform**: Browser, Node.js, Desktop (Tauri), React Native, PartyKit +- **Real-time sync**: State automatically syncs across connected devices +- **Module-based**: Organize code into isolated, reusable modules +- **RPC built-in**: Actions automatically work across client/server +- **Type-safe**: Full TypeScript support with module interface merging + +## Architecture + +``` +┌─────────────────────────────────────────┐ +│ Your Modules │ +│ (Feature, Utility, Initializer) │ +├─────────────────────────────────────────┤ +│ ModuleAPI │ +│ (States, Actions, Routes, Lifecycle) │ +├─────────────────────────────────────────┤ +│ Springboard Engine │ +│ (RPC, State Sync, Module Registry) │ +├─────────────────────────────────────────┤ +│ Platform Layer │ +│ (Browser, Node, Desktop, Mobile) │ +└─────────────────────────────────────────┘ +``` + +## Basic Module Structure + +```typescript +import springboard from 'springboard'; + +springboard.registerModule('MyModule', {}, async (moduleAPI) => { + // 1. Create state + const state = await moduleAPI.statesAPI.createSharedState('data', initialValue); + + // 2. Create actions + const actions = moduleAPI.createActions({ + doSomething: async (args) => { /* ... */ } + }); + + // 3. Register routes + moduleAPI.registerRoute('/', {}, MyComponent); + + // 4. Return public API + return { state, actions }; +}); +``` + +## When to Use Springboard + +- Real-time collaborative apps +- Multi-device synchronized experiences +- Apps needing offline-first capabilities +- Full-stack apps with shared business logic diff --git a/packages/springboard/cli/src/docs/content/springboard-patterns.md b/packages/springboard/cli/src/docs/content/springboard-patterns.md new file mode 100644 index 00000000..31858664 --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-patterns.md @@ -0,0 +1,300 @@ +# Common Patterns + +Proven patterns from real Springboard applications. + +## State Patterns + +### Derived State with useMemo +Don't store computed values in state: + +```typescript +// ❌ Wrong: Storing computed value +const [filteredItems, setFilteredItems] = useState([]); +useEffect(() => { + setFilteredItems(items.filter(i => i.active)); +}, [items]); + +// ✅ Correct: Compute on render +function ItemList() { + const items = itemsState.useState(); + const filter = filterState.useState(); + + const filteredItems = useMemo( + () => items.filter(i => i.active && i.name.includes(filter)), + [items, filter] + ); + + return ; +} +``` + +### Optimistic Updates +Update UI immediately, then sync with server: + +```typescript +const actions = moduleAPI.createActions({ + toggleLike: async (args: { postId: string }) => { + // Optimistic update + likesState.setStateImmer(draft => { + draft[args.postId] = !draft[args.postId]; + }); + + try { + await api.toggleLike(args.postId); + } catch (error) { + // Rollback on failure + likesState.setStateImmer(draft => { + draft[args.postId] = !draft[args.postId]; + }); + throw error; + } + } +}); +``` + +### State Selectors +Subscribe to specific parts of state: + +```typescript +function UserName() { + const user = userState.useState(); + // Re-renders on any user change + + return {user.name}; +} + +// Better: Extract only what you need +function UserName() { + const user = userState.useState(); + const name = useMemo(() => user.name, [user.name]); + + return {name}; +} +``` + +## Action Patterns + +### Action Queuing +Prevent concurrent execution: + +```typescript +let savePromise: Promise | null = null; + +const actions = moduleAPI.createActions({ + save: async (args: { data: Data }) => { + // Wait for previous save + if (savePromise) await savePromise; + + savePromise = (async () => { + await api.save(args.data); + })(); + + await savePromise; + savePromise = null; + } +}); +``` + +### Debounced Actions +Delay execution until activity stops: + +```typescript +let debounceTimer: NodeJS.Timeout; + +const actions = moduleAPI.createActions({ + search: async (args: { query: string }) => { + return new Promise((resolve) => { + clearTimeout(debounceTimer); + debounceTimer = setTimeout(async () => { + const results = await api.search(args.query); + searchResults.setState(results); + resolve(results); + }, 300); + }); + } +}); +``` + +### Batch Updates +Group multiple state changes: + +```typescript +const actions = moduleAPI.createActions({ + importData: async (args: { items: Item[] }) => { + // Single state update instead of many + itemsState.setStateImmer(draft => { + for (const item of args.items) { + draft.push(item); + } + }); + } +}); +``` + +## Navigation Patterns + +### Navigation with Reason +Track why navigation happened: + +```typescript +type NavReason = + | { type: 'user_click' } + | { type: 'form_submit'; formId: string } + | { type: 'auto_redirect' } + | { type: 'deep_link' }; + +const navReasonState = await moduleAPI.statesAPI.createUserAgentState( + 'navReason', + null +); + +function navigateWithReason(path: string, reason: NavReason) { + navReasonState.setState(reason); + navigate(path); +} + +// Usage +navigateWithReason('/dashboard', { type: 'user_click' }); +``` + +### Breadcrumb Trail +Track navigation history: + +```typescript +const breadcrumbState = await moduleAPI.statesAPI.createUserAgentState( + 'breadcrumbs', + [] +); + +function pushBreadcrumb(path: string) { + breadcrumbState.setStateImmer(draft => { + draft.push(path); + if (draft.length > 10) draft.shift(); // Keep last 10 + }); +} +``` + +## Module Communication Patterns + +### Event Bus +Loose coupling between modules: + +```typescript +// events module +type AppEvent = + | { type: 'user:login'; userId: string } + | { type: 'document:saved'; docId: string }; + +const eventBus = new Subject(); + +springboard.registerModule('events', {}, async () => { + return { + emit: (event: AppEvent) => eventBus.next(event), + subscribe: (handler: (event: AppEvent) => void) => eventBus.subscribe(handler) + }; +}); + +// In other module +const events = moduleAPI.getModule('events'); +const sub = events.subscribe(event => { + if (event.type === 'user:login') { + // Handle login + } +}); +moduleAPI.onDestroy(() => sub.unsubscribe()); +``` + +### Hook System +Let modules extend functionality: + +```typescript +type Hook = (value: T) => T | Promise; + +const hooks = new Map[]>(); + +springboard.registerModule('hooks', {}, async () => { + return { + register: (name: string, hook: Hook) => { + if (!hooks.has(name)) hooks.set(name, []); + hooks.get(name)!.push(hook); + }, + run: async (name: string, value: T): Promise => { + const hookList = hooks.get(name) || []; + let result = value; + for (const hook of hookList) { + result = await hook(result); + } + return result; + } + }; +}); + +// Usage: before-save hook +hooks.register('document:before-save', async (doc) => { + return { ...doc, updatedAt: Date.now() }; +}); +``` + +## Error Handling Patterns + +### Global Error Boundary +```typescript +moduleAPI.registerRoute('/', {}, () => { + return ( + }> + + + ); +}); +``` + +### Action Error Wrapper +```typescript +function withErrorHandling Promise>( + action: T, + errorMessage: string +): T { + return (async (...args: Parameters) => { + try { + return await action(...args); + } catch (error) { + moduleAPI.deps.core.showError(errorMessage); + throw error; + } + }) as T; +} + +const actions = moduleAPI.createActions({ + save: withErrorHandling( + async (args: { data: Data }) => await api.save(args.data), + 'Failed to save' + ) +}); +``` + +## Loading State Pattern + +```typescript +interface AsyncState { + data: T | null; + loading: boolean; + error: string | null; +} + +const dataState = await moduleAPI.statesAPI.createSharedState>( + 'data', + { data: null, loading: false, error: null } +); + +const actions = moduleAPI.createActions({ + fetchData: async () => { + dataState.setState({ data: null, loading: true, error: null }); + try { + const data = await api.fetchData(); + dataState.setState({ data, loading: false, error: null }); + } catch (error) { + dataState.setState({ data: null, loading: false, error: error.message }); + } + } +}); +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-platforms.md b/packages/springboard/cli/src/docs/content/springboard-platforms.md new file mode 100644 index 00000000..a341f6bd --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-platforms.md @@ -0,0 +1,217 @@ +# Platform Support + +Springboard supports multiple deployment platforms with platform-specific code. + +## Supported Platforms + +| Platform | Runtime | Use Case | +|----------|---------|----------| +| browser | Web browser | Standard web apps | +| node | Node.js | Server, CLI tools | +| desktop | Tauri | Desktop apps | +| react-native | React Native | Mobile apps | +| partykit | PartyKit edge | Serverless realtime | + +## Platform Detection + +```typescript +// Check platform at runtime +if (typeof window !== 'undefined') { + // Browser +} else { + // Node.js +} + +// Check if maestro (server/authoritative) +if (moduleAPI.deps.core.isMaestro()) { + // Running as server +} +``` + +## Conditional Compilation + +Use `@platform` directives for platform-specific code: + +```typescript +// @platform "node" +import fs from 'fs'; + +async function readFile(path: string) { + return fs.readFileSync(path, 'utf-8'); +} +// @platform end + +// @platform "browser" +async function readFile(path: string) { + const response = await fetch(path); + return response.text(); +} +// @platform end +``` + +The code block is only included in the specified platform build. + +### Multiple Platforms +```typescript +// @platform "node" +// @platform "desktop" +// Code included in both Node.js and Desktop builds +import { exec } from 'child_process'; +// @platform end +``` + +## Platform-Specific Services + +### Browser + +- **RPC**: WebSocket + HTTP fallback +- **Storage**: localStorage +- **Files**: Download via browser API + +```typescript +// Browser-specific features +const { userAgent } = moduleAPI.deps.core.storage; +await userAgent.set('key', 'value'); // Uses localStorage +``` + +### Node.js + +- **RPC**: WebSocket client +- **Storage**: JSON file on disk +- **Files**: fs module + +```typescript +// @platform "node" +import { writeFileSync } from 'fs'; + +async function exportData(data: any) { + writeFileSync('./export.json', JSON.stringify(data, null, 2)); +} +// @platform end +``` + +### Desktop (Tauri) + +- **RPC**: Tauri IPC bridge +- **Storage**: Local file system +- **Files**: Native file dialogs + +```typescript +// @platform "desktop" +import { save } from '@tauri-apps/api/dialog'; + +async function saveFileDialog(content: string) { + const path = await save({ defaultPath: 'document.txt' }); + if (path) { + await writeTextFile(path, content); + } +} +// @platform end +``` + +### React Native + +- **RPC**: WebView bridge +- **Storage**: AsyncStorage +- **Files**: Platform-specific APIs + +```typescript +// @platform "react-native" +import AsyncStorage from '@react-native-async-storage/async-storage'; + +async function saveLocal(key: string, value: string) { + await AsyncStorage.setItem(key, value); +} +// @platform end +``` + +## Build Configuration + +### CLI Options +```bash +# Build for specific platform +sb build src/index.tsx --platforms browser +sb build src/index.tsx --platforms node +sb build src/index.tsx --platforms desktop +sb build src/index.tsx --platforms all + +# Development +sb dev src/index.tsx --platforms main # browser + node +``` + +### Environment Variables +```typescript +// Available in browser builds +process.env.WS_HOST // WebSocket host +process.env.DATA_HOST // HTTP API host + +// Available in node builds +process.env.PORT // Server port +process.env.NODE_KV_STORE_DATA_FILE // KV store path +``` + +## Cross-Platform Patterns + +### Abstract Platform Differences +```typescript +// platform-utils.ts +export async function readLocalFile(key: string): Promise { + // @platform "browser" + return localStorage.getItem(key); + // @platform end + + // @platform "node" + try { + const { readFileSync } = await import('fs'); + return readFileSync(`./data/${key}.txt`, 'utf-8'); + } catch { + return null; + } + // @platform end +} +``` + +### Feature Detection +```typescript +const features = { + hasFileSystem: typeof window === 'undefined', + hasClipboard: typeof navigator?.clipboard !== 'undefined', + isOnline: typeof navigator?.onLine !== 'undefined' ? navigator.onLine : true +}; +``` + +## Offline Support + +### Browser Offline Mode +```bash +sb build src/index.tsx --platforms browser_offline +``` + +Builds a fully offline-capable version: +- All assets bundled +- `isMaestro()` returns `true` +- LocalStorage for all state + +### Detecting Online Status +```typescript +// @platform "browser" +function useOnlineStatus() { + const [online, setOnline] = useState(navigator.onLine); + + useEffect(() => { + const handleOnline = () => setOnline(true); + const handleOffline = () => setOnline(false); + + window.addEventListener('online', handleOnline); + window.addEventListener('offline', handleOffline); + + return () => { + window.removeEventListener('online', handleOnline); + window.removeEventListener('offline', handleOffline); + }; + }, []); + + return online; +} +// @platform end +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-routing.md b/packages/springboard/cli/src/docs/content/springboard-routing.md new file mode 100644 index 00000000..fc775ccd --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-routing.md @@ -0,0 +1,191 @@ +# Routing & Navigation + +Springboard uses React Router under the hood. Routes are registered via `moduleAPI.registerRoute()`. + +## Registering Routes + +### Basic Route +```typescript +moduleAPI.registerRoute('/', {}, HomePage); +``` + +### Route with Parameters +```typescript +moduleAPI.registerRoute('items/:itemId', {}, ItemDetailPage); + +function ItemDetailPage() { + const { itemId } = useParams(); + return
Item: {itemId}
; +} +``` + +### Absolute vs Relative Paths + +```typescript +// Absolute (starts with /) +moduleAPI.registerRoute('/admin', {}, AdminPage); +// → matches: /admin + +// Relative (no leading /) +moduleAPI.registerRoute('settings', {}, SettingsPage); +// → matches: /modules/MyModule/settings + +moduleAPI.registerRoute('', {}, ModuleHomePage); +// → matches: /modules/MyModule +``` + +## Route Options + +### hideApplicationShell +```typescript +moduleAPI.registerRoute('/fullscreen', { + hideApplicationShell: true +}, FullscreenPage); +``` +Hides the app shell (navigation, sidebar, etc.) for this route. Use for: +- Presentation modes +- Embedded views +- Login pages + +### documentMeta +```typescript +moduleAPI.registerRoute('/product/:id', { + documentMeta: { + title: 'Product Details', + description: 'View product information', + 'og:type': 'product' + } +}, ProductPage); +``` + +### Dynamic documentMeta +```typescript +moduleAPI.registerRoute('/product/:id', { + documentMeta: async ({ params }) => { + const product = await getProduct(params.id); + return { + title: product.name, + description: product.description, + 'og:image': product.imageUrl + }; + } +}, ProductPage); +``` + +## Application Shell + +### Registering Custom Shell +```typescript +moduleAPI.registerApplicationShell(({ children, modules }) => ( +
+ +
{children}
+
+)); +``` + +The shell wraps all routes except those with `hideApplicationShell: true`. + +## Navigation + +### Programmatic Navigation +```typescript +import { useNavigate } from 'react-router'; + +function MyComponent() { + const navigate = useNavigate(); + + const handleClick = () => { + navigate('/modules/MyModule/items/123'); + }; + + return ; +} +``` + +### Navigation with Reason (Pattern from SongDrive) +```typescript +// Define navigation reasons for type-safety +type NavigationReason = + | { reason: 'user_click' } + | { reason: 'action_complete'; actionId: string } + | { reason: 'auto_redirect' }; + +// Track why navigation happened +function navigateWithReason(path: string, reason: NavigationReason) { + // Store reason for analytics/debugging + sessionStorage.setItem('nav_reason', JSON.stringify(reason)); + navigate(path); +} +``` + +### Link Component +```typescript +import { Link } from 'react-router'; + +function Navigation() { + return ( + + ); +} +``` + +## Route Component Props + +Route components receive a `navigate` prop: + +```typescript +type RouteComponentProps = { + navigate: (routeName: string) => void; +}; + +function MyPage({ navigate }: RouteComponentProps) { + return ( + + ); +} +``` + +## Common Patterns + +### Protected Routes +```typescript +function ProtectedPage() { + const user = authState.useState(); + + if (!user) { + return ; + } + + return ; +} +``` + +### Route Guards in Module +```typescript +springboard.registerModule('Admin', {}, async (moduleAPI) => { + const authModule = moduleAPI.getModule('auth'); + + moduleAPI.registerRoute('/admin', {}, () => { + const user = authModule.userState.useState(); + + if (!user?.isAdmin) { + return
Access Denied
; + } + + return ; + }); +}); +``` + +### Nested Routes +```typescript +moduleAPI.registerRoute('dashboard', {}, DashboardLayout); +moduleAPI.registerRoute('dashboard/overview', {}, Overview); +moduleAPI.registerRoute('dashboard/settings', {}, Settings); +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-server-modules.md b/packages/springboard/cli/src/docs/content/springboard-server-modules.md new file mode 100644 index 00000000..2900cc7e --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-server-modules.md @@ -0,0 +1,241 @@ +# Server Modules + +Server modules run on the Node.js server and can register HTTP routes and RPC middleware. + +## Server Module Registration + +```typescript +import { serverRegistry } from 'springboard-server'; + +serverRegistry.registerServerModule((serverAPI) => { + const { hono, hooks, getEngine } = serverAPI; + + // Register HTTP routes + hono.get('/api/health', (c) => c.json({ status: 'ok' })); + + // Register RPC middleware + hooks.registerRpcMiddleware(async (c) => { + const authHeader = c.req.header('Authorization'); + return { userId: extractUserId(authHeader) }; + }); +}); +``` + +## ServerModuleAPI + +```typescript +type ServerModuleAPI = { + hono: Hono; // Hono web framework instance + hooks: ServerHooks; // Server lifecycle hooks + getEngine: () => Springboard; // Access Springboard engine +}; +``` + +## Hono Routes + +Server modules use [Hono](https://hono.dev) for HTTP routing: + +```typescript +serverRegistry.registerServerModule(({ hono }) => { + // GET request + hono.get('/api/items', async (c) => { + const items = await db.getItems(); + return c.json(items); + }); + + // POST request + hono.post('/api/items', async (c) => { + const body = await c.req.json(); + const item = await db.createItem(body); + return c.json(item, 201); + }); + + // URL parameters + hono.get('/api/items/:id', async (c) => { + const { id } = c.req.param(); + const item = await db.getItem(id); + if (!item) return c.json({ error: 'Not found' }, 404); + return c.json(item); + }); + + // Query parameters + hono.get('/api/search', async (c) => { + const query = c.req.query('q'); + const results = await db.search(query); + return c.json(results); + }); +}); +``` + +## RPC Middleware + +Middleware runs before each RPC call and can inject context: + +```typescript +serverRegistry.registerServerModule(({ hooks }) => { + hooks.registerRpcMiddleware(async (c) => { + // Extract user from request + const token = c.req.header('Authorization')?.replace('Bearer ', ''); + + if (token) { + const user = await verifyToken(token); + return { user }; // Available in RPC handlers + } + + return {}; + }); +}); +``` + +The returned object is merged into RPC context: + +```typescript +// In a module action +const actions = moduleAPI.createActions({ + getUserData: async (args, options) => { + // Access middleware context (implementation specific) + const userId = options?.context?.user?.id; + return await db.getUserData(userId); + } +}); +``` + +## Accessing Springboard Engine + +```typescript +serverRegistry.registerServerModule(({ getEngine }) => { + // Access the Springboard engine for state/modules + const engine = getEngine(); + + // Example: Background task using engine + setInterval(async () => { + // Access registered modules + const registry = engine.getModuleRegistry(); + // ... perform background sync + }, 60000); +}); +``` + +## File Uploads + +```typescript +serverRegistry.registerServerModule(({ hono }) => { + hono.post('/api/upload', async (c) => { + const formData = await c.req.formData(); + const file = formData.get('file') as File; + + const buffer = await file.arrayBuffer(); + const filename = `uploads/${Date.now()}-${file.name}`; + + await writeFile(filename, Buffer.from(buffer)); + + return c.json({ path: filename }); + }); +}); +``` + +## Static Files + +```typescript +import { serveStatic } from 'hono/serve-static'; + +serverRegistry.registerServerModule(({ hono }) => { + // Serve static files from 'public' directory + hono.use('/static/*', serveStatic({ root: './' })); +}); +``` + +## CORS Configuration + +```typescript +import { cors } from 'hono/cors'; + +serverRegistry.registerServerModule(({ hono }) => { + hono.use('/api/*', cors({ + origin: ['https://myapp.com'], + allowMethods: ['GET', 'POST', 'PUT', 'DELETE'], + allowHeaders: ['Content-Type', 'Authorization'] + })); +}); +``` + +## Error Handling + +```typescript +serverRegistry.registerServerModule(({ hono }) => { + // Global error handler + hono.onError((err, c) => { + console.error('Server error:', err); + return c.json({ + error: err.message, + stack: process.env.NODE_ENV === 'development' ? err.stack : undefined + }, 500); + }); + + // Route-specific try/catch + hono.get('/api/risky', async (c) => { + try { + const result = await riskyOperation(); + return c.json(result); + } catch (error) { + return c.json({ error: error.message }, 400); + } + }); +}); +``` + +## Database Integration + +```typescript +import { drizzle } from 'drizzle-orm/better-sqlite3'; +import Database from 'better-sqlite3'; + +const sqlite = new Database('data.db'); +const db = drizzle(sqlite); + +serverRegistry.registerServerModule(({ hono }) => { + hono.get('/api/users', async (c) => { + const users = await db.select().from(usersTable); + return c.json(users); + }); +}); +``` + +## Common Patterns + +### Auth-Protected Routes +```typescript +const requireAuth = async (c, next) => { + const token = c.req.header('Authorization'); + if (!token) return c.json({ error: 'Unauthorized' }, 401); + + try { + const user = await verifyToken(token); + c.set('user', user); + await next(); + } catch { + return c.json({ error: 'Invalid token' }, 401); + } +}; + +serverRegistry.registerServerModule(({ hono }) => { + hono.use('/api/protected/*', requireAuth); + + hono.get('/api/protected/me', (c) => { + const user = c.get('user'); + return c.json(user); + }); +}); +``` + +### Rate Limiting +```typescript +import { rateLimiter } from 'hono-rate-limiter'; + +serverRegistry.registerServerModule(({ hono }) => { + hono.use('/api/*', rateLimiter({ + windowMs: 60 * 1000, // 1 minute + limit: 100, // 100 requests per window + })); +}); +``` diff --git a/packages/springboard/cli/src/docs/content/springboard-state-management.md b/packages/springboard/cli/src/docs/content/springboard-state-management.md new file mode 100644 index 00000000..0f1200cd --- /dev/null +++ b/packages/springboard/cli/src/docs/content/springboard-state-management.md @@ -0,0 +1,143 @@ +# State Management + +Springboard provides three types of state, each with different persistence and sync behavior. + +## State Types Comparison + +| Type | Persistence | Sync | Use Case | +|------|-------------|------|----------| +| SharedState | In-memory (server cache) | Real-time cross-device | Temporary shared data, UI sync | +| PersistentState | Database | Cross-device on load | User data, settings, saved state | +| UserAgentState | localStorage | None (device-local) | UI preferences, local settings | + +## createSharedState + +```typescript +const counter = await moduleAPI.statesAPI.createSharedState('counter', 0); +``` + +**Behavior:** +- Cached on server +- Broadcasts changes to all connected devices instantly +- Lost on server restart (unless persisted separately) +- Use for: cursor positions, typing indicators, live collaboration + +**Example:** +```typescript +const presenceState = await moduleAPI.statesAPI.createSharedState('presence', { + users: [] as { id: string; cursor: { x: number; y: number } }[] +}); + +// Update broadcasts to all devices +presenceState.setStateImmer(draft => { + const user = draft.users.find(u => u.id === myId); + if (user) user.cursor = { x, y }; +}); +``` + +## createPersistentState + +```typescript +const settings = await moduleAPI.statesAPI.createPersistentState('settings', { + theme: 'light', + language: 'en' +}); +``` + +**Behavior:** +- Stored in database (server-side) +- Loaded on app start +- Synced across devices +- Survives restarts +- Use for: user preferences, saved documents, game state + +**Example:** +```typescript +const todoList = await moduleAPI.statesAPI.createPersistentState('todos', { + items: [] as Todo[] +}); + +// Changes persist to database +todoList.setStateImmer(draft => { + draft.items.push({ id: uuid(), text: 'New todo', done: false }); +}); +``` + +## createUserAgentState + +```typescript +const uiState = await moduleAPI.statesAPI.createUserAgentState('ui', { + sidebarOpen: true, + lastViewedTab: 'home' +}); +``` + +**Behavior:** +- Stored in localStorage (browser) or local file (node) +- Never synced to server or other devices +- Device-specific +- Use for: collapsed panels, scroll positions, local drafts + +**Example:** +```typescript +const localPrefs = await moduleAPI.statesAPI.createUserAgentState('localPrefs', { + volume: 0.8, + recentSearches: [] as string[] +}); + +// Only affects this device +localPrefs.setStateImmer(draft => { + draft.recentSearches.unshift(query); + draft.recentSearches = draft.recentSearches.slice(0, 10); +}); +``` + +## StateSupervisor API + +All state types return a `StateSupervisor`: + +```typescript +// Get current value +const value = state.getState(); + +// Set new value (immutable) +state.setState(newValue); +state.setState(prev => ({ ...prev, count: prev.count + 1 })); + +// Mutate with Immer (mutable syntax, immutable result) +state.setStateImmer(draft => { + draft.count += 1; + draft.items.push(newItem); +}); + +// React hook (auto-subscribes component) +function MyComponent() { + const value = state.useState(); + return
{value.count}
; +} + +// RxJS Subject for manual subscriptions +const subscription = state.subject.subscribe(newValue => { + console.log('State changed:', newValue); +}); + +// Cleanup in onDestroy +moduleAPI.onDestroy(() => subscription.unsubscribe()); +``` + +## Choosing the Right State Type + +**Use SharedState when:** +- Changes need immediate sync (< 100ms) +- Data is ephemeral (OK to lose on restart) +- Examples: cursor positions, typing status, live reactions + +**Use PersistentState when:** +- Data must survive restarts +- Changes should sync but don't need instant delivery +- Examples: documents, settings, user profiles + +**Use UserAgentState when:** +- Data is device-specific +- No need to sync across devices +- Examples: UI state, local preferences, cached data diff --git a/packages/springboard/cli/src/docs/index.ts b/packages/springboard/cli/src/docs/index.ts new file mode 100644 index 00000000..363ad211 --- /dev/null +++ b/packages/springboard/cli/src/docs/index.ts @@ -0,0 +1,80 @@ +/** + * Documentation system for Springboard CLI + * + * Provides documentation discovery and retrieval for AI coding agents. + */ + +import { readFileSync } from 'fs'; +import { join } from 'path'; + +export interface DocSection { + slug: string; + title: string; + use_cases: string; +} + +export interface SectionsData { + sections: DocSection[]; +} + +const docsDir = __dirname; + +/** + * Load sections metadata + */ +export function getSections(): DocSection[] { + const data = JSON.parse( + readFileSync(join(docsDir, 'sections.json'), 'utf-8') + ) as SectionsData; + return data.sections; +} + +/** + * Get a section by slug + */ +export function getSection(slug: string): DocSection | undefined { + const sections = getSections(); + return sections.find(s => + s.slug === slug || + s.slug.endsWith(`/${slug}`) || + s.title.toLowerCase() === slug.toLowerCase() + ); +} + +/** + * Get documentation content for a section + */ +export function getDocContent(slug: string): string | null { + const section = getSection(slug); + if (!section) return null; + + // Convert slug to filename: springboard/module-api -> springboard-module-api.md + const filename = section.slug.replace(/\//g, '-') + '.md'; + + try { + return readFileSync(join(docsDir, 'content', filename), 'utf-8'); + } catch { + return null; + } +} + +/** + * List sections with their use_cases for display + */ +export function listSections(): { slug: string; title: string; use_cases: string }[] { + return getSections().map(s => ({ + slug: s.slug, + title: s.title, + use_cases: s.use_cases + })); +} + +/** + * Format sections list for output + */ +export function formatSectionsList(): string { + const sections = listSections(); + return sections.map(s => + `${s.slug}\n ${s.title}\n Use cases: ${s.use_cases}` + ).join('\n\n'); +} diff --git a/packages/springboard/cli/src/docs/sections.json b/packages/springboard/cli/src/docs/sections.json new file mode 100644 index 00000000..7e99dc5d --- /dev/null +++ b/packages/springboard/cli/src/docs/sections.json @@ -0,0 +1,69 @@ +{ + "sections": [ + { + "slug": "springboard/overview", + "title": "Springboard Overview", + "use_cases": "getting started, framework introduction, what is springboard, architecture overview, multi-device apps, real-time sync, full-stack javascript" + }, + { + "slug": "springboard/module-api", + "title": "ModuleAPI Reference", + "use_cases": "always, any springboard module, core API, registerRoute, createActions, onDestroy, getModule, statesAPI, module development" + }, + { + "slug": "springboard/state-management", + "title": "State Management", + "use_cases": "state, data storage, createSharedState, createPersistentState, createUserAgentState, cross-device sync, localStorage, database, StateSupervisor, useState, setState, setStateImmer" + }, + { + "slug": "springboard/actions", + "title": "Actions & RPC", + "use_cases": "actions, createActions, createAction, RPC, remote procedure call, server communication, async functions, action handlers" + }, + { + "slug": "springboard/routing", + "title": "Routing & Navigation", + "use_cases": "routes, registerRoute, navigation, React Router, URL params, documentMeta, SEO, hideApplicationShell, page components" + }, + { + "slug": "springboard/module-types", + "title": "Module Types", + "use_cases": "feature module, utility module, initializer module, module patterns, when to use each type, module organization" + }, + { + "slug": "springboard/module-communication", + "title": "Module Communication", + "use_cases": "getModule, module dependencies, AllModules interface, type-safe module access, interface merging, cross-module calls" + }, + { + "slug": "springboard/core-dependencies", + "title": "Core Dependencies", + "use_cases": "coreDependencies, log, showError, files, storage, rpc, isMaestro, KVStore, error handling, file operations" + }, + { + "slug": "springboard/lifecycle", + "title": "Module Lifecycle", + "use_cases": "onDestroy, cleanup, initialization order, module registration, Provider, subscriptions, memory leaks" + }, + { + "slug": "springboard/platforms", + "title": "Platform Support", + "use_cases": "browser, node, react-native, desktop, tauri, partykit, platform-specific code, deployment targets" + }, + { + "slug": "springboard/server-modules", + "title": "Server Modules", + "use_cases": "serverRegistry, Hono, HTTP routes, API endpoints, RPC middleware, server-side code, backend" + }, + { + "slug": "springboard/patterns", + "title": "Common Patterns", + "use_cases": "best practices, navigation reasons, modal system, activity tracking, hook system, songdrive patterns" + }, + { + "slug": "springboard/anti-patterns", + "title": "Anti-Patterns to Avoid", + "use_cases": "common mistakes, getModule at module level, missing optional chaining, race conditions, computed values in state" + } + ] +} diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts index 21bc3204..2d985ff0 100644 --- a/packages/springboard/cli/src/docs_command.ts +++ b/packages/springboard/cli/src/docs_command.ts @@ -33,8 +33,20 @@ Workflow: .description('List available documentation sections with use cases') .option('--json', 'Output as JSON') .action(async (options: { json?: boolean }) => { - console.log('TODO: Implement list command'); - // Will list all documentation sections with their use_cases keywords + const { listSections } = await import('./docs/index.js'); + const sections = listSections(); + + if (options.json) { + console.log(JSON.stringify(sections, null, 2)); + } else { + console.log('Available Springboard Documentation Sections:\n'); + for (const section of sections) { + console.log(`${section.slug}`); + console.log(` ${section.title}`); + console.log(` Use cases: ${section.use_cases}`); + console.log(); + } + } }); // sb docs get @@ -42,8 +54,24 @@ Workflow: .description('Fetch documentation for specific sections') .argument('', 'Documentation section(s) to fetch') .action(async (sections: string[]) => { - console.log('TODO: Implement get command for sections:', sections); - // Will fetch and output documentation content + const { getDocContent, getSection } = await import('./docs/index.js'); + + for (const slug of sections) { + const section = getSection(slug); + if (!section) { + console.error(`Section "${slug}" not found. Run 'sb docs list' to see available sections.\n`); + continue; + } + + const content = getDocContent(slug); + if (!content) { + console.error(`Content for "${slug}" not available.\n`); + continue; + } + + console.log(content); + console.log('\n---\n'); + } }); // sb docs validate @@ -54,54 +82,239 @@ Workflow: .option('--json', 'Output as JSON (default)') .option('--quiet', 'Only output errors') .action(async (file: string | undefined, options: { stdin?: boolean; json?: boolean; quiet?: boolean }) => { - console.log('TODO: Implement validate command'); - // Will run TypeScript compilation + custom AST visitors + ESLint - // Output: { issues: [], suggestions: [], hasErrors: boolean } - }); - - // sb docs scaffold - const scaffold = docs.command('scaffold') - .description('Generate module templates'); - - scaffold.command('module') - .description('Generate a basic module') - .argument('', 'Module name') - .action(async (name: string) => { - console.log('TODO: Implement scaffold module for:', name); - }); - - scaffold.command('feature') - .description('Generate a feature module') - .argument('', 'Module name') - .action(async (name: string) => { - console.log('TODO: Implement scaffold feature for:', name); - }); - - scaffold.command('utility') - .description('Generate a utility module') - .argument('', 'Module name') - .action(async (name: string) => { - console.log('TODO: Implement scaffold utility for:', name); + // TODO: Implement validation with TypeScript AST analysis + // For now, output a placeholder that explains what will be checked + const result = { + issues: [], + suggestions: [ + 'Validation not yet implemented. Will check for:', + '- getModule called at module level (should be in routes/actions)', + '- Missing optional chaining on module access', + '- Direct state mutation (should use setState/setStateImmer)', + '- Missing onDestroy cleanup for subscriptions', + '- Computed values stored in state (should use useMemo)' + ], + hasErrors: false + }; + console.log(JSON.stringify(result, null, 2)); }); // sb docs context docs.command('context') .description('Output full context prompt for AI agents') .action(async () => { - console.log('TODO: Implement context command'); - // Will output a comprehensive prompt with: - // - Framework overview - // - Available documentation sections - // - Workflow instructions - // - Key concepts + const { formatSectionsList } = await import('./docs/index.js'); + const { listExamples } = await import('./examples/index.js'); + + const sectionsList = formatSectionsList(); + const examples = listExamples(); + + const context = `# Springboard Development Context + +You are working on a Springboard application. Springboard is a full-stack JavaScript framework built on React, Hono, JSON-RPC, and WebSockets for building real-time, multi-device applications. + +## Available Documentation Sections + +${sectionsList} + +## Key Concepts + +### Module Structure +\`\`\`typescript +import springboard from 'springboard'; + +springboard.registerModule('ModuleName', {}, async (moduleAPI) => { + // Create state (pick the right type!) + const state = await moduleAPI.statesAPI.createSharedState('name', initialValue); + + // Create actions (automatically RPC-enabled) + const actions = moduleAPI.createActions({ + actionName: async (args) => { /* ... */ } + }); + + // Register routes + moduleAPI.registerRoute('/', {}, MyComponent); + + // Cleanup + moduleAPI.onDestroy(() => { /* cleanup */ }); + + // Return public API + return { state, actions }; +}); +\`\`\` + +### State Types +- **createSharedState**: Real-time sync across devices (ephemeral) +- **createPersistentState**: Database-backed, survives restarts +- **createUserAgentState**: Local only (localStorage) + +### StateSupervisor Methods +\`\`\`typescript +state.getState() // Get current value +state.setState(newValue) // Immutable update +state.setStateImmer(draft => {}) // Mutable update with Immer +state.useState() // React hook +\`\`\` + +## Workflow + +1. **Use this context** + your React/TypeScript knowledge to write code +2. **Run \`sb docs validate \`** to check for common issues +3. **Fetch specific docs** with \`sb docs get
\` only when needed +4. **See examples** with \`sb docs examples list\` and \`sb docs examples show \` + +## Available Examples + +${examples.map(e => `- ${e.name}: ${e.description}`).join('\n')} + +## Common Mistakes to Avoid + +1. **Don't call getModule at module level** - call inside routes/actions +2. **Use optional chaining** for module access: \`maybeModule?.actions?.doSomething()\` +3. **Don't mutate state directly** - use setState or setStateImmer +4. **Clean up subscriptions** in onDestroy +5. **Don't store computed values** in state - use useMemo + +## Getting Specific Documentation + +If you need detailed documentation on a topic, run: +\`\`\`bash +sb docs get springboard/module-api +sb docs get springboard/state-management +sb docs get springboard/patterns +\`\`\` +`; + + console.log(context); }); // sb docs types docs.command('types') .description('Output core TypeScript type definitions') .action(async () => { - console.log('TODO: Implement types command'); - // Will output ModuleAPI, StateSupervisor, CoreDependencies, etc. + const types = `# Springboard Core Type Definitions + +## ModuleAPI + +\`\`\`typescript +interface ModuleAPI { + moduleId: string; + fullPrefix: string; + + statesAPI: { + createSharedState(name: string, initial: T): Promise>; + createPersistentState(name: string, initial: T): Promise>; + createUserAgentState(name: string, initial: T): Promise>; + }; + + createActions>(actions: T): T; + createAction(name: string, opts: {}, cb: ActionCallback): ActionFn; + + registerRoute(path: string, options: RegisterRouteOptions, component: React.ComponentType): void; + registerApplicationShell(component: React.ComponentType): void; + + getModule(id: K): AllModules[K]; + + onDestroy(callback: () => void): void; + setRpcMode(mode: 'local' | 'remote'): void; + + deps: { + core: CoreDependencies; + module: ModuleDependencies; + }; +} +\`\`\` + +## StateSupervisor + +\`\`\`typescript +interface StateSupervisor { + getState(): T; + setState(value: T | ((prev: T) => T)): T; + setStateImmer(callback: (draft: T) => void): T; + useState(): T; // React hook + subject: Subject; // RxJS Subject +} +\`\`\` + +## CoreDependencies + +\`\`\`typescript +interface CoreDependencies { + log: (...args: any[]) => void; + showError: (error: string) => void; + files: { + saveFile: (name: string, content: string) => Promise; + }; + storage: { + remote: KVStore; + userAgent: KVStore; + }; + rpc: { + remote: Rpc; + local?: Rpc; + }; + isMaestro: () => boolean; +} +\`\`\` + +## KVStore + +\`\`\`typescript +interface KVStore { + get(key: string): Promise; + set(key: string, value: T): Promise; + getAll(): Promise | null>; +} +\`\`\` + +## RegisterRouteOptions + +\`\`\`typescript +interface RegisterRouteOptions { + hideApplicationShell?: boolean; + documentMeta?: DocumentMeta | ((context: RouteContext) => DocumentMeta); +} + +interface DocumentMeta { + title?: string; + description?: string; + keywords?: string; + 'og:title'?: string; + 'og:description'?: string; + 'og:image'?: string; + [key: string]: string | undefined; +} +\`\`\` + +## Module Interface Merging + +\`\`\`typescript +// Declare your module's exports for type-safe getModule() +declare module 'springboard/module_registry/module_registry' { + interface AllModules { + myModule: { + state: StateSupervisor; + actions: { + doSomething: (args: Args) => Promise; + }; + }; + } +} +\`\`\` +`; + + console.log(types); + }); + + // sb docs scaffold (keeping for now - may remove in favor of examples) + const scaffold = docs.command('scaffold') + .description('Generate module templates (use "examples show" for reference code)') + .action(() => { + console.log('For module templates, use examples instead:'); + console.log(' sb docs examples list'); + console.log(' sb docs examples show basic-feature-module'); + console.log('\nExamples provide complete, working code you can copy and modify.'); }); // sb docs examples From b9f4c54d9c359ca59c95591f0e1ee4a19ce1e9a8 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 9 Feb 2026 04:08:35 +0000 Subject: [PATCH 10/20] Remove validate and scaffold commands from sb docs These commands don't match svelte-mcp patterns: - validate: svelte-mcp has real AST validation, ours was just a placeholder - scaffold: svelte-mcp doesn't have scaffolding, uses playground-link instead Remaining commands match svelte-mcp interface: - list: matches list-sections tool - get: matches get-documentation tool - context: matches svelte-task prompt - types: Springboard-specific addition - examples: replaces playground-link for offline use Co-Authored-By: Claude Opus 4.5 --- packages/springboard/cli/src/docs_command.ts | 48 +++----------------- 1 file changed, 6 insertions(+), 42 deletions(-) diff --git a/packages/springboard/cli/src/docs_command.ts b/packages/springboard/cli/src/docs_command.ts index 2d985ff0..91290bbf 100644 --- a/packages/springboard/cli/src/docs_command.ts +++ b/packages/springboard/cli/src/docs_command.ts @@ -3,8 +3,8 @@ import { Command } from 'commander'; /** * Creates the `sb docs` command with all subcommands for AI agent support. * - * Provides documentation discovery, code validation, scaffolding, and context - * for AI coding agents working with Springboard applications. + * Provides documentation discovery and context for AI coding agents + * working with Springboard applications. */ export function createDocsCommand(): Command { const docs = new Command('docs') @@ -20,8 +20,8 @@ Getting Started: Workflow: 1. sb docs context # Get full framework context (run this first) - 2. sb docs validate # Check your code follows Springboard patterns - 3. sb docs get
# Fetch specific docs only when needed + 2. sb docs get
# Fetch specific docs only when needed + 3. sb docs examples show # View example code `) .action(() => { // When `sb docs` is called without subcommand, show help @@ -74,31 +74,6 @@ Workflow: } }); - // sb docs validate - docs.command('validate') - .description('Validate a module file for common issues') - .argument('[file]', 'Module file to validate') - .option('--stdin', 'Read code from stdin instead of file') - .option('--json', 'Output as JSON (default)') - .option('--quiet', 'Only output errors') - .action(async (file: string | undefined, options: { stdin?: boolean; json?: boolean; quiet?: boolean }) => { - // TODO: Implement validation with TypeScript AST analysis - // For now, output a placeholder that explains what will be checked - const result = { - issues: [], - suggestions: [ - 'Validation not yet implemented. Will check for:', - '- getModule called at module level (should be in routes/actions)', - '- Missing optional chaining on module access', - '- Direct state mutation (should use setState/setStateImmer)', - '- Missing onDestroy cleanup for subscriptions', - '- Computed values stored in state (should use useMemo)' - ], - hasErrors: false - }; - console.log(JSON.stringify(result, null, 2)); - }); - // sb docs context docs.command('context') .description('Output full context prompt for AI agents') @@ -159,9 +134,8 @@ state.useState() // React hook ## Workflow 1. **Use this context** + your React/TypeScript knowledge to write code -2. **Run \`sb docs validate \`** to check for common issues -3. **Fetch specific docs** with \`sb docs get
\` only when needed -4. **See examples** with \`sb docs examples list\` and \`sb docs examples show \` +2. **Fetch specific docs** with \`sb docs get
\` only when needed +3. **See examples** with \`sb docs examples list\` and \`sb docs examples show \` ## Available Examples @@ -307,16 +281,6 @@ declare module 'springboard/module_registry/module_registry' { console.log(types); }); - // sb docs scaffold (keeping for now - may remove in favor of examples) - const scaffold = docs.command('scaffold') - .description('Generate module templates (use "examples show" for reference code)') - .action(() => { - console.log('For module templates, use examples instead:'); - console.log(' sb docs examples list'); - console.log(' sb docs examples show basic-feature-module'); - console.log('\nExamples provide complete, working code you can copy and modify.'); - }); - // sb docs examples const examplesCmd = docs.command('examples') .description('View example modules'); From a033f9b9cc01233ec880f9c8b6f285ed6abb5063 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 9 Feb 2026 04:15:26 +0000 Subject: [PATCH 11/20] Add useModule hook for React component module access Port the useModule pattern from SongDrive to Springboard core. This hook provides type-safe module access from React components: const audioPlayer = useModule('AudioPlayer'); const currentFile = audioPlayer.currentlyPlayingFile.useState(); Use this instead of moduleAPI.getModule() when in React components. Co-Authored-By: Claude Opus 4.5 --- packages/springboard/core/engine/engine.tsx | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/packages/springboard/core/engine/engine.tsx b/packages/springboard/core/engine/engine.tsx index 1782a7d4..88b77b28 100644 --- a/packages/springboard/core/engine/engine.tsx +++ b/packages/springboard/core/engine/engine.tsx @@ -5,7 +5,7 @@ import {ClassModuleCallback, ModuleCallback, RegisterModuleOptions, springboard, import React, {createContext, useContext, useState} from 'react'; import {useMount} from 'springboard/hooks/useMount'; -import {ExtraModuleDependencies, Module, ModuleRegistry} from 'springboard/module_registry/module_registry'; +import {AllModules, ExtraModuleDependencies, Module, ModuleRegistry} from 'springboard/module_registry/module_registry'; import {SharedStateService} from '../services/states/shared_state_service'; import {ModuleAPI} from './module_api'; @@ -227,6 +227,19 @@ export const useSpringboardEngine = () => { return useContext(engineContext); }; +/** + * React hook to access a module by ID from within a component. + * Use this instead of moduleAPI.getModule() when in React components. + * + * @example + * const audioPlayer = useModule('AudioPlayer'); + * const currentFile = audioPlayer.currentlyPlayingFile.useState(); + */ +export const useModule = (moduleId: ModuleId): AllModules[ModuleId] => { + const engine = useSpringboardEngine(); + return engine.moduleRegistry.getModule(moduleId); +}; + type SpringboardProviderProps = React.PropsWithChildren<{ engine: Springboard; }>; From 7f9d079a4d70140142680a1927b9954598bb0c4f Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 05:49:41 +0000 Subject: [PATCH 12/20] Remove validate references from generated CLAUDE.md and AGENTS.md Update the generated agent instruction files to reflect the removal of sb docs validate. Replace with examples commands in the workflow. Co-Authored-By: Claude Opus 4.5 --- .../create-springboard-app/src/cli.ts | 20 ++++++++----------- 1 file changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/springboard/create-springboard-app/src/cli.ts b/packages/springboard/create-springboard-app/src/cli.ts index 8c638e67..7f1f07e4 100644 --- a/packages/springboard/create-springboard-app/src/cli.ts +++ b/packages/springboard/create-springboard-app/src/cli.ts @@ -93,19 +93,17 @@ documentation sections. It's the single command you need to start working effici 2. **Write code** using your knowledge + the context from step 1 -3. **Run \`sb docs validate \`** to check your code - - Fix any issues reported - - Re-run validate until clean - -4. **Fetch specific docs** only when needed: +3. **Fetch specific docs** only when needed: - \`sb docs get
\` - Use section names from the context output +4. **View examples** for reference code: + - \`sb docs examples list\` - See available examples + - \`sb docs examples show \` - View example code + ## Other Useful Commands - \`sb docs --help\` - See all available commands - \`sb docs types\` - Get TypeScript type definitions - -This approach ensures your code follows Springboard patterns and avoids common mistakes. `; writeFileSync(`${process.cwd()}/CLAUDE.md`, claudeMdContent); console.log('Created CLAUDE.md with AI agent instructions'); @@ -137,16 +135,14 @@ Please lean on the \`sb docs\` commands to make sure the code you're writing is 1. **Start with context**: Run \`sb docs context\` (includes full docs list) 2. **Write code**: Use your knowledge + the context from step 1 -3. **Validate often**: Run \`sb docs validate \` to catch issues early -4. **Fetch docs when needed**: Use \`sb docs get
\` for specific topics +3. **Fetch docs when needed**: Use \`sb docs get
\` for specific topics +4. **View examples**: Use \`sb docs examples show \` for reference code ### Other Commands - \`sb docs --help\` - See all available commands - \`sb docs types\` - Get TypeScript type definitions -- \`sb docs list\` - Just the docs list (context includes this already) - -These tools help prevent common mistakes and ensure your code aligns with Springboard's architecture. +- \`sb docs examples list\` - See available example modules `; writeFileSync(`${process.cwd()}/AGENTS.md`, agentsMdContent); console.log('Created AGENTS.md with AI agent instructions'); From 98e4760d25d4798287c906c3391744c4e8a72484 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 05:50:43 +0000 Subject: [PATCH 13/20] Consolidate CLAUDE.md and AGENTS.md into single template No benefit to maintaining two different versions of the same content. Use one string for both files. Co-Authored-By: Claude Opus 4.6 --- .../create-springboard-app/src/cli.ts | 63 +++---------------- 1 file changed, 9 insertions(+), 54 deletions(-) diff --git a/packages/springboard/create-springboard-app/src/cli.ts b/packages/springboard/create-springboard-app/src/cli.ts index 7f1f07e4..d076d303 100644 --- a/packages/springboard/create-springboard-app/src/cli.ts +++ b/packages/springboard/create-springboard-app/src/cli.ts @@ -69,8 +69,8 @@ program writeFileSync(`${process.cwd()}/src/index.tsx`, exampleString); console.log('Created application entrypoint src/index.tsx'); - // Create CLAUDE.md for Claude Code agents - const claudeMdContent = `# Springboard Development Guide + // Create CLAUDE.md and AGENTS.md for AI coding agents + const agentDocsContent = `# Springboard Development Guide This application is built with the **Springboard framework**. @@ -82,70 +82,25 @@ This application is built with the **Springboard framework**. npx sb docs context \`\`\` -This command outputs comprehensive framework information including all available -documentation sections. It's the single command you need to start working efficiently. +This outputs comprehensive framework information including available documentation +sections, key concepts, and workflow guidance. ## Recommended Workflow 1. **Run \`sb docs context\`** at the start of your session - - This gives you everything: framework overview, available docs, and workflow guidance - - You don't need to run \`sb docs list\` separately - 2. **Write code** using your knowledge + the context from step 1 - -3. **Fetch specific docs** only when needed: - - \`sb docs get
\` - Use section names from the context output - -4. **View examples** for reference code: - - \`sb docs examples list\` - See available examples - - \`sb docs examples show \` - View example code +3. **Fetch specific docs** only when needed: \`sb docs get
\` +4. **View examples** for reference code: \`sb docs examples show \` ## Other Useful Commands -- \`sb docs --help\` - See all available commands -- \`sb docs types\` - Get TypeScript type definitions -`; - writeFileSync(`${process.cwd()}/CLAUDE.md`, claudeMdContent); - console.log('Created CLAUDE.md with AI agent instructions'); - - // Create AGENTS.md for other AI coding assistants - const agentsMdContent = `# AI Agent Instructions - -This application is created with the **Springboard framework**. - -## Before You Start - -Run this command to get comprehensive framework information: - -\`\`\`bash -npx sb docs context -\`\`\` - -This single command provides: -- Framework overview and key concepts -- Full list of available documentation sections with use cases -- Recommended workflow for AI agents -- Everything you need to start coding - -## Important - -Please lean on the \`sb docs\` commands to make sure the code you're writing is correct and uses the framework as intended. - -### Recommended Workflow - -1. **Start with context**: Run \`sb docs context\` (includes full docs list) -2. **Write code**: Use your knowledge + the context from step 1 -3. **Fetch docs when needed**: Use \`sb docs get
\` for specific topics -4. **View examples**: Use \`sb docs examples show \` for reference code - -### Other Commands - - \`sb docs --help\` - See all available commands - \`sb docs types\` - Get TypeScript type definitions - \`sb docs examples list\` - See available example modules `; - writeFileSync(`${process.cwd()}/AGENTS.md`, agentsMdContent); - console.log('Created AGENTS.md with AI agent instructions'); + writeFileSync(`${process.cwd()}/CLAUDE.md`, agentDocsContent); + writeFileSync(`${process.cwd()}/AGENTS.md`, agentDocsContent); + console.log('Created CLAUDE.md and AGENTS.md with AI agent instructions'); const packageJsonPath = `${process.cwd()}/package.json`; const packageJson = JSON.parse(readFileSync(packageJsonPath).toString()); From 5001fbe7e7395bfec80948fb1c397e1a3fc386e3 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:20:05 +0000 Subject: [PATCH 14/20] Include CLI in springboard package with sb bin alias Add the CLI to the main springboard package: - Add bin entry pointing to cli/dist/cli.js as 'sb' command - Include cli/dist in published files - Update build-all.sh to build CLI - Update prepublishOnly to use build:all This allows users to run 'sb docs' commands after installing springboard. Co-Authored-By: Claude Opus 4.6 --- packages/springboard/package.json | 8 ++++++-- packages/springboard/scripts/build-all.sh | 8 +++++++- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/packages/springboard/package.json b/packages/springboard/package.json index 59019bf6..d3461ca4 100644 --- a/packages/springboard/package.json +++ b/packages/springboard/package.json @@ -322,10 +322,14 @@ ] } }, + "bin": { + "sb": "./cli/dist/cli.js" + }, "files": [ "src", "dist", - "vite-plugin" + "vite-plugin", + "cli/dist" ], "scripts": { "test": "vitest --run", @@ -337,7 +341,7 @@ "build:watch": "npm run build -- --watch", "build:all": "./scripts/build-all.sh", "build:vite-plugin": "cd vite-plugin && npm run build", - "prepublishOnly": "npm run build && npm run build:vite-plugin", + "prepublishOnly": "npm run build:all", "publish:local": "./scripts/publish-local.sh" }, "repository": { diff --git a/packages/springboard/scripts/build-all.sh b/packages/springboard/scripts/build-all.sh index 23ca1b54..bb2ff39c 100755 --- a/packages/springboard/scripts/build-all.sh +++ b/packages/springboard/scripts/build-all.sh @@ -17,7 +17,12 @@ cd "$PACKAGE_DIR" pnpm build echo "" -echo "2. Building vite-plugin..." +echo "2. Building CLI..." +cd "$PACKAGE_DIR/cli" +pnpm build + +echo "" +echo "3. Building vite-plugin..." cd "$PACKAGE_DIR/vite-plugin" pnpm build @@ -27,4 +32,5 @@ echo "✓ Build complete!" echo "" echo "Outputs:" echo " - Main package: $PACKAGE_DIR/dist/" +echo " - CLI: $PACKAGE_DIR/cli/dist/" echo " - Vite plugin: $PACKAGE_DIR/vite-plugin/dist/" From 87e0959409a4c2d2371c91ef056d448658e7a1c0 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:29:23 +0000 Subject: [PATCH 15/20] change docs command to be imported with require --- packages/springboard/cli/src/cli.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index a1f7c69a..d935972c 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -17,9 +17,9 @@ import concurrently from 'concurrently'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); -const packageJSON = require('../package.json'); +const packageJSON = require('../../package.json'); -import {createDocsCommand} from './docs_command'; +const createDocsCommand = require('./docs_command'); /** * Resolve an entrypoint path to an absolute path From db24ad818fe1b3821dfab9eedd816a37b28740db Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:30:20 +0000 Subject: [PATCH 16/20] remove comments --- packages/springboard/cli/src/cli.ts | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index d935972c..dee299a4 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -1,17 +1,4 @@ -/** - * Springboard CLI - * - * Vite-based CLI wrapper for multi-platform application builds. - * Implements Option D: Monolithic CLI Wrapper from PLAN_VITE_CLI_INTEGRATION.md - * - * Commands: - * - sb dev - Start development server with HMR - * - sb build - Build for production - * - sb start - Start the production server - */ - import path from 'path'; -import fs from 'node:fs'; import { program } from 'commander'; import concurrently from 'concurrently'; import { createRequire } from 'node:module'; From e1a86ce1439bf6f8b646863a2bd200d209bc7f0b Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:36:01 +0000 Subject: [PATCH 17/20] add commander dep to springboard --- packages/springboard/cli/src/cli.ts | 28 ++++++++++++++-------------- packages/springboard/package.json | 1 + pnpm-lock.yaml | 13 +++++++++++-- 3 files changed, 26 insertions(+), 16 deletions(-) diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index dee299a4..69d5bd4f 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -1,6 +1,6 @@ import path from 'path'; import { program } from 'commander'; -import concurrently from 'concurrently'; +// import concurrently from 'concurrently'; import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); @@ -184,19 +184,19 @@ program .action(async () => { console.log('Starting production server...'); - concurrently( - [ - { - command: 'node dist/server/dist/local-server.cjs', - name: 'Server', - prefixColor: 'blue', - }, - ], - { - prefix: 'name', - restartTries: 0, - } - ); + // concurrently( + // [ + // { + // command: 'node dist/server/dist/local-server.cjs', + // name: 'Server', + // prefixColor: 'blue', + // }, + // ], + // { + // prefix: 'name', + // restartTries: 0, + // } + // ); }); program.addCommand(createDocsCommand()); diff --git a/packages/springboard/package.json b/packages/springboard/package.json index d3461ca4..71a7907b 100644 --- a/packages/springboard/package.json +++ b/packages/springboard/package.json @@ -366,6 +366,7 @@ }, "homepage": "https://springboard.js.org", "dependencies": { + "commander": "^14.0.3", "dexie": "^4.2.1", "json-rpc-2.0": "^1.7.1", "reconnecting-websocket": "^4.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index d9a3d1e3..5de93829 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -417,6 +417,9 @@ importers: '@tauri-apps/plugin-shell': specifier: ^2.3.3 version: 2.3.3 + commander: + specifier: ^14.0.3 + version: 14.0.3 crossws: specifier: ^0.4.4 version: 0.4.4 @@ -2000,6 +2003,10 @@ packages: resolution: {integrity: sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==} engines: {node: '>=18'} + commander@14.0.3: + resolution: {integrity: sha512-H+y0Jo/T1RZ9qPP4Eh1pkcQcLRglraJaSLoyOtHxu6AapkjWVCy2Sit1QQ4x3Dng8qDlSsZEet7g5Pq06MvTgw==} + engines: {node: '>=20'} + commander@4.1.1: resolution: {integrity: sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==} engines: {node: '>= 6'} @@ -2471,7 +2478,7 @@ packages: glob@7.2.3: resolution: {integrity: sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==} - deprecated: Glob versions prior to v9 are no longer supported + deprecated: Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me globals@13.24.0: resolution: {integrity: sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==} @@ -5491,7 +5498,7 @@ snapshots: sirv: 3.0.2 tinyglobby: 0.2.15 tinyrainbow: 3.0.3 - vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@25.2.3)(@vitest/ui@4.0.18)(jsdom@25.0.1)(tsx@4.20.6) + vitest: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@20.17.48)(@vitest/ui@4.0.18)(jsdom@25.0.1)(tsx@4.21.0) '@vitest/utils@2.1.9': dependencies: @@ -5785,6 +5792,8 @@ snapshots: commander@12.1.0: {} + commander@14.0.3: {} + commander@4.1.1: {} composed-offset-position@0.0.6(@floating-ui/utils@0.2.10): From 80a0f24672cd5caf1ccf230cd00f9aafa878ac6a Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:38:15 +0000 Subject: [PATCH 18/20] fix import --- packages/springboard/cli/src/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index 69d5bd4f..cce9830c 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -6,7 +6,7 @@ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const packageJSON = require('../../package.json'); -const createDocsCommand = require('./docs_command'); +const {createDocsCommand} = require('./docs_command'); /** * Resolve an entrypoint path to an absolute path From 549964d708ce805fe45a205e1511a9d6c8505329 Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:47:34 +0000 Subject: [PATCH 19/20] Fix ES module __dirname usage in CLI Replace __dirname with ES module equivalent using import.meta.url: - import { fileURLToPath } from 'url' - import { dirname } from 'path' - const __filename = fileURLToPath(import.meta.url) - const __dirname = dirname(__filename) Fixes ReferenceError when CLI is run from published package. Co-Authored-By: Claude Opus 4.6 --- packages/springboard/cli/src/docs/index.ts | 5 ++++- packages/springboard/cli/src/examples/index.ts | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/springboard/cli/src/docs/index.ts b/packages/springboard/cli/src/docs/index.ts index 363ad211..28bd628e 100644 --- a/packages/springboard/cli/src/docs/index.ts +++ b/packages/springboard/cli/src/docs/index.ts @@ -5,7 +5,8 @@ */ import { readFileSync } from 'fs'; -import { join } from 'path'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; export interface DocSection { slug: string; @@ -17,6 +18,8 @@ export interface SectionsData { sections: DocSection[]; } +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const docsDir = __dirname; /** diff --git a/packages/springboard/cli/src/examples/index.ts b/packages/springboard/cli/src/examples/index.ts index 2c07b6c1..719e9d48 100644 --- a/packages/springboard/cli/src/examples/index.ts +++ b/packages/springboard/cli/src/examples/index.ts @@ -6,7 +6,8 @@ */ import { readFileSync } from 'fs'; -import { join } from 'path'; +import { join, dirname } from 'path'; +import { fileURLToPath } from 'url'; export interface Example { name: string; @@ -17,6 +18,8 @@ export interface Example { code: string; } +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const examplesDir = __dirname; export const examples: Example[] = [ From 72a1841759fb8d1b912c0f5de6fe98249a97f05d Mon Sep 17 00:00:00 2001 From: Vibe Kanban Date: Mon, 16 Feb 2026 07:49:49 +0000 Subject: [PATCH 20/20] use esm for docs import --- packages/springboard/cli/src/cli.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/springboard/cli/src/cli.ts b/packages/springboard/cli/src/cli.ts index cce9830c..1986e8f7 100644 --- a/packages/springboard/cli/src/cli.ts +++ b/packages/springboard/cli/src/cli.ts @@ -6,7 +6,7 @@ import { createRequire } from 'node:module'; const require = createRequire(import.meta.url); const packageJSON = require('../../package.json'); -const {createDocsCommand} = require('./docs_command'); +import {createDocsCommand} from './docs_command.js'; /** * Resolve an entrypoint path to an absolute path