diff --git a/.agents/skills/repo-review/SKILL.md b/.agents/skills/repo-review/SKILL.md index dd4e4d7..bd26ef1 100644 --- a/.agents/skills/repo-review/SKILL.md +++ b/.agents/skills/repo-review/SKILL.md @@ -14,38 +14,47 @@ compatibility: Requires GitHub CLI (gh) authenticated and installed. This skill performs a "deep read" of a repository. It avoids superficial listings by first understanding the project's architecture and then evaluating open items against that context. ## Prerequisites -* **Tooling:** The agent must have access to the `gh` CLI. -* **Auth:** The user must be authenticated (`gh auth status`). + +- **Tooling:** The agent must have access to the `gh` CLI. +- **Auth:** The user must be authenticated (`gh auth status`). ## Instructions ### Step 1: Initialize the Mental Model + Before looking at tasks, understand the environment. Run these commands in sequence: + 1. **Metadata:** `gh repo view --json name,description,stargazerCount,forkCount` 2. **Structure:** `gh api repos//contents/` 3. **Identity:** Read the project manifest (e.g., `package.json`, `go.mod`, `pyproject.toml`) and `README.md`. 4. **Entry Point:** Read the primary entry file (e.g., `index.ts`, `main.go`) to identify the Public API surface. ### Step 2: Retrieve State & Delta + Fetch current items and recent changes: + 1. **Issues:** `gh issue list --repo --state open --json number,title,body,author,createdAt,updatedAt,labels,comments` 2. **PRs:** `gh pr list --repo --state open --json number,title,body,author,createdAt,updatedAt,labels,additions,deletions,changedFiles,headRefName` 3. **24h Delta:** `gh issue list --state closed --since 24h` and `gh pr list --state merged --since 24h`. ### Step 3: Deep Analysis Logic + For every issue and PR, do not just summarize the text. Perform a logic check: -* **Issues:** Use `gh api repos//contents/` to inspect the code mentioned in the report. -* **PRs:** Use `gh pr diff ` to review actual implementation. Evaluate if the code follows the patterns found in Step 1. -* **Flags:** - * ๐Ÿšจ **SECURITY:** Scan for credentials, auth bypass, or injection keywords. - * ๐Ÿค **EXTERNAL:** Prioritize PRs from non-maintainers. - * โœ… **READY:** Verify mergeability with `gh pr view --json mergeable,statusCheckRollup`. + +- **Issues:** Use `gh api repos//contents/` to inspect the code mentioned in the report. +- **PRs:** Use `gh pr diff ` to review actual implementation. Evaluate if the code follows the patterns found in Step 1. +- **Flags:** + - ๐Ÿšจ **SECURITY:** Scan for credentials, auth bypass, or injection keywords. + - ๐Ÿค **EXTERNAL:** Prioritize PRs from non-maintainers. + - โœ… **READY:** Verify mergeability with `gh pr view --json mergeable,statusCheckRollup`. ### Step 4: Pattern Synthesis + Group items into high-level insights: -* **Bug Clusters:** Identify if 3+ issues share a root cause in a specific module. -* **Hot Modules:** Flag files that appear in multiple open items. -* **Client Patterns:** Group issues by consumer (e.g., "VS Code users are reporting X"). + +- **Bug Clusters:** Identify if 3+ issues share a root cause in a specific module. +- **Hot Modules:** Flag files that appear in multiple open items. +- **Client Patterns:** Group issues by consumer (e.g., "VS Code users are reporting X"). ## Output Template @@ -54,22 +63,28 @@ Group items into high-level insights: **Snapshot:** {N} Issues ({+X} since yesterday) | {M} PRs ({+Y} since yesterday) ### โšก Suggested Actions Today + 1. **[Priority] #{Num}:** {One-line reason} 2. **[Priority] #{Num}:** {One-line reason} --- ### ๐Ÿ“‚ Open Issues Deep Dive + #### [Flag] #{Num} โ€” {Title} -* **Context:** Plain-English explanation of the "why" and "where" in the codebase. -* **Key Evidence:** > {Blockquote of critical user text} -* **Resolution Path:** {Quick fix/Design decision/Blocked} + +- **Context:** Plain-English explanation of the "why" and "where" in the codebase. +- **Key Evidence:** > {Blockquote of critical user text} +- **Resolution Path:** {Quick fix/Design decision/Blocked} ### ๐Ÿ”€ Pull Request Analysis + #### [Flag] #{Num} โ€” {Title} -* **Impact:** Summary of files changed and architectural fit. -* **Quality Note:** Honest assessment of implementation and CI status. + +- **Impact:** Summary of files changed and architectural fit. +- **Quality Note:** Honest assessment of implementation and CI status. ### ๐Ÿ” Patterns & Health -* **Hot Modules:** `{list/of/files}` -* **Release Cadence:** Last release was `{days}` ago. + +- **Hot Modules:** `{list/of/files}` +- **Release Cadence:** Last release was `{days}` ago. diff --git a/.agents/skills/stitch-sdk-bug-bash/SKILL.md b/.agents/skills/stitch-sdk-bug-bash/SKILL.md index 2d09a46..45f4aba 100644 --- a/.agents/skills/stitch-sdk-bug-bash/SKILL.md +++ b/.agents/skills/stitch-sdk-bug-bash/SKILL.md @@ -12,48 +12,53 @@ This skill provides a framework and instructions for finding bugs in the Stitch ## The Mindset: Adversarial Exploration When using this skill, do not just verify that the SDK works. Try to break it! -- Pass invalid or boundary parameters. -- Attempt operations on deleted or stale handles. -- Simulate unexpected API responses if possible or find edge cases where projection might fail. + +- Pass invalid or boundary parameters. +- Attempt operations on deleted or stale handles. +- Simulate unexpected API responses if possible or find edge cases where projection might fail. --- ## Surface Areas to Cover ### 1. Root & Initialization (`Stitch`) -- **Zero Config**: Verify the singleton works without explicit config if `STITCH_API_KEY` is present. -- **Invalid Config**: Pass an empty API key or invalid base URL to `StitchToolClient` and verify that the first call fails with a clear authentication or connection error, not a generic noise error. + +- **Zero Config**: Verify the singleton works without explicit config if `STITCH_API_KEY` is present. +- **Invalid Config**: Pass an empty API key or invalid base URL to `StitchToolClient` and verify that the first call fails with a clear authentication or connection error, not a generic noise error. ### 2. Project Lifecycle (`Project`) -- **Handle Creation**: Verify that `stitch.project('invalid-id')` does not throw (lazy instantiation) but the first call on it fails safely. -- **Factory vs API**: Verify that creating a project handle via the factory doesn't trigger API calls, but methods like `project.listScreens()` do. + +- **Handle Creation**: Verify that `stitch.project('invalid-id')` does not throw (lazy instantiation) but the first call on it fails safely. +- **Factory vs API**: Verify that creating a project handle via the factory doesn't trigger API calls, but methods like `project.listScreens()` do. ### 3. Screen Lifecycle (`Screen`) -- **The Handover**: Verify that properties from `Project.generate()` are correctly populated on the returned `Screen` instances without a second fetch. -- **Null Safety in Projections**: Test tools or scenarios that return empty arrays or missing optional fields. Verify that the SDK handle handles them as `undefined` or empty arrays rather than crashing on null property access! + +- **The Handover**: Verify that properties from `Project.generate()` are correctly populated on the returned `Screen` instances without a second fetch. +- **Null Safety in Projections**: Test tools or scenarios that return empty arrays or missing optional fields. Verify that the SDK handle handles them as `undefined` or empty arrays rather than crashing on null property access! ### 4. Design System (`DesignSystem`) -- **Application**: Create a design system, and apply it to a list of screens. Verify that if the list is empty or invalid, the SDK fails cleanly! -- **Handles**: Verify that `project.designSystem('ds-id')` correctly receives the `projectId` and injects it into calls like `ds.apply(...)`. + +- **Application**: Create a design system, and apply it to a list of screens. Verify that if the list is empty or invalid, the SDK fails cleanly! +- **Handles**: Verify that `project.designSystem('ds-id')` correctly receives the `projectId` and injects it into calls like `ds.apply(...)`. --- ## Tricky Situations Matrix (Standard Functional Edges) -| Scenario | What to try | Expected Behavior | -| --- | --- | --- | -| **Stale Handles** | Create a screen, delete the project, then try to edit the screen handle. | Clean API error indicating resource not found, wrapped in `StitchError`. | -| **Empty Prompts** | Call `project.generate('')` or with only whitespace. | Safe rejection or clear API error, no crash in codegen. | +| Scenario | What to try | Expected Behavior | +| ----------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | +| **Stale Handles** | Create a screen, delete the project, then try to edit the screen handle. | Clean API error indicating resource not found, wrapped in `StitchError`. | +| **Empty Prompts** | Call `project.generate('')` or with only whitespace. | Safe rejection or clear API error, no crash in codegen. | | **Projections on null** | Force an API call that returns a response without the expected projection field (if you can simulate or find such a tool fallback case). | The SDK should use optional chaining (e.g., `raw?.prop`) and return `undefined` rather than throwing `TypeError: cannot read property of undefined`. | -| **Massive arrays** | Pass hundreds of screen IDs to `ds.apply()`. | Check if it hits payload limits gracefully or fails with a clear message. | +| **Massive arrays** | Pass hundreds of screen IDs to `ds.apply()`. | Check if it hits payload limits gracefully or fails with a clear message. | --- ## Diagnostic Hygiene -- Always wrap your test calls in `try/catch`. -- Log the error and inspect `error.code` or `error.name` to see if it's a `StitchError` or a generic raw error. -- If an execution throws a raw `TypeError` or "cannot read property of undefined", that is a **HIGH PRIORITY BUG** in the SDK's projection logic! +- Always wrap your test calls in `try/catch`. +- Log the error and inspect `error.code` or `error.name` to see if it's a `StitchError` or a generic raw error. +- If an execution throws a raw `TypeError` or "cannot read property of undefined", that is a **HIGH PRIORITY BUG** in the SDK's projection logic! --- @@ -74,7 +79,7 @@ async function bash() { try { // 1. Create a fresh project project = await stitch.createProject({ - displayName: `Bug Bash ${new Date().toISOString()}` + displayName: `Bug Bash ${new Date().toISOString()}`, }); console.log(`โœ“ Created Project: ${project.id}`); @@ -89,7 +94,7 @@ async function bash() { // 3. Create a design system const ds = await project.createDesignSystem({ name: "Bash Style", - variables: { primaryColor: "#ff0000" } + variables: { primaryColor: "#ff0000" }, }); console.log(`โœ“ Created Design System: ${ds.id}`); @@ -104,7 +109,6 @@ async function bash() { } catch (e) { console.log("โœ— Did applying to empty list fail? Inspect error."); } - } catch (error) { console.error("๐Ÿ’ฅ Bash failed with error:", error); } finally { @@ -112,7 +116,7 @@ async function bash() { if (project) { console.log(`๐Ÿงน Cleaning up project ${project.id}...`); // Assuming we have a deleteProject binding or we just leave it if not available - // await project.delete(); + // await project.delete(); } } } diff --git a/.agents/skills/stitch-sdk-development/SKILL.md b/.agents/skills/stitch-sdk-development/SKILL.md index 10cfa42..23f40d4 100644 --- a/.agents/skills/stitch-sdk-development/SKILL.md +++ b/.agents/skills/stitch-sdk-development/SKILL.md @@ -51,13 +51,17 @@ When the Stitch MCP server adds a new tool: `domain-map.json` expresses two things: **Classes**: What domain objects exist and how they're constructed. + ```json { "Screen": { "constructorParams": ["projectId", "screenId"], "fieldMapping": { "projectId": { "from": "projectId" }, - "screenId": { "from": "id", "fallback": { "field": "name", "splitOn": "/screens/" } } + "screenId": { + "from": "id", + "fallback": { "field": "name", "splitOn": "/screens/" } + } }, "parentField": "projectId", "idField": "screenId" @@ -66,6 +70,7 @@ When the Stitch MCP server adds a new tool: ``` **Bindings**: How MCP tools map to class methods. + ```json { "tool": "generate_screen_from_text", @@ -74,7 +79,10 @@ When the Stitch MCP server adds a new tool: "args": { "projectId": { "from": "self" }, "prompt": { "from": "param" }, - "name": { "from": "computed", "template": "projects/{projectId}/screens/{screenId}" } + "name": { + "from": "computed", + "template": "projects/{projectId}/screens/{screenId}" + } }, "returns": { "class": "Screen", @@ -92,9 +100,13 @@ When the Stitch MCP server adds a new tool: **Response projections**: Structured `ProjectionStep[]` arrays validated against `outputSchema`. Use `index` for single items, `each` for arrays. Empty `[]` = direct return. **Cache**: Methods can specify a `cache` with a structured `projection` to check `this.data` before calling the API: + ```json { - "cache": { "projection": [{ "prop": "htmlCode" }, { "prop": "downloadUrl" }], "description": "Use cached download URL from generation response" } + "cache": { + "projection": [{ "prop": "htmlCode" }, { "prop": "downloadUrl" }], + "description": "Use cached download URL from generation response" + } } ``` @@ -113,7 +125,7 @@ For AI agents and orchestration scripts. Raw tool pipe. The agent receives tool The `stitch` singleton exposes both domain methods and tool methods via a `Proxy`. No instantiation needed โ€” it lazily creates a `StitchToolClient` from env vars on first access. ```typescript -import { stitch } from '@google/stitch-sdk'; +import { stitch } from "@google/stitch-sdk"; // Discover available tools const { tools } = await stitch.listTools(); @@ -135,9 +147,9 @@ The singleton reads `STITCH_API_KEY` (or `STITCH_ACCESS_TOKEN` + `GOOGLE_CLOUD_P For explicit control (multiple clients, custom config, testing), instantiate `StitchToolClient` directly: ```typescript -import { StitchToolClient } from '@google/stitch-sdk'; +import { StitchToolClient } from "@google/stitch-sdk"; -const client = new StitchToolClient({ apiKey: 'my-key' }); +const client = new StitchToolClient({ apiKey: "my-key" }); const tools = await client.listTools(); const result = await client.callTool("create_project", { title: "My App" }); await client.close(); @@ -152,13 +164,13 @@ await client.close(); For agents built on the [Vercel AI SDK](https://sdk.vercel.ai/). Transforms MCP tool schemas into AI SDK-compatible tool definitions, enabling plug-and-play with `generateText()`. ```typescript -import { stitchTools } from '@google/stitch-sdk/ai'; -import { generateText } from 'ai'; -import { google } from '@ai-sdk/google'; +import { stitchTools } from "@google/stitch-sdk/ai"; +import { generateText } from "ai"; +import { google } from "@ai-sdk/google"; const result = await generateText({ model: google("gemini-2.0-flash"), - tools: stitchTools(), // all tools + tools: stitchTools(), // all tools // or: stitchTools({ include: ["create_project"] }) // filtered prompt: "Create a project called My App", }); @@ -170,9 +182,9 @@ const result = await generateText({ ```typescript const tool = stitch.toolMap.get("create_project"); -tool.params; // ToolParam[] โ€” flat, pre-parsed -tool.params.filter(p => p.required); // required params only -tool.inputSchema; // raw ToolInputSchema still available +tool.params; // ToolParam[] โ€” flat, pre-parsed +tool.params.filter((p) => p.required); // required params only +tool.inputSchema; // raw ToolInputSchema still available ``` The raw `toolDefinitions` array and standalone `toolMap` are also exported from the main entry point. @@ -228,8 +240,16 @@ The membrane is declared in `domain-map.json` via `sideEffects` on any class wit "Project": { "extensionPath": "../../src/project-ext.js", "sideEffects": [ - { "method": "uploadImage", "reason": "private_rest", "specPath": "src/spec/upload.ts" }, - { "method": "downloadAssets", "reason": "filesystem_io", "specPath": "src/spec/download.ts" } + { + "method": "uploadImage", + "reason": "private_rest", + "specPath": "src/spec/upload.ts" + }, + { + "method": "downloadAssets", + "reason": "filesystem_io", + "specPath": "src/spec/download.ts" + } ] } } @@ -238,6 +258,7 @@ The membrane is declared in `domain-map.json` via `sideEffects` on any class wit Valid `reason` values: `filesystem_io`, `binary_data`, `private_rest`, `complex_orchestration`. The generator validates at Stage 3: + 1. No `sideEffect.method` collides with a generated binding method name 2. Each `specPath` points to an existing file @@ -257,13 +278,13 @@ Follow the Spec โ†’ Handler โ†’ Extension pattern: #### Rules -| Rule | Rationale | -|---|---| -| Extension methods must NOT override generated methods | Prevents silent shadowing | -| Extension methods must delegate to a Handler | Prevents inline business logic | -| Handlers must implement a Spec interface | Typed service contract | -| Handlers must return Result, never throw | Consistent error surface | -| Extensions must NOT import from `singleton.ts` | Prevents circular dependencies | +| Rule | Rationale | +| ----------------------------------------------------- | ------------------------------ | +| Extension methods must NOT override generated methods | Prevents silent shadowing | +| Extension methods must delegate to a Handler | Prevents inline business logic | +| Handlers must implement a Spec interface | Typed service contract | +| Handlers must return Result, never throw | Consistent error surface | +| Extensions must NOT import from `singleton.ts` | Prevents circular dependencies | --- @@ -323,7 +344,8 @@ Do not rely on cached descriptions of files or directory trees. Read the source. ## Import Convention Use `.js` extensions for ESM compatibility: + ```typescript -import { StitchError } from '../../src/spec/errors.js'; // โœ“ -import { StitchError } from '../../src/spec/errors'; // โœ— +import { StitchError } from "../../src/spec/errors.js"; // โœ“ +import { StitchError } from "../../src/spec/errors"; // โœ— ``` diff --git a/.agents/skills/stitch-sdk-domain-design/SKILL.md b/.agents/skills/stitch-sdk-domain-design/SKILL.md index d333122..09ba44e 100644 --- a/.agents/skills/stitch-sdk-domain-design/SKILL.md +++ b/.agents/skills/stitch-sdk-domain-design/SKILL.md @@ -36,7 +36,11 @@ Each class represents a domain entity. Ask: "What noun does the user interact wi "constructorParams": [], "isRoot": true, "factories": [ - { "method": "project", "returns": "Project", "description": "Create a Project handle from an ID." } + { + "method": "project", + "returns": "Project", + "description": "Create a Project handle from an ID." + } ] } } @@ -44,13 +48,13 @@ Each class represents a domain entity. Ask: "What noun does the user interact wi ### Key decisions: -| Field | Purpose | Example | -|---|---|---| -| `constructorParams` | Fields stored on the instance | `["projectId", "screenId"]` | -| `fieldMapping` | Per-field data source mapping with optional `stripPrefix` | See below | -| `parentField` | Which param is injected from a parent class | `"projectId"` | -| `idField` | Which param the `.id` getter aliases | `"screenId"` | -| `factories` | Local factory methods (no API call) | `[{ "method": "project", "returns": "Project" }]` | +| Field | Purpose | Example | +| ------------------- | --------------------------------------------------------- | ------------------------------------------------- | +| `constructorParams` | Fields stored on the instance | `["projectId", "screenId"]` | +| `fieldMapping` | Per-field data source mapping with optional `stripPrefix` | See below | +| `parentField` | Which param is injected from a parent class | `"projectId"` | +| `idField` | Which param the `.id` getter aliases | `"screenId"` | +| `factories` | Local factory methods (no API call) | `[{ "method": "project", "returns": "Project" }]` | ### Field Mapping @@ -61,7 +65,10 @@ Use `fieldMapping` when a param needs a different source field, prefix stripping "constructorParams": ["projectId", "screenId"], "fieldMapping": { "projectId": { "from": "name", "stripPrefix": "projects/" }, - "screenId": { "from": "id", "fallback": { "field": "name", "splitOn": "/screens/" } } + "screenId": { + "from": "id", + "fallback": { "field": "name", "splitOn": "/screens/" } + } } } ``` @@ -77,12 +84,12 @@ Each binding maps one MCP tool to one class method. Ask: "Who owns this action?" ### Arg routing -| Type | Meaning | Code generated | -|---|---|---| -| `self` | From `this.field` | `projectId: this.projectId` | -| `param` | From method parameter | `prompt: prompt` | -| `computed` | Template interpolation | `name: \`projects/${this.projectId}/screens/${screenId}\`` | -| `selfArray` | Wrap self field as array | `selectedScreenIds: [this.screenId]` | +| Type | Meaning | Code generated | +| ----------- | ------------------------ | ---------------------------------------------------------- | +| `self` | From `this.field` | `projectId: this.projectId` | +| `param` | From method parameter | `prompt: prompt` | +| `computed` | Template interpolation | `name: \`projects/${this.projectId}/screens/${screenId}\`` | +| `selfArray` | Wrap self field as array | `selectedScreenIds: [this.screenId]` | Optional params use `"optional": true`. Renamed params use `"rename": "newName"`. @@ -94,13 +101,13 @@ The `returns.projection` array tells codegen how to navigate the API response. E { prop: string; index?: number; each?: boolean; fallback?: string } ``` -| Projection | Generated code | Use when | -|---|---|---| -| `[]` (empty) | `raw` | Direct return (whole response) | -| `[{ "prop": "projects" }]` | `raw.projects` | Array inside object | -| `[{ "prop": "outputComponents", "index": 0 }, { "prop": "design" }, { "prop": "screens", "index": 0 }]` | `raw.outputComponents[0].design.screens[0]` | Deeply nested single item | -| `[{ "prop": "outputComponents", "each": true }, { "prop": "design" }, { "prop": "screens", "each": true }]` | `flatMap` chain | Collect all items across arrays | -| `[{ "prop": "screenshot" }, { "prop": "downloadUrl" }]` | `raw.screenshot.downloadUrl` | Navigate nested properties | +| Projection | Generated code | Use when | +| ----------------------------------------------------------------------------------------------------------- | ------------------------------------------- | ------------------------------- | +| `[]` (empty) | `raw` | Direct return (whole response) | +| `[{ "prop": "projects" }]` | `raw.projects` | Array inside object | +| `[{ "prop": "outputComponents", "index": 0 }, { "prop": "design" }, { "prop": "screens", "index": 0 }]` | `raw.outputComponents[0].design.screens[0]` | Deeply nested single item | +| `[{ "prop": "outputComponents", "each": true }, { "prop": "design" }, { "prop": "screens", "each": true }]` | `flatMap` chain | Collect all items across arrays | +| `[{ "prop": "screenshot" }, { "prop": "downloadUrl" }]` | `raw.screenshot.downloadUrl` | Navigate nested properties | **Decision**: Use `"index": 0` when extracting a single item. Use `"each": true` when collecting all items (array result). You **cannot** use both on the same step. @@ -110,8 +117,15 @@ The `returns.projection` array tells codegen how to navigate the API response. E ### Return class wrapping When `returns.class` is set, the extracted data is wrapped in a domain class constructor: + ```json -{ "returns": { "class": "Screen", "projection": [{ "prop": "screens" }], "array": true } } +{ + "returns": { + "class": "Screen", + "projection": [{ "prop": "screens" }], + "array": true + } +} ``` The codegen automatically spreads `parentField` into the data if the child class declares one. @@ -119,6 +133,7 @@ The codegen automatically spreads `parentField` into the data if the child class ### Cache-aware methods Add a `cache` field with a structured `projection` to check `this.data` before calling the API: + ```json { "cache": { @@ -131,6 +146,7 @@ Add a `cache` field with a structured `projection` to check `this.data` before c When the cached property is a nested object (like `File` with a `downloadUrl`), use multiple projection steps to drill into it. Generated code: + ```typescript if (this.data?.htmlCode?.downloadUrl) return this.data?.htmlCode?.downloadUrl; // ... else call API @@ -167,6 +183,7 @@ bun scripts/validate-generated.ts # Lock integrity ``` If a projection is invalid, you'll see: + ``` โŒ Binding "Project.generate" projection step 2: property "screenz" not found in outputSchema. diff --git a/.agents/skills/stitch-sdk-pipeline/SKILL.md b/.agents/skills/stitch-sdk-pipeline/SKILL.md index e6db54a..97b3ecc 100644 --- a/.agents/skills/stitch-sdk-pipeline/SKILL.md +++ b/.agents/skills/stitch-sdk-pipeline/SKILL.md @@ -48,6 +48,7 @@ Connects to the Stitch MCP server, calls `tools/list`, and writes the raw schema Read `tools-manifest.json` and edit `packages/sdk/generated/domain-map.json` to map tools โ†’ classes โ†’ methods. Key decisions at this stage: + - Which class owns each tool? - What are the arg routing rules (self, param, computed, selfArray)? - What is the response projection path? @@ -140,12 +141,14 @@ Verifies that `stitch-sdk.lock` hashes match the actual generated files. Catches After the pipeline passes, audit agent skills for freshness. Read the current source of truth and update any skills that reference stale methods, args, or examples. **Inputs**: + - `packages/sdk/src/index.ts` (public surface) - Generated class files in `packages/sdk/generated/src/` - `packages/sdk/src/spec/errors.ts` (error codes) - `packages/sdk/src/spec/client.ts` (config schema) **Skills to audit** (in priority order): + 1. `stitch-sdk-usage` โ€” highest churn, references specific methods and constructor signatures 2. `stitch-sdk-readme` โ€” must document `stitchTools()`, `toolDefinitions`, and AI SDK integration examples 3. `stitch-sdk-development` โ€” check cache examples match current domain-map patterns @@ -154,6 +157,7 @@ After the pipeline passes, audit agent skills for freshness. Read the current so **Skills to skip**: `stitch-sdk-pipeline` (self-referential), `red-green-yellow` (generic methodology). **What to check**: + - Every method name in a code example exists on its class - Every import in an example matches an export in `index.ts` - Constructor signatures match the actual constructors @@ -176,27 +180,27 @@ Runs Stage 1 โ†’ 3 โ†’ 4 โ†’ 5 in sequence. Does **not** include Stage 2 (agent) ### Starting from a specific stage -| Scenario | Start from | -|---|---| -| New tool added to MCP server | Stage 1 | -| Need to change how a tool maps to a method | Stage 2 | -| Changed `ir-schema.ts` or `generate-sdk.ts` | Stage 3 | -| Changed code in `packages/sdk/src/` (client, errors) | Stage 4 | -| Just want to verify everything works | Stage 5 | -| Changed AI SDK tools adapter or tool definitions | Stage 5 | -| Public API surface changed | Stage 9 | +| Scenario | Start from | +| ---------------------------------------------------- | ---------- | +| New tool added to MCP server | Stage 1 | +| Need to change how a tool maps to a method | Stage 2 | +| Changed `ir-schema.ts` or `generate-sdk.ts` | Stage 3 | +| Changed code in `packages/sdk/src/` (client, errors) | Stage 4 | +| Just want to verify everything works | Stage 5 | +| Changed AI SDK tools adapter or tool definitions | Stage 5 | +| Public API surface changed | Stage 9 | ### Key files -| File | Location | Role | -|---|---|---| -| `tools-manifest.json` | `packages/sdk/generated/` | Raw MCP tool schemas (Stage 1 output) | -| `domain-map.json` | `packages/sdk/generated/` | IR: tool โ†’ class โ†’ method mappings (Stage 2 output) | -| `tool-definitions.ts` | `packages/sdk/generated/src/` | Generated JSON Schema tool definitions for AI SDK | -| `tools-adapter.ts` | `packages/sdk/src/` | `stitchTools()` โ€” AI SDK v6 adapter (imported via `@google/stitch-sdk/ai`) | -| `ir-schema.ts` | `scripts/` | Zod schema defining valid IR structure | -| `tool-schema.ts` | `scripts/` | TypeScript types for JSON Schema | -| `generate-sdk.ts` | `scripts/` | ts-morph codegen (Stage 3) | -| `stitch-sdk.lock` | `packages/sdk/generated/` | Integrity hashes for drift detection | -| `stitch-html.ts` | `packages/sdk/test/helpers/` | Stitch HTML parser (Tailwind config + font extraction) | -| `component-validator.ts` | `packages/sdk/test/helpers/` | SWC AST validator for generated React components | +| File | Location | Role | +| ------------------------ | ----------------------------- | -------------------------------------------------------------------------- | +| `tools-manifest.json` | `packages/sdk/generated/` | Raw MCP tool schemas (Stage 1 output) | +| `domain-map.json` | `packages/sdk/generated/` | IR: tool โ†’ class โ†’ method mappings (Stage 2 output) | +| `tool-definitions.ts` | `packages/sdk/generated/src/` | Generated JSON Schema tool definitions for AI SDK | +| `tools-adapter.ts` | `packages/sdk/src/` | `stitchTools()` โ€” AI SDK v6 adapter (imported via `@google/stitch-sdk/ai`) | +| `ir-schema.ts` | `scripts/` | Zod schema defining valid IR structure | +| `tool-schema.ts` | `scripts/` | TypeScript types for JSON Schema | +| `generate-sdk.ts` | `scripts/` | ts-morph codegen (Stage 3) | +| `stitch-sdk.lock` | `packages/sdk/generated/` | Integrity hashes for drift detection | +| `stitch-html.ts` | `packages/sdk/test/helpers/` | Stitch HTML parser (Tailwind config + font extraction) | +| `component-validator.ts` | `packages/sdk/test/helpers/` | SWC AST validator for generated React components | diff --git a/.agents/skills/stitch-sdk-readme/SKILL.md b/.agents/skills/stitch-sdk-readme/SKILL.md index cfd981d..e4d245f 100644 --- a/.agents/skills/stitch-sdk-readme/SKILL.md +++ b/.agents/skills/stitch-sdk-readme/SKILL.md @@ -13,18 +13,18 @@ This skill produces the README for `@google/stitch-sdk`. It combines a structura Do not hard-code the API surface. Read it from the codebase at invocation time: -| What you need | Where to find it | -|---|---| -| Public exports (full surface) | `packages/sdk/src/index.ts` | -| Domain class methods + signatures | Source files for each exported class (`sdk.ts`, `project.ts`, `screen.ts`) | -| Generated method bindings | `packages/sdk/generated/domain-map.json` โ†’ `bindings[]` array | -| Handwritten methods | Methods in class source files that aren't in domain-map bindings (e.g. `Screen.edit`, `Screen.variants`) | -| AI SDK tools adapter | `packages/sdk/src/ai.ts` โ†’ subpath entry for `stitchTools()` | -| Generated tool definitions | `packages/sdk/generated/src/tool-definitions.ts` โ†’ JSON Schema for each tool | -| Tool client methods | `packages/sdk/src/client.ts` | -| Error codes | `packages/sdk/src/spec/errors.ts` โ†’ `StitchErrorCode` | -| Config options | `packages/sdk/src/spec/client.ts` โ†’ `StitchConfigSchema` | -| Proxy config | `packages/sdk/src/proxy/core.ts` | +| What you need | Where to find it | +| --------------------------------- | -------------------------------------------------------------------------------------------------------- | +| Public exports (full surface) | `packages/sdk/src/index.ts` | +| Domain class methods + signatures | Source files for each exported class (`sdk.ts`, `project.ts`, `screen.ts`) | +| Generated method bindings | `packages/sdk/generated/domain-map.json` โ†’ `bindings[]` array | +| Handwritten methods | Methods in class source files that aren't in domain-map bindings (e.g. `Screen.edit`, `Screen.variants`) | +| AI SDK tools adapter | `packages/sdk/src/ai.ts` โ†’ subpath entry for `stitchTools()` | +| Generated tool definitions | `packages/sdk/generated/src/tool-definitions.ts` โ†’ JSON Schema for each tool | +| Tool client methods | `packages/sdk/src/client.ts` | +| Error codes | `packages/sdk/src/spec/errors.ts` โ†’ `StitchErrorCode` | +| Config options | `packages/sdk/src/spec/client.ts` โ†’ `StitchConfigSchema` | +| Proxy config | `packages/sdk/src/proxy/core.ts` | After reading these files, you have the complete API surface. Structure it using the Bookstore Test template below. @@ -36,7 +36,7 @@ A reader decides whether to use a library the same way a person decides to buy a ### The Cover -A single sentence stating what problem this library solves โ€” not what the library *is*. The reader should recognize their own situation. No taglines, no badges, no logos. +A single sentence stating what problem this library solves โ€” not what the library _is_. The reader should recognize their own situation. No taglines, no badges, no logos. **For this SDK**, the cover is about generating UI from text and extracting HTML/screenshots programmatically. @@ -48,12 +48,15 @@ A single sentence stating what problem this library solves โ€” not what the libr Immediately show the library in use. Code first, not setup. **Primary workflow** โ€” the punchline everything in the SDK exists to produce: + ``` project(id) โ†’ generate โ†’ getHtml ``` + Show this as the first code block, with one line noting the env var requirement. Do not show installation, imports, or config before this. Show `callTool("create_project", ...)` separately for project creation. **Secondary workflows** โ€” reveal depth progressively: + 1. Listing and iterating over existing projects/screens 2. Editing a screen and generating variants 3. Tool access via singleton (`stitch.listTools()`, `stitch.callTool()`) โ€” zero setup @@ -61,6 +64,7 @@ Show this as the first code block, with one line noting the env var requirement. 5. AI SDK integration via `stitchTools()` โ€” import from `@google/stitch-sdk/ai`, show `generateText` with `tools: stitchTools()` and `stepCountIs` Rules for this section: + - **No setup first.** One line mentioning `STITCH_API_KEY` is enough before the first example. - **Dual install paths.** Show `npm install @google/stitch-sdk` first (core SDK, standalone). Then show `npm install @google/stitch-sdk ai` for AI SDK users. The `ai` package is only needed when importing from `@google/stitch-sdk/ai`. - **Straightforward language.** No "powerful", "seamless", "robust", "enterprise-grade". @@ -74,6 +78,7 @@ The reader is committed. Document the full API as a reference. Structure by class in this order: `Stitch` โ†’ `Project` โ†’ `DesignSystem` โ†’ `Screen` โ†’ `StitchToolClient` โ†’ `toolDefinitions` / `toolMap` โ†’ `stitchTools()` (AI SDK) โ†’ `StitchProxy` โ†’ `stitch` singleton. Each entry should have: + - What it does (one line) - Usage example (minimal, runnable) - Parameters (table) @@ -93,7 +98,7 @@ After generating the README, verify: - [ ] Can a reader understand what the library does in under 10 seconds? - [ ] Is there a runnable code example within the first scroll? -- [ ] Does setup/config appear *after* the first code example? +- [ ] Does setup/config appear _after_ the first code example? - [ ] Is every code block valid, copy-pasteable code? - [ ] Is the language descriptive rather than promotional? - [ ] Does the reference section cover every public export from `index.ts`? @@ -103,12 +108,12 @@ After generating the README, verify: ## Anti-patterns -| Anti-pattern | Why it fails | -|---|---| +| Anti-pattern | Why it fails | +| --------------------------------------------- | ---------------------------------------------------------- | | Leading with badges, logos, or status shields | Visual noise before the reader knows what the library does | -| "Getting Started" as the first section | Forces setup before demonstrating value | -| Feature bullet lists without code | Tells instead of shows | -| "Easy to use", "simple", "just works" | Self-congratulatory claims that invite skepticism | -| Long install/config blocks before any usage | Asks for investment before demonstrating return | -| Collapsible sections hiding core API docs | Buries the content committed readers came for | -| Hard-coding the API in docs without sourcing | Goes stale when tools are added | +| "Getting Started" as the first section | Forces setup before demonstrating value | +| Feature bullet lists without code | Tells instead of shows | +| "Easy to use", "simple", "just works" | Self-congratulatory claims that invite skepticism | +| Long install/config blocks before any usage | Asks for investment before demonstrating return | +| Collapsible sections hiding core API docs | Buries the content committed readers came for | +| Hard-coding the API in docs without sourcing | Goes stale when tools are added | diff --git a/.agents/skills/stitch-sdk-usage/SKILL.md b/.agents/skills/stitch-sdk-usage/SKILL.md index c713d75..1961a30 100644 --- a/.agents/skills/stitch-sdk-usage/SKILL.md +++ b/.agents/skills/stitch-sdk-usage/SKILL.md @@ -22,11 +22,11 @@ export STITCH_API_KEY="your-api-key" ## Quick Start ```typescript -import { stitch } from '@google/stitch-sdk'; +import { stitch } from "@google/stitch-sdk"; const project = await stitch.createProject("My App"); const screen = await project.generate("A settings page with dark theme"); -const html = await screen.getHtml(); // download URL for the HTML +const html = await screen.getHtml(); // download URL for the HTML const imageUrl = await screen.getImage(); // download URL for the screenshot ``` @@ -35,7 +35,7 @@ The `stitch` singleton reads `STITCH_API_KEY` from the environment and connects ## Working with Projects ```typescript -import { stitch } from '@google/stitch-sdk'; +import { stitch } from "@google/stitch-sdk"; // List all projects const projects = await stitch.projects(); @@ -64,7 +64,7 @@ const updated = await ds.update({ displayName: "Updated Theme" }); // Apply to screens (requires SelectedScreenInstance objects from project.data.screenInstances) const screens = await ds.apply([ - { id: "instance-id", sourceScreen: "projects/123/screens/456" } + { id: "instance-id", sourceScreen: "projects/123/screens/456" }, ]); ``` @@ -73,13 +73,13 @@ const screens = await ds.apply([ Upload an existing image file (PNG, JPG, JPEG, WEBP) to create a screen directly from a mockup or asset. ```typescript -import { stitch } from '@google/stitch-sdk'; +import { stitch } from "@google/stitch-sdk"; const project = stitch.project("your-project-id"); // Upload a local image file -const [screen] = await project.uploadImage('./mockup.png', { - title: 'Home Screen', +const [screen] = await project.uploadImage("./mockup.png", { + title: "Home Screen", }); console.log(screen.id); @@ -99,12 +99,13 @@ The method reads the file from disk and posts it directly to the Stitch REST API **Throws** `StitchError` with codes: `NOT_FOUND` (file not found), `UNKNOWN_ERROR` (unsupported format or upload failure), `AUTH_FAILED` (invalid API key). - ## Generating and Iterating on Screens ```typescript // Generate a new screen from a prompt -const screen = await project.generate("Login page with email and password fields"); +const screen = await project.generate( + "Login page with email and password fields", +); // Edit an existing screen const edited = await screen.edit("Make the background dark and add a subtitle"); @@ -134,7 +135,7 @@ Both methods use cached data from the generation response when available, fallin For agents and orchestration scripts that forward JSON payloads to MCP tools: ```typescript -import { stitch } from '@google/stitch-sdk'; +import { stitch } from "@google/stitch-sdk"; // Find available tools const { tools } = await stitch.listTools(); @@ -143,7 +144,8 @@ for (const tool of tools) { } // Call a tool with a JSON payload const result = await stitch.callTool("generate_screen_from_text", { - projectId: "123", prompt: "A login page" + projectId: "123", + prompt: "A login page", }); await stitch.close(); ``` @@ -153,15 +155,15 @@ await stitch.close(); All SDK methods throw `StitchError` on failure. Use try/catch: ```typescript -import { stitch, StitchError } from '@google/stitch-sdk'; +import { stitch, StitchError } from "@google/stitch-sdk"; try { const project = stitch.project("bad-id"); await project.screens(); } catch (e) { if (e instanceof StitchError) { - console.log(e.code); // "AUTH_FAILED", "NOT_FOUND", etc. - console.log(e.message); // Human-readable description + console.log(e.code); // "AUTH_FAILED", "NOT_FOUND", etc. + console.log(e.message); // Human-readable description console.log(e.recoverable); // Whether retrying might succeed } } @@ -173,23 +175,23 @@ Error codes: `AUTH_FAILED`, `NOT_FOUND`, `PERMISSION_DENIED`, `RATE_LIMITED`, `N ### Stitch Class -| Method | Returns | Description | -|---|---|---| -| `createProject(title)` | `Promise` | Create a new project | -| `projects()` | `Promise` | List all projects | -| `project(id)` | `Project` | Reference a project by ID (no network call) | +| Method | Returns | Description | +| ---------------------- | -------------------- | ------------------------------------------- | +| `createProject(title)` | `Promise` | Create a new project | +| `projects()` | `Promise` | List all projects | +| `project(id)` | `Project` | Reference a project by ID (no network call) | ### Project Class -| Method | Returns | Description | -|---|---|---| -| `generate(prompt, deviceType?)` | `Promise` | Generate a screen from a text prompt | -| `screens()` | `Promise` | List all screens in the project | -| `getScreen(screenId)` | `Promise` | Retrieve a specific screen by ID | -| `uploadImage(filePath, opts?)` | `Promise` | Upload an image file and create a screen from it | -| `createDesignSystem(designSystem)` | `Promise` | Create a design system for this project | -| `listDesignSystems()` | `Promise` | List all design systems | -| `designSystem(id)` | `DesignSystem` | Reference by ID (no API call) | +| Method | Returns | Description | +| ---------------------------------- | ------------------------- | ------------------------------------------------ | +| `generate(prompt, deviceType?)` | `Promise` | Generate a screen from a text prompt | +| `screens()` | `Promise` | List all screens in the project | +| `getScreen(screenId)` | `Promise` | Retrieve a specific screen by ID | +| `uploadImage(filePath, opts?)` | `Promise` | Upload an image file and create a screen from it | +| `createDesignSystem(designSystem)` | `Promise` | Create a design system for this project | +| `listDesignSystems()` | `Promise` | List all design systems | +| `designSystem(id)` | `DesignSystem` | Reference by ID (no API call) | `deviceType`: `"MOBILE"` | `"DESKTOP"` | `"TABLET"` | `"AGNOSTIC"` @@ -197,35 +199,35 @@ Error codes: `AUTH_FAILED`, `NOT_FOUND`, `PERMISSION_DENIED`, `RATE_LIMITED`, `N ### DesignSystem Class -| Method | Returns | Description | -|---|---|---| -| `update(designSystem)` | `Promise` | Update the design system's theme | -| `apply(selectedScreenInstances)` | `Promise` | Apply this design system to screens | +| Method | Returns | Description | +| -------------------------------- | ----------------------- | ----------------------------------- | +| `update(designSystem)` | `Promise` | Update the design system's theme | +| `apply(selectedScreenInstances)` | `Promise` | Apply this design system to screens | ### Screen Class -| Method | Returns | Description | -|---|---|---| -| `getHtml()` | `Promise` | Get the screen's HTML download URL | -| `getImage()` | `Promise` | Get the screen's screenshot download URL | -| `edit(prompt, deviceType?, modelId?)` | `Promise` | Edit the screen using a text prompt | -| `variants(prompt, options, deviceType?, modelId?)` | `Promise` | Generate variants of the screen | +| Method | Returns | Description | +| -------------------------------------------------- | ------------------- | ---------------------------------------- | +| `getHtml()` | `Promise` | Get the screen's HTML download URL | +| `getImage()` | `Promise` | Get the screen's screenshot download URL | +| `edit(prompt, deviceType?, modelId?)` | `Promise` | Edit the screen using a text prompt | +| `variants(prompt, options, deviceType?, modelId?)` | `Promise` | Generate variants of the screen | `modelId`: `"GEMINI_3_PRO"` | `"GEMINI_3_FLASH"` ### StitchToolClient (for agents) -| Method | Returns | Description | -|---|---|---| -| `callTool(name, args)` | `Promise` | Call any MCP tool by name | -| `listTools()` | `Promise` | Discover available tools | -| `connect()` | `Promise` | Establish MCP connection (auto-called by callTool) | -| `close()` | `Promise` | Close the connection | +| Method | Returns | Description | +| ---------------------- | ---------------- | -------------------------------------------------- | +| `callTool(name, args)` | `Promise` | Call any MCP tool by name | +| `listTools()` | `Promise` | Discover available tools | +| `connect()` | `Promise` | Establish MCP connection (auto-called by callTool) | +| `close()` | `Promise` | Close the connection | ### Explicit Configuration ```typescript -import { Stitch, StitchToolClient } from '@google/stitch-sdk'; +import { Stitch, StitchToolClient } from "@google/stitch-sdk"; const client = new StitchToolClient({ apiKey: "your-api-key", @@ -237,10 +239,10 @@ const sdk = new Stitch(client); const projects = await sdk.projects(); ``` -| Option | Env Variable | Description | -|---|---|---| -| `apiKey` | `STITCH_API_KEY` | API key for authentication | -| `accessToken` | `STITCH_ACCESS_TOKEN` | OAuth access token | -| `projectId` | `GOOGLE_CLOUD_PROJECT` | GCP project ID (required with OAuth) | -| `baseUrl` | โ€” | MCP server URL (default: `https://stitch.googleapis.com/mcp`) | -| `timeout` | โ€” | Request timeout in ms (default: 300000) | +| Option | Env Variable | Description | +| ------------- | ---------------------- | ------------------------------------------------------------- | +| `apiKey` | `STITCH_API_KEY` | API key for authentication | +| `accessToken` | `STITCH_ACCESS_TOKEN` | OAuth access token | +| `projectId` | `GOOGLE_CLOUD_PROJECT` | GCP project ID (required with OAuth) | +| `baseUrl` | โ€” | MCP server URL (default: `https://stitch.googleapis.com/mcp`) | +| `timeout` | โ€” | Request timeout in ms (default: 300000) | diff --git a/.agents/skills/tdd/SKILL.md b/.agents/skills/tdd/SKILL.md index 2db4681..d783dfc 100644 --- a/.agents/skills/tdd/SKILL.md +++ b/.agents/skills/tdd/SKILL.md @@ -13,19 +13,25 @@ This skill implements a structural framework for AI-assisted programming to ensu ## The Three-Phase Cycle ### Phase 1: Red (Establish Failure) + You must prove the feature does not exist and that your test is valid. + 1. **Write One Test**: Create a single test case (e.g., in Vitest or Jest) for the next small piece of behavior. -2. **Execute & Fail**: Run the test. It must fail. +2. **Execute & Fail**: Run the test. It must fail. 3. **Verify**: Ensure the failure is related to the missing logic (e.g., `ReferenceError: add is not defined`) and not a configuration error. ### Phase 2: Green (Minimal Pass) + Make the test pass as quickly and simply as possible. + 1. **Minimal Implementation**: Write the simplest code that satisfies the test. Do not build for the future; focus strictly on the current "Red" test. 2. **Run Tests**: Execute the suite. All tests must be Green. 3. **Evidence**: The transition from Red to Green is the "Proof of Work" for the developer. ### Phase 3: Refactor (Clean Up) + Improve the code structure while maintaining the "Green" state. + 1. **Clean Up**: Improve naming, remove duplication, and optimize the code written in Phase 2. 2. **Safety Net**: Rerun the tests after every change. If they turn Red, revert the change immediately. @@ -34,14 +40,18 @@ Improve the code structure while maintaining the "Green" state. ## Core Operational Rules ### 1. No "Horizontal Splurging" + You are strictly forbidden from writing a large "splurge" of multiple tests at once. You must follow a strictly incremental loop: + - **Write 1 Test -> See it Fail -> Write 1 Fix -> See it Pass**. - Repeat this loop for every sub-feature. ### 2. Impose Backpressure + Use automated assertions and strong typing (TypeScript) as backpressure to prevent the AI from "guessing" the solution or "playing in the mud" with low-quality code. ### 3. Verification of Integrity + Never modify an existing test to make a failing implementation pass. If a test must change, it must be because the requirement changed, not because the code is difficult to write. --- @@ -49,19 +59,21 @@ Never modify an existing test to make a failing implementation pass. If a test m ## Example Workflow (TypeScript + Vitest) **Step 1: Red** + ```typescript // math.test.ts -import { describe, it, expect } from 'vitest'; -import { add } from './math'; +import { describe, it, expect } from "vitest"; +import { add } from "./math"; -describe('add', () => { - it('should sum two numbers', () => { +describe("add", () => { + it("should sum two numbers", () => { expect(add(2, 2)).toBe(4); // Fails: ReferenceError: add is not defined }); }); ``` **Step 2: Green** + ```typescript // math.ts export const add = (a: any, b: any) => { @@ -70,6 +82,7 @@ export const add = (a: any, b: any) => { ``` **Step 3: Refactor** + ```typescript // math.ts /** diff --git a/.agents/skills/typed-service-contract/skill.md b/.agents/skills/typed-service-contract/skill.md index 38f1306..a748809 100644 --- a/.agents/skills/typed-service-contract/skill.md +++ b/.agents/skills/typed-service-contract/skill.md @@ -17,7 +17,9 @@ This skill defines a **Vertical Slice Architecture** backed by **Design by Contr ## Architecture Components ### 1. The Spec (`spec.ts`) -The "Contract" or "Port". It defines the *What*. It must contain: + +The "Contract" or "Port". It defines the _What_. It must contain: + - **Input Schema:** A Zod schema that parses raw input into a valid DTO. - **Output Schema:** A Zod schema defining the successful data structure. - **Error Schema:** A discriminated union of specific failure modes (not generic errors). @@ -25,7 +27,9 @@ The "Contract" or "Port". It defines the *What*. It must contain: - **Interface:** The capability definition (e.g., `interface ConfigureSpec`). ### 2. The Handler (`handler.ts`) -The "Implementation" or "Adapter". It defines the *How*. It must: + +The "Implementation" or "Adapter". It defines the _How_. It must: + - Implement the Interface defined in the Spec. - Be an "Impure" class that handles side effects (File System, API calls). - **NEVER throw** exceptions. It must catch internal errors and map them to the `Result` type. @@ -39,12 +43,13 @@ The "Implementation" or "Adapter". It defines the *How*. It must: Follow this template to define the boundaries. ```typescript -import { z } from 'zod'; +import { z } from "zod"; // 1. VALIDATION HELPERS (Reusable Refinements) -export const SafePathSchema = z.string() +export const SafePathSchema = z + .string() .min(1) - .refine(p => !p.includes('..'), "No traversal allowed"); + .refine((p) => !p.includes(".."), "No traversal allowed"); // 2. INPUT (The Command) - "Parse, don't validate" export const MyTaskInputSchema = z.object({ @@ -55,9 +60,9 @@ export type MyTaskInput = z.infer; // 3. ERROR CODES (Exhaustive) export const MyTaskErrorCode = z.enum([ - 'FILE_NOT_FOUND', - 'PERMISSION_DENIED', - 'UNKNOWN_ERROR' + "FILE_NOT_FOUND", + "PERMISSION_DENIED", + "UNKNOWN_ERROR", ]); // 4. RESULT (The Monad) @@ -73,18 +78,17 @@ export const MyTaskFailure = z.object({ message: z.string(), suggestion: z.string().optional(), recoverable: z.boolean(), - }) + }), }); -export type MyTaskResult = - | z.infer +export type MyTaskResult = + | z.infer | z.infer; // 5. INTERFACE (The Capability) export interface MyTaskSpec { execute(input: MyTaskInput): Promise; } - ``` ### Step 2: Implement the Handler (`handler.ts`) @@ -92,8 +96,8 @@ export interface MyTaskSpec { Follow this template to implement the logic. ```typescript -import { MyTaskSpec, MyTaskInput, MyTaskResult } from './spec.js'; -import * as fs from 'fs'; +import { MyTaskSpec, MyTaskInput, MyTaskResult } from "./spec.js"; +import * as fs from "fs"; export class MyTaskHandler implements MyTaskSpec { async execute(input: MyTaskInput): Promise { @@ -104,33 +108,31 @@ export class MyTaskHandler implements MyTaskSpec { return { success: false, error: { - code: 'FILE_NOT_FOUND', + code: "FILE_NOT_FOUND", message: `Path does not exist: ${input.path}`, - recoverable: true - } + recoverable: true, + }, }; } // 3. Success Return return { success: true, - data: 'Operation complete' + data: "Operation complete", }; - } catch (error) { // 4. Safety Net: Catch unknown runtime errors return { success: false, error: { - code: 'UNKNOWN_ERROR', + code: "UNKNOWN_ERROR", message: error instanceof Error ? error.message : String(error), - recoverable: false - } + recoverable: false, + }, }; } } } - ``` ### Step 3: Testing Strategy @@ -139,52 +141,50 @@ Do not write monolithic tests. Split them into **Contract Tests** and **Logic Te #### A. Contract Tests (Schema) -Test the *Bouncer*. Ensure invalid data is rejected before it reaches the handler. +Test the _Bouncer_. Ensure invalid data is rejected before it reaches the handler. -* **Focus:** Edge cases, validation rules, Zod refinements. -* **Style:** Data-driven (Table tests). +- **Focus:** Edge cases, validation rules, Zod refinements. +- **Style:** Data-driven (Table tests). ```typescript // spec.test.ts -import { MyTaskInputSchema } from './spec'; +import { MyTaskInputSchema } from "./spec"; const invalidCases = [ - { val: '../etc/passwd', err: 'No traversal allowed' }, - { val: '', err: 'min(1)' }, + { val: "../etc/passwd", err: "No traversal allowed" }, + { val: "", err: "min(1)" }, ]; -test.each(invalidCases)('validates paths', ({ val, err }) => { +test.each(invalidCases)("validates paths", ({ val, err }) => { const result = MyTaskInputSchema.safeParse({ path: val }); expect(result.success).toBe(false); }); - ``` #### B. Logic Tests (Handler) -Test the *Chef*. Mock external dependencies (fs, network) and assert the Result Object. +Test the _Chef_. Mock external dependencies (fs, network) and assert the Result Object. -* **Focus:** Business logic flow, error mapping, success states. -* **Style:** Mocked unit tests or Scenario Runners. +- **Focus:** Business logic flow, error mapping, success states. +- **Style:** Mocked unit tests or Scenario Runners. ```typescript // handler.test.ts -import { MyTaskHandler } from './handler'; -import { vi } from 'vitest'; // or jest +import { MyTaskHandler } from "./handler"; +import { vi } from "vitest"; // or jest -test('returns FILE_NOT_FOUND if path missing', async () => { +test("returns FILE_NOT_FOUND if path missing", async () => { // MOCK vi.mocked(fs.existsSync).mockReturnValue(false); - + // EXECUTE const handler = new MyTaskHandler(); - const result = await handler.execute({ path: '/fake' }); + const result = await handler.execute({ path: "/fake" }); // ASSERT (Check the Result Object) expect(result.success).toBe(false); if (!result.success) { - expect(result.error.code).toBe('FILE_NOT_FOUND'); + expect(result.error.code).toBe("FILE_NOT_FOUND"); } }); - -``` \ No newline at end of file +``` diff --git a/.fleet/archived/code-snippets.md b/.fleet/archived/code-snippets.md index a922a8c..8c1bd71 100644 --- a/.fleet/archived/code-snippets.md +++ b/.fleet/archived/code-snippets.md @@ -1,6 +1,7 @@ --- milestone: "1" --- + # Use-Case Code Snippets Create runnable TypeScript code snippets that demonstrate the Stitch SDK (`@google/stitch-sdk`) from a user's perspective. Each snippet tells a short story โ€” a developer trying to accomplish something real. Snippets live in `packages/sdk/examples/` as standalone `.ts` files using top-level `await`. @@ -16,6 +17,7 @@ cat packages/sdk/src/index.ts ``` This shows every export. The SDK has three modalities: + 1. **Domain classes** โ€” `Stitch`, `Project`, `Screen` (generated from `generated/src/`) 2. **Singleton** โ€” `stitch` (pre-configured instance, reads `STITCH_API_KEY` from env) 3. **AI SDK adapter** โ€” `stitchTools()` (returns tools for Vercel AI SDK `generateText`) @@ -157,6 +159,7 @@ Before creating a new snippet, review the existing files in `packages/sdk/exampl Snippets that call the live API should import the shared gate helper: **`packages/sdk/examples/_require-key.ts`** (shared, not a snippet itself): + ```typescript if (!process.env.STITCH_API_KEY) { console.log("โญ๏ธ Set STITCH_API_KEY to run this snippet."); @@ -188,6 +191,7 @@ Keep snippets under 80 lines. Prefer clarity over cleverness โ€” a developer new ## Insight Hints After creating snippets, report: + - Which SDK methods are covered by at least one snippet and which are uncovered - Any API capabilities (from `stitch.listTools()` or the domain-map) that would make compelling use cases but aren't represented yet - Whether the HTML output shapes (Tailwind config, Google Fonts, Material Symbols) suggest additional snippet ideas @@ -212,6 +216,7 @@ If a snippet fails, determine whether the issue is in the snippet or in the SDK: - **Snippet bug** (wrong method name, missing import, bad argument) โ€” fix the snippet and re-run. - **SDK bug** (a field returns `undefined` when the API populates it, an endpoint returns an unexpected status, or projection logic crashes on valid API data) โ€” write a detailed report: + ```bash cat > /tmp/bug-report.md << 'EOF' ## Steps to Reproduce @@ -242,4 +247,4 @@ If a snippet fails, determine whether the issue is in the snippet or in the SDK: - Use only the public SDK API (`import { ... } from "@google/stitch-sdk"`) - Include a JSDoc comment with a Usage section showing how to run it - Gate on `STITCH_API_KEY` โ€” snippets should print their expected output shape and exit gracefully when the key is absent -- Cross-reference existing snippets in `packages/sdk/examples/` before creating new ones โ€” each file should serve a unique user story \ No newline at end of file +- Cross-reference existing snippets in `packages/sdk/examples/` before creating new ones โ€” each file should serve a unique user story diff --git a/.fleet/archived/example.md b/.fleet/archived/example.md index a473a00..1f58aa4 100644 --- a/.fleet/archived/example.md +++ b/.fleet/archived/example.md @@ -8,17 +8,21 @@ Analyze the codebase for potential improvements and create issues for the engineering team. ## Tools + - Test Coverage: `npx vitest --coverage --json` ## Assessment Hints + - Focus on missing error handling in API routes - Look for hardcoded configuration values ## Insight Hints + - Report on overall test coverage metrics - Note any unusually complex functions (cyclomatic complexity) ## Constraints + - Do NOT propose changes already covered by open issues - Do NOT propose changes rejected in recently closed issues - Keep tasks small and isolated โ€” one logical change per issue diff --git a/.fleet/goals/sdk-examples.md b/.fleet/goals/sdk-examples.md index f25e717..5788569 100644 --- a/.fleet/goals/sdk-examples.md +++ b/.fleet/goals/sdk-examples.md @@ -9,12 +9,14 @@ Add practical examples demonstrating how to use the Stitch SDK (`@google/stitch- ## SDK Overview The Stitch SDK generates AI-powered UI screens and returns: + - **HTML** with embedded Tailwind CDN (`cdn.tailwindcss.com`) + a `