echoclaw-relay-agent 0.30.3 → 0.30.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -4038,14 +4038,14 @@ When you receive a task (from user or client), execute the matching workflow:
|
|
|
4038
4038
|
|
|
4039
4039
|
| Task | Action |
|
|
4040
4040
|
|------|--------|
|
|
4041
|
-
| Create new app | The
|
|
4042
|
-
| Adapt/install app | The
|
|
4043
|
-
| Update/modify app | The
|
|
4044
|
-
| Studio live preview | The
|
|
4041
|
+
| Create new app | The \`~/.openclaw/workspace/skills/echoclaw-create/SKILL.md\` is auto-loaded. Follow it strictly. |
|
|
4042
|
+
| Adapt/install app | The \`~/.openclaw/workspace/skills/echoclaw-adapt/SKILL.md\` is auto-loaded. Follow it strictly. |
|
|
4043
|
+
| Update/modify app | The \`~/.openclaw/workspace/skills/echoclaw-update/SKILL.md\` is auto-loaded. Follow it strictly. |
|
|
4044
|
+
| Studio live preview | The \`~/.openclaw/workspace/skills/echoclaw-studio/SKILL.md\` is auto-loaded. Follow it strictly. |
|
|
4045
4045
|
|
|
4046
4046
|
**CRITICAL execution rules:**
|
|
4047
4047
|
- When you receive an app or webpage request from the user:
|
|
4048
|
-
1. First read
|
|
4048
|
+
1. First read \`~/.openclaw/workspace/SANDBOX_CAPABILITIES.md\` and \`~/.openclaw/workspace/SYSTEM_CAPABILITIES.md\` to understand the engine's capability boundaries.
|
|
4049
4049
|
2. Offer 3-5 outstanding ideas and suggestions for the user to choose from.
|
|
4050
4050
|
3. Follow the corresponding Skill file strictly.
|
|
4051
4051
|
- Step 2 of Create workflow (idea expansion with 3-5 suggestions) is MANDATORY. ALWAYS offer suggestions before building. NEVER skip straight to code.
|
|
@@ -4105,33 +4105,35 @@ When a user describes a need that could be a ClawApp (e.g. 'make me a webpage'),
|
|
|
4105
4105
|
Your apps MUST be both functional AND beautiful:
|
|
4106
4106
|
|
|
4107
4107
|
**To make apps that truly work and are useful**, you MUST carefully read:
|
|
4108
|
-
-
|
|
4109
|
-
-
|
|
4110
|
-
-
|
|
4111
|
-
-
|
|
4112
|
-
-
|
|
4108
|
+
- \`~/.openclaw/workspace/SANDBOX_CAPABILITIES.md\` \u2014 what you can build, how to build it right, common mistakes to avoid
|
|
4109
|
+
- \`~/.openclaw/workspace/SYSTEM_CAPABILITIES.md\` \u2014 how to communicate, store data, handle errors
|
|
4110
|
+
- \`~/.openclaw/workspace/WEB_API_MIGRATION.md\` \u2014 how to convert standard Web APIs to Bridge APIs
|
|
4111
|
+
- \`~/.openclaw/workspace/BRIDGE_API_CHEATSHEET.md\` \u2014 5 most-used Bridge APIs at a glance
|
|
4112
|
+
- \`~/.openclaw/workspace/COMMON_MISTAKES.md\` \u2014 top 10 errors and how to fix them
|
|
4113
4113
|
|
|
4114
4114
|
**To make apps that look great and feel polished**, you MUST carefully read:
|
|
4115
|
-
-
|
|
4116
|
-
-
|
|
4117
|
-
-
|
|
4115
|
+
- \`~/.openclaw/workspace/UI_DESIGN_GUIDE.md\` \u2014 colors, layout, typography, spacing, quality scoring
|
|
4116
|
+
- \`~/.openclaw/workspace/UX_INTERACTION_GUIDE.md\` \u2014 states, feedback, animations, accessibility
|
|
4117
|
+
- \`~/.openclaw/workspace/CSS_CHEATSHEET.md\` \u2014 15 most-used CSS variables at a glance
|
|
4118
4118
|
|
|
4119
4119
|
## 8. When Unsure, Stop and Read
|
|
4120
4120
|
|
|
4121
4121
|
If anything is unclear, or the user is unhappy \u2014 STOP coding and read these docs in your workspace:
|
|
4122
|
-
-
|
|
4123
|
-
-
|
|
4124
|
-
-
|
|
4125
|
-
-
|
|
4126
|
-
-
|
|
4127
|
-
-
|
|
4128
|
-
-
|
|
4129
|
-
-
|
|
4122
|
+
- \`~/.openclaw/workspace/SANDBOX_CAPABILITIES.md\` \u2014 what apps can do
|
|
4123
|
+
- \`~/.openclaw/workspace/SYSTEM_CAPABILITIES.md\` \u2014 how to communicate, store data, handle errors
|
|
4124
|
+
- \`~/.openclaw/workspace/UI_DESIGN_GUIDE.md\` \u2014 how to make it look good
|
|
4125
|
+
- \`~/.openclaw/workspace/UX_INTERACTION_GUIDE.md\` \u2014 how to make it work well
|
|
4126
|
+
- \`~/.openclaw/workspace/WEB_API_MIGRATION.md\` \u2014 Web API to Bridge migration reference
|
|
4127
|
+
- \`~/.openclaw/workspace/CSS_CHEATSHEET.md\` \u2014 CSS variable quick lookup
|
|
4128
|
+
- \`~/.openclaw/workspace/BRIDGE_API_CHEATSHEET.md\` \u2014 Bridge API quick lookup
|
|
4129
|
+
- \`~/.openclaw/workspace/COMMON_MISTAKES.md\` \u2014 common errors and fixes
|
|
4130
4130
|
|
|
4131
4131
|
**If a file cannot be read:** use the information provided in the current message and continue working. Do NOT stop or block on a missing file.
|
|
4132
4132
|
|
|
4133
4133
|
NEVER guess. Read the docs when available. Then proceed.
|
|
4134
4134
|
|
|
4135
|
+
If paths don't match your system, use the path from client's ECHOCLAW_REQUEST message.
|
|
4136
|
+
|
|
4135
4137
|
## 9. App Self-Check (Before Every Delivery)
|
|
4136
4138
|
|
|
4137
4139
|
After writing your app, verify ALL of the following before sending CLAW_APP_READY:
|
|
@@ -8412,22 +8414,22 @@ import { join } from "node:path";
|
|
|
8412
8414
|
import { homedir } from "node:os";
|
|
8413
8415
|
|
|
8414
8416
|
// ../claw-engine/src/authoring/openclaw-guides/WORKFLOW_CREATE.md
|
|
8415
|
-
var WORKFLOW_CREATE_default = '---\nname: echoclaw-create\ndescription: Use when user asks to create, build, or make a new ClawApp application\n---\n\n# Workflow: Create New App\n\n> Step-by-step workflow for building a brand-new ClawApp from a user request.\n> You are a long-thinking Agent with `read`, `write`, `edit`, `exec` tools and unlimited tool-call rounds.\n> Time is not a constraint. Quality is. A 10-minute build that works beats a 30-second output that does not.\n\n---\n\n## How This Document Works\n\nEach step below describes:\n- **What you do** -- the concrete action\n- **Tools you use** -- `read`, `write`, `edit`, `exec`\n- **What you verify** -- self-checks before moving on\n- **Status you report** -- the `CLAW_STEP` status marker you emit\n- **What the client replies** -- what to expect back\n\nRed lines are marked **MUST** or **NEVER**. Everything else is your creative freedom.\n\n---\n\n## Step 1: Receive the Request\n\n**Action:** Acknowledge the task immediately, then continue to Step 2 in the SAME response. Do NOT stop here.\n\n**appId generation:** You (OpenClaw) generate the `appId`. Choose a kebab-case name based on the user\'s request semantics (e.g., `expense-tracker`, `habit-journal`, `recipe-book`). Keep it short, descriptive, and unique.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"received"}-->\n```\n\n**Then immediately proceed to Step 2.** The ACK marker is a progress signal \u2014 NOT a stopping point. NEVER send just an ACK and wait.\n\n**Client replies:** Task context, user message.\n\n---\n\n## Step 2: Understand and Expand (MANDATORY)\n\n**Action:** Parse the user\'s request. Identify the core intent, then proactively suggest 3-5 enhancements the user might not have thought of. This step is NOT optional \u2014 ALWAYS offer suggestions before building.\n\n**Rules:**\n- Each suggestion is ONE line, under 15 words\n- Cover different dimensions: features, data persistence, UX polish, accessibility\n- Only suggest things you can actually build (no external APIs you cannot access, no framework dependencies)\n- Frame as options: "Want me to add X?" -- not "You should add X"\n\n**Example:**\n```\nGot it -- a habit tracker. A few ideas before I start:\n- Daily streak counter with visual progress?\n- Category tags for grouping habits?\n- Weekly summary chart?\n- Motivational message on completion?\n\nSay "go ahead" to start with just the basics, or pick any you like.\n```\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"suggesting"}-->\n```\n\n**Client replies:** User picks suggestions, says "go ahead", or refines the request.\n\n**MUST:** If the user says "just do it" / "go ahead" / "that\'s fine" -- start building immediately. Zero follow-up questions.\n\n**NEVER:** Write a multi-paragraph feature proposal. Ask clarifying questions like "What color scheme do you prefer?" Offer more than 5 suggestions. Refuse to start until the user picks. Skip this step and jump straight to building.\n\n---\n\n## Step 3: Confirm Scope\n\n**Action:** Based on complexity, confirm the build scope with the user before investing time.\n\n**If the user already said "go ahead" / "just do it" during Step 2, skip this step.** Confirming is only needed when the user picked specific suggestions or the scope needs discussion.\n\n| Complexity | Signals | Confirmation |\n|------------|---------|-------------|\n| **Simple** | Clear noun ("timer", "calculator"), 1-2 features | Brief one-liner: "I\'ll build X with Y." Then proceed. |\n| **Medium** | 2-4 features implied, clear scope | List planned features, wait for "yes" / "sounds good" |\n| **Complex** | Vague/broad request ("dashboard", "manager"), 5+ features | Feature list + data model summary, wait for explicit confirmation |\n\n**How to assess complexity:**\n1. Count the verbs in the request -- each verb implies a feature\n2. Clear nouns ("timer") = lower complexity. Abstract nouns ("dashboard") = higher complexity\n3. When in doubt, go one tier up -- better to briefly confirm than to rebuild\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"confirming"}-->\n```\n\n**Client replies:** User confirms scope, adjusts features, or says "just do it" (blanket confirmation).\n\n---\n\n## Step 4: Read Reference Documents\n\n**Action:** Read the platform specification documents using the `read` tool. Do not skip. Do not rely on memory from previous sessions.\n\n**Tools:** `read`\n\n**Documents to read:**\n\n| Document | What you learn | Path |\n|----------|---------------|------|\n| **SANDBOX_CAPABILITIES.md** | Bridge API surface, capability tiers, resource limits, what you can and cannot build | `openclaw-guides/SANDBOX_CAPABILITIES.md` |\n| **UI_DESIGN_GUIDE.md** | Color system (`var(--ec-*)`), layout patterns, typography, visual refinements, do/don\'t rules | `openclaw-guides/UI_DESIGN_GUIDE.md` |\n| **UX_INTERACTION_GUIDE.md** | Interaction hierarchy, element states, feedback mechanisms, empty/error states, data flow patterns | `openclaw-guides/UX_INTERACTION_GUIDE.md` |\n| **ICON_SPEC.md** | Icon SVG rules, manifest.color selection, design guidelines, checklist | `openclaw-guides/ICON_SPEC.md` |\n\n**Verify:** All four documents loaded into context. You understand the available Bridge APIs, the CSS variable system, and the interaction patterns.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"reading"}-->\n```\n\n**Client replies:** Acknowledgment, optionally with additional guidance.\n\n**MUST:** Read these documents with the `read` tool every time. Fresh context for every build.\n\n**NEVER:** Skip reading because "I already know the spec." Context from previous sessions may be stale or compacted.\n\n---\n\n## Step 5: Plan Architecture\n\n**Action:** Before writing ANY code, define the complete architecture. This step prevents the #1 failure mode: beautiful UI with broken logic.\n\n**What to plan:**\n\n### 5a. Data Model\n\nDefine every piece of state the app manages. Write it out explicitly.\n\n```\nstate = {\n tasks: [{ id, title, done, createdAt }],\n filter: \'all\' | \'active\' | \'completed\',\n editingId: null\n}\n```\n\n### 5b. Feature List (verbs, not nouns)\n\nExhaustive list of what the user can DO. Every verb becomes a function.\n\n```\n- Add a task (with title input)\n- Toggle task completion\n- Delete a task (with confirmation)\n- Filter by all / active / completed\n- Clear all completed tasks\n- Persist tasks across sessions\n```\n\n### 5c. Event Handler Inventory\n\nMap every interactive element to its handler. This is your wiring diagram.\n\n```\n"Add" button -> addTask()\nTask checkbox -> toggleTask(id)\nTask delete icon -> confirmDelete(id)\nFilter tab ("All") -> setFilter(\'all\')\nFilter tab ("Active") -> setFilter(\'active\')\n"Clear completed" -> clearCompleted()\n```\n\n**MUST:** Every interactive element in the plan has a named handler. If you cannot name the handler, you have not thought through the feature.\n\n### 5d. Persistence Strategy\n\nWhat data is saved, where, and when.\n\n```\n- Storage: echoclaw.storage (KV) for simple apps, echoclaw.db (SQLite) for complex data\n- Save trigger: on every mutation\n- Load: on init, with empty-state fallback\n- Schema: define table structure if using SQLite\n```\n\n### 5e. Capability Declaration\n\nList which Bridge capabilities the app needs, referencing SANDBOX_CAPABILITIES.md.\n\n```\ncapabilities: ["storage", "timer", "notification"]\n```\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"planning"}-->\n```\n\n**Client replies:** Acknowledgment.\n\n---\n\n## Step 6: Build the Skeleton\n\n**Action:** Write the HTML structure and CSS layout. Structure only -- no logic yet.\n\n**Tools:** `write`\n\n**What to write:**\n\n1. **`claw.json`** -- App manifest with all required fields:\n - `manifest.id` (kebab-case)\n - `manifest.name` (en + zh-CN)\n - `manifest.version` ("1.0.0")\n - `manifest.color` (brand hex -- the ONLY hardcoded hex allowed)\n - `manifest.icon_svg` (per ICON_SPEC.md: stroke style, `currentColor`, <=4 elements, no `<svg>` wrapper, viewBox 0 0 40 40)\n - `manifest.description` (en + zh-CN)\n - `manifest.capabilities` (from Step 5e)\n - `manifest.shared_libs` (if using Chart.js, D3, etc.)\n - `prompt.seed` (goal, constraints, acceptance_tests)\n\n2. **`index.html`** -- Complete HTML document with:\n - `<!DOCTYPE html>` + `<html>` + `<head>` + `<body>`\n - CSS in `<style>` -- all colors via `var(--ec-*)`, dark theme, responsive\n - HTML structure matching the chosen layout pattern from UI_DESIGN_GUIDE.md\n - Empty `<script>` section (logic comes next)\n\n**CSS rules (from UI_DESIGN_GUIDE.md):**\n- **MUST:** Every color via `var(--ec-*)` CSS variables. Zero hardcoded hex/rgb/rgba/hsl.\n- **MUST:** Design for 1024x768 default, 544x548 minimum safe area.\n- **MUST:** No left-side navigation (shell sidebar occupies that space).\n- **MUST:** Bottom-right 64x64px reserved (host Info button).\n- **MUST:** Custom scrollbar styling.\n- **MUST:** Responsive strategy with `@media` breakpoints.\n- **NEVER:** External fonts, fixed pixel widths on layout containers, `prefers-color-scheme` queries.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->\n```\n\n---\n\n## Step 7: Write Core Logic\n\n**Action:** Implement state management and all data operations. This is the brain of the app.\n\n**Tools:** `edit` (add `<script>` content to index.html)\n\n**What to write:**\n\n1. **State initialization** -- declare the state object from Step 5a\n2. **CRUD functions** -- one function per verb from Step 5b\n3. **Persistence layer** -- load from storage on init, save on every mutation\n4. **Bridge context** -- read theme, locale, device info via `echoclaw.getContext()`\n\n**Build order within this step:**\n```\n1. State object declaration\n2. Storage load/save helpers\n3. Core logic functions (the verbs from Step 5b)\n4. Bridge initialization (context, theme subscription)\n```\n\n**MUST:** Every function from the event handler inventory (Step 5c) is implemented here. A button without a handler is a dead control.\n\n**MUST:** Load persisted data on init with an empty-state fallback. Never assume storage has data.\n\n**NEVER:** Write rendering logic here. That comes in Step 8. Separation keeps the code verifiable.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->\n```\n\n---\n\n## Step 8: Fill in the UI\n\n**Action:** Wire up rendering and internationalization.\n\n**Tools:** `edit`\n\n**What to write:**\n\n1. **Render function** -- `render()` that reads state and updates the DOM\n2. **Event binding** -- attach every handler from Step 5c to its element\n3. **i18n** -- `t(key)` helper function with `en` + `zh-CN` dictionaries\n4. **Locale detection** -- read `echoclaw.getContext().locale`, subscribe to `locale:changed`\n\n**i18n pattern:**\n```javascript\nconst LANG = {\n en: { addTask: \'Add Task\', noTasks: \'No tasks yet\', ... },\n \'zh-CN\': { addTask: \'\u6DFB\u52A0\u4EFB\u52A1\', noTasks: \'\u6682\u65E0\u4EFB\u52A1\', ... }\n};\nlet currentLocale = echoclaw.getContext().locale || \'en\';\nfunction t(key) { return LANG[currentLocale]?.[key] || LANG.en[key] || key; }\n```\n\n**MUST:** Every user-visible string goes through `t()`. No hardcoded English in the HTML.\n\n**MUST:** Every interactive element has hover + focus states (from UX_INTERACTION_GUIDE.md).\n\n**MUST:** Handle all three data states: loading, empty, populated (from UX_INTERACTION_GUIDE.md).\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->\n```\n\n---\n\n## Step 9: Polish\n\n**Action:** Add micro-interactions, animations, and visual refinements that make the app feel alive.\n\n**Tools:** `edit`\n\n**What to add:**\n\n1. **Entry animations** -- staggered fade-in for cards/list items (max 120ms total spread)\n2. **Hover effects** -- `translateY(-1px)` lift on cards, brightness shift on buttons\n3. **Transitions** -- smooth state changes via `transition: all var(--ec-duration-fast) var(--ec-ease-standard)`\n4. **Glass effect (optional)** -- `backdrop-filter: blur(20px)` on 1-2 accent surfaces only, not every card\n5. **Gradient accents** -- subtle 1-2px gradient lines on hero cards\n6. **Focus rings** -- `box-shadow: 0 0 0 2px var(--ec-focus-ring)` on all focusable elements\n7. **Disabled states** -- `opacity: 0.4; cursor: not-allowed; pointer-events: none`\n8. **Loading states** -- spinner or skeleton where async operations occur\n9. **Toast notifications** -- for success/error feedback on user actions\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->\n```\n\n---\n\n## Step 10: Self-Check with Tools\n\n**Action:** Verify the output using `read` and `exec` tools. Not a mental review -- actual tool calls that produce verifiable results.\n\n**Tools:** `read`, `exec`\n\n### 10a. Read Back the Full File\n\n```\nread("apps/<appId>/index.html")\n```\n\nRead the entire file. Confirm it is complete and well-formed. Look for truncation, unclosed tags, missing sections.\n\n### 10b. Run Structural Checks\n\nUse `exec` to run these grep commands:\n\n```bash\n# Empty event handlers (MUST = 0)\ngrep -cn \'onclick=""\\|onchange=""\' index.html\n\n# Leftover stubs (MUST = 0)\ngrep -cn \'TODO\\|FIXME\\|PLACEHOLDER\' index.html\n\n# Debug logs (SHOULD = 0)\ngrep -cn \'console\\.log\' index.html\n\n# Hardcoded colors -- only manifest.color hex is allowed (SHOULD = 0 or only the brand color)\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html\n\n# CSS variable usage (MUST > 0)\ngrep -c \'var(--ec-\' index.html\n\n# i18n function usage (MUST > 0)\ngrep -c "t(\'" index.html\n\n# File size (MUST < 1,048,576 bytes)\nwc -c index.html\n\n# Event handler count vs interactive element count\ngrep -c \'addEventListener\\|onclick\\|onsubmit\\|onchange\\|oninput\' index.html\n```\n\n### 10c. Functional Cross-Check\n\nVerify every interactive element from Step 5c has a corresponding handler in the code:\n\n```bash\n# Count buttons\ngrep -c \'<button\' index.html\n\n# Count handler attachments\ngrep -c \'addEventListener\\|onclick\' index.html\n\n# These numbers should be close. More buttons than handlers = dead controls.\n```\n\n### 10d. Review Against Spec Rules\n\nWhile reading back the file, mentally verify:\n- No left-side navigation\n- Bottom-right 64x64px area is clear\n- Scrollbar styling is present\n- Empty state is handled for every data-driven view\n- Error state is handled for every async operation\n- One primary action per view (using `--ec-agent` background)\n- Destructive actions are behind confirmation\n\n### 10e. Quality Gate Scorecard (Target: >=14/20)\n\nScore each item 0-2. If total < 14, loop back to fix before proceeding.\n\n| # | Check | 0 | 1 | 2 |\n|---|-------|---|---|---|\n| 1 | Hero element | None | Has a large element | Stunning hero with clear visual dominance |\n| 2 | CSS variables | Hard-coded colors present | Mostly variables | 100% `var(--ec-*)`, zero hard-coded |\n| 3 | Dark theme | Broken / unreadable | Acceptable contrast | Polished dark theme with surface hierarchy |\n| 4 | Desktop layout | Narrow single column | Reasonable use of width | Full-width multi-column, uses 1024px canvas |\n| 5 | Hover states | None | Partial coverage | Every interactive element has hover + focus |\n| 6 | Transitions | None | Partial | Globally smooth (`--ec-duration-*` + `--ec-ease-*`) |\n| 7 | Typographic hierarchy | Flat / single size | Some variation | 4+ level scale (hero \u2192 title \u2192 body \u2192 caption) |\n| 8 | Spacing consistency | Arbitrary / random | Mostly consistent | 8px grid via `--ec-space-*` throughout |\n| 9 | State visibility | No loading/empty/error | Partial coverage | All data views handle loading + empty + error |\n| 10 | Self-contained | External dependencies found | Minimal issues | Zero external resources, fully inline |\n\n### 10f. Pre-Output Self-Check (MUST Complete Before Output)\n\nVerify each item. Failure of ANY critical item = automatic rejection by the engine:\n\n- [ ] **Zero hard-coded color literals** in HTML/CSS/JS. Includes hex (`#xxx`), `rgb()`, `rgba()`, `hsl()`, `hsla()`, and named colors. Only `var(--ec-xxx)` allowed. `manifest.color` MAY be hex.\n- [ ] **Agent theme color** uses `var(--ec-agent)` / `var(--ec-agent-text)`. No hard-coded brand color in HTML.\n- [ ] **Zero external URLs.** No `<link href="http...">`, `<script src="http...">`, `@import url("http...")`.\n- [ ] **No left-side navigation.** No class or id named `left-panel`, `left-sidebar`, `left-nav`.\n- [ ] **All capabilities declared and minimized.** Every Bridge API and browser API used is in `manifest.capabilities`.\n- [ ] **Permission flow.** A `requestPermission()` call with denial handling precedes every prompt-level API call.\n- [ ] **Bridge-only networking.** No direct `new WebSocket()`, `fetch()` to external URLs, or `localStorage`.\n- [ ] **Size < 1 MB.** Total HTML verified via `wc -c`.\n- [ ] **Interactivity complete.** Every `<button>` and clickable element has `:hover` CSS and `cursor: pointer`.\n- [ ] **No credential leaks.** No token or password stored via `echoclaw.storage`. Credentials use `echoclaw.auth.setSecret()`.\n- [ ] **Auth profile present** (if using auth APIs). Apps with `echoclaw.auth.*` declare the `auth` capability and `auth_profile`.\n- [ ] **All network requests** use `echoclaw.invoke(\'fetch\', ...)`. No direct fetch/XHR/axios/WebSocket/EventSource.\n- [ ] **All visible text uses `t()`.** Includes `aria-label`, `title`, `placeholder`, empty-state, and error-state copy.\n- [ ] **No dangerous code.** No `eval()`, `new Function()`, unsanitized `innerHTML`, or remote script injection.\n- [ ] **Bottom-right 64x64px** area is clear of interactive elements (host Info button zone).\n- [ ] **Icon generated per ICON_SPEC.md.** `manifest.icon_svg` uses stroke style, `currentColor`, <=4 elements, no `<svg>` wrapper.\n- [ ] **Sharing readiness.** All environment-dependent values are `config_fields`. Sensitive fields are `sensitive: true` and NOT in HTML. `install_contract` declared. `share_profile` populated.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"verifying"}-->\n```\n\n**MUST:** Use the `read` tool to read back the file. Do not verify from memory.\n\n**MUST:** Use the `exec` tool to run grep checks. Do not estimate results mentally.\n\n**NEVER:** Skip this step because "I\'m confident in my output." Self-checks catch real bugs.\n\n---\n\n## Step 11: Fix Issues\n\n**Action:** For every issue found in Step 10, fix it. Then re-verify.\n\n**Tools:** `edit`, `read`, `exec`\n\n**Process:**\n1. Fix the specific issue with `edit` (surgical change, not full rewrite)\n2. `read` the affected section to confirm the fix\n3. Re-run the relevant `exec` check to confirm the count changed\n4. Repeat until all checks pass\n\n**Loop limit:** If you have fixed the same issue 3 times and it keeps regressing, report the problem:\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"error","message":"Stuck in fix loop: [describe issue]"}-->\n```\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"fixing"}-->\n```\n\n---\n\n## Step 12: Describe What You Built\n\n**Action:** Tell the user what you built in natural language. Focus on what they can DO, not how you implemented it.\n\n**Format:**\n```\nI built a [app type] with the following features:\n- [Feature 1]: [one-line description of user-visible behavior]\n- [Feature 2]: [one-line description]\n- Data is saved [persistence strategy]\n- Supports dark theme and en/zh-CN\n```\n\n**Rules:**\n- 3-6 bullet points max\n- Describe user-visible behavior, not code structure\n- Mention persistence strategy (users care about their data)\n- Mention theme and i18n support\n\n**Example:**\n> I built a Kanban board with drag-and-drop card management:\n> - Create and rename columns, add cards with titles and descriptions\n> - Drag cards between columns to change status\n> - Delete cards with a confirmation dialog\n> - All data persists locally via Bridge storage\n> - Supports dark theme and en/zh-CN\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"describing"}-->\n```\n\n---\n\n## Step 13: Await User Confirmation\n\n**Action:** Wait for the user to confirm they are satisfied before finalizing.\n\n**Emit:**\n```\n<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"awaiting_confirmation"}-->\n```\n\n**Client replies:** User confirms ("looks good", "ship it", "done") or requests changes.\n\n**If user requests changes:** Loop back to the appropriate step. Minor tweaks go to Step 9 (polish). Feature additions go to Step 7 (logic). The update follows the same verify-fix cycle (Steps 10-11).\n\n**NEVER:** Send `CLAW_APP_READY` without user confirmation. The user decides when it is done.\n\n---\n\n## Step 14: Deliver\n\n**Action:** Write `session.json` and send the completion marker.\n\n**Tools:** `write`\n\n**Write `session.json`:**\n```json\n{\n "appId": "<id>",\n "createdAt": "<ISO timestamp>",\n "lastUpdated": "<ISO timestamp>",\n "entries": [\n {\n "timestamp": "<ISO>",\n "role": "user",\n "summary": "<summarized user request -- never raw message or PII>",\n "op": "create"\n },\n {\n "timestamp": "<ISO>",\n "role": "openclaw",\n "summary": "<what was built, key design decisions>",\n "op": "create"\n }\n ]\n}\n```\n\n**Emit:**\n```\n<!--CLAW_APP_READY:{"appId":"<id>","op":"create","contentHash":"<sha256 of index.html>","summary":"<brief description>"}-->\n```\n\n**MUST:** The `contentHash` matches the actual file on disk.\n\n---\n\n## Build Order Summary\n\n```\nStep 1 received Acknowledge task\nStep 2 suggesting Offer 3-5 enhancements\nStep 3 confirming Confirm scope based on complexity\nStep 4 reading Read SANDBOX_CAPABILITIES + UI_DESIGN_GUIDE + UX_INTERACTION_GUIDE\nStep 5 planning Data model + feature list + handler inventory + persistence\nStep 6 building claw.json + HTML skeleton + CSS\nStep 7 building State management + core logic + persistence\nStep 8 building Render function + event binding + i18n\nStep 9 building Animations + hover + micro-interactions\nStep 10 verifying read-back + exec(grep) self-checks\nStep 11 fixing Fix issues, re-verify\nStep 12 describing Natural language summary for the user\nStep 13 awaiting_confirmation Wait for user to confirm\nStep 14 CLAW_APP_READY Write session.json, deliver\n```\n\n---\n\n## Why This Order Matters\n\n### Logic Before UI\n\nThe #1 failure mode of AI-generated apps is beautiful CSS with broken JavaScript. Building in layers prevents this:\n\n- **Step 7 (logic) before Step 8 (UI)** forces the data model to exist before the render function references it\n- **Step 5c (handler inventory) before Step 7** ensures every interactive element has a planned handler before code is written\n- **Step 10 (verify) checks handler counts** -- dead controls are caught mechanically, not by hope\n\n### Self-Checks Use Tools\n\n"Carefully review your output" does not work. You are an LLM -- you will hallucinate that the output is correct. Instead:\n\n- `read` the file back -- see what is actually on disk\n- `exec(grep)` for empty handlers -- get a number, not a feeling\n- `exec(wc)` for file size -- know it is under the limit\n\n### Bidirectional Communication\n\nEvery step emits a status. The client can:\n- Acknowledge and let you continue\n- Provide additional guidance\n- Tell you to read a specific document\n- Abort the task\n\nThis is a conversation, not a one-shot generation.\n\n---\n\n## Core Principles\n\n1. **Think like the human using the app.** Every button must do something. Every action must give feedback. Every data view must handle empty state.\n\n2. **Red lines are hard. Everything else is free.** The CSS variable system, the no-left-sidebar rule, the i18n requirement -- these are non-negotiable. Your visual design, code architecture, animation choices, and development pace are entirely yours.\n\n3. **Self-checks are mechanical, not mental.** Use `read` and `exec`. Count handlers. Grep for stubs. Measure file size. Numbers do not lie.\n\n4. **Time is not a constraint.** Take 15 tool-call rounds if the app needs it. A working app delivered in 10 minutes is worth more than a broken app delivered in 30 seconds.\n\n5. **Describe before delivering.** The user should know what they are getting before they see it. Surprises are for birthdays, not software.\n\n---\n\n*Workflow: Create New App -- For OpenClaw system prompt*\n';
|
|
8417
|
+
var WORKFLOW_CREATE_default = '---\nname: echoclaw-create\ndescription: >\n Create a brand-new ClawApp from a user request.\n Use when (1) user asks to create, build, or make a new app,\n (2) user describes a tool/utility they want ("make me a timer"),\n (3) user shares a concept and expects a working deliverable.\n NOT for updating existing apps (use echoclaw-update),\n adapting shared templates (use echoclaw-adapt),\n or Studio live-preview mode (use echoclaw-studio).\n Produces: claw.json manifest + index.html (single-file app) + session.json history.\n---\n\n# Workflow: Create New App\n\n> You are a long-thinking Agent with `read`, `write`, `edit`, `exec` tools and unlimited rounds.\n> Quality over speed. A 10-minute build that works beats a 30-second output that does not.\n\nRed lines are marked **MUST** / **NEVER**. Everything else is creative freedom.\nFor self-check details see `references/CHECKLIST.md`.\nFor architecture templates see `references/ARCHITECTURE_PATTERNS.md`.\n\n---\n\n## Step 1: Receive the Request\n\nParse the user message. Generate a kebab-case `appId` from semantics (e.g. `expense-tracker`).\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"received"}-->`\n\n**MUST:** Immediately continue to Step 2 in the SAME response. The ACK is a progress signal, not a stopping point.\n\n---\n\n## Step 2: Understand and Expand (MANDATORY)\n\nSuggest 3-5 enhancements the user might not have thought of.\n\n- Each suggestion ONE line, <15 words\n- Cover different dimensions: features, persistence, UX, accessibility\n- Only suggest things you can actually build (no external APIs, no framework deps)\n- Frame as options: "Want me to add X?" -- not "You should add X"\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"suggesting"}-->`\n\n**MUST:** If user says "just do it" / "go ahead" -- start building immediately. Zero follow-up questions.\n**NEVER:** Skip this step and jump straight to building.\n**NEVER:** Write multi-paragraph proposals \u2014 each suggestion is ONE line only.\n**NEVER:** Ask clarifying questions \u2014 suggest, don\'t interrogate.\n**NEVER:** Offer more than 5 suggestions.\n\n---\n\n## Step 3: Confirm Scope\n\nBased on complexity, confirm before investing time.\n\n| Complexity | Signals | Confirmation |\n|------------|---------|-------------|\n| Simple | Clear noun ("timer"), 1-2 features | One-liner: "I\'ll build X." Proceed. |\n| Medium | 2-4 features, clear scope | List features, wait for "yes" |\n| Complex | Vague request ("dashboard"), 5+ features | Feature list + data model, wait for explicit OK |\n\n**If user already said "go ahead" in Step 2, skip this step.**\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"confirming"}-->`\n\n---\n\n## Step 4: Read Reference Documents\n\nUse `read` tool to load all four platform specs. Do not rely on memory from previous sessions.\n\n| Document | What you learn |\n|----------|---------------|\n| `SANDBOX_CAPABILITIES.md` | Bridge API surface, capability tiers, resource limits |\n| `UI_DESIGN_GUIDE.md` | Color system (`var(--ec-*)`), layout patterns, typography |\n| `UX_INTERACTION_GUIDE.md` | Interaction hierarchy, states, feedback, data flow |\n| `ICON_SPEC.md` | SVG icon rules, manifest.color, design checklist |\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"reading"}-->`\n\n**MUST:** `read` these docs every time. Fresh context for every build.\n**NEVER:** Skip because "I already know the spec."\n\n---\n\n## Step 5: Plan Architecture\n\nBefore writing ANY code, define complete architecture. This prevents the #1 failure: beautiful UI with broken logic.\n\nPlan these five aspects (see `references/ARCHITECTURE_PATTERNS.md` for templates):\n\n1. **Data Model** -- every piece of state, written out explicitly\n2. **Feature List** -- exhaustive list of user verbs; each verb becomes a function\n3. **Event Handler Inventory** -- map every interactive element to a named handler\n4. **Persistence Strategy** -- what is saved, where (`echoclaw.storage` vs `echoclaw.db`), when\n5. **Capability Declaration** -- which Bridge capabilities the app needs\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"planning"}-->`\n\n**MUST:** Every interactive element has a named handler. If you cannot name the handler, the feature is not thought through.\n\n---\n\n## Step 6: Build the Skeleton\n\nWrite HTML structure and CSS layout. Structure only -- no logic yet.\n\n**Tools:** `write`\n\n**Outputs:**\n1. **`claw.json`** -- manifest with: `id`, `name` (en + zh-CN), `version`, `color` (the ONLY hardcoded hex allowed), `icon_svg` (per ICON_SPEC.md), `description`, `capabilities`, `shared_libs`, `prompt.seed`\n - `prompt.seed.goal` \u2014 one sentence describing what the app does for the user\n - `prompt.seed.constraints` \u2014 list of platform rules this app must respect (e.g. no external APIs, CSS variables only)\n - `prompt.seed.acceptance_tests` \u2014 2-4 checkable statements a human can verify to confirm the app works correctly\n2. **`index.html`** -- `<!DOCTYPE html>`, CSS in `<style>`, HTML body, empty `<script>`\n\n**CSS red lines:**\n- **MUST:** All colors via `var(--ec-*)`. Zero hardcoded hex/rgb/rgba/hsl.\n- **MUST:** Design for 1024x768 default, 544x548 minimum safe area.\n- **MUST:** No left-side navigation. Bottom-right 64x64px reserved.\n- **MUST:** Custom scrollbar styling. Responsive `@media` breakpoints.\n- **NEVER:** External fonts, fixed pixel widths on layout containers, `prefers-color-scheme`.\n\nSee `references/CHECKLIST.md` for the full CSS/layout checklist.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->`\n\n---\n\n## Step 7: Write Core Logic\n\nImplement state management and all data operations. The brain of the app.\n\n**Tools:** `edit` (add `<script>` content)\n\nBuild order:\n1. State object declaration (from Step 5)\n2. Storage load/save helpers\n3. Core logic functions (every verb from Step 5)\n4. Bridge initialization (`echoclaw.getContext()`, theme subscription)\n\n**MUST:** Every function from the handler inventory (Step 5) is implemented. A button without a handler is a dead control.\n**MUST:** Load persisted data on init with empty-state fallback.\n**NEVER:** Write rendering logic here. That comes in Step 8.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->`\n\n---\n\n## Step 8: Fill in the UI\n\nWire rendering, event binding, and internationalization.\n\n**Tools:** `edit`\n\n1. **`render()`** -- reads state, updates DOM\n2. **Event binding** -- attach every handler from Step 5 to its element\n3. **`t(key)`** -- i18n helper with `en` + `zh-CN` dictionaries. Core structure:\n ```js\n const i18n = { en: { add: \'Add\', empty: \'Nothing here yet\' }, \'zh-CN\': { add: \'\u6DFB\u52A0\', empty: \'\u6682\u65E0\u5185\u5BB9\' } };\n const locale = echoclaw.getContext().locale || \'en\';\n const t = key => (i18n[locale] || i18n[\'en\'])[key] || key;\n ```\n4. **Locale detection** -- `echoclaw.getContext().locale`, subscribe to `locale:changed`\n\n**MUST:** Every user-visible string goes through `t()`. Includes `aria-label`, `title`, `placeholder`, empty-state, error-state copy.\n**MUST:** Every interactive element has hover + focus states.\n**MUST:** Handle all three data states: loading, empty, populated.\n\nSee `references/ARCHITECTURE_PATTERNS.md` for i18n pattern template.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->`\n\n---\n\n## Step 9: Polish\n\nAdd micro-interactions and visual refinements.\n\n- Entry animations (staggered fade-in, max 120ms spread)\n- Hover effects (`translateY(-1px)`, brightness shifts)\n- Transitions (`var(--ec-duration-fast)` + `var(--ec-ease-standard)`)\n- Focus rings (`box-shadow: 0 0 0 2px var(--ec-focus-ring)`)\n- Disabled states (`opacity: 0.4; cursor: not-allowed; pointer-events: none`)\n- Loading states (spinner/skeleton for async ops)\n- Toast notifications (success/error feedback)\n- Optional: glass effect (`backdrop-filter: blur(20px)`) on 1-2 accent surfaces\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"building"}-->`\n\n---\n\n## Step 10: Self-Check with Tools\n\nVerify output using `read` and `exec`. Not a mental review -- actual tool calls.\n\n**Tools:** `read`, `exec`\n\n1. `read` back the full file. Check for truncation, unclosed tags, missing sections.\n2. Run structural grep checks (see `references/CHECKLIST.md` Section A for exact commands).\n3. Cross-check: button count vs handler count. More buttons than handlers = dead controls.\n4. Review against spec rules (no left nav, bottom-right clear, scrollbar styled, empty/error states).\n5. Score the Quality Gate (target >= 14/20, see `references/CHECKLIST.md` Section B).\n6. Run Pre-Output checklist (see `references/CHECKLIST.md` Section C). ANY critical failure = fix before output.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"verifying"}-->`\n\n**MUST:** Use `read` to read back the file. Do not verify from memory.\n**MUST:** Use `exec` to run grep checks. Do not estimate results mentally.\n**NEVER:** Skip this step because "I\'m confident in my output."\n\n---\n\n## Step 11: Fix Issues\n\nFor every issue found in Step 10, fix with `edit`, `read` back, re-run the check.\n\n**Loop limit:** 3 attempts on the same issue. If stuck, report:\n`<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"error","message":"Stuck in fix loop: [describe]"}-->`\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"fixing"}-->`\n\n---\n\n## Step 12: Describe What You Built\n\nTell the user what you built. Focus on what they can DO, not implementation.\n\n- 3-6 bullet points max\n- Describe user-visible behavior, not code structure\n- Mention persistence strategy, theme support, i18n\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"describing"}-->`\n\n---\n\n## Step 13: Await User Confirmation\n\nWait for the user to confirm satisfaction.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"create","status":"awaiting_confirmation"}-->`\n\nIf user requests changes: minor tweaks -> Step 9, feature additions -> Step 7, then re-verify (Steps 10-11).\n\n**NEVER:** Send `CLAW_APP_READY` without user confirmation.\n\n---\n\n## Step 14: Deliver\n\nWrite `session.json` (appId, timestamps, summarized entries -- never raw messages or PII).\n\n**Emit:** `<!--CLAW_APP_READY:{"appId":"<id>","op":"create","contentHash":"<sha256 of index.html>","summary":"<brief>"}-->`\n\n**MUST:** `contentHash` matches the actual file on disk.\n\n---\n\n## Build Order Summary\n\n```\nStep 1 received Acknowledge task\nStep 2 suggesting Offer 3-5 enhancements\nStep 3 confirming Confirm scope (skip if user said "go ahead")\nStep 4 reading Read 4 platform spec docs\nStep 5 planning Data model + features + handlers + persistence + capabilities\nStep 6 building claw.json + HTML skeleton + CSS\nStep 7 building State management + core logic + persistence\nStep 8 building Render + event binding + i18n\nStep 9 building Animations + hover + polish\nStep 10 verifying read-back + exec(grep) self-checks\nStep 11 fixing Fix issues, re-verify\nStep 12 describing Natural language summary\nStep 13 awaiting_confirmation Wait for user OK\nStep 14 CLAW_APP_READY Write session.json, deliver\n```\n\n---\n\n## Core Principles\n\n1. **Think like the user.** Every button must do something. Every action must give feedback. Every data view must handle empty state.\n2. **Red lines are hard. Everything else is free.** CSS variables, no-left-sidebar, i18n -- non-negotiable. Design, architecture, animation -- yours.\n3. **Self-checks are mechanical, not mental.** Use `read` and `exec`. Count handlers. Grep for stubs. Numbers do not lie.\n4. **Logic before UI.** Step 7 before Step 8 forces the data model to exist before the render function references it. Step 5 handler inventory before Step 7 ensures every element has a planned handler.\n5. **Time is not a constraint.** Take 15 tool-call rounds if needed. A working app beats a fast broken one.\n';
|
|
8416
8418
|
|
|
8417
8419
|
// ../claw-engine/src/authoring/openclaw-guides/WORKFLOW_ADAPT.md
|
|
8418
|
-
var WORKFLOW_ADAPT_default = '---\nname: echoclaw-adapt\ndescription: Use when
|
|
8420
|
+
var WORKFLOW_ADAPT_default = '---\nname: echoclaw-adapt\ndescription: >\n Use when: A user installs a shared ClawApp template \u2014 you must fill config_fields\n so the app works in the new user\'s environment.\n NOT for: Creating or modifying app HTML; use WORKFLOW_CREATE or WORKFLOW_UPDATE instead.\n NOT for: Apps where install_contract.mode !== "ready" \u2014 escalate to client.\n---\n\n# Workflow: Adapt (Install Shared App)\n\n> Scenario: User installs a shared app. HTML is immutable \u2014 you only resolve config_fields.\n> Complexity: Low (1-3 min, 3-8 tool calls)\n\n**Bidirectional loop:** Every step emits a CLAW_STEP status. Wait for client ACK before\nproceeding. CLAW_STEP is a progress signal \u2014 NEVER stop and wait after emitting one.\n\n---\n\n## Steps\n\n### Step 1 \u2014 Acknowledge Receipt\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "received", op: "adapt")` \u2014 MUST emit immediately.\n\n1. Parse task frontmatter: extract `appId` (provided by client \u2014 do NOT generate a new one)\n and task parameters.\n2. Emit acknowledgment before reading any files.\n3. Proceed immediately to Step 2 \u2014 do not wait.\n\n**Client reply:** ACK. May include `next_doc` pointing to the app package location.\n\n---\n\n### Step 2 \u2014 Read the App Package\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n**Reads:** `apps/{appId}/input/package.claw.json`, `apps/{appId}/input/index.html`\n\n1. Read `claw.json` \u2014 extract `manifest`, `config_fields[]`, `install_contract`.\n2. Read `index.html` \u2014 store its content as the immutable baseline.\n3. Verify `install_contract.mode === "ready"` (confirms HTML is immutable).\n\n**Self-check:** `config_fields` array is non-empty; `install_contract` present with\n`mode: "ready"`; `index.html` is non-empty and well-formed.\n\n**Client reply:** ACK. May include `next_doc: "SYSTEM_CAPABILITIES.md"`.\n\n---\n\n### Step 3 \u2014 Read Reference Docs\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n**Reads:** `SYSTEM_CAPABILITIES.md` (manifest format), `SANDBOX_CAPABILITIES.md` (field\nresolution rules).\n\n1. Understand valid field types: `url`, `email`, `number`, `string`, `secret`.\n2. Cross-reference each `config_field` type against the spec; note `auto_resolve` strategies\n declared (`from_env`, `from_gateway`, `from_context`).\n\n**Client reply:** ACK.\n\n---\n\n### Step 4 \u2014 Resolve Configuration Values\n\n**Tools:** `read`, `exec`\n**Status:** `CLAW_STEP(status: "building")`\n\nFor each field in `config_fields[]`, apply the first matching resolution strategy:\n\n| Strategy | Action |\n|----------|--------|\n| `auto_resolve` declared | Attempt automatic resolution (`from_env`, `from_gateway`, `from_context`) |\n| `sensitive: true` | Store via `echoclaw.auth.setSecret()` \u2014 use `{{SECRET:key_name}}` placeholder, NEVER embed raw value |\n| Manual (user-provided) | Use value from task context or user message |\n| Default value exists | Use declared default if no user override |\n\n**Self-check (exec):**\n```bash\ngrep -c \'{{.*}}\' claw.json # Must = 0 for resolved non-sensitive fields\n```\nEvery non-sensitive field must have a non-empty, non-placeholder value. Sensitive fields\nmust use `"value": "{{SECRET:key_name}}"` format. Field values must match declared types.\n\n**Client reply:** ACK.\n\n---\n\n### Step 5 \u2014 Verify Immutability + Completeness\n\n**Tools:** `read`, `exec`\n**Status:** `CLAW_STEP(status: "verifying")`\n\nRun all three checks. Any failure requires fix and re-run before proceeding.\n\n```bash\n# 5a. HTML unchanged \u2014 must produce no output\ndiff <(cat input/index.html) <(cat index.html)\n\n# 5b. No unresolved placeholders or empty values in claw.json\ngrep -cn \'{{.*}}\' claw.json # Must = 0\ngrep -cn \'"value":\\s*""\' claw.json # Must = 0\n\n# 5c. No debug artifacts\ngrep -cn \'TODO\\|FIXME\\|PLACEHOLDER\' claw.json # Must = 0\n```\n\nIf HTML diff produces output: you violated immutability \u2014 revert and re-resolve.\nAfter 3 failed rounds: emit `CLAW_STEP(status: "error", message: "Stuck in verify loop")`.\n\n**Client reply:** ACK.\n\n---\n\n### Step 6 \u2014 Describe Configuration Result\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "describing")` \u2014 MUST emit.\n\n**NEVER send CLAW_APP_READY without this step first.**\n\nSummarize resolved configuration in natural language:\n```\nI configured the app with the following settings:\n- [field_name]: [resolved_value] (auto-resolved / user-provided / default)\n- [field_name]: stored securely (sensitive \u2014 not embedded in HTML)\nAll [N] config fields resolved successfully.\n```\n\nRules: one line per field; sensitive fields show "stored securely" only; state resolution method.\n\n**Client reply:** User reviews config summary.\n\n---\n\n### Step 7 \u2014 Write Session Metadata\n\n**Tools:** `write`\n**Status:** `CLAW_STEP(status: "building")`\n\nWrite `session.json`:\n```json\n{\n "appId": "<id>",\n "createdAt": "<ISO timestamp>",\n "lastUpdated": "<ISO timestamp>",\n "entries": [{\n "timestamp": "<ISO>",\n "role": "openclaw",\n "summary": "Adapted app for new user. Resolved config values: [list fields].",\n "op": "adapt",\n "config_resolved": ["field1", "field2"]\n }]\n}\n```\n\n**Client reply:** ACK.\n\n---\n\n### Step 8 \u2014 User Confirmation + Delivery\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "awaiting_confirmation")` then `CLAW_APP_READY`\n\n**NEVER send `CLAW_APP_READY` without explicit user confirmation.**\n\n- **User confirms** ("yes", "looks good", "install it"): emit `CLAW_APP_READY` with `op: "adapt"`.\n- **User requests change** ("change X to Y"): loop back to Step 4, then re-verify (Step 5)\n and re-describe (Step 6).\n- **User cancels**: stop immediately \u2014 no `CLAW_APP_READY`.\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"adapt","contentHash":"<sha256 of index.html>","summary":"Configured 3 fields: API endpoint, refresh interval, theme"}-->\n```\n\n`contentHash` MUST be the SHA-256 hash of `index.html` on disk.\n\n---\n\n## Capability Boundary\n\n| Can do | Cannot do |\n|--------|-----------|\n| Fill config_fields with values | Modify HTML structure or content |\n| Resolve auto_resolve strategies (from_env, from_gateway) | Access actual gateway at runtime |\n| Validate field types (url, email, number) | Verify external service connectivity |\n| Detect missing required fields | Create new config_fields not in the original |\n| Store sensitive values via secretRef | Inject sensitive values into HTML |\n\n---\n\n## Error Recovery\n\n| Situation | Action |\n|-----------|--------|\n| `config_fields` is empty | `CLAW_STEP(status: "error", message: "No config_fields to resolve")` |\n| `install_contract.mode !== "ready"` | Not an adapt task \u2014 escalate to client |\n| Verify loop stuck (> 3 rounds) | `CLAW_STEP(status: "error", message: "Stuck in fix loop")` |\n| User cancels mid-flow | Stop immediately, no `CLAW_APP_READY` |\n| Disconnect during resolution | On reconnect, re-read workspace, resume from last completed step |\n\n---\n\n## Document References\n\n| Step | Document | Section |\n|------|----------|---------|\n| 2 | App package | `claw.json`, `index.html` |\n| 3 | `SYSTEM_CAPABILITIES.md` | Manifest format, workspace structure |\n| 4 | `SANDBOX_CAPABILITIES.md` | Config field resolution rules |\n';
|
|
8419
8421
|
|
|
8420
8422
|
// ../claw-engine/src/authoring/openclaw-guides/WORKFLOW_UPDATE.md
|
|
8421
|
-
var WORKFLOW_UPDATE_default = '---\nname: echoclaw-update\ndescription: Use when modifying, editing, or updating an existing ClawApp\n---\n\n# Workflow: Update (Modify Existing App)\n\n> Scenario: A user wants to modify an installed app \u2014 add a feature, change layout, fix a bug.\n> Complexity: Medium (3-15 minutes, 10-40 tool calls)\n\n---\n\n## Overview\n\nThe user has a working app and wants changes. Your job is to understand what exists, apply surgical modifications, and verify nothing breaks. The cardinal rule: **read first, edit second, verify third.**\n\n**Bidirectional loop:** Every step emits a CLAW_STEP status. The client acknowledges and may provide `next_doc`, `guidance`, or `abort`. You wait for the client\'s reply before proceeding to the next step.\n\n---\n\n## Step-by-Step Workflow\n\n### Step 1: Acknowledge Receipt\n\n**Trigger:** You receive an update/modify task.\n**Tools:** None\n**Status:** `CLAW_STEP(status: "received", op: "update")` \u2014 **MUST emit.**\n\nAction:\n- Parse the task to extract `appId` and the user\'s change request. The `appId` comes from the existing app \u2014 provided by the client. Do NOT generate a new one.\n- Emit acknowledgment immediately.\n- **Then immediately proceed to Step 2.** The CLAW_STEP marker is a progress signal \u2014 NOT a stopping point. NEVER send just an acknowledgment and wait.\n\n**Client reply:** ACK. May include `next_doc` pointing to the app directory.\n\n---\n\n### Step 2: Read the Existing App\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n**Reads:** `apps/{appId}/` directory \u2014 all three core files.\n\nAction \u2014 read in this order:\n1. `claw.json` \u2014 manifest, config_fields, install_contract, prompt.\n2. `index.html` \u2014 the full current HTML (read the entire file, not just sections).\n3. `session.json` \u2014 conversation history and design decisions.\n\n**Self-check:**\n- All 3 files exist and are non-empty.\n- `claw.json` parses as valid JSON.\n- `index.html` is well-formed HTML.\n\n**Client reply:** ACK.\n\n---\n\n### Step 3: Understand Design History\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n**Reads:** `session.json` entries, DevLog if present.\n\nBefore making any change, you MUST understand:\n\n| Question | Source |\n|----------|--------|\n| What does the app do? | `session.json` \u2192 `entries[0].summary` (original intent) |\n| Why is it built this way? | `session.json` \u2192 entries with design decisions |\n| What has changed before? | `session.json` \u2192 entries with `op: "update"` |\n| What must NOT change? | Everything the user didn\'t ask to change |\n\nCapture a mental model of:\n- **Preserved features:** List every existing interactive element and its handler.\n- **Change target:** Identify exactly which part of the code the user\'s request affects.\n\n**Baseline handler count (exec tool):**\n```bash\n# Count handlers BEFORE changes \u2014 this is your regression baseline\ngrep -c \'addEventListener\\|onclick\\|onchange\\|onsubmit\\|onkeydown\\|onkeyup\' index.html\n# Record this number. After changes, it must be >= this value.\n```\n\n**Client reply:** ACK.\n\n---\n\n### Step 4: Assess Complexity\n\n**Tools:** None\n**Status:** No separate CLAW_STEP \u2014 this is an internal decision point.\n\nApply the complexity-based confirmation rules:\n\n| Tier | Signal | Action |\n|------|--------|--------|\n| **Trivial** | "change color to blue", "fix typo" | Skip confirmation \u2014 just do it |\n| **Simple** | "add a reset button" | Brief one-liner: "I\'ll add a reset button below the controls." Then proceed. |\n| **Medium** | "add chart view for the data" | Confirm scope: "I\'ll add a bar chart showing monthly totals below the table. The existing table stays unchanged. Sound good?" Wait for yes. |\n| **Complex** | "redesign the entire layout" | Detailed feature list + what will be preserved + what will change. Wait for explicit confirmation. |\n\nIf the user says "just do it" at any complexity level, proceed immediately.\n\n**Client reply:** User confirms (for Medium/Complex) or implicit proceed (for Trivial/Simple).\n\n---\n\n### Step 5: Read Relevant Specs\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n\nRead only the specs relevant to the requested change:\n\n| Change type | Read |\n|-------------|------|\n| Visual / layout change | `UI_DESIGN_GUIDE.md` \u2014 CSS variables, viewport, color rules |\n| New feature with Bridge API | `SANDBOX_CAPABILITIES.md` \u2014 Bridge API methods, signatures, limits |\n| UI interaction change | `UX_INTERACTION_GUIDE.md` \u2014 interaction patterns |\n| Any change | `SYSTEM_CAPABILITIES.md` \u2014 general rules (if not already in context) |\n\nDo NOT read all specs for a trivial change. Match reading depth to change complexity.\n\n**Client reply:** ACK.\n\n---\n\n### Step 6: Apply Changes with Edit\n\n**Tools:** `edit` (preferred), `write` (only for major rewrites)\n**Status:** `CLAW_STEP(status: "building")`\n\n**Golden rule: prefer `edit` over `write`.**\n\n| Situation | Tool | Why |\n|-----------|------|-----|\n| Adding a button | `edit` | Surgical insert, everything else preserved |\n| Changing a color | `edit` | Single-line diff |\n| Adding a new feature section | `edit` | Insert at the right location |\n| Adding event handler for new element | `edit` | Append to JS section |\n| Complete redesign (user explicitly asked) | `write` | Full overwrite \u2014 then re-verify EVERYTHING |\n\nApply changes incrementally:\n1. Modify HTML structure first (if needed).\n2. Add/modify CSS next.\n3. Add/modify JS logic last.\n4. Each edit should be a self-contained change \u2014 don\'t leave half-wired elements.\n\n**Client reply:** ACK.\n\n---\n\n### Step 7: Self-Check (Verification)\n\n**Tools:** `read`, `exec`\n**Status:** `CLAW_STEP(status: "verifying")`\n\nRun the full self-check suite. Every check uses tools \u2014 no mental review.\n\n**7a. Regression check \u2014 handler count:**\n```bash\n# Handler count AFTER changes \u2014 must be >= baseline from Step 3\ngrep -c \'addEventListener\\|onclick\\|onchange\\|onsubmit\\|onkeydown\\|onkeyup\' index.html\n# Compare with baseline. If lower, you broke something.\n```\n\n**7b. No stubs or debug artifacts:**\n```bash\ngrep -cn \'TODO\\|FIXME\\|PLACEHOLDER\' index.html # Must = 0\ngrep -cn \'console\\.log\' index.html # Should = 0\n```\n\n**7c. No empty handlers:**\n```bash\ngrep -cn \'onclick=""\\|onchange=""\' index.html # Must = 0\n```\n\n**7d. Color compliance:**\n```bash\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # Hardcoded colors (only manifest.color allowed)\ngrep -c \'var(--ec-\' index.html # CSS variable usage (should be > 0)\n```\n\n**7e. Structural integrity:**\n```bash\nwc -c index.html # Must be < 500KB\n```\n\n**7f. Functional check (for non-trivial changes):**\n```bash\nnode -e "const h=require(\'fs\').readFileSync(\'index.html\',\'utf8\'); \\\n const btns=(h.match(/<button/g)||[]).length; \\\n const handlers=(h.match(/addEventListener|onclick/g)||[]).length; \\\n console.log(\'buttons:\',btns,\'handlers:\',handlers); \\\n if(btns>handlers) process.exit(1)"\n```\n\n**If any check fails:** Fix the issue with `edit`, re-run checks. Track fix rounds.\n\n**Client reply:** ACK.\n\n---\n\n### Step 8: Fix Loop (If Needed)\n\n**Tools:** `edit`, `read`, `exec`\n**Status:** `CLAW_STEP(status: "fixing")`\n\nIf Step 7 found issues:\n1. Fix the specific issue with `edit`.\n2. Re-read the affected section with `read`.\n3. Re-run the specific check with `exec`.\n4. Repeat until the check passes.\n\n**Circuit breaker:** After 3 fix rounds, emit `CLAW_STEP(status: "error", message: "Stuck in fix loop after 3 rounds")`. The client can respond with `fallback` (accept with known issues), `retry` (fresh context hint), or `abort`.\n\n---\n\n### Step 9: Update Metadata\n\n**Tools:** `edit`\n**Status:** `CLAW_STEP(status: "building")`\n\nAfter code changes pass verification, update the metadata files:\n\n1. **`claw.json`** \u2014 bump version if the manifest has one; update `manifest.description` if the app\'s purpose changed.\n2. **`session.json`** \u2014 append a new entry:\n ```json\n {\n "timestamp": "2026-03-22T14:00:00Z",\n "role": "openclaw",\n "summary": "Added chart view for monthly data. Preserved existing table and export features.",\n "op": "update"\n }\n ```\n\n**Client reply:** ACK.\n\n---\n\n### Step 10: Describe Changes (MANDATORY)\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "describing")` \u2014 **MUST emit.**\n\n**NEVER send CLAW_APP_READY without describing what you changed first.**\n\nSummarize what changed and what was preserved. Format:\n\n```\nChanges made:\n- [What changed]: [user-visible description]\n- [What changed]: [user-visible description]\n\nPreserved:\n- [Feature X] \u2014 unchanged\n- [Feature Y] \u2014 unchanged\n```\n\nRules:\n- 2-4 bullet points for changes, 1-3 for preserved items.\n- Focus on user-visible behavior, not code details.\n- Mention data persistence if relevant ("Your existing data is preserved").\n\n**Client reply:** User reviews the summary.\n\n---\n\n### Step 11: User Confirmation + Delivery (MANDATORY \u2014 DO NOT SKIP)\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "awaiting_confirmation")` then `CLAW_APP_READY`\n\n**MUST** wait for the user to confirm satisfaction. **NEVER** send `CLAW_APP_READY` without explicit user confirmation.\n\n- **User confirms:** Emit `CLAW_APP_READY` with `op: "update"`.\n- **User requests another change:** Loop back to Step 4 (assess complexity of the new request).\n- **User says "undo" / "revert":** Emit `CLAW_STEP(status: "error", message: "User requested revert")` \u2014 the client handles rollback.\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"update","contentHash":"<sha256 of index.html>","summary":"Added chart view, preserved table and export"}-->\n```\n\n**MUST:** The `contentHash` is the SHA-256 hash of the final `index.html` file on disk.\n\n---\n\n## Capability Boundary\n\n| Can do | Cannot do |\n|--------|-----------|\n| Read and understand existing code | Run the app in a browser to test |\n| Surgical edits via `edit` tool | Guarantee runtime behavior matches intent |\n| Verify structural integrity via grep | Test user interaction flows |\n| Preserve existing features by reading first | Detect CSS rendering bugs |\n| Bump version and update session.json | Rollback to previous version (client\'s job) |\n\n---\n\n## Error Recovery\n\n| Situation | Action |\n|-----------|--------|\n| Missing files (no index.html) | Emit `CLAW_STEP(status: "error", message: "App files not found")` |\n| session.json missing | Proceed without history \u2014 note this in the description |\n| Verify loop stuck (> 3 rounds) | Emit `CLAW_STEP(status: "error")` with details |\n| User cancels mid-edit | Stop immediately, no `CLAW_APP_READY` |\n| Disconnect during edit | On reconnect, re-read workspace, compare with session.json to determine state |\n| Handler count decreased | Treat as regression \u2014 fix before proceeding |\n\n---\n\n## Sub-Workflow: Comply (Fix Review Issues)\n\nWhen the engine returns `validation_failed` or a review file appears in `review/`, enter the Comply sub-workflow. This is a focused fix-and-resubmit cycle \u2014 NOT a full rebuild.\n\n### Comply Step 1: Read the Review\n\n**Tools:** `read`\n\nRead the review file (`review/round-N.review.md`) carefully. It contains three sections:\n- **"What\'s Correct (DO NOT TOUCH)"** \u2014 preserve these unless a critical issue requires changing them.\n- **"Critical Issues"** \u2014 MUST fix. Each includes the current value and expected value as locators.\n- **"Warnings"** \u2014 fix where possible, but do not break correct items to fix warnings.\n\n### Comply Step 2: Read the Current Output\n\n**Tools:** `read`\n\nRead the current app from `input/` (the version that failed review). Understand the full context before making changes.\n\n### Comply Step 3: Apply Surgical Fixes\n\n**Tools:** `edit`\n\nFix ONLY the listed critical issues:\n1. Use `edit` for each fix \u2014 surgical changes, not rewrites.\n2. Use the `current` text snippet from the review to find the exact location (line numbers may be stale).\n3. Do NOT touch anything marked as "What\'s Correct."\n4. Do NOT refactor, reorganize, or "improve" code that is not listed as an issue.\n\n### Comply Step 4: Verify Fixes\n\n**Tools:** `read`, `exec`\n\nFor each critical issue:\n1. Re-read the affected section with `read` to confirm the fix.\n2. Run the relevant `exec` check (grep for colors, size check, etc.).\n3. Verify the fix matches the expected value from the review.\n\n### Comply Step 5: Write Fixed Output\n\n**Tools:** `write`\n\nWrite the fixed package to `output/package.claw.json`. Then STOP and wait for re-review.\n\n### Comply Error Tracking\n\nThe review file includes an error trend indicator:\n- **"improving"** \u2014 error count is decreasing. Keep going.\n- **"stagnant"** \u2014 error count is not changing. Re-read the review more carefully.\n- **"worsening"** \u2014 error count is increasing. You are likely breaking correct items while fixing others. The system may fall back to auto-fix.\n\n**Circuit breaker:** After 3 comply rounds with a "worsening" or "stagnant" trend, emit `CLAW_STEP(status: "error", message: "Comply loop not converging")`. The engine will attempt auto-fix.\n\n---\n\n## Document References\n\n| Step | Document | What to look for |\n|------|----------|-----------------|\n| 2-3 | App files | `claw.json`, `index.html`, `session.json` |\n| 5 | `UI_DESIGN_GUIDE.md` | CSS variables, viewport rules, color system |\n| 5 | `SANDBOX_CAPABILITIES.md` | Bridge API methods and signatures |\n| 5 | `UX_INTERACTION_GUIDE.md` | Interaction patterns for UI changes |\n| 5 | `SYSTEM_CAPABILITIES.md` | General development rules |\n| Comply | `review/round-N.review.md` | Critical issues, correct items, fix instructions |\n';
|
|
8423
|
+
var WORKFLOW_UPDATE_default = '---\nname: echoclaw-update\ndescription: >\n Modify, edit, or update an existing ClawApp.\n Use when (1) user asks to change, fix, or improve an installed app,\n (2) user reports a bug or broken feature in their app,\n (3) user wants to add a new feature to an existing app,\n (4) user wants layout, style, or text changes to a running app.\n NOT for creating new apps from scratch (use echoclaw-create),\n adapting shared templates (use echoclaw-adapt),\n or Studio live-preview mode (use echoclaw-studio).\n Reads existing app files, applies surgical changes, verifies nothing breaks.\n---\n\n# Workflow: Update Existing App\n\n> You are a long-thinking Agent with `read`, `write`, `edit`, `exec` tools and unlimited rounds.\n> Cardinal rule: **read first, edit second, verify third.**\n\nRed lines are marked **MUST** / **NEVER**. Everything else is creative freedom.\nFor self-check commands see `references/CHECKLIST.md`.\nFor comply (fix review issues) sub-workflow see `references/COMPLY.md`.\n\n**Bidirectional loop:** Every step emits a CLAW_STEP status. The client may reply with `next_doc`, `guidance`, or `abort`. Wait for the client\'s reply before proceeding.\n\n---\n\n## Step 1: Acknowledge Receipt\n\nParse the task to extract `appId` and the change request. The `appId` comes from the existing app -- do NOT generate a new one.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"received"}-->`\n\n**MUST:** Immediately continue to Step 2 in the SAME response. The ACK is a progress signal, not a stopping point.\n\n---\n\n## Step 2: Read the Existing App\n\n**Tools:** `read`\n\nRead all three core files from `apps/{appId}/`:\n\n1. `claw.json` -- manifest, config_fields, install_contract, prompt\n2. `index.html` -- the full current HTML (read the entire file)\n3. `session.json` -- conversation history and design decisions\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"reading"}-->`\n\n**Self-check:** All 3 files exist and are non-empty. `claw.json` parses as valid JSON. `index.html` is well-formed HTML.\n\n---\n\n## Step 3: Understand Design History\n\n**Tools:** `read`, `exec`\n\nBefore any change, build a mental model from `session.json`:\n- **What does the app do?** (`entries[0].summary`)\n- **Why is it built this way?** Key architectural decisions recorded in prior entries that must not be broken\n- **What has changed before?** (entries with `op: "update"`)\n- **Preserved features:** Every existing interactive element and its handler\n- **Change target:** Exactly which part of the code the request affects\n\n**Baseline handler count (regression baseline):**\n```bash\ngrep -c \'addEventListener\\|onclick\\|onchange\\|onsubmit\\|onkeydown\\|onkeyup\' index.html\n# Record this number. After changes, it must be >= this value.\n```\n\n---\n\n## Step 4: Assess Complexity\n\nNo separate CLAW_STEP -- internal decision point.\n\n| Tier | Signal | Action |\n|------|--------|--------|\n| **Trivial** | "change color to blue", "fix typo" | Skip confirmation -- just do it |\n| **Simple** | "add a reset button" | Brief one-liner, then proceed |\n| **Medium** | "add chart view for the data" | Confirm scope + what stays unchanged. Wait for yes. |\n| **Complex** | "redesign the entire layout" | Detailed feature list + preserved items. Wait for explicit OK. |\n\nIf the user says "just do it" at any tier, proceed immediately.\n\n---\n\n## Step 5: Read Relevant Specs\n\n**Tools:** `read`\n\nRead only the specs relevant to the requested change:\n\n| Change type | Read |\n|-------------|------|\n| Visual / layout | `UI_DESIGN_GUIDE.md` |\n| New Bridge API feature | `SANDBOX_CAPABILITIES.md` |\n| UI interaction | `UX_INTERACTION_GUIDE.md` |\n| Any change (if not in context) | `SYSTEM_CAPABILITIES.md` |\n\nDo NOT read all specs for a trivial change. Match reading depth to change complexity.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"reading"}-->`\n\n---\n\n## Step 6: Apply Changes with Edit\n\n**Tools:** `edit` (preferred), `write` (only for complete redesigns the user explicitly asked for)\n\n**Golden rule: prefer `edit` over `write`.** Surgical insert preserves everything else. Full overwrite requires re-verifying EVERYTHING.\n\nApply changes incrementally: HTML structure -> CSS -> JS logic. Each edit is self-contained -- no half-wired elements.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"building"}-->`\n\n---\n\n## Step 7: Self-Check (Verification)\n\n**Tools:** `read`, `exec`\n\nRun the full self-check suite. Every check uses tools -- no mental review.\n\nSee `references/CHECKLIST.md` for exact commands. Key checks:\n\n1. **Regression check** -- handler count >= baseline from Step 3\n2. **No stubs** -- zero TODO/FIXME/PLACEHOLDER\n3. **No empty handlers** -- zero `onclick=""`\n4. **Color compliance** -- CSS variables used, no hardcoded colors\n5. **Structural integrity** -- file size < 500KB\n6. **Functional cross-check** -- button count vs handler count\n\n**If any check fails:** Fix with `edit`, re-run the specific check.\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"verifying"}-->`\n\n**MUST:** Use `read` to read back the file. Do not verify from memory.\n**MUST:** Use `exec` to run grep checks. Do not estimate results mentally.\n\n---\n\n## Step 8: Fix Loop (If Needed)\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"fixing"}-->`\n\nIf Step 7 found issues: fix with `edit`, `read` back, re-run the check. Repeat until pass.\n\n**Circuit breaker:** After 3 fix rounds, emit:\n`<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"error","message":"Stuck in fix loop after 3 rounds"}-->`\nClient responds: `fallback` (accept with known issues), `retry` (fresh context), or `abort`.\n\n---\n\n## Step 9: Update Metadata\n\n**Tools:** `edit`\n\nAfter code changes pass verification:\n\n1. **`claw.json`** -- bump version if present; update `manifest.description` if purpose changed\n2. **`session.json`** -- append a new entry:\n ```json\n {\n "timestamp": "<ISO>",\n "role": "openclaw",\n "summary": "<what changed and what was preserved>",\n "op": "update"\n }\n ```\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"building"}-->`\n\n---\n\n## Step 10: Describe Changes (MANDATORY)\n\nSummarize what changed (2-4 bullets) and what was preserved (1-3 bullets). Focus on user-visible behavior, not code. Mention data persistence if relevant.\n\n```\nChanges made:\n- [What changed]: [user-visible description]\nPreserved:\n- [Feature X] -- unchanged\n```\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"describing"}-->`\n\n**NEVER:** Send `CLAW_APP_READY` without describing changes first.\n\n---\n\n## Step 11: User Confirmation + Delivery (MANDATORY)\n\n**Emit:** `<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"awaiting_confirmation"}-->`\n\n- **User confirms:** Emit `CLAW_APP_READY` with `op: "update"`\n- **User requests another change:** Loop back to Step 4\n- **User says "undo" / "revert":** Emit `CLAW_STEP(status: "error", message: "User requested revert")` -- client handles rollback\n\n**Emit:** `<!--CLAW_APP_READY:{"appId":"<id>","op":"update","contentHash":"<sha256 of index.html>","summary":"<brief>"}-->`\n\n**MUST:** `contentHash` matches the actual file on disk.\n**NEVER:** Send `CLAW_APP_READY` without explicit user confirmation.\n\n---\n\n## Workflow Summary\n\n```\nStep 1 received Acknowledge task (then immediately continue)\nStep 2 reading Read claw.json + index.html + session.json\nStep 3 (reading) Understand design history + baseline handler count\nStep 4 -- Assess complexity (internal decision)\nStep 5 reading Read relevant spec docs\nStep 6 building Apply surgical edits\nStep 7 verifying Self-check with read + exec\nStep 8 fixing Fix loop (if needed, max 3 rounds)\nStep 9 building Update claw.json version + session.json\nStep 10 describing Summarize changes + preserved features\nStep 11 awaiting_confirmation Wait for user OK -> CLAW_APP_READY\n```\n\n---\n\n## Capability Boundary & Error Recovery\n\n**Can do:** Read existing code, surgical edits, verify structure via grep, preserve features, bump version.\n**Cannot do:** Run in browser, guarantee runtime behavior, test interaction flows, detect CSS rendering bugs, rollback (client\'s job).\n\n**Error recovery:**\n- Missing files -> emit `CLAW_STEP(status: "error", message: "App files not found")`\n- session.json missing -> proceed without history, note in description\n- Verify loop stuck (> 3 rounds) -> emit `CLAW_STEP(status: "error")` with details\n- User cancels mid-edit -> stop immediately, no `CLAW_APP_READY`\n- Disconnect -> on reconnect, re-read workspace, compare with session.json\n- Handler count decreased -> treat as regression, fix before proceeding\n\n---\n\n## Core Principles\n\n1. **Read first, edit second, verify third.** Never modify code you haven\'t read. Never deliver code you haven\'t verified.\n2. **Surgical over wholesale.** `edit` preserves what works. `write` risks breaking what was fine.\n3. **Regression is the enemy.** Baseline handler count before changes. Compare after. A decrease = a bug you introduced.\n4. **Self-checks are mechanical, not mental.** Use `read` and `exec`. Numbers do not lie.\n5. **Describe before delivering.** The user should know what changed and what was preserved before they see it.\n';
|
|
8422
8424
|
|
|
8423
8425
|
// ../claw-engine/src/authoring/openclaw-guides/WORKFLOW_STUDIO.md
|
|
8424
|
-
var WORKFLOW_STUDIO_default = '---\nname: echoclaw-studio\ndescription: Use when creating an app with live preview in Studio mode\n---\n\n# Workflow: Studio (Live Preview Mode)\n\n> Scenario: A user creates an app in Studio mode with real-time preview. Each change renders immediately.\n> Complexity: High but incremental (many small steps, each < 30 seconds)\n\n---\n\n## Overview\n\nStudio mode is fundamentally different from chat-driven creation. Instead of building the entire app and delivering once, you build in layers and send a `CLAW_PREVIEW` after each layer. The user sees the app evolve in real-time and can steer the direction at any point.\n\n**Key differences from chat mode:**\n\n| Aspect | Chat mode | Studio mode |\n|--------|-----------|-------------|\n| Delivery | Single `CLAW_APP_READY` at the end | `CLAW_PREVIEW` after every meaningful change |\n| Feedback | User reviews after completion | User sees rendering in real-time |\n| Scope per step | Entire app | One layer or one change |\n| Self-check depth | Full suite | Quick structural checks (full suite only before finalize) |\n| Completion | System-driven (verification passes) | **User-driven** (user says "done") |\n\n**Bidirectional loop:** Every step emits a status marker. The client relays user feedback. `CLAW_APP_READY` is ONLY sent when the user explicitly signals completion \u2014 never auto-promote.\n\n---\n\n## Phase A: Initial Creation\n\n### Step 1: Acknowledge Receipt\n\n**Trigger:** You receive a create task in Studio mode.\n**Tools:** None\n**Status:** `CLAW_STEP(status: "received", op: "create")` \u2014 **MUST emit.**\n\nAction:\n- Parse the request to extract the user\'s app idea.\n- Generate the `appId` yourself. Choose a kebab-case name based on the user\'s request semantics (e.g., `expense-tracker`, `habit-journal`). Keep it short, descriptive, and unique.\n- Emit acknowledgment immediately.\n- **Then immediately proceed to Step 2.** The CLAW_STEP marker is a progress signal \u2014 NOT a stopping point. NEVER send just an acknowledgment and wait.\n\n**Client reply:** ACK.\n\n---\n\n### Step 2: Idea Expansion (MANDATORY)\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "suggesting")` \u2014 **MUST emit.**\n\nProactively suggest 3-5 enhancements before reading specs. This step is NOT optional \u2014 ALWAYS offer suggestions before building. In Studio mode, this happens BEFORE spec reading to keep the conversational flow fast.\n\nFormat:\n```\nGot it \u2014 [app description]. A few ideas before I start:\n- [Suggestion 1]?\n- [Suggestion 2]?\n- [Suggestion 3]?\n- [Suggestion 4]?\n\nSay "go ahead" to start with the basics, or pick any you like.\n```\n\nRules:\n- 3-5 suggestions, each ONE line, under 15 words.\n- Cover different dimensions: features, data, visuals, UX polish.\n- Only suggest things you can actually build (no external APIs, no frameworks).\n- Frame as options, not requirements: "Want me to add X?" not "You should add X."\n\nUser responses:\n- "go ahead" / "just do it" / "that\'s fine": Start building immediately with just the basics. Zero follow-up questions.\n- User picks specific suggestions: Incorporate only those.\n- User adds their own ideas: Incorporate those too.\n\n**Client reply:** User\'s selection or "go ahead."\n\n---\n\n### Step 3: Read Specs\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n**Reads:** `UI_DESIGN_GUIDE.md`, `SANDBOX_CAPABILITIES.md`, `ICON_SPEC.md`, `UX_INTERACTION_GUIDE.md`\n\nAction:\n- Read visual rules, CSS variable system, viewport constraints.\n- Read development rules, Bridge API overview, workspace format.\n- Read icon specification: stroke style, `currentColor`, <=4 elements, manifest.color selection.\n- Read interaction patterns, gesture handling, and UX behavior rules.\n- Internalize constraints so they survive context compaction.\n\n**Client reply:** ACK.\n\n---\n\n### Step 3.5 (Optional \u2014 for medium/complex apps): Quick Plan\n\n**Tools:** None\n**Status:** `CLAW_STEP(status: "planning")`\n\nFor apps with 3+ features or non-trivial state (dashboards, managers, multi-view apps), define the architecture before building:\n\n1. **State shape** \u2014 the data model the app manages.\n2. **Key event handlers** \u2014 map interactive elements to handler names.\n\n```\nstate = { items: [...], filter: \'all\', editing: null }\n"Add" button \u2192 addItem()\n"Delete" icon \u2192 deleteItem(id)\nFilter tabs \u2192 setFilter(value)\n```\n\n**Skip this step** for simple apps (timer, calculator, single-purpose tools) where the state and handlers are obvious.\n\n**Client reply:** ACK.\n\n---\n\n### Step 4: Build Skeleton + CSS \u2192 First Preview\n\n**Tools:** `write`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\nBuild the visual foundation:\n1. Write `claw.json` with manifest (name, description, icon per ICON_SPEC.md, color, capabilities).\n2. Write `index.html` with:\n - DOCTYPE + head (meta viewport, title)\n - CSS: all colors use `var(--ec-*)`, dark theme, responsive layout\n - Body: HTML structure with all major containers and placeholder content\n - No JS logic yet \u2014 this is the visual skeleton\n\n**Quick self-check (exec tool):**\n```bash\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # Hardcoded colors (must = 0, except manifest.color)\nwc -c index.html # File size (must < 500KB)\n```\n\n**Send preview:**\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":1}-->\n```\n\nThe client reads `index.html` from workspace and renders it in the Studio preview panel.\n\n**Client reply:** ACK. User may provide feedback on the visual direction.\n\n---\n\n### Step 5: Write JS Logic \u2192 Second Preview\n\n**Tools:** `edit`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\nAdd the functional layer:\n1. State object \u2014 define the data model.\n2. Core logic \u2014 CRUD operations, calculations, transformations.\n3. Event handlers \u2014 wire every interactive element to its handler.\n4. Rendering function \u2014 update DOM from state.\n5. Initialization \u2014 load from storage if applicable, set up initial state.\n\n**Quick self-check (exec tool):**\n```bash\ngrep -cn \'onclick=""\\|onchange=""\' index.html # Empty handlers (must = 0)\ngrep -cn \'TODO\\|FIXME\' index.html # Stubs (must = 0)\n```\n\n**Send preview:**\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":2}-->\n```\n\n**Client reply:** ACK. User sees a functional (though unpolished) app.\n\n---\n\n### Step 6: Polish + i18n \u2192 Third Preview\n\n**Tools:** `edit`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\nAdd the polish layer:\n1. i18n \u2014 `t()` function + `en`/`zh-CN` dictionaries; replace all hardcoded strings.\n2. Empty states \u2014 show meaningful UI when there\'s no data.\n3. Loading/error states \u2014 handle async operations gracefully.\n4. Transitions and animations \u2014 subtle CSS transitions for state changes.\n5. Bridge integration \u2014 storage persistence, context (theme, locale).\n6. Accessibility \u2014 proper ARIA labels, keyboard navigation for interactive elements.\n\n**Quick self-check (exec tool):**\n```bash\ngrep -cn \'TODO\\|FIXME\' index.html # Must = 0\ngrep -c "t(\'" index.html # i18n usage (should be > 0)\ngrep -c \'var(--ec-\' index.html # CSS variable usage (should be > 0)\n```\n\n**Send preview:**\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":3}-->\n```\n\n**Client reply:** ACK. User sees the polished app.\n\n---\n\n## Phase B: Iterative Refinement\n\nAfter the initial 3 previews, the user can request changes at any time. Each change follows a mini-cycle:\n\n### Step 7: Receive User Feedback\n\n**Trigger:** User sends a change request ("make the header bigger", "add a delete button", "the chart is wrong").\n**Tools:** None\n**Status:** `CLAW_STEP(status: "received")`\n\n**Client reply:** ACK.\n\n---\n\n### Step 8: Read Current State\n\n**Tools:** `read`\n**Status:** `CLAW_STEP(status: "reading")`\n\nRead the current `index.html` to understand what exists. Do NOT rely on memory of what you wrote earlier \u2014 always re-read.\n\n**Client reply:** ACK.\n\n---\n\n### Step 9: Apply Change\n\n**Tools:** `edit`\n**Status:** `CLAW_STEP(status: "building")`\n\nApply the change surgically with `edit`. One change at a time \u2014 do not batch unrelated modifications.\n\n**Client reply:** ACK.\n\n---\n\n### Step 10: Quick Verify + Preview\n\n**Tools:** `exec`\n**Status:** `CLAW_STEP(status: "verifying")` then `CLAW_PREVIEW`\n\nRun quick structural checks:\n```bash\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # Hardcoded colors\ngrep -cn \'TODO\\|FIXME\' index.html # Stubs\nwc -c index.html # Size\n```\n\nSend updated preview:\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":N}-->\n```\n\nIncrement `iteration` counter for every preview sent.\n\n**Client reply:** User sees the change. They may request more changes (loop back to Step 7) or signal completion.\n\n---\n\n## Phase C: Finalize\n\n### Step 11: User Signals Completion (MANDATORY \u2014 DO NOT SKIP)\n\n**Trigger:** User explicitly says "done", "looks good", "ship it", "install it", "that\'s it", or "perfect".\n**Tools:** None\n**Status:** No CLAW_STEP yet \u2014 proceed to full verification first.\n\n**Critical rule:** You **MUST NOT** auto-promote to `CLAW_APP_READY`. **MUST** wait for explicit user confirmation:\n- Silence is NOT a completion signal.\n- If the user stops requesting changes, stay in the iterative loop.\n- Only an explicit verbal signal triggers finalization.\n- **NEVER** send `CLAW_APP_READY` without explicit user confirmation.\n\n---\n\n### Step 12: Full Verification\n\n**Tools:** `read`, `exec`\n**Status:** `CLAW_STEP(status: "verifying")`\n\nNow run the complete self-check suite (not just quick checks):\n\n**12a. Structural checks:**\n```bash\ngrep -cn \'onclick=""\\|onchange=""\' index.html # Empty handlers (must = 0)\ngrep -cn \'TODO\\|FIXME\\|PLACEHOLDER\' index.html # Stubs (must = 0)\ngrep -cn \'console\\.log\' index.html # Debug logs (should = 0)\ngrep -c \'addEventListener\\|onclick\\|onsubmit\' index.html # Handler count\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # Hardcoded colors\ngrep -c \'var(--ec-\' index.html # CSS variable usage (> 0)\ngrep -c "t(\'" index.html # i18n usage (> 0)\nwc -c index.html # File size (< 500KB)\n```\n\n**12b. Functional check:**\n```bash\nnode -e "const h=require(\'fs\').readFileSync(\'index.html\',\'utf8\'); \\\n const btns=(h.match(/<button/g)||[]).length; \\\n const handlers=(h.match(/addEventListener|onclick/g)||[]).length; \\\n console.log(\'buttons:\',btns,\'handlers:\',handlers); \\\n if(btns>handlers) process.exit(1)"\n```\n\n**12c. Read-back:** Read the complete file with `read` tool to verify it\'s well-formed and complete.\n\n**If any check fails:** Fix with `edit`, re-check. Send one more `CLAW_PREVIEW` after fixes so the user can verify.\n\n---\n\n### Step 13: Write Session Metadata\n\n**Tools:** `write`\n**Status:** `CLAW_STEP(status: "building")`\n\nWrite `session.json` with:\n```json\n{\n "appId": "my-app",\n "createdAt": "2026-03-22T10:00:00Z",\n "lastUpdated": "2026-03-22T10:15:00Z",\n "entries": [\n {\n "timestamp": "...",\n "role": "user",\n "summary": "Requested [app description]",\n "op": "create"\n },\n {\n "timestamp": "...",\n "role": "openclaw",\n "summary": "Created [app type] with [key features]. [N] preview iterations.",\n "op": "create"\n }\n ]\n}\n```\n\n---\n\n### Step 14: Deliver\n\n**Tools:** None\n**Status:** `CLAW_APP_READY`\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"create","contentHash":"<sha256 of index.html>","summary":"[App description with key features]"}-->\n```\n\n**MUST:** The `contentHash` is the SHA-256 hash of the final `index.html` file on disk.\n\nThis triggers the client to pull files from workspace and install the app.\n\n---\n\n## CLAW_PREVIEW Marker Format\n\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":3}-->\n```\n\n| Field | Type | Required | Description |\n|-------|------|----------|-------------|\n| `appId` | string | yes | App identifier |\n| `iteration` | number | yes | Monotonically increasing counter (1, 2, 3, ...) |\n\n**Co-location rule:** `CLAW_STEP` and `CLAW_PREVIEW` are written in the **same reply message**. Emit `CLAW_STEP` first, then `CLAW_PREVIEW`. Do NOT send them in separate messages.\n\nBehavior:\n- Client reads the updated `index.html` from workspace (file-based, not embedded in the marker).\n- Does NOT trigger installation \u2014 only renders in the Studio preview panel.\n- Client can respond with user feedback (text), resize events, or visual issue reports.\n\n---\n\n## Studio-Specific Self-Check Strategy\n\nStudio prioritizes speed over completeness during iteration. Use a tiered approach:\n\n| Phase | Check depth | When |\n|-------|-------------|------|\n| Quick check | Hardcoded colors, stubs, file size | After every edit (Steps 4-6, 10) |\n| Full check | Complete self-check suite + functional checks | Only before finalization (Step 12) |\n\nQuick checks should take < 2 seconds. Full checks may take 10-30 seconds.\n\n---\n\n## Capability Boundary\n\n| Can do | Cannot do |\n|--------|-----------|\n| Send incremental previews via CLAW_PREVIEW | Observe user interaction with preview |\n| Rapid edit -> verify -> preview cycle | Receive click/scroll events from preview |\n| Progressive enhancement (structure -> logic -> polish) | Hot-reload JS state (full re-render on each preview) |\n| Respond to user text feedback | See runtime errors in preview |\n| Arbitrarily many preview iterations | Auto-finalize without user signal |\n\n---\n\n## Error Recovery\n\n| Situation | Action |\n|-----------|--------|\n| Full verification fails after user says "done" | Fix issues, send one more CLAW_PREVIEW, then finalize |\n| Fix loop stuck (> 3 rounds) | Emit `CLAW_STEP(status: "error")` \u2014 client decides |\n| User says "start over" | Write a fresh `index.html`, reset iteration to 1 |\n| Disconnect during build | On reconnect, re-read workspace, resume from last iteration |\n| Context compaction during long session | Re-read `UI_DESIGN_GUIDE.md` before the next edit |\n\n---\n\n## Document References\n\n| Step | Document | What to look for |\n|------|----------|-----------------|\n| 3 | `UI_DESIGN_GUIDE.md` | CSS variables, viewport, color system, layout patterns |\n| 3 | `SANDBOX_CAPABILITIES.md` | Development rules, Bridge API overview, workspace format |\n| 3 | `ICON_SPEC.md` | Icon SVG rules, manifest.color selection, design guidelines |\n| 3 | `UX_INTERACTION_GUIDE.md` | Interaction patterns, gesture handling, UX behavior rules |\n| 5 | `SANDBOX_CAPABILITIES.md` | Bridge API methods for storage, context, capabilities |\n| 12 | Self-check toolkit | grep/node commands from the verification section above |\n';
|
|
8426
|
+
var WORKFLOW_STUDIO_default = '---\nname: echoclaw-studio\ndescription: >\n Use when: creating or editing an app in Studio mode with live preview.\n NOT for: chat-mode app creation (WORKFLOW_CREATE), system-triggered updates\n (WORKFLOW_UPDATE), or one-shot delivery without preview.\n---\n\n# Workflow: Studio (Live Preview Mode)\n\n> Build in layers. Send `CLAW_PREVIEW` after each layer. `CLAW_APP_READY` only\n> when the user explicitly says they are done \u2014 never auto-promote.\n\n| Aspect | Chat | Studio |\n|--------|------|--------|\n| Delivery | Single `CLAW_APP_READY` at end | `CLAW_PREVIEW` after every layer |\n| Scope per step | Entire app | One layer / one change |\n| Self-check | Full suite | Quick per step; full only before finalize |\n| Completion trigger | Verification passes | **User says "done"** |\n| Rendering | Not visible until delivery | **User sees rendering in real-time after each `CLAW_PREVIEW`** |\n\n---\n\n## Phase A \u2014 Initial Creation\n\n### Step 1: Acknowledge Receipt\n\n**Trigger:** Create task received in Studio mode.\n**Status:** `CLAW_STEP(status: "received", op: "create")` \u2014 MUST emit.\n\nParse the app idea, generate `appId` in kebab-case (e.g. `expense-tracker`), emit immediately,\nthen proceed to Step 2. `CLAW_STEP` is a progress signal \u2014 NOT a stop.\n\n---\n\n### Step 2: Idea Expansion (MANDATORY)\n\n**Status:** `CLAW_STEP(status: "suggesting")`\n\nAlways suggest 3\u20135 enhancements before building:\n\n```\nGot it \u2014 [app description]. A few ideas before I start:\n- [Suggestion 1]?\n- [Suggestion 2]?\n- [Suggestion 3]?\n\nSay "go ahead" to start with the basics, or pick any you like.\n```\n\n- Each suggestion: ONE line, under 15 words. Cover: features, data, visuals, UX polish.\n- Only suggest buildable things (no external APIs/frameworks). Frame as options, not requirements.\n- "go ahead": build basics immediately, zero follow-up questions.\n\n---\n\n### Step 3: Read Specs\n\n**Tools:** `read` | **Status:** `CLAW_STEP(status: "reading")`\n\nRead: `UI_DESIGN_GUIDE.md`, `SANDBOX_CAPABILITIES.md`, `ICON_SPEC.md`, `UX_INTERACTION_GUIDE.md`.\nInternalize constraints so they survive context compaction.\n\n---\n\n### Step 3.5: Quick Plan (conditional)\n\n**Status:** `CLAW_STEP(status: "planning")`\n\nSkip for simple apps (timer, calculator). Required for apps with 3+ features or non-trivial state.\nOutput state shape + handler map in \u226410 lines:\n```\nstate = { items: [], filter: \'all\', editing: null }\n"Add" \u2192 addItem() "Delete" \u2192 deleteItem(id) Filter tabs \u2192 setFilter(v)\n```\n\n---\n\n### Step 4: Build Skeleton + CSS \u2192 Preview #1\n\n**Tools:** `write`, `exec`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\n1. Write `claw.json`: name, description, icon (ICON_SPEC.md), color, capabilities.\n2. Write `index.html`: DOCTYPE + head, all colors via `var(--ec-*)`, dark theme,\n full HTML structure with placeholder content. No JS logic yet.\n\nQuick self-check:\n```bash\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # must = 0 (hardcoded colors)\nwc -c index.html # must < 500KB\n```\n\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":1}-->\n```\n\n---\n\n### Step 5: Write JS Logic \u2192 Preview #2\n\n**Tools:** `edit`, `exec`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\nAdd in order: state object, core logic (CRUD/calculations), event handlers,\nrender function (DOM from state), initialization (storage load, initial state).\n\nQuick self-check:\n```bash\ngrep -cn \'onclick=""\\|onchange=""\' index.html # must = 0 (empty handlers)\ngrep -cn \'TODO\\|FIXME\' index.html # must = 0\n```\n\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":2}-->\n```\n\n---\n\n### Step 6: Polish + i18n \u2192 Preview #3\n\n**Tools:** `edit`, `exec`\n**Status:** `CLAW_STEP(status: "building")` then `CLAW_PREVIEW`\n\nAdd: `t()` i18n function + `en`/`zh-CN` dictionaries (replace all hardcoded strings),\nempty states, loading/error states, CSS transitions, Bridge storage + context\nintegration, ARIA labels and keyboard navigation.\n\nQuick self-check:\n```bash\ngrep -cn \'TODO\\|FIXME\' index.html # must = 0\ngrep -c "t(\'" index.html # should be > 0\ngrep -c \'var(--ec-\' index.html # should be > 0\n```\n\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":3}-->\n```\n\n---\n\n## Phase B \u2014 Iterative Refinement\n\nRepeat this 4-step loop for each user change request until they signal completion.\n\n**Step 7 \u2014 Receive:** `CLAW_STEP(status: "received")` \u2014 ACK the request.\n\n**Step 8 \u2014 Read:** `CLAW_STEP(status: "reading")` \u2014 re-read `index.html`. Never rely on memory.\n\n**Step 9 \u2014 Apply:** `CLAW_STEP(status: "building")` \u2014 `edit` surgically. One change at a time.\n\n**Step 10 \u2014 Verify + Preview:** `CLAW_STEP(status: "verifying")` then `CLAW_PREVIEW`\n\n```bash\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # hardcoded colors\ngrep -cn \'TODO\\|FIXME\' index.html # stubs\nwc -c index.html # size\n```\n\n```\n<!--CLAW_PREVIEW:{"appId":"my-app","iteration":N}-->\n```\n\nIncrement `iteration` for every preview. Loop back to Step 7 on further requests.\n\n---\n\n## Phase C \u2014 Finalize\n\n### Step 11: Await Explicit Completion Signal\n\n**Trigger:** User says "done", "looks good", "ship it", "install it", "perfect".\n\n- NEVER auto-promote. Silence is NOT a completion signal.\n- If user stops requesting changes, stay in the iterative loop.\n\n---\n\n### Step 12: Full Verification\n\n**Tools:** `read`, `exec` | **Status:** `CLAW_STEP(status: "verifying")`\n\n12a. Structural:\n```bash\ngrep -cn \'onclick=""\\|onchange=""\' index.html # must = 0\ngrep -cn \'TODO\\|FIXME\\|PLACEHOLDER\' index.html # must = 0\ngrep -cn \'console\\.log\' index.html # should = 0\ngrep -cn \'#[0-9a-fA-F]\\{3,8\\}\' index.html # must = 0\ngrep -c \'var(--ec-\' index.html && grep -c "t(\'" index.html # both > 0\nwc -c index.html # < 500KB\n```\n\n12b. Functional:\n```bash\nnode -e "const h=require(\'fs\').readFileSync(\'index.html\',\'utf8\'); \\\n const b=(h.match(/<button/g)||[]).length; \\\n const hd=(h.match(/addEventListener|onclick/g)||[]).length; \\\n console.log(\'buttons:\',b,\'handlers:\',hd); if(b>hd) process.exit(1)"\n```\n\n12c. Read-back: `read` the complete file to verify it is well-formed.\n\nIf any check fails: fix, re-check, send one more `CLAW_PREVIEW` before finalizing.\n\n---\n\n### Step 13: Write Session Metadata\n\n**Tools:** `write` | **Status:** `CLAW_STEP(status: "building")`\n\nWrite `session.json` with `appId`, `createdAt`, `lastUpdated`, and `entries` array:\n- User entry: `{ role: "user", summary: "Requested [description]", op: "create" }`\n- Agent entry: `{ role: "openclaw", summary: "Created [type] with [features]. [N] previews.", op: "create" }`\n\n---\n\n### Step 14: Deliver\n\n**Status:** `CLAW_APP_READY`\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"create","contentHash":"<sha256 of index.html>","summary":"[description]"}-->\n```\n\n`contentHash` = SHA-256 of the final `index.html` on disk.\nTriggers the client to pull files from workspace and install the app.\n\n---\n\n## Reference\n\n**CLAW_PREVIEW format:** `<!--CLAW_PREVIEW:{"appId":"my-app","iteration":N}-->`\n- `iteration`: monotonically increasing integer starting at 1.\n- Co-location rule: `CLAW_STEP` and `CLAW_PREVIEW` in the **same reply**.\n- Client reads `index.html` from workspace and renders in Studio panel. Does NOT install.\n- Self-check tiers: quick (colors, stubs, size) after every edit; full suite only at Step 12.\n\n**Error recovery:**\n\n| Situation | Action |\n|-----------|--------|\n| Full verification fails after "done" | Fix \u2192 one more `CLAW_PREVIEW` \u2192 finalize |\n| Fix loop stuck > 3 rounds | `CLAW_STEP(status: "error")` \u2014 client decides |\n| User says "start over" | Fresh `index.html`, reset iteration to 1 |\n| Disconnect during build | Reconnect \u2192 re-read workspace \u2192 resume from last iteration |\n| Context compaction mid-session | Re-read `UI_DESIGN_GUIDE.md` before next edit |\n';
|
|
8425
8427
|
|
|
8426
8428
|
// ../claw-engine/src/authoring/openclaw-guides/SANDBOX_CAPABILITIES.md
|
|
8427
|
-
var SANDBOX_CAPABILITIES_default = "# EchoClaw Sandbox Capabilities Reference\n\n> For OpenClaw: Complete reference of what APIs, capabilities, and resources are available inside the EchoClaw sandbox.\n> Engine version: v1.9.3\n\n---\n\n## 1. Bridge Overview\n\nAll app code runs inside a sandboxed `<iframe>`. There is **no direct access** to the host OS, filesystem, or network. Everything goes through `window.echoclaw` \u2014 the Bridge object injected by the host shell.\n\nCommunication model:\n- **`echoclaw.invoke(action, payload)`** \u2014 Request-response (async, returns Promise)\n- **`echoclaw.emit(action, payload)`** \u2014 Fire-and-forget (no return value)\n- **`echoclaw.subscribe(event, callback)`** \u2014 Event listener (returns unsubscribe function)\n- **`echoclaw.getContext()`** \u2014 Sync, returns current theme/device/capabilities\n\n---\n\n## 2. Bridge API \u2014 Complete Reference\n\n### 2.1 Context\n\n```js\nconst ctx = echoclaw.getContext();\n// Returns:\n// {\n// theme: 'dark', // current theme\n// locale: 'en' | 'zh-CN',\n// device: { width, height, pixelRatio, platform },\n// capabilities: ['ui', 'storage', 'network', ...],\n// appId: 'my-app-id'\n// }\n```\n\n### 2.2 Key-Value Storage (`echoclaw.storage`)\n\nPersistent key-value store scoped to the app. No capability declaration needed (auto-granted with `storage`).\n\n```js\n// Set a value (string, number, object \u2014 auto JSON-serialized)\nawait echoclaw.storage.set('key', value);\n\n// Get a value (returns parsed value, or null if not found)\nconst val = await echoclaw.storage.get('key');\n\n// Remove a key\nawait echoclaw.storage.remove('key');\n\n// List all keys\nconst keys = await echoclaw.storage.keys();\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max keys | 1,000 |\n| Max total size | 5 MB |\n\n### 2.3 SQLite Database (`echoclaw.db`)\n\nFull SQL database per app. Requires `database` capability (auto-granted).\n\n```js\n// Execute DDL/DML (CREATE, INSERT, UPDATE, DELETE)\nconst result = await echoclaw.db.exec(\n 'INSERT INTO tasks (title, done) VALUES (?, ?)',\n ['Buy milk', false]\n);\n// result: { changes: 1, lastInsertRowid: 42 }\n\n// Query (SELECT)\nconst rows = await echoclaw.db.query(\n 'SELECT * FROM tasks WHERE done = ? LIMIT ?',\n [false, 50]\n);\n// rows: [{ id: 1, title: 'Buy milk', done: 0 }, ...]\n```\n\n**Allowed SQL:** All standard DDL (CREATE TABLE, DROP TABLE, ALTER TABLE) and DML (SELECT, INSERT, UPDATE, DELETE). Safe PRAGMAs like `journal_mode`, `foreign_keys` are allowed.\n\n**Blocked SQL:** `ATTACH`, `DETACH`, `LOAD_EXTENSION`, and dangerous PRAGMAs (`database_list`, `compile_options`, `integrity_check`, `quick_check`).\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Auto quota | 10 MB (no user prompt) |\n| Standard max | 50 MB (requires user approval) |\n| Extended max | 200 MB (requires second approval) |\n| Max rows per query | 1,000 (bridge enforces pagination) |\n| Max transactions/sec | 200 |\n\n**Schema versioning:** Use `manifest.db_version` (integer). When manifest version > stored version, run your migration SQL on startup.\n\n### 2.4 Sandbox File System (`echoclaw.fs.app*`)\n\nRead/write files within the app's sandboxed directory. Requires `storage` capability (auto-granted).\n\n```js\n// Write a file (string content)\nawait echoclaw.fs.appWrite('data/export.json', JSON.stringify(data));\n\n// Read a file\nconst content = await echoclaw.fs.appRead('data/export.json');\n\n// List files in a directory\nconst files = await echoclaw.fs.appList('data/');\n// files: ['export.json', 'backup.json']\n\n// Remove a file\nawait echoclaw.fs.appRemove('data/export.json');\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max sandbox storage | 250 MB per app |\n\n### 2.5 External File Picker (`echoclaw.invoke('fs.pick', ...)` / `echoclaw.invoke('fs.save', ...)`)\n\nOpens the native OS file dialog. Requires `filesystem` capability (prompt-tier \u2014 user must approve).\n\n```js\n// Open file picker\nconst file = await echoclaw.invoke('fs.pick', {\n accept: ['.json', '.csv', '.txt'], // allowed extensions\n maxSizeBytes: 10 * 1024 * 1024 // default 10MB\n});\n// file: { name: 'data.csv', size: 2048, content: '...', encoding: 'utf-8', mimeType: 'text/csv' }\n// Returns null if user cancels\n\n// Save file dialog\nconst result = await echoclaw.invoke('fs.save', {\n content: csvString,\n defaultName: 'export.csv',\n accept: ['.csv'],\n encoding: 'utf-8' // or 'base64' for binary\n});\n// result: { saved: true, name: 'export.csv' }\n```\n\n**Notes:**\n- File path is never exposed \u2014 only basename returned\n- Text extensions (.json, .csv, .txt, .md, .html, .js, etc.) are read as UTF-8; all others as base64\n- Pick max: 10 MB; Save max: 50 MB\n\n### 2.6 Network \u2014 Fetch Proxy (`echoclaw.invoke('fetch', ...)`)\n\nAll HTTP requests go through the bridge proxy. Requires `network` capability (auto-granted) AND `manifest.network_allow` whitelist.\n\n```js\nconst response = await echoclaw.invoke('fetch', {\n url: 'https://api.example.com/data',\n method: 'GET',\n headers: { 'Authorization': 'Bearer ...' }\n});\n// response: { status: 200, headers: {...}, body: '...' }\n```\n\n**Network access rules:**\n- `manifest.network_allow` MUST list every domain the app connects to\n- No whitelist = no network access (even with `network` capability)\n- Max 20 entries in `network_allow`\n- Localhost (127.0.0.1, ::1) requires `local_network` capability + user approval\n- Private LAN (10.x, 172.x, 192.168.x, .local) requires `lan_network` capability + user approval\n- Cloud metadata (169.254.x, metadata.google.internal) is ALWAYS blocked \u2014 no capability can unblock\n\n### 2.7 WebSocket (`echoclaw.ws`)\n\nProxied WebSocket connections. Requires `network` capability + `ws` capability + `manifest.network_allow`.\n\n```js\n// Connect (channel is your unique ID for this connection)\nawait echoclaw.invoke('ws.connect', {\n channel: 'my-channel',\n opts: { url: 'wss://api.example.com/ws', protocols: ['v1'] }\n});\n\n// Send a message\nawait echoclaw.invoke('ws.send', { channel: 'my-channel', data: { type: 'subscribe', topic: 'updates' } });\n// or send raw string:\nawait echoclaw.invoke('ws.send', { channel: 'my-channel', data: 'ping' });\n\n// Listen for events\nechoclaw.subscribe('ws:open', ({ channel }) => { ... });\nechoclaw.subscribe('ws:message', ({ channel, data }) => { ... });\nechoclaw.subscribe('ws:error', ({ channel, error }) => { ... });\nechoclaw.subscribe('ws:close', ({ channel, code, reason }) => { ... });\n\n// Close\nawait echoclaw.invoke('ws.close', { channel: 'my-channel' });\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max concurrent connections | 3 (default), 5 (extended, requires prompt) |\n| Max message size | 1 MB |\n\n**URL validation:** Only `ws:` and `wss:` protocols accepted. Same network access rules as fetch (whitelist, SSRF guard).\n\n### 2.8 Notifications (`echoclaw.emit`)\n\n```js\nechoclaw.emit('notify', {\n title: 'Timer Complete',\n body: 'Your 25-minute session is done!',\n // shell renders the notification \u2014 app does not control presentation\n});\n```\n\n**Limit:** Max 10 notifications per minute per app.\n\n### 2.9 Inter-App Communication (`app_comm`)\n\nSend messages between apps. Requires `app_comm` capability (auto-granted). Shell injects unforgeable sender identity.\n\n```js\n// Send to another app\nechoclaw.emit('app_comm', {\n targetAppId: 'other-app',\n payload: { action: 'refresh', data: {...} }\n});\n\n// Receive from other apps\nechoclaw.subscribe('app_comm:message', ({ senderAppId, payload }) => {\n // senderAppId is injected by shell \u2014 cannot be spoofed\n});\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max messages/sec | 20 per app |\n| Max payload size | 64 KB |\n\n### 2.10 Authentication (`echoclaw.auth`)\n\nManages credentials for third-party services. Requires `auth` capability (prompt-tier \u2014 user must approve).\n\n```js\n// \u2500\u2500 Tier 1: Plaintext session (non-sensitive fields only) \u2500\u2500\nawait echoclaw.auth.setSession({ userId: '123', displayName: 'Alice' });\nconst session = await echoclaw.auth.getSession();\nawait echoclaw.auth.clearSession();\n\n// \u2500\u2500 Tier 2: Keychain read-only (install-time API keys) \u2500\u2500\nconst apiKey = await echoclaw.invoke('getSecret', { key: 'openai_api_key' });\n\n// \u2500\u2500 Tier 3: Keychain read-write (runtime tokens) \u2500\u2500\nawait echoclaw.auth.setSecret('google.access_token', tokenValue);\nconst token = await echoclaw.auth.getSecret('google.access_token');\nawait echoclaw.auth.deleteSecret('google.access_token');\n\n// \u2500\u2500 Auto-refresh token \u2500\u2500\nconst validToken = await echoclaw.auth.getValidToken('google.access_token');\n\n// \u2500\u2500 Check auth status \u2500\u2500\nconst isAuthed = await echoclaw.auth.isAuthenticated({ provider: 'google' });\n\n// \u2500\u2500 Logout (clears runtime tokens, preserves install-time secrets) \u2500\u2500\nawait echoclaw.auth.logout();\n```\n\n**Auth events:**\n```js\nechoclaw.subscribe('auth:sessionExpired', ({ reason, key, providerId }) => { ... });\nechoclaw.subscribe('auth:tokenRefreshed', ({ key, providerId, expiresAt }) => { ... });\n```\n\n**Token naming convention:** `{providerId}.{tokenType}` \u2014 e.g., `google.access_token`, `github.refresh_token`.\n\n**Manifest requirement:** Apps using auth MUST declare `manifest.auth_profile` with strategy, token_tier, providers, session_fields, and unauthenticated_behavior.\n\n### 2.11 Keyboard Shortcuts (`echoclaw.invoke('registerShortcut', ...)`)\n\nRegister global keyboard shortcuts. Requires `shortcuts` capability.\n\n```js\nechoclaw.invoke('registerShortcut', {\n id: 'sc_search',\n keys: 'Ctrl+K' // or 'Cmd+Shift+P', 'Escape', 'Alt+Enter'\n});\n\nechoclaw.subscribe('shortcut:triggered', ({ id, keys }) => {\n if (id === 'sc_search') openSearchDialog();\n});\n```\n\n**Key format:** Modifiers (`Ctrl`, `Cmd`, `Shift`, `Alt`) + key, joined by `+`. `Ctrl` and `Cmd` are unified as `Mod` internally (Cmd on macOS, Ctrl on Windows/Linux).\n\n**Reserved shortcuts (cannot be overridden):**\n`Mod+Q`, `Mod+W`, `Mod+N`, `Mod+,`, `Mod+1-9`, `Mod+L`, `Mod+F`, `Mod+Z`, `Mod+Shift+Z`, `Mod+A`, `Mod+C`, `Mod+X`, `Mod+V`\n\n**Limit:** Max 50 shortcuts per app.\n\n### 2.12 Logging\n\n```js\nechoclaw.log('info', 'User clicked save', { itemCount: 5 });\nechoclaw.log('warn', 'Approaching storage limit');\nechoclaw.log('error', 'Failed to parse CSV', { line: 42 });\n```\n\n### 2.13 State Save/Load\n\n```js\n// Save app state (persisted across sessions)\nechoclaw.emit('saveState', { currentTab: 'settings', filters: [...] });\n\n// On load, state is provided via context or event\nechoclaw.subscribe('stateRestored', (savedState) => {\n applyState(savedState);\n});\n```\n\n**Limit:** Max state payload: 512 KB.\n\n---\n\n## 3. Capability System\n\nEach capability has a **tier** that determines how it is granted:\n\n### Auto-granted (no user prompt needed)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `ui` | Baseline \u2014 HTML rendering in iframe (always implicit) |\n| `timer` | `setTimeout`, `setInterval`, `requestAnimationFrame` |\n| `storage` | `echoclaw.storage.*` (KV) + `echoclaw.fs.app*` (sandbox files) |\n| `notification` | `echoclaw.emit('notify', ...)` \u2014 shell renders notifications |\n| `network` | Fetch proxy via bridge (REQUIRES `network_allow` whitelist) |\n| `audio` | Web Audio API, `<audio>` playback, autoplay |\n| `fullscreen` | `Element.requestFullscreen()` |\n| `app_comm` | Inter-app messaging via bridge |\n| `database` | SQLite per-app database (staged quota: 10MB auto \u2192 50MB \u2192 200MB) |\n| `secure_credential` | OS keychain storage (read via `getSecret`) |\n\n### Prompt-required (user must approve on first use)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `clipboard` | `navigator.clipboard` read/write |\n| `microphone` | `getUserMedia({ audio: true })` |\n| `camera` | `getUserMedia({ video: true })` |\n| `location` | `navigator.geolocation` |\n| `media_capture` | `getDisplayMedia()` \u2014 screen/window capture |\n| `filesystem` | `echoclaw.invoke('fs.pick', {})` / `echoclaw.invoke('fs.save', {})` \u2014 native file dialogs |\n| `local_network` | Bridge proxy to localhost (127.0.0.1, ::1) \u2014 for Ollama, local dev servers |\n| `lan_network` | Bridge proxy to private LAN (10.x, 172.x, 192.168.x, .local) \u2014 for NAS, home automation |\n| `background_task` | Register periodic sync tasks (min 60s interval, max 5 active, 10s timeout per run) |\n| `auth` | `echoclaw.auth.*` \u2014 credential management, OAuth flow support |\n\n### Bridge-only (shell mediates all calls)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `device_control` | Hardware control \u2014 shell API only, never direct |\n\n---\n\n## 4. Shared Libraries\n\nPre-bundled libraries injected by the shell **before** app HTML. They do NOT count toward the HTML size limit.\n\nDeclare in manifest: `\"shared_libs\": [\"chart.js\", \"dayjs\"]`\n\n| Library | Version | Global | Size (minified) |\n|---------|---------|--------|-----------------|\n| `chart.js` | 4.4.0 | `Chart` | ~197 KB |\n| `d3` | 7.9.0 | `d3` | ~269 KB |\n| `marked` | 12.0.0 | `marked` | ~45 KB |\n| `prism` | 1.29.0 | `Prism` | ~80 KB |\n| `dayjs` | 1.11.10 | `dayjs` | ~7 KB |\n| `sortablejs` | 1.15.0 | `Sortable` | ~45 KB |\n\n**Usage example:**\n```js\n// manifest: { \"shared_libs\": [\"chart.js\"] }\n// In your HTML <script>:\nconst chart = new Chart(ctx, {\n type: 'bar',\n data: { labels: ['Mon','Tue','Wed'], datasets: [{ data: [10,20,30] }] }\n});\n```\n\n---\n\n## 5. Resource Limits Summary\n\n| Resource | Limit |\n|----------|-------|\n| **HTML size** | 1 MB hard limit (800 KB warning). Shared libs excluded. |\n| **Bridge calls** | 60/sec sustained + 120 burst/sec |\n| **KV storage** | 5 MB total, 1,000 keys max |\n| **SQLite** | 10 MB auto \u2192 50 MB (prompt) \u2192 200 MB (second prompt) |\n| **SQLite queries** | 1,000 rows/query, 200 TPS |\n| **Sandbox files** | 250 MB per app |\n| **File picker** | 10 MB read, 50 MB save |\n| **WebSocket** | 3 connections (5 extended), 1 MB/message |\n| **Notifications** | 10/minute |\n| **App comm** | 20 msg/sec, 64 KB/payload |\n| **State save** | 512 KB |\n| **Background tasks** | 5 active, 60s min interval, 10s timeout |\n| **Shortcuts** | 50 per app |\n| **Network allow** | 20 entries max |\n| **Crash restarts** | 3/hour (then stopped) |\n\n---\n\n## 6. How to Build Functional Apps\n\n> **The difference between a demo and a real app is not UI \u2014 it is whether the app actually WORKS.**\n> A beautiful expense tracker that loses data on reload is useless. A plain calculator that computes correctly is valuable.\n> This section teaches you the patterns that make apps functional, not just pretty.\n\n### 6.1 CRUD Pattern \u2014 Data That Survives Reload\n\nAny app that manages a list (tasks, expenses, notes, contacts) MUST persist data. Use `echoclaw.storage` for simple cases, `echoclaw.db` for structured/queryable data.\n\n**Complete example \u2014 Expense Tracker with KV Storage:**\n\n```html\n<div id=\"app\">\n <h2 id=\"total\">Total: $0.00</h2>\n <form id=\"add-form\">\n <input id=\"desc\" type=\"text\" placeholder=\"Description\" required />\n <input id=\"amount\" type=\"number\" step=\"0.01\" min=\"0.01\" placeholder=\"Amount\" required />\n <select id=\"category\">\n <option value=\"food\">Food</option>\n <option value=\"transport\">Transport</option>\n <option value=\"other\">Other</option>\n </select>\n <button type=\"submit\">Add Expense</button>\n </form>\n <div id=\"error\" style=\"color:var(--ec-danger);display:none\"></div>\n <ul id=\"list\"></ul>\n</div>\n<script>\n // \u2500\u2500 State \u2500\u2500\n let expenses = [];\n\n // \u2500\u2500 Persistence \u2500\u2500\n async function loadExpenses() {\n const saved = await echoclaw.storage.get('expenses');\n if (saved) expenses = saved;\n render();\n }\n async function saveExpenses() {\n await echoclaw.storage.set('expenses', expenses);\n }\n\n // \u2500\u2500 CRUD Operations \u2500\u2500\n function addExpense(desc, amount, category) {\n const expense = {\n id: Date.now().toString(),\n desc: desc.trim(),\n amount: parseFloat(amount),\n category,\n date: new Date().toISOString()\n };\n expenses.unshift(expense);\n saveExpenses();\n render();\n }\n\n function deleteExpense(id) {\n expenses = expenses.filter(e => e.id !== id);\n saveExpenses();\n render();\n }\n\n // \u2500\u2500 Render \u2500\u2500\n function render() {\n const total = expenses.reduce((s, e) => s + e.amount, 0);\n document.getElementById('total').textContent = `Total: $${total.toFixed(2)}`;\n\n const list = document.getElementById('list');\n if (expenses.length === 0) {\n list.innerHTML = '<li class=\"empty\">No expenses yet. Add one above.</li>';\n return;\n }\n list.innerHTML = expenses.map(e => `\n <li>\n <span>${e.desc} \u2014 $${e.amount.toFixed(2)} (${e.category})</span>\n <button onclick=\"deleteExpense('${e.id}')\">Delete</button>\n </li>\n `).join('');\n }\n\n // \u2500\u2500 Form Handling with Validation \u2500\u2500\n document.getElementById('add-form').addEventListener('submit', (e) => {\n e.preventDefault();\n const desc = document.getElementById('desc').value;\n const amount = document.getElementById('amount').value;\n const category = document.getElementById('category').value;\n const errorEl = document.getElementById('error');\n\n // Validate\n if (!desc.trim()) {\n errorEl.textContent = 'Description cannot be empty';\n errorEl.style.display = 'block';\n return;\n }\n if (isNaN(amount) || parseFloat(amount) <= 0) {\n errorEl.textContent = 'Amount must be a positive number';\n errorEl.style.display = 'block';\n return;\n }\n\n errorEl.style.display = 'none';\n addExpense(desc, amount, category);\n e.target.reset();\n });\n\n // \u2500\u2500 Init: Load persisted data \u2500\u2500\n loadExpenses();\n</script>\n```\n\n**Why this works:** Data is loaded from storage on startup, saved after every change, form inputs are validated, empty state is handled, and delete actually removes data from both UI and storage.\n\n### 6.2 External Data Pattern \u2014 Real Data, Not Fake Data\n\nWhen the user asks for weather, stocks, news, or any external data \u2014 you MUST fetch real data from real APIs. Never hardcode fake data and pretend it is real.\n\n**Complete example \u2014 Weather Widget with fetch + error handling:**\n\n```html\n<div id=\"weather\">\n <div id=\"loading\">Loading weather...</div>\n <div id=\"content\" style=\"display:none\">\n <h2 id=\"city-name\"></h2>\n <div id=\"temp\"></div>\n <div id=\"condition\"></div>\n <button id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"error-state\" style=\"display:none\">\n <p id=\"error-msg\"></p>\n <button id=\"retry-btn\">Retry</button>\n </div>\n <div id=\"last-updated\"></div>\n</div>\n<script>\n const API_URL = 'https://wttr.in'; // Must be in manifest.network_allow\n\n async function fetchWeather(city = 'London') {\n const loading = document.getElementById('loading');\n const content = document.getElementById('content');\n const errorState = document.getElementById('error-state');\n\n // Show loading, hide others\n loading.style.display = 'block';\n content.style.display = 'none';\n errorState.style.display = 'none';\n\n try {\n const response = await echoclaw.invoke('fetch', {\n url: `${API_URL}/${encodeURIComponent(city)}?format=j1`,\n method: 'GET'\n });\n\n if (response.status !== 200) {\n throw new Error(`API returned status ${response.status}`);\n }\n\n const data = JSON.parse(response.body);\n const current = data.current_condition[0];\n\n document.getElementById('city-name').textContent = city;\n document.getElementById('temp').textContent = `${current.temp_C}\xB0C`;\n document.getElementById('condition').textContent = current.weatherDesc[0].value;\n document.getElementById('last-updated').textContent =\n `Updated: ${new Date().toLocaleTimeString()}`;\n\n // Cache for offline fallback\n await echoclaw.storage.set('weather_cache', {\n data: { city, temp: current.temp_C, condition: current.weatherDesc[0].value },\n timestamp: Date.now()\n });\n\n loading.style.display = 'none';\n content.style.display = 'block';\n } catch (err) {\n loading.style.display = 'none';\n\n // Try cached data first\n const cache = await echoclaw.storage.get('weather_cache');\n if (cache && Date.now() - cache.timestamp < 3600000) {\n document.getElementById('city-name').textContent = cache.data.city;\n document.getElementById('temp').textContent = `${cache.data.temp}\xB0C`;\n document.getElementById('condition').textContent = cache.data.condition + ' (cached)';\n content.style.display = 'block';\n } else {\n document.getElementById('error-msg').textContent =\n 'Could not load weather data. Check your connection.';\n errorState.style.display = 'block';\n }\n }\n }\n\n document.getElementById('refresh-btn')?.addEventListener('click', () => fetchWeather());\n document.getElementById('retry-btn')?.addEventListener('click', () => fetchWeather());\n fetchWeather();\n</script>\n```\n\n**Key elements:** Loading state shown during fetch, error state with retry button, cached fallback for offline resilience, response status checking, user-friendly error messages (never raw error codes).\n\n### 6.3 Real-Time Update Pattern \u2014 Timers That Actually Tick\n\nWhen the user asks for a timer, clock, countdown, or any time-based feature \u2014 it MUST update in real time using `setInterval`. Never show a static time display.\n\n```js\n// \u2500\u2500 Pomodoro Timer Core Logic \u2500\u2500\nlet remaining = 25 * 60; // seconds\nlet running = false;\nlet intervalId = null;\n\nfunction startTimer() {\n if (running) return;\n running = true;\n intervalId = setInterval(() => {\n remaining--;\n updateDisplay();\n if (remaining <= 0) {\n clearInterval(intervalId);\n running = false;\n echoclaw.emit('notify', {\n title: 'Timer Complete',\n body: 'Your focus session is done!'\n });\n }\n }, 1000);\n}\n\nfunction pauseTimer() {\n clearInterval(intervalId);\n running = false;\n}\n\nfunction resetTimer(minutes = 25) {\n clearInterval(intervalId);\n running = false;\n remaining = minutes * 60;\n updateDisplay();\n}\n\nfunction updateDisplay() {\n const min = Math.floor(remaining / 60).toString().padStart(2, '0');\n const sec = (remaining % 60).toString().padStart(2, '0');\n document.getElementById('timer').textContent = `${min}:${sec}`;\n // Update progress ring/bar\n const progress = 1 - remaining / (25 * 60);\n document.getElementById('progress').style.width = `${progress * 100}%`;\n}\n\n// Wire buttons to real functions\ndocument.getElementById('start-btn').addEventListener('click', startTimer);\ndocument.getElementById('pause-btn').addEventListener('click', pauseTimer);\ndocument.getElementById('reset-btn').addEventListener('click', resetTimer);\nupdateDisplay(); // Show initial state immediately\n```\n\n### 6.4 Form Validation Pattern \u2014 Reject Bad Input Before It Causes Problems\n\nEvery form MUST validate input before processing. Never trust user input blindly.\n\n```js\nfunction validateForm(fields) {\n const errors = [];\n\n // Required field check\n if (!fields.name?.trim()) {\n errors.push({ field: 'name', message: 'Name is required' });\n }\n\n // Type check\n if (fields.email && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(fields.email)) {\n errors.push({ field: 'email', message: 'Invalid email format' });\n }\n\n // Range check\n if (fields.age !== undefined) {\n const age = parseInt(fields.age);\n if (isNaN(age) || age < 0 || age > 150) {\n errors.push({ field: 'age', message: 'Age must be between 0 and 150' });\n }\n }\n\n // Length check\n if (fields.bio && fields.bio.length > 500) {\n errors.push({ field: 'bio', message: 'Bio must be under 500 characters' });\n }\n\n return errors;\n}\n\nfunction showErrors(errors) {\n // Clear previous errors\n document.querySelectorAll('.field-error').forEach(el => el.remove());\n document.querySelectorAll('.input-error').forEach(el => el.classList.remove('input-error'));\n\n errors.forEach(({ field, message }) => {\n const input = document.getElementById(field);\n if (input) {\n input.classList.add('input-error');\n const errorEl = document.createElement('div');\n errorEl.className = 'field-error';\n errorEl.textContent = message;\n input.parentNode.insertBefore(errorEl, input.nextSibling);\n }\n });\n}\n\nfunction showSuccess(message) {\n const toast = document.getElementById('toast');\n toast.textContent = message;\n toast.classList.add('show');\n setTimeout(() => toast.classList.remove('show'), 3000);\n}\n\n// Form submission with validation\ndocument.getElementById('form').addEventListener('submit', async (e) => {\n e.preventDefault();\n const fields = Object.fromEntries(new FormData(e.target));\n const errors = validateForm(fields);\n\n if (errors.length > 0) {\n showErrors(errors);\n return;\n }\n\n // Process valid data\n await echoclaw.storage.set(`record_${Date.now()}`, fields);\n showSuccess('Saved successfully!');\n e.target.reset();\n});\n```\n\n### 6.5 Data Visualization Pattern \u2014 Charts with Real Dynamic Data\n\nWhen using Chart.js or D3, always bind charts to real data sources (storage, fetch, or user input). Never show a chart with hardcoded demo numbers that the user cannot change.\n\n```js\n// manifest: { \"shared_libs\": [\"chart.js\"] }\n\nlet myChart = null;\n\nasync function loadAndRenderChart() {\n // Load real data from storage\n const expenses = await echoclaw.storage.get('expenses') || [];\n\n // Aggregate by category\n const byCategory = {};\n expenses.forEach(e => {\n byCategory[e.category] = (byCategory[e.category] || 0) + e.amount;\n });\n\n const labels = Object.keys(byCategory);\n const data = Object.values(byCategory);\n\n // Handle empty state\n if (labels.length === 0) {\n document.getElementById('chart-empty').style.display = 'block';\n document.getElementById('chart-container').style.display = 'none';\n return;\n }\n\n document.getElementById('chart-empty').style.display = 'none';\n document.getElementById('chart-container').style.display = 'block';\n\n // Destroy previous chart instance to avoid memory leak\n if (myChart) myChart.destroy();\n\n const ctx = document.getElementById('myChart').getContext('2d');\n myChart = new Chart(ctx, {\n type: 'doughnut',\n data: {\n labels,\n datasets: [{\n data,\n backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']\n }]\n },\n options: {\n responsive: true,\n plugins: {\n legend: { position: 'bottom', labels: { color: 'var(--ec-text)' } }\n }\n }\n });\n}\n\n// Re-render when data changes\nloadAndRenderChart();\n```\n\n### 6.6 Data Import/Export Pattern \u2014 Getting Data In and Out\n\nApps that manage data SHOULD support import and export so users don't feel trapped.\n\n```js\n// \u2500\u2500 Export to CSV \u2500\u2500\nasync function exportData() {\n const expenses = await echoclaw.storage.get('expenses') || [];\n if (expenses.length === 0) {\n showToast('No data to export');\n return;\n }\n\n const header = 'Date,Description,Amount,Category\\n';\n const rows = expenses.map(e =>\n `${e.date},${e.desc.replace(/,/g, ';')},${e.amount},${e.category}`\n ).join('\\n');\n\n const result = await echoclaw.invoke('fs.save', {\n content: header + rows,\n defaultName: 'expenses.csv',\n accept: ['.csv'],\n encoding: 'utf-8'\n });\n\n if (result.saved) showToast('Exported successfully!');\n}\n\n// \u2500\u2500 Import from CSV \u2500\u2500\nasync function importData() {\n const file = await echoclaw.invoke('fs.pick', {\n accept: ['.csv'],\n maxSizeBytes: 5 * 1024 * 1024\n });\n\n if (!file) return; // User cancelled\n\n try {\n const lines = file.content.split('\\n').filter(l => l.trim());\n const newExpenses = lines.slice(1).map(line => { // Skip header\n const [date, desc, amount, category] = line.split(',');\n if (!desc || isNaN(parseFloat(amount))) throw new Error('Invalid format');\n return {\n id: Date.now().toString() + Math.random(),\n date, desc, amount: parseFloat(amount), category: category || 'other'\n };\n });\n\n const existing = await echoclaw.storage.get('expenses') || [];\n await echoclaw.storage.set('expenses', [...newExpenses, ...existing]);\n showToast(`Imported ${newExpenses.length} records`);\n render();\n } catch (err) {\n showToast('Import failed: invalid CSV format');\n }\n}\n```\n\n---\n\n## 7. Functional Anti-Patterns \u2014 Common Mistakes That Break Apps\n\nThese are the most frequent reasons apps look good but don't work. **Avoid every one of these.**\n\n### 7.1 Dead Buttons \u2014 Functions Defined But Never Connected\n\n```js\n// \u274C WRONG: Function exists but button has no event listener\nfunction saveNote() { /* ... */ }\n// <button id=\"save-btn\">Save</button>\n// ... but nobody called addEventListener!\n\n// \u2705 RIGHT: Always wire the handler\ndocument.getElementById('save-btn').addEventListener('click', saveNote);\n```\n\n**Self-check:** After writing your HTML, search for every `<button>`, `<a>`, and `<form>`. Does each one have a corresponding event listener or `onclick`? If not, it is a dead button.\n\n### 7.2 Fake Data Pretending to Be Real\n\n```js\n// \u274C WRONG: Hardcoded array shown to user as if it's their data\nconst tasks = [\n { title: 'Buy groceries', done: false },\n { title: 'Walk the dog', done: true }\n];\n\n// \u2705 RIGHT: Start empty, load from storage, let user create data\nlet tasks = [];\nasync function init() {\n tasks = await echoclaw.storage.get('tasks') || [];\n render();\n}\n```\n\n**Rule:** If the user didn't create the data, it must not be there. The only exception is explicit demo/sample data that is clearly labeled as such.\n\n### 7.3 Silent Failures \u2014 Fetch Errors With No User Feedback\n\n```js\n// \u274C WRONG: User clicks refresh, nothing happens, no idea why\nasync function loadData() {\n try {\n const res = await echoclaw.invoke('fetch', { url: API_URL });\n renderData(JSON.parse(res.body));\n } catch (e) {\n console.log(e); // User sees NOTHING\n }\n}\n\n// \u2705 RIGHT: Show the user what happened and give them a way to recover\nasync function loadData() {\n showLoading(true);\n try {\n const res = await echoclaw.invoke('fetch', { url: API_URL });\n if (res.status !== 200) throw new Error(`Server error (${res.status})`);\n renderData(JSON.parse(res.body));\n } catch (e) {\n showError('Could not load data. Please check your connection and try again.');\n } finally {\n showLoading(false);\n }\n}\n```\n\n### 7.4 Volatile Data \u2014 Only In Memory, Lost On Reload\n\n```js\n// \u274C WRONG: Data only lives in a JS variable\nlet notes = [];\nfunction addNote(text) {\n notes.push({ text, time: Date.now() });\n render();\n}\n// User adds 10 notes, closes app, reopens \u2192 all gone\n\n// \u2705 RIGHT: Persist after every mutation\nasync function addNote(text) {\n notes.push({ text, time: Date.now() });\n await echoclaw.storage.set('notes', notes); // \u2190 This line is the difference\n render();\n}\n```\n\n### 7.5 No Input Validation \u2014 Trusting Everything The User Types\n\n```js\n// \u274C WRONG: Accept anything, crash later\nfunction addExpense(amount) {\n total += amount; // What if amount is \"abc\"? Or -999?\n}\n\n// \u2705 RIGHT: Validate first, reject with clear message\nfunction addExpense(input) {\n const amount = parseFloat(input);\n if (isNaN(amount) || amount <= 0) {\n showError('Please enter a positive number');\n return;\n }\n if (amount > 1000000) {\n showError('Amount seems too large. Please check.');\n return;\n }\n total += amount;\n save();\n render();\n}\n```\n\n### 7.6 Missing Empty State \u2014 Blank Screen When No Data Exists\n\n```js\n// \u274C WRONG: No data = completely blank page. User confused.\nfunction render() {\n list.innerHTML = items.map(i => `<li>${i.name}</li>`).join('');\n}\n\n// \u2705 RIGHT: Show helpful empty state\nfunction render() {\n if (items.length === 0) {\n list.innerHTML = `\n <div class=\"empty-state\">\n <p>No items yet</p>\n <p>Click \"Add\" to create your first item</p>\n </div>`;\n return;\n }\n list.innerHTML = items.map(i => `<li>${i.name}</li>`).join('');\n}\n```\n\n### 7.7 Calculation Errors \u2014 Wrong Math in Calculators and Trackers\n\n```js\n// \u274C WRONG: Floating point traps\n0.1 + 0.2 // \u2192 0.30000000000000004\n\n// \u2705 RIGHT: Round for display, use integers for currency\nfunction formatMoney(cents) {\n return (cents / 100).toFixed(2);\n}\nfunction parseMoney(dollars) {\n return Math.round(parseFloat(dollars) * 100);\n}\n```\n\n---\n\n## 8. Functional Quality Gate\n\nBefore delivering any app, verify it passes ALL of these functional checks. This is just as important as the visual quality gate.\n\n### Mandatory Checks (ALL must pass)\n\n| # | Check | How to Verify |\n|---|-------|---------------|\n| 1 | **Every button/link triggers real logic** | Search HTML for all `<button>`, `<a>`, `<form>`. Each one must have a working event handler. No dead UI. |\n| 2 | **Data persists across reload** | If the app manages any user data (tasks, notes, settings, scores), it MUST use `echoclaw.storage` or `echoclaw.db`. Close and reopen = data still there. |\n| 3 | **Error states are handled** | Every `fetch` call has `try/catch`. User sees a message + retry option on failure, never a blank screen or console error. |\n| 4 | **Inputs are validated** | Required fields are checked. Numbers are range-checked. Invalid input shows a clear error message near the field. |\n| 5 | **Empty state is designed** | When there is no data (first launch, all items deleted), the app shows a helpful message, not a blank void. |\n| 6 | **User requirements are fully covered** | Re-read the user's original request. Every feature they asked for must be functional, not just present as UI. \"I want to add tasks\" means add WORKS. |\n| 7 | **Calculations are correct** | If the app does math (totals, averages, percentages, dates), spot-check with known values. Use `toFixed()` for currency. |\n| 8 | **No hardcoded fake data** | The app starts empty (or loads from storage). No sample data masquerading as real user data. |\n| 9 | **Real-time features actually update** | Timers tick. Clocks show current time. Countdowns count down. Auto-refresh refreshes. |\n| 10 | **Loading states are shown** | Any async operation (fetch, large data load) shows a loading indicator. User is never left staring at an unresponsive screen. |\n\n### Scoring\n\n| Score | Meaning | Action |\n|-------|---------|--------|\n| 10/10 | Ship it | Deliver with confidence |\n| 8-9/10 | Minor gaps | Fix before delivery |\n| \u22647/10 | Not ready | Rework \u2014 the app does not actually work |\n\n### Decision Tree \u2014 Which Storage Pattern To Use\n\n```\nUser request involves data?\n\u251C\u2500\u2500 No (static page, calculator, presentation) \u2192 No storage needed\n\u251C\u2500\u2500 Yes\n\u2502 \u251C\u2500\u2500 Simple key-value data (settings, preferences, small lists <100 items)\n\u2502 \u2502 \u2192 echoclaw.storage (KV)\n\u2502 \u251C\u2500\u2500 Structured data with queries (filter, sort, search, relations, >100 items)\n\u2502 \u2502 \u2192 echoclaw.db (SQLite)\n\u2502 \u2514\u2500\u2500 Temporary session state (current tab, scroll position)\n\u2502 \u2192 echoclaw.emit('saveState', ...)\n```\n\n### Decision Tree \u2014 When To Use Fetch\n\n```\nApp needs external data?\n\u251C\u2500\u2500 No (offline tools, local data only) \u2192 No fetch needed\n\u251C\u2500\u2500 Yes\n\u2502 \u251C\u2500\u2500 Public API (weather, quotes, exchange rates)\n\u2502 \u2502 \u2192 echoclaw.invoke('fetch', ...) + manifest.network_allow\n\u2502 \u251C\u2500\u2500 User's own API (needs API key)\n\u2502 \u2502 \u2192 config_fields with sensitive:true + echoclaw.invoke('getSecret', ...)\n\u2502 \u2514\u2500\u2500 No API available for this data\n\u2502 \u2192 Tell the user honestly. Do NOT generate fake data.\n```\n\n---\n\n## 9. What You CAN Build (20 Categories)\n\nWhen a user's request matches any of the following scenarios, proactively suggest creating a ClawApp.\n\n**Data & Visualization**\n1. Dashboards & KPI reports (sales, product metrics, team statistics)\n2. Real-time monitoring panels (API health, cloud costs, smart home status)\n3. Data exploration & visualization (CSV/database analysis, interactive charts)\n\n**Management & Productivity**\n4. Project/task management boards (Kanban, to-do lists, OKR tracking)\n5. Roadmaps & timelines (product planning, Gantt charts, study plans)\n6. Calendars & scheduling (weekly calendars, travel itineraries, shift rosters)\n7. Pipeline tracking (CRM, recruiting, job search, fundraising progress)\n8. Ticket & queue management (customer support, bug tracking, incident triage)\n\n**Web Pages & Presentations**\n9. Marketing landing pages & event pages (product launches, conferences)\n10. Personal homepages & portfolios (online resumes, design galleries)\n11. Presentations & reports (client proposals, weekly reports, investor updates)\n\n**Tools & Calculators**\n12. Productivity tools (calculators, converters, countdowns, password generators)\n13. Finance & budget tracking (personal budgets, subscription management)\n14. Decision comparison tools (product selection, vendor comparison)\n15. Business simulators (mortgage calculators, pricing models, nutrition calculators)\n\n**Forms & Data Collection**\n16. Forms & approval workflows (client onboarding, audit checklists)\n17. Inventory & collection management (equipment, books, lab supplies)\n\n**Learning & Entertainment**\n18. Learning & exam prep apps (course tracking, flashcards, spaced repetition)\n19. Interactive mini-games (lotteries, text adventures, quizzes, casual games)\n20. Media galleries & mood boards (inspiration boards, content calendars, recipes)\n\n**Heuristic \u2014 suggest a ClawApp when the request exhibits:**\n- Persistent state (not a one-off answer)\n- Repeated use (the user will return)\n- Visual structure (charts, cards, maps, timelines, calendars)\n- Interactive operations (filtering, editing, toggling, drill-down)\n- Ongoing updates (real-time or periodic data entry)\n- Sharing & presentation (intended to be shown to others)\n- **Fallback: any request that is \"visible and operable\" defaults to a ClawApp**\n\n---\n\n## 10. What You CANNOT Do (Hard Limits)\n\n| Restriction | Reason |\n|-------------|--------|\n| No external `<script src=\"...\">` or CDN links | Self-contained HTML only; use `shared_libs` for common libraries |\n| No direct DOM access to host shell | Sandbox isolation \u2014 communicate only via bridge |\n| No raw TCP/UDP sockets | Only HTTP (fetch proxy) and WebSocket (ws proxy) |\n| No filesystem browsing | File picker opens a dialog; you get content, not paths |\n| No access to other apps' data | Storage, DB, and files are scoped per app ID |\n| No cloud metadata endpoints | 169.254.x and metadata.google.internal are always blocked |\n| No ATTACH/DETACH in SQL | Cannot access other databases or load native extensions |\n| No arbitrary network access | Every target must be in `manifest.network_allow` |\n| No direct iframe `allow` manipulation | Shell controls sandbox attributes based on declared capabilities |\n| No persistent background processes | Background tasks have 10s timeout and 60s min interval |\n| No native OS API calls | Bridge-mediated only; `device_control` is shell-exclusive |\n| No localStorage / IndexedDB | Use `echoclaw.storage` (KV) or `echoclaw.db` (SQLite) instead |\n| No cookies | Sandboxed iframe has no cookie access |\n| No `window.open()` / popups | Single-iframe model |\n| Cannot override reserved keyboard shortcuts | Shell shortcuts (Mod+Q, Mod+W, Mod+C, etc.) take precedence |\n\n### Known Platform Limitations (Temporary)\n\nThe following are **temporary Bridge feature gaps** \u2014 the desktop platform natively supports these capabilities, but the Bridge API layer has not yet implemented them. All items are scheduled for implementation. When encountered, inform the user and provide the listed workaround. Do NOT submit feedback for these items.\n\n| Limitation | Current Status | Workaround |\n|------------|----------------|------------|\n| WebSocket proxy | Stub \u2014 `ws.connect()` throws error | Use fetch polling via `echoclaw.invoke('fetch', ...)` |\n| SQLite database | KV-emulated \u2014 no SQL query capability | Use `echoclaw.storage` KV store |\n| File system picker | Stub \u2014 `fs.pick()` / `fs.save()` unavailable | Use `echoclaw.fs.appRead` / `appWrite` sandbox files |\n| Secret encryption | Plaintext localStorage | Functional but security pending upgrade |\n| Light theme | Hard-coded dark mode | Design for dark theme only |\n| Permission prompts | All permissions auto-approved | Functional but missing user confirmation step |\n| Inter-app broadcast | Stub \u2014 `broadcast()` logs only | N/A \u2014 apps run independently |\n| Keyboard shortcuts | Stub \u2014 `registerShortcut()` logs only | Use button interactions instead |\n| 14 deprecated node types | stat / tabs / table / slider etc. unavailable | Use composition recipes to build equivalent UI |\n\n---\n\n## 11. iframe Sandbox Attributes\n\nThe shell dynamically sets iframe `sandbox` and `allow` based on declared capabilities:\n\n```\nsandbox=\"allow-scripts\"\n```\n\nAdditional `allow` attributes added per capability:\n| Capability | `allow` value |\n|------------|---------------|\n| `audio` | `autoplay` |\n| `clipboard` | `clipboard-read; clipboard-write` |\n| `microphone` | `microphone` |\n| `camera` | `camera` |\n| `location` | `geolocation` |\n| `media_capture` | `display-capture` |\n\nNo `allow-same-origin` \u2014 apps cannot access host cookies, localStorage, or IndexedDB.\n\n---\n\n## 12. Design Constraints\n\n- **Viewport:** Design for 1024x768 iframe; minimum safe area is 544x548\n- **Theme:** Dark mode first. ALL colors via `var(--ec-xxx)` CSS variables (only `manifest.color` hex is exempt)\n- **Layout:** No left sidebar (reserved by shell). Bottom-right 64x64px reserved for host Info button.\n- **Icons:** SVG only (inline). No emoji, no icon fonts.\n- **Localization:** Bilingual minimum (en + zh-CN) using a `t()` helper function.\n- **Interaction:** All interactive elements MUST have hover + focus states.\n\n---\n\n## 13. Events Reference\n\n### Host \u2192 App Events\n\n| Event | Payload | When |\n|-------|---------|------|\n| `theme:changed` | `{ theme, variables }` | User switches theme |\n| `locale:changed` | `{ locale }` | User changes language |\n| `resize` | `{ width, height }` | iframe resized |\n| `stateRestored` | `(savedState)` | App state restored on load |\n| `ws:open` | `{ channel }` | WebSocket connected |\n| `ws:message` | `{ channel, data }` | WebSocket message received |\n| `ws:error` | `{ channel, error }` | WebSocket error |\n| `ws:close` | `{ channel, code, reason }` | WebSocket closed |\n| `app_comm:message` | `{ senderAppId, payload }` | Message from another app |\n| `auth:sessionExpired` | `{ reason, key, providerId }` | Auth session expired |\n| `auth:tokenRefreshed` | `{ key, providerId, expiresAt }` | Token auto-refreshed |\n| `shortcut:triggered` | `{ id, keys }` | Registered shortcut pressed |\n\n---\n\nFor Web API to EchoClaw Bridge migration mapping, see `WEB_API_MIGRATION.md`.\n\n---\n\n## 14. Gold-Standard Reference App\n\nFor a complete, production-quality example that demonstrates every best practice (CRUD, search, tags, export, i18n, form validation, toast feedback, confirm dialogs, empty/loading/error states, responsive layout, custom scrollbars, and a 20/20 Quality Gate score), see the reference app:\n\n**`packages/claw-engine/src/factory-apps/reference-app.claw.json`** \u2014 Personal Notes app, the gold standard for EchoClaw app quality.\n\n---\n\n## 15. Quick Reference Card\n\nThe most-used APIs at a glance. See sections above for full signatures and error handling.\n\n| Task | One-liner |\n|------|-----------|\n| Read KV | `const v = await echoclaw.storage.get('key')` |\n| Write KV | `await echoclaw.storage.set('key', value)` |\n| Delete KV | `await echoclaw.storage.remove('key')` |\n| Clear all | `await echoclaw.storage.clear()` |\n| SQL SELECT | `const rows = await echoclaw.db.query('SELECT * FROM t WHERE x=?', [val])` |\n| SQL INSERT | `await echoclaw.db.exec('INSERT INTO t (col) VALUES (?)', [val])` |\n| HTTP GET | `const r = await echoclaw.invoke('fetch', { url, method: 'GET' })` |\n| HTTP POST | `const r = await echoclaw.invoke('fetch', { url, method: 'POST', body: JSON.stringify(d), headers: {'Content-Type':'application/json'} })` |\n| Open file | `const f = await echoclaw.invoke('fs.pick', { accept: ['.csv'] })` |\n| Save file | `await echoclaw.invoke('fs.save', { content: text, defaultName: 'export.csv' })` |\n| Notify | `echoclaw.emit('notify', { title: 'Done', body: 'Saved' })` |\n| Get context | `const ctx = echoclaw.getContext()` \u2192 `ctx.locale`, `ctx.device.width` |\n| Subscribe | `const unsub = echoclaw.subscribe('theme:changed', cb)` |\n| Request permission | `await echoclaw.requestPermission('clipboard')` |\n\n**Always wrap bridge calls in try/catch.** Show user-facing error messages \u2014 never expose error codes or stack traces.\n\n";
|
|
8429
|
+
var SANDBOX_CAPABILITIES_default = "# EchoClaw Sandbox Capabilities Reference\n\n> For OpenClaw: Complete reference of what APIs, capabilities, and resources are available inside the EchoClaw sandbox.\n> Engine version: v1.9.3\n\n---\n\n## 1. Bridge Overview\n\nAll app code runs inside a sandboxed `<iframe>`. There is **no direct access** to the host OS, filesystem, or network. Everything goes through `window.echoclaw` \u2014 the Bridge object injected by the host shell.\n\nCommunication model:\n- **`echoclaw.invoke(action, payload)`** \u2014 Request-response (async, returns Promise)\n- **`echoclaw.emit(action, payload)`** \u2014 Fire-and-forget (no return value)\n- **`echoclaw.subscribe(event, callback)`** \u2014 Event listener (returns unsubscribe function)\n- **`echoclaw.getContext()`** \u2014 Sync, returns current theme/device/capabilities\n\n---\n\n## 2. Bridge API \u2014 Complete Reference\n\n### 2.1 Context\n\n```js\nconst ctx = echoclaw.getContext();\n// Returns:\n// {\n// theme: 'dark', // current theme\n// locale: 'en' | 'zh-CN',\n// device: { width, height, pixelRatio, platform },\n// capabilities: ['ui', 'storage', 'network', ...],\n// appId: 'my-app-id'\n// }\n```\n\n### 2.2 Key-Value Storage (`echoclaw.storage`)\n\nPersistent key-value store scoped to the app. No capability declaration needed (auto-granted with `storage`).\n\n```js\n// Set a value (string, number, object \u2014 auto JSON-serialized)\nawait echoclaw.storage.set('key', value);\n\n// Get a value (returns parsed value, or null if not found)\nconst val = await echoclaw.storage.get('key');\n\n// Remove a key\nawait echoclaw.storage.remove('key');\n\n// List all keys\nconst keys = await echoclaw.storage.keys();\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max keys | 1,000 |\n| Max total size | 5 MB |\n\n### 2.3 SQLite Database (`echoclaw.db`)\n\nFull SQL database per app. Requires `database` capability (auto-granted).\n\n```js\n// Execute DDL/DML (CREATE, INSERT, UPDATE, DELETE)\nconst result = await echoclaw.db.exec(\n 'INSERT INTO tasks (title, done) VALUES (?, ?)',\n ['Buy milk', false]\n);\n// result: { changes: 1, lastInsertRowid: 42 }\n\n// Query (SELECT)\nconst rows = await echoclaw.db.query(\n 'SELECT * FROM tasks WHERE done = ? LIMIT ?',\n [false, 50]\n);\n// rows: [{ id: 1, title: 'Buy milk', done: 0 }, ...]\n```\n\n**Allowed SQL:** All standard DDL (CREATE TABLE, DROP TABLE, ALTER TABLE) and DML (SELECT, INSERT, UPDATE, DELETE). Safe PRAGMAs like `journal_mode`, `foreign_keys` are allowed.\n\n**Blocked SQL:** `ATTACH`, `DETACH`, `LOAD_EXTENSION`, and dangerous PRAGMAs (`database_list`, `compile_options`, `integrity_check`, `quick_check`).\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Auto quota | 10 MB (no user prompt) |\n| Standard max | 50 MB (requires user approval) |\n| Extended max | 200 MB (requires second approval) |\n| Max rows per query | 1,000 (bridge enforces pagination) |\n| Max transactions/sec | 200 |\n\n**Schema versioning:** Use `manifest.db_version` (integer). When manifest version > stored version, run your migration SQL on startup.\n\n### 2.4 Sandbox File System (`echoclaw.fs.app*`)\n\nRead/write files within the app's sandboxed directory. Requires `storage` capability (auto-granted).\n\n```js\n// Write a file (string content)\nawait echoclaw.fs.appWrite('data/export.json', JSON.stringify(data));\n\n// Read a file\nconst content = await echoclaw.fs.appRead('data/export.json');\n\n// List files in a directory\nconst files = await echoclaw.fs.appList('data/');\n// files: ['export.json', 'backup.json']\n\n// Remove a file\nawait echoclaw.fs.appRemove('data/export.json');\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max sandbox storage | 250 MB per app |\n\n### 2.5 External File Picker (`echoclaw.invoke('fs.pick', ...)` / `echoclaw.invoke('fs.save', ...)`)\n\nOpens the native OS file dialog. Requires `filesystem` capability (prompt-tier \u2014 user must approve).\n\n```js\n// Open file picker\nconst file = await echoclaw.invoke('fs.pick', {\n accept: ['.json', '.csv', '.txt'], // allowed extensions\n maxSizeBytes: 10 * 1024 * 1024 // default 10MB\n});\n// file: { name: 'data.csv', size: 2048, content: '...', encoding: 'utf-8', mimeType: 'text/csv' }\n// Returns null if user cancels\n\n// Save file dialog\nconst result = await echoclaw.invoke('fs.save', {\n content: csvString,\n defaultName: 'export.csv',\n accept: ['.csv'],\n encoding: 'utf-8' // or 'base64' for binary\n});\n// result: { saved: true, name: 'export.csv' }\n```\n\n**Notes:**\n- File path is never exposed \u2014 only basename returned\n- Text extensions (.json, .csv, .txt, .md, .html, .js, etc.) are read as UTF-8; all others as base64\n- Pick max: 10 MB; Save max: 50 MB\n\n### 2.6 Network \u2014 Fetch Proxy (`echoclaw.invoke('fetch', ...)`)\n\nAll HTTP requests go through the bridge proxy. Requires `network` capability (auto-granted) AND `manifest.network_allow` whitelist.\n\n```js\nconst response = await echoclaw.invoke('fetch', {\n url: 'https://api.example.com/data',\n method: 'GET',\n headers: { 'Authorization': 'Bearer ...' }\n});\n// response: { status: 200, headers: {...}, body: '...' }\n```\n\n**Network access rules:**\n- `manifest.network_allow` MUST list every domain the app connects to\n- No whitelist = no network access (even with `network` capability)\n- Max 20 entries in `network_allow`\n- Localhost (127.0.0.1, ::1) requires `local_network` capability + user approval\n- Private LAN (10.x, 172.x, 192.168.x, .local) requires `lan_network` capability + user approval\n- Cloud metadata (169.254.x, metadata.google.internal) is ALWAYS blocked \u2014 no capability can unblock\n\n### 2.7 WebSocket (`echoclaw.ws`)\n\nProxied WebSocket connections. Requires `network` capability + `manifest.network_allow`.\n\n```js\n// Connect (channel is your unique ID for this connection)\nawait echoclaw.invoke('ws.connect', {\n channel: 'my-channel',\n opts: { url: 'wss://api.example.com/ws', protocols: ['v1'] }\n});\n\n// Send a message\nawait echoclaw.invoke('ws.send', { channel: 'my-channel', data: { type: 'subscribe', topic: 'updates' } });\n// or send raw string:\nawait echoclaw.invoke('ws.send', { channel: 'my-channel', data: 'ping' });\n\n// Listen for events\nechoclaw.subscribe('ws:open', ({ channel }) => { ... });\nechoclaw.subscribe('ws:message', ({ channel, data }) => { ... });\nechoclaw.subscribe('ws:error', ({ channel, error }) => { ... });\nechoclaw.subscribe('ws:close', ({ channel, code, reason }) => { ... });\n\n// Close\nawait echoclaw.invoke('ws.close', { channel: 'my-channel' });\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max concurrent connections | 3 (default), 5 (extended, requires prompt) |\n| Max message size | 1 MB |\n\n**URL validation:** Only `ws:` and `wss:` protocols accepted. Same network access rules as fetch (whitelist, SSRF guard).\n\n### 2.8 Notifications (`echoclaw.emit`)\n\n```js\nechoclaw.emit('notify', {\n title: 'Timer Complete',\n body: 'Your 25-minute session is done!',\n // shell renders the notification \u2014 app does not control presentation\n});\n```\n\n**Limit:** Max 10 notifications per minute per app.\n\n### 2.9 Inter-App Communication (`app_comm`)\n\nSend messages between apps. Requires `app_comm` capability (auto-granted). Shell injects unforgeable sender identity.\n\n```js\n// Send to another app\nechoclaw.emit('app_comm', {\n targetAppId: 'other-app',\n payload: { action: 'refresh', data: {...} }\n});\n\n// Receive from other apps\nechoclaw.subscribe('app_comm:message', ({ senderAppId, payload }) => {\n // senderAppId is injected by shell \u2014 cannot be spoofed\n});\n```\n\n**Limits:**\n| Resource | Limit |\n|----------|-------|\n| Max messages/sec | 20 per app |\n| Max payload size | 64 KB |\n\n### 2.10 Authentication (`echoclaw.auth`)\n\nManages credentials for third-party services. Requires `auth` capability (prompt-tier \u2014 user must approve).\n\n```js\n// \u2500\u2500 Tier 1: Plaintext session (non-sensitive fields only) \u2500\u2500\nawait echoclaw.auth.setSession({ userId: '123', displayName: 'Alice' });\nconst session = await echoclaw.auth.getSession();\nawait echoclaw.auth.clearSession();\n\n// \u2500\u2500 Tier 2: Keychain read-only (install-time API keys) \u2500\u2500\nconst apiKey = await echoclaw.invoke('getSecret', { key: 'openai_api_key' });\n\n// \u2500\u2500 Tier 3: Keychain read-write (runtime tokens) \u2500\u2500\nawait echoclaw.auth.setSecret('google.access_token', tokenValue);\nconst token = await echoclaw.auth.getSecret('google.access_token');\nawait echoclaw.auth.deleteSecret('google.access_token');\n\n// \u2500\u2500 Auto-refresh token \u2500\u2500\nconst validToken = await echoclaw.auth.getValidToken('google.access_token');\n\n// \u2500\u2500 Check auth status \u2500\u2500\nconst isAuthed = await echoclaw.auth.isAuthenticated({ provider: 'google' });\n\n// \u2500\u2500 Logout (clears runtime tokens, preserves install-time secrets) \u2500\u2500\nawait echoclaw.auth.logout();\n```\n\n**Auth events:**\n```js\nechoclaw.subscribe('auth:sessionExpired', ({ reason, key, providerId }) => { ... });\nechoclaw.subscribe('auth:tokenRefreshed', ({ key, providerId, expiresAt }) => { ... });\n```\n\n**Token naming convention:** `{providerId}.{tokenType}` \u2014 e.g., `google.access_token`, `github.refresh_token`.\n\n**Manifest requirement:** Apps using auth MUST declare `manifest.auth_profile` with strategy, token_tier, providers, session_fields, and unauthenticated_behavior.\n\n### 2.11 Keyboard Shortcuts (`echoclaw.invoke('registerShortcut', ...)`)\n\nRegister global keyboard shortcuts. Requires `shortcuts` capability.\n\n```js\nechoclaw.invoke('registerShortcut', {\n id: 'sc_search',\n keys: 'Ctrl+K' // or 'Cmd+Shift+P', 'Escape', 'Alt+Enter'\n});\n\nechoclaw.subscribe('shortcut:triggered', ({ id, keys }) => {\n if (id === 'sc_search') openSearchDialog();\n});\n```\n\n**Key format:** Modifiers (`Ctrl`, `Cmd`, `Shift`, `Alt`) + key, joined by `+`. `Ctrl` and `Cmd` are unified as `Mod` internally (Cmd on macOS, Ctrl on Windows/Linux).\n\n**Reserved shortcuts (cannot be overridden):**\n`Mod+Q`, `Mod+W`, `Mod+N`, `Mod+,`, `Mod+1-9`, `Mod+L`, `Mod+F`, `Mod+Z`, `Mod+Shift+Z`, `Mod+A`, `Mod+C`, `Mod+X`, `Mod+V`\n\n**Limit:** Max 50 shortcuts per app.\n\n### 2.12 Logging\n\n```js\nechoclaw.log('info', 'User clicked save', { itemCount: 5 });\nechoclaw.log('warn', 'Approaching storage limit');\nechoclaw.log('error', 'Failed to parse CSV', { line: 42 });\n```\n\n### 2.13 State Save/Load\n\n```js\n// Save app state (persisted across sessions)\nechoclaw.emit('saveState', { currentTab: 'settings', filters: [...] });\n\n// On load, state is provided via context or event\nechoclaw.subscribe('stateRestored', (savedState) => {\n applyState(savedState);\n});\n```\n\n**Limit:** Max state payload: 512 KB.\n\n---\n\n## 3. Capability System\n\nEach capability has a **tier** that determines how it is granted:\n\n### Auto-granted (no user prompt needed)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `ui` | Baseline \u2014 HTML rendering in iframe (always implicit) |\n| `timer` | `setTimeout`, `setInterval`, `requestAnimationFrame` |\n| `storage` | `echoclaw.storage.*` (KV) + `echoclaw.fs.app*` (sandbox files) |\n| `notification` | `echoclaw.emit('notify', ...)` \u2014 shell renders notifications |\n| `network` | Fetch proxy via bridge (REQUIRES `network_allow` whitelist) |\n| `audio` | Web Audio API, `<audio>` playback, autoplay |\n| `fullscreen` | `Element.requestFullscreen()` |\n| `app_comm` | Inter-app messaging via bridge |\n| `database` | SQLite per-app database (staged quota: 10MB auto \u2192 50MB \u2192 200MB) |\n| `secure_credential` | OS keychain storage (read via `getSecret`) |\n\n### Prompt-required (user must approve on first use)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `clipboard` | `navigator.clipboard` read/write |\n| `microphone` | `getUserMedia({ audio: true })` |\n| `camera` | `getUserMedia({ video: true })` |\n| `location` | `navigator.geolocation` |\n| `media_capture` | `getDisplayMedia()` \u2014 screen/window capture |\n| `filesystem` | `echoclaw.invoke('fs.pick', {})` / `echoclaw.invoke('fs.save', {})` \u2014 native file dialogs |\n| `local_network` | Bridge proxy to localhost (127.0.0.1, ::1) \u2014 for Ollama, local dev servers |\n| `lan_network` | Bridge proxy to private LAN (10.x, 172.x, 192.168.x, .local) \u2014 for NAS, home automation |\n| `background_task` | Register periodic sync tasks (min 60s interval, max 5 active, 10s timeout per run) |\n| `auth` | `echoclaw.auth.*` \u2014 credential management, OAuth flow support |\n\n### Bridge-only (shell mediates all calls)\n\n| Capability | What It Unlocks |\n|------------|----------------|\n| `device_control` | Hardware control \u2014 shell API only, never direct |\n\n---\n\n## 4. Shared Libraries\n\nPre-bundled libraries injected by the shell **before** app HTML. They do NOT count toward the HTML size limit.\n\nDeclare in manifest: `\"shared_libs\": [\"chart.js\", \"dayjs\"]`\n\n| Library | Version | Global | Size (minified) |\n|---------|---------|--------|-----------------|\n| `chart.js` | 4.4.0 | `Chart` | ~197 KB |\n| `d3` | 7.9.0 | `d3` | ~269 KB |\n| `marked` | 12.0.0 | `marked` | ~45 KB |\n| `prism` | 1.29.0 | `Prism` | ~80 KB |\n| `dayjs` | 1.11.10 | `dayjs` | ~7 KB |\n| `sortablejs` | 1.15.0 | `Sortable` | ~45 KB |\n\n**Usage example:**\n```js\n// manifest: { \"shared_libs\": [\"chart.js\"] }\n// In your HTML <script>:\nconst chart = new Chart(ctx, {\n type: 'bar',\n data: { labels: ['Mon','Tue','Wed'], datasets: [{ data: [10,20,30] }] }\n});\n```\n\n---\n\n## 5. Resource Limits Summary\n\n| Resource | Limit |\n|----------|-------|\n| **HTML size** | 1 MB hard limit (800 KB warning). Shared libs excluded. |\n| **Bridge calls** | 60/sec sustained + 120 burst/sec |\n| **KV storage** | 5 MB total, 1,000 keys max |\n| **SQLite** | 10 MB auto \u2192 50 MB (prompt) \u2192 200 MB (second prompt) |\n| **SQLite queries** | 1,000 rows/query, 200 TPS |\n| **Sandbox files** | 250 MB per app |\n| **File picker** | 10 MB read, 50 MB save |\n| **WebSocket** | 3 connections (5 extended), 1 MB/message |\n| **Notifications** | 10/minute |\n| **App comm** | 20 msg/sec, 64 KB/payload |\n| **State save** | 512 KB |\n| **Background tasks** | 5 active, 60s min interval, 10s timeout |\n| **Shortcuts** | 50 per app |\n| **Network allow** | 20 entries max |\n| **Crash restarts** | 3/hour (then stopped) |\n\n---\n\n## 6. How to Build Functional Apps\n\n> **The difference between a demo and a real app is not UI \u2014 it is whether the app actually WORKS.**\n> A beautiful expense tracker that loses data on reload is useless. A plain calculator that computes correctly is valuable.\n> This section teaches you the patterns that make apps functional, not just pretty.\n\n### 6.1 CRUD Pattern \u2014 Data That Survives Reload\n\nAny app that manages a list (tasks, expenses, notes, contacts) MUST persist data. Use `echoclaw.storage` for simple cases, `echoclaw.db` for structured/queryable data.\n\n**Complete example \u2014 Expense Tracker with KV Storage:**\n\n```html\n<div id=\"app\">\n <h2 id=\"total\">Total: $0.00</h2>\n <form id=\"add-form\">\n <input id=\"desc\" type=\"text\" placeholder=\"Description\" required />\n <input id=\"amount\" type=\"number\" step=\"0.01\" min=\"0.01\" placeholder=\"Amount\" required />\n <select id=\"category\">\n <option value=\"food\">Food</option>\n <option value=\"transport\">Transport</option>\n <option value=\"other\">Other</option>\n </select>\n <button type=\"submit\">Add Expense</button>\n </form>\n <div id=\"error\" style=\"color:var(--ec-danger);display:none\"></div>\n <ul id=\"list\"></ul>\n</div>\n<script>\n // \u2500\u2500 State \u2500\u2500\n let expenses = [];\n\n // \u2500\u2500 Persistence \u2500\u2500\n async function loadExpenses() {\n const saved = await echoclaw.storage.get('expenses');\n if (saved) expenses = saved;\n render();\n }\n async function saveExpenses() {\n await echoclaw.storage.set('expenses', expenses);\n }\n\n // \u2500\u2500 CRUD Operations \u2500\u2500\n function addExpense(desc, amount, category) {\n const expense = {\n id: Date.now().toString(),\n desc: desc.trim(),\n amount: parseFloat(amount),\n category,\n date: new Date().toISOString()\n };\n expenses.unshift(expense);\n saveExpenses();\n render();\n }\n\n function deleteExpense(id) {\n expenses = expenses.filter(e => e.id !== id);\n saveExpenses();\n render();\n }\n\n // \u2500\u2500 Render \u2500\u2500\n function render() {\n const total = expenses.reduce((s, e) => s + e.amount, 0);\n document.getElementById('total').textContent = `Total: $${total.toFixed(2)}`;\n\n const list = document.getElementById('list');\n if (expenses.length === 0) {\n list.innerHTML = '<li class=\"empty\">No expenses yet. Add one above.</li>';\n return;\n }\n list.innerHTML = expenses.map(e => `\n <li>\n <span>${e.desc} \u2014 $${e.amount.toFixed(2)} (${e.category})</span>\n <button onclick=\"deleteExpense('${e.id}')\">Delete</button>\n </li>\n `).join('');\n }\n\n // \u2500\u2500 Form Handling with Validation \u2500\u2500\n document.getElementById('add-form').addEventListener('submit', (e) => {\n e.preventDefault();\n const desc = document.getElementById('desc').value;\n const amount = document.getElementById('amount').value;\n const category = document.getElementById('category').value;\n const errorEl = document.getElementById('error');\n\n // Validate\n if (!desc.trim()) {\n errorEl.textContent = 'Description cannot be empty';\n errorEl.style.display = 'block';\n return;\n }\n if (isNaN(amount) || parseFloat(amount) <= 0) {\n errorEl.textContent = 'Amount must be a positive number';\n errorEl.style.display = 'block';\n return;\n }\n\n errorEl.style.display = 'none';\n addExpense(desc, amount, category);\n e.target.reset();\n });\n\n // \u2500\u2500 Init: Load persisted data \u2500\u2500\n loadExpenses();\n</script>\n```\n\n**Why this works:** Data is loaded from storage on startup, saved after every change, form inputs are validated, empty state is handled, and delete actually removes data from both UI and storage.\n\n### 6.2 External Data Pattern \u2014 Real Data, Not Fake Data\n\nWhen the user asks for weather, stocks, news, or any external data \u2014 you MUST fetch real data from real APIs. Never hardcode fake data and pretend it is real.\n\n**Complete example \u2014 Weather Widget with fetch + error handling:**\n\n```html\n<div id=\"weather\">\n <div id=\"loading\">Loading weather...</div>\n <div id=\"content\" style=\"display:none\">\n <h2 id=\"city-name\"></h2>\n <div id=\"temp\"></div>\n <div id=\"condition\"></div>\n <button id=\"refresh-btn\">Refresh</button>\n </div>\n <div id=\"error-state\" style=\"display:none\">\n <p id=\"error-msg\"></p>\n <button id=\"retry-btn\">Retry</button>\n </div>\n <div id=\"last-updated\"></div>\n</div>\n<script>\n const API_URL = 'https://wttr.in'; // Must be in manifest.network_allow\n\n async function fetchWeather(city = 'London') {\n const loading = document.getElementById('loading');\n const content = document.getElementById('content');\n const errorState = document.getElementById('error-state');\n\n // Show loading, hide others\n loading.style.display = 'block';\n content.style.display = 'none';\n errorState.style.display = 'none';\n\n try {\n const response = await echoclaw.invoke('fetch', {\n url: `${API_URL}/${encodeURIComponent(city)}?format=j1`,\n method: 'GET'\n });\n\n if (response.status !== 200) {\n throw new Error(`API returned status ${response.status}`);\n }\n\n const data = JSON.parse(response.body);\n const current = data.current_condition[0];\n\n document.getElementById('city-name').textContent = city;\n document.getElementById('temp').textContent = `${current.temp_C}\xB0C`;\n document.getElementById('condition').textContent = current.weatherDesc[0].value;\n document.getElementById('last-updated').textContent =\n `Updated: ${new Date().toLocaleTimeString()}`;\n\n // Cache for offline fallback\n await echoclaw.storage.set('weather_cache', {\n data: { city, temp: current.temp_C, condition: current.weatherDesc[0].value },\n timestamp: Date.now()\n });\n\n loading.style.display = 'none';\n content.style.display = 'block';\n } catch (err) {\n loading.style.display = 'none';\n\n // Try cached data first\n const cache = await echoclaw.storage.get('weather_cache');\n if (cache && Date.now() - cache.timestamp < 3600000) {\n document.getElementById('city-name').textContent = cache.data.city;\n document.getElementById('temp').textContent = `${cache.data.temp}\xB0C`;\n document.getElementById('condition').textContent = cache.data.condition + ' (cached)';\n content.style.display = 'block';\n } else {\n document.getElementById('error-msg').textContent =\n 'Could not load weather data. Check your connection.';\n errorState.style.display = 'block';\n }\n }\n }\n\n document.getElementById('refresh-btn')?.addEventListener('click', () => fetchWeather());\n document.getElementById('retry-btn')?.addEventListener('click', () => fetchWeather());\n fetchWeather();\n</script>\n```\n\n**Key elements:** Loading state shown during fetch, error state with retry button, cached fallback for offline resilience, response status checking, user-friendly error messages (never raw error codes).\n\n### 6.3 Real-Time Update Pattern \u2014 Timers That Actually Tick\n\nWhen the user asks for a timer, clock, countdown, or any time-based feature \u2014 it MUST update in real time using `setInterval`. Never show a static time display.\n\n```js\n// \u2500\u2500 Pomodoro Timer Core Logic \u2500\u2500\nlet remaining = 25 * 60; // seconds\nlet running = false;\nlet intervalId = null;\n\nfunction startTimer() {\n if (running) return;\n running = true;\n intervalId = setInterval(() => {\n remaining--;\n updateDisplay();\n if (remaining <= 0) {\n clearInterval(intervalId);\n running = false;\n echoclaw.emit('notify', {\n title: 'Timer Complete',\n body: 'Your focus session is done!'\n });\n }\n }, 1000);\n}\n\nfunction pauseTimer() {\n clearInterval(intervalId);\n running = false;\n}\n\nfunction resetTimer(minutes = 25) {\n clearInterval(intervalId);\n running = false;\n remaining = minutes * 60;\n updateDisplay();\n}\n\nfunction updateDisplay() {\n const min = Math.floor(remaining / 60).toString().padStart(2, '0');\n const sec = (remaining % 60).toString().padStart(2, '0');\n document.getElementById('timer').textContent = `${min}:${sec}`;\n // Update progress ring/bar\n const progress = 1 - remaining / (25 * 60);\n document.getElementById('progress').style.width = `${progress * 100}%`;\n}\n\n// Wire buttons to real functions\ndocument.getElementById('start-btn').addEventListener('click', startTimer);\ndocument.getElementById('pause-btn').addEventListener('click', pauseTimer);\ndocument.getElementById('reset-btn').addEventListener('click', resetTimer);\nupdateDisplay(); // Show initial state immediately\n```\n\n### 6.4 Form Validation Pattern \u2014 Reject Bad Input Before It Causes Problems\n\nEvery form MUST validate input before processing. Never trust user input blindly.\n\n```js\nfunction validateForm(fields) {\n const errors = [];\n\n // Required field check\n if (!fields.name?.trim()) {\n errors.push({ field: 'name', message: 'Name is required' });\n }\n\n // Type check\n if (fields.email && !/^[^\\s@]+@[^\\s@]+\\.[^\\s@]+$/.test(fields.email)) {\n errors.push({ field: 'email', message: 'Invalid email format' });\n }\n\n // Range check\n if (fields.age !== undefined) {\n const age = parseInt(fields.age);\n if (isNaN(age) || age < 0 || age > 150) {\n errors.push({ field: 'age', message: 'Age must be between 0 and 150' });\n }\n }\n\n // Length check\n if (fields.bio && fields.bio.length > 500) {\n errors.push({ field: 'bio', message: 'Bio must be under 500 characters' });\n }\n\n return errors;\n}\n\nfunction showErrors(errors) {\n // Clear previous errors\n document.querySelectorAll('.field-error').forEach(el => el.remove());\n document.querySelectorAll('.input-error').forEach(el => el.classList.remove('input-error'));\n\n errors.forEach(({ field, message }) => {\n const input = document.getElementById(field);\n if (input) {\n input.classList.add('input-error');\n const errorEl = document.createElement('div');\n errorEl.className = 'field-error';\n errorEl.textContent = message;\n input.parentNode.insertBefore(errorEl, input.nextSibling);\n }\n });\n}\n\nfunction showSuccess(message) {\n const toast = document.getElementById('toast');\n toast.textContent = message;\n toast.classList.add('show');\n setTimeout(() => toast.classList.remove('show'), 3000);\n}\n\n// Form submission with validation\ndocument.getElementById('form').addEventListener('submit', async (e) => {\n e.preventDefault();\n const fields = Object.fromEntries(new FormData(e.target));\n const errors = validateForm(fields);\n\n if (errors.length > 0) {\n showErrors(errors);\n return;\n }\n\n // Process valid data\n await echoclaw.storage.set(`record_${Date.now()}`, fields);\n showSuccess('Saved successfully!');\n e.target.reset();\n});\n```\n\n### 6.5 Data Visualization Pattern \u2014 Charts with Real Dynamic Data\n\nWhen using Chart.js or D3, always bind charts to real data sources (storage, fetch, or user input). Never show a chart with hardcoded demo numbers that the user cannot change.\n\n```js\n// manifest: { \"shared_libs\": [\"chart.js\"] }\n\nlet myChart = null;\n\nasync function loadAndRenderChart() {\n // Load real data from storage\n const expenses = await echoclaw.storage.get('expenses') || [];\n\n // Aggregate by category\n const byCategory = {};\n expenses.forEach(e => {\n byCategory[e.category] = (byCategory[e.category] || 0) + e.amount;\n });\n\n const labels = Object.keys(byCategory);\n const data = Object.values(byCategory);\n\n // Handle empty state\n if (labels.length === 0) {\n document.getElementById('chart-empty').style.display = 'block';\n document.getElementById('chart-container').style.display = 'none';\n return;\n }\n\n document.getElementById('chart-empty').style.display = 'none';\n document.getElementById('chart-container').style.display = 'block';\n\n // Destroy previous chart instance to avoid memory leak\n if (myChart) myChart.destroy();\n\n const ctx = document.getElementById('myChart').getContext('2d');\n myChart = new Chart(ctx, {\n type: 'doughnut',\n data: {\n labels,\n datasets: [{\n data,\n backgroundColor: ['#FF6384', '#36A2EB', '#FFCE56', '#4BC0C0', '#9966FF']\n }]\n },\n options: {\n responsive: true,\n plugins: {\n legend: { position: 'bottom', labels: { color: 'var(--ec-text)' } }\n }\n }\n });\n}\n\n// Re-render when data changes\nloadAndRenderChart();\n```\n\n### 6.6 Data Import/Export Pattern \u2014 Getting Data In and Out\n\nApps that manage data SHOULD support import and export so users don't feel trapped.\n\n```js\n// \u2500\u2500 Export to CSV \u2500\u2500\nasync function exportData() {\n const expenses = await echoclaw.storage.get('expenses') || [];\n if (expenses.length === 0) {\n showToast('No data to export');\n return;\n }\n\n const header = 'Date,Description,Amount,Category\\n';\n const rows = expenses.map(e =>\n `${e.date},${e.desc.replace(/,/g, ';')},${e.amount},${e.category}`\n ).join('\\n');\n\n const result = await echoclaw.invoke('fs.save', {\n content: header + rows,\n defaultName: 'expenses.csv',\n accept: ['.csv'],\n encoding: 'utf-8'\n });\n\n if (result.saved) showToast('Exported successfully!');\n}\n\n// \u2500\u2500 Import from CSV \u2500\u2500\nasync function importData() {\n const file = await echoclaw.invoke('fs.pick', {\n accept: ['.csv'],\n maxSizeBytes: 5 * 1024 * 1024\n });\n\n if (!file) return; // User cancelled\n\n try {\n const lines = file.content.split('\\n').filter(l => l.trim());\n const newExpenses = lines.slice(1).map(line => { // Skip header\n const [date, desc, amount, category] = line.split(',');\n if (!desc || isNaN(parseFloat(amount))) throw new Error('Invalid format');\n return {\n id: Date.now().toString() + Math.random(),\n date, desc, amount: parseFloat(amount), category: category || 'other'\n };\n });\n\n const existing = await echoclaw.storage.get('expenses') || [];\n await echoclaw.storage.set('expenses', [...newExpenses, ...existing]);\n showToast(`Imported ${newExpenses.length} records`);\n render();\n } catch (err) {\n showToast('Import failed: invalid CSV format');\n }\n}\n```\n\n---\n\n## 7. Functional Anti-Patterns \u2014 Common Mistakes That Break Apps\n\nThese are the most frequent reasons apps look good but don't work. **Avoid every one of these.**\n\n### 7.1 Dead Buttons \u2014 Functions Defined But Never Connected\n\n```js\n// \u274C WRONG: Function exists but button has no event listener\nfunction saveNote() { /* ... */ }\n// <button id=\"save-btn\">Save</button>\n// ... but nobody called addEventListener!\n\n// \u2705 RIGHT: Always wire the handler\ndocument.getElementById('save-btn').addEventListener('click', saveNote);\n```\n\n**Self-check:** After writing your HTML, search for every `<button>`, `<a>`, and `<form>`. Does each one have a corresponding event listener or `onclick`? If not, it is a dead button.\n\n### 7.2 Fake Data Pretending to Be Real\n\n```js\n// \u274C WRONG: Hardcoded array shown to user as if it's their data\nconst tasks = [\n { title: 'Buy groceries', done: false },\n { title: 'Walk the dog', done: true }\n];\n\n// \u2705 RIGHT: Start empty, load from storage, let user create data\nlet tasks = [];\nasync function init() {\n tasks = await echoclaw.storage.get('tasks') || [];\n render();\n}\n```\n\n**Rule:** If the user didn't create the data, it must not be there. The only exception is explicit demo/sample data that is clearly labeled as such.\n\n### 7.3 Silent Failures \u2014 Fetch Errors With No User Feedback\n\n```js\n// \u274C WRONG: User clicks refresh, nothing happens, no idea why\nasync function loadData() {\n try {\n const res = await echoclaw.invoke('fetch', { url: API_URL });\n renderData(JSON.parse(res.body));\n } catch (e) {\n console.log(e); // User sees NOTHING\n }\n}\n\n// \u2705 RIGHT: Show the user what happened and give them a way to recover\nasync function loadData() {\n showLoading(true);\n try {\n const res = await echoclaw.invoke('fetch', { url: API_URL });\n if (res.status !== 200) throw new Error(`Server error (${res.status})`);\n renderData(JSON.parse(res.body));\n } catch (e) {\n showError('Could not load data. Please check your connection and try again.');\n } finally {\n showLoading(false);\n }\n}\n```\n\n### 7.4 Volatile Data \u2014 Only In Memory, Lost On Reload\n\n```js\n// \u274C WRONG: Data only lives in a JS variable\nlet notes = [];\nfunction addNote(text) {\n notes.push({ text, time: Date.now() });\n render();\n}\n// User adds 10 notes, closes app, reopens \u2192 all gone\n\n// \u2705 RIGHT: Persist after every mutation\nasync function addNote(text) {\n notes.push({ text, time: Date.now() });\n await echoclaw.storage.set('notes', notes); // \u2190 This line is the difference\n render();\n}\n```\n\n### 7.5 No Input Validation \u2014 Trusting Everything The User Types\n\n```js\n// \u274C WRONG: Accept anything, crash later\nfunction addExpense(amount) {\n total += amount; // What if amount is \"abc\"? Or -999?\n}\n\n// \u2705 RIGHT: Validate first, reject with clear message\nfunction addExpense(input) {\n const amount = parseFloat(input);\n if (isNaN(amount) || amount <= 0) {\n showError('Please enter a positive number');\n return;\n }\n if (amount > 1000000) {\n showError('Amount seems too large. Please check.');\n return;\n }\n total += amount;\n save();\n render();\n}\n```\n\n### 7.6 Missing Empty State \u2014 Blank Screen When No Data Exists\n\n```js\n// \u274C WRONG: No data = completely blank page. User confused.\nfunction render() {\n list.innerHTML = items.map(i => `<li>${i.name}</li>`).join('');\n}\n\n// \u2705 RIGHT: Show helpful empty state\nfunction render() {\n if (items.length === 0) {\n list.innerHTML = `\n <div class=\"empty-state\">\n <p>No items yet</p>\n <p>Click \"Add\" to create your first item</p>\n </div>`;\n return;\n }\n list.innerHTML = items.map(i => `<li>${i.name}</li>`).join('');\n}\n```\n\n### 7.7 Calculation Errors \u2014 Wrong Math in Calculators and Trackers\n\n```js\n// \u274C WRONG: Floating point traps\n0.1 + 0.2 // \u2192 0.30000000000000004\n\n// \u2705 RIGHT: Round for display, use integers for currency\nfunction formatMoney(cents) {\n return (cents / 100).toFixed(2);\n}\nfunction parseMoney(dollars) {\n return Math.round(parseFloat(dollars) * 100);\n}\n```\n\n---\n\n## 8. Functional Quality Gate\n\nBefore delivering any app, verify it passes ALL of these functional checks. This is just as important as the visual quality gate.\n\n### Mandatory Checks (ALL must pass)\n\n| # | Check | How to Verify |\n|---|-------|---------------|\n| 1 | **Every button/link triggers real logic** | Search HTML for all `<button>`, `<a>`, `<form>`. Each one must have a working event handler. No dead UI. |\n| 2 | **Data persists across reload** | If the app manages any user data (tasks, notes, settings, scores), it MUST use `echoclaw.storage` or `echoclaw.db`. Close and reopen = data still there. |\n| 3 | **Error states are handled** | Every `fetch` call has `try/catch`. User sees a message + retry option on failure, never a blank screen or console error. |\n| 4 | **Inputs are validated** | Required fields are checked. Numbers are range-checked. Invalid input shows a clear error message near the field. |\n| 5 | **Empty state is designed** | When there is no data (first launch, all items deleted), the app shows a helpful message, not a blank void. |\n| 6 | **User requirements are fully covered** | Re-read the user's original request. Every feature they asked for must be functional, not just present as UI. \"I want to add tasks\" means add WORKS. |\n| 7 | **Calculations are correct** | If the app does math (totals, averages, percentages, dates), spot-check with known values. Use `toFixed()` for currency. |\n| 8 | **No hardcoded fake data** | The app starts empty (or loads from storage). No sample data masquerading as real user data. |\n| 9 | **Real-time features actually update** | Timers tick. Clocks show current time. Countdowns count down. Auto-refresh refreshes. |\n| 10 | **Loading states are shown** | Any async operation (fetch, large data load) shows a loading indicator. User is never left staring at an unresponsive screen. |\n\n### Scoring\n\n| Score | Meaning | Action |\n|-------|---------|--------|\n| 10/10 | Ship it | Deliver with confidence |\n| 8-9/10 | Minor gaps | Fix before delivery |\n| \u22647/10 | Not ready | Rework \u2014 the app does not actually work |\n\n### Decision Tree \u2014 Which Storage Pattern To Use\n\n```\nUser request involves data?\n\u251C\u2500\u2500 No (static page, calculator, presentation) \u2192 No storage needed\n\u251C\u2500\u2500 Yes\n\u2502 \u251C\u2500\u2500 Simple key-value data (settings, preferences, small lists <100 items)\n\u2502 \u2502 \u2192 echoclaw.storage (KV)\n\u2502 \u251C\u2500\u2500 Structured data with queries (filter, sort, search, relations, >100 items)\n\u2502 \u2502 \u2192 echoclaw.db (SQLite)\n\u2502 \u2514\u2500\u2500 Temporary session state (current tab, scroll position)\n\u2502 \u2192 echoclaw.emit('saveState', ...)\n```\n\n### Decision Tree \u2014 When To Use Fetch\n\n```\nApp needs external data?\n\u251C\u2500\u2500 No (offline tools, local data only) \u2192 No fetch needed\n\u251C\u2500\u2500 Yes\n\u2502 \u251C\u2500\u2500 Public API (weather, quotes, exchange rates)\n\u2502 \u2502 \u2192 echoclaw.invoke('fetch', ...) + manifest.network_allow\n\u2502 \u251C\u2500\u2500 User's own API (needs API key)\n\u2502 \u2502 \u2192 config_fields with sensitive:true + echoclaw.invoke('getSecret', ...)\n\u2502 \u2514\u2500\u2500 No API available for this data\n\u2502 \u2192 Tell the user honestly. Do NOT generate fake data.\n```\n\n---\n\n## 9. What You CAN Build (20 Categories)\n\nWhen a user's request matches any of the following scenarios, proactively suggest creating a ClawApp.\n\n**Data & Visualization**\n1. Dashboards & KPI reports (sales, product metrics, team statistics)\n2. Real-time monitoring panels (API health, cloud costs, smart home status)\n3. Data exploration & visualization (CSV/database analysis, interactive charts)\n\n**Management & Productivity**\n4. Project/task management boards (Kanban, to-do lists, OKR tracking)\n5. Roadmaps & timelines (product planning, Gantt charts, study plans)\n6. Calendars & scheduling (weekly calendars, travel itineraries, shift rosters)\n7. Pipeline tracking (CRM, recruiting, job search, fundraising progress)\n8. Ticket & queue management (customer support, bug tracking, incident triage)\n\n**Web Pages & Presentations**\n9. Marketing landing pages & event pages (product launches, conferences)\n10. Personal homepages & portfolios (online resumes, design galleries)\n11. Presentations & reports (client proposals, weekly reports, investor updates)\n\n**Tools & Calculators**\n12. Productivity tools (calculators, converters, countdowns, password generators)\n13. Finance & budget tracking (personal budgets, subscription management)\n14. Decision comparison tools (product selection, vendor comparison)\n15. Business simulators (mortgage calculators, pricing models, nutrition calculators)\n\n**Forms & Data Collection**\n16. Forms & approval workflows (client onboarding, audit checklists)\n17. Inventory & collection management (equipment, books, lab supplies)\n\n**Learning & Entertainment**\n18. Learning & exam prep apps (course tracking, flashcards, spaced repetition)\n19. Interactive mini-games (lotteries, text adventures, quizzes, casual games)\n20. Media galleries & mood boards (inspiration boards, content calendars, recipes)\n\n**Heuristic \u2014 suggest a ClawApp when the request exhibits:**\n- Persistent state (not a one-off answer)\n- Repeated use (the user will return)\n- Visual structure (charts, cards, maps, timelines, calendars)\n- Interactive operations (filtering, editing, toggling, drill-down)\n- Ongoing updates (real-time or periodic data entry)\n- Sharing & presentation (intended to be shown to others)\n- **Fallback: any request that is \"visible and operable\" defaults to a ClawApp**\n\n---\n\n## 10. What You CANNOT Do (Hard Limits)\n\n| Restriction | Reason |\n|-------------|--------|\n| No external `<script src=\"...\">` or CDN links | Self-contained HTML only; use `shared_libs` for common libraries |\n| No direct DOM access to host shell | Sandbox isolation \u2014 communicate only via bridge |\n| No raw TCP/UDP sockets | Only HTTP (fetch proxy) and WebSocket (ws proxy) |\n| No filesystem browsing | File picker opens a dialog; you get content, not paths |\n| No access to other apps' data | Storage, DB, and files are scoped per app ID |\n| No cloud metadata endpoints | 169.254.x and metadata.google.internal are always blocked |\n| No ATTACH/DETACH in SQL | Cannot access other databases or load native extensions |\n| No arbitrary network access | Every target must be in `manifest.network_allow` |\n| No direct iframe `allow` manipulation | Shell controls sandbox attributes based on declared capabilities |\n| No persistent background processes | Background tasks have 10s timeout and 60s min interval |\n| No native OS API calls | Bridge-mediated only; `device_control` is shell-exclusive |\n| No localStorage / IndexedDB | Use `echoclaw.storage` (KV) or `echoclaw.db` (SQLite) instead |\n| No cookies | Sandboxed iframe has no cookie access |\n| No `window.open()` / popups | Single-iframe model |\n| Cannot override reserved keyboard shortcuts | Shell shortcuts (Mod+Q, Mod+W, Mod+C, etc.) take precedence |\n\n### Known Platform Limitations (Temporary)\n\nThe following are **temporary Bridge feature gaps** \u2014 the desktop platform natively supports these capabilities, but the Bridge API layer has not yet implemented them. All items are scheduled for implementation. When encountered, inform the user and provide the listed workaround. Do NOT submit feedback for these items.\n\n| Limitation | Current Status | Workaround |\n|------------|----------------|------------|\n| WebSocket proxy | Stub \u2014 `ws.connect()` throws error | Use fetch polling via `echoclaw.invoke('fetch', ...)` |\n| SQLite database | KV-emulated \u2014 no SQL query capability | Use `echoclaw.storage` KV store |\n| File system picker | Stub \u2014 `fs.pick()` / `fs.save()` unavailable | Use `echoclaw.fs.appRead` / `appWrite` sandbox files |\n| Secret encryption | Plaintext localStorage | Functional but security pending upgrade |\n| Light theme | Hard-coded dark mode | Design for dark theme only |\n| Permission prompts | All permissions auto-approved | Functional but missing user confirmation step |\n| Inter-app broadcast | Stub \u2014 `broadcast()` logs only | N/A \u2014 apps run independently |\n| Keyboard shortcuts | Stub \u2014 `registerShortcut()` logs only | Use button interactions instead |\n| 14 deprecated node types | stat / tabs / table / slider etc. unavailable | Use composition recipes to build equivalent UI |\n\n---\n\n## 11. iframe Sandbox Attributes\n\nThe shell dynamically sets iframe `sandbox` and `allow` based on declared capabilities:\n\n```\nsandbox=\"allow-scripts\"\n```\n\nAdditional `allow` attributes added per capability:\n| Capability | `allow` value |\n|------------|---------------|\n| `audio` | `autoplay` |\n| `clipboard` | `clipboard-read; clipboard-write` |\n| `microphone` | `microphone` |\n| `camera` | `camera` |\n| `location` | `geolocation` |\n| `media_capture` | `display-capture` |\n\nNo `allow-same-origin` \u2014 apps cannot access host cookies, localStorage, or IndexedDB.\n\n---\n\n## 12. Design Constraints\n\n- **Viewport:** Design for 1024x768 iframe; minimum safe area is 544x548\n- **Theme:** Dark mode first. ALL colors via `var(--ec-xxx)` CSS variables (only `manifest.color` hex is exempt)\n- **Layout:** No left sidebar (reserved by shell). Bottom-right 64x64px reserved for host Info button.\n- **Icons:** SVG only (inline). No emoji, no icon fonts.\n- **Localization:** Bilingual minimum (en + zh-CN) using a `t()` helper function.\n- **Interaction:** All interactive elements MUST have hover + focus states.\n\n---\n\n## 13. Events Reference\n\n### Host \u2192 App Events\n\n| Event | Payload | When |\n|-------|---------|------|\n| `theme:changed` | `{ theme, variables }` | User switches theme |\n| `locale:changed` | `{ locale }` | User changes language |\n| `resize` | `{ width, height }` | iframe resized |\n| `stateRestored` | `(savedState)` | App state restored on load |\n| `ws:open` | `{ channel }` | WebSocket connected |\n| `ws:message` | `{ channel, data }` | WebSocket message received |\n| `ws:error` | `{ channel, error }` | WebSocket error |\n| `ws:close` | `{ channel, code, reason }` | WebSocket closed |\n| `app_comm:message` | `{ senderAppId, payload }` | Message from another app |\n| `auth:sessionExpired` | `{ reason, key, providerId }` | Auth session expired |\n| `auth:tokenRefreshed` | `{ key, providerId, expiresAt }` | Token auto-refreshed |\n| `shortcut:triggered` | `{ id, keys }` | Registered shortcut pressed |\n\n---\n\nFor Web API to EchoClaw Bridge migration mapping, see `WEB_API_MIGRATION.md`.\n\n---\n\n## 14. Gold-Standard Reference App\n\nFor a complete, production-quality example that demonstrates every best practice (CRUD, search, tags, export, i18n, form validation, toast feedback, confirm dialogs, empty/loading/error states, responsive layout, custom scrollbars, and a 20/20 Quality Gate score), see the reference app:\n\n**`packages/claw-engine/src/factory-apps/reference-app.claw.json`** \u2014 Personal Notes app, the gold standard for EchoClaw app quality.\n\n---\n\n## 15. Quick Reference Card\n\nThe most-used APIs at a glance. See sections above for full signatures and error handling.\n\n| Task | One-liner |\n|------|-----------|\n| Read KV | `const v = await echoclaw.storage.get('key')` |\n| Write KV | `await echoclaw.storage.set('key', value)` |\n| Delete KV | `await echoclaw.storage.remove('key')` |\n| Clear all | `await echoclaw.storage.clear()` |\n| SQL SELECT | `const rows = await echoclaw.db.query('SELECT * FROM t WHERE x=?', [val])` |\n| SQL INSERT | `await echoclaw.db.exec('INSERT INTO t (col) VALUES (?)', [val])` |\n| HTTP GET | `const r = await echoclaw.invoke('fetch', { url, method: 'GET' })` |\n| HTTP POST | `const r = await echoclaw.invoke('fetch', { url, method: 'POST', body: JSON.stringify(d), headers: {'Content-Type':'application/json'} })` |\n| Open file | `const f = await echoclaw.invoke('fs.pick', { accept: ['.csv'] })` |\n| Save file | `await echoclaw.invoke('fs.save', { content: text, defaultName: 'export.csv' })` |\n| Notify | `echoclaw.emit('notify', { title: 'Done', body: 'Saved' })` |\n| Get context | `const ctx = echoclaw.getContext()` \u2192 `ctx.locale`, `ctx.device.width` |\n| Subscribe | `const unsub = echoclaw.subscribe('theme:changed', cb)` |\n| Request permission | `await echoclaw.requestPermission('clipboard')` |\n\n**Always wrap bridge calls in try/catch.** Show user-facing error messages \u2014 never expose error codes or stack traces.\n\n";
|
|
8428
8430
|
|
|
8429
8431
|
// ../claw-engine/src/authoring/openclaw-guides/SYSTEM_CAPABILITIES.md
|
|
8430
|
-
var SYSTEM_CAPABILITIES_default = '# EchoClaw System Capabilities Reference\n\n> For OpenClaw: what the underlying platform provides. Use this to know what infrastructure apps can depend on.\n\n---\n\n## 1. Communication Architecture\n\n```\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 E2E Encrypted \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 localhost:18789 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Desktop \u2502 \u25C4\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561 RelayAgent \u2502 \u25C4\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561 OpenClaw \u2502\n\u2502 (Client)\u2502 (Relay Server) \u2502 (MCP Node) \u2502 WebSocket Gateway \u2502 (You) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**Three-party model:**\n- **Desktop** \u2014 source of truth for installed apps, renders iframes, enforces sandbox\n- **RelayAgent** \u2014 MCP bridge node, syncs workspace files, forwards chat, zero AI capability\n- **OpenClaw** \u2014 you. Receives tasks via chat, writes files to workspace, signals completion\n\n**Protocol:** Sync v3.1 \u2014 file-driven. You work with files in your workspace (`apps/{appId}/`). Chat carries only lightweight notification markers, not app content.\n\n---\n\n## 2. Chat Notification Markers\n\nMarkers are HTML comments embedded in chat messages. The client and RelayAgent parse these mechanically (no AI).\n\n### CLAW_STEP \u2014 Confirm task receipt\n\nSend immediately when you receive a task.\n\n```\n<!--CLAW_STEP:{"appId":"my-app","op":"create","status":"received"}-->\n```\n\n| Field | Type | Required | Values |\n|----------|--------|----------|--------|\n| `appId` | string | yes | App identifier |\n| `op` | string | yes | `create` \\| `update` \\| `adapt` \\| `comply` \\| `publish` |\n| `status` | string | yes | `received` \\| `planning` \\| `building` \\| `verifying` \\| `working` \\| `error` |\n| `message`| string | no | Error description \u2014 MUST include when status=`error` |\n\n**Status progression:** `received` \u2192 `planning` \u2192 `building` \u2192 `verifying` \u2192 (done, send CLAW_APP_READY)\n\n### CLAW_STEP Usage Examples\n\nSend a CLAW_STEP at every significant workflow point. These update the user\'s UI in real time.\n\n```\nTask received, starting work:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"received"}-->\n\nFinished planning, starting to build:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"building"}-->\n\nRunning self-check before delivery:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"verifying"}-->\n\nSomething went wrong (show user a message):\n<!--CLAW_STEP:{"appId":"todo-app","op":"update","status":"error","message":"API endpoint returned 403 \u2014 user needs to check their API key"}-->\n\nClient asks "are you still working?", reply immediately:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"working"}-->\n```\n\n**Rules:**\n- Send `received` immediately when you get the task \u2014 before planning anything\n- Send `error` with a human-readable `message` if you cannot complete the task\n- NEVER stop after sending CLAW_STEP \u2014 it is a progress signal, not a checkpoint. Continue immediately.\n\n### CLAW_APP_READY \u2014 Signal files are ready\n\nSend after writing all workspace files. Desktop triggers file pull from RelayAgent.\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"create","contentHash":"abc123","summary":"Timer app with 25/5 intervals"}-->\n```\n\n| Field | Type | Required | Description |\n|---------------|--------|----------|-------------|\n| `appId` | string | yes | App identifier |\n| `op` | string | yes | `create` \\| `update` \\| `adapt` \\| `comply` \\| `publish` |\n| `contentHash` | string | no | SHA hash of index.html (integrity check) |\n| `summary` | string | no | Brief description shown to user |\n\n### CLAW_FEEDBACK \u2014 System error notifications (inbound)\n\nSent TO you by the client/RelayAgent when something goes wrong. These are rule-based, not AI-generated.\n\n```\n<!--CLAW_FEEDBACK:{"appId":"my-app","code":"file_not_found","message":"..."}-->\n```\n\n| Code | Meaning |\n|---------------------|---------|\n| `file_not_found` | CLAW_APP_READY sent but workspace files missing |\n| `json_parse_error` | claw.json is not valid JSON |\n| `html_empty` | index.html exists but is 0 bytes |\n| `schema_invalid` | claw.json missing required fields (manifest, prompt) |\n| `task_timeout` | No CLAW_STEP or CLAW_APP_READY within timeout |\n| `task_ack_missing` | No CLAW_STEP received |\n| `hash_mismatch` | contentHash doesn\'t match actual file |\n| `validation_failed` | Engine validation failed \u2014 fix and resend with op=`comply` |\n\n---\n\n## 3. Workspace File Structure\n\nEach app lives in `apps/{appId}/` with these files:\n\n```\napps/\n\u2514\u2500\u2500 my-app/\n \u251C\u2500\u2500 claw.json # App package (manifest + prompt + setup, NO html)\n \u251C\u2500\u2500 index.html # App HTML (standalone file, never JSON-escaped)\n \u251C\u2500\u2500 session.json # Conversation context, requirement evolution\n \u251C\u2500\u2500 devlog.json # Optional development log\n \u2514\u2500\u2500 .status # "draft" or "published"\n```\n\n**Rules:**\n- `claw.json` and `index.html` are always separate files \u2014 never embed HTML in JSON\n- `.status = "draft"` during creation, changes to `"published"` after successful delivery\n- `session.json` persists across sessions \u2014 read it to resume context\n\n### session.json format\n\n```json\n{\n "appId": "my-app",\n "createdAt": "2026-03-22T10:00:00Z",\n "lastUpdated": "2026-03-22T12:30:00Z",\n "entries": [\n {\n "timestamp": "2026-03-22T10:00:00Z",\n "role": "user",\n "summary": "Requested a Pomodoro timer with custom intervals",\n "op": "create"\n },\n {\n "timestamp": "2026-03-22T10:05:00Z",\n "role": "openclaw",\n "summary": "Created timer with 25/5/15 intervals, localStorage persistence",\n "op": "create"\n }\n ]\n}\n```\n\n**Growth policy:** Keep \u226450 entries. When exceeding, compact older entries into a summary. Entries are AI-summarized \u2014 never include raw user messages or PII.\n\n---\n\n## 4. Building Apps That Actually Work\n\n> **Critical:** Knowing the APIs is not enough. See **SANDBOX_CAPABILITIES.md \xA76-8** for complete functional patterns, anti-patterns, and quality gates. Every app you build MUST pass the Functional Quality Gate (\xA78) before delivery.\n>\n> **Key principle from multi-AI consensus:** The difference between a useful app and a pretty shell is:\n> 1. **Data survives reload** \u2014 use `echoclaw.storage` or `echoclaw.db`, never memory-only variables\n> 2. **External data is real** \u2014 use `echoclaw.invoke(\'fetch\', ...)` with real APIs, never hardcoded fake data\n> 3. **Errors are visible** \u2014 every fetch/db call has try/catch with user-facing error messages and retry\n> 4. **Input is validated** \u2014 check types, ranges, required fields before processing\n> 5. **Every UI element works** \u2014 no dead buttons, no unbound handlers, no decoration-only controls\n>\n> When planning an app, ask yourself: "If the user closes and reopens this, does their data survive? If the network fails, do they see a helpful message? If they enter garbage, does the app handle it gracefully?" If any answer is no, fix it before delivering.\n\n---\n\n## 5. Data Storage\n\n### 5.1 Key-Value Storage (`echoclaw.storage`)\n\nCapability: `storage` (auto-grant, always available).\n\nApps access via the bridge:\n```js\nawait echoclaw.storage.get(\'key\')\nawait echoclaw.storage.set(\'key\', value)\nawait echoclaw.storage.remove(\'key\')\nawait echoclaw.storage.keys()\nawait echoclaw.storage.clear()\n```\n\n**Limits:**\n| Parameter | Value |\n|----------------|---------|\n| Max keys | 1,000 |\n| Max total size | 5 MB |\n\nValues are JSON-serializable. Scoped per app \u2014 no cross-app access.\n\n### 5.2 SQLite Database (`echoclaw.db`)\n\nCapability: `database` (auto-grant).\n\nApps access via the bridge:\n```js\n// SELECT \u2014 returns array of row objects\nconst rows = await echoclaw.invoke(\'db.query\', { sql: \'SELECT * FROM tasks WHERE done = ?\', params: [0] })\n\n// INSERT/UPDATE/DELETE/DDL \u2014 returns { changes, lastInsertRowid }\nconst result = await echoclaw.invoke(\'db.exec\', { sql: \'INSERT INTO tasks (title) VALUES (?)\', params: [\'New task\'] })\n```\n\n**Limits:**\n| Parameter | Value | Condition |\n|--------------------------|---------|-----------|\n| Auto-grant quota | 10 MB | No prompt needed |\n| Standard max | 50 MB | Requires user approval |\n| Extended max | 200 MB | Requires second approval |\n| Max rows per query | 1,000 | Bridge enforces pagination |\n| Max transactions/sec | 200 | IO rate limit |\n\n**Blocked SQL:** `ATTACH`, `DETACH`, `LOAD_EXTENSION`, dangerous PRAGMAs (`database_list`, `compile_options`, `integrity_check`, `quick_check`).\n\n**Allowed:** All DDL (CREATE TABLE, DROP TABLE, etc.), all DML, safe PRAGMAs (journal_mode, foreign_keys, etc.).\n\n**Schema versioning:** Declare `manifest.db_version` (integer starting from 1). Shell stores this in the app\'s `_meta` table. When manifest version > stored version, run migration SQL.\n\n### 5.3 Secure Credential Storage (`echoclaw.auth` / `echoclaw.invoke(\'credential.get\')`)\n\nCapability: `secure_credential` (auto-grant) or `auth` (prompt-required for runtime OAuth).\n\nStores secrets in OS keychain (macOS Keychain / Windows Credential Manager). Never stored in KV or SQLite. Never returned in HTML. Namespace-isolated per app.\n\n---\n\n## 6. File System (`echoclaw.fs`)\n\nCapability: `filesystem` (prompt-required \u2014 user must approve).\n\n### fs.pick \u2014 Open file dialog\n\n```js\nconst file = await echoclaw.invoke(\'fs.pick\', {\n accept: [\'.json\', \'.csv\', \'.txt\'], // optional filter\n maxSizeBytes: 10485760 // optional, default 10MB\n})\n// Returns: { name, size, content, encoding, mimeType } or null (cancelled)\n```\n\n- `name` is basename only \u2014 no full path exposed\n- `encoding`: `"utf-8"` for text files, `"base64"` for binary\n- Text extensions: `.txt`, `.json`, `.csv`, `.xml`, `.html`, `.css`, `.js`, `.md`, `.yaml`, `.svg`, `.sql`, etc.\n- Max pick size: 10 MB (default)\n\n### fs.save \u2014 Save file dialog\n\n```js\nconst result = await echoclaw.invoke(\'fs.save\', {\n content: \'CSV content here\',\n defaultName: \'export.csv\',\n accept: [\'.csv\'],\n encoding: \'utf-8\' // or \'base64\' for binary\n})\n// Returns: { saved: true, name: \'export.csv\' } or { saved: false }\n```\n\n- Max save size: 50 MB\n\n---\n\n## 7. WebSocket (`echoclaw.ws`)\n\nCapability: `network` (auto-grant) + targets declared in `manifest.network_allow`.\n\n### Connect\n\n```js\nawait echoclaw.invoke(\'ws.connect\', {\n channel: \'my-channel\',\n opts: { url: \'wss://api.example.com/ws\', protocols: [\'v1\'] }\n})\n```\n\n### Send / Close\n\n```js\nawait echoclaw.invoke(\'ws.send\', { channel: \'my-channel\', data: \'hello\' })\nawait echoclaw.invoke(\'ws.close\', { channel: \'my-channel\' })\n```\n\n### Receive events\n\n```js\nechoclaw.subscribe(\'ws:open\', (e) => { /* e.channel */ })\nechoclaw.subscribe(\'ws:message\', (e) => { /* e.channel, e.data */ })\nechoclaw.subscribe(\'ws:error\', (e) => { /* e.channel, e.error */ })\nechoclaw.subscribe(\'ws:close\', (e) => { /* e.channel, e.code, e.reason */ })\n```\n\n**Limits:**\n| Parameter | Value |\n|------------------------|-------|\n| Max connections/app | 3 (default), 5 (extended, requires prompt) |\n| Max message size | 1 MB |\n\n**Network rules:** All WS connections go through bridge proxy. Target must be in `manifest.network_allow`. Protocol `ws:` or `wss:` only.\n\n---\n\n## 8. Permission Model\n\nThree tiers determine sandbox behavior:\n\n### Auto-grant (no user prompt)\n\n| Capability | Description |\n|--------------------|-------------|\n| `ui` | Baseline \u2014 always implicit |\n| `timer` | setTimeout, setInterval, requestAnimationFrame |\n| `storage` | KV storage + sandbox files |\n| `notification` | Shell-rendered notifications (rate-limited: 10/min) |\n| `network` | Bridge proxy \u2014 requires `network_allow` whitelist |\n| `audio` | Web Audio API, `<audio>` playback, autoplay |\n| `fullscreen` | Element.requestFullscreen() |\n| `app_comm` | Inter-app messaging (shell injects unforgeable sender ID) |\n| `database` | SQLite per-app (10MB auto, >10MB needs prompt) |\n| `secure_credential`| OS keychain storage |\n\n### Prompt-required (user must approve on first use)\n\n| Capability | Description |\n|--------------------|-------------|\n| `clipboard` | Read/write clipboard |\n| `microphone` | Audio recording |\n| `camera` | Photo/video capture |\n| `location` | Geolocation |\n| `media_capture` | Screen/window capture |\n| `filesystem` | File picker/save dialogs |\n| `local_network` | Localhost access (127.0.0.1, ::1) |\n| `lan_network` | Private LAN (10.x, 172.x, 192.168.x) |\n| `background_task` | Scheduled tasks (min 60s interval, max 5/app) |\n| `auth` | Runtime credential storage + auth lifecycle |\n\n### Bridge-only (shell mediates all calls)\n\n| Capability | Description |\n|--------------------|-------------|\n| `device_control` | Hardware control \u2014 shell API only |\n\n---\n\n## 9. App Manifest Format (`claw.json`)\n\nThe `claw.json` file contains a `ClawAppPackage` object (minus the `html` field, which lives in `index.html`).\n\n### Top-level structure\n\n```json\n{\n "manifest": { ... },\n "prompt": { ... },\n "setup": { ... },\n "meta": { ... },\n "devlog": { ... },\n "lifecycle": { ... }\n}\n```\n\n### manifest (required)\n\n```json\n{\n "id": "pomodoro-timer",\n "name": { "en": "Pomodoro Timer", "zh-CN": "\u756A\u8304\u949F" },\n "version": "1.0.0",\n "color": "#FF6B6B",\n "icon_svg": "<circle cx=\\"12\\" cy=\\"12\\" r=\\"10\\" fill=\\"currentColor\\"/>",\n "description": { "en": "Focus timer with intervals", "zh-CN": "\u4E13\u6CE8\u8BA1\u65F6\u5668" },\n "capabilities": ["storage", "notification", "timer"],\n "db_version": 1,\n "network_allow": ["wss://api.example.com"],\n "shared_libs": ["chart.js", "dayjs"],\n "about": {\n "summary": { "en": "A productivity timer...", "zh-CN": "\u4E00\u4E2A\u4E13\u6CE8\u8BA1\u65F6\u5668..." },\n "highlights": [\n { "en": "Customizable intervals", "zh-CN": "\u53EF\u81EA\u5B9A\u4E49\u95F4\u9694" }\n ]\n },\n "auth_profile": {\n "strategy": "none",\n "token_tier": "install_time",\n "unauthenticated_behavior": "degrade"\n },\n "attribution": []\n}\n```\n\n| Field | Type | Required | Description |\n|-----------------|------------------|----------|-------------|\n| `id` | string | yes | Kebab-case unique ID |\n| `name` | LocalizedString | yes | Display name (must include `en` + `zh-CN`) |\n| `version` | string | yes | Semver (e.g. "1.0.0") |\n| `color` | string | yes | Brand hex color \u2014 the ONLY hex allowed in HTML |\n| `icon_svg` | string | no | SVG inner elements (no `<svg>` wrapper) |\n| `description` | LocalizedString | yes | One-line description |\n| `capabilities` | ClawCapability[] | no | Required capabilities |\n| `db_version` | number | no | SQLite schema version (integer, starts at 1) |\n| `network_allow` | string[] | no | Allowed network targets (required if using `network`) |\n| `shared_libs` | string[] | no | Pre-bundled library IDs |\n| `about` | object | no | Rich profile for About view |\n| `auth_profile` | object | no | Auth strategy declaration |\n| `attribution` | object[] | no | Open source attribution (immutable once set) |\n\n### prompt (required) \u2014 App DNA\n\n```json\n{\n "seed": {\n "goal": "A Pomodoro timer with customizable work/break intervals",\n "constraints": ["offline-first", "no network required"],\n "acceptance_tests": ["User can start a 25-minute timer", "User can customize intervals"],\n "share_intent": "shareable"\n },\n "current": {\n "goal": "Pomodoro timer with stats tracking and daily streaks",\n "notes": "Added statistics dashboard in v1.1.0"\n },\n "history": [\n { "version": "1.1.0", "change": "Added statistics tracking", "timestamp": "2026-03-22T12:00:00Z" }\n ]\n}\n```\n\n### setup (optional) \u2014 Install configuration\n\nDeclares what external services and user-specific config the app needs.\n\nKey fields: `config_fields` (user-provided config), `ai_tasks` (AI tasks for template apps), `install_contract` (installation behavior boundaries), `app_mode` (`"ready"` or `"template"`).\n\n### lifecycle (optional) \u2014 Lifecycle hooks\n\n```json\n{\n "on_install": "Create a daily briefing cron task at the configured time",\n "on_uninstall": "Delete the morning-briefing-collector cron task",\n "on_update": "Preserve user\'s existing config, only update HTML"\n}\n```\n\n---\n\n## 10. Shared Libraries\n\nPre-bundled libraries injected by the shell before app HTML. Not counted toward the 1MB HTML size limit.\n\n| Library | Version | Size | Globals |\n|-------------|---------|--------|------------|\n| `chart.js` | 4.4.0 | ~197KB | `Chart` |\n| `d3` | 7.9.0 | ~269KB | `d3` |\n| `marked` | 12.0.0 | ~45KB | `marked` |\n| `prism` | 1.29.0 | ~80KB | `Prism` |\n| `dayjs` | 1.11.10 | ~7KB | `dayjs` |\n| `sortablejs`| 1.15.0 | ~45KB | `Sortable` |\n\nDeclare in manifest: `"shared_libs": ["chart.js", "dayjs"]`\n\n---\n\n## 11. Resource Limits Summary\n\n| Resource | Limit |\n|----------------------------------|-------|\n| HTML file size | 1 MB |\n| KV storage keys | 1,000 |\n| KV storage total | 5 MB |\n| SQLite auto quota | 10 MB |\n| SQLite max (with approval) | 200 MB |\n| SQLite rows/query | 1,000 |\n| SQLite TPS | 200 |\n| Sandbox file storage | 250 MB |\n| WS connections/app | 3 (5 extended) |\n| WS message size | 1 MB |\n| Bridge invokes/sec | 60 (burst: 120) |\n| Notifications/min | 10 |\n| App comm messages/sec | 20 |\n| App comm payload | 64 KB |\n| Background task min interval | 60s |\n| Background tasks/app | 5 |\n| Background task timeout | 10s |\n| App restarts/hour (crash loop) | 3 |\n| File pick max size | 10 MB |\n| File save max size | 50 MB |\n\n---\n\n## 12. Bridge Error Codes\n\nAll `echoclaw.invoke()` rejections use this shape:\n\n```json\n{\n "code": "PERMISSION_DENIED",\n "message": "Capability \'clipboard\' not approved by user",\n "action": "clipboard.read",\n "retriable": false\n}\n```\n\n| Code | Meaning |\n|----------------------------|---------|\n| `TIMEOUT` | Bridge call timed out |\n| `CANCELLED` | Call was cancelled |\n| `PERMISSION_DENIED` | User denied the prompt-tier capability |\n| `CAPABILITY_NOT_DECLARED` | Capability not in manifest.capabilities |\n| `RATE_LIMITED` | Exceeded invoke rate limit |\n| `INVALID_ARGUMENT` | Bad parameters |\n| `NOT_FOUND` | Resource not found |\n| `QUOTA_EXCEEDED` | Storage/DB quota exceeded |\n| `BRIDGE_ERROR` | Bridge internal error |\n| `INTERNAL_ERROR` | Unexpected error |\n| `UNAVAILABLE` | Bridge not ready |\n\n---\n\n## 13. Design for Sharing (Mandatory)\n\nEvery ClawApp MUST be designed for sharing from the moment of creation. The receiving party\'s AI SHALL NOT modify the HTML structure \u2014 it SHALL only populate configuration values.\n\n### Core Principles\n\n1. **HTML is a finished artifact.** Once created, the HTML/CSS/JS is immutable. It MUST NOT be redone by another AI after sharing.\n2. **Environment-dependent values = config_fields.** API endpoints, usernames, preferences MUST be written as `{{KEY}}` placeholders.\n3. **Sensitive values go through the Bridge.** API keys, tokens, passwords MUST be declared via `config_fields` (`sensitive: true`) and MUST NOT be injected into HTML. At runtime, retrieve via `echoclaw.invoke(\'getSecret\', { key })`.\n4. **install_contract protects integrity.** Declare `mode: \'ready\'` + `immutable_segments` so the system can verify HTML has not been tampered with.\n\n### config_fields Scenarios\n\n| Scenario | config_field example | sensitive? |\n|----------|---------------------|-----------|\n| External API endpoint | `api_url` (type: url) | No |\n| API Key / Token | `api_key` (type: password) | Yes |\n| User display name | `display_name` (type: text) | No |\n| Timezone / language preference | `timezone` (type: select) | No |\n| Local service endpoint | `ollama_url` (type: url, auto_resolve: from_env) | No |\n| Gateway Agent ID | `agent_id` (type: text, auto_resolve: from_gateway) | No |\n\n### auto_resolve Strategies\n\nMinimize manual user input. Declare `auto_resolve` for any value that can be obtained automatically:\n\n| Strategy | Source | Use when |\n|----------|--------|----------|\n| `from_gateway` | Gateway connection info (agentId, etc.) | Value is part of the relay/gateway context |\n| `from_openclaw_config` | Local OpenClaw configuration file | Value is in the user\'s OpenClaw settings |\n| `from_env` | Environment variables | Local service endpoints (Ollama, dev servers) |\n| `from_context` | Conversation context | Value was mentioned by the user in the current session |\n| `detect_installed` | Installed app inventory | Value depends on whether another app/skill is installed |\n\n```json\n{\n "key": "agent_id",\n "auto_resolve": {\n "strategy": "from_gateway",\n "source": "agentId",\n "fallback": "ask_user"\n }\n}\n```\n\n### Installation Paths (resolveInstallAction)\n\nThe engine provides `resolveInstallAction(pkg)`. Clients MUST call it before installation:\n\n| Return value | Meaning | AI involvement? |\n|--------------|---------|-----------------|\n| `install_direct` | HTML is complete, no configuration needed | No |\n| `install_with_config` | HTML is complete, config fields need filling | Only resolving config values |\n| `complete_template` | Template mode, `ai_tasks` need completion | Yes \u2014 work within designated slots |\n| `generate_fresh` | No HTML, generate from DNA | Yes \u2014 full generation |\n\n### share_profile\n\nEvery shareable app MUST populate `share_profile`:\n\n```json\n{\n "share_profile": {\n "value_prop": {\n "en": "Track your daily habits with streaks and statistics",\n "zh-CN": "\u901A\u8FC7\u8FDE\u7EED\u8BB0\u5F55\u548C\u7EDF\u8BA1\u8FFD\u8E2A\u4F60\u7684\u65E5\u5E38\u4E60\u60EF"\n },\n "ai_handoff_notes": "No config needed. App is ready to use out of the box. Uses echoclaw.storage for persistence."\n }\n}\n```\n\n- `value_prop`: One-line bilingual description for the recipient. What does this app DO for them?\n- `ai_handoff_notes`: Instructions for the receiving AI. What does it need to know to install/configure this app?\n\n### install_contract and immutable_segments\n\n```json\n{\n "install_contract": {\n "mode": "ready",\n "immutable_segments": [\n { "marker": "<!-- LAYOUT_START -->", "hash": "abc123" },\n { "marker": "<!-- STYLE_START -->", "hash": "def456" }\n ]\n }\n}\n```\n\n- `mode: "ready"`: HTML is complete, no AI modification needed.\n- `mode: "template"`: HTML has `<!-- SLOT:name -->` markers for AI to fill.\n- `immutable_segments`: Content between markers MUST NOT be modified. The engine verifies hashes.\n\n### Sharing Self-Check\n\n- [ ] All environment-dependent values are declared as `config_fields`. No hard-coded URLs/IDs/usernames in HTML.\n- [ ] Sensitive fields are marked `sensitive: true` and NOT injected into HTML via `{{KEY}}`.\n- [ ] `install_contract` is declared (`mode: \'ready\'` or `\'template\'`).\n- [ ] `share_profile` is populated (`value_prop` + `ai_handoff_notes`).\n- [ ] Out-of-the-box apps have an empty `config_fields` array (no configuration needed = `install_direct` path).\n\n---\n\n## 14. Network Security Rules\n\nAll network access (fetch and WebSocket) goes through the bridge proxy. Direct iframe network access is blocked via CSP `connect-src: \'none\'`.\n\n1. **Whitelist required:** `manifest.network_allow` must list every target. No whitelist = no network.\n2. **Always blocked:** Cloud metadata endpoints (169.254.x), link-local addresses.\n3. **Localhost:** Requires `local_network` capability + user approval + declared in `network_allow`.\n4. **Private LAN:** Requires `lan_network` capability + user approval + declared in `network_allow`.\n5. **DNS rebinding:** Bridge validates resolved IPs match the declared targets.\n\n---\n\n## 15. Feedback Protocol\n\nWhen you encounter a platform limitation or issue, follow this protocol for submitting feedback to the development team.\n\n### When To Submit Feedback\n\nSubmit feedback ONLY in these scenarios:\n1. **New issue not in the known limitations list** \u2014 a platform limitation not listed in SANDBOX_CAPABILITIES.md\n2. **Documentation vs reality mismatch** \u2014 spec says a feature is supported, but the actual call errors\n3. **Overly strict validation** \u2014 a validation rule blocks a reasonable user requirement\n4. **Spec blind spot** \u2014 the user\'s request exposes a scenario the spec does not cover\n5. **Client rendering anomaly** \u2014 after receiving render diagnostics, you confirmed the issue is on the client side\n\n**Critical:** If the client relays render diagnostics (JS errors, Bridge call failures, layout anomalies), FIRST attempt to fix your own code. Only submit feedback if the issue is confirmed as a platform problem.\n\n### Frequency Control\n\n- **Same issue** (identical `issue_type` + `spec_reference`): submit ONCE per conversation session\n- **User consent prompt**: ask "would you like me to report this?" at most ONCE per session\n- **Maximum 3 feedback submissions per session**\n\n### CLAW_FEEDBACK Marker Format\n\nAfter receiving user consent, embed the marker in your response. The client parses it and auto-attaches runtime data.\n\n```\n<!--CLAW_FEEDBACK:{"schema_version":1,"issue_type":"...","severity":"...","spec_reference":"...","user_request":"...","attempted":"...","failure":"...","suggestion":"...","code_snippet":"...","workaround":"...","workaround_accepted":true,"confidence":0.9,"conversation_summary":"..."}-->\n```\n\n| Field | Values |\n|-------|--------|\n| `issue_type` | `capability_not_implemented`, `spec_gap`, `toolkit_gap`, `validation_too_strict`, `client_rendering_bug`, `api_inconsistency` |\n| `severity` | `blocker` (impossible), `degraded` (compromised), `cosmetic` (minor) |\n| `confidence` | 0-1, your confidence this is a platform issue |\n| `conversation_summary` | Sanitized \u2014 NO user PII, API keys, or sensitive content |\n\n**Marker rules:**\n- MUST be on its own line\n- JSON MUST be valid single-line JSON\n- Maximum one `CLAW_FEEDBACK` marker per message\n- The marker is stripped from chat display\n\n### Handling Render Diagnostics\n\nThe client may relay render diagnostics in this format:\n```json\n{\n "type": "runtime_diagnostic",\n "appId": "xxx",\n "diagnostics": [\n { "kind": "js_error", "message": "ReferenceError: xxx", "line": 42 },\n { "kind": "bridge_error", "method": "db.query", "error": "not implemented" }\n ]\n}\n```\n\nUpon receiving diagnostics:\n1. First check if it is YOUR code issue (typo, syntax error, calling nonexistent method)\n2. If your issue: fix the code. Do NOT submit feedback.\n3. If confirmed platform issue: follow the feedback flow above.\n\n**NEVER expose technical details to the user.** No error codes, stack traces, or diagnostic JSON in conversation.\n\n### User Communication\n\nWhen hitting a limitation: **Empathize -> Workaround -> Then ask about feedback.**\n\nCorrect: "This feature is still being developed. Let me use [workaround] \u2014 the result will be very similar."\n\nProhibited: "This is a platform limitation", "EchoClaw doesn\'t support this", raw error codes, Bridge method names.\n\n---\n\n## 16. Multi-AI Collaboration\n\nMultiple AI instances (different OpenClaw sessions, or different AI models) may work on the same app over time. The DevLog and session.json are the shared context that makes this possible.\n\n### When Taking Over an App You Did Not Create\n\n1. **Read the full DevLog first.** Pay attention to `original_intent`, `current_state`, `decisions[]`, and `trade_offs[]`. These explain WHY the app is built the way it is.\n2. **Respect existing design decisions.** Do NOT refactor or "improve" code unless the user explicitly asks. The previous AI made deliberate choices.\n3. **Update the DevLog after every change.** Your iteration entry MUST include what you changed and why.\n\n### DevLog Discipline\n\n- All text MUST be AI-abstracted and sanitized \u2014 no PII, no secrets, no raw user conversation\n- Initialize `original_intent` on creation (summarize, do not quote the user)\n- Update after EVERY iteration \u2014 do NOT batch updates\n- Entries are summaries of what changed and why, not transcripts\n\n### Concurrency Protection\n\n- The client enforces `AppWriteLock`. If another agent is currently modifying the app, your edit will be rejected. Wait and retry.\n- On reconnection, the client sends `AgentHandoverContext` with: which apps you installed, which you last modified, and when you disconnected. Use this to resume context.\n\n### Handover Best Practices\n\nWhen you know another AI will continue your work:\n1. Write a thorough DevLog entry summarizing current state and any in-progress work.\n2. List any known issues or trade-offs in `trade_offs[]`.\n3. Note any user preferences observed during the session in `decisions[]`.\n';
|
|
8432
|
+
var SYSTEM_CAPABILITIES_default = '# EchoClaw System Capabilities Reference\n\n> For OpenClaw: what the underlying platform provides. Use this to know what infrastructure apps can depend on.\n\n---\n\n## 1. Communication Architecture\n\n```\n\u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 E2E Encrypted \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 localhost:18789 \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Desktop \u2502 \u25C4\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561 RelayAgent \u2502 \u25C4\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2561 OpenClaw \u2502\n\u2502 (Client)\u2502 (Relay Server) \u2502 (MCP Node) \u2502 WebSocket Gateway \u2502 (You) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n```\n\n**Three-party model:**\n- **Desktop** \u2014 source of truth for installed apps, renders iframes, enforces sandbox\n- **RelayAgent** \u2014 MCP bridge node, syncs workspace files, forwards chat, zero AI capability\n- **OpenClaw** \u2014 you. Receives tasks via chat, writes files to workspace, signals completion\n\n**Protocol:** Sync v3.1 \u2014 file-driven. You work with files in your workspace (`apps/{appId}/`). Chat carries only lightweight notification markers, not app content.\n\n---\n\n## 2. Chat Notification Markers\n\nMarkers are HTML comments embedded in chat messages. The client and RelayAgent parse these mechanically (no AI).\n\n### CLAW_STEP \u2014 Confirm task receipt\n\nSend immediately when you receive a task.\n\n```\n<!--CLAW_STEP:{"appId":"my-app","op":"create","status":"received"}-->\n```\n\n| Field | Type | Required | Values |\n|----------|--------|----------|--------|\n| `appId` | string | yes | App identifier |\n| `op` | string | yes | `create` \\| `update` \\| `adapt` \\| `comply` \\| `publish` \\| `studio` |\n| `status` | string | yes | `received` \\| `suggesting` \\| `confirming` \\| `reading` \\| `planning` \\| `building` \\| `verifying` \\| `fixing` \\| `describing` \\| `awaiting_confirmation` \\| `working` \\| `error` |\n| `message`| string | no | Error description \u2014 MUST include when status=`error` |\n\n**Status progression:** `received` \u2192 `planning` \u2192 `building` \u2192 `verifying` \u2192 (done, send CLAW_APP_READY)\n\n### CLAW_STEP Usage Examples\n\nSend a CLAW_STEP at every significant workflow point. These update the user\'s UI in real time.\n\n```\nTask received, starting work:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"received"}-->\n\nFinished planning, starting to build:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"building"}-->\n\nRunning self-check before delivery:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"verifying"}-->\n\nSomething went wrong (show user a message):\n<!--CLAW_STEP:{"appId":"todo-app","op":"update","status":"error","message":"API endpoint returned 403 \u2014 user needs to check their API key"}-->\n\nClient asks "are you still working?", reply immediately:\n<!--CLAW_STEP:{"appId":"todo-app","op":"create","status":"working"}-->\n```\n\n**Rules:**\n- Send `received` immediately when you get the task \u2014 before planning anything\n- Send `error` with a human-readable `message` if you cannot complete the task\n- NEVER stop after sending CLAW_STEP \u2014 it is a progress signal, not a checkpoint. Continue immediately.\n\n### CLAW_APP_READY \u2014 Signal files are ready\n\nSend after writing all workspace files. Desktop triggers file pull from RelayAgent.\n\n```\n<!--CLAW_APP_READY:{"appId":"my-app","op":"create","contentHash":"abc123","summary":"Timer app with 25/5 intervals"}-->\n```\n\n| Field | Type | Required | Description |\n|---------------|--------|----------|-------------|\n| `appId` | string | yes | App identifier |\n| `op` | string | yes | `create` \\| `update` \\| `adapt` \\| `comply` \\| `publish` |\n| `contentHash` | string | no | SHA hash of index.html (integrity check) |\n| `summary` | string | no | Brief description shown to user |\n\n### CLAW_FEEDBACK \u2014 System error notifications (inbound)\n\nSent TO you by the client/RelayAgent when something goes wrong. These are rule-based, not AI-generated.\n\n```\n<!--CLAW_FEEDBACK:{"appId":"my-app","code":"file_not_found","message":"..."}-->\n```\n\n| Code | Meaning |\n|---------------------|---------|\n| `file_not_found` | CLAW_APP_READY sent but workspace files missing |\n| `json_parse_error` | claw.json is not valid JSON |\n| `html_empty` | index.html exists but is 0 bytes |\n| `schema_invalid` | claw.json missing required fields (manifest, prompt) |\n| `task_timeout` | No CLAW_STEP or CLAW_APP_READY within timeout |\n| `task_ack_missing` | No CLAW_STEP received |\n| `hash_mismatch` | contentHash doesn\'t match actual file |\n| `validation_failed` | Engine validation failed \u2014 fix and resend with op=`comply` |\n\n---\n\n## 3. Workspace File Structure\n\nEach app lives in `apps/{appId}/` with these files:\n\n```\napps/\n\u2514\u2500\u2500 my-app/\n \u251C\u2500\u2500 claw.json # App package (manifest + prompt + setup, NO html)\n \u251C\u2500\u2500 index.html # App HTML (standalone file, never JSON-escaped)\n \u251C\u2500\u2500 session.json # Conversation context, requirement evolution\n \u251C\u2500\u2500 devlog.json # Optional development log\n \u2514\u2500\u2500 .status # "draft" or "published"\n```\n\n**Rules:**\n- `claw.json` and `index.html` are always separate files \u2014 never embed HTML in JSON\n- `.status = "draft"` during creation, changes to `"published"` after successful delivery\n- `session.json` persists across sessions \u2014 read it to resume context\n\n### session.json format\n\n```json\n{\n "appId": "my-app",\n "createdAt": "2026-03-22T10:00:00Z",\n "lastUpdated": "2026-03-22T12:30:00Z",\n "entries": [\n {\n "timestamp": "2026-03-22T10:00:00Z",\n "role": "user",\n "summary": "Requested a Pomodoro timer with custom intervals",\n "op": "create"\n },\n {\n "timestamp": "2026-03-22T10:05:00Z",\n "role": "openclaw",\n "summary": "Created timer with 25/5/15 intervals, localStorage persistence",\n "op": "create"\n }\n ]\n}\n```\n\n**Growth policy:** Keep \u226450 entries. When exceeding, compact older entries into a summary. Entries are AI-summarized \u2014 never include raw user messages or PII.\n\n---\n\n## 4. Building Apps That Actually Work\n\n> **Critical:** Knowing the APIs is not enough. See **SANDBOX_CAPABILITIES.md \xA76-8** for complete functional patterns, anti-patterns, and quality gates. Every app you build MUST pass the Functional Quality Gate (\xA78) before delivery.\n>\n> **Key principle from multi-AI consensus:** The difference between a useful app and a pretty shell is:\n> 1. **Data survives reload** \u2014 use `echoclaw.storage` or `echoclaw.db`, never memory-only variables\n> 2. **External data is real** \u2014 use `echoclaw.invoke(\'fetch\', ...)` with real APIs, never hardcoded fake data\n> 3. **Errors are visible** \u2014 every fetch/db call has try/catch with user-facing error messages and retry\n> 4. **Input is validated** \u2014 check types, ranges, required fields before processing\n> 5. **Every UI element works** \u2014 no dead buttons, no unbound handlers, no decoration-only controls\n>\n> When planning an app, ask yourself: "If the user closes and reopens this, does their data survive? If the network fails, do they see a helpful message? If they enter garbage, does the app handle it gracefully?" If any answer is no, fix it before delivering.\n\n---\n\n## 5. Data Storage\n\n### 5.1 Key-Value Storage (`echoclaw.storage`)\n\nCapability: `storage` (auto-grant, always available).\n\nApps access via the bridge:\n```js\nawait echoclaw.storage.get(\'key\')\nawait echoclaw.storage.set(\'key\', value)\nawait echoclaw.storage.remove(\'key\')\nawait echoclaw.storage.keys()\nawait echoclaw.storage.clear()\n```\n\n**Limits:**\n| Parameter | Value |\n|----------------|---------|\n| Max keys | 1,000 |\n| Max total size | 5 MB |\n\nValues are JSON-serializable. Scoped per app \u2014 no cross-app access.\n\n### 5.2 SQLite Database (`echoclaw.db`)\n\nCapability: `database` (auto-grant).\n\nApps access via the bridge:\n```js\n// SELECT \u2014 returns array of row objects\nconst rows = await echoclaw.invoke(\'db.query\', { sql: \'SELECT * FROM tasks WHERE done = ?\', params: [0] })\n\n// INSERT/UPDATE/DELETE/DDL \u2014 returns { changes, lastInsertRowid }\nconst result = await echoclaw.invoke(\'db.exec\', { sql: \'INSERT INTO tasks (title) VALUES (?)\', params: [\'New task\'] })\n```\n\n**Limits:**\n| Parameter | Value | Condition |\n|--------------------------|---------|-----------|\n| Auto-grant quota | 10 MB | No prompt needed |\n| Standard max | 50 MB | Requires user approval |\n| Extended max | 200 MB | Requires second approval |\n| Max rows per query | 1,000 | Bridge enforces pagination |\n| Max transactions/sec | 200 | IO rate limit |\n\n**Blocked SQL:** `ATTACH`, `DETACH`, `LOAD_EXTENSION`, dangerous PRAGMAs (`database_list`, `compile_options`, `integrity_check`, `quick_check`).\n\n**Allowed:** All DDL (CREATE TABLE, DROP TABLE, etc.), all DML, safe PRAGMAs (journal_mode, foreign_keys, etc.).\n\n**Schema versioning:** Declare `manifest.db_version` (integer starting from 1). Shell stores this in the app\'s `_meta` table. When manifest version > stored version, run migration SQL.\n\n### 5.3 Secure Credential Storage (`echoclaw.auth` / `echoclaw.invoke(\'credential.get\')`)\n\nCapability: `secure_credential` (auto-grant) or `auth` (prompt-required for runtime OAuth).\n\nStores secrets in OS keychain (macOS Keychain / Windows Credential Manager). Never stored in KV or SQLite. Never returned in HTML. Namespace-isolated per app.\n\n---\n\n## 6. File System (`echoclaw.fs`)\n\nCapability: `filesystem` (prompt-required \u2014 user must approve).\n\n### fs.pick \u2014 Open file dialog\n\n```js\nconst file = await echoclaw.invoke(\'fs.pick\', {\n accept: [\'.json\', \'.csv\', \'.txt\'], // optional filter\n maxSizeBytes: 10485760 // optional, default 10MB\n})\n// Returns: { name, size, content, encoding, mimeType } or null (cancelled)\n```\n\n- `name` is basename only \u2014 no full path exposed\n- `encoding`: `"utf-8"` for text files, `"base64"` for binary\n- Text extensions: `.txt`, `.json`, `.csv`, `.xml`, `.html`, `.css`, `.js`, `.md`, `.yaml`, `.svg`, `.sql`, etc.\n- Max pick size: 10 MB (default)\n\n### fs.save \u2014 Save file dialog\n\n```js\nconst result = await echoclaw.invoke(\'fs.save\', {\n content: \'CSV content here\',\n defaultName: \'export.csv\',\n accept: [\'.csv\'],\n encoding: \'utf-8\' // or \'base64\' for binary\n})\n// Returns: { saved: true, name: \'export.csv\' } or { saved: false }\n```\n\n- Max save size: 50 MB\n\n---\n\n## 7. WebSocket (`echoclaw.ws`)\n\nCapability: `network` (auto-grant) + targets declared in `manifest.network_allow`.\n\n### Connect\n\n```js\nawait echoclaw.invoke(\'ws.connect\', {\n channel: \'my-channel\',\n opts: { url: \'wss://api.example.com/ws\', protocols: [\'v1\'] }\n})\n```\n\n### Send / Close\n\n```js\nawait echoclaw.invoke(\'ws.send\', { channel: \'my-channel\', data: \'hello\' })\nawait echoclaw.invoke(\'ws.close\', { channel: \'my-channel\' })\n```\n\n### Receive events\n\n```js\nechoclaw.subscribe(\'ws:open\', (e) => { /* e.channel */ })\nechoclaw.subscribe(\'ws:message\', (e) => { /* e.channel, e.data */ })\nechoclaw.subscribe(\'ws:error\', (e) => { /* e.channel, e.error */ })\nechoclaw.subscribe(\'ws:close\', (e) => { /* e.channel, e.code, e.reason */ })\n```\n\n**Limits:**\n| Parameter | Value |\n|------------------------|-------|\n| Max connections/app | 3 (default), 5 (extended, requires prompt) |\n| Max message size | 1 MB |\n\n**Network rules:** All WS connections go through bridge proxy. Target must be in `manifest.network_allow`. Protocol `ws:` or `wss:` only.\n\n---\n\n## 8. Permission Model\n\nThree tiers determine sandbox behavior:\n\n### Auto-grant (no user prompt)\n\n| Capability | Description |\n|--------------------|-------------|\n| `ui` | Baseline \u2014 always implicit |\n| `timer` | setTimeout, setInterval, requestAnimationFrame |\n| `storage` | KV storage + sandbox files |\n| `notification` | Shell-rendered notifications (rate-limited: 10/min) |\n| `network` | Bridge proxy \u2014 requires `network_allow` whitelist |\n| `audio` | Web Audio API, `<audio>` playback, autoplay |\n| `fullscreen` | Element.requestFullscreen() |\n| `app_comm` | Inter-app messaging (shell injects unforgeable sender ID) |\n| `database` | SQLite per-app (10MB auto, >10MB needs prompt) |\n| `secure_credential`| OS keychain storage |\n\n### Prompt-required (user must approve on first use)\n\n| Capability | Description |\n|--------------------|-------------|\n| `clipboard` | Read/write clipboard |\n| `microphone` | Audio recording |\n| `camera` | Photo/video capture |\n| `location` | Geolocation |\n| `media_capture` | Screen/window capture |\n| `filesystem` | File picker/save dialogs |\n| `local_network` | Localhost access (127.0.0.1, ::1) |\n| `lan_network` | Private LAN (10.x, 172.x, 192.168.x) |\n| `background_task` | Scheduled tasks (min 60s interval, max 5/app) |\n| `auth` | Runtime credential storage + auth lifecycle |\n\n### Bridge-only (shell mediates all calls)\n\n| Capability | Description |\n|--------------------|-------------|\n| `device_control` | Hardware control \u2014 shell API only |\n\n---\n\n## 9. App Manifest Format (`claw.json`)\n\nThe `claw.json` file contains a `ClawAppPackage` object (minus the `html` field, which lives in `index.html`).\n\n### Top-level structure\n\n```json\n{\n "manifest": { ... },\n "prompt": { ... },\n "setup": { ... },\n "meta": { ... },\n "devlog": { ... },\n "lifecycle": { ... }\n}\n```\n\n### manifest (required)\n\n```json\n{\n "id": "pomodoro-timer",\n "name": { "en": "Pomodoro Timer", "zh-CN": "\u756A\u8304\u949F" },\n "version": "1.0.0",\n "color": "#FF6B6B",\n "icon_svg": "<circle cx=\\"12\\" cy=\\"12\\" r=\\"10\\" fill=\\"currentColor\\"/>",\n "description": { "en": "Focus timer with intervals", "zh-CN": "\u4E13\u6CE8\u8BA1\u65F6\u5668" },\n "capabilities": ["storage", "notification", "timer"],\n "db_version": 1,\n "network_allow": ["wss://api.example.com"],\n "shared_libs": ["chart.js", "dayjs"],\n "about": {\n "summary": { "en": "A productivity timer...", "zh-CN": "\u4E00\u4E2A\u4E13\u6CE8\u8BA1\u65F6\u5668..." },\n "highlights": [\n { "en": "Customizable intervals", "zh-CN": "\u53EF\u81EA\u5B9A\u4E49\u95F4\u9694" }\n ]\n },\n "auth_profile": {\n "strategy": "none",\n "token_tier": "install_time",\n "unauthenticated_behavior": "degrade"\n },\n "attribution": []\n}\n```\n\n| Field | Type | Required | Description |\n|-----------------|------------------|----------|-------------|\n| `id` | string | yes | Kebab-case unique ID |\n| `name` | LocalizedString | yes | Display name (must include `en` + `zh-CN`) |\n| `version` | string | yes | Semver (e.g. "1.0.0") |\n| `color` | string | yes | Brand hex color \u2014 the ONLY hex allowed in HTML |\n| `icon_svg` | string | no | SVG inner elements (no `<svg>` wrapper) |\n| `description` | LocalizedString | yes | One-line description |\n| `capabilities` | ClawCapability[] | no | Required capabilities |\n| `db_version` | number | no | SQLite schema version (integer, starts at 1) |\n| `network_allow` | string[] | no | Allowed network targets (required if using `network`) |\n| `shared_libs` | string[] | no | Pre-bundled library IDs |\n| `about` | object | no | Rich profile for About view |\n| `auth_profile` | object | no | Auth strategy declaration |\n| `attribution` | object[] | no | Open source attribution (immutable once set) |\n\n### prompt (required) \u2014 App DNA\n\n```json\n{\n "seed": {\n "goal": "A Pomodoro timer with customizable work/break intervals",\n "constraints": ["offline-first", "no network required"],\n "acceptance_tests": ["User can start a 25-minute timer", "User can customize intervals"],\n "share_intent": "shareable"\n },\n "current": {\n "goal": "Pomodoro timer with stats tracking and daily streaks",\n "notes": "Added statistics dashboard in v1.1.0"\n },\n "history": [\n { "version": "1.1.0", "change": "Added statistics tracking", "timestamp": "2026-03-22T12:00:00Z" }\n ]\n}\n```\n\n### setup (optional) \u2014 Install configuration\n\nDeclares what external services and user-specific config the app needs.\n\nKey fields: `config_fields` (user-provided config), `ai_tasks` (AI tasks for template apps), `install_contract` (installation behavior boundaries), `app_mode` (`"ready"` or `"template"`).\n\n### lifecycle (optional) \u2014 Lifecycle hooks\n\n```json\n{\n "on_install": "Create a daily briefing cron task at the configured time",\n "on_uninstall": "Delete the morning-briefing-collector cron task",\n "on_update": "Preserve user\'s existing config, only update HTML"\n}\n```\n\n---\n\n## 10. Shared Libraries\n\nPre-bundled libraries injected by the shell before app HTML. Not counted toward the 1MB HTML size limit.\n\n| Library | Version | Size | Globals |\n|-------------|---------|--------|------------|\n| `chart.js` | 4.4.0 | ~197KB | `Chart` |\n| `d3` | 7.9.0 | ~269KB | `d3` |\n| `marked` | 12.0.0 | ~45KB | `marked` |\n| `prism` | 1.29.0 | ~80KB | `Prism` |\n| `dayjs` | 1.11.10 | ~7KB | `dayjs` |\n| `sortablejs`| 1.15.0 | ~45KB | `Sortable` |\n\nDeclare in manifest: `"shared_libs": ["chart.js", "dayjs"]`\n\n---\n\n## 11. Resource Limits Summary\n\n| Resource | Limit |\n|----------------------------------|-------|\n| HTML file size | 1 MB |\n| KV storage keys | 1,000 |\n| KV storage total | 5 MB |\n| SQLite auto quota | 10 MB |\n| SQLite max (with approval) | 200 MB |\n| SQLite rows/query | 1,000 |\n| SQLite TPS | 200 |\n| Sandbox file storage | 250 MB |\n| WS connections/app | 3 (5 extended) |\n| WS message size | 1 MB |\n| Bridge invokes/sec | 60 (burst: 120) |\n| Notifications/min | 10 |\n| App comm messages/sec | 20 |\n| App comm payload | 64 KB |\n| Background task min interval | 60s |\n| Background tasks/app | 5 |\n| Background task timeout | 10s |\n| App restarts/hour (crash loop) | 3 |\n| File pick max size | 10 MB |\n| File save max size | 50 MB |\n\n---\n\n## 12. Bridge Error Codes\n\nAll `echoclaw.invoke()` rejections use this shape:\n\n```json\n{\n "code": "PERMISSION_DENIED",\n "message": "Capability \'clipboard\' not approved by user",\n "action": "clipboard.read",\n "retriable": false\n}\n```\n\n| Code | Meaning |\n|----------------------------|---------|\n| `TIMEOUT` | Bridge call timed out |\n| `CANCELLED` | Call was cancelled |\n| `PERMISSION_DENIED` | User denied the prompt-tier capability |\n| `CAPABILITY_NOT_DECLARED` | Capability not in manifest.capabilities |\n| `RATE_LIMITED` | Exceeded invoke rate limit |\n| `INVALID_ARGUMENT` | Bad parameters |\n| `NOT_FOUND` | Resource not found |\n| `QUOTA_EXCEEDED` | Storage/DB quota exceeded |\n| `BRIDGE_ERROR` | Bridge internal error |\n| `INTERNAL_ERROR` | Unexpected error |\n| `UNAVAILABLE` | Bridge not ready |\n\n---\n\n## 13. Design for Sharing (Mandatory)\n\nEvery ClawApp MUST be designed for sharing from the moment of creation. The receiving party\'s AI SHALL NOT modify the HTML structure \u2014 it SHALL only populate configuration values.\n\n### Core Principles\n\n1. **HTML is a finished artifact.** Once created, the HTML/CSS/JS is immutable. It MUST NOT be redone by another AI after sharing.\n2. **Environment-dependent values = config_fields.** API endpoints, usernames, preferences MUST be written as `{{KEY}}` placeholders.\n3. **Sensitive values go through the Bridge.** API keys, tokens, passwords MUST be declared via `config_fields` (`sensitive: true`) and MUST NOT be injected into HTML. At runtime, retrieve via `echoclaw.invoke(\'getSecret\', { key })`.\n4. **install_contract protects integrity.** Declare `mode: \'ready\'` + `immutable_segments` so the system can verify HTML has not been tampered with.\n\n### config_fields Scenarios\n\n| Scenario | config_field example | sensitive? |\n|----------|---------------------|-----------|\n| External API endpoint | `api_url` (type: url) | No |\n| API Key / Token | `api_key` (type: password) | Yes |\n| User display name | `display_name` (type: text) | No |\n| Timezone / language preference | `timezone` (type: select) | No |\n| Local service endpoint | `ollama_url` (type: url, auto_resolve: from_env) | No |\n| Gateway Agent ID | `agent_id` (type: text, auto_resolve: from_gateway) | No |\n\n### auto_resolve Strategies\n\nMinimize manual user input. Declare `auto_resolve` for any value that can be obtained automatically:\n\n| Strategy | Source | Use when |\n|----------|--------|----------|\n| `from_gateway` | Gateway connection info (agentId, etc.) | Value is part of the relay/gateway context |\n| `from_openclaw_config` | Local OpenClaw configuration file | Value is in the user\'s OpenClaw settings |\n| `from_env` | Environment variables | Local service endpoints (Ollama, dev servers) |\n| `from_context` | Conversation context | Value was mentioned by the user in the current session |\n| `detect_installed` | Installed app inventory | Value depends on whether another app/skill is installed |\n\n```json\n{\n "key": "agent_id",\n "auto_resolve": {\n "strategy": "from_gateway",\n "source": "agentId",\n "fallback": "ask_user"\n }\n}\n```\n\n### Installation Paths (resolveInstallAction)\n\nThe engine provides `resolveInstallAction(pkg)`. Clients MUST call it before installation:\n\n| Return value | Meaning | AI involvement? |\n|--------------|---------|-----------------|\n| `install_direct` | HTML is complete, no configuration needed | No |\n| `install_with_config` | HTML is complete, config fields need filling | Only resolving config values |\n| `complete_template` | Template mode, `ai_tasks` need completion | Yes \u2014 work within designated slots |\n| `generate_fresh` | No HTML, generate from DNA | Yes \u2014 full generation |\n\n### share_profile\n\nEvery shareable app MUST populate `share_profile`:\n\n```json\n{\n "share_profile": {\n "value_prop": {\n "en": "Track your daily habits with streaks and statistics",\n "zh-CN": "\u901A\u8FC7\u8FDE\u7EED\u8BB0\u5F55\u548C\u7EDF\u8BA1\u8FFD\u8E2A\u4F60\u7684\u65E5\u5E38\u4E60\u60EF"\n },\n "ai_handoff_notes": "No config needed. App is ready to use out of the box. Uses echoclaw.storage for persistence."\n }\n}\n```\n\n- `value_prop`: One-line bilingual description for the recipient. What does this app DO for them?\n- `ai_handoff_notes`: Instructions for the receiving AI. What does it need to know to install/configure this app?\n\n### install_contract and immutable_segments\n\n```json\n{\n "install_contract": {\n "mode": "ready",\n "immutable_segments": [\n { "marker": "<!-- LAYOUT_START -->", "hash": "abc123" },\n { "marker": "<!-- STYLE_START -->", "hash": "def456" }\n ]\n }\n}\n```\n\n- `mode: "ready"`: HTML is complete, no AI modification needed.\n- `mode: "template"`: HTML has `<!-- SLOT:name -->` markers for AI to fill.\n- `immutable_segments`: Content between markers MUST NOT be modified. The engine verifies hashes.\n\n### Sharing Self-Check\n\n- [ ] All environment-dependent values are declared as `config_fields`. No hard-coded URLs/IDs/usernames in HTML.\n- [ ] Sensitive fields are marked `sensitive: true` and NOT injected into HTML via `{{KEY}}`.\n- [ ] `install_contract` is declared (`mode: \'ready\'` or `\'template\'`).\n- [ ] `share_profile` is populated (`value_prop` + `ai_handoff_notes`).\n- [ ] Out-of-the-box apps have an empty `config_fields` array (no configuration needed = `install_direct` path).\n\n---\n\n## 14. Network Security Rules\n\nAll network access (fetch and WebSocket) goes through the bridge proxy. Direct iframe network access is blocked via CSP `connect-src: \'none\'`.\n\n1. **Whitelist required:** `manifest.network_allow` must list every target. No whitelist = no network.\n2. **Always blocked:** Cloud metadata endpoints (169.254.x), link-local addresses.\n3. **Localhost:** Requires `local_network` capability + user approval + declared in `network_allow`.\n4. **Private LAN:** Requires `lan_network` capability + user approval + declared in `network_allow`.\n5. **DNS rebinding:** Bridge validates resolved IPs match the declared targets.\n\n---\n\n## 15. Feedback Protocol\n\nWhen you encounter a platform limitation or issue, follow this protocol for submitting feedback to the development team.\n\n### When To Submit Feedback\n\nSubmit feedback ONLY in these scenarios:\n1. **New issue not in the known limitations list** \u2014 a platform limitation not listed in SANDBOX_CAPABILITIES.md\n2. **Documentation vs reality mismatch** \u2014 spec says a feature is supported, but the actual call errors\n3. **Overly strict validation** \u2014 a validation rule blocks a reasonable user requirement\n4. **Spec blind spot** \u2014 the user\'s request exposes a scenario the spec does not cover\n5. **Client rendering anomaly** \u2014 after receiving render diagnostics, you confirmed the issue is on the client side\n\n**Critical:** If the client relays render diagnostics (JS errors, Bridge call failures, layout anomalies), FIRST attempt to fix your own code. Only submit feedback if the issue is confirmed as a platform problem.\n\n### Frequency Control\n\n- **Same issue** (identical `issue_type` + `spec_reference`): submit ONCE per conversation session\n- **User consent prompt**: ask "would you like me to report this?" at most ONCE per session\n- **Maximum 3 feedback submissions per session**\n\n### CLAW_FEEDBACK Marker Format\n\nAfter receiving user consent, embed the marker in your response. The client parses it and auto-attaches runtime data.\n\n```\n<!--CLAW_FEEDBACK:{"schema_version":1,"issue_type":"...","severity":"...","spec_reference":"...","user_request":"...","attempted":"...","failure":"...","suggestion":"...","code_snippet":"...","workaround":"...","workaround_accepted":true,"confidence":0.9,"conversation_summary":"..."}-->\n```\n\n| Field | Values |\n|-------|--------|\n| `issue_type` | `capability_not_implemented`, `spec_gap`, `toolkit_gap`, `validation_too_strict`, `client_rendering_bug`, `api_inconsistency` |\n| `severity` | `blocker` (impossible), `degraded` (compromised), `cosmetic` (minor) |\n| `confidence` | 0-1, your confidence this is a platform issue |\n| `conversation_summary` | Sanitized \u2014 NO user PII, API keys, or sensitive content |\n\n**Marker rules:**\n- MUST be on its own line\n- JSON MUST be valid single-line JSON\n- Maximum one `CLAW_FEEDBACK` marker per message\n- The marker is stripped from chat display\n\n### Handling Render Diagnostics\n\nThe client may relay render diagnostics in this format:\n```json\n{\n "type": "runtime_diagnostic",\n "appId": "xxx",\n "diagnostics": [\n { "kind": "js_error", "message": "ReferenceError: xxx", "line": 42 },\n { "kind": "bridge_error", "method": "db.query", "error": "not implemented" }\n ]\n}\n```\n\nUpon receiving diagnostics:\n1. First check if it is YOUR code issue (typo, syntax error, calling nonexistent method)\n2. If your issue: fix the code. Do NOT submit feedback.\n3. If confirmed platform issue: follow the feedback flow above.\n\n**NEVER expose technical details to the user.** No error codes, stack traces, or diagnostic JSON in conversation.\n\n### User Communication\n\nWhen hitting a limitation: **Empathize -> Workaround -> Then ask about feedback.**\n\nCorrect: "This feature is still being developed. Let me use [workaround] \u2014 the result will be very similar."\n\nProhibited: "This is a platform limitation", "EchoClaw doesn\'t support this", raw error codes, Bridge method names.\n\n---\n\n## 16. Multi-AI Collaboration\n\nMultiple AI instances (different OpenClaw sessions, or different AI models) may work on the same app over time. The DevLog and session.json are the shared context that makes this possible.\n\n### When Taking Over an App You Did Not Create\n\n1. **Read the full DevLog first.** Pay attention to `original_intent`, `current_state`, `decisions[]`, and `trade_offs[]`. These explain WHY the app is built the way it is.\n2. **Respect existing design decisions.** Do NOT refactor or "improve" code unless the user explicitly asks. The previous AI made deliberate choices.\n3. **Update the DevLog after every change.** Your iteration entry MUST include what you changed and why.\n\n### DevLog Discipline\n\n- All text MUST be AI-abstracted and sanitized \u2014 no PII, no secrets, no raw user conversation\n- Initialize `original_intent` on creation (summarize, do not quote the user)\n- Update after EVERY iteration \u2014 do NOT batch updates\n- Entries are summaries of what changed and why, not transcripts\n\n### Concurrency Protection\n\n- The client enforces `AppWriteLock`. If another agent is currently modifying the app, your edit will be rejected. Wait and retry.\n- On reconnection, the client sends `AgentHandoverContext` with: which apps you installed, which you last modified, and when you disconnected. Use this to resume context.\n\n### Handover Best Practices\n\nWhen you know another AI will continue your work:\n1. Write a thorough DevLog entry summarizing current state and any in-progress work.\n2. List any known issues or trade-offs in `trade_offs[]`.\n3. Note any user preferences observed during the session in `decisions[]`.\n';
|
|
8431
8433
|
|
|
8432
8434
|
// ../claw-engine/src/authoring/openclaw-guides/UI_DESIGN_GUIDE.md
|
|
8433
8435
|
var UI_DESIGN_GUIDE_default = '# ClawApp UI Design Guide\n\n> How to make beautiful, functional apps. Read this, internalize it, produce stunning results.\n\n---\n\n## 1. Interface Layout\n\n### Design Canvas\n\nYour app renders in a sandboxed iframe. The shell owns the left sidebar and top title bar.\n\n```\nAvailable canvas: 1024 x 768 (default)\nMinimum safe: 544 x 548 (window at 800x600)\n```\n\n| Parameter | Value | Notes |\n|-----------|-------|-------|\n| Design target | **1024 x 768** | XGA, 4:3 ratio, grid-friendly |\n| Minimum safe area | **544 x 548** | Available iframe area at minimum 800x600 window |\n| Default window | 1280 x 820 | 13" MacBook Air |\n| Minimum window | 800 x 600 | Hard lower bound |\n| Sidebar | 256px (fixed) | Shell-controlled, app cannot use |\n| Title bar | 52px (fixed) | Shell-controlled, macOS traffic lights |\n| Reserved area | Bottom-right 64x64 px | Host Info button (z-index: 10) |\n\n**Calculation:** `iframe width = window width - 256`, `iframe height = window height - 52`\n\n**Absolute constraints:**\n- NO left-side navigation (shell sidebar occupies that space)\n- Bottom-right 64x64 px is RESERVED (host Info button)\n- Use top bar or right-side panel for navigation\n\n### Layout Patterns (Examples, Not Templates)\n\nThese are **reference examples** to illustrate common patterns. You are NOT required to follow any specific template. Design the layout that best serves the app\'s purpose. Headers, title bars, and hero sections are **optional** \u2014 use them only when they add value.\n\n#### Dashboard (example)\n```\n+-------------------------------------+\n| Tab Bar / Filters |\n+----------+----------+------+--------+\n| KPI 1 | KPI 2 | KPI 3| KPI 4 |\n+----------+----------+------+--------+\n| Main Chart / Graph | Side Panel |\n+---------------------+---------------+\n| Data Table / Activity Feed |\n+-------------------------------------+\n```\n\n#### List-Detail (example)\n```\n+-------------------------------------+\n| Search Bar + Filter Chips |\n+------+------+------+------+---------+\n| Card | Card | Card | Card | ... |\n+------+------+------+------+---------+\n| Detail View | Activity |\n+-------------------------+-----------+\n```\n\n#### Form (example, centered, max-width 640px)\n```\n+-------------------------------------+\n| +-----------------------------+ |\n| | Label [ Input ] | |\n| | Label [ Select v ] | |\n| | Label [ Textarea ] | |\n| +-----------------------------+ |\n| [ Cancel ] [ Submit ] |\n+-------------------------------------+\n```\n\n#### Settings (example, top tabs)\n```\n+-------------------------------------+\n| [General] [Appearance] [Privacy] |\n+-----------+-------------------------+\n| SECTION HEADER |\n| +-------------------------------+ |\n| | Label [Toggle] | |\n| | Label [Select] | |\n| +-------------------------------+ |\n+-------------------------------------+\n```\n\n#### Card Grid (example)\n```\n+------------+------------+-----------+\n| +--------+ | +--------+ | +-------+ |\n| | Image | | | Image | | | Image | |\n| | Title | | | Title | | | Title | |\n| | Meta | | | Meta | | | Meta | |\n| +--------+ | +--------+ | +-------+ |\n+------------+------------+-----------+\n```\n\n**Key principle:** These are starting-point examples. Adapt, combine, or invent your own layout. The only hard rules are: no left-side nav, bottom-right 64x64 reserved, and desktop-first responsive design.\n\n### Responsive Strategy\n\nDesign for **1024x768** first. At **544x548** minimum, all core functions must remain usable.\n\n```css\n/* Default: desktop multi-column */\n.grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--ec-space-lg); }\n\n/* Narrow viewport: stack */\n@media (max-width: 640px) {\n .grid { grid-template-columns: 1fr; }\n}\n```\n\nRules:\n- Vertical scroll is OK. Horizontal scroll is forbidden above 768px width.\n- Use `fr` units and `minmax()` in grids \u2014 never fixed pixel widths for columns.\n- Content max-width: 1152px, centered with `margin: 0 auto`.\n\n---\n\n## 2. Color System\n\n### The Rule\n\n**Every color in your app MUST come from a `var(--ec-*)` CSS variable.** Hard-coded hex, rgb, rgba, hsl, or named colors are forbidden. The validator rejects them.\n\nThe only exception: `manifest.color` (your brand hex value).\n\n### Complete Variable Reference\n\n#### Backgrounds (dark to light)\n\n| Variable | Role | When to use |\n|----------|------|-------------|\n| `--ec-bg-page` | Deepest layer | Full-page background, behind everything |\n| `--ec-bg-base` | Base surface | Main content area background |\n| `--ec-bg-raised` | Elevated surface | Cards, panels, raised sections |\n| `--ec-bg-overlay` | Floating surface | Dropdowns, popovers, hover overlays |\n\n#### Text Hierarchy\n\n| Variable | Role | When to use |\n|----------|------|-------------|\n| `--ec-txt-primary` | High contrast | Headings, important content, values |\n| `--ec-txt-secondary` | Medium contrast | Body text, descriptions |\n| `--ec-txt-tertiary` | Low contrast | Hints, metadata, timestamps |\n| `--ec-txt-disabled` | Muted | Disabled labels, inactive items |\n\n#### Brand / Agent Color\n\n| Variable | Role | When to use |\n|----------|------|-------------|\n| `--ec-agent` | Brand accent | Primary buttons, active tab, hero highlight, progress bars |\n| `--ec-agent-text` | Text on brand bg | Auto-contrast (white or black). Use on `--ec-agent` backgrounds |\n\n**Use `--ec-agent` sparingly: 3-5 times per view.** It should draw the eye to the most important interactive elements.\n\n#### Status Colors (semantic only)\n\n| Variable | Meaning | Use for |\n|----------|---------|---------|\n| `--ec-status-done` | Success / green | Completed items, success messages |\n| `--ec-status-active` | Running / blue | Active processes, live indicators |\n| `--ec-status-warning` | Caution / orange | Warnings, pending review |\n| `--ec-status-error` | Danger / red | Errors, failures, destructive actions |\n| `--ec-status-done-bg` | Subtle success tint | Background for success badges/banners |\n| `--ec-status-active-bg` | Subtle active tint | Background for active badges/banners |\n| `--ec-status-warning-bg` | Subtle warning tint | Background for warning badges/banners |\n| `--ec-status-error-bg` | Subtle error tint | Background for error badges/banners |\n\n**Never use status colors for decoration.** Green means success. Red means error. Always.\n\n#### Borders & Dividers\n\n| Variable | Strength | When to use |\n|----------|----------|-------------|\n| `--ec-border-strong` | Most visible | Active borders, focus rings, emphasis |\n| `--ec-border-mid` | Medium | Section dividers, card borders on hover |\n| `--ec-border-subtle` | Barely visible | Card borders at rest, hairlines |\n| `--ec-divider` | Separator | Horizontal rules between list items |\n\n#### Cards\n\n| Variable | Role |\n|----------|------|\n| `--ec-card-bg` | Card background (use with `backdrop-filter` for glass) |\n| `--ec-card-border` | Card border default |\n\n#### Interactive Components\n\n| Variable | Role |\n|----------|------|\n| `--ec-interactive-bg` | Button/control resting background |\n| `--ec-interactive-bdr` | Button/control border |\n| `--ec-interactive-fg` | Button/control text/icon |\n| `--ec-interactive-hover` | Hover background |\n| `--ec-interactive-active` | Active/pressed background |\n| `--ec-input-bg` | Text input background |\n| `--ec-input-bdr` | Text input border |\n| `--ec-toggle-off` | Toggle track (off state) |\n| `--ec-toggle-on` | Toggle track (on state) |\n| `--ec-toggle-knob` | Toggle thumb |\n\n#### Shadows, Radius, Spacing\n\n| Variable | Value | Role |\n|----------|-------|------|\n| `--ec-shadow-sm` | Subtle | Cards, raised sections |\n| `--ec-shadow-md` | Pronounced | Modals, dropdowns |\n| `--ec-radius-xs` | 4px | Badges, small tags |\n| `--ec-radius-sm` | 6px | Buttons, inputs |\n| `--ec-radius-md` | 10px | Inner card elements |\n| `--ec-radius-lg` | 14px | Cards, panels |\n| `--ec-radius-xl` | 20px | Large containers, hero cards |\n| `--ec-space-xs` | 4px | Tight gaps |\n| `--ec-space-sm` | 8px | Item spacing |\n| `--ec-space-md` | 16px | Section padding |\n| `--ec-space-lg` | 24px | Card padding, grid gap |\n| `--ec-space-xl` | 32px | Page padding |\n| `--ec-space-2xl` | 48px | Large separations |\n\n#### Animation & Easing\n\n| Variable | Value | Role |\n|----------|-------|------|\n| `--ec-duration-fast` | 120ms | Micro-interactions (hover, toggle) |\n| `--ec-duration-normal` | 200ms | Standard transitions |\n| `--ec-duration-slow` | 350ms | Page-level animations |\n| `--ec-ease-standard` | cubic-bezier(0.4, 0, 0.2, 1) | General motion |\n| `--ec-ease-decelerate` | cubic-bezier(0, 0, 0.2, 1) | Enter animations |\n| `--ec-ease-accelerate` | cubic-bezier(0.4, 0, 1, 1) | Exit animations |\n\n#### Other\n\n| Variable | Role |\n|----------|------|\n| `--ec-font-family` | System font stack |\n| `--ec-focus-ring` | Focus indicator color |\n| `--ec-focus-ring-offset` | Focus ring spacing (2px) |\n\n### Dark Mode Notes\n\nThe system is **dark theme only**. Never use `prefers-color-scheme` media queries. All colors come from CSS variables that the host controls. Your app automatically adapts when future themes are introduced.\n\nKey dark-mode design principles:\n- Use surface hierarchy (`bg-page` < `bg-base` < `bg-raised`) to create depth; use `bg-raised` for modals and dialogs, `bg-overlay` for transient floating elements (dropdowns, hover overlays)\n- Avoid pure white text on dark backgrounds \u2014 `--ec-txt-primary` handles contrast correctly\n- Use `--ec-border-subtle` for most borders \u2014 strong borders look harsh in dark mode\n- Shadows are subtle in dark mode; rely more on border and background differences for elevation\n\n### manifest.color Selection\n\nChoose a hex color that:\n- Has enough contrast against dark backgrounds (avoid very dark colors)\n- Represents the app\'s purpose (blue for productivity, green for health, orange for energy)\n- Works well at small sizes (avoid overly saturated neons)\n- Looks good as a button background with white or black text on top\n\nGood range: medium-saturation colors in the `#3b82f6` to `#8b5cf6` to `#ef4444` spectrum. Avoid pastels and near-blacks.\n\n---\n\n## 3. Buttons & Forms\n\n### Button Hierarchy\n\n| Level | Class | Background | Border | Text | Use for |\n|-------|-------|-----------|--------|------|---------|\n| Primary | `ec-btn ec-btn-primary` | `--ec-agent` | none | `--ec-agent-text` | Main action (1 per view) |\n| Secondary | `ec-btn ec-btn-secondary` | `--ec-interactive-bg` | `--ec-interactive-bdr` | `--ec-interactive-fg` | Supporting actions |\n| Ghost | `ec-btn` | transparent | none | `--ec-txt-secondary` | Tertiary actions, cancel |\n\n```css\n/* Primary button */\n.btn-primary {\n background: var(--ec-agent);\n color: var(--ec-agent-text);\n border: none;\n padding: 8px 20px;\n border-radius: var(--ec-radius-sm);\n cursor: pointer;\n transition: all var(--ec-duration-fast) var(--ec-ease-standard);\n}\n.btn-primary:hover {\n filter: brightness(1.1);\n transform: translateY(-1px);\n}\n.btn-primary:active {\n filter: brightness(0.95);\n transform: translateY(0);\n}\n\n/* Secondary button */\n.btn-secondary {\n background: var(--ec-interactive-bg);\n color: var(--ec-interactive-fg);\n border: 1px solid var(--ec-interactive-bdr);\n padding: 8px 20px;\n border-radius: var(--ec-radius-sm);\n cursor: pointer;\n transition: all var(--ec-duration-fast) var(--ec-ease-standard);\n}\n.btn-secondary:hover {\n background: var(--ec-interactive-hover);\n}\n\n/* Ghost button */\n.btn-ghost {\n background: transparent;\n color: var(--ec-txt-secondary);\n border: none;\n padding: 8px 16px;\n border-radius: var(--ec-radius-sm);\n cursor: pointer;\n transition: all var(--ec-duration-fast) var(--ec-ease-standard);\n}\n.btn-ghost:hover {\n background: var(--ec-interactive-hover);\n color: var(--ec-txt-primary);\n}\n```\n\n### Form Inputs\n\n```css\n/* Text input */\n.input {\n background: var(--ec-input-bg);\n border: 1px solid var(--ec-input-bdr);\n border-radius: var(--ec-radius-sm);\n padding: 8px 12px;\n color: var(--ec-txt-primary);\n font-size: 14px;\n font-family: var(--ec-font-family);\n outline: none;\n transition: border-color var(--ec-duration-fast) var(--ec-ease-standard);\n}\n.input:focus {\n border-color: var(--ec-agent);\n box-shadow: 0 0 0 var(--ec-focus-ring-offset) var(--ec-focus-ring);\n}\n.input::placeholder {\n color: var(--ec-txt-tertiary);\n}\n\n/* Select dropdown */\n.select {\n background: var(--ec-input-bg);\n border: 1px solid var(--ec-input-bdr);\n border-radius: var(--ec-radius-sm);\n padding: 8px 32px 8px 12px;\n color: var(--ec-txt-primary);\n appearance: none;\n cursor: pointer;\n}\n\n/* Toggle switch */\n.toggle {\n width: 40px; height: 22px;\n background: var(--ec-toggle-off);\n border-radius: 11px;\n cursor: pointer;\n transition: background var(--ec-duration-fast) var(--ec-ease-standard);\n position: relative;\n}\n.toggle.active { background: var(--ec-toggle-on); }\n.toggle::after {\n content: \'\';\n width: 18px; height: 18px;\n background: var(--ec-toggle-knob);\n border-radius: 50%;\n position: absolute;\n top: 2px; left: 2px;\n transition: transform var(--ec-duration-fast) var(--ec-ease-standard);\n}\n.toggle.active::after { transform: translateX(18px); }\n```\n\n### States\n\n| State | Visual Treatment |\n|-------|-----------------|\n| Default | Base colors as defined above |\n| Hover | Brightness shift or background change + `cursor: pointer` |\n| Focus | `box-shadow: 0 0 0 2px var(--ec-focus-ring)` |\n| Active/Pressed | Slightly darker, `translateY(0)` |\n| Disabled | `opacity: 0.5; cursor: not-allowed; pointer-events: none` |\n| Loading | Spinner icon or pulsing opacity animation |\n\n**Every interactive element MUST have hover and focus styles.** The validator checks for this.\n\n---\n\n## 4. Spacing & Typography\n\n### Spacing System (8px Grid)\n\nAll spacing values must align to the 8px grid. Use the CSS variables.\n\n| Context | Value | Variable |\n|---------|-------|----------|\n| Tight inline gaps | 4px | `--ec-space-xs` |\n| Icon-to-text gap | 8px | `--ec-space-sm` |\n| Card inner padding | 16-24px | `--ec-space-md` to `--ec-space-lg` |\n| Grid gap between cards | 24px | `--ec-space-lg` |\n| Page edge padding | 24-32px | `--ec-space-lg` to `--ec-space-xl` |\n| Major section separation | 48px | `--ec-space-2xl` |\n\n```css\n/* Example: consistent spacing */\n.page { padding: var(--ec-space-lg); }\n.card { padding: var(--ec-space-md); }\n.card-grid { display: grid; gap: var(--ec-space-lg); }\n.button-group { display: flex; gap: var(--ec-space-sm); }\n.form-field { margin-bottom: var(--ec-space-md); }\n```\n\n### Typography Scale\n\n| Role | Size | Weight | Notes |\n|------|------|--------|-------|\n| Hero stat | 36-48px | 700 | The single biggest number on screen. 2-3x surroundings. |\n| Page title | 20-24px | 700 | One per view. |\n| Section heading | 16-18px | 600 | Group labels, card titles. |\n| Body text | 13-14px | 400 | Default readable text. |\n| Caption / meta | 11-13px | 400-500 | Timestamps, secondary info. Use `--ec-txt-secondary`. |\n| Footnote | 11px | 400 | Fine print. Use `--ec-txt-tertiary`. |\n| Micro label | 10px | 600 | Uppercase badges, status tags. Letter-spacing: 0.5px. |\n| Min touch target | 32x32px | \u2014 | All clickable elements. |\n\n```css\n/* Typography baseline */\nbody {\n font-family: var(--ec-font-family);\n font-size: 14px;\n line-height: 1.5;\n color: var(--ec-txt-primary);\n}\n\nh1 { font-size: 22px; font-weight: 700; line-height: 1.2; }\nh2 { font-size: 17px; font-weight: 600; line-height: 1.3; }\nh3 { font-size: 15px; font-weight: 600; line-height: 1.4; }\n\n.hero-stat { font-size: 42px; font-weight: 700; line-height: 1.0; }\n.caption { font-size: 12px; color: var(--ec-txt-secondary); }\n.micro { font-size: 10px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.5px; }\n```\n\n### Line Height Guidelines\n\n| Text type | line-height |\n|-----------|-------------|\n| Hero numbers | 1.0 - 1.1 |\n| Headings | 1.2 - 1.3 |\n| Body text | 1.5 - 1.6 |\n| UI labels | 1.0 (single line) |\n\n### Text Overflow\n\nAlways handle overflow for dynamic content:\n\n```css\n.truncate {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* Multi-line clamp */\n.line-clamp-2 {\n display: -webkit-box;\n -webkit-line-clamp: 2;\n -webkit-box-orient: vertical;\n overflow: hidden;\n}\n```\n\n---\n\n## 5. Visual Refinements\n\n### Optional: Glass Effect (Frosted Glass Cards)\n\nGlass effect is **one option** for elevated surfaces \u2014 not the default. Use it sparingly when it adds visual value (e.g., a single hero card or overlay). Most cards should use solid `var(--ec-bg-raised)` backgrounds instead.\n\n```css\n/* Optional: apply only where glass effect adds value */\n.card-glass {\n background: var(--ec-card-bg);\n backdrop-filter: blur(20px);\n -webkit-backdrop-filter: blur(20px);\n border: 1px solid var(--ec-border-subtle);\n border-radius: var(--ec-radius-lg);\n box-shadow: var(--ec-shadow-sm);\n transition: all var(--ec-duration-normal) var(--ec-ease-standard);\n}\n.card-glass:hover {\n border-color: var(--ec-border-mid);\n transform: translateY(-1px);\n box-shadow: var(--ec-shadow-md);\n}\n```\n\n### Subtle Gradient Accents\n\nAdd depth with barely-visible gradients using CSS variables:\n\n```css\n/* Gradient border glow on hero card */\n.hero-card {\n position: relative;\n background: var(--ec-bg-raised);\n border-radius: var(--ec-radius-lg);\n overflow: hidden;\n}\n.hero-card::before {\n content: \'\';\n position: absolute;\n top: 0; left: 0; right: 0;\n height: 2px;\n background: linear-gradient(90deg, transparent, var(--ec-agent), transparent);\n opacity: 0.6;\n}\n```\n\n### Entry Animations\n\n```css\n/* Fade in */\n@keyframes fadeIn {\n from { opacity: 0; transform: translateY(4px); }\n to { opacity: 1; transform: translateY(0); }\n}\n\n/* Apply with stagger */\n.card { animation: fadeIn 0.3s var(--ec-ease-decelerate) both; }\n.card:nth-child(1) { animation-delay: 0ms; }\n.card:nth-child(2) { animation-delay: 30ms; }\n.card:nth-child(3) { animation-delay: 60ms; }\n.card:nth-child(4) { animation-delay: 90ms; }\n/* Max stagger spread: 120ms total */\n```\n\n### Scrollbar Styling (MANDATORY)\n\n```css\n::-webkit-scrollbar { width: 6px; height: 6px; }\n::-webkit-scrollbar-track { background: transparent; }\n::-webkit-scrollbar-thumb {\n background: var(--ec-border-mid);\n border-radius: 3px;\n}\n::-webkit-scrollbar-thumb:hover { background: var(--ec-border-strong); }\n```\n\n### Border Radius Hierarchy\n\nNever use the same radius on a container and its children.\n\n| Element | Radius | Variable |\n|---------|--------|----------|\n| Outer card | 12-14px | `--ec-radius-lg` |\n| Inner element | 8-10px | `--ec-radius-md` |\n| Buttons, inputs | 6-8px | `--ec-radius-sm` |\n| Badges, tags | 4px | `--ec-radius-xs` |\n| Pill shapes | 9999px | (literal, for pills only) |\n\n### Z-Index Layers\n\n| Layer | z-index | Usage |\n|-------|---------|-------|\n| Base content | 0 | Normal flow |\n| Sticky headers | 10 | Fixed headers, tab bars |\n| Dropdowns | 20 | Select menus, autocomplete |\n| Overlay / backdrop | 40 | Dimmed background |\n| Modal / dialog | 50 | Centered dialogs |\n| Toast / notification | 100 | Top-right toasts |\n\n### Empty / Loading / Error States\n\nEvery data-driven view MUST handle all three:\n\n```html\n<!-- Loading: skeleton -->\n<div class="skeleton" style="\n width: 100%; height: 20px;\n background: var(--ec-bg-raised);\n border-radius: var(--ec-radius-sm);\n animation: pulse 1.5s infinite;\n"></div>\n\n<!-- Empty: centered message -->\n<div style="text-align: center; padding: var(--ec-space-2xl);">\n <svg ...><!-- illustrative icon --></svg>\n <p style="color: var(--ec-txt-secondary); margin-top: var(--ec-space-md);">No items yet</p>\n <button class="btn-primary" style="margin-top: var(--ec-space-md);">Create First Item</button>\n</div>\n\n<!-- Error: banner -->\n<div style="\n background: var(--ec-status-error-bg);\n border: 1px solid var(--ec-status-error);\n border-radius: var(--ec-radius-sm);\n padding: var(--ec-space-md);\n color: var(--ec-status-error);\n">\n Something went wrong. <button>Retry</button>\n</div>\n```\n\n---\n\n## 6. Hard Rules (Violation = Reject)\n\n| # | Rule | Severity |\n|---|------|----------|\n| 1 | **CSS variables only.** No hard-coded hex, rgb, rgba, hsl colors. | CRITICAL |\n| 2 | **No left-side navigation.** Shell occupies the left sidebar. Apps may use top bar and right-side panels. | CRITICAL |\n| 3 | **Self-contained.** CSS in `<style>`, JS in `<script>`, icons as inline SVG. Zero external resources. | CRITICAL |\n| 4 | **No `allow-same-origin`.** All host communication goes through `window.echoclaw` Bridge. | CRITICAL |\n| 5 | **No `javascript:` URLs.** | CRITICAL |\n| 6 | **Credential storage tiered.** Tokens/passwords MUST use `echoclaw.auth.setSecret()`. Using `echoclaw.storage` for credentials is forbidden. | CRITICAL |\n| 7 | **Declare capabilities.** Using audio/camera/mic/clipboard/location/fullscreen requires declaration in `manifest.capabilities`. | HIGH |\n| 8 | **Auth capability declaration.** Using `echoclaw.auth.*` requires declaring `auth` capability + `auth_profile`. | HIGH |\n| 9 | **Prompt-level permissions require `requestPermission`.** Call `echoclaw.requestPermission()` before use and handle denial. | HIGH |\n| 10 | **Desktop-first layout.** Design target 1024x768, multi-column layout. Must not break at 544x548. | HIGH |\n| 11 | **Dark theme only.** CSS variables control colors. No `prefers-color-scheme` media queries. | HIGH |\n| 12 | **Hover + Focus states.** All interactive elements must have `:hover`, `:focus`, and `cursor: pointer`. | MEDIUM |\n| 13 | **Total HTML size \u2264 1MB.** | MEDIUM |\n\n### Soft Rules (Warning, Non-blocking)\n\n| # | Rule | Severity |\n|---|------|----------|\n| S1 | **Bridge-only API.** No direct `new WebSocket()`, `fetch()` to external URLs, or `localStorage`. Use Bridge equivalents. | WARNING |\n| S2 | **`<img>` must have `alt` attribute.** | WARNING |\n\n---\n\n## 7. Do and Don\'t\n\n### FORBIDDEN\n\n| Practice | Why |\n|----------|-----|\n| Hard-coded hex/rgb/rgba/hsl colors | Validator rejects. Breaks theming. |\n| External fonts (`<link>` to Google Fonts) | Violates self-contained rule. Use `--ec-font-family`. |\n| Fixed pixel widths on layout containers | Breaks at different viewport sizes. Use `fr`, `%`, `minmax()`. |\n| Heavy box-shadows (large blur, dark color) | Looks wrong on dark backgrounds. Use `--ec-shadow-sm/md`. |\n| Left-side navigation | Shell owns left sidebar. Use top or right. |\n| `prefers-color-scheme` media queries | Dark-only system. Variables handle everything. |\n| Same border-radius on parent and child | Looks flat and unpolished. Vary by nesting level. |\n| Emoji as UI icons | Inconsistent cross-platform. Use inline SVG. |\n| Animation stagger > 120ms total spread | Feels sluggish. Keep total stagger tight. |\n| Unhandled loading/empty/error states | Feels broken. Always handle all three. |\n| `max-width` < 640px on page container | Too narrow for desktop. Validator warns. |\n| Missing `:hover` on interactive elements | Feels dead. Validator warns. |\n| `echoclaw.storage.set(\'token\', ...)` | Credentials belong in `echoclaw.auth.setSecret()`. |\n| Token hardcoded in HTML | Obtain tokens at runtime via auth flow. |\n| Showing raw error on token expiry | Use `echoclaw.auth.getValidToken()` for auto-refresh. |\n| Direct `fetch()` / `new WebSocket()` | Use `echoclaw.invoke(\'fetch\', ...)` / `echoclaw.ws.connect()`. |\n| `localStorage` / `sessionStorage` | Use `echoclaw.storage.*` or `echoclaw.db.*`. |\n\n### ENCOURAGED\n\n| Practice | Effect |\n|----------|--------|\n| CSS variables for all visual values | Future-proof theming, passes validation |\n| Flex and Grid layouts | Responsive, clean alignment |\n| `backdrop-filter: blur(20px)` sparingly (1-2 elements) | Optional frosted glass accent \u2014 not for every card |\n| Subtle gradient accents (1-2px lines) | Adds depth without visual noise |\n| `border-radius` with nesting hierarchy | Professional, layered feel |\n| `transition: all 0.2s ease` on hover | Smooth, polished interactions |\n| `translateY(-1px)` hover lift on cards | Satisfying micro-interaction |\n| One hero element per screen (2-3x size) | Clear visual hierarchy |\n| Status dots/badges with semantic colors | Instant state comprehension |\n| Staggered fade-in entry animations | Alive, not static |\n| Thin scrollbars with transparent track | Native desktop feel |\n| `text-overflow: ellipsis` on dynamic text | Clean handling of variable content |\n| Using EC Toolkit classes (`ec-*`) | Rapid, pre-polished components |\n\n---\n\n## 8. EC Toolkit (Optional CSS Component Library)\n\nThe host injects a pre-built CSS component library alongside the theme variables. You MAY use `ec-*` classes to rapidly build polished UI, or ignore them entirely and write your own CSS.\n\n### Core Components\n\n| Class | Description |\n|-------|-------------|\n| `ec-card` | Standard card with `--ec-card-bg`, border, radius, shadow |\n| `ec-card-glass` | Optional: frosted glass card with `backdrop-filter: blur(20px)` \u2014 use sparingly |\n| `ec-btn ec-btn-primary` | Primary button using `--ec-agent` background |\n| `ec-btn ec-btn-secondary` | Secondary button with `--ec-interactive-bg` |\n| `ec-input` | Styled text input with focus ring |\n| `ec-select` | Styled select dropdown |\n| `ec-toggle` | Toggle switch with on/off states |\n| `ec-badge ec-badge-done` | Status badge (variants: `ec-badge-done`, `ec-badge-active`, `ec-badge-warning`, `ec-badge-error`) |\n| `ec-tabs` / `ec-tab` | Tab bar container and individual tab items |\n| `ec-table` | Styled data table |\n| `ec-modal` | Modal dialog overlay |\n| `ec-toast` | Toast notification (top-right) |\n| `ec-toolbar` | Horizontal toolbar with actions |\n| `ec-progress` | Progress bar |\n| `ec-ring` | Circular progress ring |\n| `ec-kpi` | KPI card (large number + label + trend) |\n| `ec-list` / `ec-list-item` | List container and items with hover states |\n| `ec-skeleton` | Skeleton loading placeholder with pulse animation |\n| `ec-empty` | Centered empty-state message (icon + text + optional action) |\n| `ec-error-banner` | Error banner with `--ec-status-error` theming |\n| `ec-dot` | Status dot indicator (pair with status color variables) |\n| `ec-avatar` | Circular avatar container |\n| `ec-dropdown` | Dropdown menu container |\n\n### Animation Classes\n\n| Class | Effect |\n|-------|--------|\n| `ec-animate-fade-in` | Fade in with subtle upward shift |\n| `ec-animate-scale-in` | Scale from 95% to 100% with fade |\n| `ec-animate-slide-up` | Slide up from below with fade |\n| `ec-animate-bounce-in` | Bounce entrance effect |\n| `ec-animate-spin` | Continuous rotation (for spinners) |\n| `ec-stagger` | Apply to parent; children animate with staggered delay |\n\n### Layout Utilities\n\n| Class | Effect |\n|-------|--------|\n| `ec-flex` | `display: flex` with horizontal direction |\n| `ec-flex-col` | `display: flex; flex-direction: column` |\n| `ec-grid ec-grid-2` | 2-column grid (also: `ec-grid-3`, `ec-grid-4`) |\n| `ec-gap-sm` / `ec-gap-md` / `ec-gap-lg` | Gap using `--ec-space-sm/md/lg` |\n| `ec-flex-1` | `flex: 1` \u2014 fill remaining space |\n| `ec-truncate` | Text overflow ellipsis (single line) |\n\n**Usage note:** EC Toolkit classes and custom CSS can be freely mixed. The toolkit handles theming automatically via the same `--ec-*` variables.\n\n---\n\n## 9. Internationalization (i18n)\n\nAll ClawApps MUST embed both Chinese and English (zh-CN + en) at generation time. This is NOT optional.\n\n### Implementation Pattern\n\n```javascript\nconst I18N = {\n \'en\': {\n \'title\': \'Dashboard\',\n \'addItem\': \'Add Item\',\n \'noData\': \'No data yet\',\n \'error\': \'Something went wrong\',\n \'retry\': \'Retry\',\n // ... all user-visible strings\n },\n \'zh-CN\': {\n \'title\': \'\u4EEA\u8868\u76D8\',\n \'addItem\': \'\u6DFB\u52A0\u9879\u76EE\',\n \'noData\': \'\u6682\u65E0\u6570\u636E\',\n \'error\': \'\u51FA\u4E86\u70B9\u95EE\u9898\',\n \'retry\': \'\u91CD\u8BD5\',\n // ... all user-visible strings\n }\n};\n\nlet currentLocale = \'en\';\nfunction t(key) {\n return (I18N[currentLocale] || I18N[\'en\'])[key] || key;\n}\n```\n\n### Rules\n\n| Rule | Detail |\n|------|--------|\n| **All visible text through `t()`** | Hard-coded Chinese or English strings in HTML are forbidden. Includes `aria-label`, `title`, `placeholder`, empty-state, error-state copy. |\n| **Both languages at creation** | Generate both `en` and `zh-CN` dictionaries at build time. Do NOT rely on runtime translation APIs. |\n| **Technical terms stay English** | Terms like API, WebSocket, OAuth, SQL, JSON, URL SHALL remain in English in both locales. |\n| **Default locale is `\'en\'`** | When Bridge is unavailable or locale is unknown, fall back to `\'en\'`. |\n| **Locale detection** | Read `echoclaw.getContext().locale` on init. Subscribe to `locale:changed` event for runtime switches. |\n\n### Locale Detection Pattern\n\n```javascript\n// On initialization\nconst ctx = echoclaw.getContext();\ncurrentLocale = ctx.locale || \'en\';\nrender();\n\n// Listen for runtime locale changes\nechoclaw.subscribe(\'locale:changed\', ({ locale }) => {\n currentLocale = locale;\n render();\n});\n```\n\n---\n\n## 10. Quality Gate (Score >= 14/20 or Redo)\n\n| # | Check | 0 | 1 | 2 |\n|---|-------|---|---|---|\n| 1 | Hero element | None | Has large element | Stunning hero |\n| 2 | CSS variables | Hard-coded colors | Mostly variables | 100% variables |\n| 3 | Dark theme | Broken in dark mode | Acceptable | Refined dark UI |\n| 4 | Desktop layout | Narrow single column | Reasonable | Full-width multi-column |\n| 5 | Hover states | None | Partial | All interactive |\n| 6 | Transitions | None | Partial | Globally smooth |\n| 7 | Typography hierarchy | Flat | Some variation | 4+ level gradient |\n| 8 | Spacing | Random | Mostly consistent | 8px grid |\n| 9 | State visibility | None | Partial | All states clear |\n| 10 | Self-contained | External deps | Minor | Zero external |\n\n---\n\n## 11. Output Format\n\nThe final `.claw` output is a JSON object with three top-level keys:\n\n```json\n{\n "manifest": {\n "id": "kebab-case-id",\n "name": { "en": "Display Name", "zh-CN": "\u663E\u793A\u540D\u79F0" },\n "version": "1.0.0",\n "color": "#hex",\n "icon_svg": "<path ... />",\n "description": { "en": "One-line description", "zh-CN": "\u4E00\u884C\u63CF\u8FF0" },\n "capabilities": ["ui", "storage"],\n "auth_profile": { "...if auth capability is declared..." }\n },\n "html": "<!DOCTYPE html>...",\n "prompt": {\n "seed": {\n "goal": "Verb-first purpose sentence",\n "constraints": ["self-contained", "..."],\n "acceptance_tests": ["User can ...", "User can ..."]\n },\n "current": { "goal": "Same as seed for v1" }\n }\n}\n```\n\n---\n\n## 12. Mandatory Pre-Output Checklist (v1.9.2)\n\nBefore every HTML output, confirm each item. Any failure = engine auto-reject.\n\n- [ ] **Zero hard-coded color values.** No hex (#xxx), rgb(), rgba(), hsl(), hsla() in HTML/CSS. Only `manifest.color` may be literal hex. All others use `var(--ec-xxx)`.\n- [ ] **Agent theme color** uses `var(--ec-agent)` / `var(--ec-agent-text)`. No hard-coded brand colors.\n- [ ] **All network requests** use `echoclaw.invoke(\'fetch\', ...)`. No direct fetch/XHR/axios/WebSocket/EventSource to external URLs.\n- [ ] **Tokens/passwords** use `echoclaw.auth.setSecret()`. No localStorage/sessionStorage/indexedDB/echoclaw.storage for credentials.\n- [ ] **All visible text** uses `t()` i18n function. Includes aria-label, title, placeholder, empty-state, error-state copy.\n- [ ] **No left-side navigation.** Shell owns the left sidebar. App uses only top bar or right-side panel.\n- [ ] **All capabilities declared and minimized.** Only declare capabilities actually used.\n- [ ] **No dangerous code** \u2014 no eval(), new Function(), unsanitized innerHTML, or remote script injection.\n- [ ] **Every `<button>` and clickable element** has `:hover` CSS and `cursor: pointer`.\n- [ ] **Total HTML < 1MB.**\n\n---\n\n## 13. Visual Cases \u2014 What Good Looks Like\n\nThese are descriptions of high-quality app UIs to calibrate your output.\n\n### Case A: Pomodoro Timer\n\n**Layout:** Full-width centered canvas. Large circular progress ring (200px diameter) dominates the upper half \u2014 the hero element. Below it: current session label, time remaining in 48px monospace font, and three buttons (Start / Pause / Reset) in a horizontal row.\n\n**Color:** Ring uses `var(--ec-agent)` stroke on a `var(--ec-bg-raised)` track. Background: `var(--ec-bg-page)`. Time text: `var(--ec-txt-primary)`. When in break mode, the ring subtly shifts hue using a CSS filter \u2014 no extra variables needed.\n\n**Animation:** Ring strokes animate with `stroke-dashoffset` transitions at `var(--ec-duration-slow)`. Button press triggers `transform: scale(0.96)`. Session-complete fires a brief `@keyframes bounce-in` on the label.\n\n**States:** Start button shows spinner when loading stored session. If no session exists, shows a subtle empty-state card with "\u5F00\u59CB\u4F60\u7684\u7B2C\u4E00\u4E2A\u756A\u8304\u949F" and a CTA.\n\n---\n\n### Case B: Notes Dashboard\n\n**Layout:** Top tab bar (All / Starred / Archived) + search input. Below: card grid (3 columns at 1024px). Each card shows title, first 2 lines of content, tag chips, and relative timestamp. FAB (bottom-right-safe area) for new note.\n\n**Color:** Cards use `var(--ec-card-bg)` + `var(--ec-card-border)`. On hover, border highlights to `var(--ec-agent)` at 40% opacity. Tags use `var(--ec-agent)` background at 15% opacity. Star icon uses `var(--ec-status-warning)` when active.\n\n**Animation:** Cards enter with `ec-animate-fade-in` staggered by 50ms each. New card slides in from bottom. Delete triggers a `scale(0) opacity(0)` exit before DOM removal.\n\n**States:** Loading shows 6 skeleton cards (animated shimmer). Empty state shows a large centered illustration + "\u8FD8\u6CA1\u6709\u7B14\u8BB0" + "\u65B0\u5EFA\u7B14\u8BB0" primary button. Search with no results shows "\u6CA1\u6709\u627E\u5230\u76F8\u5173\u7B14\u8BB0" with a clear-search link.\n\n---\n\n### Case C: Data Export Tool\n\n**Layout:** Two-panel. Left: file/data source selector list (fixed 280px). Right: preview pane with column toggle checkboxes + export format selector (CSV/JSON/XLSX). Bottom bar: record count badge + primary Export button.\n\n**Color:** Selected source row uses `var(--ec-agent)` background at 12% + left border accent `3px solid var(--ec-agent)`. Export button uses full `var(--ec-agent)` fill. Record count badge: `var(--ec-bg-raised)` + `var(--ec-txt-secondary)`.\n\n**Animation:** Panel transitions use `transform: translateX` slide (150ms). Export button shows a spinner inside the button during processing, then swaps to a checkmark for 2s before returning to default.\n\n**States:** When no source selected, right panel shows a centered "\u9009\u62E9\u5DE6\u4FA7\u6570\u636E\u6E90" hint. When exporting, the button disables and shows "\u5BFC\u51FA\u4E2D\u2026". On success, shows a toast "\u5DF2\u5BFC\u51FA 247 \u6761\u8BB0\u5F55".\n\n---\n\n## 14. Animation Strategy\n\nUse animation to communicate change \u2014 not to decorate. Three tiers:\n\n### Tier 1: Micro-interactions (always-on)\n\nEvery interactive element must have these. They run in < 150ms.\n\n```css\n/* Hover lift */\n.card:hover { transform: translateY(-2px); box-shadow: var(--ec-shadow-md); }\n\n/* Button press */\nbutton:active { transform: scale(0.97); opacity: 0.85; }\n\n/* Input focus */\ninput:focus { border-color: var(--ec-agent); box-shadow: 0 0 0 3px color-mix(in srgb, var(--ec-agent) 15%, transparent); }\n```\n\n### Tier 2: State transitions (triggered by user action)\n\nWhen content changes or a view switches. Keep between 150\u2013300ms.\n\n```css\n/* Tab panel switch */\n.tab-panel { transition: opacity 200ms var(--ec-ease-standard); }\n.tab-panel.entering { opacity: 0; }\n\n/* Modal open */\n.modal { animation: scaleIn 200ms var(--ec-ease-decelerate) forwards; }\n@keyframes scaleIn { from { transform: scale(0.95); opacity: 0; } to { transform: scale(1); opacity: 1; } }\n```\n\n### Tier 3: Choreographed sequences (entrance, empty\u2192populated)\n\nFor first-load and significant state changes. Use stagger for lists.\n\n```css\n/* List items stagger in */\n.list-item { animation: fadeSlideUp 300ms var(--ec-ease-decelerate) both; }\n.list-item:nth-child(1) { animation-delay: 0ms; }\n.list-item:nth-child(2) { animation-delay: 50ms; }\n.list-item:nth-child(3) { animation-delay: 100ms; }\n\n@keyframes fadeSlideUp {\n from { opacity: 0; transform: translateY(8px); }\n to { opacity: 1; transform: translateY(0); }\n}\n```\n\n**Rules:**\n- Never animate layout-thrashing properties (width, height, top, left) \u2014 use `transform` and `opacity` only\n- `prefers-reduced-motion` users: `@media (prefers-reduced-motion: reduce) { * { animation-duration: 0.01ms !important; } }`\n- Don\'t loop animations unless the content is genuinely live (real-time data)\n\n---\n\n## 15. Color Pairing Recommendations\n\nUse these combinations to build a coherent palette for any app type.\n\n| App Type | Primary (`--ec-agent`) | Accent use | Best for |\n|----------|----------------------|------------|----------|\n| Productivity / Tasks | `#3b82f6` (blue) | Active items, progress rings | Todo, calendar, project mgmt |\n| Finance / Numbers | `#22c55e` (green) | Positive values, success states | Budgets, invoices, portfolios |\n| Creative / Media | `#8b5cf6` (purple) | Highlights, tag chips | Notes, writing, galleries |\n| Monitoring / DevOps | `#06b6d4` (cyan) | Live status dots, charts | Dashboards, uptime, logs |\n| Urgent / Time-sensitive | `#ef4444` (red) | Countdown rings, alert badges | Timers, reminders, alarms |\n| Wellness / Health | `#10b981` (emerald) | Streaks, progress bars | Habits, fitness, sleep |\n| Social / Communication | `#ec4899` (pink) | Avatars, reactions, mention chips | Chat, contacts, social |\n\n### Semantic Color Usage (Non-negotiable)\n\n```\n--ec-status-done \u2192 Success, completed, saved, streak\n--ec-status-active \u2192 Running, syncing, in-progress, live\n--ec-status-warning \u2192 Caution, near-limit, approaching deadline\n--ec-status-error \u2192 Failed, blocked, overdue, critical\n```\n\nNever use status colors for decoration. A green badge means success \u2014 don\'t make it green just because the brand is green.\n\n---\n\n*ClawApp UI Design Guide \u2014 For OpenClaw system prompt*\n\n---\n**Reference App:** See `reference-app.claw.json` in the workspace for a complete example that scores 20/20 on the Quality Gate.\n';
|
|
@@ -8450,6 +8452,18 @@ var BRIDGE_API_CHEATSHEET_default = "# Bridge API Quick Reference\n\n> The 5 mos
|
|
|
8450
8452
|
// ../claw-engine/src/authoring/openclaw-guides/COMMON_MISTAKES.md
|
|
8451
8453
|
var COMMON_MISTAKES_default = "# Common Mistakes\n\n> The most frequent errors found in OpenClaw-generated apps. Check these before delivery.\n\n---\n\n## Mistake List\n\n```\n\u274C onclick=\"\" (empty handler) \u2192 \u2705 onclick=\"handleClick()\"\n Buttons with empty or missing onclick do nothing. Every button needs a real handler.\n\n\u274C #fff (hard-coded color) \u2192 \u2705 var(--ec-txt-primary)\n Hard-coded hex is a rejection-level error. Use CSS variables for every color.\n\n\u274C localStorage.setItem() \u2192 \u2705 echoclaw.storage.set('key', value)\n localStorage is blocked by sandbox. All storage goes through the bridge.\n\n\u274C fetch('url') \u2192 \u2705 echoclaw.invoke('fetch', { url, method: 'GET' })\n Direct fetch is blocked by CSP. All network requests go through the bridge.\n\n\u274C No loading state \u2192 \u2705 Show skeleton screen or spinner\n Every async operation (storage load, API call) must show a loading indicator.\n\n\u274C No empty state \u2192 \u2705 Show centered icon + message + CTA button\n If there's no data, tell the user why and what to do next.\n\n\u274C No error state \u2192 \u2705 Show error message + retry button\n Wrap every await in try/catch. Surface errors using var(--ec-status-error).\n\n\u274C console.log as \"logic\" \u2192 \u2705 Actually modify DOM or call an API\n console.log does not update the UI. Write real handlers that change state.\n\n\u274C No i18n (raw string in HTML) \u2192 \u2705 All visible text goes through t()\n Every user-facing string \u2014 labels, placeholders, empty states, errors \u2014 must use t().\n\n\u274C Icon with >4 SVG elements \u2192 \u2705 Maximum 4 stroke elements, legible at 20px\n Complex icons are illegible at small sizes and fail the icon spec check.\n\n\u274C echoclaw.subscribe() leak \u2192 \u2705 Call the returned unsubscribe function on cleanup\n subscribe() returns an unsubscribe function. Failing to call it causes memory leaks.\n Always store the return value and call it when the component/listener is no longer needed.\n```\n\n---\n\n## Quick Pre-Delivery Checklist\n\n- [ ] Every `<button>` has a non-empty `onclick` or `addEventListener`\n- [ ] Zero hex colors in HTML/CSS (only `manifest.color` hex is allowed)\n- [ ] Zero `localStorage` / `sessionStorage` / `fetch()` / `new WebSocket()` calls\n- [ ] Every data view has loading + empty + error states\n- [ ] All visible strings wrapped in `t()`\n\n---\n\n## Expanded: The 3 States Every Data View Needs\n\n```html\n<!-- Loading state -->\n<div id=\"state-loading\" class=\"ec-skeleton\">\n <!-- skeleton rows -->\n</div>\n\n<!-- Empty state -->\n<div id=\"state-empty\" style=\"display:none; text-align:center; padding: var(--ec-space-2xl)\">\n <svg ...></svg>\n <p style=\"color: var(--ec-txt-secondary)\">${t('no_items_yet')}</p>\n <button class=\"ec-btn ec-btn-primary\" onclick=\"openCreateModal()\">${t('create_first')}</button>\n</div>\n\n<!-- Error state -->\n<div id=\"state-error\" style=\"display:none; color: var(--ec-status-error)\">\n <span id=\"error-msg\"></span>\n <button onclick=\"loadData()\">${t('retry')}</button>\n</div>\n\n<!-- Content -->\n<div id=\"state-content\" style=\"display:none\">\n <!-- real data here -->\n</div>\n```\n\n```js\nasync function loadData() {\n showState('loading');\n try {\n const items = await echoclaw.storage.get('items') ?? [];\n if (items.length === 0) { showState('empty'); return; }\n renderItems(items);\n showState('content');\n } catch (err) {\n document.getElementById('error-msg').textContent = t('load_failed');\n showState('error');\n }\n}\n\nfunction showState(name) {\n ['loading','empty','error','content'].forEach(s =>\n document.getElementById('state-' + s).style.display = s === name ? '' : 'none'\n );\n}\n```\n\n---\n\n*Common Mistakes Reference \u2014 For OpenClaw self-check before delivery*\n";
|
|
8452
8454
|
|
|
8455
|
+
// ../claw-engine/src/authoring/openclaw-guides/references/echoclaw-create/CHECKLIST.md
|
|
8456
|
+
var CHECKLIST_default = "# Self-Check Checklist\n\n> Run these checks at Step 10 before delivering any ClawApp.\n\n---\n\n## Section A: Structural Grep Checks\n\nRun these with `exec`. Every check produces a number, not a feeling.\n\n```bash\n# Empty event handlers (MUST = 0)\ngrep -cn 'onclick=\"\"\\|onchange=\"\"' index.html\n\n# Leftover stubs (MUST = 0)\ngrep -cn 'TODO\\|FIXME\\|PLACEHOLDER' index.html\n\n# Debug logs (SHOULD = 0)\ngrep -cn 'console\\.log' index.html\n\n# Hardcoded colors -- only manifest.color hex allowed (SHOULD = 0 or 1)\ngrep -cn '#[0-9a-fA-F]\\{3,8\\}' index.html\n\n# CSS variable usage (MUST > 0)\ngrep -c 'var(--ec-' index.html\n\n# i18n function usage (MUST > 0)\ngrep -c \"t('\" index.html\n\n# File size (MUST < 1,048,576 bytes)\nwc -c index.html\n\n# Event handler count vs interactive element count\ngrep -c 'addEventListener\\|onclick\\|onsubmit\\|onchange\\|oninput' index.html\n```\n\n### Functional Cross-Check\n\n```bash\n# Count buttons\ngrep -c '<button' index.html\n\n# Count handler attachments\ngrep -c 'addEventListener\\|onclick' index.html\n\n# These numbers should be close. More buttons than handlers = dead controls.\n```\n\n---\n\n## Section B: Quality Gate Scorecard (Target: >= 14/20)\n\nScore each item 0-2. If total < 14, loop back to fix before proceeding.\n\n| # | Check | 0 | 1 | 2 |\n|---|-------|---|---|---|\n| 1 | Hero element | None | Has a large element | Stunning hero with clear visual dominance |\n| 2 | CSS variables | Hard-coded colors present | Mostly variables | 100% `var(--ec-*)`, zero hard-coded |\n| 3 | Dark theme | Broken / unreadable | Acceptable contrast | Polished dark theme with surface hierarchy |\n| 4 | Desktop layout | Narrow single column | Reasonable use of width | Full-width multi-column, uses 1024px canvas |\n| 5 | Hover states | None | Partial coverage | Every interactive element has hover + focus |\n| 6 | Transitions | None | Partial | Globally smooth (`--ec-duration-*` + `--ec-ease-*`) |\n| 7 | Typographic hierarchy | Flat / single size | Some variation | 4+ level scale (hero -> title -> body -> caption) |\n| 8 | Spacing consistency | Arbitrary / random | Mostly consistent | 8px grid via `--ec-space-*` throughout |\n| 9 | State visibility | No loading/empty/error | Partial coverage | All data views handle loading + empty + error |\n| 10 | Self-contained | External dependencies found | Minimal issues | Zero external resources, fully inline |\n\n---\n\n## Section C: Pre-Output Checklist (MUST Complete Before Delivery)\n\nFailure of ANY critical item = automatic rejection by the engine.\n\n### Colors & Theming\n- [ ] **Zero hard-coded color literals** in HTML/CSS/JS. Includes hex (`#xxx`), `rgb()`, `rgba()`, `hsl()`, `hsla()`, named colors. Only `var(--ec-xxx)` allowed. `manifest.color` MAY be hex.\n- [ ] **Agent theme color** uses `var(--ec-agent)` / `var(--ec-agent-text)`. No hard-coded brand color in HTML.\n\n### External Resources\n- [ ] **Zero external URLs.** No `<link href=\"http...\">`, `<script src=\"http...\">`, `@import url(\"http...\")`.\n\n### Layout\n- [ ] **No left-side navigation.** No class or id named `left-panel`, `left-sidebar`, `left-nav`.\n- [ ] **Bottom-right 64x64px** area clear of interactive elements (host Info button zone).\n\n### Capabilities & Security\n- [ ] **All capabilities declared and minimized.** Every Bridge API and browser API used is in `manifest.capabilities`.\n- [ ] **Permission flow.** `requestPermission()` with denial handling precedes every prompt-level API call.\n- [ ] **Bridge-only networking.** No direct `new WebSocket()`, `fetch()` to external URLs, or `localStorage`.\n- [ ] **No credential leaks.** No token/password via `echoclaw.storage`. Credentials use `echoclaw.auth.setSecret()`.\n- [ ] **No dangerous code.** No `eval()`, `new Function()`, unsanitized `innerHTML`, remote script injection.\n\n### Networking & Auth\n- [ ] **All network requests** use `echoclaw.invoke('fetch', ...)`. No direct fetch/XHR/axios/WebSocket/EventSource.\n- [ ] **Auth profile present** (if using auth APIs). Apps with `echoclaw.auth.*` declare `auth` capability and `auth_profile`.\n\n### Internationalization\n- [ ] **All visible text uses `t()`.** Includes `aria-label`, `title`, `placeholder`, empty-state, error-state copy.\n\n### Interactivity\n- [ ] **Interactivity complete.** Every `<button>` and clickable element has `:hover` CSS and `cursor: pointer`.\n- [ ] **Scrollbar styling** is present.\n\n### Icon\n- [ ] **Icon per ICON_SPEC.md.** `manifest.icon_svg` uses stroke style, `currentColor`, <=4 elements, no `<svg>` wrapper, viewBox 0 0 40 40.\n\n### Size\n- [ ] **Size < 1 MB.** Total HTML verified via `wc -c`.\n\n### Sharing Readiness\n- [ ] **Sharing readiness.** All environment-dependent values are `config_fields`. Sensitive fields are `sensitive: true` and NOT in HTML. `install_contract` declared. `share_profile` populated.\n\n---\n\n## Section D: Spec Rule Review (Mental Verification)\n\nWhile reading back the file, verify:\n\n- No left-side navigation\n- Bottom-right 64x64px area is clear\n- Scrollbar styling is present\n- Empty state handled for every data-driven view\n- Error state handled for every async operation\n- One primary action per view (using `--ec-agent` background)\n- Destructive actions behind confirmation\n- `echoclaw.subscribe()` return values stored and cleaned up\n";
|
|
8457
|
+
|
|
8458
|
+
// ../claw-engine/src/authoring/openclaw-guides/references/echoclaw-create/ARCHITECTURE_PATTERNS.md
|
|
8459
|
+
var ARCHITECTURE_PATTERNS_default = '# Architecture Patterns\n\n> Templates for Step 5 (Plan Architecture). Copy and fill in for each new app.\n\n---\n\n## Data Model Template\n\nDefine every piece of state the app manages. Write it out explicitly.\n\n```javascript\n// Example: Todo app\nstate = {\n tasks: [{ id, title, done, createdAt }],\n filter: \'all\' | \'active\' | \'completed\',\n editingId: null\n}\n```\n\nRules:\n- Every field has a type annotation (even informal)\n- Arrays define the shape of their items\n- Enum values list all options with `|`\n- Include transient UI state (editingId, isLoading) alongside persistent data\n\n---\n\n## Feature List Template (verbs, not nouns)\n\nExhaustive list of what the user can DO. Every verb becomes a function.\n\n```\n- Add a task (with title input)\n- Toggle task completion\n- Delete a task (with confirmation)\n- Filter by all / active / completed\n- Clear all completed tasks\n- Persist tasks across sessions\n```\n\nRules:\n- Use action verbs: add, toggle, delete, filter, sort, export\n- Parenthetical notes clarify the interaction (e.g. "with confirmation")\n- Include persistence as an explicit verb ("persist X across sessions")\n\n---\n\n## Event Handler Inventory Template\n\nMap every interactive element to its handler. This is your wiring diagram.\n\n```\n"Add" button -> addTask()\nTask checkbox -> toggleTask(id)\nTask delete icon -> confirmDelete(id)\nFilter tab ("All") -> setFilter(\'all\')\nFilter tab ("Active") -> setFilter(\'active\')\n"Clear completed" -> clearCompleted()\n```\n\nRules:\n- Left side: the UI element as the user sees it (label or description)\n- Right side: exact function name with parameters\n- Every row = one interactive element = one handler\n- If you cannot name the handler, the feature is not thought through\n\n---\n\n## Persistence Strategy Template\n\nWhat data is saved, where, and when.\n\n```\n- Storage: echoclaw.storage (KV) for simple apps\n echoclaw.db (SQLite) for complex/relational data\n- Save trigger: on every mutation (debounced if high-frequency)\n- Load: on init, with empty-state fallback\n- Schema: (if SQLite) define table structure\n```\n\n### When to use KV vs SQLite\n\n| Use Case | Recommendation |\n|----------|---------------|\n| Settings, preferences, small state | `echoclaw.storage` (KV) |\n| Lists < 100 items, flat structure | `echoclaw.storage` (KV) |\n| Relational data, multiple entity types | `echoclaw.db` (SQLite) |\n| Lists > 100 items, need filtering/sorting | `echoclaw.db` (SQLite) |\n| Time-series data, logs, history | `echoclaw.db` (SQLite) |\n\n---\n\n## Capability Declaration Template\n\nList which Bridge capabilities the app needs.\n\n```\ncapabilities: ["storage", "timer", "notification"]\n```\n\nCommon capabilities:\n- `storage` -- KV persistence\n- `db` -- SQLite persistence\n- `timer` -- setTimeout/setInterval via bridge\n- `notification` -- push notifications\n- `clipboard` -- read/write clipboard\n- `audio` -- play sounds\n- `haptics` -- vibration feedback\n- `auth` -- authentication APIs (requires `auth_profile`)\n- `fetch` -- network requests via bridge\n\n**MUST:** Declare only capabilities you actually use. Minimize the set.\n\n---\n\n## i18n Pattern Template\n\nStandard implementation for internationalization.\n\n```javascript\nconst LANG = {\n en: {\n addTask: \'Add Task\',\n noTasks: \'No tasks yet\',\n deleteConfirm: \'Delete this task?\',\n // ... all user-visible strings\n },\n \'zh-CN\': {\n addTask: \'\u6DFB\u52A0\u4EFB\u52A1\',\n noTasks: \'\u6682\u65E0\u4EFB\u52A1\',\n deleteConfirm: \'\u786E\u5B9A\u5220\u9664\u6B64\u4EFB\u52A1\uFF1F\',\n // ... all user-visible strings\n }\n};\n\nlet currentLocale = echoclaw.getContext().locale || \'en\';\n\nfunction t(key) {\n return LANG[currentLocale]?.[key] || LANG.en[key] || key;\n}\n\n// Subscribe to locale changes\nechoclaw.subscribe(\'locale:changed\', (newLocale) => {\n currentLocale = newLocale;\n render();\n});\n```\n\nRules:\n- Every user-visible string goes through `t()`\n- Includes: labels, placeholders, `aria-label`, `title`, empty-state text, error messages\n- Fallback chain: current locale -> `en` -> raw key\n- Subscribe to `locale:changed` and re-render\n\n---\n\n## claw.json Manifest Template\n\n```json\n{\n "manifest": {\n "id": "my-app",\n "name": { "en": "My App", "zh-CN": "\u6211\u7684\u5E94\u7528" },\n "version": "1.0.0",\n "color": "#4A90D9",\n "icon_svg": "<path d=\'...\' stroke=\'currentColor\' stroke-width=\'2\' fill=\'none\'/>",\n "description": {\n "en": "A brief description of what this app does.",\n "zh-CN": "\u7B80\u8981\u63CF\u8FF0\u5E94\u7528\u529F\u80FD\u3002"\n },\n "capabilities": ["storage"],\n "shared_libs": []\n },\n "prompt": {\n "seed": {\n "goal": "What this app helps the user accomplish",\n "constraints": ["Key technical constraints"],\n "acceptance_tests": ["User can do X", "Data persists across sessions"]\n }\n }\n}\n```\n\n### icon_svg Rules (from ICON_SPEC.md)\n- Stroke style only (no filled shapes)\n- Use `currentColor` for all strokes\n- Maximum 4 SVG elements\n- No `<svg>` wrapper tag (just inner elements)\n- viewBox: 0 0 40 40\n- Must be legible at 20px\n\n### manifest.color Selection\n- Choose a brand hex that represents the app\'s purpose\n- This is the ONLY hardcoded hex allowed anywhere in the app\n- Used by the shell for theming; the app itself uses `var(--ec-agent)`\n\n---\n\n## session.json Template\n\nWritten at Step 14 (Deliver).\n\n```json\n{\n "appId": "<id>",\n "createdAt": "<ISO timestamp>",\n "lastUpdated": "<ISO timestamp>",\n "entries": [\n {\n "timestamp": "<ISO>",\n "role": "user",\n "summary": "<summarized user request -- never raw message or PII>",\n "op": "create"\n },\n {\n "timestamp": "<ISO>",\n "role": "openclaw",\n "summary": "<what was built, key design decisions>",\n "op": "create"\n }\n ]\n}\n```\n\nRules:\n- `summary` is a paraphrase, never the raw user message\n- Never include PII (names, emails, keys) in session.json\n- Each entry has a role (`user` or `openclaw`) and an `op`\n';
|
|
8460
|
+
|
|
8461
|
+
// ../claw-engine/src/authoring/openclaw-guides/references/echoclaw-update/CHECKLIST.md
|
|
8462
|
+
var CHECKLIST_default2 = "# Update Self-Check Checklist\n\n> Run these checks at Step 7 before delivering any updated ClawApp.\n\n---\n\n## Section A: Regression Check\n\nThe most important check for updates. You recorded the baseline in Step 3.\n\n```bash\n# Handler count AFTER changes -- must be >= baseline from Step 3\ngrep -c 'addEventListener\\|onclick\\|onchange\\|onsubmit\\|onkeydown\\|onkeyup' index.html\n# Compare with baseline. If lower, you broke something.\n```\n\n---\n\n## Section B: Structural Grep Checks\n\nRun these with `exec`. Every check produces a number, not a feeling.\n\n```bash\n# No stubs or debug artifacts (MUST = 0)\ngrep -cn 'TODO\\|FIXME\\|PLACEHOLDER' index.html\n\n# No debug logs (SHOULD = 0)\ngrep -cn 'console\\.log' index.html\n\n# No empty handlers (MUST = 0)\ngrep -cn 'onclick=\"\"\\|onchange=\"\"' index.html\n\n# Hardcoded colors -- only manifest.color hex allowed (SHOULD = 0 or 1)\ngrep -cn '#[0-9a-fA-F]\\{3,8\\}' index.html\n\n# CSS variable usage (MUST > 0)\ngrep -c 'var(--ec-' index.html\n\n# File size (MUST < 500KB for updates)\nwc -c index.html\n```\n\n---\n\n## Section C: Functional Cross-Check\n\n```bash\n# Count buttons\ngrep -c '<button' index.html\n\n# Count handler attachments\ngrep -c 'addEventListener\\|onclick' index.html\n\n# More buttons than handlers = dead controls. Fix before proceeding.\n```\n\n### Node-based check (for non-trivial changes)\n\n```bash\nnode -e \"const h=require('fs').readFileSync('index.html','utf8'); \\\n const btns=(h.match(/<button/g)||[]).length; \\\n const handlers=(h.match(/addEventListener|onclick/g)||[]).length; \\\n console.log('buttons:',btns,'handlers:',handlers); \\\n if(btns>handlers) process.exit(1)\"\n```\n\n---\n\n## Section D: Color Compliance\n\n```bash\n# Hardcoded colors in HTML (only manifest.color allowed)\ngrep -cn '#[0-9a-fA-F]\\{3,8\\}' index.html\n\n# CSS variable usage (should be > 0)\ngrep -c 'var(--ec-' index.html\n```\n\nVerify:\n- [ ] Zero hard-coded color literals in HTML/CSS/JS (hex, rgb, rgba, hsl, hsla, named colors)\n- [ ] Agent theme uses `var(--ec-agent)` / `var(--ec-agent-text)`\n- [ ] Only `manifest.color` in claw.json may be a hex value\n\n---\n\n## Section E: Spec Rule Review (Mental Verification)\n\nWhile reading back the file, verify unchanged compliance:\n\n- No left-side navigation\n- Bottom-right 64x64px area is clear\n- Scrollbar styling is present\n- Empty state handled for every data-driven view\n- Error state handled for every async operation\n- All user-visible text goes through `t()`\n- No external URLs (fonts, scripts, stylesheets)\n- All network requests use `echoclaw.invoke('fetch', ...)`\n- No dangerous code (`eval()`, `new Function()`, unsanitized `innerHTML`)\n";
|
|
8463
|
+
|
|
8464
|
+
// ../claw-engine/src/authoring/openclaw-guides/references/echoclaw-update/COMPLY.md
|
|
8465
|
+
var COMPLY_default = '# Sub-Workflow: Comply (Fix Review Issues)\n\n> Triggered when the engine returns `validation_failed` or a review file appears in `review/`.\n> This is a focused fix-and-resubmit cycle -- NOT a full rebuild.\n\n---\n\n## Comply Step 1: Read the Review\n\n**Tools:** `read`\n\nRead `review/round-N.review.md`. It contains three sections:\n\n| Section | Meaning |\n|---------|---------|\n| **"What\'s Correct (DO NOT TOUCH)"** | Preserve unless a critical issue requires changing |\n| **"Critical Issues"** | MUST fix. Includes current value and expected value as locators |\n| **"Warnings"** | Fix where possible. Do not break correct items to fix warnings |\n\n---\n\n## Comply Step 2: Read the Current Output\n\n**Tools:** `read`\n\nRead the current app from `input/` (the version that failed review). Understand the full context before making changes.\n\n---\n\n## Comply Step 3: Apply Surgical Fixes\n\n**Tools:** `edit`\n\nFix ONLY the listed critical issues:\n\n1. Use `edit` for each fix -- surgical changes, not rewrites\n2. Use the `current` text snippet from the review to find the exact location (line numbers may be stale)\n3. Do NOT touch anything marked as "What\'s Correct"\n4. Do NOT refactor, reorganize, or "improve" code not listed as an issue\n\n---\n\n## Comply Step 4: Verify Fixes\n\n**Tools:** `read`, `exec`\n\nFor each critical issue:\n1. `read` the affected section to confirm the fix\n2. Run the relevant `exec` check (grep for colors, size check, etc.)\n3. Verify the fix matches the expected value from the review\n\n---\n\n## Comply Step 5: Write Fixed Output\n\n**Tools:** `write`\n\nWrite the fixed package to `output/package.claw.json`. Then STOP and wait for re-review.\n\n---\n\n## Error Tracking\n\nThe review file includes an error trend indicator:\n\n| Trend | Meaning | Action |\n|-------|---------|--------|\n| **improving** | Error count decreasing | Keep going |\n| **stagnant** | Error count unchanged | Re-read the review more carefully |\n| **worsening** | Error count increasing | You are breaking correct items while fixing others |\n\n**Circuit breaker:** After 3 comply rounds with "worsening" or "stagnant" trend, emit:\n`<!--CLAW_STEP:{"appId":"<id>","op":"update","status":"error","message":"Comply loop not converging"}-->`\n\nThe engine will attempt auto-fix.\n';
|
|
8466
|
+
|
|
8453
8467
|
// src/gateway/WorkspaceSync.ts
|
|
8454
8468
|
var WORKSPACE_DIR = join(homedir(), ".openclaw", "workspace");
|
|
8455
8469
|
var VERSION_FILE = ".echoclaw-guide-version";
|
|
@@ -8470,9 +8484,23 @@ var REFERENCE_FILES = [
|
|
|
8470
8484
|
{ filename: "COMMON_MISTAKES.md", content: COMMON_MISTAKES_default }
|
|
8471
8485
|
];
|
|
8472
8486
|
var SKILL_FILES = [
|
|
8473
|
-
{
|
|
8487
|
+
{
|
|
8488
|
+
skillName: "echoclaw-create",
|
|
8489
|
+
content: WORKFLOW_CREATE_default,
|
|
8490
|
+
references: [
|
|
8491
|
+
{ filename: "CHECKLIST.md", content: CHECKLIST_default },
|
|
8492
|
+
{ filename: "ARCHITECTURE_PATTERNS.md", content: ARCHITECTURE_PATTERNS_default }
|
|
8493
|
+
]
|
|
8494
|
+
},
|
|
8474
8495
|
{ skillName: "echoclaw-adapt", content: WORKFLOW_ADAPT_default },
|
|
8475
|
-
{
|
|
8496
|
+
{
|
|
8497
|
+
skillName: "echoclaw-update",
|
|
8498
|
+
content: WORKFLOW_UPDATE_default,
|
|
8499
|
+
references: [
|
|
8500
|
+
{ filename: "CHECKLIST.md", content: CHECKLIST_default2 },
|
|
8501
|
+
{ filename: "COMPLY.md", content: COMPLY_default }
|
|
8502
|
+
]
|
|
8503
|
+
},
|
|
8476
8504
|
{ skillName: "echoclaw-studio", content: WORKFLOW_STUDIO_default }
|
|
8477
8505
|
];
|
|
8478
8506
|
var LEGACY_FILES = [
|
|
@@ -8529,6 +8557,14 @@ async function doSync(payload) {
|
|
|
8529
8557
|
await mkdir(skillDir, { recursive: true });
|
|
8530
8558
|
const content = skill.content.replace(/\{\{TIMESTAMP\}\}/g, timestamp);
|
|
8531
8559
|
await writeFile(join(skillDir, "SKILL.md"), content, "utf-8");
|
|
8560
|
+
if (skill.references && skill.references.length > 0) {
|
|
8561
|
+
const refsDir = join(skillDir, "references");
|
|
8562
|
+
await mkdir(refsDir, { recursive: true });
|
|
8563
|
+
for (const ref of skill.references) {
|
|
8564
|
+
const refContent = ref.content.replace(/\{\{TIMESTAMP\}\}/g, timestamp);
|
|
8565
|
+
await writeFile(join(refsDir, ref.filename), refContent, "utf-8");
|
|
8566
|
+
}
|
|
8567
|
+
}
|
|
8532
8568
|
}
|
|
8533
8569
|
for (const legacyFile of LEGACY_FILES) {
|
|
8534
8570
|
await unlink(join(workspacePath, legacyFile)).catch(() => {
|
package/dist/cli.js
CHANGED
|
@@ -16,10 +16,14 @@
|
|
|
16
16
|
* 10. COMMON_MISTAKES.md
|
|
17
17
|
*
|
|
18
18
|
* ~/.openclaw/workspace/skills/ (workflow Skill files)
|
|
19
|
-
* skills/echoclaw-create/SKILL.md
|
|
20
|
-
* skills/echoclaw-
|
|
21
|
-
* skills/echoclaw-
|
|
22
|
-
* skills/echoclaw-
|
|
19
|
+
* skills/echoclaw-create/SKILL.md ← WORKFLOW_CREATE.md content
|
|
20
|
+
* skills/echoclaw-create/references/CHECKLIST.md
|
|
21
|
+
* skills/echoclaw-create/references/ARCHITECTURE_PATTERNS.md
|
|
22
|
+
* skills/echoclaw-adapt/SKILL.md ← WORKFLOW_ADAPT.md content
|
|
23
|
+
* skills/echoclaw-update/SKILL.md ← WORKFLOW_UPDATE.md content
|
|
24
|
+
* skills/echoclaw-update/references/CHECKLIST.md
|
|
25
|
+
* skills/echoclaw-update/references/COMPLY.md
|
|
26
|
+
* skills/echoclaw-studio/SKILL.md ← WORKFLOW_STUDIO.md content
|
|
23
27
|
*
|
|
24
28
|
* Content is inlined from claw-engine at build time via esbuild text loader.
|
|
25
29
|
*
|
package/dist/index.js
CHANGED
package/package.json
CHANGED