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.
- package/README.md +2 -2
- package/bin/autoloop.sh +382 -0
- package/bin/ctl +327 -5
- package/bin/native-app.py +5 -2
- package/bin/watchdog.sh +357 -0
- package/cognitive/__init__.py +14 -0
- package/cognitive/__pycache__/__init__.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/dispatcher.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/evaluator.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/goal_engine.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/learning.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/orchestrator.cpython-314.pyc +0 -0
- package/cognitive/__pycache__/planner.cpython-314.pyc +0 -0
- package/cognitive/dispatcher.py +192 -0
- package/cognitive/evaluator.py +289 -0
- package/cognitive/goal_engine.py +232 -0
- package/cognitive/learning.py +189 -0
- package/cognitive/orchestrator.py +303 -0
- package/cognitive/planner.py +207 -0
- package/cognitive/prompts/analyst.md +31 -0
- package/cognitive/prompts/coder.md +22 -0
- package/cognitive/prompts/reviewer.md +33 -0
- package/cognitive/prompts/tester.md +21 -0
- package/cognitive/prompts/writer.md +25 -0
- package/config.sh +6 -1
- package/dag/__init__.py +5 -0
- package/dag/__pycache__/__init__.cpython-314.pyc +0 -0
- package/dag/__pycache__/graph.cpython-314.pyc +0 -0
- package/dag/graph.py +222 -0
- package/lib/jobs.sh +12 -1
- package/package.json +5 -1
- package/postinstall.sh +1 -1
- package/service/controller.sh +43 -11
- package/web/audit.py +122 -0
- package/web/checkpoint.py +80 -0
- package/web/config.py +2 -5
- package/web/handler.py +464 -26
- package/web/handler_fs.py +15 -14
- package/web/handler_goals.py +203 -0
- package/web/handler_jobs.py +165 -42
- package/web/handler_memory.py +203 -0
- package/web/jobs.py +576 -12
- package/web/personas.py +419 -0
- package/web/pipeline.py +682 -50
- package/web/presets.py +506 -0
- package/web/projects.py +58 -4
- package/web/static/api.js +90 -3
- package/web/static/app.js +8 -0
- package/web/static/base.css +51 -12
- package/web/static/context.js +14 -4
- package/web/static/form.css +3 -2
- package/web/static/goals.css +363 -0
- package/web/static/goals.js +300 -0
- package/web/static/i18n.js +288 -0
- package/web/static/index.html +142 -6
- package/web/static/jobs.css +951 -4
- package/web/static/jobs.js +890 -54
- package/web/static/memoryview.js +117 -0
- package/web/static/personas.js +228 -0
- package/web/static/pipeline.css +308 -1
- package/web/static/pipelines.js +249 -14
- package/web/static/presets.js +244 -0
- package/web/static/send.js +26 -4
- package/web/static/settings-style.css +34 -3
- package/web/static/settings.js +37 -1
- package/web/static/stream.js +242 -19
- package/web/static/utils.js +54 -2
- package/web/webhook.py +210 -0
package/web/static/api.js
CHANGED
|
@@ -8,15 +8,58 @@ let AUTH_TOKEN = '';
|
|
|
8
8
|
let _backendConnected = false;
|
|
9
9
|
let serviceRunning = null;
|
|
10
10
|
|
|
11
|
+
/* ── Connection health tracking ── */
|
|
12
|
+
let _connFailCount = 0;
|
|
13
|
+
const _CONN_FAIL_THRESHOLD = 3;
|
|
14
|
+
let _connBannerVisible = false;
|
|
15
|
+
|
|
16
|
+
function _updateConnBanner(ok) {
|
|
17
|
+
if (ok) {
|
|
18
|
+
if (_connFailCount > 0) _connFailCount = 0;
|
|
19
|
+
if (_connBannerVisible) {
|
|
20
|
+
_connBannerVisible = false;
|
|
21
|
+
const banner = document.getElementById('connLostBanner');
|
|
22
|
+
if (banner) banner.classList.remove('visible');
|
|
23
|
+
}
|
|
24
|
+
} else {
|
|
25
|
+
_connFailCount++;
|
|
26
|
+
if (_connFailCount >= _CONN_FAIL_THRESHOLD && !_connBannerVisible) {
|
|
27
|
+
_connBannerVisible = true;
|
|
28
|
+
const banner = document.getElementById('connLostBanner');
|
|
29
|
+
if (banner) banner.classList.add('visible');
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
11
34
|
async function apiFetch(path, options = {}) {
|
|
12
35
|
const headers = { 'Content-Type': 'application/json', ...options.headers };
|
|
13
36
|
if (AUTH_TOKEN) {
|
|
14
37
|
headers['Authorization'] = `Bearer ${AUTH_TOKEN}`;
|
|
15
38
|
}
|
|
16
|
-
|
|
39
|
+
let resp;
|
|
40
|
+
try {
|
|
41
|
+
resp = await fetch(`${API}${path}`, { ...options, headers });
|
|
42
|
+
} catch (networkErr) {
|
|
43
|
+
_updateConnBanner(false);
|
|
44
|
+
throw networkErr;
|
|
45
|
+
}
|
|
46
|
+
_updateConnBanner(true);
|
|
17
47
|
if (!resp.ok) {
|
|
18
|
-
|
|
19
|
-
|
|
48
|
+
let msg = '';
|
|
49
|
+
try {
|
|
50
|
+
const ct = resp.headers.get('content-type') || '';
|
|
51
|
+
if (ct.includes('application/json')) {
|
|
52
|
+
const body = await resp.json();
|
|
53
|
+
if (body.error && typeof body.error === 'object') {
|
|
54
|
+
msg = body.error.message || JSON.stringify(body.error);
|
|
55
|
+
} else {
|
|
56
|
+
msg = body.error || body.message || JSON.stringify(body);
|
|
57
|
+
}
|
|
58
|
+
} else {
|
|
59
|
+
msg = await resp.text();
|
|
60
|
+
}
|
|
61
|
+
} catch { /* parse fail — fall through */ }
|
|
62
|
+
throw new Error(msg || `HTTP ${resp.status}`);
|
|
20
63
|
}
|
|
21
64
|
const ct = resp.headers.get('content-type') || '';
|
|
22
65
|
if (ct.includes('application/json')) return resp.json();
|
|
@@ -33,6 +76,50 @@ async function checkStatus() {
|
|
|
33
76
|
}
|
|
34
77
|
}
|
|
35
78
|
|
|
79
|
+
/* ── Goals API ── */
|
|
80
|
+
async function fetchGoals(status) {
|
|
81
|
+
const qs = status ? `?status=${status}` : '';
|
|
82
|
+
return apiFetch(`/api/goals${qs}`);
|
|
83
|
+
}
|
|
84
|
+
async function createGoal(objective, mode = 'gate', opts = {}) {
|
|
85
|
+
return apiFetch('/api/goals', {
|
|
86
|
+
method: 'POST',
|
|
87
|
+
body: JSON.stringify({ objective, mode, ...opts }),
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
async function getGoal(id) { return apiFetch(`/api/goals/${id}`); }
|
|
91
|
+
async function updateGoal(id, fields) {
|
|
92
|
+
return apiFetch(`/api/goals/${id}/update`, {
|
|
93
|
+
method: 'POST', body: JSON.stringify(fields),
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
async function approveGoal(id) {
|
|
97
|
+
return apiFetch(`/api/goals/${id}/approve`, { method: 'POST' });
|
|
98
|
+
}
|
|
99
|
+
async function cancelGoal(id) {
|
|
100
|
+
return apiFetch(`/api/goals/${id}`, { method: 'DELETE' });
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/* ── Memory API ── */
|
|
104
|
+
async function fetchMemories(params = {}) {
|
|
105
|
+
const qs = new URLSearchParams(params).toString();
|
|
106
|
+
return apiFetch(`/api/memory${qs ? '?' + qs : ''}`);
|
|
107
|
+
}
|
|
108
|
+
async function createMemory(data) {
|
|
109
|
+
return apiFetch('/api/memory', {
|
|
110
|
+
method: 'POST', body: JSON.stringify(data),
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
async function getMemory(id) { return apiFetch(`/api/memory/${id}`); }
|
|
114
|
+
async function updateMemory(id, fields) {
|
|
115
|
+
return apiFetch(`/api/memory/${id}/update`, {
|
|
116
|
+
method: 'POST', body: JSON.stringify(fields),
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async function deleteMemory(id) {
|
|
120
|
+
return apiFetch(`/api/memory/${id}`, { method: 'DELETE' });
|
|
121
|
+
}
|
|
122
|
+
|
|
36
123
|
async function serviceAction(action) {
|
|
37
124
|
const btn = document.getElementById(`btn${action.charAt(0).toUpperCase() + action.slice(1)}`);
|
|
38
125
|
if (btn) btn.disabled = true;
|
package/web/static/app.js
CHANGED
|
@@ -37,13 +37,21 @@ async function autoConnect() {
|
|
|
37
37
|
|
|
38
38
|
async function init() {
|
|
39
39
|
applyI18n();
|
|
40
|
+
applyTheme(localStorage.getItem('theme') || 'dark');
|
|
40
41
|
await autoConnect();
|
|
41
42
|
loadRecentDirs();
|
|
43
|
+
fetchPersonas();
|
|
42
44
|
fetchPipelines();
|
|
43
45
|
checkStatus();
|
|
46
|
+
fetchRegisteredProjects();
|
|
44
47
|
fetchJobs();
|
|
48
|
+
fetchStats();
|
|
49
|
+
_applyJobListCollapse();
|
|
50
|
+
requestNotificationPermission();
|
|
45
51
|
|
|
46
52
|
jobPollTimer = setInterval(fetchJobs, 3000);
|
|
53
|
+
setInterval(fetchStats, 15000);
|
|
54
|
+
setInterval(fetchRegisteredProjects, 30000);
|
|
47
55
|
setInterval(checkStatus, 10000);
|
|
48
56
|
|
|
49
57
|
const promptInput = document.getElementById('promptInput');
|
package/web/static/base.css
CHANGED
|
@@ -29,6 +29,32 @@
|
|
|
29
29
|
--shadow: 0 2px 8px rgba(0,0,0,0.3);
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
/* ── Light Theme ── */
|
|
33
|
+
[data-theme="light"] {
|
|
34
|
+
--bg: #f5f6fa;
|
|
35
|
+
--surface: #ffffff;
|
|
36
|
+
--surface-hover: #f0f1f5;
|
|
37
|
+
--surface-active: #e8e9ef;
|
|
38
|
+
--border: #d8dae3;
|
|
39
|
+
--border-light: #c5c8d4;
|
|
40
|
+
--text: #1a1d27;
|
|
41
|
+
--text-secondary: #4a4e63;
|
|
42
|
+
--text-muted: #8b8fa3;
|
|
43
|
+
--accent: #4f6cf7;
|
|
44
|
+
--accent-hover: #3d5ce5;
|
|
45
|
+
--accent-glow: rgba(79, 108, 247, 0.12);
|
|
46
|
+
--green: #0d9668;
|
|
47
|
+
--green-dim: rgba(13, 150, 104, 0.1);
|
|
48
|
+
--red: #dc3545;
|
|
49
|
+
--red-dim: rgba(220, 53, 69, 0.08);
|
|
50
|
+
--yellow: #c07d10;
|
|
51
|
+
--yellow-dim: rgba(192, 125, 16, 0.08);
|
|
52
|
+
--blue: #2563eb;
|
|
53
|
+
--blue-dim: rgba(37, 99, 235, 0.08);
|
|
54
|
+
--shadow: 0 2px 8px rgba(0,0,0,0.08);
|
|
55
|
+
--stream-bg: #f0f1f5;
|
|
56
|
+
}
|
|
57
|
+
|
|
32
58
|
html { font-size: 14px; }
|
|
33
59
|
|
|
34
60
|
body {
|
|
@@ -164,6 +190,7 @@ button {
|
|
|
164
190
|
border: 1px solid transparent;
|
|
165
191
|
letter-spacing: 0.02em;
|
|
166
192
|
text-transform: lowercase;
|
|
193
|
+
transition: all 0.15s ease;
|
|
167
194
|
}
|
|
168
195
|
|
|
169
196
|
.ctx-btn:hover {
|
|
@@ -178,6 +205,14 @@ button {
|
|
|
178
205
|
border-color: var(--accent);
|
|
179
206
|
}
|
|
180
207
|
|
|
208
|
+
.ctx-btn.active[id="ctxResume"],
|
|
209
|
+
.ctx-btn.active[id="ctxFork"] {
|
|
210
|
+
background: var(--blue);
|
|
211
|
+
color: #fff;
|
|
212
|
+
border-color: var(--blue);
|
|
213
|
+
box-shadow: 0 1px 4px rgba(0,0,0,0.15);
|
|
214
|
+
}
|
|
215
|
+
|
|
181
216
|
.ctx-btn-x {
|
|
182
217
|
padding: 4px 6px;
|
|
183
218
|
border-radius: var(--radius);
|
|
@@ -200,21 +235,32 @@ button {
|
|
|
200
235
|
|
|
201
236
|
.ctx-session-label {
|
|
202
237
|
display: none;
|
|
203
|
-
padding:
|
|
238
|
+
padding: 3px 10px;
|
|
204
239
|
border-radius: 12px;
|
|
205
|
-
font-size: 0.
|
|
240
|
+
font-size: 0.75rem;
|
|
241
|
+
font-weight: 600;
|
|
206
242
|
font-family: var(--font-mono);
|
|
207
|
-
background: var(--blue
|
|
208
|
-
color:
|
|
243
|
+
background: var(--blue);
|
|
244
|
+
color: #fff;
|
|
209
245
|
overflow: hidden;
|
|
210
246
|
text-overflow: ellipsis;
|
|
211
247
|
white-space: nowrap;
|
|
212
248
|
line-height: 1.6;
|
|
213
249
|
min-width: 0;
|
|
250
|
+
max-width: 200px;
|
|
251
|
+
box-shadow: 0 0 0 2px var(--blue-dim), 0 1px 4px rgba(0,0,0,0.15);
|
|
252
|
+
animation: ctx-label-pop 0.2s ease-out;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
@keyframes ctx-label-pop {
|
|
256
|
+
from { opacity: 0; transform: scale(0.9); }
|
|
257
|
+
to { opacity: 1; transform: scale(1); }
|
|
214
258
|
}
|
|
215
259
|
|
|
216
260
|
.ctx-session-label.visible {
|
|
217
|
-
display: inline;
|
|
261
|
+
display: inline-flex;
|
|
262
|
+
align-items: center;
|
|
263
|
+
gap: 4px;
|
|
218
264
|
}
|
|
219
265
|
|
|
220
266
|
.session-picker {
|
|
@@ -371,13 +417,6 @@ button {
|
|
|
371
417
|
white-space: nowrap;
|
|
372
418
|
}
|
|
373
419
|
|
|
374
|
-
.session-item-cost {
|
|
375
|
-
font-size: 0.65rem;
|
|
376
|
-
color: var(--green);
|
|
377
|
-
font-family: var(--font-mono);
|
|
378
|
-
flex-shrink: 0;
|
|
379
|
-
}
|
|
380
|
-
|
|
381
420
|
.session-filter-bar {
|
|
382
421
|
display: flex;
|
|
383
422
|
align-items: center;
|
package/web/static/context.js
CHANGED
|
@@ -24,19 +24,29 @@ function _updateContextUI() {
|
|
|
24
24
|
const promptInfo = document.getElementById('promptSessionInfo');
|
|
25
25
|
|
|
26
26
|
[newBtn, resumeBtn, forkBtn].forEach(b => b.classList.remove('active'));
|
|
27
|
+
label.classList.remove('visible');
|
|
28
|
+
label.textContent = '';
|
|
29
|
+
|
|
27
30
|
if (_contextMode === 'new') {
|
|
28
31
|
newBtn.classList.add('active');
|
|
29
|
-
|
|
30
|
-
if (promptInfo) promptInfo.textContent = 'new session';
|
|
32
|
+
if (promptInfo) promptInfo.textContent = '';
|
|
31
33
|
} else if (_contextMode === 'resume') {
|
|
32
34
|
resumeBtn.classList.add('active');
|
|
33
35
|
const sid = _contextSessionId ? _contextSessionId.slice(0, 8) : '';
|
|
34
|
-
|
|
36
|
+
if (sid) {
|
|
37
|
+
const promptSnippet = _contextSessionPrompt ? _contextSessionPrompt.slice(0, 24) : '';
|
|
38
|
+
label.textContent = promptSnippet ? `${sid}… ${promptSnippet}` : `${sid}…`;
|
|
39
|
+
label.classList.add('visible');
|
|
40
|
+
}
|
|
35
41
|
if (promptInfo) promptInfo.textContent = sid ? `resume:${sid}` : 'resume';
|
|
36
42
|
} else if (_contextMode === 'fork') {
|
|
37
43
|
forkBtn.classList.add('active');
|
|
38
44
|
const sid = _contextSessionId ? _contextSessionId.slice(0, 8) : '';
|
|
39
|
-
|
|
45
|
+
if (sid) {
|
|
46
|
+
const promptSnippet = _contextSessionPrompt ? _contextSessionPrompt.slice(0, 24) : '';
|
|
47
|
+
label.textContent = promptSnippet ? `${sid}… ${promptSnippet}` : `${sid}…`;
|
|
48
|
+
label.classList.add('visible');
|
|
49
|
+
}
|
|
40
50
|
if (promptInfo) promptInfo.textContent = sid ? `fork:${sid}` : 'fork';
|
|
41
51
|
}
|
|
42
52
|
}
|
package/web/static/form.css
CHANGED
|
@@ -522,8 +522,8 @@ textarea { min-height: unset; position: relative; }
|
|
|
522
522
|
}
|
|
523
523
|
|
|
524
524
|
.prompt-wrapper.drag-over textarea {
|
|
525
|
-
border-color:
|
|
526
|
-
box-shadow:
|
|
525
|
+
border-color: transparent;
|
|
526
|
+
box-shadow: none;
|
|
527
527
|
}
|
|
528
528
|
|
|
529
529
|
.drop-overlay {
|
|
@@ -545,6 +545,7 @@ textarea { min-height: unset; position: relative; }
|
|
|
545
545
|
|
|
546
546
|
.prompt-wrapper.drag-over .drop-overlay {
|
|
547
547
|
display: flex;
|
|
548
|
+
box-shadow: 0 0 0 3px var(--accent-glow);
|
|
548
549
|
}
|
|
549
550
|
|
|
550
551
|
.image-previews {
|
|
@@ -0,0 +1,363 @@
|
|
|
1
|
+
/* ═══════════════════════════════════════════════
|
|
2
|
+
Goals — 목표 관리 + DAG 시각화 스타일
|
|
3
|
+
═══════════════════════════════════════════════ */
|
|
4
|
+
|
|
5
|
+
/* ── Goal Filter Bar ── */
|
|
6
|
+
.goal-filter-bar {
|
|
7
|
+
display: flex;
|
|
8
|
+
align-items: center;
|
|
9
|
+
gap: 8px;
|
|
10
|
+
padding: 8px 16px;
|
|
11
|
+
border-bottom: 1px solid var(--border);
|
|
12
|
+
}
|
|
13
|
+
.goal-filter-btns {
|
|
14
|
+
display: flex;
|
|
15
|
+
gap: 2px;
|
|
16
|
+
}
|
|
17
|
+
.goal-filter-btn {
|
|
18
|
+
padding: 4px 12px;
|
|
19
|
+
font-size: 0.72rem;
|
|
20
|
+
border: none;
|
|
21
|
+
background: transparent;
|
|
22
|
+
color: var(--text-muted);
|
|
23
|
+
border-radius: 4px;
|
|
24
|
+
cursor: pointer;
|
|
25
|
+
transition: all var(--transition);
|
|
26
|
+
}
|
|
27
|
+
.goal-filter-btn:hover { background: var(--surface-hover); color: var(--text-secondary); }
|
|
28
|
+
.goal-filter-btn.active { background: var(--accent-glow); color: var(--accent); font-weight: 500; }
|
|
29
|
+
|
|
30
|
+
/* ── Goal List ── */
|
|
31
|
+
.goal-list {
|
|
32
|
+
display: flex;
|
|
33
|
+
flex-direction: column;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/* ── Goal Card ── */
|
|
37
|
+
.goal-card {
|
|
38
|
+
padding: 14px 16px;
|
|
39
|
+
border-bottom: 1px solid var(--border);
|
|
40
|
+
cursor: pointer;
|
|
41
|
+
transition: background var(--transition);
|
|
42
|
+
}
|
|
43
|
+
.goal-card:hover { background: var(--surface-hover); }
|
|
44
|
+
.goal-card:last-child { border-bottom: none; }
|
|
45
|
+
.goal-card.expanded { background: var(--surface-hover); }
|
|
46
|
+
|
|
47
|
+
.goal-card-header {
|
|
48
|
+
display: flex;
|
|
49
|
+
align-items: flex-start;
|
|
50
|
+
gap: 10px;
|
|
51
|
+
}
|
|
52
|
+
.goal-card-status {
|
|
53
|
+
flex-shrink: 0;
|
|
54
|
+
width: 8px;
|
|
55
|
+
height: 8px;
|
|
56
|
+
border-radius: 50%;
|
|
57
|
+
margin-top: 5px;
|
|
58
|
+
}
|
|
59
|
+
.goal-card-status.pending { background: var(--text-muted); }
|
|
60
|
+
.goal-card-status.planning { background: var(--yellow); animation: pulse-glow 2s infinite; }
|
|
61
|
+
.goal-card-status.ready { background: var(--blue); }
|
|
62
|
+
.goal-card-status.running { background: var(--blue); animation: pulse-glow 1.5s infinite; }
|
|
63
|
+
.goal-card-status.gate_waiting { background: var(--yellow); }
|
|
64
|
+
.goal-card-status.evaluating { background: var(--yellow); animation: pulse-glow 2s infinite; }
|
|
65
|
+
.goal-card-status.completed { background: var(--green); }
|
|
66
|
+
.goal-card-status.failed { background: var(--red); }
|
|
67
|
+
.goal-card-status.cancelled { background: var(--text-muted); opacity: 0.5; }
|
|
68
|
+
|
|
69
|
+
@keyframes pulse-glow {
|
|
70
|
+
0%, 100% { opacity: 1; }
|
|
71
|
+
50% { opacity: 0.4; }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
.goal-card-body { flex: 1; min-width: 0; }
|
|
75
|
+
.goal-card-objective {
|
|
76
|
+
font-size: 0.82rem;
|
|
77
|
+
font-weight: 500;
|
|
78
|
+
color: var(--text);
|
|
79
|
+
line-height: 1.4;
|
|
80
|
+
margin-bottom: 4px;
|
|
81
|
+
}
|
|
82
|
+
.goal-card-meta {
|
|
83
|
+
display: flex;
|
|
84
|
+
align-items: center;
|
|
85
|
+
gap: 12px;
|
|
86
|
+
font-size: 0.68rem;
|
|
87
|
+
color: var(--text-muted);
|
|
88
|
+
}
|
|
89
|
+
.goal-card-meta .goal-mode-badge {
|
|
90
|
+
padding: 1px 6px;
|
|
91
|
+
border-radius: 3px;
|
|
92
|
+
background: var(--accent-glow);
|
|
93
|
+
color: var(--accent);
|
|
94
|
+
font-weight: 500;
|
|
95
|
+
font-size: 0.65rem;
|
|
96
|
+
text-transform: uppercase;
|
|
97
|
+
}
|
|
98
|
+
.goal-card-meta .goal-status-label {
|
|
99
|
+
font-weight: 500;
|
|
100
|
+
}
|
|
101
|
+
.goal-card-actions {
|
|
102
|
+
display: flex;
|
|
103
|
+
gap: 4px;
|
|
104
|
+
flex-shrink: 0;
|
|
105
|
+
}
|
|
106
|
+
.goal-card-actions .btn {
|
|
107
|
+
padding: 3px 8px;
|
|
108
|
+
font-size: 0.68rem;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/* ── Goal Progress Bar ── */
|
|
112
|
+
.goal-progress-bar {
|
|
113
|
+
height: 3px;
|
|
114
|
+
background: var(--border);
|
|
115
|
+
border-radius: 2px;
|
|
116
|
+
margin-top: 8px;
|
|
117
|
+
overflow: hidden;
|
|
118
|
+
}
|
|
119
|
+
.goal-progress-fill {
|
|
120
|
+
height: 100%;
|
|
121
|
+
background: var(--accent);
|
|
122
|
+
border-radius: 2px;
|
|
123
|
+
transition: width 0.5s ease;
|
|
124
|
+
}
|
|
125
|
+
.goal-progress-fill.done { background: var(--green); }
|
|
126
|
+
.goal-progress-fill.failed { background: var(--red); }
|
|
127
|
+
|
|
128
|
+
/* ── Goal Detail (expanded) ── */
|
|
129
|
+
.goal-detail {
|
|
130
|
+
padding: 12px 16px 16px 34px;
|
|
131
|
+
display: none;
|
|
132
|
+
}
|
|
133
|
+
.goal-card.expanded .goal-detail { display: block; }
|
|
134
|
+
|
|
135
|
+
/* ── DAG Tree View ── */
|
|
136
|
+
.dag-tree {
|
|
137
|
+
margin-top: 8px;
|
|
138
|
+
}
|
|
139
|
+
.dag-tree ul {
|
|
140
|
+
list-style: none;
|
|
141
|
+
padding-left: 20px;
|
|
142
|
+
position: relative;
|
|
143
|
+
}
|
|
144
|
+
.dag-tree > ul { padding-left: 0; }
|
|
145
|
+
|
|
146
|
+
.dag-tree li {
|
|
147
|
+
position: relative;
|
|
148
|
+
padding: 4px 0 4px 18px;
|
|
149
|
+
}
|
|
150
|
+
.dag-tree li::before {
|
|
151
|
+
content: '';
|
|
152
|
+
position: absolute;
|
|
153
|
+
left: 0;
|
|
154
|
+
top: 0;
|
|
155
|
+
bottom: 0;
|
|
156
|
+
width: 1px;
|
|
157
|
+
background: var(--border-light);
|
|
158
|
+
}
|
|
159
|
+
.dag-tree li::after {
|
|
160
|
+
content: '';
|
|
161
|
+
position: absolute;
|
|
162
|
+
left: 0;
|
|
163
|
+
top: 14px;
|
|
164
|
+
width: 12px;
|
|
165
|
+
height: 1px;
|
|
166
|
+
background: var(--border-light);
|
|
167
|
+
}
|
|
168
|
+
.dag-tree li:last-child::before { height: 14px; }
|
|
169
|
+
|
|
170
|
+
.dag-node {
|
|
171
|
+
display: inline-flex;
|
|
172
|
+
align-items: center;
|
|
173
|
+
gap: 6px;
|
|
174
|
+
padding: 4px 10px;
|
|
175
|
+
border-radius: 4px;
|
|
176
|
+
font-size: 0.72rem;
|
|
177
|
+
background: var(--surface);
|
|
178
|
+
border: 1px solid var(--border);
|
|
179
|
+
transition: all var(--transition);
|
|
180
|
+
}
|
|
181
|
+
.dag-node:hover { border-color: var(--accent); }
|
|
182
|
+
|
|
183
|
+
.dag-node-dot {
|
|
184
|
+
width: 6px;
|
|
185
|
+
height: 6px;
|
|
186
|
+
border-radius: 50%;
|
|
187
|
+
flex-shrink: 0;
|
|
188
|
+
}
|
|
189
|
+
.dag-node-dot.pending { background: var(--text-muted); }
|
|
190
|
+
.dag-node-dot.running { background: var(--blue); animation: pulse-glow 1.5s infinite; }
|
|
191
|
+
.dag-node-dot.completed { background: var(--green); }
|
|
192
|
+
.dag-node-dot.failed { background: var(--red); }
|
|
193
|
+
|
|
194
|
+
.dag-node-label { color: var(--text); }
|
|
195
|
+
.dag-node-type {
|
|
196
|
+
font-size: 0.62rem;
|
|
197
|
+
color: var(--text-muted);
|
|
198
|
+
font-family: var(--font-mono, monospace);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/* ── Goal Create Form ── */
|
|
202
|
+
.goal-create-form {
|
|
203
|
+
padding: 12px 16px;
|
|
204
|
+
border-bottom: 1px solid var(--border);
|
|
205
|
+
display: none;
|
|
206
|
+
}
|
|
207
|
+
.goal-create-form.visible { display: block; }
|
|
208
|
+
.goal-create-row {
|
|
209
|
+
display: flex;
|
|
210
|
+
gap: 8px;
|
|
211
|
+
align-items: flex-end;
|
|
212
|
+
margin-bottom: 8px;
|
|
213
|
+
}
|
|
214
|
+
.goal-create-row .form-field { flex: 1; }
|
|
215
|
+
.goal-create-row .form-field label {
|
|
216
|
+
display: block;
|
|
217
|
+
font-size: 0.68rem;
|
|
218
|
+
color: var(--text-secondary);
|
|
219
|
+
margin-bottom: 3px;
|
|
220
|
+
}
|
|
221
|
+
.goal-create-row .form-field input,
|
|
222
|
+
.goal-create-row .form-field select,
|
|
223
|
+
.goal-create-row .form-field textarea {
|
|
224
|
+
width: 100%;
|
|
225
|
+
padding: 6px 10px;
|
|
226
|
+
font-size: 0.78rem;
|
|
227
|
+
background: var(--bg);
|
|
228
|
+
border: 1px solid var(--border);
|
|
229
|
+
border-radius: var(--radius);
|
|
230
|
+
color: var(--text);
|
|
231
|
+
font-family: var(--font);
|
|
232
|
+
}
|
|
233
|
+
.goal-create-row .form-field textarea {
|
|
234
|
+
resize: vertical;
|
|
235
|
+
min-height: 36px;
|
|
236
|
+
}
|
|
237
|
+
.goal-create-actions {
|
|
238
|
+
display: flex;
|
|
239
|
+
justify-content: flex-end;
|
|
240
|
+
gap: 6px;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/* ── Memory Section ── */
|
|
244
|
+
.memory-filter-bar {
|
|
245
|
+
display: flex;
|
|
246
|
+
align-items: center;
|
|
247
|
+
gap: 8px;
|
|
248
|
+
padding: 8px 16px;
|
|
249
|
+
border-bottom: 1px solid var(--border);
|
|
250
|
+
}
|
|
251
|
+
.memory-search-wrap {
|
|
252
|
+
flex: 1;
|
|
253
|
+
display: flex;
|
|
254
|
+
align-items: center;
|
|
255
|
+
gap: 6px;
|
|
256
|
+
}
|
|
257
|
+
.memory-search-wrap input {
|
|
258
|
+
flex: 1;
|
|
259
|
+
padding: 5px 10px;
|
|
260
|
+
font-size: 0.75rem;
|
|
261
|
+
background: var(--bg);
|
|
262
|
+
border: 1px solid var(--border);
|
|
263
|
+
border-radius: var(--radius);
|
|
264
|
+
color: var(--text);
|
|
265
|
+
}
|
|
266
|
+
.memory-type-select {
|
|
267
|
+
padding: 5px 8px;
|
|
268
|
+
font-size: 0.72rem;
|
|
269
|
+
background: var(--bg);
|
|
270
|
+
border: 1px solid var(--border);
|
|
271
|
+
border-radius: var(--radius);
|
|
272
|
+
color: var(--text);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/* ── Memory Card ── */
|
|
276
|
+
.memory-list {
|
|
277
|
+
display: flex;
|
|
278
|
+
flex-direction: column;
|
|
279
|
+
}
|
|
280
|
+
.memory-card {
|
|
281
|
+
padding: 12px 16px;
|
|
282
|
+
border-bottom: 1px solid var(--border);
|
|
283
|
+
transition: background var(--transition);
|
|
284
|
+
}
|
|
285
|
+
.memory-card:hover { background: var(--surface-hover); }
|
|
286
|
+
.memory-card:last-child { border-bottom: none; }
|
|
287
|
+
|
|
288
|
+
.memory-card-header {
|
|
289
|
+
display: flex;
|
|
290
|
+
align-items: center;
|
|
291
|
+
gap: 8px;
|
|
292
|
+
margin-bottom: 4px;
|
|
293
|
+
}
|
|
294
|
+
.memory-type-badge {
|
|
295
|
+
padding: 1px 6px;
|
|
296
|
+
border-radius: 3px;
|
|
297
|
+
font-size: 0.62rem;
|
|
298
|
+
font-weight: 600;
|
|
299
|
+
text-transform: uppercase;
|
|
300
|
+
}
|
|
301
|
+
.memory-type-badge.decision { background: var(--blue-dim); color: var(--blue); }
|
|
302
|
+
.memory-type-badge.pattern { background: var(--green-dim); color: var(--green); }
|
|
303
|
+
.memory-type-badge.failure { background: var(--red-dim); color: var(--red); }
|
|
304
|
+
.memory-type-badge.context { background: var(--yellow-dim); color: var(--yellow); }
|
|
305
|
+
|
|
306
|
+
.memory-card-title {
|
|
307
|
+
font-size: 0.78rem;
|
|
308
|
+
font-weight: 500;
|
|
309
|
+
color: var(--text);
|
|
310
|
+
flex: 1;
|
|
311
|
+
}
|
|
312
|
+
.memory-card-actions {
|
|
313
|
+
display: flex;
|
|
314
|
+
gap: 4px;
|
|
315
|
+
}
|
|
316
|
+
.memory-card-actions button {
|
|
317
|
+
padding: 2px 6px;
|
|
318
|
+
background: transparent;
|
|
319
|
+
border: none;
|
|
320
|
+
color: var(--text-muted);
|
|
321
|
+
cursor: pointer;
|
|
322
|
+
border-radius: 3px;
|
|
323
|
+
transition: all var(--transition);
|
|
324
|
+
}
|
|
325
|
+
.memory-card-actions button:hover { background: var(--surface-active); color: var(--text); }
|
|
326
|
+
|
|
327
|
+
.memory-card-content {
|
|
328
|
+
font-size: 0.72rem;
|
|
329
|
+
color: var(--text-secondary);
|
|
330
|
+
line-height: 1.5;
|
|
331
|
+
margin-bottom: 4px;
|
|
332
|
+
white-space: pre-wrap;
|
|
333
|
+
max-height: 60px;
|
|
334
|
+
overflow: hidden;
|
|
335
|
+
}
|
|
336
|
+
.memory-card-tags {
|
|
337
|
+
display: flex;
|
|
338
|
+
gap: 4px;
|
|
339
|
+
flex-wrap: wrap;
|
|
340
|
+
}
|
|
341
|
+
.memory-tag {
|
|
342
|
+
padding: 1px 6px;
|
|
343
|
+
font-size: 0.62rem;
|
|
344
|
+
background: var(--surface-active);
|
|
345
|
+
color: var(--text-muted);
|
|
346
|
+
border-radius: 3px;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
/* ── Memory Create Form ── */
|
|
350
|
+
.memory-create-form {
|
|
351
|
+
padding: 12px 16px;
|
|
352
|
+
border-bottom: 1px solid var(--border);
|
|
353
|
+
display: none;
|
|
354
|
+
}
|
|
355
|
+
.memory-create-form.visible { display: block; }
|
|
356
|
+
|
|
357
|
+
/* ── Empty State ── */
|
|
358
|
+
.goal-empty, .memory-empty {
|
|
359
|
+
padding: 32px 20px;
|
|
360
|
+
text-align: center;
|
|
361
|
+
color: var(--text-muted);
|
|
362
|
+
font-size: 0.8rem;
|
|
363
|
+
}
|