oh-my-claude-sisyphus 3.7.0 → 3.7.2

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 (66) hide show
  1. package/README.md +24 -1
  2. package/commands/hud.md +37 -5
  3. package/commands/omc-setup.md +105 -0
  4. package/dist/__tests__/hud/analytics-display.test.js +137 -1
  5. package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
  6. package/dist/__tests__/hud-windows.test.d.ts +2 -0
  7. package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
  8. package/dist/__tests__/hud-windows.test.js +91 -0
  9. package/dist/__tests__/hud-windows.test.js.map +1 -0
  10. package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -1
  11. package/dist/features/rate-limit-wait/daemon.js +41 -1
  12. package/dist/features/rate-limit-wait/daemon.js.map +1 -1
  13. package/dist/features/state-manager/index.d.ts.map +1 -1
  14. package/dist/features/state-manager/index.js +4 -1
  15. package/dist/features/state-manager/index.js.map +1 -1
  16. package/dist/hooks/permission-handler/__tests__/index.test.js +47 -0
  17. package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -1
  18. package/dist/hooks/permission-handler/index.d.ts +1 -1
  19. package/dist/hooks/permission-handler/index.d.ts.map +1 -1
  20. package/dist/hooks/permission-handler/index.js +11 -15
  21. package/dist/hooks/permission-handler/index.js.map +1 -1
  22. package/dist/hooks/plugin-patterns/index.d.ts +5 -0
  23. package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
  24. package/dist/hooks/plugin-patterns/index.js +26 -1
  25. package/dist/hooks/plugin-patterns/index.js.map +1 -1
  26. package/dist/hooks/session-end/index.d.ts +0 -8
  27. package/dist/hooks/session-end/index.d.ts.map +1 -1
  28. package/dist/hooks/session-end/index.js +5 -12
  29. package/dist/hooks/session-end/index.js.map +1 -1
  30. package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
  31. package/dist/hooks/subagent-tracker/index.js +32 -17
  32. package/dist/hooks/subagent-tracker/index.js.map +1 -1
  33. package/dist/hud/analytics-display.d.ts +16 -0
  34. package/dist/hud/analytics-display.d.ts.map +1 -1
  35. package/dist/hud/analytics-display.js +35 -9
  36. package/dist/hud/analytics-display.js.map +1 -1
  37. package/dist/hud/render.d.ts.map +1 -1
  38. package/dist/hud/render.js +49 -18
  39. package/dist/hud/render.js.map +1 -1
  40. package/dist/hud/types.d.ts +2 -0
  41. package/dist/hud/types.d.ts.map +1 -1
  42. package/dist/hud/types.js +14 -0
  43. package/dist/hud/types.js.map +1 -1
  44. package/dist/installer/index.d.ts.map +1 -1
  45. package/dist/installer/index.js +3 -2
  46. package/dist/installer/index.js.map +1 -1
  47. package/hooks/keyword-detector.sh +4 -4
  48. package/hooks/persistent-mode.sh +10 -10
  49. package/hooks/session-start.sh +4 -4
  50. package/package.json +1 -1
  51. package/scripts/keyword-detector.mjs +4 -4
  52. package/scripts/persistent-mode.mjs +6 -6
  53. package/scripts/persistent-mode.sh +10 -10
  54. package/scripts/session-start.mjs +4 -4
  55. package/skills/hud/SKILL.md +37 -5
  56. package/skills/omc-setup/SKILL.md +162 -4
  57. package/skills/writer-memory/SKILL.md +443 -0
  58. package/skills/writer-memory/lib/character-tracker.ts +338 -0
  59. package/skills/writer-memory/lib/memory-manager.ts +804 -0
  60. package/skills/writer-memory/lib/relationship-graph.ts +400 -0
  61. package/skills/writer-memory/lib/scene-organizer.ts +544 -0
  62. package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
  63. package/skills/writer-memory/templates/synopsis-template.md +46 -0
  64. package/templates/hooks/keyword-detector.sh +4 -4
  65. package/templates/hooks/persistent-mode.sh +10 -10
  66. package/templates/hooks/session-start.sh +4 -4
