@archme/cli 0.4.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 (4) hide show
  1. package/README.md +300 -0
  2. package/SKILL.md +243 -0
  3. package/dist/cli.js +17686 -0
  4. package/package.json +45 -0
package/README.md ADDED
@@ -0,0 +1,300 @@
1
+ # archme-cli
2
+
3
+ Multi-subcommand CLI for ArchitectMe. The daemon (original purpose of this
4
+ package) is one subcommand; the others cover code analysis, OAuth login, and
5
+ MCP API key management.
6
+
7
+ See also:
8
+ - Proposal: `../future-proposals/26-05-12-archme-cli-and-component-anchors.md`
9
+ - Analyze methodology: `./SKILL.md`
10
+ - Daemon commit-driven flow: `../future-proposals/26-04-29-github-app-commit-driven-updates.md`
11
+ - Release runbook (bump → distribute → set floor): `./RELEASE.md`
12
+
13
+ ## Subcommands at a glance
14
+
15
+ | Subcommand | Purpose |
16
+ |---|---|
17
+ | `archme daemon` | Local job runner — claims `archme_jobs` rows and runs Claude to update architecture |
18
+ | `archme analyze outline\|repo\|diff\|match\|prefetch` | Code analysis primitives (tree-sitter) for keeping architecture in sync with code |
19
+ | `archme login \| logout` | OAuth flow — authenticate this machine against ArchitectMe |
20
+ | `archme keys list\|create\|revoke` | Manage MCP API keys for headless/CI use |
21
+
22
+ ## Installation
23
+
24
+ ```bash
25
+ npm install -g @archme/cli
26
+ ```
27
+
28
+ Requires Node 20+. `keytar` is an *optional* dependency — if its native build
29
+ fails, credentials fall back to `~/.archme/credentials.json` (0600) and
30
+ everything else keeps working.
31
+
32
+ ### Developing on the CLI
33
+
34
+ From a clone, `npm link` puts the local copy on your `PATH`:
35
+
36
+ ```bash
37
+ cd archme-cli
38
+ npm install
39
+ npm link
40
+
41
+ which archme # → <your-npm-prefix>/bin/archme
42
+ ```
43
+
44
+ To uninstall: `cd archme-cli && npm unlink` (or `npm rm -g @archme/cli`).
45
+
46
+ **One-off / no global install:**
47
+
48
+ ```bash
49
+ npx tsx archme-cli/src/bin.ts <subcommand> [args]
50
+ ```
51
+
52
+ The published package runs `dist/cli.js`, built by `npm run build` (esbuild
53
+ inlines the repo's top-level `shared/` modules, which do not exist inside the
54
+ package). `npm run build:check` builds and then executes the artifact — that is
55
+ what CI and `prepublishOnly` run. When iterating on sources, prefer
56
+ `npx tsx src/bin.ts`, which never consults `dist/`.
57
+
58
+ ### Do you need env config?
59
+
60
+ | Goal | Env setup |
61
+ |---|---|
62
+ | `archme analyze` (outline / repo / diff / match / prefetch) against local files | **None.** Analyze doesn't talk to Supabase. |
63
+ | `archme login` / `archme daemon` / `archme scan` against **prod** | **None.** Defaults are hardcoded; just `archme login && archme daemon`. |
64
+ | Same commands against **local Supabase** | `ARCHME_ENV=local`. The per-developer OAuth client id is set up once via `npm run setup:local-daemon-oauth` from the repo root (after `supabase start`) — that writes `~/.archme/local-config.json`, which the CLI auto-reads when `ARCHME_ENV=local`. |
65
+ | Self-hosted ArchitectMe deployment | Override individual env vars: `SUPABASE_URL=... SUPABASE_ANON_KEY=... ARCHME_DAEMON_CLIENT_ID=... ARCHME_MCP_BASE_URL=... archme daemon`. |
66
+
67
+ The CLI never reads `.env` files. Configuration comes from (in order of precedence): explicit env vars > `~/.archme/local-config.json` (when `ARCHME_ENV=local`) > built-in preset for `ARCHME_ENV` (`prod` by default). The full set of overrideable vars is `SUPABASE_URL`, `SUPABASE_ANON_KEY`, `ARCHME_DAEMON_CLIENT_ID`, `ARCHME_MCP_BASE_URL`, plus `CLONE_BASE_DIR` (daemon only, defaults to `~/.archme-job-runner/clones`).
68
+
69
+ ---
70
+
71
+ ## archme daemon
72
+
73
+ Local daemon that consumes `archme_jobs` rows from Supabase and runs Claude
74
+ on this machine to update the architecture diagram. It is the local worker
75
+ for the GitHub-App → Supabase → laptop-daemon flow
76
+ (see `../future-proposals/26-04-29-github-app-commit-driven-updates.md`).
77
+
78
+ ### How it works
79
+
80
+ 1. Subscribes to Supabase Realtime `INSERT` events on `archme_jobs` filtered
81
+ to `status = 'pending'`. On startup and reconnect, also runs a sweep over
82
+ any pending rows.
83
+ 2. Atomically claims a row through the `claim_archme_job` RPC using its
84
+ registered org device id.
85
+ 3. Requests short-lived GitHub App installation credentials for the claimed
86
+ job's repo, then uses the repo under `CLONE_BASE_DIR/<owner>/<repo>` as a
87
+ shared Git object cache.
88
+ 4. Creates a temporary detached worktree at the job's `after_sha`.
89
+ 5. Generates a temp `mcp.json` pointing at the archme MCP with
90
+ `?project=<slug>&branch=<branch>` from the job row.
91
+ 6. Shells out to `claude -p <prompt> --mcp-config <path>` with its working
92
+ directory set to that temporary worktree. Claude reads the architecture,
93
+ reasons about the diff, and calls `update_architecture`.
94
+ 7. Calls the `archme-job-complete` edge function to mark the row
95
+ `completed` (with the model's summary) or `failed` (with the error) and
96
+ post the GitHub check run.
97
+ 8. Runs a safety sweep every 30 seconds so pending rows are still picked up if
98
+ Realtime misses an event.
99
+
100
+ ### Setup
101
+
102
+ ```bash
103
+ cd archme-cli
104
+ npm install
105
+ ```
106
+
107
+ That's it for the prod target — config defaults live in `src/config/defaults.ts`. For local-Supabase development, see "Do you need env config?" above.
108
+
109
+ ### Auth
110
+
111
+ The daemon always uses one-time browser enrollment:
112
+
113
+ ```bash
114
+ cd archme-cli
115
+ npm run enroll
116
+ ```
117
+
118
+ The command prints a code and opens `/agents/pair` in your browser. Pick the
119
+ org this daemon should work for, then confirm the matching code while signed
120
+ in to ArchitectMe. Pairing creates an enabled `agent_devices` row for that
121
+ org. The daemon stores the resulting refresh token, org id, and device id in
122
+ macOS Keychain under service `archme-daemon`, using `SUPABASE_URL` as the
123
+ account. If Keychain is unavailable, it falls back to
124
+ `~/.archme/credentials.json` with mode `0600`.
125
+
126
+ On every token refresh the daemon persists the rotated refresh token. If the
127
+ stored token is revoked, invalid, or missing org/device identity, startup
128
+ exits with re-enrollment instructions.
129
+
130
+ The web app must be configured with `VITE_ARCHME_DAEMON_CLIENT_ID` matching
131
+ the pre-registered Supabase OAuth client for the daemon. That client needs
132
+ `<app-origin>/agents/pair/callback` as an allowed redirect URI.
133
+
134
+ For local Supabase, run this from the repo root after `supabase start`:
135
+
136
+ ```bash
137
+ npm run setup:local-daemon-oauth
138
+ ```
139
+
140
+ That creates or updates the local daemon OAuth client and writes:
141
+
142
+ - `.env.local` for the Vite app (so `/agents/pair` knows the client id)
143
+ - `~/.archme/local-config.json` for the CLI (auto-loaded when `ARCHME_ENV=local`)
144
+
145
+ After that:
146
+
147
+ ```bash
148
+ ARCHME_ENV=local archme login
149
+ ARCHME_ENV=local archme daemon
150
+ ```
151
+
152
+ To complete the browser step locally, the local app still needs a normal
153
+ ArchitectMe web session. If your browser is not already signed in, configure
154
+ the local Supabase sign-in provider before starting Supabase.
155
+
156
+ ### Run
157
+
158
+ ```bash
159
+ npm run dev
160
+ ```
161
+
162
+ Logs prefixed with `[<job-id>]` per job.
163
+
164
+ ### Enrollment Verification
165
+
166
+ ```bash
167
+ cd archme-cli
168
+ npm run enroll
169
+ # confirm the browser prompt
170
+ npm run dev
171
+ ```
172
+
173
+ On macOS, the credential should be visible with:
174
+
175
+ ```bash
176
+ security find-generic-password -s archme-daemon -a "$SUPABASE_URL"
177
+ ```
178
+
179
+ If Keychain was unavailable, check `~/.archme/credentials.json` exists with
180
+ mode `0600`.
181
+
182
+ ### Job Verification
183
+
184
+ With the daemon running, enqueue a job for a watched repo/project. The watched
185
+ repo must have a valid GitHub App `installation_id`; the daemon uses that
186
+ installation to mint short-lived HTTPS clone/fetch credentials.
187
+
188
+ ```sql
189
+ select enqueue_archme_job(
190
+ '<watched_repo_id>',
191
+ '<org_id>',
192
+ '<user_id>',
193
+ '<project_id>',
194
+ '<repo_owner>',
195
+ '<repo_name>',
196
+ '<repo_branch>',
197
+ '<archme_branch>',
198
+ '<before_sha>',
199
+ '<after_sha>',
200
+ false,
201
+ null
202
+ );
203
+ ```
204
+
205
+ Within one safety sweep the daemon should claim the row, set
206
+ `claimed_by_device_id`, fetch the repo without persisting tokenized remotes,
207
+ run Claude, call the completion edge function, and mark the row
208
+ `completed`. Open the project in the web app on the same branch; the diagram
209
+ should reflect the change, or the row's `result.summary` should explain why
210
+ no update was needed.
211
+
212
+ ---
213
+
214
+ ## archme analyze
215
+
216
+ Code analysis primitives that power keeping ArchitectMe architecture in sync
217
+ with actual source code. Parses files with
218
+ [web-tree-sitter](https://github.com/tree-sitter/tree-sitter/tree/master/lib/binding_web)
219
+ and emits structured JSON that the MCP server and agents consume.
220
+
221
+ Grammars are fetched on demand to `~/.archme/tools/grammars/` (override with
222
+ `ARCHME_TOOLS_DIR`). No build step required — grammars are downloaded the
223
+ first time a language is encountered.
224
+
225
+ **Supported languages:** TypeScript, TSX, JavaScript, Python, Go, Java, Ruby,
226
+ Rust, Swift, Kotlin.
227
+
228
+ For the full agent methodology (how to run a repo scan, match anchors, and
229
+ write architecture updates) see `./SKILL.md`.
230
+
231
+ ### Commands
232
+
233
+ ```
234
+ archme analyze outline <file> Emit a JSON symbol outline for a single file
235
+ archme analyze repo Walk the repo and outline every supported file
236
+ archme analyze diff <range> Outline only files changed in a git range (e.g. HEAD~3..HEAD)
237
+ archme analyze match <anchor-spec> Match anchor specs (JSON file) against current symbol outlines
238
+ archme analyze prefetch [langs...] Pre-download grammars so first analysis is instant
239
+ ```
240
+
241
+ ### Example
242
+
243
+ ```bash
244
+ archme analyze outline src/api/upload.ts
245
+ ```
246
+
247
+ Output is a JSON envelope:
248
+
249
+ ```json
250
+ {
251
+ "file": "src/api/upload.ts",
252
+ "lang": "typescript",
253
+ "symbols": [
254
+ { "kind": "function", "name": "handleUpload", "line": 12, "exported": true },
255
+ { "kind": "class", "name": "UploadService", "line": 34, "exported": true }
256
+ ]
257
+ }
258
+ ```
259
+
260
+ `archme analyze repo` emits one envelope per file (newline-delimited JSON) and
261
+ is the starting point for a full codebase scan.
262
+
263
+ `archme analyze match <anchor-spec.json>` takes a JSON file describing named
264
+ anchors (component → expected symbols) and reports which anchors still resolve
265
+ and which have drifted, enabling the MCP server to flag stale architecture
266
+ nodes.
267
+
268
+ ---
269
+
270
+ ## Auth & keys
271
+
272
+ ### archme login / logout
273
+
274
+ ```bash
275
+ archme login # Opens browser OAuth flow; stores token in macOS Keychain
276
+ # (falls back to ~/.archme/credentials.json with mode 0600)
277
+ archme logout # Clears stored credentials
278
+ ```
279
+
280
+ OAuth flow uses the same Supabase OAuth 2.1 / PKCE mechanism as the daemon
281
+ enrollment. Intended for interactive use; the daemon uses its own enrollment
282
+ flow (`npm run enroll`) instead.
283
+
284
+ ### archme keys list | create | revoke
285
+
286
+ ```bash
287
+ archme keys list # List MCP API keys for the authenticated user
288
+ archme keys create <name> # Mint a new am_* key for CI / headless use
289
+ archme keys revoke <key-id> # Revoke a key
290
+ ```
291
+
292
+ > **Currently stub** — these commands print placeholder messages. Full
293
+ > implementation (Unkey integration, org-scoped key listing) is pending.
294
+
295
+ ---
296
+
297
+ ## Privacy / sync
298
+
299
+ This directory is excluded from the work-repo sync
300
+ (`scripts/sync-to-on-prem.sh`).
package/SKILL.md ADDED
@@ -0,0 +1,243 @@
1
+ # archme — Agent Skill
2
+
3
+ This skill teaches coding agents how to keep an archme architecture
4
+ in sync with its codebase. It's bundled with the `@archme/cli` package.
5
+
6
+ ## Purpose
7
+
8
+ archme is the source of truth for *architecture intent* — components,
9
+ connections, user stories, decisions. The code is the source of truth
10
+ for *what actually exists*. Your job is to keep the two in sync,
11
+ using anchors as the binding between them.
12
+
13
+ archme stores anchors. You produce and verify them.
14
+
15
+ ## What you have
16
+
17
+ Two surfaces:
18
+
19
+ - **archme MCP tools** (`read_architecture`, `update_architecture`,
20
+ `find_components_for_paths`, `get_component`, ...) — read and write
21
+ the architecture. Your only path to archme's data.
22
+
23
+ - **`@archme/cli`** (the package shipping this skill) — local code
24
+ analysis. Commands:
25
+ - `archme analyze outline <file>` — symbols in one file (preferred for anchoring)
26
+ - `archme analyze repo` — bulk outline of the whole repo (JSONL, large output — prefer outline for targeted work)
27
+ - `archme analyze diff <range>` — structured diff of a git range
28
+ - `archme analyze match <anchor-spec>` — resolve anchors against current code
29
+ - `archme analyze prefetch` — grammar fetch for CI / airgap
30
+
31
+ CLI output is always JSON. Parse it; don't grep it.
32
+
33
+ ## When to use what
34
+
35
+ | Task | Use |
36
+ |---|---|
37
+ | "What files does this component live in?" | Read architecture; walk anchors |
38
+ | "What component owns this file?" | `find_components_for_paths` |
39
+ | "What symbols are in this file?" | `archme analyze outline` |
40
+ | "What does this commit change?" | `archme analyze diff <range>` |
41
+ | "Does this anchor still resolve?" | `archme analyze match` |
42
+ | "Is the architecture missing components?" | `find_components_for_paths` on representative files; outline gaps with `archme analyze outline` |
43
+ | "Add or update a component" | `update_architecture` MCP tool |
44
+
45
+ ## Anchor kinds — when to pick which
46
+
47
+ An anchor is `{id, kind, source, confidence, created_at, ...}` plus
48
+ kind-specific fields. Pick the kind based on how the component lives
49
+ in code:
50
+
51
+ - **`file`** — component lives in exactly one file
52
+ `{kind: "file", path: "src/api/upload.ts"}`
53
+
54
+ - **`directory`** — component is a self-contained module / subtree
55
+ `{kind: "directory", path: "src/workers/email"}`
56
+
57
+ - **`glob`** — component is a fuzzy slice across files
58
+ `{kind: "glob", pattern: "src/**/*.controller.ts"}`
59
+
60
+ - **`symbol`** — component is a specific function or class
61
+ `{kind: "symbol", path: "src/api/upload.ts", name: "UploadHandler", symbolKind: "class"}`
62
+
63
+ For components without code (external services, vendors, conceptual
64
+ "Cache Layer" not yet built), use `unanchored: true` instead of
65
+ anchors. Don't fabricate anchors to anchor something that has no code.
66
+
67
+ A component can carry **multiple** anchors of mixed kinds — e.g.,
68
+ a directory anchor for helpers plus a symbol anchor for the primary
69
+ handler.
70
+
71
+ ## Confidence
72
+
73
+ Set per anchor based on how the CLI verified it:
74
+
75
+ - **1.0** — human-edited (you didn't produce this; preserve it)
76
+ - **0.8** — tree-sitter parse confirmed (`analyze` returned tier 2)
77
+ - **0.7** — glob anchor matching ≥1 file (no parse, but coverage exists)
78
+ - **0.5** — degraded (`analyze match` returned `degraded`)
79
+ - **0.3** — speculative (you couldn't fully verify)
80
+
81
+ Server doesn't enforce these — they're conventions. Downstream
82
+ consumers filter on them.
83
+
84
+ ## Workflows
85
+
86
+ ### Workflow: brownfield first scan
87
+
88
+ ```
89
+ 1. Read the current architecture (empty for a new project)
90
+ 2. Identify capabilities via explorers (code structure, git history, docs)
91
+ 3. For each component, run archme analyze outline on its key files
92
+ to get the symbol structure. Pick anchors from the outline.
93
+ 4. For external/conceptual components, set unanchored: true.
94
+ 5. Run archme analyze match to verify all anchors resolve.
95
+ 6. Submit components via update_architecture, including anchors.
96
+ 7. Sample 5-10 code files; call find_components_for_paths for each.
97
+ Every file should resolve to ≥1 component.
98
+ 8. Submit stories, connections, requirements as usual.
99
+ ```
100
+
101
+ ### Story granularity
102
+
103
+ Before you mint a user story, decide it deserves to be one. A story should be
104
+ **foundational — a full, meaningful user flow with its own goal**, not a fragment
105
+ of one. A single conditional, edge case, error/redirect path, or robustness rule
106
+ scoped to one existing surface is never a story of its own (smell test: one
107
+ `alt`/`else` branch, touches a single component, nothing else references it).
108
+ Instead: if it meaningfully changes how the flow behaves, fold it into the owning
109
+ story's `sequence` as a proportional branch or step (`alt`/`else`, `break`); if
110
+ it's a bug fix or hardening the existing sequence already depicts correctly at
111
+ its altitude, change nothing — the story is already right. If that surface has no
112
+ owning story yet, create the surface/flow-level story — a full flow with the rule
113
+ as one branch of its sequence — never a story for the rule alone. This is the
114
+ same gate the scan and daemon-diff prompts enforce verbatim (canonical source:
115
+ `shared/storyGranularity.ts`).
116
+
117
+ ### Bugs you happen to notice
118
+
119
+ **Don't go hunting for bugs — stay on the architecture task.** But if an obvious,
120
+ real bug in the user's code surfaces while you read it (a null-deref, a missing
121
+ `await`, an unhandled error path, a broken invariant), don't ignore it and don't
122
+ fix it. The flow is passive and orchestrator-gated: a code-reading sub-agent
123
+ **surfaces** a suspect back to the orchestrator (`Possible bug: <what>
124
+ (file:line)` in its findings) rather than filing it; the orchestrator
125
+ **dispatches one bug-validation sub-agent** that confirms each suspect against
126
+ the code and **files the confirmed ones itself via `create_proposal`** (it reads
127
+ code + writes a proposal, exactly like a build sub-agent, and never touches the
128
+ user's code). Logging reuses the
129
+ proposals surface, not a bespoke mechanism: a prose-only proposal (empty graph
130
+ delta, `body` naming what's wrong, where by file:line/symbol, and why) under the
131
+ project's standing **"Bugs" folder**, `stage: 'backlog'` so it parks until a human
132
+ triages. The daemon injects the Bugs folder id for you; a manual scan
133
+ finds-or-creates it (`search_proposals({ kind: 'folder', text: 'Bugs' })`, else
134
+ `create_proposal({ kind: 'folder', title: 'Bugs' })`). Dedup by searching the
135
+ folder by title — the same standing bug is re-noticed on every commit. Keep the
136
+ bar high (a few per run at most, code-review-worthy only); logging a bug is
137
+ **orthogonal** to your architecture task — still call
138
+ `update_architecture`/`report_no_change` as normal. Canonical source:
139
+ `shared/bugReport.ts`.
140
+
141
+ ### Workflow: ambient editing
142
+
143
+ The user is editing `src/api/upload.ts`. You should:
144
+
145
+ ```
146
+ 1. Call find_components_for_paths with ["src/api/upload.ts"]
147
+ 2. Read the matching component(s) via get_component
148
+ 3. If the edit changes architectural shape (new dependency, removed
149
+ feature, rename), propose an update_architecture call to the user
150
+ 4. Otherwise: no action — the architecture is still valid
151
+ ```
152
+
153
+ ### Workflow: reviewing a diff
154
+
155
+ The user wants to know what a PR/commit range affects:
156
+
157
+ ```
158
+ 1. Run archme analyze diff <before>..<after>
159
+ 2. Collect changed file paths from the diff output
160
+ 3. Call find_components_for_paths with those paths
161
+ 4. For each affected component, decide if the architecture needs
162
+ updating (new edges, removed parts, etc.)
163
+ 5. If yes: update_architecture
164
+ ```
165
+
166
+ ### Workflow: orphan recovery
167
+
168
+ `archme analyze match` returned `orphan` for an anchor:
169
+
170
+ ```
171
+ - Run archme analyze outline on the file the anchor points at
172
+ (if file kind: check if the path moved)
173
+ - Run git log -- <path> to see history (renamed? deleted?)
174
+ - Decide:
175
+ (a) Anchor is stale — code moved. Update the anchor's path or
176
+ replace with a different kind.
177
+ (b) Component should be removed — the code it modeled is gone.
178
+ Use update_architecture's componentUpdates.remove.
179
+ (c) Component still exists but in a different shape — re-anchor
180
+ via update_architecture.
181
+ - Submit the fix
182
+ ```
183
+
184
+ ### Workflow: adding a new component mid-feature
185
+
186
+ The user is implementing a new subsystem (e.g., adding a new worker):
187
+
188
+ ```
189
+ 1. Outline the new files (archme analyze outline)
190
+ 2. Decide the component's anchor kind based on file layout
191
+ 3. update_architecture with the new component + anchors
192
+ 4. Update parent component's connections if needed
193
+ 5. Optionally add a story for the new capability
194
+ ```
195
+
196
+ ## Trust model
197
+
198
+ You are the agent of correctness:
199
+
200
+ - The server **validates shape** (well-formed anchors, paths without
201
+ traversal, confidence in [0,1], etc.). It cannot validate semantic
202
+ correctness because it has no code access.
203
+ - **You verify code existence** via `archme analyze match` before
204
+ submitting. Server trust depends on this.
205
+ - The server returns warnings (e.g., `unanchored:true` with non-empty
206
+ anchors); they don't block writes but indicate something to clean up.
207
+
208
+ ## What NOT to do
209
+
210
+ - **Don't fabricate anchors.** If you can't verify it via the CLI,
211
+ don't submit it with high confidence.
212
+ - **Don't submit anchors with confidence > 0.8 without `analyze match`.**
213
+ You have a deterministic verifier; use it.
214
+ - **Don't anchor everything as `symbol`.** Many components are not
215
+ single symbols — they're directories, globs, or whole files.
216
+ - **Don't use line ranges as anchors.** Anchors are stable across
217
+ edits; line numbers rot on the first change.
218
+ - **Don't try to derive cross-file references.** v1's CLI doesn't
219
+ do call graphs. If you need to know callers, grep for the symbol
220
+ name — but be explicit that it's a heuristic.
221
+ - **Don't update `unanchored: true` components with anchors.**
222
+ Either it has anchors (then drop `unanchored`) or it doesn't (keep
223
+ `unanchored: true`).
224
+
225
+ ## Report friction
226
+
227
+ If any part of working with archme is confusing, broken, or harder than it
228
+ should be — a tool error you can't act on, a description that doesn't match the
229
+ behavior, a validation rule that rejects something valid, a missing capability,
230
+ or a step that just felt frustrating — call the `report_feedback` MCP tool to
231
+ leave a short note in the moment: `report_feedback({ area, severity, summary,
232
+ detail? })`, where `severity` is `frustration` (worked around it),
233
+ `not_working` (broken/blocking), or `suggestion` (an idea). It's a welcome,
234
+ low-friction signal, not an error — don't let the signal get lost. This is
235
+ meta-feedback about archme's own tooling, NOT a place to log bugs in the user's
236
+ own code.
237
+
238
+ ## Where to look
239
+
240
+ - Spec (this skill's source of truth):
241
+ `future-proposals/26-05-12-archme-cli-and-component-anchors.md`
242
+ - Tool descriptions live in the MCP server (`supabase/functions/mcp/index.ts`).
243
+ - CLI source: `archme-cli/src/analyze/`.