@brmbh/tack 0.0.0-stage → 0.1.0

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 (3) hide show
  1. package/README.md +182 -2
  2. package/bin/tack.cjs +23 -0
  3. package/package.json +34 -5
package/README.md CHANGED
@@ -1,3 +1,183 @@
1
- # Temporary Holding Version
1
+ # tack
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ Point at your running site, say what should change, and hand it to your coding agent.
4
+
5
+ `tack` puts a local proxy in front of your dev server and adds a small toolbar
6
+ to every page. Click an element to comment on it, or click any text and retype
7
+ it in place. Each note stores the page, a verified CSS selector, the element's
8
+ text and HTML, and the **source file:line it most likely comes from**. Your
9
+ agent (Claude Code, Cursor, Codex…) reads the notes, changes the code, and
10
+ resolves them. The pin leaves the page once it's done; the note and the agent's answer stay in the list (`L` → All).
11
+
12
+ Nothing gets installed in your project, and it works with any framework: static
13
+ HTML, Vite, Next, Astro, WordPress, PHP. It runs only on your machine.
14
+
15
+ ## Install
16
+
17
+ **With your agent** (Claude Code, Codex, Cursor, Gemini CLI, Copilot…): paste this into it, in your project.
18
+
19
+ ```text
20
+ Set me up to use Tack in this project. Run `npx -y @brmbh/tack@latest init` in the project root;
21
+ it installs the tack skill for this agent. Read the installed skill (.claude/skills/tack/SKILL.md
22
+ or .agents/skills/tack/SKILL.md), then start a tack session on my dev server and give me the URL.
23
+ ```
24
+
25
+ **From the command line**, like any other tool:
26
+
27
+ ```bash
28
+ npx @brmbh/tack init # in your project: installs the agent skill
29
+ npx @brmbh/tack --target http://localhost:3000
30
+ npm i -g @brmbh/tack # optional: a `tack` command everywhere
31
+ ```
32
+
33
+ Needs Node 18+ for `npx`, nothing else: tack ships as a standalone binary for macOS (arm64, x64),
34
+ Linux (x64, arm64) and Windows (x64). `tack init` writes the skill to `.claude/skills/tack/`
35
+ (Claude Code) and `.agents/skills/tack/` (Codex, Cursor, Gemini CLI, GitHub Copilot, Amp, Cline
36
+ and others). `--global` puts it in your home folder instead. Then tell your agent
37
+ "start tack", and later "do the tack notes".
38
+
39
+ ## Use
40
+
41
+ ```bash
42
+ tack --target http://localhost:3000 # → http://localhost:7777
43
+ ```
44
+
45
+ Open the printed URL and browse as usual.
46
+
47
+ | Key | |
48
+ |---|---|
49
+ | `C` | Comment mode: click an element and type. Select text first to quote it. |
50
+ | `E` | Edit mode: click text, retype it, `Enter` saves (`Shift+Enter` = newline), `Esc` cancels. |
51
+ | `L` | Feedback list (all pages). Copy button = Markdown for any agent. |
52
+ | `H` | Show/hide pins. |
53
+ | `Esc` | Leave the current mode. |
54
+
55
+ Pins show open notes only (black). A dot on a pin means the agent asked a question. Done notes leave the canvas and stay in the list (`L` → All), with the agent's answer.
56
+
57
+ ## Hand it to your agent
58
+
59
+ Feedback is written to `<project>/.tack/`, as `notes.json` (the source of truth) and `FEEDBACK.md`
60
+ (readable by anything). Three ways for an agent to pick it up:
61
+
62
+ 1. **Skill** (Claude Code, Codex, Cursor and every agent that reads `.agents/skills`): run
63
+ `tack init` in the project, or `tack init --global`, then tell the agent "do the tack notes".
64
+ 2. **MCP**: `claude mcp add tack -- tack mcp`. Tools: `tack_list`, `tack_get`,
65
+ `tack_resolve`, `tack_reply`.
66
+ 3. **CLI** (any agent that can run shell commands):
67
+ ```bash
68
+ tack list --md
69
+ tack resolve 3 -m "Shortened the lead, index.html:1370"
70
+ tack reply 4 -m "Bolder as in weight 700, or bigger?"
71
+ ```
72
+
73
+ Commit `.tack/` if you want the review history in git, or add it to `.gitignore`.
74
+
75
+ ## Console bridge
76
+
77
+ The proxy also sees what the page does, so the agent doesn't have to ask you to open DevTools.
78
+ A tiny script at the top of `<head>` captures `console.*`, uncaught errors (with
79
+ `file:line:col`), unhandled rejections, failed `fetch` calls and broken images/scripts. The
80
+ proxy adds upstream 4xx/5xx responses and **missing files that the dev server answered with
81
+ its HTML fallback** (Vite serves `index.html` with status 200 for any unknown path, so a
82
+ missing `.json` or `.png` looks like success). Everything goes to `.tack/console.jsonl`.
83
+
84
+ ```bash
85
+ tack logs --md # last 50 entries
86
+ tack logs --level problems --since 5m # errors, warnings, network, proxy
87
+ tack logs --page /pricing/ --md
88
+ ```
89
+
90
+ MCP: `tack_console`. Every new note also carries the problems from **its own page load**,
91
+ with repeats collapsed. `tack --verbose` prints every proxied request.
92
+
93
+ ## AI suggestions (optional)
94
+
95
+ In edit mode (`E`), the popover has **Suggest**: say what the new versions should do (or leave it
96
+ empty) and tack streams a few versions of the text. Pick one, it appears on the page, `Enter`
97
+ saves it as a normal edit note for your agent.
98
+
99
+ Any model, your own key, via the [Vercel AI SDK](https://ai-sdk.dev). Set it up in the
100
+ overlay: the **gear icon** opens AI settings. Pick where the model runs (any OpenAI-compatible
101
+ endpoint such as Ollama Cloud, OpenRouter, LM Studio or a local Ollama, or the Vercel AI
102
+ Gateway), paste your key, name the model, **Save & test connection**.
103
+
104
+ The key is stored on your computer in `~/.config/tack/settings.json` (mode 600), for all
105
+ projects. Never in the project, never in the browser; the overlay can write it but only ever
106
+ reads back `••••` plus the last four characters. Environment variables win over the file:
107
+
108
+ ```bash
109
+ TACK_BASE_URL=https://ollama.com/v1 TACK_API_KEY=… TACK_MODEL=minimax-m3:cloud # OpenAI-compatible
110
+ AI_GATEWAY_API_KEY=… TACK_MODEL=anthropic/claude-sonnet-5.5 # AI Gateway
111
+ ```
112
+
113
+ Models without JSON-schema output answer in plain lines, which tack parses as they stream.
114
+
115
+ The model gets the element, its page, the source around the likely file:line and your
116
+ `AGENTS.md` / `CLAUDE.md` / `README.md`, and can **read** more itself (list, search, read files
117
+ in the project). Point it at notes outside the repo with `.tack/config.json`:
118
+
119
+ ```json
120
+ { "knowledge": ["~/notes/my-project"] }
121
+ ```
122
+
123
+ It never writes files, and refuses `.env*`, keys and `.git/`. What it reads is sent to the
124
+ model provider you chose.
125
+
126
+ ## Options
127
+
128
+ ```
129
+ --target <url> dev server to proxy (required)
130
+ --port <n> proxy port (default 7777)
131
+ --dir <path> project root: where .tack/ lives and source hints are searched (default cwd)
132
+ --store <path> put notes somewhere else
133
+ --host <addr> bind address (default 127.0.0.1)
134
+ ```
135
+
136
+ ## How it works
137
+
138
+ - **Proxy** (`src/server.ts`): forwards every request to the target, injects
139
+ `/__tack/overlay.js` into HTML responses, rewrites absolute links to the dev server so
140
+ you stay inside the proxy, strips CSP headers, and pipes websockets through so hot
141
+ reload keeps working.
142
+ - **Same origin**: the proxy serves the page from its own address, so the overlay can
143
+ read the real DOM, which a screenshot tool can't. Selectors come from the actual
144
+ element and are checked for uniqueness when the note is captured.
145
+ - **Overlay** (`overlay/overlay.ts`): vanilla TypeScript in a shadow root, bundled by
146
+ Bun when requested (prebuilt into the release binary). The site's CSS can't reach it.
147
+ - **Source hints** (`src/hints.ts`): a deterministic text search over the project
148
+ (git-tracked files, minus build output). It still matches when text is wrapped across
149
+ lines, contains HTML entities or smart quotes, or is split by inline tags. It falls
150
+ back to `id`, then class names.
151
+ - **MCP** (`src/mcp.ts`): about 100 lines of JSON-RPC over stdio, no SDK.
152
+ - **Stable targets**: clicks on anonymous wrappers inside a heading or paragraph (split-text
153
+ lines and words, styling spans) snap to the heading/paragraph. Runtime classes
154
+ (`is-*`, `split-*`, `line`, `word`…) never go into selectors, and a pin is only drawn if the
155
+ element's tag and text still match. Otherwise tack looks for the element by text.
156
+ - **Redirects** pass through with their `Location` (rewritten when it points back at the
157
+ target). A redirect to another host (apex → `www`, locale subdomains) leaves the proxy: use
158
+ the final URL as `--target`.
159
+ - **Revalidation**: conditional request headers are stripped, so every request gets a full
160
+ response. A passed-through 304 had turned into an empty 200 (a broken web manifest).
161
+
162
+ ## Develop
163
+
164
+ ```bash
165
+ bun install
166
+ bun test
167
+ bunx tsc --noEmit
168
+ ```
169
+
170
+ Requires [Bun](https://bun.sh) ≥ 1.2 to develop; users get a compiled binary.
171
+
172
+ ## Release
173
+
174
+ ```bash
175
+ bun run build # dist/npm/tack + dist/npm/tack-<platform> (5 binaries)
176
+ npm login # once, account with publish rights on @brmbh
177
+ for d in dist/npm/tack-*; do (cd "$d" && npm publish --access public); done
178
+ (cd dist/npm/tack && npm publish --access public) # last: it depends on the binaries
179
+ ```
180
+
181
+ Bump `version` in `package.json` first; every package gets the same version.
182
+ Known limit: the darwin-x64 binary crashes under Rosetta (x64 Node on Apple Silicon), real
183
+ Intel Macs are fine. Use an arm64 Node on Apple Silicon.
package/bin/tack.cjs ADDED
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env node
2
+ // Runs the tack binary that npm installed for this platform (@brmbh/tack-<os>-<arch>).
3
+ const { spawn } = require('node:child_process')
4
+
5
+ const os = process.platform === 'win32' ? 'windows' : process.platform
6
+ const name = `@brmbh/tack-${os}-${process.arch}`
7
+ let bin
8
+ try {
9
+ bin = require.resolve(`${name}/bin/tack${process.platform === 'win32' ? '.exe' : ''}`)
10
+ } catch {
11
+ console.error(`tack: no binary for ${process.platform}-${process.arch} (${name} is not installed).`)
12
+ console.error('Supported: macOS arm64/x64, Linux x64/arm64, Windows x64. If yours is one, reinstall without --no-optional.')
13
+ process.exit(1)
14
+ }
15
+
16
+ const child = spawn(bin, process.argv.slice(2), { stdio: 'inherit' })
17
+ // The proxy runs until stopped: hand stop signals to it instead of orphaning it.
18
+ for (const sig of ['SIGINT', 'SIGTERM', 'SIGHUP']) process.on(sig, () => child.kill(sig))
19
+ child.on('error', (err) => {
20
+ console.error(`tack: could not start ${bin}: ${err.message}`)
21
+ process.exit(1)
22
+ })
23
+ child.on('exit', (code, signal) => process.exit(code ?? (signal ? 1 : 0)))
package/package.json CHANGED
@@ -1,6 +1,35 @@
1
1
  {
2
- "name": "@brmbh/tack",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
2
+ "name": "@brmbh/tack",
3
+ "version": "0.1.0",
4
+ "description": "Point at your running site, comment or edit text in place, and hand the feedback to your coding agent.",
5
+ "license": "UNLICENSED",
6
+ "homepage": "https://github.com/brmbh/tack",
7
+ "author": "Jan Brombach",
8
+ "keywords": [
9
+ "feedback",
10
+ "coding-agent",
11
+ "claude-code",
12
+ "cursor",
13
+ "codex",
14
+ "proxy",
15
+ "review",
16
+ "skills"
17
+ ],
18
+ "bin": {
19
+ "tack": "bin/tack.cjs"
20
+ },
21
+ "files": [
22
+ "bin",
23
+ "README.md"
24
+ ],
25
+ "engines": {
26
+ "node": ">=18"
27
+ },
28
+ "optionalDependencies": {
29
+ "@brmbh/tack-darwin-arm64": "0.1.0",
30
+ "@brmbh/tack-darwin-x64": "0.1.0",
31
+ "@brmbh/tack-linux-x64": "0.1.0",
32
+ "@brmbh/tack-linux-arm64": "0.1.0",
33
+ "@brmbh/tack-windows-x64": "0.1.0"
34
+ }
35
+ }