@@ -0,0 +1,339 @@
1
+ /**
2
+ * Synopsis Builder - 정서 중심 시놉시스 생성기
3
+ *
4
+ * Korean writers think: emotion → relationship → event → plot
5
+ * NOT plot-first!
6
+ */
7
+
8
+ import { loadMemory, saveMemory, now } from './memory-manager';
9
+ import type { WriterMemory, Character, Relationship, Scene, SynopsisState } from './memory-manager';
10
+
11
+ // === Synopsis Generation ===
12
+
13
+ export function generateSynopsis(options?: {
14
+ protagonist?: string;
15
+ format?: 'full' | 'brief' | 'pitch';
16
+ }): string | null {
17
+ const memory = loadMemory();
18
+ if (!memory) return null;
19
+
20
+ const format = options?.format || 'full';
21
+ const protagonist = options?.protagonist;
22
+
23
+ const attitude = extractProtagonistAttitude(memory, protagonist);
24
+ const relationships = extractCoreRelationships(memory, protagonist);
25
+ const theme = extractEmotionalTheme(memory);
26
+ const genreContrast = extractGenreVsEmotion(memory);
27
+ const aftertaste = extractEndingAftertaste(memory);
28
+
29
+ switch (format) {
30
+ case 'brief':
31
+ return formatBriefSynopsis(attitude, relationships, theme, memory);
32
+ case 'pitch':
33
+ return formatPitchSynopsis(attitude, relationships, theme, genreContrast, memory);
34
+ default:
35
+ return formatFullSynopsis(attitude, relationships, theme, genreContrast, aftertaste, memory);
36
+ }
37
+ }
38
+
39
+ // === 5 Essential Element Extractors ===
40
+
41
+ function findProtagonist(memory: WriterMemory, name?: string): Character | null {
42
+ const chars = Object.values(memory.characters);
43
+ if (name) {
44
+ return chars.find(c => c.name === name || c.aliases?.includes(name)) || null;
45
+ }
46
+ return chars[0] || null;
47
+ }
48
+
49
+ export function extractProtagonistAttitude(memory: WriterMemory, protagonistName?: string): string {
50
+ const protagonist = findProtagonist(memory, protagonistName);
51
+
52
+ if (!protagonist) {
53
+ return '⚠️ 주인공 정보 없음. 캐릭터를 먼저 등록하세요.';
54
+ }
55
+
56
+ const parts: string[] = [];
57
+ if (protagonist.arc) parts.push(protagonist.arc);
58
+ if (protagonist.attitude) parts.push(protagonist.attitude);
59
+
60
+ if (parts.length === 0) {
61
+ return `⚠️ ${protagonist.name}의 태도 정보 미입력. arc와 attitude 필드를 채우세요.`;
62
+ }
63
+
64
+ return parts.join('. ');
65
+ }
66
+
67
+ export function extractCoreRelationships(memory: WriterMemory, protagonistName?: string): string {
68
+ const protagonist = findProtagonist(memory, protagonistName);
69
+
70
+ if (!protagonist) {
71
+ return '⚠️ 주인공 정보 없음.';
72
+ }
73
+
74
+ const rels = memory.relationships.filter(
75
+ r => r.from === protagonist.name || r.to === protagonist.name
76
+ );
77
+
78
+ if (rels.length === 0) {
79
+ return `⚠️ ${protagonist.name} 중심의 관계 정보 없음. 관계를 등록하세요.`;
80
+ }
81
+
82
+ return rels.map(r => {
83
+ const other = r.from === protagonist.name ? r.to : r.from;
84
+ return `${protagonist.name}-${other}: ${r.dynamic || r.type}`;
85
+ }).join('\n');
86
+ }
87
+
88
+ export function extractEmotionalTheme(memory: WriterMemory): string {
89
+ if (memory.themes.length === 0) {
90
+ return '⚠️ 테마 정보 없음. 작품의 정서적 주제를 입력하세요.';
91
+ }
92
+
93
+ return memory.themes.map(t => t.description || t.name).join('. ');
94
+ }
95
+
96
+ export function extractGenreVsEmotion(memory: WriterMemory): string {
97
+ const synopsis = memory.synopsis;
98
+ if (synopsis?.genreVsRealEmotion) {
99
+ return synopsis.genreVsRealEmotion;
100
+ }
101
+
102
+ const genre = memory.project.genre || '미지정';
103
+ return `장르: ${genre}. 실제 정서: 미정의. genreVsRealEmotion 필드를 입력하세요.`;
104
+ }
105
+
106
+ export function extractEndingAftertaste(memory: WriterMemory): string {
107
+ const synopsis = memory.synopsis;
108
+ if (synopsis?.endingAftertaste) {
109
+ return synopsis.endingAftertaste;
110
+ }
111
+
112
+ return '❌ 엔딩 정서 잔상 미입력. synopsis update endingAftertaste "..." 로 추가하세요.';
113
+ }
114
+
115
+ // === Synopsis State Management ===
116
+
117
+ export function saveSynopsisState(state: SynopsisState): boolean {
118
+ const memory = loadMemory();
119
+ if (!memory) return false;
120
+
121
+ memory.synopsis = { ...state, lastGenerated: now() };
122
+ return saveMemory(memory);
123
+ }
124
+
125
+ export function loadSynopsisState(): SynopsisState | null {
126
+ const memory = loadMemory();
127
+ return memory?.synopsis || null;
128
+ }
129
+
130
+ export function updateSynopsisElement(element: keyof SynopsisState, value: string): boolean {
131
+ const memory = loadMemory();
132
+ if (!memory) return false;
133
+
134
+ memory.synopsis = memory.synopsis || {
135
+ protagonistAttitude: '',
136
+ coreRelationships: '',
137
+ emotionalTheme: '',
138
+ genreVsRealEmotion: '',
139
+ endingAftertaste: ''
140
+ };
141
+
142
+ (memory.synopsis as any)[element] = value;
143
+ memory.synopsis.lastGenerated = now();
144
+ return saveMemory(memory);
145
+ }
146
+
147
+ // === Format Functions ===
148
+
149
+ export function formatFullSynopsis(
150
+ attitude: string,
151
+ relationships: string,
152
+ theme: string,
153
+ genreContrast: string,
154
+ aftertaste: string,
155
+ memory: WriterMemory
156
+ ): string {
157
+ const projectName = memory.project.name || '제목 미정';
158
+ const chars = Object.values(memory.characters);
159
+ const charList = chars.map(c => `- **${c.name}**: ${c.attitude || c.arc || '설명 없음'}`).join('\n');
160
+ const emotionFlow = memory.scenes
161
+ .filter(s => s.emotionTags?.length > 0)
162
+ .map(s => s.emotionTags[0])
163
+ .join(' → ') || '아직 정의되지 않음';
164
+
165
+ return `═══════════════════════════════
166
+ 시놉시스: ${projectName}
167
+ ═══════════════════════════════
168
+
169
+ ## 1. 주인공의 태도
170
+ ${attitude}
171
+
172
+ ## 2. 관계의 핵심 구도
173
+ ${relationships}
174
+
175
+ ## 3. 정서적 테마
176
+ ${theme}
177
+
178
+ ## 4. 장르와 실제 감정의 거리
179
+ ${genreContrast}
180
+
181
+ ## 5. 엔딩이 남기는 잔상
182
+ ${aftertaste}
183
+
184
+ ---
185
+ **등장인물**:
186
+ ${charList || '(등장인물 없음)'}
187
+
188
+ **장면 수**: ${memory.scenes.length}개
189
+
190
+ **감정 흐름**: ${emotionFlow}
191
+ `;
192
+ }
193
+
194
+ export function formatBriefSynopsis(
195
+ attitude: string,
196
+ relationships: string,
197
+ theme: string,
198
+ memory: WriterMemory
199
+ ): string {
200
+ const chars = Object.values(memory.characters);
201
+ const protagonist = chars[0];
202
+ const name = protagonist?.name || '주인공';
203
+
204
+ return `${name}은 ${attitude.split('.')[0]}. ${theme.split('.')[0]}을 통해 ${relationships.split('\n')[0] || '관계를 형성하며'} 변화한다.`;
205
+ }
206
+
207
+ export function formatPitchSynopsis(
208
+ attitude: string,
209
+ relationships: string,
210
+ theme: string,
211
+ genreContrast: string,
212
+ memory: WriterMemory
213
+ ): string {
214
+ const projectName = memory.project.name || '이 이야기';
215
+ const chars = Object.values(memory.characters);
216
+ const protagonist = chars[0];
217
+ const name = protagonist?.name || '주인공';
218
+
219
+ return `${projectName}는 ${attitude.split('.')[0]} ${name}이 ${theme.split('.')[0]}을 깨닫는 이야기. ${genreContrast.split('.')[0]}.`;
220
+ }
221
+
222
+ // === Checklist ===
223
+
224
+ export interface ChecklistItem {
225
+ element: string;
226
+ elementKr: string;
227
+ status: 'complete' | 'partial' | 'missing';
228
+ source: string;
229
+ suggestion: string;
230
+ }
231
+
232
+ export function getSynopsisChecklist(memory: WriterMemory): ChecklistItem[] {
233
+ const chars = Object.values(memory.characters);
234
+ const protagonist = chars[0];
235
+
236
+ const checklist: ChecklistItem[] = [];
237
+
238
+ // 1. Protagonist Attitude
239
+ const hasArc = protagonist?.arc ? true : false;
240
+ const hasAttitude = protagonist?.attitude ? true : false;
241
+ checklist.push({
242
+ element: 'protagonistAttitude',
243
+ elementKr: '주인공 태도 요약',
244
+ status: hasArc && hasAttitude ? 'complete' : hasArc || hasAttitude ? 'partial' : 'missing',
245
+ source: protagonist ? `캐릭터 '${protagonist.name}'에서 추출` : '주인공 없음',
246
+ suggestion: hasArc && hasAttitude ? '' : 'char update <name> arc "..." attitude "..."'
247
+ });
248
+
249
+ // 2. Core Relationships
250
+ const relCount = protagonist ? memory.relationships.filter(
251
+ r => r.from === protagonist.name || r.to === protagonist.name
252
+ ).length : 0;
253
+ checklist.push({
254
+ element: 'coreRelationships',
255
+ elementKr: '관계 핵심 구도',
256
+ status: relCount >= 2 ? 'complete' : relCount === 1 ? 'partial' : 'missing',
257
+ source: `관계 ${relCount}개 등록됨`,
258
+ suggestion: relCount >= 2 ? '' : 'rel add <from> <to> <type>'
259
+ });
260
+
261
+ // 3. Emotional Theme
262
+ checklist.push({
263
+ element: 'emotionalTheme',
264
+ elementKr: '정서적 테마',
265
+ status: memory.themes.length > 0 ? 'complete' : 'missing',
266
+ source: `테마 ${memory.themes.length}개 등록됨`,
267
+ suggestion: memory.themes.length > 0 ? '' : 'theme add <name>'
268
+ });
269
+
270
+ // 4. Genre vs Emotion
271
+ const hasGenreContrast = memory.synopsis?.genreVsRealEmotion ? true : false;
272
+ checklist.push({
273
+ element: 'genreVsEmotion',
274
+ elementKr: '장르와 실제 감정의 거리',
275
+ status: hasGenreContrast ? 'complete' : 'missing',
276
+ source: hasGenreContrast ? '명시적으로 입력됨' : '미입력',
277
+ suggestion: hasGenreContrast ? '' : 'synopsis update genreVsRealEmotion "..."'
278
+ });
279
+
280
+ // 5. Ending Aftertaste
281
+ const hasAftertaste = memory.synopsis?.endingAftertaste ? true : false;
282
+ checklist.push({
283
+ element: 'endingAftertaste',
284
+ elementKr: '엔딩 정서 잔상',
285
+ status: hasAftertaste ? 'complete' : 'missing',
286
+ source: hasAftertaste ? '명시적으로 입력됨' : '미입력',
287
+ suggestion: hasAftertaste ? '' : 'synopsis update endingAftertaste "..."'
288
+ });
289
+
290
+ return checklist;
291
+ }
292
+
293
+ // === Export ===
294
+
295
+ export function exportSynopsisAsMarkdown(): string {
296
+ const memory = loadMemory();
297
+ if (!memory) return '# Error: No memory found';
298
+
299
+ const synopsis = generateSynopsis({ format: 'full' });
300
+ if (!synopsis) return '# Error: Could not generate synopsis';
301
+
302
+ const meta = `---
303
+ project: ${memory.project.name || 'Untitled'}
304
+ genre: ${memory.project.genre || 'Unspecified'}
305
+ generated: ${new Date().toISOString()}
306
+ ---
307
+
308
+ `;
309
+
310
+ return meta + synopsis;
311
+ }
312
+
313
+ export function exportSynopsisAsJSON(): object {
314
+ const memory = loadMemory();
315
+ if (!memory) return { error: 'No memory found' };
316
+
317
+ const checklist = getSynopsisChecklist(memory);
318
+
319
+ return {
320
+ metadata: {
321
+ project: memory.project.name,
322
+ genre: memory.project.genre,
323
+ generated: new Date().toISOString()
324
+ },
325
+ elements: {
326
+ protagonistAttitude: extractProtagonistAttitude(memory),
327
+ coreRelationships: extractCoreRelationships(memory),
328
+ emotionalTheme: extractEmotionalTheme(memory),
329
+ genreVsEmotion: extractGenreVsEmotion(memory),
330
+ endingAftertaste: extractEndingAftertaste(memory)
331
+ },
332
+ checklist,
333
+ formats: {
334
+ full: generateSynopsis({ format: 'full' }),
335
+ brief: generateSynopsis({ format: 'brief' }),
336
+ pitch: generateSynopsis({ format: 'pitch' })
337
+ }
338
+ };
339
+ }
@@ -0,0 +1,46 @@
1
+ # 시놉시스: {{PROJECT_NAME}}
2
+
3
+ > 장르: {{GENRE}} | 최종 업데이트: {{DATE}}
4
+
5
+ ---
6
+
7
+ ## 1. 주인공의 태도 (Protagonist Attitude)
8
+
9
+ {{PROTAGONIST_ATTITUDE}}
10
+
11
+ ## 2. 관계의 핵심 구도 (Core Relationships)
12
+
13
+ {{CORE_RELATIONSHIPS}}
14
+
15
+ ## 3. 정서적 테마 (Emotional Theme)
16
+
17
+ {{EMOTIONAL_THEME}}
18
+
19
+ ## 4. 장르와 실제 감정의 거리 (Genre vs Real Emotion)
20
+
21
+ {{GENRE_VS_EMOTION}}
22
+
23
+ ## 5. 엔딩이 남기는 잔상 (Ending Aftertaste)
24
+
25
+ {{ENDING_AFTERTASTE}}
26
+
27
+ ---
28
+
29
+ ## 부록
30
+
31
+ ### 등장인물
32
+
33
+ {{CHARACTER_LIST}}
34
+
35
+ ### 장면 흐름
36
+
37
+ {{SCENE_FLOW}}
38
+
39
+ ### 감정 궤도
40
+
41
+ {{EMOTION_ARC}}
42
+
43
+ ---
44
+
45
+ *이 시놉시스는 writer-memory 시스템에 의해 자동 생성되었습니다.*
46
+ *플롯이 아닌 감정 설계도 기반의 시놉시스입니다.*
@@ -48,8 +48,8 @@ PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
48
48
  # Check for ultrawork keywords (highest priority)
