projmux 0.4.5 → 0.4.7
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-ko.md +58 -282
- package/README.md +57 -273
- package/docs/agent-workflow.md +56 -0
- package/docs/architecture.md +199 -0
- package/docs/assets/projmux-icon.png +0 -0
- package/docs/assets/projmux-shell-sidebar.gif +0 -0
- package/docs/cli.md +432 -0
- package/docs/configuration.md +127 -0
- package/docs/hooks.md +118 -0
- package/docs/install.md +99 -0
- package/docs/keybindings.md +337 -0
- package/docs/migration-plan.md +82 -0
- package/docs/native-picker-no-fzf-poc.md +228 -0
- package/docs/native-picker-parity.md +145 -0
- package/docs/notify-queue.md +200 -0
- package/docs/npm-distribution.md +86 -0
- package/docs/picker-ui-plan.md +111 -0
- package/docs/pr-guideline.md +106 -0
- package/docs/repo-layout.md +50 -0
- package/docs/roadmap.md +95 -0
- package/docs/shell-autostart.md +33 -0
- package/docs/statusbar.md +157 -0
- package/docs/testing.md +57 -0
- package/docs/upgrading.md +100 -0
- package/docs/usage-tracking.md +153 -0
- package/package.json +7 -5
package/docs/testing.md
ADDED
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Testing
|
|
2
|
+
|
|
3
|
+
The local validation contract is exposed through make targets so CI, agents,
|
|
4
|
+
and humans run the same entrypoints.
|
|
5
|
+
|
|
6
|
+
## Targets
|
|
7
|
+
|
|
8
|
+
- `make test` runs the fast Go unit suite. These tests avoid tmux, TTY, GUI,
|
|
9
|
+
and host shell dependencies.
|
|
10
|
+
- Picker unit coverage includes the backend-neutral item/action contract, fzf
|
|
11
|
+
adapter mapping, native title-focused filtering, numeric selection, and
|
|
12
|
+
shared close actions.
|
|
13
|
+
- `make test-integration` builds `test/docker/Dockerfile` and runs
|
|
14
|
+
`test/integration/linux-smoke.sh` in Docker. It validates Linux dependency
|
|
15
|
+
discovery, tmux config generation/install, app config reload against a real
|
|
16
|
+
`tmux` server, and notify queue CRUD.
|
|
17
|
+
- `make test-install-smoke` builds the same Docker image and runs
|
|
18
|
+
`test/install/smoke.sh`. It validates `make install`, atomic binary
|
|
19
|
+
replacement into an isolated install dir, `tmux apply`, and post-install
|
|
20
|
+
`notify reconcile` initialization with a fresh HOME/XDG state tree.
|
|
21
|
+
- `make test-e2e` builds the same Docker image and runs
|
|
22
|
+
`test/e2e/linux-smoke.sh`. It validates a minimal real-tmux workflow:
|
|
23
|
+
sessions, panes, config sourcing, reply-state notify reconciliation, focus
|
|
24
|
+
notify fallback, and status notify rendering.
|
|
25
|
+
|
|
26
|
+
## Docker-Covered Checks
|
|
27
|
+
|
|
28
|
+
The Docker suites are intended to cover portable Linux behavior that can be
|
|
29
|
+
made deterministic in a container:
|
|
30
|
+
|
|
31
|
+
- binary build and source install into an isolated prefix
|
|
32
|
+
- `doctor` dependency checks for `tmux`, `fzf`, `git`, and `stty`
|
|
33
|
+
- tmux config print/install/apply paths
|
|
34
|
+
- notify queue push/list/ack/reconcile state transitions
|
|
35
|
+
- focus fallback behavior when a tmux server has sessions but no attached
|
|
36
|
+
client
|
|
37
|
+
- status rendering that only depends on tmux state and local files
|
|
38
|
+
|
|
39
|
+
The test container disables networking during `docker run`. The image build may
|
|
40
|
+
use the network to fetch the pinned base image, apt packages, and pinned fzf
|
|
41
|
+
version, but suite execution should not need network access after the image is
|
|
42
|
+
built.
|
|
43
|
+
|
|
44
|
+
## Host-Only Checks
|
|
45
|
+
|
|
46
|
+
The Docker suites do not replace checks that depend on a real host terminal,
|
|
47
|
+
desktop shell, or OS integration:
|
|
48
|
+
|
|
49
|
+
- terminal emulator key delivery and swallowing for `Alt-1..5`, `Ctrl-N`, and
|
|
50
|
+
CSI-u chords
|
|
51
|
+
- Windows Terminal and WSL interop
|
|
52
|
+
- macOS host path, shell, and GUI behavior
|
|
53
|
+
- desktop notification click callbacks
|
|
54
|
+
- terminal-specific popup rendering and interactive key dispatch
|
|
55
|
+
|
|
56
|
+
Keep those checks as manual or host-run smoke validation until a dedicated
|
|
57
|
+
host harness exists.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# Upgrading
|
|
2
|
+
|
|
3
|
+
projmux has two update surfaces:
|
|
4
|
+
|
|
5
|
+
- `projmux shell` reads the cached release status before opening the app. When
|
|
6
|
+
the cache is fresh and a newer release is available, startup shows a picker
|
|
7
|
+
with Update Now, Later, and Skip This Version actions.
|
|
8
|
+
- Settings > About > Update shows the current version, detected installer,
|
|
9
|
+
cached latest version, Check Updates, and Update Now actions.
|
|
10
|
+
|
|
11
|
+
Startup never reaches the network. Refresh the cache explicitly when you want
|
|
12
|
+
projmux to check GitHub Releases:
|
|
13
|
+
|
|
14
|
+
```sh
|
|
15
|
+
projmux update check
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
Apply the cached update through the detected installer:
|
|
19
|
+
|
|
20
|
+
```sh
|
|
21
|
+
projmux update apply
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Use `--dry-run` to see the planned action and `--no-apply` to skip reloading
|
|
25
|
+
the live tmux config after the binary changes.
|
|
26
|
+
|
|
27
|
+
## npm Installs
|
|
28
|
+
|
|
29
|
+
The recommended install path is:
|
|
30
|
+
|
|
31
|
+
```sh
|
|
32
|
+
npm install -g projmux
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
For npm-managed installs, `projmux update apply` runs:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
npm update -g projmux
|
|
39
|
+
projmux tmux apply
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
The npm shim sets `PROJMUX_INSTALLER=npm` so projmux can detect this path
|
|
43
|
+
automatically. You can also update manually with `npm update -g projmux`.
|
|
44
|
+
|
|
45
|
+
## Go Installs
|
|
46
|
+
|
|
47
|
+
If you installed with `go install`, `projmux update apply` delegates to the
|
|
48
|
+
same atomic replacement flow as `projmux upgrade`:
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
projmux upgrade # @latest, replace + apply
|
|
52
|
+
projmux upgrade --ref @v0.4.0 # pin a specific tag
|
|
53
|
+
projmux upgrade --ref @main # track a branch
|
|
54
|
+
projmux upgrade --target /usr/local/bin/projmux # replace another path
|
|
55
|
+
projmux upgrade --no-apply # skip 'projmux tmux apply'
|
|
56
|
+
projmux upgrade --dry-run # print the steps only
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
`projmux upgrade` reinstalls via `go install`, atomically replaces the active
|
|
60
|
+
file, and reapplies the live tmux config so a running `-L projmux` server picks
|
|
61
|
+
up new bindings without a restart.
|
|
62
|
+
|
|
63
|
+
The command reads `PROJMUX_PROJDIR` from the calling shell and memoizes the
|
|
64
|
+
primary path to `~/.config/projmux/projdir`, so the new binary keeps the same
|
|
65
|
+
project root context as the one it replaces.
|
|
66
|
+
|
|
67
|
+
To switch the saved project root during the upgrade:
|
|
68
|
+
|
|
69
|
+
```sh
|
|
70
|
+
PROJMUX_PROJDIR=/new/path projmux upgrade
|
|
71
|
+
|
|
72
|
+
# Multi-path also works; only the primary entry is persisted.
|
|
73
|
+
PROJMUX_PROJDIR="/main/repos:/secondary/repos" projmux upgrade
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## GitHub Release Installs
|
|
77
|
+
|
|
78
|
+
When `PROJMUX_INSTALLER=github-release`, `projmux update apply` downloads the
|
|
79
|
+
latest matching `projmux_<version>_<goos>_<goarch>.tar.gz` asset from GitHub
|
|
80
|
+
Releases, extracts the binary, atomically replaces the current executable, and
|
|
81
|
+
then runs `projmux tmux apply` unless `--no-apply` is set.
|
|
82
|
+
|
|
83
|
+
Set the installer explicitly if you manage a release binary outside npm or Go:
|
|
84
|
+
|
|
85
|
+
```sh
|
|
86
|
+
export PROJMUX_INSTALLER=github-release
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
## Source Checkouts
|
|
90
|
+
|
|
91
|
+
Source installs are updated from the checkout:
|
|
92
|
+
|
|
93
|
+
```sh
|
|
94
|
+
git pull --ff-only
|
|
95
|
+
make install
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
`projmux update apply` reports an actionable error for
|
|
99
|
+
`PROJMUX_INSTALLER=source` because source trees may have local changes and need
|
|
100
|
+
the repository's normal review and test flow.
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
# Usage tracking
|
|
2
|
+
|
|
3
|
+
`projmux usage` and `projmux status usage` report authoritative 5-hour
|
|
4
|
+
and weekly utilisation for both Claude Code and the Codex CLI. Both
|
|
5
|
+
adapters read from the upstream's own view of the account so the
|
|
6
|
+
percentages match what `claude /usage` and `codex` show natively.
|
|
7
|
+
|
|
8
|
+
## Adapters
|
|
9
|
+
|
|
10
|
+
### Claude (`internal/core/usage/adapters/claude`)
|
|
11
|
+
|
|
12
|
+
OAuth-authenticated HTTPS fetch:
|
|
13
|
+
|
|
14
|
+
- `GET https://api.anthropic.com/api/oauth/usage` with the bearer token
|
|
15
|
+
read from `~/.claude/.credentials.json`.
|
|
16
|
+
- On HTTP 401 the adapter performs a single refresh round-trip:
|
|
17
|
+
`POST https://api.anthropic.com/api/oauth/token` with the stored
|
|
18
|
+
refresh token, then rewrites `.credentials.json` with the rotated
|
|
19
|
+
access/refresh pair before retrying the usage call.
|
|
20
|
+
- Tokens are never logged.
|
|
21
|
+
|
|
22
|
+
Throttle: 5 minutes (`ThrottleHinter`). The `60s` cadence used in 0.3
|
|
23
|
+
trips 429 in practice, so the adapter raises the manager's per-adapter
|
|
24
|
+
floor.
|
|
25
|
+
|
|
26
|
+
429 backoff (`BackoffStater`):
|
|
27
|
+
|
|
28
|
+
- Base `30m`, doubling per consecutive 429, cap `60m`.
|
|
29
|
+
- A `Retry-After` header (when ≥ 60s) raises the floor.
|
|
30
|
+
- During backoff `Collect` short-circuits — no network call, prior
|
|
31
|
+
rows are preserved.
|
|
32
|
+
- A clean 200 resets the consecutive counter.
|
|
33
|
+
- `--force` (BackoffResetter) clears the persisted state and attempts
|
|
34
|
+
the call regardless of streak.
|
|
35
|
+
|
|
36
|
+
### Codex (`internal/core/usage/adapters/codex`)
|
|
37
|
+
|
|
38
|
+
Local rollout JSONL parser. No network calls.
|
|
39
|
+
|
|
40
|
+
- Walks `${HOME}/.codex/sessions/<YYYY>/<MM>/<DD>/rollout-*.jsonl`
|
|
41
|
+
newest-first by mtime (NOT filename — Dropbox-synced rollouts can
|
|
42
|
+
arrive out of order).
|
|
43
|
+
- Caps the search to 8 days back so a stale tree never blocks the
|
|
44
|
+
status interval.
|
|
45
|
+
- Parses each line until it finds the latest `token_count` record's
|
|
46
|
+
`rate_limits` payload:
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"limit_id": "codex",
|
|
50
|
+
"primary": { "used_percent": 8.0, "resets_at": "..." },
|
|
51
|
+
"secondary": { "used_percent": 4.5, "resets_at": "..." }
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
- `primary` → 5h window, `secondary` → weekly window.
|
|
55
|
+
|
|
56
|
+
Codex shares the manager's default `30s` throttle (no
|
|
57
|
+
`ThrottleHinter`). It does not implement `BackoffStater` — local-only
|
|
58
|
+
read.
|
|
59
|
+
|
|
60
|
+
## Snapshot store
|
|
61
|
+
|
|
62
|
+
```
|
|
63
|
+
${PROJMUX_USAGE_STATE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/projmux/usage}/snapshots.json
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
JSON document keyed by adapter, recording:
|
|
67
|
+
|
|
68
|
+
- per-window `Snapshot{Model, Window, Pct, Limit, ResetsAt, UpdatedAt}`
|
|
69
|
+
- per-adapter `last_collect` timestamp (drives the throttle)
|
|
70
|
+
- per-adapter `Backoff{Until, Consecutive}` (drives the cooldown)
|
|
71
|
+
|
|
72
|
+
Adapter failures merge over the prior slice rather than replacing it,
|
|
73
|
+
so a 429 keeps the last known good rows visible. Force-redirect the
|
|
74
|
+
file at install time via `PROJMUX_USAGE_STATE_DIR` to share the cache
|
|
75
|
+
across machines (Dropbox, iCloud Drive).
|
|
76
|
+
|
|
77
|
+
## CLI
|
|
78
|
+
|
|
79
|
+
### `projmux usage`
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
projmux usage [--model codex|claude|all] [--window 5h|weekly|all]
|
|
83
|
+
[--json] [--force|-f]
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
Calls `Manager.Collect` (or `ForceCollect` with `--force`), filters by
|
|
87
|
+
model/window, and renders the tab-aligned table:
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
MODEL WINDOW PCT RESETS_AT STALE
|
|
91
|
+
claude 5h 80% 2026-05-07T14:00:00+09:00
|
|
92
|
+
claude weekly 35% 2026-05-09T00:00:00+09:00
|
|
93
|
+
codex 5h 8% 2026-05-07T11:30:00+09:00
|
|
94
|
+
codex weekly 4% 2026-05-09T00:00:00+09:00
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
`STALE` is `*` when `now - UpdatedAt > 10m`. A `--json` payload returns
|
|
98
|
+
the `Snapshot` array; if any adapter is in backoff, the wrapper
|
|
99
|
+
`{snapshots, backoff: {model: {until, consecutive}}}` is emitted
|
|
100
|
+
instead. A backoff note is appended to the human table:
|
|
101
|
+
|
|
102
|
+
```
|
|
103
|
+
claude is in backoff, try again in 30m (use --force to bypass)
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### `projmux status usage`
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
projmux status usage [--max-width N] [--force|-f]
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
The HUD bar wired to the tmux status interval. Triggers an
|
|
113
|
+
opportunistic refresh: `MaybeCollect(throttle=30s)` (subject to
|
|
114
|
+
per-adapter throttle and active backoff). Errors are swallowed unless
|
|
115
|
+
`PROJMUX_USAGE_DEBUG` is set. Then loads the cache and renders.
|
|
116
|
+
|
|
117
|
+
Output degrades through six tiers as `--max-width` shrinks:
|
|
118
|
+
|
|
119
|
+
1. Long form with last-sync age + bars: `Claude (3m) 5h [████████░░]
|
|
120
|
+
80% · weekly [...] Codex 5h [...] 20% · weekly [...]`
|
|
121
|
+
2. Drop the age indicator (legacy long form).
|
|
122
|
+
3. Drop the weekly bar.
|
|
123
|
+
4. Drop bars entirely (`Claude 5h:80% weekly:30%`).
|
|
124
|
+
5. Single-letter labels (`C 5h:80% weekly:30%`).
|
|
125
|
+
6. Hard rune-truncate with trailing `…`.
|
|
126
|
+
|
|
127
|
+
The age indicator's colour ramps with staleness: dim grey
|
|
128
|
+
(<1h), yellow (1–6h), bold red (≥6h). Codex opts out of the indicator
|
|
129
|
+
because the rollout file is always near-current (no throttle gap to
|
|
130
|
+
report).
|
|
131
|
+
|
|
132
|
+
## Force semantics
|
|
133
|
+
|
|
134
|
+
`--force` / `-f` does two things:
|
|
135
|
+
|
|
136
|
+
1. Bypasses the per-adapter throttle gate so every adapter's `Collect`
|
|
137
|
+
runs even if `now - last_collect < throttle`.
|
|
138
|
+
2. Calls `BackoffResetter.ResetBackoff` on adapters that implement it,
|
|
139
|
+
clearing the in-memory and on-disk backoff so the network call
|
|
140
|
+
attempts regardless of any active 429 cooldown.
|
|
141
|
+
|
|
142
|
+
A force that itself returns 429 records a fresh `consecutive=1` —
|
|
143
|
+
forcing does NOT preserve the prior streak (the user explicitly chose
|
|
144
|
+
to attempt now). Bind it to a tmux key (e.g. `prefix U`) for a manual
|
|
145
|
+
"refresh now" gesture.
|
|
146
|
+
|
|
147
|
+
## Environment variables
|
|
148
|
+
|
|
149
|
+
| Variable | Effect |
|
|
150
|
+
| --- | --- |
|
|
151
|
+
| `PROJMUX_USAGE_STATE_DIR` | Override snapshot directory. Resolved verbatim, no `~` expansion. |
|
|
152
|
+
| `PROJMUX_USAGE_DEBUG` | Surface adapter errors from `status usage` to stderr. |
|
|
153
|
+
| `PROJMUX_USAGE_LIMITS_PATH` | Deprecated; read but ignored (limits come from upstream APIs). |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "projmux",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.7",
|
|
4
4
|
"description": "tmux project session manager",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"homepage": "https://github.com/crevissepartners/projmux#readme",
|
|
@@ -17,15 +17,17 @@
|
|
|
17
17
|
},
|
|
18
18
|
"files": [
|
|
19
19
|
"npm/projmux.js",
|
|
20
|
+
"docs/*.md",
|
|
21
|
+
"docs/assets/*",
|
|
20
22
|
"README.md",
|
|
21
23
|
"README-ko.md",
|
|
22
24
|
"LICENSE"
|
|
23
25
|
],
|
|
24
26
|
"optionalDependencies": {
|
|
25
|
-
"@projmux/darwin-arm64": "0.4.
|
|
26
|
-
"@projmux/darwin-x64": "0.4.
|
|
27
|
-
"@projmux/linux-arm64": "0.4.
|
|
28
|
-
"@projmux/linux-x64": "0.4.
|
|
27
|
+
"@projmux/darwin-arm64": "0.4.7",
|
|
28
|
+
"@projmux/darwin-x64": "0.4.7",
|
|
29
|
+
"@projmux/linux-arm64": "0.4.7",
|
|
30
|
+
"@projmux/linux-x64": "0.4.7"
|
|
29
31
|
},
|
|
30
32
|
"scripts": {
|
|
31
33
|
"package:npm": "scripts/package-npm.sh",
|