agent-canvas 0.1.5 → 0.1.8

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.
Files changed (64) hide show
  1. package/.github/workflows/ci.yml +23 -0
  2. package/.github/workflows/release.yml +36 -0
  3. package/CLAUDE.md +86 -0
  4. package/LICENSE +21 -0
  5. package/README.md +71 -0
  6. package/bin/agent-canvas.ts +26 -353
  7. package/bin/lib/commands/daemon.ts +51 -0
  8. package/bin/lib/commands/fetch.ts +20 -0
  9. package/bin/lib/commands/install.ts +48 -0
  10. package/bin/lib/commands/push.ts +67 -0
  11. package/bin/lib/commands/watch.ts +23 -0
  12. package/bin/lib/config.ts +10 -0
  13. package/bin/lib/daemon-lifecycle.ts +74 -0
  14. package/bin/lib/helpers.ts +78 -0
  15. package/bun.lock +176 -0
  16. package/daemon/build.ts +27 -61
  17. package/daemon/bunfig.toml +2 -0
  18. package/daemon/client/AnnotationProvider.tsx +2 -2
  19. package/daemon/client/AnnotationSidebar.tsx +199 -31
  20. package/daemon/client/App.tsx +39 -3
  21. package/daemon/client/FileBrowser.tsx +8 -7
  22. package/daemon/client/FileViewer.tsx +153 -16
  23. package/daemon/client/PlanRenderer.tsx +6 -3
  24. package/daemon/client/ResponsePreview.tsx +5 -116
  25. package/daemon/client/SessionSwitcher.tsx +21 -10
  26. package/daemon/client/compat/preact-all.ts +4 -0
  27. package/daemon/client/components/Markdown.tsx +44 -0
  28. package/daemon/client/components/ResponseNote.tsx +3 -8
  29. package/daemon/client/components/UserInput.tsx +2 -4
  30. package/daemon/client/components/index.ts +1 -0
  31. package/daemon/client/domDiff.ts +82 -2
  32. package/daemon/client/generateMarkdown.ts +46 -9
  33. package/daemon/client/highlightRange.ts +8 -6
  34. package/daemon/client/runtime.ts +2 -0
  35. package/daemon/client/styles.css +126 -0
  36. package/daemon/client/unifiedDiff.ts +155 -2
  37. package/daemon/client/utils.ts +10 -0
  38. package/daemon/dist/client.css +1 -1
  39. package/daemon/dist/client.js +83 -36
  40. package/daemon/dist/components.js +63 -5
  41. package/daemon/dist/index.html +5 -12
  42. package/daemon/dist/jsx-runtime.js +3 -0
  43. package/daemon/dist/preact-compat.js +1 -0
  44. package/daemon/langMap.ts +8 -0
  45. package/daemon/src/compiler.ts +28 -2
  46. package/daemon/src/handlers/api.ts +150 -0
  47. package/daemon/src/handlers/files.ts +114 -0
  48. package/daemon/src/handlers/static.ts +61 -0
  49. package/daemon/src/handlers/utils.ts +14 -0
  50. package/daemon/src/router.ts +16 -0
  51. package/daemon/src/server.ts +28 -403
  52. package/daemon/src/session.ts +43 -24
  53. package/daemon/src/websocket.ts +83 -0
  54. package/daemon/tsconfig.json +20 -0
  55. package/package.json +4 -14
  56. package/skills/canvas/SKILL.md +47 -22
  57. package/skills/canvas/components.md +31 -0
  58. package/skills/canvas/flows.md +2 -2
  59. package/tsconfig.json +14 -0
  60. package/daemon/dist/jsx-dev-runtime-shim.js +0 -17
  61. package/daemon/dist/jsx-runtime-shim.js +0 -17
  62. package/daemon/dist/react-dom-client-shim.js +0 -4
  63. package/daemon/dist/react-dom-shim.js +0 -4
  64. package/daemon/dist/react-shim.js +0 -4
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "jsx": "react-jsx",
7
+ "strict": true,
8
+ "esModuleInterop": true,
9
+ "skipLibCheck": true,
10
+ "noEmit": true,
11
+ "rootDir": ".",
12
+ "types": ["@types/bun"],
13
+ "paths": {
14
+ "#canvas/runtime": ["./client/runtime.ts"],
15
+ "#canvas/components": ["./client/components/index.ts"]
16
+ }
17
+ },
18
+ "include": ["src/**/*", "client/**/*"],
19
+ "exclude": ["client/compat/**/*"]
20
+ }
package/package.json CHANGED
@@ -1,28 +1,18 @@
1
1
  {
2
2
  "name": "agent-canvas",
3
- "version": "0.1.5",
3
+ "version": "0.1.8",
4
4
  "description": "Interactive visual canvas for Claude Code — rich annotatable documents in the browser",
5
5
  "bin": {
6
6
  "agent-canvas": "bin/agent-canvas.js"
7
7
  },
8
- "files": [
9
- "bin/",
10
- "daemon/src/",
11
- "daemon/client/",
12
- "daemon/dist/",
13
- "daemon/build.ts",
14
- "daemon/package.json",
15
- "skills/",
16
- "hooks/"
17
- ],
18
- "scripts": {
8
+ "scripts": {
19
9
  "build": "bun daemon/build.ts",
20
10
  "typecheck": "tsc --noEmit && tsc --noEmit -p daemon/tsconfig.json",
21
11
  "prepublishOnly": "bun daemon/build.ts"
22
12
  },
23
13
  "dependencies": {
24
- "react": "^18.3.1",
25
- "react-dom": "^18.3.1"
14
+ "marked": "^17.0.4",
15
+ "preact": "^10.25.0"
26
16
  },
27
17
  "devDependencies": {
28
18
  "@tailwindcss/cli": "4",
@@ -15,18 +15,18 @@ disable-model-invocation: true
15
15
 
16
16
  Canvas opens a rich, annotatable document in the user's browser. You write JSX, the user reviews and annotates it like Google Docs, and feedback comes back as markdown. Use it whenever structured visual communication beats inline chat.
17
17
 
18
- **Read the flow reference before starting**: Check `flows.md` to pick the right flow for your task. Each flow has a specific sequence of canvases with templates.
18
+ **Read the flow reference before starting**: Check `${CLAUDE_SKILL_DIR}/flows.md` to pick the right flow for your task. Each flow has a specific sequence of canvases with templates.
19
19
 
20
- **Read the component reference as needed**: Check `components.md` for the full component API.
20
+ **Read the component reference as needed**: Check `${CLAUDE_SKILL_DIR}/components.md` for the full component API.
21
21
 
22
22
  ## Core Mechanics
23
23
 
24
24
  ### Writing a Canvas
25
25
 
26
- Use the **Write** tool to create a `.jsx` file in `.claude/agent-canvas/`:
26
+ Use the **Write** tool to create a `.jsx` file in `.claude/agent-canvas/${CLAUDE_SESSION_ID}/`:
27
27
 
28
28
  ```jsx
29
- // Write to: .claude/agent-canvas/plan.jsx
29
+ // Write to: .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx
30
30
 
31
31
  <Section title="Authentication Redesign">
32
32
  A proposal to replace session-based auth with JWT tokens.
@@ -50,7 +50,7 @@ Components are auto-available — no imports needed. The file is a JSX fragment
50
50
  After writing the file, push it to open the canvas in the browser:
51
51
 
52
52
  ```bash
53
- bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan"
53
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan"
54
54
  ```
55
55
 
56
56
  This opens the canvas in the user's browser (first time) and exits immediately. The round label defaults to the filename if `--label` is omitted. **Always show the `browserUrl` from the push output to the user** so they can open the canvas manually if auto-open didn't work.
@@ -60,46 +60,71 @@ This opens the canvas in the user's browser (first time) and exits immediately.
60
60
  After pushing, wait for the user to review and submit:
61
61
 
62
62
  ```bash
63
- bunx agent-canvas wait
63
+ bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
64
64
  ```
65
65
 
66
- This **blocks until the user submits feedback**, then prints feedback markdown to stdout. If feedback was already submitted (e.g. retrying after a disconnect), it returns immediately with the existing feedback.
66
+ This **blocks until the user submits feedback**, then prints feedback markdown to stdout. If feedback was already submitted but not yet consumed, it returns immediately with the existing feedback.
67
67
 
68
- > **Note:** If `push` outputs a warning "CANVAS_SESSION_ID not set, using generated ID: ...", you must pass that ID to wait: `bunx agent-canvas wait --session <id>`
68
+ **Timeout handling**: The watch command may time out if the user takes a while to review. If it times out, don't retry automatically — wait for the user to tell you they've submitted feedback, then run the watch command again (or use `fetch`) to retrieve it.
69
+
70
+ ### Checking for feedback without blocking
71
+
72
+ Use `fetch` to check for feedback without waiting:
73
+
74
+ ```bash
75
+ bunx agent-canvas fetch --session ${CLAUDE_SESSION_ID}
76
+ ```
77
+
78
+ Returns immediately — prints feedback to stdout if available, otherwise produces no output. Use this to retry after a timeout or when the user tells you they've submitted.
69
79
 
70
80
  ### Iterating
71
81
 
72
82
  Use the **Edit** tool to modify the existing JSX based on feedback — targeted edits, not full rewrites. Then push and wait again:
73
83
 
74
84
  ```bash
75
- bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan (revised)"
76
- bunx agent-canvas wait
85
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan (revised)"
86
+ bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
77
87
  ```
78
88
 
79
89
  Each push creates a new **round** — the user sees previous rounds and their feedback in the UI.
80
90
 
91
+ ### Responding to feedback
92
+
93
+ 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.
94
+
95
+ ```bash
96
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --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."
97
+ ```
98
+
99
+ Use `--response` to:
100
+ - Confirm what you incorporated and how
101
+ - Explain any opposing stance — if you disagree with a suggestion, say why
102
+ - Call out anything you intentionally did NOT change and why
103
+
104
+ Keep it concise (2-4 sentences). The user can see the diff in the canvas UI, so focus on the "why" not the "what".
105
+
81
106
  ### Multiple Canvases
82
107
 
83
108
  Maintain separate files for different phases:
84
109
 
85
110
  ```bash
86
- bunx agent-canvas push .claude/agent-canvas/discovery.jsx --label "Discovery"
87
- bunx agent-canvas wait
111
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/discovery.jsx --session ${CLAUDE_SESSION_ID} --label "Discovery"
112
+ bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
88
113
  # ... process feedback ...
89
- bunx agent-canvas push .claude/agent-canvas/requirements.jsx --label "Requirements"
90
- bunx agent-canvas wait
114
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/requirements.jsx --session ${CLAUDE_SESSION_ID} --label "Requirements"
115
+ bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
91
116
  # ... process feedback ...
92
- bunx agent-canvas push .claude/agent-canvas/plan.jsx --label "Implementation Plan"
93
- bunx agent-canvas wait
117
+ bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/plan.jsx --session ${CLAUDE_SESSION_ID} --label "Implementation Plan"
118
+ bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}
94
119
  ```
95
120
 
96
121
  ### File Location
97
122
 
98
- All canvas files go in `.claude/agent-canvas/` within the project root. Add `.claude/agent-canvas/` to `.gitignore` — these are ephemeral working files.
123
+ 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.
99
124
 
100
125
  ## Choosing a Flow
101
126
 
102
- Determine which flow fits before starting. See `flows.md` for full details with templates.
127
+ Determine which flow fits before starting. See `${CLAUDE_SKILL_DIR}/flows.md` for full details with templates.
103
128
 
104
129
  ```
105
130
  User wants something built/changed?
@@ -129,10 +154,10 @@ User wants to make a decision?
129
154
 
130
155
  1. **Determine flow** from user intent
131
156
  2. **Announce** briefly: "I'll start with discovery, then create a detailed plan."
132
- 3. **Write canvas JSX** with the Write tool to `.claude/agent-canvas/<name>.jsx`
133
- 4. **Push**: `bunx agent-canvas push .claude/agent-canvas/<name>.jsx --label "<Label>"`
157
+ 3. **Write canvas JSX** with the Write tool to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/<name>.jsx`
158
+ 4. **Push**: `bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/<name>.jsx --session ${CLAUDE_SESSION_ID} --label "<Label>"`
134
159
  5. **Tell the user** the canvas is ready for review (they see it in their browser)
135
- 6. **Wait**: `bunx agent-canvas wait` — this blocks until the user submits feedback
160
+ 6. **Wait**: `bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}` — this blocks until the user submits feedback
136
161
  7. **Read feedback** — check for annotations, answers, added context files, approval
137
162
  8. **Edit with the Edit tool and re-push + wait**, or advance to next phase
138
163
  9. **After implementation**, push a summary canvas
@@ -165,7 +190,7 @@ Then interview deeply in each selected area across subsequent rounds. Or ask in
165
190
 
166
191
  ## Important Rules
167
192
 
168
- - **Write** canvas files to `.claude/agent-canvas/` using the Write tool. Never use bash heredocs or cat.
193
+ - **Write** canvas files to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/` using the Write tool. Never use bash heredocs or cat.
169
194
  - **Edit** canvas files using the Edit tool. Never rewrite entire files — make targeted edits.
170
195
  - JSX can be a fragment (just tags) or a full module with `export default function Canvas()`.
171
196
  - Every `<Item>` and interactive component needs a unique `id`.
@@ -100,6 +100,37 @@ Inline code block with syntax highlighting. For code that's part of the narrativ
100
100
  **Props:**
101
101
  - `language` (string) — syntax highlighting language
102
102
 
103
+ ### `<Markdown>`
104
+
105
+ Renders markdown content with full GFM support (headings, lists, tables, code blocks, links, etc.). Two modes:
106
+
107
+ **Inline source** — markdown as children:
108
+ ```jsx
109
+ <Markdown>{`
110
+ # Summary
111
+
112
+ This is **bold** and *italic*. Here's a list:
113
+
114
+ - First item
115
+ - Second item
116
+ - Nested item
117
+
118
+ \`\`\`typescript
119
+ const x = 42;
120
+ \`\`\`
121
+ `}</Markdown>
122
+ ```
123
+
124
+ **File reference** — loads file content at compile time:
125
+ ```jsx
126
+ <Markdown file="docs/architecture.md" />
127
+ <Markdown file="CHANGELOG.md" />
128
+ ```
129
+
130
+ **Props:**
131
+ - `file` (string) — path relative to project root. Content is resolved at compile time.
132
+ - Children (string) — inline markdown source. Used when `file` is not specified.
133
+
103
134
  ### `<ImageView src="string">`
104
135
 
105
136
  Displays an image from the project directory.
@@ -2,7 +2,7 @@
2
2
 
3
3
  Each flow is a sequence of canvas rounds. Phases can be skipped or combined based on context.
4
4
 
5
- **Tooling reminder**: Write all `.jsx` files using the **Write** tool to `.claude/agent-canvas/`. Edit them with the **Edit** tool. Push with `bunx agent-canvas push .claude/agent-canvas/<file>.jsx --label "<Label>"`, then `bunx agent-canvas wait` to block for feedback. The JSX blocks below show what to write — they are the file content, not bash commands.
5
+ **Tooling reminder**: Write all `.jsx` files using the **Write** tool to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/`. Edit them with the **Edit** tool. Push with `bunx agent-canvas push .claude/agent-canvas/${CLAUDE_SESSION_ID}/<file>.jsx --session ${CLAUDE_SESSION_ID} --label "<Label>"`, then `bunx agent-canvas watch --session ${CLAUDE_SESSION_ID}` to block for feedback. The JSX blocks below show what to write — they are the file content, not bash commands.
6
6
 
