oh-my-claude-sisyphus 3.6.3 → 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 (163) hide show
  1. package/README.md +40 -1
  2. package/commands/hud.md +37 -5
  3. package/commands/omc-setup.md +105 -0
  4. package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
  5. package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
  6. package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
  7. package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
  8. package/dist/__tests__/hud/analytics-display.test.js +137 -1
  9. package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
  10. package/dist/__tests__/hud-windows.test.d.ts +2 -0
  11. package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
  12. package/dist/__tests__/hud-windows.test.js +91 -0
  13. package/dist/__tests__/hud-windows.test.js.map +1 -0
  14. package/dist/__tests__/installer.test.js +1 -1
  15. package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
  16. package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
  17. package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
  18. package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
  19. package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
  20. package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
  21. package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
  22. package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
  23. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
  24. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
  25. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
  26. package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
  27. package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
  28. package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
  29. package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
  30. package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
  31. package/dist/cli/commands/wait.d.ts +52 -0
  32. package/dist/cli/commands/wait.d.ts.map +1 -0
  33. package/dist/cli/commands/wait.js +229 -0
  34. package/dist/cli/commands/wait.js.map +1 -0
  35. package/dist/cli/index.js +54 -0
  36. package/dist/cli/index.js.map +1 -1
  37. package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
  38. package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
  39. package/dist/features/rate-limit-wait/daemon.js +585 -0
  40. package/dist/features/rate-limit-wait/daemon.js.map +1 -0
  41. package/dist/features/rate-limit-wait/index.d.ts +16 -0
  42. package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
  43. package/dist/features/rate-limit-wait/index.js +18 -0
  44. package/dist/features/rate-limit-wait/index.js.map +1 -0
  45. package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
  46. package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
  47. package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
  48. package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
  49. package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
  50. package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
  51. package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
  52. package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
  53. package/dist/features/rate-limit-wait/types.d.ts +121 -0
  54. package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
  55. package/dist/features/rate-limit-wait/types.js +8 -0
  56. package/dist/features/rate-limit-wait/types.js.map +1 -0
  57. package/dist/features/state-manager/index.d.ts.map +1 -1
  58. package/dist/features/state-manager/index.js +4 -1
  59. package/dist/features/state-manager/index.js.map +1 -1
  60. package/dist/hooks/bridge.d.ts +1 -1
  61. package/dist/hooks/bridge.d.ts.map +1 -1
  62. package/dist/hooks/bridge.js +50 -4
  63. package/dist/hooks/bridge.js.map +1 -1
  64. package/dist/hooks/index.d.ts +5 -0
  65. package/dist/hooks/index.d.ts.map +1 -1
  66. package/dist/hooks/index.js +15 -0
  67. package/dist/hooks/index.js.map +1 -1
  68. package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
  69. package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
  70. package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
  71. package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
  72. package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
  73. package/dist/hooks/omc-orchestrator/index.js +95 -8
  74. package/dist/hooks/omc-orchestrator/index.js.map +1 -1
  75. package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
  76. package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
  77. package/dist/hooks/permission-handler/__tests__/index.test.js +291 -0
  78. package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
  79. package/dist/hooks/permission-handler/index.d.ts +42 -0
  80. package/dist/hooks/permission-handler/index.d.ts.map +1 -0
  81. package/dist/hooks/permission-handler/index.js +107 -0
  82. package/dist/hooks/permission-handler/index.js.map +1 -0
  83. package/dist/hooks/plugin-patterns/index.d.ts +5 -0
  84. package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
  85. package/dist/hooks/plugin-patterns/index.js +26 -1
  86. package/dist/hooks/plugin-patterns/index.js.map +1 -1
  87. package/dist/hooks/pre-compact/index.d.ts +82 -0
  88. package/dist/hooks/pre-compact/index.d.ts.map +1 -0
  89. package/dist/hooks/pre-compact/index.js +265 -0
  90. package/dist/hooks/pre-compact/index.js.map +1 -0
  91. package/dist/hooks/session-end/index.d.ts +42 -0
  92. package/dist/hooks/session-end/index.d.ts.map +1 -0
  93. package/dist/hooks/session-end/index.js +200 -0
  94. package/dist/hooks/session-end/index.js.map +1 -0
  95. package/dist/hooks/setup/index.d.ts +66 -0
  96. package/dist/hooks/setup/index.d.ts.map +1 -0
  97. package/dist/hooks/setup/index.js +299 -0
  98. package/dist/hooks/setup/index.js.map +1 -0
  99. package/dist/hooks/setup/types.d.ts +25 -0
  100. package/dist/hooks/setup/types.d.ts.map +1 -0
  101. package/dist/hooks/setup/types.js +5 -0
  102. package/dist/hooks/setup/types.js.map +1 -0
  103. package/dist/hooks/subagent-tracker/index.d.ts +68 -29
  104. package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
  105. package/dist/hooks/subagent-tracker/index.js +316 -131
  106. package/dist/hooks/subagent-tracker/index.js.map +1 -1
  107. package/dist/hud/analytics-display.d.ts +16 -0
  108. package/dist/hud/analytics-display.d.ts.map +1 -1
  109. package/dist/hud/analytics-display.js +35 -9
  110. package/dist/hud/analytics-display.js.map +1 -1
  111. package/dist/hud/render.d.ts.map +1 -1
  112. package/dist/hud/render.js +49 -18
  113. package/dist/hud/render.js.map +1 -1
  114. package/dist/hud/types.d.ts +2 -0
  115. package/dist/hud/types.d.ts.map +1 -1
  116. package/dist/hud/types.js +14 -0
  117. package/dist/hud/types.js.map +1 -1
  118. package/dist/installer/index.d.ts +1 -1
  119. package/dist/installer/index.d.ts.map +1 -1
  120. package/dist/installer/index.js +4 -3
  121. package/dist/installer/index.js.map +1 -1
  122. package/hooks/hooks.json +83 -1
  123. package/hooks/keyword-detector.sh +4 -4
  124. package/hooks/persistent-mode.sh +10 -10
  125. package/hooks/session-start.sh +4 -4
  126. package/package.json +3 -1
  127. package/scripts/keyword-detector.mjs +4 -4
  128. package/scripts/permission-handler.mjs +23 -0
  129. package/scripts/persistent-mode.mjs +6 -6
  130. package/scripts/persistent-mode.sh +10 -10
  131. package/scripts/pre-compact.mjs +23 -0
  132. package/scripts/session-end.mjs +23 -0
  133. package/scripts/session-start.mjs +4 -4
  134. package/scripts/setup-init.mjs +23 -0
  135. package/scripts/setup-maintenance.mjs +23 -0
  136. package/scripts/subagent-tracker.mjs +35 -0
  137. package/skills/hud/SKILL.md +37 -5
  138. package/skills/omc-setup/SKILL.md +162 -4
  139. package/skills/writer-memory/SKILL.md +443 -0
  140. package/skills/writer-memory/lib/character-tracker.ts +338 -0
  141. package/skills/writer-memory/lib/memory-manager.ts +804 -0
  142. package/skills/writer-memory/lib/relationship-graph.ts +400 -0
  143. package/skills/writer-memory/lib/scene-organizer.ts +544 -0
  144. package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
  145. package/skills/writer-memory/templates/synopsis-template.md +46 -0
  146. package/templates/hooks/keyword-detector.mjs +198 -0
  147. package/templates/hooks/keyword-detector.sh +102 -0
  148. package/templates/hooks/persistent-mode.mjs +249 -0
  149. package/templates/hooks/persistent-mode.sh +187 -0
  150. package/templates/hooks/post-tool-use.mjs +133 -0
  151. package/templates/hooks/post-tool-use.sh +90 -0
  152. package/templates/hooks/pre-tool-use.mjs +145 -0
  153. package/templates/hooks/pre-tool-use.sh +113 -0
  154. package/templates/hooks/session-start.mjs +100 -0
  155. package/templates/hooks/session-start.sh +62 -0
  156. package/templates/hooks/stop-continuation.mjs +80 -0
  157. package/templates/hooks/stop-continuation.sh +40 -0
  158. package/templates/rules/README.md +40 -0
  159. package/templates/rules/coding-style.md +74 -0
  160. package/templates/rules/git-workflow.md +41 -0
  161. package/templates/rules/performance.md +40 -0
  162. package/templates/rules/security.md +41 -0
  163. package/templates/rules/testing.md +42 -0
