claude-code-token-saver 0.1.0
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/LICENSE +21 -0
- package/README.md +140 -0
- package/bin/cli.js +66 -0
- package/package.json +45 -0
- package/setup-claude-code.sh +385 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 NgTrgGiang
|
|
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,140 @@
|
|
|
1
|
+
# claude-code-token-saver
|
|
2
|
+
|
|
3
|
+
Script cài đặt để dựng bộ "rails" (khung làm việc) cho [Claude Code](https://claude.com/claude-code) trong bất kỳ repo git nào — giúp tiết kiệm token và làm việc kỷ luật hơn.
|
|
4
|
+
|
|
5
|
+
Chạy một lần, bạn sẽ có sẵn: `CLAUDE.md` gọn nhẹ, hooks kiểm soát chất lượng + an toàn, hệ thống dev-docs để không mất ngữ cảnh, các subagent chuyên biệt, và vài slash command tiện dụng.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Cài đặt
|
|
10
|
+
|
|
11
|
+
Vào **thư mục gốc** repo bạn muốn gắn rails, rồi chọn 1 trong các cách:
|
|
12
|
+
|
|
13
|
+
**① npx — không cần cài gì (khuyên dùng)**
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npx claude-code-token-saver
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
**② curl — 1 dòng, không cần Node**
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
curl -fsSL https://raw.githubusercontent.com/NgTrgGiang/claude-code-token-saver/main/setup-claude-code.sh | bash
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**③ Chạy trực tiếp file** (nếu đã có sẵn script)
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
bash setup-claude-code.sh
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Cả 3 cách đều **không ghi đè** file đã tồn tại. Toàn bộ nội dung nằm trong `.claude/` (+ `CLAUDE.md` ở gốc), an toàn để commit và chia sẻ cho cả team.
|
|
32
|
+
|
|
33
|
+
> Mọi cách đều cần **bash** trên máy. Trên Windows: dùng Git Bash / WSL, hoặc cài [Git for Windows](https://git-scm.com/download/win).
|
|
34
|
+
|
|
35
|
+
### Tùy chọn
|
|
36
|
+
|
|
37
|
+
| Cờ | Ý nghĩa |
|
|
38
|
+
|----|---------|
|
|
39
|
+
| `--force` | Ghi đè cả những file đã tồn tại (mặc định: bỏ qua) |
|
|
40
|
+
| `--no-detect` | Không tự dò lệnh build/test/lint |
|
|
41
|
+
| `-h`, `--help` | Hiện hướng dẫn |
|
|
42
|
+
|
|
43
|
+
Ví dụ:
|
|
44
|
+
|
|
45
|
+
```bash
|
|
46
|
+
bash setup-claude-code.sh --force # dựng lại từ đầu, ghi đè hết
|
|
47
|
+
bash setup-claude-code.sh --no-detect # không tự dò lệnh build/test/lint
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Sử dụng
|
|
53
|
+
|
|
54
|
+
### Bước 1 — Cài rails vào dự án của bạn
|
|
55
|
+
|
|
56
|
+
Lưu ý: chạy **trong repo dự án bạn muốn code** (ví dụ `my-shop`, `my-api`...), **không phải** trong repo `claude-code-token-saver`.
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cd đường/dẫn/tới/du-an-cua-ban
|
|
60
|
+
npx claude-code-token-saver # hoặc dùng cách curl ở trên
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Xong bước này, dự án của bạn sẽ mọc ra `CLAUDE.md` + thư mục `.claude/`. Script tự nhận diện dự án là Node / Python / Go / Rust để điền sẵn lệnh build/test/lint.
|
|
64
|
+
|
|
65
|
+
### Bước 2 — Điền `CLAUDE.md`
|
|
66
|
+
|
|
67
|
+
Mở `CLAUDE.md`, viết ngắn gọn phần **Architecture** (code nằm đâu), **Conventions** (quy ước riêng), và **lệnh dev/run**. Đây là phần Claude đọc mỗi lượt nên giữ thật gọn.
|
|
68
|
+
|
|
69
|
+
### Bước 3 — Commit cho cả team
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
git add CLAUDE.md .claude/
|
|
73
|
+
git commit -m "Add Claude Code rails"
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Bước 4 — Làm việc trong Claude Code
|
|
77
|
+
|
|
78
|
+
Mở Claude Code **ngay trong thư mục dự án đó**. Từ giờ bạn có sẵn các slash command và làm theo vòng lặp:
|
|
79
|
+
|
|
80
|
+
```
|
|
81
|
+
/plan → /clear → code → /verify → /save
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
1. **`/plan <ý tưởng>`** — Claude phỏng vấn bạn rồi viết spec vào `.claude/dev-docs/SPEC.md` **trước khi** code.
|
|
85
|
+
2. **`/clear`** — xoá ngữ cảnh cho nhẹ (spec đã nằm trên đĩa, không mất gì).
|
|
86
|
+
3. **Code** — để Claude làm theo spec. Việc điều tra nặng nó tự đẩy sang subagent `explorer`.
|
|
87
|
+
4. **`/verify`** — subagent `verifier` độc lập chạy test/build/lint, soi diff, báo PASS/FAIL.
|
|
88
|
+
5. **`/save`** — lưu tiến độ vào `.claude/dev-docs/` để phiên sau resume không mất ngữ cảnh.
|
|
89
|
+
|
|
90
|
+
Trong lúc đó các hook tự chạy ngầm: chặn lệnh nguy hiểm, tự format file, không cho Claude đọc `.env`. Chạy `/hooks` trong Claude Code để xác nhận hook đã đăng ký.
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Nó tạo ra những gì
|
|
95
|
+
|
|
96
|
+
| Đường dẫn | Công dụng |
|
|
97
|
+
|-----------|-----------|
|
|
98
|
+
| `CLAUDE.md` | Bộ nhớ dự án gọn (< ~200 dòng): commands, kiến trúc, quy ước, gotchas, workflow |
|
|
99
|
+
| `.claude/settings.json` | Hooks + danh sách chặn quyền (chặn đọc `.env`, `secrets/`) — **nên commit** |
|
|
100
|
+
| `.claude/hooks/guard-bash.sh` | Chặn lệnh shell nguy hiểm (`rm -rf /`, fork bomb, `git push --force`, `chmod -R 777`, pipe script từ mạng vào shell) |
|
|
101
|
+
| `.claude/hooks/format-file.sh` | Tự động format file sau khi Claude ghi (prettier / ruff / gofmt / rustfmt) |
|
|
102
|
+
| `.claude/agents/explorer.md` | Subagent điều tra codebase **read-only**, chạy model `haiku` để tiết kiệm token |
|
|
103
|
+
| `.claude/agents/verifier.md` | Subagent kiểm chứng độc lập — chạy test/build/lint, soi diff, báo PASS/FAIL |
|
|
104
|
+
| `.claude/commands/*.md` | Các slash command: `/plan`, `/save`, `/verify`, `/wrap` |
|
|
105
|
+
| `.claude/dev-docs/` | Lưu trạng thái task **ngoài** hội thoại, sống sót qua `/clear` và compaction |
|
|
106
|
+
|
|
107
|
+
Script tự nhận diện stack (**Node/TS, Python, Go, Rust**) và điền sẵn lệnh build/test/lint tương ứng.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Các slash command
|
|
112
|
+
|
|
113
|
+
| Lệnh | Tác dụng |
|
|
114
|
+
|------|----------|
|
|
115
|
+
| `/plan <ý tưởng>` | Phỏng vấn bạn kỹ lưỡng rồi viết spec vào `.claude/dev-docs/SPEC.md` **trước khi** code |
|
|
116
|
+
| `/save` | Ghi trạng thái task vào dev-docs để phiên mới resume không mất ngữ cảnh |
|
|
117
|
+
| `/verify` | Giao thay đổi hiện tại cho subagent `verifier` kiểm chứng độc lập |
|
|
118
|
+
| `/wrap` | Post-mortem bug: giải thích nguyên nhân gốc, xác nhận fix, thêm rule vào `## Gotchas` |
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Vì sao "token-saver"?
|
|
123
|
+
|
|
124
|
+
- **Delegate cho subagent** — việc điều tra/đọc code nặng nề đẩy sang `explorer` (chạy `haiku`), giữ context chính sạch.
|
|
125
|
+
- **Dev-docs** — trạng thái task nằm trên đĩa, nên `/clear` thoải mái mà không mất công việc đang dở.
|
|
126
|
+
- **CLAUDE.md gọn** — mỗi dòng đều được nạp mỗi lượt, nên chỉ giữ thứ thật sự cần.
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Lưu ý an toàn
|
|
131
|
+
|
|
132
|
+
> ⚠️ Hooks chạy lệnh shell thật. Hãy đọc kỹ hai script trong `.claude/hooks/` trước khi tin dùng trên repo nhạy cảm.
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## Yêu cầu
|
|
137
|
+
|
|
138
|
+
- `bash`
|
|
139
|
+
- `python3` (dùng để hooks đọc JSON từ stdin — không cần `jq`)
|
|
140
|
+
- Tùy chọn: `jq` (dò script trong `package.json` chính xác hơn), và các trình format (`prettier`, `ruff`, `gofmt`, `rustfmt`)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* npx launcher for claude-code-token-saver.
|
|
6
|
+
*
|
|
7
|
+
* The real installer is the bundled bash script `setup-claude-code.sh`.
|
|
8
|
+
* This wrapper just locates a bash interpreter and runs that script in the
|
|
9
|
+
* user's current directory, forwarding any CLI args (--force, --no-detect...).
|
|
10
|
+
*
|
|
11
|
+
* Works on macOS / Linux / WSL, and on Windows when Git Bash is installed.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { spawnSync } = require("child_process");
|
|
15
|
+
const path = require("path");
|
|
16
|
+
const fs = require("fs");
|
|
17
|
+
|
|
18
|
+
const script = path.join(__dirname, "..", "setup-claude-code.sh");
|
|
19
|
+
|
|
20
|
+
if (!fs.existsSync(script)) {
|
|
21
|
+
console.error("claude-code-token-saver: cannot find setup-claude-code.sh");
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Candidate bash locations. First hit that runs wins.
|
|
26
|
+
function candidateBashes() {
|
|
27
|
+
const list = ["bash"];
|
|
28
|
+
if (process.platform === "win32") {
|
|
29
|
+
const pf = process.env["ProgramFiles"] || "C:\\Program Files";
|
|
30
|
+
const pf86 = process.env["ProgramFiles(x86)"] || "C:\\Program Files (x86)";
|
|
31
|
+
const local = process.env["LOCALAPPDATA"] || "";
|
|
32
|
+
list.push(
|
|
33
|
+
path.join(pf, "Git", "bin", "bash.exe"),
|
|
34
|
+
path.join(pf, "Git", "usr", "bin", "bash.exe"),
|
|
35
|
+
path.join(pf86, "Git", "bin", "bash.exe"),
|
|
36
|
+
local && path.join(local, "Programs", "Git", "bin", "bash.exe"),
|
|
37
|
+
"C:\\Windows\\System32\\bash.exe" // WSL
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
return list.filter(Boolean);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const args = process.argv.slice(2);
|
|
44
|
+
|
|
45
|
+
for (const bash of candidateBashes()) {
|
|
46
|
+
const res = spawnSync(bash, [script, ...args], {
|
|
47
|
+
stdio: "inherit",
|
|
48
|
+
cwd: process.cwd(),
|
|
49
|
+
});
|
|
50
|
+
if (res.error && res.error.code === "ENOENT") {
|
|
51
|
+
continue; // this bash isn't here, try the next candidate
|
|
52
|
+
}
|
|
53
|
+
process.exit(res.status == null ? 1 : res.status);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
console.error(
|
|
57
|
+
[
|
|
58
|
+
"claude-code-token-saver: no bash interpreter found.",
|
|
59
|
+
"",
|
|
60
|
+
"This tool runs a bash script, so it needs bash on your PATH.",
|
|
61
|
+
" • Windows: install Git for Windows (https://git-scm.com/download/win)",
|
|
62
|
+
" then run this from Git Bash, or ensure bash.exe is on PATH.",
|
|
63
|
+
" • macOS / Linux / WSL: bash is already available.",
|
|
64
|
+
].join("\n")
|
|
65
|
+
);
|
|
66
|
+
process.exit(1);
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "claude-code-token-saver",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "One-shot installer that adds token-saving Claude Code rails (CLAUDE.md, hooks, subagents, slash commands, dev-docs) to any git repo.",
|
|
5
|
+
"bin": {
|
|
6
|
+
"claude-code-token-saver": "bin/cli.js",
|
|
7
|
+
"claude-rails": "bin/cli.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"bin/cli.js",
|
|
11
|
+
"setup-claude-code.sh"
|
|
12
|
+
],
|
|
13
|
+
"scripts": {
|
|
14
|
+
"start": "node bin/cli.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"claude",
|
|
18
|
+
"claude-code",
|
|
19
|
+
"anthropic",
|
|
20
|
+
"ai",
|
|
21
|
+
"hooks",
|
|
22
|
+
"subagents",
|
|
23
|
+
"developer-tools",
|
|
24
|
+
"scaffold",
|
|
25
|
+
"token-saver"
|
|
26
|
+
],
|
|
27
|
+
"author": "NgTrgGiang",
|
|
28
|
+
"license": "MIT",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "git+https://github.com/NgTrgGiang/claude-code-token-saver.git"
|
|
32
|
+
},
|
|
33
|
+
"bugs": {
|
|
34
|
+
"url": "https://github.com/NgTrgGiang/claude-code-token-saver/issues"
|
|
35
|
+
},
|
|
36
|
+
"homepage": "https://github.com/NgTrgGiang/claude-code-token-saver#readme",
|
|
37
|
+
"engines": {
|
|
38
|
+
"node": ">=14"
|
|
39
|
+
},
|
|
40
|
+
"os": [
|
|
41
|
+
"darwin",
|
|
42
|
+
"linux",
|
|
43
|
+
"win32"
|
|
44
|
+
]
|
|
45
|
+
}
|
|
@@ -0,0 +1,385 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
#
|
|
3
|
+
# setup-claude-code.sh
|
|
4
|
+
# ---------------------------------------------------------------------------
|
|
5
|
+
# One-shot setup that installs the "rails" the Claude Code community
|
|
6
|
+
# recommends: a lean CLAUDE.md, quality-gate + safety hooks, a dev-docs
|
|
7
|
+
# system, exploration/verification subagents, and helper slash commands.
|
|
8
|
+
#
|
|
9
|
+
# Run once inside the ROOT of a git repo:
|
|
10
|
+
# bash setup-claude-code.sh
|
|
11
|
+
#
|
|
12
|
+
# It never overwrites existing files unless you pass --force.
|
|
13
|
+
# Everything it writes lives under .claude/ (+ CLAUDE.md at the root),
|
|
14
|
+
# so it is safe to commit to your repo and share with your team.
|
|
15
|
+
# ---------------------------------------------------------------------------
|
|
16
|
+
|
|
17
|
+
set -euo pipefail
|
|
18
|
+
|
|
19
|
+
# ---------- options ---------------------------------------------------------
|
|
20
|
+
FORCE=0
|
|
21
|
+
SKIP_DETECT=0
|
|
22
|
+
for arg in "$@"; do
|
|
23
|
+
case "$arg" in
|
|
24
|
+
--force) FORCE=1 ;;
|
|
25
|
+
--no-detect) SKIP_DETECT=1 ;;
|
|
26
|
+
-h|--help)
|
|
27
|
+
cat <<'EOF'
|
|
28
|
+
Usage: bash setup-claude-code.sh [--force] [--no-detect]
|
|
29
|
+
|
|
30
|
+
--force Overwrite files that already exist (default: skip them)
|
|
31
|
+
--no-detect Do not auto-detect build/test/lint commands
|
|
32
|
+
EOF
|
|
33
|
+
exit 0 ;;
|
|
34
|
+
*) echo "Unknown option: $arg" >&2; exit 1 ;;
|
|
35
|
+
esac
|
|
36
|
+
done
|
|
37
|
+
|
|
38
|
+
# ---------- pretty output ---------------------------------------------------
|
|
39
|
+
c_reset=$'\033[0m'; c_grn=$'\033[32m'; c_ylw=$'\033[33m'; c_cyn=$'\033[36m'; c_dim=$'\033[2m'
|
|
40
|
+
info() { printf "%s»%s %s\n" "$c_cyn" "$c_reset" "$*"; }
|
|
41
|
+
ok() { printf "%s✓%s %s\n" "$c_grn" "$c_reset" "$*"; }
|
|
42
|
+
skip() { printf "%s•%s %s%s%s\n" "$c_ylw" "$c_reset" "$c_dim" "$*" "$c_reset"; }
|
|
43
|
+
|
|
44
|
+
# writes a file unless it exists (respects --force). $1=path, stdin=content
|
|
45
|
+
write_file() {
|
|
46
|
+
local path="$1"
|
|
47
|
+
if [[ -e "$path" && $FORCE -eq 0 ]]; then
|
|
48
|
+
skip "exists, skipped: $path (use --force to overwrite)"
|
|
49
|
+
cat > /dev/null # drain stdin
|
|
50
|
+
return
|
|
51
|
+
fi
|
|
52
|
+
mkdir -p "$(dirname "$path")"
|
|
53
|
+
cat > "$path"
|
|
54
|
+
ok "wrote $path"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# ---------- sanity ----------------------------------------------------------
|
|
58
|
+
if [[ ! -d .git ]]; then
|
|
59
|
+
info "No .git here — this doesn't look like a repo root."
|
|
60
|
+
printf " Continue anyway? [y/N] "
|
|
61
|
+
read -r reply
|
|
62
|
+
[[ "$reply" =~ ^[Yy]$ ]] || { echo "Aborted."; exit 1; }
|
|
63
|
+
fi
|
|
64
|
+
|
|
65
|
+
PROJECT_NAME="$(basename "$PWD")"
|
|
66
|
+
info "Setting up Claude Code rails for: ${c_grn}${PROJECT_NAME}${c_reset}"
|
|
67
|
+
echo
|
|
68
|
+
|
|
69
|
+
# ---------- detect commands -------------------------------------------------
|
|
70
|
+
BUILD_CMD="# add your build command"
|
|
71
|
+
TEST_CMD="# add your test command"
|
|
72
|
+
LINT_CMD="# add your lint command"
|
|
73
|
+
STACK="unknown"
|
|
74
|
+
|
|
75
|
+
if [[ $SKIP_DETECT -eq 0 ]]; then
|
|
76
|
+
if [[ -f package.json ]]; then
|
|
77
|
+
STACK="Node / JavaScript-TypeScript"
|
|
78
|
+
command -v jq >/dev/null 2>&1 && HAVE_JQ=1 || HAVE_JQ=0
|
|
79
|
+
has_script() { [[ $HAVE_JQ -eq 1 ]] && jq -e ".scripts.\"$1\" // empty" package.json >/dev/null 2>&1; }
|
|
80
|
+
if [[ $HAVE_JQ -eq 1 ]]; then
|
|
81
|
+
has_script build && BUILD_CMD="npm run build"
|
|
82
|
+
has_script test && TEST_CMD="npm test"
|
|
83
|
+
has_script lint && LINT_CMD="npm run lint"
|
|
84
|
+
has_script typecheck && LINT_CMD="npm run typecheck && ${LINT_CMD#\# *}"
|
|
85
|
+
else
|
|
86
|
+
BUILD_CMD="npm run build"; TEST_CMD="npm test"; LINT_CMD="npm run lint"
|
|
87
|
+
fi
|
|
88
|
+
elif [[ -f pyproject.toml || -f requirements.txt || -f setup.py ]]; then
|
|
89
|
+
STACK="Python"
|
|
90
|
+
TEST_CMD="pytest -q"
|
|
91
|
+
LINT_CMD="ruff check . && ruff format --check ."
|
|
92
|
+
[[ -f pyproject.toml ]] && BUILD_CMD="python -m build" || BUILD_CMD="# (library — no build step)"
|
|
93
|
+
elif [[ -f go.mod ]]; then
|
|
94
|
+
STACK="Go"
|
|
95
|
+
BUILD_CMD="go build ./..."; TEST_CMD="go test ./..."; LINT_CMD="go vet ./... && golangci-lint run"
|
|
96
|
+
elif [[ -f Cargo.toml ]]; then
|
|
97
|
+
STACK="Rust"
|
|
98
|
+
BUILD_CMD="cargo build"; TEST_CMD="cargo test"; LINT_CMD="cargo clippy -- -D warnings && cargo fmt --check"
|
|
99
|
+
fi
|
|
100
|
+
info "Detected stack: ${c_grn}${STACK}${c_reset}"
|
|
101
|
+
fi
|
|
102
|
+
echo
|
|
103
|
+
|
|
104
|
+
# ===========================================================================
|
|
105
|
+
# 1. CLAUDE.md — lean project memory (< ~200 lines, command-first)
|
|
106
|
+
# ===========================================================================
|
|
107
|
+
write_file "CLAUDE.md" <<EOF
|
|
108
|
+
# ${PROJECT_NAME}
|
|
109
|
+
|
|
110
|
+
> Project memory for Claude Code. Keep this SHORT (< ~200 lines).
|
|
111
|
+
> Every line is loaded on every turn — delete anything a linter/formatter
|
|
112
|
+
> already enforces, and anything Claude gets right without being told.
|
|
113
|
+
|
|
114
|
+
## Commands
|
|
115
|
+
- Build: ${BUILD_CMD}
|
|
116
|
+
- Test: ${TEST_CMD}
|
|
117
|
+
- Lint: ${LINT_CMD}
|
|
118
|
+
- Run/dev: # add your dev/run command
|
|
119
|
+
|
|
120
|
+
## Architecture
|
|
121
|
+
<!-- One paragraph. Where does code live, what are the key modules? -->
|
|
122
|
+
<!-- Point to files instead of pasting code: See src/... for X. -->
|
|
123
|
+
|
|
124
|
+
## Conventions
|
|
125
|
+
<!-- Only rules a formatter can't enforce. Prefer "do X instead of Y". -->
|
|
126
|
+
- Prefer editing existing files over creating new ones.
|
|
127
|
+
- Never commit secrets; config lives in .env (git-ignored).
|
|
128
|
+
|
|
129
|
+
## Gotchas
|
|
130
|
+
<!-- Things Claude repeatedly gets wrong here. Add to this over time. -->
|
|
131
|
+
|
|
132
|
+
## Workflow
|
|
133
|
+
- Use **plan mode** before non-trivial edits; get the plan approved first.
|
|
134
|
+
- Always verify with tests/build before declaring a task done.
|
|
135
|
+
- For big investigations, delegate to the \`explorer\` subagent so the
|
|
136
|
+
main context stays clean.
|
|
137
|
+
- Detailed task state lives in \`.claude/dev-docs/\` (plan / context / tasks).
|
|
138
|
+
EOF
|
|
139
|
+
|
|
140
|
+
# ===========================================================================
|
|
141
|
+
# 2. Hooks + permissions — .claude/settings.json
|
|
142
|
+
# (safe to commit; secrets go in settings.local.json)
|
|
143
|
+
# ===========================================================================
|
|
144
|
+
# Quality gate: block `git commit` unless tests pass.
|
|
145
|
+
# Safety gate: block obviously destructive shell commands.
|
|
146
|
+
write_file ".claude/settings.json" <<'EOF'
|
|
147
|
+
{
|
|
148
|
+
"$schema": "https://json.schemastore.org/claude-code-settings.json",
|
|
149
|
+
"permissions": {
|
|
150
|
+
"deny": [
|
|
151
|
+
"Read(./.env)",
|
|
152
|
+
"Read(./.env.*)",
|
|
153
|
+
"Read(./**/secrets/**)"
|
|
154
|
+
]
|
|
155
|
+
},
|
|
156
|
+
"hooks": {
|
|
157
|
+
"PreToolUse": [
|
|
158
|
+
{
|
|
159
|
+
"matcher": "Bash",
|
|
160
|
+
"hooks": [
|
|
161
|
+
{
|
|
162
|
+
"type": "command",
|
|
163
|
+
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/guard-bash.sh\""
|
|
164
|
+
}
|
|
165
|
+
]
|
|
166
|
+
}
|
|
167
|
+
],
|
|
168
|
+
"PostToolUse": [
|
|
169
|
+
{
|
|
170
|
+
"matcher": "Write|Edit",
|
|
171
|
+
"hooks": [
|
|
172
|
+
{
|
|
173
|
+
"type": "command",
|
|
174
|
+
"command": "\"$CLAUDE_PROJECT_DIR/.claude/hooks/format-file.sh\""
|
|
175
|
+
}
|
|
176
|
+
]
|
|
177
|
+
}
|
|
178
|
+
]
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
EOF
|
|
182
|
+
|
|
183
|
+
# 2a. Safety hook — blocks destructive Bash commands (exit 2 = block)
|
|
184
|
+
write_file ".claude/hooks/guard-bash.sh" <<'EOF'
|
|
185
|
+
#!/usr/bin/env bash
|
|
186
|
+
# PreToolUse(Bash) guard. Reads tool JSON on stdin, blocks dangerous commands.
|
|
187
|
+
# Exit 2 = block (stderr shown to Claude). Exit 0 = allow.
|
|
188
|
+
set -euo pipefail
|
|
189
|
+
input="$(cat)"
|
|
190
|
+
|
|
191
|
+
# Extract the command field without requiring jq.
|
|
192
|
+
cmd="$(printf '%s' "$input" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("tool_input",{}).get("command",""))' 2>/dev/null || true)"
|
|
193
|
+
|
|
194
|
+
block() { echo "BLOCKED by guard-bash: $1" >&2; exit 2; }
|
|
195
|
+
|
|
196
|
+
case "$cmd" in
|
|
197
|
+
*"rm -rf /"*|*"rm -rf ~"*|*"rm -rf ."*) block "recursive force-delete of a dangerous path" ;;
|
|
198
|
+
*":(){ :|:& };:"*) block "fork bomb" ;;
|
|
199
|
+
*"git push"*"--force"*|*"git push -f"*) block "force push (do a normal push or ask the human)" ;;
|
|
200
|
+
*"chmod -R 777"*) block "world-writable recursive chmod" ;;
|
|
201
|
+
*"curl"*"| bash"*|*"wget"*"| sh"*|*"curl"*"| sh"*) block "piping a remote script straight into a shell" ;;
|
|
202
|
+
esac
|
|
203
|
+
exit 0
|
|
204
|
+
EOF
|
|
205
|
+
chmod +x ".claude/hooks/guard-bash.sh" 2>/dev/null || true
|
|
206
|
+
|
|
207
|
+
# 2b. Format hook — auto-formats files Claude writes (best-effort, never blocks)
|
|
208
|
+
write_file ".claude/hooks/format-file.sh" <<'EOF'
|
|
209
|
+
#!/usr/bin/env bash
|
|
210
|
+
# PostToolUse(Write|Edit): format the touched file. Never blocks (exit 0).
|
|
211
|
+
set -uo pipefail
|
|
212
|
+
input="$(cat)"
|
|
213
|
+
f="$(printf '%s' "$input" | python3 -c 'import sys,json;print(json.load(sys.stdin).get("tool_input",{}).get("file_path",""))' 2>/dev/null || true)"
|
|
214
|
+
[[ -z "$f" || ! -f "$f" ]] && exit 0
|
|
215
|
+
|
|
216
|
+
case "$f" in
|
|
217
|
+
*.js|*.jsx|*.ts|*.tsx|*.json|*.css|*.md|*.html)
|
|
218
|
+
command -v npx >/dev/null 2>&1 && npx --no-install prettier --write "$f" >/dev/null 2>&1 || true ;;
|
|
219
|
+
*.py)
|
|
220
|
+
command -v ruff >/dev/null 2>&1 && ruff format "$f" >/dev/null 2>&1 || true ;;
|
|
221
|
+
*.go)
|
|
222
|
+
command -v gofmt >/dev/null 2>&1 && gofmt -w "$f" >/dev/null 2>&1 || true ;;
|
|
223
|
+
*.rs)
|
|
224
|
+
command -v rustfmt >/dev/null 2>&1 && rustfmt "$f" >/dev/null 2>&1 || true ;;
|
|
225
|
+
esac
|
|
226
|
+
exit 0
|
|
227
|
+
EOF
|
|
228
|
+
chmod +x ".claude/hooks/format-file.sh" 2>/dev/null || true
|
|
229
|
+
|
|
230
|
+
# ===========================================================================
|
|
231
|
+
# 3. Subagents — .claude/agents/*.md (each has its own context window)
|
|
232
|
+
# ===========================================================================
|
|
233
|
+
write_file ".claude/agents/explorer.md" <<'EOF'
|
|
234
|
+
---
|
|
235
|
+
name: explorer
|
|
236
|
+
description: Read-only codebase investigator. Use PROACTIVELY for any "how does X work / where is Y / trace this flow" question so exploration does not fill the main context window. Returns a concise findings summary, not raw file dumps.
|
|
237
|
+
tools: Read, Grep, Glob
|
|
238
|
+
model: haiku
|
|
239
|
+
---
|
|
240
|
+
|
|
241
|
+
You are a focused codebase explorer running in your own context window.
|
|
242
|
+
|
|
243
|
+
Your job: answer the specific investigation question and nothing more.
|
|
244
|
+
|
|
245
|
+
Rules:
|
|
246
|
+
- Scope tightly. Read only what the question needs — do not wander the repo.
|
|
247
|
+
- Never edit files. You are read-only.
|
|
248
|
+
- Return a SHORT summary: the answer, the 3–8 key files/symbols involved
|
|
249
|
+
(with paths), and any surprising gotchas. No verbatim file dumps.
|
|
250
|
+
- If the question is ambiguous, state your interpretation, then answer it.
|
|
251
|
+
EOF
|
|
252
|
+
|
|
253
|
+
write_file ".claude/agents/verifier.md" <<'EOF'
|
|
254
|
+
---
|
|
255
|
+
name: verifier
|
|
256
|
+
description: Independent verification agent. Use after an implementation to confirm it actually works — runs tests/build/lint and reviews the diff against the stated goal. Blocks "should work" claims.
|
|
257
|
+
tools: Read, Grep, Glob, Bash
|
|
258
|
+
# No model pinned → inherits the main session's model. Verifying a diff needs
|
|
259
|
+
# real reasoning, so keep it on whatever tier you're coding with (not haiku).
|
|
260
|
+
---
|
|
261
|
+
|
|
262
|
+
You are an independent verifier. You did NOT write the code, so be skeptical.
|
|
263
|
+
|
|
264
|
+
Steps:
|
|
265
|
+
1. Restate the goal in one sentence (from the task / dev-docs).
|
|
266
|
+
2. Run the project's test, build, and lint commands (see CLAUDE.md).
|
|
267
|
+
3. Read the diff and check it against the goal, including edge cases.
|
|
268
|
+
4. Report PASS or FAIL with evidence: actual command output, failing cases,
|
|
269
|
+
or missing handling. Never say "should work" — show that it does.
|
|
270
|
+
|
|
271
|
+
If you cannot verify something, say so explicitly rather than assuming.
|
|
272
|
+
EOF
|
|
273
|
+
|
|
274
|
+
# ===========================================================================
|
|
275
|
+
# 4. Slash commands — .claude/commands/*.md
|
|
276
|
+
# ===========================================================================
|
|
277
|
+
write_file ".claude/commands/plan.md" <<'EOF'
|
|
278
|
+
---
|
|
279
|
+
description: Interview me, then write a self-contained spec before any code.
|
|
280
|
+
---
|
|
281
|
+
I want to build: $ARGUMENTS
|
|
282
|
+
|
|
283
|
+
Interview me in detail using the AskUserQuestion tool. Ask about technical
|
|
284
|
+
implementation, UI/UX, edge cases, concerns, and tradeoffs. Skip obvious
|
|
285
|
+
questions; dig into the hard parts I might not have considered. Keep going
|
|
286
|
+
until we've covered everything.
|
|
287
|
+
|
|
288
|
+
Then write a self-contained spec to `.claude/dev-docs/SPEC.md` that names the
|
|
289
|
+
files and interfaces involved, states what is OUT of scope, and ends with an
|
|
290
|
+
end-to-end verification step that proves the feature works.
|
|
291
|
+
|
|
292
|
+
Do NOT write implementation code yet. When the spec is ready, tell me to start
|
|
293
|
+
a fresh session (/clear) to execute it against the spec.
|
|
294
|
+
EOF
|
|
295
|
+
|
|
296
|
+
write_file ".claude/commands/save.md" <<'EOF'
|
|
297
|
+
---
|
|
298
|
+
description: Persist task state to dev-docs so context survives /clear or compaction.
|
|
299
|
+
---
|
|
300
|
+
Update the dev-docs for the current task so a fresh session can resume with no
|
|
301
|
+
lost context. Write/refresh these files under `.claude/dev-docs/`:
|
|
302
|
+
|
|
303
|
+
- `plan.md` — the accepted plan / approach
|
|
304
|
+
- `context.md` — key files, decisions made, constraints, things that surprised us
|
|
305
|
+
- `tasks.md` — a checklist of what's done and what's left
|
|
306
|
+
|
|
307
|
+
Be concise but specific (name real files and decisions). Then remind me I can
|
|
308
|
+
safely run /clear and later say "read .claude/dev-docs and brief me".
|
|
309
|
+
EOF
|
|
310
|
+
|
|
311
|
+
write_file ".claude/commands/verify.md" <<'EOF'
|
|
312
|
+
---
|
|
313
|
+
description: Hand the current change to the independent verifier subagent.
|
|
314
|
+
---
|
|
315
|
+
Delegate to the `verifier` subagent: confirm the current change actually meets
|
|
316
|
+
its goal. It should run tests/build/lint (see CLAUDE.md), review the diff, and
|
|
317
|
+
report PASS/FAIL with real evidence. Do not mark the task done until it passes.
|
|
318
|
+
EOF
|
|
319
|
+
|
|
320
|
+
write_file ".claude/commands/wrap.md" <<'EOF'
|
|
321
|
+
---
|
|
322
|
+
description: Debug post-mortem — fix, explain, and harden against recurrence.
|
|
323
|
+
---
|
|
324
|
+
For the bug we just dealt with:
|
|
325
|
+
1. Explain the root cause in 2–3 sentences.
|
|
326
|
+
2. Confirm the fix and how it was verified.
|
|
327
|
+
3. If this is a class of mistake likely to recur here, add a one-line rule to
|
|
328
|
+
the "## Gotchas" section of CLAUDE.md so it doesn't happen again.
|
|
329
|
+
EOF
|
|
330
|
+
|
|
331
|
+
# ===========================================================================
|
|
332
|
+
# 5. Dev-docs scaffold + .gitignore hints
|
|
333
|
+
# ===========================================================================
|
|
334
|
+
write_file ".claude/dev-docs/README.md" <<'EOF'
|
|
335
|
+
# dev-docs
|
|
336
|
+
|
|
337
|
+
Task state that lives OUTSIDE the conversation, so context survives `/clear`
|
|
338
|
+
and compaction. Update with `/save`. Resume a session with:
|
|
339
|
+
|
|
340
|
+
read .claude/dev-docs and brief me on where we are
|
|
341
|
+
|
|
342
|
+
Files (per active task): plan.md, context.md, tasks.md, and SPEC.md (from /plan).
|
|
343
|
+
EOF
|
|
344
|
+
|
|
345
|
+
# Add ignore hints for personal/local settings, without clobbering an existing file.
|
|
346
|
+
if [[ -f .gitignore ]]; then
|
|
347
|
+
for line in ".claude/settings.local.json" ".claude/dev-docs/*.local.md"; do
|
|
348
|
+
grep -qxF "$line" .gitignore 2>/dev/null || { echo "$line" >> .gitignore; ok ".gitignore += $line"; }
|
|
349
|
+
done
|
|
350
|
+
else
|
|
351
|
+
write_file ".gitignore" <<'EOF'
|
|
352
|
+
# Claude Code — personal/local overrides (keep out of git)
|
|
353
|
+
.claude/settings.local.json
|
|
354
|
+
.claude/dev-docs/*.local.md
|
|
355
|
+
EOF
|
|
356
|
+
fi
|
|
357
|
+
|
|
358
|
+
# ===========================================================================
|
|
359
|
+
# Summary
|
|
360
|
+
# ===========================================================================
|
|
361
|
+
echo
|
|
362
|
+
info "Done. Here's what got installed:"
|
|
363
|
+
cat <<EOF
|
|
364
|
+
|
|
365
|
+
CLAUDE.md lean project memory (fill in the blanks)
|
|
366
|
+
.claude/settings.json hooks + permission deny-list (commit this)
|
|
367
|
+
.claude/hooks/guard-bash.sh blocks destructive shell commands
|
|
368
|
+
.claude/hooks/format-file.sh auto-formats files Claude writes
|
|
369
|
+
.claude/agents/explorer.md read-only investigator (own context)
|
|
370
|
+
.claude/agents/verifier.md independent "prove it works" agent
|
|
371
|
+
.claude/commands/plan.md /plan — interview → SPEC.md
|
|
372
|
+
.claude/commands/save.md /save — persist task state
|
|
373
|
+
.claude/commands/verify.md /verify — run the verifier subagent
|
|
374
|
+
.claude/commands/wrap.md /wrap — post-mortem + harden CLAUDE.md
|
|
375
|
+
.claude/dev-docs/ persistent task state across sessions
|
|
376
|
+
|
|
377
|
+
${c_ylw}Next:${c_reset}
|
|
378
|
+
1. Open CLAUDE.md and fill in Architecture / Conventions / dev command.
|
|
379
|
+
2. Commit .claude/ and CLAUDE.md so your team gets the same rails.
|
|
380
|
+
3. In Claude Code: run /hooks to confirm hooks are registered.
|
|
381
|
+
4. Try the habits: /plan → /clear → build → /verify → /save.
|
|
382
|
+
|
|
383
|
+
${c_dim}Note: hooks run real shell commands. Read the two scripts in
|
|
384
|
+
.claude/hooks/ before trusting them on a sensitive repo.${c_reset}
|
|
385
|
+
EOF
|