claude-controller 0.2.0 → 0.3.0

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 (68) hide show
  1. package/README.md +2 -2
  2. package/bin/autoloop.sh +382 -0
  3. package/bin/ctl +327 -5
  4. package/bin/native-app.py +5 -2
  5. package/bin/watchdog.sh +357 -0
  6. package/cognitive/__init__.py +14 -0
  7. package/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
  8. package/cognitive/__pycache__/dispatcher.cpython-314.pyc +0 -0
  9. package/cognitive/__pycache__/evaluator.cpython-314.pyc +0 -0
  10. package/cognitive/__pycache__/goal_engine.cpython-314.pyc +0 -0
  11. package/cognitive/__pycache__/learning.cpython-314.pyc +0 -0
  12. package/cognitive/__pycache__/orchestrator.cpython-314.pyc +0 -0
  13. package/cognitive/__pycache__/planner.cpython-314.pyc +0 -0
  14. package/cognitive/dispatcher.py +192 -0
  15. package/cognitive/evaluator.py +289 -0
  16. package/cognitive/goal_engine.py +232 -0
  17. package/cognitive/learning.py +189 -0
  18. package/cognitive/orchestrator.py +303 -0
  19. package/cognitive/planner.py +207 -0
  20. package/cognitive/prompts/analyst.md +31 -0
  21. package/cognitive/prompts/coder.md +22 -0
  22. package/cognitive/prompts/reviewer.md +33 -0
  23. package/cognitive/prompts/tester.md +21 -0
  24. package/cognitive/prompts/writer.md +25 -0
  25. package/config.sh +6 -1
  26. package/dag/__init__.py +5 -0
  27. package/dag/__pycache__/__init__.cpython-314.pyc +0 -0
  28. package/dag/__pycache__/graph.cpython-314.pyc +0 -0
  29. package/dag/graph.py +222 -0
  30. package/lib/jobs.sh +12 -1
  31. package/package.json +5 -1
  32. package/postinstall.sh +1 -1
  33. package/service/controller.sh +43 -11
  34. package/web/audit.py +122 -0
  35. package/web/checkpoint.py +80 -0
  36. package/web/config.py +2 -5
  37. package/web/handler.py +464 -26
  38. package/web/handler_fs.py +15 -14
  39. package/web/handler_goals.py +203 -0
  40. package/web/handler_jobs.py +165 -42
  41. package/web/handler_memory.py +203 -0
  42. package/web/jobs.py +576 -12
  43. package/web/personas.py +419 -0
  44. package/web/pipeline.py +682 -50
  45. package/web/presets.py +506 -0
  46. package/web/projects.py +58 -4
  47. package/web/static/api.js +90 -3
  48. package/web/static/app.js +8 -0
  49. package/web/static/base.css +51 -12
  50. package/web/static/context.js +14 -4
  51. package/web/static/form.css +3 -2
  52. package/web/static/goals.css +363 -0
  53. package/web/static/goals.js +300 -0
  54. package/web/static/i18n.js +288 -0
  55. package/web/static/index.html +142 -6
  56. package/web/static/jobs.css +951 -4
  57. package/web/static/jobs.js +890 -54
  58. package/web/static/memoryview.js +117 -0
  59. package/web/static/personas.js +228 -0
  60. package/web/static/pipeline.css +308 -1
  61. package/web/static/pipelines.js +249 -14
  62. package/web/static/presets.js +244 -0
  63. package/web/static/send.js +26 -4
  64. package/web/static/settings-style.css +34 -3
  65. package/web/static/settings.js +37 -1
  66. package/web/static/stream.js +242 -19
  67. package/web/static/utils.js +54 -2
  68. package/web/webhook.py +210 -0
@@ -14,6 +14,12 @@
14
14
  </head>
15
15
  <body>
16
16
 
17
+ <!-- Connection lost banner -->
18
+ <div class="conn-lost-banner" id="connLostBanner">
19
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M1 1l22 22"/><path d="M16.72 11.06A10.94 10.94 0 0 1 19 12.55"/><path d="M5 12.55a10.94 10.94 0 0 1 5.17-2.39"/><path d="M10.71 5.05A16 16 0 0 1 22.56 9"/><path d="M1.42 9a15.91 15.91 0 0 1 4.7-2.88"/><path d="M8.53 16.11a6 6 0 0 1 6.95 0"/><line x1="12" y1="20" x2="12.01" y2="20"/></svg>
20
+ <span data-i18n="conn_lost">서버 연결이 끊어졌습니다. 재연결 시도 중...</span>
21
+ </div>
22
+
17
23
  <!-- ── Layout ── -->
