claude-master-toolkit 0.1.3 → 0.1.5
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.md +20 -20
- package/bin/.ctk-legacy +398 -0
- package/claude-dist/CLAUDE.md +86 -0
- package/claude-dist/agents/sdd-orchestrator.md +196 -0
- package/claude-dist/hooks/session-start.sh +86 -0
- package/claude-dist/hooks/user-prompt-submit.sh +73 -0
- package/claude-dist/revision-skills/judgment-day/SKILL.md +350 -0
- package/claude-dist/revision-skills/judgment-day-minimal/SKILL.md +26 -0
- package/claude-dist/revision-skills/repo-layer-master/SKILL.md +124 -0
- package/claude-dist/revision-skills/workctl/SKILL.md +135 -0
- package/claude-dist/settings.patch.json +24 -0
- package/claude-dist/skills/delegate/SKILL.md +68 -0
- package/claude-dist/skills/engram-protocol/SKILL.md +92 -0
- package/claude-dist/skills/sdd-continue/SKILL.md +27 -0
- package/claude-dist/skills/sdd-ff/SKILL.md +30 -0
- package/claude-dist/skills/sdd-new/SKILL.md +31 -0
- package/dist/cli.js +53 -146
- package/package.json +3 -1
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# claude-master-toolkit
|
|
2
2
|
|
|
3
3
|
**Token-efficient CLI + metrics dashboard for [Claude Code](https://claude.ai/code).**
|
|
4
4
|
|
|
@@ -15,7 +15,7 @@ Opens a local dashboard at `http://localhost:3200` backed by SQLite. Reads your
|
|
|
15
15
|
|
|
16
16
|
## Why
|
|
17
17
|
|
|
18
|
-
Claude Code stores every session as JSONL under `~/.claude/projects/`. That's the source of truth for real token usage and cost — but there's no good way to see it. `
|
|
18
|
+
Claude Code stores every session as JSONL under `~/.claude/projects/`. That's the source of truth for real token usage and cost — but there's no good way to see it. `claude-master-toolkit` parses those files into SQLite and gives you:
|
|
19
19
|
|
|
20
20
|
- **Realized cost** per session, per project, per day — using the actual Claude 4.6 / 4.5 pricing table.
|
|
21
21
|
- **Context window telemetry** — know when you're about to hit the 200k wall before the warning.
|
|
@@ -27,7 +27,7 @@ Think of it as the missing `/cost` page for Claude Code.
|
|
|
27
27
|
## Install
|
|
28
28
|
|
|
29
29
|
```bash
|
|
30
|
-
npm install -g
|
|
30
|
+
npm install -g claude-master-toolkit
|
|
31
31
|
```
|
|
32
32
|
|
|
33
33
|
Requires Node 18+. The database auto-initializes on first run at `~/.claude/state/claude-master-toolkit/ctk.sqlite`.
|
|
@@ -37,9 +37,9 @@ Requires Node 18+. The database auto-initializes on first run at `~/.claude/stat
|
|
|
37
37
|
### Dashboard
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
40
|
+
ctk dashboard # http://localhost:3200
|
|
41
|
+
ctk dashboard -p 4000 # custom port
|
|
42
|
+
ctk dashboard --no-open # don't auto-open browser
|
|
43
43
|
```
|
|
44
44
|
|
|
45
45
|
The server watches `~/.claude/projects/` and syncs new session events in real time.
|
|
@@ -48,19 +48,19 @@ The server watches `~/.claude/projects/` and syncs new session events in real ti
|
|
|
48
48
|
|
|
49
49
|
All commands are designed to return **compact, Claude-friendly output** — ideal for the `Bash` tool in agent loops.
|
|
50
50
|
|
|
51
|
-
| Command
|
|
52
|
-
|
|
|
53
|
-
| `
|
|
54
|
-
| `
|
|
55
|
-
| `
|
|
56
|
-
| `
|
|
57
|
-
| `
|
|
58
|
-
| `
|
|
59
|
-
| `
|
|
60
|
-
| `
|
|
61
|
-
| `
|
|
62
|
-
| `
|
|
63
|
-
| `
|
|
51
|
+
| Command | What it does |
|
|
52
|
+
| --------------------------- | --------------------------------------------------------------------------- |
|
|
53
|
+
| `ctk cost [--quiet]` | Realized cost of the current session (reads session JSONL, applies pricing) |
|
|
54
|
+
| `ctk context` | Current window usage, cumulative totals, cost, threshold advice |
|
|
55
|
+
| `ctk tokens [file]` | Rough token count for a file or stdin (`chars / 4`) |
|
|
56
|
+
| `ctk estimate <file>` | Faithful pre-flight count via Anthropic `count_tokens` API |
|
|
57
|
+
| `ctk slice <file> <symbol>` | Extract just one function / class / type from a file |
|
|
58
|
+
| `ctk find <query> [path]` | Ranked ripgrep, top 20 results |
|
|
59
|
+
| `ctk git-log [N]` | One-line log for last N commits |
|
|
60
|
+
| `ctk git-changed` | Files changed vs main branch with line counts |
|
|
61
|
+
| `ctk test-summary [cmd]` | Run a test command, print only pass/fail summary |
|
|
62
|
+
| `ctk model <phase>` | Model alias recommendation per SDD phase |
|
|
63
|
+
| `ctk dashboard` | Launch the local metrics dashboard |
|
|
64
64
|
|
|
65
65
|
Every command supports `--json` for machine-readable output.
|
|
66
66
|
|
|
@@ -80,7 +80,7 @@ Pricing table is hard-coded in `src/shared/pricing.ts` for the Claude 4.6 / 4.5
|
|
|
80
80
|
|
|
81
81
|
## Context window vs cumulative cost
|
|
82
82
|
|
|
83
|
-
These are **different metrics** and `
|
|
83
|
+
These are **different metrics** and `ctk` reports both:
|
|
84
84
|
|
|
85
85
|
- **Context window** = what Claude sees right now. Equals the last turn's `input + cache_read + output`. Not a running sum — summing double-counts cache reads.
|
|
86
86
|
- **Cumulative cost** = every turn's tokens weighted by per-model prices. This is what `/cost` shows.
|
package/bin/.ctk-legacy
ADDED
|
@@ -0,0 +1,398 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ctk — Claude Master Toolkit CLI
|
|
3
|
+
# Token-efficient command helpers for Claude Code.
|
|
4
|
+
|
|
5
|
+
set -euo pipefail
|
|
6
|
+
|
|
7
|
+
VERSION="0.1.0"
|
|
8
|
+
|
|
9
|
+
usage() {
|
|
10
|
+
cat <<'EOF'
|
|
11
|
+
ctk — Claude Master Toolkit
|
|
12
|
+
|
|
13
|
+
USAGE
|
|
14
|
+
ctk <command> [args]
|
|
15
|
+
|
|
16
|
+
COMMANDS
|
|
17
|
+
slice <file> <symbol> Extract a symbol's block from a file (function/class/etc.)
|
|
18
|
+
git-log [N] Compact git log: last N commits (default 10), one line each
|
|
19
|
+
git-changed List files changed vs main branch with line counts
|
|
20
|
+
test-summary [cmd] Run test command, show only pass/fail summary (default: yarn test)
|
|
21
|
+
find <query> [path] Ranked search via ripgrep, top 20 results with 1 line context
|
|
22
|
+
model <phase> Print model alias for an SDD phase (respects user preference)
|
|
23
|
+
model-pref [get|set|clear] Get/set/clear model selection preference
|
|
24
|
+
tokens [file] Rough token estimate for a file or stdin (chars/4)
|
|
25
|
+
estimate <file|-> Faithful pre-flight token count via Anthropic API (fallback: rough)
|
|
26
|
+
cost [--quiet] Realized cost of current session (reads session JSONL)
|
|
27
|
+
context Show current Claude Code session context usage
|
|
28
|
+
version Print version
|
|
29
|
+
help This help
|
|
30
|
+
|
|
31
|
+
EXAMPLES
|
|
32
|
+
ctk slice src/foo.ts handleSubmit
|
|
33
|
+
ctk git-log 5
|
|
34
|
+
ctk find "useEffect" src/
|
|
35
|
+
ctk model sdd-propose # depends on preference — see below
|
|
36
|
+
ctk model-pref get # show current preference
|
|
37
|
+
ctk model-pref set auto # enable smart routing
|
|
38
|
+
ctk model-pref set pinned:sonnet # pin everything to sonnet (absolute)
|
|
39
|
+
ctk model-pref clear # back to default (inherit)
|
|
40
|
+
EOF
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
cmd_slice() {
|
|
44
|
+
local file="${1:?file required}" symbol="${2:?symbol required}"
|
|
45
|
+
[[ -f "$file" ]] || { echo "ctk slice: file not found: $file" >&2; exit 1; }
|
|
46
|
+
|
|
47
|
+
# Heuristic: find the line matching the symbol definition, then extract to matching brace/end.
|
|
48
|
+
# Works for JS/TS/Go/Python/Rust with common patterns.
|
|
49
|
+
awk -v sym="$symbol" '
|
|
50
|
+
BEGIN { depth = 0; inside = 0 }
|
|
51
|
+
!inside && $0 ~ ("(function|const|let|var|class|func|def|fn|type|interface)[[:space:]]+" sym "[[:space:](<:=]") {
|
|
52
|
+
inside = 1; start = NR
|
|
53
|
+
}
|
|
54
|
+
inside {
|
|
55
|
+
print NR": "$0
|
|
56
|
+
# Brace counting for C-like
|
|
57
|
+
for (i = 1; i <= length($0); i++) {
|
|
58
|
+
c = substr($0, i, 1)
|
|
59
|
+
if (c == "{") depth++
|
|
60
|
+
else if (c == "}") { depth--; if (depth == 0 && NR > start) { exit } }
|
|
61
|
+
}
|
|
62
|
+
# Python/indent-based: blank line after non-empty inside def = end
|
|
63
|
+
if ($0 ~ /^[[:space:]]*$/ && NR > start + 1 && depth == 0) exit
|
|
64
|
+
}
|
|
65
|
+
' "$file"
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
cmd_git_log() {
|
|
69
|
+
local n="${1:-10}"
|
|
70
|
+
git log --oneline --decorate -n "$n" 2>/dev/null || {
|
|
71
|
+
echo "ctk git-log: not a git repo" >&2; exit 1;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
cmd_git_changed() {
|
|
76
|
+
local base
|
|
77
|
+
base="$(git symbolic-ref --short refs/remotes/origin/HEAD 2>/dev/null | sed 's|origin/||')"
|
|
78
|
+
base="${base:-main}"
|
|
79
|
+
git diff --stat "$base...HEAD" 2>/dev/null || {
|
|
80
|
+
echo "ctk git-changed: not a git repo or no base branch" >&2; exit 1;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
cmd_test_summary() {
|
|
85
|
+
local cmd="${*:-yarn test}"
|
|
86
|
+
local out rc
|
|
87
|
+
out="$($cmd 2>&1)" && rc=0 || rc=$?
|
|
88
|
+
# Print last 20 lines which usually contain the summary
|
|
89
|
+
echo "$out" | tail -20
|
|
90
|
+
echo "---"
|
|
91
|
+
echo "exit: $rc"
|
|
92
|
+
return $rc
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
cmd_find() {
|
|
96
|
+
local query="${1:?query required}"
|
|
97
|
+
local path="${2:-.}"
|
|
98
|
+
if command -v rg >/dev/null 2>&1; then
|
|
99
|
+
rg --color=never --line-number --max-count 3 --heading "$query" "$path" 2>/dev/null | head -60
|
|
100
|
+
else
|
|
101
|
+
grep -rn --max-count=3 "$query" "$path" 2>/dev/null | head -60
|
|
102
|
+
fi
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
MODEL_PREF_FILE="$HOME/.claude/state/claude-master-toolkit/model-preference"
|
|
106
|
+
|
|
107
|
+
# Pricing table — USD per 1M tokens, as of Claude 4.6 / 4.5 family.
|
|
108
|
+
# Source: https://www.anthropic.com/pricing (user-verified values)
|
|
109
|
+
# Format: "INPUT OUTPUT CACHE_READ CACHE_WRITE"
|
|
110
|
+
_pricing() {
|
|
111
|
+
case "$1" in
|
|
112
|
+
opus*) echo "5 25 0.50 6.25" ;; # Opus 4.6
|
|
113
|
+
sonnet*) echo "3 15 0.30 3.75" ;; # Sonnet 4.6
|
|
114
|
+
haiku*) echo "1 5 0.10 1.25" ;; # Haiku 4.5
|
|
115
|
+
*) echo "3 15 0.30 3.75" ;; # fallback: sonnet
|
|
116
|
+
esac
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
_latest_session_file() {
|
|
120
|
+
local cwd encoded proj_dir
|
|
121
|
+
cwd="${CLAUDE_PROJECT_DIR:-$PWD}"
|
|
122
|
+
encoded="$(printf '%s' "$cwd" | sed 's|/|-|g')"
|
|
123
|
+
proj_dir="$HOME/.claude/projects/$encoded"
|
|
124
|
+
[[ -d "$proj_dir" ]] || return 1
|
|
125
|
+
find "$proj_dir" -maxdepth 1 -name '*.jsonl' -type f -printf '%T@ %p\n' 2>/dev/null \
|
|
126
|
+
| sort -rn | head -1 | awk '{print $2}'
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
# Extract the LAST turn's usage from a session JSONL.
|
|
130
|
+
# Used for "current context window occupied" — the sum below is the actual window,
|
|
131
|
+
# NOT the cumulative across turns (that's what _session_tokens gives, for cost).
|
|
132
|
+
# Prints: "INPUT OUTPUT CACHE_READ CACHE_WRITE"
|
|
133
|
+
_session_latest_usage() {
|
|
134
|
+
local file="$1"
|
|
135
|
+
[[ -f "$file" ]] || { echo "0 0 0 0"; return; }
|
|
136
|
+
local line i o cr cw
|
|
137
|
+
line="$(tac "$file" 2>/dev/null | grep -m 1 '"input_tokens"' || true)"
|
|
138
|
+
[[ -z "$line" ]] && { echo "0 0 0 0"; return; }
|
|
139
|
+
i="$(echo "$line" | grep -oE '"input_tokens":[0-9]+' | head -1 | cut -d: -f2)"
|
|
140
|
+
o="$(echo "$line" | grep -oE '"output_tokens":[0-9]+' | head -1 | cut -d: -f2)"
|
|
141
|
+
cr="$(echo "$line" | grep -oE '"cache_read_input_tokens":[0-9]+' | head -1 | cut -d: -f2)"
|
|
142
|
+
cw="$(echo "$line" | grep -oE '"cache_creation_input_tokens":[0-9]+' | head -1 | cut -d: -f2)"
|
|
143
|
+
echo "${i:-0} ${o:-0} ${cr:-0} ${cw:-0}"
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
# Extract CUMULATIVE token counts from a session JSONL (for cost).
|
|
147
|
+
# Prints: "INPUT OUTPUT CACHE_READ CACHE_WRITE" (all integers).
|
|
148
|
+
_session_tokens() {
|
|
149
|
+
local file="$1"
|
|
150
|
+
[[ -f "$file" ]] || { echo "0 0 0 0"; return; }
|
|
151
|
+
awk '
|
|
152
|
+
BEGIN { i=0; o=0; cr=0; cw=0 }
|
|
153
|
+
{
|
|
154
|
+
while (match($0, /"input_tokens":[0-9]+/)) { i += substr($0, RSTART+15, RLENGTH-15); $0 = substr($0, RSTART+RLENGTH) }
|
|
155
|
+
while (match($0, /"output_tokens":[0-9]+/)) { o += substr($0, RSTART+16, RLENGTH-16); $0 = substr($0, RSTART+RLENGTH) }
|
|
156
|
+
while (match($0, /"cache_read_input_tokens":[0-9]+/)) { cr += substr($0, RSTART+26, RLENGTH-26); $0 = substr($0, RSTART+RLENGTH) }
|
|
157
|
+
while (match($0, /"cache_creation_input_tokens":[0-9]+/)) { cw += substr($0, RSTART+30, RLENGTH-30); $0 = substr($0, RSTART+RLENGTH) }
|
|
158
|
+
}
|
|
159
|
+
END { print i, o, cr, cw }
|
|
160
|
+
' "$file"
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
# Compute cost from tokens and a model alias.
|
|
164
|
+
# Usage: _compute_cost <model> <in> <out> <cache_r> <cache_w>
|
|
165
|
+
_compute_cost() {
|
|
166
|
+
local model="$1" in="$2" out="$3" cr="$4" cw="$5"
|
|
167
|
+
local prices
|
|
168
|
+
read -r p_in p_out p_cr p_cw <<<"$(_pricing "$model")"
|
|
169
|
+
awk -v i="$in" -v o="$out" -v cr="$cr" -v cw="$cw" \
|
|
170
|
+
-v pi="$p_in" -v po="$p_out" -v pcr="$p_cr" -v pcw="$p_cw" \
|
|
171
|
+
'BEGIN { printf "%.4f", (i*pi + o*po + cr*pcr + cw*pcw) / 1000000 }'
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
_read_main_model() {
|
|
175
|
+
# Best-effort read of the user's currently configured main model.
|
|
176
|
+
# Falls back to "sonnet" if not discoverable.
|
|
177
|
+
if [[ -f "$HOME/.claude/settings.json" ]] && command -v jq >/dev/null 2>&1; then
|
|
178
|
+
jq -r '.model // "sonnet"' "$HOME/.claude/settings.json" 2>/dev/null || echo "sonnet"
|
|
179
|
+
else
|
|
180
|
+
echo "sonnet"
|
|
181
|
+
fi
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
cmd_model() {
|
|
185
|
+
local phase="${1:?phase required}"
|
|
186
|
+
local pref="inherit"
|
|
187
|
+
[[ -f "$MODEL_PREF_FILE" ]] && pref="$(cat "$MODEL_PREF_FILE")"
|
|
188
|
+
|
|
189
|
+
# pinned:<model> — absolute override, no phase logic
|
|
190
|
+
if [[ "$pref" == pinned:* ]]; then
|
|
191
|
+
echo "${pref#pinned:}"
|
|
192
|
+
return
|
|
193
|
+
fi
|
|
194
|
+
|
|
195
|
+
local current
|
|
196
|
+
current="$(_read_main_model)"
|
|
197
|
+
|
|
198
|
+
case "$pref" in
|
|
199
|
+
inherit)
|
|
200
|
+
# Full respect — return the current main model regardless of phase
|
|
201
|
+
echo "$current"
|
|
202
|
+
;;
|
|
203
|
+
auto)
|
|
204
|
+
# Smart routing — opt-in cost optimization
|
|
205
|
+
case "$phase" in
|
|
206
|
+
sdd-propose|sdd-design|orchestrator)
|
|
207
|
+
# Architectural — prefer opus
|
|
208
|
+
echo "opus"
|
|
209
|
+
;;
|
|
210
|
+
sdd-archive)
|
|
211
|
+
# Copy-and-close — cheapest tier
|
|
212
|
+
echo "haiku"
|
|
213
|
+
;;
|
|
214
|
+
sdd-explore|sdd-spec|sdd-tasks|sdd-apply|sdd-verify|default)
|
|
215
|
+
# Mechanical / structural — sonnet is sufficient
|
|
216
|
+
echo "sonnet"
|
|
217
|
+
;;
|
|
218
|
+
*)
|
|
219
|
+
echo "$current"
|
|
220
|
+
;;
|
|
221
|
+
esac
|
|
222
|
+
;;
|
|
223
|
+
opus|sonnet|haiku)
|
|
224
|
+
# Explicit model as floor/default, no phase logic
|
|
225
|
+
echo "$pref"
|
|
226
|
+
;;
|
|
227
|
+
*)
|
|
228
|
+
# Unknown preference — safe fallback
|
|
229
|
+
echo "$current"
|
|
230
|
+
;;
|
|
231
|
+
esac
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
cmd_model_pref() {
|
|
235
|
+
mkdir -p "$(dirname "$MODEL_PREF_FILE")"
|
|
236
|
+
local action="${1:-get}"
|
|
237
|
+
case "$action" in
|
|
238
|
+
get)
|
|
239
|
+
if [[ -f "$MODEL_PREF_FILE" ]]; then
|
|
240
|
+
cat "$MODEL_PREF_FILE"
|
|
241
|
+
else
|
|
242
|
+
echo "inherit (default)"
|
|
243
|
+
fi
|
|
244
|
+
;;
|
|
245
|
+
set)
|
|
246
|
+
local value="${2:?value required — inherit | auto | opus | sonnet | haiku | pinned:<model>}"
|
|
247
|
+
case "$value" in
|
|
248
|
+
inherit|auto|opus|sonnet|haiku|pinned:*)
|
|
249
|
+
echo "$value" > "$MODEL_PREF_FILE"
|
|
250
|
+
echo "Model preference set: $value"
|
|
251
|
+
;;
|
|
252
|
+
*)
|
|
253
|
+
echo "ctk model-pref: invalid value '$value'" >&2
|
|
254
|
+
echo "valid: inherit | auto | opus | sonnet | haiku | pinned:<model>" >&2
|
|
255
|
+
exit 1
|
|
256
|
+
;;
|
|
257
|
+
esac
|
|
258
|
+
;;
|
|
259
|
+
clear)
|
|
260
|
+
rm -f "$MODEL_PREF_FILE"
|
|
261
|
+
echo "Model preference cleared (→ inherit)"
|
|
262
|
+
;;
|
|
263
|
+
*)
|
|
264
|
+
echo "ctk model-pref: unknown action '$action'" >&2
|
|
265
|
+
echo "usage: ctk model-pref [get | set <value> | clear]" >&2
|
|
266
|
+
exit 1
|
|
267
|
+
;;
|
|
268
|
+
esac
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
cmd_tokens() {
|
|
272
|
+
local chars
|
|
273
|
+
if [[ -n "${1:-}" ]]; then
|
|
274
|
+
chars=$(wc -c <"$1")
|
|
275
|
+
else
|
|
276
|
+
chars=$(wc -c)
|
|
277
|
+
fi
|
|
278
|
+
# Rough: ~4 chars per token (English). Conservative estimate.
|
|
279
|
+
echo $(( chars / 4 ))
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
cmd_cost() {
|
|
283
|
+
local quiet=0
|
|
284
|
+
[[ "${1:-}" == "--quiet" ]] && quiet=1
|
|
285
|
+
local session_file
|
|
286
|
+
session_file="$(_latest_session_file)" || {
|
|
287
|
+
(( quiet )) || echo "ctk cost: no session data"; return 0;
|
|
288
|
+
}
|
|
289
|
+
[[ -n "$session_file" ]] || { (( quiet )) || echo "ctk cost: no session files"; return 0; }
|
|
290
|
+
|
|
291
|
+
local tokens model in_tok out_tok cr_tok cw_tok cost
|
|
292
|
+
tokens="$(_session_tokens "$session_file")"
|
|
293
|
+
read -r in_tok out_tok cr_tok cw_tok <<<"$tokens"
|
|
294
|
+
model="$(_read_main_model)"
|
|
295
|
+
cost="$(_compute_cost "$model" "$in_tok" "$out_tok" "$cr_tok" "$cw_tok")"
|
|
296
|
+
|
|
297
|
+
if (( quiet )); then
|
|
298
|
+
echo "$cost"
|
|
299
|
+
else
|
|
300
|
+
printf 'Model: %s | In: %s | Out: %s | Cache R/W: %s / %s | Cost: $%s\n' \
|
|
301
|
+
"$model" "$in_tok" "$out_tok" "$cr_tok" "$cw_tok" "$cost"
|
|
302
|
+
fi
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
cmd_estimate() {
|
|
306
|
+
local src="${1:?file or - for stdin required}"
|
|
307
|
+
local text
|
|
308
|
+
if [[ "$src" == "-" ]]; then
|
|
309
|
+
text="$(cat)"
|
|
310
|
+
elif [[ -f "$src" ]]; then
|
|
311
|
+
text="$(cat "$src")"
|
|
312
|
+
else
|
|
313
|
+
echo "ctk estimate: not a file: $src (use '-' for stdin)" >&2
|
|
314
|
+
exit 1
|
|
315
|
+
fi
|
|
316
|
+
|
|
317
|
+
if [[ -n "${ANTHROPIC_API_KEY:-}" ]] && command -v curl >/dev/null 2>&1 && command -v jq >/dev/null 2>&1; then
|
|
318
|
+
# Use official count_tokens endpoint — faithful, model-agnostic for counting purposes
|
|
319
|
+
local body response tokens
|
|
320
|
+
body=$(jq -n --arg t "$text" '{
|
|
321
|
+
model: "claude-sonnet-4-5",
|
|
322
|
+
messages: [{role: "user", content: $t}]
|
|
323
|
+
}')
|
|
324
|
+
response=$(curl -sS -X POST https://api.anthropic.com/v1/messages/count_tokens \
|
|
325
|
+
-H "x-api-key: $ANTHROPIC_API_KEY" \
|
|
326
|
+
-H "anthropic-version: 2023-06-01" \
|
|
327
|
+
-H "content-type: application/json" \
|
|
328
|
+
-d "$body" 2>/dev/null)
|
|
329
|
+
tokens=$(echo "$response" | jq -r '.input_tokens // empty' 2>/dev/null)
|
|
330
|
+
if [[ -n "$tokens" ]]; then
|
|
331
|
+
# Also show estimated cost at current main model's input rate
|
|
332
|
+
local model p_in p_out p_cr p_cw cost
|
|
333
|
+
model="$(_read_main_model)"
|
|
334
|
+
read -r p_in p_out p_cr p_cw <<<"$(_pricing "$model")"
|
|
335
|
+
cost=$(awk -v t="$tokens" -v p="$p_in" 'BEGIN { printf "%.4f", t*p/1000000 }')
|
|
336
|
+
printf '%s tokens (exact, count_tokens API) | est. input cost: $%s (%s)\n' "$tokens" "$cost" "$model"
|
|
337
|
+
return
|
|
338
|
+
fi
|
|
339
|
+
echo "ctk estimate: API error, falling back to rough estimate" >&2
|
|
340
|
+
fi
|
|
341
|
+
|
|
342
|
+
local chars rough
|
|
343
|
+
chars=${#text}
|
|
344
|
+
rough=$(( chars / 4 ))
|
|
345
|
+
echo "$rough tokens (rough — set ANTHROPIC_API_KEY for exact count)"
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
cmd_context() {
|
|
349
|
+
local session_file
|
|
350
|
+
session_file="$(_latest_session_file)" || {
|
|
351
|
+
echo "ctk context: no Claude Code session data"; return 0;
|
|
352
|
+
}
|
|
353
|
+
[[ -n "$session_file" ]] || { echo "ctk context: no session files"; return 0; }
|
|
354
|
+
|
|
355
|
+
# Current window = last turn's (input + cache_read + output). NOT cumulative.
|
|
356
|
+
local li lo lcr lcw window pct
|
|
357
|
+
read -r li lo lcr lcw <<<"$(_session_latest_usage "$session_file")"
|
|
358
|
+
window=$(( li + lcr + lo ))
|
|
359
|
+
pct=$(( window * 100 / 200000 ))
|
|
360
|
+
|
|
361
|
+
# Cumulative cost (all turns weighted by price)
|
|
362
|
+
local ci co ccr ccw model cost
|
|
363
|
+
read -r ci co ccr ccw <<<"$(_session_tokens "$session_file")"
|
|
364
|
+
model="$(_read_main_model)"
|
|
365
|
+
cost="$(_compute_cost "$model" "$ci" "$co" "$ccr" "$ccw")"
|
|
366
|
+
|
|
367
|
+
printf 'Session: %s\n' "$(basename "$session_file")"
|
|
368
|
+
printf 'Window: %s tokens (~%d%% of 200k) — last turn: in=%s cache_r=%s out=%s\n' \
|
|
369
|
+
"$window" "$pct" "$li" "$lcr" "$lo"
|
|
370
|
+
printf 'Session: in=%s out=%s cache_r=%s cache_w=%s (cumulative)\n' "$ci" "$co" "$ccr" "$ccw"
|
|
371
|
+
printf 'Cost: $%s (%s)\n' "$cost" "$model"
|
|
372
|
+
if (( pct >= 70 )); then
|
|
373
|
+
echo "Advice: /compact (continuing) or /clear (new task)"
|
|
374
|
+
fi
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
main() {
|
|
378
|
+
local cmd="${1:-help}"
|
|
379
|
+
shift || true
|
|
380
|
+
case "$cmd" in
|
|
381
|
+
slice) cmd_slice "$@" ;;
|
|
382
|
+
git-log) cmd_git_log "$@" ;;
|
|
383
|
+
git-changed) cmd_git_changed "$@" ;;
|
|
384
|
+
test-summary) cmd_test_summary "$@" ;;
|
|
385
|
+
find) cmd_find "$@" ;;
|
|
386
|
+
model) cmd_model "$@" ;;
|
|
387
|
+
model-pref) cmd_model_pref "$@" ;;
|
|
388
|
+
tokens) cmd_tokens "$@" ;;
|
|
389
|
+
estimate) cmd_estimate "$@" ;;
|
|
390
|
+
cost) cmd_cost "$@" ;;
|
|
391
|
+
context) cmd_context "$@" ;;
|
|
392
|
+
version|-v|--version) echo "ctk $VERSION" ;;
|
|
393
|
+
help|-h|--help|"") usage ;;
|
|
394
|
+
*) echo "ctk: unknown command: $cmd" >&2; usage; exit 1 ;;
|
|
395
|
+
esac
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
main "$@"
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
<!-- claude-master-toolkit: managed file -->
|
|
2
|
+
<!-- Edit source at ~/Documentos/Coding/claude-master-toolkit/claude/CLAUDE.md -->
|
|
3
|
+
|
|
4
|
+
## Rules
|
|
5
|
+
|
|
6
|
+
- Never add "Co-Authored-By" or AI attribution to commits. Use conventional commits only, add end prefix specify (autommated by AI).
|
|
7
|
+
- Never build after changes.
|
|
8
|
+
- When asking a question, STOP and wait for response. Never continue or assume answers.
|
|
9
|
+
- Never agree with user claims without verification. Say "let me verify" and check code/docs first.
|
|
10
|
+
- If user is wrong, explain WHY with evidence. If you were wrong, acknowledge with proof.
|
|
11
|
+
- Always propose alternatives with tradeoffs when relevant.
|
|
12
|
+
- Verify technical claims before stating them. If unsure, investigate first.
|
|
13
|
+
|
|
14
|
+
## Personality
|
|
15
|
+
|
|
16
|
+
Senior Architect, 15+ years experience, GDE & MVP. Passionate teacher who genuinely wants people to learn and grow. Gets frustrated when someone can do better but isn't — not out of anger, but because you CARE about their growth.
|
|
17
|
+
|
|
18
|
+
## Language
|
|
19
|
+
|
|
20
|
+
- Always respond in the same language the user writes in.
|
|
21
|
+
- Use a warm, professional, and direct tone. No slang, no regional expressions.
|
|
22
|
+
|
|
23
|
+
## Tone
|
|
24
|
+
|
|
25
|
+
Passionate and direct, but from a place of CARING. When someone is wrong: (1) validate the question makes sense, (2) explain WHY it's wrong with technical reasoning, (3) show the correct way with examples. Frustration comes from caring they can do better. Use CAPS for emphasis.
|
|
26
|
+
|
|
27
|
+
## Philosophy
|
|
28
|
+
|
|
29
|
+
- CONCEPTS > CODE: call out people who code without understanding fundamentals
|
|
30
|
+
- AI IS A TOOL: we direct, AI executes; the human always leads
|
|
31
|
+
- SOLID FOUNDATIONS: design patterns, architecture, bundlers before frameworks
|
|
32
|
+
- AGAINST IMMEDIACY: no shortcuts; real learning takes effort and time
|
|
33
|
+
|
|
34
|
+
## Expertise
|
|
35
|
+
|
|
36
|
+
Clean/Hexagonal/Screaming Architecture, testing, atomic design, container-presentational pattern, LazyVim, Tmux, Zellij.
|
|
37
|
+
|
|
38
|
+
## Behavior
|
|
39
|
+
|
|
40
|
+
- Push back when user asks for code without context or understanding
|
|
41
|
+
- Use construction/architecture analogies to explain concepts
|
|
42
|
+
- Correct errors ruthlessly but explain WHY technically
|
|
43
|
+
- For concepts: (1) explain problem, (2) propose solution with examples, (3) mention tools/resources
|
|
44
|
+
|
|
45
|
+
## Skills (Auto-load based on context)
|
|
46
|
+
|
|
47
|
+
Load BEFORE writing code. Multiple skills can apply simultaneously.
|
|
48
|
+
|
|
49
|
+
| Context | Skill |
|
|
50
|
+
| ---------------------------------------------------------- | ------------------------------------------------- |
|
|
51
|
+
| Go tests, Bubbletea TUI testing | go-testing |
|
|
52
|
+
| Creating new AI skills | skill-creator |
|
|
53
|
+
| SDD meta-commands (`/sdd-new`, `/sdd-ff`, `/sdd-continue`) | delegates to `sdd-orchestrator` sub-agent |
|
|
54
|
+
| Any `/sdd-*` executor phase | existing SDD skill (sdd-explore, sdd-apply, etc.) |
|
|
55
|
+
| Delegating work to sub-agents | `/delegate` (enforces `ctk model` + context check)|
|
|
56
|
+
|
|
57
|
+
## Persistent Memory (Engram)
|
|
58
|
+
|
|
59
|
+
Engram is a plugin — its SessionStart hook injects the full protocol automatically. You do NOT need to memorize rules here. Just remember the principle:
|
|
60
|
+
|
|
61
|
+
**SAVE PROACTIVELY** after any decision, bug fix, convention, discovery, preference, or non-obvious pattern. Do not wait to be asked. On recall requests ("recordá", "qué hicimos"), search memory before answering.
|
|
62
|
+
|
|
63
|
+
Full rules and templates live in the `engram-protocol` skill if you ever need to re-read them.
|
|
64
|
+
|
|
65
|
+
## Model Selection Layer
|
|
66
|
+
|
|
67
|
+
**MANDATORY:** Before ANY Agent tool call, run `ctk model <phase>` and pass the result as the `model` parameter. Prefer using `/delegate` which enforces this automatically.
|
|
68
|
+
|
|
69
|
+
- `inherit` (default) — sub-agents use the same model as the main conversation.
|
|
70
|
+
- `auto` — smart routing: Opus for architectural phases, Haiku for archive, inherit otherwise.
|
|
71
|
+
- `pinned:<model>` — absolute override, never deviates.
|
|
72
|
+
|
|
73
|
+
If `ctk` is unavailable, inherit the main model. Never impose a model the user did not choose.
|
|
74
|
+
|
|
75
|
+
## Context Guardian
|
|
76
|
+
|
|
77
|
+
Hooks in `~/.claude/hooks/` warn you when context window usage crosses 70/85/95% thresholds. When a warning fires, recommend:
|
|
78
|
+
|
|
79
|
+
- **`/compact`** if the work is coherent and you want to keep the thread alive
|
|
80
|
+
- **`/clear`** if the new task is unrelated to the accumulated context
|
|
81
|
+
|
|
82
|
+
Cache TTL (5 min) is NOT context expiry — never recommend clearing for "inactivity".
|
|
83
|
+
|
|
84
|
+
## Strict TDD Mode
|
|
85
|
+
|
|
86
|
+
Enabled.
|