@invariant.guru/cli 0.7.1 → 0.8.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 +211 -37
- package/dist/main.js +956 -930
- package/dist/ui/assets/Editor-0hQt3BGG.js +38 -0
- package/dist/ui/assets/{MarkdownBody-mdOs0gnW.js → MarkdownBody-BZDzSb5s.js} +4 -4
- package/dist/ui/assets/Preview-DDmkokv0.js +1 -0
- package/dist/ui/assets/index-CviOvvQi.js +260 -0
- package/dist/ui/assets/index-DJDYH97C.css +1 -0
- package/dist/ui/index.html +28 -3
- package/package.json +5 -4
- package/dist/ui/assets/Editor-DOz_xbEw.js +0 -38
- package/dist/ui/assets/Preview-D2NRuXXW.js +0 -1
- package/dist/ui/assets/index-D7CqVS9q.css +0 -1
- package/dist/ui/assets/index-Xjxl1JDa.js +0 -253
package/README.md
CHANGED
|
@@ -1,6 +1,47 @@
|
|
|
1
1
|
# Invariant CLI
|
|
2
2
|
|
|
3
|
-
A
|
|
3
|
+
A harness-agnostic workspace for spec-driven AI development. The **session file
|
|
4
|
+
is the unit of work**: what was asked, the plan, and where it has got to, in one
|
|
5
|
+
markdown file your agent reads and reports back into.
|
|
6
|
+
|
|
7
|
+
It is also a package manager for agent context — agents, skills, commands, rules
|
|
8
|
+
and instructions, stored **once** in `.agents/` (the
|
|
9
|
+
[Agent Skills](https://agentskills.io) standard most harnesses read natively),
|
|
10
|
+
with each CLI target getting a view of it rather than its own copy.
|
|
11
|
+
|
|
12
|
+
Everything is local. No account, no server, no network.
|
|
13
|
+
|
|
14
|
+
## We never spend tokens
|
|
15
|
+
|
|
16
|
+
**Invariant does not call an LLM. Ever.**
|
|
17
|
+
|
|
18
|
+
You already pay for a harness subscription — Claude Code, Codex, Cursor,
|
|
19
|
+
whatever it is — and you are not going to pay a second time, in tokens, for a
|
|
20
|
+
management layer. So this tool ships no API key, proxies no model traffic, and
|
|
21
|
+
runs no inference. Your agent calls *it*:
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
invariant session next <id> # what to do now, and the next command
|
|
25
|
+
invariant session status <id> planning # …and the agent reports back
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Those are ordinary shell commands. Any harness that can run one participates,
|
|
29
|
+
and nothing here is coupled to any of them.
|
|
30
|
+
|
|
31
|
+
## Where your work lives
|
|
32
|
+
|
|
33
|
+
On your machine, in your repository:
|
|
34
|
+
|
|
35
|
+
| | |
|
|
36
|
+
|---|---|
|
|
37
|
+
| `.invariant/sessions/` | your sessions, as markdown |
|
|
38
|
+
| `.invariant/events/` | a local journal of what changed, git-ignored |
|
|
39
|
+
| `~/.invariant/` | the daemon's own state: which projects, which port, its log |
|
|
40
|
+
|
|
41
|
+
Sessions contain code excerpts and architecture plans, which is exactly why none
|
|
42
|
+
of it goes anywhere. When there is a hosted layer for teams it will be opt-in, it
|
|
43
|
+
will relay these same events and nothing else, and it will be self-hostable from
|
|
44
|
+
the day it exists.
|
|
4
45
|
|
|
5
46
|
## Requirements
|
|
6
47
|
|
|
@@ -28,6 +69,46 @@ invariant update --check
|
|
|
28
69
|
invariant update
|
|
29
70
|
```
|
|
30
71
|
|
|
72
|
+
## The loop, end to end
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
invariant init claude # once, at the repository root
|
|
76
|
+
invariant ui # the dashboard; leave it running
|
|
77
|
+
invariant plan "add the cargo booking command"
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`plan` prints a line to paste into your agent:
|
|
81
|
+
|
|
82
|
+
```
|
|
83
|
+
execute instructions from /repo/.invariant/sessions/session-01a0….md
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
From there the agent drives it, and the board follows along:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
invariant session status <id> planning # it announces itself
|
|
90
|
+
invariant session status <id> planned --title "…" # the plan is ready — your turn
|
|
91
|
+
invariant session status <id> in-progress # you said go
|
|
92
|
+
invariant session step complete <id> 1 # …and it works through the plan
|
|
93
|
+
invariant session status <id> executed --summary "…"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
You never type those — the agent does, because `invariant sync` taught it the
|
|
97
|
+
protocol. When it is unsure, it asks:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
invariant session next <id>
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
Then have the work read by someone who did not write it:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
invariant review open --session <id> # paste into a NEW conversation
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Triage the comments in the UI, and either implement the accepted ones or send
|
|
110
|
+
them to the merge request. That is the whole loop.
|
|
111
|
+
|
|
31
112
|
## Getting Started
|
|
32
113
|
|
|
33
114
|
The core loop is **init → install → add → sync**, with `plan` on top when you start a task.
|
|
@@ -242,6 +323,21 @@ invariant ui status # the daemon, and the projects it serves
|
|
|
242
323
|
invariant ui stop # stop it
|
|
243
324
|
```
|
|
244
325
|
|
|
326
|
+
`invariant ui` is the **daemon** with a browser opened on top of it. The daemon
|
|
327
|
+
is the process that serves every project, watches for changes, and journals
|
|
328
|
+
them; scripts and hooks talk to it by its own name:
|
|
329
|
+
|
|
330
|
+
```bash
|
|
331
|
+
invariant daemon start # no browser
|
|
332
|
+
invariant daemon status --json
|
|
333
|
+
invariant daemon logs -f # ~/.invariant/logs/daemon.log
|
|
334
|
+
invariant daemon compact # snapshot and purge this project's old events
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Leave it running. The verbs work without it — they write the file directly and
|
|
338
|
+
leave the event for the daemon to pick up — but with it running, every change
|
|
339
|
+
reaches every open page as it happens.
|
|
340
|
+
|
|
245
341
|
**One server, every project.** Running `invariant ui` in a second repository does
|
|
246
342
|
not start a second server — it registers that folder and opens it in the one
|
|
247
343
|
already running. `⌘P` switches between projects; `⇧⌘H` goes to the list of them
|
|
@@ -250,9 +346,11 @@ reload, bookmarks and a second tab on a second project all work the way you
|
|
|
250
346
|
would expect, and nothing from one project survives into the next.
|
|
251
347
|
|
|
252
348
|
```bash
|
|
253
|
-
invariant projects # every registered project
|
|
349
|
+
invariant projects # every registered project, by group
|
|
254
350
|
invariant projects add [path] # register one (defaults to the current folder)
|
|
255
351
|
invariant projects remove # forget one — its files are left alone
|
|
352
|
+
invariant projects rename … # name it on this machine (three folders called `frontend`)
|
|
353
|
+
invariant projects group … # file it under a group
|
|
256
354
|
```
|
|
257
355
|
|
|
258
356
|
The list lives in `~/.invariant/projects.json`.
|
|
@@ -622,20 +720,124 @@ token kept in `~/.invariant/ui.json` (mode 0600). Every request names the projec
|
|
|
622
720
|
it acts on (`/api/p/<project>/…`) and is scoped to that project's directory for
|
|
623
721
|
its whole lifetime, so one project can never read or write another's files.
|
|
624
722
|
|
|
723
|
+
### `invariant session <subcommand>`
|
|
724
|
+
|
|
725
|
+
The verb surface an agent drives sessions with. Every verb validates, records
|
|
726
|
+
what it did, and returns an exit code — see
|
|
727
|
+
[docs/spec-session-protocol.md](docs/spec-session-protocol.md).
|
|
728
|
+
|
|
729
|
+
```bash
|
|
730
|
+
invariant session # list
|
|
731
|
+
invariant session next [<id>] # what to do now, and the command for it
|
|
732
|
+
invariant session show <id> [--body]
|
|
733
|
+
invariant session path <id> # the absolute path, alone
|
|
734
|
+
invariant session status <id> <status> [--summary "…"] [--title "…"]
|
|
735
|
+
invariant session title <id> "A better title"
|
|
736
|
+
invariant session step list <id>
|
|
737
|
+
invariant session step complete <id> 2 # …or part of the step's text, or --all
|
|
738
|
+
invariant session write <id> --section Plan --file plan.md
|
|
739
|
+
invariant session link <id> --parent <session> # give an orphaned review its subject
|
|
740
|
+
invariant session archive <id>
|
|
741
|
+
invariant session migrate --all # repair legacy headers
|
|
742
|
+
invariant session events <id> # what the log says happened
|
|
743
|
+
invariant session reconcile [--dry-run] # infer what nobody reported
|
|
744
|
+
invariant session protocol # the block that teaches an agent this
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
| Exit | Meaning |
|
|
748
|
+
|---|---|
|
|
749
|
+
| `0` | it worked |
|
|
750
|
+
| `1` | it failed |
|
|
751
|
+
| `2` | usage: a missing argument, an unknown status, an ambiguous step |
|
|
752
|
+
| `3` | not found |
|
|
753
|
+
| `4` | a conflict: a stale etag, a review that is not triaged |
|
|
754
|
+
| `5` | the daemon was required (`INVARIANT_DAEMON=require`) and is not running |
|
|
755
|
+
|
|
756
|
+
`--json` on any of them prints the same shape the HTTP API returns.
|
|
757
|
+
|
|
758
|
+
### `invariant validate [sessions...]`
|
|
759
|
+
|
|
760
|
+
Check this project's sessions against the published format. The point of the
|
|
761
|
+
command is the exit code: `0` clean, `1` findings.
|
|
762
|
+
|
|
763
|
+
```bash
|
|
764
|
+
invariant validate # every session
|
|
765
|
+
invariant validate --reviews # review documents too
|
|
766
|
+
invariant validate --events # the event log too
|
|
767
|
+
invariant validate --fix # rewrite the headers a rewrite can fix
|
|
768
|
+
invariant validate --json --quiet # for CI
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
Beyond each file's own rules it checks what no single file can: a `parent` that
|
|
772
|
+
resolves, a review document that exists when the session says it should, two
|
|
773
|
+
sessions claiming the same review. See
|
|
774
|
+
[docs/spec-session-format.md](docs/spec-session-format.md).
|
|
775
|
+
|
|
776
|
+
### `invariant daemon <subcommand>`
|
|
777
|
+
|
|
778
|
+
The one local process. It serves every registered project, watches for changes,
|
|
779
|
+
journals them, and hosts the UI.
|
|
780
|
+
|
|
781
|
+
```bash
|
|
782
|
+
invariant daemon start [port] # start or attach; no browser
|
|
783
|
+
invariant daemon stop
|
|
784
|
+
invariant daemon status --json
|
|
785
|
+
invariant daemon logs -f
|
|
786
|
+
invariant daemon compact # snapshot and purge this project's old events
|
|
787
|
+
```
|
|
788
|
+
|
|
789
|
+
| Subcommand | Description |
|
|
790
|
+
|------------|-------------|
|
|
791
|
+
| `start [port]` | Start (or attach to) the daemon. Default port 4200, probing forward |
|
|
792
|
+
| `stop` | Graceful shutdown, then SIGTERM, then SIGKILL |
|
|
793
|
+
| `status` | The daemon and the projects it serves (`--json`). Exit 0 running, 1 not |
|
|
794
|
+
| `logs` | Print `~/.invariant/logs/daemon.log` (`-f` to follow) |
|
|
795
|
+
| `compact` | Fold old events into a snapshot and purge the segments |
|
|
796
|
+
|
|
797
|
+
`invariant ui` is the same process with a browser opened on top of it.
|
|
798
|
+
|
|
799
|
+
Environment:
|
|
800
|
+
|
|
801
|
+
| | |
|
|
802
|
+
|---|---|
|
|
803
|
+
| `INVARIANT_DAEMON` | `auto` (default) \| `require` \| `off` — whether a verb may use the daemon |
|
|
804
|
+
| `INVARIANT_DAEMON_PORT` | The port a spawned daemon binds |
|
|
805
|
+
| `INVARIANT_READ_MODEL` | `fs` turns the event-log projection off |
|
|
806
|
+
| `INVARIANT_HOME` | Relocates all of `~/.invariant` |
|
|
807
|
+
|
|
625
808
|
### `invariant projects`
|
|
626
809
|
|
|
627
810
|
The projects the UI serves, kept in `~/.invariant/projects.json`.
|
|
628
811
|
|
|
629
812
|
```bash
|
|
630
|
-
invariant projects
|
|
631
|
-
invariant projects add [path]
|
|
632
|
-
invariant projects remove [id|path]
|
|
813
|
+
invariant projects # list them, by group, with what each is waiting on
|
|
814
|
+
invariant projects add [path] # register a folder (defaults to the current one)
|
|
815
|
+
invariant projects remove [id|path] # forget one — nothing on disk is touched
|
|
816
|
+
invariant projects rename <id|path> <name> # what this machine calls it
|
|
817
|
+
invariant projects rename <id|path> --clear # back to the name in invariant.json
|
|
818
|
+
invariant projects group <id|path> <group> # file it under a group, creating one if needed
|
|
819
|
+
invariant projects group <id|path> --none # take it out of its group
|
|
820
|
+
invariant projects groups # the groups, and how full each one is
|
|
821
|
+
invariant projects groups new <name> # create an empty group
|
|
822
|
+
invariant projects groups rm <name|id> # delete a group — its projects survive, ungrouped
|
|
633
823
|
```
|
|
634
824
|
|
|
635
825
|
A project must have been initialized (`invariant init`) before it can be added.
|
|
636
826
|
Removing one only edits the list: re-adding the path brings back every session,
|
|
637
827
|
review and setting exactly as they were.
|
|
638
828
|
|
|
829
|
+
**Names.** Three checkouts whose folders are all called `frontend` are three
|
|
830
|
+
unreadable rows. `rename` gives one a name *on this machine* — it is stored in
|
|
831
|
+
`~/.invariant/projects.json`, never in `invariant.json`, so it does not reach
|
|
832
|
+
anyone who clones the repository. The manifest name stays visible underneath the
|
|
833
|
+
override in the UI, and `--clear` goes back to it. Without an override, a rename
|
|
834
|
+
inside `invariant.json` still shows up on its own.
|
|
835
|
+
|
|
836
|
+
**Groups.** A group is a collapsible section on the projects screen. Groups are
|
|
837
|
+
machine-local too, names are unique, and deleting one never deletes a project —
|
|
838
|
+
its members simply become ungrouped. `--flat` on `invariant projects` keeps the
|
|
839
|
+
old ungrouped output for anything parsing it.
|
|
840
|
+
|
|
639
841
|
In the UI: `⌘K` opens the command palette, `⌘P` switches project, `⇧⌘H` goes to
|
|
640
842
|
the project list, `⇧⌘V` toggles rendered preview and editing, `N` starts a
|
|
641
843
|
session, `?` lists every shortcut. On a review session,
|
|
@@ -718,42 +920,14 @@ invariant package publish
|
|
|
718
920
|
|
|
719
921
|
`package create` options: `-n, --name`, `--version`, `-d, --description`, `-a, --author`, `-l, --license`, `-r, --repository`.
|
|
720
922
|
|
|
721
|
-
### `invariant proxy <subcommand>`
|
|
722
|
-
|
|
723
|
-
Shared local LLM proxy daemon. Routes a repo's agent traffic through a local proxy by writing a managed env block (`ANTHROPIC_BASE_URL`) into `.claude/settings.json`.
|
|
724
|
-
|
|
725
|
-
```bash
|
|
726
|
-
invariant proxy start
|
|
727
|
-
invariant proxy status
|
|
728
|
-
invariant proxy status --json
|
|
729
|
-
invariant proxy on # route the current repo (or `on <dir>`)
|
|
730
|
-
invariant proxy off # revert the managed env block
|
|
731
|
-
invariant proxy stop
|
|
732
|
-
```
|
|
733
|
-
|
|
734
|
-
| Subcommand | Description |
|
|
735
|
-
|------------|-------------|
|
|
736
|
-
| `start` | Start the shared proxy daemon |
|
|
737
|
-
| `stop` | Stop the daemon (token is preserved) |
|
|
738
|
-
| `status` | Daemon status, routes, and per-repo activations (`--json`) |
|
|
739
|
-
| `on [dir]` | Route a repo through the proxy |
|
|
740
|
-
| `off [dir]` | Revert the managed env block (daemon keeps running) |
|
|
741
|
-
|
|
742
|
-
Agents read `ANTHROPIC_BASE_URL` once at launch — restart running sessions after `proxy on`.
|
|
743
|
-
|
|
744
|
-
### `invariant claude [args...]`
|
|
745
|
-
|
|
746
|
-
Launch Claude Code through the proxy, auto-starting the daemon. All arguments pass through untouched.
|
|
747
|
-
|
|
748
|
-
```bash
|
|
749
|
-
invariant claude
|
|
750
|
-
invariant claude -- --resume
|
|
751
|
-
```
|
|
752
|
-
|
|
753
923
|
### `invariant stats [subcommand]`
|
|
754
924
|
|
|
755
925
|
Inspect Claude Code token usage and cost.
|
|
756
926
|
|
|
927
|
+
> **Best effort, Claude Code only.** It reads local transcript files whose
|
|
928
|
+
> format is not a contract, so it may go quiet after a Claude Code release. It
|
|
929
|
+
> reads them and nothing else — no network, no LLM call.
|
|
930
|
+
|
|
757
931
|
```bash
|
|
758
932
|
invariant stats
|
|
759
933
|
invariant stats --live
|