@njeybe/nativ 2.7.0 → 2.8.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/CHANGELOG.md +15 -0
- package/dist/core/setup/pm-playbook.d.ts +1 -1
- package/dist/core/setup/pm-playbook.js +1 -1
- package/dist/server/studio-ui/pages/benchmarks/script.js +9 -8
- package/dist/server/studio-ui/pages/benchmarks/styles.js +3 -1
- package/dist/server/studio-ui/pages/database/browser.js +1 -1
- package/dist/server/studio-ui/pages/database/connection.js +5 -4
- package/dist/server/studio-ui/pages/database/data-shell.js +8 -13
- package/dist/server/studio-ui/pages/database/drift.js +1 -1
- package/dist/server/studio-ui/pages/database/events.js +1 -2
- package/dist/server/studio-ui/pages/database/explorer.js +4 -4
- package/dist/server/studio-ui/pages/flow/layout.js +1 -1
- package/dist/server/studio-ui/pages/flow/styles.js +1 -1
- package/dist/server/studio-ui/pages/flow/timeline-view.js +1 -1
- package/dist/server/studio-ui/pages/home/script.js +26 -29
- package/dist/server/studio-ui/pages/home/styles.js +2 -6
- package/dist/server/studio-ui/pages/tasks/script.js +25 -40
- package/dist/server/studio-ui/pages/tasks/styles.js +9 -9
- package/dist/server/studio-ui/pages/team/script.js +27 -8
- package/dist/server/studio-ui/pages/team/styles.js +0 -1
- package/dist/server/studio-ui/shell/console.js +13 -7
- package/dist/server/studio-ui/shell/detail-view.js +3 -3
- package/dist/server/studio-ui/shell/dispatch.js +9 -8
- package/dist/server/studio-ui/shell/events-tools.js +11 -0
- package/dist/server/studio-ui/shell/format.js +31 -2
- package/dist/server/studio-ui/shell/markup-head.js +1 -1
- package/dist/server/studio-ui/shell/markup-tools.js +13 -8
- package/dist/server/studio-ui/shell/model.js +8 -2
- package/dist/server/studio-ui/shell/paint.js +1 -3
- package/dist/server/studio-ui/shell/pipeline-state.js +2 -11
- package/dist/server/studio-ui/shell/pulse.js +8 -7
- package/dist/server/studio-ui/shell/sidebar-view.js +2 -3
- package/dist/server/studio-ui/shell/triage.js +1 -1
- package/dist/server/studio-ui/styles/console.js +3 -0
- package/dist/server/studio-ui/styles/shell.js +4 -6
- package/package.json +1 -1
- package/plugin/.claude-plugin/plugin.json +1 -1
- package/plugin/agents/architect.md +2 -2
- package/plugin/agents/verifier.md +4 -1
- package/plugin/skills/nativ/SKILL.md +7 -7
- package/templates/AGENTS.md +7 -7
- package/templates/GEMINI.md +1 -1
- package/templates/claude-agents/architect.md +2 -2
- package/templates/claude-agents/verifier.md +4 -1
- package/templates/dot-ai/subagents/architect.md +2 -2
|
@@ -3,7 +3,7 @@ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-te
|
|
|
3
3
|
#panel-tasks .col { background: var(--surface-2); border-radius: 6px; padding: 8px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
|
|
4
4
|
#panel-tasks .col-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 2px; font-weight: 500; font-size: 13px; }
|
|
5
5
|
#panel-tasks .col-head .count { margin-left: auto; background: var(--surface); border-radius: 999px; padding: 0 8px; font-size: 12px; color: var(--fg-2); }
|
|
6
|
-
#panel-tasks .col-empty { font-size: 12.5px; color: var(--fg-3); padding:
|
|
6
|
+
#panel-tasks .col-empty { margin: 0; font-size: 12.5px; color: var(--fg-3); padding: 4px 2px; }
|
|
7
7
|
#panel-tasks .tcard { display: flex; flex-direction: column; gap: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 6px; padding: 10px; text-align: left; width: 100%; cursor: pointer; }
|
|
8
8
|
#panel-tasks .tcard:hover { border-color: var(--line-2); }
|
|
9
9
|
#panel-tasks .tcard.in_progress { border-color: color-mix(in srgb, var(--run) 45%, var(--line)); }
|
|
@@ -51,24 +51,24 @@ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-te
|
|
|
51
51
|
#panel-tasks .tg-head[aria-expanded="true"] .chev { transform: rotate(90deg); }
|
|
52
52
|
#panel-tasks .trow {
|
|
53
53
|
display: grid; grid-template-columns: 76px minmax(0, 1fr) 110px 150px 72px; gap: 12px;
|
|
54
|
-
align-items: center; height: 36px; padding: 0 8px;
|
|
54
|
+
align-items: center; height: 36px; padding: 0 8px;
|
|
55
55
|
font-size: 13px; cursor: pointer;
|
|
56
56
|
}
|
|
57
57
|
#panel-tasks .trow:hover, #panel-tasks .trow:focus-visible { background: var(--surface-2); }
|
|
58
58
|
#panel-tasks .trow:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
|
|
59
|
-
#panel-tasks .trow.thead { cursor: default; color: var(--fg-3); font-size: 12px; background: none; }
|
|
59
|
+
#panel-tasks .trow.thead { border-bottom: 1px solid var(--line); cursor: default; color: var(--fg-3); font-size: 12px; background: none; }
|
|
60
60
|
#panel-tasks .trow .mono { font-variant-numeric: tabular-nums; font-size: 12px; }
|
|
61
61
|
#panel-tasks .c-id { color: var(--fg-3); }
|
|
62
62
|
#panel-tasks .c-title, #panel-tasks .c-agent { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
63
63
|
#panel-tasks .c-agent { color: var(--fg-2); }
|
|
64
64
|
#panel-tasks .c-time { text-align: right; color: var(--fg-2); }
|
|
65
65
|
#panel-tasks .c-status { display: flex; align-items: center; gap: 6px; }
|
|
66
|
-
#panel-tasks .c-status
|
|
67
|
-
#panel-tasks .st-
|
|
68
|
-
#panel-tasks .st-
|
|
69
|
-
#panel-tasks .st-
|
|
70
|
-
#panel-tasks .st-
|
|
71
|
-
#panel-tasks .
|
|
66
|
+
#panel-tasks .c-status { white-space: nowrap; overflow: hidden; }
|
|
67
|
+
#panel-tasks .st-done { color: var(--ok); }
|
|
68
|
+
#panel-tasks .st-running { color: var(--run); }
|
|
69
|
+
#panel-tasks .st-stuck { color: var(--bad); }
|
|
70
|
+
#panel-tasks .st-next { color: var(--fg); }
|
|
71
|
+
#panel-tasks .panel-title .lead { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%; }
|
|
72
72
|
#panel-tasks .tdispatch {
|
|
73
73
|
display: none; margin-left: auto; border: 1px solid var(--line-2); background: var(--surface);
|
|
74
74
|
color: var(--fg); border-radius: 6px; padding: 2px 8px; font-size: 12px; cursor: pointer;
|
|
@@ -13,6 +13,7 @@ export const teamScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
13
13
|
var stamps = tasks.map(function (t) { return t.status === 'in_progress' ? (t.startT || 0) : (t.endT || 0); });
|
|
14
14
|
return {
|
|
15
15
|
agent: key || null,
|
|
16
|
+
tasks: tasks,
|
|
16
17
|
total: tasks.length,
|
|
17
18
|
done: done.length,
|
|
18
19
|
work: timed.length ? timed.reduce(function (n, t) { return n + t.durationMs; }, 0) : null,
|
|
@@ -26,18 +27,38 @@ export const teamScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
26
27
|
function buildRows(model) {
|
|
27
28
|
var groups = {};
|
|
28
29
|
var order = [];
|
|
30
|
+
var ids = {};
|
|
29
31
|
model.tasks.forEach(function (t) {
|
|
30
|
-
var key = t.agent
|
|
31
|
-
if (!groups[key]) { groups[key] = []; order.push(key); }
|
|
32
|
+
var key = t.agent ? Studio.agentInfo(t.agent).label : '';
|
|
33
|
+
if (!groups[key]) { groups[key] = []; ids[key] = []; order.push(key); }
|
|
32
34
|
groups[key].push(t);
|
|
35
|
+
if (ids[key].indexOf(t.agent) < 0) ids[key].push(t.agent);
|
|
33
36
|
});
|
|
34
|
-
return order.map(function (k) {
|
|
37
|
+
return order.map(function (k) {
|
|
38
|
+
var plain = ids[k].filter(function (i) { return !/-agent$/.test(i || ''); });
|
|
39
|
+
var id = plain.length ? plain[0] : String(ids[k][0] || '').replace(/-agent$/, '');
|
|
40
|
+
var r = statsFor(k ? id : '', groups[k]);
|
|
41
|
+
r.label = k || 'Unassigned';
|
|
42
|
+
if (ids[k].length === 1) r.agent = ids[k][0] || null;
|
|
43
|
+
return r;
|
|
44
|
+
})
|
|
35
45
|
.sort(function (a, b) { return b.total - a.total; });
|
|
36
46
|
}
|
|
37
47
|
|
|
38
48
|
function currentTask(r) {
|
|
39
49
|
var t = r.running;
|
|
40
|
-
if (!t) return
|
|
50
|
+
if (!t) return waiting(r);
|
|
51
|
+
return taskButton(t);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function waiting(r) {
|
|
55
|
+
var stuck = r.tasks.filter(function (t) { return Studio.status.bucket(t) === 'stuck'; })[0];
|
|
56
|
+
if (stuck) return '<span class="idle">Stuck</span> ' + taskButton(stuck);
|
|
57
|
+
var next = r.tasks.some(function (t) { return Studio.status.bucket(t) === 'next'; });
|
|
58
|
+
return '<span class="idle">' + (next ? 'Up next' : 'Done') + '</span>';
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function taskButton(t) {
|
|
41
62
|
return '<button type="button" class="cur" data-act="team-open" data-tid="' + esc(t.id) +
|
|
42
63
|
'" title="' + esc(t.title || t.id) + '"><span class="mono">' + esc(t.id) + '</span>' +
|
|
43
64
|
'<span class="ttl">' + esc(t.title || '') + '</span></button>';
|
|
@@ -53,9 +74,7 @@ export const teamScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
53
74
|
}
|
|
54
75
|
|
|
55
76
|
function row(r) {
|
|
56
|
-
|
|
57
|
-
return '<tr data-agent="' + esc(r.agent || 'unassigned') + '"><td class="who">' + dot +
|
|
58
|
-
esc(r.agent || 'unassigned') + '</td>' +
|
|
77
|
+
return '<tr data-agent="' + esc(r.agent || 'unassigned') + '"><td class="who">' + esc(r.label) + '</td>' +
|
|
59
78
|
'<td>' + currentTask(r) + '</td>' +
|
|
60
79
|
'<td class="mono r">' + r.done + '</td>' +
|
|
61
80
|
'<td class="mono r hide-sm">' + (r.work == null ? DASH : esc(Studio.fmt.dur(r.work))) + '</td>' +
|
|
@@ -102,7 +121,7 @@ export const teamScript = String.raw ` var esc = Studio.ui.esc;
|
|
|
102
121
|
|
|
103
122
|
Studio.registerPage('team', {
|
|
104
123
|
deps: ['tasks', 'telemetry', 'runs'],
|
|
105
|
-
actions: { 'team-open': function (el) { Studio.
|
|
124
|
+
actions: { 'team-open': function (el) { Studio.openTask(el.getAttribute('data-tid')); } },
|
|
106
125
|
render: render,
|
|
107
126
|
skeleton: skeleton
|
|
108
127
|
});
|
|
@@ -8,7 +8,6 @@ export const teamCss = String.raw `#panel-team .page-head h1 { font-size: 22px;
|
|
|
8
8
|
#panel-team .r { text-align: right; }
|
|
9
9
|
#panel-team .mono { font-family: var(--mono); font-variant-numeric: tabular-nums; }
|
|
10
10
|
#panel-team .who { font-weight: 500; }
|
|
11
|
-
#panel-team .run-dot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--run); margin-right: 8px; }
|
|
12
11
|
#panel-team .idle { color: var(--fg-3); }
|
|
13
12
|
#panel-team .cur { display: inline-flex; gap: 8px; max-width: 360px; padding: 0; border: 0; background: none; color: var(--fg); font: inherit; cursor: pointer; }
|
|
14
13
|
#panel-team .cur:hover .ttl { text-decoration: underline; }
|
|
@@ -38,6 +38,7 @@ export const consoleScript = String.raw ` // ─── Live runner console draw
|
|
|
38
38
|
|
|
39
39
|
function rcStepper(run) {
|
|
40
40
|
var status = run ? run.status : null;
|
|
41
|
+
if (!run) return '';
|
|
41
42
|
var stage = RC_STAGE[status] != null ? RC_STAGE[status] : rc.stage;
|
|
42
43
|
var settled = !!status && !RUN_ACTIVE[status];
|
|
43
44
|
var failed = status === 'failed' || status === 'aborted';
|
|
@@ -60,15 +61,19 @@ export const consoleScript = String.raw ` // ─── Live runner console draw
|
|
|
60
61
|
$('rc-task').textContent = rc.taskId || '';
|
|
61
62
|
$('rc-title').textContent = task ? task.title : '';
|
|
62
63
|
$('rc-steps').innerHTML = rcStepper(run);
|
|
63
|
-
$('rc-abort').
|
|
64
|
+
$('rc-abort').hidden = !isRunActive(run);
|
|
65
|
+
$('rc-steps').hidden = !run;
|
|
66
|
+
$('rc-foot').hidden = !run;
|
|
64
67
|
$('rc-elapsed').textContent = mmss(elapsedOf(run) || 0);
|
|
65
68
|
$('rc-clock').textContent = $('rc-elapsed').textContent;
|
|
66
69
|
syncInspectorStatus(run);
|
|
67
70
|
$('rc-bytes').textContent = formatBytes((run && run.logBytes) || 0);
|
|
68
|
-
|
|
71
|
+
$('rc-bytes-wrap').hidden = !(run && run.logBytes > 0);
|
|
72
|
+
var gate = '';
|
|
69
73
|
if (run && run.verification) gate = run.verification.success ? 'Passed' : 'Failed';
|
|
70
74
|
else if (run && run.status === 'verifying') gate = 'Running';
|
|
71
75
|
$('rc-gate').textContent = gate;
|
|
76
|
+
$('rc-gate-wrap').hidden = !gate;
|
|
72
77
|
// Native runs report grounded usage; cli runs have none to show.
|
|
73
78
|
var usage = run && run.usage;
|
|
74
79
|
$('rc-spend-wrap').hidden = !usage;
|
|
@@ -81,7 +86,7 @@ export const consoleScript = String.raw ` // ─── Live runner console draw
|
|
|
81
86
|
if (run && !isRunActive(run)) {
|
|
82
87
|
exit = RUN_LABEL[run.status] || run.status;
|
|
83
88
|
if (typeof run.exitCode === 'number') exit += ' (exit ' + run.exitCode + ')';
|
|
84
|
-
if (run.error) exit += '
|
|
89
|
+
if (run.error) exit += ', ' + run.error;
|
|
85
90
|
}
|
|
86
91
|
$('rc-exit').textContent = exit;
|
|
87
92
|
}
|
|
@@ -148,7 +153,7 @@ export const consoleScript = String.raw ` // ─── Live runner console draw
|
|
|
148
153
|
return api('/api/pipeline/worktrees/diff?taskId=' + encodeURIComponent(taskId)).then(function (body) {
|
|
149
154
|
if (seq !== rc.diffSeq || rc.taskId !== taskId) return;
|
|
150
155
|
var files = Array.isArray(body.filesChanged) ? body.filesChanged : [];
|
|
151
|
-
var summary = '<code>' + esc(body.branch || '') + '</code
|
|
156
|
+
var summary = '<code>' + esc(body.branch || '') + '</code>, ' + (body.hasChanges ? files.length + ' uncommitted file' + (files.length === 1 ? '' : 's') : 'working tree clean');
|
|
152
157
|
setDiffState(summary, files, body.diff ? diffToHtml(body.diff) : '<span class="rc-empty">No uncommitted changes in this worktree yet.</span>', files.length);
|
|
153
158
|
}, function (e) {
|
|
154
159
|
if (seq !== rc.diffSeq || rc.taskId !== taskId) return;
|
|
@@ -196,10 +201,11 @@ export const consoleScript = String.raw ` // ─── Live runner console draw
|
|
|
196
201
|
|
|
197
202
|
// Inspector header status: dot color follows the run state only.
|
|
198
203
|
function syncInspectorStatus(run) {
|
|
199
|
-
var
|
|
200
|
-
var
|
|
204
|
+
var task = rc.taskId ? taskById(rc.taskId) : null;
|
|
205
|
+
var bucket = Studio.status.runBucket(run) || (task ? Studio.status.bucket(task) : 'next');
|
|
206
|
+
var tone = { done: 'ok', stuck: 'err', running: 'run' }[bucket] || 'idle';
|
|
201
207
|
$('rc-status').setAttribute('data-tone', tone);
|
|
202
|
-
$('rc-status-text').textContent =
|
|
208
|
+
$('rc-status-text').textContent = Studio.status.word(bucket);
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
// Below 1024px the inspector is a modal sheet over the content.
|
|
@@ -28,8 +28,8 @@ export const detailViewScript = String.raw ` // ─── Task detail panel (pu
|
|
|
28
28
|
}
|
|
29
29
|
if (t.status === 'in_progress') {
|
|
30
30
|
var live = t.run && isRunActive(t.run);
|
|
31
|
-
return dBtn('task-logs', '
|
|
32
|
-
dBtn('task-abort', 'Stop', { danger: true, busy: b
|
|
31
|
+
return (t.run ? dBtn('task-logs', 'Watch live', { busy: b }) : '') +
|
|
32
|
+
(live ? dBtn('task-abort', 'Stop', { danger: true, busy: b }) : '');
|
|
33
33
|
}
|
|
34
34
|
if (t.status === 'pending' && !t.waitingOn.length) {
|
|
35
35
|
return dBtn('task-start', 'Start', { primary: true, busy: b }) + dBtn('task-dispatch', 'Dispatch', { busy: b });
|
|
@@ -73,7 +73,7 @@ export const detailViewScript = String.raw ` // ─── Task detail panel (pu
|
|
|
73
73
|
chipList('Needs first', t.deps.length ? t.deps.map(function (d) { return depChip(model, d); }).join('') : '<span class="faint">Nothing. It can start right away.</span>') +
|
|
74
74
|
chipList('Unblocks', t.children.length ? t.children.map(function (d) { return depChip(model, d); }).join('') : '<span class="faint">No later task depends on this.</span>') +
|
|
75
75
|
chipList('Files it may change', files) +
|
|
76
|
-
'<div><div class="dlabel">How it is checked</div><div class="code">' + esc(t.verify || 'No check command.') + '</div></div>' +
|
|
76
|
+
'<div><div class="dlabel">How it is checked</div><div class="code wrap">' + esc(t.verify || 'No check command.') + '</div></div>' +
|
|
77
77
|
'<div class="chips"><button type="button" class="btn" data-act="show-flow">' + ui.icon('flow') + 'Show in Flow</button></div></div>';
|
|
78
78
|
};
|
|
79
79
|
|
|
@@ -25,9 +25,9 @@ export const dispatchScript = String.raw ` // ─── Autonomous dispatch: PO
|
|
|
25
25
|
pipe.busy[key] = 'abort';
|
|
26
26
|
refreshUi([]);
|
|
27
27
|
postJson('/api/pipeline/tasks/abort', { taskId: taskId, reason: 'Aborted from Nativ Studio' }).then(function () {
|
|
28
|
-
toast('
|
|
28
|
+
toast('Stop signal sent to ' + taskId, 'ok');
|
|
29
29
|
}, function (e) {
|
|
30
|
-
toast('Could not
|
|
30
|
+
toast('Could not stop ' + taskId + ': ' + e.message);
|
|
31
31
|
}).then(function () {
|
|
32
32
|
delete pipe.busy[key];
|
|
33
33
|
return fetchPipeline(['status', 'tasks', 'runs']);
|
|
@@ -47,9 +47,9 @@ export const dispatchScript = String.raw ` // ─── Autonomous dispatch: PO
|
|
|
47
47
|
|
|
48
48
|
function confirmAbort(taskId) {
|
|
49
49
|
confirmDialog({
|
|
50
|
-
title: '
|
|
50
|
+
title: 'Stop agent run',
|
|
51
51
|
body: 'Terminate the Claude process tree for ' + taskId + '? The isolated worktree and its branch are left in place so the partial work can be inspected.',
|
|
52
|
-
confirmLabel: '
|
|
52
|
+
confirmLabel: 'Stop',
|
|
53
53
|
danger: true
|
|
54
54
|
}, function () { runAbort(taskId); });
|
|
55
55
|
}
|
|
@@ -152,14 +152,15 @@ export const dispatchScript = String.raw ` // ─── Autonomous dispatch: PO
|
|
|
152
152
|
function syncDispatchNext() {
|
|
153
153
|
var m = Studio.model;
|
|
154
154
|
if (!m) return;
|
|
155
|
-
var t = m.tasks.filter(function (x) { return
|
|
155
|
+
var t = m.tasks.filter(function (x) { return Studio.status.isReady(x); })[0];
|
|
156
156
|
var key = t ? t.id : '';
|
|
157
157
|
if (key === nextSeen) return;
|
|
158
158
|
nextSeen = key;
|
|
159
159
|
nextId = t ? t.id : null;
|
|
160
|
-
nextBtn.
|
|
161
|
-
|
|
162
|
-
|
|
160
|
+
nextBtn.hidden = !t;
|
|
161
|
+
if (!t) return;
|
|
162
|
+
$('dispatch-next-label').textContent = 'Dispatch ' + t.id;
|
|
163
|
+
nextBtn.title = 'Open the dispatch dialog for ' + t.id;
|
|
163
164
|
}
|
|
164
165
|
nextBtn.addEventListener('click', function () { if (nextId) openDispatchDialog(nextId); });
|
|
165
166
|
Studio.onPaint(syncDispatchNext);
|
|
@@ -8,6 +8,17 @@ export const eventsToolsScript = String.raw ` // ─── Events: live runner
|
|
|
8
8
|
$('rc-clear').addEventListener('click', function () {
|
|
9
9
|
$('rc-body').innerHTML = '<span class="rc-empty">Console cleared. New output will stream in here.</span>';
|
|
10
10
|
});
|
|
11
|
+
function setMoreMenu(open) {
|
|
12
|
+
$('rc-more-menu').hidden = !open;
|
|
13
|
+
$('rc-more').setAttribute('aria-expanded', String(open));
|
|
14
|
+
}
|
|
15
|
+
$('rc-more').addEventListener('click', function () { setMoreMenu($('rc-more-menu').hidden); });
|
|
16
|
+
$('rc-more-menu').addEventListener('click', function (e) {
|
|
17
|
+
if (e.target.closest('#rc-open-diff')) setMoreMenu(false);
|
|
18
|
+
});
|
|
19
|
+
document.addEventListener('click', function (e) {
|
|
20
|
+
if (!$('rc-more-menu').hidden && !e.target.closest('.rc-more')) setMoreMenu(false);
|
|
21
|
+
});
|
|
11
22
|
$('rc-abort').addEventListener('click', function () { if (rc.taskId) confirmAbort(rc.taskId); });
|
|
12
23
|
document.querySelectorAll('.rc-tab').forEach(function (btn) {
|
|
13
24
|
btn.addEventListener('click', function () { setConsoleTab(btn.getAttribute('data-rc-tab')); });
|
|
@@ -24,7 +24,34 @@ export const formatScript = String.raw ` // ─── Studio client core: state
|
|
|
24
24
|
});
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
var
|
|
27
|
+
var BUCKETS = [['next', 'Up next'], ['running', 'Running'], ['done', 'Done'], ['stuck', 'Stuck']];
|
|
28
|
+
var BUCKET_WORD = {};
|
|
29
|
+
BUCKETS.forEach(function (b) { BUCKET_WORD[b[0]] = b[1]; });
|
|
30
|
+
var STATUS_BUCKET = { completed: 'done', in_progress: 'running', pending: 'next', blocked: 'stuck' };
|
|
31
|
+
var STATUS_LABEL = {};
|
|
32
|
+
Object.keys(STATUS_BUCKET).forEach(function (k) { STATUS_LABEL[k] = BUCKET_WORD[STATUS_BUCKET[k]]; });
|
|
33
|
+
var RUN_BUCKET = { spawning_worktree: 'running', running: 'running', verifying: 'running',
|
|
34
|
+
merging: 'running', completed: 'done', failed: 'stuck', aborted: 'next' };
|
|
35
|
+
|
|
36
|
+
var status = {
|
|
37
|
+
buckets: BUCKETS,
|
|
38
|
+
isReady: function (t) {
|
|
39
|
+
return t.status === 'pending' && (!!t.isAvailable || !(t.waitingOn || []).length);
|
|
40
|
+
},
|
|
41
|
+
bucket: function (t) {
|
|
42
|
+
if (t.status === 'completed') return 'done';
|
|
43
|
+
if (t.status === 'in_progress') return t.stale ? 'next' : 'running';
|
|
44
|
+
if (t.status === 'blocked') return 'stuck';
|
|
45
|
+
return 'next';
|
|
46
|
+
},
|
|
47
|
+
runBucket: function (run) { return run ? (RUN_BUCKET[run.status] || null) : null; },
|
|
48
|
+
word: function (key) { return BUCKET_WORD[key] || STATUS_LABEL[key] || 'Unknown'; },
|
|
49
|
+
waitText: function (t) {
|
|
50
|
+
if (t.status !== 'pending' || status.isReady(t)) return '';
|
|
51
|
+
var n = (t.waitingOn || []).length;
|
|
52
|
+
return n === 1 ? 'Waits for 1 task' : 'Waits for ' + n + ' tasks';
|
|
53
|
+
}
|
|
54
|
+
};
|
|
28
55
|
var AGENTS = {
|
|
29
56
|
'backend': ['Backend', '--ag-backend'],
|
|
30
57
|
'frontend': ['Frontend', '--ag-frontend'],
|
|
@@ -118,7 +145,8 @@ export const formatScript = String.raw ` // ─── Studio client core: state
|
|
|
118
145
|
if (!model) return '';
|
|
119
146
|
var current = Studio.state.milestoneId;
|
|
120
147
|
var opts = model.milestones.slice().reverse().map(function (m) {
|
|
121
|
-
var
|
|
148
|
+
var count = m.done + '/' + m.total + (m.id === model.activeMilestoneId ? ', active' : '');
|
|
149
|
+
var text = m.id + ' ' + m.short + ' (' + count + ')';
|
|
122
150
|
return '<option value="' + esc(m.id) + '"' + (m.id === current ? ' selected' : '') + '>' + esc(text) + '</option>';
|
|
123
151
|
}).join('');
|
|
124
152
|
return '<select class="sel" data-ms-select aria-label="Milestone">' + opts + '</select>';
|
|
@@ -136,6 +164,7 @@ export const formatScript = String.raw ` // ─── Studio client core: state
|
|
|
136
164
|
|
|
137
165
|
Studio.fmt = fmt;
|
|
138
166
|
Studio.ui = ui;
|
|
167
|
+
Studio.status = status;
|
|
139
168
|
Studio.agentInfo = agentInfo;
|
|
140
169
|
Studio.statusLabel = function (status) { return STATUS_LABEL[status] || 'Unknown'; };
|
|
141
170
|
|
|
@@ -25,7 +25,7 @@ export function markupHead(version, projectName, projectHidden) {
|
|
|
25
25
|
<div class="top-right">
|
|
26
26
|
<button class="btn" id="btn-find" type="button" aria-keyshortcuts="Control+K Meta+K">${svg('search')}<span>Find task</span><span class="kbd">Ctrl+K</span></button>
|
|
27
27
|
<button class="btn ghost" id="btn-theme" type="button" title="Switch light or dark" aria-label="Switch light or dark"><span class="ic-sun">${svg('sun')}</span><span class="ic-moon">${svg('moon')}</span></button>
|
|
28
|
-
<button class="btn primary" id="btn-dispatch-next" type="button"
|
|
28
|
+
<button class="btn primary" id="btn-dispatch-next" type="button" hidden>${svg('play')}<span id="dispatch-next-label">Dispatch</span></button>
|
|
29
29
|
<div class="t1" id="t1">
|
|
30
30
|
<button class="t1-chip" id="t1-chip" type="button" aria-haspopup="dialog" aria-expanded="false" aria-controls="t1-menu" data-state="idle" title="Tier 1 AI Strategist"><svg class="icon t1-spark" width="14" height="14" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M197.58,129.06,146,110l-19-51.62a15.92,15.92,0,0,0-29.88,0L78,110l-51.62,19a15.92,15.92,0,0,0,0,29.88L78,178l19,51.62a15.92,15.92,0,0,0,29.88,0L146,178l51.62-19a15.92,15.92,0,0,0,0-29.88ZM137,164.22a8,8,0,0,0-4.74,4.74L112,223.85,91.78,169A8,8,0,0,0,87,164.22L32.15,144,87,123.78A8,8,0,0,0,91.78,119L112,64.15,132.22,119a8,8,0,0,0,4.74,4.74L191.85,144ZM144,40a8,8,0,0,1,8-8h16V16a8,8,0,0,1,16,0V32h16a8,8,0,0,1,0,16H184V64a8,8,0,0,1-16,0V48H152A8,8,0,0,1,144,40ZM248,88a8,8,0,0,1-8,8h-8v8a8,8,0,0,1-16,0V96h-8a8,8,0,0,1,0-16h8V72a8,8,0,0,1,16,0v8h8A8,8,0,0,1,248,88Z"/></svg><span class="t1-chip-label">Tier 1 AI:</span><span class="t1-chip-model" id="t1-chip-model">Gemini 3.8 Flash</span><svg class="icon t1-caret" width="12" height="12" viewBox="0 0 256 256" fill="currentColor" aria-hidden="true"><path d="M213.66,101.66l-80,80a8,8,0,0,1-11.32,0l-80-80A8,8,0,0,1,53.66,90.34L128,164.69l74.34-74.35a8,8,0,0,1,11.32,11.32Z"/></svg></button>
|
|
31
31
|
<div class="t1-menu" id="t1-menu" role="dialog" aria-label="Tier 1 AI Strategist" tabindex="-1" hidden>
|
|
@@ -14,16 +14,21 @@ export const markupTools = String.raw `</main>
|
|
|
14
14
|
<header class="rc-head">
|
|
15
15
|
<div class="rc-id-row">
|
|
16
16
|
<code class="rc-task" id="rc-task"></code>
|
|
17
|
-
<span class="rc-status" id="rc-status"><span class="rc-dot" aria-hidden="true"></span><span id="rc-status-text">
|
|
17
|
+
<span class="rc-status" id="rc-status"><span class="rc-dot" aria-hidden="true"></span><span id="rc-status-text">Up next</span></span>
|
|
18
18
|
<code class="rc-clock" id="rc-clock">00:00</code>
|
|
19
19
|
</div>
|
|
20
20
|
<span class="rc-title" id="rc-title"></span>
|
|
21
21
|
<ol class="rc-steps" id="rc-steps" aria-label="Runner lifecycle"></ol>
|
|
22
22
|
<div class="rc-controls">
|
|
23
|
-
<button class="rc-btn danger" type="button" id="rc-abort" aria-label="
|
|
24
|
-
<
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
<button class="rc-btn danger" type="button" id="rc-abort" aria-label="Stop the running agent" hidden>Stop</button>
|
|
24
|
+
<span class="rc-more">
|
|
25
|
+
<button class="rc-btn" type="button" id="rc-more" aria-haspopup="true" aria-expanded="false" aria-controls="rc-more-menu">More</button>
|
|
26
|
+
<div class="rc-menu" id="rc-more-menu" hidden>
|
|
27
|
+
<button class="rc-btn" type="button" id="rc-open-diff" aria-label="Open the worktree diff">Open diff</button>
|
|
28
|
+
<button class="rc-btn" type="button" id="rc-clear" aria-label="Clear the console buffer">Clear</button>
|
|
29
|
+
<button class="rc-btn" type="button" id="rc-autoscroll" aria-pressed="true" aria-label="Toggle console auto-scroll">Auto-scroll</button>
|
|
30
|
+
</div>
|
|
31
|
+
</span>
|
|
27
32
|
<button class="rc-btn" type="button" id="rc-minimize" aria-expanded="true" aria-controls="rc-body" aria-label="Collapse the inspector">Collapse</button>
|
|
28
33
|
<button class="rc-btn" type="button" id="rc-close" aria-label="Close the runner console">Close</button>
|
|
29
34
|
</div>
|
|
@@ -43,12 +48,12 @@ export const markupTools = String.raw `</main>
|
|
|
43
48
|
<pre class="rc-diff-body" id="rc-diff-body" aria-label="Unified diff" tabindex="0"></pre>
|
|
44
49
|
</div>
|
|
45
50
|
<div class="rc-heal" id="rc-heal" role="tabpanel" aria-labelledby="rc-tab-heal" tabindex="0" hidden></div>
|
|
46
|
-
<footer class="rc-foot">
|
|
51
|
+
<footer class="rc-foot" id="rc-foot" hidden>
|
|
47
52
|
<span>Elapsed <strong id="rc-elapsed">00:00</strong></span>
|
|
48
|
-
<span>Output <strong id="rc-bytes">0 B</strong></span>
|
|
53
|
+
<span id="rc-bytes-wrap" hidden>Output <strong id="rc-bytes">0 B</strong></span>
|
|
49
54
|
<span id="rc-spend-wrap" hidden>Spend <strong id="rc-spend">$0.0000</strong></span>
|
|
50
55
|
<span id="rc-cache-wrap" hidden>Cache hit <strong id="rc-cache">0%</strong></span>
|
|
51
|
-
<span>Gatekeeper <strong id="rc-gate"
|
|
56
|
+
<span id="rc-gate-wrap" hidden>Gatekeeper <strong id="rc-gate"></strong></span>
|
|
52
57
|
<span id="rc-exit"></span>
|
|
53
58
|
</footer>
|
|
54
59
|
</section>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
export const modelScript = String.raw ` // ─── Studio client model: buildModel(api) is pure ──────────────────────────
|
|
3
3
|
var RUN_ACTIVE = { spawning_worktree: 1, running: 1, verifying: 1, merging: 1 };
|
|
4
4
|
function isRunActive(run) { return !!run && !!RUN_ACTIVE[run.status]; }
|
|
5
|
+
var STALE_MS = 4 * 3600 * 1000;
|
|
5
6
|
|
|
6
7
|
/** Text before the first colon, at most 46 characters. */
|
|
7
8
|
function shortName(name) {
|
|
@@ -46,11 +47,14 @@ export const modelScript = String.raw ` // ─── Studio client model: build
|
|
|
46
47
|
if (startT == null && run && isRunActive(run)) startT = toTime(run.startedAt);
|
|
47
48
|
var cost = b && b.actual ? positive(b.actual.costUsd) : null;
|
|
48
49
|
if (cost == null && b && b.estimated) cost = positive(b.estimated.costUsd);
|
|
50
|
+
var claimT = toTime(t.claimedAt);
|
|
51
|
+
var stale = running && claimT != null && Studio.now() - claimT > STALE_MS && !isRunActive(run);
|
|
49
52
|
return {
|
|
50
53
|
id: t.id,
|
|
51
54
|
title: t.title || t.id,
|
|
52
55
|
description: t.description || '',
|
|
53
|
-
status: t.status,
|
|
56
|
+
status: stale ? 'pending' : t.status,
|
|
57
|
+
claimedStatus: t.status,
|
|
54
58
|
agent: t.assignedSubagent || null,
|
|
55
59
|
deps: t.dependencies || [],
|
|
56
60
|
files: t.targetFiles || [],
|
|
@@ -62,6 +66,8 @@ export const modelScript = String.raw ` // ─── Studio client model: build
|
|
|
62
66
|
reason: t.status === 'blocked' && t.notes ? String(t.notes).replace(/^\s+|\s+$/g, '') || null : null,
|
|
63
67
|
attempt: attemptsOf(t, running),
|
|
64
68
|
maxAttempts: (t.circuitBreaker && t.circuitBreaker.maxThreshold) || 3,
|
|
69
|
+
claimT: claimT,
|
|
70
|
+
stale: stale,
|
|
65
71
|
startT: startT,
|
|
66
72
|
endT: t.status === 'completed' && b ? toTime(b.completedAt) : null,
|
|
67
73
|
durationMs: b ? positive(b.durationMs) : null,
|
|
@@ -175,7 +181,7 @@ export const modelScript = String.raw ` // ─── Studio client model: build
|
|
|
175
181
|
tasks: tasks,
|
|
176
182
|
byId: byId,
|
|
177
183
|
activeMilestoneId: active,
|
|
178
|
-
running: tasks.filter(function (t) { return t.status === 'in_progress'; }),
|
|
184
|
+
running: tasks.filter(function (t) { return t.status === 'in_progress' && !t.stale; }),
|
|
179
185
|
stuck: tasks.filter(function (t) { return t.status === 'blocked'; }),
|
|
180
186
|
needsYou: needsYouItems(tasks, byId, api.escalations),
|
|
181
187
|
summary: summaryOf(api, milestones, tasks)
|
|
@@ -67,11 +67,9 @@ export const paintScript = String.raw ` // ─── Painting ─────
|
|
|
67
67
|
var side = $('side-nav');
|
|
68
68
|
var main = mainWorktree();
|
|
69
69
|
var agents = pipe.worktrees.filter(function (w) { return w.isAgentWorktree; }).length;
|
|
70
|
-
var s = pipe.report && pipe.report.summary;
|
|
71
70
|
var navHtml = Studio.sidebarHtml({
|
|
72
71
|
model: Studio.model, view: Studio.state.view,
|
|
73
|
-
worktreeCount: pipe.loaded.worktrees && !pipe.errors.worktrees ? agents : null
|
|
74
|
-
benchmarkText: s ? compactNum(Number(s.averageThroughputOpsPerSec) || 0) : ''
|
|
72
|
+
worktreeCount: pipe.loaded.worktrees && !pipe.errors.worktrees ? agents : null
|
|
75
73
|
});
|
|
76
74
|
if (side.__html !== navHtml) { side.innerHTML = navHtml; side.__html = navHtml; }
|
|
77
75
|
var foot = $('side-foot');
|
|
@@ -117,18 +117,9 @@ export const pipelineStateScript = String.raw ` // ─── Pipeline state ─
|
|
|
117
117
|
return main;
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
/** 1234 becomes "1.2k" so the sidebar badge stays small. */
|
|
121
|
-
function compactNum(n) {
|
|
122
|
-
if (!isFinite(n)) return '-';
|
|
123
|
-
if (n >= 1e6) return (n / 1e6).toFixed(1).replace(/\.0$/, '') + 'M';
|
|
124
|
-
if (n >= 1e3) return (n / 1e3).toFixed(1).replace(/\.0$/, '') + 'k';
|
|
125
|
-
return String(Math.round(n));
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
/** Newest Just-finished style helpers live in pages; here only what the runner tools need. */
|
|
129
120
|
var RUN_LABEL = {
|
|
130
|
-
spawning_worktree: '
|
|
131
|
-
completed: '
|
|
121
|
+
spawning_worktree: 'Running', running: 'Running', verifying: 'Running', merging: 'Running',
|
|
122
|
+
completed: 'Done', failed: 'Stuck', aborted: 'Up next'
|
|
132
123
|
};
|
|
133
124
|
function runFor(taskId) {
|
|
134
125
|
var found = null;
|
|
@@ -6,10 +6,11 @@ export const pulseScript = String.raw ` // ─── Pulse strip ────
|
|
|
6
6
|
if (!s.loaded) return { kind: 'loading', text: '-- -- -- --' };
|
|
7
7
|
var counts = [
|
|
8
8
|
{ key: 'running', label: 'running', n: s.running, tone: 'run' },
|
|
9
|
-
{ key: '
|
|
10
|
-
{ key: 'decisions', label: 'decisions', n: s.decisions, tone: 'warn' }
|
|
9
|
+
{ key: 'stuck', label: 'stuck', n: s.blocked, tone: 'bad' },
|
|
10
|
+
{ key: 'decisions', label: s.decisions === 1 ? 'decision' : 'decisions', n: s.decisions, tone: 'warn' }
|
|
11
11
|
];
|
|
12
|
-
counts.
|
|
12
|
+
counts = counts.filter(function (c) { return c.n > 0; });
|
|
13
|
+
counts.forEach(function (c) { c.zero = false; });
|
|
13
14
|
return { kind: 'ready', ms: s.ms, counts: counts };
|
|
14
15
|
}
|
|
15
16
|
|
|
@@ -17,11 +18,10 @@ export const pulseScript = String.raw ` // ─── Pulse strip ────
|
|
|
17
18
|
if (p.kind !== 'ready') {
|
|
18
19
|
return '<span class="pulse-note ' + p.kind + '">' + esc(p.text) + '</span>';
|
|
19
20
|
}
|
|
21
|
+
if (!p.counts.length) return '';
|
|
20
22
|
var out = '<span class="pulse-ms num">' + esc(p.ms) + '</span>';
|
|
21
23
|
p.counts.forEach(function (c) {
|
|
22
|
-
|
|
23
|
-
out += '<button type="button" class="pulse-item ' + tone + '" data-pulse="' + c.key + '">' +
|
|
24
|
-
(c.zero ? '' : '<span class="pdot"></span>') +
|
|
24
|
+
out += '<button type="button" class="pulse-item ' + c.tone + '" data-pulse="' + c.key + '">' +
|
|
25
25
|
'<span class="num">' + c.n + '</span> ' + c.label + '</button>';
|
|
26
26
|
});
|
|
27
27
|
return out;
|
|
@@ -49,10 +49,11 @@ export const pulseScript = String.raw ` // ─── Pulse strip ────
|
|
|
49
49
|
var html = pulseHtml(pulseParts(pulseInput()));
|
|
50
50
|
if (pulseEl.__html === html) return;
|
|
51
51
|
pulseEl.innerHTML = html;
|
|
52
|
+
pulseEl.hidden = !html;
|
|
52
53
|
pulseEl.__html = html;
|
|
53
54
|
}
|
|
54
55
|
|
|
55
|
-
// Studio.taskFilter ('running' or '
|
|
56
|
+
// Studio.taskFilter ('running' or 'stuck') is read by the Tasks page when it opens.
|
|
56
57
|
if (pulseEl) pulseEl.addEventListener('click', function (e) {
|
|
57
58
|
var b = e.target.closest ? e.target.closest('[data-pulse]') : null;
|
|
58
59
|
if (!b) return;
|
|
@@ -5,15 +5,14 @@ export const sidebarViewScript = String.raw ` // ─── Sidebar markup (pure
|
|
|
5
5
|
function navBadges(env) {
|
|
6
6
|
var badges = {}, model = env.model;
|
|
7
7
|
if (model && model.needsYou.length) badges.home = ['warn', model.needsYou.length];
|
|
8
|
-
var ready = model ? model.tasks.filter(
|
|
8
|
+
var ready = model ? model.tasks.filter(Studio.status.isReady).length : 0;
|
|
9
9
|
var active = model ? model.running.length + ready : 0;
|
|
10
10
|
if (active) badges.tasks = [model.running.length ? 'run' : '', active];
|
|
11
11
|
if (env.worktreeCount != null) badges.worktrees = ['', env.worktreeCount];
|
|
12
|
-
if (env.benchmarkText) badges.benchmarks = ['', env.benchmarkText];
|
|
13
12
|
return badges;
|
|
14
13
|
}
|
|
15
14
|
|
|
16
|
-
/** env = { model, view, worktreeCount
|
|
15
|
+
/** env = { model, view, worktreeCount }. */
|
|
17
16
|
Studio.sidebarHtml = function (env) {
|
|
18
17
|
var badges = navBadges(env);
|
|
19
18
|
var item = function (p) {
|
|
@@ -35,7 +35,7 @@ export const triageScript = String.raw ` // ─── Tier 1 AI Strategist (/ap
|
|
|
35
35
|
$('t1-chip-model').textContent = modelName(model);
|
|
36
36
|
$('t1-model').textContent = model;
|
|
37
37
|
var last = lastTriage();
|
|
38
|
-
$('t1-latency').textContent = last ? fmtMs(last.latencyMs) + '
|
|
38
|
+
$('t1-latency').textContent = last ? fmtMs(last.latencyMs) + ', ' + modelName(last.model || model) : 'No evaluations yet';
|
|
39
39
|
$('t1-key').textContent = pipe.errors.triage && !t ? 'Status unavailable' : !t ? 'Checking' : t.hasApiKey ? 'Configured' : 'Not set (offline rules)';
|
|
40
40
|
var sw = $('t1-auto');
|
|
41
41
|
var on = !!(t && t.autoTriageEnabled);
|
|
@@ -32,6 +32,9 @@ body.has-console-min main { margin-right: 56px; }
|
|
|
32
32
|
.rc-step.is-active { color: var(--term-text); background: color-mix(in srgb, var(--primary) 32%, transparent); border-color: var(--primary); }
|
|
33
33
|
.rc-step.is-failed { color: var(--term-text); background: color-mix(in srgb, var(--danger) 32%, transparent); border-color: var(--danger); }
|
|
34
34
|
.rc-controls { display: flex; gap: 6px; flex-wrap: wrap; }
|
|
35
|
+
.rc-more { position: relative; }
|
|
36
|
+
.rc-menu { position: absolute; top: calc(100% + 4px); left: 0; z-index: 2; display: flex; flex-direction: column; gap: 4px; padding: 6px; background: var(--term-bg); border: 1px solid var(--term-line); border-radius: var(--r-control); }
|
|
37
|
+
.rc-menu[hidden], .rc-btn[hidden], .rc-steps[hidden], .rc-foot[hidden] { display: none; }
|
|
35
38
|
.rc-btn { padding: 5px 9px; font: 500 11px/1 var(--font); color: var(--term-text); background: transparent; border: 1px solid var(--term-line); border-radius: var(--r-control); cursor: pointer; }
|
|
36
39
|
.rc-btn:hover { background: color-mix(in srgb, var(--term-muted) 22%, transparent); }
|
|
37
40
|
.rc-btn:disabled { opacity: .45; cursor: not-allowed; }
|
|
@@ -19,7 +19,7 @@ export const shellCss = String.raw `/* Layout */
|
|
|
19
19
|
.nav-badge.run { background: var(--run-soft); color: var(--run); }
|
|
20
20
|
.side-foot { margin-top: auto; font-size: 12px; color: var(--fg-3); padding: 0 8px; display: flex; flex-direction: column; gap: 4px; }
|
|
21
21
|
.side-foot > div { display: flex; flex-direction: column; gap: 4px; }
|
|
22
|
-
.side-foot .mono { color: var(--fg-2); font-size: 11px;
|
|
22
|
+
.side-foot .mono { color: var(--fg-2); font-size: 11px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
|
|
23
23
|
.foot-project { color: var(--fg); font-weight: 500; }
|
|
24
24
|
.main { min-width: 0; display: flex; flex-direction: column; }
|
|
25
25
|
.top { position: sticky; top: env(safe-area-inset-top, 0px); z-index: 20; background: color-mix(in srgb, var(--bg) 88%, transparent); backdrop-filter: blur(8px); -webkit-backdrop-filter: blur(8px); border-bottom: 1px solid var(--line); display: flex; align-items: center; gap: 10px; flex-wrap: wrap; padding: 10px 24px; }
|
|
@@ -28,21 +28,19 @@ export const shellCss = String.raw `/* Layout */
|
|
|
28
28
|
.top-right { margin-left: auto; display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
|
|
29
29
|
.btn.ghost { background: none; border-color: transparent; color: var(--fg-2); box-shadow: none; }
|
|
30
30
|
.btn.ghost:hover { background: var(--surface-2); }
|
|
31
|
-
#btn-dispatch-next[disabled] { opacity: .55; cursor: not-allowed; }
|
|
32
31
|
.spacer { flex: 1; }
|
|
33
32
|
.ic-sun, .ic-moon { display: inline-flex; }
|
|
34
33
|
.ic-moon { display: none; }
|
|
35
34
|
:root[data-theme="light"] .ic-sun { display: none; }
|
|
36
35
|
:root[data-theme="light"] .ic-moon { display: inline-flex; }
|
|
37
36
|
@media (prefers-color-scheme: light) { :root:not([data-theme="dark"]) .ic-sun { display: none; } :root:not([data-theme="dark"]) .ic-moon { display: inline-flex; } }
|
|
37
|
+
#pulse[hidden] { display: none; }
|
|
38
38
|
.pulse-strip { min-height: 32px; height: auto; width: auto; border-radius: 0; background: none; box-shadow: none; animation: none; display: flex; flex-wrap: wrap; align-items: center; gap: 0 20px; padding: 0 24px; border-bottom: 1px solid var(--line); font: 12.5px var(--mono); color: var(--fg-2); }
|
|
39
39
|
#pulse .num { font-variant-numeric: tabular-nums; }
|
|
40
40
|
#pulse .pulse-ms { color: var(--fg); padding-right: 20px; border-right: 1px solid var(--line); }
|
|
41
41
|
#pulse .pulse-item { display: inline-flex; align-items: center; gap: 6px; min-height: 32px; padding: 0; border: 0; background: none; font: inherit; cursor: pointer; }
|
|
42
|
-
#pulse .pulse-
|
|
42
|
+
#pulse .pulse-note.loading { color: var(--fg-3); }
|
|
43
43
|
#pulse .pulse-item.run { color: var(--run); } #pulse .pulse-item.bad { color: var(--bad); } #pulse .pulse-item.warn, #pulse .pulse-note.offline { color: var(--warn); }
|
|
44
|
-
#pulse .pdot { width: 7px; height: 7px; border-radius: 50%; background: currentColor; }
|
|
45
|
-
#pulse .pulse-item.run .pdot { animation: blink 1.6s infinite; }
|
|
46
44
|
@media (max-width: 1023px) { .pulse-strip { padding-top: 4px; padding-bottom: 4px; gap: 0 16px; } #pulse .pulse-ms { flex-basis: 100%; border-right: 0; padding-right: 0; } }
|
|
47
45
|
.wordmark.is-rail { display: none; }
|
|
48
46
|
@media (max-width: 1023px) { .wordmark { display: none; } .wordmark.is-rail { display: block; } }
|
|
@@ -132,5 +130,5 @@ export const shellCss = String.raw `/* Layout */
|
|
|
132
130
|
.drawer { width: 100%; }
|
|
133
131
|
#runner-console-drawer { inset: auto 0 0 0; }
|
|
134
132
|
}
|
|
135
|
-
@media (prefers-reduced-motion: reduce) { .pulse, .live-dot.flash
|
|
133
|
+
@media (prefers-reduced-motion: reduce) { .pulse, .live-dot.flash { animation: none !important; } * { transition: none !important; } }
|
|
136
134
|
`;
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nativ",
|
|
3
3
|
"displayName": "nativ",
|
|
4
|
-
"version": "2.
|
|
4
|
+
"version": "2.8.0",
|
|
5
5
|
"description": "Role-based multi-agent workflow for Claude Code: architect, worker, verifier and explorer agents, task-scope enforcement, and the nativ MCP server.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "JB Natividad"
|