pointer-feedback 0.1.4 → 0.2.1

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 +230 -5
  2. package/dist/cli.js +2886 -1148
  3. package/package.json +11 -5
package/README.md CHANGED
@@ -18,7 +18,8 @@ npx pointer <command> [options]
18
18
 
19
19
  ### `pointer init`
20
20
  Set up the feedback widget in your project.
21
- - Scaffolds `.pointer/config.json`, `.pointer/credentials.env.example`, `.pointer/stack.json`.
21
+ - Scaffolds `.pointer/config.json`, `.pointer/stack.json`, and authenticates — saving the API key
22
+ to this machine's **global credential store** by default (see **Authentication** below).
22
23
  - Detects the application framework (Vite, Next.js, Angular, static HTML).
23
24
  - Injects the `<pointer-feedback>` web component.
24
25
 
@@ -26,6 +27,224 @@ Set up the feedback widget in your project.
26
27
  pointer init --server https://api.pointer.moamen.work --key ptr_... --project my-project
27
28
  ```
28
29
 
30
+ #### Committed vs. gitignored
31
+
32
+ `init` only expects these files to be shared via git: **`.pointer/config.json`**, **`.pointer/stack.json`**,
33
+ and — in a multi-project repo (see **Monorepos** below) — every **`.pointer/projects/<key>.stack.json`**.
34
+ Everything else (`pointer.sh`, `manifest.json`, and — only if you opted into `--scope repo` —
35
+ `credentials.env`) is derived or per-machine and stays gitignored, along with every skill layout the
36
+ CLI can write: `.claude/skills/pointer-init/`, `.claude/skills/pointer-feedback/` (Claude Code),
37
+ `.cursor/rules/pointer-init.md`, `.cursor/rules/pointer-feedback.md` (Cursor),
38
+ `.windsurf/rules/pointer-init.md`, `.windsurf/rules/pointer-feedback.md` (Windsurf),
39
+ `.agents/skills/pointer-init/`, `.agents/skills/pointer-feedback/` (the Agent Skills standard layout
40
+ used by `other`/`antigravity`, and symlinked into from the three tools above), and — if you passed
41
+ `--skills-dir <dir>` — `<dir>/pointer-init/`, `<dir>/pointer-feedback/`. A repo that has not re-run
42
+ `init`/`update` since 2026-09-16 may still carry the older `.agents/pointer-init/`,
43
+ `.agents/pointer-feedback/` layout (pre-dating the `.agents/skills/...` convention); it stays
44
+ gitignored too, and the next install removes it. `init` manages the `.gitignore` block for you
45
+ (`upsertGitignore`), migrating an older repo's block — including one that still re-included
46
+ `pointer.sh` or the now-removed `credentials.env.example`, or predates any of the paths above —
47
+ automatically and idempotently. Both `init` (every mode, including a join) and `update` also delete
48
+ an actual leftover `.pointer/credentials.env.example` or `.pointer/.token_cache` file still on disk
49
+ from an older install — never `.pointer/credentials.env` itself.
50
+
51
+ That split is why a clone of an already-configured repo has `config.json`/`stack.json` (they were
52
+ committed) but is missing the skills and `pointer.sh` (they were never committed) — see **join
53
+ mode** below and `pointer update`. The API key never needs cloning at all, wherever it was saved —
54
+ see **Authentication**.
55
+
56
+ #### Join mode: re-running `init` in an already-configured repo
57
+
58
+ If `.pointer/config.json` already has a `server` **and** a `project` — because someone already ran
59
+ `init` here and committed the config — a further `init` run is a **join**, not a first install:
60
+
61
+ - Server, project, AI tool and delivery are all read back from the committed config; you are asked
62
+ for **nothing but your API key** (`--key`, or the interactive prompt) —
63
+ and not even that if one already resolves from the environment, this repo, or (the common case,
64
+ once you've run `pointer login` once) **this machine's global credential store**. See
65
+ **Authentication** below.
66
+ - Nothing is injected — the `<pointer-feedback>` snippet (or, for `delivery: "extension"`, nothing)
67
+ is already in the app's committed source.
68
+ - The skills and `.pointer/pointer.sh` ARE (re-)installed, since they are gitignored and this
69
+ clone/machine has none yet.
70
+ - `.pointer/stack.json` is only regenerated if it is missing — it is committed and rarely differs
71
+ machine to machine.
72
+
73
+ ```bash
74
+ # In a repo that already has .pointer/config.json:
75
+ pointer init --yes --key ptr_... # no --project/--create needed
76
+ ```
77
+
78
+ `--json`'s output gains a `mode` field: `"install"` for a first install, `"join"` for the above. The
79
+ human summary prints `Joined <product> project <key> as <you>` instead of `<product> is set up`.
80
+
81
+ #### Environments: managed in the dashboard, not asked here
82
+
83
+ `init` never asks which environment(s) an app runs in, and never writes `environment`/
84
+ `environments` to `.pointer/config.json`. Environments and their per-project activation live in the
85
+ dashboard, next to the project's URLs — the widget (and the Chrome extension) resolve the
86
+ environment for a comment from the page's own URL at runtime, and a signed-in reviewer can switch it
87
+ from the toolbar. The injected snippet correspondingly never carries a fixed `environment` attribute
88
+ by default.
89
+
90
+ `--environment <list>` (comma-separated: `local`, `staging`, `production`) remains as a deliberate,
91
+ **optional** opt-in: given, it activates the project for exactly those environments (server-side,
92
+ additive — it never deactivates one you didn't name) and pins the injected snippet's `environment`
93
+ attribute to the first of them in canonical order. Omitted, nothing about environments is asked,
94
+ activated, or recorded. A config written by a CLI from before this change may still carry
95
+ `environment`/`environments` — those fields are read for backward compatibility (deprecated, never
96
+ written any more).
97
+
98
+ #### Delivery: embed vs. extension
99
+
100
+ Interactively, `init` first asks **how reviewers will open the feedback widget**:
101
+
102
+ - **Embed it in this app** (default, recommended) — today's behaviour: the `<pointer-feedback>`
103
+ loader is injected into your app.
104
+ - **Chrome extension only** — no code change at all. Each reviewer installs the extension, signs
105
+ in, opens the app, picks the project from the extension popup and clicks **Activate**; the
106
+ widget is injected by the extension rather than by your app's own code.
107
+
108
+ Pass `--delivery embed` or `--delivery extension` to answer non-interactively (`--yes`/`--json`
109
+ default to `embed` and skip the question; `--no-inject` also stays `embed`, it just skips
110
+ injection). The choice is recorded in `.pointer/config.json` as `delivery`, and `pointer doctor`'s
111
+ widget check is mode-aware — it reports `ok` for an extension install instead of a false "widget
112
+ not found". The Chrome Web Store URL shown at the end of an extension-mode `init` (and by `doctor`)
113
+ comes from the server (`GET /api/branding`, a super-admin setting) at run time, never hard-coded.
114
+
115
+ #### Monorepos
116
+
117
+ A repo with many independently-deployed apps (an Nx workspace, or any monorepo) can register more
118
+ than one Pointer project against one `.pointer/config.json`. Single-project config is unchanged;
119
+ multi-project config drops the top-level `project` in favour of a `projects` map — there is **no
120
+ default project** in this mode:
121
+
122
+ ```json
123
+ {
124
+ "server": "https://api.example.com",
125
+ "aiTool": "claude-code",
126
+ "delivery": "extension",
127
+ "cliVersion": "0.2.0",
128
+ "projects": {
129
+ "tuwaiq-profile": {
130
+ "path": "apps/profile",
131
+ "htmlPath": "apps/profile/src/index.html",
132
+ "delivery": "embed"
133
+ },
134
+ "tuwaiq-landing": { "path": "apps/landing" }
135
+ }
136
+ }
137
+ ```
138
+
139
+ Repo-level fields (`server`, `aiTool`, `skillsDir`, `cliVersion`, `delivery`) apply to every app
140
+ unless a project entry overrides them. Per-project fields: `path` (repo-relative app directory,
141
+ required), `htmlPath`, `delivery`. (`environment`/`environments` may still appear on an entry written
142
+ by an older CLI — deprecated, read for backward compatibility only; environments now live in the
143
+ dashboard, see above.)
144
+
145
+ **Resolution order**, identical for every command that touches a project: `--project <key>` → the
146
+ project whose `path` contains the current directory (the CLI walks up from `cwd` to the nearest
147
+ `.pointer/config.json` to find the repo root first, so this works from inside `apps/<x>` too) → the
148
+ only configured project → otherwise "every project" for commands that support it (`list`, `apply`,
149
+ `status --deployed`), or exit 2 with `Several projects configured — pass --project <key> (one of: a, b, c)`.
150
+
151
+ - **`list`/`apply`/`apply --plan`**: with no single project resolvable, run for **every** configured
152
+ project. `list --json` returns `[{ project, comments: [...] }]`; `apply`'s prompt gets one section
153
+ per project, headed with its key and `path`, so the AI edits the right app. `apply --mark`/`--fail`
154
+ act on a comment id (unique server-wide) and need no project; `--mark all` does, since it commits
155
+ the whole pending queue — pass `--project` or run from inside that app.
156
+ - **`doctor`**: `project`/`widget`/`stack`/`source-map` run once per project, the key folded into
157
+ the message (`[tuwaiq-profile] Widget found in apps/profile/src/index.html`); `config`/`server`/
158
+ `meta`/`clock`/`key`/`skills`/`stale`/`gitignore` run once for the whole repo.
159
+ - **`mcp`**: accepts `--project`; without it, every project-scoped tool call needs a `project`
160
+ argument once more than one project applies — call `pointer_list_projects` to see the choices.
161
+ - **`status --deployed`**: per project; with no single project resolvable, reports the build
162
+ against every configured one.
163
+ - **`.pointer/pointer.sh`** (the no-Node fallback): `-p <key>` (or `POINTER_PROJECT`) picks the
164
+ project; with neither, in a multi-project repo, it prints the configured keys and exits 2.
165
+ - Each app's own `.pointer/projects/<key>.stack.json` replaces the single `.pointer/stack.json` —
166
+ committed, exactly like `stack.json` is today.
167
+
168
+ **Setting it up**: `pointer init --path apps/<dir> --project <key> [--create "Name"]` adds (or
169
+ updates) one app; run it again with a different `--path`/`--project` to add another. The first time
170
+ this runs against a single-project config, that project is migrated into `projects` (best guess at
171
+ its `path` from the recorded `htmlPath`, or `.` if there is none — `init` warns you to check it). In
172
+ an Nx workspace (`nx.json` at the root), an interactive `init` also offers a multi-select of every
173
+ discovered `apps/*` app (from `project.json` with `projectType: "application"`, or a directory with
174
+ its own `index.html` but no `project.json`) instead of asking about the repo root.
175
+
176
+ ### Authentication
177
+
178
+ Authenticate **once per machine**, not once per repo. `pointer login` — with no `--key`, on a real
179
+ terminal — opens your browser to sign in (mirrors `gh auth login`): it prints a link and a short
180
+ code, waits for you to approve it in the dashboard, and saves the personal API key it hands back to
181
+ a global, per-machine credential store. Pass `--key <key>` to skip the browser and validate a pasted
182
+ key instead (from your profile page or the dashboard's quick-start guide). Every other command
183
+ (`init` in join mode, `doctor`, `apply`, `list`, `mcp`, and `.pointer/pointer.sh`) then finds
184
+ whichever key was saved without being asked again — in this repo, or any other repo on the same
185
+ machine, against the same server.
186
+
187
+ **Resolution order**, identical everywhere a key is needed:
188
+
189
+ 1. **`POINTER_API_KEY`** environment variable — the right choice for CI, and always wins outright.
190
+ 2. This repo's **`.pointer/credentials.env`** — written only when you opt out of the global store
191
+ (`--scope repo`, or choosing "Repo" at the prompt below; `--local-credentials` is the older alias).
192
+ 3. The **global store**: `~/.config/pointer/credentials.json` (honours `$XDG_CONFIG_HOME`; on
193
+ Windows, `%APPDATA%\pointer\credentials.json`), keyed by server, mode `0600`.
194
+
195
+ ```bash
196
+ # Once per machine, per server:
197
+ pointer login --server https://api.pointer.moamen.work
198
+ # Open this link and enter the code to sign in:
199
+ # https://app.pointer.moamen.work/cli-login?code=ABCD-EFGH
200
+ # Code: ABCD-EFGH
201
+ # Waiting for approval… (Ctrl+C to cancel)
202
+ # ✔ Signed in to https://api.pointer.moamen.work as Jane Doe (jane@example.com) — saved for all repos on this machine
203
+
204
+ pointer whoami
205
+ # https://api.pointer.moamen.work — Jane Doe (jane@example.com) — key source: global store
206
+
207
+ pointer logout # removes this machine's saved key for a server
208
+ ```
209
+
210
+ - **`pointer login [--server <url>] [--key <key>] [--no-browser]`** — resolves the server from
211
+ `--server`, then this repo's `.pointer/config.json`, then `$POINTER_SERVER`, then the build
212
+ default. With no `--key` it runs the browser sign-in flow above (`--no-browser` prints the
213
+ link/code but skips trying to open a browser); `--key <key>` validates a pasted key instead,
214
+ exactly like `init` does (`/api/auth/login-with-key` + `/api/auth/me`). Either way the resulting
215
+ key is saved to the global store (or `.pointer/credentials.env` with `--scope repo`).
216
+ - **`pointer logout [--server <url>]`** — removes this machine's saved entry for that server. Never
217
+ touches a repo's own `.pointer/credentials.env`.
218
+ - **`pointer whoami [--server <url>] [--json]`** — prints the server, the signed-in account, and
219
+ which source answered the key (`env` / `repo` / `global`) — **never the key itself**.
220
+
221
+ **`init`'s first-run sign-in.** When no key resolves from anywhere (env, repo, or global store) and
222
+ `--key` wasn't passed, an interactive `init` asks how to sign in — "Sign in in your browser
223
+ (recommended)" (the same device-code flow as `pointer login`) or "Paste an API key" (today's hidden
224
+ prompt). `--yes`/`--json` skip the question and require `--key`.
225
+
226
+ **`init` and the global store.** A first install that authenticates a key it did not already trust
227
+ (typed interactively, signed in via the browser, or passed via `--key`) asks:
228
+
229
+ ```
230
+ Save this key for all repos on this machine? (Y/n)
231
+ ```
232
+
233
+ Answering yes (the default, and also the default under `--yes`/`--json` when `--key` is given)
234
+ saves it globally and writes **no** `.pointer/credentials.env` at all — there is nothing repo-local
235
+ to gitignore, review, or accidentally commit. Choosing "Repo" at the prompt, or passing **`--scope repo`** (alias `--local-credentials`),
236
+ keeps the pre-global-store behaviour: the key is written to `.pointer/credentials.env` instead
237
+ (still gitignored, still per-machine). A join (`init` run again in an already-configured repo) never
238
+ even asks, either way: it tries `resolveApiKey`'s three sources first and only prompts when none of
239
+ them resolve.
240
+
241
+ **CI**: set `POINTER_API_KEY` — it always wins, and nothing is written anywhere.
242
+
243
+ **Multiple accounts on one machine** (e.g. a personal key for most repos, a service account for
244
+ one): run `pointer login --scope repo` in that repo, or `pointer init --key <key> --scope repo` (or `pointer login` normally, then override
245
+ per-repo with `.pointer/credentials.env`) for the repo that needs the different key — the repo-local
246
+ file wins over the global store for that repo only.
247
+
29
248
  ### `pointer doctor`
30
249
  Diagnose an existing installation and report issues.
31
250
  - Checks config files, server reachability, API version compatibility, clock skew, and widget injection.
@@ -38,13 +257,19 @@ pointer doctor --fix
38
257
  ```
39
258
 
40
259
  ### `pointer update`
41
- Refresh the local AI skills (`.claude/skills/`, `.agents/`) and `pointer.sh` from the configured server.
260
+ Refresh the local AI skills (`.claude/skills/`, `.agents/`) and `pointer.sh` from the configured
261
+ server — **and installs them if they are missing entirely**, not just when they're stale. Since
262
+ they're gitignored (see "Committed vs. gitignored" above), a freshly cloned repo that already has
263
+ `.pointer/config.json` has neither until `update` (or a join `init`) puts them there.
42
264
 
43
265
  ```bash
44
- pointer update
45
- pointer update --check
266
+ pointer update # installs anything missing, refreshes anything stale
267
+ pointer update --check # reports missing/stale files without changing anything
46
268
  ```
47
269
 
270
+ `pointer doctor` (below) surfaces the same gap as a `skills` warning — "Skills not installed — run
271
+ `npx pointer-feedback update`" — and `doctor --fix` runs the same install.
272
+
48
273
  ### `pointer apply`
49
274
  Turn pending feedback comments into a self-contained AI apply prompt, or hand it off directly to an AI tool.
50
275
 
@@ -112,7 +337,7 @@ pointer reply 12 "Investigating this now."
112
337
  ### `pointer mcp`
113
338
  Run the Model Context Protocol (MCP) server over standard I/O for AI coding agents.
114
339
 
115
- Exposes typed tools to Claude Code, Cursor, Windsurf, OpenCode, and any MCP-compatible environment without exposing API keys to the model context.
340
+ Exposes typed tools to your AI tool over MCP any MCP-compatible environment without exposing API keys to the model context.
116
341
 
117
342
  #### Configuration (User-Level, Do Not Commit)
118
343