18
24
  <div class="layout">
19
25
 
@@ -65,6 +71,7 @@
65
71
  <label class="form-label" style="display:flex;align-items:center;gap:6px;">
66
72
  <span data-i18n="prompt">프롬프트</span>
67
73
  <span class="img-count-badge" id="imgCountBadge"></span>
74
+ <span id="personaBadge" style="display:none;"></span>
68
75
  <span id="promptSessionInfo" style="margin-left:auto;font-size:0.68rem;font-weight:400;color:var(--text-muted);font-family:var(--font-mono,monospace);"></span>
69
76
  </label>
70
77
  <div class="prompt-wrapper" id="promptWrapper">
@@ -131,8 +138,16 @@
131
138
  <input type="text" class="setting-input" id="automationInterval" placeholder="5m" style="width:60px;padding:3px 8px;font-size:0.72rem;">
132
139
  <span class="automation-inline-hint">예: 30s, 5m, 1h</span>
133
140
  </div>
141
+ <!-- DAG 의존성 입력: AI 전용 (API depends_on), 프론트 UI 숨김 -->
142
+ <div class="automation-inline" id="depsRow" style="display:none;">
143
+ <input type="hidden" id="depsInput">
144
+ </div>
134
145
  <div class="form-actions-buttons">
135
146
  <button type="button" class="btn" onclick="clearPromptForm();" data-i18n="reset">초기화</button>
147
+ <!-- DAG 버튼 숨김: AI가 API로 depends_on을 직접 사용 -->
148
+ <button type="button" class="btn" id="btnDepsToggle" onclick="toggleDeps()" style="display:none;">
149
+ <span>DAG</span>
150
+ </button>
136
151
  <button type="button" class="btn" id="btnAutoToggle" onclick="toggleAutomation()" title="자동화 모드 전환" style="display:flex;align-items:center;gap:4px;">
137
152
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
138
153
  <span>자동화</span>
@@ -162,6 +177,9 @@
162
177
  <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="3 6 5 6 21 6"/><path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2"/></svg>
163
178
  <span data-i18n="delete_completed">완료 삭제</span>
164
179
  </button>
180
+ <button class="btn btn-sm btn-icon" id="btnViewMode" onclick="toggleJobViewMode()" title="프로젝트별 그룹 보기">
181
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="7" height="7"/><rect x="14" y="3" width="7" height="7"/><rect x="3" y="14" width="7" height="7"/><rect x="14" y="14" width="7" height="7"/></svg>
182
+ </button>
165
183
  <button class="btn btn-sm btn-icon" onclick="fetchJobs()" title="새로고침">
166
184
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
167
185
  </button>
@@ -170,6 +188,39 @@
170
188
  </button>
171
189
  </div>
172
190
  </div>
191
+ <div class="project-strip" id="projectStrip"></div>
192
+ <div class="project-detail" id="projectDetail" style="display:none;"></div>
193
+ <div class="stats-bar" id="statsBar">
194
+ <div class="stats-period-btns">
195
+ <button class="stats-period-btn active" data-period="all" onclick="setStatsPeriod('all')">전체</button>
196
+ <button class="stats-period-btn" data-period="day" onclick="setStatsPeriod('day')">24h</button>
197
+ <button class="stats-period-btn" data-period="week" onclick="setStatsPeriod('week')">7d</button>
198
+ <button class="stats-period-btn" data-period="month" onclick="setStatsPeriod('month')">30d</button>
199
+ </div>
200
+ <div class="stats-metrics" id="statsMetrics">
201
+ <span class="stats-metric" id="statTotal" title="전체 작업 수">-</span>
202
+ <span class="stats-metric stats-success" id="statSuccess" title="성공률">-</span>
203
+ <span class="stats-metric stats-duration" id="statDuration" title="평균 소요시간">-</span>
204
+ </div>
205
+ </div>
206
+ <div class="job-filter-bar" id="jobFilterBar">
207
+ <div class="job-filter-btns">
208
+ <button class="job-filter-btn active" data-filter="all" onclick="setJobFilter('all')">전체</button>
209
+ <button class="job-filter-btn" data-filter="running" onclick="setJobFilter('running')">실행 중</button>
210
+ <button class="job-filter-btn" data-filter="done" onclick="setJobFilter('done')">완료</button>
211
+ <button class="job-filter-btn" data-filter="failed" onclick="setJobFilter('failed')">실패</button>
212
+ </div>
213
+ <div class="job-project-filter">
214
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 19a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h5l2 3h9a2 2 0 0 1 2 2z"/></svg>
215
+ <select id="jobProjectSelect" onchange="setJobProjectFilter(this.value)">
216
+ <option value="all" data-i18n="all_projects">전체 프로젝트</option>
217
+ </select>
218
+ </div>
219
+ <div class="job-search-wrap">
220
+ <svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="11" cy="11" r="8"/><line x1="21" y1="21" x2="16.65" y2="16.65"/></svg>
221
+ <input type="text" class="job-search-input" id="jobSearchInput" placeholder="프롬프트 검색..." oninput="applyJobFilters()">
222
+ </div>
223
+ </div>
173
224
  <div class="table-wrap" id="jobTableWrap">