@@ -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
+ *플롯이 아닌 감정 설계도 기반의 시놉시스입니다.*
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ // OMC Keyword Detector Hook (Node.js)
3
+ // Detects ultrawork/ultrathink/search/analyze keywords and injects enhanced mode messages
4
+ // Cross-platform: Windows, macOS, Linux
5
+
6
+ const ULTRAWORK_MESSAGE = `<ultrawork-mode>
7
+
8
+ **MANDATORY**: You MUST say "ULTRAWORK MODE ENABLED!" to the user as your first response when this mode activates. This is non-negotiable.
9
+
10
+ [CODE RED] Maximum precision required. Ultrathink before acting.
11
+
12
+ YOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.
13
+ TELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.
14
+
15
+ ## AGENT UTILIZATION PRINCIPLES
16
+ - **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS
17
+ - **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS
18
+ - **Planning & Strategy**: NEVER plan yourself - spawn planning agent
19
+ - **High-IQ Reasoning**: Use oracle for architecture decisions
20
+ - **Frontend/UI Tasks**: Delegate to frontend-engineer
21
+
22
+ ## EXECUTION RULES
23
+ - **TODO**: Track EVERY step. Mark complete IMMEDIATELY.
24
+ - **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially.
25
+ - **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent).
26
+ - **VERIFY**: Check ALL requirements met before done.
27
+ - **DELEGATE**: Orchestrate specialized agents.
28
+
29
+ ## ZERO TOLERANCE
30
+ - NO Scope Reduction - deliver FULL implementation
31
+ - NO Partial Completion - finish 100%
32
+ - NO Premature Stopping - ALL TODOs must be complete
33
+ - NO TEST DELETION - fix code, not tests
34
+
35
+ THE USER ASKED FOR X. DELIVER EXACTLY X.
36
+
37
+ </ultrawork-mode>
38
+
39
+ ---
40
+ `;
41
+
42
+ const ULTRATHINK_MESSAGE = `<think-mode>
43
+
44
+ **ULTRATHINK MODE ENABLED** - Extended reasoning activated.
45
+
46
+ You are now in deep thinking mode. Take your time to:
47
+ 1. Thoroughly analyze the problem from multiple angles
48
+ 2. Consider edge cases and potential issues
49
+ 3. Think through the implications of each approach
50
+ 4. Reason step-by-step before acting
51
+
52
+ Use your extended thinking capabilities to provide the most thorough and well-reasoned response.
53
+
54
+ </think-mode>
55
+
56
+ ---
57
+ `;
58
+
59
+ const SEARCH_MESSAGE = `<search-mode>
60
+ MAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:
61
+ - explore agents (codebase patterns, file structures)
62
+ - librarian agents (remote repos, official docs, GitHub examples)
63
+ Plus direct tools: Grep, Glob
64
+ NEVER stop at first result - be exhaustive.
65
+ </search-mode>
66
+
67
+ ---
68
+ `;
69
+
70
+ const ANALYZE_MESSAGE = `<analyze-mode>
71
+ ANALYSIS MODE. Gather context before diving deep:
72
+
73
+ CONTEXT GATHERING (parallel):
74
+ - 1-2 explore agents (codebase patterns, implementations)
75
+ - 1-2 librarian agents (if external library involved)
76
+ - Direct tools: Grep, Glob, LSP for targeted searches
77
+
78
+ IF COMPLEX (architecture, multi-system, debugging after 2+ failures):
79
+ - Consult oracle agent for strategic guidance
80
+
81
+ SYNTHESIZE findings before proceeding.
82
+ </analyze-mode>
83
+
84
+ ---
85
+ `;
86
+
87
+ // Read all stdin
88
+ async function readStdin() {
89
+ const chunks = [];
90
+ for await (const chunk of process.stdin) {
91
+ chunks.push(chunk);
92
+ }
93
+ return Buffer.concat(chunks).toString('utf-8');
94
+ }
95
+
96
+ // Extract prompt from various JSON structures
97
+ function extractPrompt(input) {
98
+ try {
99
+ const data = JSON.parse(input);
100
+ if (data.prompt) return data.prompt;
101
+ if (data.message?.content) return data.message.content;
102
+ if (Array.isArray(data.parts)) {
103
+ return data.parts
104
+ .filter(p => p.type === 'text')
105
+ .map(p => p.text)
106
+ .join(' ');
107
+ }
108
+ return '';
109
+ } catch {
110
+ // Fallback: try to extract with regex
111
+ const match = input.match(/"(?:prompt|content|text)"\s*:\s*"([^"]+)"/);
112
+ return match ? match[1] : '';
113
+ }
114
+ }
115
+
116
+ // Remove code blocks to prevent false positives
117
+ function removeCodeBlocks(text) {
118
+ return text
119
+ .replace(/```[\s\S]*?```/g, '')
120
+ .replace(/`[^`]+`/g, '');
121
+ }
122
+
123
+ import { writeFileSync, mkdirSync, existsSync } from 'fs';
124
+ import { join } from 'path';
125
+ import { homedir } from 'os';
126
+
127
+ // Create ultrawork state file
128
+ function activateUltraworkState(directory, prompt) {
129
+ const state = {
130
+ active: true,
131
+ started_at: new Date().toISOString(),
132
+ original_prompt: prompt,
133
+ reinforcement_count: 0,
134
+ last_checked_at: new Date().toISOString()
135
+ };
136
+ const localDir = join(directory, '.omc');
137
+ if (!existsSync(localDir)) { try { mkdirSync(localDir, { recursive: true }); } catch {} }
138
+ try { writeFileSync(join(localDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
139
+ const globalDir = join(homedir(), '.claude');
140
+ if (!existsSync(globalDir)) { try { mkdirSync(globalDir, { recursive: true }); } catch {} }
141
+ try { writeFileSync(join(globalDir, 'ultrawork-state.json'), JSON.stringify(state, null, 2)); } catch {}
142
+ }
143
+
144
+ // Main
145
+ async function main() {
146
+ try {
147
+ const input = await readStdin();
148
+ if (!input.trim()) {
149
+ console.log(JSON.stringify({ continue: true }));
150
+ return;
151
+ }
152
+
153
+ let data = {};
154
+ try { data = JSON.parse(input); } catch {}
155
+ const directory = data.directory || process.cwd();
156
+
157
+ const prompt = extractPrompt(input);
158
+ if (!prompt) {
159
+ console.log(JSON.stringify({ continue: true }));
160
+ return;
161
+ }
162
+
163
+ const cleanPrompt = removeCodeBlocks(prompt).toLowerCase();
164
+
165
+ // Check for ultrawork keywords (highest priority)
166
+ if (/\b(ultrawork|ulw|uw)\b/.test(cleanPrompt)) {
167
+ activateUltraworkState(directory, prompt);
168
+ console.log(JSON.stringify({ continue: true, message: ULTRAWORK_MESSAGE }));
169
+ return;
170
+ }
171
+
172
+ // Check for ultrathink/think keywords
173
+ if (/\b(ultrathink|think)\b/.test(cleanPrompt)) {
174
+ console.log(JSON.stringify({ continue: true, message: ULTRATHINK_MESSAGE }));
175
+ return;
176
+ }
177
+
178
+ // Check for search keywords
179
+ 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)) {
180
+ console.log(JSON.stringify({ continue: true, message: SEARCH_MESSAGE }));
181
+ return;
182
+ }
183
+
184
+ // Check for analyze keywords
185
+ 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)) {
186
+ console.log(JSON.stringify({ continue: true, message: ANALYZE_MESSAGE }));
187
+ return;
188
+ }
189
+
190
+ // No keywords detected
191
+ console.log(JSON.stringify({ continue: true }));
192
+ } catch (error) {
193
+ // On any error, allow continuation
194
+ console.log(JSON.stringify({ continue: true }));
195
+ }
196
+ }
197
+
198
+ main();
@@ -0,0 +1,102 @@
1
+ #!/bin/bash
2
+ # OMC Keyword Detector Hook
3
+ # Detects ultrawork/ultrathink/search/analyze keywords and injects enhanced mode messages
4
+ # Also activates persistent ultrawork state when ultrawork keyword is detected
5
+
6
+ # Read stdin (JSON input from Claude Code)
7
+ INPUT=$(cat)
8
+
9
+ # Extract directory from input
10
+ DIRECTORY=""
11
+ if command -v jq &> /dev/null; then
12
+ DIRECTORY=$(echo "$INPUT" | jq -r '.directory // ""' 2>/dev/null)
13
+ fi
14
+ if [ -z "$DIRECTORY" ] || [ "$DIRECTORY" = "null" ]; then
15
+ DIRECTORY=$(pwd)
16
+ fi
17
+
18
+ # Extract the prompt text - try multiple JSON paths
19
+ PROMPT=""
20
+ if command -v jq &> /dev/null; then
21
+ # Try to extract from various possible JSON structures
22
+ PROMPT=$(echo "$INPUT" | jq -r '
23
+ if .prompt then .prompt
24
+ elif .message.content then .message.content
25
+ elif .parts then ([.parts[] | select(.type == "text") | .text] | join(" "))
26
+ else ""
27
+ end
28
+ ' 2>/dev/null)
29
+ fi
30
+
31
+ # Fallback: simple grep extraction if jq fails
32
+ if [ -z "$PROMPT" ] || [ "$PROMPT" = "null" ]; then
33
+ PROMPT=$(echo "$INPUT" | grep -oP '"(prompt|content|text)"\s*:\s*"\K[^"]+' | head -1)
34
+ fi
35
+
36
+ # Exit if no prompt found
37
+ if [ -z "$PROMPT" ]; then
38
+ echo '{"continue": true}'
39
+ exit 0
40
+ fi
41
+
42
+ # Remove code blocks before checking keywords (prevents false positives)
43
+ PROMPT_NO_CODE=$(echo "$PROMPT" | sed 's/```[^`]*```//g' | sed 's/`[^`]*`//g')
44
+
45
+ # Convert to lowercase for case-insensitive matching
46
+ PROMPT_LOWER=$(echo "$PROMPT_NO_CODE" | tr '[:upper:]' '[:lower:]')
47
+
48
+ # Check for ultrawork keywords (highest priority)
49
+ if echo "$PROMPT_LOWER" | grep -qE '\b(ultrawork|ulw|uw)\b'; then
50
+ # Create persistent ultrawork state
51
+ mkdir -p "$DIRECTORY/.omc/state" 2>/dev/null
52
+ mkdir -p "$HOME/.omc/state" 2>/dev/null
53
+
54
+ # Escape prompt for JSON
55
+ PROMPT_ESCAPED=$(echo "$PROMPT" | sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | tr '\n' ' ')
56
+
57
+ STATE_JSON="{
58
+ \"active\": true,
59
+ \"started_at\": \"$(date -Iseconds)\",
60
+ \"original_prompt\": \"$PROMPT_ESCAPED\",
61
+ \"reinforcement_count\": 0,
62
+ \"last_checked_at\": \"$(date -Iseconds)\"
63
+ }"
64
+
65
+ # Write state to both local and global locations
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
+
69
+ # Return ultrawork mode injection
70
+ cat << 'EOF'
71
+ {"continue": true, "message": "<ultrawork-mode>\\n\\n**MANDATORY**: You MUST say \"ULTRAWORK MODE ENABLED!\" to the user as your first response when this mode activates. This is non-negotiable.\\n\\n[CODE RED] Maximum precision required. Ultrathink before acting.\\n\\nYOU MUST LEVERAGE ALL AVAILABLE AGENTS TO THEIR FULLEST POTENTIAL.\\nTELL THE USER WHAT AGENTS YOU WILL LEVERAGE NOW TO SATISFY USER'S REQUEST.\\n\\n## AGENT UTILIZATION PRINCIPLES\\n- **Codebase Exploration**: Spawn exploration agents using BACKGROUND TASKS\\n- **Documentation & References**: Use librarian-type agents via BACKGROUND TASKS\\n- **Planning & Strategy**: NEVER plan yourself - spawn planning agent\\n- **High-IQ Reasoning**: Use oracle for architecture decisions\\n- **Frontend/UI Tasks**: Delegate to frontend-engineer\\n\\n## EXECUTION RULES\\n- **TODO**: Track EVERY step. Mark complete IMMEDIATELY.\\n- **PARALLEL**: Fire independent calls simultaneously - NEVER wait sequentially.\\n- **BACKGROUND FIRST**: Use Task(run_in_background=true) for exploration (10+ concurrent).\\n- **VERIFY**: Check ALL requirements met before done.\\n- **DELEGATE**: Orchestrate specialized agents.\\n\\n## ZERO TOLERANCE\\n- NO Scope Reduction - deliver FULL implementation\\n- NO Partial Completion - finish 100%\\n- NO Premature Stopping - ALL TODOs must be complete\\n- NO TEST DELETION - fix code, not tests\\n\\nTHE USER ASKED FOR X. DELIVER EXACTLY X.\\n\\n</ultrawork-mode>\\n\\n---\\n"}
72
+ EOF
73
+ exit 0
74
+ fi
75
+
76
+ # Check for ultrathink/think keywords
77
+ if echo "$PROMPT_LOWER" | grep -qE '\b(ultrathink|think)\b'; then
78
+ cat << 'EOF'
79
+ {"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"}
80
+ EOF
81
+ exit 0
82
+ fi
83
+
84
+ # Check for search keywords (EN + multilingual)
85
+ if echo "$PROMPT_LOWER" | grep -qE '\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'; then
86
+ cat << 'EOF'
87
+ {"continue": true, "message": "<search-mode>\\nMAXIMIZE SEARCH EFFORT. Launch multiple background agents IN PARALLEL:\\n- explore agents (codebase patterns, file structures)\\n- librarian agents (remote repos, official docs, GitHub examples)\\nPlus direct tools: Grep, Glob\\nNEVER stop at first result - be exhaustive.\\n</search-mode>\\n\\n---\\n"}
88
+ EOF
89
+ exit 0
90
+ fi
91
+
92
+ # Check for analyze keywords
93
+ if echo "$PROMPT_LOWER" | grep -qE '\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'; then
94
+ cat << 'EOF'
95
+ {"continue": true, "message": "<analyze-mode>\\nANALYSIS MODE. Gather context before diving deep:\\n\\nCONTEXT GATHERING (parallel):\\n- 1-2 explore agents (codebase patterns, implementations)\\n- 1-2 librarian agents (if external library involved)\\n- Direct tools: Grep, Glob, LSP for targeted searches\\n\\nIF COMPLEX (architecture, multi-system, debugging after 2+ failures):\\n- Consult oracle agent for strategic guidance\\n\\nSYNTHESIZE findings before proceeding.\\n</analyze-mode>\\n\\n---\\n"}
96
+ EOF
97
+ exit 0
98
+ fi
99
+
100
+ # No keywords detected - continue without modification
101
+ echo '{"continue": true}'
102
+ exit 0