@invariant.guru/cli 0.5.4 → 0.5.5

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 (3) hide show
  1. package/README.md +402 -111
  2. package/dist/main.js +1 -1
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,112 +1,268 @@
1
1
  # Invariant CLI
2
2
 
3
- A package manager for Claude AI specification files. Install, compose, and manage reusable markdown instruction blocks for your `CLAUDE.md` files.
3
+ A package manager for AI coding-agent context. Install, compose, and sync reusable agents, skills, commands, rules, contexts, and instructions into `.claude/`, `.cursor/`, and other CLI target folders.
4
+
5
+ ## Requirements
6
+
7
+ - Node.js `>= 24`
8
+ - `git` on `PATH` (used for GitHub package sources)
4
9
 
5
10
  ## Installation
6
11
 
7
12
  ```bash
8
- yarn global add @invariant--labs/cli
13
+ # npm
14
+ npm install -g @invariant.guru/cli
15
+
16
+ # yarn
17
+ yarn global add @invariant.guru/cli
18
+
19
+ # pnpm
20
+ pnpm add -g @invariant.guru/cli
9
21
  ```
10
22
 
11
- ## Quick Start
23
+ Verify and keep it current:
12
24
 
13
25
  ```bash
14
- # Initialize a project
15
- invariant init
26
+ invariant version
27
+ invariant update --check
28
+ invariant update
29
+ ```
16
30
 
17
- # Install a package
18
- invariant install everything-claude-code
31
+ ## Getting Started
19
32
 
20
- # Add items from the package
21
- invariant add agent:everything-claude-code/planner
33
+ The core loop is **init → install → add → sync**, with `plan` on top when you start a task.
22
34
 
23
- # Generate CLAUDE.md
24
- invariant claude
35
+ ### 1. `invariant init` — set up the project
36
+
37
+ Run once at the repository root. Creates `invariant.json` (the manifest) and `.invariant/` (cache, contexts, instructions, sessions, codegraph).
38
+
39
+ ```bash
40
+ invariant init # defaults to the `claude` target
41
+ invariant init claude cursor # multiple targets
42
+ invariant init --name my-project
43
+ ```
44
+
45
+ Supported targets: `claude`, `codex`, `cursor`, `windsurf`, `aider`, `copilot`.
46
+
47
+ ### 2. `invariant install` — pull a package
48
+
49
+ Packages come from the registry or straight from GitHub. Installing caches the package **and** activates its items.
50
+
51
+ ```bash
52
+ # From GitHub (owner/repo, optional #ref)
53
+ invariant install github:invariant-guru/inv-nest-clean-architecture
54
+
55
+ # Pin a branch or tag
56
+ invariant install github:invariant-guru/inv-nest-clean-architecture#main
57
+
58
+ # From the registry (same package, published name)
59
+ invariant install nest-clean-architecture
60
+ ```
61
+
62
+ This writes the package into `invariant.json`:
63
+
64
+ ```json
65
+ {
66
+ "packages": {
67
+ "nest-clean-architecture": {
68
+ "name": "nest-clean-architecture",
69
+ "version": "1.0.0",
70
+ "source": "github",
71
+ "sourceUrl": "github:invariant-guru/inv-nest-clean-architecture"
72
+ }
73
+ }
74
+ }
75
+ ```
76
+
77
+ ### 3. `invariant add` — pick what's active
78
+
79
+ `install` already activates everything. Use `add` to curate: a whole package, one item type, or a single item.
80
+
81
+ **Add the whole module** — every agent, skill, command, rule, context, and instruction the package ships:
82
+
83
+ ```bash
84
+ invariant add nest-clean-architecture
85
+ ```
86
+
87
+ ```
88
+ Adding all items from nest-clean-architecture...
89
+ ✓ Added items from nest-clean-architecture
90
+ agents:
91
+ - clean-architect
92
+ skills:
93
+ - implement-aggregate-root
94
+ - implement-command
95
+ - implement-query
96
+ - ...
97
+ rules:
98
+ - aggregate-root
99
+ - command
100
+ - ...
101
+ instructions:
102
+ - nest-clean-architecture
103
+ ```
104
+
105
+ **Add one type** — e.g. only the skills, or only the rules:
106
+
107
+ ```bash
108
+ invariant add skills:nest-clean-architecture
109
+ invariant add rules:nest-clean-architecture
110
+ ```
111
+
112
+ **Add single items** — one target per item, mixed types allowed:
113
+
114
+ ```bash
115
+ invariant add agent:nest-clean-architecture/clean-architect
116
+ invariant add skill:nest-clean-architecture/implement-command \
117
+ rule:nest-clean-architecture/command
25
118
  ```
26
119
 
120
+ Check what landed with `invariant inspect nest-clean-architecture`, or use `invariant add-interactive` for a checkbox tree of every installed item.
121
+
122
+ ### 4. `invariant sync` — write the files
123
+
124
+ `sync` renders the active set into each configured target: `CLAUDE.md`, `.claude/agents/`, `.claude/skills/<name>/SKILL.md`, `.claude/commands/`, `.claude/rules/`, and the equivalents for other targets.
125
+
126
+ ```bash
127
+ invariant sync
128
+ invariant sync --target claude
129
+ ```
130
+
131
+ `add`, `install`, and `add-interactive` all accept `-s, --sync` to do this in one shot:
132
+
133
+ ```bash
134
+ invariant install github:invariant-guru/inv-nest-clean-architecture --sync
135
+ ```
136
+
137
+ ### 5. `invariant plan` — start a task
138
+
139
+ Composes a session file under `.invariant/sessions/` containing your prompt plus the active instructions (and optionally a context), then prints the one-liner to paste into your agent.
140
+
141
+ ```bash
142
+ invariant plan "implement the cargo booking command"
143
+ invariant plan "implement the cargo booking command" --context nest-clean-architecture/dev
144
+ invariant plan "implement the cargo booking command" --full
145
+ ```
146
+
147
+ When the session is done, stamp it:
148
+
149
+ ```bash
150
+ invariant plan:complete <session-id> --summary "Booking command + tests"
151
+ ```
152
+
153
+ ### Full walkthrough
154
+
155
+ ```bash
156
+ invariant init claude
157
+ invariant install github:invariant-guru/inv-nest-clean-architecture
158
+ invariant add nest-clean-architecture # activate the whole module
159
+ invariant sync
160
+ invariant plan "add the cargo booking command"
161
+ ```
162
+
163
+ ---
164
+
27
165
  ## Commands
28
166
 
29
- ### `invariant init`
167
+ ### `invariant init [targets...]`
30
168
 
31
- Initialize Invariant in your project. Creates `invariant.json` and `.invariant/` directory.
169
+ Initialize a new invariant project with the given target CLI(s). Defaults to `claude`.
32
170
 
33
171
  ```bash
34
172
  invariant init
173
+ invariant init claude cursor
35
174
  invariant init --name my-project
175
+ invariant init --hybrid
36
176
  ```
37
177
 
38
- ### `invariant install [package...]`
178
+ | Option | Description |
179
+ |--------|-------------|
180
+ | `-n, --name <name>` | Project name (defaults to directory name) |
181
+ | `--hybrid` | Initialize as hybrid mode — project *and* package in one directory |
182
+
183
+ ### `invariant install [packages...]`
39
184
 
40
- Install packages from the registry (or GitHub) and activate their items. If the package already has curated `active` entries in `invariant.json`, exactly those are restored; otherwise all items are added.
185
+ Install packages from the registry or GitHub and activate their items. If the package already has curated `active` entries in `invariant.json`, exactly those are restored; otherwise all items are added.
41
186
 
42
187
  ```bash
43
- invariant install everything-claude-code
188
+ invariant install github:invariant-guru/inv-nest-clean-architecture
189
+ invariant install github:invariant-guru/inv-nest-clean-architecture#v1.2.0
190
+ invariant install nest-clean-architecture
191
+ invariant install nest-clean-architecture@1.0.0
44
192
 
45
- # Install every package declared in invariant.json (same activation rule per package)
193
+ # Reinstall every package declared in invariant.json
46
194
  invariant install
47
195
 
48
- # Only cache the package, don't activate anything (pre-0.5 behavior)
49
- invariant install everything-claude-code --no-add
50
-
51
- # Run `invariant sync` automatically afterwards
52
- invariant install everything-claude-code --sync
196
+ # Cache only, activate nothing
197
+ invariant install nest-clean-architecture --no-add
53
198
  ```
