@zq-silk/yui 0.2.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +603 -133
- package/README.md +806 -31
- package/dist/agent/agent.js +2 -1
- package/dist/agent/argumentPolicy.js +3 -1
- package/dist/agent/launchEnvironment.js +106 -0
- package/dist/agent/managedRuntimeEnvironment.js +34 -0
- package/dist/brief/taskBrief.js +11 -1
- package/dist/cli/agentConfigurationPicker.js +287 -0
- package/dist/cli/commandCatalog.js +488 -60
- package/dist/cli/completion.js +146 -22
- package/dist/cli/helpRenderer.js +3 -1
- package/dist/cli/interactionCandidates.js +53 -15
- package/dist/cli/interactionPolicy.js +267 -30
- package/dist/cli/interactiveSelection.js +6 -2
- package/dist/cli/invocationRouter.js +5 -1
- package/dist/cli/operatorWizard.js +87 -0
- package/dist/cli/roleOptionCatalog.js +1 -0
- package/dist/cli/roleWizard.js +185 -21
- package/dist/cli/updateCommand.js +62 -19
- package/dist/cli/updateOrchestrator.js +539 -0
- package/dist/cli/updatePorts.js +1119 -0
- package/dist/cli/upgradeCommand.js +112 -0
- package/dist/cli.js +1420 -86
- package/dist/commands/agentCommands.js +146 -3
- package/dist/commands/configCommands.js +126 -0
- package/dist/commands/controllerCommands.js +365 -0
- package/dist/commands/globalRoleCommands.js +168 -126
- package/dist/commands/jobCommands.js +18 -8
- package/dist/commands/operatorCommands.js +159 -9
- package/dist/commands/profileCommands.js +203 -0
- package/dist/commands/projectCommands.js +650 -0
- package/dist/commands/roleConfiguration.js +85 -24
- package/dist/commands/roleRuntimeGuard.js +12 -0
- package/dist/commands/roleSkillValidation.js +47 -0
- package/dist/commands/taskActor.js +127 -0
- package/dist/commands/taskCommands.js +4201 -313
- package/dist/commands/taskCompletionGate.js +131 -0
- package/dist/commands/taskContextCommand.js +244 -30
- package/dist/commands/taskInputCommands.js +177 -59
- package/dist/commands/taskIntegrationCommands.js +303 -0
- package/dist/commands/taskOverviewCommand.js +363 -0
- package/dist/commands/taskRoleRuntimeStatus.js +125 -19
- package/dist/commands/textInput.js +15 -0
- package/dist/completion/completionInstaller.js +26 -22
- package/dist/config/yuiConfig.js +4 -3
- package/dist/context/dispatchContext.js +90 -38
- package/dist/context/roleSessionContext.js +119 -0
- package/dist/controller/claudeLifecycleHook.js +203 -0
- package/dist/controller/clientRuntime.js +408 -56
- package/dist/controller/codexLifecycleHook.js +108 -0
- package/dist/controller/controller.js +1089 -32
- package/dist/controller/domainIdentity.js +505 -0
- package/dist/controller/ephemeralResourceReaper.js +131 -0
- package/dist/controller/fileSchedulerStoreAdapter.js +2153 -103
- package/dist/controller/providerHookRunFence.js +127 -0
- package/dist/controller/resourceCleanupLinux.js +286 -0
- package/dist/controller/resourceInventory.js +531 -0
- package/dist/controller/resourceInventoryLinux.js +610 -0
- package/dist/controller/runtime.js +629 -10
- package/dist/controller/runtimeEventInbox.js +564 -0
- package/dist/controller/runtimeEventProcessor.js +248 -0
- package/dist/controller/runtimeLaunchCoordinator.js +477 -0
- package/dist/controller/sessionNotify.js +121 -78
- package/dist/coordination/deadlineScheduler.js +15 -0
- package/dist/coordination/mailboxScheduler.js +108 -0
- package/dist/coordination/workMailbox.js +329 -0
- package/dist/coordination/workMailboxQueue.js +86 -0
- package/dist/core/controllerClient.js +19 -5
- package/dist/core/controllerEndpoint.js +37 -0
- package/dist/core/controllerServer.js +218 -10
- package/dist/core/protocol.js +6 -2
- package/dist/decision/decision.js +2 -1
- package/dist/doctor/doctor.js +681 -32
- package/dist/domain/validation.js +53 -0
- package/dist/errors/cliError.js +5 -3
- package/dist/event/taskEvent.js +7 -3
- package/dist/execution/codexThreadNaming.js +160 -0
- package/dist/execution/executionGroup.js +579 -0
- package/dist/executor/agentAdapter.js +255 -40
- package/dist/executor/agentConfigurationCatalog.js +326 -0
- package/dist/executor/agentConfigurationProbe.js +506 -0
- package/dist/executor/agentExecutor.js +625 -10
- package/dist/executor/codexConfigConflict.js +290 -0
- package/dist/executor/effectiveLaunch.js +340 -0
- package/dist/executor/executorRegistry.js +238 -36
- package/dist/executor/fileRoleLaunchPlanner.js +550 -40
- package/dist/executor/turnCompletion.js +126 -0
- package/dist/input/inputRequest.js +30 -9
- package/dist/integration/changeSet.js +36 -0
- package/dist/integration/checkResult.js +24 -0
- package/dist/integration/gitIntegrationService.js +695 -0
- package/dist/integration/integrationAttempt.js +142 -0
- package/dist/interaction/operatorPresentation.js +96 -0
- package/dist/lifecycle/canonicalLifecycleEvent.js +342 -0
- package/dist/lifecycle/exactRunTerminalization.js +572 -0
- package/dist/lifecycle/providerLifecycleMapping.js +190 -0
- package/dist/lifecycle/taskRoleSessionReset.js +124 -0
- package/dist/message/message.js +23 -7
- package/dist/milestone/milestone.js +2 -1
- package/dist/operator/operatorSessionHistory.js +124 -0
- package/dist/output/agentConfigurationPresentation.js +43 -0
- package/dist/output/rolePresentation.js +34 -10
- package/dist/output/terminal.js +8 -0
- package/dist/output/timePresentation.js +55 -0
- package/dist/profile/agentProfile.js +128 -0
- package/dist/repository/gitWorkspace.js +578 -24
- package/dist/repository/project.js +213 -0
- package/dist/repository/taskWorkspaceCoordinator.js +392 -0
- package/dist/repository/taskWorkspacePreparer.js +1688 -191
- package/dist/review/reviewConfig.js +11 -0
- package/dist/review/reviewRound.js +399 -0
- package/dist/review/taskFinalReviewContract.js +90 -0
- package/dist/role/role.js +124 -23
- package/dist/run/agentRun.js +155 -12
- package/dist/run/runIdentity.js +82 -0
- package/dist/runtime/exactControlPlane.js +472 -0
- package/dist/runtime/index.js +8 -0
- package/dist/runtime/lifecycleReservation.js +38 -0
- package/dist/runtime/ports.js +11 -0
- package/dist/runtime/preallocatedNativeSession.js +13 -0
- package/dist/runtime/promptEnvelope.js +30 -0
- package/dist/runtime/runtimeBinding.js +31 -0
- package/dist/runtime/runtimeOwner.js +14 -0
- package/dist/runtime/sessionLaunchRequest.js +62 -0
- package/dist/runtime/sessionTitle.js +54 -0
- package/dist/runtime/taskRuntimeIsolation.js +643 -0
- package/dist/runtime/tmuxAdapters.js +315 -0
- package/dist/runtime/turnCompletion.js +3 -0
- package/dist/runtime/validation.js +23 -0
- package/dist/scheduler/activeRoleRunDelivery.js +342 -32
- package/dist/scheduler/activeTaskProgress.js +63 -0
- package/dist/scheduler/leaderFailure.js +2 -1
- package/dist/scheduler/leaderWakeupProcessor.js +307 -66
- package/dist/scheduler/operatorInputNotificationProcessor.js +109 -46
- package/dist/scheduler/operatorNotification.js +44 -2
- package/dist/scheduler/ports.js +28 -1
- package/dist/scheduler/roleRunLiveness.js +131 -25
- package/dist/scheduler/roleRunStall.js +951 -0
- package/dist/scheduler/taskExecutionProjection.js +544 -0
- package/dist/scheduler/wakeupQueue.js +3 -0
- package/dist/setup/setupCommand.js +302 -52
- package/dist/storage/compatibleTaskStore.js +102 -0
- package/dist/storage/migration/baseline.js +78 -0
- package/dist/storage/migration/classifier.js +51 -0
- package/dist/storage/migration/compatibleCodec.js +53 -0
- package/dist/storage/migration/engine.js +147 -0
- package/dist/storage/migration/index.js +33 -0
- package/dist/storage/migration/planner.js +154 -0
- package/dist/storage/migration/productionRegistry.js +486 -0
- package/dist/storage/migration/registry.js +169 -0
- package/dist/storage/migration/report.js +54 -0
- package/dist/storage/migration/types.js +31 -0
- package/dist/storage/storageSchema.js +147 -123
- package/dist/storage/storageVersions.js +11 -0
- package/dist/storage/taskStore.js +1793 -197
- package/dist/storage/upgrade/homeClassification.js +156 -0
- package/dist/storage/upgrade/homeMigrationTarget.js +595 -0
- package/dist/storage/upgrade/offlineUpgradeInventory.js +315 -0
- package/dist/storage/upgrade/productionMigrationRegistry.js +6 -0
- package/dist/storage/upgrade/recordVersionScan.js +176 -0
- package/dist/storage/upgrade/recordVersions.js +159 -0
- package/dist/storage/upgrade/switchProgress.js +80 -0
- package/dist/storage/upgrade/upgradeOrchestrator.js +948 -0
- package/dist/storage/upgrade/upgradeReceipt.js +161 -0
- package/dist/storage/upgradeCoordination.js +186 -0
- package/dist/storage/upgradeFence.js +366 -0
- package/dist/task/task.js +132 -26
- package/dist/task/taskRecordReference.js +66 -0
- package/dist/tmux/commandExecutor.js +75 -2
- package/dist/tmux/tmuxManager.js +747 -49
- package/dist/version.js +23 -0
- package/dist/web/assets/assetManifest.js +62 -0
- package/dist/web/assets/client/app.js +631 -0
- package/dist/web/assets/client/components.js +605 -0
- package/dist/web/assets/client/dom.js +14 -0
- package/dist/web/assets/client/format.js +28 -0
- package/dist/web/assets/client/i18n.js +494 -0
- package/dist/web/assets/client/markdown.js +114 -0
- package/dist/web/assets/client/theme.js +32 -0
- package/dist/web/assets/client/view.js +458 -0
- package/dist/web/assets/fontData.js +12 -0
- package/dist/web/assets/fonts.js +12 -0
- package/dist/web/assets/shell.js +114 -0
- package/dist/web/assets/styles/cards.js +135 -0
- package/dist/web/assets/styles/layout.js +47 -0
- package/dist/web/assets/styles/markdown.js +29 -0
- package/dist/web/assets/styles/responsive.js +39 -0
- package/dist/web/assets/styles/tokens.js +101 -0
- package/dist/web/assets/styles/widgets.js +147 -0
- package/dist/web/tmuxWebTerminal.js +158 -0
- package/dist/web/webServer.js +463 -0
- package/dist/web/webSnapshot.js +148 -0
- package/dist/workItem/workItem.js +642 -23
- package/dist/workspace/gitChangeSetCapture.js +86 -0
- package/dist/workspace/workItemChangeSetManager.js +445 -0
- package/dist/worktree/managedWorkspace.js +202 -0
- package/docs/task-local-identity.md +62 -0
- package/i18n/README.zh-CN.md +406 -31
- package/package.json +10 -2
- package/skills/yui-leader/SKILL.md +601 -39
- package/skills/yui-operator/SKILL.md +255 -34
- package/skills/yui-reviewer/SKILL.md +57 -0
- package/skills/yui-worker/SKILL.md +214 -17
- package/dist/commands/repositoryCommands.js +0 -86
- package/dist/operator/operatorContext.js +0 -66
- package/dist/repository/repository.js +0 -55
- package/dist/scheduler/archivedTaskRuntime.js +0 -12
- package/dist/worktree/roleWorkspace.js +0 -62
|
@@ -0,0 +1,458 @@
|
|
|
1
|
+
export const VIEW_SCRIPT = `
|
|
2
|
+
// Page composition: sidebar lists, overview, and the task detail scaffold.
|
|
3
|
+
// All reusable widgets and cards come from components.js.
|
|
4
|
+
import { clear, node } from "/assets/js/dom.js";
|
|
5
|
+
import { byNewest, formatDateTime } from "/assets/js/format.js";
|
|
6
|
+
import {
|
|
7
|
+
anchorSection,
|
|
8
|
+
attentionRow,
|
|
9
|
+
conclusionMeta,
|
|
10
|
+
emptyRow,
|
|
11
|
+
historyEventRow,
|
|
12
|
+
inputCard,
|
|
13
|
+
messageCard,
|
|
14
|
+
metaItem,
|
|
15
|
+
metricTile,
|
|
16
|
+
overviewRow,
|
|
17
|
+
pagedList,
|
|
18
|
+
pathMetaItem,
|
|
19
|
+
pill,
|
|
20
|
+
reviewCard,
|
|
21
|
+
richText,
|
|
22
|
+
roleCard,
|
|
23
|
+
runCard,
|
|
24
|
+
sectionHead,
|
|
25
|
+
taskCard,
|
|
26
|
+
translatedStatus,
|
|
27
|
+
workItemCard
|
|
28
|
+
} from "/assets/js/components.js";
|
|
29
|
+
|
|
30
|
+
const statuses = ["all", "active", "draft", "completed", "archived"];
|
|
31
|
+
|
|
32
|
+
export function renderFilters(container, state, t, onFilter) {
|
|
33
|
+
const counts = state.counts || {};
|
|
34
|
+
// Single-row status chip row: every status is visible at once with its count,
|
|
35
|
+
// so the user can switch filters without opening a dropdown. The row never
|
|
36
|
+
// wraps; on narrow widths it scrolls horizontally.
|
|
37
|
+
//
|
|
38
|
+
// The row and its chips are created once and reused across renders so that
|
|
39
|
+
// switching filters does not reset the row's horizontal scroll position.
|
|
40
|
+
let row = container.querySelector(":scope > .filter-row");
|
|
41
|
+
if (!row) {
|
|
42
|
+
row = node("div", "filter-row");
|
|
43
|
+
statuses.forEach(function (status) {
|
|
44
|
+
const btn = node("button", "filter-chip");
|
|
45
|
+
btn.type = "button";
|
|
46
|
+
btn.dataset.status = status;
|
|
47
|
+
if (status !== "all") {
|
|
48
|
+
const dot = node("span", "filter-dot");
|
|
49
|
+
dot.classList.add(status);
|
|
50
|
+
btn.append(dot);
|
|
51
|
+
}
|
|
52
|
+
btn.append(document.createTextNode(translatedStatus(t, "status", status)));
|
|
53
|
+
const badge = node("span", "filter-count");
|
|
54
|
+
btn.append(badge);
|
|
55
|
+
btn.addEventListener("click", function () { onFilter(status); });
|
|
56
|
+
row.append(btn);
|
|
57
|
+
});
|
|
58
|
+
container.append(row);
|
|
59
|
+
}
|
|
60
|
+
statuses.forEach(function (status) {
|
|
61
|
+
const btn = row.querySelector('.filter-chip[data-status="' + status + '"]');
|
|
62
|
+
if (!btn) return;
|
|
63
|
+
btn.classList.toggle("is-active", state.filter === status);
|
|
64
|
+
const count = status === "all" ? counts.total : counts[status];
|
|
65
|
+
const badge = btn.querySelector(".filter-count");
|
|
66
|
+
if (badge) {
|
|
67
|
+
if (count !== undefined && count !== null) {
|
|
68
|
+
badge.textContent = String(count);
|
|
69
|
+
badge.hidden = false;
|
|
70
|
+
} else {
|
|
71
|
+
badge.hidden = true;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function taskGroupOf(task, attentionIds) {
|
|
78
|
+
if (attentionIds.has(task.id)) return "attention";
|
|
79
|
+
if (task.status === "active") return "active";
|
|
80
|
+
if (task.status === "draft") return "draft";
|
|
81
|
+
if (task.status === "archived") return "archived";
|
|
82
|
+
return "finished";
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function taskMatchesQuery(task, query, locale) {
|
|
86
|
+
if (!query) return true;
|
|
87
|
+
const hay = [task.title, task.id]
|
|
88
|
+
.concat(task.tags || [])
|
|
89
|
+
.concat(task.projectNames || [])
|
|
90
|
+
.join(" ")
|
|
91
|
+
.toLocaleLowerCase(locale);
|
|
92
|
+
return hay.includes(query);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function renderTasks(container, state, t, locale, onSelect) {
|
|
96
|
+
clear(container);
|
|
97
|
+
const query = state.query.trim().toLocaleLowerCase(locale);
|
|
98
|
+
const attentionIds = new Set((state.attention || []).map(function (item) { return item.taskId; }));
|
|
99
|
+
const groups = { attention: [], active: [], draft: [], finished: [], archived: [] };
|
|
100
|
+
(state.tasks || []).forEach(function (task) {
|
|
101
|
+
if (state.filter !== "all" && task.status !== state.filter) return;
|
|
102
|
+
if (!taskMatchesQuery(task, query, locale)) return;
|
|
103
|
+
groups[taskGroupOf(task, attentionIds)].push(task);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
const order = [
|
|
107
|
+
["attention", t("group.attention")],
|
|
108
|
+
["active", t("group.active")],
|
|
109
|
+
["draft", t("group.draft")],
|
|
110
|
+
["finished", t("group.finished")],
|
|
111
|
+
["archived", t("group.archived")]
|
|
112
|
+
];
|
|
113
|
+
const firstGroup = order.find(function (entry) { return groups[entry[0]].length > 0; });
|
|
114
|
+
if (!firstGroup) {
|
|
115
|
+
container.append(node("div", "empty", t("empty.tasks")));
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
order.forEach(function (entry) {
|
|
119
|
+
const list = groups[entry[0]];
|
|
120
|
+
if (!list.length) return;
|
|
121
|
+
const block = node("section", "task-group");
|
|
122
|
+
const head = node("h4", "task-group-head");
|
|
123
|
+
head.append(node("span", "", entry[1]), node("span", "count", String(list.length)));
|
|
124
|
+
block.append(head);
|
|
125
|
+
list.forEach(function (task) {
|
|
126
|
+
block.append(taskCard(task, attentionIds.has(task.id), state, t, locale, onSelect));
|
|
127
|
+
});
|
|
128
|
+
container.append(block);
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function overviewBlock(head, tasks, t, locale, onSelect) {
|
|
133
|
+
const block = node("section", "overview-block");
|
|
134
|
+
block.append(sectionHead(head, { count: tasks.length }));
|
|
135
|
+
const list = node("div", "overview-list");
|
|
136
|
+
tasks.forEach(function (task) {
|
|
137
|
+
list.append(overviewRow(task, t, locale, onSelect));
|
|
138
|
+
});
|
|
139
|
+
block.append(list);
|
|
140
|
+
return block;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export function renderOverview(detail, state, t, locale, onSelect) {
|
|
144
|
+
clear(detail);
|
|
145
|
+
const wrap = node("div", "overview");
|
|
146
|
+
const counts = state.counts;
|
|
147
|
+
|
|
148
|
+
const total = counts ? counts.total : 0;
|
|
149
|
+
const rail = node("div", "command-rail");
|
|
150
|
+
rail.append(
|
|
151
|
+
metricTile(t("metrics.active"), counts ? counts.active : "—", { hot: true }),
|
|
152
|
+
metricTile(t("metrics.inputs"), counts ? counts.openInputs : "—", { warning: true }),
|
|
153
|
+
metricTile(t("metrics.completed"), counts ? counts.completed : "—", {}),
|
|
154
|
+
metricTile(t("metrics.total"), total, {})
|
|
155
|
+
);
|
|
156
|
+
wrap.append(rail);
|
|
157
|
+
|
|
158
|
+
const inbox = node("section", "overview-block");
|
|
159
|
+
const inboxHead = sectionHead(t("overview.inbox"), {
|
|
160
|
+
count: (state.attention || []).length
|
|
161
|
+
});
|
|
162
|
+
inbox.append(inboxHead);
|
|
163
|
+
const items = state.attention || [];
|
|
164
|
+
if (!items.length) {
|
|
165
|
+
const empty = node("div", "inbox-empty");
|
|
166
|
+
empty.append(node("span", "dot"), node("span", "", t("overview.inboxEmpty")));
|
|
167
|
+
inbox.append(empty);
|
|
168
|
+
} else {
|
|
169
|
+
const list = node("div", "inbox-list");
|
|
170
|
+
items.slice(0, 8).forEach(function (item) {
|
|
171
|
+
list.append(attentionRow(item, t, locale, onSelect));
|
|
172
|
+
});
|
|
173
|
+
inbox.append(list);
|
|
174
|
+
}
|
|
175
|
+
wrap.append(inbox);
|
|
176
|
+
|
|
177
|
+
const stalledTasks = (state.tasks || []).filter(function (task) {
|
|
178
|
+
return (task.needsAttentionCount || 0) > 0;
|
|
179
|
+
});
|
|
180
|
+
if (stalledTasks.length) {
|
|
181
|
+
const stalledBlock = node("section", "overview-block");
|
|
182
|
+
stalledBlock.append(node("h3", "", t("overview.attention") + " · " + stalledTasks.length));
|
|
183
|
+
const list = node("div", "overview-list");
|
|
184
|
+
stalledTasks.forEach(function (task) {
|
|
185
|
+
list.append(overviewRow(
|
|
186
|
+
task,
|
|
187
|
+
task.needsAttentionCount + " " + t("stats.stalledRuns"),
|
|
188
|
+
"has-inputs",
|
|
189
|
+
onSelect
|
|
190
|
+
));
|
|
191
|
+
});
|
|
192
|
+
stalledBlock.append(list);
|
|
193
|
+
wrap.append(stalledBlock);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Active work and the freshest updates sit side by side on wide screens, so
|
|
197
|
+
// the overview fills the reading surface instead of ending at the inbox.
|
|
198
|
+
const duo = node("div", "overview-duo");
|
|
199
|
+
const activeTasks = (state.tasks || []).filter(function (task) { return task.status === "active"; });
|
|
200
|
+
if (activeTasks.length) {
|
|
201
|
+
duo.append(overviewBlock(t("overview.activeNow"), activeTasks, t, locale, onSelect));
|
|
202
|
+
}
|
|
203
|
+
const recentTasks = (state.tasks || []).slice().sort(byNewest).slice(0, 8);
|
|
204
|
+
if (recentTasks.length) {
|
|
205
|
+
duo.append(overviewBlock(t("overview.recent"), recentTasks, t, locale, onSelect));
|
|
206
|
+
}
|
|
207
|
+
if (duo.childNodes.length) wrap.append(duo);
|
|
208
|
+
detail.append(wrap);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function renderLoading(container, t, key) {
|
|
212
|
+
clear(container);
|
|
213
|
+
container.append(node("div", "loading", t(key)));
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
export function renderError(container, message) {
|
|
217
|
+
clear(container);
|
|
218
|
+
container.append(node("div", "error", message));
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
const RUN_PAGE_SIZE = 12;
|
|
222
|
+
const LIST_PAGE_SIZE = 10;
|
|
223
|
+
const RUN_FILTERS = ["all", "active", "yielded", "failed"];
|
|
224
|
+
|
|
225
|
+
function runFilterRow(sortedRuns, t, onChange) {
|
|
226
|
+
const row = node("div", "filter-row run-filter");
|
|
227
|
+
const buttons = [];
|
|
228
|
+
RUN_FILTERS.forEach(function (status) {
|
|
229
|
+
const count = status === "all"
|
|
230
|
+
? sortedRuns.length
|
|
231
|
+
: sortedRuns.filter(function (run) { return run.status === status; }).length;
|
|
232
|
+
if (status !== "all" && count === 0) return;
|
|
233
|
+
const btn = node("button", "filter-chip");
|
|
234
|
+
btn.type = "button";
|
|
235
|
+
btn.dataset.status = status;
|
|
236
|
+
if (status !== "all") btn.append(node("span", "filter-dot " + status));
|
|
237
|
+
btn.append(document.createTextNode(status === "all" ? t("status.all") : t("run." + status)));
|
|
238
|
+
btn.append(node("span", "filter-count", String(count)));
|
|
239
|
+
btn.addEventListener("click", function () { onChange(status); });
|
|
240
|
+
buttons.push({ status: status, element: btn });
|
|
241
|
+
row.append(btn);
|
|
242
|
+
});
|
|
243
|
+
return {
|
|
244
|
+
row: row,
|
|
245
|
+
sync: function (active) {
|
|
246
|
+
buttons.forEach(function (entry) {
|
|
247
|
+
entry.element.classList.toggle("is-active", entry.status === active);
|
|
248
|
+
});
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
export function renderTaskDetail(detail, data, t, locale, actions) {
|
|
254
|
+
actions = actions || {};
|
|
255
|
+
clear(detail);
|
|
256
|
+
const task = data.task;
|
|
257
|
+
const scaffold = node("div", "detail-scaffold");
|
|
258
|
+
|
|
259
|
+
// 1. Summary (anchor #detail-top)
|
|
260
|
+
const summaryBody = node("div", "section-body");
|
|
261
|
+
const headBlock = node("div", "detail-head");
|
|
262
|
+
headBlock.append(node("span", "detail-kicker", task.id));
|
|
263
|
+
headBlock.append(node("h2", "detail-title", task.title));
|
|
264
|
+
if (task.description) headBlock.append(node("p", "detail-description", task.description));
|
|
265
|
+
const meta = node("div", "detail-meta");
|
|
266
|
+
meta.append(metaItem(t("detail.updated"), formatDateTime(task.updatedAt, locale)));
|
|
267
|
+
meta.append(pill(t, "status", task.status));
|
|
268
|
+
if (task.priority) meta.append(pill(t, "priority", task.priority));
|
|
269
|
+
if (task.dueAt) meta.append(metaItem(t("detail.due"), formatDateTime(task.dueAt, locale)));
|
|
270
|
+
if (task.projectNames && task.projectNames.length) {
|
|
271
|
+
meta.append(metaItem(t("detail.project"), task.projectNames.join(", ")));
|
|
272
|
+
}
|
|
273
|
+
if (task.cwd) meta.append(pathMetaItem(t("detail.workspace"), task.cwd));
|
|
274
|
+
headBlock.append(meta);
|
|
275
|
+
summaryBody.append(headBlock);
|
|
276
|
+
|
|
277
|
+
if (task.completionSummary) {
|
|
278
|
+
const conclusion = node("div", "conclusion");
|
|
279
|
+
conclusion.append(node("h3", "", t("detail.conclusion")));
|
|
280
|
+
conclusion.append(richText(null, task.completionSummary, t));
|
|
281
|
+
conclusion.append(conclusionMeta(task, t, locale, "completed"));
|
|
282
|
+
summaryBody.append(conclusion);
|
|
283
|
+
} else if (task.status === "retired" || task.retirementSummary) {
|
|
284
|
+
const conclusion = node("div", "conclusion archived");
|
|
285
|
+
conclusion.append(node("h3", "", t("detail.retired")));
|
|
286
|
+
if (task.retirementSummary) conclusion.append(richText(null, task.retirementSummary, t));
|
|
287
|
+
if (task.replacementTaskId) {
|
|
288
|
+
conclusion.append(node("p", "muted", t("detail.replacement") + " · " + task.replacementTaskId));
|
|
289
|
+
}
|
|
290
|
+
conclusion.append(conclusionMeta(task, t, locale, "retired"));
|
|
291
|
+
summaryBody.append(conclusion);
|
|
292
|
+
} else if (task.status === "archived" || task.archiveSummary || task.archiveReason) {
|
|
293
|
+
const conclusion = node("div", "conclusion archived");
|
|
294
|
+
conclusion.append(node("h3", "", t("detail.archived")));
|
|
295
|
+
if (task.archiveSummary) conclusion.append(richText(null, task.archiveSummary, t));
|
|
296
|
+
if (task.archiveReason) conclusion.append(node("p", "muted", task.archiveReason));
|
|
297
|
+
conclusion.append(conclusionMeta(task, t, locale, "archived"));
|
|
298
|
+
summaryBody.append(conclusion);
|
|
299
|
+
}
|
|
300
|
+
scaffold.append(anchorSection("detail-top", sectionHead(t("tabs.summary")),
|
|
301
|
+
summaryBody));
|
|
302
|
+
|
|
303
|
+
// 2. Attention (anchor #detail-attention) — open input requests, promoted from old mid-list position
|
|
304
|
+
if (data.openInputs.length) {
|
|
305
|
+
const attentionBody = node("div", "section-body");
|
|
306
|
+
data.openInputs.forEach(function (input) {
|
|
307
|
+
attentionBody.append(inputCard(input, { single: true }, t, locale, actions));
|
|
308
|
+
});
|
|
309
|
+
scaffold.append(anchorSection("detail-attention",
|
|
310
|
+
sectionHead(t("detail.attention"), { count: data.openInputs.length }),
|
|
311
|
+
attentionBody));
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
const stalledRuns = data.runtimeHealth && data.runtimeHealth.needsAttentionRuns
|
|
315
|
+
? data.runtimeHealth.needsAttentionRuns
|
|
316
|
+
: [];
|
|
317
|
+
if (stalledRuns.length) {
|
|
318
|
+
const runtimeHealthBody = node("div", "section-body");
|
|
319
|
+
stalledRuns.forEach(function (run) {
|
|
320
|
+
const card = node("article", "record-card");
|
|
321
|
+
card.append(
|
|
322
|
+
node("strong", "", run.roleName + " · " + run.runId),
|
|
323
|
+
node("p", "record-copy", (run.kind || "execution-stalled") + " · " + (run.classification || "truly-stalled")),
|
|
324
|
+
node("small", "", t("detail.lastProgress") + " · " + formatDateTime(run.progressAt, locale))
|
|
325
|
+
);
|
|
326
|
+
runtimeHealthBody.append(card);
|
|
327
|
+
});
|
|
328
|
+
scaffold.append(anchorSection(
|
|
329
|
+
"detail-health",
|
|
330
|
+
sectionHead(t("detail.runtimeHealth"), { count: stalledRuns.length }),
|
|
331
|
+
runtimeHealthBody
|
|
332
|
+
));
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
// 3. Focus (anchor #detail-focus) — brief + technical approach
|
|
336
|
+
const focusBody = node("div", "section-body");
|
|
337
|
+
if (data.brief) {
|
|
338
|
+
const focusCard = node("article", "record-card");
|
|
339
|
+
focusCard.append(richText(t("detail.focus"), data.brief.currentFocus || data.brief.objective, t));
|
|
340
|
+
if (data.brief.technicalApproach) {
|
|
341
|
+
focusCard.append(richText(t("detail.technicalApproach"), data.brief.technicalApproach, t, { muted: true }));
|
|
342
|
+
}
|
|
343
|
+
if (data.brief.leaderSummary) focusCard.append(richText(t("detail.leaderSummary"), data.brief.leaderSummary, t, { muted: true }));
|
|
344
|
+
focusBody.append(focusCard);
|
|
345
|
+
} else {
|
|
346
|
+
focusBody.append(emptyRow(t, "empty.brief"));
|
|
347
|
+
}
|
|
348
|
+
scaffold.append(anchorSection("detail-focus", sectionHead(t("tabs.focus")), focusBody));
|
|
349
|
+
|
|
350
|
+
// 4. Work items (anchor #detail-work) — the operational deck
|
|
351
|
+
const workBody = node("div", "section-body");
|
|
352
|
+
if (!data.workItems.length) {
|
|
353
|
+
workBody.append(emptyRow(t));
|
|
354
|
+
} else {
|
|
355
|
+
const workItemTitles = {};
|
|
356
|
+
data.workItems.forEach(function (item) { workItemTitles[item.id] = item.title; });
|
|
357
|
+
data.workItems.slice().sort(byNewest).forEach(function (item) {
|
|
358
|
+
workBody.append(workItemCard(item, workItemTitles, t, locale, actions, task.id));
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
scaffold.append(anchorSection("detail-work",
|
|
362
|
+
sectionHead(t("detail.workItems"), { count: data.workItems.length }),
|
|
363
|
+
workBody));
|
|
364
|
+
|
|
365
|
+
// 5. Runs (anchor #detail-exec) — execution grid with status filter + paging
|
|
366
|
+
const execWrap = node("div", "section-body");
|
|
367
|
+
const execBody = node("div", "run-grid");
|
|
368
|
+
const sortedRuns = data.runs.slice().sort(byNewest);
|
|
369
|
+
if (!sortedRuns.length) {
|
|
370
|
+
execBody.append(emptyRow(t, "empty.runs"));
|
|
371
|
+
execWrap.append(execBody);
|
|
372
|
+
} else {
|
|
373
|
+
let activeRunFilter = "all";
|
|
374
|
+
const filter = runFilterRow(sortedRuns, t, function (status) {
|
|
375
|
+
activeRunFilter = status;
|
|
376
|
+
filter.sync(status);
|
|
377
|
+
renderRunGrid();
|
|
378
|
+
});
|
|
379
|
+
function renderRunGrid() {
|
|
380
|
+
clear(execBody);
|
|
381
|
+
const visible = activeRunFilter === "all"
|
|
382
|
+
? sortedRuns
|
|
383
|
+
: sortedRuns.filter(function (run) { return run.status === activeRunFilter; });
|
|
384
|
+
pagedList(execBody, visible, RUN_PAGE_SIZE, function (run) {
|
|
385
|
+
return runCard(run, t, locale);
|
|
386
|
+
}, t);
|
|
387
|
+
}
|
|
388
|
+
filter.sync(activeRunFilter);
|
|
389
|
+
execWrap.append(filter.row, execBody);
|
|
390
|
+
renderRunGrid();
|
|
391
|
+
}
|
|
392
|
+
scaffold.append(anchorSection("detail-exec",
|
|
393
|
+
sectionHead(t("detail.execution"), { count: data.runs.length }),
|
|
394
|
+
execWrap));
|
|
395
|
+
|
|
396
|
+
// 5b. Reviews (anchor #detail-reviews) — review rounds for yielded candidates
|
|
397
|
+
const reviewBody = node("div", "row-list");
|
|
398
|
+
const sortedRounds = (data.reviewRounds || []).slice().sort(byNewest);
|
|
399
|
+
if (!sortedRounds.length) {
|
|
400
|
+
reviewBody.append(emptyRow(t));
|
|
401
|
+
} else {
|
|
402
|
+
pagedList(reviewBody, sortedRounds, LIST_PAGE_SIZE, function (round) {
|
|
403
|
+
return reviewCard(round, t, locale);
|
|
404
|
+
}, t);
|
|
405
|
+
}
|
|
406
|
+
scaffold.append(anchorSection("detail-reviews",
|
|
407
|
+
sectionHead(t("detail.reviews"), { count: sortedRounds.length }),
|
|
408
|
+
reviewBody));
|
|
409
|
+
|
|
410
|
+
// 6. Roles (anchor #detail-roles)
|
|
411
|
+
const rolesBody = node("div", "row-list");
|
|
412
|
+
if (!data.roles.length) rolesBody.append(emptyRow(t));
|
|
413
|
+
data.roles.forEach(function (role) {
|
|
414
|
+
rolesBody.append(roleCard(role, task, t, locale, actions));
|
|
415
|
+
});
|
|
416
|
+
scaffold.append(anchorSection("detail-roles",
|
|
417
|
+
sectionHead(t("detail.roles"), { count: data.roles.length }),
|
|
418
|
+
rolesBody));
|
|
419
|
+
|
|
420
|
+
// 7. History (anchor #detail-history) — merged milestones + decisions, chronological
|
|
421
|
+
const historyEvents = [];
|
|
422
|
+
(data.milestones || []).forEach(function (m) {
|
|
423
|
+
historyEvents.push({ kind: "milestone", item: m });
|
|
424
|
+
});
|
|
425
|
+
(data.decisions || []).forEach(function (d) {
|
|
426
|
+
historyEvents.push({ kind: "decision", item: d });
|
|
427
|
+
});
|
|
428
|
+
historyEvents.sort(function (a, b) {
|
|
429
|
+
return Date.parse(b.item.createdAt || b.item.updatedAt) - Date.parse(a.item.createdAt || a.item.updatedAt);
|
|
430
|
+
});
|
|
431
|
+
const historyBody = node("div", "section-body");
|
|
432
|
+
if (!historyEvents.length) {
|
|
433
|
+
historyBody.append(emptyRow(t, "detail.historyEmpty"));
|
|
434
|
+
} else {
|
|
435
|
+
pagedList(historyBody, historyEvents, LIST_PAGE_SIZE, function (event) {
|
|
436
|
+
return historyEventRow(event, t, locale);
|
|
437
|
+
}, t);
|
|
438
|
+
}
|
|
439
|
+
scaffold.append(anchorSection("detail-history",
|
|
440
|
+
sectionHead(t("tabs.history"), { count: historyEvents.length }),
|
|
441
|
+
historyBody));
|
|
442
|
+
|
|
443
|
+
// 8. Messages (anchor #detail-messages)
|
|
444
|
+
const messagesBody = node("div", "row-list");
|
|
445
|
+
if (!data.messages.length) {
|
|
446
|
+
messagesBody.append(emptyRow(t));
|
|
447
|
+
} else {
|
|
448
|
+
pagedList(messagesBody, data.messages.slice().sort(byNewest), LIST_PAGE_SIZE, function (message) {
|
|
449
|
+
return messageCard(message, t, locale);
|
|
450
|
+
}, t);
|
|
451
|
+
}
|
|
452
|
+
scaffold.append(anchorSection("detail-messages",
|
|
453
|
+
sectionHead(t("detail.messages"), { count: data.messages.length }),
|
|
454
|
+
messagesBody));
|
|
455
|
+
|
|
456
|
+
detail.append(scaffold);
|
|
457
|
+
}
|
|
458
|
+
`;
|