oh-my-claude-sisyphus 1.11.1 → 2.0.1

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.
Files changed (211) hide show
  1. package/README.md +37 -12
  2. package/dist/__tests__/example.test.d.ts +2 -0
  3. package/dist/__tests__/example.test.d.ts.map +1 -0
  4. package/dist/__tests__/example.test.js +20 -0
  5. package/dist/__tests__/example.test.js.map +1 -0
  6. package/dist/__tests__/hooks.test.d.ts +2 -0
  7. package/dist/__tests__/hooks.test.d.ts.map +1 -0
  8. package/dist/__tests__/hooks.test.js +644 -0
  9. package/dist/__tests__/hooks.test.js.map +1 -0
  10. package/dist/__tests__/installer.test.d.ts +2 -0
  11. package/dist/__tests__/installer.test.d.ts.map +1 -0
  12. package/dist/__tests__/installer.test.js +369 -0
  13. package/dist/__tests__/installer.test.js.map +1 -0
  14. package/dist/__tests__/model-routing.test.d.ts +2 -0
  15. package/dist/__tests__/model-routing.test.d.ts.map +1 -0
  16. package/dist/__tests__/model-routing.test.js +814 -0
  17. package/dist/__tests__/model-routing.test.js.map +1 -0
  18. package/dist/__tests__/skills.test.d.ts +2 -0
  19. package/dist/__tests__/skills.test.d.ts.map +1 -0
  20. package/dist/__tests__/skills.test.js +126 -0
  21. package/dist/__tests__/skills.test.js.map +1 -0
  22. package/dist/__tests__/types.test.d.ts +2 -0
  23. package/dist/__tests__/types.test.d.ts.map +1 -0
  24. package/dist/__tests__/types.test.js +77 -0
  25. package/dist/__tests__/types.test.js.map +1 -0
  26. package/dist/agents/definitions.d.ts +1 -1
  27. package/dist/agents/definitions.d.ts.map +1 -1
  28. package/dist/agents/definitions.js +35 -3
  29. package/dist/agents/definitions.js.map +1 -1
  30. package/dist/agents/index.d.ts +1 -1
  31. package/dist/agents/index.d.ts.map +1 -1
  32. package/dist/agents/index.js +3 -1
  33. package/dist/agents/index.js.map +1 -1
  34. package/dist/agents/oracle.d.ts.map +1 -1
  35. package/dist/agents/oracle.js +43 -1
  36. package/dist/agents/oracle.js.map +1 -1
  37. package/dist/agents/orchestrator-sisyphus.js +2 -2
  38. package/dist/agents/orchestrator-sisyphus.js.map +1 -1
  39. package/dist/cli/index.js +22 -11
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/config/loader.d.ts.map +1 -1
  42. package/dist/config/loader.js +49 -0
  43. package/dist/config/loader.js.map +1 -1
  44. package/dist/features/auto-update.d.ts.map +1 -1
  45. package/dist/features/auto-update.js +14 -3
  46. package/dist/features/auto-update.js.map +1 -1
  47. package/dist/features/builtin-skills/skills.d.ts.map +1 -1
  48. package/dist/features/builtin-skills/skills.js +0 -1351
  49. package/dist/features/builtin-skills/skills.js.map +1 -1
  50. package/dist/features/index.d.ts +1 -0
  51. package/dist/features/index.d.ts.map +1 -1
  52. package/dist/features/index.js +14 -0
  53. package/dist/features/index.js.map +1 -1
  54. package/dist/features/model-routing/index.d.ts +34 -0
  55. package/dist/features/model-routing/index.d.ts.map +1 -0
  56. package/dist/features/model-routing/index.js +48 -0
  57. package/dist/features/model-routing/index.js.map +1 -0
  58. package/dist/features/model-routing/prompts/haiku.d.ts +54 -0
  59. package/dist/features/model-routing/prompts/haiku.d.ts.map +1 -0
  60. package/dist/features/model-routing/prompts/haiku.js +141 -0
  61. package/dist/features/model-routing/prompts/haiku.js.map +1 -0
  62. package/dist/features/model-routing/prompts/index.d.ts +45 -0
  63. package/dist/features/model-routing/prompts/index.d.ts.map +1 -0
  64. package/dist/features/model-routing/prompts/index.js +116 -0
  65. package/dist/features/model-routing/prompts/index.js.map +1 -0
  66. package/dist/features/model-routing/prompts/opus.d.ts +34 -0
  67. package/dist/features/model-routing/prompts/opus.d.ts.map +1 -0
  68. package/dist/features/model-routing/prompts/opus.js +153 -0
  69. package/dist/features/model-routing/prompts/opus.js.map +1 -0
  70. package/dist/features/model-routing/prompts/sonnet.d.ts +38 -0
  71. package/dist/features/model-routing/prompts/sonnet.d.ts.map +1 -0
  72. package/dist/features/model-routing/prompts/sonnet.js +149 -0
  73. package/dist/features/model-routing/prompts/sonnet.js.map +1 -0
  74. package/dist/features/model-routing/router.d.ts +92 -0
  75. package/dist/features/model-routing/router.d.ts.map +1 -0
  76. package/dist/features/model-routing/router.js +267 -0
  77. package/dist/features/model-routing/router.js.map +1 -0
  78. package/dist/features/model-routing/rules.d.ts +32 -0
  79. package/dist/features/model-routing/rules.d.ts.map +1 -0
  80. package/dist/features/model-routing/rules.js +224 -0
  81. package/dist/features/model-routing/rules.js.map +1 -0
  82. package/dist/features/model-routing/scorer.d.ts +35 -0
  83. package/dist/features/model-routing/scorer.d.ts.map +1 -0
  84. package/dist/features/model-routing/scorer.js +241 -0
  85. package/dist/features/model-routing/scorer.js.map +1 -0
  86. package/dist/features/model-routing/signals.d.ts +26 -0
  87. package/dist/features/model-routing/signals.d.ts.map +1 -0
  88. package/dist/features/model-routing/signals.js +283 -0
  89. package/dist/features/model-routing/signals.js.map +1 -0
  90. package/dist/features/model-routing/types.d.ts +195 -0
  91. package/dist/features/model-routing/types.d.ts.map +1 -0
  92. package/dist/features/model-routing/types.js +86 -0
  93. package/dist/features/model-routing/types.js.map +1 -0
  94. package/dist/hooks/agent-usage-reminder/index.d.ts +1 -1
  95. package/dist/hooks/agent-usage-reminder/index.d.ts.map +1 -1
  96. package/dist/hooks/agent-usage-reminder/index.js +1 -1
  97. package/dist/hooks/agent-usage-reminder/index.js.map +1 -1
  98. package/dist/hooks/auto-slash-command/executor.js.map +1 -1
  99. package/dist/hooks/auto-slash-command/index.d.ts +3 -3
  100. package/dist/hooks/auto-slash-command/index.d.ts.map +1 -1
  101. package/dist/hooks/auto-slash-command/index.js.map +1 -1
  102. package/dist/hooks/background-notification/index.js +1 -1
  103. package/dist/hooks/background-notification/index.js.map +1 -1
  104. package/dist/hooks/bridge.d.ts.map +1 -1
  105. package/dist/hooks/bridge.js.map +1 -1
  106. package/dist/hooks/comment-checker/filters.d.ts +1 -1
  107. package/dist/hooks/comment-checker/filters.d.ts.map +1 -1
  108. package/dist/hooks/comment-checker/filters.js +1 -1
  109. package/dist/hooks/comment-checker/filters.js.map +1 -1
  110. package/dist/hooks/comment-checker/index.js +1 -1
  111. package/dist/hooks/comment-checker/index.js.map +1 -1
  112. package/dist/hooks/context-window-limit-recovery/index.d.ts.map +1 -1
  113. package/dist/hooks/context-window-limit-recovery/index.js.map +1 -1
  114. package/dist/hooks/index.d.ts +3 -3
  115. package/dist/hooks/index.d.ts.map +1 -1
  116. package/dist/hooks/index.js +3 -3
  117. package/dist/hooks/index.js.map +1 -1
  118. package/dist/hooks/keyword-detector/index.d.ts +1 -1
  119. package/dist/hooks/keyword-detector/index.d.ts.map +1 -1
  120. package/dist/hooks/keyword-detector/index.js +1 -1
  121. package/dist/hooks/keyword-detector/index.js.map +1 -1
  122. package/dist/hooks/persistent-mode/index.d.ts.map +1 -1
  123. package/dist/hooks/persistent-mode/index.js.map +1 -1
  124. package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
  125. package/dist/hooks/plugin-patterns/index.js +12 -9
  126. package/dist/hooks/plugin-patterns/index.js.map +1 -1
  127. package/dist/hooks/preemptive-compaction/index.d.ts +2 -2
  128. package/dist/hooks/preemptive-compaction/index.d.ts.map +1 -1
  129. package/dist/hooks/preemptive-compaction/index.js +1 -11
  130. package/dist/hooks/preemptive-compaction/index.js.map +1 -1
  131. package/dist/hooks/ralph-loop/index.js.map +1 -1
  132. package/dist/hooks/rules-injector/matcher.js +1 -1
  133. package/dist/hooks/rules-injector/matcher.js.map +1 -1
  134. package/dist/hooks/session-recovery/index.d.ts +1 -1
  135. package/dist/hooks/session-recovery/index.d.ts.map +1 -1
  136. package/dist/hooks/session-recovery/index.js +1 -1
  137. package/dist/hooks/session-recovery/index.js.map +1 -1
  138. package/dist/hooks/sisyphus-orchestrator/index.d.ts.map +1 -1
  139. package/dist/hooks/sisyphus-orchestrator/index.js.map +1 -1
  140. package/dist/hooks/ultrawork-state/index.js +1 -1
  141. package/dist/hooks/ultrawork-state/index.js.map +1 -1
  142. package/dist/index.d.ts +2 -2
  143. package/dist/index.d.ts.map +1 -1
  144. package/dist/index.js +4 -2
  145. package/dist/index.js.map +1 -1
  146. package/dist/installer/hooks.d.ts +1 -1
  147. package/dist/installer/hooks.js +1 -1
  148. package/dist/installer/index.d.ts +8 -7
  149. package/dist/installer/index.d.ts.map +1 -1
  150. package/dist/installer/index.js +648 -2141
  151. package/dist/installer/index.js.map +1 -1
  152. package/dist/shared/types.d.ts +25 -0
  153. package/dist/shared/types.d.ts.map +1 -1
  154. package/dist/tools/lsp/servers.d.ts.map +1 -1
  155. package/dist/tools/lsp/servers.js +2 -1
  156. package/dist/tools/lsp/servers.js.map +1 -1
  157. package/package.json +18 -10
  158. package/scripts/install.sh +236 -260
  159. package/scripts/keyword-detector.mjs +209 -0
  160. package/scripts/persistent-mode.mjs +241 -0
  161. package/scripts/post-tool-verifier.mjs +217 -0
  162. package/scripts/pre-tool-enforcer.mjs +99 -0
  163. package/scripts/test-pr25.sh +525 -0
  164. package/dist/agents/model-lists.d.ts +0 -26
  165. package/dist/agents/model-lists.d.ts.map +0 -1
  166. package/dist/agents/model-lists.js +0 -62
  167. package/dist/agents/model-lists.js.map +0 -1
  168. package/dist/auth/index.d.ts +0 -10
  169. package/dist/auth/index.d.ts.map +0 -1
  170. package/dist/auth/index.js +0 -13
  171. package/dist/auth/index.js.map +0 -1
  172. package/dist/auth/manager.d.ts +0 -54
  173. package/dist/auth/manager.d.ts.map +0 -1
  174. package/dist/auth/manager.js +0 -248
  175. package/dist/auth/manager.js.map +0 -1
  176. package/dist/auth/oauth-google.d.ts +0 -47
  177. package/dist/auth/oauth-google.d.ts.map +0 -1
  178. package/dist/auth/oauth-google.js +0 -280
  179. package/dist/auth/oauth-google.js.map +0 -1
  180. package/dist/auth/oauth-openai.d.ts +0 -46
  181. package/dist/auth/oauth-openai.d.ts.map +0 -1
  182. package/dist/auth/oauth-openai.js +0 -264
  183. package/dist/auth/oauth-openai.js.map +0 -1
  184. package/dist/auth/pkce.d.ts +0 -14
  185. package/dist/auth/pkce.d.ts.map +0 -1
  186. package/dist/auth/pkce.js +0 -35
  187. package/dist/auth/pkce.js.map +0 -1
  188. package/dist/auth/storage.d.ts +0 -52
  189. package/dist/auth/storage.d.ts.map +0 -1
  190. package/dist/auth/storage.js +0 -230
  191. package/dist/auth/storage.js.map +0 -1
  192. package/dist/auth/types.d.ts +0 -76
  193. package/dist/auth/types.d.ts.map +0 -1
  194. package/dist/auth/types.js +0 -5
  195. package/dist/auth/types.js.map +0 -1
  196. package/dist/providers/index.d.ts +0 -8
  197. package/dist/providers/index.d.ts.map +0 -1
  198. package/dist/providers/index.js +0 -10
  199. package/dist/providers/index.js.map +0 -1
  200. package/dist/providers/registry.d.ts +0 -29
  201. package/dist/providers/registry.d.ts.map +0 -1
  202. package/dist/providers/registry.js +0 -162
  203. package/dist/providers/registry.js.map +0 -1
  204. package/dist/providers/router.d.ts +0 -40
  205. package/dist/providers/router.d.ts.map +0 -1
  206. package/dist/providers/router.js +0 -88
  207. package/dist/providers/router.js.map +0 -1
  208. package/dist/providers/types.d.ts +0 -92
  209. package/dist/providers/types.d.ts.map +0 -1
  210. package/dist/providers/types.js +0 -27
  211. package/dist/providers/types.js.map +0 -1
