@njeybe/nativ 2.2.0 → 2.3.1

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 (141) hide show
  1. package/CHANGELOG.md +24 -1
  2. package/README.md +15 -0
  3. package/dist/runner/agent-supervisor.d.ts +5 -3
  4. package/dist/runner/agent-supervisor.js +17 -12
  5. package/dist/server/studio-server.js +1 -130
  6. package/dist/server/studio-ui/document.d.ts +3 -0
  7. package/dist/server/studio-ui/document.js +22 -0
  8. package/dist/server/studio-ui/pages/benchmarks/index.d.ts +2 -0
  9. package/dist/server/studio-ui/pages/benchmarks/index.js +11 -0
  10. package/dist/server/studio-ui/pages/benchmarks/script.d.ts +1 -0
  11. package/dist/server/studio-ui/pages/benchmarks/script.js +104 -0
  12. package/dist/server/studio-ui/pages/benchmarks/styles.d.ts +1 -0
  13. package/dist/server/studio-ui/pages/benchmarks/styles.js +30 -0
  14. package/dist/server/studio-ui/pages/database/browser.d.ts +1 -0
  15. package/dist/server/studio-ui/pages/database/browser.js +326 -0
  16. package/dist/server/studio-ui/pages/database/connection.d.ts +1 -0
  17. package/dist/server/studio-ui/pages/database/connection.js +158 -0
  18. package/dist/server/studio-ui/pages/database/data-shell.d.ts +1 -0
  19. package/dist/server/studio-ui/pages/database/data-shell.js +165 -0
  20. package/dist/server/studio-ui/pages/database/drift.d.ts +1 -0
  21. package/dist/server/studio-ui/pages/database/drift.js +301 -0
  22. package/dist/server/studio-ui/pages/database/events.d.ts +1 -0
  23. package/dist/server/studio-ui/pages/database/events.js +231 -0
  24. package/dist/server/studio-ui/pages/database/explorer.d.ts +1 -0
  25. package/dist/server/studio-ui/pages/database/explorer.js +183 -0
  26. package/dist/server/studio-ui/pages/database/index.d.ts +2 -0
  27. package/dist/server/studio-ui/pages/database/index.js +28 -0
  28. package/dist/server/studio-ui/pages/database/markup.d.ts +1 -0
  29. package/dist/server/studio-ui/pages/database/markup.js +32 -0
  30. package/dist/server/studio-ui/pages/database/state.d.ts +2 -0
  31. package/dist/server/studio-ui/pages/database/state.js +57 -0
  32. package/dist/server/studio-ui/pages/database/styles.d.ts +1 -0
  33. package/dist/server/studio-ui/pages/database/styles.js +118 -0
  34. package/dist/server/studio-ui/pages/flow/index.d.ts +2 -0
  35. package/dist/server/studio-ui/pages/flow/index.js +11 -0
  36. package/dist/server/studio-ui/pages/flow/layout.d.ts +1 -0
  37. package/dist/server/studio-ui/pages/flow/layout.js +105 -0
  38. package/dist/server/studio-ui/pages/flow/plan-view.d.ts +1 -0
  39. package/dist/server/studio-ui/pages/flow/plan-view.js +67 -0
  40. package/dist/server/studio-ui/pages/flow/script.d.ts +1 -0
  41. package/dist/server/studio-ui/pages/flow/script.js +61 -0
  42. package/dist/server/studio-ui/pages/flow/styles.d.ts +1 -0
  43. package/dist/server/studio-ui/pages/flow/styles.js +59 -0
  44. package/dist/server/studio-ui/pages/flow/timeline-model.d.ts +1 -0
  45. package/dist/server/studio-ui/pages/flow/timeline-model.js +95 -0
  46. package/dist/server/studio-ui/pages/flow/timeline-view.d.ts +1 -0
  47. package/dist/server/studio-ui/pages/flow/timeline-view.js +92 -0
  48. package/dist/server/studio-ui/pages/home/index.d.ts +2 -0
  49. package/dist/server/studio-ui/pages/home/index.js +11 -0
  50. package/dist/server/studio-ui/pages/home/script.d.ts +1 -0
  51. package/dist/server/studio-ui/pages/home/script.js +160 -0
  52. package/dist/server/studio-ui/pages/home/styles.d.ts +1 -0
  53. package/dist/server/studio-ui/pages/home/styles.js +48 -0
  54. package/dist/server/studio-ui/pages/index.d.ts +2 -0
  55. package/dist/server/studio-ui/pages/index.js +17 -0
  56. package/dist/server/studio-ui/pages/tasks/index.d.ts +2 -0
  57. package/dist/server/studio-ui/pages/tasks/index.js +11 -0
  58. package/dist/server/studio-ui/pages/tasks/script.d.ts +1 -0
  59. package/dist/server/studio-ui/pages/tasks/script.js +96 -0
  60. package/dist/server/studio-ui/pages/tasks/styles.d.ts +1 -0
  61. package/dist/server/studio-ui/pages/tasks/styles.js +37 -0
  62. package/dist/server/studio-ui/pages/team/index.d.ts +2 -0
  63. package/dist/server/studio-ui/pages/team/index.js +11 -0
  64. package/dist/server/studio-ui/pages/team/script.d.ts +1 -0
  65. package/dist/server/studio-ui/pages/team/script.js +112 -0
  66. package/dist/server/studio-ui/pages/team/styles.d.ts +1 -0
  67. package/dist/server/studio-ui/pages/team/styles.js +22 -0
  68. package/dist/server/studio-ui/pages/types.d.ts +13 -0
  69. package/dist/server/studio-ui/pages/types.js +1 -0
  70. package/dist/server/studio-ui/pages/worktrees/index.d.ts +2 -0
  71. package/dist/server/studio-ui/pages/worktrees/index.js +11 -0
  72. package/dist/server/studio-ui/pages/worktrees/script.d.ts +1 -0
  73. package/dist/server/studio-ui/pages/worktrees/script.js +104 -0
  74. package/dist/server/studio-ui/pages/worktrees/styles.d.ts +1 -0
  75. package/dist/server/studio-ui/pages/worktrees/styles.js +15 -0
  76. package/dist/server/studio-ui/shell/actions.d.ts +1 -0
  77. package/dist/server/studio-ui/shell/actions.js +49 -0
  78. package/dist/server/studio-ui/shell/boot.d.ts +2 -0
  79. package/dist/server/studio-ui/shell/boot.js +13 -0
  80. package/dist/server/studio-ui/shell/console.d.ts +1 -0
  81. package/dist/server/studio-ui/shell/console.js +194 -0
  82. package/dist/server/studio-ui/shell/core.d.ts +1 -0
  83. package/dist/server/studio-ui/shell/core.js +50 -0
  84. package/dist/server/studio-ui/shell/detail-view.d.ts +1 -0
  85. package/dist/server/studio-ui/shell/detail-view.js +80 -0
  86. package/dist/server/studio-ui/shell/detail.d.ts +1 -0
  87. package/dist/server/studio-ui/shell/detail.js +83 -0
  88. package/dist/server/studio-ui/shell/dispatch.d.ts +1 -0
  89. package/dist/server/studio-ui/shell/dispatch.js +150 -0
  90. package/dist/server/studio-ui/shell/events-tools.d.ts +1 -0
  91. package/dist/server/studio-ui/shell/events-tools.js +94 -0
  92. package/dist/server/studio-ui/shell/format.d.ts +1 -0
  93. package/dist/server/studio-ui/shell/format.js +151 -0
  94. package/dist/server/studio-ui/shell/icons.d.ts +2 -0
  95. package/dist/server/studio-ui/shell/icons.js +20 -0
  96. package/dist/server/studio-ui/shell/markup-head.d.ts +1 -0
  97. package/dist/server/studio-ui/shell/markup-head.js +43 -0
  98. package/dist/server/studio-ui/shell/markup-panels.d.ts +2 -0
  99. package/dist/server/studio-ui/shell/markup-panels.js +7 -0
  100. package/dist/server/studio-ui/shell/markup-tools.d.ts +1 -0
  101. package/dist/server/studio-ui/shell/markup-tools.js +222 -0
  102. package/dist/server/studio-ui/shell/model.d.ts +1 -0
  103. package/dist/server/studio-ui/shell/model.js +192 -0
  104. package/dist/server/studio-ui/shell/paint.d.ts +1 -0
  105. package/dist/server/studio-ui/shell/paint.js +154 -0
  106. package/dist/server/studio-ui/shell/palette-view.d.ts +1 -0
  107. package/dist/server/studio-ui/shell/palette-view.js +60 -0
  108. package/dist/server/studio-ui/shell/palette.d.ts +1 -0
  109. package/dist/server/studio-ui/shell/palette.js +80 -0
  110. package/dist/server/studio-ui/shell/pipeline-state.d.ts +1 -0
  111. package/dist/server/studio-ui/shell/pipeline-state.js +152 -0
  112. package/dist/server/studio-ui/shell/proposals.d.ts +1 -0
  113. package/dist/server/studio-ui/shell/proposals.js +197 -0
  114. package/dist/server/studio-ui/shell/pure.d.ts +3 -0
  115. package/dist/server/studio-ui/shell/pure.js +9 -0
  116. package/dist/server/studio-ui/shell/router.d.ts +1 -0
  117. package/dist/server/studio-ui/shell/router.js +95 -0
  118. package/dist/server/studio-ui/shell/sidebar-view.d.ts +1 -0
  119. package/dist/server/studio-ui/shell/sidebar-view.js +38 -0
  120. package/dist/server/studio-ui/shell/sync.d.ts +1 -0
  121. package/dist/server/studio-ui/shell/sync.js +120 -0
  122. package/dist/server/studio-ui/shell/toasts-format.d.ts +1 -0
  123. package/dist/server/studio-ui/shell/toasts-format.js +89 -0
  124. package/dist/server/studio-ui/shell/triage.d.ts +1 -0
  125. package/dist/server/studio-ui/shell/triage.js +210 -0
  126. package/dist/server/studio-ui/styles/console.d.ts +1 -0
  127. package/dist/server/studio-ui/styles/console.js +83 -0
  128. package/dist/server/studio-ui/styles/dialogs.d.ts +1 -0
  129. package/dist/server/studio-ui/styles/dialogs.js +118 -0
  130. package/dist/server/studio-ui/styles/responsive.d.ts +1 -0
  131. package/dist/server/studio-ui/styles/responsive.js +28 -0
  132. package/dist/server/studio-ui/styles/shell.d.ts +1 -0
  133. package/dist/server/studio-ui/styles/shell.js +117 -0
  134. package/dist/server/studio-ui/styles/strategist.d.ts +1 -0
  135. package/dist/server/studio-ui/styles/strategist.js +77 -0
  136. package/dist/server/studio-ui/styles/tokens-base.d.ts +1 -0
  137. package/dist/server/studio-ui/styles/tokens-base.js +131 -0
  138. package/dist/server/studio-ui.d.ts +1 -10
  139. package/dist/server/studio-ui.js +63 -5314
  140. package/package.json +2 -2
  141. package/plugin/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,96 @@
