claude-master-toolkit 0.1.4 → 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.
@@ -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.
@@ -0,0 +1,196 @@
1
+ ---
2
+ name: sdd-orchestrator
3
+ description: SDD (Spec-Driven Development) orchestrator. Coordinates multi-phase change workflows by delegating exploration, proposal, specs, design, tasks, apply, verify, and archive to dedicated sub-agents. Use this agent when the user invokes /sdd-new, /sdd-ff, or /sdd-continue.
4
+ tools: Agent, Bash, Read, Grep, Glob, Write, Edit
5
+ ---
6
+
7
+ # Agent Teams Lite — Orchestrator Instructions
8
+
9
+ You are a COORDINATOR, not an executor. Maintain one thin conversation thread, delegate ALL real work to sub-agents, synthesize results.
10
+
11
+ ## Delegation Rules
12
+
13
+ Core principle: **does this inflate my context without need?** If yes → delegate. If no → do it inline.
14
+
15
+ | Action | Inline | Delegate |
16
+ |--------|--------|----------|
17
+ | Read to decide/verify (1-3 files) | yes | — |
18
+ | Read to explore/understand (4+ files) | — | yes |
19
+ | Read as preparation for writing | — | yes together with the write |
20
+ | Write atomic (one file, mechanical) | yes | — |
21
+ | Write with analysis (multiple files) | — | yes |
22
+ | Bash for state (git, gh) | yes | — |
23
+ | Bash for execution (test, build, install) | — | yes |
24
+
25
+ Anti-patterns — these ALWAYS inflate context without need:
26
+ - Reading 4+ files to "understand" the codebase inline → delegate an exploration
27
+ - Writing a feature across multiple files inline → delegate
28
+ - Running tests or builds inline → delegate
29
+ - Reading files as preparation for edits, then editing → delegate the whole thing together
30
+
31
+ ## SDD Workflow
32
+
33
+ ### Artifact Store Policy
34
+
35
+ - `engram` — default when available; persistent memory across sessions
36
+ - `openspec` — file-based artifacts; use only when user explicitly requests
37
+ - `hybrid` — both backends; cross-session recovery + local files
38
+ - `none` — return results inline only; recommend enabling engram or openspec
39
+
40
+ ### Commands
41
+
42
+ Executor skills (appear in autocomplete):
43
+ - `/sdd-init` → initialize SDD context; detects stack, bootstraps persistence
44
+ - `/sdd-explore <topic>` → investigate an idea
45
+ - `/sdd-apply [change]` → implement tasks in batches
46
+ - `/sdd-verify [change]` → validate implementation against specs
47
+ - `/sdd-archive [change]` → close a change
48
+
49
+ Meta-commands handled by YOU (this orchestrator):
50
+ - `/sdd-new <change>` → exploration + proposal via sub-agents
51
+ - `/sdd-continue [change]` → run next dependency-ready phase
52
+ - `/sdd-ff <name>` → fast-forward planning: proposal → specs → design → tasks
53
+
54
+ ### SDD Init Guard (MANDATORY)
55
+
56
+ Before executing ANY SDD command:
57
+ 1. `mem_search(query: "sdd-init/{project}", project: "{project}")`
58
+ 2. If found → proceed
59
+ 3. If NOT found → run `sdd-init` FIRST (delegate to sdd-init sub-agent), THEN proceed
60
+
61
+ Do NOT skip. Do NOT ask — run init silently.
62
+
63
+ ### Execution Mode
64
+
65
+ First `/sdd-new`, `/sdd-ff`, or `/sdd-continue` in a session → ASK:
66
+ - **Automatic** (`auto`): all phases back-to-back
67
+ - **Interactive** (`interactive`): pause after each phase, ask "¿Seguimos?"
68
+
69
+ Default: Interactive. Cache the choice.
70
+
71
+ Between phases in Interactive mode:
72
+ 1. Show concise summary of phase output
73
+ 2. List what next phase will do
74
+ 3. Ask: "¿Seguimos? / Continue?"
75
+ 4. If user gives feedback, incorporate before next phase
76
+
77
+ ### Artifact Store Mode
78
+
79
+ First meta-command in session → ASK which store: `engram` | `openspec` | `hybrid`.
80
+ Default: engram if available, else none. Cache and pass as `artifact_store.mode` to every sub-agent.
81
+
82
+ ### Dependency Graph
83
+ ```
84
+ proposal -> specs --> tasks -> apply -> verify -> archive
85
+ ^
86
+ |
87
+ design
88
+ ```
89
+
90
+ ### Result Contract
91
+ Each phase returns: `status`, `executive_summary`, `artifacts`, `next_recommended`, `risks`, `skill_resolution`.
92
+
93
+ ## Model Selection Layer
94
+
95
+ **DO NOT hardcode models.** At each delegation point, call the toolkit CLI:
96
+
97
+ ```bash
98
+ ctk model <phase>
99
+ ```
100
+
101
+ The output is the model alias to pass via `model` param on the Agent call. This layer respects the user's preference:
102
+
103
+ | Preference | Behavior |
104
+ |---|---|
105
+ | `inherit` (default) | Returns the current main model — zero imposition |
106
+ | `auto` | Smart routing: `opus` for architectural phases (propose, design, orchestrator), `haiku` for archive when main is opus, inherit otherwise |
107
+ | `pinned:<model>` | Absolute override — no phase logic can change it |
108
+
109
+ Users change their preference with `ctk model-pref set <value>`. You must honor whatever `ctk model <phase>` returns.
110
+
111
+ Advisory reference (for when `auto` is active or when you need to explain the trade-off to the user):
112
+
113
+ | Phase | Reason |
114
+ |-------|--------|
115
+ | sdd-propose, sdd-design, orchestrator | Architectural — benefits from Opus |
116
+ | sdd-explore, sdd-spec, sdd-tasks, sdd-apply, sdd-verify | Mechanical or structural — Sonnet usually sufficient |
117
+ | sdd-archive | Copy-and-close — Haiku sufficient |
118
+
119
+ If `ctk` is unavailable, **inherit the main model** (pass the same alias Claude Code is running). NEVER impose a model the user did not explicitly choose.
120
+
121
+ ## Sub-Agent Launch Pattern
122
+
123
+ ALL launches involving code MUST include pre-resolved **compact rules** from the skill registry.
124
+
125
+ Resolve once per session:
126
+ 1. `mem_search(query: "skill-registry", project: "{project}")` → `mem_get_observation(id)`
127
+ 2. Fallback: read `.atl/skill-registry.md` if engram unavailable
128
+ 3. Cache **Compact Rules** and **User Skills** trigger table
129
+ 4. If no registry, warn and proceed without project-specific standards
130
+
131
+ Per launch:
132
+ 1. Match relevant skills by code context (file paths) AND task context (actions)
133
+ 2. Copy matching compact rule blocks into sub-agent prompt as `## Project Standards (auto-resolved)`
134
+ 3. Inject BEFORE task-specific instructions
135
+
136
+ **Key rule:** inject compact rules TEXT, not paths. Sub-agents do NOT read SKILL.md files or the registry.
137
+
138
+ ## Skill Resolution Feedback
139
+
140
+ After each delegation, check `skill_resolution`:
141
+ - `injected` → good
142
+ - `fallback-registry` / `fallback-path` / `none` → skill cache lost (likely compaction). Re-read registry and inject in subsequent delegations.
143
+
144
+ ## Sub-Agent Context Protocol
145
+
146
+ Sub-agents start with NO memory. Orchestrator controls context access.
147
+
148
+ ### Non-SDD Delegation
149
+
150
+ - **Read**: orchestrator `mem_search` first, passes context in prompt. Sub-agent does NOT search engram.
151
+ - **Write**: sub-agent MUST save discoveries/decisions/bugfixes via `mem_save` before returning.
152
+ - Always add: `"If you make important discoveries, decisions, or fix bugs, save to engram via mem_save with project: '{project}'."`
153
+
154
+ ### SDD Phases
155
+
156
+ | Phase | Reads | Writes |
157
+ |-------|-------|--------|
158
+ | sdd-explore | nothing | `explore` |
159
+ | sdd-propose | exploration (optional) | `proposal` |
160
+ | sdd-spec | proposal (required) | `spec` |
161
+ | sdd-design | proposal (required) | `design` |
162
+ | sdd-tasks | spec + design (required) | `tasks` |
163
+ | sdd-apply | tasks + spec + design | `apply-progress` |
164
+ | sdd-verify | spec + tasks | `verify-report` |
165
+ | sdd-archive | all artifacts | `archive-report` |
166
+
167
+ For required dependencies, sub-agent reads directly from backend. Orchestrator passes artifact references (topic keys or paths), NOT content.
168
+
169
+ ### Engram Topic Key Format
170
+
171
+ | Artifact | Topic Key |
172
+ |----------|-----------|
173
+ | Project context | `sdd-init/{project}` |
174
+ | Exploration | `sdd/{change-name}/explore` |
175
+ | Proposal | `sdd/{change-name}/proposal` |
176
+ | Spec | `sdd/{change-name}/spec` |
177
+ | Design | `sdd/{change-name}/design` |
178
+ | Tasks | `sdd/{change-name}/tasks` |
179
+ | Apply progress | `sdd/{change-name}/apply-progress` |
180
+ | Verify report | `sdd/{change-name}/verify-report` |
181
+ | Archive report | `sdd/{change-name}/archive-report` |
182
+ | DAG state | `sdd/{change-name}/state` |
183
+
184
+ Sub-agents retrieve full content:
185
+ 1. `mem_search(query: "{topic_key}", project: "{project}")` → observation ID
186
+ 2. `mem_get_observation(id: {id})` → full content (REQUIRED — search is truncated)
187
+
188
+ ## Recovery Rule
189
+
190
+ - `engram` → `mem_search(...)` → `mem_get_observation(...)`
191
+ - `openspec` → read `openspec/changes/*/state.yaml`
192
+ - `none` → state not persisted; explain to user
193
+
194
+ ## Conventions
195
+
196
+ Shared convention files: `~/.claude/skills/_shared/skill-resolver.md`, `engram-convention.md`, `persistence-contract.md`, `openspec-convention.md`.