diff --git a/.claude/commands/add-attachment.md b/.claude/commands/add-attachment.md deleted file mode 100644 index fbbe7d50c..000000000 --- a/.claude/commands/add-attachment.md +++ /dev/null @@ -1,175 +0,0 @@ -# Adding Attachments to Outliner - -This guide explains how to attach charms to nodes in the outliner tree. - -The user wants to: $ARGUMENTS - -## Overview - -Attachments allow you to link charms to specific nodes in a page's outline structure. Any charm can be attached to provide additional functionality, data, or visual representations at that location. - -## Prerequisites - -- A page charm with an outline structure -- A charm to attach (either existing or newly created from a recipe) -- CT binary configured with proper credentials - -## Basic Steps - -### Step 1: Identify Target Nodes - -First, find the page charm and locate nodes where you want to add attachments: - -```bash -# Get the outline structure -./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [PAGE_CHARM_ID] outline - -# Filter for specific nodes (e.g., nodes without attachments) -./dist/ct charm get --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [PAGE_CHARM_ID] outline | jq '.root.children[].children[] | select(.attachments == [])' -``` - -### Step 2: Create or Identify Attachment Charm - -Either use an existing charm or create a new one from a recipe: - -```bash -# Create new charm from recipe -./dist/ct charm new --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [RECIPES_PATH]/[recipe-name].tsx -# Returns: NEW_CHARM_ID - -# Configure the charm if needed -echo '[INPUT_DATA]' | ./dist/ct charm set --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] --charm [NEW_CHARM_ID] [INPUT_NAME] --input -``` - -### Step 3: Link Charm to Node - -Attach the charm to the target node's attachments array: - -```bash -./dist/ct charm link --identity [IDENTITY_FILE] --api-url [API_URL] --space [SPACE_NAME] [ATTACHMENT_CHARM_ID] [PAGE_CHARM_ID]/[PATH_TO_NODE]/attachments/[INDEX] -``` - -## Path Structure - -The outline follows this hierarchy: -``` -root -├── body: "text content" -├── children: [ -│ ├── [0] -│ │ ├── body: "child text" -│ │ ├── children: [...] -│ │ └── attachments: [] -│ └── [1] -│ ├── body: "another child" -│ ├── children: [...] -│ └── attachments: [] -└── attachments: [] -``` - -Example paths: -- `page/outline/root/attachments/0` - First attachment at root -- `page/outline/root/children/0/attachments/0` - First attachment of first child -- `page/outline/root/children/1/children/2/attachments/0` - Nested child attachment - -## Common Attachment Patterns - -### 1. Data Fetchers -Attach charms that fetch and display external data (GitHub repos, APIs, databases): -```bash -# Example: Attach a data fetcher to a node mentioning a resource -./dist/ct charm link --identity [...] [FETCHER_CHARM] [PAGE]/outline/root/children/0/attachments/0 -``` - -### 2. Visualizations -Attach charts, graphs, or other visual representations: -```bash -# Example: Attach a chart to display metrics mentioned in text -./dist/ct charm link --identity [...] [CHART_CHARM] [PAGE]/outline/root/children/1/attachments/0 -``` - -### 3. Interactive Elements -Attach forms, buttons, or interactive components: -```bash -# Example: Attach an input form to a task node -./dist/ct charm link --identity [...] [FORM_CHARM] [PAGE]/outline/root/children/2/attachments/0 -``` - -### 4. Multiple Attachments -Nodes can have multiple attachments at different indices: -```bash -# Attach multiple charms to the same node -./dist/ct charm link --identity [...] [CHARM_1] [PAGE]/outline/root/attachments/0 -./dist/ct charm link --identity [...] [CHARM_2] [PAGE]/outline/root/attachments/1 -./dist/ct charm link --identity [...] [CHARM_3] [PAGE]/outline/root/attachments/2 -``` - -## Efficient Querying - -When working with large outlines or many attachments: - -### Filter Unlinked Nodes -```bash -# Find nodes without attachments -jq '.root.children[].children[] | select(.attachments == []) | {body: .body, path: path(.)}' -``` - -### Target Specific Paths -```bash -# Get only a specific branch to avoid large responses -jq '.root.children[0].children[3]' -``` - -### Check Existing Attachments -```bash -# List nodes with attachments -jq '.. | select(.attachments? and .attachments != []) | {body: .body, attachments: .attachments | length}' -``` - -## Verification - -After linking, verify the attachment: - -```bash -# Check the specific node's attachments -./dist/ct charm get --identity [...] --charm [PAGE_CHARM_ID] outline | jq '[PATH_TO_NODE].attachments' - -# Example: Check root's first attachment -./dist/ct charm get --identity [...] --charm [PAGE_CHARM_ID] outline | jq '.root.attachments[0]' -``` - -## Tips - -1. **Index Management**: Attachments are indexed arrays. Use index 0 for the first attachment, 1 for the second, etc. - -2. **Selective Updates**: Target specific nodes rather than re-processing entire outlines. - -3. **Attachment Types**: Any charm can be an attachment - consider what makes sense for the content. - -4. **Performance**: When outlines contain many attachments with large data, use jq filters to query only needed parts. - -## Example: Complete Workflow - -```bash -# 1. Find a page charm -./dist/ct charm ls --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace -# Returns: page123 - -# 2. Examine outline for attachment points -./dist/ct charm get --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm page123 outline | jq '.root.children[0]' -# Found: Node at children[0] needs a visualization - -# 3. Create visualization charm -./dist/ct charm new --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace ~/recipes/chart.tsx -# Returns: chart456 - -# 4. Configure the chart -echo '{"data": [1,2,3,4,5]}' | ./dist/ct charm set --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm chart456 chartData --input - -# 5. Attach to node -./dist/ct charm link --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace chart456 page123/outline/root/children/0/attachments/0 - -# 6. Verify -./dist/ct charm get --identity ~/dev/.ct.key --api-url https://api.example.com --space myspace --charm page123 outline | jq '.root.children[0].attachments | length' -# Returns: 1 (success!) -``` diff --git a/.claude/commands/deploy-research.md b/.claude/commands/deploy-research.md deleted file mode 100644 index 461125fd6..000000000 --- a/.claude/commands/deploy-research.md +++ /dev/null @@ -1,72 +0,0 @@ -# Deploy Research Command - -Deploy research findings as a CommonTools research report. - -## Usage - -`/deploy-research [research content or file]` - -## Process - -Launch a deployment subagent using the Task tool: - -``` -Task: Deploy research findings to CommonTools - -You are a deployment specialist. Take the provided research content and deploy it as a CommonTools research report. - -**Your Task:** -1. Check for claude-research.key identity (create if needed) -2. Deploy charm using recipes/research-report.tsx -3. Set title and content using CT commands -4. Return the final URL to the user - -**Research Content:** [paste research findings here] - -**Return to me:** The deployed research report URL. -``` - -## Deployment Steps - -### 1. Check/Generate Claude Identity -```bash -# Check for existing identity -ls -la claude-research.key - -# If not found, generate new one -./dist/ct id new > claude-research.key -``` - -### 2. Create Research Report Charm -```bash -./dist/ct charm new --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev recipes/research-report.tsx -``` - -### 3. Set Title and Content -```bash -# Set the title -echo '"Research: "' | ./dist/ct charm set --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev --charm title - -# Set the content (use temp file for complex content) -cat research-content.tmp | jq -Rs . | ./dist/ct charm set --identity claude-research.key --api-url https://toolshed.saga-castor.ts.net --space YYYY-MM-DD-claude-dev --charm content - -# Clean up -rm research-content.tmp -``` - -### 4. Provide URL -Return the final URL: -``` -https://toolshed.saga-castor.ts.net/YYYY-MM-DD-claude-dev/ -``` - -## Standard Parameters -- **Identity**: `claude-research.key` -- **API URL**: `https://toolshed.saga-castor.ts.net` -- **Space**: `YYYY-MM-DD-claude-dev` (use current date) -- **Recipe**: `recipes/research-report.tsx` - -## Error Handling -- If deployment fails, provide research findings directly to user -- Don't block research results on deployment issues -- Validate JSON formatting before setting content \ No newline at end of file diff --git a/.claude/commands/explore-recipe.md b/.claude/commands/explore-recipe.md deleted file mode 100644 index de4c13f85..000000000 --- a/.claude/commands/explore-recipe.md +++ /dev/null @@ -1,12 +0,0 @@ -- you can start local dev using `scripts/start-local-dev.sh` -- read `ct.md` to understand the `ct` binary -- IMPORTANT: when you load the shell in the browser, you may have to complete registration by generating a one-time passphrase (this will persist between sessions) -- use playwright MCP to explore recipes and charms from the browser - - use ct to support and debug your exporation -- if the user gives an instruction like 'explore packages/patterns/counter.tsx` you should work out how to deploy it using ct, load it in the browser using playwright and explore it based on the recipe's sourcecode - - then you should describe what you find to the user -- if the user gives no spacename, use the current date + a descriptive suffix e.g. `2025-06-01-counter` -- you can stop the server using `scripts/stop-local-dev.sh` -- you can restart the server using `scripts/start-local-dev.sh --force` -- you should visit the charm directly using http://localhost:8000// -- DO NOT deploy the charms to the remote server, make sure you use `--api-url http://localhost:8000` with `ct` diff --git a/.claude/commands/imagine-recipe.md b/.claude/commands/imagine-recipe.md deleted file mode 100644 index 8c0406193..000000000 --- a/.claude/commands/imagine-recipe.md +++ /dev/null @@ -1,149 +0,0 @@ -# Interactive Recipe Imagination and Creation - -This script guides Claude through creating new CommonTools recipes based on user ideas. It follows a streamlined approach similar to recipe development but focuses on bringing new recipe concepts to life. - -## Prerequisites - -**Before starting recipe imagination:** -- User should have an existing space or have run the space setup script -- Claude MUST read the common CT setup instructions in `docs/common/CT.md` - -**Recipe Documentation Reference:** -Before working on recipes, search for these documentation files in the `docs/common` folder: -- `RECIPES.md` - Core recipe development patterns and examples -- `COMPONENTS.md` - Available UI components and usage patterns -- `HANDLERS.md` - Event handler patterns and troubleshooting - -The user provides an initial prompt describing what they want their recipe to do: $ARGUMENTS - -## Script Flow for Claude - -### STEP 1: Initial Setup and Context - -**Read common setup instructions:** -- First, read `docs/common/CT.md` for shared CT binary setup -- Follow those instructions for CT binary check, identity management, environment setup -- Collect required parameters (API URL, space name, recipe path, identity file) - -**Verify existing space:** -- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` -- Show user the existing charms in their space for context -- Ask clarifying questions about the recipe idea - -### STEP 2: Requirements Gathering and Research - -**Clarify the recipe requirements:** -1. Ask targeted questions about: - - What inputs the recipe needs (other charm results, user inputs, external APIs) - - What outputs it should produce - - What UI interactions are needed - - How it should integrate with existing charms - -**Research existing patterns:** -1. Search user's recipe repo: `find [recipe-path] -name "*.tsx" -type f | head -20` -2. **Search patterns package:** Look in `packages/patterns` for related examples and reusable components -3. Look for similar recipes or reusable patterns -4. Check existing space charms for potential data sources and targets -5. Reference the recipe documentation files for patterns and components - -### STEP 3: Design and Plan - -**Create implementation plan:** -1. Design the recipe structure (single file vs multi-file) -2. Plan the input/output schemas -3. Identify UI components needed (reference COMPONENTS.md) -4. Plan integration and linking strategy -5. Present plan to user and get approval - -### STEP 4: Implementation - -**Create the recipe:** -1. Ensure TypeScript setup: User should have run `ct init` in recipes directory -2. Create the recipe file following CommonTools patterns -3. Implement UI components using `ct-` prefixed components -4. Define proper schemas and handlers (reference HANDLERS.md for patterns) -5. Add error handling and validation - -**Test syntax (if requested or if deployment fails):** -- Run: `./dist/ct dev [recipe-file] --no-run` -- Fix any syntax errors - -### STEP 5: Deploy and Test - -**Deploy new charm:** -1. Deploy: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-file]` -2. Record the new CHARM_ID -3. Verify deployment: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` - -**Create integrations:** -1. Link to data sources: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] [source]/[field] [target]/[input]` -2. Verify links work: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]` - -**Test and refine:** -1. Use inspection commands to verify behavior -2. Use cell operations for testing: `./dist/ct charm get/set` commands -3. **Use Playwright for UI testing (if MCP available):** Test the recipe's user interface by navigating to the space URL and interacting with the deployed charm -4. Make refinements using `./dist/ct charm setsrc` if needed -5. Iterate based on user feedback - -### STEP 6: Documentation and Handoff - -**Finalize the recipe:** -- Verify it meets original requirements -- Ensure proper integration with existing charms -- Add helpful comments and documentation - -**Repository management:** -- Help save recipe to correct location -- Guide git workflow if user wants to commit -- Provide usage instructions - -## Common Recipe Patterns - -**Recipe types to consider:** -- **Filter recipes**: Process collections, output filtered subsets -- **Transformer recipes**: Convert data between formats -- **Aggregator recipes**: Combine multiple inputs -- **Generator recipes**: Create new data based on inputs -- **UI recipes**: Provide interactive interfaces -- **Integration recipes**: Connect to external APIs - -## Key Commands Reference - -```bash -# List charms -./dist/ct charm ls --identity [key] --api-url [url] --space [space] - -# Create new charm -./dist/ct charm new --identity [key] --api-url [url] --space [space] [recipe-file] - -# Link charms -./dist/ct charm link --identity [key] --api-url [url] --space [space] [source]/[field] [target]/[input] - -# Inspect charm -./dist/ct charm inspect --identity [key] --api-url [url] --space [space] --charm [id] - -# Update recipe source -./dist/ct charm setsrc --identity [key] --api-url [url] --space [space] --charm [id] [recipe-file] - -# Test recipe syntax -./dist/ct dev [recipe-file] --no-run - -# Cell operations for testing -./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [id] [path] -echo '[json-value]' | ./dist/ct charm set --identity [key] --api-url [url] --space [space] --charm [id] [path] -``` - -## Notes for Claude - -- **Always search `packages/patterns` and the recipes repository first** - Look for related examples before writing new code -- Start simple and iterate - build basic functionality first -- Reference the recipe documentation files frequently -- Test incrementally after each major change -- Don't test syntax before deploying unless explicitly requested or deployment fails -- Keep track of charm IDs when creating new ones -- Use cell operations for precise testing and debugging -- **Use Playwright MCP for comprehensive UI testing** - Navigate to the space URL and test the recipe's interface directly in the browser -- Focus on user needs and practical functionality - -Remember: Recipe imagination is about turning ideas into working code. Help users build step by step, testing along the way, and creating recipes that solve real problems in their CommonTools spaces. diff --git a/.claude/commands/maintain-docs.md b/.claude/commands/maintain-docs.md deleted file mode 100644 index 5f9edf1fd..000000000 --- a/.claude/commands/maintain-docs.md +++ /dev/null @@ -1,56 +0,0 @@ -# Maintain Documentation - -This command helps maintain the accuracy of the CT documentation by comparing the ct.md file with the actual CLI help output and identifying discrepancies. - -## What this command does: - -1. **Reads ct.md** - Loads the current documentation file -2. **Rebuilds the CT binary** - Ensures we're checking against the latest version -3. **Runs help commands** - Executes various `--help` commands to get actual CLI documentation -4. **Compares documentation** - Identifies discrepancies between ct.md and actual help output -5. **Offers fixes** - Proposes updates to fix any inconsistencies found - -## How to use: - -Simply ask to "maintain docs" or "check ct documentation" and the command will: -- Analyze all command signatures and examples -- Check for missing or outdated commands -- Verify parameter descriptions match -- Ensure examples are up-to-date - -## Commands checked: - -The following ct commands will be verified: -- `ct charm ls` -- `ct charm new` -- `ct charm link` -- `ct charm inspect` -- `ct charm getsrc` -- `ct charm setsrc` -- `ct charm apply` -- `ct charm map` -- `ct dev` - -## What gets verified: - -- Command syntax and usage patterns -- Parameter names and descriptions -- Example commands and their descriptions -- Environment variable documentation -- Any new commands not yet documented - -## Example usage: - -``` -User: maintain the ct docs -Assistant: I'll check the ct.md documentation against the actual CLI help output... - -[Rebuilds binary, runs help commands, compares with documentation] - -Found the following discrepancies: -1. The `getsrc` command now outputs to a folder, but docs show single file -2. New `--main-export` parameter not documented for `setsrc` -3. Missing documentation for new `ct charm map` command - -Would you like me to update the documentation to fix these issues? -``` \ No newline at end of file diff --git a/.claude/commands/recipe-dev.md b/.claude/commands/recipe-dev.md deleted file mode 100644 index 506c4ba7e..000000000 --- a/.claude/commands/recipe-dev.md +++ /dev/null @@ -1,410 +0,0 @@ -# Interactive Recipe Development Script - -This script guides Claude through recipe development with the `ct` utility after initial space setup. Claude should follow these steps to help users modify recipes, create new ones, and network them together. - -## Prerequisites - -**Before starting recipe development:** -- User should have already run the space setup script or have an existing space -- Claude MUST read the common CT setup instructions in `docs/common/CT.md` - -## Script Flow for Claude - -### STEP 0: Determine Starting Point - -**Check if user provided configuration:** -- If user provides a config file (like `.common.json`) with space, api-url, key, and recipes paths: - 1. Read the config - 2. Test the connection: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` - 3. Show what charms exist in the space (if any) - 4. Ask: "What would you like to do with this space?" (e.g., modify existing recipe, create new one, adjust networking) - 5. Skip to STEP 2 - -- If NO config provided: - 1. Ask: "Do you have an existing CommonTools space set up, or would you like help setting one up?" - 2. Continue to STEP 1 for setup - -### STEP 1: Initial Setup and Context (ONLY if no config provided) - -**Read common setup instructions:** -- First, read `docs/common/CT.md` for shared CT binary setup -- Follow those instructions for: - - CT binary check - - Identity management - - Environment setup - - Parameter collection (API URL, space name, recipe path) - - Recipe development TypeScript setup (user should run `ct init` in recipes directory) - -**Verify existing space:** -- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` -- Show user the existing charms in their space -- Ask user what they want to work on (modify existing recipe, create new one, or adjust networking) - -### STEP 2: Recipe Development Workflows - -Before working on recipes it is recommended to search for `COMPONENTS.md` and `RECIPES.md` files in the `docs/common` folder. - -Read `HANDLERS.md` when confused about event handler errors. - -## Development Workflow Best Practices - -When developing and testing patterns iteratively, follow this workflow: - -### Step-by-Step Development Process - -1. **Check Syntax** (Optional - only if requested or deployment fails): - ```bash - ./dist/ct dev pattern.tsx --no-run - ``` - This validates TypeScript/JSX syntax without executing the pattern. - -2. **Test Execution Locally** (Optional): - ```bash - ./dist/ct dev pattern.tsx - ``` - This runs the pattern in a local test environment. Useful for catching runtime errors. - -3. **Deploy to Test Space**: - ```bash - ./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] pattern.tsx - ``` - Deployment will automatically compile and validate the recipe. Record the CHARM_ID returned. - -4. **Iterate with setsrc**: - ```bash - ./dist/ct charm setsrc --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] pattern.tsx - ``` - Update the charm without creating a new one. Much faster for iteration. - -5. **Inspect and Debug**: - ```bash - # View charm data and outputs - ./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] - - # Get specific field values - ./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items/0/name - - # Set test data - echo '{"name": "Test Item"}' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] testData - ``` - -### Common Error Patterns and Solutions - -**Type Errors:** -- Missing `OpaqueRef` type on array map parameters -- Using `Cell>>` instead of `Cell` in handlers -- Forgetting to import `ID` when using `[ID]` - -**Runtime Errors:** -- Trying to use DOM access (`document.getElementById`) -- Using conditionals (if/ternary) instead of `ifElse` -- Calling `llm()` from within handlers or lift functions - -**Deployment Errors:** -- Import path issues (use relative imports like `./file.tsx`) -- Missing dependencies in the recipe -- Syntax errors that weren't caught locally - -**Data Not Updating:** -- Forgetting `$` prefix for bidirectional binding -- Handler not being called (check event names match) -- Cell not being passed to handler correctly - -### Tips for Efficient Development - -- **Don't pre-test syntax** unless deployment fails - the deployment process validates automatically -- **Use `inspect` frequently** to understand current state -- **Use `get/set` commands** to manually test data transformations -- **Keep handler definitions at module level** for better reusability -- **Start simple** then add complexity incrementally -- **Check example patterns** in `packages/patterns/` for reference - -#### Workflow A: Modifying Existing Recipes - -**Get recipe source:** -1. Ask user which charm they want to modify (show charm list if needed) -2. Run: `./dist/ct charm getsrc --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]` -3. Save the output to a temporary file or show user the current source -4. Ask what changes they want to make - -**Edit recipe:** -1. Guide user through making changes to the recipe code -2. If saving to file: Create a temporary file with the modified recipe -3. (Optional) Only if user requests or if deployment fails: Test syntax locally: `./dist/ct dev [modified-recipe-path] --no-run` -4. If syntax errors occur, help fix them - -**Update recipe source:** -1. Update the charm: - `./dist/ct charm setsrc --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] [modified-recipe-path]` -2. Verify update: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]` -3. Explain what changed and how it affects the charm's behavior - -#### Workflow B: Creating New Recipes - -**Design new recipe:** -1. Ask user what the recipe should do -2. Help them understand: - - What inputs it needs (other charm results, well-known cells, user inputs) - - What outputs it should produce - - What processing logic is required - -**Create recipe file:** -1. Ensure TypeScript setup is current: User should run `ct init` manually in their recipes directory -2. Guide user through creating a new .tsx file -3. Start with a template based on their requirements -4. (Optional) Only if user requests or if deployment fails: Test syntax: `./dist/ct dev [new-recipe-path] --no-run` -5. If syntax errors occur, iterate on the recipe until it's correct - -**Deploy new charm:** -1. Deploy the charm directly: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [new-recipe-path]` -2. Record the new CHARM_ID -3. Help user connect it to other charms as needed - -#### Workflow C: Networking and Linking - -**Inspect current connections:** -1. For each charm, run: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id]` -2. Show user the current inputs and outputs -3. Identify unconnected inputs or useful outputs - -**Create new links:** -1. Ask user what data flow they want to create -2. Help them understand source → target relationships -3. Execute links: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] [source-charm]/[field] [target-charm]/[input-field]` -4. Verify the link worked by inspecting the target charm - -NOTE: some recipes take the well known `allCharms` cell as a linked input which has the ID `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye`, this can be achieved like so: - -`./dist/ct charm link baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [recipe]/allCharms` - -**Remove links (if needed):** -1. If user wants to disconnect charms, guide them through identifying which links to remove -2. Use appropriate unlink commands (if available in ct) - -### STEP 3: Advanced Recipe Development - -**Working with complex data flows:** -1. Help user visualize the data flow between charms -2. Suggest intermediate processing recipes if needed -3. Guide creation of aggregator or transformer recipes - -**Debugging recipes:** -1. Use inspect commands to see actual data: `./dist/ct charm inspect --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] --json` -2. Help user understand why recipes might not be producing expected results -3. Suggest logging or debug outputs in recipes - -**Direct data manipulation with cell operations:** -Use the new cell get/set commands for precise data debugging and manipulation: - -```bash -# Inspect specific data fields -./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config/apiKey -./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items/0/status - -# Set test data for debugging -echo '"test-value"' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] debugField -echo '[{"test": true}]' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] testItems - -# Reset or clear problematic data -echo 'null' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] problematicField -``` - -**Recipe patterns:** -- **Filter recipes**: Take collection input, output filtered subset -- **Transformer recipes**: Convert data from one format to another -- **Aggregator recipes**: Combine multiple inputs into single output -- **Generator recipes**: Create new data based on inputs -- **Side-effect recipes**: Perform actions (send emails, create files, etc.) - -#### Workflow D: Multi-File Recipe Development - -**Understanding multi-file recipes:** -Multi-file recipes allow you to compose functionality from multiple source files. When deployed, all imported files are bundled together into a self-contained charm. - -**Key concepts:** -1. **Import/Export pattern**: Export schemas, types, and even entire recipes from one file, import them in another -2. **Self-contained deployment**: When you deploy a recipe that imports others, CT bundles all dependencies -3. **Two deployment strategies**: - - **Single charm**: Deploy the main recipe that imports others (creates one bundled charm) - - **Linked charms**: Deploy each recipe separately and link their outputs/inputs - -**Common pitfalls and solutions:** - -1. **Schema mismatches between linked charms**: - - Problem: Charm A outputs `{items: [...]}` but Charm B expects `{source: {items: [...]}}` - - Solution: Carefully design schemas. Consider having "adapter" recipes if needed - - Better: Export shared schemas from a common file - -2. **File organization confusion**: - - Problem: Multiple versions of the same recipe in different locations - - Solution: Use clear folder structure (e.g., `recipes/feature-name/main.tsx`) - - Always clean up old versions after reorganizing - -3. **Deployment vs development paths**: - - Problem: Import paths work locally but fail when deployed - - Solution: Use relative imports (`./list.tsx` not absolute paths) - - The recipe will be compiled during deployment, which will catch any syntax errors - -**Best practices for multi-file recipes:** - -1. **Export reusable schemas**: - ```typescript - // list.tsx - export const TodoItemSchema = { ... }; - export const TodoListSchema = { ... }; - - // suggestions.tsx - import { TodoListSchema } from "./list.tsx"; - ``` - -2. **Clear separation of concerns**: - - Core functionality in one file - - UI enhancements in another - - Shared utilities in a common file - -3. **Test incrementally**: - - Test each file independently first - - Test the composed recipe locally - - Deploy and verify linking works - -4. **Debug multi-file deployments**: - - Use `ct charm getsrc` to verify what was actually deployed - - Check that all files were bundled correctly - - Inspect charm inputs/outputs to ensure schemas match - -### STEP 4: Testing and Validation - -**Test recipe changes:** -1. After any modification, inspect affected charms -2. Trace data flow through the network -3. Verify outputs match expectations - -**Create test scenarios:** -1. Help user create test charms with known inputs -2. Connect to recipes being developed -3. Verify outputs are correct - -### Common Recipe Development Tasks - -**Finding recipe examples:** -- **Search patterns package first:** Look in `packages/patterns` for related examples and reusable components -- Search user's recipe repo: `find [recipe-path] -name "*.tsx" -type f | xargs grep -l "[pattern]"` -- Show similar recipes as examples -- Help adapt existing recipes for new purposes - -**Understanding recipe structure:** -- Explain CommonTools recipe format -- Show how to define inputs, outputs, and processing -- Guide on using UI components and controls - -**Handler pattern for UI interactions:** -Handlers in CommonTools follow a specific pattern for managing UI events and state: - -```typescript -// Handler definition: handler(eventSchema, stateSchema, handlerFunction) -const myHandler = handler( - {}, // Event schema (data from UI events like clicks) - { // State schema (data the handler needs to operate on) - type: "object", - properties: { - items: { type: "array" }, - someValue: { type: "string" }, - }, - required: ["items"], - }, - (event, { items, someValue }) => { - // Handler function receives (event, state) - // Modify state directly - items.push(someValue); - } -); - -// Handler invocation: pass state data matching the state schema - -``` - -Key points: -- Event schema: For UI event data (usually empty `{}` for simple clicks) -- State schema: Declares what data the handler needs access to -- Handler invocation: Pass an object matching the state schema -- Handler function: Receives `(event, state)` - destructure state as needed - -**Performance considerations:** -- Advise on efficient data processing -- Suggest when to use pagination or batching -- Help optimize expensive operations - -### Error Handling - -**Recipe syntax errors:** -- Parse error messages from `ct dev` -- Guide user to fix TypeScript/JSX issues -- Suggest proper imports and types - -**Runtime errors:** -- Help interpret charm execution errors -- Debug data type mismatches -- Fix missing or incorrect inputs - -**Network errors:** -- Diagnose connection issues -- Verify API URL and identity -- Check space permissions - -### Notes for Claude - -- Do NOT test recipe syntax before deploying unless explicitly requested by user or if deployment fails -- The deployment process (`ct charm new` or `ct charm setsrc`) will compile and validate the recipe automatically -- Keep track of charm IDs when creating new ones -- Help user understand data flow direction (source → target) -- Encourage incremental development and testing -- Save modified recipes to files before using setsrc -- Use inspect commands liberally to show current state -- Use cell get/set commands for precise data debugging and testing -- Leverage cell operations to set up test data and verify recipe behavior - -### Quick Command Reference - -**Development Commands:** -```bash -# Get recipe source -./dist/ct charm getsrc --identity [key] --api-url [url] --space [space] --charm [id] - -# Update recipe source -./dist/ct charm setsrc --identity [key] --api-url [url] --space [space] --charm [id] [recipe-file] - -# Test recipe syntax -./dist/ct dev [recipe-file] --no-run - -# Create new charm -./dist/ct charm new --identity [key] --api-url [url] --space [space] [recipe-file] - -# Link charms -./dist/ct charm link --identity [key] --api-url [url] --space [space] [source]/[field] [target]/[input] - -# Inspect charm -./dist/ct charm inspect --identity [key] --api-url [url] --space [space] --charm [id] - -# Get cell data -./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [id] [path] - -# Set cell data -echo '[json-value]' | ./dist/ct charm set --identity [key] --api-url [url] --space [space] --charm [id] [path] - -# List all charms -./dist/ct charm ls --identity [key] --api-url [url] --space [space] -``` - -### Recipe Development Best Practices - -1. **Start simple**: Create basic recipes first, then add complexity -2. **Test incrementally**: Deploy and test each change -3. **Use meaningful names**: Name charms and fields descriptively -4. **Document recipes**: Add comments explaining logic -5. **Handle errors**: Include error handling in recipes -6. **Validate inputs**: Check data types and required fields -7. **Output consistently**: Use predictable output structures - -Remember: Recipe development is iterative. Help users build step by step, testing along the way. diff --git a/.claude/commands/research.md b/.claude/commands/research.md deleted file mode 100644 index cf30db840..000000000 --- a/.claude/commands/research.md +++ /dev/null @@ -1,66 +0,0 @@ -# Research Command - -Research topics thoroughly using codebase exploration, documentation review, and analysis. - -## Usage - -`/research [question or topic]` - -## Process - -Launch a research subagent using the Task tool, then always ask about deployment: - -``` -Task: Research [topic/question] - -You are a research specialist. Conduct thorough investigation of the topic using all available tools. - -**First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system. - -**Your Task:** -1. **Consult the wiki first** - Read .claude/commands/search-wiki.md to learn how to check for existing knowledge on this topic -2. **Explore the codebase** using Glob, Grep, and Read tools -3. **Review documentation** (README.md, CLAUDE.md, etc.) -4. **Analyze git history** for relevant changes -5. **Examine tests** to understand behavior -6. **Provide comprehensive findings** with specific code references - -**Return to me:** Detailed research report with executive summary, analysis, architecture insights, and actionable findings. - -**CRITICAL:** After delivering the report, you MUST ask the user if they want to deploy it using the .claude/commands/deploy-research.md command. -``` - -## Research Methodology - -### Core Steps -- **Learn ct usage first** - Read docs/common/CT.md to understand CommonTools -- **Start with wiki search** to avoid duplicating previous research -- Use Task tool for systematic codebase exploration -- Check recent git history and commits -- Review existing documentation and tests -- Find relevant files, patterns, and implementations -- Provide specific file paths and line numbers - -### Required Final Step -- **Always ask about deployment** - Even if the user doesn't seem interested, you must offer the .claude/commands/deploy-research.md option - -### Output Format -- **Executive summary** of key findings -- **Detailed analysis** with code references -- **Architecture insights** and design decisions -- **Recent changes** and development history -- **Recommendations** or next steps if applicable - -## Required: Ask About Deployment - -After research is complete, you MUST ask: "Would you like me to deploy this as a CommonTools research report?" - -If yes, use the .claude/commands/deploy-research.md command. Make sure to read docs/common/CT.md first to understand how to use the CommonTools system properly. - -## When to Use - -- Understanding how specific code works -- Exploring new areas of the codebase -- Before making architectural changes -- Investigating bugs or issues -- Learning about patterns and conventions diff --git a/.claude/commands/review-code.md b/.claude/commands/review-code.md deleted file mode 100644 index 18e557404..000000000 --- a/.claude/commands/review-code.md +++ /dev/null @@ -1,48 +0,0 @@ -# Code Review Guidelines - -Review the code we have written with these priorities: - -## Core Principles - -**Channel the spirit of Rich Hickey**: Embrace simplicity, embrace immutability, embrace data. - -Also consider the lessons of Erlang (Joe Armstrong), Elixir (José Valim), Elm (Evan Czaplicki), and Rust. - -## Specific Focus Areas - -### Code Structure -- **Extract pure functions** for common logic and reusable operations -- **Pay attention to the story that parameters and names tell** - use the code as a self-documenting structure -- **Examine similar code** to ensure consistency and avoid duplication -- **Use consistent naming conventions** that clearly express intent -- **Decoupled modules** - consider inversion of control, decomposition, and breaking apart large files by extracting clear domains - -### Type Safety & Data -- **Declare types for repeated shapes** - avoid inline type definitions -- **Do not work around type issues** with `any` or excessive null checks and if statements -- **Make invalid states unrepresentable** - follow the CLAUDE.md guidelines on avoiding ambiguous types - -### Error Handling -- **Handle errors gracefully, or design APIs that make errors impossible** -- Prefer consistent result types (Result) to throwing for routinely-fallible operations -- Prefer throwing over silent failures or unclear undefined returns -- Follow the error handling patterns outlined in CLAUDE.md - -### Functional Style -- **Prefer a pure, functional programming style** over imperative approaches -- Favor immutable data transformations in library code -- Minimize side effects and make them explicit when necessary - - -### Testing -- Test all pure functions -- Use tests to cement expectations, not to create busywork and upkeep -- Aim for code coverage but do not worry about complex integration tests -- Ensure tests are always kept up to date during refactors - -### Functional-Reactive Programming -- When working on recipes, favor functional-reactive programming patterns to handle asynchronous data streams and side effects. See @recipe-dev.md. - -# Caveats - -$ARGUMENTS diff --git a/.claude/commands/review-docs.md b/.claude/commands/review-docs.md deleted file mode 100644 index fd8cbc5d2..000000000 --- a/.claude/commands/review-docs.md +++ /dev/null @@ -1,76 +0,0 @@ -# Documentation Review Command - -Review documentation for accuracy, completeness, and developer workflow issues, with a focus on areas with recent development activity. - -## Process - -### Phase 1: Context Discovery (5-10 minutes) -1. **Check recent git activity** to identify areas of active development: - - `git log --oneline --since="2 weeks ago" -- "*.md"` - Recent doc changes - - `git log --oneline --since="2 weeks ago" --name-only` - Recent code changes - - Focus on frequently modified packages and new features - -2. **Quick documentation landscape scan**: - - Identify main documentation files and their relationships - - Check for symlinks, aliases, or generated files - - Spot obvious critical issues (broken links, missing core docs) - -### Phase 2: Targeted Analysis (15-20 minutes) -3. **Focus on developer workflow blockers**: - - Can someone follow the setup/development instructions? - - Do import statements and code examples actually work? - - Are package paths and directory structures accurate? - -4. **Cross-reference docs with recent code changes**: - - Check if areas with recent development have up-to-date documentation - - Verify that new features or architectural changes are documented - - Look for implementation details that contradict existing docs - -5. **Verify structural claims**: - - Do referenced packages, files, and directories exist? - - Are import paths and command examples correct? - - Do links resolve properly? - -### Phase 3: Prioritized Reporting (5 minutes) -6. **Categorize findings by impact**: - - **Critical**: Blocks developer workflow, incorrect instructions - - **High**: Significant confusion or outdated major features - - **Medium**: Minor inconsistencies in active development areas - - **Low**: Stylistic issues, minor formatting problems - -7. **Ask clarifying questions** when scope is unclear: - - "Should I focus on specific packages or workflows?" - - "Are there particular types of issues you're most concerned about?" - - "Should I prioritize accuracy fixes or structural improvements?" - -## Focus Areas - -### Primary Concerns -- **Workflow blockers**: Instructions that don't work -- **Import/reference accuracy**: Code examples that fail -- **Recent change documentation**: Areas with active development -- **Package relationship clarity**: How components fit together - -### Secondary Concerns -- Architectural documentation gaps -- Missing guides for complex patterns -- Cross-reference accuracy -- Link integrity - -### Defer Unless Specifically Requested -- Minor formatting inconsistencies -- Stylistic preferences -- Comprehensive style guide compliance -- Low-impact wording improvements - -## Key Questions to Answer -1. **"If someone tried to follow these docs today, where would they get stuck?"** -2. **"What recent changes might have made existing docs inaccurate?"** -3. **"Are there new features or patterns that need documentation?"** -4. **"Do the most actively developed areas have adequate documentation?"** - -## Verification Before Fixes -- Confirm file relationships (symlinks, aliases) before flagging duplicates -- Verify proposed fixes are correct (check actual package names, paths) -- Test that suggested import statements and commands actually work -- Ask for confirmation on significant structural changes \ No newline at end of file diff --git a/.claude/commands/search-wiki.md b/.claude/commands/search-wiki.md deleted file mode 100644 index a4ca13478..000000000 --- a/.claude/commands/search-wiki.md +++ /dev/null @@ -1,47 +0,0 @@ -# Search Wiki Command - -Search the project wiki for existing knowledge, solutions, and documentation. Use this proactively before solving problems or when you need information. - -## Command Pattern - -When you need to search the wiki, launch a search subagent using the Task tool: - -``` -Task: Search wiki for [topic/problem/keywords] - -You are a wiki search specialist. Your job is to search the project wiki for relevant information and present findings clearly. - -**Standard Parameters:** -- Identity: claude.key -- API URL: https://toolshed.saga-castor.ts.net/ -- Space: 2025-wiki -- Wiki Charm ID: baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq - -**Your Task:** -0. **First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system. - -1. Get all wiki content: `./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq wiki` - -2. Search through the content for: [specific search criteria] - -3. Present your findings as: - - **Relevant pages found**: List page keys and brief descriptions - - **Key excerpts**: Most relevant content snippets - - **Exact solutions**: If you find direct solutions to the problem - - **Related information**: Similar or adjacent topics that might help - -4. If you find specific pages worth reading in full, get them with: `./dist/ct charm get [params] wiki/[page-key]` - -**Return to me**: A clear summary of what you found, with actionable information extracted and organized for immediate use. -``` - -## When to Search -- Before starting new development work -- When encountering errors or problems -- Before asking user for help -- When exploring unfamiliar code areas -- When debugging complex issues - -The subagent will handle the command execution and content analysis, returning organized results. - -The final response should be a report based on what was found by the subagent giving as much detail as appropriate to the user's query. diff --git a/.claude/commands/setup-space.md b/.claude/commands/setup-space.md deleted file mode 100644 index a0ea967f2..000000000 --- a/.claude/commands/setup-space.md +++ /dev/null @@ -1,174 +0,0 @@ -# Interactive Space Setup Script - -This script guides Claude through setting up a complete space with the `ct` utility. Claude should follow these steps to interactively help the user set up recipes and network them together in a space. - -## Script Flow for Claude - -### STEP 1: Initial Setup Check and Preparation - -**Read common setup instructions:** -- First, read `docs/common/CT.md` for shared CT binary setup and configuration -- Follow those instructions for: - - Checking if user is in the right directory (should be in `labs`) - - CT binary check and build if needed - - Identity keyfile management - - Environment variable setup (CT_API_URL and CT_IDENTITY) - - API URL collection and connectivity test - - **Note:** Claude Code cannot cd into directories. User should run `ct init` manually in their recipes directory for TypeScript setup - -### STEP 2: Space-Specific Setup - -**Get space name:** -- Ask user what they want to name their space (no spaces, lowercase recommended) -- Store as variable for commands - -**Find recipe path:** -- Follow the recipe path discovery process from common/ct.md -- **Important:** User must run `ct init` manually in their recipes directory (Claude Code cannot cd into directories) -- Look specifically for these key recipes: - - `find [user-provided-path] -name "*gmail*" -o -name "*simple-list*" -o -name "*page*" -o -name "*factory*" | head -10` - - Verify key recipes exist: `ls -la [user-provided-path]/gmail.tsx [user-provided-path]/simple-list.tsx [user-provided-path]/page.tsx [user-provided-path]/factory.tsx` - - If recipes are in a subfolder, help them find the right path: `find [user-provided-path] -name "recipes" -type d` - -### STEP 3: Execute Space Setup Workflow - -**For each step below, Claude should:** -1. Explain what we're doing -2. Run the command -3. Capture and show the charm ID from output -4. Verify the operation worked -5. Store charm IDs for later linking - -**Create simple-list charm:** -- Verify recipe exists: `ls -la [recipe-path]/simple-list.tsx` -- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/simple-list.tsx` -- Extract and record the SIMPLE_LIST_CHARM_ID from output -- Verify: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` - -**Create gmail charm:** -- Verify recipe exists: `ls -la [recipe-path]/gmail.tsx` -- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/gmail.tsx` -- Record GMAIL_CHARM_ID - -**Create page charm:** -- Verify recipe exists: `ls -la [recipe-path]/page.tsx` -- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/page.tsx` -- Record PAGE_CHARM_ID -- Link well-known charms list to page allCharms input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [PAGE_CHARM_ID]/allCharms` -- Link well-known charms list to page mentionable input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [PAGE_CHARM_ID]/mentionable` - -**Create factory charm:** -- Verify recipe exists: `ls -la [recipe-path]/factory.tsx` -- Run: `./dist/ct charm new --identity [keyfile] --api-url [api-url] --space [spacename] [recipe-path]/factory.tsx` -- Record FACTORY_CHARM_ID -- Link well-known charms list to factory allCharms input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [FACTORY_CHARM_ID]/allCharms` -- Link well-known charms list to factory mentionable input: `./dist/ct charm link --identity [keyfile] --api-url [api-url] --space [spacename] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [FACTORY_CHARM_ID]/mentionable` - -### STEP 4: Final Verification and Optional Setup - -**Show final space:** -- Run: `./dist/ct charm ls --identity [keyfile] --api-url [api-url] --space [spacename]` -- Explain what each charm does - -**Offer additional recipes:** -- Run: `find [recipe-path] -name "*.tsx" -type f` -- Ask user if they want to deploy any additional recipes -- For each additional recipe: verify, create charm, ask about linking needs - -### Error Handling - -**General error handling:** -- Refer to error handling section in `docs/common/CT.md` for common issues -- Don't continue to dependent steps until current step works - -**Space setup specific errors:** - -**If recipe files are missing:** -- Ask user to double-check the recipe repository path -- Help user locate them: `find [user-provided-path] -name "*.tsx" -type f` -- Look in common subdirectories: `find [user-provided-path] -name "recipes" -type d` -- Ask user to provide the correct path to their recipe repository -- Verify all required files exist before proceeding: `ls -la [corrected-path]/simple-list.tsx [corrected-path]/gmail.tsx [corrected-path]/page.tsx [corrected-path]/factory.tsx` - -**If charm creation fails:** -- Test recipe syntax with `./dist/ct dev [recipe] --no-run` -- Check network connectivity -- Verify identity file permissions - -### Data Management During Setup - -**Viewing charm data:** -Use the new cell get commands to inspect charm data during setup: -```bash -# View charm input parameters -./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] title - -# View nested data -./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config/apiKey - -# View array elements -./dist/ct charm get --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items/0/name -``` - -**Modifying charm data:** -Use cell set commands to configure charms during setup: -```bash -# Set simple values -echo '"Updated Title"' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] title - -# Set configuration objects -echo '{"apiKey": "your-key", "enabled": true}' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] config - -# Set array data -echo '[{"name": "Item 1"}, {"name": "Item 2"}]' | ./dist/ct charm set --identity [keyfile] --api-url [api-url] --space [spacename] --charm [charm-id] items -``` - -### Notes for Claude - -- Always run commands and show real output to user -- Extract and track charm IDs as you go (they start with "bafy") -- Verify each step worked before proceeding -- Be helpful if user needs to troubleshoot -- Ask questions when paths or parameters are unclear -- Keep track of what's been created to avoid duplicates -- Use cell get/set commands to inspect and configure charm data as needed - -### Important: External Recipe Repository Handling - -- **Recipes are NOT in the labs repo** - they are in a separate repository -- **Always ask user for the full path** to their recipe repository first -- **Example paths might be:** - - `/Users/username/my-recipes` - - `../my-recipe-repo` - - `/home/user/projects/recipe-collection` -- **Validate the path exists** before proceeding: `ls -la [user-path]` -- **Find recipes in their repo** with: `find [user-path] -name "*.tsx" | head -10` -- **Use full paths in all ct commands** - don't assume recipes are local to labs -- **If user gives wrong path**, help them find the right location with find commands - -## Reference Information for Claude - -### Key Commands and Linking Concepts: -- See `docs/common/CT.md` for: - - Complete list of CT commands - - Understanding linking syntax and concepts - - Examples of charm-to-charm and well-known ID linking - -### Expected Workflow: -1. Simple-list charm → standalone list for basic items -2. Gmail charm → standalone email fetcher -3. Page charm → link well-known charms list to its allCharms and mentionable inputs -4. Factory charm → link well-known charms list to its allCharms and mentionable inputs - -### Well-Known IDs: -- Charms list in any space: `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye` - -### Space Setup Specific Notes: -- See `docs/common/CT.md` for general troubleshooting -- Recipe files needed for initial setup: - - simple-list.tsx - - gmail.tsx - - page.tsx - - factory.tsx -- Verify all these recipes exist before starting the setup workflow -- Remember: Claude Code cannot cd into directories - user must manually run `ct init` in their recipes directory diff --git a/.claude/commands/update-wiki.md b/.claude/commands/update-wiki.md deleted file mode 100644 index b4d629f44..000000000 --- a/.claude/commands/update-wiki.md +++ /dev/null @@ -1,55 +0,0 @@ -# Update Wiki Command - -Add knowledge, solutions, progress reports, and documentation to the project wiki. Use this to capture learnings and insights for future sessions. - -## Command Pattern - -When you need to update the wiki, follow these steps: - -**Standard Parameters:** -- Identity: claude.key -- API URL: https://toolshed.saga-castor.ts.net/ -- Space: 2025-wiki -- Wiki Charm ID: baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq - -**Process:** - -0. **First, learn how to use ct:** Read docs/common/CT.md to understand how to use the CommonTools system. - -1. Choose appropriate page key following naming conventions: - - Solutions: `[problem-type]-solution` or `fix-[specific-issue]` - - How-to guides: `how-to-[action]` - - Progress reports: `progress-YYYY-MM-DD-[username]-[topic]` - - Tips: `tips-[technology/area]` - - Timestamped entries: `YYYY-MM-DD-[username]-[topic]` - -2. Format content using appropriate template: - - **Problem-Solution**: Problem description, solution steps, context - - **Progress Report**: Status, findings, next steps, blockers - - **How-To Guide**: Overview, steps, examples, troubleshooting - -3. Create JSON file and add to wiki: - ```bash - cat > /tmp/wiki-update.json << 'EOF' - { - "key": "your-page-key", - "value": "# Title\n\nFormatted content here..." - } - EOF - - cat /tmp/wiki-update.json | ./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq update - ``` - -4. Verify the update worked by reading it back: - ```bash - ./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space 2025-wiki --charm baedreigkqfmhscbwwfhkjxicogsw3m66nxbetlhlnjkscgbs56hsqjrmkq wiki/[your-page-key] - ``` - -## When to Update -- After solving non-trivial problems -- When discovering useful patterns or techniques -- For ongoing complex work (progress reports) -- When finding workarounds for common issues -- At end of debugging sessions with lessons learned - -Updates should be well-formatted and include all necessary context for future reference. diff --git a/.claude/commands/walk-space.md b/.claude/commands/walk-space.md deleted file mode 100644 index aed62470c..000000000 --- a/.claude/commands/walk-space.md +++ /dev/null @@ -1,269 +0,0 @@ -# CommonTools Space Mapping Workflow - -**INSTRUCTIONS FOR AI AGENT**: When the `/walk-space` command is invoked, execute the steps in this workflow to map a CommonTools space. Do not explain the workflow - actually run the commands and store data in the memory knowledge graph. - -## Overview - -This workflow enables semantic mapping and change tracking of CommonTools spaces using the memory MCP knowledge graph system. It creates a searchable knowledge graph of charm states, relationships, and evolution over time. - -## Core Concepts - -### Space Mapping -- **Goal**: Build a semantic understanding of a CommonTools space -- **Method**: Extract charm data and relationships, store as searchable fragments -- **Benefit**: Enable natural language queries about space contents and history - -### Change Tracking -- **Goal**: Monitor how spaces evolve over time -- **Method**: Create timestamped snapshots and document differences -- **Benefit**: Understand user patterns and space development - -## Workflow Steps - -### 1. Initial Space Discovery - -```bash -# First, list all charms in the space -./dist/ct charm ls --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] - -# Optionally generate visual map -./dist/ct charm map --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] -./dist/ct charm map --identity ~/dev/.ct.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --format dot -``` - -### 2. Data Extraction - -For each charm discovered: - -```bash -# Get key fields -./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] title -./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] tags -./dist/ct charm get --identity [key] --api-url [url] --space [space] --charm [charm-id] outline -# Additional fields as needed based on charm type -``` - -### 3. Create Knowledge Graph Entries - -Use `mcp__memory__add_memory` to build the knowledge graph: - -#### Individual Charm Documentation -- **Name**: "Charm: [Name] ([Type])" -- **Episode Body**: Include charm ID, type, purpose, content summary, technical details, connections -- **Source**: "text" or "json" (for structured charm data) -- **Source Description**: "charm [type] in [space-name]" -- **Group ID**: "[space-name]" - -#### Space Relationships (JSON Format) -- **Name**: "Space Relationships: [space-name] @ [timestamp]" -- **Episode Body**: JSON string containing charm connections and data flows - ```json - { - "space": "space-name", - "timestamp": "ISO-timestamp", - "relationships": [ - {"source": "charm-id-1", "target": "charm-id-2", "type": "data-flow"}, - {"source": "charm-id-2", "target": "charm-id-3", "type": "connection"} - ] - } - ``` -- **Source**: "json" -- **Source Description**: "relationship mapping for [space-name]" -- **Group ID**: "[space-name]" - -#### Space Snapshot -- **Name**: "Space Snapshot: [space-name] @ [ISO-timestamp]" -- **Episode Body**: Complete state of all charms, connections, and metadata -- **Source**: "json" -- **Source Description**: "complete snapshot of [space-name]" -- **Group ID**: "[space-name]" - -### 4. Iterative Monitoring - -On subsequent scans: - -1. **Rescan the space** - - List charms again - - Extract current data for each charm - -2. **Compare with previous snapshot** - - Check for new/removed charms - - Identify changed titles, tags, content - - Note new or removed connections - -3. **Document changes** - Use `mcp__memory__add_memory`: - - **Name**: "Space Changes: [space-name] @ [ISO-timestamp]" - - **Episode Body**: Detailed list of all changes detected, including added/removed charms, modified content - - **Source**: "text" - - **Source Description**: "changes detected in [space-name]" - - **Group ID**: "[space-name]" - -4. **Create new snapshot** - - Reference previous snapshot - - Link to change documentation - - Update charm states - -### 5. Search and Analysis - -#### Find specific charms -Use the memory MCP tools: - -- **Semantic search for nodes**: `mcp__memory__search_memory_nodes` - - Query: "dog pet border collie" - - Query: "page recipe outliner component" - - Group IDs: ["[space-name]"] - -- **Search for facts/relationships**: `mcp__memory__search_memory_facts` - - Query: "data flow connections" - - Group IDs: ["[space-name]"] - -#### Track evolution -- **Get recent episodes**: `mcp__memory__get_episodes` - - Group ID: "[space-name]" - - Last N: 10 (to see recent snapshots) - -- **Search for changes**: `mcp__memory__search_memory_nodes` - - Query: "changes modifications updates snapshot" - - Group IDs: ["[space-name]"] - -## Memory Episode Guidelines - -### Essential Fields -- **Name**: Consistent naming pattern for easy identification (include type in name) -- **Episode Body**: Structured content (text or JSON) -- **Source**: "text" for narratives, "json" for structured data, "message" for conversations -- **Source Description**: Descriptive context including content type and space -- **Group ID**: Single identifier per space (like a tag, but only one allowed) - -### Group ID Strategy -- Use `[space-name]` as the single group ID for all content related to that space -- Differentiate content types through: - - **Name patterns**: Include type ("Charm:", "Snapshot:", "Changes:", "Reflection:") - - **Source descriptions**: Be specific about what kind of data it is - - **Episode body structure**: Use consistent formats for each type - -## Example Implementation Flow - -### Initial Space Mapping -1. List charms using `ct charm ls` -2. For each charm: - - Use `ct charm get` to extract data - - Add to knowledge graph with `mcp__memory__add_memory` (group: "[space-name]") -3. Document relationships with `mcp__memory__add_memory` (source: "json", group: "[space-name]") -4. Create baseline snapshot with `mcp__memory__add_memory` (source: "json", group: "[space-name]") - -### Subsequent Scans -1. List charms again with `ct charm ls` -2. Extract current data for comparison -3. Search previous data: `mcp__memory__search_memory_nodes` or `mcp__memory__get_episodes` (group: "[space-name]") -4. If changes detected: - - Record changes with `mcp__memory__add_memory` (group: "[space-name]") - - Create new snapshot with `mcp__memory__add_memory` (group: "[space-name]") - - Add updated charm states to graph (new episodes build on existing knowledge) - -## Benefits - -1. **Semantic Search**: Find charms by meaning, not just keywords -2. **Change History**: Understand how spaces evolve -3. **Relationship Mapping**: Visualize data flows and dependencies -4. **Pattern Recognition**: Identify common usage patterns -5. **Space Understanding**: Build AI comprehension of user intent -6. **AI Collaboration**: Agents can contribute meaningful content based on user data -7. **Reflective Analysis**: Generate insights and questions that enrich user documentation - -## Known Limitations - -- Embedding similarity scores may be too high (bug to be filed) -- Circular references in charm inspect can cause errors -- Large spaces may require pagination strategies - -## AI Reflection Process - -### Purpose -Enable AI agents to contribute meaningful content to spaces by analyzing user data and creating reflective pages with observations, questions, and suggestions. - -### Workflow for Content Reflection - -1. **Analyze existing content** - - Use `mcp__memory__search_memory_nodes` to find all entities about specific topics - - Use `mcp__memory__search_memory_facts` to understand relationships - - Extract actual user data from charm fields (not just metadata) - - Focus on what the user has written, not technical implementation - -2. **Generate reflections** - - **Observations**: What patterns or interesting details do you notice? - - **Questions**: What would you like to know more about? - - **Suggestions**: How could the user enrich their documentation? - - **Pattern Analysis**: What does their documentation style reveal? - -3. **Deploy reflection page** - ```bash - # Deploy a new page.tsx instance - ./dist/ct charm new --identity [key] --api-url [url] --space [space] [page-recipe-path] - - # Set meaningful title - echo '"Reflections on [Topic] 🤔"' | ./dist/ct charm set [params] --charm [new-charm-id] title - - # Set appropriate tags - echo '["reflection", "analysis", "topic", "ai-observations"]' | ./dist/ct charm set [params] --charm [new-charm-id] tags - - # Create outline content (write to file first to avoid escaping issues) - cat outline.json | ./dist/ct charm set [params] --charm [new-charm-id] outline - - # Link to allCharms for mentionable functionality - ./dist/ct charm link [params] [allCharms-id] [new-charm-id]/mentionable - ``` - -4. **Document the reflection** - Add to knowledge graph recording the AI contribution: - - Name: "AI Reflection: [Topic] @ [timestamp]" - - Episode Body: Include charm ID, key insights, observations, questions - - Source: "text" - - Source Description: "ai reflection on [topic] in [space-name]" - - Group ID: "[space-name]" - -### Reflection Content Guidelines - -- **Be specific**: Reference actual data the user entered -- **Be curious**: Ask thoughtful questions based on the content -- **Be helpful**: Suggest concrete ways to expand documentation -- **Be respectful**: Frame observations positively -- **Be relevant**: Focus on the user's interests, not technical details - -### Example Reflection Structure -```json -{ - "root": { - "body": "", - "children": [ - { - "body": "## Observations about [Topic]", - "children": [/* Specific observations about user's content */] - }, - { - "body": "## Questions I am Curious About", - "children": [/* Thoughtful questions based on the data */] - }, - { - "body": "## Suggestions for Enriching [Topic]", - "children": [/* Concrete suggestions for expansion */] - }, - { - "body": "## Patterns I Noticed", - "children": [/* Analysis of documentation style and evolution */] - } - ] - } -} -``` - -## Future Enhancements - -1. **Automated scanning**: Periodic space checks -2. **Change notifications**: Alert on significant modifications -3. **Pattern analysis**: Identify common charm combinations -4. **Space templates**: Suggest charms based on usage patterns -5. **Version control**: Git-like branching for space states -6. **AI collaboration**: Multiple agents contributing different perspectives -7. **Content suggestions**: AI-generated prompts based on existing content \ No newline at end of file diff --git a/.claude/skills/ct/SKILL.md b/.claude/skills/ct/SKILL.md new file mode 100644 index 000000000..d3e3ed07f --- /dev/null +++ b/.claude/skills/ct/SKILL.md @@ -0,0 +1,375 @@ +--- +name: ct +description: Guide for using the ct (CommonTools) binary to interact with charms, recipes, and the Common Fabric. Use this skill when deploying recipes, managing charms, linking data between charms, or debugging recipe execution. Triggers include requests to "deploy this recipe", "call a handler", "link these charms", "get data from charm", or "test this recipe locally". +--- + +# CT (CommonTools CLI) + +## Overview + +The `ct` binary is the primary command-line interface for interacting with the CommonTools framework. Use this skill when working with recipes (TypeScript programs that define interactive data transformations), charms (deployed instances of recipes), and the Common Fabric (the distributed runtime for charms). + +## When to Use This Skill + +Use this skill for: +- Deploying recipes as charms to a space +- Managing charm data (get/set fields, call handlers) +- Linking charms together for reactive data flow +- Testing and debugging recipes locally +- Managing identity and space configurations +- Visualizing charm relationships + +## Prerequisites and Setup + +### Running CT: Binary vs Source + +The `ct` command can be run in two ways: + +**1. As a compiled binary (recommended for production):** + +```bash +./dist/ct [command] +``` + +Verify the binary exists: + +```bash +ls -la ./dist/ct +``` + +If missing, build it with: + +```bash +deno task build-binaries --cli-only +``` + +**2. From source (for active development):** + +```bash +deno task ct [command] +``` + +Use this approach when actively developing the ct tool itself, as it runs the latest source code without requiring a rebuild. + +**Which to use:** +- Use `./dist/ct` by default and in production environments +- Use `deno task ct` when actively developing/debugging the ct tool +- If uncertain, try `./dist/ct` first; if the binary doesn't exist, use `deno task ct` + +### Identity Management + +Check for existing identity: + +```bash +ls -la claude.key +``` + +If missing, create one: + +```bash +./dist/ct id new > claude.key +``` + +To get the DID (Decentralized Identifier) of an identity: + +```bash +./dist/ct id did claude.key +``` + +### Recipe Development Setup + +When working in a recipes repository, initialize TypeScript support: + +```bash +./dist/ct init +``` + +This creates/updates `tsconfig.json` with proper type definitions for recipe development. + +### Standard Parameters + +Most commands require these parameters: +- `--identity` / `-i`: Path to identity keyfile (commonly `claude.key`) +- `--api-url` / `-a`: Fabric instance URL (commonly `https://toolshed.saga-castor.ts.net/`) +- `--space` / `-s`: Space name or DID + +**Environment Variables:** Set `CT_API_URL` and `CT_IDENTITY` to avoid repeating these parameters. + +**Important:** For `*.ts.net` URLs, ensure connection to the CT Tailnet. Commands will hang/timeout if not connected. + +## Core Workflows + +### 1. Testing Recipes Locally + +Before deploying, test recipes locally using `ct dev`: + +**Type check and execute:** +```bash +./dist/ct dev ./recipe.tsx +``` + +**Type check only (no execution):** +```bash +./dist/ct dev ./recipe.tsx --no-run +``` + +**Show transformed TypeScript:** +```bash +./dist/ct dev ./recipe.tsx --show-transformed +``` + +**Save compiled output:** +```bash +./dist/ct dev ./recipe.tsx --output compiled.js +``` + +Use `ct dev` for rapid iteration during recipe development. Fix syntax errors before deploying. + +### 2. Deploying and Managing Charms + +**List charms in a space:** +```bash +./dist/ct charm ls --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] +``` + +**Deploy a recipe as a new charm:** +```bash +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] [path/to/recipe.tsx] +``` + +**Inspect charm details:** +```bash +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] +``` + +**Get charm recipe source:** +```bash +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] ./output.tsx +``` + +**Update charm recipe source:** +```bash +./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [path/to/updated-recipe.tsx] +``` + +### 3. Reading and Writing Charm Data + +Charms have two cells: +- **Result Cell** (default): Computed output/result of the charm +- **Input Cell**: Input parameters/arguments passed to the charm + +**Get value from result cell:** +```bash +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [path] +``` + +**Get value from input cell:** +```bash +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [path] --input +``` + +**Set value in result cell (via stdin):** +```bash +echo '"New Value"' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [path] +``` + +**Set value in input cell:** +```bash +echo '{"key": "value"}' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [path] --input +``` + +**Path syntax examples:** +- `title` - top-level field +- `items/0/name` - array element field +- `config/database/host` - nested object field + +**Important:** Values must be valid JSON. Strings need quotes: `'"text"'` not `'text'` + +### 4. Calling Charm Handlers + +Handlers are functions defined in recipes that perform operations with validation and side effects. Use `call` instead of `set` for complex operations. + +**Call handler with no arguments:** +```bash +./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] [handler-name] +``` + +**Call handler with JSON arguments (via stdin):** +```bash +echo '{"title": "New Item", "priority": 1}' | ./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] addItem +``` + +**Call handler with JSON arguments (inline):** +```bash +./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --charm [charm-id] updateStatus '{"status": "completed"}' +``` + +### 5. Linking Charms Together + +Linking is the primary way to build complex applications from simple recipes. Links create reactive data flow between charms. + +**How linking works:** +- **Source side**: Reads from a charm's result/output field +- **Target side**: Writes to another charm's input field +- **Syntax**: `[source-charm]/[field] → [target-charm]/[input-field]` +- **Reactivity**: When source updates, target automatically receives new data + +**Basic link:** +```bash +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] bafySourceCharm/emails bafyTargetCharm/emailData +``` + +**Nested field link:** +```bash +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] bafyCharmA/data/users/0/email bafyCharmB/config/primaryEmail +``` + +**Link well-known ID (e.g., all charms list):** +```bash +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye bafyTargetCharm/allCharms +``` + +**Common patterns:** +- Email list → Document generator +- Search results → Summarizer +- Database query → Dashboard display +- Form input → Validation processor + +### 6. Visualizing Space Architecture + +**ASCII map of charms and connections:** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] +``` + +**Generate Graphviz diagram:** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --format dot +``` + +**Render to PNG (requires Graphviz installed):** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space-name] --format dot | dot -Tpng -o map.png +``` + +**Online visualization:** +1. Generate DOT output +2. Paste into https://dreampuf.github.io/GraphvizOnline/ + +## Decision Guide: Which Command to Use + +**GET vs SET vs CALL:** +- **GET**: Read data from charm (result or input cell) +- **SET**: Direct field modification (simple, fast, no validation) +- **CALL**: Execute handler (complex operations, validation, side effects) + +**When to use each:** +- Use **GET** to inspect charm state +- Use **SET** for simple value updates without business logic +- Use **CALL** for operations that need validation, computation, or side effects +- Use **LINK** to establish reactive data flow between charms + +**GETSRC vs SETSRC:** +- **GETSRC**: Retrieve recipe source code from deployed charm +- **SETSRC**: Update recipe source code in deployed charm + +Use these when iterating on deployed charms or extracting recipes for local development. + +## Common Patterns and Best Practices + +### Path Format + +Always use forward slashes for paths: +- ✅ `config/database/host` +- ❌ `config.database.host` + +Array indices are numeric: +- ✅ `items/0/name` +- ❌ `items[0]/name` + +### JSON Input Requirements + +All values passed to `set` and `call` must be valid JSON: + +```bash +# Strings (note the nested quotes) +echo '"hello world"' | ./dist/ct charm set ... title + +# Numbers +echo '42' | ./dist/ct charm set ... count + +# Booleans +echo 'true' | ./dist/ct charm set ... enabled + +# Objects +echo '{"name": "John", "age": 30}' | ./dist/ct charm set ... user + +# Arrays +echo '["item1", "item2"]' | ./dist/ct charm set ... tags +``` + +### Error Handling + +**Common issues:** +- Commands hang/timeout → Not connected to CT Tailnet +- Permission denied → Check identity file permissions (`chmod 600 claude.key`) +- Invalid path → Verify forward slash syntax +- JSON parse error → Check JSON formatting (proper quotes, no trailing commas) + +**Debugging steps:** +1. For recipe errors: Run `./dist/ct dev [recipe] --no-run` to check syntax +2. For connection issues: Verify Tailnet connection for `*.ts.net` URLs +3. For data issues: Use `ct charm inspect` to examine charm state +4. For linking issues: Use `ct charm map` to visualize connections + +### Building Complex Applications + +**Composability Pattern:** +1. Create small, focused recipes (each does one thing well) +2. Deploy recipes as separate charms +3. Link charms together for data flow +4. Use `ct charm map` to visualize architecture +5. Add new functionality by deploying and linking new charms + +**Example architecture:** +``` +[User Input Form] → [Validator] → [Database Writer] + ↓ + [Email Notifier] +``` + +Implement by creating 4 recipes, deploying as charms, then linking them together. + +## Resources + +### references/commands.md + +Comprehensive command reference with all flags, options, and detailed examples. Consult when needing specific command syntax or advanced features. + +### references/well-known-ids.md + +Documentation of well-known charm IDs (like `allCharms`) that provide access to system-level data. Reference when building tools that need space-wide information. + +## Quick Reference + +**Most common commands:** + +```bash +# Test recipe locally +./dist/ct dev ./recipe.tsx + +# List charms +./dist/ct charm ls -i claude.key -a https://toolshed.saga-castor.ts.net/ -s myspace + +# Deploy recipe +./dist/ct charm new -i claude.key -a https://toolshed.saga-castor.ts.net/ -s myspace ./recipe.tsx + +# Get data +./dist/ct charm get -i claude.key -a https://toolshed.saga-castor.ts.net/ -s myspace -c bafyID title + +# Call handler +echo '{"name": "value"}' | ./dist/ct charm call -i claude.key -a https://toolshed.saga-castor.ts.net/ -s myspace -c bafyID handler + +# Link charms +./dist/ct charm link -i claude.key -a https://toolshed.saga-castor.ts.net/ -s myspace bafyA/field bafyB/input +``` diff --git a/.claude/skills/ct/references/commands.md b/.claude/skills/ct/references/commands.md new file mode 100644 index 000000000..ee6e42793 --- /dev/null +++ b/.claude/skills/ct/references/commands.md @@ -0,0 +1,287 @@ +# CT Command Reference + +This reference provides detailed command syntax and examples for the ct binary. + +## Standard Parameters + +Most commands accept these standard parameters: + +- `--identity` / `-i` ``: Path to identity keyfile (default: `claude.key`) +- `--api-url` / `-a` ``: URL of the fabric instance +- `--space` / `-s` ``: Space name or DID +- `--charm` / `-c` ``: Target charm ID (for charm-specific commands) + +**Environment Variables:** +- `CT_API_URL`: URL of the fabric instance +- `CT_IDENTITY`: Path to identity keyfile + +## Command Groups + +### Identity Commands (`ct id`) + +**Create new identity:** +```bash +./dist/ct id new > claude.key +``` + +**Get DID from keyfile:** +```bash +./dist/ct id did +``` + +**Derive identity from passphrase:** +```bash +./dist/ct id derive +``` + +### Development Commands (`ct dev`) + +**Test recipe locally with execution:** +```bash +./dist/ct dev ./recipe.tsx +``` + +**Type check recipe without execution:** +```bash +./dist/ct dev ./recipe.tsx --no-run +``` + +**Compile without type checking:** +```bash +./dist/ct dev ./recipe.tsx --no-check +``` + +**Save compiled output:** +```bash +./dist/ct dev ./recipe.tsx --output out.js +``` + +**Show transformed source:** +```bash +./dist/ct dev ./recipe.tsx --show-transformed +``` + +**Use named export:** +```bash +./dist/ct dev ./recipe.tsx --main-export myRecipe +``` + +### Initialization Commands (`ct init`) + +**Initialize TypeScript environment for recipes:** +```bash +./dist/ct init +``` + +Creates/updates tsconfig.json for proper type definitions in recipe development. + +### Charm Commands (`ct charm`) + +#### List Charms + +**List all charms in space:** +```bash +./dist/ct charm ls --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] +``` + +#### Deploy Charm + +**Deploy recipe as new charm:** +```bash +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] [recipe-path] +``` + +#### Get Data + +**Get field from charm result:** +```bash +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [path] +``` + +**Get field from charm input:** +```bash +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [path] --input +``` + +**Path examples:** +- `title` - top-level field +- `items/0/name` - array element field +- `config/database/host` - nested object field + +#### Set Data + +**Set field in charm result (value via stdin):** +```bash +echo '"New Value"' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [path] +``` + +**Set field in charm input:** +```bash +echo '{"config": "value"}' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [path] --input +``` + +**Important:** Values must be valid JSON. Strings need double quotes: `'"text"'` + +#### Call Handler + +**Call handler with no arguments:** +```bash +./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [handler-name] +``` + +**Call handler with JSON arguments (via stdin):** +```bash +echo '{"key": "value"}' | ./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [handler-name] +``` + +**Call handler with JSON arguments (inline):** +```bash +./dist/ct charm call --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [handler-name] '{"key": "value"}' +``` + +#### Link Charms + +**Link charm field to another charm's input:** +```bash +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] [source-charm]/[field] [target-charm]/[input-field] +``` + +**Examples:** +```bash +# Basic link +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] bafycharm1/emails bafycharm2/emailData + +# Nested field link +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] bafycharm1/data/users/0/email bafycharm2/config/primaryEmail + +# Link well-known ID +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye bafycharm1/allCharms +``` + +**How linking works:** +- Source side: Reads from charm's result/output field +- Target side: Writes to charm's input parameters +- Data flows automatically when source updates +- Changes propagate reactively through linked chains + +#### Recipe Source Management + +**Get recipe source from deployed charm:** +```bash +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [output-path] +``` + +**Update recipe source in deployed charm:** +```bash +./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] [recipe-path] +``` + +#### Inspect and Debug + +**Inspect charm details:** +```bash +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +**Get raw JSON output:** +```bash +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] --json +``` + +**Display rendered view:** +```bash +./dist/ct charm view --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +**Render charm UI to HTML:** +```bash +./dist/ct charm render --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +#### Apply Inputs (Bulk Replacement) + +**Replace all inputs at once (via stdin):** +```bash +echo '{"input1": "value1", "input2": "value2"}' | ./dist/ct charm apply --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +Note: Prefer `call` or `set` for most cases. Use `apply` only when replacing all inputs is needed. + +#### Step Execution + +**Run single scheduling step:** +```bash +./dist/ct charm step --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +Executes: start → idle → synced → stop + +#### Remove Charm + +**Remove a charm:** +```bash +./dist/ct charm rm --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +#### Visualization + +**ASCII map of charms and links:** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] +``` + +**Graphviz DOT format:** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --format dot +``` + +**Render to image (requires Graphviz):** +```bash +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --format dot | dot -Tpng -o map.png +``` + +**Online visualization:** +1. Generate DOT output +2. URL encode and append to: `https://dreampuf.github.io/GraphvizOnline/?engine=dot#` +3. Or paste into: `https://dreampuf.github.io/GraphvizOnline/` + +## Common Patterns + +### When to Use Each Command + +- **GET**: Read any data from charm (result or input) +- **SET**: Directly modify specific values (simple, fast) +- **CALL**: Use charm's handlers (for complex operations, validation, side effects) +- **LINK**: Connect charms for reactive data flow +- **LS**: Discover what charms exist in a space +- **NEW**: Deploy a recipe as a new charm +- **GETSRC/SETSRC**: Retrieve or update recipe source code + +### Path Syntax + +- Use forward slashes: `config/database/host` +- Array indices are numeric: `users/0/profile` +- Nested objects: `data/items/2/metadata/tags` + +### JSON Input Requirements + +Values passed to `set` and `call` must be valid JSON: +- Strings: `'"text"'` (note the quotes) +- Numbers: `'42'` +- Booleans: `'true'` or `'false'` +- Objects: `'{"key": "value"}'` +- Arrays: `'["item1", "item2"]'` + +### Error Handling + +**If commands fail:** +- Check network connectivity and Tailnet connection (for `*.ts.net` URLs) +- Verify identity file exists and has correct permissions +- Ensure space name and charm ID are correct +- For recipe syntax errors, use `./dist/ct dev [recipe] --no-run` + +**Common issues:** +- Commands hang/timeout → Not connected to CT Tailnet +- Permission denied → Check identity file permissions +- Invalid path → Verify path syntax with forward slashes +- JSON parse error → Check JSON formatting (proper quotes, no trailing commas) diff --git a/.claude/skills/ct/references/well-known-ids.md b/.claude/skills/ct/references/well-known-ids.md new file mode 100644 index 000000000..96f3f9ebc --- /dev/null +++ b/.claude/skills/ct/references/well-known-ids.md @@ -0,0 +1,32 @@ +# Well-Known IDs + +CommonTools provides well-known IDs for accessing system-level data within a space. + +## allCharms (Charms List) + +**ID:** `baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye` + +**Purpose:** Contains a list of all charms in the current space. + +**Common Usage:** Link to charm inputs that need to access the full charm list. + +**Example:** +```bash +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] baedreiahv63wxwgaem4hzjkizl4qncfgvca7pj5cvdon7cukumfon3ioye [target-charm]/allCharms +``` + +**Use Cases:** +- Building UI that displays all charms in a space +- Creating dashboards or management interfaces +- Implementing charm discovery or search functionality +- Building tools that operate across multiple charms + +## How Well-Known IDs Work + +Well-known IDs are special charm IDs that: +- Are consistent across all spaces +- Provide access to system-level data +- Can be used in links just like regular charm IDs +- Always have the same ID regardless of deployment + +When linking a well-known ID to a charm's input field, the target charm will receive live updates whenever the underlying data changes (e.g., when new charms are added to the space). diff --git a/.claude/skills/lit-component/SKILL.md b/.claude/skills/lit-component/SKILL.md new file mode 100644 index 000000000..94734ded2 --- /dev/null +++ b/.claude/skills/lit-component/SKILL.md @@ -0,0 +1,383 @@ +--- +name: lit-component +description: Guide for developing Lit web components in the Common UI v2 system (@commontools/ui/v2). Use when creating or modifying ct- prefixed components, implementing theme integration, working with Cell abstractions, or building reactive UI components that integrate with the Common Tools runtime. +--- + +# Lit Component Development for Common UI + +This skill provides guidance for developing Lit web components within the Common UI v2 component library (`packages/ui/src/v2`). + +## When to Use This Skill + +Use this skill when: +- Creating new `ct-` prefixed components in the UI package +- Modifying existing Common UI v2 components +- Implementing theme-aware components +- Integrating components with Cell abstractions from the runtime +- Building reactive components for pattern/recipe UIs +- Debugging component lifecycle or reactivity issues + +## Core Philosophy + +Common UI is inspired by SwiftUI and emphasizes: + +1. **Default Configuration Works**: Components should work together with minimal configuration +2. **Composition Over Control**: Emphasize composing components rather than granular styling +3. **Adaptive to User Preferences**: Respect system preferences and theme settings (theme is ambient context, not explicit props) +4. **Reactive Binding Model**: Integration with FRP-style Cell abstractions from the runtime +5. **Progressive Enhancement**: Components work with plain values but enhance with Cells for reactivity +6. **Separation of Concerns**: Presentation components, theme-aware inputs, Cell-aware state, runtime-integrated operations + +## Quick Start Pattern + +### 1. Choose Component Category + +Identify which category the component falls into: + +- **Layout**: Arranges other components (vstack, hstack, screen) +- **Visual**: Displays styled content (separator, skeleton, label) +- **Input**: Captures user interaction (button, input, checkbox) +- **Complex/Integrated**: Deep runtime integration with Cells (render, list, outliner) + +**Complexity spectrum:** Components range from pure presentation (no runtime) to deeply integrated (Cell operations, pattern execution, backlink resolution). Choose the simplest pattern that meets requirements. + +See `references/component-patterns.md` for detailed patterns for each category and `references/advanced-patterns.md` for complex integration patterns. + +### 2. Create Component Files + +Create the component directory structure: + +``` +packages/ui/src/v2/components/ct-component-name/ +├── ct-component-name.ts # Component implementation +├── index.ts # Export and registration +└── styles.ts # Optional: for complex components +``` + +### 3. Implement Component + +Basic template: + +```typescript +import { css, html } from "lit"; +import { BaseElement } from "../../core/base-element.ts"; + +export class CTComponentName extends BaseElement { + static override styles = [ + BaseElement.baseStyles, + css` + :host { + display: block; + box-sizing: border-box; + } + + *, + *::before, + *::after { + box-sizing: inherit; + } + `, + ]; + + static override properties = { + // Define reactive properties + }; + + constructor() { + super(); + // Set defaults + } + + override render() { + return html``; + } +} + +globalThis.customElements.define("ct-component-name", CTComponentName); +``` + +### 4. Create Index File + +```typescript +import { CTComponentName } from "./ct-component-name.ts"; + +if (!customElements.get("ct-component-name")) { + customElements.define("ct-component-name", CTComponentName); +} + +export { CTComponentName }; +export type { /* exported types */ }; +``` + +## Theme Integration + +For components that need to consume theme (input and complex components): + +```typescript +import { consume } from "@lit/context"; +import { property } from "lit/decorators.js"; +import { + applyThemeToElement, + type CTTheme, + defaultTheme, + themeContext, +} from "../theme-context.ts"; + +export class MyComponent extends BaseElement { + @consume({ context: themeContext, subscribe: true }) + @property({ attribute: false }) + declare theme?: CTTheme; + + override firstUpdated(changed: Map) { + super.firstUpdated(changed); + this._updateThemeProperties(); + } + + override updated(changed: Map) { + super.updated(changed); + if (changed.has("theme")) { + this._updateThemeProperties(); + } + } + + private _updateThemeProperties() { + const currentTheme = this.theme || defaultTheme; + applyThemeToElement(this, currentTheme); + } +} +``` + +Then use theme CSS variables with fallbacks: + +```css +.button { + background-color: var( + --ct-theme-color-primary, + var(--ct-color-primary, #3b82f6) + ); + border-radius: var( + --ct-theme-border-radius, + var(--ct-border-radius-md, 0.375rem) + ); + font-family: var(--ct-theme-font-family, inherit); +} +``` + +**Complete theme reference:** See `references/theme-system.md` for all available CSS variables and helper functions. + +## Cell Integration + +For components that work with reactive runtime data: + +```typescript +import { property } from "lit/decorators.js"; +import type { Cell } from "@commontools/runner"; +import { isCell } from "@commontools/runner"; + +export class MyComponent extends BaseElement { + @property({ attribute: false }) + declare cell: Cell; + + private _unsubscribe: (() => void) | null = null; + + override updated(changedProperties: Map) { + super.updated(changedProperties); + + if (changedProperties.has("cell")) { + // Clean up previous subscription + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + + // Subscribe to new Cell + if (this.cell && isCell(this.cell)) { + this._unsubscribe = this.cell.sink(() => { + this.requestUpdate(); + }); + } + } + } + + override disconnectedCallback() { + super.disconnectedCallback(); + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + } + + override render() { + if (!this.cell) return html``; + + const value = this.cell.get(); + return html`
${value}
`; + } +} +``` + +**Complete Cell patterns:** See `references/cell-integration.md` for: +- Subscription management +- Nested property access with `.key()` +- Array cell manipulation +- Transaction-based mutations +- Finding cells by equality + +## Reactive Controllers + +For reusable component behaviors, use reactive controllers. Example: `InputTimingController` for debouncing/throttling: + +```typescript +import { InputTimingController } from "../../core/input-timing-controller.ts"; + +export class CTInput extends BaseElement { + @property() + timingStrategy: "immediate" | "debounce" | "throttle" | "blur" = "debounce"; + + @property() + timingDelay: number = 500; + + private inputTiming = new InputTimingController(this, { + strategy: this.timingStrategy, + delay: this.timingDelay, + }); + + private handleInput(event: Event) { + const value = (event.target as HTMLInputElement).value; + + this.inputTiming.schedule(() => { + this.emit("ct-change", { value }); + }); + } +} +``` + +## Common Patterns + +### Event Emission + +Use the `emit()` helper from `BaseElement`: + +```typescript +private handleChange(newValue: string) { + this.emit("ct-change", { value: newValue }); +} +``` + +Events are automatically `bubbles: true` and `composed: true`. + +### Dynamic Classes + +Use `classMap` for conditional classes: + +```typescript +import { classMap } from "lit/directives/class-map.js"; + +const classes = { + button: true, + [this.variant]: true, + disabled: this.disabled, +}; + +return html``; +``` + +### List Rendering + +Use `repeat` directive with stable keys: + +```typescript +import { repeat } from "lit/directives/repeat.js"; + +return html` + ${repeat( + items, + (item) => item.id, // stable key + (item) => html`
${item.title}
` + )} +`; +``` + +## Testing + +Colocate tests with components: + +```typescript +// ct-button.test.ts +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; +import { CTButton } from "./ct-button.ts"; + +describe("CTButton", () => { + it("should be defined", () => { + expect(CTButton).toBeDefined(); + }); + + it("should have default properties", () => { + const element = new CTButton(); + expect(element.variant).toBe("primary"); + }); +}); +``` + +Run with: `deno task test` (includes required flags) + +## Package Structure + +Components are exported from `@commontools/ui/v2`: + +```typescript +// packages/ui/src/v2/index.ts +export { CTButton } from "./components/ct-button/index.ts"; +export type { ButtonVariant } from "./components/ct-button/index.ts"; +``` + +## Reference Documentation + +Load these references as needed for detailed guidance: + +- **`references/component-patterns.md`** - Detailed patterns for each component category, file structure, type safety, styling conventions, event handling, and lifecycle methods +- **`references/theme-system.md`** - Theme philosophy, `ct-theme` provider, CTTheme interface, CSS variables, and theming patterns +- **`references/cell-integration.md`** - Comprehensive Cell integration patterns including subscriptions, mutations, array handling, and common pitfalls +- **`references/advanced-patterns.md`** - Advanced architectural patterns revealed by complex components: context provision, third-party integration, reactive controllers, path-based operations, diff-based rendering, and progressive enhancement + +## Key Conventions + +1. **Always extend `BaseElement`** - Provides `emit()` helper and base CSS variables +2. **Include box-sizing reset** - Ensures consistent layout behavior +3. **Use `attribute: false`** for objects/arrays/Cells - Prevents serialization errors +4. **Prefix custom events with `ct-`** - Namespace convention +5. **Export types separately** - Use `export type { ... }` +6. **Clean up subscriptions** - Always unsubscribe in `disconnectedCallback()` +7. **Use transactions for Cell mutations** - Never mutate cells directly +8. **Provide CSS variable fallbacks** - Components should work without theme context +9. **Document with JSDoc** - Include `@element`, `@attr`, `@fires`, `@example` +10. **Run tests with `deno task test`** - Not plain `deno test` + +## Common Pitfalls to Avoid + +- ❌ Forgetting to clean up Cell subscriptions (causes memory leaks) +- ❌ Mutating Cells without transactions (breaks reactivity) +- ❌ Using array index as key in `repeat()` (breaks reactivity) +- ❌ Missing box-sizing reset (causes layout issues) +- ❌ Not providing CSS variable fallbacks (breaks without theme) +- ❌ Using `attribute: true` for objects/arrays (serialization errors) +- ❌ Skipping `super` calls in lifecycle methods (breaks base functionality) + +## Architecture Patterns to Study + +Study these components to understand architectural patterns: + +**Basic patterns:** +- **Simple visual:** `ct-separator` - Minimal component, CSS parts, ARIA +- **Layout:** `ct-vstack` - Flexbox abstraction, utility classes with `classMap` +- **Themed input:** `ct-button` - Theme consumption, event emission, variants + +**Advanced patterns:** +- **Context provider:** `ct-theme` - Ambient configuration with `@provide`, `display: contents`, reactive Cell subscriptions +- **Cell integration:** `ct-list` - Array cell manipulation, finding by equality, transaction-based mutations +- **Runtime rendering:** `ct-render` - Recipe loading, UI extraction, lifecycle management +- **Third-party integration:** `ct-code-editor` - CodeMirror lifecycle, Compartments, bidirectional sync, CellController +- **Tree operations:** `ct-outliner` - Path-based operations, diff-based rendering, keyboard commands, MentionController + +Each component reveals deeper patterns - study them not just for API but for architectural principles. diff --git a/.claude/skills/lit-component/references/advanced-patterns.md b/.claude/skills/lit-component/references/advanced-patterns.md new file mode 100644 index 000000000..215b5b15a --- /dev/null +++ b/.claude/skills/lit-component/references/advanced-patterns.md @@ -0,0 +1,453 @@ +# Advanced Component Patterns + +This document explores advanced patterns revealed by complex components like `ct-theme`, `ct-code-editor`, and `ct-outliner`. + +## Architectural Principles + +### 1. Context Provision Pattern (`ct-theme`) + +**Philosophy:** Components should receive configuration from their environment, not just properties. Theme is ambient context that flows down the tree. + +**Key Insight:** Use `display: contents` to be invisible in layout while providing context. + +```typescript +import { provide } from "@lit/context"; + +export class CTThemeProvider extends BaseElement { + static override styles = css` + :host { + display: contents; /* Do not add extra layout */ + } + `; + + @property({ attribute: false }) + theme: any = {}; + + @provide({ context: themeContext }) + @property({ attribute: false }) + _computedTheme: CTTheme = defaultTheme; + + private _recomputeAndApply() { + // Merge partial theme with defaults (recipe-style support) + this._computedTheme = mergeWithDefaultTheme(this.theme); + // Apply to self for CSS variable cascade + applyThemeToElement(this, this._computedTheme); + // Subscribe to Cell properties for reactive theme updates + this.#setupSubscriptions(); + } + + #setupSubscriptions() { + // Clean up previous subscriptions + for (const off of this.#unsubs) off(); + this.#unsubs = []; + + // Subscribe to Cell properties in theme object + for (const key of Object.keys(this.theme)) { + const val = this.theme[key]; + if (isCell(val)) { + const off = val.sink(() => this._recomputeAndApply()); + this.#unsubs.push(off); + } + } + } +} +``` + +**Pattern Lessons:** +1. **Context providers are invisible:** Use `display: contents` to not affect layout +2. **Merge partial with defaults:** Support both full and recipe-style partial themes +3. **Subscribe to Cell properties:** Theme values can be reactive Cells +4. **Apply to self:** Set CSS variables on the provider element so they cascade +5. **Clean up subscriptions:** Always unsubscribe in `disconnectedCallback()` + +**When to use:** Any time you need to provide ambient configuration (theme, locale, services) to a subtree. + +--- + +### 2. Third-Party Integration Pattern (`ct-code-editor`) + +**Philosophy:** Complex third-party libraries (CodeMirror, Monaco, etc.) require careful lifecycle management and bidirectional synchronization with Lit properties. + +**Key Insights:** +- Initialize in `firstUpdated()`, not `connectedCallback()` or constructor +- Use Compartments for reconfigurable extensions +- Synchronize bidirectionally: Lit props → library AND library → Lit/Cells +- Clean up library instances in `disconnectedCallback()` + +```typescript +export class CTCodeEditor extends BaseElement { + private _editorView: EditorView | undefined; + private _lang = new Compartment(); + private _readonly = new Compartment(); + private _cleanupFns: Array<() => void> = []; + + // Cell controller manages Cell <-> value synchronization + private _cellController = createStringCellController(this, { + timing: { strategy: "debounce", delay: 500 }, + onChange: (newValue, oldValue) => { + this.emit("ct-change", { value: newValue, oldValue }); + }, + }); + + protected override firstUpdated(_changedProperties: PropertyValues): void { + super.firstUpdated(_changedProperties); + this._initializeEditor(); + this._cellController.bind(this.value); + this._setupCellSyncHandler(); + } + + private _initializeEditor(): void { + const editorElement = this.shadowRoot?.querySelector(".code-editor"); + if (!editorElement) return; + + const extensions: Extension[] = [ + basicSetup, + // Use Compartments for reconfigurable extensions + this._lang.of(getLangExtFromMimeType(this.language)), + this._readonly.of(EditorState.readOnly.of(this.readonly)), + + // Sync editor changes → Cell/value + EditorView.updateListener.of((update) => { + if (update.docChanged && !this.readonly) { + this.setValue(update.state.doc.toString()); + } + }), + ]; + + this._editorView = new EditorView({ + state: EditorState.create({ + doc: this.getValue(), + extensions, + }), + parent: editorElement, + }); + } + + override updated(changedProperties: Map) { + super.updated(changedProperties); + + // Sync value changes (different Cell) → controller → editor + if (changedProperties.has("value")) { + this._cellController.bind(this.value); + this._updateEditorFromCellValue(); + } + + // Reconfigure extensions using Compartments + if (changedProperties.has("language") && this._editorView) { + this._editorView.dispatch({ + effects: this._lang.reconfigure(getLangExtFromMimeType(this.language)), + }); + } + + if (changedProperties.has("readonly") && this._editorView) { + this._editorView.dispatch({ + effects: this._readonly.reconfigure( + EditorState.readOnly.of(this.readonly) + ), + }); + } + } + + private _updateEditorFromCellValue(): void { + if (this._editorView) { + const newValue = this.getValue(); + const currentValue = this._editorView.state.doc.toString(); + if (newValue !== currentValue) { + this._editorView.dispatch({ + changes: { from: 0, to: this._editorView.state.doc.length, insert: newValue }, + }); + } + } + } + + override disconnectedCallback() { + super.disconnectedCallback(); + this._cleanupFns.forEach((fn) => fn()); + this._cleanupFns = []; + if (this._editorView) { + this._editorView.destroy(); + this._editorView = undefined; + } + } +} +``` + +**Pattern Lessons:** +1. **Deferred initialization:** Wait for `firstUpdated()` to ensure DOM is ready +2. **Compartments for reconfiguration:** Use library-specific patterns for dynamic updates +3. **Bidirectional sync:** Editor changes flow to Cells, Cell changes flow to editor +4. **Prevent circular updates:** Check if values actually changed before updating +5. **Controller abstraction:** Use reactive controllers for common patterns (CellController, InputTimingController) +6. **Explicit cleanup:** Destroy library instances and clear subscriptions + +**When to use:** Integrating any third-party library (Monaco, Quill, ProseMirror, D3, etc.) that manages its own DOM and state. + +--- + +### 3. Reactive Controller Pattern + +**Philosophy:** Extract reusable component behaviors into reactive controllers rather than mixins or base classes. + +**Common Controllers in Common UI:** + +#### InputTimingController +Manages input debouncing/throttling/blur strategies: + +```typescript +private inputTiming = new InputTimingController(this, { + strategy: this.timingStrategy, + delay: this.timingDelay, +}); + +private handleInput(event: Event) { + const value = (event.target as HTMLInputElement).value; + this.inputTiming.schedule(() => { + this.emit("ct-change", { value }); + }); +} +``` + +#### CellController (String/Array) +Manages Cell ↔ value synchronization with timing: + +```typescript +private _cellController = createStringCellController(this, { + timing: { strategy: "debounce", delay: 500 }, + onChange: (newValue, oldValue) => { + this.emit("ct-change", { value: newValue, oldValue }); + }, +}); + +// Bind to Cell or plain value +this._cellController.bind(this.value); + +// Get current value (works with Cell or plain value) +const currentValue = this._cellController.getValue(); + +// Set value (syncs to Cell if bound) +this._cellController.setValue(newValue); +``` + +#### MentionController +Manages @-mention autocomplete with Cell: + +```typescript +private mentionController = new MentionController(this); + +// In firstUpdated +this.mentionController.setup({ + mentionableCell: this.mentionable, + onMentionSelect: (charm) => { + // Handle mention selection + }, +}); +``` + +**Pattern Lessons:** +1. **Controllers own cleanup:** Implement `hostDisconnected()` for cleanup +2. **Controllers don't render:** They manage state and side effects only +3. **Configuration over inheritance:** Prefer configuring controllers over extending base classes +4. **Share controllers across component types:** Same timing logic for input/textarea/editor + +**When to create a controller:** +- Behavior is reused across multiple components +- Behavior has complex lifecycle management (subscriptions, timers, external resources) +- Behavior is independent of rendering + +--- + +### 4. Path-Based Operations Pattern (`ct-outliner`) + +**Philosophy:** For tree structures, use paths (arrays of indices) instead of direct references to enable operations that work with both immutable state and Cells. + +**Key Insight:** Paths are stable references that survive tree mutations. + +```typescript +// Path is array of indices: [0, 2, 1] means root -> first child -> third child -> second child +type NodePath = number[]; + +// Get node by path +function getNodeByPath(tree: Tree, path: NodePath): Node | null { + let current: Node = tree.root; + for (const index of path) { + if (!current.children || index >= current.children.length) { + return null; + } + current = current.children[index]; + } + return current; +} + +// Get node Cell by path (for mutations) +function getNodeCellByPath(treeCell: Cell, path: NodePath): Cell { + let current = treeCell.key("root"); + for (const index of path) { + current = current.key("children").key(index); + } + return current; +} + +// Operations use paths for stability +class TreeOperations { + insertChild(treeCell: Cell, parentPath: NodePath, index: number): void { + const parentCell = getNodeCellByPath(treeCell, parentPath); + const childrenCell = parentCell.key("children"); + + mutateCell(childrenCell, (cell) => { + const children = cell.get() || []; + children.splice(index, 0, { body: "", children: [] }); + cell.set(children); + }); + } + + deleteNode(treeCell: Cell, path: NodePath): void { + const parentPath = path.slice(0, -1); + const index = path[path.length - 1]; + const parentCell = getNodeCellByPath(treeCell, parentPath); + + mutateCell(parentCell.key("children"), (cell) => { + const children = cell.get() || []; + children.splice(index, 1); + cell.set(children); + }); + } +} +``` + +**Pattern Lessons:** +1. **Paths over references:** Paths remain valid after mutations, references don't +2. **Separate read and write paths:** `getNodeByPath` for reads, `getNodeCellByPath` for mutations +3. **Path utilities:** Create helper functions for common path operations (parent, siblings, depth) +4. **Diff-based updates:** Calculate diffs between tree versions to minimize DOM updates +5. **Event context includes paths:** Keyboard events include current node path for operations + +**When to use:** Any hierarchical structure (trees, outlines, nested lists) that supports editing. + +--- + +### 5. Diff-Based Rendering Pattern (`ct-outliner`) + +**Philosophy:** For complex nested structures, calculate minimal diffs to update only what changed rather than re-rendering everything. + +```typescript +class TreeDiffCalculator { + diff(oldTree: Tree, newTree: Tree): TreeDiff { + // Calculate minimal set of operations to transform oldTree → newTree + const operations: Operation[] = []; + this._diffNodes(oldTree.root, newTree.root, [], operations); + return { operations }; + } + + private _diffNodes( + oldNode: Node, + newNode: Node, + path: NodePath, + operations: Operation[] + ): void { + // Check if node body changed + if (oldNode.body !== newNode.body) { + operations.push({ type: "update", path, body: newNode.body }); + } + + // Diff children arrays + this._diffChildren( + oldNode.children || [], + newNode.children || [], + path, + operations + ); + } +} + +class PathDiffApplier { + apply(treeCell: Cell, diff: TreeDiff): void { + // Apply minimal operations to Cell tree + for (const op of diff.operations) { + switch (op.type) { + case "update": + this._updateNode(treeCell, op.path, op.body); + break; + case "insert": + this._insertNode(treeCell, op.path, op.node); + break; + case "delete": + this._deleteNode(treeCell, op.path); + break; + } + } + } +} +``` + +**Pattern Lessons:** +1. **Two-phase updates:** Calculate diff, then apply operations +2. **Path-based operations:** All operations reference paths for stability +3. **Minimal updates:** Only touch changed nodes, not entire tree +4. **Preserve focus:** Diff application preserves user focus/selection +5. **Transaction boundaries:** Apply entire diff in single transaction + +**When to use:** +- Large nested structures where full re-render is expensive +- Preserving DOM state (focus, selection, scroll position) is important +- Structure changes frequently in response to user input + +--- + +### 6. Progressive Enhancement Pattern + +**Philosophy:** Components should work with plain values but enhance when given Cells for reactivity. + +```typescript +@property({ attribute: false }) +value: Cell | string; + +// Support both Cell and plain value +private getValue(): string { + return isCell(this.value) ? this.value.get() : this.value; +} + +private setValue(newValue: string): void { + if (isCell(this.value)) { + mutateCell(this.value, (cell) => cell.set(newValue)); + } else { + this.value = newValue; + this.requestUpdate(); + } +} + +// Set up subscription only if Cell +override updated(changedProperties: Map) { + if (changedProperties.has("value")) { + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + + if (isCell(this.value)) { + this._unsubscribe = this.value.sink(() => { + this.requestUpdate(); + }); + } + } +} +``` + +**Pattern Lessons:** +1. **Type unions:** `value: Cell | T` for flexibility +2. **Runtime checks:** Use `isCell()` to determine behavior +3. **Unified interface:** Provide `getValue()`/`setValue()` that work with both +4. **Conditional subscriptions:** Only subscribe to Cells, not plain values +5. **Graceful fallback:** Component works without Cells, just not reactively + +--- + +## Component Complexity Spectrum + +Components fall on a spectrum of integration depth: + +1. **Pure presentation:** Layout, visual components (no runtime integration) +2. **Themed inputs:** Consume theme, emit events (shallow integration) +3. **Cell-aware:** Support Cell properties, manage subscriptions (medium integration) +4. **Runtime-integrated:** Deep Cell manipulation, pattern execution, backlink resolution (deep integration) + +Choose the simplest pattern that meets requirements. Don't add Cell support just because you can. diff --git a/.claude/skills/lit-component/references/cell-integration.md b/.claude/skills/lit-component/references/cell-integration.md new file mode 100644 index 000000000..86d896292 --- /dev/null +++ b/.claude/skills/lit-component/references/cell-integration.md @@ -0,0 +1,286 @@ +# Cell Integration Patterns + +This document covers patterns for integrating Common Tools runtime Cell abstractions with Lit components. + +## What are Cells? + +Cells are reactive data containers from the Common Tools runtime (`@commontools/runner`). They provide: +- Reactive updates via subscriptions +- Transactional mutations +- Path-based access to nested data +- Runtime integration for charm execution + +## When to Use Cell Integration + +Use Cell integration when: +- The component needs to render reactive data from the runtime +- The component allows users to edit data that should sync back to the runtime +- The component is part of a pattern/recipe UI that manipulates charm state + +Do NOT use Cell integration for: +- Simple presentational components +- Components that work with plain JavaScript values +- Layout components + +## Basic Cell Patterns + +### Property Declaration + +Declare Cell properties using `@property({ attribute: false })`: + +```typescript +import { property } from "lit/decorators.js"; +import type { Cell } from "@commontools/runner"; + +export class MyComponent extends BaseElement { + @property({ attribute: false }) + declare cell: Cell; +} +``` + +Note: `attribute: false` prevents Lit from trying to serialize Cells as attributes. + +### Subscribing to Cell Changes + +Subscribe to cell changes in `updated()` lifecycle: + +```typescript +private _unsubscribe: (() => void) | null = null; + +override updated(changedProperties: Map) { + super.updated(changedProperties); + + if (changedProperties.has("cell")) { + // Clean up previous subscription + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } + + // Subscribe to new Cell if it exists + if (this.cell && isCell(this.cell)) { + this._unsubscribe = this.cell.sink(() => { + this.requestUpdate(); + }); + } + } +} + +override disconnectedCallback() { + super.disconnectedCallback(); + // Clean up subscription + if (this._unsubscribe) { + this._unsubscribe(); + this._unsubscribe = null; + } +} +``` + +### Reading Cell Values + +Use `.get()` to read cell values in `render()`: + +```typescript +override render() { + if (!this.cell) { + return html`
No data
`; + } + + const value = this.cell.get(); + + return html`
${value}
`; +} +``` + +### Accessing Nested Properties + +Use `.key()` for nested access: + +```typescript +const userCell: Cell<{ name: string; email: string }>; + +// Access nested properties +const nameCell = userCell.key("name"); +const name = nameCell.get(); // Get the actual value +``` + +### Array Cells + +For array cells, use `.key(index)`: + +```typescript +const listCell: Cell; +const items = listCell.get(); + +// Render each item using its cell +return html` + ${items.map((_, index) => { + const itemCell = listCell.key(index); + return this.renderItem(itemCell); + })} +`; +``` + +## Mutating Cells + +### Simple Mutations with Transactions + +Always use transactions for mutations: + +```typescript +function mutateCell(cell: Cell, mutator: (cell: Cell) => void): void { + const tx = cell.runtime.edit(); + mutator(cell.withTx(tx)); + tx.commit(); +} + +// Usage +mutateCell(myCell, (cell) => { + cell.set(newValue); +}); +``` + +### Array Mutations + +```typescript +// Add item +mutateCell(listCell, (cell) => { + cell.push({ title: "New Item", [ID]: crypto.randomUUID() }); +}); + +// Remove item by filter +mutateCell(listCell, (cell) => { + const filtered = cell.get().filter((_, i) => !cell.key(i).equals(itemToRemove)); + cell.set(filtered); +}); + +// Update item property +mutateCell(listCell, (cell) => { + cell.key(index).key("title").set(newTitle); +}); +``` + +### Finding Cells in Arrays + +Use cell equality checking to find items: + +```typescript +function findCellIndex(listCell: Cell, itemCell: Cell): number { + const length = listCell.get().length; + for (let i = 0; i < length; i++) { + if (itemCell.equals(listCell.key(i))) { + return i; + } + } + return -1; +} +``` + +## Advanced Patterns + +### Supporting Both Cell and Plain Values + +```typescript +@property({ attribute: false }) +value: Cell | T[] | null = null; + +override render() { + if (!this.value) { + return html`
No data
`; + } + + const items = isCell(this.value) ? this.value.get() : this.value; + + return html` + ${items.map((item, index) => this.renderItem(item, index))} + `; +} +``` + +### Rendering UI from Cells + +The `ct-render` component handles rendering cells with `[UI]` properties: + +```typescript +import { UI } from "@commontools/api"; + +// In render() +return html``; +``` + +The component automatically: +- Extracts UI subcells +- Loads recipes if needed +- Handles cleanup + +### Cell Type Checking + +```typescript +import { isCell } from "@commontools/runner"; + +if (isCell(this.value)) { + // It's a Cell, use .get(), .key(), etc. +} else { + // It's a plain value +} +``` + +## Common Pitfalls + +### ❌ Don't: Forget to clean up subscriptions + +```typescript +// BAD - memory leak +this.cell.sink(() => this.requestUpdate()); +``` + +### ✅ Do: Clean up in disconnectedCallback + +```typescript +// GOOD +this._unsubscribe = this.cell.sink(() => this.requestUpdate()); + +override disconnectedCallback() { + super.disconnectedCallback(); + if (this._unsubscribe) { + this._unsubscribe(); + } +} +``` + +### ❌ Don't: Mutate cells directly + +```typescript +// BAD - no transaction +this.cell.set(newValue); +``` + +### ✅ Do: Use transactions + +```typescript +// GOOD +mutateCell(this.cell, (cell) => cell.set(newValue)); +``` + +### ❌ Don't: Use array index as key in repeat() + +```typescript +// BAD - breaks reactivity +repeat(items, (_, index) => index, ...) +``` + +### ✅ Do: Use stable identifiers or composite keys + +```typescript +// GOOD +repeat(items, (item, index) => `${index}-${item.title}`, ...) +``` + +## Real-World Example: ct-list + +See `packages/ui/src/v2/components/ct-list/ct-list.ts` for a complete example demonstrating: +- Cell subscription management +- Array cell manipulation +- Finding items by cell equality +- Transaction-based mutations +- Supporting both Cell and plain array values diff --git a/.claude/skills/lit-component/references/component-patterns.md b/.claude/skills/lit-component/references/component-patterns.md new file mode 100644 index 000000000..b1fbd6aaa --- /dev/null +++ b/.claude/skills/lit-component/references/component-patterns.md @@ -0,0 +1,480 @@ +# Component Development Patterns + +This document covers standard patterns and conventions for developing Lit components in Common UI. + +## Component Categories + +Common UI components fall into distinct categories, each with specific patterns: + +### 1. Layout Components + +Components that arrange other components without providing content themselves. + +**Examples:** `ct-vstack`, `ct-hstack`, `ct-screen`, `ct-autolayout` + +**Characteristics:** +- Use flexbox or grid +- Accept child elements via slots +- Provide gap, alignment, and spacing controls +- No theme color consumption (mostly) +- Simple property-based configuration + +**Pattern:** +```typescript +export class CTVStack extends BaseElement { + static override properties = { + gap: { type: String }, + align: { type: String }, + justify: { type: String }, + }; + + declare gap: string; + declare align: string; + declare justify: string; + + override render() { + const classes = { + stack: true, + [`gap-${this.gap}`]: true, + [`align-${this.align}`]: true, + [`justify-${this.justify}`]: true, + }; + + return html` +
+ +
+ `; + } +} +``` + +### 2. Visual Components + +Components that display content with styling. + +**Examples:** `ct-label`, `ct-separator`, `ct-skeleton` + +**Characteristics:** +- May consume theme +- Provide visual feedback or decoration +- Usually simple with few properties + +**Pattern:** +```typescript +export class CTSeparator extends BaseElement { + static override properties = { + orientation: { type: String }, + decorative: { type: Boolean }, + }; + + declare orientation: "horizontal" | "vertical"; + declare decorative: boolean; + + override render() { + return html` +
+ `; + } +} +``` + +### 3. Input Components + +Components that capture user input. + +**Examples:** `ct-button`, `ct-input`, `ct-checkbox`, `ct-textarea` + +**Characteristics:** +- Consume theme for consistent styling +- Emit custom events (use `this.emit()`) +- May use `InputTimingController` for debouncing +- Handle disabled states + +**Pattern:** +```typescript +export class CTButton extends BaseElement { + @consume({ context: themeContext, subscribe: true }) + @property({ attribute: false }) + declare theme?: CTTheme; + + static override properties = { + variant: { type: String }, + disabled: { type: Boolean, reflect: true }, + }; + + override firstUpdated(changed: Map) { + super.firstUpdated(changed); + this._updateThemeProperties(); + } + + override updated(changed: Map) { + super.updated(changed); + if (changed.has("theme")) { + this._updateThemeProperties(); + } + } + + private _updateThemeProperties() { + const currentTheme = this.theme || defaultTheme; + applyThemeToElement(this, currentTheme); + } + + private _handleClick(e: Event) { + if (this.disabled) { + e.preventDefault(); + e.stopPropagation(); + return; + } + // Emit custom event + this.emit("ct-click", { /* detail */ }); + } +} +``` + +### 4. Complex/Integrated Components + +Components that deeply integrate with the runtime and Cell abstractions. + +**Examples:** `ct-render`, `ct-list`, `ct-code-editor`, `ct-outliner` + +**Characteristics:** +- Work with Cell properties +- Manage subscriptions +- Handle transactions for mutations +- Complex lifecycle management +- May use reactive controllers + +**Pattern:** See `references/cell-integration.md` for detailed patterns. + +## File Structure + +Each component should follow this structure: + +``` +ct-component-name/ +├── ct-component-name.ts # Component implementation +├── index.ts # Export and registration +└── styles.ts # Optional: extracted styles (for complex components) +``` + +### Component Implementation File + +```typescript +// ct-button.ts +import { css, html } from "lit"; +import { property } from "lit/decorators.js"; +import { BaseElement } from "../../core/base-element.ts"; + +export type ButtonVariant = "primary" | "secondary" | "destructive"; + +export class CTButton extends BaseElement { + static override styles = [ + BaseElement.baseStyles, + css` + /* component styles */ + `, + ]; + + static override properties = { + variant: { type: String }, + }; + + declare variant: ButtonVariant; + + constructor() { + super(); + this.variant = "primary"; + } + + override render() { + return html``; + } +} + +globalThis.customElements.define("ct-button", CTButton); +``` + +### Index File + +```typescript +// index.ts +import { CTButton, ButtonVariant } from "./ct-button.ts"; + +if (!customElements.get("ct-button")) { + customElements.define("ct-button", CTButton); +} + +export { CTButton }; +export type { ButtonVariant }; +``` + +Note: The conditional check prevents duplicate registration errors during hot module replacement. + +## Type Safety + +### Export Types + +Always export types separately: + +```typescript +export type { ButtonVariant, ButtonSize }; +``` + +### Property Type Declarations + +Use `declare` for typed properties: + +```typescript +static override properties = { + variant: { type: String }, + size: { type: String }, + disabled: { type: Boolean, reflect: true }, +}; + +declare variant: ButtonVariant; +declare size: ButtonSize; +declare disabled: boolean; +``` + +### Type Imports + +Import types with `type` keyword when possible: + +```typescript +import type { Cell } from "@commontools/runner"; +import type { CTTheme } from "../theme-context.ts"; +``` + +## Styling Conventions + +### Base Styles + +Always extend `BaseElement.baseStyles` for CSS variables: + +```typescript +static override styles = [ + BaseElement.baseStyles, + css` + /* component styles */ + `, +]; +``` + +### Box Sizing + +Always include box-sizing reset: + +```css +:host { + display: block; + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} +``` + +### CSS Parts + +Expose major elements as parts for external styling: + +```typescript +return html` + +`; +``` + +### Class Organization + +Use `classMap` from `lit/directives/class-map.js` for dynamic classes: + +```typescript +import { classMap } from "lit/directives/class-map.js"; + +const classes = { + button: true, + [this.variant]: true, + [this.size]: true, + disabled: this.disabled, +}; + +return html``; +``` + +## Event Handling + +### Emitting Events + +Use the `emit()` helper from `BaseElement`: + +```typescript +protected emit( + eventName: string, + detail?: T, + options?: EventInit, +): boolean +``` + +Events are automatically `bubbles: true` and `composed: true`. + +**Pattern:** +```typescript +private handleChange(newValue: string) { + this.emit("ct-change", { value: newValue }); +} +``` + +### Event Naming + +- Prefix custom events with `ct-` +- Use present tense: `ct-change`, not `ct-changed` +- Be specific: `ct-add-item`, `ct-remove-item` + +### Event Documentation + +Document events in JSDoc: + +```typescript +/** + * @fires ct-change - Fired when value changes with detail: { value } + * @fires ct-submit - Fired when form is submitted with detail: { formData } + */ +``` + +## Property Conventions + +### Reflecting Properties + +Use `reflect: true` for boolean states that should be visible in DOM: + +```typescript +static override properties = { + disabled: { type: Boolean, reflect: true }, + readonly: { type: Boolean, reflect: true }, +}; +``` + +### Default Values + +Set defaults in constructor: + +```typescript +constructor() { + super(); + this.variant = "primary"; + this.size = "default"; + this.disabled = false; +} +``` + +### Non-Attribute Properties + +Use `attribute: false` for objects, arrays, and Cells: + +```typescript +static override properties = { + theme: { type: Object, attribute: false }, + cell: { attribute: false }, + items: { type: Array, attribute: false }, +}; +``` + +## Lifecycle Methods + +### Common Lifecycle Pattern + +```typescript +override firstUpdated(changed: Map) { + super.firstUpdated(changed); + // One-time setup + this._updateThemeProperties(); + this._setupEventListeners(); +} + +override updated(changed: Map) { + super.updated(changed); + // React to property changes + if (changed.has("theme")) { + this._updateThemeProperties(); + } +} + +override disconnectedCallback() { + super.disconnectedCallback(); + // Cleanup + this._cleanup(); +} +``` + +## Documentation + +### JSDoc Comments + +Provide comprehensive JSDoc: + +```typescript +/** + * CTButton - Interactive button element with multiple variants + * + * @element ct-button + * + * @attr {string} variant - Visual style: "primary" | "secondary" | "destructive" + * @attr {string} size - Button size: "default" | "sm" | "lg" | "icon" + * @attr {boolean} disabled - Whether the button is disabled + * + * @slot - Default slot for button content + * + * @fires ct-click - Fired when button is clicked + * + * @example + * Click Me + */ +``` + +## Testing + +Tests should be colocated with components: + +``` +ct-component/ +├── ct-component.ts +├── ct-component.test.ts +└── index.ts +``` + +### Basic Test Structure + +```typescript +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; +import { CTButton } from "./ct-button.ts"; + +describe("CTButton", () => { + it("should be defined", () => { + expect(CTButton).toBeDefined(); + }); + + it("should create element instance", () => { + const element = new CTButton(); + expect(element).toBeInstanceOf(CTButton); + }); + + it("should have default properties", () => { + const element = new CTButton(); + expect(element.variant).toBe("primary"); + expect(element.disabled).toBe(false); + }); +}); +``` + +Run tests with: `deno task test` (NOT `deno test` - the task includes important flags) diff --git a/.claude/skills/lit-component/references/theme-system.md b/.claude/skills/lit-component/references/theme-system.md new file mode 100644 index 000000000..418d84260 --- /dev/null +++ b/.claude/skills/lit-component/references/theme-system.md @@ -0,0 +1,239 @@ +# Common UI Theme System + +This document covers the philosophy and implementation of the Common UI theme system. + +## Philosophy: Ambient Configuration + +**Core Insight:** Theme is not a property passed to each component - it's ambient context that flows down the tree like CSS inheritance. + +**Design Goals:** +1. **Default works:** Components should work without explicit theme +2. **Composition over specification:** Nest theme providers for progressive refinement +3. **Reactive:** Theme values can be Cells that update live +4. **Recipe-friendly:** Support partial themes with recipe-style properties (`accentColor`, `fontFace`) +5. **CSS-native:** Emit CSS variables for performance and browser devtools visibility + +## The `ct-theme` Provider Component + +The `` component wraps children and provides theme context using `@lit/context`. + +**Key characteristics:** +- Uses `display: contents` to be invisible in layout +- Merges partial themes with defaults (supports recipe-style) +- Subscribes to Cell properties for reactive updates +- Applies CSS variables to itself for cascade to children + +```typescript +// Wrap any subtree to theme it + + Themed Button + + +// Nest providers for refinement + +
Dark section
+ +
Dark section with red accent
+
+
+``` + +## Theme Context + +The theme system uses `@lit/context` to provide theme values throughout the component tree. + +### CTTheme Interface + +```typescript +interface CTTheme { + fontFamily: string; + monoFontFamily: string; + borderRadius: string; + density: "compact" | "comfortable" | "spacious"; + colorScheme: "light" | "dark" | "auto"; + animationSpeed: "none" | "slow" | "normal" | "fast"; + colors: { + primary: ColorToken; + primaryForeground: ColorToken; + secondary: ColorToken; + secondaryForeground: ColorToken; + background: ColorToken; + surface: ColorToken; + surfaceHover: ColorToken; + text: ColorToken; + textMuted: ColorToken; + border: ColorToken; + borderMuted: ColorToken; + success: ColorToken; + successForeground: ColorToken; + error: ColorToken; + errorForeground: ColorToken; + warning: ColorToken; + warningForeground: ColorToken; + accent: ColorToken; + accentForeground: ColorToken; + }; +} + +type ColorToken = string | { light: string; dark: string }; +``` + +## Consuming Theme in Components + +### Basic Theme Consumption + +Use `@consume` decorator to access theme context: + +```typescript +import { consume } from "@lit/context"; +import { property } from "lit/decorators.js"; +import { + applyThemeToElement, + type CTTheme, + defaultTheme, + themeContext, +} from "../theme-context.ts"; + +export class MyComponent extends BaseElement { + @consume({ context: themeContext, subscribe: true }) + @property({ attribute: false }) + declare theme?: CTTheme; + + override firstUpdated(changed: Map) { + super.firstUpdated(changed); + this._updateThemeProperties(); + } + + override updated(changed: Map) { + super.updated(changed); + if (changed.has("theme")) { + this._updateThemeProperties(); + } + } + + private _updateThemeProperties() { + const currentTheme = this.theme || defaultTheme; + applyThemeToElement(this, currentTheme); + } +} +``` + +### Using Theme CSS Variables + +After calling `applyThemeToElement()`, the following CSS variables are available: + +**Typography:** +- `--ct-theme-font-family` +- `--ct-theme-mono-font-family` +- `--ct-theme-border-radius` +- `--ct-theme-animation-duration` + +**Colors:** +- `--ct-theme-color-primary` +- `--ct-theme-color-primary-foreground` +- `--ct-theme-color-secondary` +- `--ct-theme-color-secondary-foreground` +- `--ct-theme-color-background` +- `--ct-theme-color-surface` +- `--ct-theme-color-surface-hover` +- `--ct-theme-color-text` +- `--ct-theme-color-text-muted` +- `--ct-theme-color-border` +- `--ct-theme-color-border-muted` +- `--ct-theme-color-success` +- `--ct-theme-color-success-foreground` +- `--ct-theme-color-error` +- `--ct-theme-color-error-foreground` +- `--ct-theme-color-warning` +- `--ct-theme-color-warning-foreground` +- `--ct-theme-color-accent` +- `--ct-theme-color-accent-foreground` + +**Spacing:** +- `--ct-theme-spacing-tight` +- `--ct-theme-spacing-normal` +- `--ct-theme-spacing-loose` +- `--ct-theme-spacing-padding-message` +- `--ct-theme-spacing-padding-code` +- `--ct-theme-spacing-padding-block` + +### CSS Variable Fallback Pattern + +Always provide fallbacks to base CSS variables for components that may be used without theme context: + +```css +.button { + background-color: var( + --ct-theme-color-primary, + var(--ct-color-primary, #3b82f6) + ); + font-family: var(--ct-theme-font-family, inherit); + border-radius: var( + --ct-theme-border-radius, + var(--ct-border-radius-md, 0.375rem) + ); +} +``` + +## Base CSS Variables + +Components inherit base CSS variables from `BaseElement.baseStyles`. These are defined in `packages/ui/src/v2/styles/variables.ts`. + +### Available Base Variables + +**Colors:** +- `--ct-colors-primary-{50-900}`: Primary color scale +- `--ct-colors-gray-{50-900}`: Gray color scale +- `--ct-colors-success`, `--ct-colors-warning`, `--ct-colors-error`, `--ct-colors-info` + +**Typography:** +- `--ct-font-family-sans`, `--ct-font-family-mono` +- `--ct-font-size-{xs,sm,base,lg,xl,2xl,3xl,4xl}` +- `--ct-font-weight-{light,normal,medium,semibold,bold}` +- `--ct-line-height-{none,tight,snug,normal,relaxed,loose}` + +**Spacing:** +- `--ct-spacing-{0,1,2,3,4,5,6,8,10,12,16,20,24}` + +**Border Radius:** +- `--ct-border-radius-{none,sm,base,md,lg,xl,2xl,3xl,full}` + +**Shadows:** +- `--ct-shadow-{sm,base,md,lg,xl,none}` + +**Transitions:** +- `--ct-transition-duration-{fast,base,slow}` +- `--ct-transition-timing-{ease,ease-in,ease-out,ease-in-out}` + +**Z-index:** +- `--ct-z-index-{auto,0,10,20,30,40,50,100,1000}` + +## Theme Helper Functions + +### resolveColorScheme(scheme: ColorScheme): "light" | "dark" + +Resolves "auto" to actual color scheme based on system preference. + +### resolveColor(token: ColorToken, colorScheme: "light" | "dark"): string + +Resolves a ColorToken to a concrete color value. + +### getThemeColor(value: keyof CTTheme["colors"] | ColorToken | string, theme: CTTheme): string + +Gets a color that can be semantic (theme key), a token, or a specific value. + +### getThemeSpacing(value: string, theme: CTTheme): string + +Gets spacing that can be semantic or specific. + +### getAnimationDuration(speed: CTTheme["animationSpeed"]): string + +Returns CSS duration value based on animation speed setting. + +### createThemeVariant(baseTheme: CTTheme, overrides: Partial): CTTheme + +Creates a theme override with granular control. + +### mergeWithDefaultTheme(partialTheme: any, baseTheme?: CTTheme): CTTheme + +Merges partial theme with default, supporting recipe-style properties like `accentColor`, `fontFace`, and `borderRadius`. diff --git a/.claude/skills/recipe-dev/SKILL.md b/.claude/skills/recipe-dev/SKILL.md new file mode 100644 index 000000000..46f9e85c3 --- /dev/null +++ b/.claude/skills/recipe-dev/SKILL.md @@ -0,0 +1,363 @@ +--- +name: recipe-dev +description: Guide for developing CommonTools recipes (TypeScript patterns that define reactive data transformations with UI). Use this skill when creating recipes, modifying existing recipes, linking charms, debugging recipe errors, or working with the recipe framework. Triggers include requests like "build a recipe", "fix this recipe error", "deploy this charm", "link these charms", or questions about handlers, cells, and reactive patterns. +--- + +# Recipe Development + +## Overview + +Develop CommonTools recipes using the ct binary and the reactive recipe framework. Recipes are TypeScript/JSX programs that define data transformations with interactive UIs, deployed as "charms" that can be linked together for complex workflows. + +## When to Use This Skill + +Use this skill when: +- Building new recipes from scratch +- Modifying or debugging existing recipes +- Understanding recipe framework concepts (cells, handlers, derive, lift) +- Deploying and managing charms +- Linking charms together for data flow +- Troubleshooting type errors or runtime issues +- Working with multi-file recipe structures + +## Prerequisites + +Before starting recipe development: + +1. **Know the ct binary** - Use the **ct** skill for ct command reference +2. **Read the documentation** - Key docs to reference: + - `docs/common/RECIPES.md` - Core recipe concepts and best practices + - `docs/common/PATTERNS.md` - Common recipe patterns with examples + - `docs/common/HANDLERS.md` - Handler patterns and type guidance + - `docs/common/COMPONENTS.md` - UI components and bidirectional binding +3. **Check example recipes** - Look in `packages/patterns/` for working examples + +## Quick Decision Tree + +**What do you want to do?** + +→ **Create a new recipe** → Go to "Building a New Recipe" +→ **Modify existing recipe** → Go to "Modifying Recipes" +→ **Fix recipe errors** → Go to "Debugging Recipes" +→ **Deploy recipe as charm** → Use ct skill, see "Deployment Workflow" +→ **Link charms together** → Use ct skill for linking commands +→ **Understand recipe concepts** → Read `docs/common/RECIPES.md` and `PATTERNS.md` + +## Building a New Recipe + +### Step 1: Start Simple + +Begin with minimal viable recipe: + +```typescript +/// +import { Default, NAME, OpaqueRef, recipe, UI } from "commontools"; + +interface Item { + title: string; + done: Default; +} + +interface Input { + items: Default; +} + +export default recipe("My Recipe", ({ items }) => { + return { + [NAME]: "My Recipe", + [UI]: ( +
+ {items.map((item: OpaqueRef) => ( +
{item.title}
+ ))} +
+ ), + items, + }; +}); +``` + +### Step 2: Add Interactivity + +Add bidirectional binding for simple updates: + +```typescript +{items.map((item: OpaqueRef) => ( + + {item.title} + +))} +``` + +**Golden Rule:** Use bidirectional binding (`$prop`) for simple value updates. Only use handlers for structural changes, validation, or side effects. + +### Step 3: Add Handlers for Structural Changes + +```typescript +import { Cell, handler } from "commontools"; + +const addItem = handler< + { detail: { message: string } }, + { items: Cell } +>(({ detail }, { items }) => { + const title = detail?.message?.trim(); + if (!title) return; + + items.push({ title, done: false }); +}); + +// In UI + +``` + +### Step 4: Test and Deploy + +See "Deployment Workflow" section below. + +## Modifying Recipes + +### Getting Recipe Source + +```bash +# Use ct skill commands to get source +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] ./recipe.tsx +``` + +### Making Changes + +1. Edit the recipe file +2. Check syntax (optional): `./dist/ct dev recipe.tsx --no-run` +3. Update charm: `./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] recipe.tsx` + +## Debugging Recipes + +### Common Error Categories + +**Type Errors** (see `HANDLERS.md` for details): +- Missing `OpaqueRef` annotation in `.map()` +- Wrong style syntax (object vs string, see `COMPONENTS.md`) +- Using `Cell[]>` instead of `Cell` in handlers +- Forgetting `Cell<>` wrapper in handler state types + +**Runtime Errors** (see `RECIPES.md` for details): +- DOM access (use cells instead) +- Conditionals in JSX (use `ifElse()`) +- Calling `llm()` from handlers (only works in recipe body) + +**Data Not Updating** (see `COMPONENTS.md` for details): +- Forgot `$` prefix for bidirectional binding +- Handler event name mismatch +- Cell not passed correctly to handler + +### Debugging Process + +1. **Check TypeScript errors first** - Run `./dist/ct dev recipe.tsx --no-run` +2. **Consult the docs** - Match error pattern to relevant doc: + - Type errors → `HANDLERS.md` + - Component issues → `COMPONENTS.md` + - Pattern questions → `PATTERNS.md` + - Core concepts → `RECIPES.md` +3. **Inspect deployed charm** - Use ct skill commands to inspect state +4. **Check examples** - Look in `packages/patterns/` for similar recipes + +### Quick Error Reference + +| Error Message | Check | +|---------------|-------| +| "Property X does not exist on type 'OpaqueRef'" | Missing `OpaqueRef` in `.map()` - See `HANDLERS.md` | +| "Type 'string' is not assignable to type 'CSSProperties'" | Using string style on HTML element - See `COMPONENTS.md` | +| Handler type mismatch | Check `Cell` vs `Cell>>` - See `HANDLERS.md` | +| Data not updating | Missing `$` prefix or wrong event name - See `COMPONENTS.md` | + +## Deployment Workflow + +### Initial Deployment + +```bash +# 1. Test syntax (optional) +./dist/ct dev recipe.tsx --no-run + +# 2. Deploy to test space +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space recipe.tsx +# Record the charm ID returned + +# 3. Inspect deployed charm +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] +``` + +### Iteration Cycle + +```bash +# Update existing charm (much faster than deploying new) +./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] recipe.tsx +``` + +**Note:** Don't pre-test syntax unless deployment fails. The deployment process validates automatically. + +## Key Concepts Summary + +### Bidirectional Binding + +**Use `$` prefix for automatic two-way data binding:** + +```typescript + + + +``` + +**When NOT to use:** Need validation, side effects, or structural changes (use handlers). + +See `COMPONENTS.md` for full details. + +### Handlers + +**Use for structural changes and side effects:** + +```typescript +const removeItem = handler< + unknown, + { items: Cell>>; item: Cell } +>((_event, { items, item }) => { + const currentItems = items.get(); + const index = currentItems.findIndex(el => el.equals(item)); + if (index >= 0) { + items.set(currentItems.toSpliced(index, 1)); + } +}); +``` + +**Critical Rule:** Use `Cell` in handler parameters, not `Cell[]>`. + +See `HANDLERS.md` for complete handler patterns. + +### Reactive Transformations + +**derive() for computed values:** + +```typescript +const filteredItems = derive(items, (list) => + list.filter(item => !item.done) +); +``` + +**lift() for reusable functions:** + +```typescript +const groupByCategory = lift((items: Item[]) => { + // grouping logic +}); + +const grouped = groupByCategory(items); +``` + +See `RECIPES.md` for reactive programming details. + +### Type Annotations + +**Always annotate `.map()` parameters:** + +```typescript +{items.map((item: OpaqueRef) => ( + +))} +``` + +**Why:** TypeScript can't infer types for bidirectional binding without annotation. + +See `PATTERNS.md` for common patterns. + +## Multi-File Recipes + +When building complex recipes across multiple files: + +**Structure:** +``` +recipes/feature/ + main.tsx # Entry point + schemas.tsx # Shared types + utils.tsx # Helper functions +``` + +**Best Practices:** +- Use relative imports: `import { Schema } from "./schemas.tsx"` +- Export shared schemas for reuse +- ct bundles all dependencies automatically on deployment + +**Common Pitfall:** +- Schema mismatches between linked charms +- Solution: Export shared schemas from common file + +See `PATTERNS.md` Level 3-4 for linking and composition patterns. + +## Development Tips + +**DO:** +- Start simple, add features incrementally +- Use bidirectional binding when possible +- Reference `packages/patterns/` for examples +- Use `charm inspect` frequently when debugging +- Read relevant doc files before asking questions + +**DON'T:** +- Test syntax before deploying (unless deployment fails) +- Add multiple features before testing +- Use handlers for simple value updates +- Forget `OpaqueRef` annotations in `.map()` +- Duplicate content from `docs/common/` - reference it instead + +## Documentation Map + +When working with recipes, consult these docs based on your task: + +| Task | Read | +|------|------| +| Understanding recipe structure | `docs/common/RECIPES.md` | +| Common patterns (lists, filtering, linking) | `docs/common/PATTERNS.md` | +| Handler type errors or patterns | `docs/common/HANDLERS.md` | +| Component usage and bidirectional binding | `docs/common/COMPONENTS.md` | +| ct binary commands | Use **ct** skill | +| Working examples | `packages/patterns/` directory | + +## Resources + +### references/workflow-guide.md + +Practical ct command workflows for: +- Setting up development environment +- Development cycle (deploy, iterate, debug) +- Common tasks (modify, link, visualize) +- Debugging commands +- Multi-file recipe development +- Configuration management + +Consult when you need practical command examples beyond theory. + +## Quick Command Reference + +```bash +# Test syntax +./dist/ct dev recipe.tsx --no-run + +# Deploy new charm +./dist/ct charm new -i claude.key -a https://toolshed.saga-castor.ts.net/ -s space recipe.tsx + +# Update existing charm +./dist/ct charm setsrc -i claude.key -a https://toolshed.saga-castor.ts.net/ -s space -c charm-id recipe.tsx + +# Inspect charm +./dist/ct charm inspect -i claude.key -a https://toolshed.saga-castor.ts.net/ -s space -c charm-id + +# For full ct command reference, use the ct skill +``` + +## Remember + +- **Use the ct skill** for ct binary commands and deployment details +- **Read `docs/common/` files** for recipe framework concepts - don't ask for duplicated information +- **Check `packages/patterns/`** for working examples before building from scratch +- **Start simple** - minimal viable recipe first, then add features +- **Bidirectional binding first** - only use handlers when truly needed diff --git a/.claude/skills/recipe-dev/references/workflow-guide.md b/.claude/skills/recipe-dev/references/workflow-guide.md new file mode 100644 index 000000000..ab8289d15 --- /dev/null +++ b/.claude/skills/recipe-dev/references/workflow-guide.md @@ -0,0 +1,220 @@ +# Recipe Development Workflow Guide + +This guide covers the practical workflow for developing recipes with the ct binary, complementing the theory in `docs/common/*.md`. + +## Quick Start Workflow + +### 1. Setup (First Time Only) + +```bash +# Verify ct binary exists +ls -la ./dist/ct + +# If missing, you have two options: +# Option 1: Build the binary +deno task build-binaries --cli-only + +# Option 2: Use ct from source +deno task ct --help + +# Create identity if needed +ls -la claude.key || ./dist/ct id new > claude.key +# Or with deno task: deno task ct id new > claude.key + +# Initialize TypeScript in recipes directory +cd /path/to/recipes && ./dist/ct init +# Or with deno task: deno task ct init +``` + +**Note:** You can use `deno task ct` instead of `./dist/ct` throughout this guide if the binary isn't built. + +### 2. Development Cycle + +**Step 1: Write the recipe** +- Start simple (basic types, minimal UI) +- Add one feature at a time +- Reference `packages/patterns/` for examples + +**Step 2: Test syntax (optional, only if requested)** +```bash +./dist/ct dev recipe.tsx --no-run +``` + +**Step 3: Deploy to test space** +```bash +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space recipe.tsx +# Record the charm ID returned +``` + +**Step 4: Iterate with setsrc** +```bash +# Much faster than deploying new charms +./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] recipe.tsx +``` + +**Step 5: Inspect and debug** +```bash +# View charm state +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] + +# Get specific fields +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] items/0/title + +# Set test data +echo '{"title": "Test Item"}' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space test-space --charm [charm-id] testData +``` + +## Common Tasks + +### Modify Existing Recipe + +```bash +# 1. Get current source +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] ./recipe.tsx + +# 2. Edit the file +# (make your changes) + +# 3. Update charm +./dist/ct charm setsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] ./recipe.tsx + +# 4. Verify +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] +``` + +### Link Charms Together + +```bash +# Deploy both charms first +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] source-recipe.tsx +# Returns: source-charm-id + +./dist/ct charm new --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] target-recipe.tsx +# Returns: target-charm-id + +# Link data flow +./dist/ct charm link --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] source-charm-id/items target-charm-id/items +``` + +### Visualize Space + +```bash +# ASCII map +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] + +# Graphviz DOT for visualization +./dist/ct charm map --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --format dot +``` + +## Debugging Recipes + +### Common Error Patterns + +**Type Errors:** +- Missing `OpaqueRef` in `.map()` - See `HANDLERS.md` +- Wrong style syntax (object vs string) - See `COMPONENTS.md` +- Using `Cell[]>` in handlers instead of `Cell` - See `HANDLERS.md` + +**Runtime Errors:** +- DOM access (use cells instead) - See `RECIPES.md` +- Conditionals in JSX (use `ifElse`) - See `RECIPES.md` +- Calling `llm()` from handlers - See `RECIPES.md` + +**Data Not Updating:** +- Forgot `$` prefix for bidirectional binding - See `COMPONENTS.md` +- Handler not being called - check event names match component +- Cell not passed correctly to handler + +### Debugging Commands + +```bash +# View full charm state with JSON +./dist/ct charm inspect --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] --json + +# Check specific data paths +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] items +./dist/ct charm get --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] config/apiKey + +# Set test values +echo '"test-value"' | ./dist/ct charm set --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] testField +``` + +## Multi-File Recipes + +When building recipes that import from multiple files: + +**Structure:** +``` +recipes/ + feature/ + main.tsx # Entry point + schemas.tsx # Shared types + utils.tsx # Helper functions +``` + +**Best Practices:** +1. Use relative imports: `import { Schema } from "./schemas.tsx"` +2. Export shared schemas for reuse +3. Test each file independently first +4. Deploy main.tsx - ct bundles all dependencies + +**Common Pitfall:** +- Schema mismatches between linked charms +- Solution: Export shared schemas from common file + +## Tips for Efficient Development + +**DO:** +- Use `ct dev` to catch TypeScript errors early +- Deploy once, iterate with `setsrc` +- Test one feature at a time +- Use `charm inspect` frequently +- Check `packages/patterns/` for examples + +**DON'T:** +- Deploy new charm for every change +- Add multiple features before testing +- Pre-test syntax unless deployment fails +- Use `ct dev` with `--no-run` unless specifically debugging syntax + +## Configuration Management + +**Option 1: Environment Variables** +```bash +export CT_API_URL="https://toolshed.saga-castor.ts.net/" +export CT_IDENTITY="./claude.key" + +# Commands become shorter +./dist/ct charm ls --space my-space +``` + +**Option 2: Store config in file** +Create `.common.json`: +```json +{ + "identity": "claude.key", + "apiUrl": "https://toolshed.saga-castor.ts.net/", + "space": "my-space" +} +``` + +Then reference when needed (ct doesn't read this automatically, but you can). + +## Performance Tips + +- Use `charm inspect` instead of `charm get` for multiple fields +- Link charms for reactive updates instead of polling +- Deploy to test space first, then production +- Use `--format dot` for large space visualization (renders better) + +## Common Space Patterns + +**Development Setup:** +- `test-space` - For rapid iteration and testing +- `production-space` - For deployed, stable charms +- Personal spaces for experiments + +**Charm Organization:** +- Name charms descriptively (reflected in `[NAME]`) +- Use `charm map` to visualize before linking +- Document charm dependencies