oh-my-customcode 0.19.4 → 0.20.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/README.md CHANGED
@@ -124,7 +124,7 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
124
124
  | **QA** | 3 | qa-planner, qa-writer, qa-engineer |
125
125
  | **Total** | **41** | |
126
126
 
127
- ### Skills (56)
127
+ ### Skills (58)
128
128
 
129
129
  | Category | Count | Skills |
130
130
  |----------|-------|--------|
@@ -137,7 +137,7 @@ Claude Code selects the appropriate model and parallelizes independent tasks (up
137
137
  | **Package Management** | 3 | npm-publish, npm-version, npm-audit |
138
138
  | **Operations** | 7 | update-docs, update-external, audit-agents, fix-refs, sauron-watch, monitoring-setup, claude-code-bible |
139
139
  | **Utilities** | 5 | lists, help, status, result-aggregation, writing-clearly-and-concisely |
140
- | **Quality & Workflow** | 2 | multi-model-verification, structured-dev-cycle |
140
+ | **Quality & Workflow** | 4 | multi-model-verification, structured-dev-cycle, model-escalation, stuck-recovery |
141
141
  | **Deploy** | 2 | vercel-deploy, codex-exec |
142
142
  | **External** | 1 | skills-sh-search |
143
143
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-customcode",
3
- "version": "0.19.4",
3
+ "version": "0.20.0",
4
4
  "description": "Batteries-included agent harness for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {
@@ -76,6 +76,10 @@
76
76
  {
77
77
  "type": "command",
78
78
  "command": "bash .claude/hooks/scripts/agent-teams-advisor.sh"
79
+ },
80
+ {
81
+ "type": "command",
82
+ "command": "bash .claude/hooks/scripts/model-escalation-advisor.sh"
79
83
  }
80
84
  ],
81
85
  "description": "HUD statusline + R010 git delegation guard + R018 Agent Teams advisor on Task spawn"
@@ -163,6 +167,26 @@
163
167
  }
164
168
  ],
165
169
  "description": "Type check Python files with ty after edits"
170
+ },
171
+ {
172
+ "matcher": "tool == \"Task\"",
173
+ "hooks": [
174
+ {
175
+ "type": "command",
176
+ "command": "bash .claude/hooks/scripts/task-outcome-recorder.sh"
177
+ }
178
+ ],
179
+ "description": "Record task outcomes (success/failure) for model escalation decisions"
180
+ },
181
+ {
182
+ "matcher": "tool == \"Edit\" || tool == \"Write\" || tool == \"Bash\" || tool == \"Task\"",
183
+ "hooks": [
184
+ {
185
+ "type": "command",
186
+ "command": "bash .claude/hooks/scripts/stuck-detector.sh"
187
+ }
188
+ ],
189
+ "description": "Detect repetitive failure loops and advise recovery strategies"
166
190
  }
167
191
  ],