174
225
  <table>
175
226
  <thead>
@@ -179,38 +230,69 @@
179
230
  <th data-i18n="prompt">프롬프트</th>
180
231
  <th style="width:12%;" data-i18n="folder">폴더</th>
181
232
  <th style="width:9%;">Session</th>
182
- <th style="width:14%;" data-i18n="created_at">생성 시간</th>
183
233
  <th style="width:10%;"></th>
184
234
  </tr>
185
235
  </thead>
186
236
  <tbody id="jobTableBody">
187
- <tr data-job-id="__loading__"><td colspan="7" class="empty-state">
237
+ <tr data-job-id="__loading__"><td colspan="6" class="empty-state">
188
238
  <div data-i18n="loading_jobs">작업 목록을 불러오는 중...</div>
189
239
  </td></tr>
190
240
  </tbody>
191
241
  </table>
242
+ <div id="jobPagination"></div>
192
243
  </div>
193
244
  </div>
194
245
  </div>
195
246
 
196
- <!-- Section 3: Automations -->
247
+ <!-- Section 3: Personas -->
248
+ <div class="section" id="personaSection">
249
+ <div class="card">
250
+ <div class="card-header">
251
+ <div class="card-title">
252
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>
253
+ <span data-i18n="personas">페르소나</span>
254
+ </div>
255
+ <div class="card-header-actions">
256
+ <button class="btn btn-sm" onclick="openCreatePersonaDialog()" title="커스텀 페르소나 만들기">
257
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
258
+ <span>만들기</span>
259
+ </button>
260
+ <button class="btn btn-sm btn-icon" onclick="fetchPersonas()" title="새로고침">
261
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
262
+ </button>
263
+ </div>
264
+ </div>
265
+ <div id="personaGrid" class="persona-grid">
266
+ <div class="empty-state" style="padding:20px; text-align:center; color:var(--text-muted); font-size:0.8rem;">
267
+ 페르소나를 불러오는 중...
268
+ </div>
269
+ </div>
270
+ </div>
271
+ </div>
272
+
273
+ <!-- Section 4: Automations -->
197
274
  <div class="section" id="pipelineSection">
198
275
  <div class="card">
199
276
  <div class="card-header">
200
277
  <div class="card-title">
201
278
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><polyline points="12 6 12 12 16 14"/></svg>
202
- <span>자동화</span>
279
+ <span data-i18n="automations">자동화</span>
203
280
  <span id="pipelineCount" style="font-size:0.72rem; color:var(--text-muted); font-weight:400;"></span>
204
281
  </div>
205
282
  <div class="card-header-actions">
283
+ <button class="btn btn-sm btn-primary" onclick="openCreatePipeline()" title="새 자동화 생성">
284
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>
285
+ <span style="margin-left:2px;">새 자동화</span>
286
+ </button>
206
287
  <button class="btn btn-sm btn-icon" onclick="fetchPipelines()" title="새로고침">
207
288
  <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="23 4 23 10 17 10"/><path d="M20.49 15a9 9 0 1 1-2.12-9.36L23 10"/></svg>
208
289
  </button>
209
290
  </div>
210
291
  </div>
292
+ <div id="evolutionSummary"></div>
211
293
  <div id="pipelineList" class="pipeline-list">
212
294
  <div class="empty-state" style="padding:20px; text-align:center; color:var(--text-muted); font-size:0.8rem;">
213
- 자동화가 없습니다. 위 입력창에서 자동화 버튼을 눌러 등록하세요.
295
+ 자동화가 없습니다. 위 프리셋을 적용하거나 입력창에서 자동화를 등록하세요.
214
296
  </div>
215
297
  </div>
216
298
  </div>
@@ -221,6 +303,12 @@
221
303
 
222
304
  <!-- Pipeline modal removed — 자동화 등록은 입력창에서 직접 수행 -->
223
305
 