54
199
 
200
+ Source syntax:
201
+
202
+ | Form | Meaning |
203
+ |------|---------|
204
+ | `name` | Registry package, latest version |
205
+ | `name@version` | Registry package, pinned version |
206
+ | `github:owner/repo` | GitHub repo, default branch |
207
+ | `github:owner/repo#ref` | GitHub repo at a branch, tag, or commit |
208
+
55
209
  | Option | Description |
56
210
  |--------|-------------|
57
211
  | `--no-add` | Only cache the package, do not activate its items |
58
212
  | `-s, --sync` | Run `invariant sync` after installing |
59
213
 
60
- ### `invariant add <target> [<target>...]`
61
-
62
- Activate items from installed packages. Accepts one or more targets.
214
+ ### `invariant add [targets...]`
63
215
 
64
- ```bash
65
- # Add all items from a package
66
- invariant add everything-claude-code
216
+ Activate items from cached packages. Accepts one or more targets.
67
217
 
68
- # Add a specific agent
69
- invariant add agent:everything-claude-code/planner
218
+ Target formats:
70
219
 
71
- # Add multiple items at once
72
- invariant add skill:my-pkg/react-expert skill:my-pkg/ts-patterns
220
+ | Form | Meaning |
221
+ |------|---------|
222
+ | `<package>` | All items from the package |
223
+ | `<type>:<package>` | All items of that type |
224
+ | `<type>:<package>/<item>` | One specific item |
225
+ | `<package>/<item>` | One item, searching every type |
73
226
 
74
- # Mix types and packages
75
- invariant add agent:pkg-a/planner skill:pkg-b/tdd
227
+ Types (singular or plural): `agent`, `skill`, `command`, `rule`, `context`, `instruction`.
76
228
 
77
- # Run `invariant sync` automatically afterwards
78
- invariant add everything-claude-code --sync
229
+ ```bash
230
+ invariant add nest-clean-architecture
231
+ invariant add agent:nest-clean-architecture/clean-architect
232
+ invariant add skill:nest-clean-architecture/implement-query \
233
+ skill:nest-clean-architecture/implement-repository
234
+ invariant add rules:nest-clean-architecture # every rule
235
+ invariant add agent:nest-clean-architecture/clean-architect skill:skills/skill-creator --sync
79
236
  ```
80
237
 
238
+ Run with no arguments to print usage.
239
+
81
240
  | Option | Description |
82
241
  |--------|-------------|
83
242
  | `-s, --sync` | Run `invariant sync` after adding |
84
243
 
85
244
  ### `invariant add-interactive`
86
245
 
87
- Interactively select/unselect what's active — like `yarn upgrade-interactive`. Checked rows reflect the current activation state; checking adds, unchecking removes.
246
+ Interactively select/unselect what's active — like `yarn upgrade-interactive`. Checked rows reflect current activation state; checking adds, unchecking removes.
88
247
 
89
- By default it shows a **hierarchical tree**: package → item type (agents / skills / commands / rules / contexts / instructions) → item. Toggling a package or type **cascades** to all its items. Category rows show a `(selected/total)` count and a tri-state glyph — `◉` all, `◐` some, `◯` none:
248
+ Default view is a **hierarchical tree**: package → item type → item. Toggling a package or type row **cascades** to its items. Category rows show a `selected/total` count and a tri-state glyph — `◉` all, `◐` some, `◯` none:
90
249
 
91
250
  ```
92
- ◐ nest-clean-architecture@1.0.0 (1/3)
93
- ◯ agents (0/1)
94
- architect
95
- ◐ skills (1/2)
96
- ◉ qweqwe
97
- ◯ sdgsd
251
+ ◐ nest-clean-architecture@1.0.0 1/3
252
+
253
+ ├─ agents 0/1
254
+ └─ ◯ clean-architect
255
+
256
+ └─ ◐ skills 1/2
257
+ ├─ ◉ implement-command
258
+ └─ ◯ implement-query
98
259
  ```
99
260
 
100
- Keys: `↑↓` move · `space` toggle (cascades on a package/type row) · `a` all · `i` invert · `enter` confirm.
261
+ Keys: `↑↓` move · `space` toggle (cascades on a package/type row) · `a` toggle all · `i` invert · `enter` confirm.
101
262
 
102
263
  ```bash
103
- # Item-level tree (default)
104
- invariant add-interactive
105
-
106
- # Coarse selection: one checkbox per whole package
107
- invariant add-interactive --packages
108
-
109
- # Apply changes and sync immediately
264
+ invariant add-interactive # item-level tree (default)
265
+ invariant add-interactive --packages # one checkbox per whole package
110
266
  invariant add-interactive --sync
111
267
  ```
112
268
 
@@ -118,124 +274,259 @@ In `--packages` mode an already-active package left checked is untouched, so a c
118
274
  | `-s, --sync` | Run `invariant sync` after applying changes |
119
275
  | `-t, --target <target>` | With `--sync`, sync only to a specific target |
120
276
 
121
- ### `invariant remove <target> [<target>...]`
277
+ ### `invariant remove [targets...]`
122
278
 
123
- Remove added items (inverse of `add`). The package stays in cache for future use. Accepts one or more targets.
279
+ Deactivate items (inverse of `add`). The package stays in cache. Same target formats as `add`.
124
280
 
125
281
  ```bash
126
- # Remove all active items from a package
127
- invariant remove everything-claude-code
282
+ invariant remove nest-clean-architecture
283
+ invariant remove agent:nest-clean-architecture/clean-architect
284
+ invariant remove rules:nest-clean-architecture
285
+ invariant remove skill:nest-clean-architecture/implement-saga \
286
+ skill:nest-clean-architecture/implement-saga-test
287
+ ```
288
+
289
+ ### `invariant uninstall <packages...>`
128
290
 
129
- # Remove a specific agent
130
- invariant remove agent:everything-claude-code/planner
291
+ Completely remove packages: deletes all added items, drops the cache, and removes them from `invariant.json`.
131
292
 
132
- # Remove multiple items at once
133
- invariant remove skill:my-pkg/react-expert skill:my-pkg/ts-patterns
293
+ ```bash
294
+ invariant uninstall nest-clean-architecture
295
+ invariant uninstall nest-clean-architecture skills
134
296
  ```
135
297
 
136
- ### `invariant uninstall <package>`
298
+ ### `invariant sync`
137
299
 
138
- Completely remove a package: deletes all added items, removes from cache, and removes from config.
300
+ Sync active instructions, contexts, and items to every configured CLI target (or just one).
139
301
 
140
302
  ```bash
141
- invariant uninstall everything-claude-code
303
+ invariant sync
304
+ invariant sync --target cursor
142
305
  ```
143
306
 
307
+ | Option | Description |
308
+ |--------|-------------|
309
+ | `-t, --target <target>` | Sync only to a specific target (`claude`, `codex`, `cursor`, `windsurf`, `aider`, `copilot`) |
310
+
144
311
  ### `invariant inspect [package]`
145
312
 
146
313
  Show package contents with active items highlighted.
147
314
 
148
- **Without a target** — shows only active (added) items across all installed packages:
315
+ **Without a target** — only active items across all installed packages:
149
316
 
150
317
  ```bash
151
318
  invariant inspect
152
319
  ```
153
320
 
154
321
  ```
155
- everything-claude-code@main
322
+ nest-clean-architecture@1.0.0
156
323
  ────────────────────────────────────────
157
324
  agents/
158
- planner
325
+ clean-architect
159
326
  skills/
160
- backend-patterns/SKILL
327
+ implement-command
161
328
  ```
162
329
 
163
- **With `--details`** — shows all items (active and inactive) across all packages:
330
+ **With `--details`** — all items, active and inactive, across all packages. **With a package name** — always all items for that package.
164
331
 
165
332
  ```bash
166
333
  invariant inspect --details