168
192
  "Stop": [
@@ -0,0 +1,103 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # Model Escalation Advisor Hook
5
+ # Trigger: PreToolUse, tool == "Task"
6
+ # Purpose: Advise model escalation when failure patterns detected
7
+ # Protocol: stdin JSON -> process -> stdout pass-through, exit 0 always
8
+
9
+ input=$(cat)
10
+
11
+ # Extract current task info
12
+ agent_type=$(echo "$input" | jq -r '.tool_input.subagent_type // "unknown"')
13
+ current_model=$(echo "$input" | jq -r '.tool_input.model // "inherit"')
14
+
15
+ # Session-scoped outcome log
16
+ OUTCOME_FILE="/tmp/.claude-task-outcomes-${PPID}"
17
+
18
+ # Skip if no history
19
+ if [ ! -f "$OUTCOME_FILE" ]; then
20
+ echo "$input"
21
+ exit 0
22
+ fi
23
+
24
+ # Thresholds
25
+ FAILURE_THRESHOLD=2
26
+ CONSECUTIVE_THRESHOLD=3
27
+ COOLDOWN=5
28
+
29
+ # Count failures for this agent type
30
+ agent_failures=0
31
+ if [ -n "$agent_type" ] && [ "$agent_type" != "unknown" ]; then
32
+ agent_failures=$(grep -c "\"agent_type\":\"${agent_type}\".*\"outcome\":\"failure\"" "$OUTCOME_FILE" 2>/dev/null || echo "0")
33
+ fi
34
+
35
+ # Count consecutive failures (tail entries)
36
+ consecutive_failures=$(tail -${CONSECUTIVE_THRESHOLD} "$OUTCOME_FILE" 2>/dev/null | grep -c '"outcome":"failure"' 2>/dev/null || echo "0")
37
+
38
+ # Escalation path
39
+ next_model=""
40
+ cost_multiplier=""
41
+ case "$current_model" in
42
+ haiku)
43
+ next_model="sonnet"
44
+ cost_multiplier="~3-5x"
45
+ ;;
46
+ sonnet)
47
+ next_model="opus"
48
+ cost_multiplier="~5-10x"
49
+ ;;
50
+ *)
51
+ next_model=""
52
+ ;;
53
+ esac
54
+
55
+ # Advise escalation
56
+ if [ -n "$next_model" ]; then
57
+ should_advise=false
58
+ reason=""
59
+
60
+ if [ "$agent_failures" -ge "$FAILURE_THRESHOLD" ]; then
61
+ should_advise=true
62
+ reason="${agent_type} failed ${agent_failures}x with ${current_model}"
63
+ elif [ "$consecutive_failures" -ge "$CONSECUTIVE_THRESHOLD" ]; then
64
+ should_advise=true
65
+ reason="${consecutive_failures} consecutive failures"
66
+ fi
67
+
68
+ if [ "$should_advise" = true ]; then
69
+ echo "" >&2
70
+ echo "--- [Model Escalation Advisory] ---" >&2
71
+ echo " Agent type: ${agent_type}" >&2
72
+ echo " Current model: ${current_model}" >&2
73
+ echo " ⚡ Recommended: Escalate to ${next_model}" >&2
74
+ echo " Cost impact: ${cost_multiplier} per task" >&2
75
+ echo " Reason: ${reason}" >&2
76
+ echo "------------------------------------" >&2
77
+ fi
78
+ fi
79
+
80
+ # De-escalation check
81
+ if [ "$current_model" != "haiku" ] && [ "$current_model" != "inherit" ] && [ "$current_model" != "" ]; then
82
+ recent_successes=$(tail -${COOLDOWN} "$OUTCOME_FILE" 2>/dev/null | grep -c '"outcome":"success"' 2>/dev/null || echo "0")
83
+
84
+ if [ "$recent_successes" -ge "$COOLDOWN" ]; then
85
+ lower_model=""
86
+ case "$current_model" in
87
+ opus) lower_model="sonnet" ;;
88
+ sonnet) lower_model="haiku" ;;
89
+ esac
90
+
91
+ if [ -n "$lower_model" ]; then
92
+ echo "" >&2
93
+ echo "--- [Model De-escalation Advisory] ---" >&2
94
+ echo " ↓ Consider: ${current_model} → ${lower_model}" >&2
95
+ echo " ${recent_successes} consecutive successes" >&2
96
+ echo "--------------------------------------" >&2
97
+ fi
98
+ fi
99
+ fi
100
+
101
+ # Pass through
102
+ echo "$input"
103
+ exit 0
@@ -0,0 +1,125 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # Stuck Detector Hook
5
+ # Trigger: PostToolUse, tool matches "Edit|Write|Bash|Task"
6
+ # Purpose: Detect repetitive failure loops and advise recovery
7
+ # Protocol: stdin JSON -> process -> stdout pass-through, exit 0 always
8
+
9
+ input=$(cat)
10
+
11
+ # Extract tool info
12
+ tool_name=$(echo "$input" | jq -r '.tool_name // "unknown"')
13
+ file_path=$(echo "$input" | jq -r '.tool_input.file_path // .tool_input.command // ""' | head -c 120)
14
+ is_error=$(echo "$input" | jq -r '.tool_output.is_error // false')
15
+ output_preview=$(echo "$input" | jq -r '.tool_output.output // ""' | head -c 200)
16
+
17
+ # Session-scoped history
18
+ HISTORY_FILE="/tmp/.claude-tool-history-${PPID}"
19
+
20
+ # Create entry
21
+ timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
22
+
23
+ # Generate error hash for deduplication (first 50 chars of error)
24
+ error_hash=""
25
+ if [ "$is_error" = "true" ]; then
26
+ error_hash=$(echo "$output_preview" | head -c 50 | md5sum 2>/dev/null | cut -d' ' -f1 || echo "unknown")
27
+ fi
28
+
29
+ entry=$(jq -n \
30
+ --arg ts "$timestamp" \
31
+ --arg tool "$tool_name" \
32
+ --arg path "$file_path" \
33
+ --arg err "$is_error" \
34
+ --arg hash "$error_hash" \
35
+ --arg preview "$output_preview" \
36
+ '{timestamp: $ts, tool: $tool, path: $path, is_error: $err, error_hash: $hash, preview: $preview}')
37
+
38
+ echo "$entry" >> "$HISTORY_FILE"
39
+
40
+ # Ring buffer: keep last 100 entries
41
+ if [ -f "$HISTORY_FILE" ]; then
42
+ line_count=$(wc -l < "$HISTORY_FILE")
43
+ if [ "$line_count" -gt 100 ]; then
44
+ tail -100 "$HISTORY_FILE" > "${HISTORY_FILE}.tmp"
45
+ mv "${HISTORY_FILE}.tmp" "$HISTORY_FILE"
46
+ fi
47
+ fi
48
+
49
+ # --- Detection Logic ---
50
+
51
+ # Only check for patterns if we have enough history
52
+ if [ ! -f "$HISTORY_FILE" ]; then
53
+ echo "$input"
54
+ exit 0
55
+ fi
56
+
57
+ recent_count=$(wc -l < "$HISTORY_FILE")
58
+ if [ "$recent_count" -lt 3 ]; then
59
+ echo "$input"
60
+ exit 0
61
+ fi
62
+
63
+ stuck_detected=false
64
+ signal_type=""
65
+ pattern_desc=""
66
+ occurrence_count=0
67
+ threshold=0
68
+ recovery=""
69
+
70
+ # Signal 1: Repeated error (same error_hash 3+ times in last 10 entries)
71
+ if [ "$is_error" = "true" ] && [ -n "$error_hash" ]; then
72
+ error_repeat=$(tail -10 "$HISTORY_FILE" | grep -c "\"error_hash\":\"${error_hash}\"" 2>/dev/null || echo "0")
73
+ if [ "$error_repeat" -ge 3 ]; then
74
+ stuck_detected=true
75
+ signal_type="Repeated error"
76
+ pattern_desc="Same error appeared ${error_repeat} times in last 10 tool calls"
77
+ occurrence_count=$error_repeat
78
+ threshold=3
79
+ recovery="Rephrase the task or try a different approach"
80
+ fi
81
+ fi
82
+
83
+ # Signal 2: Edit loop (same file edited 3+ times in last 8 entries)
84
+ if [ "$stuck_detected" = false ] && { [ "$tool_name" = "Edit" ] || [ "$tool_name" = "Write" ]; }; then
85
+ if [ -n "$file_path" ]; then
86
+ escaped_path=$(echo "$file_path" | sed 's/[.[\*^$()+?{|]/\\&/g')
87
+ edit_repeat=$(tail -8 "$HISTORY_FILE" | grep -c "\"path\":\"${escaped_path}\"" 2>/dev/null || echo "0")
88
+ if [ "$edit_repeat" -ge 3 ]; then
89
+ stuck_detected=true
90
+ signal_type="Edit loop"
91
+ pattern_desc="$(basename "$file_path") edited ${edit_repeat} times in last 8 calls"
92
+ occurrence_count=$edit_repeat
93
+ threshold=3
94
+ recovery="Try a different file or approach instead of re-editing"
95
+ fi
96
+ fi
97
+ fi
98
+
99
+ # Signal 3: Tool spam (same tool 5+ times in last 8 entries)
100
+ if [ "$stuck_detected" = false ]; then
101
+ tool_repeat=$(tail -8 "$HISTORY_FILE" | grep -c "\"tool\":\"${tool_name}\"" 2>/dev/null || echo "0")
102
+ if [ "$tool_repeat" -ge 5 ]; then
103
+ stuck_detected=true
104
+ signal_type="Tool loop"
105
+ pattern_desc="${tool_name} called ${tool_repeat} times in last 8 calls"
106
+ occurrence_count=$tool_repeat
107
+ threshold=5
108
+ recovery="Step back and reconsider the approach"
109
+ fi
110
+ fi
111
+
112
+ # Output advisory if stuck detected
113
+ if [ "$stuck_detected" = true ]; then
114
+ echo "" >&2
115
+ echo "--- [Stuck Detection] Loop detected ---" >&2
116
+ echo " Signal: ${signal_type}" >&2
117
+ echo " Pattern: ${pattern_desc}" >&2
118
+ echo " Occurrences: ${occurrence_count}/${threshold}" >&2
119
+ echo " 💡 Recovery: ${recovery}" >&2
120
+ echo "----------------------------------------" >&2
121
+ fi
122
+
123
+ # Pass through
124
+ echo "$input"
125
+ exit 0
@@ -0,0 +1,63 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+
4
+ # Task Outcome Recorder Hook
5
+ # Trigger: PostToolUse, tool == "Task"
6
+ # Purpose: Record task outcomes for model escalation decisions
7
+ # Protocol: stdin JSON -> process -> stdout pass-through, exit 0 always
8
+
9
+ input=$(cat)
10
+
11
+ # Extract task info
12
+ agent_type=$(echo "$input" | jq -r '.tool_input.subagent_type // "unknown"')
13
+ model=$(echo "$input" | jq -r '.tool_input.model // "inherit"')
14
+ description=$(echo "$input" | jq -r '.tool_input.description // ""' | head -c 80)
15
+
16
+ # Determine outcome
17
+ is_error=$(echo "$input" | jq -r '.tool_output.is_error // false')
18
+
19
+ if [ "$is_error" = "true" ]; then
20
+ outcome="failure"
21
+ error_summary=$(echo "$input" | jq -r '.tool_output.output // ""' | head -c 200)
22
+ else
23
+ outcome="success"
24
+ error_summary=""
25
+ fi
26
+
27
+ # Session-scoped outcome log
28
+ OUTCOME_FILE="/tmp/.claude-task-outcomes-${PPID}"
29
+
30
+ # Append JSON line entry
31
+ timestamp=$(date -u +%Y-%m-%dT%H:%M:%SZ)
32
+ entry=$(jq -n \
33
+ --arg ts "$timestamp" \
34
+ --arg agent "$agent_type" \
35
+ --arg model "$model" \
36
+ --arg outcome "$outcome" \
37
+ --arg desc "$description" \
38
+ --arg err "$error_summary" \
39
+ '{timestamp: $ts, agent_type: $agent, model: $model, outcome: $outcome, description: $desc, error_summary: $err}')
40
+
41
+ echo "$entry" >> "$OUTCOME_FILE"
42
+
43
+ # Ring buffer: keep last 50 entries
44
+ if [ -f "$OUTCOME_FILE" ]; then
45
+ line_count=$(wc -l < "$OUTCOME_FILE")
46
+ if [ "$line_count" -gt 50 ]; then
47
+ tail -50 "$OUTCOME_FILE" > "${OUTCOME_FILE}.tmp"
48
+ mv "${OUTCOME_FILE}.tmp" "$OUTCOME_FILE"
49
+ fi
50
+ fi
51
+
52
+ # Report failures to stderr
53
+ if [ "$outcome" = "failure" ]; then
54
+ echo "" >&2
55
+ echo "--- [Task Outcome] FAILURE: ${agent_type}:${model} ---" >&2
56
+ echo " ${description}" >&2
57
+ echo " Error: $(echo "$error_summary" | head -c 100)" >&2
58
+ echo "-----------------------------------------------" >&2
59
+ fi
60
+
61
+ # Pass through
62
+ echo "$input"
63
+ exit 0
@@ -26,8 +26,22 @@ source: # For external agents
26
26
  origin: github | npm
