Skip to content

Commit b9e214b

Browse files
kurtstohrerclaude
andcommitted
Add element text in task context, add init-mcp CLI, plus stabilization work
- Bridge captures visible element text (aria-label/title/textContent) via a shared getVisibleText helper and surfaces it on `resolve:at-point`, `resolve:element-context`, and click/contextmenu events. The text threads through primarySelection and into pin, select, arrow from/to, and highlight task contexts so agents can disambiguate elements by visible label; `selected_element_text` added to ElementContext. - New `annotask init-mcp` CLI: writes editor-specific MCP config at `.mcp.json` (Claude Code), `.cursor/mcp.json`, `.vscode/mcp.json` (uses VS Code's `servers` key), or `.codeium/windsurf/mcp_config.json`. `--editor=all` writes every target; merges with any pre-existing MCP servers; skips when an `annotask` entry already exists unless `--force`. - Ongoing stabilization: zod validation at HTTP/MCP boundaries (src/server/schemas.ts, src/server/validation.ts), extracted Task detail sub-components, split Perf/Theme views, moved vendor copy to scripts/copy-vendor.mjs, added state + validation + route tests. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 258b5c2 commit b9e214b

59 files changed

Lines changed: 2422 additions & 1138 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,6 @@ plan/
1515

1616
# Local-only (references external repos)
1717
playgrounds/antenna-vite/
18+
19+
# Claude Code session state
20+
.claude/worktrees/

CHANGELOG.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
# Changelog
2+
3+
All notable changes to this project are documented here. Versions follow [Semantic Versioning](https://semver.org/). Dates are ISO 8601.
4+
5+
## [Unreleased]
6+
7+
### Added
8+
- **`src/server/validation.ts`** — canonical home for screenshot filename regex, valid task statuses, allowed status transitions, POST/PATCH field whitelists, and `agent_feedback` schema. Replaces ad-hoc duplication across the HTTP API, MCP server, and state layer.
9+
- **`src/server/schemas.ts`** — zod schemas for every HTTP body and MCP tool argument set. The HTTP API and MCP server now parse at the boundary via `schema.safeParse()` instead of ad-hoc type checks.
10+
- **`AnnotaskServer.flush()`** — drain pending task/perf writes before shutdown. `startStandaloneServer`'s `close()` is now async and flushes before closing.
11+
- **`scripts/copy-vendor.mjs`** — replaces the inline `cp` chain in `pnpm build:vendor`. Fails the build loudly if an upstream package renames or drops a vendored file.
12+
- **`src/shell/utils/routes.ts`** + **`src/shell/composables/useLocalStorageRef.ts`** — shared helpers that replace per-site `normalizeRoute` copies and `ref + watch + localStorage` triplets previously inlined in `App.vue`.
13+
- **`docs/REVIEWING.md`** — PR review checklist with the invariants the codebase needs to keep.
14+
- **`CHANGELOG.md`** (this file) and a **Release Process** section in `CONTRIBUTING.md`.
15+
- Unit tests for `createProjectState` (concurrent PATCH, concurrent delete+update, screenshot cleanup, path-traversal refusal, disk round-trip), every `validation.ts` export, and `normalizeRoute`. Test count: 128 → 160.
16+
- Optional `code` field on API error responses (keeps legacy `error: string` intact).
17+
- Zod (`^4.3.6`) as a runtime dependency.
18+
19+
### Changed
20+
- **BREAKING (internal):** `ProjectState.addTask/updateTask/deleteTask` are now `async`. Callers inside annotask already `await` them; any external consumer of `createProjectState` must await.
21+
- Task mutations serialize through a single in-process mutex (`withTaskLock`). Concurrent PATCH requests to disjoint fields of the same task no longer lose writes.
22+
- Screenshot unlinks (on `accepted` or delete) are chained after the successful write. A failed write no longer leaves an orphan screenshot.
23+
- Screenshot upload filenames use `crypto.randomBytes(8)` (16 hex chars) instead of `Math.random().toString(36).slice(2,7)` — no more 5-char collision window.
24+
- `fs.watch` on `.annotask/` no longer invalidates the task cache on our own atomic writes (tracked via `selfWriteUntil` window).
25+
- WebSocket server enforces a 1 MiB per-frame size cap (`maxPayload`).
26+
- `Cache-Control` on screenshot responses is now `private` instead of `public`.
27+
- `server.json` is written with mode `0o600` — other users on shared machines can no longer read the live PID + port.
28+
- Component scanner caches now carry a 5-minute TTL and coalesce concurrent scans through a single in-flight promise.
29+
- MCP batch dispatch isolates per-item exceptions so one bad request no longer truncates the response array.
30+
- MCP `annotask_update_task` runs new `agent_feedback` entries through the shared zod schema instead of a tool-local duplicate.
31+
- MCP server version is baked from `package.json` at build time (`__ANNOTASK_VERSION__`) instead of a hardcoded `'0.0.29'`.
32+
- `iframeBridge` no longer posts to `targetOrigin='*'`. The shell derives the iframe's origin from `iframe.src`/`contentWindow.location.origin` and refuses to send until it knows a concrete origin. Request IDs are now monotonic.
33+
- `useAnnotationRects` and `useSelectionModel` rAF loops skip work while `document.hidden` is true.
34+
- `useErrorMonitor` caps its buffer at 256 entries (oldest discarded, dedup keys tracked).
35+
- `usePerfMonitor.perfFindings` caps at 128 entries (worst severities kept).
36+
- `package.json`: added `"sideEffects": false` (tree-shaking); excluded sourcemaps from the published tarball; pinned `axe-core` and `html2canvas-pro` to exact versions since both are vendored into `dist/vendor/`.
37+
- `.gitignore`: ignores `.claude/worktrees/`.
38+
39+
### Removed
40+
- `src/shell/composables/useThemeMode.ts` — the deprecated wrapper around `useShellTheme` was unused at any call site.
41+
42+
### Security
43+
- `annotask_get_screenshot` MCP tool now routes `task.screenshot` through `isSafeScreenshot` before path construction, closing a path-traversal hole for maliciously constructed task records.
44+
- Added `originMatchesPort` helper for endpoints that want a stricter same-port origin check (available for future hardening; not wired by default since the shell can be served from a different port than the user's app).
45+
46+
### Breaking
47+
48+
- **API error shape.** Responses now use `{ error: { code, message } }` instead of a bare `error: string`. The `code` is one of `invalid_json`, `body_too_large`, `body_not_object`, `validation_failed`, `invalid_transition`, `forbidden_origin`, `not_found`. `PATCH`/`DELETE` on a missing task now returns HTTP 404 (previously 200 with a magic `{error: 'Task not found'}` string). The CLI and API tests have been updated accordingly.
49+
50+
### Extracted
51+
52+
- `src/shell/composables/useChangeHistory.ts``doUndo`, `doClearChanges`, `commitChangesAsTask`, and `selectionChanges` moved out of `App.vue`. App.vue is now 2012 lines (from 2142 at the start of the cleanup).
53+
- `src/shell/components/PerfFindingDetail.vue` + `PerfResourceTables.vue` — pulled out of `PerfTab.vue`, which is now 199 lines (from 463).
54+
- `src/shell/components/TaskAgentFeedback.vue`, `TaskInteractionHistory.vue`, `TaskJsonView.vue` — pulled out of `TaskDetailModal.vue`, which is now 791 lines (from 966).
55+
- `src/shell/components/ThemeLibrariesTab.vue`, `ThemeAddTokenForm.vue` — pulled out of `ThemePage.vue`, which is now 819 lines (from 865). The add-token form, previously duplicated 5× with minor placeholder variations, is now one component reused across colors / families / scale / spacing / radius.

CLAUDE.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ annotask watch # Live stream changes via WebSocket
5353
annotask update-task <id> --status=<status> # Update task status
5454
annotask screenshot <id> # Download a task's screenshot
5555
annotask init-skills # Install agent skills into project
56+
annotask init-mcp # Write editor MCP config (--editor=claude|cursor|vscode|windsurf|all)
5657
annotask mcp # Start MCP stdio server (proxies to dev server)
5758
```
5859

@@ -115,9 +116,8 @@ The shell has a VS Code-style theme system with 18 built-in themes and custom th
115116

116117
- `src/shell/themes/types.ts``ShellThemeColors` (62 vars), `ShellTheme` interface, `THEME_COLOR_KEYS` array
117118
- `src/shell/themes/builtin.ts` — 18 built-in theme definitions with `deriveDefaults()` helper
118-
- `src/shell/composables/useShellTheme.ts` — Core composable: applies themes at runtime via `style.setProperty()`, handles localStorage persistence, system preference detection, custom theme CRUD, migration from legacy `useThemeMode`
119+
- `src/shell/composables/useShellTheme.ts` — Core composable: applies themes at runtime via `style.setProperty()`, handles localStorage persistence, system preference detection, custom theme CRUD, and a one-shot migration from the legacy `annotask:themeMode` key
119120
- `src/shell/components/ShellThemeEditor.vue` — Full-screen custom theme creator with grouped color pickers and live preview
120-
- `src/shell/composables/useThemeMode.ts`**Deprecated** thin wrapper around `useShellTheme`
121121

122122
### How themes are applied
123123

CONTRIBUTING.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,3 +64,19 @@ No new features should be added until the stabilization exit criteria from `plan
6464
4. No Annotask instrumentation in production builds
6565
5. Core flow covered by automated tests + CI
6666
6. README explains what works, what's experimental, and how to verify
67+
68+
## Release Process
69+
70+
Versions are SemVer. The source of truth is `package.json`; the MCP server and shell read it at build time via `__ANNOTASK_VERSION__`.
71+
72+
1. **Write the changelog entry first.** Edit `CHANGELOG.md` under `## [Unreleased]`. Categorize under Added / Changed / Fixed / Security / Removed.
73+
2. **Verify.** `pnpm typecheck && pnpm test && pnpm build`. The build step runs `scripts/copy-vendor.mjs`, which fails loudly if the vendored upstream files have moved.
74+
3. **Bump.** Edit `package.json` `version` and promote `## [Unreleased]` to `## [x.y.z] — YYYY-MM-DD` in `CHANGELOG.md`. Commit `chore(release): vx.y.z`.
75+
4. **Tag + publish.** `git tag vx.y.z && git push --follow-tags`. `npm publish` from a clean working tree.
76+
5. **Post-release.** Reopen a fresh `## [Unreleased]` block in `CHANGELOG.md` and commit.
77+
78+
Never bump `package.json` without a changelog entry in the same commit — the review checklist (`docs/REVIEWING.md`) pins this.
79+
80+
## Reviewing PRs
81+
82+
See `docs/REVIEWING.md` for the review checklist. Core rules: one canonical definition per rule, no business logic in `App.vue`, shared-state composables use singleton-with-refcount, every boundary validates with zod, every rAF loop has a `document.hidden` guard, size budgets hold.

docs/REVIEWING.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# Reviewing Annotask PRs
2+
3+
A short checklist. Reviewers should skim this and block PRs that violate it without a clear justification in the description.
4+
5+
## Always-on rules
6+
7+
- [ ] **One canonical definition per rule.** Screenshot filename regex, status transitions, PATCH field whitelists, rect-to-shell coordinate transforms, wire schemas — each has exactly one home (`src/server/validation.ts`, `src/server/schemas.ts`, or `src/shell/utils/`). New duplicates are rejected.
8+
- [ ] **No business logic in `App.vue`.** Orchestration only: wire composables, pass props, forward events. New logic goes into a composable under `src/shell/composables/`.
9+
- [ ] **Shared-state composables are singletons with refcount**, not module-level globals. Use VueUse's `createSharedComposable` or the local idiom — never attach listeners at module scope without a matching teardown.
10+
- [ ] **Every boundary validates.** HTTP bodies, MCP tool args, WS messages, `postMessage` payloads all parse through a schema (`zod` for server/MCP; a hand-rolled validator for the bridge).
11+
- [ ] **Every rAF loop has a `document.hidden` guard** and an explicit teardown. Reviewers verify both.
12+
- [ ] **No `any` in `src/schema.ts` or `src/shared/bridge-types.ts`.** These are the shared contracts; drift here causes silent cross-process breakage.
13+
- [ ] **Tests required for:** every new MCP tool, every new HTTP endpoint, every task state transition rule.
14+
- [ ] **Pin what you vendor.** Anything copied into `dist/vendor/` has an exact-version pin in `package.json` and a guard in `scripts/copy-vendor.mjs`.
15+
- [ ] **Bump + changelog in the same commit** as the user-visible change. CI (or reviewer) verifies the top `CHANGELOG.md` heading matches the new `package.json` version.
16+
17+
## Size budgets (soft caps)
18+
19+
Files over budget either ship with an extraction plan in the PR description or get split in the same PR.
20+
21+
| File / type | Budget |
22+
|---|---|
23+
| `src/shell/App.vue` | ≤ 800 lines (currently over — trending down) |
24+
| Any Vue component | ≤ 500 lines |
25+
| Any composable | ≤ 200 lines |
26+
27+
## Security-sensitive changes
28+
29+
Extra scrutiny for PRs that touch:
30+
31+
- `src/server/origin.ts` — CORS / origin checks
32+
- `src/server/validation.ts` + `src/server/schemas.ts` — wire contract
33+
- `src/server/state.ts` — task store serialization
34+
- `src/shell/services/iframeBridge.ts` — postMessage origin handling
35+
- `scripts/copy-vendor.mjs` + vendored package pins
36+
37+
## How to check
38+
39+
```bash
40+
pnpm typecheck
41+
pnpm test # unit
42+
pnpm test:e2e # end-to-end (Playwright)
43+
pnpm build # runs the vendor-guard script
44+
wc -l src/shell/App.vue # size budget
45+
```

package.json

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
"description": "Visual UI design tool for web apps. Make changes in the browser and generate structured reports that AI agents can apply to source code. Works with Vue, React, Svelte, SolidJS, and plain HTML via Vite or Webpack.",
55
"license": "MIT",
66
"type": "module",
7+
"sideEffects": false,
78
"module": "./dist/index.js",
89
"types": "./dist/index.d.ts",
910
"exports": {
@@ -25,13 +26,14 @@
2526
},
2627
"files": [
2728
"dist",
28-
"skills"
29+
"skills",
30+
"!dist/**/*.map"
2931
],
3032
"scripts": {
3133
"build": "pnpm build:shell && pnpm build:plugin && pnpm build:vendor",
3234
"build:shell": "vite build --config vite.config.shell.ts",
3335
"build:plugin": "tsup",
34-
"build:vendor": "mkdir -p dist/vendor && cp node_modules/axe-core/axe.min.js dist/vendor/axe-core.min.js && cp node_modules/html2canvas-pro/dist/html2canvas-pro.min.js dist/vendor/html2canvas.min.js",
36+
"build:vendor": "node scripts/copy-vendor.mjs",
3537
"dev:shell": "vite --config vite.config.shell.ts",
3638
"dev:vue-vite": "pnpm --filter @annotask/playground-vue-vite dev",
3739
"dev:vue-webpack": "pnpm --filter @annotask/playground-vue-webpack dev",
@@ -70,7 +72,8 @@
7072
}
7173
},
7274
"dependencies": {
73-
"ws": "^8.20.0"
75+
"ws": "^8.20.0",
76+
"zod": "^4.3.6"
7477
},
7578
"devDependencies": {
7679
"@playwright/test": "^1.58.2",
@@ -79,9 +82,9 @@
7982
"@types/prismjs": "^1.26.6",
8083
"@types/ws": "^8.18.1",
8184
"@vitejs/plugin-vue": "^5.0.0",
82-
"axe-core": "^4.11.1",
85+
"axe-core": "4.11.1",
8386
"dompurify": "^3.3.3",
84-
"html2canvas-pro": "^2.0.2",
87+
"html2canvas-pro": "2.0.2",
8588
"js-yaml": "^4.1.1",
8689
"jsdom": "^29.0.1",
8790
"marked": "^17.0.5",

playgrounds/react-vite/.mcp.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"mcpServers": {
3+
"annotask": {
4+
"type": "http",
5+
"url": "http://localhost:5174/__annotask/mcp"
6+
}
7+
}
8+
}

playgrounds/react-vite/annotask-logo.svg

Lines changed: 1 addition & 1 deletion
Loading

playgrounds/react-vite/src/components/Hero.tsx

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ export default function Hero() {
1414

1515
<p className={styles.subhead}>
1616
Click any element in your dev server. Edit it. Annotate it. Annotask captures the
17-
intent and your coding agent applies the change. No screenshots, no Slack threads,
18-
no guesswork.
17+
intent and your coding agent applies the change.
1918
</p>
2019

2120
<div className={styles.ctaRow}>
@@ -46,42 +45,6 @@ export default function Hero() {
4645
</li>
4746
</ul>
4847

49-
<div className={styles.mockup} aria-hidden="true">
50-
<div className={styles.mockupChrome}>
51-
<span className={styles.dot} style={{ background: '#ff5f57' }} />
52-
<span className={styles.dot} style={{ background: '#febc2e' }} />
53-
<span className={styles.dot} style={{ background: '#28c840' }} />
54-
<span className={styles.url}>localhost:5174</span>
55-
</div>
56-
<div className={styles.mockupBody}>
57-
<div className={styles.mockupSidebar}>
58-
<div className={styles.sidebarHeader}>Tasks</div>
59-
<div className={styles.sidebarItem}>
60-
<span className={styles.statusPin}>📌</span>
61-
Make hero CTA larger
62-
</div>
63-
<div className={styles.sidebarItem}>
64-
<span className={styles.statusReview}></span>
65-
Soften card shadows
66-
</div>
67-
<div className={styles.sidebarItem}>
68-
<span className={styles.statusInProgress}></span>
69-
Fix focus ring contrast
70-
</div>
71-
</div>
72-
<div className={styles.mockupCanvas}>
73-
<div className={styles.canvasHero}>
74-
<div className={styles.canvasHeading}>Your live app</div>
75-
<div className={styles.canvasButton}>Click me</div>
76-
<div className={styles.selectionRing} />
77-
<div className={styles.pinNote}>
78-
<span className={styles.pinDot}>1</span>
79-
Make this 24px taller
80-
</div>
81-
</div>
82-
</div>
83-
</div>
84-
</div>
8548
</div>
8649
</section>
8750
)

0 commit comments

Comments
 (0)