1
+ // Tasks page: one milestone as four columns, then the other milestones as a history list.
2
+ export const tasksScript = String.raw ` var ui = Studio.ui;
3
+ var CAP = 5;
4
+ var view = { lastMs: null, showAll: false, open: {} };
5
+
6
+ function head(m) {
7
+ var lead = m ? ui.esc(m.name) : 'The work of one milestone, by state.';
8
+ return '<div class="panel-head"><div class="panel-title"><h1>Tasks</h1><p class="lead">' + lead + '</p></div>' +
9
+ '<div class="panel-actions">' + ui.milestoneSelect() + '</div></div>';
10
+ }
11
+
12
+ function byNewest(a, b) { return (b.endT || 0) - (a.endT || 0); }
13
+
14
+ function metaHtml(t, fmt) {
15
+ if (t.status === 'completed') {
16
+ return '<span>' + ui.esc(fmt.ago(t.endT)) + '</span>' + (t.durationMs ? '<span>took ' + ui.esc(fmt.dur(t.durationMs)) + '</span>' : '');
17
+ }
18
+ if (t.status === 'in_progress') {
19
+ var live = t.startT ? '<span data-since="' + t.startT + '">' + fmt.dur(Studio.now() - t.startT) + '</span>' : '';
20
+ return '<span class="run">Running ' + live + '</span><span>attempt ' + (t.attempt || 1) + ' of ' + t.maxAttempts + '</span>';
21
+ }
22
+ if (t.status === 'blocked') {
23
+ return '<span class="bad">Paused after ' + (t.attempt || t.maxAttempts) + ' of ' + t.maxAttempts + ' attempts</span>';
24
+ }
25
+ if (t.waitingOn.length) return '<span>Waiting on ' + t.waitingOn.map(ui.esc).join(', ') + '</span>';
26
+ return '<span>Ready to start</span>';
27
+ }
28
+
29
+ function card(t, fmt) {
30
+ return '<button type="button" class="tcard ' + ui.esc(t.status) + '" data-task="' + ui.esc(t.id) + '">' +
31
+ '<div class="l1">' + ui.statusIcon(t.status) + '<span class="mono faint">' + ui.esc(t.id) + '</span>' + ui.agentChip(t.agent) + '</div>' +
32
+ '<div class="title">' + ui.esc(t.title) + '</div><div class="meta">' + metaHtml(t, fmt) + '</div></button>';
33
+ }
34
+
35
+ function column(label, status, list, shown, empty, extra, fmt) {
36
+ var body = shown.length ? shown.map(function (t) { return card(t, fmt); }).join('') : '<div class="col-empty">' + empty + '</div>';
37
+ return '<div class="col" data-col="' + status + '"><div class="col-head">' + ui.statusIcon(status) + label +
38
+ '<span class="count">' + list.length + '</span></div>' + body + (extra || '') + '</div>';
39
+ }
40
+
41
+ function board(m, fmt) {
42
+ var by = function (s) { return m.tasks.filter(function (t) { return t.status === s; }); };
43
+ var done = by('completed').reverse().sort(byNewest);
44
+ var shown = view.showAll ? done : done.slice(0, CAP);
45
+ var more = done.length > CAP
46
+ ? '<button type="button" class="more" data-act="tasks-more">' + (view.showAll ? 'Show fewer' : 'Show all ' + done.length) + '</button>' : '';
47
+ var up = by('pending'), run = by('in_progress'), stuck = by('blocked');
48
+ return '<div class="board">' +
49
+ column('Up next', 'pending', up, up, 'Nothing queued', '', fmt) +
50
+ column('Running', 'in_progress', run, run, 'No task is running', '', fmt) +
51
+ column('Just finished', 'completed', done, shown, 'Nothing finished yet', more, fmt) +
52
+ column('Stuck', 'blocked', stuck, stuck, 'Nothing is stuck', '', fmt) + '</div>';
53
+ }
54
+
55
+ function histRow(m, fmt) {
56
+ var open = !!view.open[m.id];
57
+ var full = m.total > 0 && m.done === m.total;
58
+ var tasks = open ? '<div class="hist-tasks">' + m.tasks.slice().reverse().sort(byNewest).map(function (t) {
59
+ return ui.taskRow(t, t.durationMs ? fmt.dur(t.durationMs) : '');
60
+ }).join('') + '</div>' : '';
61
+ return '<div><button type="button" class="hist-row" data-act="tasks-hist" data-ms-id="' + ui.esc(m.id) + '" aria-expanded="' + open + '">' +
62
+ '<span class="chev">' + ui.icon('chev', 2) + '</span><span class="mono faint">' + ui.esc(m.id) + '</span>' +
63
+ '<span class="t">' + ui.esc(m.short) + '</span>' +
64
+ '<span class="status-pill ' + (full ? 'completed' : 'in_progress') + '">' + m.done + '/' + m.total + '</span>' +
65
+ '<span class="faint num small">' + (m.lastEndT ? ui.esc(fmt.day(m.lastEndT)) : 'no timing') + '</span>' +
66
+ '<span class="faint num small">' + (m.workMs ? ui.esc(fmt.dur(m.workMs)) + ' work' : '') + '</span>' +
67
+ '<span class="faint num small">' + (m.costUsd != null ? ui.esc(fmt.money(m.costUsd)) : '') + '</span></button>' + tasks + '</div>';
68
+ }
69
+
70
+ function others(model, current, fmt) {
71
+ var list = model.milestones.filter(function (m) { return m.id !== current.id; }).reverse();
72
+ if (!list.length) return '';
73
+ return '<h2 class="section-title">Other milestones</h2><section class="hist">' +
74
+ list.map(function (m) { return histRow(m, fmt); }).join('') + '</section>';
75
+ }
76
+
77
+ Studio.registerPage('tasks', {
78
+ deps: ['tasks', 'runs', 'escalations'],
79
+ render: function (ctx) {
80
+ var model = ctx.model;
81
+ var wanted = ctx.state.milestoneId || model.activeMilestoneId;
82
+ var m = model.milestones.filter(function (x) { return x.id === wanted; })[0];
83
+ if (!m) return head(null) + ui.empty('No milestones yet', 'Add tasks to the plan to see them here.');
84
+ if (view.lastMs !== m.id) { view.lastMs = m.id; view.showAll = false; }
85
+ return head(m) + board(m, ctx.fmt) + others(model, m, ctx.fmt);
86
+ },
87
+ actions: {
88
+ 'tasks-more': function () { view.showAll = !view.showAll; Studio.repaint('tasks'); },
89
+ 'tasks-hist': function (el) {
90
+ var id = el.getAttribute('data-ms-id');
91
+ view.open[id] = !view.open[id];
92
+ Studio.repaint('tasks');
93
+ }
94
+ }
95
+ });
96
+ `;
@@ -0,0 +1 @@
1
+ export declare const tasksCss: string;
@@ -0,0 +1,37 @@
1
+ // Board and history styles, scoped under the Tasks panel.
2
+ export const tasksCss = String.raw `#panel-tasks .board { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 12px; align-items: start; }
3
+ #panel-tasks .col { background: var(--surface-2); border-radius: 12px; padding: 8px; display: flex; flex-direction: column; gap: 8px; min-width: 0; }
4
+ #panel-tasks .col-head { display: flex; align-items: center; gap: 8px; padding: 4px 6px 2px; font-weight: 500; font-size: 13px; }
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: 14px 8px; text-align: center; border: 1px dashed var(--line-2); border-radius: 8px; }
7
+ #panel-tasks .tcard { display: flex; flex-direction: column; gap: 6px; background: var(--surface); border: 1px solid var(--line); border-radius: 9px; padding: 10px; text-align: left; width: 100%; cursor: pointer; }
8
+ #panel-tasks .tcard:hover { border-color: var(--line-2); box-shadow: var(--shadow); }
9
+ #panel-tasks .tcard.in_progress { border-color: color-mix(in srgb, var(--run) 45%, var(--line)); }
10
+ #panel-tasks .tcard.blocked { border-color: color-mix(in srgb, var(--bad) 45%, var(--line)); }
11
+ #panel-tasks .tcard .l1 { display: flex; align-items: center; gap: 6px; }
12
+ #panel-tasks .tcard .l1 .agent { margin-left: auto; }
13
+ #panel-tasks .tcard .title { font-size: 13px; font-weight: 500; display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
14
+ #panel-tasks .tcard .meta { font-size: 12px; color: var(--fg-3); display: flex; gap: 8px; flex-wrap: wrap; }
15
+ #panel-tasks .tcard .meta .run { color: var(--run); }
16
+ #panel-tasks .tcard .meta .bad { color: var(--bad); }
17
+ #panel-tasks .more { border: 0; background: none; color: var(--accent); font-size: 12.5px; padding: 6px; cursor: pointer; text-align: center; }
18
+ #panel-tasks .more:hover { text-decoration: underline; }
19
+ #panel-tasks .section-title { margin: 22px 0 8px; font-size: 15px; font-weight: 600; }
20
+ #panel-tasks .hist { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; overflow: hidden; }
21
+ #panel-tasks .hist-row { display: grid; grid-template-columns: 16px 34px minmax(0, 1fr) auto 70px 90px 60px; gap: 12px; align-items: center; padding: 9px 16px; border: 0; border-top: 1px solid var(--line); background: none; text-align: left; width: 100%; cursor: pointer; }
22
+ #panel-tasks .hist > div:first-child .hist-row { border-top: 0; }
23
+ #panel-tasks .hist-row:hover { background: var(--surface-2); }
24
+ #panel-tasks .hist-row .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
25
+ #panel-tasks .hist-row .chev { display: inline-grid; transition: transform .15s; }
26
+ #panel-tasks .hist-row .chev svg { width: 14px; height: 14px; }
27
+ #panel-tasks .hist-row[aria-expanded="true"] .chev { transform: rotate(90deg); }
28
+ #panel-tasks .hist-row .small { font-size: 12px; }
29
+ #panel-tasks .hist-tasks { background: var(--surface-2); }
30
+ #panel-tasks .hist-tasks .row-btn { padding-left: 46px; }
31
+ @media (max-width: 1100px) { #panel-tasks .board { grid-template-columns: repeat(2, minmax(0, 1fr)); } }
32
+ @media (max-width: 640px) {
33
+ #panel-tasks .board { grid-template-columns: minmax(0, 1fr); }
34
+ #panel-tasks .hist-row { grid-template-columns: 16px 34px minmax(0, 1fr) auto; }
35
+ #panel-tasks .hist-row .small { display: none; }
36
+ }
37
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const teamPage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { teamScript } from './script.js';
3
+ import { teamCss } from './styles.js';
4
+ export const teamPage = {
5
+ id: 'team',
6
+ group: 'project',
7
+ label: 'Team',
8
+ icon: ICON_PATHS.team,
9
+ css: teamCss,
10
+ script: teamScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const teamScript: string;
@@ -0,0 +1,112 @@
1
+ // Team page client script: one pure render, one table row per agent.
2
+ export const teamScript = String.raw ` var esc = Studio.ui.esc;
3
+ var DASH = '–';
4
+
5
+ function head() {
6
+ return '<div class="page-head"><div><h1>Team</h1><p>Each agent in plain terms: what it did, how long it took, what it cost.</p></div></div>';
7
+ }
8
+
9
+ function sum(list) {
10
+ return list.reduce(function (total, n) { return total + n; }, 0);
11
+ }
12
+
13
+ function statsFor(key, tasks) {
14
+ var done = tasks.filter(function (t) { return t.status === 'completed'; });
15
+ var timed = done.filter(function (t) { return t.durationMs; });
16
+ var work = sum(timed.map(function (t) { return t.durationMs; }));
17
+ var costs = tasks.filter(function (t) { return t.costUsd != null; });
18
+ var stamps = tasks.map(function (t) { return t.status === 'in_progress' ? (t.startT || 0) : (t.endT || 0); });
19
+ return {
20
+ agent: key || null,
21
+ total: tasks.length,
22
+ done: done.length,
23
+ work: timed.length ? work : null,
24
+ avg: timed.length ? work / timed.length : null,
25
+ cost: costs.length ? sum(costs.map(function (t) { return t.costUsd; })) : null,
26
+ last: Math.max.apply(null, [0].concat(stamps)) || null,
27
+ running: tasks.filter(function (t) { return t.status === 'in_progress'; })[0] || null,
28
+ stuck: tasks.filter(function (t) { return t.status === 'blocked'; })[0] || null
29
+ };
30
+ }
31
+
32
+ function buildRows(model) {
33
+ var groups = {};
34
+ var order = [];
35
+ model.tasks.forEach(function (t) {
36
+ var key = t.agent || '';
37
+ if (!groups[key]) { groups[key] = []; order.push(key); }
38
+ groups[key].push(t);
39
+ });
40
+ return order.map(function (k) { return statsFor(k, groups[k]); })
41
+ .sort(function (a, b) { return b.total - a.total; });
42
+ }
43
+
44
+ function chip(task, label) {
45
+ return '<button type="button" class="chip" data-task="' + esc(task.id) + '">' +
46
+ Studio.ui.statusIcon(task.status) + esc(label) + '</button>';
47
+ }
48
+
49
+ function rightNow(r) {
50
+ if (r.running) return chip(r.running, r.running.id);
51
+ if (r.stuck) return chip(r.stuck, 'Stuck on ' + r.stuck.id);
52
+ return '<span class="faint">Idle</span>';
53
+ }
54
+
55
+ function shareBar(r, max) {
56
+ var info = Studio.agentInfo(r.agent);
57
+ var pct = max ? Math.round((r.total / max) * 100) : 0;
58
+ return '<div class="bar"><i style="width:' + pct + '%;background:var(' + info.token + ')"></i></div>';
59
+ }
60
+
61
+ function lastActive(r) {
62
+ return r.last ? Studio.ui.when(r.last) : DASH;
63
+ }
64
+
65
+ function row(r, max) {
66
+ return '<tr data-agent="' + esc(r.agent || 'unassigned') + '"><td>' + Studio.ui.agentChip(r.agent) + '</td>' +
67
+ '<td>' + rightNow(r) + '</td>' +
68
+ '<td class="r">' + r.done + '<span class="faint"> / ' + r.total + '</span></td>' +
69
+ '<td class="share">' + shareBar(r, max) + '</td>' +
70
+ '<td class="r">' + (r.avg == null ? DASH : esc(Studio.fmt.dur(r.avg))) + '</td>' +
71
+ '<td class="r">' + (r.work == null ? DASH : esc(Studio.fmt.dur(r.work))) + '</td>' +
72
+ '<td class="r">' + (r.cost == null ? DASH : esc(Studio.fmt.money(r.cost))) + '</td>' +
73
+ '<td class="r faint">' + lastActive(r) + '</td></tr>';
74
+ }
75
+
76
+ function table(rows) {
77
+ var max = Math.max.apply(null, rows.map(function (r) { return r.total; }));
78
+ var cols = ['Agent', 'Right now', 'Tasks done', 'Share of work', 'Avg. time', 'Total time', 'Est. cost', 'Last active'];
79
+ var th = cols.map(function (c, i) {
80
+ var cls = i === 3 ? ' class="share"' : (i > 1 ? ' class="r"' : '');
81
+ return '<th' + cls + '>' + c + '</th>';
82
+ }).join('');
83
+ return '<section class="panel table-wrap"><table><thead><tr>' + th + '</tr></thead><tbody>' +
84
+ rows.map(function (r) { return row(r, max); }).join('') + '</tbody></table></section>';
85
+ }
86
+
87
+ function defs() {
88
+ return '<section class="panel"><div class="panel-head"><h2>What these columns mean</h2></div><div class="panel-body defs">' +
89
+ '<div><b>Tasks done</b>: finished and checked, out of all tasks given to this agent.</div>' +
90
+ '<div><b>Avg. time</b>: from start to a passing check, for tasks that recorded timing.</div>' +
91
+ '<div><b>Est. cost</b>: estimated from token counts. The real bill is on Home under Spend.</div>' +
92
+ '<div><b>Right now</b>: the task this agent is working on, or the one it is stuck on.</div></div></section>';
93
+ }
94
+
95
+ function skeleton() {
96
+ var bar = function (w) { return '<div class="skel" style="width:' + w + '"></div>'; };
97
+ var block = '<div class="skel-block">' + bar('100%') + bar('92%') + bar('74%') + '</div>';
98
+ return head() + block + block;
99
+ }
100
+
101
+ function render(ctx) {
102
+ var rows = buildRows(ctx.model);
103
+ if (!rows.length) return head() + '<section class="panel">' + ctx.ui.empty('No tasks yet', 'Agents show up here once the plan has tasks.') + '</section>';
104
+ return head() + table(rows) + defs();
105
+ }
106
+
107
+ Studio.registerPage('team', {
108
+ deps: ['tasks', 'telemetry', 'runs'],
109
+ render: render,
110
+ skeleton: skeleton
111
+ });
112
+ `;
@@ -0,0 +1 @@
1
+ export declare const teamCss: string;
@@ -0,0 +1,22 @@
1
+ // Team page styles, all scoped under #panel-team.
2
+ export const teamCss = String.raw `#panel-team .page-head { display: flex; align-items: flex-end; gap: 12px; flex-wrap: wrap; }
3
+ #panel-team .page-head h1 { font-size: 22px; font-weight: 600; margin: 0; letter-spacing: -.01em; text-wrap: balance; }
4
+ #panel-team .page-head p { margin: 4px 0 0; color: var(--fg-2); }
5
+ #panel-team .panel { background: var(--surface); border: 1px solid var(--line); border-radius: 12px; min-width: 0; margin-top: 16px; }
6
+ #panel-team .panel-head { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-bottom: 1px solid var(--line); }
7
+ #panel-team .panel-head h2 { font-size: 14px; font-weight: 600; margin: 0; }
8
+ #panel-team .panel-body { padding: 14px 16px; }
9
+ #panel-team .table-wrap { overflow-x: auto; }
10
+ #panel-team table { width: 100%; border-collapse: collapse; font-variant-numeric: tabular-nums; }
11
+ #panel-team th { text-align: left; font-weight: 500; font-size: 12px; color: var(--fg-3); padding: 10px 16px; border-bottom: 1px solid var(--line); white-space: nowrap; }
12
+ #panel-team td { padding: 11px 16px; border-bottom: 1px solid var(--line); white-space: nowrap; }
13
+ #panel-team tr:last-child td { border-bottom: 0; }
14
+ #panel-team td.r, #panel-team th.r { text-align: right; }
15
+ #panel-team .share { width: 110px; }
16
+ #panel-team .bar { height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; }
17
+ #panel-team .bar i { display: block; height: 100%; border-radius: 3px; }
18
+ #panel-team .chip { display: inline-flex; align-items: center; gap: 6px; padding: 2px 10px 2px 6px; border: 1px solid var(--line); border-radius: 999px; background: var(--surface); font: 12px var(--mono); cursor: pointer; }
19
+ #panel-team .chip:hover { background: var(--surface-2); }
20
+ #panel-team .defs { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 10px 24px; font-size: 13px; color: var(--fg-2); }
21
+ #panel-team .defs b { color: var(--fg); font-weight: 500; }
22
+ `;
@@ -0,0 +1,13 @@
1
+ export interface PageModule {
2
+ id: string;
3
+ group: 'now' | 'project' | 'system';
4
+ label: string;
5
+ /** Inner SVG markup for a 24-unit viewBox (stroke icon). */
6
+ icon: string;
7
+ /** Styles scoped under #panel-<id>. */
8
+ css: string;
9
+ /** Client script; the shell wraps it in its own function scope and passes Studio. */
10
+ script: string;
11
+ /** Static markup placed inside the page panel before the first paint. */
12
+ markup?: string;
13
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../types.js';
2
+ export declare const worktreesPage: PageModule;
@@ -0,0 +1,11 @@
1
+ import { ICON_PATHS } from '../../shell/icons.js';
2
+ import { worktreesCss } from './styles.js';
3
+ import { worktreesScript } from './script.js';
4
+ export const worktreesPage = {
5
+ id: 'worktrees',
6
+ group: 'project',
7
+ label: 'Worktrees',
8
+ icon: ICON_PATHS.worktrees,
9
+ css: worktreesCss,
10
+ script: worktreesScript,
11
+ };
@@ -0,0 +1 @@
1
+ export declare const worktreesScript: string;
@@ -0,0 +1,104 @@
1
+ // Worktrees view script.
2
+ export const worktreesScript = String.raw ` // ─── View 3: Agent worktrees ───────────────────────────────────────────────
3
+ function shortPath(p) {
4
+ var parts = String(p || '').split(/[\\/]+/).filter(Boolean);
5
+ return parts.length > 2 ? '…/' + parts.slice(-2).join('/') : String(p || '');
6
+ }
7
+
8
+ function worktreeTaskStatus(w) {
9
+ if (w.taskStatus) return w.taskStatus;
10
+ var task = w.taskId ? taskById(w.taskId) : null;
11
+ return task ? task.status : null;
12
+ }
13
+
14
+ function worktreeBadge(w, status) {
15
+ if (!w.isAgentWorktree) return '<span class="badge badge-neutral">Main workspace</span>';
16
+ if (status === 'completed') return '<span class="badge badge-success">Tests Passed (Ready to merge)</span>';
17
+ if (status === 'in_progress') return '<span class="badge badge-active">Agent working</span>';
18
+ if (status === 'blocked') return '<span class="badge badge-danger">Blocked</span>';
19
+ if (status === 'pending') return '<span class="badge badge-neutral">Pending</span>';
20
+ return '<span class="badge badge-neutral" title="No matching task in the master plan">Untracked</span>';
21
+ }
22
+
23
+ function worktreeRow(w) {
24
+ var status = worktreeTaskStatus(w);
25
+ var task = w.taskId ? taskById(w.taskId) : null;
26
+ // The server decides merge eligibility (and why not); fall back to the task status from the plan.
27
+ var canMerge = w.mergeEligible != null ? !!w.mergeEligible : status === 'completed';
28
+ var blockedWhy = w.mergeBlockedReason || 'Available once the task is completed';
29
+ var busy = w.taskId && pipe.busy['wt:' + w.taskId];
30
+ var actions = '<span class="muted">–</span>';
31
+ if (w.isAgentWorktree && w.taskId) {
32
+ actions = busy
33
+ ? '<button class="btn small" type="button" disabled><span class="spinner" aria-hidden="true"></span>' + (busy === 'merge' ? 'Merging…' : 'Deleting…') + '</button>'
34
+ : '<button class="btn small" type="button" data-act="wt-diff" data-key="' + esc(w.taskId) + '" aria-label="' + esc('Inspect diff for ' + w.taskId) + '">Inspect Diff</button>' +
35
+ '<button class="btn small primary" type="button" data-act="wt-merge" data-key="' + esc(w.taskId) + '"' + (canMerge ? '' : ' disabled title="' + esc(blockedWhy) + '"') + '>Merge to Main</button>' +
36
+ '<button class="btn small danger" type="button" data-act="wt-remove" data-key="' + esc(w.taskId) + '">Delete Workspace</button>';
37
+ }
38
+ return '<tr><td data-label="Branch"><code>' + esc(w.branch) + '</code></td>' +
39
+ '<td data-label="Task ID">' + (w.taskId ? '<div><code>' + esc(w.taskId) + '</code>' + (task ? '<div class="cell-sub" title="' + esc(task.title) + '">' + esc(task.title) + '</div>' : '') + '</div>' : '<span class="muted">–</span>') + '</td>' +
40
+ '<td data-label="Workspace Folder"><code class="path" title="' + esc(w.path) + '">' + esc(shortPath(w.path)) + '</code></td>' +
41
+ '<td data-label="Latest Commit"><code>' + esc(String(w.head || '').slice(0, 7) || '–') + '</code></td>' +
42
+ '<td data-label="Status">' + worktreeBadge(w, status) + '</td>' +
43
+ '<td data-label="Actions"><div class="row-actions">' + actions + '</div></td></tr>';
44
+ }
45
+
46
+ function renderWorktrees() {
47
+ var head = panelHead('Agent Worktrees', 'Isolated git workspaces for tasks. Changes are tested automatically before merging into your main branch.');
48
+ if (!pipe.loaded.worktrees) return head + '<div class="card section" aria-hidden="true"><div class="skeleton"></div><div class="skeleton"></div><div class="skeleton short"></div></div>';
49
+ if (pipe.errors.worktrees && !pipe.worktrees.length) return head + errorCard('worktrees', pipe.errors.worktrees, 'worktrees');
50
+ var rows = pipe.worktrees.slice().sort(function (a, b) { return (a.isAgentWorktree ? 1 : 0) - (b.isAgentWorktree ? 1 : 0); });
51
+ if (!rows.some(function (w) { return w.isAgentWorktree; })) {
52
+ return head + staleNote('worktrees') + '<div class="card state"><div class="state-icon">' + ICON.branch + '</div><h2>No agent worktrees</h2>' +
53
+ '<p>Run <code>nativ worktree create &lt;taskId&gt;</code> to give an agent its own isolated workspace folder for an independent task.</p></div>';
54
+ }
55
+ return head + staleNote('worktrees') + '<div class="card wt-card"><table class="grid"><thead><tr>' +
56
+ '<th scope="col">Branch Name</th><th scope="col">Task ID</th><th scope="col">Workspace Folder</th><th scope="col">Latest Commit</th><th scope="col">Status</th><th scope="col">Actions</th>' +
57
+ '</tr></thead><tbody>' + rows.map(worktreeRow).join('') + '</tbody></table></div>';
58
+ }
59
+
60
+ function confirmWorktree(action, taskId) {
61
+ var w = null;
62
+ pipe.worktrees.some(function (x) { if (x.taskId === taskId) { w = x; return true; } return false; });
63
+ var branch = w ? w.branch : 'agent/task-' + taskId;
64
+ if (action === 'merge') {
65
+ confirmDialog({
66
+ title: 'Merge to Main',
67
+ body: 'Merge ' + branch + ' into the main workspace? Automated tests will run first and reject the merge if tests fail.',
68
+ confirmLabel: 'Merge to Main'
69
+ }, function () { runWorktreeAction('merge', taskId); });
70
+ } else {
71
+ confirmDialog({
72
+ title: 'Delete workspace',
73
+ body: 'Remove the workspace folder for ' + taskId + ' and delete branch ' + branch + '? Uncommitted work will be removed.',
74
+ confirmLabel: 'Delete Workspace',
75
+ danger: true
76
+ }, function () { runWorktreeAction('remove', taskId); });
77
+ }
78
+ }
79
+
80
+ function runWorktreeAction(action, taskId) {
81
+ var key = 'wt:' + taskId;
82
+ if (pipe.busy[key]) return;
83
+ pipe.busy[key] = action;
84
+ repaint('worktrees');
85
+ postJson('/api/pipeline/worktrees/action', { action: action, taskId: taskId }).then(function (r) {
86
+ toast(r.message || (action === 'merge' ? 'Merged workspace for ' : 'Deleted workspace for ') + taskId, 'ok');
87
+ }, function (e) {
88
+ toast((action === 'merge' ? 'Merge rejected for ' : 'Could not delete workspace for ') + taskId + ': ' + e.message);
89
+ }).then(function () {
90
+ delete pipe.busy[key];
91
+ return fetchPipeline(['worktrees', 'tasks', 'status']);
92
+ });
93
+ }
94
+
95
+ Studio.registerPage('worktrees', {
96
+ deps: ['worktrees', 'tasks'],
97
+ render: function () { return renderWorktrees(); },
98
+ actions: {
99
+ 'wt-diff': function (el) { openConsole(el.getAttribute('data-key'), false, 'diff'); },
100
+ 'wt-merge': function (el) { confirmWorktree('merge', el.getAttribute('data-key')); },
101
+ 'wt-remove': function (el) { confirmWorktree('remove', el.getAttribute('data-key')); }
102
+ }
103
+ });
104
+ `;
@@ -0,0 +1 @@
1
+ export declare const worktreesCss: string;
@@ -0,0 +1,15 @@
1
+ // Worktrees view styles.
2
+ export const worktreesCss = String.raw `/* Agent worktrees */
3
+ .wt-card { overflow: hidden; }
4
+ table.grid { width: 100%; border-collapse: collapse; font-size: 13.5px; }
5
+ table.grid th { padding: 10px 16px; text-align: left; font-size: 12.5px; font-weight: 600; color: var(--muted); background: var(--bg); border-bottom: 1px solid var(--border); white-space: nowrap; }
6
+ table.grid th:last-child { text-align: right; }
7
+ table.grid td { padding: 12px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
8
+ table.grid tbody tr:last-child td { border-bottom: none; }
9
+ table.grid tbody tr:hover td { background: var(--bg); }
10
+ table.grid code { font-size: 12.5px; }
11
+ .cell-sub { max-width: 280px; margin-top: 2px; font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
12
+ .path { display: inline-block; max-width: 260px; vertical-align: bottom; font-size: 13px; color: var(--muted); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
13
+ .row-actions { display: flex; justify-content: flex-end; gap: 6px; }
14
+
15
+ `;
@@ -0,0 +1 @@
1
+ export declare const actionsScript: string;
@@ -0,0 +1,49 @@
1
+ // Task actions behind the detail panel and the pages: start, dispatch, stop, logs, proposals.
2
+ export const actionsScript = String.raw ` // ─── Task actions ──────────────────────────────────────────────────────────
3
+ /** Plain-language failure text: no stack, one closing period. */
4
+ function plainFail(what, e) {
5
+ return what + ': ' + String((e && e.message) || 'unknown error').replace(/[.\s]+$/, '') + '.';
6
+ }
7
+
8
+ function runTaskAction(action, taskId) {
9
+ var key = 'task:' + taskId;
10
+ if (pipe.busy[key]) return;
11
+ pipe.busy[key] = action;
12
+ refreshUi([]);
13
+ postJson('/api/pipeline/tasks/action', { action: action, taskId: taskId }).then(function () {
14
+ toast('Started ' + taskId, 'ok');
15
+ }, function (e) {
16
+ toast(plainFail('Could not start ' + taskId, e));
17
+ }).then(function () {
18
+ delete pipe.busy[key];
19
+ return fetchPipeline(['status', 'tasks', 'worktrees']);
20
+ });
21
+ }
22
+
23
+ function askArchitect(taskId) {
24
+ var escalation = escalationFor(taskId);
25
+ if (!escalation) { toast('There is nothing waiting for the architect on ' + taskId + '.'); return; }
26
+ openConsole(taskId, true, 'heal');
27
+ runTriage(escalation);
28
+ }
29
+
30
+ var TASK_ACTIONS = {
31
+ start: function (id) { runTaskAction('start', id); },
32
+ dispatch: function (id) { openDispatchDialog(id); },
33
+ abort: function (id) { confirmAbort(id); },
34
+ logs: function (id) { openConsole(id, false, 'logs'); },
35
+ output: function (id) { openConsole(id, false, 'logs'); },
36
+ proposal: function (id) { openConsole(id, false, 'heal'); },
37
+ ask: askArchitect,
38
+ flow: function (id) {
39
+ var t = Studio.model && Studio.model.byId[id];
40
+ if (t) Studio.go('flow', { milestoneId: t.milestoneId, focusTaskId: id, keepDetail: true });
41
+ }
42
+ };
43
+ Studio.taskAction = function (name, taskId) {
44
+ if (TASK_ACTIONS[name]) TASK_ACTIONS[name](taskId);
45
+ };
46
+ /** Name of the action running for a task, or null. */
47
+ Studio.busyAction = function (taskId) { return pipe.busy['task:' + taskId] || null; };
48
+
49
+ `;
@@ -0,0 +1,2 @@
1
+ import type { PageModule } from '../pages/types.js';
2
+ export declare function bootScript(pages: PageModule[]): string;
@@ -0,0 +1,13 @@
1
+ // Startup: pages register, the first view opens, data loads and the live stream connects.
2
+ export function bootScript(pages) {
3
+ const order = JSON.stringify(pages.map((p) => ({ id: p.id, group: p.group, label: p.label, icon: p.icon })));
4
+ return String.raw ` // ─── Boot ──────────────────────────────────────────────────────────────────
5
+ Studio.order = ${order};
6
+ setView(Studio.resolveHash(location.hash, pageIds()));
7
+ fetchPipeline(ALL_PARTS);
8
+ connectEvents();
9
+ })();
10
+ </script>
11
+ </body>
12
+ </html>`;
13
+ }
@@ -0,0 +1 @@
1
+ export declare const consoleScript: string;