27
27
  url: https://...
28
28
  version: 1.0.0
29
+ escalation: # Model escalation policy (optional)
30
+ enabled: true # Enable auto-escalation advisory
31
+ path: haiku → sonnet → opus # Escalation sequence
32
+ threshold: 2 # Failures before advisory
29
33
  ```
30
34
 
35
+ ### Escalation Policy
36
+
37
+ When `escalation.enabled: true`, the model-escalation hooks will track outcomes for this agent type and advise escalation when failures exceed the threshold. This is advisory-only — the orchestrator decides whether to accept the recommendation.
38
+
39
+ | Field | Default | Description |
40
+ |-------|---------|-------------|
41
+ | `enabled` | false | Enable escalation tracking for this agent |
42
+ | `path` | haiku → sonnet → opus | Model upgrade sequence |
43
+ | `threshold` | 2 | Failure count before escalation advisory |
44
+
31
45
  ## Memory Scopes
32
46
 
33
47
  | Scope | Location | Git Tracked |
@@ -0,0 +1,60 @@
1
+ ---
2
+ name: model-escalation
3
+ description: Advisory model escalation based on task outcome tracking
4
+ ---
5
+
6
+ # Model Escalation Skill
7
+
8
+ Tracks task outcomes and advises model upgrades when failures are detected. **Advisory-only** — the orchestrator makes the final decision (R010).
9
+
10
+ ## Escalation Path
11
+
12
+ ```
13
+ haiku → sonnet → opus
14
+ ```
15
+
16
+ ## Trigger Conditions
17
+
18
+ | Condition | Action |
19
+ |-----------|--------|
20
+ | 2+ failures with same model for same agent type | Advise escalation |
21
+ | 3+ consecutive failures across any agent type | Advise global escalation |
22
+ | Sustained success after escalation | Advise de-escalation |
23
+
24
+ ## Thresholds
25
+
26
+ | Parameter | Default | Description |
27
+ |-----------|---------|-------------|
28
+ | `failure_threshold` | 2 | Failures before escalation advisory |
29
+ | `consecutive_threshold` | 3 | Consecutive failures for global advisory |
30
+ | `cooldown_tasks` | 5 | Successes before de-escalation advisory |
31
+
32
+ ## Cost Guard
33
+
34
+ - Advisory includes estimated cost multiplier
35
+ - De-escalation suggested after sustained success at higher tier
36
+ - Cost tracked per session via PPID-scoped temp file
37
+
38
+ ## Architecture
39
+
40
+ ```
41
+ PostToolUse (Task) → task-outcome-recorder.sh
42
+ Records: agent_type, model, success/failure, error_summary
43
+ Storage: /tmp/.claude-task-outcomes-$PPID (JSON lines, max 50)
44
+
45
+ PreToolUse (Task) → model-escalation-advisor.sh
46
+ Reads outcomes → counts failures → advises escalation via stderr
47
+ Advisory only — never blocks, never modifies tool input
48
+ ```
49
+
50
+ ## Advisory Format
51
+
52
+ ```
53
+ --- [Model Escalation Advisory] ---
54
+ Agent type: {agent_type}
55
+ Current model: {current_model}
56
+ Recent failures: {count}/{threshold}
57
+ ⚡ Recommended: Escalate to {next_model}
58
+ Cost impact: {multiplier} per task
59
+ ---
60
+ ```
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: stuck-recovery
3
+ description: Detect stuck loops and advise recovery strategies
4
+ ---
5
+
6
+ # Stuck Recovery Skill
7
+
8
+ Detects when tasks are stuck in repetitive failure loops and advises recovery strategies. **Advisory-only** — the orchestrator decides the action (R010).
9
+
10
+ ## Detection Signals
11
+
12
+ | Signal | Pattern | Threshold |
13
+ |--------|---------|-----------|
14
+ | Repeated error | Same error message appears 3+ times | 3 occurrences |
15
+ | Edit loop | Same file edited 3+ times in sequence | 3 edits |
16
+ | Agent retry | Same agent_type fails 3+ times consecutively | 3 failures |
17
+ | Tool loop | Same tool called 5+ times with similar input | 5 calls |
18
+
19
+ ## Recovery Strategies
20
+
21
+ | Strategy | When | Action |
22
+ |----------|------|--------|
23
+ | Fresh context | Repeated same error | Suggest rephrasing the task |
24
+ | Model escalation | Agent retry loop | Trigger model-escalation advisory |
25
+ | Alternative approach | Edit loop detected | Suggest different file/method |
26
+ | Human intervention | All automated strategies exhausted | Ask user for guidance |
27
+
28
+ ## Architecture
29
+
30
+ ```
31
+ PostToolUse (Edit, Write, Bash, Task) → stuck-detector.sh
32
+ Tracks: tool_name, file_path, error_hash, agent_type
33
+ Storage: /tmp/.claude-tool-history-$PPID (JSON lines, max 100)
34
+ Detection: sliding window pattern matching
35
+ Output: stderr advisory when loop detected
36
+ ```
37
+
38
+ ## Advisory Format
39
+
40
+ ```
41
+ --- [Stuck Detection] Loop detected ---
42
+ Signal: {signal_type}
43
+ Pattern: {description}
44
+ Occurrences: {count}/{threshold}
45
+ 💡 Recovery: {suggested_strategy}
46
+ ---
47
+ ```
48
+
49
+ ## Integration
50
+
51
+ - Complements model-escalation skill (escalation is one recovery strategy)
52
+ - Respects R010 (advisory only, orchestrator decides)
53
+ - Uses same PPID-scoped temp file pattern as other hooks
54
+ - Works with task-outcome-recorder.sh data when available
@@ -18,7 +18,7 @@
18
18
  "name": "skills",
19
19
  "path": ".claude/skills",
20
20
  "description": "Reusable skill modules (includes slash commands)",
21
- "files": 56
21
+ "files": 58
22
22
  },
23
23
  {
24
24
  "name": "guides",