@aligndottech/cli 0.21.1 → 0.22.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 (2) hide show
  1. package/README.md +66 -529
  2. package/package.json +1 -1
package/README.md CHANGED
@@ -7,81 +7,24 @@
7
7
 
8
8
  **Your AI agents know the code. They don't know the company.**
9
9
 
10
- The decisions behind the code live in commits, tickets, chat and meetings - and months later
11
- nobody can tell what still stands, what conflicts, or why. [Align](https://align.tech) pulls
12
- those decisions into one graph your agents and your team check before they build.
13
-
14
- This CLI is the free, open-source way in. It builds a decision graph from the tools you
15
- already use - git history, GitHub, GitLab, Jira, Confluence, Linear, Slack, Teams, Zoom,
16
- Notion - and serves it to any MCP agent, with an edit hook that surfaces prior decisions
17
- before the agent writes. No account required. Beta, pre-1.0.
18
-
19
- In a published benchmark, giving a coding agent recorded product decisions took decision
20
- compliance from 46% to 95% ([Dillon & Varanasi, arXiv:2605.08112](https://arxiv.org/abs/2605.08112) -
21
- a small vendor study, 8 tasks and 41 decision points, and it isn't our data).
22
-
23
- ```
24
- npm install -g @aligndottech/cli
25
- ```
26
-
27
- Node 20+ required. MIT licensed.
28
-
29
- > **Install notes.** Cloud mode needs no native build. Local-only mode additionally
30
- > uses an on-device embedding model (`@huggingface/transformers`, an optional dependency)
31
- > that ships native binaries for macOS, glibc Linux, and Windows (x64/arm64) - on
32
- > those platforms `npm i -g` just works. On Alpine/musl, uncommon architectures, or
33
- > behind a strict proxy the optional model may not install; the global install still
34
- > succeeds and cloud mode works, and local-only mode will tell you the model is unavailable
35
- > rather than failing silently. The first import downloads the model from
36
- > huggingface.co (~23MB) once, and nothing local can be embedded or searched until that
37
- > succeeds - so on a restricted network, check that host is reachable before you begin.
38
-
39
- ## Quick start
40
-
41
- ### No account, nothing leaves your machine
10
+ The decisions behind the code live in commits, tickets, chat and meetings. Months later nobody
11
+ can tell what still stands, what conflicts, or why. Align pulls them into one graph your agents
12
+ check before they build.
42
13
 
43
14
  ```bash
44
- align setup --local # local graph, no sign-in; seeds from your git history
45
- align ask "why do we use postgres" # answered from your own graph
46
- ```
47
-
48
- Run it inside a git repository: `align setup --local` seeds the graph from that repo's commit
49
- history, so you have something to ask about straight away. `align import <tool>` adds more
50
- sources later.
51
-
52
- That is the whole free path. Your decisions live in a SQLite file on your machine and are
53
- never sent to Align. [Cloud or local-only](#cloud-or-local-only) sets out exactly what does
54
- and does not touch the network.
55
-
56
- With an AI provider available (an API key in your environment, or a running Ollama) `align ask`
57
- writes an answer with its sources. Without one it returns the matching decisions as a ranked
58
- list instead, which needs no key and never leaves your machine.
59
-
60
- ### With an Align account
61
-
62
- Sync across machines, cross-tool relationship detection, and an upgrade path to a shared team
63
- workspace.
64
-
65
- ```bash
66
- align setup
15
+ npm install -g @aligndottech/cli
16
+ align setup --local
17
+ align ask "why do we use postgres"
67
18
  ```
68
19
 
69
- One guided command: login (or local-only mode), connect your tools via read-only OAuth, seed the graph from your git history, configure your editors' MCP, and wire up automatic alignment checks for AI agents.
70
-
71
- Or step by step:
20
+ Node 20+. MIT. No account needed. Beta, pre-1.0.
72
21
 
73
- ```bash
74
- align login # authenticate
75
- align setup # connect tools (read-only OAuth) + configure MCP
76
- align import git # pull commit history - no token needed
77
- align ask "how does our auth work" # natural language answer from your graph
78
- ```
79
-
80
- > **Seeing `401 unauthorized`?** Every command defaults to the hosted gateway, so it needs
81
- > `align login` first. If you meant the no-account path, run `align setup --local` (or
82
- > `align local start`) and the same commands will use your local graph instead.
22
+ Run it inside a git repository. `--local` seeds the graph from your commit history, so you have
23
+ something to ask about straight away, and your decisions stay in a SQLite file on your machine.
24
+ Nothing is sent to Align. [What touches the network](docs/local-mode.md).
83
25
 
84
- Want a hand setting this up? I do free 30 minute setup calls: https://calendly.com/tom-align/setup
26
+ Want sync across machines and cross-tool relationship detection? Drop the flag. `align setup`
27
+ logs you in, connects your tools via read-only OAuth, and wires up your editors.
85
28
 
86
29
  ## How it works
87
30
 
@@ -94,483 +37,77 @@ Want a hand setting this up? I do free 30 minute setup calls: https://calendly.c
94
37
  Slack, Teams, Zoom ─┘ + relationships┘ └─ align check (CI)
95
38
  ```
96
39
 
97
- 1. **Import** pulls the decisions out of the tools you already use - read-only, nothing is modified.
98
- 2. Align links them into a **cross-tool decision graph**: what was decided, why, who decided it, and how decisions relate (supersedes, conflicts with, depends on).
99
- 3. Your agents and you **query and check against** that graph - over MCP, an edit hook in your agent, CI, or plain `align ask`.
100
-
101
- Wiring context into an agent is the easy part, and this repo is the open-source version of it.
102
- The hard part is the record underneath: what your team actually decided, across every tool,
103
- kept current. The CLI and MCP server are open source (this repo,
104
- plus the [connector SDK](https://github.com/aligndottech/align-connector-sdk)). The hosted
105
- graph and the heavier cross-tool relationship detection are a separate commercial gateway; you
106
- can also run fully local with `--local`.
107
-
108
- ## Auto-alignment for AI agents
109
-
110
- When you run `align setup`, Align makes itself available to your AI agents four ways, so the context fires whether or not the model thinks to ask for it:
111
-
112
- 1. **MCP server** - your assistant (Claude Code, Cursor, Claude Desktop, Windsurf) can query the decision graph inline. The server ships with instructions telling the agent to check alignment *before* making non-trivial changes.
113
- 2. **Edit hooks** - setup registers `align check --advisory` with every host that exposes a hook API, so prior decisions related to the change reach the model whether or not it thought to ask. **Claude Code** (`.claude/settings.json`), **pi** (`.pi/extensions/align.ts`), **Gemini CLI** (`.gemini/settings.json`) and **OpenCode** (`.opencode/plugins/align.js`) all check the *proposed* change before it is written. It is **non-blocking and fail-open**: it never denies an edit by default, and if Align is missing, slow or unreachable the edit proceeds untouched. As `align setup` writes it, the hook is retrieval only, so it needs no AI provider key and makes no provider call. (Adding `--block-on-critical` yourself changes both halves of that - see the table below.)
114
-
115
- **Cursor and Codex CLI cannot do this**, and that is a limit of those hosts, not a gap in setup: Cursor has no `beforeFileEdit` and its `afterFileEdit` hook has no output fields, and Codex's `PreToolUse` intercepts Bash only. They get layers 1, 3 and 4. The full per-host matrix, and why, is in [docs/agent-hooks.md](docs/agent-hooks.md).
116
- 3. **Editor rules** - a managed, marker-delimited block in your `CLAUDE.md` and `AGENTS.md`, plus a `.cursor/rules/align.md` file (Cursor doesn't honor Claude Code hooks), nudge agents to consult the graph.
117
- 4. **A shared `.mcp.json`** at the repo root - the tool-agnostic MCP config that pi, Claude Code and others read, so one committed file wires up the whole team rather than each person's per-host config.
118
-
119
- The hook, rule and `.mcp.json` files are committed to the repo, so the whole team's agents get the same guardrail. Re-running `align setup` updates them in place (idempotent - no duplicate hooks or blocks).
120
-
121
- > **Heads up:** the first time Claude Code loads a project with a committed hook, it shows a one-time "approve hooks" prompt. Accept it to enable automatic alignment.
122
-
123
- You can also run the advisory check yourself. It always exits 0, and when it finds related prior decisions (or could not check at all) prints the hook output in whichever host's shape you ask for - `--format text` is plain prose for a host with no JSON contract. It reports the decisions as related, not as conflicts: retrieval finds decisions on the same subject and does not adjudicate opposition. A decision the hook surfaced moments ago in the same directory is not repeated, so the pre and post hooks don't say everything twice.
124
-
125
- ```bash
126
- align check --advisory # Claude Code shape (default)
127
- align check --advisory --format text # plain text, for any other agent
128
- ```
129
-
130
- ## Asking questions
131
-
132
- `align ask` retrieves the most relevant decisions from your graph and synthesises a concise natural language answer:
133
-
134
- ```bash
135
- align ask "why do we use postgres"
136
- align ask "how does the auth module work"
137
- align ask "what was decided about caching"
138
- align ask "do we use redis"
139
- ```
140
-
141
- Ask in plain English. In cloud mode the gateway picks keyword or semantic search based on your phrasing; in local-only mode every query is semantic. Either way, full questions ("why do we use postgres") work as well as short terms ("postgres"). Pass a file path instead of a question to find decisions related to that file:
142
-
143
- ```bash
144
- align ask src/auth/session.ts
145
- ```
146
-
147
- ### AI provider for conversational answers
148
-
149
- Align is **provider-agnostic** - `align ask` (and local relationship typing) uses **your own AI provider**. It resolves one, in order:
150
-
151
- 1. **Any OpenAI-compatible endpoint** via `ALIGN_LLM_BASE_URL` (+ `ALIGN_LLM_API_KEY`, `ALIGN_LLM_MODEL`) - covers OpenRouter, Together, DeepSeek, LM Studio, vLLM, or any self-hosted OpenAI-compatible server. This outranks the named keys below, so it wins even when `ANTHROPIC_API_KEY` is also set. Example:
152
- ```bash
153
- export ALIGN_LLM_BASE_URL=https://api.deepseek.com
154
- export ALIGN_LLM_API_KEY=sk-...
155
- export ALIGN_LLM_MODEL=deepseek-chat
156
- ```
157
- 2. A named provider via env key: `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY` (or `GOOGLE_API_KEY`), `GROQ_API_KEY`, `MISTRAL_API_KEY`, or `GROK_API_KEY` (or `XAI_API_KEY`). Each has an optional model override (`ALIGN_ANTHROPIC_MODEL`, `ALIGN_OPENAI_MODEL`, `ALIGN_GEMINI_MODEL`, `ALIGN_GROQ_MODEL`, `ALIGN_MISTRAL_MODEL`, `ALIGN_GROK_MODEL`).
158
- 3. [Ollama](https://ollama.com) running locally (auto-detected on `localhost:11434`, override `OLLAMA_HOST`), with a general-purpose model from a recognised family installed: `llama`, `mistral`, `gemma`, `phi`, `qwen` or `deepseek-r`. The installed version is read from Ollama itself, so a new release of any of those works the day it ships; families are tried in the order above, and within the first one you have installed, the newest wins.
159
-
160
- Ollama will not answer from a model outside those families, or from one tuned for a different job (any tag containing `coder`, `code`, `math`, `embed`, `guard`, `vision`, `uncensored` or `dolphin`). Such a model will still write fluent prose about your decisions, including relationships between them that do not exist, and it is not obvious from the output that anything went wrong. To use any model regardless, name it and it is used as-is:
161
-
162
- ```bash
163
- export ALIGN_OLLAMA_MODEL=my-fine-tune:latest
164
- ```
165
-
166
- If none is available, `align ask` still works - it prints a ranked list of the matching decisions instead of a synthesised paragraph. No key is ever required.
167
-
168
- **Note:** A Claude.ai or ChatGPT subscription is not the same as an API key - you need a separate API account. [Groq](https://console.groq.com/keys) offers a free tier with no credit card.
169
-
170
- In cloud mode, retrieval (the search over your graph) runs on Align's gateway and the provider key is only used locally to phrase the answer. In local-only mode, retrieval is on-device too.
171
-
172
- ## Authentication
173
-
174
- ```bash
175
- align login # opens your browser; the token arrives via a localhost callback
176
- align login --token algt_... # non-interactive, good for CI / self-hosted
177
- align whoami # verify current session
178
- align logout # clear stored credentials
179
- ```
180
-
181
- `align login` starts a small localhost listener, opens the Align sign-in page, and receives the token on the callback - nothing to paste. It prints the sign-in URL too, in case the browser does not open; if the callback cannot complete (a locked-down machine, no free port), use `align login --token` instead. Tokens are stored locally in your OS config directory. To create one manually, go to **Settings > API Tokens** in the Align web app.
182
-
183
- ## Cloud or local-only
184
-
185
- `align setup` offers two modes:
186
-
187
- - **Personal cloud** (default) - your decision graph is hosted at Align: synced across machines, backed up, and upgradeable to a shared team workspace. Connectors connect via **read-only browser OAuth** (no tokens to paste), and `align ask` retrieval runs server-side. Nothing you connect can be modified by the CLI - it only reads.
188
- - **Local-only** (`align setup --local`) - **no account, and no Align servers**: the graph, embeddings, and search all live in a SQLite database on your machine, and the CLI never sends your decisions to us.
189
-
190
- What uses the network in local-only mode, all worth knowing before you point it at work content:
191
-
192
- - The embedding model downloads once from huggingface.co (~23MB), on the first import.
193
- - `align import <tool>` calls that tool's API, read-only, with the token you pasted - that is what an import is. The data goes from your tool to your machine; none of it goes to Align.
194
- - **Only when an AI provider is available** - an API key in your environment, or a running Ollama, which needs no key - three surfaces call **your own provider**: `align ask` sends your question plus the titles and summaries of the decisions it retrieved (up to `--limit`, default 8), and `align check` and the MCP tool `align_check_alignment` send up to 2,000 characters of the proposed change paired with one retrieved decision at a time. The editor hook never does by default - it is retrieval only, provider or no provider. The one exception is explicit: adding `--block-on-critical` to your hook line opts that hook into background adjudication on the same terms as `align check`, roughly once per edit where retrieval found something.
195
- - Ollama runs on your own machine by default, so those calls stay local - unless you have pointed `OLLAMA_HOST` at another box, in which case they go there.
196
- - With no provider available at all, nothing goes to any AI provider - retrieval is on-device, so the editor hook still surfaces related decisions - and nothing ever goes to Align. The network surface is then just the one-time model download and whatever imports you run.
197
-
198
- How the local graph behaves:
199
-
200
- - Seeds from your git history out of the box; other sources connect by pasting a **read-only personal token** (OAuth needs the hosted callback, so it isn't available offline). Add `--env local` to any `align import <tool>` run.
201
- - **Re-importing is safe**: a decision is identified by its source URL and title, so running the same import twice updates what changed rather than duplicating the graph.
202
- - Related decisions are surfaced on-device by semantic similarity; typed relationships (supersedes / conflicts with / depends on) are typed at query time using **your own AI provider key** (Anthropic, OpenAI, or a local Ollama) - without one, related decisions still surface, just not typed. The heavier cross-tool relationship detection runs in the hosted gateway.
203
- - Run `align local status` to inspect the graph, `align local reset` to wipe it.
204
- - Works in local-only mode today: `setup`, `import <tool>`, `capture`, `ask`, `search`, `check`, `status`, `context sync`, `mcp`, and the `local` commands. Not yet routed to the local graph (they address the cloud gateway): `decisions`, `export`, `drift`, `links`, `spaces`, `check --resolve`, and the connector-scan commands under `align import`.
205
-
206
- Pick cloud for sync + team upgrade, local for maximum privacy. You can always start local and move to cloud later.
207
-
208
- ## Importing decisions
209
-
210
- Pull your existing work into the decision graph. The more sources you add, the richer the cross-tool relationship detection.
211
-
212
- **Easiest way: `align setup`.** It connects each source via a **read-only browser OAuth** consent - no tokens to create or paste. The CLI only ever *reads*; it can't modify your tools (write access lives only in the team/org bot apps). GitHub, Jira, Confluence, Slack, Microsoft Teams, Zoom, Linear, GitLab (gitlab.com), and Notion all use OAuth. Self-managed GitLab (a custom domain) uses a read-only token you paste.
213
-
214
- The same OAuth flow also works per source: `align import <source> --personal` opens the browser consent (or reuses the token a previous `align setup` cached) - no PAT to create. The `align import <source> --token ...` forms below are the manual / CI alternative (and how to connect self-managed hosts). Every import previews what will be imported and asks for confirmation before sending anything (use `--approve` to skip the prompt). In local-only mode, add `--env local` - a machine that has also logged in to cloud otherwise imports to the cloud graph.
215
-
216
- ### Git
217
-
218
- ```bash
219
- align import git
220
- ```
221
-
222
- | Flag | Default | Description |
223
- |------|---------|-------------|
224
- | `--limit` | `500` | Max commits to import |
225
- | `--branch` | current branch | Git branch to scan |
226
- | `--from` | - | Start date (ISO, e.g. `2025-01-01`) |
227
- | `--to` | - | End date (ISO) |
228
- | `--approve` | - | Skip confirmation prompt |
229
-
230
- ### GitHub / GitLab
231
-
232
- Prefer `align setup` - GitHub and gitlab.com connect via read-only OAuth (no token to create). Manual / self-managed alternative:
233
-
234
- ```bash
235
- align import github --token ghp_... # or connect via `align setup` (read-only OAuth)
236
- align import gitlab --token glpat-... # self-managed GitLab: create a read_api (read-only) token
237
- ```
238
-
239
- ### Jira
240
-
241
- ```bash
242
- align import jira \
243
- --token <your-jira-api-token> \
244
- --email your@email.com \
245
- --domain yourorg.atlassian.net
246
- ```
247
-
248
- ### Linear
249
-
250
- Prefer `align setup` - Linear connects via read-only OAuth (scope `read`). Manual alternative:
251
-
252
- ```bash
253
- align import linear --token lin_api_...
254
- ```
255
-
256
- ### Confluence
257
-
258
- ```bash
259
- align import confluence \
260
- --token <your-confluence-api-token> \
261
- --email your@email.com \
262
- --domain yourorg.atlassian.net
263
- ```
264
-
265
- ### Slack (experimental)
266
-
267
- Prefer `align setup` - Slack connects via read-only OAuth (read scopes only, no `chat:write`). Note: the Slack app must have public distribution enabled, or you authorize from its home workspace.
268
-
269
- > **Manual alternative:** `align import slack` requires a Slack **user** token (`xoxp-...`), not a bot token.
270
- >
271
- > To get one: go to [api.slack.com/apps](https://api.slack.com/apps), create an app, add these User Token Scopes under OAuth & Permissions: `channels:read`, `channels:history`, `groups:read`, `groups:history`. Install to your workspace and copy the OAuth User Token.
272
-
273
- ```bash
274
- align import slack --token xoxp-<your-slack-user-token>
275
- ```
276
-
277
- | Flag | Default | Description |
278
- |------|---------|-------------|
279
- | `--limit` | `50` | Max threads to import |
280
- | `--days-back` | `90` | How many days back to scan |
281
-
282
- ### Notion
283
-
284
- Create an internal integration with **only "Read content"** capability (no insert/update), then paste its secret:
285
-
286
- ```bash
287
- align import notion --token <your-notion-integration-token>
288
- ```
289
-
290
- ### Microsoft Teams / Zoom
291
-
292
- Prefer `align setup` (OAuth) - neither has a personal token a human can create in-app, so they are not offered in local-only setup. `align import teams --token <Graph token>` and `align import zoom --token <OAuth token>` exist for tokens you obtained elsewhere.
293
-
294
- ### Connector scans (cloud)
295
-
296
- With a cloud account, the gateway can also run connector-side scans and hold the results as suggestions for review:
297
-
298
- ```bash
299
- align import --all # start a scan across every enabled connector
300
- align import list # scan jobs and their status
301
- align import suggestions # review what a scan found
302
- align import scan-runs # scan history
303
- ```
304
-
305
- ## Capturing decisions
306
-
307
- ```bash
308
- # Capture a decision from a URL - the platform is detected automatically
309
- align capture https://github.com/org/repo/pull/42
310
- align capture https://yourco.atlassian.net/browse/ENG-123
311
- align capture https://yourco.slack.com/archives/C123/p1700000000000000
312
- ```
313
-
314
- `align capture` takes a URL; raw text capture is not supported from the CLI yet. Over MCP, `align_capture` accepts text too, in local-only mode.
315
-
316
- ## Searching and browsing
317
-
318
- ```bash
319
- align ask "any question in plain English" # natural language answer
320
- align search "authentication strategy" # keyword/semantic search - returns a list
321
- align decisions list # browse the graph (cloud)
322
- align decisions list --space backend # filter by space
323
- align decisions list --platform jira # filter by source platform
324
- align decisions show <id> # full detail for one decision (cloud)
325
- align links list # cross-tool decision relationships (cloud)
326
- align drift # decisions that may be out of date (cloud)
327
- align export # export decisions as a structured brief (cloud)
328
- ```
329
-
330
- `align ask` synthesises an answer. `align search` returns a ranked list - useful when you want to browse. The commands marked cloud address your hosted graph; in local-only mode use `align search`, `align ask` and `align local status` instead.
331
-
332
- ## Alignment check
333
-
334
- Check your current changes against the decision graph.
335
-
336
- ```bash
337
- align check # check the staged diff (falls back to the HEAD diff when nothing is staged)
338
- align check --all # check the full working-tree diff vs HEAD
339
- ```
340
-
341
- Four outcomes: aligned or nothing-related exits `0`; a conflict exits `1`; and `2` means the
342
- check retrieved decisions it could not adjudicate, or could not run at all - not a pass, and
343
- distinguishable from a conflict on purpose. That clean 0/1/2 contract is guaranteed under
344
- `--ci`; in default interactive mode a transport error or a missing git repository also exits
345
- `1`.
346
-
347
- Modes:
348
-
349
- | Mode | Behavior |
350
- |------|----------|
351
- | (default) | Human-readable output; exits `1` on any conflict. |
352
- | `--hook` | Pre-commit mode: silent when there's no context, only fails on **critical** conflicts. |
353
- | `--advisory` | Agent hook mode (detects pre vs post from the hook payload on stdin): **always exits 0**, emits related, unadjudicated decisions - or an explicit "could not check" notice - in the host's hook shape (`--format claude\|gemini\|pi\|opencode\|text`). Fail-open. |
354
- | `--advisory --block-on-critical` | Opt-in deferred adjudication: when retrieval finds related decisions, the hook additionally spawns a background full check, and a **retry of a change already judged a critical conflict is denied** (Claude Code `permissionDecision: "deny"`), with the verdict expiring after 15 minutes. The verdict is keyed on the tool, the target file and the text together, so a different file or an adjusted approach proceeds untouched - it catches a re-presentation, never a first proposal. Runs per edit, up to 3 at a time per project. In local mode it calls **your own AI provider**, which the default hook never does. |
355
- | `--ci` | Emits JSON to stdout; the 0/1/2 exit contract above. **Pass `--base`** or there is nothing to diff. |
356
-
357
- Useful flags: `--title "what this change decides"` improves adjudication on a bare diff;
358
- `--base <ref>` diffs `base...HEAD` instead of the staged diff; `--depth related|full|exhaustive`
359
- sets how deep an answer to request, where `related` is retrieval only and `exhaustive`
360
- adjudicates everything retrieved, for a strict CI gate whose `fail-on` treats unknown as a
361
- failure. `--depth` is ignored under `--advisory`, which is retrieval only by design.
362
-
363
- In CI, always pass `--base` - a clean checkout has no staged diff, and a check with nothing to
364
- diff would pass without looking:
365
-
366
- ```yaml
367
- - name: Check alignment
368
- run: align check --base origin/${{ github.base_ref }} --ci
369
- env:
370
- ALIGN_TOKEN: ${{ secrets.ALIGN_TOKEN }}
371
- ```
372
-
373
- Or use the published GitHub Action, which always passes `--base`, writes the verdict to the
374
- job summary and annotates the changed files:
375
- [`aligndottech/decision-check`](https://github.com/aligndottech/decision-check).
376
-
377
- When a check flags a conflict, resolve it so it stops surfacing (only meaningful while the
378
- current diff is conflicting):
379
-
380
- ```bash
381
- align check --resolve <decision_id>:honored # or overridden | context_changed
382
- ```
383
-
384
- An exit `2` can also mean the judge reached your change and declined to rule, which no re-run
385
- will change on its own. Answer it once, using the event id the failing check prints:
386
-
387
- ```bash
388
- align adjudicate <event-id> --verdict accepted --note "why this may proceed"
389
- ```
390
-
391
- `--verdict conflicting` records the opposite. The answer is matched against a digest of the
392
- content that was checked, so re-running the check on the same change finds it, and answering
393
- something you were never shown is not available.
394
-
395
- ## Write decisions into your agent's context files
396
-
397
- Agents read local files before they reach for any tool. `align context sync` writes your
398
- active decisions to `.align/decisions.md` and adds one import line to CLAUDE.md, so an
399
- agent knows what your team decided without a single tool call. Superseded and archived
400
- decisions are history, and stay out of it.
401
-
402
- ```bash
403
- align context sync # write .align/decisions.md, import it from CLAUDE.md
404
- ```
405
-
406
- Align owns `.align/decisions.md` outright and regenerates it on each sync - your CLAUDE.md
407
- is never rewritten, only ever appended with the single `@.align/decisions.md` line, once.
408
- If the repo has no CLAUDE.md, the command prints the line to add instead of inventing a
409
- file. Re-run after new decisions land; unchanged decisions produce a byte-identical file,
410
- so syncing never dirties a clean tree.
411
-
412
- ## MCP server
413
-
414
- Run Align as a local [Model Context Protocol](https://modelcontextprotocol.io) server so AI assistants (Claude Code, Claude Desktop, Cursor, Windsurf) can query your decision graph inline.
415
-
416
- ```bash
417
- align mcp --setup # auto-configure detected editors
418
- align mcp # start the server directly
419
- ```
420
-
421
- ### Manual configuration
422
-
423
- **Claude Desktop** - `~/Library/Application Support/Claude/claude_desktop_config.json` (macOS) or `~/.config/Claude/claude_desktop_config.json` (Linux):
424
-
425
- ```json
426
- {
427
- "mcpServers": {
428
- "align": {
429
- "command": "align",
430
- "args": ["mcp"],
431
- "env": { "ALIGN_TOKEN": "algt_..." }
432
- }
433
- }
434
- }
435
- ```
436
-
437
- **Claude Code** - `~/.claude.json` or your workspace `.mcp.json`:
438
-
439
- ```json
440
- {
441
- "mcpServers": {
442
- "align": { "command": "align", "args": ["mcp"] }
443
- }
444
- }
445
- ```
446
-
447
- **Cursor** - `~/.cursor/mcp.json` (same format as Claude Code above).
40
+ 1. **Import** pulls decisions out of the tools you already use. Read-only, nothing is modified.
41
+ 2. Align links them into a **decision graph**: what was decided, why, who decided it, and how
42
+ decisions relate (supersedes, conflicts with, depends on).
43
+ 3. Your agents **query and check against** it, over MCP, an edit hook, CI, or `align ask`.
448
44
 
449
- **pi** - MCP is not built in; install the adapter first with `pi install npm:pi-mcp-adapter`, then restart pi. `align setup` writes `~/.pi/agent/mcp.json` (or `$PI_CODING_AGENT_DIR/mcp.json`):
45
+ ## Your agent checks before it writes
450
46
 
451
- ```json
452
- {
453
- "mcpServers": {
454
- "align": { "command": "align", "args": ["mcp"], "directTools": true }
455
- }
456
- }
457
- ```
47
+ `align setup` wires Align in four ways, so context fires whether or not the model thinks to ask.
458
48
 
459
- `directTools` matters: the adapter is lazy by default and hides every server behind a single proxy tool the agent has to search first, which defeats the "check alignment *before* the edit" instruction.
49
+ | | What you get |
50
+ |---|---|
51
+ | **MCP server** | Claude Code, Cursor, Claude Desktop and Windsurf query the graph inline |
52
+ | **Edit hooks** | Prior decisions reach the model before it writes. Claude Code, pi, Gemini CLI, OpenCode |
53
+ | **Editor rules** | A managed block in `CLAUDE.md`, `AGENTS.md` and `.cursor/rules/align.md` |
54
+ | **Shared `.mcp.json`** | One committed file wires up the whole team |
460
55
 
461
- Once configured, your assistant can call these tools to query and update your decision graph in context:
56
+ The hook is **non-blocking and fail-open**. It never denies an edit by default, and if Align is
57
+ missing, slow or unreachable the edit proceeds untouched. It needs no AI provider key.
462
58
 
463
- | Tool | Purpose |
464
- |------|---------|
465
- | `align_ask` | Natural-language question about past decisions |
466
- | `align_search` | Search the decision graph |
467
- | `align_capture` | Capture a decision from a URL (raw text too, in local-only mode) |
468
- | `align_check_alignment` | Check a proposed change for conflicts with prior decisions |
469
- | `align_check_drift` | Check whether code/config has drifted from a decision |
470
- | `align_get_related_decisions` | Decisions related to a file or module |
471
- | `align_get_conflicts` | Active conflicts in the graph |
472
- | `align_get_impact` | Upstream/downstream impact of a decision |
59
+ Cursor and Codex CLI can't do the pre-edit hook, and that's a limit of those hosts. They get the
60
+ other three. Full per-host matrix: [docs/agent-hooks.md](docs/agent-hooks.md).
473
61
 
474
- ## Environments
62
+ > The first time Claude Code loads a project with a committed hook, it shows a one-time "approve
63
+ > hooks" prompt. Accept it to enable automatic alignment.
475
64
 
476
- By default the CLI targets `prod` (`api.align.tech`). Use a sticky default, or override per command - `--env` belongs after the command name:
65
+ ## Everyday commands
477
66
 
478
67
  ```bash
479
- align env set preview # stick to preview for this machine
480
- align env get # show current default
481
- align search "auth" --env local # one-off override on any command
68
+ align ask "how does our auth work" # natural language answer with sources
69
+ align search "authentication" # ranked list, no AI needed
70
+ align ask src/auth/session.ts # a file path finds decisions about that file
71
+ align import git # pull commit history, no token
72
+ align import jira --token ... # add more sources
73
+ align capture <url> # capture one decision from a PR, ticket or thread
74
+ align check # check your staged diff against the graph
75
+ align context sync # write decisions to .align/decisions.md
482
76
  ```
483
77
 
484
- One naming trap: `--env local` means your embedded SQLite graph only after `align setup --local`
485
- has configured it. On a machine that never ran that, `local` is a developer convenience that
486
- addresses a gateway on `localhost:8080`.
487
-
488
- ## Environment variables
489
-
490
- | Variable | Description |
491
- |----------|-------------|
492
- | `ALIGN_TOKEN` | API token (alternative to `align login`) |
493
- | `ALIGN_ENV` | Default environment (`prod`, `preview`, `local`) |
494
- | `ALIGN_GATEWAY_URL` | Override gateway URL (self-hosted) |
495
- | `ALIGN_TENANT_ID` | Override tenant ID (self-hosted / CI). Against `preview` or `prod` it needs `ALIGN_TOKEN` set too: a tenant on its own authenticates nothing, and the CLI refuses rather than sending it |
496
- | `ALIGN_TELEMETRY` | Set it to anything other than `1`/`true`/`yes`/`on` to send no usage events at all (empty counts as unset, so leaves them on). Cloud mode reports one `cli.command` event per invocation to the same gateway - the command name, nothing else. Local mode sends nothing either way |
497
- | `ALIGN_DEBUG` | Set to any value to print the full stack trace when the CLI crashes with an unexpected error |
498
- | `ANTHROPIC_API_KEY` | Anthropic API key for `align ask` synthesis |
499
- | `OPENAI_API_KEY` | OpenAI API key for `align ask` synthesis |
500
- | `GEMINI_API_KEY` / `GOOGLE_API_KEY` | Google Gemini API key for `align ask` synthesis |
501
- | `GROQ_API_KEY` | Groq API key for `align ask` synthesis |
502
- | `MISTRAL_API_KEY` | Mistral API key for `align ask` synthesis |
503
- | `GROK_API_KEY` / `XAI_API_KEY` | xAI Grok API key for `align ask` synthesis |
504
- | `ALIGN_LLM_BASE_URL` | Any OpenAI-compatible endpoint. Outranks the named keys above |
505
- | `ALIGN_LLM_API_KEY` | Bearer token for `ALIGN_LLM_BASE_URL` |
506
- | `ALIGN_LLM_MODEL` | Model name for `ALIGN_LLM_BASE_URL` (default `gpt-4o-mini`) |
507
- | `OLLAMA_HOST` | Ollama host (default: `http://localhost:11434`). Your own machine by default; point it at a shared box and local relationship typing goes there instead |
508
- | `ALIGN_OLLAMA_MODEL` | Use this Ollama model, whatever family it is from |
509
- | `ALIGN_INGEST_CONCURRENCY` | Concurrent ingest batch requests during imports (default `6`) |
510
- | `PI_CODING_AGENT_DIR` | Where `align mcp --setup` writes pi's `mcp.json`, if pi keeps its config somewhere non-standard |
78
+ `align ask` needs an AI provider to write prose. Without one it returns the matching decisions
79
+ as a ranked list, which needs no key. Bring your own: Anthropic, OpenAI, Gemini, Groq, Mistral,
80
+ Grok, any OpenAI-compatible endpoint, or a local Ollama.
81
+ [Setting one up](docs/configuration.md#ai-provider).
511
82
 
512
- Advanced: override the model per provider with `ALIGN_ANTHROPIC_MODEL`, `ALIGN_OPENAI_MODEL`, `ALIGN_GEMINI_MODEL`, `ALIGN_GROQ_MODEL`, `ALIGN_MISTRAL_MODEL`, `ALIGN_GROK_MODEL`, or `ALIGN_OLLAMA_MODEL`.
83
+ ## Docs
513
84
 
514
- ## Self-hosted
515
-
516
- ```bash
517
- align login --env local --token algt_...
518
- # or
519
- ALIGN_GATEWAY_URL=https://api.yourco.com align decisions list
520
- ```
85
+ | | |
86
+ |---|---|
87
+ | [Importing](docs/importing.md) | Every source, tokens, flags |
88
+ | [Alignment check](docs/check.md) | Modes, exit codes, CI, the GitHub Action |
89
+ | [Cloud or local-only](docs/local-mode.md) | What runs where, what touches the network |
90
+ | [MCP server](docs/mcp.md) | Editor config, the tools your assistant gets |
91
+ | [Configuration](docs/configuration.md) | AI providers, env vars, auth, self-hosting |
92
+ | [Agent hooks](docs/agent-hooks.md) | Per-host capability matrix |
93
+ | [All commands](docs/commands.md) | Full reference |
521
94
 
522
- `ALIGN_GATEWAY_URL` changes **where** the CLI talks to; `--env` changes **how** it authenticates, and the two are independent. That matters if you also set `ALIGN_TENANT_ID`, because a tenant with nothing authenticating it is refused in `prod`/`preview` (auth mode):
95
+ ## Why bother
523
96
 
524
- | Your gateway | Use | Why |
525
- |---|---|---|
526
- | enforces auth (the default) | `ALIGN_TOKEN` alongside `ALIGN_GATEWAY_URL` | the token is what names your tenant, so `ALIGN_TENANT_ID` is optional |
527
- | runs in demo mode | `--env local` with `ALIGN_GATEWAY_URL` | `local` is the mode where an `x-tenant-id` header with no bearer is the intended way to address a gateway |
528
-
529
- With no `--env` the CLI defaults to `prod`, which authenticates. So `ALIGN_TENANT_ID` set on its own, with no token, is refused rather than sent: it cannot succeed against a gated route, and against an ungated one it would read a tenant you were never authorised for.
97
+ In a published benchmark, giving a coding agent recorded product decisions took decision
98
+ compliance from 46% to 95%
99
+ ([Dillon & Varanasi, arXiv:2605.08112](https://arxiv.org/abs/2605.08112) - a small vendor study,
100
+ 8 tasks and 41 decision points, and it isn't our data).
530
101
 
531
- ## Command reference
102
+ Wiring context into an agent is the easy part, and this repo is the open-source version of it.
103
+ The hard part is the record underneath: what your team actually decided, across every tool, kept
104
+ current.
532
105
 
533
- ```
534
- align setup Guided onboarding: connect tools, configure MCP + auto-alignment
535
- align login Authenticate with Align
536
- align logout Remove stored credentials
537
- align whoami Show current authenticated user and tenant
538
- align ask <query> Ask a natural language question (or pass a file path)
539
- align search <query> Keyword/semantic search - returns a ranked list
540
- align capture <url> Capture a decision from a URL (platform auto-detected)
541
- align check Check current changes against the decision graph
542
- align adjudicate <event-id> Answer a check that reached the judge and declined to rule (cloud)
543
- align import git Import from Git commit history (no auth)
544
- align import github Import from GitHub
545
- align import gitlab Import from GitLab
546
- align import jira Import from Jira
547
- align import linear Import from Linear
548
- align import confluence Import from Confluence
549
- align import slack Import from Slack (experimental)
550
- align import teams Import from Microsoft Teams
551
- align import zoom Import from Zoom recording transcripts
552
- align import notion Import from Notion
553
- align import --all Start a connector scan across enabled connectors (cloud)
554
- align import list List scan jobs (cloud)
555
- align import suggestions Review scan suggestions (cloud)
556
- align import scan-runs Scan history (cloud)
557
- align decisions list List decisions in your graph (cloud)
558
- align decisions show <id> Show full detail for a decision (cloud)
559
- align status Value readout: what your graph has done for you
560
- align context sync Write decisions to .align/decisions.md + CLAUDE.md import
561
- align export Export decisions as a structured brief (cloud)
562
- align drift Show decisions that may be out of date (cloud)
563
- align links list Show cross-tool decision relationships (cloud)
564
- align spaces list List spaces (project scopes) (cloud)
565
- align env set <name> Set default environment
566
- align env get Show current environment
567
- align mcp Start local MCP server
568
- align mcp --setup Auto-configure editors to use Align as MCP server
569
- align local start Initialize a local decision graph
570
- align local status Show local graph statistics
571
- align local reset Wipe the local graph
572
- ```
106
+ Want a hand setting it up? I do free 30 minute setup calls:
107
+ https://calendly.com/tom-align/setup
573
108
 
574
109
  ## License
575
110
 
576
- MIT - see [LICENSE](./LICENSE). The CLI and MCP server are open source; the hosted gateway is a separate commercial service.
111
+ MIT, see [LICENSE](./LICENSE). The CLI and MCP server are open source, along with the
112
+ [connector SDK](https://github.com/aligndottech/align-connector-sdk). The hosted gateway is a
113
+ separate commercial service.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aligndottech/cli",
3
- "version": "0.21.1",
3
+ "version": "0.22.0",
4
4
  "mcpName": "io.github.aligndottech/cli",
5
5
  "type": "module",
6
6
  "description": "Align CLI - capture decisions, check alignment, and manage connectors",