334
+ invariant inspect nest-clean-architecture
167
335
  ```
168
336
 
337
+ | Option | Description |
338
+ |--------|-------------|
339
+ | `-d, --details` | Show all items including inactive ones |
340
+
341
+ ### `invariant list`
342
+
343
+ List installed or available packages.
344
+
345
+ ```bash
346
+ invariant list # installed packages
347
+ invariant list --remote # available packages from the registry
348
+ invariant list --active # only packages with active items
169
349
  ```
170
- everything-claude-code@main
171
- ────────────────────────────────────────
172
- agents/
173
- planner
174
- architect
175
- ○ code-reviewer
176
- skills/
177
- ✓ backend-patterns/SKILL
178
- tdd-workflow/SKILL
179
- contexts/
180
- ○ dev
181
- review
350
+
351
+ | Option | Description |
352
+ |--------|-------------|
353
+ | `-r, --remote` | List available packages from the registry |
354
+ | `-a, --active` | List only active packages |
355
+
356
+ ### `invariant plan <prompt>`
357
+
358
+ Generate a session file in `.invariant/sessions/` with the task instructions plus active context.
359
+
360
+ ```bash
361
+ invariant plan "implement user authentication"
362
+ invariant plan "implement user authentication" --full
363
+ invariant plan "implement user authentication" --context nest-clean-architecture/dev
182
364
  ```
183
365
 
184
- **With a specific package** — always shows all items (active and inactive):
366
+ | Option | Description |
367
+ |--------|-------------|
368
+ | `-c, --context <context>` | Context to include (format: `package/context-name`) |
369
+ | `-f, --full` | Inline full content from active packages instead of referencing the context file |
370
+
371
+ ### `invariant plan:complete <session>`
372
+
373
+ Stamp a completion header at the top of a finished session file.
185
374
 
186
375
  ```bash
187
- invariant inspect everything-claude-code
376
+ invariant plan:complete session-uuid --summary "Auth command + e2e tests"
377
+ invariant plan:complete session-uuid -n "Follow-up: rate limiting" -d 2026-08-19
188
378
  ```
189
379
 
190
- | Flag | Description |
191
- |------|-------------|
192
- | `-d, --details` | Show all items including inactive ones |
380
+ | Option | Description |
381
+ |--------|-------------|
382
+ | `-s, --summary <summary>` | Short resume of what the session accomplished |
383
+ | `-n, --note <note>` | Extra note line (repeatable) |
384
+ | `-d, --date <date>` | Completion date (`YYYY-MM-DD`), defaults to today |
193
385
 
194
- ### `invariant list`
386
+ ### `invariant scan`
195
387
 
196
- List installed or available packages.
388
+ Scan the codebase with embedded tree-sitter grammars and write a CodeGraph to `.invariant/codegraph/` — a structured inventory of files, languages, and top-level symbols for LLM navigation.
197
389
 
198
390
  ```bash
