oh-my-claude-sisyphus 3.8.3 → 3.8.4
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +3 -3
- package/package.json +1 -1
- package/templates/hooks/session-start.mjs +95 -1
- package/templates/hooks/keyword-detector.sh +0 -268
- package/templates/hooks/persistent-mode.sh +0 -244
- package/templates/hooks/post-tool-use.sh +0 -90
- package/templates/hooks/pre-tool-use.sh +0 -113
- package/templates/hooks/session-start.sh +0 -62
- package/templates/hooks/stop-continuation.sh +0 -93
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
{
|
|
11
11
|
"name": "oh-my-claudecode",
|
|
12
12
|
"description": "Claude Code native multi-agent orchestration with intelligent model routing, 28 agent variants, and 30 powerful skills. Zero learning curve. Maximum power.",
|
|
13
|
-
"version": "3.8.
|
|
13
|
+
"version": "3.8.4",
|
|
14
14
|
"author": {
|
|
15
15
|
"name": "Yeachan Heo",
|
|
16
16
|
"email": "hurrc04@gmail.com"
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "oh-my-claudecode",
|
|
3
|
-
"version": "3.8.
|
|
3
|
+
"version": "3.8.4",
|
|
4
4
|
"description": "Multi-agent orchestration system for Claude Code",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "oh-my-claudecode contributors"
|
|
7
7
|
},
|
|
8
|
-
"repository": "https://github.com/
|
|
9
|
-
"homepage": "https://github.com/
|
|
8
|
+
"repository": "https://github.com/Yeachan-Heo/oh-my-claudecode",
|
|
9
|
+
"homepage": "https://github.com/Yeachan-Heo/oh-my-claudecode",
|
|
10
10
|
"license": "MIT",
|
|
11
11
|
"keywords": [
|
|
12
12
|
"claude-code",
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
// Restores persistent mode states when session starts
|
|
4
4
|
// Cross-platform: Windows, macOS, Linux
|
|
5
5
|
|
|
6
|
-
import { existsSync, readFileSync, readdirSync } from 'fs';
|
|
6
|
+
import { existsSync, readFileSync, readdirSync, writeFileSync, mkdirSync } from 'fs';
|
|
7
7
|
import { join } from 'path';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
9
|
|
|
@@ -24,6 +24,76 @@ function readJsonFile(path) {
|
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
function writeJsonFile(path, data) {
|
|
28
|
+
try {
|
|
29
|
+
const dir = join(path, '..');
|
|
30
|
+
if (!existsSync(dir)) {
|
|
31
|
+
mkdirSync(dir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
writeFileSync(path, JSON.stringify(data, null, 2), 'utf-8');
|
|
34
|
+
return true;
|
|
35
|
+
} catch {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
async function checkForUpdates(currentVersion) {
|
|
41
|
+
const cacheFile = join(homedir(), '.omc', 'update-check.json');
|
|
42
|
+
const now = Date.now();
|
|
43
|
+
const CACHE_DURATION = 24 * 60 * 60 * 1000; // 24 hours
|
|
44
|
+
|
|
45
|
+
// Check cache first
|
|
46
|
+
const cached = readJsonFile(cacheFile);
|
|
47
|
+
if (cached && cached.timestamp && (now - cached.timestamp) < CACHE_DURATION) {
|
|
48
|
+
return cached.updateAvailable ? cached : null;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Fetch latest version from npm
|
|
52
|
+
try {
|
|
53
|
+
const controller = new AbortController();
|
|
54
|
+
const timeoutId = setTimeout(() => controller.abort(), 2000);
|
|
55
|
+
|
|
56
|
+
const response = await fetch('https://registry.npmjs.org/oh-my-claude-sisyphus/latest', {
|
|
57
|
+
signal: controller.signal
|
|
58
|
+
});
|
|
59
|
+
clearTimeout(timeoutId);
|
|
60
|
+
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error('Network response was not ok');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
const data = await response.json();
|
|
66
|
+
const latestVersion = data.version;
|
|
67
|
+
|
|
68
|
+
const updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
|
|
69
|
+
|
|
70
|
+
const cacheData = {
|
|
71
|
+
timestamp: now,
|
|
72
|
+
latestVersion,
|
|
73
|
+
currentVersion,
|
|
74
|
+
updateAvailable
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
writeJsonFile(cacheFile, cacheData);
|
|
78
|
+
|
|
79
|
+
return updateAvailable ? cacheData : null;
|
|
80
|
+
} catch (error) {
|
|
81
|
+
// Silent fail - network unavailable or timeout
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function compareVersions(v1, v2) {
|
|
87
|
+
const parts1 = v1.replace(/^v/, '').split('.').map(Number);
|
|
88
|
+
const parts2 = v2.replace(/^v/, '').split('.').map(Number);
|
|
89
|
+
|
|
90
|
+
for (let i = 0; i < 3; i++) {
|
|
91
|
+
const diff = (parts1[i] || 0) - (parts2[i] || 0);
|
|
92
|
+
if (diff !== 0) return diff;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
27
97
|
function countIncompleteTodos(todosDir) {
|
|
28
98
|
let count = 0;
|
|
29
99
|
if (existsSync(todosDir)) {
|
|
@@ -49,6 +119,30 @@ async function main() {
|
|
|
49
119
|
const directory = data.directory || process.cwd();
|
|
50
120
|
const messages = [];
|
|
51
121
|
|
|
122
|
+
// Check for updates (non-blocking)
|
|
123
|
+
const packageJsonPath = join(directory, 'package.json');
|
|
124
|
+
let currentVersion = '3.8.4'; // fallback
|
|
125
|
+
const packageJson = readJsonFile(packageJsonPath);
|
|
126
|
+
if (packageJson?.version) {
|
|
127
|
+
currentVersion = packageJson.version;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const updateInfo = await checkForUpdates(currentVersion);
|
|
131
|
+
if (updateInfo) {
|
|
132
|
+
messages.push(`<session-restore>
|
|
133
|
+
|
|
134
|
+
[OMC UPDATE AVAILABLE]
|
|
135
|
+
|
|
136
|
+
A new version of oh-my-claudecode is available: v${updateInfo.latestVersion} (current: ${updateInfo.currentVersion})
|
|
137
|
+
|
|
138
|
+
To update, run: claude /install-plugin oh-my-claudecode
|
|
139
|
+
|
|
140
|
+
</session-restore>
|
|
141
|
+
|
|
142
|
+
---
|
|
143
|
+
`);
|
|
144
|
+
}
|
|
145
|
+
|
|
52
146
|
// Check for ultrawork state
|
|
53
147
|
const ultraworkState = readJsonFile(join(directory, '.omc', 'ultrawork-state.json'))
|
|
54
148
|
|| readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
|
|
@@ -1,268 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Keyword Detector Hook (Bash)
|
|
3
|
-
# Detects magic keywords and invokes skill tools
|
|
4
|
-
# Linux/macOS compatible
|
|
5
|
-
#
|
|
6
|
-
# Supported keywords (in priority order):
|
|
7
|
-
# 1. cancel: Stop active modes
|
|
8
|
-
# 2. ralph: Persistence mode until task completion
|
|
9
|
-
# 3. autopilot: Full autonomous execution
|
|
10
|
-
# 4. ultrapilot: Parallel autopilot
|
|
11
|
-
# 5. ultrawork/ulw: Maximum parallel execution
|
|
12
|
-
# 6. ecomode/eco: Token-efficient execution
|
|
13
|
-
# 7. swarm: N coordinated agents
|
|
14
|
-
# 8. pipeline: Sequential agent chaining
|
|
15
|
-
# 9. ralplan: Iterative planning with consensus
|
|
16
|
-
# 10. plan: Planning interview mode
|
|
17
|
-
# 11. tdd: Test-driven development
|
|
18
|
-
# 12. research: Research orchestration
|
|
19
|
-
# 13. ultrathink/think: Extended reasoning
|
|
20
|
-
# 14. deepsearch: Codebase search (restricted patterns)
|
|
21
|
-
# 15. analyze: Analysis mode (restricted patterns)
|
|
22
|
-
|
|
23
|
-
# Read stdin (JSON input from Claude Code)
|
|
24
|
-
INPUT=$(cat)
|
|
25
|
-
|
|
26
|
-
# Extract directory from input
|
|
27
|
-
DIRECTORY=""
|
|
28
|
-
if command -v jq &> /dev/null; then
|
|
29
|
-
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
30
|
-
fi
|
|
31
|
-
if [ -z "$DIRECTORY" ] || [ "$DIRECTORY" = "null" ]; then
|
|
32
|
-
DIRECTORY=$(pwd)
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# Extract the prompt text - try multiple JSON paths
|
|
36
|
-
PROMPT=""
|
|
37
|
-
if command -v jq &> /dev/null; then
|
|
38
|
-
# Try to extract from various possible JSON structures
|
|
39
|
-
PROMPT=$(echo "$INPUT" | jq -r '
|
|
40
|
-
if .prompt then .prompt
|
|
41
|
-
elif .message.content then .message.content
|
|
42
|
-
elif .parts then ([.parts[] | select(.type == "text") | .text] | join(" "))
|
|
43
|
-
else ""
|
|
44
|
-
end
|
|
45
|
-
' 2>/dev/null)
|
|
46
|
-
fi
|
|
47
|
-
|
|
48
|
-
# Fallback: simple grep extraction if jq fails
|
|
49
|
-
if [ -z "$PROMPT" ] || [ "$PROMPT" = "null" ]; then
|
|
50
|
-
PROMPT=$(echo "$INPUT" | grep -oP '"(prompt|content|text)"\s*:\s*"\K[^"]+' | head -1)
|
|
51
|
-
fi
|
|
52
|
-
|
|
53
|
-
# Exit if no prompt found
|
|
54
|
-
if [ -z "$PROMPT" ]; then
|
|
55
|
-
echo '{"continue": true}'
|
|
56
|
-
exit 0
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
# Remove code blocks before checking keywords (prevents false positives)
|
|
60
|
-
PROMPT_NO_CODE=$(echo "$PROMPT" | sed 's/```[^`]*```//g' | sed 's/`[^`]*`//g')
|
|
61
|
-
|
|
62
|
-
# Convert to lowercase for case-insensitive matching
|
|
63
|
-
PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
|
|
64
|
-
|
|
65
|
-
# Create a skill invocation message that tells Claude to use the Skill tool
|
|
66
|
-
create_skill_invocation() {
|
|
67
|
-
local skill_name="$1"
|
|
68
|
-
local original_prompt="$2"
|
|
69
|
-
local args="$3"
|
|
70
|
-
|
|
71
|
-
local args_section=""
|
|
72
|
-
if [ -n "$args" ]; then
|
|
73
|
-
args_section="\\nArguments: $args"
|
|
74
|
-
fi
|
|
75
|
-
|
|
76
|
-
local skill_upper=$(echo "$skill_name" | tr '[:lower:]' '[:upper:]')
|
|
77
|
-
|
|
78
|
-
cat << EOF
|
|
79
|
-
[MAGIC KEYWORD: ${skill_upper}]
|
|
80
|
-
|
|
81
|
-
You MUST invoke the skill using the Skill tool:
|
|
82
|
-
|
|
83
|
-
Skill: oh-my-claudecode:${skill_name}${args_section}
|
|
84
|
-
|
|
85
|
-
User request:
|
|
86
|
-
${original_prompt}
|
|
87
|
-
|
|
88
|
-
IMPORTANT: Invoke the skill IMMEDIATELY. Do not proceed without loading the skill instructions.
|
|
89
|
-
EOF
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
# Clear state files for cancel operation
|
|
93
|
-
clear_state_files() {
|
|
94
|
-
local directory="$1"
|
|
95
|
-
local modes=("ralph" "autopilot" "ultrapilot" "ultrawork" "ecomode" "swarm" "pipeline")
|
|
96
|
-
|
|
97
|
-
for mode in "${modes[@]}"; do
|
|
98
|
-
rm -f "$directory/.omc/state/${mode}-state.json" 2>/dev/null
|
|
99
|
-
rm -f "$HOME/.omc/state/${mode}-state.json" 2>/dev/null
|
|
100
|
-
done
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
# Activate state for a mode
|
|
104
|
-
activate_state() {
|
|
105
|
-
local directory="$1"
|
|
106
|
-
local prompt="$2"
|
|
107
|
-
local state_name="$3"
|
|
108
|
-
|
|
109
|
-
# Create directories
|
|
110
|
-
mkdir -p "$directory/.omc/state" 2>/dev/null
|
|
111
|
-
mkdir -p "$HOME/.omc/state" 2>/dev/null
|
|
112
|
-
|
|
113
|
-
# Escape prompt for JSON
|
|
114
|
-
local prompt_escaped=$(echo "$prompt" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ')
|
|
115
|
-
local timestamp=$(date -Iseconds 2>/dev/null || date +%Y-%m-%dT%H:%M:%S%z)
|
|
116
|
-
|
|
117
|
-
local state_json="{
|
|
118
|
-
\"active\": true,
|
|
119
|
-
\"started_at\": \"$timestamp\",
|
|
120
|
-
\"original_prompt\": \"$prompt_escaped\",
|
|
121
|
-
\"reinforcement_count\": 0,
|
|
122
|
-
\"last_checked_at\": \"$timestamp\"
|
|
123
|
-
}"
|
|
124
|
-
|
|
125
|
-
# Write state to both local and global locations
|
|
126
|
-
echo "$state_json" > "$directory/.omc/state/${state_name}-state.json" 2>/dev/null
|
|
127
|
-
echo "$state_json" > "$HOME/.omc/state/${state_name}-state.json" 2>/dev/null
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
# Output JSON with skill invocation message
|
|
131
|
-
output_skill() {
|
|
132
|
-
local skill_name="$1"
|
|
133
|
-
local prompt="$2"
|
|
134
|
-
local args="$3"
|
|
135
|
-
|
|
136
|
-
local message=$(create_skill_invocation "$skill_name" "$prompt" "$args")
|
|
137
|
-
# Escape for JSON: backslashes, quotes, and newlines
|
|
138
|
-
local escaped_message=$(echo "$message" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | awk '{printf "%s\\n", $0}' | sed 's/\\n$//')
|
|
139
|
-
|
|
140
|
-
echo "{\"continue\": true, \"message\": \"$escaped_message\"}"
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
# Priority 1: Cancel (BEFORE other modes - clears states)
|
|
144
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(stop|cancel|abort)\b'; then
|
|
145
|
-
clear_state_files "$DIRECTORY"
|
|
146
|
-
output_skill "cancel" "$PROMPT"
|
|
147
|
-
exit 0
|
|
148
|
-
fi
|
|
149
|
-
|
|
150
|
-
# Priority 2: Ralph keywords
|
|
151
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ralph|don'\''t stop|must complete|until done)\b'; then
|
|
152
|
-
activate_state "$DIRECTORY" "$PROMPT" "ralph"
|
|
153
|
-
activate_state "$DIRECTORY" "$PROMPT" "ultrawork"
|
|
154
|
-
output_skill "ralph" "$PROMPT"
|
|
155
|
-
exit 0
|
|
156
|
-
fi
|
|
157
|
-
|
|
158
|
-
# Priority 3: Autopilot keywords
|
|
159
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(autopilot|auto pilot|auto-pilot|autonomous|full auto|fullsend)\b' || \
|
|
160
|
-
echo "$PROMPT_LOWER" | grep -qE '\bbuild\s+me\s+' || \
|
|
161
|
-
echo "$PROMPT_LOWER" | grep -qE '\bcreate\s+me\s+' || \
|
|
162
|
-
echo "$PROMPT_LOWER" | grep -qE '\bmake\s+me\s+' || \
|
|
163
|
-
echo "$PROMPT_LOWER" | grep -qE '\bi\s+want\s+a\s+' || \
|
|
164
|
-
echo "$PROMPT_LOWER" | grep -qE '\bi\s+want\s+an\s+' || \
|
|
165
|
-
echo "$PROMPT_LOWER" | grep -qE '\bhandle\s+it\s+all\b' || \
|
|
166
|
-
echo "$PROMPT_LOWER" | grep -qE '\bend\s+to\s+end\b' || \
|
|
167
|
-
echo "$PROMPT_LOWER" | grep -qE '\be2e\s+this\b'; then
|
|
168
|
-
activate_state "$DIRECTORY" "$PROMPT" "autopilot"
|
|
169
|
-
output_skill "autopilot" "$PROMPT"
|
|
170
|
-
exit 0
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
# Priority 4: Ultrapilot
|
|
174
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ultrapilot|ultra-pilot)\b' || \
|
|
175
|
-
echo "$PROMPT_LOWER" | grep -qE '\bparallel\s+build\b' || \
|
|
176
|
-
echo "$PROMPT_LOWER" | grep -qE '\bswarm\s+build\b'; then
|
|
177
|
-
activate_state "$DIRECTORY" "$PROMPT" "ultrapilot"
|
|
178
|
-
output_skill "ultrapilot" "$PROMPT"
|
|
179
|
-
exit 0
|
|
180
|
-
fi
|
|
181
|
-
|
|
182
|
-
# Priority 5: Ultrawork keywords
|
|
183
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
|
|
184
|
-
activate_state "$DIRECTORY" "$PROMPT" "ultrawork"
|
|
185
|
-
output_skill "ultrawork" "$PROMPT"
|
|
186
|
-
exit 0
|
|
187
|
-
fi
|
|
188
|
-
|
|
189
|
-
# Priority 6: Ecomode keywords
|
|
190
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(eco|ecomode|eco-mode|efficient|save-tokens|budget)\b'; then
|
|
191
|
-
activate_state "$DIRECTORY" "$PROMPT" "ecomode"
|
|
192
|
-
output_skill "ecomode" "$PROMPT"
|
|
193
|
-
exit 0
|
|
194
|
-
fi
|
|
195
|
-
|
|
196
|
-
# Priority 7: Swarm - parse N from "swarm N agents"
|
|
197
|
-
SWARM_MATCH=$(echo "$PROMPT_LOWER" | grep -oE '\bswarm\s+[0-9]+\s+agents?\b' | grep -oE '[0-9]+')
|
|
198
|
-
if [ -n "$SWARM_MATCH" ]; then
|
|
199
|
-
output_skill "swarm" "$PROMPT" "$SWARM_MATCH"
|
|
200
|
-
exit 0
|
|
201
|
-
fi
|
|
202
|
-
if echo "$PROMPT_LOWER" | grep -qE '\bcoordinated\s+agents\b'; then
|
|
203
|
-
output_skill "swarm" "$PROMPT" "3"
|
|
204
|
-
exit 0
|
|
205
|
-
fi
|
|
206
|
-
|
|
207
|
-
# Priority 8: Pipeline
|
|
208
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(pipeline)\b' || \
|
|
209
|
-
echo "$PROMPT_LOWER" | grep -qE '\bchain\s+agents\b'; then
|
|
210
|
-
output_skill "pipeline" "$PROMPT"
|
|
211
|
-
exit 0
|
|
212
|
-
fi
|
|
213
|
-
|
|
214
|
-
# Priority 9: Ralplan keyword (before plan to avoid false match)
|
|
215
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ralplan)\b'; then
|
|
216
|
-
output_skill "ralplan" "$PROMPT"
|
|
217
|
-
exit 0
|
|
218
|
-
fi
|
|
219
|
-
|
|
220
|
-
# Priority 10: Plan keywords
|
|
221
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(plan this|plan the)\b'; then
|
|
222
|
-
output_skill "plan" "$PROMPT"
|
|
223
|
-
exit 0
|
|
224
|
-
fi
|
|
225
|
-
|
|
226
|
-
# Priority 11: TDD
|
|
227
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(tdd)\b' || \
|
|
228
|
-
echo "$PROMPT_LOWER" | grep -qE '\btest\s+first\b' || \
|
|
229
|
-
echo "$PROMPT_LOWER" | grep -qE '\bred\s+green\b'; then
|
|
230
|
-
output_skill "tdd" "$PROMPT"
|
|
231
|
-
exit 0
|
|
232
|
-
fi
|
|
233
|
-
|
|
234
|
-
# Priority 12: Research
|
|
235
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(research)\b' || \
|
|
236
|
-
echo "$PROMPT_LOWER" | grep -qE '\banalyze\s+data\b' || \
|
|
237
|
-
echo "$PROMPT_LOWER" | grep -qE '\bstatistics\b'; then
|
|
238
|
-
output_skill "research" "$PROMPT"
|
|
239
|
-
exit 0
|
|
240
|
-
fi
|
|
241
|
-
|
|
242
|
-
# Priority 13: Ultrathink/think keywords (keep inline message)
|
|
243
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(ultrathink|think hard|think deeply)\b'; then
|
|
244
|
-
cat << 'EOF'
|
|
245
|
-
{"continue": true, "message": "<think-mode>\n\n**ULTRATHINK MODE ENABLED** - Extended reasoning activated.\n\nYou are now in deep thinking mode. Take your time to:\n1. Thoroughly analyze the problem from multiple angles\n2. Consider edge cases and potential issues\n3. Think through the implications of each approach\n4. Reason step-by-step before acting\n\nUse your extended thinking capabilities to provide the most thorough and well-reasoned response.\n\n</think-mode>\n\n---\n"}
|
|
246
|
-
EOF
|
|
247
|
-
exit 0
|
|
248
|
-
fi
|
|
249
|
-
|
|
250
|
-
# Priority 14: Deepsearch (RESTRICTED patterns)
|
|
251
|
-
if echo "$PROMPT_LOWER" | grep -qE '\b(deepsearch)\b' || \
|
|
252
|
-
echo "$PROMPT_LOWER" | grep -qE '\bsearch\s+(the\s+)?(codebase|code|files|project)\b' || \
|
|
253
|
-
echo "$PROMPT_LOWER" | grep -qE '\bfind\s+(in\s+)?(codebase|code|all\s+files)\b'; then
|
|
254
|
-
output_skill "deepsearch" "$PROMPT"
|
|
255
|
-
exit 0
|
|
256
|
-
fi
|
|
257
|
-
|
|
258
|
-
# Priority 15: Analyze (RESTRICTED patterns)
|
|
259
|
-
if echo "$PROMPT_LOWER" | grep -qE '\bdeep\s*analyze\b' || \
|
|
260
|
-
echo "$PROMPT_LOWER" | grep -qE '\binvestigate\s+(the|this|why)\b' || \
|
|
261
|
-
echo "$PROMPT_LOWER" | grep -qE '\bdebug\s+(the|this|why)\b'; then
|
|
262
|
-
output_skill "analyze" "$PROMPT"
|
|
263
|
-
exit 0
|
|
264
|
-
fi
|
|
265
|
-
|
|
266
|
-
# No keywords detected - continue without modification
|
|
267
|
-
echo '{"continue": true}'
|
|
268
|
-
exit 0
|
|
@@ -1,244 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Persistent Mode Hook
|
|
3
|
-
# Unified handler for ultrawork, ralph-loop, and todo continuation
|
|
4
|
-
# Prevents stopping when work remains incomplete
|
|
5
|
-
|
|
6
|
-
# Validate session ID to prevent path traversal attacks
|
|
7
|
-
# Returns 0 (success) for valid, 1 for invalid
|
|
8
|
-
is_valid_session_id() {
|
|
9
|
-
local id="$1"
|
|
10
|
-
if [ -z "$id" ]; then
|
|
11
|
-
return 1
|
|
12
|
-
fi
|
|
13
|
-
# Allow alphanumeric, hyphens, and underscores only
|
|
14
|
-
# Must not start with dot or hyphen, max 256 chars
|
|
15
|
-
if echo "$id" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$'; then
|
|
16
|
-
return 0
|
|
17
|
-
fi
|
|
18
|
-
return 1
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
# Read stdin
|
|
22
|
-
INPUT=$(cat)
|
|
23
|
-
|
|
24
|
-
# Get session ID and directory
|
|
25
|
-
SESSION_ID=""
|
|
26
|
-
DIRECTORY=""
|
|
27
|
-
if command -v jq &> /dev/null; then
|
|
28
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
|
|
29
|
-
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
30
|
-
fi
|
|
31
|
-
|
|
32
|
-
# Default to current directory
|
|
33
|
-
if [ -z "$DIRECTORY" ]; then
|
|
34
|
-
DIRECTORY=$(pwd)
|
|
35
|
-
fi
|
|
36
|
-
|
|
37
|
-
# Check for incomplete tasks in new Task system (priority over todos)
|
|
38
|
-
TASKS_DIR="$HOME/.claude/tasks"
|
|
39
|
-
TASK_COUNT=0
|
|
40
|
-
JQ_AVAILABLE=false
|
|
41
|
-
if command -v jq &> /dev/null; then
|
|
42
|
-
JQ_AVAILABLE=true
|
|
43
|
-
fi
|
|
44
|
-
|
|
45
|
-
if [ -n "$SESSION_ID" ] && is_valid_session_id "$SESSION_ID" && [ -d "$TASKS_DIR/$SESSION_ID" ]; then
|
|
46
|
-
for task_file in "$TASKS_DIR/$SESSION_ID"/*.json; do
|
|
47
|
-
if [ -f "$task_file" ] && [ "$(basename "$task_file")" != ".lock" ]; then
|
|
48
|
-
if [ "$JQ_AVAILABLE" = "true" ]; then
|
|
49
|
-
STATUS=$(jq -r '.status // "pending"' "$task_file" 2>/dev/null)
|
|
50
|
-
# Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
|
|
51
|
-
# 'deleted' and 'completed' are both treated as done
|
|
52
|
-
if [ "$STATUS" = "pending" ] || [ "$STATUS" = "in_progress" ]; then
|
|
53
|
-
TASK_COUNT=$((TASK_COUNT + 1))
|
|
54
|
-
fi
|
|
55
|
-
else
|
|
56
|
-
# Fallback: grep for incomplete status values (pending or in_progress)
|
|
57
|
-
# This is less accurate but provides basic functionality
|
|
58
|
-
if grep -qE '"status"[[:space:]]*:[[:space:]]*"(pending|in_progress)"' "$task_file" 2>/dev/null; then
|
|
59
|
-
TASK_COUNT=$((TASK_COUNT + 1))
|
|
60
|
-
fi
|
|
61
|
-
fi
|
|
62
|
-
fi
|
|
63
|
-
done
|
|
64
|
-
|
|
65
|
-
# Warn if using fallback (only once per invocation, to stderr)
|
|
66
|
-
if [ "$JQ_AVAILABLE" = "false" ] && [ "$TASK_COUNT" -gt 0 ]; then
|
|
67
|
-
echo "[OMC WARNING] jq not installed - Task counting may be less accurate. Install jq for best results." >&2
|
|
68
|
-
fi
|
|
69
|
-
fi
|
|
70
|
-
|
|
71
|
-
# Extract stop reason for abort detection
|
|
72
|
-
STOP_REASON=""
|
|
73
|
-
USER_REQUESTED=""
|
|
74
|
-
if command -v jq &> /dev/null; then
|
|
75
|
-
STOP_REASON=$(echo "$INPUT" | jq -r '.stop_reason // .stopReason // ""' 2>/dev/null)
|
|
76
|
-
USER_REQUESTED=$(echo "$INPUT" | jq -r '.user_requested // .userRequested // "false"' 2>/dev/null)
|
|
77
|
-
fi
|
|
78
|
-
|
|
79
|
-
# Check for user abort before continuation checks
|
|
80
|
-
# NOTE: Abort patterns are assumed - verify against actual Claude Code API values
|
|
81
|
-
if [ "$USER_REQUESTED" = "true" ] || echo "$STOP_REASON" | grep -qiE "(abort|cancel|interrupt|ctrl_c|manual_stop)"; then
|
|
82
|
-
echo '{"continue": true}'
|
|
83
|
-
exit 0
|
|
84
|
-
fi
|
|
85
|
-
|
|
86
|
-
# Check for active ultrawork state
|
|
87
|
-
ULTRAWORK_STATE=""
|
|
88
|
-
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
|
|
89
|
-
ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
90
|
-
elif [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
|
|
91
|
-
ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
92
|
-
fi
|
|
93
|
-
|
|
94
|
-
# Check for active ralph loop
|
|
95
|
-
RALPH_STATE=""
|
|
96
|
-
if [ -f "$DIRECTORY/.omc/state/ralph-state.json" ]; then
|
|
97
|
-
RALPH_STATE=$(cat "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null)
|
|
98
|
-
fi
|
|
99
|
-
|
|
100
|
-
# Check for verification state (oracle verification)
|
|
101
|
-
VERIFICATION_STATE=""
|
|
102
|
-
if [ -f "$DIRECTORY/.omc/state/ralph-verification.json" ]; then
|
|
103
|
-
VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/state/ralph-verification.json" 2>/dev/null)
|
|
104
|
-
fi
|
|
105
|
-
|
|
106
|
-
# Check for incomplete todos
|
|
107
|
-
INCOMPLETE_COUNT=0
|
|
108
|
-
TODOS_DIR="$HOME/.claude/todos"
|
|
109
|
-
if [ -d "$TODOS_DIR" ]; then
|
|
110
|
-
for todo_file in "$TODOS_DIR"/*.json; do
|
|
111
|
-
if [ -f "$todo_file" ]; then
|
|
112
|
-
if command -v jq &> /dev/null; then
|
|
113
|
-
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
114
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
115
|
-
else
|
|
116
|
-
# Fallback: count "pending" or "in_progress" occurrences
|
|
117
|
-
COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_file" 2>/dev/null) || COUNT=0
|
|
118
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
119
|
-
fi
|
|
120
|
-
fi
|
|
121
|
-
done
|
|
122
|
-
fi
|
|
123
|
-
|
|
124
|
-
# Check project todos as well
|
|
125
|
-
for todo_path in "$DIRECTORY/.omc/todos.json" "$DIRECTORY/.claude/todos.json"; do
|
|
126
|
-
if [ -f "$todo_path" ]; then
|
|
127
|
-
if command -v jq &> /dev/null; then
|
|
128
|
-
COUNT=$(jq 'if type == "array" then [.[] | select(.status != "completed" and .status != "cancelled")] | length else 0 end' "$todo_path" 2>/dev/null || echo "0")
|
|
129
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
130
|
-
else
|
|
131
|
-
# Fallback: count "pending" or "in_progress" occurrences
|
|
132
|
-
COUNT=$(grep -c '"status"[[:space:]]*:[[:space:]]*"pending\|in_progress"' "$todo_path" 2>/dev/null) || COUNT=0
|
|
133
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
134
|
-
fi
|
|
135
|
-
fi
|
|
136
|
-
done
|
|
137
|
-
|
|
138
|
-
# Combine Task and todo counts
|
|
139
|
-
TOTAL_INCOMPLETE=$((TASK_COUNT + INCOMPLETE_COUNT))
|
|
140
|
-
|
|
141
|
-
# Priority 1: Ralph Loop with Oracle Verification
|
|
142
|
-
if [ -n "$RALPH_STATE" ]; then
|
|
143
|
-
IS_ACTIVE=$(echo "$RALPH_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
144
|
-
if [ "$IS_ACTIVE" = "true" ]; then
|
|
145
|
-
ITERATION=$(echo "$RALPH_STATE" | jq -r '.iteration // 1' 2>/dev/null)
|
|
146
|
-
MAX_ITER=$(echo "$RALPH_STATE" | jq -r '.max_iterations // 10' 2>/dev/null)
|
|
147
|
-
PROMISE=$(echo "$RALPH_STATE" | jq -r '.completion_promise // "TASK_COMPLETE"' 2>/dev/null)
|
|
148
|
-
PROMPT=$(echo "$RALPH_STATE" | jq -r '.prompt // ""' 2>/dev/null)
|
|
149
|
-
|
|
150
|
-
# Check if oracle verification is pending
|
|
151
|
-
if [ -n "$VERIFICATION_STATE" ]; then
|
|
152
|
-
IS_PENDING=$(echo "$VERIFICATION_STATE" | jq -r '.pending // false' 2>/dev/null)
|
|
153
|
-
if [ "$IS_PENDING" = "true" ]; then
|
|
154
|
-
ATTEMPT=$(echo "$VERIFICATION_STATE" | jq -r '.verification_attempts // 0' 2>/dev/null)
|
|
155
|
-
MAX_ATTEMPTS=$(echo "$VERIFICATION_STATE" | jq -r '.max_verification_attempts // 3' 2>/dev/null)
|
|
156
|
-
ORIGINAL_TASK=$(echo "$VERIFICATION_STATE" | jq -r '.original_task // ""' 2>/dev/null)
|
|
157
|
-
COMPLETION_CLAIM=$(echo "$VERIFICATION_STATE" | jq -r '.completion_claim // ""' 2>/dev/null)
|
|
158
|
-
ORACLE_FEEDBACK=$(echo "$VERIFICATION_STATE" | jq -r '.oracle_feedback // ""' 2>/dev/null)
|
|
159
|
-
NEXT_ATTEMPT=$((ATTEMPT + 1))
|
|
160
|
-
|
|
161
|
-
FEEDBACK_SECTION=""
|
|
162
|
-
if [ -n "$ORACLE_FEEDBACK" ] && [ "$ORACLE_FEEDBACK" != "null" ]; then
|
|
163
|
-
FEEDBACK_SECTION="\\n**Previous Oracle Feedback (rejected):**\\n$ORACLE_FEEDBACK\\n"
|
|
164
|
-
fi
|
|
165
|
-
|
|
166
|
-
cat << EOF
|
|
167
|
-
{"continue": false, "reason": "<ralph-verification>\\n\\n[ORACLE VERIFICATION REQUIRED - Attempt $NEXT_ATTEMPT/$MAX_ATTEMPTS]\\n\\nThe agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.\\n\\n**Original Task:**\\n$ORIGINAL_TASK\\n\\n**Completion Claim:**\\n$COMPLETION_CLAIM\\n$FEEDBACK_SECTION\\n## MANDATORY VERIFICATION STEPS\\n\\n1. **Spawn Oracle Agent** for verification:\\n \`\`\`\\n Task(subagent_type=\"oracle\", prompt=\"Verify this task completion claim...\")\\n \`\`\`\\n\\n2. **Oracle must check:**\\n - Are ALL requirements from the original task met?\\n - Is the implementation complete, not partial?\\n - Are there any obvious bugs or issues?\\n - Does the code compile/run without errors?\\n - Are tests passing (if applicable)?\\n\\n3. **Based on Oracle's response:**\\n - If APPROVED: Output \`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\`\\n - If REJECTED: Continue working on the identified issues\\n\\nDO NOT output the completion promise again until Oracle approves.\\n\\n</ralph-verification>\\n\\n---\\n"}
|
|
168
|
-
EOF
|
|
169
|
-
exit 0
|
|
170
|
-
fi
|
|
171
|
-
fi
|
|
172
|
-
|
|
173
|
-
if [ "$ITERATION" -lt "$MAX_ITER" ]; then
|
|
174
|
-
# Increment iteration
|
|
175
|
-
NEW_ITER=$((ITERATION + 1))
|
|
176
|
-
echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null
|
|
177
|
-
|
|
178
|
-
cat << EOF
|
|
179
|
-
{"continue": false, "reason": "<ralph-loop-continuation>\\n\\n[RALPH LOOP - ITERATION $NEW_ITER/$MAX_ITER]\\n\\nYour previous attempt did not output the completion promise. The work is NOT done yet.\\n\\nCRITICAL INSTRUCTIONS:\\n1. Review your progress and the original task\\n2. Check your todo list - are ALL items marked complete?\\n3. Continue from where you left off\\n4. When FULLY complete, output: <promise>$PROMISE</promise>\\n5. Do NOT stop until the task is truly done\\n\\nOriginal task: $PROMPT\\n\\n</ralph-loop-continuation>\\n\\n---\\n"}
|
|
180
|
-
EOF
|
|
181
|
-
exit 0
|
|
182
|
-
fi
|
|
183
|
-
fi
|
|
184
|
-
fi
|
|
185
|
-
|
|
186
|
-
# Priority 2: Ultrawork Mode with incomplete todos
|
|
187
|
-
if [ -n "$ULTRAWORK_STATE" ] && [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
|
|
188
|
-
# Check if active (with jq fallback)
|
|
189
|
-
IS_ACTIVE=""
|
|
190
|
-
if command -v jq &> /dev/null; then
|
|
191
|
-
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
192
|
-
else
|
|
193
|
-
# Fallback: grep for "active": true
|
|
194
|
-
if echo "$ULTRAWORK_STATE" | grep -q '"active"[[:space:]]*:[[:space:]]*true'; then
|
|
195
|
-
IS_ACTIVE="true"
|
|
196
|
-
fi
|
|
197
|
-
fi
|
|
198
|
-
|
|
199
|
-
if [ "$IS_ACTIVE" = "true" ]; then
|
|
200
|
-
# Get reinforcement count (with fallback)
|
|
201
|
-
REINFORCE_COUNT=0
|
|
202
|
-
if command -v jq &> /dev/null; then
|
|
203
|
-
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | jq -r '.reinforcement_count // 0' 2>/dev/null)
|
|
204
|
-
else
|
|
205
|
-
REINFORCE_COUNT=$(echo "$ULTRAWORK_STATE" | grep -oP '"reinforcement_count"[[:space:]]*:[[:space:]]*\K[0-9]+' 2>/dev/null) || REINFORCE_COUNT=0
|
|
206
|
-
fi
|
|
207
|
-
NEW_COUNT=$((REINFORCE_COUNT + 1))
|
|
208
|
-
|
|
209
|
-
# Get original prompt (with fallback)
|
|
210
|
-
ORIGINAL_PROMPT=""
|
|
211
|
-
if command -v jq &> /dev/null; then
|
|
212
|
-
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
213
|
-
else
|
|
214
|
-
ORIGINAL_PROMPT=$(echo "$ULTRAWORK_STATE" | grep -oP '"original_prompt"[[:space:]]*:[[:space:]]*"\K[^"]+' 2>/dev/null) || ORIGINAL_PROMPT=""
|
|
215
|
-
fi
|
|
216
|
-
|
|
217
|
-
# Update state file (best effort)
|
|
218
|
-
if command -v jq &> /dev/null; then
|
|
219
|
-
echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
|
|
220
|
-
fi
|
|
221
|
-
|
|
222
|
-
cat << EOF
|
|
223
|
-
{"continue": false, "reason": "<ultrawork-persistence>\\n\\n[ULTRAWORK MODE STILL ACTIVE - Reinforcement #$NEW_COUNT]\\n\\nYour ultrawork session is NOT complete. $TOTAL_INCOMPLETE incomplete items remain.\\n\\nREMEMBER THE ULTRAWORK RULES:\\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially\\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)\\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each\\n- **VERIFY**: Check ALL requirements met before done\\n- **NO Premature Stopping**: ALL TODOs must be complete\\n\\nContinue working on the next pending item. DO NOT STOP until all items are marked complete.\\n\\nOriginal task: $ORIGINAL_PROMPT\\n\\n</ultrawork-persistence>\\n\\n---\\n"}
|
|
224
|
-
EOF
|
|
225
|
-
exit 0
|
|
226
|
-
fi
|
|
227
|
-
fi
|
|
228
|
-
|
|
229
|
-
# Priority 3: Todo/Task Continuation (baseline)
|
|
230
|
-
if [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
|
|
231
|
-
if [ "$TASK_COUNT" -gt 0 ]; then
|
|
232
|
-
ITEM_TYPE="Tasks"
|
|
233
|
-
else
|
|
234
|
-
ITEM_TYPE="todos"
|
|
235
|
-
fi
|
|
236
|
-
cat << EOF
|
|
237
|
-
{"continue": false, "reason": "<todo-continuation>\\n\\n[SYSTEM REMINDER - CONTINUATION]\\n\\nIncomplete $ITEM_TYPE remain ($TOTAL_INCOMPLETE remaining). Continue working on the next pending item.\\n\\n- Proceed without asking for permission\\n- Mark each item complete when finished\\n- Do not stop until all items are done\\n\\n</todo-continuation>\\n\\n---\\n"}
|
|
238
|
-
EOF
|
|
239
|
-
exit 0
|
|
240
|
-
fi
|
|
241
|
-
|
|
242
|
-
# No blocking needed
|
|
243
|
-
echo '{"continue": true}'
|
|
244
|
-
exit 0
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Post-Tool-Use Hook
|
|
3
|
-
# Processes <remember> tags from Task agent output
|
|
4
|
-
# Saves to .omc/notepad.md for compaction-resilient memory
|
|
5
|
-
|
|
6
|
-
# Read stdin
|
|
7
|
-
INPUT=$(cat)
|
|
8
|
-
|
|
9
|
-
# Get directory and tool info
|
|
10
|
-
DIRECTORY=""
|
|
11
|
-
TOOL_NAME=""
|
|
12
|
-
TOOL_OUTPUT=""
|
|
13
|
-
if command -v jq &> /dev/null; then
|
|
14
|
-
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
15
|
-
TOOL_NAME=$(echo "$INPUT" | jq -r '.toolName // ""' 2>/dev/null)
|
|
16
|
-
TOOL_OUTPUT=$(echo "$INPUT" | jq -r '.toolOutput // ""' 2>/dev/null)
|
|
17
|
-
else
|
|
18
|
-
# Fallback: use grep/sed for extraction
|
|
19
|
-
DIRECTORY=$(echo "$INPUT" | grep -oP '"directory"\s*:\s*"\K[^"]+' | head -1)
|
|
20
|
-
TOOL_NAME=$(echo "$INPUT" | grep -oP '"toolName"\s*:\s*"\K[^"]+' | head -1)
|
|
21
|
-
TOOL_OUTPUT=$(echo "$INPUT" | grep -oP '"toolOutput"\s*:\s*"\K[^"]+' | head -1)
|
|
22
|
-
fi
|
|
23
|
-
|
|
24
|
-
if [ -z "$DIRECTORY" ]; then
|
|
25
|
-
DIRECTORY=$(pwd)
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
# Only process Task tool output
|
|
29
|
-
if [ "$TOOL_NAME" != "Task" ] && [ "$TOOL_NAME" != "task" ]; then
|
|
30
|
-
echo '{"continue": true}'
|
|
31
|
-
exit 0
|
|
32
|
-
fi
|
|
33
|
-
|
|
34
|
-
# Check for <remember> tags
|
|
35
|
-
if ! echo "$TOOL_OUTPUT" | grep -q '<remember'; then
|
|
36
|
-
echo '{"continue": true}'
|
|
37
|
-
exit 0
|
|
38
|
-
fi
|
|
39
|
-
|
|
40
|
-
# Create .omc directory if needed
|
|
41
|
-
OMC_DIR="$DIRECTORY/.omc"
|
|
42
|
-
NOTEPAD_FILE="$OMC_DIR/notepad.md"
|
|
43
|
-
mkdir -p "$OMC_DIR" 2>/dev/null
|
|
44
|
-
|
|
45
|
-
# Initialize notepad.md if it doesn't exist
|
|
46
|
-
if [ ! -f "$NOTEPAD_FILE" ]; then
|
|
47
|
-
cat > "$NOTEPAD_FILE" << 'NOTEPAD_INIT'
|
|
48
|
-
# Notepad
|
|
49
|
-
<!-- Auto-managed by OMC. Manual edits preserved in MANUAL section. -->
|
|
50
|
-
|
|
51
|
-
## Priority Context
|
|
52
|
-
<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->
|
|
53
|
-
|
|
54
|
-
## Working Memory
|
|
55
|
-
<!-- Session notes. Auto-pruned after 7 days. -->
|
|
56
|
-
|
|
57
|
-
## MANUAL
|
|
58
|
-
<!-- User content. Never auto-pruned. -->
|
|
59
|
-
NOTEPAD_INIT
|
|
60
|
-
fi
|
|
61
|
-
|
|
62
|
-
# Process priority remember tags
|
|
63
|
-
PRIORITY_CONTENT=$(echo "$TOOL_OUTPUT" | grep -oP '<remember\s+priority>\K[\s\S]*?(?=</remember>)' | head -1)
|
|
64
|
-
if [ -n "$PRIORITY_CONTENT" ]; then
|
|
65
|
-
# Read current notepad
|
|
66
|
-
NOTEPAD_CONTENT=$(cat "$NOTEPAD_FILE")
|
|
67
|
-
# Replace Priority Context section
|
|
68
|
-
NEW_NOTEPAD=$(echo "$NOTEPAD_CONTENT" | sed '/## Priority Context/,/## Working Memory/{
|
|
69
|
-
/## Priority Context/!{/## Working Memory/!d}
|
|
70
|
-
}' | sed "/## Priority Context/a\\<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->\\n$PRIORITY_CONTENT")
|
|
71
|
-
echo "$NEW_NOTEPAD" > "$NOTEPAD_FILE"
|
|
72
|
-
fi
|
|
73
|
-
|
|
74
|
-
# Process regular remember tags
|
|
75
|
-
while IFS= read -r CONTENT; do
|
|
76
|
-
if [ -n "$CONTENT" ]; then
|
|
77
|
-
TIMESTAMP=$(date '+%Y-%m-%d %H:%M')
|
|
78
|
-
# Append to Working Memory section (before MANUAL)
|
|
79
|
-
sed -i "/## MANUAL/i\\### $TIMESTAMP\\n$CONTENT\\n" "$NOTEPAD_FILE" 2>/dev/null || {
|
|
80
|
-
# macOS sed fallback
|
|
81
|
-
sed -i '' "/## MANUAL/i\\
|
|
82
|
-
### $TIMESTAMP\\
|
|
83
|
-
$CONTENT\\
|
|
84
|
-
" "$NOTEPAD_FILE"
|
|
85
|
-
}
|
|
86
|
-
fi
|
|
87
|
-
done < <(echo "$TOOL_OUTPUT" | grep -oP '<remember>\K[\s\S]*?(?=</remember>)')
|
|
88
|
-
|
|
89
|
-
echo '{"continue": true}'
|
|
90
|
-
exit 0
|
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Pre-Tool-Use Hook
|
|
3
|
-
# Enforces delegation by warning when orchestrator attempts direct source file edits
|
|
4
|
-
|
|
5
|
-
# Read stdin (JSON input from Claude Code)
|
|
6
|
-
INPUT=$(cat)
|
|
7
|
-
|
|
8
|
-
# Extract tool name and file path
|
|
9
|
-
TOOL_NAME=""
|
|
10
|
-
FILE_PATH=""
|
|
11
|
-
if command -v jq &> /dev/null; then
|
|
12
|
-
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // .toolName // ""' 2>/dev/null)
|
|
13
|
-
FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // .toolInput.file_path // .toolInput.filePath // ""' 2>/dev/null)
|
|
14
|
-
else
|
|
15
|
-
TOOL_NAME=$(echo "$INPUT" | grep -oP '"tool_name"\s*:\s*"\K[^"]+' | head -1)
|
|
16
|
-
if [ -z "$TOOL_NAME" ]; then
|
|
17
|
-
TOOL_NAME=$(echo "$INPUT" | grep -oP '"toolName"\s*:\s*"\K[^"]+' | head -1)
|
|
18
|
-
fi
|
|
19
|
-
FILE_PATH=$(echo "$INPUT" | grep -oP '"file_path"\s*:\s*"\K[^"]+' | head -1)
|
|
20
|
-
if [ -z "$FILE_PATH" ]; then
|
|
21
|
-
FILE_PATH=$(echo "$INPUT" | grep -oP '"filePath"\s*:\s*"\K[^"]+' | head -1)
|
|
22
|
-
fi
|
|
23
|
-
fi
|
|
24
|
-
|
|
25
|
-
# Handle Bash tool separately - check for file modification patterns
|
|
26
|
-
if [ "$TOOL_NAME" = "Bash" ] || [ "$TOOL_NAME" = "bash" ]; then
|
|
27
|
-
# Extract command
|
|
28
|
-
COMMAND=""
|
|
29
|
-
if command -v jq &> /dev/null; then
|
|
30
|
-
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // .toolInput.command // ""' 2>/dev/null)
|
|
31
|
-
else
|
|
32
|
-
COMMAND=$(echo "$INPUT" | grep -oP '"command"\s*:\s*"\K[^"]+' | head -1)
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
# Check for file modification patterns
|
|
36
|
-
if echo "$COMMAND" | grep -qE '(sed\s+-i|>\s*[^&]|>>\s*|tee\s+|cat\s+.*>\s*|echo\s+.*>\s*|printf\s+.*>\s*)'; then
|
|
37
|
-
# Check if modifying source files
|
|
38
|
-
SOURCE_PATTERN='\.(ts|tsx|js|jsx|mjs|cjs|py|pyw|go|rs|java|kt|scala|c|cpp|cc|h|hpp|rb|php|svelte|vue|graphql|gql|sh|bash|zsh)'
|
|
39
|
-
if echo "$COMMAND" | grep -qE "$SOURCE_PATTERN"; then
|
|
40
|
-
# Might be modifying source files - warn
|
|
41
|
-
WARNING="[DELEGATION NOTICE] Bash command may modify source files: $COMMAND
|
|
42
|
-
|
|
43
|
-
Recommended: Delegate to executor agent instead:
|
|
44
|
-
Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
|
|
45
|
-
|
|
46
|
-
This is a soft warning. Operation will proceed."
|
|
47
|
-
WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs . 2>/dev/null || echo "\"$WARNING\"")
|
|
48
|
-
echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
|
|
49
|
-
exit 0
|
|
50
|
-
fi
|
|
51
|
-
fi
|
|
52
|
-
# Bash command is OK
|
|
53
|
-
echo '{"continue": true}'
|
|
54
|
-
exit 0
|
|
55
|
-
fi
|
|
56
|
-
|
|
57
|
-
# Only check Edit and Write tools
|
|
58
|
-
if [ "$TOOL_NAME" != "Edit" ] && [ "$TOOL_NAME" != "Write" ] && \
|
|
59
|
-
[ "$TOOL_NAME" != "edit" ] && [ "$TOOL_NAME" != "write" ]; then
|
|
60
|
-
echo '{"continue": true}'
|
|
61
|
-
exit 0
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
# No file path? Allow
|
|
65
|
-
if [ -z "$FILE_PATH" ]; then
|
|
66
|
-
echo '{"continue": true}'
|
|
67
|
-
exit 0
|
|
68
|
-
fi
|
|
69
|
-
|
|
70
|
-
# Check allowed paths (always OK)
|
|
71
|
-
if [[ "$FILE_PATH" == *".omc/"* ]] || \
|
|
72
|
-
[[ "$FILE_PATH" == *".claude/"* ]] || \
|
|
73
|
-
[[ "$FILE_PATH" == *"/.claude/"* ]] || \
|
|
74
|
-
[[ "$FILE_PATH" == "CLAUDE.md" ]] || \
|
|
75
|
-
[[ "$FILE_PATH" == *"/CLAUDE.md" ]] || \
|
|
76
|
-
[[ "$FILE_PATH" == "AGENTS.md" ]] || \
|
|
77
|
-
[[ "$FILE_PATH" == *"/AGENTS.md" ]]; then
|
|
78
|
-
echo '{"continue": true}'
|
|
79
|
-
exit 0
|
|
80
|
-
fi
|
|
81
|
-
|
|
82
|
-
# Check if source file extension (should warn)
|
|
83
|
-
EXT="${FILE_PATH##*.}"
|
|
84
|
-
EXT_LOWER=$(echo "$EXT" | tr '[:upper:]' '[:lower:]')
|
|
85
|
-
|
|
86
|
-
SOURCE_EXTS="ts tsx js jsx mjs cjs py pyw go rs java kt scala c cpp cc h hpp rb php svelte vue graphql gql sh bash zsh"
|
|
87
|
-
|
|
88
|
-
IS_SOURCE=false
|
|
89
|
-
for src_ext in $SOURCE_EXTS; do
|
|
90
|
-
if [ "$EXT_LOWER" = "$src_ext" ]; then
|
|
91
|
-
IS_SOURCE=true
|
|
92
|
-
break
|
|
93
|
-
fi
|
|
94
|
-
done
|
|
95
|
-
|
|
96
|
-
if [ "$IS_SOURCE" = true ]; then
|
|
97
|
-
# Emit warning but allow (soft enforcement)
|
|
98
|
-
WARNING="[DELEGATION NOTICE] Direct $TOOL_NAME on source file: $FILE_PATH
|
|
99
|
-
|
|
100
|
-
Recommended: Delegate to executor agent instead:
|
|
101
|
-
Task(subagent_type=\"oh-my-claudecode:executor\", model=\"sonnet\", prompt=\"...\")
|
|
102
|
-
|
|
103
|
-
This is a soft warning. Operation will proceed."
|
|
104
|
-
|
|
105
|
-
# Escape for JSON
|
|
106
|
-
WARNING_ESCAPED=$(echo "$WARNING" | jq -Rs .)
|
|
107
|
-
echo "{\"continue\": true, \"message\": $WARNING_ESCAPED}"
|
|
108
|
-
exit 0
|
|
109
|
-
fi
|
|
110
|
-
|
|
111
|
-
# Not a source file, allow without warning
|
|
112
|
-
echo '{"continue": true}'
|
|
113
|
-
exit 0
|
|
@@ -1,62 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Session Start Hook
|
|
3
|
-
# Restores persistent mode states and injects context when session starts
|
|
4
|
-
|
|
5
|
-
# Read stdin
|
|
6
|
-
INPUT=$(cat)
|
|
7
|
-
|
|
8
|
-
# Get directory
|
|
9
|
-
DIRECTORY=""
|
|
10
|
-
if command -v jq &> /dev/null; then
|
|
11
|
-
DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
|
|
12
|
-
fi
|
|
13
|
-
|
|
14
|
-
if [ -z "$DIRECTORY" ]; then
|
|
15
|
-
DIRECTORY=$(pwd)
|
|
16
|
-
fi
|
|
17
|
-
|
|
18
|
-
MESSAGES=""
|
|
19
|
-
|
|
20
|
-
# Check for active ultrawork state
|
|
21
|
-
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ] || [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
|
|
22
|
-
if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
|
|
23
|
-
ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
24
|
-
else
|
|
25
|
-
ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
|
|
26
|
-
fi
|
|
27
|
-
|
|
28
|
-
IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)
|
|
29
|
-
if [ "$IS_ACTIVE" = "true" ]; then
|
|
30
|
-
STARTED_AT=$(echo "$ULTRAWORK_STATE" | jq -r '.started_at // ""' 2>/dev/null)
|
|
31
|
-
PROMPT=$(echo "$ULTRAWORK_STATE" | jq -r '.original_prompt // ""' 2>/dev/null)
|
|
32
|
-
MESSAGES="$MESSAGES<session-restore>\\n\\n[ULTRAWORK MODE RESTORED]\\n\\nYou have an active ultrawork session from $STARTED_AT.\\nOriginal task: $PROMPT\\n\\nContinue working in ultrawork mode until all tasks are complete.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
33
|
-
fi
|
|
34
|
-
fi
|
|
35
|
-
|
|
36
|
-
# Check for incomplete todos
|
|
37
|
-
INCOMPLETE_COUNT=0
|
|
38
|
-
TODOS_DIR="$HOME/.claude/todos"
|
|
39
|
-
if [ -d "$TODOS_DIR" ]; then
|
|
40
|
-
for todo_file in "$TODOS_DIR"/*.json; do
|
|
41
|
-
if [ -f "$todo_file" ]; then
|
|
42
|
-
if command -v jq &> /dev/null; then
|
|
43
|
-
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
44
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
45
|
-
fi
|
|
46
|
-
fi
|
|
47
|
-
done
|
|
48
|
-
fi
|
|
49
|
-
|
|
50
|
-
if [ "$INCOMPLETE_COUNT" -gt 0 ]; then
|
|
51
|
-
MESSAGES="$MESSAGES<session-restore>\\n\\n[PENDING TASKS DETECTED]\\n\\nYou have $INCOMPLETE_COUNT incomplete tasks from a previous session.\\nPlease continue working on these tasks.\\n\\n</session-restore>\\n\\n---\\n\\n"
|
|
52
|
-
fi
|
|
53
|
-
|
|
54
|
-
# Output message if we have any
|
|
55
|
-
if [ -n "$MESSAGES" ]; then
|
|
56
|
-
# Escape for JSON
|
|
57
|
-
MESSAGES_ESCAPED=$(echo "$MESSAGES" | sed 's/"/\\"/g')
|
|
58
|
-
echo "{\"continue\": true, \"message\": \"$MESSAGES_ESCAPED\"}"
|
|
59
|
-
else
|
|
60
|
-
echo '{"continue": true}'
|
|
61
|
-
fi
|
|
62
|
-
exit 0
|
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
# OMC Stop Continuation Hook
|
|
3
|
-
# Checks for incomplete todos and injects continuation prompt
|
|
4
|
-
# Ported from oh-my-opencode's todo-continuation-enforcer
|
|
5
|
-
|
|
6
|
-
# Validate session ID to prevent path traversal attacks
|
|
7
|
-
is_valid_session_id() {
|
|
8
|
-
local id="$1"
|
|
9
|
-
if [ -z "$id" ]; then
|
|
10
|
-
return 1
|
|
11
|
-
fi
|
|
12
|
-
if echo "$id" | grep -qE '^[a-zA-Z0-9][a-zA-Z0-9_-]{0,255}$'; then
|
|
13
|
-
return 0
|
|
14
|
-
fi
|
|
15
|
-
return 1
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
# Read stdin
|
|
19
|
-
INPUT=$(cat)
|
|
20
|
-
|
|
21
|
-
# Get session ID if available
|
|
22
|
-
SESSION_ID=""
|
|
23
|
-
if command -v jq &> /dev/null; then
|
|
24
|
-
SESSION_ID=$(echo "$INPUT" | jq -r '.sessionId // .session_id // ""' 2>/dev/null)
|
|
25
|
-
fi
|
|
26
|
-
|
|
27
|
-
# Check for incomplete tasks in new Task system
|
|
28
|
-
TASKS_DIR="$HOME/.claude/tasks"
|
|
29
|
-
TASK_COUNT=0
|
|
30
|
-
JQ_AVAILABLE=false
|
|
31
|
-
if command -v jq &> /dev/null; then
|
|
32
|
-
JQ_AVAILABLE=true
|
|
33
|
-
fi
|
|
34
|
-
|
|
35
|
-
if [ -n "$SESSION_ID" ] && is_valid_session_id "$SESSION_ID" && [ -d "$TASKS_DIR/$SESSION_ID" ]; then
|
|
36
|
-
for task_file in "$TASKS_DIR/$SESSION_ID"/*.json; do
|
|
37
|
-
if [ -f "$task_file" ] && [ "$(basename "$task_file")" != ".lock" ]; then
|
|
38
|
-
if [ "$JQ_AVAILABLE" = "true" ]; then
|
|
39
|
-
STATUS=$(jq -r '.status // "pending"' "$task_file" 2>/dev/null)
|
|
40
|
-
# Match TypeScript isTaskIncomplete(): only pending/in_progress are incomplete
|
|
41
|
-
# 'deleted' and 'completed' are both treated as done
|
|
42
|
-
if [ "$STATUS" = "pending" ] || [ "$STATUS" = "in_progress" ]; then
|
|
43
|
-
TASK_COUNT=$((TASK_COUNT + 1))
|
|
44
|
-
fi
|
|
45
|
-
else
|
|
46
|
-
# Fallback: grep for incomplete status values (pending or in_progress)
|
|
47
|
-
if grep -qE '"status"[[:space:]]*:[[:space:]]*"(pending|in_progress)"' "$task_file" 2>/dev/null; then
|
|
48
|
-
TASK_COUNT=$((TASK_COUNT + 1))
|
|
49
|
-
fi
|
|
50
|
-
fi
|
|
51
|
-
fi
|
|
52
|
-
done
|
|
53
|
-
|
|
54
|
-
if [ "$JQ_AVAILABLE" = "false" ] && [ "$TASK_COUNT" -gt 0 ]; then
|
|
55
|
-
echo "[OMC WARNING] jq not installed - Task counting may be less accurate." >&2
|
|
56
|
-
fi
|
|
57
|
-
fi
|
|
58
|
-
|
|
59
|
-
# Check for incomplete todos in the Claude todos directory
|
|
60
|
-
TODOS_DIR="$HOME/.claude/todos"
|
|
61
|
-
if [ -d "$TODOS_DIR" ]; then
|
|
62
|
-
# Look for any todo files with incomplete items
|
|
63
|
-
INCOMPLETE_COUNT=0
|
|
64
|
-
for todo_file in "$TODOS_DIR"/*.json; do
|
|
65
|
-
if [ -f "$todo_file" ]; then
|
|
66
|
-
if command -v jq &> /dev/null; then
|
|
67
|
-
COUNT=$(jq '[.[] | select(.status != "completed" and .status != "cancelled")] | length' "$todo_file" 2>/dev/null || echo "0")
|
|
68
|
-
INCOMPLETE_COUNT=$((INCOMPLETE_COUNT + COUNT))
|
|
69
|
-
fi
|
|
70
|
-
fi
|
|
71
|
-
done
|
|
72
|
-
|
|
73
|
-
# Combine task and todo counts
|
|
74
|
-
TOTAL_INCOMPLETE=$((TASK_COUNT + INCOMPLETE_COUNT))
|
|
75
|
-
|
|
76
|
-
if [ "$TOTAL_INCOMPLETE" -gt 0 ]; then
|
|
77
|
-
# Use Task terminology if we have tasks, otherwise todos
|
|
78
|
-
if [ "$TASK_COUNT" -gt 0 ]; then
|
|
79
|
-
cat << EOF
|
|
80
|
-
{"continue": false, "reason": "[SYSTEM REMINDER - TASK CONTINUATION]\\n\\nIncomplete Tasks remain ($TOTAL_INCOMPLETE remaining). Continue working on the next pending Task.\\n\\n- Proceed without asking for permission\\n- Mark each Task complete when finished\\n- Do not stop until all Tasks are done"}
|
|
81
|
-
EOF
|
|
82
|
-
else
|
|
83
|
-
cat << EOF
|
|
84
|
-
{"continue": false, "reason": "[SYSTEM REMINDER - TODO CONTINUATION]\\n\\nIncomplete tasks remain in your todo list ($TOTAL_INCOMPLETE remaining). Continue working on the next pending task.\\n\\n- Proceed without asking for permission\\n- Mark each task complete when finished\\n- Do not stop until all tasks are done"}
|
|
85
|
-
EOF
|
|
86
|
-
fi
|
|
87
|
-
exit 0
|
|
88
|
-
fi
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
# No incomplete todos - allow stop
|
|
92
|
-
echo '{"continue": true}'
|
|
93
|
-
exit 0
|