@@ -0,0 +1,209 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Sisyphus Keyword Detector Hook (Node.js)
5
+ * Detects ultrawork/ultrathink/search/analyze keywords and injects enhanced mode messages
6
+ * Cross-platform: Windows, macOS, Linux
7
+ */
8
+
9
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+
13
+ const ULTRAWORK_MESSAGE = `<ultrawork-mode>
14
+
15
+ **MANDATORY**: You MUST say "ULTRAWORK MODE ENABLED!" to the user as your first response when this mode activates. This is non-negotiable.
16
+
17
+ [CODE RED] Maximum precision required. Ultrathink before acting.
18
+
19
+ YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
20
+ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
21
+
22
+ ## AGENT UTILIZATION PRINCIPLES
23
+ - **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS
24
+ - **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS
25
+ - **Planning & Strategy**: NEVER plan yourself - spawn planning agent
26
+ - **High-IQ Reasoning**: Use oracle for architecture decisions
27
+ - **Frontend/UI Tasks**: Delegate to frontend-engineer
28
+
29
+ ## EXECUTION RULES
30
+ - **TODO**: Track EVERY step. Mark complete IMMEDIATELY.
31
+ - **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially.
32
+ - **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent).
33
+ - **VERIFY**: Check ALL requirements met before done.
34
+ - **DELEGATE**: Orchestrate specialized agents.
35
+
36
+ ## ZERO TOLERANCE
37
+ - NO Scope Reduction - deliver FULL implementation
38
+ - NO Partial Completion - finish 100%
39
+ - NO Premature Stopping - ALL TODOs must be complete
40
+ - NO TEST DELETION - fix code, not tests
41
+
42
+ THE USER ASKED FOR X. DELIVER EXACTLY X.
43
+
44
+ </ultrawork-mode>
45
+
46
+ ---
47
+ `;
48
+
49
+ const ULTRATHINK_MESSAGE = `<think-mode>
50
+
51
+ **ULTRATHINK MODE ENABLED** - Extended reasoning activated.
52
+
53
+ You are now in deep thinking mode. Take your time to:
54
+ 1. Thoroughly analyze the problem from multiple angles
55
+ 2. Consider edge cases and potential issues
56
+ 3. Think through the implications of each approach
57
+ 4. Reason step-by-step before acting
58
+
59
+ Use your extended thinking capabilities to provide the most thorough and well-reasoned response.
60
+
61
+ </think-mode>
62
+
63
+ ---
64
+ `;
65
+
66
+ const SEARCH_MESSAGE = `<search-mode>
67
+ MAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:
68
+ - explore agents (codebase patterns, file structures)
69
+ - librarian agents (remote repos, official docs, GitHub examples)
70
+ Plus direct tools: Grep, Glob
71
+ NEVER stop at first result - be exhaustive.
72
+ </search-mode>
73
+
74
+ ---
75
+ `;
76
+
77
+ const ANALYZE_MESSAGE = `<analyze-mode>
78
+ ANALYSIS MODE. Gather context before diving deep:
79
+
80
+ CONTEXT GATHERING (parallel):
81
+ - 1-2 explore agents (codebase patterns, implementations)
82
+ - 1-2 librarian agents (if external library involved)
83
+ - Direct tools: Grep, Glob, LSP for targeted searches
84
+
85
+ IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
86
+ - Consult oracle agent for strategic guidance
87
+
88
+ SYNTHESIZE findings before proceeding.
89
+ </analyze-mode>
90
+
91
+ ---
92
+ `;
93
+
94
+ // Read all stdin
95
+ async function readStdin() {
96
+ const chunks = [];
97
+ for await (const chunk of process.stdin) {
98
+ chunks.push(chunk);
99
+ }
100
+ return Buffer.concat(chunks).toString('utf-8');
101
+ }
102
+
103
+ // Extract prompt from various JSON structures
104
+ function extractPrompt(input) {
105
+ try {
106
+ const data = JSON.parse(input);
107
+ if (data.prompt) return data.prompt;
108
+ if (data.message?.content) return data.message.content;
109
+ if (Array.isArray(data.parts)) {
110
+ return data.parts
111
+ .filter(p => p.type === 'text')
112
+ .map(p => p.text)
113
+ .join(' ');
114
+ }
115
+ return '';
116
+ } catch {
117
+ // Fallback: try to extract with regex
118
+ const match = input.match(/"(?:prompt|content|text)"\s*:\s*"([^"]+)"/);
119
+ return match ? match[1] : '';
120
+ }
121
+ }
122
+
123
+ // Remove code blocks to prevent false positives
124
+ function removeCodeBlocks(text) {
125
+ return text
126
+ .replace(/```[\s\S]*?```/g, '')
127
+ .replace(/`[^`]+`/g, '');
128
+ }
129
+
130
+ // Create ultrawork state file
131
+ function activateUltraworkState(directory, prompt) {
132
+ const state = {
133
+ active: true,
134
+ started_at: new Date().toISOString(),
135
+ original_prompt: prompt,
136
+ reinforcement_count: 0,
137
+ last_checked_at: new Date().toISOString()
138
+ };
139
+
140
+ // Write to local .sisyphus directory
141
+ const localDir = join(directory, '.sisyphus');
142
+ if (!existsSync(localDir)) {
143
+ try { mkdirSync(localDir, { recursive: true }); } catch {}
144
+ }
145
+ try { writeFileSync(join(localDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
146
+
147
+ // Write to global .claude directory
148
+ const globalDir = join(homedir(), '.claude');
149
+ if (!existsSync(globalDir)) {
150
+ try { mkdirSync(globalDir, { recursive: true }); } catch {}
151
+ }
152
+ try { writeFileSync(join(globalDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
153
+ }
154
+
155
+ // Main
156
+ async function main() {
157
+ try {
158
+ const input = await readStdin();
159
+ if (!input.trim()) {
160
+ console.log(JSON.stringify({ continue: true }));
161
+ return;
162
+ }
163
+
164
+ let data = {};
165
+ try { data = JSON.parse(input); } catch {}
166
+ const directory = data.directory || process.cwd();
167
+
168
+ const prompt = extractPrompt(input);
169
+ if (!prompt) {
170
+ console.log(JSON.stringify({ continue: true }));
171
+ return;
172
+ }
173
+
174
+ const cleanPrompt = removeCodeBlocks(prompt).toLowerCase();
175
+
176
+ // Check for ultrawork keywords (highest priority)
177
+ if (/\b(ultrawork|ulw|uw)\b/.test(cleanPrompt)) {
178
+ activateUltraworkState(directory, prompt);
179
+ console.log(JSON.stringify({ continue: true, message: ULTRAWORK_MESSAGE }));
180
+ return;
181
+ }
182
+
183
+ // Check for ultrathink/think keywords
184
+ if (/\b(ultrathink|think)\b/.test(cleanPrompt)) {
185
+ console.log(JSON.stringify({ continue: true, message: ULTRATHINK_MESSAGE }));
186
+ return;
187
+ }
188
+
189
+ // Check for search keywords
190
+ if (/\b(search|find|locate|lookup|explore|discover|scan|grep|query|browse|detect|trace|seek|track|pinpoint|hunt)\b|where\s+is|show\s+me|list\s+all/.test(cleanPrompt)) {
191
+ console.log(JSON.stringify({ continue: true, message: SEARCH_MESSAGE }));
192
+ return;
193
+ }
194
+
195
+ // Check for analyze keywords
196
+ if (/\b(analyze|analyse|investigate|examine|research|study|deep.?dive|inspect|audit|evaluate|assess|review|diagnose|scrutinize|dissect|debug|comprehend|interpret|breakdown|understand)\b|why\s+is|how\s+does|how\s+to/.test(cleanPrompt)) {
197
+ console.log(JSON.stringify({ continue: true, message: ANALYZE_MESSAGE }));
198
+ return;
199
+ }
200
+
201
+ // No keywords detected
202
+ console.log(JSON.stringify({ continue: true }));
203
+ } catch (error) {
204
+ // On any error, allow continuation
205
+ console.log(JSON.stringify({ continue: true }));
206
+ }
207
+ }
208
+
209
+ main();
@@ -0,0 +1,241 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * Sisyphus Persistent Mode Hook (Node.js)
5
+ * Unified handler for ultrawork, ralph-loop, and todo continuation
6
+ * Cross-platform: Windows, macOS, Linux
7
+ */
8
+
9
+ import { existsSync, readFileSync, writeFileSync, readdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+
13
+ // Read all stdin
14
+ async function readStdin() {
15
+ const chunks = [];
16
+ for await (const chunk of process.stdin) {
17
+ chunks.push(chunk);
18
+ }
19
+ return Buffer.concat(chunks).toString('utf-8');
20
+ }
21
+
22
+ // Read JSON file safely
23
+ function readJsonFile(path) {
24
+ try {
25
+ if (!existsSync(path)) return null;
26
+ return JSON.parse(readFileSync(path, 'utf-8'));
27
+ } catch {
28
+ return null;
29
+ }
30
+ }
31
+
32
+ // Write JSON file safely
33
+ function writeJsonFile(path, data) {
34
+ try {
35
+ writeFileSync(path, JSON.stringify(data, null, 2));
36
+ return true;
37
+ } catch {
38
+ return false;
39
+ }
40
+ }
41
+
42
+ // Count incomplete todos
43
+ function countIncompleteTodos(todosDir, projectDir) {
44
+ let count = 0;
45
+
46
+ // Check global todos
47
+ if (existsSync(todosDir)) {
48
+ try {
49
+ const files = readdirSync(todosDir).filter(f => f.endsWith('.json'));
50
+ for (const file of files) {
51
+ const todos = readJsonFile(join(todosDir, file));
52
+ if (Array.isArray(todos)) {
53
+ count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
54
+ }
55
+ }
56
+ } catch {}
57
+ }
58
+
59
+ // Check project todos
60
+ for (const path of [
61
+ join(projectDir, '.sisyphus', 'todos.json'),
62
+ join(projectDir, '.claude', 'todos.json')
63
+ ]) {
64
+ const data = readJsonFile(path);
65
+ const todos = data?.todos || data;
66
+ if (Array.isArray(todos)) {
67
+ count += todos.filter(t => t.status !== 'completed' && t.status !== 'cancelled').length;
68
+ }
69
+ }
70
+
71
+ return count;
72
+ }
73
+
74
+ async function main() {
75
+ try {
76
+ const input = await readStdin();
77
+ let data = {};
78
+ try { data = JSON.parse(input); } catch {}
79
+
80
+ const directory = data.directory || process.cwd();
81
+ const todosDir = join(homedir(), '.claude', 'todos');
82
+
83
+ // Check for ultrawork state
84
+ let ultraworkState = readJsonFile(join(directory, '.sisyphus', 'ultrawork-state.json'))
85
+ || readJsonFile(join(homedir(), '.claude', 'ultrawork-state.json'));
86
+
87
+ // Check for ralph loop state
88
+ const ralphState = readJsonFile(join(directory, '.sisyphus', 'ralph-state.json'));
89
+
90
+ // Check for verification state
91
+ const verificationState = readJsonFile(join(directory, '.sisyphus', 'ralph-verification.json'));
92
+
93
+ // Count incomplete todos
94
+ const incompleteCount = countIncompleteTodos(todosDir, directory);
95
+
96
+ // Priority 1: Ralph Loop with Oracle Verification
97
+ if (ralphState?.active) {
98
+ const iteration = ralphState.iteration || 1;
99
+ const maxIter = ralphState.max_iterations || 10;
100
+
101
+ // Check if oracle verification is pending
102
+ if (verificationState?.pending) {
103
+ const attempt = (verificationState.verification_attempts || 0) + 1;
104
+ const maxAttempts = verificationState.max_verification_attempts || 3;
105
+
106
+ console.log(JSON.stringify({
107
+ continue: false,
108
+ reason: `<ralph-verification>
109
+
110
+ [ORACLE VERIFICATION REQUIRED - Attempt ${attempt}/${maxAttempts}]
111
+
112
+ The agent claims the task is complete. Before accepting, YOU MUST verify with Oracle.
113
+
114
+ **Original Task:**
115
+ ${verificationState.original_task || ralphState.prompt || 'No task specified'}
116
+
117
+ **Completion Claim:**
118
+ ${verificationState.completion_claim || 'Task marked complete'}
119
+
120
+ ${verificationState.oracle_feedback ? `**Previous Oracle Feedback (rejected):**
121
+ ${verificationState.oracle_feedback}
122
+ ` : ''}
123
+
124
+ ## MANDATORY VERIFICATION STEPS
125
+
126
+ 1. **Spawn Oracle Agent** for verification
127
+ 2. **Oracle must check:**
128
+ - Are ALL requirements from the original task met?
129
+ - Is the implementation complete, not partial?
130
+ - Are there any obvious bugs or issues?
131
+ - Does the code compile/run without errors?
132
+ - Are tests passing (if applicable)?
133
+
134
+ 3. **Based on Oracle's response:**
135
+ - If APPROVED: Output \`<oracle-approved>VERIFIED_COMPLETE</oracle-approved>\`
136
+ - If REJECTED: Continue working on the identified issues
137
+
138
+ </ralph-verification>
139
+
140
+ ---
141
+ `
142
+ }));
143
+ return;
144
+ }
145
+
146
+ if (iteration < maxIter) {
147
+ const newIter = iteration + 1;
148
+ ralphState.iteration = newIter;
149
+ writeJsonFile(join(directory, '.sisyphus', 'ralph-state.json'), ralphState);
150
+
151
+ console.log(JSON.stringify({
152
+ continue: false,
153
+ reason: `<ralph-loop-continuation>
154
+
155
+ [RALPH LOOP - ITERATION ${newIter}/${maxIter}]
156
+
157
+ Your previous attempt did not output the completion promise. The work is NOT done yet.
158
+
159
+ CRITICAL INSTRUCTIONS:
160
+ 1. Review your progress and the original task
161
+ 2. Check your todo list - are ALL items marked complete?
162
+ 3. Continue from where you left off
163
+ 4. When FULLY complete, output: <promise>${ralphState.completion_promise || 'TASK_COMPLETE'}</promise>
164
+ 5. Do NOT stop until the task is truly done
165
+
166
+ ${ralphState.prompt ? `Original task: ${ralphState.prompt}` : ''}
167
+
168
+ </ralph-loop-continuation>
169
+
170
+ ---
171
+ `
172
+ }));
173
+ return;
174
+ }
175
+ }
176
+
177
+ // Priority 2: Ultrawork with incomplete todos
178
+ if (ultraworkState?.active && incompleteCount > 0) {
179
+ const newCount = (ultraworkState.reinforcement_count || 0) + 1;
180
+ ultraworkState.reinforcement_count = newCount;
181
+ ultraworkState.last_checked_at = new Date().toISOString();
182
+
183
+ writeJsonFile(join(directory, '.sisyphus', 'ultrawork-state.json'), ultraworkState);
184
+
185
+ console.log(JSON.stringify({
186
+ continue: false,
187
+ reason: `<ultrawork-persistence>
188
+
189
+ [ULTRAWORK MODE STILL ACTIVE - Reinforcement #${newCount}]
190
+
191
+ Your ultrawork session is NOT complete. ${incompleteCount} incomplete todos remain.
192
+
193
+ REMEMBER THE ULTRAWORK RULES:
194
+ - **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially
195
+ - **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent)
196
+ - **TODO**: Track EVERY step. Mark complete IMMEDIATELY after each
197
+ - **VERIFY**: Check ALL requirements met before done
198
+ - **NO Premature Stopping**: ALL TODOs must be complete
199
+
200
+ Continue working on the next pending task. DO NOT STOP until all tasks are marked complete.
201
+
202
+ ${ultraworkState.original_prompt ? `Original task: ${ultraworkState.original_prompt}` : ''}
203
+
204
+ </ultrawork-persistence>
205
+
206
+ ---
207
+ `
208
+ }));
209
+ return;
210
+ }
211
+
212
+ // Priority 3: Todo Continuation
213
+ if (incompleteCount > 0) {
214
+ console.log(JSON.stringify({
215
+ continue: false,
216
+ reason: `<todo-continuation>
217
+
218
+ [SYSTEM REMINDER - TODO CONTINUATION]
219
+
220
+ Incomplete tasks remain in your todo list (${incompleteCount} remaining). Continue working on the next pending task.
221
+
222
+ - Proceed without asking for permission
223
+ - Mark each task complete when finished
224
+ - Do not stop until all tasks are done
225
+
226
+ </todo-continuation>
227
+
228
+ ---
229
+ `
230
+ }));
231
+ return;
232
+ }
233
+
234
+ // No blocking needed
235
+ console.log(JSON.stringify({ continue: true }));
236
+ } catch (error) {
237
+ console.log(JSON.stringify({ continue: true }));
238
+ }
239
+ }
240
+
241
+ main();
@@ -0,0 +1,217 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * PostToolUse Hook: Verification Reminder System (Node.js)
5
+ * Monitors tool execution and provides contextual guidance
6
+ * Cross-platform: Windows, macOS, Linux
7
+ */
8
+
9
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
10
+ import { join } from 'path';
11
+ import { homedir } from 'os';
12
+
13
+ // State file for session tracking
14
+ const STATE_FILE = join(homedir(), '.claude', '.session-stats.json');
15
+
16
+ // Ensure state directory exists
17
+ try {
18
+ const stateDir = join(homedir(), '.claude');
19
+ if (!existsSync(stateDir)) {
20
+ mkdirSync(stateDir, { recursive: true });
21
+ }
22
+ } catch {}
23
+
24
+ // Read all stdin
25
+ async function readStdin() {
26
+ const chunks = [];
27
+ for await (const chunk of process.stdin) {
28
+ chunks.push(chunk);
29
+ }
30
+ return Buffer.concat(chunks).toString('utf-8');
31
+ }
32
+
33
+ // Load session statistics
34
+ function loadStats() {
35
+ try {
36
+ if (existsSync(STATE_FILE)) {
37
+ return JSON.parse(readFileSync(STATE_FILE, 'utf-8'));
38
+ }
39
+ } catch {}
40
+ return { sessions: {} };
41
+ }
42
+
43
+ // Save session statistics
44
+ function saveStats(stats) {
45
+ try {
46
+ writeFileSync(STATE_FILE, JSON.stringify(stats, null, 2));
47
+ } catch {}
48
+ }
49
+
50
+ // Update stats for this session
51
+ function updateStats(toolName, sessionId) {
52
+ const stats = loadStats();
53
+
54
+ if (!stats.sessions[sessionId]) {
55
+ stats.sessions[sessionId] = {
56
+ tool_counts: {},
57
+ last_tool: '',
58
+ total_calls: 0,
59
+ started_at: Math.floor(Date.now() / 1000)
60
+ };
61
+ }
62
+
63
+ const session = stats.sessions[sessionId];
64
+ session.tool_counts[toolName] = (session.tool_counts[toolName] || 0) + 1;
65
+ session.last_tool = toolName;
66
+ session.total_calls = (session.total_calls || 0) + 1;
67
+ session.updated_at = Math.floor(Date.now() / 1000);
68
+
69
+ saveStats(stats);
70
+ return session.tool_counts[toolName];
71
+ }
72
+
73
+ // Detect failures in Bash output
74
+ function detectBashFailure(output) {
75
+ const errorPatterns = [
76
+ /error:/i,
77
+ /failed/i,
78
+ /cannot/i,
79
+ /permission denied/i,
80
+ /command not found/i,
81
+ /no such file/i,
82
+ /exit code: [1-9]/i,
83
+ /exit status [1-9]/i,
84
+ /fatal:/i,
85
+ /abort/i,
86
+ ];
87
+
88
+ return errorPatterns.some(pattern => pattern.test(output));
89
+ }
90
+
91
+ // Detect background operation
92
+ function detectBackgroundOperation(output) {
93
+ const bgPatterns = [
94
+ /started/i,
95
+ /running/i,
96
+ /background/i,
97
+ /async/i,
98
+ /task_id/i,
99
+ /spawned/i,
100
+ ];
101
+
102
+ return bgPatterns.some(pattern => pattern.test(output));
103
+ }
104
+
105
+ // Detect write failure
106
+ function detectWriteFailure(output) {
107
+ const errorPatterns = [
108
+ /error/i,
109
+ /failed/i,
110
+ /permission denied/i,
111
+ /read-only/i,
112
+ /not found/i,
113
+ ];
114
+
115
+ return errorPatterns.some(pattern => pattern.test(output));
116
+ }
117
+
118
+ // Generate contextual message
119
+ function generateMessage(toolName, toolOutput, sessionId, toolCount) {
120
+ let message = '';
121
+
122
+ switch (toolName) {
123
+ case 'Bash':
124
+ if (detectBashFailure(toolOutput)) {
125
+ message = 'Command failed. Please investigate the error and fix before continuing.';
126
+ } else if (detectBackgroundOperation(toolOutput)) {
127
+ message = 'Background operation detected. Remember to verify results before proceeding.';
128
+ }
129
+ break;
130
+
131
+ case 'Task':
132
+ if (detectWriteFailure(toolOutput)) {
133
+ message = 'Task delegation failed. Verify agent name and parameters.';
134
+ } else if (detectBackgroundOperation(toolOutput)) {
135
+ message = 'Background task launched. Use TaskOutput to check results when needed.';
136
+ } else if (toolCount > 5) {
137
+ message = `Multiple tasks delegated (${toolCount} total). Track their completion status.`;
138
+ }
139
+ break;
140
+
141
+ case 'Edit':
142
+ if (detectWriteFailure(toolOutput)) {
143
+ message = 'Edit operation failed. Verify file exists and content matches exactly.';
144
+ } else {
145
+ message = 'Code modified. Verify changes work as expected before marking complete.';
146
+ }
147
+ break;
148
+
149
+ case 'Write':
150
+ if (detectWriteFailure(toolOutput)) {
151
+ message = 'Write operation failed. Check file permissions and directory existence.';
152
+ } else {
153
+ message = 'File written. Test the changes to ensure they work correctly.';
154
+ }
155
+ break;
156
+
157
+ case 'TodoWrite':
158
+ if (/created|added/i.test(toolOutput)) {
159
+ message = 'Todo list updated. Proceed with next task on the list.';
160
+ } else if (/completed|done/i.test(toolOutput)) {
161
+ message = 'Task marked complete. Continue with remaining todos.';
162
+ } else if (/in_progress/i.test(toolOutput)) {
163
+ message = 'Task marked in progress. Focus on completing this task.';
164
+ }
165
+ break;
166
+
167
+ case 'Read':
168
+ if (toolCount > 10) {
169
+ message = `Extensive reading (${toolCount} files). Consider using Grep for pattern searches.`;
170
+ }
171
+ break;
172
+
173
+ case 'Grep':
174
+ if (/^0$|no matches/i.test(toolOutput)) {
175
+ message = 'No matches found. Verify pattern syntax or try broader search.';
176
+ }
177
+ break;
178
+
179
+ case 'Glob':
180
+ if (!toolOutput.trim() || /no files/i.test(toolOutput)) {
181
+ message = 'No files matched pattern. Verify glob syntax and directory.';
182
+ }
183
+ break;
184
+ }
185
+
186
+ return message;
187
+ }
188
+
189
+ async function main() {
190
+ try {
191
+ const input = await readStdin();
192
+ const data = JSON.parse(input);
193
+
194
+ const toolName = data.toolName || '';
195
+ const toolOutput = data.toolOutput || '';
196
+ const sessionId = data.sessionId || 'unknown';
197
+
198
+ // Update session statistics
199
+ const toolCount = updateStats(toolName, sessionId);
200
+
201
+ // Generate contextual message
202
+ const message = generateMessage(toolName, toolOutput, sessionId, toolCount);
203
+
204
+ // Build response
205
+ const response = { continue: true };
206
+ if (message) {
207
+ response.message = message;
208
+ }
209
+
210
+ console.log(JSON.stringify(response, null, 2));
211
+ } catch (error) {
212
+ // On error, always continue
213
+ console.log(JSON.stringify({ continue: true }));
214
+ }
215
+ }
216
+
217
+ main();