@anyslate/cli 0.3.0 → 0.3.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.
package/README.md CHANGED
@@ -1,39 +1,92 @@
1
- # @anyslate/cli
1
+ <p align="center">
2
+ <picture>
3
+ <source media="(prefers-color-scheme: dark)" srcset="https://mcp.anyslate.io/logo-full-dark.png">
4
+ <img src="https://mcp.anyslate.io/logo-full-light.png" alt="AnySlate" width="280">
5
+ </picture>
6
+ </p>
2
7
 
3
- The bridge that lets your AI tools quietly tell AnySlate what you've been working on - without you having to remember to checkpoint anything.
8
+ <h1 align="center">@anyslate/cli</h1>
4
9
 
5
- ## What this is
10
+ <p align="center">
11
+ <strong>Your AI tools remember what you did. Automatically.</strong><br>
12
+ Lifecycle hooks, git commits and CI runs → your AnySlate memory, without you lifting a finger.
13
+ </p>
6
14
 
7
- A small command-line tool that runs on your machine. Your AI tools (Claude Code, git, CI) call it on your behalf as you work. It records what's happening into your AnySlate **Activity feed** - file edits, shell commands, commits - automatically and continuously.
15
+ <p align="center">
16
+ <a href="https://www.npmjs.com/package/@anyslate/cli"><img alt="npm" src="https://img.shields.io/npm/v/@anyslate/cli?color=f45f5f&labelColor=252c3e"></a>
17
+ <a href="https://nodejs.org"><img alt="node" src="https://img.shields.io/node/v/@anyslate/cli?color=f45f5f&labelColor=252c3e"></a>
18
+ <img alt="dependencies" src="https://img.shields.io/badge/runtime%20deps-0-f45f5f?labelColor=252c3e">
19
+ <img alt="license" src="https://img.shields.io/npm/l/@anyslate/cli?color=f45f5f&labelColor=252c3e">
20
+ </p>
8
21
 
9
- Think of it like a Fitbit, but for your AI work. You don't tell a Fitbit "I just took 47 steps." You wear it, walk around, the count updates. Same shape here. You install this once, paste a small config snippet into your AI tool's settings, and from that point on, capture happens in the background while you work.
22
+ ---
10
23
 
11
- **You almost never run a CLI command yourself.** The tool exists to be invisible plumbing that other tools call. The setup below takes 5 minutes and ends with `anyslate doctor` telling you it works.
24
+ ## What is this?
12
25
 
13
- ## Where it fits in your workflow
26
+ **AnySlate** gives your AI tools a long-term memory — a place where decisions, tasks and
27
+ work history live, so Claude, Cursor or Codex can pick up where you left off instead of
28
+ starting cold every session.
14
29
 
15
- Most days you'll never type `anyslate` yourself. Here's what actually happens:
30
+ **This CLI is how that memory gets filled in when no AI is watching.**
16
31
 
