claude-mode.ps1 was 2,407 lines. It is now 153: the help block, parameters, paths, the managed-key list, a loader, and the dispatch. The rest moved, verbatim, into eleven files under lib/ - providers, output, files, core, vault, switch, guards, health, catalogue, commands, menu - dot-sourced into the script's scope in their original order, with the same check as the bash split that every original line landed in exactly one file. Inside a module $PSScriptRoot is lib\, so the one path beside the main script (providers.json) now goes through $script:Here. install.ps1 ships lib\, clearing old modules first. The Windows suite parses every module and install.ps1 (36 checks, all green on Windows PowerShell 5.1); install.ps1 is parsed but never run, since it edits the real profile and User PATH. linux/bootstrap.sh and scripts/build-package.ps1 existed only to build and serve packages for the Arkylx Index. Both installers fetch the repository's own archive, so a push to master is the release; the two scripts, the dist/ ignore and their mentions in the docs are gone. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
122 lines
6.3 KiB
Markdown
122 lines
6.3 KiB
Markdown
# Development
|
|
|
|
How to change claude-mode without breaking it. How the pieces fit is in
|
|
[architecture.md](architecture.md); why they are shaped that way, in
|
|
[design.md](design.md).
|
|
|
|
## Running the tests
|
|
|
|
```bash
|
|
scripts/test.sh # static checks, Python unit tests, CLI tests (~30s)
|
|
scripts/test.sh --windows # ...and the Windows suite on winbox over SSH
|
|
scripts/test.sh --windows host # ...on another Windows host
|
|
make test # the same, if make is installed
|
|
```
|
|
|
|
Nothing needs installing beyond bash, python3 and curl. `qmllint` and `shellcheck`
|
|
are used when present and skipped, out loud, when not.
|
|
|
|
| suite | what it covers |
|
|
|---|---|
|
|
| `tests/static.sh` | every script parses; JSON is valid; `VERSION` matches the widget manifest and has a `CHANGELOG.md` entry; `providers.json` uses only the kinds, probes and checks the code implements, every default preset exists, no id or alias shadows a command; Markdown links resolve; QML has no syntax errors |
|
|
| `tests/python/` | `linux/cm-json.py`, function by function: providers and the column contract the shell depends on, the original three scaffolds byte for byte, every catalogue parser, the cache, session scan/dismiss/repair, rename, defaults, health, the cost guard |
|
|
| `tests/cli/` | the POSIX CLI end to end, in a sandbox, against fake servers: providers, preflight, presets, models, doctor, a real switch, sessions, and the key helper under a rename race |
|
|
| `tests/windows/` | `claude-mode.ps1` on a real Windows PowerShell 5.1, in a sandbox, against the same fake servers |
|
|
|
|
**The tests never touch a real install.** Each CLI test gets its own `HOME`,
|
|
`CM_ROOT` and `CLAUDE_CONFIG_DIR` with the repository's scripts installed into it,
|
|
and the vault forced to the plain-file backend (`CLAUDE_MODE_VAULT=file`), so your
|
|
keyring, `~/.claude-mode` and `~/.claude/settings.json` are never read or written;
|
|
a switch a test performs rewrites the sandbox's `settings.json`. The Windows suite
|
|
points `USERPROFILE` at a temp folder for its process, which moves every path the
|
|
script uses. `tests/fake_server.py` stands in for Ollama, LM Studio, a keyed
|
|
gateway and a listing-less proxy, on ports the OS picks.
|
|
|
|
The Windows suite needs key-based SSH to the host and python on the host (for the
|
|
fake servers). It copies a payload to the host's `%TEMP%`, runs it, and removes it.
|
|
|
|
**Do not test by switching your own machine.** A switch rewrites the live
|
|
`settings.json` that every Claude Code session — including one you may be working
|
|
in — depends on. Test in the sandbox; `tests/lib.sh` shows how to get one by hand.
|
|
|
|
### Writing a test
|
|
|
|
A CLI test sources `tests/lib.sh`, which provides the sandbox, `run_cm` (output in
|
|
`$OUT`, colour stripped; status in `$RC`), `expect_rc` / `expect_out` /
|
|
`expect_eq` / `expect_file`, `start_fake` (sets `OLLAMA_URL`, `LMSTUDIO_URL`,
|
|
`KEYED_URL`, `PROXY_URL`), `ready_preset`, `set_state` and `preflight_code`, and
|
|
ends with `finish`. Assertions count rather than stop, so one run reports every
|
|
failure. A test earns its place by failing when the thing it describes breaks —
|
|
check that it does, by breaking it.
|
|
|
|
## Conventions
|
|
|
|
The code follows these already; they are written down so it keeps doing so.
|
|
|
|
**Portability of the POSIX port.** It runs on the bash 3.2 macOS ships: no
|
|
associative arrays, no `mapfile`, no fractional `read -t`, no GNU-only `stat` or
|
|
`find` flags. `linux/claude-mode` uses no `awk` (absent from minimal images).
|
|
python3 is the only dependency, and handles every piece of JSON — bash only ever
|
|
sees flat lines of text.
|
|
|
|
**Never read TSV with `IFS=$'\t' read`** when a column can be empty: tab counts as
|
|
whitespace to `read`, so empty fields merge and later columns shift. Use `cut`
|
|
(`tsv_field`), or have the producer print `-` for empty.
|
|
|
|
**Windows PowerShell 5.1.** No `?:` or `??`; `"$id:latest"` parses as a
|
|
drive-qualified variable, so write `"${id}:latest"`. The `.ps1` files are ASCII
|
|
only, because 5.1 reads a file without a BOM in the ANSI codepage. In the
|
|
`lib/*.ps1` modules `$PSScriptRoot` is `lib\`: reach anything beside
|
|
`claude-mode.ps1` through `$script:Here`.
|
|
|
|
**Files are LF everywhere**, `.ps1` and `.cmd` included (`.gitattributes`,
|
|
`.editorconfig`).
|
|
|
|
**Comments say why**, and name the thing that went wrong when a line exists
|
|
because of it. What the code does is left to the code.
|
|
|
|
**Keys never leave the vault**: not into `settings.json`, `health.json`, the model
|
|
cache, logs, or test output.
|
|
|
|
## Providers
|
|
|
|
Adding a provider that reuses existing behaviour is an entry in `providers.json`
|
|
and a preset in `presets/` — see [providers.md](providers.md#adding-a-provider).
|
|
The static check enforces the entry's shape; add a CLI test if the provider does
|
|
anything the others do not. A new *kind* of behaviour needs a parser in
|
|
`cm-json.py`, a branch in the bash CLI and in `claude-mode.ps1`, and its value
|
|
added to the check in `tests/static.sh`.
|
|
|
|
`cm-json.py`'s `PROVIDER_TSV` is a contract: the shell addresses its columns by
|
|
number. Append only, and update the column list in `linux/claude-mode` and the
|
|
unit test that pins it.
|
|
|
|
## The bar widget
|
|
|
|
- Install with `bash omarchy/install.sh`, then `omarchy restart shell`: an edited
|
|
QML file does not reliably reload in a running shell, and `Modes.js`, a
|
|
`.pragma library`, never does.
|
|
- Anything with a text field must live in a `KeyboardPanel`. A `PopupCard` is an
|
|
xdg-popup, which never receives the keyboard, so its fields look fine and take
|
|
no typing.
|
|
- The panel's height clamps to the screen rather than scrolling: long content
|
|
needs its own bounded, scrolling area, or it is cut off.
|
|
- Provider presentation comes from `health.json`, not from `Modes.js` — new
|
|
providers need no QML change.
|
|
- There are no automated UI tests. `tests/static.sh` catches QML syntax errors;
|
|
everything else is checked by eye, on the real bar.
|
|
|
|
## Releasing
|
|
|
|
1. `scripts/test.sh --windows` — everything green.
|
|
2. `scripts/bump-version.sh X.Y.Z`, and a `CHANGELOG.md` entry for it (the static
|
|
check fails without one).
|
|
3. Commit.
|
|
4. Install on your own machine: `bash linux/install.sh`, `bash omarchy/install.sh`,
|
|
`omarchy restart shell`, and look at the bar.
|
|
5. Push. Both installers fetch the repository's own archive (`install.ps1` piped
|
|
through `iex`, `linux/install.sh` through `curl | bash`), so a push to `master`
|
|
is the release.
|
|
|
|
Semver: a minor version adds behaviour, a patch fixes it.
|