49
49
  if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
50
50
  # Create persistent ultrawork state
51
- mkdir -p "$DIRECTORY/.omc" 2>/dev/null
52
- mkdir -p "$HOME/.claude" 2>/dev/null
51
+ mkdir -p "$DIRECTORY/.omc/state" 2>/dev/null
52
+ mkdir -p "$HOME/.omc/state" 2>/dev/null
53
53
 
54
54
  # Escape prompt for JSON
55
55
  PROMPT_ESCAPED=$(echo "$PROMPT" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ')
@@ -63,8 +63,8 @@ if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
63
63
  }"
64
64
 
65
65
  # Write state to both local and global locations
66
- echo "$STATE_JSON" > "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null
67
- echo "$STATE_JSON" > "$HOME/.claude/ultrawork-state.json" 2>/dev/null
66
+ echo "$STATE_JSON" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
67
+ echo "$STATE_JSON" > "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null
68
68
 
69
69
  # Return ultrawork mode injection
70
70
  cat << 'EOF'
@@ -36,22 +36,22 @@ fi
36
36
 
37
37
  # Check for active ultrawork state
38
38
  ULTRAWORK_STATE=""
39
- if [ -f "$DIRECTORY/.omc/ultrawork-state.json" ]; then
40
- ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null)
41
- elif [ -f "$HOME/.claude/ultrawork-state.json" ]; then
42
- ULTRAWORK_STATE=$(cat "$HOME/.claude/ultrawork-state.json" 2>/dev/null)
39
+ if [ -f "$DIRECTORY/.omc/state/ultrawork-state.json" ]; then
40
+ ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null)
41
+ elif [ -f "$HOME/.omc/state/ultrawork-state.json" ]; then
42
+ ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
43
43
  fi
