@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 +105 -32
- package/package.json +1 -1
- package/src/commands/login.mjs +29 -4
package/README.md
CHANGED
|
@@ -1,39 +1,92 @@
|
|
|
1
|
-
|
|
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
|
-
|
|
8
|
+
<h1 align="center">@anyslate/cli</h1>
|
|
4
9
|
|
|
5
|
-
|
|
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
|
-
|
|
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
|
-
|
|
22
|
+
---
|
|
10
23
|
|
|
11
|
-
|
|
24
|
+
## What is this?
|
|
12
25
|
|
|
13
|
-
|
|
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
|
-
|
|
30
|
+
**This CLI is how that memory gets filled in when no AI is watching.**
|
|
16
31
|
|
|
17
|
-
|
|
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 |
|
|
20
|
-
| Claude edits `routes/auth.ts` |
|
|
21
|
-
| Claude runs `npm test` |
|
|
22
|
-
| You `git commit` |
|
|
23
|
-
|
|
|
24
|
-
| You close the laptop |
|
|
25
|
-
| **You
|
|
26
|
-
| **You want to
|
|
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
|
|
75
|
+
The first six rows are automatic. The last two are the rare moments you'd type something.
|
|
29
76
|
|
|
30
|
-
### What hook captures
|
|
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:
|
|
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
|
|
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,
|
|
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
|
|
45
|
-
|
|
46
|
-
|
|
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
|
-
|
|
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
|
-
|
|
106
|
+
## Why bother
|
|
53
107
|
|
|
54
108
|
- **You stop forgetting to checkpoint.** Capture happens whether you remember or not.
|
|
55
|
-
- **
|
|
56
|
-
|
|
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
|
|
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.
|
|
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": {
|
package/src/commands/login.mjs
CHANGED
|
@@ -68,14 +68,39 @@ export async function runLogin(argv, deps = {}) {
|
|
|
68
68
|
// ---------------------------------------------------------------------------
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
|
-
* Resolve the service root
|
|
72
|
-
*
|
|
73
|
-
*
|
|
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
|
|
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` +
|