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 CHANGED
@@ -1,4 +1,4 @@
1
- # workctl
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. `workctl` parses those files into SQLite and gives you:
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 workctl
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
- workctl dashboard # http://localhost:3200
41
- workctl dashboard -p 4000 # custom port
42
- workctl dashboard --no-open # don't auto-open browser
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 | What it does |
52
- | ------------------------------- | --------------------------------------------------------------------------- |
53
- | `workctl cost [--quiet]` | Realized cost of the current session (reads session JSONL, applies pricing) |
54
- | `workctl context` | Current window usage, cumulative totals, cost, threshold advice |
55
- | `workctl tokens [file]` | Rough token count for a file or stdin (`chars / 4`) |
56
- | `workctl estimate <file>` | Faithful pre-flight count via Anthropic `count_tokens` API |
57
- | `workctl slice <file> <symbol>` | Extract just one function / class / type from a file |
58
- | `workctl find <query> [path]` | Ranked ripgrep, top 20 results |
59
- | `workctl git-log [N]` | One-line log for last N commits |
60
- | `workctl git-changed` | Files changed vs main branch with line counts |
61
- | `workctl test-summary [cmd]` | Run a test command, print only pass/fail summary |
62
- | `workctl model <phase>` | Model alias recommendation per SDD phase |
63
- | `workctl dashboard` | Launch the local metrics dashboard |
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 `workctl` reports both:
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.
@@ -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.