44
44
 
45
45
  # Check for active ralph loop
46
46
  RALPH_STATE=""
47
- if [ -f "$DIRECTORY/.omc/ralph-state.json" ]; then
48
- RALPH_STATE=$(cat "$DIRECTORY/.omc/ralph-state.json" 2>/dev/null)
47
+ if [ -f "$DIRECTORY/.omc/state/ralph-state.json" ]; then
48
+ RALPH_STATE=$(cat "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null)
49
49
  fi
50
50
 
51
51
  # Check for verification state (oracle verification)
52
52
  VERIFICATION_STATE=""
53
- if [ -f "$DIRECTORY/.omc/ralph-verification.json" ]; then
54
- VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/ralph-verification.json" 2>/dev/null)
53
+ if [ -f "$DIRECTORY/.omc/state/ralph-verification.json" ]; then
54
+ VERIFICATION_STATE=$(cat "$DIRECTORY/.omc/state/ralph-verification.json" 2>/dev/null)
55
55
  fi
56
56
 
57
57
  # Check for incomplete todos
@@ -121,7 +121,7 @@ EOF
121
121
  if [ "$ITERATION" -lt "$MAX_ITER" ]; then
122
122
  # Increment iteration
123
123
  NEW_ITER=$((ITERATION + 1))
124
- echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/ralph-state.json" 2>/dev/null
124
+ echo "$RALPH_STATE" | jq ".iteration = $NEW_ITER" > "$DIRECTORY/.omc/state/ralph-state.json" 2>/dev/null
125
125
 
126
126
  cat << EOF
127
127
  {"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"}
@@ -164,7 +164,7 @@ if [ -n "$ULTRAWORK_STATE" ] && [ "$INCOMPLETE_COUNT" -gt 0 ]; then
164
164
 
165
165
  # Update state file (best effort)
166
166
  if command -v jq &> /dev/null; then
167
- echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null
167
+ echo "$ULTRAWORK_STATE" | jq ".reinforcement_count = $NEW_COUNT | .last_checked_at = \"$(date -Iseconds)\"" > "$DIRECTORY/.omc/state/ultrawork-state.json" 2>/dev/null
168
168
  fi
169
169
 
170
170
  cat << EOF
@@ -18,11 +18,11 @@ fi
18
18
  MESSAGES=""
19
19
 
20
20
  # Check for active ultrawork state
21
- if [ -f "$DIRECTORY/.omc/ultrawork-state.json" ] || [ -f "$HOME/.claude/ultrawork-state.json" ]; then
22
- if [ -f "$DIRECTORY/.omc/ultrawork-state.json" ]; then
23
- ULTRAWORK_STATE=$(cat "$DIRECTORY/.omc/ultrawork-state.json" 2>/dev/null)
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
24
  else
25
- ULTRAWORK_STATE=$(cat "$HOME/.claude/ultrawork-state.json" 2>/dev/null)
25
+ ULTRAWORK_STATE=$(cat "$HOME/.omc/state/ultrawork-state.json" 2>/dev/null)
26
26
  fi
27
27
 
28
28
  IS_ACTIVE=$(echo "$ULTRAWORK_STATE" | jq -r '.active // false' 2>/dev/null)