agent-canvas 0.1.16 → 0.1.18
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.
- package/bin/agent-canvas.ts +3 -0
- package/bin/lib/commands/install.ts +7 -7
- package/bin/lib/commands/instructions.ts +66 -0
- package/bin/lib/instructions/component-callout.md +27 -0
- package/bin/lib/instructions/component-checklist.md +17 -0
- package/bin/lib/instructions/component-choice.md +27 -0
- package/bin/lib/instructions/component-codeblock.md +24 -0
- package/bin/lib/instructions/component-custom.md +34 -0
- package/bin/lib/instructions/component-diff.md +18 -0
- package/bin/lib/instructions/component-filepreview.md +15 -0
- package/bin/lib/instructions/component-image.md +17 -0
- package/bin/lib/instructions/component-item.md +43 -0
- package/bin/lib/instructions/component-markdown.md +33 -0
- package/bin/lib/instructions/component-mermaid.md +40 -0
- package/bin/lib/instructions/component-multichoice.md +19 -0
- package/bin/lib/instructions/component-note.md +11 -0
- package/bin/lib/instructions/component-priority.md +15 -0
- package/bin/lib/instructions/component-rangeinput.md +20 -0
- package/bin/lib/instructions/component-section.md +22 -0
- package/bin/lib/instructions/component-svg.md +31 -0
- package/bin/lib/instructions/component-table.md +21 -0
- package/bin/lib/instructions/component-usefeedback.md +33 -0
- package/bin/lib/instructions/component-userinput.md +17 -0
- package/bin/lib/instructions/flow-decision.md +53 -0
- package/bin/lib/instructions/flow-explain.md +47 -0
- package/bin/lib/instructions/flow-feature.md +188 -0
- package/bin/lib/instructions/flow-plan.md +106 -0
- package/bin/lib/instructions/flow-review.md +60 -0
- package/bin/lib/instructions/overview.md +173 -0
- package/bin/lib/instructions/theming.md +50 -0
- package/daemon/client/AnnotationSidebar.tsx +40 -20
- package/daemon/client/App.tsx +8 -1
- package/daemon/dist/client.js +35 -35
- package/daemon/src/compiler.ts +1 -0
- package/daemon/src/handlers/api.ts +3 -1
- package/daemon/src/server.ts +1 -1
- package/daemon/src/websocket.ts +62 -1
- package/package.json +1 -1
- package/skills/canvas/SKILL.md +9 -194
- package/skills/canvas/components.md +0 -403
- package/skills/canvas/flows.md +0 -406
package/daemon/src/compiler.ts
CHANGED
|
@@ -130,6 +130,7 @@ export async function compilePlan(jsx: string, projectRoot?: string): Promise<Co
|
|
|
130
130
|
entrypoints: [tmpFile],
|
|
131
131
|
format: "esm",
|
|
132
132
|
external: ["react", "react-dom", "#canvas/components", "#canvas/runtime"],
|
|
133
|
+
jsx: { runtime: "automatic", importSource: "react" },
|
|
133
134
|
});
|
|
134
135
|
|
|
135
136
|
if (!result.success) {
|
|
@@ -8,11 +8,12 @@ import type { Route } from "../router";
|
|
|
8
8
|
export interface ApiContext {
|
|
9
9
|
sessionManager: SessionManager;
|
|
10
10
|
broadcastPlanUpdate: (id: string) => void;
|
|
11
|
+
broadcastRevisionUpdate: (id: string) => void;
|
|
11
12
|
port: number;
|
|
12
13
|
}
|
|
13
14
|
|
|
14
15
|
export function createApiHandlers(ctx: ApiContext): Route[] {
|
|
15
|
-
const { sessionManager, broadcastPlanUpdate, port } = ctx;
|
|
16
|
+
const { sessionManager, broadcastPlanUpdate, broadcastRevisionUpdate, port } = ctx;
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* Read *.jsx canvas files from a directory.
|
|
@@ -170,6 +171,7 @@ export function createApiHandlers(ctx: ApiContext): Route[] {
|
|
|
170
171
|
const result = sessionManager.getLatestUnconsumedFeedback(sessionId);
|
|
171
172
|
if (!result) return jsonResponse({ found: false });
|
|
172
173
|
sessionManager.consumeFeedback(sessionId, result.revision);
|
|
174
|
+
broadcastRevisionUpdate(sessionId);
|
|
173
175
|
return jsonResponse({ found: true, revision: result.revision, feedback: result.feedback });
|
|
174
176
|
}
|
|
175
177
|
|
package/daemon/src/server.ts
CHANGED
|
@@ -21,7 +21,7 @@ const sessionManager = new SessionManager();
|
|
|
21
21
|
const wsManager = createWebSocketManager(sessionManager);
|
|
22
22
|
|
|
23
23
|
const routes = [
|
|
24
|
-
...createApiHandlers({ sessionManager, broadcastPlanUpdate: wsManager.broadcastPlanUpdate, port: PORT }),
|
|
24
|
+
...createApiHandlers({ sessionManager, broadcastPlanUpdate: wsManager.broadcastPlanUpdate, broadcastRevisionUpdate: wsManager.broadcastRevisionUpdate, port: PORT }),
|
|
25
25
|
...createFileHandlers(sessionManager),
|
|
26
26
|
...createUploadHandlers(sessionManager),
|
|
27
27
|
...createStaticHandlers(),
|
package/daemon/src/websocket.ts
CHANGED
|
@@ -36,12 +36,63 @@ export function createWebSocketManager(sessionManager: SessionManager) {
|
|
|
36
36
|
}
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
function broadcastWatcherStatus(sessionId: string) {
|
|
40
|
+
const sockets = browserSockets.get(sessionId);
|
|
41
|
+
if (!sockets) return;
|
|
42
|
+
const watching = (waitSockets.get(sessionId)?.size ?? 0) > 0;
|
|
43
|
+
const payload = JSON.stringify({ type: "watcher-status", watching });
|
|
44
|
+
for (const ws of sockets) {
|
|
45
|
+
ws.send(payload);
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Ping wait sockets periodically to detect dead connections.
|
|
50
|
+
// We can't rely on ws.close() triggering the close handler for dead sockets,
|
|
51
|
+
// so we manually remove dead sockets and broadcast status.
|
|
52
|
+
const PING_INTERVAL = 5_000;
|
|
53
|
+
const pongReceived = new WeakSet<ServerWebSocket<WSData>>();
|
|
54
|
+
|
|
55
|
+
function removeWaitSocket(ws: ServerWebSocket<WSData>, sessionId: string) {
|
|
56
|
+
const sockets = waitSockets.get(sessionId);
|
|
57
|
+
if (sockets) {
|
|
58
|
+
sockets.delete(ws);
|
|
59
|
+
if (sockets.size === 0) waitSockets.delete(sessionId);
|
|
60
|
+
}
|
|
61
|
+
try { ws.close(); } catch {}
|
|
62
|
+
broadcastWatcherStatus(sessionId);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setInterval(() => {
|
|
66
|
+
for (const [sessionId, sockets] of waitSockets) {
|
|
67
|
+
for (const ws of sockets) {
|
|
68
|
+
if (!pongReceived.has(ws)) {
|
|
69
|
+
// No pong since last ping — connection is dead
|
|
70
|
+
removeWaitSocket(ws, sessionId);
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
pongReceived.delete(ws);
|
|
74
|
+
try { ws.ping(); } catch {
|
|
75
|
+
removeWaitSocket(ws, sessionId);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}, PING_INTERVAL);
|
|
80
|
+
|
|
39
81
|
const handlers = {
|
|
40
82
|
open(ws: ServerWebSocket<WSData>) {
|
|
41
83
|
const { type, sessionId } = ws.data;
|
|
42
84
|
const map = type === "browser" ? browserSockets : waitSockets;
|
|
43
85
|
if (!map.has(sessionId)) map.set(sessionId, new Set());
|
|
44
86
|
map.get(sessionId)!.add(ws);
|
|
87
|
+
if (type === "browser") {
|
|
88
|
+
// Send current watcher status to newly connected browser
|
|
89
|
+
const watching = (waitSockets.get(sessionId)?.size ?? 0) > 0;
|
|
90
|
+
ws.send(JSON.stringify({ type: "watcher-status", watching }));
|
|
91
|
+
} else {
|
|
92
|
+
// CLI waiter connected — notify browsers
|
|
93
|
+
pongReceived.add(ws); // Give it a free pass on first interval
|
|
94
|
+
broadcastWatcherStatus(sessionId);
|
|
95
|
+
}
|
|
45
96
|
},
|
|
46
97
|
message(ws: ServerWebSocket<WSData>, message: string | Buffer) {
|
|
47
98
|
const { type, sessionId } = ws.data;
|
|
@@ -59,6 +110,7 @@ export function createWebSocketManager(sessionManager: SessionManager) {
|
|
|
59
110
|
const waiters = waitSockets.get(sessionId);
|
|
60
111
|
if (waiters && waiters.size > 0) {
|
|
61
112
|
sessionManager.consumeFeedback(sessionId, session.currentRevision);
|
|
113
|
+
broadcastRevisionUpdate(sessionId);
|
|
62
114
|
const payload = JSON.stringify({ type: "submit", feedback });
|
|
63
115
|
for (const waiter of waiters) {
|
|
64
116
|
waiter.send(payload);
|
|
@@ -71,13 +123,22 @@ export function createWebSocketManager(sessionManager: SessionManager) {
|
|
|
71
123
|
} catch {}
|
|
72
124
|
}
|
|
73
125
|
},
|
|
126
|
+
pong(ws: ServerWebSocket<WSData>) {
|
|
127
|
+
if (ws.data.type === "wait") {
|
|
128
|
+
pongReceived.add(ws);
|
|
129
|
+
}
|
|
130
|
+
},
|
|
74
131
|
close(ws: ServerWebSocket<WSData>) {
|
|
75
132
|
const { type, sessionId } = ws.data;
|
|
76
133
|
const map = type === "browser" ? browserSockets : waitSockets;
|
|
77
134
|
map.get(sessionId)?.delete(ws);
|
|
78
135
|
if (map.get(sessionId)?.size === 0) map.delete(sessionId);
|
|
136
|
+
if (type === "wait") {
|
|
137
|
+
// CLI waiter disconnected — notify browsers
|
|
138
|
+
broadcastWatcherStatus(sessionId);
|
|
139
|
+
}
|
|
79
140
|
},
|
|
80
141
|
};
|
|
81
142
|
|
|
82
|
-
return { handlers, broadcastPlanUpdate, broadcastRevisionUpdate };
|
|
143
|
+
return { handlers, broadcastPlanUpdate, broadcastRevisionUpdate, broadcastWatcherStatus };
|
|
83
144
|
}
|
package/package.json
CHANGED
package/skills/canvas/SKILL.md
CHANGED
|
@@ -11,203 +11,18 @@ disable-model-invocation: true
|
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
## Session
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
Your canvas session ID is: `${CLAUDE_SESSION_ID}`
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Use this value for:
|
|
19
|
+
- File paths: `.claude/agent-canvas/${CLAUDE_SESSION_ID}/`
|
|
20
|
+
- CLI flags: `--session ${CLAUDE_SESSION_ID}`
|
|
19
21
|
|
|
20
|
-
|
|
22
|
+
## Instructions
|
|
21
23
|
|
|
22
|
-
|
|
24
|
+
!`bunx agent-canvas instructions`
|
|
23
25
|
|
|
24
|
-
|
|
26
|
+
!`bunx agent-canvas instructions --list`
|
|
25
27
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
```jsx
|
|
29
|
-
// Write to: .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx
|
|
30
|
-
|
|
31
|
-
<Section title="Authentication Redesign">
|
|
32
|
-
A proposal to replace session-based auth with JWT tokens.
|
|
33
|
-
|
|
34
|
-
<Item label="Current state" badge="context" badgeVariant="info">
|
|
35
|
-
The app uses express-session with Redis store.
|
|
36
|
-
<FilePreview path="src/auth/session.ts" lines={[1, 45]} />
|
|
37
|
-
</Item>
|
|
38
|
-
|
|
39
|
-
<Item label="Proposed change" badge="proposal">
|
|
40
|
-
Switch to stateless JWT with refresh token rotation.
|
|
41
|
-
<Callout type="warning">This will invalidate all existing sessions.</Callout>
|
|
42
|
-
</Item>
|
|
43
|
-
</Section>
|
|
44
|
-
```
|
|
45
|
-
|
|
46
|
-
Components are auto-available — no imports needed. The file is a JSX fragment (just tags) or a full module with `export default`.
|
|
47
|
-
|
|
48
|
-
### Pushing
|
|
49
|
-
|
|
50
|
-
After writing canvas files, push the directory to open all canvases in the browser:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID} --label "Implementation Plan"
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
This pushes all `*.jsx` files in the directory as a snapshot (like a git commit). Each file appears as a separate tab in the browser. The canvas opens in the user's browser (first time) and the command exits immediately. **Always show the `browserUrl` from the push output to the user** so they can open the canvas manually if auto-open didn't work.
|
|
57
|
-
|
|
58
|
-
### Waiting for feedback
|
|
59
|
-
|
|
60
|
-
After pushing, start the watch command **in the background** using the Bash tool's `run_in_background` parameter:
|
|
61
|
-
|
|
62
|
-
```bash
|
|
63
|
-
# Use Bash tool with run_in_background: true
|
|
64
|
-
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
65
|
-
```
|
|
66
|
-
|
|
67
|
-
This runs the watch process in the background. You will be **automatically notified** when the user submits feedback — the output will contain the feedback markdown. Do NOT poll, sleep, or proactively check on it. Just stop and wait for the notification.
|
|
68
|
-
|
|
69
|
-
If feedback was already submitted but not yet consumed, the command returns immediately with the existing feedback.
|
|
70
|
-
|
|
71
|
-
**Important**: After starting the background watch, do not continue with other work unless instructed. Wait for the notification that feedback has arrived before proceeding.
|
|
72
|
-
|
|
73
|
-
### Checking for feedback without blocking
|
|
74
|
-
|
|
75
|
-
Use `fetch` to retrieve feedback if the user tells you they've already submitted:
|
|
76
|
-
|
|
77
|
-
```bash
|
|
78
|
-
bunx agent-canvas fetch --session ${CLAUDE_SESSION_ID}
|
|
79
|
-
```
|
|
80
|
-
|
|
81
|
-
Returns immediately — prints feedback to stdout if available, otherwise produces no output. Use this when the user tells you they've submitted feedback (e.g. after a reconnect or if the background watch was lost).
|
|
82
|
-
|
|
83
|
-
### Iterating
|
|
84
|
-
|
|
85
|
-
Use the **Edit** tool to modify the existing JSX based on feedback — targeted edits, not full rewrites. Then push and watch again:
|
|
86
|
-
|
|
87
|
-
```bash
|
|
88
|
-
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID} --label "Implementation Plan (revised)"
|
|
89
|
-
|
|
90
|
-
# Use Bash tool with run_in_background: true
|
|
91
|
-
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
|
|
92
|
-
```
|
|
93
|
-
|
|
94
|
-
Each push creates a new **round** (revision) — all canvas files are snapshotted. The user sees which files changed in the revision selector.
|
|
95
|
-
|
|
96
|
-
### Responding to feedback
|
|
97
|
-
|
|
98
|
-
When pushing a revised canvas after receiving user feedback, use `--response` to tell the user how you addressed their feedback. The response renders as a short banner at the top of the canvas.
|
|
99
|
-
|
|
100
|
-
```bash
|
|
101
|
-
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID} --label "Plan (revised)" --response "Incorporated all feedback. Switched to connection pooling as suggested. I kept the sync approach for writes — see the note in Phase 2 for my reasoning."
|
|
102
|
-
```
|
|
103
|
-
|
|
104
|
-
Use `--response` to:
|
|
105
|
-
- Confirm what you incorporated and how
|
|
106
|
-
- Explain any opposing stance — if you disagree with a suggestion, say why
|
|
107
|
-
- Call out anything you intentionally did NOT change and why
|
|
108
|
-
|
|
109
|
-
Keep it concise (2-4 sentences). The user can see the diff in the canvas UI, so focus on the "why" not the "what".
|
|
110
|
-
|
|
111
|
-
### Multiple Canvases
|
|
112
|
-
|
|
113
|
-
All `*.jsx` files in the canvas directory are pushed together as a snapshot. Each file appears as a separate tab in the browser. Use different files for different phases or concerns:
|
|
114
|
-
|
|
115
|
-
```
|
|
116
|
-
.claude/agent-canvas/${CLAUDE_SESSION_ID}/
|
|
117
|
-
discovery.jsx # Discovery interview
|
|
118
|
-
requirements.jsx # Requirements spec
|
|
119
|
-
plan.jsx # Implementation plan
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
Write new files as phases progress, then push the directory. Previous phase files remain visible as context. The user can annotate any canvas and submit one combined feedback per revision.
|
|
123
|
-
|
|
124
|
-
```bash
|
|
125
|
-
# Write discovery.jsx first, push and get feedback
|
|
126
|
-
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID} --label "Discovery"
|
|
127
|
-
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID} # run_in_background: true
|
|
128
|
-
|
|
129
|
-
# Later, add requirements.jsx, push again — both files visible as tabs
|
|
130
|
-
bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID} --label "Requirements"
|
|
131
|
-
bunx agent-canvas watch --session ${CLAUDE_SESSION_ID} # run_in_background: true
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
### File Location
|
|
135
|
-
|
|
136
|
-
All canvas files go in `.claude/agent-canvas/${CLAUDE_SESSION_ID}/` within the project root. Add `.claude/agent-canvas/` to `.gitignore` — these are ephemeral working files.
|
|
137
|
-
|
|
138
|
-
## Choosing a Flow
|
|
139
|
-
|
|
140
|
-
Determine which flow fits before starting. See `${CLAUDE_SKILL_DIR}/flows.md` for full details with templates.
|
|
141
|
-
|
|
142
|
-
```
|
|
143
|
-
User wants something built/changed?
|
|
144
|
-
├─ Vague request ("add auth", "refactor the API")
|
|
145
|
-
│ └─ FEATURE flow: discovery → requirements → plan → [implement] → summary
|
|
146
|
-
├─ Clear request with specifics
|
|
147
|
-
│ └─ PLAN flow: plan → [implement] → summary
|
|
148
|
-
├─ Complete instructions, just wants execution
|
|
149
|
-
│ └─ Skip canvas, implement directly, optionally push summary
|
|
150
|
-
|
|
151
|
-
User wants to understand something?
|
|
152
|
-
├─ "How does X work?", "Explain the auth system"
|
|
153
|
-
│ └─ EXPLAIN flow: single explanatory canvas
|
|
154
|
-
|
|
155
|
-
User wants review/audit?
|
|
156
|
-
├─ "Review this code", "Check for issues"
|
|
157
|
-
│ └─ REVIEW flow: findings canvas with categorized issues
|
|
158
|
-
|
|
159
|
-
User wants to make a decision?
|
|
160
|
-
├─ "Should we use X or Y?", "What database?"
|
|
161
|
-
│ └─ DECISION flow: options with tradeoffs, recommendation
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Adapt dynamically**: flows are guidelines, not rigid pipelines. Pivot if needed, skip if the user says so.
|
|
165
|
-
|
|
166
|
-
## Flow Execution Pattern
|
|
167
|
-
|
|
168
|
-
1. **Determine flow** from user intent
|
|
169
|
-
2. **Announce** briefly: "I'll start with discovery, then create a detailed plan."
|
|
170
|
-
3. **Write canvas JSX** with the Write tool to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/<name>.jsx`
|
|
171
|
-
4. **Push the directory + tell the user**: Push with `bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/ --session ${CLAUDE_SESSION_ID}` and show the `browserUrl` from the output so the user can open it
|
|
172
|
-
5. **IMMEDIATELY watch in background** — run `bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}` with `run_in_background: true` right after push. Never stop after pushing. The push→watch sequence is atomic: no push without a watch. You will be notified when feedback arrives — do not poll or sleep.
|
|
173
|
-
6. **Read feedback** — check for annotations, answers, added context files, approval
|
|
174
|
-
7. **Edit with the Edit tool and re-push + watch**, or advance to next phase
|
|
175
|
-
8. **After implementation**, push a summary canvas
|
|
176
|
-
|
|
177
|
-
## Interview Integration
|
|
178
|
-
|
|
179
|
-
For discovery phases, use iterative canvases or offer a checklist:
|
|
180
|
-
|
|
181
|
-
```jsx
|
|
182
|
-
<Section title="Before we start">
|
|
183
|
-
Which areas should we discuss in depth?
|
|
184
|
-
|
|
185
|
-
<MultiChoice id="interview-areas" label="Select areas to cover" required
|
|
186
|
-
options={[
|
|
187
|
-
"Backend architecture & data model",
|
|
188
|
-
"API design & contracts",
|
|
189
|
-
"Frontend / UI behavior",
|
|
190
|
-
"Edge cases & error handling",
|
|
191
|
-
"Performance & scaling",
|
|
192
|
-
"Security considerations",
|
|
193
|
-
"Testing strategy",
|
|
194
|
-
"Migration / deployment"
|
|
195
|
-
]} />
|
|
196
|
-
|
|
197
|
-
<UserInput id="anything-else" label="Anything else I should know upfront?" />
|
|
198
|
-
</Section>
|
|
199
|
-
```
|
|
200
|
-
|
|
201
|
-
Then interview deeply in each selected area across subsequent rounds. Or ask in chat first: "Would you prefer a thorough interview before I plan, or should I make assumptions and you correct in review?"
|
|
202
|
-
|
|
203
|
-
## Important Rules
|
|
204
|
-
|
|
205
|
-
- **Write** canvas files to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/` using the Write tool. Never use bash heredocs or cat.
|
|
206
|
-
- **Edit** canvas files using the Edit tool. Never rewrite entire files — make targeted edits.
|
|
207
|
-
- JSX can be a fragment (just tags) or a full module with `export default function Canvas()`.
|
|
208
|
-
- Every `<Item>` and interactive component needs a unique `id`.
|
|
209
|
-
- `<FilePreview path="...">` paths are relative to project root.
|
|
210
|
-
- For inline styling use `style={{ ... }}` — Tailwind is NOT available in canvas JSX.
|
|
211
|
-
- Read any files the user added to context (listed under "Added context" in feedback) with the Read tool before the next iteration.
|
|
212
|
-
- Always push a summary canvas after implementation, even if brief.
|
|
213
|
-
- When the user approves, confirm what you'll do next before proceeding.
|
|
28
|
+
**Getting detailed docs**: Run `bunx agent-canvas instructions <topic>` for detailed documentation on any component or flow. For example: `bunx agent-canvas instructions component-mermaid` or `bunx agent-canvas instructions flow-feature`.
|