199
- invariant list # List installed packages
200
- invariant list --remote # List available packages from registry
201
- invariant list --active # List only packages with active items
391
+ invariant scan
392
+ invariant scan --scope apps/cli src/core/auth
393
+ invariant scan --include "src/**/*.ts" --exclude "**/*.spec.ts"
394
+ invariant scan --max-files 2000
202
395
  ```
203
396
 
204
- ### `invariant claude`
397
+ | Option | Description |
398
+ |--------|-------------|
399
+ | `-i, --include <globs...>` | Include globs (default: `**/*`) |
400
+ | `-e, --exclude <globs...>` | Extra exclude globs (on top of sensible defaults) |
401
+ | `-s, --scope <scopes...>` | Scope the scan to specific paths |
402
+ | `--max-files <count>` | Cap total scanned files (default: 5000) |
403
+ | `--max-size <bytes>` | Skip files larger than this (default: 1 MB) |
404
+
405
+ Supported languages: TypeScript, JavaScript, Python, Go, Rust, JSON, Markdown, HTML, CSS.
205
406
 
206
- Generate a `CLAUDE.md` file from all active package items.
407
+ ### `invariant package <subcommand>`
408
+
409
+ Package authoring — for repositories that *are* an invariant package.
410
+
411
+ ```bash
412
+ invariant package create --name my-pkg --version 1.0.0 --author "Me"
413
+ invariant package refresh
414
+ invariant package validate
415
+ invariant package publish
416
+ ```
417
+
418
+ | Subcommand | Description |
419
+ |------------|-------------|
420
+ | `create` | Scan the current folder and add package metadata to `invariant.json` |
421
+ | `refresh` | Rescan items on disk and update `invariant.json` |
422
+ | `validate` | Validate the items declared in `invariant.json` against disk |
423
+ | `publish` | Validate, bundle, and publish the package to the registry |
424
+
425
+ `package create` options: `-n, --name`, `--version`, `-d, --description`, `-a, --author`, `-l, --license`, `-r, --repository`.
426
+
427
+ ### `invariant proxy <subcommand>`
428
+
429
+ 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`.
430
+
431
+ ```bash
432
+ invariant proxy start
433
+ invariant proxy status
434
+ invariant proxy status --json
435
+ invariant proxy on # route the current repo (or `on <dir>`)
436
+ invariant proxy off # revert the managed env block
437
+ invariant proxy stop
438
+ ```
439
+
440
+ | Subcommand | Description |
441
+ |------------|-------------|
442
+ | `start` | Start the shared proxy daemon |
443
+ | `stop` | Stop the daemon (token is preserved) |
444
+ | `status` | Daemon status, routes, and per-repo activations (`--json`) |
445
+ | `on [dir]` | Route a repo through the proxy |
446
+ | `off [dir]` | Revert the managed env block (daemon keeps running) |
447
+
448
+ Agents read `ANTHROPIC_BASE_URL` once at launch — restart running sessions after `proxy on`.
449
+
450
+ ### `invariant claude [args...]`
451
+
452
+ Launch Claude Code through the proxy, auto-starting the daemon. All arguments pass through untouched.
207
453
 
208
454
  ```bash
209
455
  invariant claude
456
+ invariant claude -- --resume
210
457
  ```
211
458
 
212
- ### `invariant plan "<instructions>"`
459
+ ### `invariant stats [subcommand]`
213
460
 
214
- Create a session file with task instructions.
461
+ Inspect Claude Code token usage and cost.
215
462
 
216
463
  ```bash
217
- invariant plan "implement user authentication"
218
- invariant plan "implement user authentication" --full
219
- invariant plan "implement user authentication" --context everything-claude-code/dev
464
+ invariant stats
465
+ invariant stats --live
466
+ invariant stats --since 7d --format table
467
+ invariant stats cost
468
+ invariant stats models
469
+ invariant stats sessions
470
+ invariant stats projects
220
471
  ```
221
472
 
222
- ### `invariant scan [options]`
473
+ | Subcommand | Description |
474
+ |------------|-------------|
475
+ | `cost` | Cost drill-down with cache savings |
476
+ | `models` | Per-model token and cost aggregate |
477
+ | `sessions` | Per-session breakdown (sorted by cost) |
478
+ | `projects` | Per-project aggregate (implies `--all-projects`) |
479
+
480
+ | Option | Description |
481
+ |--------|-------------|
482
+ | `--live` | Open the interactive dashboard |
483
+ | `--since <duration>` | Start of window (e.g. `7d`, `24h`, `today`) |
484
+ | `--until <duration>` | End of window (default: now) |
485
+ | `--format <format>` | `markdown`, `json`, or `table` |
486
+ | `--project <path>` | Absolute path to scope to |
487
+ | `--all-projects` | Aggregate across every project |
488
+ | `--model <id>` | Filter by model id |
489
+ | `--include-turns` | (JSON only) include per-turn records |
490
+ | `--verbose` | Print parse warnings and diagnostics |
491
+
492
+ ### `invariant version`
223
493
 
224
- Scan the current codebase with embedded tree-sitter grammars. Produces `.invariant/scan/scan-report.json` and `.invariant/scan/scan-report.md` — a structured inventory of files, languages, and top-level symbols.
494
+ Display the CLI version.
495
+
496
+ ### `invariant update`
497
+
498
+ Update the CLI to the latest version, using the package manager it was installed with.
225
499
 
226
500
  ```bash
227
- invariant scan
228
- invariant scan --language typescript --language python
229
- invariant scan --format json --max-files 2000
501
+ invariant update
502
+ invariant update --check
503
+ invariant update --version 0.5.4
230
504
  ```
231
505
 
232
- | Flag | Description |
233
- |------|-------------|
234
- | `-i, --include <globs...>` | Include globs (default: `**/*`) |
235
- | `-e, --exclude <globs...>` | Extra exclude globs (in addition to sensible defaults) |
236
- | `-l, --language <langs...>` | Restrict to specific languages |
237
- | `-f, --format <format>` | `json`, `markdown`, or `both` (default: `both`) |
238
- | `--max-files <n>` | Cap total scanned files (default: 5000) |
239
- | `--max-size <bytes>` | Skip files larger than this (default: 1 MB) |
506
+ | Option | Description |
507
+ |--------|-------------|
508
+ | `-v, --version <version>` | Update to a specific version |
509
+ | `-c, --check` | Only check whether an update is available |
240
510
 
241
- Supported languages: TypeScript, JavaScript, Python, Go, Rust, JSON, Markdown, HTML, CSS.
511
+ ---
512
+
513
+ ## Project layout
514
+
515
+ After `init` + `install` + `sync`:
516
+
517
+ ```
518
+ .
519
+ ├── invariant.json # manifest: targets, packages, active items
520
+ ├── .invariant/
521
+ │ ├── cache/ # downloaded packages
522
+ │ ├── contexts/ # rendered contexts
523
+ │ ├── instructions/ # rendered instructions
524
+ │ ├── sessions/ # `invariant plan` output
525
+ │ └── codegraph/ # `invariant scan` output
526
+ ├── CLAUDE.md # generated by `invariant sync`
527
+ └── .claude/
528
+ ├── agents/<name>.md
529
+ ├── skills/<name>/SKILL.md
530
+ ├── commands/<name>.md
531
+ └── rules/<name>.md
532
+ ```
package/dist/main.js CHANGED
@@ -969,7 +969,7 @@ Follow Clean Architecture principles:
969
969
  `}]}),F_}async fetchPackage(F_){const O_=this.packages.get(F_);if(!O_)throw new Rd.RegistryFetchError(`Package "${F_}" not found`);return md.Package.create({manifest:{name:O_.name,version:O_.version,description:O_.description,author:O_.author},items:O_.items,source:{type:"registry"}})}async fetchFromGitHub(F_,O_,I_){return md.Package.create({manifest:{name:O_,version:I_||"main",description:`Package from github:${F_}/${O_}`,author:F_,repository:`https://github.com/${F_}/${O_}`},items:[{type:"instructions",path:"instructions/readme.md",content:`# ${O_}
970
970
 
971
971
  This is a stub package from GitHub: ${F_}/${O_}
972
- `}],source:{type:"github",owner:F_,repo:O_,ref:I_}})}async getVersions(F_){const O_=this.packages.get(F_);return O_?[O_.version]:[]}async search(F_){const O_=[],I_=F_.toLowerCase();for(const M_ of this.packages.values())(M_.name.toLowerCase().includes(I_)||M_.description.toLowerCase().includes(I_))&&O_.push({name:M_.name,version:M_.version,description:M_.description,author:M_.author,source:"registry"});return O_}async getLatestVersion(F_){const O_=this.packages.get(F_);if(!O_)throw new Rd.RegistryFetchError(`Package "${F_}" not found`);return O_.version}async exists(F_){return this.packages.has(F_)}async listAll(){const F_=[];for(const O_ of this.packages.values())F_.push({name:O_.name,version:O_.version,description:O_.description,author:O_.author,source:"registry"});return F_}async publishPackage(F_,O_,I_){return this.packages.set(F_.name,{name:F_.name,version:F_.version,description:F_.description||"",author:F_.author||"",items:[]}),{name:F_.name,version:F_.version,url:`inmemory://${F_.name}`}}};return inMemoryRegistry_queryBuilder.InMemoryRegistryQueryBuilder=D_,inMemoryRegistry_queryBuilder.InMemoryRegistryQueryBuilder=D_=t([(0,fd.Injectable)(),Xf("design:paramtypes",[])],D_),inMemoryRegistry_queryBuilder}e(requireInMemoryRegistry_queryBuilder,"requireInMemoryRegistry_queryBuilder");var httpRegistry_queryBuilder={},hasRequiredHttpRegistry_queryBuilder;function requireHttpRegistry_queryBuilder(){if(hasRequiredHttpRegistry_queryBuilder)return httpRegistry_queryBuilder;hasRequiredHttpRegistry_queryBuilder=1;var t=httpRegistry_queryBuilder&&httpRegistry_queryBuilder.__decorate||function(F_,O_,I_,M_){var L_=arguments.length,U_=L_<3?O_:M_===null?M_=Object.getOwnPropertyDescriptor(O_,I_):M_,W_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")U_=Reflect.decorate(F_,O_,I_,M_);else for(var z_=F_.length-1;z_>=0;z_--)(W_=F_[z_])&&(U_=(L_<3?W_(U_):L_>3?W_(O_,I_,U_):W_(O_,I_))||U_);return L_>3&&U_&&Object.defineProperty(O_,I_,U_),U_};Object.defineProperty(httpRegistry_queryBuilder,"__esModule",{value:!0}),httpRegistry_queryBuilder.HttpRegistryQueryBuilder=void 0;const Xf=requireCommon$1(),fd=require$$3$2,pd=requirePersistence$3(),md=requireDomain$1(),Rd=requireErrors$3(),D_="https://api.invariant.dev";let P_=class extends pd.RegistryQueryBuilder{static{e(this,"HttpRegistryQueryBuilder")}get baseUrl(){return(process.env.INVARIANT_REGISTRY_URL||D_).replace(/\/$/,"")}async fetchFromGitHub(O_,I_,M_){const L_=M_||"main",U_=`https://codeload.github.com/${O_}/${I_}/tar.gz/${L_}`,W_=await this.downloadAndExtractTarball(U_),z_=`${I_}-${L_}/`;let X_;const K_=W_.find(Z_=>Z_.path===`${z_}invariant.json`);if(K_){const Z_=JSON.parse(K_.content);X_={name:Z_.name,version:Z_.version,description:Z_.description,author:Z_.author,repository:Z_.repository}}else X_={name:I_,version:L_,description:`Package from github:${O_}/${I_}`,author:O_};const G_=[...md.PACKAGE_ITEM_TYPES],Q_=[];for(const Z_ of G_){const $0=`${z_}${Z_}/`,B0=W_.filter(G0=>G0.path.startsWith($0)&&G0.path.endsWith(".md"));for(const G0 of B0){const Z0=G0.path.slice(z_.length);Q_.push({type:Z_,path:Z0,content:G0.content})}}return md.Package.create({manifest:{name:X_.name,version:X_.version,description:X_.description,author:X_.author,license:X_.license,repository:X_.repository||`https://github.com/${O_}/${I_}`},items:Q_,source:{type:"github",owner:O_,repo:I_,ref:L_}})}async downloadAndExtractTarball(O_){const I_=await fetch(O_,{headers:{"User-Agent":"invariant-cli"}});if(!I_.ok)throw I_.status===404?new Rd.RegistryFetchError(`Repository or branch not found: ${O_}`):new Rd.RegistryFetchError(`Failed to download: ${I_.status} ${I_.statusText}`);const M_=Buffer.from(await I_.arrayBuffer()),L_=fd.gunzipSync(M_);return this.parseTar(L_)}parseTar(O_){const I_=[];let M_=0;for(;M_<O_.length;){const L_=O_.slice(M_,M_+512);if(L_.every(G_=>G_===0))break;const U_=L_.indexOf(0),W_=L_.slice(0,Math.min(U_,100)).toString("utf-8"),z_=L_.slice(124,136).toString("utf-8").trim(),X_=parseInt(z_,8)||0,K_=L_[156];if(M_+=512,(K_===48||K_===0)&&X_>0){const G_=O_.slice(M_,M_+X_).toString("utf-8");I_.push({path:W_,content:G_})}M_+=Math.ceil(X_/512)*512}return I_}async exists(O_){const I_=`${this.baseUrl}/package/${encodeURIComponent(O_)}`,M_=await fetch(I_,{headers:{"User-Agent":"invariant-cli"}});if(M_.status===404)return!1;if(!M_.ok)throw new Rd.RegistryFetchError(`Registry error: ${M_.status}`);return!0}async fetchPackage(O_,I_){const M_=I_||await this.getLatestVersion(O_),L_=`${this.baseUrl}/package/${encodeURIComponent(O_)}/${encodeURIComponent(M_)}/download`,U_=await fetch(L_,{headers:{"User-Agent":"invariant-cli"}});if(U_.status===404)throw new Rd.RegistryFetchError(`Package "${O_}@${M_}" not found`);if(!U_.ok)throw new Rd.RegistryFetchError(`Download failed: ${U_.status}`);const W_=Buffer.from(await U_.arrayBuffer()),z_=fd.gunzipSync(W_),X_=this.parseTar(z_);return this.buildPackageFromEntries(X_,O_,M_)}async getVersions(O_){return(await this.getPackageDetails(O_)).versions.map(M_=>M_.version)}async getLatestVersion(O_){const I_=await this.getPackageDetails(O_);if(I_.versions.length===0)throw new Rd.RegistryFetchError(`Package "${O_}" has no versions`);return I_.versions[0].version}async search(O_){const I_=new URLSearchParams({q:O_,take:"100"}),M_=`${this.baseUrl}/package/search?${I_}`,L_=await fetch(M_,{headers:{"User-Agent":"invariant-cli"}});if(!L_.ok)throw new Rd.RegistryFetchError(`Search failed: ${L_.status}`);const U_=await L_.json();return this.mapSearchResults(U_)}async listAll(){const O_=`${this.baseUrl}/package/search?take=1000`,I_=await fetch(O_,{headers:{"User-Agent":"invariant-cli"}});if(!I_.ok)throw new Rd.RegistryFetchError(`List failed: ${I_.status}`);const M_=await I_.json();return this.mapSearchResults(M_)}async publishPackage(O_,I_,M_){const L_=`${this.baseUrl}/package/publish`,U_=new FormData;U_.append("name",O_.name),U_.append("version",O_.version),U_.append("description",O_.description||""),U_.append("author",O_.author||""),U_.append("readmeContent","");const W_=new Blob([new Uint8Array(I_)],{type:"application/gzip"});U_.append("tarball",W_,`${O_.name}-${O_.version}.tgz`);const z_=await fetch(L_,{method:"POST",headers:{"User-Agent":"invariant-cli",Authorization:`Bearer ${M_}`},body:U_});if(!z_.ok){const X_=await z_.text();throw new Rd.RegistryFetchError(`Publish failed: ${z_.status} ${X_}`)}return{name:O_.name,version:O_.version,url:`${this.baseUrl}/package/${O_.name}`}}async getPackageDetails(O_){const I_=`${this.baseUrl}/package/${encodeURIComponent(O_)}`,M_=await fetch(I_,{headers:{"User-Agent":"invariant-cli"}});if(M_.status===404)throw new Rd.RegistryFetchError(`Package "${O_}" not found`);if(!M_.ok)throw new Rd.RegistryFetchError(`Registry error: ${M_.status}`);return M_.json()}mapSearchResults(O_){return O_.items.map(I_=>({name:I_.name,version:I_.latestVersion,description:I_.description,author:I_.author,source:"registry"}))}buildPackageFromEntries(O_,I_,M_){let L_={name:I_,version:M_};const U_=O_.find(X_=>X_.path==="invariant.json"||X_.path.endsWith("/invariant.json"));if(U_){const X_=JSON.parse(U_.content);L_={name:X_.name||I_,version:X_.version||M_,description:X_.description,author:X_.author,license:X_.license,repository:X_.repository}}const W_=[...md.PACKAGE_ITEM_TYPES],z_=[];for(const X_ of W_)for(const K_ of O_){const G_=this.matchItemPath(K_.path,X_);G_&&K_.path.endsWith(".md")&&z_.push({type:X_,path:G_,content:K_.content})}return md.Package.create({manifest:L_,items:z_,source:{type:"registry"}})}matchItemPath(O_,I_){if(O_.startsWith(`${I_}/`))return O_;const M_=O_.indexOf(`/${I_}/`);return M_!==-1?O_.slice(M_+1):null}};return httpRegistry_queryBuilder.HttpRegistryQueryBuilder=P_,httpRegistry_queryBuilder.HttpRegistryQueryBuilder=P_=t([(0,Xf.Injectable)()],P_),httpRegistry_queryBuilder}e(requireHttpRegistry_queryBuilder,"requireHttpRegistry_queryBuilder");var hasRequiredPersistence$2;function requirePersistence$2(){return hasRequiredPersistence$2||(hasRequiredPersistence$2=1,(function(t){var Xf=persistence$2&&persistence$2.__createBinding||(Object.create?(function(pd,md,Rd,D_){D_===void 0&&(D_=Rd);var P_=Object.getOwnPropertyDescriptor(md,Rd);(!P_||("get"in P_?!md.__esModule:P_.writable||P_.configurable))&&(P_={enumerable:!0,get:e(function(){return md[Rd]},"get")}),Object.defineProperty(pd,D_,P_)}):(function(pd,md,Rd,D_){D_===void 0&&(D_=Rd),pd[D_]=md[Rd]})),fd=persistence$2&&persistence$2.__exportStar||function(pd,md){for(var Rd in pd)Rd!=="default"&&!Object.prototype.hasOwnProperty.call(md,Rd)&&Xf(md,pd,Rd)};Object.defineProperty(t,"__esModule",{value:!0}),fd(requireFileSystemStorage_repository(),t),fd(requireInMemoryRegistry_queryBuilder(),t),fd(requireHttpRegistry_queryBuilder(),t)})(persistence$2)),persistence$2}e(requirePersistence$2,"requirePersistence$2");var gateways$2={},claudeCodeCliTarget_gateway={},hasRequiredClaudeCodeCliTarget_gateway;function requireClaudeCodeCliTarget_gateway(){if(hasRequiredClaudeCodeCliTarget_gateway)return claudeCodeCliTarget_gateway;hasRequiredClaudeCodeCliTarget_gateway=1;var t=claudeCodeCliTarget_gateway&&claudeCodeCliTarget_gateway.__decorate||function(F_,O_,I_,M_){var L_=arguments.length,U_=L_<3?O_:M_===null?M_=Object.getOwnPropertyDescriptor(O_,I_):M_,W_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")U_=Reflect.decorate(F_,O_,I_,M_);else for(var z_=F_.length-1;z_>=0;z_--)(W_=F_[z_])&&(U_=(L_<3?W_(U_):L_>3?W_(O_,I_,U_):W_(O_,I_))||U_);return L_>3&&U_&&Object.defineProperty(O_,I_,U_),U_};Object.defineProperty(claudeCodeCliTarget_gateway,"__esModule",{value:!0}),claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd=".claude",D_="CLAUDE.md";let P_=class extends md.CliTargetGateway{static{e(this,"ClaudeCodeCliTargetGateway")}constructor(){super(...arguments),this.config={name:"claude",displayName:"Claude Code",supportsItemTypes:!0}}async copyItem(O_,I_,M_,L_){const U_=pd.join(O_,Rd,I_);await fd.mkdir(U_,{recursive:!0});const W_=[];if(L_.length===1&&!L_[0].relativePath.includes("/")){const z_=pd.join(U_,M_+".md");await fd.writeFile(z_,L_[0].content,"utf-8"),W_.push(z_)}else{const z_=pd.join(U_,M_);for(const X_ of L_){const K_=pd.join(z_,X_.relativePath);await fd.mkdir(pd.dirname(K_),{recursive:!0}),await fd.writeFile(K_,X_.content,"utf-8"),W_.push(K_)}}return{files:W_,itemNames:[M_]}}async removeItem(O_,I_,M_){const L_=pd.join(O_,Rd,I_),U_=pd.join(L_,M_);try{if((await fd.stat(U_)).isDirectory()){await fd.rm(U_,{recursive:!0});return}}catch{}try{await fd.unlink(U_+".md")}catch{}}async generateContextFile(O_,I_){const M_=pd.join(O_,D_);return await fd.writeFile(M_,I_,"utf-8"),M_}async contextFileExists(O_){try{return await fd.access(pd.join(O_,D_)),!0}catch{return!1}}async resolveItemPath(O_,I_,M_){const L_=pd.join(O_,Rd,I_),U_=pd.join(L_,M_);try{if((await fd.stat(U_)).isDirectory())return pd.join(Rd,I_,M_)}catch{}const W_=U_+".md";try{return await fd.access(W_),pd.join(Rd,I_,M_+".md")}catch{}return null}};return claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=P_,claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=P_=t([(0,Xf.Injectable)()],P_),claudeCodeCliTarget_gateway}e(requireClaudeCodeCliTarget_gateway,"requireClaudeCodeCliTarget_gateway");var codexCliTarget_gateway={},hasRequiredCodexCliTarget_gateway;function requireCodexCliTarget_gateway(){if(hasRequiredCodexCliTarget_gateway)return codexCliTarget_gateway;hasRequiredCodexCliTarget_gateway=1;var t=codexCliTarget_gateway&&codexCliTarget_gateway.__decorate||function(P_,F_,O_,I_){var M_=arguments.length,L_=M_<3?F_:I_===null?I_=Object.getOwnPropertyDescriptor(F_,O_):I_,U_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")L_=Reflect.decorate(P_,F_,O_,I_);else for(var W_=P_.length-1;W_>=0;W_--)(U_=P_[W_])&&(L_=(M_<3?U_(L_):M_>3?U_(F_,O_,L_):U_(F_,O_))||L_);return M_>3&&L_&&Object.defineProperty(F_,O_,L_),L_};Object.defineProperty(codexCliTarget_gateway,"__esModule",{value:!0}),codexCliTarget_gateway.CodexCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd="AGENTS.md";let D_=class extends md.CliTargetGateway{static{e(this,"CodexCliTargetGateway")}constructor(){super(...arguments),this.config={name:"codex",displayName:"OpenAI Codex",supportsItemTypes:!1}}async copyItem(F_,O_,I_,M_){return{files:[],itemNames:[]}}async removeItem(F_,O_,I_){}async generateContextFile(F_,O_){const I_=pd.join(F_,Rd);return await fd.writeFile(I_,O_,"utf-8"),I_}async contextFileExists(F_){try{return await fd.access(pd.join(F_,Rd)),!0}catch{return!1}}async resolveItemPath(F_,O_,I_){return null}};return codexCliTarget_gateway.CodexCliTargetGateway=D_,codexCliTarget_gateway.CodexCliTargetGateway=D_=t([(0,Xf.Injectable)()],D_),codexCliTarget_gateway}e(requireCodexCliTarget_gateway,"requireCodexCliTarget_gateway");var cursorCliTarget_gateway={},hasRequiredCursorCliTarget_gateway;function requireCursorCliTarget_gateway(){if(hasRequiredCursorCliTarget_gateway)return cursorCliTarget_gateway;hasRequiredCursorCliTarget_gateway=1;var t=cursorCliTarget_gateway&&cursorCliTarget_gateway.__decorate||function(P_,F_,O_,I_){var M_=arguments.length,L_=M_<3?F_:I_===null?I_=Object.getOwnPropertyDescriptor(F_,O_):I_,U_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")L_=Reflect.decorate(P_,F_,O_,I_);else for(var W_=P_.length-1;W_>=0;W_--)(U_=P_[W_])&&(L_=(M_<3?U_(L_):M_>3?U_(F_,O_,L_):U_(F_,O_))||L_);return M_>3&&L_&&Object.defineProperty(F_,O_,L_),L_};Object.defineProperty(cursorCliTarget_gateway,"__esModule",{value:!0}),cursorCliTarget_gateway.CursorCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd=".cursor/rules";let D_=class extends md.CliTargetGateway{static{e(this,"CursorCliTargetGateway")}constructor(){super(...arguments),this.config={name:"cursor",displayName:"Cursor",supportsItemTypes:!1}}async copyItem(F_,O_,I_,M_){const L_=pd.join(F_,Rd);await fd.mkdir(L_,{recursive:!0});const U_=I_+".mdc",W_=pd.join(L_,U_),z_=this.concatenateFiles(M_),X_=this.wrapWithFrontmatter(z_,O_,I_);return await fd.writeFile(W_,X_,"utf-8"),{files:[W_],itemNames:[I_]}}async removeItem(F_,O_,I_){const M_=I_+".mdc";try{await fd.unlink(pd.join(F_,Rd,M_))}catch{}}async generateContextFile(F_,O_){const I_=pd.join(F_,Rd);await fd.mkdir(I_,{recursive:!0});const M_=pd.join(I_,"invariant-context.mdc"),L_=this.wrapWithFrontmatter(O_,"context","Invariant project context");return await fd.writeFile(M_,L_,"utf-8"),M_}async contextFileExists(F_){try{return await fd.access(pd.join(F_,Rd,"invariant-context.mdc")),!0}catch{return!1}}async resolveItemPath(F_,O_,I_){const M_=pd.join(F_,Rd,I_+".mdc");try{return await fd.access(M_),pd.join(Rd,I_+".mdc")}catch{return null}}concatenateFiles(F_){return F_.length===1?F_[0].content:F_.map(O_=>O_.content).join(`
972
+ `}],source:{type:"github",owner:F_,repo:O_,ref:I_}})}async getVersions(F_){const O_=this.packages.get(F_);return O_?[O_.version]:[]}async search(F_){const O_=[],I_=F_.toLowerCase();for(const M_ of this.packages.values())(M_.name.toLowerCase().includes(I_)||M_.description.toLowerCase().includes(I_))&&O_.push({name:M_.name,version:M_.version,description:M_.description,author:M_.author,source:"registry"});return O_}async getLatestVersion(F_){const O_=this.packages.get(F_);if(!O_)throw new Rd.RegistryFetchError(`Package "${F_}" not found`);return O_.version}async exists(F_){return this.packages.has(F_)}async listAll(){const F_=[];for(const O_ of this.packages.values())F_.push({name:O_.name,version:O_.version,description:O_.description,author:O_.author,source:"registry"});return F_}async publishPackage(F_,O_,I_){return this.packages.set(F_.name,{name:F_.name,version:F_.version,description:F_.description||"",author:F_.author||"",items:[]}),{name:F_.name,version:F_.version,url:`inmemory://${F_.name}`}}};return inMemoryRegistry_queryBuilder.InMemoryRegistryQueryBuilder=D_,inMemoryRegistry_queryBuilder.InMemoryRegistryQueryBuilder=D_=t([(0,fd.Injectable)(),Xf("design:paramtypes",[])],D_),inMemoryRegistry_queryBuilder}e(requireInMemoryRegistry_queryBuilder,"requireInMemoryRegistry_queryBuilder");var httpRegistry_queryBuilder={},hasRequiredHttpRegistry_queryBuilder;function requireHttpRegistry_queryBuilder(){if(hasRequiredHttpRegistry_queryBuilder)return httpRegistry_queryBuilder;hasRequiredHttpRegistry_queryBuilder=1;var t=httpRegistry_queryBuilder&&httpRegistry_queryBuilder.__decorate||function(F_,O_,I_,M_){var L_=arguments.length,U_=L_<3?O_:M_===null?M_=Object.getOwnPropertyDescriptor(O_,I_):M_,W_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")U_=Reflect.decorate(F_,O_,I_,M_);else for(var z_=F_.length-1;z_>=0;z_--)(W_=F_[z_])&&(U_=(L_<3?W_(U_):L_>3?W_(O_,I_,U_):W_(O_,I_))||U_);return L_>3&&U_&&Object.defineProperty(O_,I_,U_),U_};Object.defineProperty(httpRegistry_queryBuilder,"__esModule",{value:!0}),httpRegistry_queryBuilder.HttpRegistryQueryBuilder=void 0;const Xf=requireCommon$1(),fd=require$$3$2,pd=requirePersistence$3(),md=requireDomain$1(),Rd=requireErrors$3(),D_="https://api.invariant.dev";let P_=class extends pd.RegistryQueryBuilder{static{e(this,"HttpRegistryQueryBuilder")}get baseUrl(){return(process.env.INVARIANT_REGISTRY_URL||D_).replace(/\/$/,"")}async fetchFromGitHub(O_,I_,M_){const L_=M_||"main",U_=`https://codeload.github.com/${O_}/${I_}/tar.gz/${L_}`,W_=await this.downloadAndExtractTarball(U_),z_=`${I_}-${L_}/`;let X_;const K_=W_.find(Z_=>Z_.path===`${z_}invariant.json`);if(K_){const Z_=JSON.parse(K_.content);X_={name:Z_.name,version:Z_.version,description:Z_.description,author:Z_.author,repository:Z_.repository}}else X_={name:I_,version:L_,description:`Package from github:${O_}/${I_}`,author:O_};const G_=[...md.PACKAGE_ITEM_TYPES],Q_=[];for(const Z_ of G_){const $0=`${z_}${Z_}/`,B0=W_.filter(G0=>G0.path.startsWith($0)&&G0.path.endsWith(".md"));for(const G0 of B0){const Z0=G0.path.slice(z_.length);Q_.push({type:Z_,path:Z0,content:G0.content})}}return md.Package.create({manifest:{name:X_.name,version:X_.version,description:X_.description,author:X_.author,license:X_.license,repository:X_.repository||`https://github.com/${O_}/${I_}`},items:Q_,source:{type:"github",owner:O_,repo:I_,ref:L_}})}async downloadAndExtractTarball(O_){const I_=await fetch(O_,{headers:{"User-Agent":"invariant-cli"}});if(!I_.ok)throw I_.status===404?new Rd.RegistryFetchError(`Repository or branch not found: ${O_}`):new Rd.RegistryFetchError(`Failed to download: ${I_.status} ${I_.statusText}`);const M_=Buffer.from(await I_.arrayBuffer()),L_=fd.gunzipSync(M_);return this.parseTar(L_)}parseTar(O_){const I_=[];let M_=0;for(;M_<O_.length;){const L_=O_.slice(M_,M_+512);if(L_.every(G_=>G_===0))break;const U_=L_.indexOf(0),W_=L_.slice(0,Math.min(U_,100)).toString("utf-8"),z_=L_.slice(124,136).toString("utf-8").trim(),X_=parseInt(z_,8)||0,K_=L_[156];if(M_+=512,(K_===48||K_===0)&&X_>0){const G_=O_.slice(M_,M_+X_).toString("utf-8");I_.push({path:W_,content:G_})}M_+=Math.ceil(X_/512)*512}return I_}async exists(O_){const I_=`${this.baseUrl}/package/${encodeURIComponent(O_)}`,M_=await fetch(I_,{headers:{"User-Agent":"invariant-cli"}});if(M_.status===404)return!1;if(!M_.ok)throw new Rd.RegistryFetchError(`Registry error: ${M_.status}`);return!0}async fetchPackage(O_,I_){const M_=I_||await this.getLatestVersion(O_),L_=`${this.baseUrl}/package/${encodeURIComponent(O_)}/${encodeURIComponent(M_)}/download`,U_=await fetch(L_,{headers:{"User-Agent":"invariant-cli"}});if(U_.status===404)throw new Rd.RegistryFetchError(`Package "${O_}@${M_}" not found`);if(!U_.ok)throw new Rd.RegistryFetchError(`Download failed: ${U_.status}`);const W_=Buffer.from(await U_.arrayBuffer()),z_=fd.gunzipSync(W_),X_=this.parseTar(z_);return this.buildPackageFromEntries(X_,O_,M_)}async getVersions(O_){return(await this.getPackageDetails(O_)).versions.map(M_=>M_.version)}async getLatestVersion(O_){const I_=await this.getPackageDetails(O_);if(I_.versions.length===0)throw new Rd.RegistryFetchError(`Package "${O_}" has no versions`);return I_.versions[0].version}async search(O_){const I_=new URLSearchParams({q:O_,take:"100"}),M_=`${this.baseUrl}/package/search?${I_}`,L_=await fetch(M_,{headers:{"User-Agent":"invariant-cli"}});if(!L_.ok)throw new Rd.RegistryFetchError(`Search failed: ${L_.status}`);const U_=await L_.json();return this.mapSearchResults(U_)}async listAll(){const O_=`${this.baseUrl}/package/search?take=1000`,I_=await fetch(O_,{headers:{"User-Agent":"invariant-cli"}});if(!I_.ok)throw new Rd.RegistryFetchError(`List failed: ${I_.status}`);const M_=await I_.json();return this.mapSearchResults(M_)}async publishPackage(O_,I_,M_){const L_=`${this.baseUrl}/package/publish`,U_=new FormData;U_.append("name",O_.name),U_.append("version",O_.version),U_.append("description",O_.description||""),U_.append("author",O_.author||""),U_.append("readmeContent","");const W_=new Blob([new Uint8Array(I_)],{type:"application/gzip"});U_.append("tarball",W_,`${O_.name}-${O_.version}.tgz`);const z_=await fetch(L_,{method:"POST",headers:{"User-Agent":"invariant-cli",Authorization:`Bearer ${M_}`},body:U_});if(!z_.ok){const X_=await z_.text();throw new Rd.RegistryFetchError(`Publish failed: ${z_.status} ${X_}`)}return{name:O_.name,version:O_.version,url:`${this.baseUrl}/package/${O_.name}`}}async getPackageDetails(O_){const I_=`${this.baseUrl}/package/${encodeURIComponent(O_)}`,M_=await fetch(I_,{headers:{"User-Agent":"invariant-cli"}});if(M_.status===404)throw new Rd.RegistryFetchError(`Package "${O_}" not found`);if(!M_.ok)throw new Rd.RegistryFetchError(`Registry error: ${M_.status}`);return M_.json()}mapSearchResults(O_){return O_.items.map(I_=>({name:I_.name,version:I_.latestVersion,description:I_.description,author:I_.author,source:"registry"}))}buildPackageFromEntries(O_,I_,M_){let L_={name:I_,version:M_};const U_=O_.find(X_=>X_.path==="invariant.json"||X_.path.endsWith("/invariant.json"));if(U_){const X_=JSON.parse(U_.content);L_={name:X_.name||I_,version:X_.version||M_,description:X_.description,author:X_.author,license:X_.license,repository:X_.repository}}const W_=[...md.PACKAGE_ITEM_TYPES],z_=[];for(const X_ of W_)for(const K_ of O_){const G_=this.matchItemPath(K_.path,X_);G_&&K_.path.endsWith(".md")&&z_.push({type:X_,path:G_,content:K_.content})}return md.Package.create({manifest:L_,items:z_,source:{type:"registry"}})}matchItemPath(O_,I_){if(O_.startsWith(`${I_}/`))return O_;const M_=O_.indexOf(`/${I_}/`);return M_!==-1?O_.slice(M_+1):null}};return httpRegistry_queryBuilder.HttpRegistryQueryBuilder=P_,httpRegistry_queryBuilder.HttpRegistryQueryBuilder=P_=t([(0,Xf.Injectable)()],P_),httpRegistry_queryBuilder}e(requireHttpRegistry_queryBuilder,"requireHttpRegistry_queryBuilder");var hasRequiredPersistence$2;function requirePersistence$2(){return hasRequiredPersistence$2||(hasRequiredPersistence$2=1,(function(t){var Xf=persistence$2&&persistence$2.__createBinding||(Object.create?(function(pd,md,Rd,D_){D_===void 0&&(D_=Rd);var P_=Object.getOwnPropertyDescriptor(md,Rd);(!P_||("get"in P_?!md.__esModule:P_.writable||P_.configurable))&&(P_={enumerable:!0,get:e(function(){return md[Rd]},"get")}),Object.defineProperty(pd,D_,P_)}):(function(pd,md,Rd,D_){D_===void 0&&(D_=Rd),pd[D_]=md[Rd]})),fd=persistence$2&&persistence$2.__exportStar||function(pd,md){for(var Rd in pd)Rd!=="default"&&!Object.prototype.hasOwnProperty.call(md,Rd)&&Xf(md,pd,Rd)};Object.defineProperty(t,"__esModule",{value:!0}),fd(requireFileSystemStorage_repository(),t),fd(requireInMemoryRegistry_queryBuilder(),t),fd(requireHttpRegistry_queryBuilder(),t)})(persistence$2)),persistence$2}e(requirePersistence$2,"requirePersistence$2");var gateways$2={},claudeCodeCliTarget_gateway={},hasRequiredClaudeCodeCliTarget_gateway;function requireClaudeCodeCliTarget_gateway(){if(hasRequiredClaudeCodeCliTarget_gateway)return claudeCodeCliTarget_gateway;hasRequiredClaudeCodeCliTarget_gateway=1;var t=claudeCodeCliTarget_gateway&&claudeCodeCliTarget_gateway.__decorate||function(F_,O_,I_,M_){var L_=arguments.length,U_=L_<3?O_:M_===null?M_=Object.getOwnPropertyDescriptor(O_,I_):M_,W_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")U_=Reflect.decorate(F_,O_,I_,M_);else for(var z_=F_.length-1;z_>=0;z_--)(W_=F_[z_])&&(U_=(L_<3?W_(U_):L_>3?W_(O_,I_,U_):W_(O_,I_))||U_);return L_>3&&U_&&Object.defineProperty(O_,I_,U_),U_};Object.defineProperty(claudeCodeCliTarget_gateway,"__esModule",{value:!0}),claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd=".claude",D_="CLAUDE.md";let P_=class extends md.CliTargetGateway{static{e(this,"ClaudeCodeCliTargetGateway")}constructor(){super(...arguments),this.config={name:"claude",displayName:"Claude Code",supportsItemTypes:!0}}async copyItem(O_,I_,M_,L_){const U_=pd.join(O_,Rd,I_);await fd.mkdir(U_,{recursive:!0});const W_=[];if(I_!=="skills"&&L_.length===1&&!L_[0].relativePath.includes("/")){const X_=pd.join(U_,M_+".md");await fd.writeFile(X_,L_[0].content,"utf-8"),W_.push(X_)}else{const X_=pd.join(U_,M_);for(const K_ of L_){const G_=pd.join(X_,K_.relativePath);await fd.mkdir(pd.dirname(G_),{recursive:!0}),await fd.writeFile(G_,K_.content,"utf-8"),W_.push(G_)}}return{files:W_,itemNames:[M_]}}async removeItem(O_,I_,M_){const L_=pd.join(O_,Rd,I_),U_=pd.join(L_,M_);try{if((await fd.stat(U_)).isDirectory()){await fd.rm(U_,{recursive:!0});return}}catch{}try{await fd.unlink(U_+".md")}catch{}}async generateContextFile(O_,I_){const M_=pd.join(O_,D_);return await fd.writeFile(M_,I_,"utf-8"),M_}async contextFileExists(O_){try{return await fd.access(pd.join(O_,D_)),!0}catch{return!1}}async resolveItemPath(O_,I_,M_){const L_=pd.join(O_,Rd,I_),U_=pd.join(L_,M_);try{if((await fd.stat(U_)).isDirectory())return pd.join(Rd,I_,M_)}catch{}const W_=U_+".md";try{return await fd.access(W_),pd.join(Rd,I_,M_+".md")}catch{}return null}};return claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=P_,claudeCodeCliTarget_gateway.ClaudeCodeCliTargetGateway=P_=t([(0,Xf.Injectable)()],P_),claudeCodeCliTarget_gateway}e(requireClaudeCodeCliTarget_gateway,"requireClaudeCodeCliTarget_gateway");var codexCliTarget_gateway={},hasRequiredCodexCliTarget_gateway;function requireCodexCliTarget_gateway(){if(hasRequiredCodexCliTarget_gateway)return codexCliTarget_gateway;hasRequiredCodexCliTarget_gateway=1;var t=codexCliTarget_gateway&&codexCliTarget_gateway.__decorate||function(P_,F_,O_,I_){var M_=arguments.length,L_=M_<3?F_:I_===null?I_=Object.getOwnPropertyDescriptor(F_,O_):I_,U_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")L_=Reflect.decorate(P_,F_,O_,I_);else for(var W_=P_.length-1;W_>=0;W_--)(U_=P_[W_])&&(L_=(M_<3?U_(L_):M_>3?U_(F_,O_,L_):U_(F_,O_))||L_);return M_>3&&L_&&Object.defineProperty(F_,O_,L_),L_};Object.defineProperty(codexCliTarget_gateway,"__esModule",{value:!0}),codexCliTarget_gateway.CodexCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd="AGENTS.md";let D_=class extends md.CliTargetGateway{static{e(this,"CodexCliTargetGateway")}constructor(){super(...arguments),this.config={name:"codex",displayName:"OpenAI Codex",supportsItemTypes:!1}}async copyItem(F_,O_,I_,M_){return{files:[],itemNames:[]}}async removeItem(F_,O_,I_){}async generateContextFile(F_,O_){const I_=pd.join(F_,Rd);return await fd.writeFile(I_,O_,"utf-8"),I_}async contextFileExists(F_){try{return await fd.access(pd.join(F_,Rd)),!0}catch{return!1}}async resolveItemPath(F_,O_,I_){return null}};return codexCliTarget_gateway.CodexCliTargetGateway=D_,codexCliTarget_gateway.CodexCliTargetGateway=D_=t([(0,Xf.Injectable)()],D_),codexCliTarget_gateway}e(requireCodexCliTarget_gateway,"requireCodexCliTarget_gateway");var cursorCliTarget_gateway={},hasRequiredCursorCliTarget_gateway;function requireCursorCliTarget_gateway(){if(hasRequiredCursorCliTarget_gateway)return cursorCliTarget_gateway;hasRequiredCursorCliTarget_gateway=1;var t=cursorCliTarget_gateway&&cursorCliTarget_gateway.__decorate||function(P_,F_,O_,I_){var M_=arguments.length,L_=M_<3?F_:I_===null?I_=Object.getOwnPropertyDescriptor(F_,O_):I_,U_;if(typeof Reflect=="object"&&typeof Reflect.decorate=="function")L_=Reflect.decorate(P_,F_,O_,I_);else for(var W_=P_.length-1;W_>=0;W_--)(U_=P_[W_])&&(L_=(M_<3?U_(L_):M_>3?U_(F_,O_,L_):U_(F_,O_))||L_);return M_>3&&L_&&Object.defineProperty(F_,O_,L_),L_};Object.defineProperty(cursorCliTarget_gateway,"__esModule",{value:!0}),cursorCliTarget_gateway.CursorCliTargetGateway=void 0;const Xf=requireCommon$1(),fd=require$$1$5,pd=require$$2$2,md=requireGateways$3(),Rd=".cursor/rules";let D_=class extends md.CliTargetGateway{static{e(this,"CursorCliTargetGateway")}constructor(){super(...arguments),this.config={name:"cursor",displayName:"Cursor",supportsItemTypes:!1}}async copyItem(F_,O_,I_,M_){const L_=pd.join(F_,Rd);await fd.mkdir(L_,{recursive:!0});const U_=I_+".mdc",W_=pd.join(L_,U_),z_=this.concatenateFiles(M_),X_=this.wrapWithFrontmatter(z_,O_,I_);return await fd.writeFile(W_,X_,"utf-8"),{files:[W_],itemNames:[I_]}}async removeItem(F_,O_,I_){const M_=I_+".mdc";try{await fd.unlink(pd.join(F_,Rd,M_))}catch{}}async generateContextFile(F_,O_){const I_=pd.join(F_,Rd);await fd.mkdir(I_,{recursive:!0});const M_=pd.join(I_,"invariant-context.mdc"),L_=this.wrapWithFrontmatter(O_,"context","Invariant project context");return await fd.writeFile(M_,L_,"utf-8"),M_}async contextFileExists(F_){try{return await fd.access(pd.join(F_,Rd,"invariant-context.mdc")),!0}catch{return!1}}async resolveItemPath(F_,O_,I_){const M_=pd.join(F_,Rd,I_+".mdc");try{return await fd.access(M_),pd.join(Rd,I_+".mdc")}catch{return null}}concatenateFiles(F_){return F_.length===1?F_[0].content:F_.map(O_=>O_.content).join(`
973
973
 
974
974
  `)}wrapWithFrontmatter(F_,O_,I_){return`---
975
975
  description: "${O_}: ${I_}"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@invariant.guru/cli",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "CLI tool for managing Claude AI specification invariants",
5
5
  "bin": {
6
6
  "invariant": "./dist/main.js"