17
- | Trigger | Who calls the CLI | What lands |
32
+ An AI can only record something if it decides to call a tool. But a lot of your real work
33
+ happens outside that: you commit code, CI deploys, you run the test suite, you make a call
34
+ while staring at a terminal. None of it has an AI present to write it down.
35
+
36
+ This CLI closes that gap. Your tools call it for you:
37
+
38
+ ```
39
+ Claude Code fires a hook ─┐
40
+ git runs post-commit ─┼─→ anyslate ─→ AnySlate memory
41
+ CI finishes a deploy ─┘
42
+ ```
43
+
44
+ Think of it like a fitness tracker for your engineering work. You don't tell a fitness
45
+ tracker "I just took 47 steps." You wear it, you walk, the number goes up. Same idea:
46
+ install this once, paste one config block, and capture happens in the background forever.
47
+
48
+ > **You will almost never type `anyslate` yourself.** It is plumbing that other tools call.
49
+ > Setup takes about five minutes and ends with `anyslate doctor` confirming it works.
50
+
51
+ ## Quick start
52
+
53
+ ```bash
54
+ npm i -g @anyslate/cli # 1. install
55
+ anyslate login # 2. sign in (opens your browser)
56
+ anyslate doctor # 3. confirm it works
57
+ ```
58
+
59
+ Then paste the hook block into `~/.claude/settings.json` — see
60
+ [Wiring into Claude Code](#wiring-into-claude-code) — and restart Claude Code. Done.
61
+
62
+ ## What actually gets captured
63
+
64
+ | Trigger | Who calls the CLI | What lands in your memory |
18
65
  |---|---|---|
19
- | You open a Claude Code session | Claude Code's `SessionStart` hook | Session-start activity; the AnySlate session is created on first sight |
20
- | Claude edits `routes/auth.ts` | Claude Code's `PostToolUse` hook | "Edited routes/auth.ts" in the activity ledger |
21
- | Claude runs `npm test` | Claude Code's `PostToolUse` hook | Command + output in the activity ledger |
22
- | You `git commit` | Git's `post-commit` hook | Commit metadata + diff stats captured |
23
- | Teammate merges your PR | GitHub webhook (no CLI needed) | PR-merged event captured |
24
- | You close the laptop | Claude Code's `Stop` hook | Session-end marker captured |
25
- | **You manually want to checkpoint a decision** | **You typing `anyslate checkpoint ...`** | **Captured immediately** |
26
- | **You want to upload a file as an artifact** | **You typing `anyslate upload-artifact ...`** | **File stored, returns `cloud://artifact/<id>`** |
66
+ | You open a Claude Code session | `SessionStart` hook | Session start + working directory; the AnySlate memory is created on first sight |
67
+ | Claude edits `routes/auth.ts` | `PostToolUse` hook | `Edited routes/auth.ts` in the activity ledger |
68
+ | Claude runs `npm test` | `PostToolUse` hook | Command + exit status in the activity ledger |
69
+ | You `git commit` | git `post-commit` hook | Commit metadata and diff stats |
70
+ | A teammate merges your PR | GitHub webhook (no CLI needed) | PR-merged event |
71
+ | You close the laptop | `Stop` hook | Session-end marker |
72
+ | **You want to record a decision** | **you typing `anyslate checkpoint`** | **Captured immediately, as a decision** |
73
+ | **You want to attach a file** | **you typing `anyslate upload-artifact`** | **Stored, returns `cloud://artifact/<id>`** |
27
74
 
28
- The first six rows are automatic - that's what the lifecycle hooks do. The bottom two are the rare moments where you'd actually type something yourself.
75
+ The first six rows are automatic. The last two are the rare moments you'd type something.
29
76
 
30
- ### What hook captures actually write - read this before you form expectations
77
+ ### What hook captures write read this before forming expectations
31
78
 
32
- Hook captures land in a dedicated **`## Activity Ledger`** section of your memory page: which files were touched, which commands ran, what exit status they returned. That is the whole contract. The ledger is capped (25 files / 15 commands per entry) and is built deterministically - no LLM runs in the hook path, so capture never bills against your AI quota.
79
+ Hook captures land in a dedicated **`## Activity Ledger`** section of your memory page:
80
+ which files were touched, which commands ran, what exit status they returned. That is the
81
+ whole contract. The ledger is capped (25 files / 15 commands per entry) and is built
82
+ deterministically — **no LLM runs in the hook path**, so background capture never bills
83
+ against your AI quota.
33
84
 
34
- Hook captures **do not** write to the **Decisions** or **Open Tasks** sections. Those stay human- and LLM-authored, and are populated by `anyslate checkpoint`, by the in-host MCP flow (`/anyslate-new`, `/anyslate-continue`), or by you editing the page. A hook firing on `Bash` has no decision in it to extract, and the product no longer pretends otherwise.
85
+ Hook captures **do not** write to **Key Decisions** or **Open Tasks**. Those stay human-
86
+ and LLM-authored. A hook firing on `Bash` has no decision inside it to extract, and the
87
+ product no longer pretends otherwise.
35
88
 
36
- If you want a decision recorded, type it:
89
+ If you want a decision recorded, say so:
37
90
 
38
91
  ```bash
39
92
  anyslate checkpoint --note "Decided: Redis for session cache, not memcached"
@@ -41,19 +94,26 @@ anyslate checkpoint --note "Decided: Redis for session cache, not memcached"
41
94
 
42
95
  ### Approval: hook captures auto-promote
43
96
 
44
- Every lifecycle hook - `Edit` / `Write` / `MultiEdit`, `Bash`, and the session start/stop markers - classifies **low-risk** and auto-promotes on its own, 30-90 seconds after capture. An edit-heavy session does **not** build an approval queue.
45
-
46
- Rows still wait for approval in **AI Memory → Activity** when they are genuinely riskier: a capture aimed at a different session than the one that produced it, a decision that supersedes an earlier one, a submission from an untrusted source, or one carrying `confidence < 0.8`.
47
-
48
- File edits used to map to `artifact_produced`, which is high-risk by classification. That was wrong on two counts: it forced a manual approval on the single most common event in a session, and `artifact_produced` is a contract meaning "I stored an artifact, here is its id" - which a hook can never satisfy, so the server rejected it. Edits now map to `task_completed` and carry the touched paths in the ledger.
97
+ Every lifecycle hook `Edit` / `Write` / `MultiEdit`, `Bash`, and the session start/stop
98
+ markers — classifies **low-risk** and auto-promotes on its own, 30–90 seconds after
99
+ capture. An edit-heavy session does **not** build an approval queue.
49
100
 
50
- ## What you get back
101
+ Rows still wait for approval in **AI Memory → Activity** when they are genuinely riskier: a
102
+ capture aimed at a different session than the one that produced it, a decision that
103
+ supersedes an earlier one, a submission from an untrusted source, or one carrying
104
+ `confidence < 0.8`.
51
105
 
52
- After the setup is in place:
106
+ ## Why bother
53
107
 
54
108
  - **You stop forgetting to checkpoint.** Capture happens whether you remember or not.
55
- - **AnySlate sees what actually happened, not just what the AI claimed.** Git knows what files really changed. The hooks know what commands really ran.
56
- - **Your work follows you across tools.** Claude Code at home, Cursor at work, terminal in between - all feed the same memory. (Cursor and Windsurf feed it through the in-host MCP server, not this CLI - see the FAQ.)
109
+ - **You see what actually happened, not just what the AI claimed.** If the agent says it
110
+ implemented `TokenRefreshService` and the diff is whitespace, that discrepancy is
111
+ visible. You trust the AI because you can check, not because you have to assume.
112
+ - **Your work follows you across tools.** Claude Code at home, Cursor at work, a terminal
113
+ in between — all feeding one memory. (Cursor and Windsurf feed it through the in-host
114
+ MCP server rather than this CLI — see the [FAQ](#faq).)
115
+ - **Nothing is canonical until it is.** Anything pending can be rejected without ever
116
+ touching a memory page.
57
117
 
58
118
  ## Install
59
119
 
@@ -237,7 +297,20 @@ Content is read as UTF-8. Binary files are not supported and are refused rather
237
297
 
238
298
  Two paths, one config file. `--token` selects the static path; its absence selects the browser flow.
239
299
 
240
- Both end the same way: one live request to `/mcp/auth/verify` that proves reachability, URL shape, token validity and scopes in a single round trip, then a `0600` write to `~/.anyslate/cli.json`. **On verification failure it writes nothing and exits non-zero.** Both are idempotent — they preserve the `apiUrl` and `handle` you already had (and re-normalize a stored `apiUrl` that carries a stale `/mcp` suffix).
300
+ Both end the same way: one live request to `/mcp/auth/verify` that proves reachability, URL shape, token validity and scopes in a single round trip, then a `0600` write to `~/.anyslate/cli.json`. **On verification failure it writes nothing and exits non-zero.** Both preserve the `handle` you already had.
301
+
302
+ **The sign-in target is explicit, never sticky.** Omitting `--api-url` always means
303
+ **production**, even if your config currently points somewhere else — the stored value is
304
+ deliberately not inherited. If it does point elsewhere, the CLI says so and shows you the
305
+ flag to use:
306
+
307
+ ```
308
+ anyslate: signing in to production (https://mcp.anyslate.io).
309
+ anyslate: your config points at https://…workers.dev — pass `--api-url https://…workers.dev` to sign in there instead.
310
+ ```
311
+
312
+ This matters because the target ends up in a URL printed to your terminal and opened in a
313
+ browser. A short command should never quietly aim at a non-production host.
241
314
 
242
315
  A token missing the `memory:write` scope is a **warning**, not a block - but `anyslate hook` needs it, so heed it.
243
316
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@anyslate/cli",
3
- "version": "0.3.0",
3
+ "version": "0.3.1",
4
4
  "description": "AnySlate CLI - lifecycle hooks, git/CI capture, and manual checkpoints for AI memory. Validates its connection at login, diagnoses itself with `anyslate doctor`, and fails open without failing silent.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -68,14 +68,39 @@ export async function runLogin(argv, deps = {}) {
68
68
  // ---------------------------------------------------------------------------
69
69
 
70
70
  /**
71
- * Resolve the service root exactly as the static path always has: an explicit
72
- * --api-url wins, else the stored value is inherited AND re-normalized so a
73
- * previously-broken `/mcp`-suffixed apiUrl cannot survive a re-login.
71
+ * Resolve the service root for `login`.
72
+ *
73
+ * `--api-url` wins. When it is ABSENT the answer is always production — the
74
+ * stored value is deliberately NOT inherited.
75
+ *
76
+ * Inheriting it (the pre-0.3.1 behaviour) meant that once anything had written
77
+ * a non-production apiUrl — a past `--api-url` run, or a stale config from an
78
+ * older build — a bare `anyslate login` silently kept signing in to that
79
+ * environment. A user who typed the shortest possible command got a non-obvious
80
+ * endpoint, and a non-production hostname ended up in the URL printed to the
81
+ * terminal and opened in a browser. Sign-in is the one place the target must be
82
+ * explicit rather than sticky: pass `--api-url` for dev or local, omit it for
83
+ * production. Every other command still reads the stored apiUrl as before —
84
+ * this override applies to `login` only.
74
85
  */
75
86
  function resolveRoot(flags, existing, io) {
76
- const merged = flags.apiUrl ?? existing.apiUrl ?? existing.api_url ?? DEFAULT_API_URL;
87
+ const stored = existing.apiUrl ?? existing.api_url ?? null;
88
+ const merged = flags.apiUrl ?? DEFAULT_API_URL;
77
89
  const shape = checkUrlShape(merged);
78
90
  const root = shape.ok ? shape.root : normalizeApiRoot(merged);
91
+
92
+ // Say so when we are switching them off a stored non-production endpoint, so
93
+ // the change of target is never silent in either direction.
94
+ if (!flags.apiUrl && stored) {
95
+ const storedRoot = normalizeApiRoot(stored);
96
+ if (storedRoot && storedRoot !== root) {
97
+ io.out.write(
98
+ `anyslate: signing in to production (${root}).\n` +
99
+ `anyslate: your config points at ${storedRoot} — pass \`--api-url ${storedRoot}\` to sign in there instead.\n`,
100
+ );
101
+ }
102
+ }
103
+
79
104
  if (shape.ok && shape.normalized) {
80
105
  io.out.write(
81
106
  `anyslate: apiUrl "${shape.original}" ends in /mcp — the CLI wants the service ROOT and appends /mcp itself.\n` +