pointer-feedback 0.1.3 → 0.2.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 +214 -4
  2. package/dist/cli.js +2797 -1072
  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,209 @@ 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 `--local-credentials` —
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` validates an API key and
179
+ saves it to a global, per-machine credential store; every other command (`init` in join mode,
180
+ `doctor`, `apply`, `list`, `mcp`, and `.pointer/pointer.sh`) then finds it without being asked again
181
+ — in this repo, or any other repo on the same machine, against the same server.
182
+
183
+ **Resolution order**, identical everywhere a key is needed:
184
+
185
+ 1. **`POINTER_API_KEY`** environment variable — the right choice for CI, and always wins outright.
186
+ 2. This repo's **`.pointer/credentials.env`** — written only when you opt out of the global store
187
+ (`--local-credentials`, or answering "no" to the save prompt below).
188
+ 3. The **global store**: `~/.config/pointer/credentials.json` (honours `$XDG_CONFIG_HOME`; on
189
+ Windows, `%APPDATA%\pointer\credentials.json`), keyed by server, mode `0600`.
190
+
191
+ ```bash
192
+ # Once per machine, per server:
193
+ pointer login --server https://api.pointer.moamen.work
194
+ # API key (from Pointer -> profile -> API key; input hidden): ****************
195
+
196
+ pointer whoami
197
+ # https://api.pointer.moamen.work — Jane Doe (jane@example.com) — key source: global store
198
+
199
+ pointer logout # removes this machine's saved key for a server
200
+ ```
201
+
202
+ - **`pointer login [--server <url>] [--key <key>]`** — resolves the server from `--server`, then
203
+ this repo's `.pointer/config.json`, then `$POINTER_SERVER`, then the build default. Prompts for
204
+ the key (hidden input) unless `--key` is given; validates it exactly like `init` does
205
+ (`/api/auth/login-with-key` + `/api/auth/me`); saves it to the global store.
206
+ - **`pointer logout [--server <url>]`** — removes this machine's saved entry for that server. Never
207
+ touches a repo's own `.pointer/credentials.env`.
208
+ - **`pointer whoami [--server <url>] [--json]`** — prints the server, the signed-in account, and
209
+ which source answered the key (`env` / `repo` / `global`) — **never the key itself**.
210
+
211
+ **`init` and the global store.** A first install that authenticates a key it did not already trust
212
+ (typed interactively, or passed via `--key`) asks:
213
+
214
+ ```
215
+ Save this key for all repos on this machine? (Y/n)
216
+ ```
217
+
218
+ Answering yes (the default, and also the default under `--yes`/`--json` when `--key` is given)
219
+ saves it globally and writes **no** `.pointer/credentials.env` at all — there is nothing repo-local
220
+ to gitignore, review, or accidentally commit. Answering no, or passing **`--local-credentials`**,
221
+ keeps the pre-global-store behaviour: the key is written to `.pointer/credentials.env` instead
222
+ (still gitignored, still per-machine). A join (`init` run again in an already-configured repo) never
223
+ even asks, either way: it tries `resolveApiKey`'s three sources first and only prompts when none of
224
+ them resolve.
225
+
226
+ **CI**: set `POINTER_API_KEY` — it always wins, and nothing is written anywhere.
227
+
228
+ **Multiple accounts on one machine** (e.g. a personal key for most repos, a service account for
229
+ one): run `pointer init --key <key> --local-credentials` (or `pointer login` normally, then override
230
+ per-repo with `.pointer/credentials.env`) for the repo that needs the different key — the repo-local
231
+ file wins over the global store for that repo only.
232
+
29
233
  ### `pointer doctor`
30
234
  Diagnose an existing installation and report issues.
31
235
  - Checks config files, server reachability, API version compatibility, clock skew, and widget injection.
@@ -38,13 +242,19 @@ pointer doctor --fix
38
242
  ```
39
243
 
40
244
  ### `pointer update`
41
- Refresh the local AI skills (`.claude/skills/`, `.agents/`) and `pointer.sh` from the configured server.
245
+ Refresh the local AI skills (`.claude/skills/`, `.agents/`) and `pointer.sh` from the configured
246
+ server — **and installs them if they are missing entirely**, not just when they're stale. Since
247
+ they're gitignored (see "Committed vs. gitignored" above), a freshly cloned repo that already has
248
+ `.pointer/config.json` has neither until `update` (or a join `init`) puts them there.
42
249
 
43
250
  ```bash
44
- pointer update
45
- pointer update --check
251
+ pointer update # installs anything missing, refreshes anything stale
252
+ pointer update --check # reports missing/stale files without changing anything
46
253
  ```
47
254
 
255
+ `pointer doctor` (below) surfaces the same gap as a `skills` warning — "Skills not installed — run
256
+ `npx pointer-feedback update`" — and `doctor --fix` runs the same install.
257
+
48
258
  ### `pointer apply`
49
259
  Turn pending feedback comments into a self-contained AI apply prompt, or hand it off directly to an AI tool.
50
260