claude-code-station 0.2.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/CHANGELOG.md +176 -0
- package/LICENSE +21 -0
- package/README.md +311 -0
- package/bin/ccs +376 -0
- package/bin/ccs-config.ts +528 -0
- package/bin/ccs-db.ts +404 -0
- package/bin/ccs-delete-session.ts +48 -0
- package/bin/ccs-delete.sh +100 -0
- package/bin/ccs-init.ts +287 -0
- package/bin/ccs-list.ts +363 -0
- package/bin/ccs-preview-session.ts +147 -0
- package/bin/ccs-preview.ts +368 -0
- package/bin/ccs-sanitize.ts +57 -0
- package/bin/ccs-scan-sessions.ts +402 -0
- package/bin/ccs-scan.ts +734 -0
- package/bin/ccs-secrets.ts +104 -0
- package/bin/ccs-time.ts +27 -0
- package/bin/ccs-utils.ts +161 -0
- package/docs/design/repos-yml-schema.md +217 -0
- package/docs/design/sqlite-schema.md +253 -0
- package/docs/v0.2.0-regression-checklist.md +40 -0
- package/docs/v0.2.0-review-notes.md +151 -0
- package/docs/v0.2.1-backlog.md +225 -0
- package/package.json +44 -0
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# v0.2.1+ Deferred Backlog
|
|
2
|
+
|
|
3
|
+
Collected from the second-round code review (CR2, 2026-04-14) across four specialist reviews (TypeScript / Security / Database / General). **None of these are ship blockers for v0.2.0** — all HIGH-severity findings and the critical MEDIUM items were addressed in Phase 6 (see `docs/v0.2.0-review-notes.md` §Phase 6 fixes). The items below are deliberate deferrals, scheduled for v0.2.1 or later as scope permits.
|
|
4
|
+
|
|
5
|
+
Severity conventions per `~/.claude/rules/common/code-review.md`:
|
|
6
|
+
CRITICAL (block) · HIGH (warn) · MEDIUM (info) · LOW (note) · INFO (optional)
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## Closed in the 2026-06-12 review cleanup (`fix/v0.2.1-review-cleanup`)
|
|
11
|
+
|
|
12
|
+
The 4-perspective defensive review (Correctness / Consistency / Design /
|
|
13
|
+
Devil's Advocate, 2026-06-12) confirmed and closed the following entries.
|
|
14
|
+
Details live in `CHANGELOG.md` [Unreleased] and the per-fix regression tests.
|
|
15
|
+
|
|
16
|
+
- **[MEDIUM] `isUnderHome` lexical resolve()** — already fixed by audit M-4
|
|
17
|
+
(realpathSync two-layer check); this entry had gone stale (review K-3).
|
|
18
|
+
- **[MEDIUM] Extract shared utils to `bin/ccs-utils.ts`** — done (review
|
|
19
|
+
A-1/A-2/A-3/K-8): truncate / timeBucket / formatRelativeTime /
|
|
20
|
+
formatDateTime / extractText / UUID_RE / MAX_JSONL_SIZE unified, with
|
|
21
|
+
`test/ccs-utils.test.ts` pinning behavior.
|
|
22
|
+
- **[MEDIUM] `git log %x09` tab separator** — switched to `%x00` (NUL) +
|
|
23
|
+
`split("\0")` with a tab-in-subject regression test (review C-1).
|
|
24
|
+
- **[MEDIUM] `--current-only` filters in JS** — pushed into SQL as
|
|
25
|
+
`cwd = ? OR (cwd >= ? AND cwd < ?)` so `idx_sessions_cwd` applies
|
|
26
|
+
(review A-10), with an exact/subdir/sibling-prefix test.
|
|
27
|
+
- **[LOW] Error message style inconsistency** — `command`/`custom` errors now
|
|
28
|
+
carry the `reposYmlPath: repos[i].field` prefix like every other
|
|
29
|
+
ConfigError (review K-4).
|
|
30
|
+
- **[LOW] 50MB JSONL cap guard untested** — oversized files now keep their
|
|
31
|
+
row, stamp size/mtime, and stop re-parsing (review C-4) — covered by an
|
|
32
|
+
ftruncate-based sparse-file test.
|
|
33
|
+
- **[LOW] `freshImport()` cache-bust comment** — resolved by documenting
|
|
34
|
+
reality instead: the ESM cache is NOT busted and resets must use exported
|
|
35
|
+
hooks (review C-6). No query-string hack added.
|
|
36
|
+
- **[LOW] Missing `--current-only` test** — added (see A-10 above).
|
|
37
|
+
- **[LOW] `install.sh` ls | grep** — replaced with `find -maxdepth 1`
|
|
38
|
+
(review K-10).
|
|
39
|
+
- **[LOW] CR3: `resolvedDefaultsCommand` origin validation** — defaults.command
|
|
40
|
+
(incl. CCS_CMD/CCR_CMD origin) is now SHELL_METACHARS-validated in
|
|
41
|
+
loadConfig and persisted to the v2 `meta` table for unmapped-session
|
|
42
|
+
fallback (reviews A-6 + A-8).
|
|
43
|
+
- **[LOW] CR3: REVIEW.md stale command-trust wording** — updated (review K-5).
|
|
44
|
+
- **[LOW] CR3 / README architecture diagram omissions** — diagram now lists
|
|
45
|
+
all bin/ modules incl. ccs-preview-session / ccs-scan-sessions / ccs-utils
|
|
46
|
+
(reviews K-6 / A-11).
|
|
47
|
+
|
|
48
|
+
Follow-up batch (same branch, second pass on user request):
|
|
49
|
+
|
|
50
|
+
- **[MEDIUM] `scanOneRepo` COUNT(*) outside transaction** — session
|
|
51
|
+
aggregation now runs inside the write transaction (better-sqlite3 is
|
|
52
|
+
synchronous, so read+write are atomic).
|
|
53
|
+
- **[LOW] PRAGMA tuning** — write-path `openDb` sets `cache_size = -8000`,
|
|
54
|
+
`temp_store = MEMORY`, `mmap_size = 64MB`.
|
|
55
|
+
- **[LOW] Concurrent scan race / advisory lock** — `<cacheDir>/scan.lock`
|
|
56
|
+
(O_EXCL create, 5-min stale takeover); a second scan skips with a stderr
|
|
57
|
+
note and `ScanResult.lockSkipped`. Tested incl. stale-lock takeover.
|
|
58
|
+
- **[HIGH] `install.sh` node_modules** — short-term option (a) shipped:
|
|
59
|
+
install.sh symlinks the repo checkout's `node_modules` into the install
|
|
60
|
+
dir (warns when absent). Option (b) npm-package distribution remains open
|
|
61
|
+
below.
|
|
62
|
+
- **Shell-surface tests** — new `test/ccs-shell.test.ts` drives `bin/ccs`
|
|
63
|
+
(non-interactive paths) and `ccs-delete.sh` (incl. the C-2 rm-failure
|
|
64
|
+
branch and the L-2 subagent-dir removal) via node:test + spawnSync.
|
|
65
|
+
No bats dependency needed.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## MEDIUM
|
|
70
|
+
|
|
71
|
+
### [MEDIUM] `ccs-list.ts --current-only` filters in JS after full fetch
|
|
72
|
+
**File**: `bin/ccs-list.ts` (current-only filter path)
|
|
73
|
+
**Issue**: `--current-only` pulls all sessions from SQLite then filters by `cwd === process.cwd()` in JS. At 10x scale (thousands of sessions) the unnecessary deserialization dominates.
|
|
74
|
+
**Fix sketch**: Push the filter into SQL as a `WHERE cwd = ?` predicate on the prepared statement. Parameterize the current cwd.
|
|
75
|
+
**Why deferred**: Current dataset sizes (hundreds of sessions) make the JS filter imperceptible. Revisit once users report lag at scale.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## LOW
|
|
80
|
+
|
|
81
|
+
### [LOW] Error message style inconsistency
|
|
82
|
+
**File**: `bin/ccs-config.ts` (multiple `throw new ConfigError(...)` sites)
|
|
83
|
+
**Issue**: Older errors prefix messages with `${reposYmlPath}:` (file-path context) while the new Phase 6 `command` / `custom` validation errors don't. Inconsistent UX.
|
|
84
|
+
**Fix sketch**: Pick one style (recommend always prefixing with source location) and unify; update tests.
|
|
85
|
+
**Why deferred**: Cosmetic; messages are still intelligible. Batch with the v0.2.1 error-handling pass.
|
|
86
|
+
|
|
87
|
+
### [LOW] `ccs-db.test.ts` uses `setTimeout(1100)` for TTL check
|
|
88
|
+
**File**: `test/ccs-db.test.ts`
|
|
89
|
+
**Issue**: Real `setTimeout` makes the test take 1.1 s minimum and introduces flake risk on loaded CI.
|
|
90
|
+
**Fix sketch**: Inject a `nowFn` parameter into the TTL-check code path (default `Date.now`); tests pass a deterministic clock.
|
|
91
|
+
**Why deferred**: Test quality improvement, not a shipping issue. Cleaner to do alongside the shared `ccs-utils.ts` extraction.
|
|
92
|
+
|
|
93
|
+
### [LOW] `ccs-scan.test.ts` coverage gaps
|
|
94
|
+
**File**: `test/ccs-scan.test.ts`
|
|
95
|
+
**Issue**: Missing cases: (a) git-uninitialized repo, (b) missing `~/.claude/projects/` dir, (c) sessions with `cwd` NOT matching any configured repo, (d) 50 MB JSONL cap guard trigger.
|
|
96
|
+
**Fix sketch**: Add four focused test cases; (d) can use a stub file with faked stat size to avoid generating 50 MB on disk.
|
|
97
|
+
**Why deferred**: Existing tests cover the happy path; these are edge cases. Prioritized for v0.2.1 test-coverage uplift.
|
|
98
|
+
|
|
99
|
+
### [LOW] `freshImport()` doesn't actually bust ESM cache
|
|
100
|
+
**File**: `test/ccs-config.test.ts`
|
|
101
|
+
**Issue**: `import(path)` without a cache-bust query is memoized by the ESM loader; successive `freshImport()` calls return the same module, which defeats the `migrationWarned` isolation intent.
|
|
102
|
+
**Fix sketch**: Append `?v=${Date.now()}` to the import specifier.
|
|
103
|
+
**Why deferred**: Current tests pass (module state happens to not leak between the specific test orderings used). Fix with M2 hook export.
|
|
104
|
+
|
|
105
|
+
### [LOW] Missing `--current-only` test in ccs-list.test.ts
|
|
106
|
+
**File**: `test/ccs-list.test.ts`
|
|
107
|
+
**Issue**: No test exercises the `ccs .` (current-dir filter) path.
|
|
108
|
+
**Fix sketch**: Add a test that seeds the DB with mixed `cwd` values and asserts only the current-cwd rows appear.
|
|
109
|
+
**Why deferred**: Coverage, not correctness. Bundle with the v0.2.1 test pass.
|
|
110
|
+
|
|
111
|
+
### [LOW] README architecture diagram filename ambiguity
|
|
112
|
+
**File**: `README.md` (architecture section)
|
|
113
|
+
**Issue**: Diagram labels read `ccs-preview.ts` in some places and `ccs-preview-session.ts` elsewhere; they are two distinct files and the diagram should disambiguate.
|
|
114
|
+
**Fix sketch**: Update the diagram source and regenerate the image/ASCII art to show both nodes with labeled responsibilities.
|
|
115
|
+
**Why deferred**: Docs-only; no functional impact.
|
|
116
|
+
|
|
117
|
+
### [LOW] `ccs-preview-session.ts` banner comment
|
|
118
|
+
**File**: `bin/ccs-preview-session.ts` (top of file)
|
|
119
|
+
**Issue**: Future readers may mistake the `console.log` calls for debug output and try to remove them; they are actually the fzf preview stdout contract.
|
|
120
|
+
**Fix sketch**: Add a banner comment at the top stating "NOTE: console.log output IS the fzf preview payload — not debug logging."
|
|
121
|
+
**Why deferred**: Pure docs.
|
|
122
|
+
|
|
123
|
+
### [LOW] `bin/ccs --flag=value` form not supported
|
|
124
|
+
**File**: `bin/ccs` (arg parser loop)
|
|
125
|
+
**Issue**: The loop recognizes `--flag value` but not `--flag=value` (POSIX-long-opt style).
|
|
126
|
+
**Fix sketch**: Either extend the parser to split on `=`, or document the limitation in README and man-page.
|
|
127
|
+
**Why deferred**: Mostly stylistic; current users have not complained.
|
|
128
|
+
|
|
129
|
+
### [LOW] `install.sh` uses `ls | grep`
|
|
130
|
+
**File**: `install.sh`
|
|
131
|
+
**Issue**: Shellcheck SC2010 — `ls | grep` is fragile with unusual filenames.
|
|
132
|
+
**Fix sketch**: Replace with a `find -printf` pattern or a glob-based `for f in pattern; do [ -e "$f" ]; done` loop.
|
|
133
|
+
**Why deferred**: Current callers use well-formed filenames; cosmetic lint.
|
|
134
|
+
|
|
135
|
+
### [LOW] `du -h` portability in `ccs-delete.sh`
|
|
136
|
+
**File**: `bin/ccs-delete.sh`
|
|
137
|
+
**Issue**: `du -h` output format differs between GNU coreutils and BSD; the parsing assumes GNU.
|
|
138
|
+
**Fix sketch**: Add a note in the script header or switch to `wc -c` + manual formatting.
|
|
139
|
+
**Why deferred**: macOS (BSD) is the primary dev platform and tests do not assert exact output.
|
|
140
|
+
|
|
141
|
+
---
|
|
142
|
+
|
|
143
|
+
## INFO
|
|
144
|
+
|
|
145
|
+
### [INFO] SQLite `created_at` omission in UPDATE clarified
|
|
146
|
+
**File**: `bin/ccs-db.ts` (session UPSERT)
|
|
147
|
+
**Issue**: `created_at` is deliberately excluded from the `ON CONFLICT ... DO UPDATE SET ...` clause (only set on INSERT). Future readers may mistake this for an oversight.
|
|
148
|
+
**Fix sketch**: Add a `-- created_at intentionally omitted: preserve original creation time` inline comment.
|
|
149
|
+
**Why deferred**: No functional change; documentation only.
|
|
150
|
+
|
|
151
|
+
### [INFO] `scanSessions` autocommits per row
|
|
152
|
+
**File**: `bin/ccs-scan.ts` (scanSessions loop)
|
|
153
|
+
**Issue**: Each row INSERT/UPDATE runs as its own implicit transaction. For a full rescan this is measurable overhead compared to batching.
|
|
154
|
+
**Fix sketch**: Wrap the iteration in `db.transaction(() => { for (...) stmt.run(...); })`.
|
|
155
|
+
**Why deferred**: Performance optimization. Requires benchmark-backed tuning; not essential for v0.2.0.
|
|
156
|
+
|
|
157
|
+
---
|
|
158
|
+
|
|
159
|
+
## CR3 additions (2026-04-14)
|
|
160
|
+
|
|
161
|
+
### [LOW] `resolvedDefaultsCommand` stored in config object without origin-site validation
|
|
162
|
+
**File**: `bin/ccs-config.ts:459-460`
|
|
163
|
+
**Issue**: env-sourced `CCS_CMD` is stored in `config.defaults.command` before reaching `resolveRepoEntry`. Current execution path always re-validates via `rejectShellMetachars(command)` at line 337, so no bypass exists, but a caller using `config.defaults.command` directly would get an unvalidated string.
|
|
164
|
+
**Fix sketch**: Validate `envCommand()` return at origin; add regression test.
|
|
165
|
+
**Why deferred**: No exploitable path in current code; defensive-only.
|
|
166
|
+
|
|
167
|
+
### [LOW] Prepared statements in scanOneRepo compiled per repo
|
|
168
|
+
**File**: `bin/ccs-scan.ts:377-435` (repo_stats upsert, handoff/pending deletes, hInsert, pInsert)
|
|
169
|
+
**Issue**: 5 `db.prepare()` calls inside per-repo transaction callback. 50 repos = 250 compile events per scan.
|
|
170
|
+
**Fix sketch**: Hoist to `scanAllRepos` scope and pass as parameter.
|
|
171
|
+
**Why deferred**: Minor perf win, safe to defer until observed as bottleneck.
|
|
172
|
+
|
|
173
|
+
### [LOW] Repo name length cap not enforced
|
|
174
|
+
**File**: `bin/ccs-config.ts`
|
|
175
|
+
**Issue**: No upper bound on repo `name` length. JSON.stringify in deleteReposNotIn would faithfully serialize a 10MB name.
|
|
176
|
+
**Fix sketch**: Add `name.length > 200 → ConfigError` in validation.
|
|
177
|
+
**Why deferred**: repos.yml is operator-authored; attack surface is self-inflicted.
|
|
178
|
+
|
|
179
|
+
### [LOW] REVIEW.md stale re: command: field trust
|
|
180
|
+
**File**: `REVIEW.md`
|
|
181
|
+
**Issue**: Bullet says "trust documented in README" — predates Phase 6 hard rejection.
|
|
182
|
+
**Fix sketch**: Update to "actively rejects shell metacharacters at config load".
|
|
183
|
+
**Why deferred**: Doc sync, not a behavioral defect.
|
|
184
|
+
|
|
185
|
+
### [LOW] README architecture diagram omits ccs-preview-session.ts
|
|
186
|
+
**File**: `README.md:259`
|
|
187
|
+
**Issue**: Diagram lists only `ccs-preview.ts`; the session preview binary is a separate file.
|
|
188
|
+
**Fix sketch**: Add a line for `ccs-preview-session.ts → preview pane (session conversation head)`.
|
|
189
|
+
**Why deferred**: Cosmetic; backlog item M6 in v0.2.1.
|
|
190
|
+
|
|
191
|
+
### [LOW] Test file runtime-assembly comment not repeated on all token fakes
|
|
192
|
+
**File**: `test/ccs-secrets.test.ts`
|
|
193
|
+
**Issue**: Comment explaining runtime assembly appears only on Stripe test; future maintainers adding e.g. a GitHub PAT test might hardcode.
|
|
194
|
+
**Fix sketch**: Move comment to describe-block docstring or repeat on each assembly site.
|
|
195
|
+
**Why deferred**: Preventive only.
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
199
|
+
## User-requested features (2026-04-14, to address in v0.2.1)
|
|
200
|
+
|
|
201
|
+
### [LOW] npm registry publish (`npm install -g claude-code-station`)
|
|
202
|
+
**File**: — (release process, not code)
|
|
203
|
+
**Issue**: The account-free distribution path is DONE — `npm install -g github:indigo-gr/claude-code-station` works (bin/ccs resolves $0 through npm's global-bin symlink, tsx ships as a dependency and is preferred from the package-local node_modules, `files` field set; verified end-to-end via `npm pack` + sandbox-prefix global install). What remains is only publishing to the npm REGISTRY for discoverability/version pinning.
|
|
204
|
+
**Fix sketch**: npm account → claim package name → `npm publish` (repo side is ready); optional release automation.
|
|
205
|
+
**Why deferred**: Pure publish decision. Tracked in `~/.claude/pendings/ccs-npm-registry-publish.md` (owner decision).
|
|
206
|
+
|
|
207
|
+
### [HIGH] `install.sh --auto-install` — auto-install missing dependencies
|
|
208
|
+
**File**: `install.sh`
|
|
209
|
+
**Issue**: Currently prints missing-dependency hints and asks "Continue anyway? y/N". First-time OSS users need to copy-paste each install command manually.
|
|
210
|
+
**Fix sketch**: New `--auto-install` flag. Detect platform (macOS → brew, Debian/Ubuntu → apt, Fedora → dnf, Arch → pacman). For each missing dep:
|
|
211
|
+
- `node`: `brew install node` / `apt install nodejs npm`
|
|
212
|
+
- `fzf`: `brew install fzf` / `apt install fzf`
|
|
213
|
+
- `tsx`: `npm install -g tsx` (once node is available)
|
|
214
|
+
- `claude`: print installer URL; cannot auto-install
|
|
215
|
+
Prompt once with a summary ("Install: fzf, tsx — proceed? [Y/n]") rather than per-tool.
|
|
216
|
+
**Why deferred**: Nice-to-have. The manual path works; auto is for OSS onboarding polish.
|
|
217
|
+
|
|
218
|
+
### ~~[MEDIUM] `ccs init --auto-discover`~~ — DONE (2026-06-12, feat/ccs-init-auto-discover)
|
|
219
|
+
Shipped as specced: `bin/ccs-init.ts` (discover / append / scaffold modes) wired
|
|
220
|
+
via the new `ccs init` subcommand. $HOME walk with depth 5 default (`--depth N`),
|
|
221
|
+
hidden+noise dir excludes, stop-at-repo-root (with a $HOME-as-dotfiles-repo
|
|
222
|
+
exception), symlink-safe; candidates dedup against repos.yml, name suggestions
|
|
223
|
+
uniquified; append is comment/format-preserving (yaml parseDocument) with a
|
|
224
|
+
`.bak` backup and full loadConfig() validation rollback. Covered by
|
|
225
|
+
`test/ccs-init.test.ts` + shell tests.
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-code-station",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "Claude Code Station — fzf-powered launcher and session picker for Claude Code with multi-repo support and SQLite-backed state cache",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"ccs": "bin/ccs"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"test": "node --import tsx --test test/*.test.ts",
|
|
11
|
+
"lint": "tsc --noEmit"
|
|
12
|
+
},
|
|
13
|
+
"keywords": [
|
|
14
|
+
"claude",
|
|
15
|
+
"claude-code",
|
|
16
|
+
"fzf",
|
|
17
|
+
"cli",
|
|
18
|
+
"session",
|
|
19
|
+
"launcher"
|
|
20
|
+
],
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "git+https://github.com/indigo-gr/claude-code-station.git"
|
|
25
|
+
},
|
|
26
|
+
"files": [
|
|
27
|
+
"bin",
|
|
28
|
+
"docs",
|
|
29
|
+
"CHANGELOG.md"
|
|
30
|
+
],
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=20"
|
|
33
|
+
},
|
|
34
|
+
"dependencies": {
|
|
35
|
+
"better-sqlite3": "^11.3.0",
|
|
36
|
+
"tsx": "^4.19.0",
|
|
37
|
+
"yaml": "^2.6.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/better-sqlite3": "^7.6.11",
|
|
41
|
+
"@types/node": "^22.0.0",
|
|
42
|
+
"typescript": "^5.6.0"
|
|
43
|
+
}
|
|
44
|
+
}
|