306
+ <!-- Theme Toggle FAB -->
307
+ <button class="theme-fab" onclick="toggleTheme()" title="테마 전환">
308
+ <svg class="theme-icon-dark" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="5"/><line x1="12" y1="1" x2="12" y2="3"/><line x1="12" y1="21" x2="12" y2="23"/><line x1="4.22" y1="4.22" x2="5.64" y2="5.64"/><line x1="18.36" y1="18.36" x2="19.78" y2="19.78"/><line x1="1" y1="12" x2="3" y2="12"/><line x1="21" y1="12" x2="23" y2="12"/><line x1="4.22" y1="19.78" x2="5.64" y2="18.36"/><line x1="18.36" y1="5.64" x2="19.78" y2="4.22"/></svg>
309
+ <svg class="theme-icon-light" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 12.79A9 9 0 1 1 11.21 3 7 7 0 0 0 21 12.79z"/></svg>
310
+ </button>
311
+
224
312
  <!-- Settings FAB -->
225
313
  <button class="settings-fab" onclick="openSettings()" title="설정">
226
314
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06A1.65 1.65 0 0 0 4.68 15a1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06A1.65 1.65 0 0 0 9 4.68a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06A1.65 1.65 0 0 0 19.4 9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
@@ -263,6 +351,54 @@
263
351
  </div>
264
352
  </div>
265
353
 
354
+ <!-- 웹훅 / Webhook -->
355
+ <div class="settings-section">
356
+ <div class="settings-section-title" data-i18n="webhook_settings">웹훅 설정</div>
357
+ <div class="setting-row">
358
+ <div class="setting-info">
359
+ <div class="setting-label" data-i18n="webhook_url">Webhook URL</div>
360
+ <div class="setting-desc" data-i18n="webhook_url_desc">작업 완료/실패 시 결과를 POST할 URL</div>
361
+ </div>
362
+ <div class="setting-control">
363
+ <input type="url" class="setting-input" id="cfgWebhookUrl" placeholder="https://example.com/webhook" style="width:260px;">
364
+ </div>
365
+ </div>
366
+ <div class="setting-row">
367
+ <div class="setting-info">
368
+ <div class="setting-label" data-i18n="webhook_secret">Webhook Secret</div>
369
+ <div class="setting-desc" data-i18n="webhook_secret_desc">HMAC-SHA256 서명용 비밀 키 (선택사항)</div>
370
+ </div>
371
+ <div class="setting-control">
372
+ <input type="password" class="setting-input" id="cfgWebhookSecret" placeholder="선택사항" style="width:260px;">
373
+ </div>
374
+ </div>
375
+ <div class="setting-row">
376
+ <div class="setting-info">
377
+ <div class="setting-label" data-i18n="webhook_events">이벤트 필터</div>
378
+ <div class="setting-desc" data-i18n="webhook_events_desc">웹훅을 보낼 이벤트 (done, failed)</div>
379
+ </div>
380
+ <div class="setting-control">
381
+ <select class="setting-input" id="cfgWebhookEvents" style="width:160px;">
382
+ <option value="done,failed">전체 (done, failed)</option>
383
+ <option value="done">완료만 (done)</option>
384
+ <option value="failed">실패만 (failed)</option>
385
+ </select>
386
+ </div>
387
+ </div>
388
+ <div class="setting-row">
389
+ <div class="setting-info">
390
+ <div class="setting-label" data-i18n="webhook_test">연결 테스트</div>
391
+ <div class="setting-desc" data-i18n="webhook_test_desc">설정된 URL로 테스트 이벤트를 전송합니다</div>
392
+ </div>
393
+ <div class="setting-control">
394
+ <button class="btn btn-sm" id="btnWebhookTest" onclick="testWebhook()">
395
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M22 2L11 13"/><polygon points="22 2 15 22 11 13 2 9 22 2"/></svg>
396
+ 테스트 전송
397
+ </button>
398
+ </div>
399
+ </div>
400
+ </div>
401
+
266
402
  </div>
267
403
  <div class="settings-footer">
268
404
  <button class="btn" onclick="closeSettings()" data-i18n="close">닫기</button>
@@ -278,7 +414,7 @@
278
414
 
279
415
  <script>
280
416
  // 캐시 버스팅: 순서 보장 로드
281
- ['i18n','utils','api','context','attachments','dirs','send','stream','jobs','pipelines','settings','app']
417
+ ['i18n','utils','api','context','attachments','dirs','send','stream','jobs','pipelines','personas','settings','app']
282
418
  .forEach(m => { const s = document.createElement('script'); s.src = m + '.js?t=' + Date.now(); s.async = false; document.body.appendChild(s); });
283
419
  </script>
284
420
  </body>