7
7
  ---
8
8
 
@@ -16,7 +16,7 @@ Each flow is a sequence of canvas rounds. Phases can be skipped or combined base
16
16
 
17
17
  Goal: understand scope, constraints, and user expectations. Start broad, go deep.
18
18
 
19
- Write to `.claude/agent-canvas/discovery.jsx`, then push and wait:
19
+ Write to `.claude/agent-canvas/${CLAUDE_SESSION_ID}/discovery.jsx`, then push and wait:
20
20
 
21
21
  ```jsx
22
22
  <Section title="Discovery: [Feature Name]">
package/tsconfig.json ADDED
@@ -0,0 +1,14 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ESNext",
4
+ "module": "ESNext",
5
+ "moduleResolution": "bundler",
6
+ "strict": true,
7
+ "esModuleInterop": true,
8
+ "skipLibCheck": true,
9
+ "allowImportingTsExtensions": true,
10
+ "noEmit": true,
11
+ "types": ["@types/bun"]
12
+ },
13
+ "include": ["bin/**/*"]
14
+ }
@@ -1,17 +0,0 @@
1
-
2
- const R = window.React;
3
- export const Fragment = R.Fragment;
4
- function _jsx(type, props, key) {
5
- if (props && 'children' in props) {
6
- const { children, ...rest } = props;
7
- if (key !== undefined && key !== null) rest.key = key;
8
- return Array.isArray(children)
9
- ? R.createElement(type, rest, ...children)
10
- : R.createElement(type, rest, children);
11
- }
12
- if (key !== undefined && key !== null) props = { ...props, key };
13
- return R.createElement(type, props);
14
- }
15
- export const jsx = _jsx;
16
- export const jsxs = _jsx;
17
- export function jsxDEV(type, props, key) { return _jsx(type, props, key); }
@@ -1,17 +0,0 @@
1
-
2
- const R = window.React;
3
- export const Fragment = R.Fragment;
4
- function _jsx(type, props, key) {
5
- if (props && 'children' in props) {
6
- const { children, ...rest } = props;
7
- if (key !== undefined && key !== null) rest.key = key;
8
- return Array.isArray(children)
9
- ? R.createElement(type, rest, ...children)
10
- : R.createElement(type, rest, children);
11
- }
12
- if (key !== undefined && key !== null) props = { ...props, key };
13
- return R.createElement(type, props);
14
- }
15
- export const jsx = _jsx;
16
- export const jsxs = _jsx;
17
- export function jsxDEV(type, props, key) { return _jsx(type, props, key); }
@@ -1,4 +0,0 @@
1
-
2
- const RD = window.ReactDOM;
3
- export const { createRoot, hydrateRoot } = RD;
4
- export default { createRoot: RD.createRoot, hydrateRoot: RD.hydrateRoot };
@@ -1,4 +0,0 @@
1
-
2
- const RD = window.ReactDOM;
3
- export default RD;
4
- export const { createPortal, flushSync, hydrate, render, unmountComponentAtNode, findDOMNode, unstable_batchedUpdates } = RD;
@@ -1,4 +0,0 @@
1
-
2
- const R = window.React;
3
- export default R;
4
- export const { useState, useEffect, useRef, useCallback, useContext, useMemo, useReducer, createContext, createElement, Fragment, Children, cloneElement, isValidElement, memo, forwardRef, lazy, Suspense, startTransition, useTransition, useDeferredValue, useId, useSyncExternalStore, useInsertionEffect, useImperativeHandle, useLayoutEffect, useDebugValue, Component, PureComponent } = R;