From 66d05da1314129e44c907d5529f1967663e5ca3c Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 16:30:52 +0000 Subject: [PATCH 1/7] clean up AGENTS.md, mostly pointing to other docs - Add docs/common/RUNTIME.md - comprehensive guide for runtime development (servers, testing, integration tests, debugging) - Add docs/common/RECIPE_DEV_DEPLOY.md - build, debug, deploy pattern - Add DEVELOPMENT.md general coding standards, design principles, and best practices (removed recipe-specific deployment content) - Move packages/patterns/README.md -> docs/common/UI_TESTING.md --- AGENTS.md | 401 ++---------------- docs/common/DEVELOPMENT.md | 324 ++++++++++++++ docs/common/RECIPE_DEV_DEPLOY.md | 262 ++++++++++++ docs/common/RUNTIME.md | 228 ++++++++++ .../README.md => docs/common/UI_TESTING.md | 0 5 files changed, 846 insertions(+), 369 deletions(-) create mode 100644 docs/common/DEVELOPMENT.md create mode 100644 docs/common/RECIPE_DEV_DEPLOY.md create mode 100644 docs/common/RUNTIME.md rename packages/patterns/README.md => docs/common/UI_TESTING.md (100%) diff --git a/AGENTS.md b/AGENTS.md index 6e8cf2f4d..bd255fd6b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,371 +1,34 @@ # Repository Guidelines for AI Agents -The instructions in this document apply to the entire repository. - -## Basics - -### Build & Test - -- Check typings with `deno task check`. -- Run all tests using `deno task test`. -- To run a single test file use `deno test path/to/test.ts`. -- To test a specific package, `cd` into the package directory and run - `deno task test`. - -### `ct` & Common Tools Framework - -Before ever calling `ct` you MUST read `docs/common/CT.md`. - -### Recipe Development - -Whenever you work on patterns (sometimes called recipes), consult the `patterns` -package for a set of well-tested minimal examples. To learn more about the -pattern framework, consult `docs/common/*.md` and `tutorials/*.md`. You should -re-read `.claude/commands/recipe-dev.md` when confused to refresh your memory on -`ct` and deploying charms. - -These patterns are composed using functions from `packages/builder` and executed -by our runtime in `packages/runner`, managed by `packages/charm` and rendered by -`packages/html`. - -IMPORTANT: ignore the top level `recipes` folder, it is defunct. - -### Formatting - -- Line width is **80 characters**. -- Indent with **2 spaces**. -- **Semicolons are required.** -- Use **double quotes** for strings. -- Always run `deno fmt` before committing. - -### TypeScript - -- Export types explicitly using `export type { ... }`. -- Provide descriptive JSDoc comments on public interfaces. -- Prefer strong typing with interfaces or types instead of `any`. -- Update package-level README.md files. - -### Imports - -- Group imports by source: standard library, external, then internal. -- Prefer named exports over default exports. -- Use package names for internal imports. -- Destructure when importing multiple names from the same module. -- Import either from `@commontools/api` (internal API) or - `@commontools/api/interface` (external API), but not both. - -### Error Handling - -- Write descriptive error messages. -- Propagate errors using async/await. -- Document possible errors in JSDoc. - -### Testing - -- Structure tests with `@std/testing/bdd` (`describe`/`it`). -- Use `@std/expect` for assertions. -- Give tests descriptive names. -- Run using `deno task test` NOT `deno test`, the flags are important - -## Good Patterns & Practices - -Not all the code fits these patterns. For bigger changes, follow these -guidelines and consider refactoring existing code towards these practices. - -### Avoid Singletons - -The singleton pattern may be useful when there's a single global state. But -running multiple instances, unit tests, and reflecting state from another state -becomes impossible. Additionally, this pattern is infectious, often requiring -consuming code to also only support a single instance. - -> **❌ Avoid** - -```ts -const cache = new Map(); -export const set = (key: string, value: string) => cache.set(key, value); -export const get = (key: string): string | undefined => cache.get(key); -``` - -```ts -export const cache = new Map(); -export const instance = new Foo(); -``` - -> **✅ Prefer** - -In both cases, we can maintain multiple caches, or instances of cache consumers. - -```ts -export class Cache { - private map: Map = new Map(); - get(key: string): string | undefined { - return this.map.get(key); - } - set(key: string, value: string) { - this.map.set(key, value); - } -} -``` - -Or with a functional pattern: - -```ts -export type Cache = Map; -export const get = (cache: Cache, key: string): string | undefined => - cache.get(key); -export const set = (cache: Cache, key: string, value: string) => - cache.set(key, value); -``` - -### Keep the Module Graph clean - -We execute our JavaScript modules in many different environments: - -- Browsers (Vite built) -- Browsers (deno-web-test>esbuild Built) -- Browsers (eval'd recipes) -- Deno (scripts and servers) -- Deno (eval'd recipes) -- Deno Workers -- Deno workers (eval'd recipes) - -Each frontend bundle or script has a single entry point[^1]. For frontend -bundles, it's a single JS file with every workspace/dependency module included. -For deno environments, the module graph is built dynamically. While JavaScript -can run in many environments, there's work to be done to run the same code -across all invocations. We should strive for a clear module graph for all -potential entries (e.g. each script, each bundle) for both portability, -maintanance, and performance. - -[^1]: Our vite frontend has multiple "pieces" for lazy loading JSDOM/TSC, but a - single "main". - -> **❌ Avoid** - -- Modules depending on each other -- Large quantity of module exports -- Adding module-specific dependencies to workspace deno.json -- Non-standard JS (env vars, vite-isms): All of our different invocation - mechanisms/environments need to handle these - -> **✅ Prefer** - -- Use - [manifest exports](https://docs.deno.com/runtime/fundamentals/workspaces/#multiple-package-entries) - to export a different entry point for a module. Don't pull in everything if - only e.g. types are needed. - - If needed, environment specific exports can be provided e.g. - `@workspace/module/browser` | `@workspace/module/deno`. -- Consider leaf nodes in the graph: A `utils` module should not be heavy with - dependencies, external or otherwise. -- Clean separation of public and private facing interfaces: only export what's - needed. -- Add module-specific dependencies to that module's dependencies, not the entire - workspace. We don't need `vite` in a deno server. - -### Avoid Ambiguous Types - -Softly-typed JS allows quite a bit. We often accept a range of inputs, and based -on type checking, perform actions. - -> **❌ Avoid** - -Minimize unknown type usage. Not only does `processData` allow any type, but -it's unclear what the intended types are: - -```ts -function processData(data: any) { - if (typeof data === "object") { - if (!data) { - processNull(data as null); - } else if (Array.isArray(data)) { - processArray(data as object[]); - } else { - processObject(data as object); - } - } else { - processPrimitive(data, typeof data); - } -} -``` - -> **✅ Prefer** - -Wrap an `any` type as another type for consumers. There are many TypeScript -solutions here, but in general, only at serialization boundaries (postMessage, -HTTP requests) _must_ we transform untyped values. Elsewhere, we should have -validated types. - -```ts -class Data { - private inner: any; - constructor(inner: any) { - this.inner = inner; - } - process() { - // if (typeof this.inner === "object") - } -} - -function processData(data: Data) { - data.process(); -} -``` - -### Avoid representing invalid state - -Similarly, permissive interfaces (including nullable properties and -non-represented exclusive states e.g. "i accept a string or array of strings") -may represent an invalid state at intermediate stages that will need be checked -at every interface: - -> **❌ Avoid** - -```ts -interface LLMRequest { - prompt?: string; - messages?: string[]; - model?: string; -} - -function request(req: LLMRequest) { - // Not only do we have to modify `req` into a valid - // state here, `processRequest` and any other user of `LLMRequest` - // must also handle this. - - if (!req.model) { - req.model = "default model"; - } - // If both prompt and messages provided, - // use only `messages` - if (req.prompt && req.messages) { - req.prompt = undefined; - } - processRequest(req); -} - -request({ prompt: "hello world" }); -``` - -> **✅ Prefer** - -For interfaces/types, not allowing unrepresented exclusive states (the prompt -input is always an array; `model` is always defined) requires more explicit -inputs, but then `LLMRequest` is always complete and valid. **Making invalid -states unrepresentable is good**. - -Constructing the request could be also be a class, if we always wanted to apply -appropriate e.g. defaults. - -```ts -enum Model { - Default = "default model"; -} - -interface LLMRequest { - messages: string[], - model: Model, -} - -function request(req: LLMRequest) { - // This is already a valid LLMRequest - processRequest(req); -} - -request({ messages: ["hello world"], model: inputModel ?? Model.Default }); -``` - -### Appropriate Error Handling - -If a function may throw, it's reasonable to wrap it in a try/catch. However, in -complex codebases, handling every error is both tedious and limiting, and may be -preferable to handle errors in a single place with context. Most importantly, -throwing errors is OK, and preventing execution of invalid states is desirable. - -Whether or not an error should be handled in a subprocess could be determined by -whether its a "fatal error" or not: was an assumption invalidated? are we -missing some required capability? Throw an error. Can we continue safely -processing and need to take no further action? Maybe a low-level try/catch is -appropriate. LLMs generally don't have this context and are liberal in their -try/catch usage. Avoid this. - -> **❌ Avoid** - -In this scenario, errors are logged different ways; if `fetch` throws, we have a -console error log. If `getData()` returns `undefined`, something unexpected -occurred, and there's nothing to be done. `run` should be considered errored and -failed. - -```ts -async function getData(): Promise { - try { - const res = await fetch(URL); - if (res.ok) { - return res.text(); - } - throw new Error("Unsuccessful HTTP response"); - } catch(e) { - console.error(e); - } -} - -async function run() { - try { - const data = await getData(); - if (data) { - // .. - } - } catch (e) { - console.error("There was an error": e); - } -} -``` - -> **✅ Prefer** - -In this case, we expect `getData()` to throw, or always return a `string`. Less -handling here, and let the caller determine what to do on failure. - -```ts -async function getData(): Promise { - const res = await fetch(URL); - if (res.ok) { - return res.text(); - } - throw new Error("Unsuccessful HTTP response"); -} - -async function run() { - const data = await getData(); - await processStr(data); -} - -async function main() { - try { - await run(); - } catch (e) { - console.error(e); - } -} -``` - -Sometimes a low-level try/catch is appropriate, of course: - -- `getData()` could have its own try/catch to e.g. retry on failure, throwing - after 3 failed attempts. -- Exposing a `isFeatureSupported(): boolean` function that based on if some - other function throws, determines if "feature" is supported. If we can handle - both scenarios and translate the error into a boolean (e.g. are all of the - ED25519 features we need supported natively for this platform? if not use a - polyfill), then this is not a fatal error, and we explicitly do not want to - throw and handle it elsewhere. - -## Ralph Container Information - -If you are running inside the Ralph Docker container (user is "ralph" or -`/app/start-servers.sh` exists): - -- See `tools/ralph/DEPLOY.md` for Playwright MCP testing and server restart - instructions -- toolshed should run on 8000 and shell on port 5173 +## Recipe/Pattern Development + +If you are developing recipes/patterns (they mean the same thing), read the +following documentation: + +- `docs/common/RECIPES.md` - Writing recipes with cells, handlers, lifts, best + practices, and [ID] usage patterns +- `docs/common/PATTERNS.md` - High-level patterns and examples for building + applications, including common mistakes and debugging tips +- `docs/common/HANDLERS.md` - Writing handler functions, event types, state + management, and handler factory patterns +- `docs/common/COMPONENTS.md` - Guide to UI components (ct-checkbox, ct-input, + ct-select, etc.) with bidirectional binding and event handling patterns +- `docs/common/RECIPE_DEV_DEPLOY.md` - Building, debugging, and deploying + recipes step-by-step +- `docs/common/DEVELOPMENT.md` - Coding style, design principles, and best + practices +- `docs/common/UI_TESTING.md` - How to work with shadow dom in our integration + tests + +Check `packages/patterns/` for working recipe examples. + +**Important:** Ignore the top level `recipes` folder - it is defunct. + +## Runtime Development + +If you are developing runtime code, read the following documentation: + +- `docs/common/RUNTIME.md` - Running servers, testing, and runtime package + overview +- `docs/common/DEVELOPMENT.md` - Coding style, design principles, and best + practices diff --git a/docs/common/DEVELOPMENT.md b/docs/common/DEVELOPMENT.md new file mode 100644 index 000000000..97445721b --- /dev/null +++ b/docs/common/DEVELOPMENT.md @@ -0,0 +1,324 @@ +# Development Guide + +This guide covers coding standards, design principles, and build/test workflows for CommonTools development. + +## Style & Conventions + +### Formatting + +- Line width is **80 characters**. +- Indent with **2 spaces**. +- **Semicolons are required.** +- Use **double quotes** for strings. +- Always run `deno fmt` before committing. + +### Imports + +- Group imports by source: standard library, external, then internal. +- Prefer named exports over default exports. +- Use package names for internal imports. +- Destructure when importing multiple names from the same module. +- Import either from `@commontools/api` (internal API) or + `@commontools/api/interface` (external API), but not both. + +## Code Design & Principles + +### Error Handling + +- Write descriptive error messages. +- Propagate errors using async/await. +- Document possible errors in JSDoc. + +### TypeScript + +- Export types explicitly using `export type { ... }`. +- Provide descriptive JSDoc comments on public interfaces. +- Prefer strong typing with interfaces or types instead of `any`. +- Update package-level README.md files. + +### Keep the Module Graph clean + +We execute our JavaScript modules in many different environments: + +- Browsers (Vite built) +- Browsers (deno-web-test>esbuild Built) +- Browsers (eval'd recipes) +- Deno (scripts and servers) +- Deno (eval'd recipes) +- Deno Workers +- Deno workers (eval'd recipes) + +> **❌ Avoid** + +- Modules depending on each other +- Large quantity of module exports +- Adding module-specific dependencies to workspace deno.json +- Non-standard JS (env vars, vite-isms): All of our different invocation + mechanisms/environments need to handle these + +> **✅ Prefer** + +- Use + [manifest exports](https://docs.deno.com/runtime/fundamentals/workspaces/#multiple-package-entries) + to export a different entry point for a module. Don't pull in everything if + only e.g. types are needed. + - If needed, environment specific exports can be provided e.g. + `@workspace/module/browser` | `@workspace/module/deno`. +- Consider leaf nodes in the graph: A `utils` module should not be heavy with + dependencies, external or otherwise. +- Clean separation of public and private facing interfaces: only export what's + needed. +- Add module-specific dependencies to that module's dependencies, not the entire + workspace. We don't need `vite` in a deno server. + +### Avoid Ambiguous Types + +Softly-typed JS allows quite a bit. We often accept a range of inputs, and based +on type checking, perform actions. + +> **❌ Avoid** + +Minimize unknown type usage. Not only does `processData` allow any type, but +it's unclear what the intended types are: + +```ts +function processData(data: any) { + if (typeof data === "object") { + if (!data) { + processNull(data as null); + } else if (Array.isArray(data)) { + processArray(data as object[]); + } else { + processObject(data as object); + } + } else { + processPrimitive(data, typeof data); + } +} +``` + +> **✅ Prefer** + +Wrap an `any` type as another type for consumers. There are many TypeScript +solutions here, but in general, only at serialization boundaries (postMessage, +HTTP requests) _must_ we transform untyped values. Elsewhere, we should have +validated types. + +```ts +class Data { + private inner: any; + constructor(inner: any) { + this.inner = inner; + } + process() { + // if (typeof this.inner === "object") + } +} + +function processData(data: Data) { + data.process(); +} +``` + +### Avoid representing invalid state + +Similarly, permissive interfaces (including nullable properties and +non-represented exclusive states e.g. "i accept a string or array of strings") +may represent an invalid state at intermediate stages that will need be checked +at every interface: + +> **❌ Avoid** + +```ts +interface LLMRequest { + prompt?: string; + messages?: string[]; + model?: string; +} + +function request(req: LLMRequest) { + // Not only do we have to modify `req` into a valid + // state here, `processRequest` and any other user of `LLMRequest` + // must also handle this. + + if (!req.model) { + req.model = "default model"; + } + // If both prompt and messages provided, + // use only `messages` + if (req.prompt && req.messages) { + req.prompt = undefined; + } + processRequest(req); +} + +request({ prompt: "hello world" }); +``` + +> **✅ Prefer** + +For interfaces/types, not allowing unrepresented exclusive states (the prompt +input is always an array; `model` is always defined) requires more explicit +inputs, but then `LLMRequest` is always complete and valid. **Making invalid +states unrepresentable is good**. + +Constructing the request could be also be a class, if we always wanted to apply +appropriate e.g. defaults. + +```ts +enum Model { + Default = "default model"; +} + +interface LLMRequest { + messages: string[], + model: Model, +} + +function request(req: LLMRequest) { + // This is already a valid LLMRequest + processRequest(req); +} + +request({ messages: ["hello world"], model: inputModel ?? Model.Default }); +``` + +### Appropriate Error Handling + +If a function may throw, it's reasonable to wrap it in a try/catch. However, in +complex codebases, handling every error is both tedious and limiting, and may be +preferable to handle errors in a single place with context. Most importantly, +throwing errors is OK, and preventing execution of invalid states is desirable. + +Whether or not an error should be handled in a subprocess could be determined by +whether its a "fatal error" or not: was an assumption invalidated? are we +missing some required capability? Throw an error. Can we continue safely +processing and need to take no further action? Maybe a low-level try/catch is +appropriate. LLMs generally don't have this context and are liberal in their +try/catch usage. Avoid this. + +> **❌ Avoid** + +In this scenario, errors are logged different ways; if `fetch` throws, we have a +console error log. If `getData()` returns `undefined`, something unexpected +occurred, and there's nothing to be done. `run` should be considered errored and +failed. + +```ts +async function getData(): Promise { + try { + const res = await fetch(URL); + if (res.ok) { + return res.text(); + } + throw new Error("Unsuccessful HTTP response"); + } catch(e) { + console.error(e); + } +} + +async function run() { + try { + const data = await getData(); + if (data) { + // .. + } + } catch (e) { + console.error("There was an error": e); + } +} +``` + +> **✅ Prefer** + +In this case, we expect `getData()` to throw, or always return a `string`. Less +handling here, and let the caller determine what to do on failure. + +```ts +async function getData(): Promise { + const res = await fetch(URL); + if (res.ok) { + return res.text(); + } + throw new Error("Unsuccessful HTTP response"); +} + +async function run() { + const data = await getData(); + await processStr(data); +} + +async function main() { + try { + await run(); + } catch (e) { + console.error(e); + } +} +``` + +Sometimes a low-level try/catch is appropriate, of course: + +- `getData()` could have its own try/catch to e.g. retry on failure, throwing + after 3 failed attempts. +- Exposing a `isFeatureSupported(): boolean` function that based on if some + other function throws, determines if "feature" is supported. If we can handle + both scenarios and translate the error into a boolean (e.g. are all of the + ED25519 features we need supported natively for this platform? if not use a + polyfill), then this is not a fatal error, and we explicitly do not want to + throw and handle it elsewhere. + +### Avoid Singletons + +The singleton pattern may be useful when there's a single global state. But +running multiple instances, unit tests, and reflecting state from another state +becomes impossible. Additionally, this pattern is infectious, often requiring +consuming code to also only support a single instance. + +> **❌ Avoid** + +```ts +const cache = new Map(); +export const set = (key: string, value: string) => cache.set(key, value); +export const get = (key: string): string | undefined => cache.get(key); +``` + +```ts +export const cache = new Map(); +export const instance = new Foo(); +``` + +> **✅ Prefer** + +In both cases, we can maintain multiple caches, or instances of cache consumers. + +```ts +export class Cache { + private map: Map = new Map(); + get(key: string): string | undefined { + return this.map.get(key); + } + set(key: string, value: string) { + this.map.set(key, value); + } +} +``` + +Or with a functional pattern: + +```ts +export type Cache = Map; +export const get = (cache: Cache, key: string): string | undefined => + cache.get(key); +export const set = (cache: Cache, key: string, value: string) => + cache.set(key, value); +``` + +## Build & Test + +- Check typings with `deno task check`. +- Run linter with `deno lint`. +- Run all tests using `deno task test` (NOT `deno test`) +- To run a single test file use `deno test path/to/test.ts`. +- To test a specific package, `cd` into the package directory and run + `deno task test`. diff --git a/docs/common/RECIPE_DEV_DEPLOY.md b/docs/common/RECIPE_DEV_DEPLOY.md new file mode 100644 index 000000000..976f45b89 --- /dev/null +++ b/docs/common/RECIPE_DEV_DEPLOY.md @@ -0,0 +1,262 @@ +# Recipe Development & Deployment Guide + +This guide covers building, debugging, and deploying recipes/patterns using the CommonTools framework. + +## Recipe Development + +### 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 + +``` + +### 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 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` | + +### 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 + +## Deployment + +### Prerequisite: `ct` tool availability + +```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 +``` + +### Prerequisites + +Before ever calling `ct` you MUST read `docs/common/CT.md`. + +This tool is used to: +- Deploy a recipe as a new charm +- Read/write charm data directly +- Invoke charm handlers for complex operations +- Link charms together +- ls/inspect/map: inspect and visualize charms + +### 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. + +#### Getting Recipe Source + +```bash +# Get source from deployed charm +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] ./recipe.tsx +``` + +### 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 + +# Get source from charm +./dist/ct charm getsrc -i claude.key -a https://toolshed.saga-castor.ts.net/ -s space -c charm-id output.tsx + +# For full ct command reference, use the ct skill +``` + +### Workflow Resources + +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 `.claude/skills/recipe-dev/references/workflow-guide.md` when you need practical command examples beyond theory. + +### Best Practices + +- **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/docs/common/RUNTIME.md b/docs/common/RUNTIME.md new file mode 100644 index 000000000..9155e5e86 --- /dev/null +++ b/docs/common/RUNTIME.md @@ -0,0 +1,228 @@ +# Runtime Development Guide + +This guide covers working on CommonTools runtime components including the backend (Toolshed), frontend (Shell), and core runtime packages. + +## Architecture Overview + +CommonTools consists of several runtime components: + +- **Toolshed** (`packages/toolshed/`) - Backend API server providing distributed runtime and storage +- **Shell** (`packages/shell/`) - Web frontend for interacting with spaces +- **Runner** (`packages/runner/`) - Recipe runtime execution engine +- **Background Charm Service** (`packages/background-charm-service/`) - Background service for running charms in Deno workers +- **Storage** - Storage layer implementation (`packages/memory/`, `packages/runner/src/storage/`) +- **Identity** (`packages/identity/`) - Identity management and cryptographic key handling + +## Running Development Servers + +### Backend (Toolshed) + +The backend runs on port 8000 by default. + +```bash +cd packages/toolshed +SHELL_URL=http://localhost:5173 deno task dev +``` +**Development Options: Toolshed pointing to cloud backend instead** +```bash +SHELL_URL=http://localhost:5173 API_URL=https://toolshed.saga-castor.ts.net/ deno task dev +``` + +**Environment Setup:** +- Copy `.env.example` to `.env` in the toolshed directory +- See `env.ts` for all available environment variables and defaults +- Default URL: http://localhost:8000 + +### Frontend (Shell) + +The frontend runs on port 5173 by default. + +```bash +cd packages/shell +deno task dev +``` + +### Background charm service +This is only needed if you are working on either the background charm service or need to support running background charms. +Default assumption is that its not needed. + +How to start: +```bash +cd packages/background-charm-service +OPERATOR_PASS="implicit trust" API_URL="http://localhost:8000" deno task start +``` + + +**Important:** For `*.ts.net` URLs, you must be connected to the CT network via Tailscale. Commands will hang or timeout if not connected. + +### Restarting Servers + +**Critical: After making edits to runtime code, you MUST restart the shell server.** + +## Testing + +### Running Tests + +**From workspace root** (recommended): + +```bash +# Run all tests (includes unit and integration) +deno task test + +# Run tests for specific package +cd packages/runner +deno task test +``` + +**Important:** Always use `deno task test` from the root, NOT `deno test`, as the task includes necessary flags. + +### Test Structure + +- **Unit tests**: Use `@std/testing/bdd` (`describe`/`it`) with `@std/expect` for assertions +- **Integration tests**: Executable scripts that test end-to-end workflows against a running API +- **Test files**: Named `*.test.ts` + +**Unit test example:** + +```typescript +import { describe, it } from "@std/testing/bdd"; +import { expect } from "@std/expect"; + +describe("Feature", () => { + it("should do something", () => { + expect(result).toBe(expected); + }); +}); +``` + +**Integration test example:** + +Integration tests are executable scripts that connect to a real backend and test full workflows. They are located in `packages/runner/integration/` and follow this pattern: + +```typescript +#!/usr/bin/env -S deno run -A + +import { Runtime } from "@commontools/runner"; +import { Identity } from "@commontools/identity"; +import { StorageManager } from "@commontools/runner/storage/cache.deno"; +import { env } from "@commontools/integration"; +const { API_URL } = env; + +console.log("=== TEST: My Integration Test ==="); + +async function test() { + const identity = await Identity.fromPassphrase("test operator"); + + const runtime = new Runtime({ + apiUrl: new URL(API_URL), + storageManager: StorageManager.open({ + as: identity, + address: new URL("/api/storage/memory", API_URL), + }), + }); + + // Test your workflow here + // ... + + await runtime.dispose(); + + // Return results or throw on failure +} + +await test(); +console.log("Done"); +Deno.exit(0); +``` + +**Key characteristics of integration tests:** +- Start with shebang: `#!/usr/bin/env -S deno run -A` +- Connect to real API using `env.API_URL` from `@commontools/integration` +- Test complete workflows (runtime, storage, charms) +- Use `console.log` for output and `Deno.exit(1)` for failures +- Run as part of CI against deployed backend + +**Adding integration tests:** + +When adding runtime features, consider adding integration tests to `packages/runner/integration/` that verify the feature works end-to-end. See existing tests like `basic-persistence.test.ts` or `array_push.test.ts` for examples. + +### Before Merging + +Ensure all tests pass: + +```bash +# Type checking +deno task check + +# All tests +deno task test + +# Formatting +deno fmt + +# Linting +deno lint +``` + +## Key Runtime Packages + +### Runner (`packages/runner/`) + +The recipe runtime execution engine and sandbox coordinator. + +- **Runtime** - Main runtime class for executing recipes +- **Storage** - Transaction model and data store layer +- **Builder** - Recipe builder API and construction utilities +- **Traverse** - Schema traversal and object management + +### Memory (`packages/memory/`) + +In-memory and persistent storage layer implementation. + +### Identity (`packages/identity/`) + +Identity management using Ed25519 cryptographic keys. + +```bash +# Create new identity +./dist/ct id new > identity.key +``` + +## Common Development Tasks + +**Common issues:** +- Servers not picking up changes → Restart servers +- Tests failing after changes → Check that all tests pass, fix before proceeding +- Type errors → Run `deno task check` from root +- Port already in use → Kill existing deno processes with `pkill -9 deno` + +### Working with Storage + +The storage system uses MVCC transactions: + +- See `packages/runner/src/storage/transaction-explainer.md` for transaction model details +- See `packages/runner/src/storage/transaction-implementation-guide.md` for implementation guide + +## Module Graph Considerations + +Runtime code runs in multiple environments: + +- Browsers (Vite built) +- Browsers (deno-web-test>esbuild Built) +- Browsers (eval'd recipes) +- Deno (scripts and servers) +- Deno (eval'd recipes) +- Deno Workers +- Deno workers (eval'd recipes) + +See [DEVELOPMENT.md](./DEVELOPMENT.md) for detailed module graph best practices. + +## CI/CD + +All changes must pass automated checks before merging: + +- Type checking (`deno task check`) +- All tests (`deno task test`) +- Code formatting (`deno fmt`) +- Linting (`deno lint`) + +The CI pipeline runs these checks automatically on PRs. diff --git a/packages/patterns/README.md b/docs/common/UI_TESTING.md similarity index 100% rename from packages/patterns/README.md rename to docs/common/UI_TESTING.md From ff4e40e16c4bfaa3f1b513504092fe694bdfbfb3 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 18:47:00 +0000 Subject: [PATCH 2/7] more direction on testing with playwright and how to deploy --- AGENTS.md | 18 ++++++------- docs/common/PATTERNS.md | 2 ++ docs/common/RECIPE_DEV_DEPLOY.md | 46 ++++++++++++++++++++++++++------ 3 files changed, 49 insertions(+), 17 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index bd255fd6b..51fe5d577 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,23 +2,23 @@ ## Recipe/Pattern Development -If you are developing recipes/patterns (they mean the same thing), read the -following documentation: +If you are developing recipes/patterns (they mean the same thing), you MUST read +ALL of the following documentation: +- `docs/common/RECIPE_DEV_DEPLOY.md` - Building, debugging, and deploying + recipes step-by-step - `docs/common/RECIPES.md` - Writing recipes with cells, handlers, lifts, best practices, and [ID] usage patterns -- `docs/common/PATTERNS.md` - High-level patterns and examples for building - applications, including common mistakes and debugging tips - `docs/common/HANDLERS.md` - Writing handler functions, event types, state management, and handler factory patterns -- `docs/common/COMPONENTS.md` - Guide to UI components (ct-checkbox, ct-input, +- `docs/common/COMPONENTS.md` - Using UI components (ct-checkbox, ct-input, ct-select, etc.) with bidirectional binding and event handling patterns -- `docs/common/RECIPE_DEV_DEPLOY.md` - Building, debugging, and deploying - recipes step-by-step +- `docs/common/PATTERNS.md` - High-level patterns and examples for building + applications, including common mistakes and debugging tips - `docs/common/DEVELOPMENT.md` - Coding style, design principles, and best practices -- `docs/common/UI_TESTING.md` - How to work with shadow dom in our integration - tests +- `docs/common/UI_TESTING.md` - Optional: How to work with shadow dom in our + integration tests Check `packages/patterns/` for working recipe examples. diff --git a/docs/common/PATTERNS.md b/docs/common/PATTERNS.md index 551779374..06d1ad104 100644 --- a/docs/common/PATTERNS.md +++ b/docs/common/PATTERNS.md @@ -740,6 +740,7 @@ const removeItem = handler(/* ... */); ``` **4. Deploy and Test (3-5 minutes)** +Read ./common/doc/RECIPE_DEV_DEPLOY.md for more information on how to deploy ```bash # Deploy ./dist/ct charm new --identity key.json --api-url ... --space test pattern.tsx @@ -760,6 +761,7 @@ echo '{"title": "Test", "done": false}' | \ ### Testing Commands Reference +Read ./common/doc/RECIPE_DEV_DEPLOY.md for more information on how to deploy ```bash # Check syntax only (fast) ./dist/ct dev pattern.tsx --no-run diff --git a/docs/common/RECIPE_DEV_DEPLOY.md b/docs/common/RECIPE_DEV_DEPLOY.md index 976f45b89..76ef7bd2e 100644 --- a/docs/common/RECIPE_DEV_DEPLOY.md +++ b/docs/common/RECIPE_DEV_DEPLOY.md @@ -153,6 +153,36 @@ See `PATTERNS.md` Level 3-4 for linking and composition patterns. - Forget `OpaqueRef` annotations in `.map()` - Duplicate content from `docs/common/` - reference it instead +## Testing +After developing the charm, if you have Playwright MCP, you must test the charm with it unless the user asks you not to. + +### Navigate to the Charm URL + +```javascript +await page.goto("http://localhost:8000//"); +``` +Note: Server may be https://toolshed.saga-castor.ts.net instead. + +### Register/Login (First Time Only) + +When you first visit, you'll see a login page. Register with a passphrase: + +1. Click the "➕ Register" button +2. Click the "🔑 Generate Passphrase" button +3. Click the "🔒 I've Saved It - Continue" button + +This will log you in and load the charm. + +### Test the Charm + +Once logged in, you can interact with the charm using Playwright commands. + +Then use Playwright to: + +1. Navigate to the URL +2. Complete registration (first time) +3. Test the charm functionality + ## Deployment ### Prerequisite: `ct` tool availability @@ -197,18 +227,18 @@ This tool is used to: ./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 +./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] +./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 +./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. @@ -217,7 +247,7 @@ This tool is used to: ```bash # Get source from deployed charm -./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net/ --space [space] --charm [id] ./recipe.tsx +./dist/ct charm getsrc --identity claude.key --api-url https://toolshed.saga-castor.ts.net --space [space] --charm [id] ./recipe.tsx ``` ### Quick Command Reference @@ -227,16 +257,16 @@ This tool is used to: ./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 +./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 +./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 +./dist/ct charm inspect -i claude.key -a https://toolshed.saga-castor.ts.net -s space -c charm-id # Get source from charm -./dist/ct charm getsrc -i claude.key -a https://toolshed.saga-castor.ts.net/ -s space -c charm-id output.tsx +./dist/ct charm getsrc -i claude.key -a https://toolshed.saga-castor.ts.net -s space -c charm-id output.tsx # For full ct command reference, use the ct skill ``` From 8b783449970802c82eb3ea57ebf153d05522f4a6 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 19:32:33 +0000 Subject: [PATCH 3/7] make revewing against all relevant .md files mandatory --- AGENTS.md | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 51fe5d577..1b1bf153a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,7 +3,9 @@ ## Recipe/Pattern Development If you are developing recipes/patterns (they mean the same thing), you MUST read -ALL of the following documentation: +ALL of the following documentation AND follow the workflow below: + +### Required Documentation (read all 6 files) - `docs/common/RECIPE_DEV_DEPLOY.md` - Building, debugging, and deploying recipes step-by-step @@ -20,7 +22,18 @@ ALL of the following documentation: - `docs/common/UI_TESTING.md` - Optional: How to work with shadow dom in our integration tests -Check `packages/patterns/` for working recipe examples. +### Development Workflow (add these steps to your todo list) + +1. Read all 6 required documentation files listed above +2. Review example patterns in `packages/patterns/` for reference +3. Build your recipe incrementally, starting simple +4. Consult the 6 .md files if you are stuck +5. Deploy and test your recipe +6. **FINAL STEP: Review your code against all 6 .md files above to verify + correctness and check for improvements** + +**Important:** Step 5 is MANDATORY before declaring work complete. Do not skip +this verification step. **Important:** Ignore the top level `recipes` folder - it is defunct. From 631d3c9f7b9036e5d48e0cdff7893fc326e54596 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 20:17:20 +0000 Subject: [PATCH 4/7] path typo --- docs/common/PATTERNS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/common/PATTERNS.md b/docs/common/PATTERNS.md index 06d1ad104..338feed7c 100644 --- a/docs/common/PATTERNS.md +++ b/docs/common/PATTERNS.md @@ -740,7 +740,7 @@ const removeItem = handler(/* ... */); ``` **4. Deploy and Test (3-5 minutes)** -Read ./common/doc/RECIPE_DEV_DEPLOY.md for more information on how to deploy +Read `./docs/common/RECIPE_DEV_DEPLOY.md` for more information on how to deploy ```bash # Deploy ./dist/ct charm new --identity key.json --api-url ... --space test pattern.tsx @@ -761,7 +761,7 @@ echo '{"title": "Test", "done": false}' | \ ### Testing Commands Reference -Read ./common/doc/RECIPE_DEV_DEPLOY.md for more information on how to deploy +Read `./docs/common/RECIPE_DEV_DEPLOY.md` for more information on how to deploy ```bash # Check syntax only (fast) ./dist/ct dev pattern.tsx --no-run From 1c9e6b52ba08611b6cc12000de4ae0599f14bfd4 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 20:26:19 +0000 Subject: [PATCH 5/7] switching to promote claude skills but still refer to documents, also is a fallback for other llms --- AGENTS.md | 23 +++++------------------ 1 file changed, 5 insertions(+), 18 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 1b1bf153a..be36ab7d1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -2,10 +2,10 @@ ## Recipe/Pattern Development -If you are developing recipes/patterns (they mean the same thing), you MUST read -ALL of the following documentation AND follow the workflow below: +If you are developing recipes/patterns (they mean the same thing), use Claude +Skills (recipe-dev) to do the work. -### Required Documentation (read all 6 files) +### Useful Recipe/Pattern documentation - `docs/common/RECIPE_DEV_DEPLOY.md` - Building, debugging, and deploying recipes step-by-step @@ -19,21 +19,8 @@ ALL of the following documentation AND follow the workflow below: applications, including common mistakes and debugging tips - `docs/common/DEVELOPMENT.md` - Coding style, design principles, and best practices -- `docs/common/UI_TESTING.md` - Optional: How to work with shadow dom in our - integration tests - -### Development Workflow (add these steps to your todo list) - -1. Read all 6 required documentation files listed above -2. Review example patterns in `packages/patterns/` for reference -3. Build your recipe incrementally, starting simple -4. Consult the 6 .md files if you are stuck -5. Deploy and test your recipe -6. **FINAL STEP: Review your code against all 6 .md files above to verify - correctness and check for improvements** - -**Important:** Step 5 is MANDATORY before declaring work complete. Do not skip -this verification step. +- `docs/common/UI_TESTING.md` - How to work with shadow dom in our integration + tests **Important:** Ignore the top level `recipes` folder - it is defunct. From 9f5eb4b1e80ccc55b2585733c0e6861ede3097b4 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 20:38:04 +0000 Subject: [PATCH 6/7] added INDEX.md for patterns --- AGENTS.md | 2 + packages/patterns/INDEX.md | 153 +++++++++++++++++++++++++++++++++++++ 2 files changed, 155 insertions(+) create mode 100644 packages/patterns/INDEX.md diff --git a/AGENTS.md b/AGENTS.md index be36ab7d1..f14bab976 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -19,6 +19,8 @@ Skills (recipe-dev) to do the work. applications, including common mistakes and debugging tips - `docs/common/DEVELOPMENT.md` - Coding style, design principles, and best practices +- `packages/patterns/INDEX.md` - Catalog of all pattern examples with summaries, + data types, and keywords - `docs/common/UI_TESTING.md` - How to work with shadow dom in our integration tests diff --git a/packages/patterns/INDEX.md b/packages/patterns/INDEX.md new file mode 100644 index 000000000..eba60ca96 --- /dev/null +++ b/packages/patterns/INDEX.md @@ -0,0 +1,153 @@ +# Pattern Index + +This index catalogs all patterns in this directory, organized alphabetically. +Each entry includes a brief summary, the data types used, and relevant +keywords/features. + +## Patterns + +- array-in-cell-ast-nocomponents.tsx: Simple list with add functionality using + array in cell + - **Data types**: array of objects (text strings) + - **Keywords**: handler, map, array operations, common-send-message + +- array-in-cell-with-remove-ast-nocomponents.tsx: List with add and remove + operations on array + - **Data types**: array of objects (text strings) + - **Keywords**: handler, map, array operations, get/set, ct-button + +- array-in-cell-with-remove-editable.tsx: Editable list with add, remove, and + update operations + - **Data types**: array of objects (text strings) + - **Keywords**: handler, map, array operations, ct-input, ct-button, + onct-change + +- aside.tsx: Full-screen layout demonstration with header, footer, and sidebars + - **Data types**: none (layout only) + - **Keywords**: ct-screen, ct-autolayout, slots (left/right/header/footer), + tabNames + +- backlinks-index.tsx: Backlinks computation system for bi-directional + references between charms + - **Data types**: array of objects (MentionableCharm with mentioned/backlinks + arrays) + - **Keywords**: lift, array operations, backlinks, mentionable pattern, + OpaqueRef + +- charm-ref-in-cell.tsx: Storing and navigating to charm references within cells + - **Data types**: object containing charm reference + - **Keywords**: lift, cell, navigateTo, ifElse, derive, isInitialized flag + +- charms-ref-in-cell.tsx: Managing an array of charm references with navigation + - **Data types**: array of charm objects + - **Keywords**: lift, cell, navigateTo, array operations, isInitialized flag, + map + +- chatbot-list-view.tsx: Chat application with sidebar list of chat sessions + - **Data types**: array of objects (CharmEntry with ID and charm), selected + charm object + - **Keywords**: lift, handler, navigateTo, ct-list-item, ct-select, ct-render, + ct-autolayout, wish, [ID] + +- chatbot-note-composed.tsx: Composition of Chat and Note recipes with tool + integration + - **Data types**: array of LLM messages, array of list items, array of charms + - **Keywords**: wish, handler, navigateTo, recipe composition, derive, tools + integration + +- chatbot-outliner.tsx: Outliner with integrated chat for adding nodes + - **Data types**: outliner object (nested structure with + body/children/attachments), array of LLM messages + - **Keywords**: handler, ct-outliner, ct-checkbox, ct-input, wish, derive, + ifElse, ct-autolayout + +- chatbot.tsx: Full-featured chatbot with LLM integration and attachments + - **Data types**: array of LLM messages, array of attachments (objects), array + of charms + - **Keywords**: llmDialog, handler, derive, wish, ct-chat, ct-prompt-input, + ct-select, Stream, generateObject + +- cheeseboard.tsx: Fetch and display pizza schedule from web + - **Data types**: array of tuples (date/pizza strings), web response object + - **Keywords**: fetchData, lift, string parsing, map + +- common-tools.tsx: Reusable tool recipes and handlers for LLM integration + - **Data types**: array of list items (objects), API response objects + - **Keywords**: handler, recipe as tool, fetchData, derive, ifElse + +- counter.tsx: Basic counter with increment/decrement operations + - **Data types**: number + - **Keywords**: handler, str template, derive (via pure function), ct-button, + Stream + +- ct-checkbox-cell.tsx: Checkbox component with bidirectional binding + - **Data types**: boolean + - **Keywords**: ct-checkbox, $checked (bidirectional binding), handler + (optional), ifElse, onct-change + +- ct-checkbox-handler.tsx: Checkbox with explicit handler for state changes + - **Data types**: boolean + - **Keywords**: ct-checkbox, handler, checked property, onct-change, ifElse + +- ct-list.tsx: List component with editable items + - **Data types**: array of objects (items with title) + - **Keywords**: ct-list, $value (bidirectional binding), editable, ct-input + +- ct-render.tsx: Rendering sub-recipes with ct-render component + - **Data types**: number (counter value) + - **Keywords**: ct-render, $cell, nested recipes, recipe composition, handler + +- ct-select.tsx: Dropdown select component with various value types + - **Data types**: string, number + - **Keywords**: ct-select, $value (bidirectional binding), items (label/value + objects) + +- ct-tags.tsx: Tags input component + - **Data types**: array of strings + - **Keywords**: ct-tags, handler, onct-change, array of strings + +- default-app.tsx: Default application with charm management and navigation + - **Data types**: array of charms (MentionableCharm objects) + - **Keywords**: wish, derive, navigateTo, handler, ct-table, ct-button, + multiple recipe instantiation + +- dice.tsx: Dice roller with random number generation + - **Data types**: number + - **Keywords**: handler, random values, ct-button, Stream + +- fetch-data.tsx: GitHub repository data fetcher + - **Data types**: complex API response object, string (URL) + - **Keywords**: fetchData, lift, derive, ct-input, $value, string parsing + +- instantiate-recipe.tsx: Factory pattern for creating counter instances + - **Data types**: number, charm references + - **Keywords**: navigateTo, handler, recipe instantiation, factory pattern + +- linkedlist-in-cell.tsx: Linked list data structure implementation + - **Data types**: linked list object (recursive structure with value/next) + - **Keywords**: cell, derive, handler, custom data structure, recursive + structure + +- list-operations.tsx: Advanced array operations with ID-based tracking + - **Data types**: array of objects with [ID] property + - **Keywords**: [ID], derive, lift, filter, map, concat, reduce, handler, + array operations, get/set + +- llm.tsx: Simple LLM question/answer interface + - **Data types**: string (question), LLM response content, array of messages + - **Keywords**: llm, cell, derive, handler, ct-message-input, onct-send + +- nested-counter.tsx: Counter with nested sub-counter instances + - **Data types**: number + - **Keywords**: nested recipes, recipe composition, passing cells, str + template, handler + +- note.tsx: Note-taking app with backlinks and mentions + - **Data types**: string (title/content), array of charms + (mentioned/backlinks) + - **Keywords**: wish, handler, navigateTo, ct-code-editor, $mentionable, + $mentioned, backlinks, cell + +- output_schema.tsx: Demonstrates explicit output schema typing + - **Data types**: number, VNode + - **Keywords**: handler, output schema, type safety, ct-button From c84f2ef572b78f9b3f5741c7f7077ea6025b05a6 Mon Sep 17 00:00:00 2001 From: Ellyse Date: Wed, 22 Oct 2025 20:48:01 +0000 Subject: [PATCH 7/7] minor code snippet fixes --- docs/common/DEVELOPMENT.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/common/DEVELOPMENT.md b/docs/common/DEVELOPMENT.md index 97445721b..7b2c8b55c 100644 --- a/docs/common/DEVELOPMENT.md +++ b/docs/common/DEVELOPMENT.md @@ -167,7 +167,7 @@ appropriate e.g. defaults. ```ts enum Model { - Default = "default model"; + Default = "default model", } interface LLMRequest { @@ -224,7 +224,7 @@ async function run() { // .. } } catch (e) { - console.error("There was an error": e); + console.error("There was an error", e); } } ``` @@ -307,7 +307,7 @@ export class Cache { Or with a functional pattern: ```ts -export type Cache = Map; +export type Cache = Map; export const get = (cache: Cache, key: string): string | undefined => cache.get(key); export const set = (cache: Cache, key: string, value: string) =>