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.
- package/README.md +24 -1
- package/commands/hud.md +37 -5
- package/commands/omc-setup.md +105 -0
- package/dist/__tests__/hud/analytics-display.test.js +137 -1
- package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
- package/dist/__tests__/hud-windows.test.d.ts +2 -0
- package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
- package/dist/__tests__/hud-windows.test.js +91 -0
- package/dist/__tests__/hud-windows.test.js.map +1 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -1
- package/dist/features/rate-limit-wait/daemon.js +41 -1
- package/dist/features/rate-limit-wait/daemon.js.map +1 -1
- package/dist/features/state-manager/index.d.ts.map +1 -1
- package/dist/features/state-manager/index.js +4 -1
- package/dist/features/state-manager/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.js +47 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -1
- package/dist/hooks/permission-handler/index.d.ts +1 -1
- package/dist/hooks/permission-handler/index.d.ts.map +1 -1
- package/dist/hooks/permission-handler/index.js +11 -15
- package/dist/hooks/permission-handler/index.js.map +1 -1
- package/dist/hooks/plugin-patterns/index.d.ts +5 -0
- package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
- package/dist/hooks/plugin-patterns/index.js +26 -1
- package/dist/hooks/plugin-patterns/index.js.map +1 -1
- package/dist/hooks/session-end/index.d.ts +0 -8
- package/dist/hooks/session-end/index.d.ts.map +1 -1
- package/dist/hooks/session-end/index.js +5 -12
- package/dist/hooks/session-end/index.js.map +1 -1
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +32 -17
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/hud/analytics-display.d.ts +16 -0
- package/dist/hud/analytics-display.d.ts.map +1 -1
- package/dist/hud/analytics-display.js +35 -9
- package/dist/hud/analytics-display.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +49 -18
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +14 -0
- package/dist/hud/types.js.map +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +3 -2
- package/dist/installer/index.js.map +1 -1
- package/hooks/keyword-detector.sh +4 -4
- package/hooks/persistent-mode.sh +10 -10
- package/hooks/session-start.sh +4 -4
- package/package.json +1 -1
- package/scripts/keyword-detector.mjs +4 -4
- package/scripts/persistent-mode.mjs +6 -6
- package/scripts/persistent-mode.sh +10 -10
- package/scripts/session-start.mjs +4 -4
- package/skills/hud/SKILL.md +37 -5
- package/skills/omc-setup/SKILL.md +162 -4
- package/skills/writer-memory/SKILL.md +443 -0
- package/skills/writer-memory/lib/character-tracker.ts +338 -0
- package/skills/writer-memory/lib/memory-manager.ts +804 -0
- package/skills/writer-memory/lib/relationship-graph.ts +400 -0
- package/skills/writer-memory/lib/scene-organizer.ts +544 -0
- package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
- package/skills/writer-memory/templates/synopsis-template.md +46 -0
- package/templates/hooks/keyword-detector.sh +4 -4
- package/templates/hooks/persistent-mode.sh +10 -10
- 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/.
|
|
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/.
|
|
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/.
|
|
42
|
-
ULTRAWORK_STATE=$(cat "$HOME/.
|
|
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/.
|
|
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/.
|
|
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)
|