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
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project are documented in this file.
|
|
4
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
5
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
6
|
+
|
|
7
|
+
## [Unreleased]
|
|
8
|
+
|
|
9
|
+
## [0.2.1] — 2026-06-21
|
|
10
|
+
|
|
11
|
+
### Added (v0.2.1 features)
|
|
12
|
+
- **`ccs init --auto-discover [--depth N]`** — scans `$HOME` (depth 5 default) for git checkout roots, excludes noise dirs (`node_modules`, `Library`, hidden dirs, ...), stops at repo roots, skips symlinks, subtracts repos already in `repos.yml`, and presents the rest in an fzf multi-select. Selections are appended to `repos.yml` comment/format-preservingly (yaml `parseDocument`) with a `.bak` backup and a full-validation rollback, then indexed immediately. Plain `ccs init` scaffolds the config template.
|
|
13
|
+
- **Account-free npm install** — `npm install -g github:indigo-gr/claude-code-station` now works end-to-end: `bin/ccs` resolves `$0` through npm's global-bin symlink to find its sibling modules, `tsx` ships as a runtime dependency and the package-local copy is preferred over PATH (a global tsx is only needed for bare-checkout use), and the `files` field keeps the tarball lean. Verified via `npm pack` + sandbox-prefix global install.
|
|
14
|
+
|
|
15
|
+
### Fixed (review 2026-06-12 — 4-perspective defensive review cleanup)
|
|
16
|
+
- **C-2 (High)** — `ccs-delete.sh` no longer dies silently under `set -e` when `rm` fails: the failure is reported, the "Press Enter" prompt still runs, and the script exits 1. `du` failure (file vanished mid-flow) falls back to `?` instead of killing the script.
|
|
17
|
+
- **C-1 (High)** — `git log` parsing uses NUL (`%x00`) separators instead of tabs, so a commit subject containing a literal TAB can no longer shift the split and persist subject fragments into `last_commit_at`. Regression test commits a tab-in-subject fixture.
|
|
18
|
+
- **C-4** — a session JSONL growing past the 50MB cap now keeps its (stale but resumable) row, stamps the new size/mtime, and stops being re-parsed on every scan; previously the row was permanently stale AND re-attempted forever.
|
|
19
|
+
- **C-5** — `ccs-list` cold-start friendly hint was dead code: the `isMissing` patterns never matched `openDb()`'s actual "state.db not found" message. Pattern added; the regression test now asserts the friendly branch specifically (the old test passed via the raw fatal path).
|
|
20
|
+
- **C-3** — Twilio Account SID masking accepts uppercase hex (`AC[a-fA-F0-9]{32}`).
|
|
21
|
+
- **DA-3** — `buildRepoResolver` registers each repo root under its realpath too, so repos registered via symlinked paths (or macOS `/var` aliasing) claim their sessions.
|
|
22
|
+
- **A-8** — sessions not mapped to any repo now launch with the documented fallback chain (`defaults.command` > `CCS_CMD` > `"claude"`) via the new `meta.defaults_command` value, instead of a hardcoded `"claude"`.
|
|
23
|
+
- **A-10** — `--current-only` filters in SQL (`cwd = ? OR (cwd >= ? AND cwd < ?)`) so `idx_sessions_cwd` applies; sibling dirs sharing the cwd as a string prefix are excluded by construction.
|
|
24
|
+
- **A-6** — the resolved `defaults.command` (including `CCS_CMD`/`CCR_CMD` origin) is SHELL_METACHARS-validated in `loadConfig()` even when no repo inherits it.
|
|
25
|
+
- **K-4** — `command` / `custom` ConfigError messages carry the `repos.yml: repos[i].field` prefix like every other validation error (the Phase 6 "at index" phrasing is retired).
|
|
26
|
+
- **K-9** — `npm test` runs via `node --import tsx --test` (works on the documented Node >= 20, not just Node >= 23.6 type-stripping); `"type": "module"` added to silence `MODULE_TYPELESS_PACKAGE_JSON`.
|
|
27
|
+
- **K-10** — `install.sh` lists installed files with `find -maxdepth 1` instead of `ls | grep` (SC2010).
|
|
28
|
+
|
|
29
|
+
### Changed (review 2026-06-12 — structure)
|
|
30
|
+
- **A-4** — session indexing extracted from `ccs-scan.ts` (999 lines) into `bin/ccs-scan-sessions.ts`; `parseSessionJsonl` / `buildRepoResolver` / `scanSessions` are exported and unit-testable.
|
|
31
|
+
- **A-1 / A-2 / A-3 / K-8** — new `bin/ccs-utils.ts` is the single source for `truncate`, `timeBucket` / `formatRelativeTime` / `formatDateTime`, `extractText` (tool blocks behind an explicit option), `UUID_RE`, and `MAX_JSONL_SIZE`. The list/preview copies (which had already drifted in whitespace handling and 日本語/English suffixes) are gone; relative time renders uniformly as English ("5m ago"). List session badges show `-` for unparseable timestamps instead of echoing the raw DB string.
|
|
32
|
+
- **Schema v2** — new `meta(key, value)` table (idempotent migration; `getMeta` degrades to null on pre-v2 caches read by readonly consumers).
|
|
33
|
+
|
|
34
|
+
### Tests (review 2026-06-12)
|
|
35
|
+
- New `test/ccs-utils.test.ts` pins the unified helper semantics (thresholds, control-char stripping, tool-block extraction, UUID gate).
|
|
36
|
+
- New regression tests: tab-in-commit-subject (C-1), oversized-JSONL keep/touch/skip via sparse `ftruncate` (C-4), symlinked-repo session mapping (DA-3), cold-start friendly hint (C-5), unmapped-session command fallback incl. pre-v2 cache degradation (A-8), `--current-only` exact/subdir/sibling-prefix (A-10), `defaults.command`/`CCS_CMD` origin validation (A-6), uppercase Twilio SID (C-3), meta round-trip + v1→v2 upgrade.
|
|
37
|
+
- `freshImport()` comment in the config tests now states the truth: the ESM cache is NOT busted; module state resets go through exported hooks (C-6).
|
|
38
|
+
- 110 tests, all green.
|
|
39
|
+
|
|
40
|
+
### Docs (review 2026-06-12)
|
|
41
|
+
- Regression checklist #4 (secret patterns: 26 unified) and #18 (risky-command warning removed in Phase 6 S2) corrected; stale `isUnderHome` backlog entry closed (fixed by audit M-4); README architecture diagram lists all 14 bin/ modules; `REVIEW.md` reflects the metachar hard-reject reality; `sqlite-schema.md` documents schema v2 + the sessions-first scan order; `repos-yml-schema.md` documents the NEW-3 name policy, unified error formats, and the unmapped-session fallback.
|
|
42
|
+
|
|
43
|
+
### Fixed (review 2026-06-12 — follow-up batch)
|
|
44
|
+
- **COUNT(*) in-transaction** — `scanOneRepo` reads its session aggregates inside the write transaction, so a concurrent process committing session changes can no longer slip between the read and the `repo_stats` upsert.
|
|
45
|
+
- **Advisory scan lock** — `<cacheDir>/scan.lock` (O_EXCL create, 5-minute stale takeover): a second concurrent scan skips with a stderr note instead of racing the sessions cleanup (original CR2 H3). `ScanResult.lockSkipped` exposes the skip.
|
|
46
|
+
- **PRAGMA tuning** — write-path connections set `cache_size = -8000`, `temp_store = MEMORY`, `mmap_size = 64MB` (readonly preview path untouched).
|
|
47
|
+
- **install.sh runtime deps** — `node_modules` is symlinked from the repo checkout into the install dir so installed scripts resolve `better-sqlite3`/`yaml` (closes the backlog HIGH short-term; npm-package distribution remains the long-term plan).
|
|
48
|
+
- **Prompt visibility on piped stdin** — `ccs-delete.sh` and `install.sh` print prompts explicitly to stderr (`prompt_read` helper) instead of relying on `read -p`, which bash renders only when stdin is a TTY. TTY behavior is unchanged (`read -p` also writes to stderr); scripted/piped use no longer loses the prompts, and the shell tests now assert them.
|
|
49
|
+
|
|
50
|
+
### Tests (review 2026-06-12 — follow-up batch)
|
|
51
|
+
- New `test/ccs-shell.test.ts`: the previously-untested bash surfaces — `bin/ccs --version/--help` and the full `ccs-delete.sh` matrix (non-UUID rejection, not-found, confirmed delete incl. subagent-dir removal, declined delete, and the C-2 rm-failure branch) — driven by node:test + spawnSync with sandboxed HOME/XDG. No bats dependency.
|
|
52
|
+
- Advisory-lock test: fresh lock → skip; stale lock → takeover + release.
|
|
53
|
+
- 118 tests, all green.
|
|
54
|
+
|
|
55
|
+
### Security (audit 2026-06-12 hardening)
|
|
56
|
+
- **H-1 / NEW-1 — session intake sanitization**: a session `.jsonl` `cwd` no longer reaches the Ctrl-Y clipboard command unchecked. Session `cwd`/`topic`/`summary`/`branch` and workspace `first_line` are now gated at scan time via a shared `bin/ccs-sanitize.ts`: shell metacharacters reject the `cwd` to an `"unknown"` sentinel (blocks deferred command injection on paste), and control characters incl. ESC are stripped (blocks ANSI/terminal-escape spoofing in the `--ansi` fzf list and preview).
|
|
57
|
+
- Ctrl-Y clipboard line now `%q`-quotes the `cwd`/`uuid` (defense-in-depth for H-1); list/preview renderers strip control chars on the display side too (defense-in-depth for NEW-1).
|
|
58
|
+
- **NEW-3** — `repos.yml` `name` now rejects the full `SHELL_METACHARS` set (was only `\t \n \\`), matching the `path`/`cwd`/`command` policy.
|
|
59
|
+
- **NEW-2** — `bin/ccs` `new:` row validation rejects control chars (incl. ESC) in addition to shell metacharacters.
|
|
60
|
+
- **M-1** — secret patterns expanded 19 → 26: GitLab PAT, GitHub fine-grained PAT, Google OAuth secret, SendGrid, npm token, Slack webhook URL, generic `KEY=value`, and any-scheme `user:pass@` URL credentials.
|
|
61
|
+
- **M-2** — session `cwd` is now run through `maskSecrets`; preview-pane header fields (`cwd`/`branch`/`version`) are masked too.
|
|
62
|
+
- **M-3** — `ccs-delete.sh` cache cleanup uses a bound-parameter delete (`bin/ccs-delete-session.ts`) instead of string-concatenated SQL.
|
|
63
|
+
- **M-4** — `isUnderHome` resolves symlinks (`realpathSync`), closing the `~/escape -> /` HOME-escape.
|
|
64
|
+
- **L-1** — `bin/ccs` HOME check requires `$HOME` itself or a `$HOME/` child (rejects sibling dirs like `/Users/xEVIL`).
|
|
65
|
+
- **L-2** — `rm -rf` of the subagent dir is guarded by a final-form `<projects>/<dir>/<uuid>` check.
|
|
66
|
+
|
|
67
|
+
### Fixed (audit 2026-06-12 logic)
|
|
68
|
+
- **C-1 (critical)** — `scan()` now runs the sessions pass before the repo pass, so `repo_stats.session_count_total` / `session_last_at` are correct after a single `--refresh` (previously lagged one scan; a fresh DB showed every repo as `💤 未使用`).
|
|
69
|
+
- **logic H-1** — `--no-sessions` scans preserve the previous session aggregates instead of rewinding them to 0.
|
|
70
|
+
- **H-2 / M-1 (time)** — list and preview parse DB timestamps through a shared `bin/ccs-time.ts` that treats naive SQLite `datetime('now')` values as UTC (no more JST skew); `sessions.indexed_at` is written as ISO 8601 for a single in-DB time format.
|
|
71
|
+
- **H-3** — preview "Recent" sessions use the same `WHERE repo_name = ?` population as the "Total" count (no more Total/Recent disagreement).
|
|
72
|
+
- **H-4** — `message_count` counts only `user`/`assistant` rows, matching the preview.
|
|
73
|
+
- **M-4 (logic)** — sessions started in a repo subdirectory map to the repo via longest-prefix matching.
|
|
74
|
+
- **M-3 (logic)** — sessions left unmapped (repo added after the session) are re-resolved on later scans.
|
|
75
|
+
- **M-2 (logic)** — `scan: false` repos show a `[scan off]` badge so their frozen stats aren't read as current.
|
|
76
|
+
- **M-5** — `extractUserText` joins all text blocks (matches the preview), so topics aren't truncated to the first block.
|
|
77
|
+
- **M-6** — `started_at` / `last_activity_at` use min/max of timestamps, not first/last line order.
|
|
78
|
+
- **L-1 (logic)** — `parseInt` results are `Number.isFinite`-guarded before DB writes.
|
|
79
|
+
- **L-3 / L-4 (perf/robustness)** — workspace dir previews `stat` in parallel and read only the first 4 KB per file; chmod failures on the cache (and WAL side files) now warn instead of failing silently.
|
|
80
|
+
- **NEW-4** — `busy_timeout = 3000` set on the DB connection so concurrent `--force` scans don't fail with `SQLITE_BUSY`.
|
|
81
|
+
- Stale `ccr-preview.ts` header comment in `ccs-preview-session.ts` corrected.
|
|
82
|
+
|
|
83
|
+
## [0.2.0] — 2026-04-26
|
|
84
|
+
|
|
85
|
+
### Changed
|
|
86
|
+
- **Renamed** from `claude-code-recall` (ccr) to `claude-code-station` (ccs). Binary, repo, and config paths all follow the new name.
|
|
87
|
+
- Environment variable `CCR_CMD` → `CCS_CMD`. `CCR_CMD` is still honored (with a stderr deprecation warning) when `CCS_CMD` is unset.
|
|
88
|
+
- Minimum Node.js version raised to **20**.
|
|
89
|
+
|
|
90
|
+
### Added
|
|
91
|
+
- **Mixed-mode launcher**: a single fzf list now shows both `NEW` repo rows (launch fresh) and `RESUME` session rows (resume past).
|
|
92
|
+
- **Repository state badges** in the preview pane: git status, handoff/ file count, pendings/ file count, and integration links.
|
|
93
|
+
- **SQLite-backed state cache** at `~/.cache/ccs/state.db` (or `$XDG_CACHE_HOME/ccs/state.db`). WAL mode, foreign keys on, prepared statements only, `0600` permissions.
|
|
94
|
+
- **Configuration file** `~/.config/ccs/repos.yml` with schema validation. Auto-generated template on first run. Full schema: `docs/design/repos-yml-schema.md`.
|
|
95
|
+
- **Per-repo custom integration fields** (`custom:` map): built-in known keys for Plane, Attio, Notion, Linear, Slack, GitHub, Figma. Unknown keys rendered verbatim under "Other Integrations".
|
|
96
|
+
- **New flags**: `--new`, `--resume`, `--refresh`, `--no-scan`, plus pass-through for unknown flags.
|
|
97
|
+
- **New key binding**: `Ctrl-R` to force DB refresh from within fzf.
|
|
98
|
+
- **Dependency**: `better-sqlite3 ^11.3.0`, `yaml ^2.6.0`.
|
|
99
|
+
- `install.sh --with-deps` opt-in flag to run `npm install` as part of install.
|
|
100
|
+
|
|
101
|
+
### Security
|
|
102
|
+
- All SQLite access uses prepared statements; FK enforced.
|
|
103
|
+
- Config/cache directories created with mode `0700`; DB with `0600`.
|
|
104
|
+
- `custom:` values documented as trusted user config; README warns against storing API keys.
|
|
105
|
+
|
|
106
|
+
### Security (Phase 6 hardening)
|
|
107
|
+
- Reject shell metacharacters in `command:` config field (S1/S2 — prevents injection via malicious repos.yml)
|
|
108
|
+
- Cap `custom` field JSON size at 64KB to prevent local DoS
|
|
109
|
+
- Apply `maskSecrets` to `sessions.topic` and `sessions.summary` columns (was only applied to handoff/pending first_line)
|
|
110
|
+
- Expand secret patterns from 13 → 19: AWS STS (ASIA), Stripe live/test, Twilio, JWT, database URLs
|
|
111
|
+
|
|
112
|
+
### Fixed (Phase 6 quality)
|
|
113
|
+
- `ccs-list.ts` now opens SQLite in readonly mode with migrate skipped (regression of prior M4 fix)
|
|
114
|
+
- `ccs-scan.ts` CLI bootstrap correctly exits non-zero on synchronous throws
|
|
115
|
+
- `ccs-preview-session.ts` converted from sync fs to async fs/promises; CLI guards `undefined` session ID
|
|
116
|
+
- `deleteReposNotIn` uses `json_each` to avoid SQLite 999-variable limit (supports 500+ repos)
|
|
117
|
+
- Replaced unquoted `cut -f` result with `IFS`-based read for space-safe row parsing
|
|
118
|
+
- Unified `node:` prefix on all fs/path imports
|
|
119
|
+
- Fixed stale `ccr-delete.sh` header comment to `ccs-delete.sh`
|
|
120
|
+
- Corrected `sqlite-schema.md` dependency note (removed misleading `npm install -g`)
|
|
121
|
+
- `ccs-list.ts` uses `homedir()` instead of `process.env.HOME || ""` for tilde substitution
|
|
122
|
+
|
|
123
|
+
### Added (Phase 8 — UX polish)
|
|
124
|
+
- `ccs-list.ts` emits a dim `── Past Sessions ──` divider between NEW repos and RESUME sessions when both are present. Selecting the divider is a no-op.
|
|
125
|
+
- RESUME sessions not mapped to a registered repo now display `❓` as a visible "either one-off run or repo not registered yet" cue. Mapped sessions show the repo's icon (default `📁`).
|
|
126
|
+
- `Ctrl-Y` / `Ctrl-I` no longer abort fzf after copying — they flash a `📋 Copied…` header and keep the picker open so you can launch or copy again without re-running `ccs`.
|
|
127
|
+
- Label column now has a dim `│` separator between the repo/session name and the description for visual clarity even when the description is empty.
|
|
128
|
+
|
|
129
|
+
### Fixed (Phase 8 — CR4 remediations)
|
|
130
|
+
- `bin/ccs`: separator-row Enter no longer prints `❌ Could not parse selection` — the empty-`ROW_CMD` guard is now scoped to actionable rows only.
|
|
131
|
+
- `bin/ccs`: narrow-terminal safety — header line 2 shortened from 81 → 63 chars so it no longer wraps at 80-column terminals.
|
|
132
|
+
- `bin/ccs`: `read -r -n1 _` for the new-row Ctrl-D notice accepts any key (was waiting for Enter despite the "press any key" message).
|
|
133
|
+
- `bin/ccs-list.ts`: removed an unnecessary non-null assertion in `sessionToRow` by testing `s.repo_display` directly for TS narrowing.
|
|
134
|
+
- `bin/ccs-list.ts`: section-separator emit guard uses the explicit `wantRepos` flag instead of the indirect `lines.length > 0` so adding future preamble rows can't spuriously trigger it.
|
|
135
|
+
- `install.sh`: checks `fzf >= 0.42.0` (required for the `change-header` binding used by the copy-toast).
|
|
136
|
+
- `README.md`: pins `fzf ≥ 0.42.0`, documents the new `^Y`/`^I` copy-keep-open behavior, the `❓` unmapped-session icon, and the section divider.
|
|
137
|
+
|
|
138
|
+
### Fixed (Phase 7 — CR3 remediations)
|
|
139
|
+
- `ccs-list.ts`: cold-start error (state.db not yet created) now prints friendly hint instead of raw stack trace
|
|
140
|
+
- `ccs-scan.ts`: apply `maskSecrets` to `last_commit_subject` and `scan_error` before DB write and stderr log
|
|
141
|
+
- `ccs-db.ts`: `DbHandle.close()` performs `wal_checkpoint(PASSIVE)` to keep WAL file from accumulating
|
|
142
|
+
- `ccs-preview-session.ts`: switched remaining bare `"path"` / `"os"` imports to `node:` prefix
|
|
143
|
+
- `docs/design/repos-yml-schema.md`: added `command` shell-metachar validation row to schema table
|
|
144
|
+
|
|
145
|
+
### Migration
|
|
146
|
+
- Existing JSONL sessions under `~/.claude/projects/*/*.jsonl` are auto-discovered on first scan — nothing to migrate manually.
|
|
147
|
+
- Remove old binaries: `rm ~/.claude/scripts/ccr*`.
|
|
148
|
+
- Rename `CCR_CMD` → `CCS_CMD` in your shell rc.
|
|
149
|
+
|
|
150
|
+
## [0.1.3] — 2026-03-11
|
|
151
|
+
|
|
152
|
+
### Fixed
|
|
153
|
+
- Use JSONL filename UUID instead of internal `sessionId` for resume/delete. Sidechain sessions whose internal id differed from filename caused "Session file not found" errors on preview and delete.
|
|
154
|
+
- Skip `[Request interrupted by user]` messages when generating session summary.
|
|
155
|
+
|
|
156
|
+
## [0.1.2] — 2026-03-11
|
|
157
|
+
|
|
158
|
+
### Added
|
|
159
|
+
- Adaptive 2-line fzf header for narrow terminals (`<80` cols → short labels, `≥80` cols → full labels).
|
|
160
|
+
|
|
161
|
+
## [0.1.1] — 2026-03-11
|
|
162
|
+
|
|
163
|
+
### Fixed
|
|
164
|
+
- Replaced `exec` with direct invocation so `CCR_CMD` can be a shell function (e.g., `opr`), not only an external command.
|
|
165
|
+
|
|
166
|
+
## [0.1.0] — 2026-03-11
|
|
167
|
+
|
|
168
|
+
### Added
|
|
169
|
+
- Initial release as `claude-code-recall` (ccr).
|
|
170
|
+
- fzf-powered session picker across all `~/.claude/projects/*/*.jsonl`.
|
|
171
|
+
- Live preview pane with conversation history.
|
|
172
|
+
- `Ctrl-Y` (copy resume command), `Ctrl-I` (copy session ID), `Ctrl-D` (delete session) keybindings.
|
|
173
|
+
- Cross-platform clipboard: `pbcopy` (macOS), `xclip` / `xsel` (X11), `wl-copy` (Wayland).
|
|
174
|
+
- `CCR_CMD` environment variable for custom claude wrapper commands.
|
|
175
|
+
- UUID validation, `$HOME`-rooted path validation, secret masking in preview, 50MB JSONL size cap.
|
|
176
|
+
- MIT License.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Daiske Watanabe
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Claude Code Station — ccs
|
|
2
|
+
|
|
3
|
+
[](LICENSE)
|
|
4
|
+
[](https://nodejs.org/)
|
|
5
|
+
|
|
6
|
+
A fast, fzf-powered launcher and session picker for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Pick a project to launch fresh **or** resume any past session — all from a single fuzzy finder, across every repo you own.
|
|
7
|
+
|
|
8
|
+
複数プロジェクトを横断するClaude Code用のfzf起動&セッション再開ランチャー。新規起動も過去セッション再開も、同じfzfから一発で。
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## What's new in v0.2.0 / v0.2.0 の新機能
|
|
13
|
+
|
|
14
|
+
- **Mixed-mode launcher** — NEW repos **and** RESUME sessions in a single fzf list / 新規リポジトリ起動と過去セッション再開を同じfzfで混在表示
|
|
15
|
+
- **Repository state badges** — git status, handoff/, pendings/, integration links at a glance / リポジトリの状態(git・handoff・pendings・連携先)をバッジで一目把握
|
|
16
|
+
- **SQLite-backed state cache** — `~/.cache/ccs/state.db` keeps multi-repo overview fast / マルチリポ状態キャッシュ(SQLite)で高速起動
|
|
17
|
+
- **Per-repo custom integration fields** — Plane, Attio, Notion, Linear, Slack, GitHub, Figma (and any custom key) / リポジトリごとの連携情報(Plane/Attio/Notion/Linear/Slack/GitHub/Figma ほか任意のカスタムキー)
|
|
18
|
+
- **Renamed**: `claude-code-recall` (ccr) → `claude-code-station` (ccs). See the [migration section](#migration-from-ccr-v01x--ccr-v01x-からの移行) below. / `claude-code-recall` (ccr) から改名。移行は下記セクション参照。
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## Why? / なぜ?
|
|
23
|
+
|
|
24
|
+
`claude --resume` only shows sessions from the current directory and provides no search. And when you have a dozen active projects, you also want to **launch a fresh session** in the right repo without `cd`-ing around.
|
|
25
|
+
|
|
26
|
+
`claude --resume` はカレントディレクトリのセッションしか出さず、検索もできない。プロジェクトが増えると「新規起動したいリポ」も「続きをやりたいセッション」も、どちらを探すのも手間。
|
|
27
|
+
|
|
28
|
+
**ccs** solves both / **ccs** が両方まとめて解決:
|
|
29
|
+
|
|
30
|
+
- Fuzzy search across all projects (repo name, session title, timestamp, content) / 全プロジェクトのファジー検索
|
|
31
|
+
- Mixed list: `NEW` repo rows + `RESUME` session rows side by side / 新規起動行と再開行を同じリストに
|
|
32
|
+
- Live preview — git status, handoff/pendings summary, integration badges, conversation head / ライブプレビュー(git状態・handoff/pendings・連携バッジ・会話冒頭)
|
|
33
|
+
- Enter once, launched in the correct cwd with the correct command / Enter一回で正しいcwd・正しいコマンドで起動
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## Install / インストール
|
|
38
|
+
|
|
39
|
+
### Prerequisites / 前提条件
|
|
40
|
+
|
|
41
|
+
- [Node.js](https://nodejs.org/) **20+**
|
|
42
|
+
- [fzf](https://github.com/junegunn/fzf) **≥ 0.42.0** — fuzzy finder (the `change-header` binding used by the copy-toast requires 0.42+; released Aug 2023)
|
|
43
|
+
- [Claude Code](https://docs.anthropic.com/en/docs/claude-code) CLI (`claude`)
|
|
44
|
+
- [tsx](https://github.com/privatenumber/tsx) — TypeScript runner. **Bundled** as a package dependency; a global `tsx` (`npm install -g tsx`) is only needed for the bare-checkout workflow. / npmインストール時は同梱されるため、グローバルtsxは checkout 直接運用時のみ必要
|
|
45
|
+
|
|
46
|
+
### Quick install (npm, recommended) / クイックインストール(npm直接・推奨)
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
npm install -g github:indigo-gr/claude-code-station
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Installs straight from this public repo — no npm registry account involved. All runtime deps (`better-sqlite3`, `yaml`, `tsx`) come bundled, and `ccs` lands on your PATH via npm's global bin. / 公開リポから直接インストール。実行時依存は全部同梱、`ccs` はnpmのグローバルbinでPATHに乗る。
|
|
53
|
+
|
|
54
|
+
### Copy install (checkout) / コピーインストール
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
git clone https://github.com/indigo-gr/claude-code-station.git
|
|
58
|
+
cd claude-code-station
|
|
59
|
+
npm install
|
|
60
|
+
./install.sh
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`install.sh --with-deps` で `npm install` も install.sh に任せられる。`install.sh` はランタイム依存解決のため checkout の `node_modules` をインストール先に symlink する(checkout は残しておくこと)。
|
|
64
|
+
|
|
65
|
+
### Manual install / 手動インストール
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install
|
|
69
|
+
cp bin/ccs bin/ccs-*.ts bin/ccs-*.sh ~/.claude/scripts/
|
|
70
|
+
chmod +x ~/.claude/scripts/ccs ~/.claude/scripts/ccs-delete.sh
|
|
71
|
+
|
|
72
|
+
# Add to ~/.zshrc or ~/.bashrc
|
|
73
|
+
export PATH="$HOME/.claude/scripts:$PATH"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Quick start / クイックスタート
|
|
79
|
+
|
|
80
|
+
1. Run `ccs init --auto-discover` — scans `$HOME` (depth 5, `--depth N` to change) for git repos and lets you multi-select which to register via fzf (TAB to toggle). / `$HOME` をスキャンして登録するリポをfzfで複数選択
|
|
81
|
+
2. Or edit `~/.config/ccs/repos.yml` by hand (`ccs init` scaffolds the template; see [Configuration](#configuration--設定)) / 手で書くなら `ccs init` でテンプレート生成
|
|
82
|
+
3. Run `ccs` — fzf opens with your repos + past sessions. / `ccs` 実行でfzfが起動
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
$ ccs
|
|
86
|
+
ccs> my-project ┃ ━━━ Repo ━━━
|
|
87
|
+
┃ 📁 ~/projects/my-project
|
|
88
|
+
NEW ClaudeCode ┃ 🌿 main (clean)
|
|
89
|
+
Claude Code設定 ┃ 📨 handoff: 2 files
|
|
90
|
+
> NEW My Project ┃ 📌 pendings: 1
|
|
91
|
+
Main development repository ┃ ━━━ Integrations ━━━
|
|
92
|
+
RESUME ~/projects/my-project 2h ago ┃ Plane: abc12345-…
|
|
93
|
+
Fix the billing webhook… ┃ Slack: #dev-general
|
|
94
|
+
RESUME ~/projects/my-project 1d ago ┃ GitHub: acme-corp/my-project
|
|
95
|
+
Plane work item sync… ┃
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Configuration / 設定
|
|
101
|
+
|
|
102
|
+
Full schema: [`docs/design/repos-yml-schema.md`](docs/design/repos-yml-schema.md)
|
|
103
|
+
|
|
104
|
+
### Minimal / 最小構成
|
|
105
|
+
|
|
106
|
+
```yaml
|
|
107
|
+
version: 1
|
|
108
|
+
repos:
|
|
109
|
+
- name: ClaudeCode
|
|
110
|
+
path: ~/.claude
|
|
111
|
+
- name: my-project
|
|
112
|
+
path: ~/projects/my-project
|
|
113
|
+
description: Main development repository
|
|
114
|
+
tags: [work]
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
### With integrations / 連携情報付き
|
|
118
|
+
|
|
119
|
+
```yaml
|
|
120
|
+
version: 1
|
|
121
|
+
repos:
|
|
122
|
+
- name: My Project
|
|
123
|
+
path: ~/projects/my-project
|
|
124
|
+
description: Main development repository
|
|
125
|
+
tags: [work]
|
|
126
|
+
custom:
|
|
127
|
+
plane_project_id: "abc12345-xxxx"
|
|
128
|
+
plane_url: "https://plane.example.com/my-project"
|
|
129
|
+
slack_channel: "#dev-general"
|
|
130
|
+
github_repo: "acme-corp/my-project"
|
|
131
|
+
notion_db: "abc123"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Known keys: `plane_project_id`, `plane_url`, `attio_workspace`, `notion_db`, `linear_team`, `slack_channel`, `github_repo`, `figma_file`. Unknown keys are rendered verbatim under "Other Integrations". / 未知キーは "Other Integrations" にそのまま表示。
|
|
135
|
+
|
|
136
|
+
### Special command (CMS dev server) / 特殊コマンド
|
|
137
|
+
|
|
138
|
+
```yaml
|
|
139
|
+
version: 1
|
|
140
|
+
defaults:
|
|
141
|
+
command: "claude"
|
|
142
|
+
repos:
|
|
143
|
+
- name: CMS
|
|
144
|
+
path: ~/sites/blog-cms
|
|
145
|
+
command: "npm run develop" # overrides claude entirely
|
|
146
|
+
icon: "🚀"
|
|
147
|
+
scan: false # not a Claude repo — skip state scan
|
|
148
|
+
tags: [dev-server]
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Precedence: `repos[].command` > `defaults.command` > `$CCS_CMD` > `"claude"`.
|
|
152
|
+
|
|
153
|
+
### Preview pane sample / プレビュー表示例
|
|
154
|
+
|
|
155
|
+
```
|
|
156
|
+
━━━ Repo: My Project ━━━
|
|
157
|
+
📁 ~/projects/my-project 🌿 main (2 modified)
|
|
158
|
+
📨 handoff: 2 files 📌 pendings: 1
|
|
159
|
+
|
|
160
|
+
━━━ Integrations ━━━
|
|
161
|
+
Plane: ✅ abc12345-xxxx → https://plane.example.com/my-project
|
|
162
|
+
Slack: ✅ #dev-general
|
|
163
|
+
GitHub: ✅ acme-corp/my-project
|
|
164
|
+
Notion: ✅ abc123
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Usage / 使い方
|
|
170
|
+
|
|
171
|
+
### Flags / フラグ
|
|
172
|
+
|
|
173
|
+
| Flag | Action |
|
|
174
|
+
|---|---|
|
|
175
|
+
| (none) | Mixed list: NEW repos + RESUME sessions / 混在表示 |
|
|
176
|
+
| `.` | Only sessions whose cwd is under current dir / カレント配下のセッションのみ |
|
|
177
|
+
| `--new` | Only NEW repo entries / 新規起動行のみ |
|
|
178
|
+
| `--resume` | Only past sessions / 再開行のみ |
|
|
179
|
+
| `--refresh` | Force DB rebuild before showing list / 走査を強制してから表示 |
|
|
180
|
+
| `--no-scan` | Skip implicit pre-scan (use stale DB) / 事前走査をスキップ |
|
|
181
|
+
| `--help` / `-h` | Help / ヘルプ |
|
|
182
|
+
| `--version` / `-v` | Version / バージョン |
|
|
183
|
+
| _anything else_ | Passed through to the launched command / 起動コマンドへパススルー |
|
|
184
|
+
|
|
185
|
+
### Keyboard / キーボード
|
|
186
|
+
|
|
187
|
+
| Key | Action |
|
|
188
|
+
|---|---|
|
|
189
|
+
| Enter | Launch (NEW) or resume (RESUME) / 起動または再開 |
|
|
190
|
+
| Ctrl-Y | Copy `cd … && cmd [--resume …]` to clipboard; fzf stays open with a "📋 Copied" header / クリップボードコピー後もfzfは開いたまま、ヘッダーに📋通知 |
|
|
191
|
+
| Ctrl-I | Copy session UUID or repo path; fzf stays open / コピー後も開いたまま |
|
|
192
|
+
| Ctrl-R | Refresh DB and reload list / DB再走査してリロード |
|
|
193
|
+
| Ctrl-D | Delete session (RESUME rows only). To remove a repo, edit `~/.config/ccs/repos.yml` directly / セッション削除はRESUME行のみ。リポ削除は`repos.yml`を直接編集 |
|
|
194
|
+
| Esc / Ctrl-C | Cancel / キャンセル |
|
|
195
|
+
|
|
196
|
+
**Visual cues / 見た目のガイド:**
|
|
197
|
+
- NEW rows and RESUME rows are separated by a dim `── Past Sessions ──` divider. Selecting the divider is a no-op.
|
|
198
|
+
- RESUME sessions mapped to a registered repo show that repo's icon (e.g., `🔄 📁 ClaudeCode`). Unmapped sessions (cwd that isn't in `repos.yml`) show `❓` — a visual cue that the session was either a one-off or that the repo isn't registered yet.
|
|
199
|
+
|
|
200
|
+
### Workflows / 主な使い方
|
|
201
|
+
|
|
202
|
+
**1. Launch a project fresh / プロジェクトを新規起動**
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
ccs --new
|
|
206
|
+
# pick repo → Enter → claude starts in repo cwd
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
**2. Resume yesterday's work / 昨日の続き**
|
|
210
|
+
|
|
211
|
+
```bash
|
|
212
|
+
ccs --resume
|
|
213
|
+
# fuzzy search by content → Enter → cd + claude --resume <id>
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**3. Resume in current directory / カレントディレクトリで再開**
|
|
217
|
+
|
|
218
|
+
```bash
|
|
219
|
+
cd ~/projects/my-project
|
|
220
|
+
ccs .
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
---
|
|
224
|
+
|
|
225
|
+
## State cache / 状態キャッシュ
|
|
226
|
+
|
|
227
|
+
ccs keeps a SQLite cache at `~/.cache/ccs/state.db` (or `$XDG_CACHE_HOME/ccs/state.db`) with repo metadata, git state badges, session summaries, and handoff/pending file lists.
|
|
228
|
+
|
|
229
|
+
`~/.cache/ccs/state.db` にリポジトリ情報・git状態・セッション要約などをキャッシュ。
|
|
230
|
+
|
|
231
|
+
- Auto-refresh on every `ccs` run (cheap — 10s TTL per repo) / 毎回の起動で自動走査(リポジトリ単位で10秒TTL)
|
|
232
|
+
- Manual force: `ccs --refresh` or press `Ctrl-R` in fzf / 手動強制: `--refresh` または fzf 内で `Ctrl-R`
|
|
233
|
+
- Reset: `rm ~/.cache/ccs/state.db` — next `ccs` run rebuilds it / リセットは DB を削除するだけ(設定 `repos.yml` は無事)
|
|
234
|
+
|
|
235
|
+
Schema details: [`docs/design/sqlite-schema.md`](docs/design/sqlite-schema.md).
|
|
236
|
+
|
|
237
|
+
---
|
|
238
|
+
|
|
239
|
+
## Migration from ccr v0.1.x / ccr v0.1.x からの移行
|
|
240
|
+
|
|
241
|
+
Existing sessions are stored under `~/.claude/projects/*/*.jsonl` — **nothing to migrate manually**. ccs auto-discovers them on first scan.
|
|
242
|
+
|
|
243
|
+
既存の JSONL セッション(`~/.claude/projects/*/*.jsonl`)は初回走査で自動発見される。**手動移行不要**。
|
|
244
|
+
|
|
245
|
+
### Command & env rename / コマンド・環境変数のリネーム
|
|
246
|
+
|
|
247
|
+
| Before (ccr v0.1.x) | After (ccs v0.2.0) |
|
|
248
|
+
|---|---|
|
|
249
|
+
| `ccr` | `ccs` |
|
|
250
|
+
| `CCR_CMD` env | `CCS_CMD` env |
|
|
251
|
+
| GitHub: `indigo-gr/claude-code-recall` | `indigo-gr/claude-code-station` |
|
|
252
|
+
|
|
253
|
+
`CCR_CMD` is still honored for now (with a deprecation warning to stderr). Rename it in your shell rc at your earliest convenience. / `CCR_CMD` は当面 deprecation warning 付きで尊重。なるべく早めに `CCS_CMD` にリネームを。
|
|
254
|
+
|
|
255
|
+
### Uninstall old ccr / 旧 ccr のアンインストール
|
|
256
|
+
|
|
257
|
+
```bash
|
|
258
|
+
rm -f ~/.claude/scripts/ccr ~/.claude/scripts/ccr-*.ts ~/.claude/scripts/ccr-*.sh
|
|
259
|
+
# Remove any `export CCR_CMD=...` from ~/.zshrc or ~/.bashrc
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## Architecture / 設計
|
|
265
|
+
|
|
266
|
+
```
|
|
267
|
+
ccs (bash)
|
|
268
|
+
├─ ccs-scan.ts → scans repos, orchestrates, writes state.db
|
|
269
|
+
│ └─ ccs-scan-sessions.ts (session walk, JSONL parse, cwd→repo resolve)
|
|
270
|
+
├─ ccs-list.ts → reads DB, emits fzf rows (NEW + RESUME)
|
|
271
|
+
├─ ccs-preview.ts → preview pane renderer (repo badges)
|
|
272
|
+
│ └─ ccs-preview-session.ts (session conversation head)
|
|
273
|
+
├─ ccs-config.ts → loads/validates ~/.config/ccs/repos.yml
|
|
274
|
+
├─ ccs-init.ts → `ccs init` scaffold / $HOME repo auto-discovery
|
|
275
|
+
├─ ccs-db.ts → better-sqlite3 wrapper (WAL, FK, migrations)
|
|
276
|
+
├─ ccs-delete.sh → Ctrl-D session delete handler
|
|
277
|
+
│ └─ ccs-delete-session.ts (cache row removal, bound params)
|
|
278
|
+
└─ shared helpers (imported by scan / list / preview):
|
|
279
|
+
├─ ccs-sanitize.ts (SHELL_METACHARS, stripControlChars)
|
|
280
|
+
├─ ccs-secrets.ts (26-pattern maskSecrets)
|
|
281
|
+
├─ ccs-time.ts (UTC-safe DB timestamp parsing)
|
|
282
|
+
└─ ccs-utils.ts (truncate, relative time, extractText, UUID_RE)
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
Design docs: [`docs/design/`](docs/design/).
|
|
286
|
+
|
|
287
|
+
---
|
|
288
|
+
|
|
289
|
+
## Security / セキュリティ
|
|
290
|
+
|
|
291
|
+
- Session IDs validated as UUID before shell execution / セッションIDはUUID形式を検証してからシェル実行
|
|
292
|
+
- `$HOME`-rooted path validation for every `cwd` / 全cwdが`$HOME`配下であることを検証
|
|
293
|
+
- Known secret patterns (API keys, tokens) masked in preview / プレビュー内の既知シークレットパターンはマスク
|
|
294
|
+
- SQLite DB created with `0600` permissions; config/cache dirs `0700` / DBは0600、設定/キャッシュディレクトリは0700
|
|
295
|
+
- No `eval`; all invocations are direct / `eval`不使用
|
|
296
|
+
- Prepared statements only for SQLite; foreign keys enforced / SQLiteはprepared statementsのみ、FK有効
|
|
297
|
+
- 50MB per-file cap when parsing JSONL sessions / JSONLパーサは1ファイル50MB上限
|
|
298
|
+
|
|
299
|
+
> **Trust note / 注意**: the `command:` field in `repos.yml` is validated for shell metacharacters at load time — only safe, sanitized commands are executed. / `repos.yml` の `command:` はロード時にシェルメタ文字を検証し、安全な文字列のみが実行される。
|
|
300
|
+
|
|
301
|
+
---
|
|
302
|
+
|
|
303
|
+
## Contributing / コントリビュート
|
|
304
|
+
|
|
305
|
+
Issues and PRs welcome. See [`REVIEW.md`](REVIEW.md) for review standards and [`CHANGELOG.md`](CHANGELOG.md) for release history.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## License / ライセンス
|
|
310
|
+
|
|
311
|
+
MIT. See [LICENSE](LICENSE).
|