@astrosheep/keiyaku 4.0.6 → 4.1.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/build/src/akuma/archetype.js +8 -1
- package/build/src/akuma/provider-recipe.d.ts +2 -0
- package/build/src/akuma/provider-recipe.js +10 -0
- package/build/src/akuma/providers/acp/config.d.ts +2 -0
- package/build/src/akuma/providers/acp/config.js +9 -1
- package/build/src/akuma/providers/acp/core.d.ts +5 -2
- package/build/src/akuma/providers/acp/core.js +29 -16
- package/build/src/akuma/providers/acp/index.js +8 -2
- package/build/src/akuma/providers/claude/index.d.ts +1 -1
- package/build/src/akuma/providers/claude/index.js +9 -5
- package/build/src/akuma/providers/codex-app-server/index.js +4 -1
- package/build/src/akuma/providers/grok-build/index.js +15 -1
- package/build/src/akuma/providers/index.js +1 -1
- package/build/src/akuma/providers/opencode-sdk/index.js +3 -0
- package/build/src/akuma/providers/pi/index.js +3 -1
- package/build/src/body/amend.js +0 -9
- package/build/src/body/region.js +2 -2
- package/build/src/cli/accepted.js +2 -0
- package/build/src/cli/commands/contract.d.ts +5 -3
- package/build/src/cli/commands/contract.js +23 -13
- package/build/src/cli/commands/task-invoke.d.ts +4 -1
- package/build/src/cli/commands/task-invoke.js +6 -3
- package/build/src/cli/commands/task.js +10 -2
- package/build/src/cli/parse.js +2 -2
- package/build/src/cli/render/akuma.js +42 -11
- package/build/src/cli/render/catalog.js +5 -1
- package/build/src/cli/render/contract.js +25 -3
- package/build/src/cli/render/kanshi.js +142 -94
- package/build/src/cli/render/receipt.d.ts +1 -0
- package/build/src/cli/render/receipt.js +18 -14
- package/build/src/cli/render/refusal.js +6 -0
- package/build/src/cli/render/task.js +63 -11
- package/build/src/cli/result.d.ts +8 -1
- package/build/src/contract-worktree.d.ts +0 -2
- package/build/src/contract-worktree.js +13 -24
- package/build/src/git/nuke.js +1 -6
- package/build/src/git/reconcile.d.ts +0 -2
- package/build/src/git/reconcile.js +2 -33
- package/build/src/git/tender.d.ts +13 -2
- package/build/src/git/tender.js +43 -4
- package/build/src/kanshi/read.js +32 -12
- package/build/src/kanshi/report.d.ts +2 -1
- package/build/src/library/continuation.d.ts +26 -0
- package/build/src/library/continuation.js +78 -0
- package/build/src/library/contract.d.ts +9 -5
- package/build/src/library/contract.js +27 -2
- package/build/src/library/delivery.d.ts +18 -13
- package/build/src/library/keiyaku.d.ts +1 -1
- package/build/src/library/mutation.js +11 -4
- package/build/src/protocol/deliver.d.ts +12 -2
- package/build/src/protocol/deliver.js +52 -19
- package/build/src/protocol/operations.d.ts +7 -2
- package/build/src/settlement/fence.d.ts +0 -2
- package/build/src/settlement/fence.js +0 -24
- package/build/src/task/board.d.ts +21 -2
- package/build/src/task/board.js +25 -12
- package/build/src/task/identity.js +14 -4
- package/build/src/task/index.d.ts +1 -0
- package/build/src/task/index.js +7 -1
- package/build/src/task/operations.d.ts +2 -1
- package/build/src/task/operations.js +15 -4
- package/build/src/task/query.js +2 -2
- package/build/src/task/store.d.ts +2 -0
- package/build/src/task/store.js +12 -83
- package/package.json +1 -1
|
@@ -14,14 +14,20 @@ function associatedIdentity(id, alias, contract) {
|
|
|
14
14
|
const contractId = contract === undefined ? undefined : associatedContractId(contract);
|
|
15
15
|
return `${identity(id, alias)}${contractId === undefined ? "" : ` [${contractId}]`}`;
|
|
16
16
|
}
|
|
17
|
-
function
|
|
17
|
+
function ruler(columns) {
|
|
18
|
+
return "─".repeat(Math.max(OPENING_STROKE.length, columns));
|
|
19
|
+
}
|
|
20
|
+
function snapshotHeading(id, alias, contract, columns) {
|
|
18
21
|
const contractId = contract === undefined ? undefined : associatedContractId(contract);
|
|
19
22
|
return [
|
|
20
|
-
OPENING_STROKE,
|
|
21
23
|
identity(id, alias),
|
|
24
|
+
ruler(columns),
|
|
22
25
|
...(contractId === undefined ? [] : [`└─ ${contractId}`]),
|
|
23
26
|
];
|
|
24
27
|
}
|
|
28
|
+
function answeredHeading(id, alias, columns) {
|
|
29
|
+
return [`✓ came back ${identity(id, alias)}`, ruler(columns)].join("\n");
|
|
30
|
+
}
|
|
25
31
|
function contractFacts(contract) {
|
|
26
32
|
return contract.kind === "failed" ? [`! contract failed ${safeText(contract.diagnostic)}`] : [];
|
|
27
33
|
}
|
|
@@ -32,7 +38,7 @@ function lifeLabel(life) {
|
|
|
32
38
|
if (life === "running")
|
|
33
39
|
return "● STILL RUNNING";
|
|
34
40
|
if (life === "asleep")
|
|
35
|
-
return "
|
|
41
|
+
return "✓ came back";
|
|
36
42
|
if (life === "killed")
|
|
37
43
|
return "× killed";
|
|
38
44
|
return `? ${life}`;
|
|
@@ -214,6 +220,19 @@ function renderTaskContextLines(created, columns) {
|
|
|
214
220
|
return [`! tasks failed ${safeText(created.diagnostic)}`];
|
|
215
221
|
return [`tasks ${created.rows.length}`, ...created.rows.flatMap((row) => renderTaskRow(row, columns))];
|
|
216
222
|
}
|
|
223
|
+
function answerContextLines(observation, columns) {
|
|
224
|
+
return [
|
|
225
|
+
...renderTaskContextLines(observation.createdTasks, columns),
|
|
226
|
+
...renderReportedChangeLines(observation.status.timeline),
|
|
227
|
+
];
|
|
228
|
+
}
|
|
229
|
+
function answeredBlock(observation, answer, alias, columns) {
|
|
230
|
+
const context = answerContextLines(observation, columns).join("\n");
|
|
231
|
+
if (context.length === 0)
|
|
232
|
+
return `${answeredHeading(observation.status.id, alias, columns)}\n${answer}`;
|
|
233
|
+
const separator = answer.endsWith("\n") ? "\n" : "\n\n";
|
|
234
|
+
return `${answeredHeading(observation.status.id, alias, columns)}\n${answer}${separator}${context}`;
|
|
235
|
+
}
|
|
217
236
|
function snapshotText(view, context, options = {}) {
|
|
218
237
|
const snapshot = view.status.timeline;
|
|
219
238
|
const activity = snapshot.kind === "idle" && snapshot.outcome !== undefined
|
|
@@ -227,13 +246,16 @@ function snapshotText(view, context, options = {}) {
|
|
|
227
246
|
...contractFacts(view.contract),
|
|
228
247
|
...(options.facts ?? []),
|
|
229
248
|
];
|
|
249
|
+
const taskContext = renderTaskContextLines(view.createdTasks, context.columns);
|
|
250
|
+
const changes = renderReportedChangeLines(snapshot);
|
|
230
251
|
const footer = options.showLife === false ? [] : ["", lifeLabel(view.status.life)];
|
|
231
252
|
return [
|
|
232
|
-
...snapshotHeading(view.status.id, options.alias, view.contract),
|
|
253
|
+
...snapshotHeading(view.status.id, options.alias, view.contract, context.columns),
|
|
233
254
|
...facts,
|
|
234
255
|
...activity,
|
|
235
|
-
...
|
|
236
|
-
...
|
|
256
|
+
...(activity.length > 0 && taskContext.length > 0 ? [""] : []),
|
|
257
|
+
...taskContext,
|
|
258
|
+
...changes,
|
|
237
259
|
...footer,
|
|
238
260
|
].join("\n");
|
|
239
261
|
}
|
|
@@ -244,18 +266,27 @@ function observationStageText(id, observation, context, options = {}) {
|
|
|
244
266
|
}
|
|
245
267
|
function waitText(result, context) {
|
|
246
268
|
const alias = result.alias;
|
|
269
|
+
const total = result.result.observations.length + result.result.unobserved.length;
|
|
270
|
+
const done = result.result.observations.filter((observation) => observation.status.life !== "running").length;
|
|
247
271
|
const blocks = [
|
|
248
|
-
...result.result.observations.map((observation) =>
|
|
272
|
+
...result.result.observations.map((observation) => {
|
|
273
|
+
const answer = statusAnswer(observation);
|
|
274
|
+
if (answer !== undefined)
|
|
275
|
+
return answeredBlock(observation, answer, alias, context.columns);
|
|
276
|
+
return snapshotText(observation, context, { ...(alias === undefined ? {} : { alias }) });
|
|
277
|
+
}),
|
|
249
278
|
...result.result.unobserved.map((member) => unobservedText(member.id, member.diagnostic)),
|
|
250
279
|
];
|
|
251
|
-
|
|
280
|
+
if (total <= 1)
|
|
281
|
+
return blocks.join("\n\n");
|
|
282
|
+
return [...blocks, `${done} of ${total} done`].join("\n\n");
|
|
252
283
|
}
|
|
253
284
|
function historyText(command, result, context) {
|
|
254
285
|
if (command.last)
|
|
255
286
|
return result.mode === "last" ? result.answer : "no answer retained";
|
|
256
287
|
if (result.mode !== "page")
|
|
257
288
|
throw new Error("history result lacks page");
|
|
258
|
-
return [...snapshotHeading(result.akuma, result.alias, result.historyResult.contract), ...groupedRows(result.history.rows, context, true)].join("\n");
|
|
289
|
+
return [...snapshotHeading(result.akuma, result.alias, result.historyResult.contract, context.columns), ...groupedRows(result.history.rows, context, true)].join("\n");
|
|
259
290
|
}
|
|
260
291
|
function tellText(result, context) {
|
|
261
292
|
const facts = typeof result.result.tell.wake === "string" ? [] : [`! error ${safeText(result.result.tell.wake.diagnostic)}`];
|
|
@@ -317,7 +348,7 @@ function callText(result, context) {
|
|
|
317
348
|
if (result.result.observation.kind === "detached") {
|
|
318
349
|
const lines = [...snapshotHeading(result.result.akuma, alias, contractId === undefined
|
|
319
350
|
? { kind: "none" }
|
|
320
|
-
: { kind: "associated", contractId }), ...cwd, ...restraint, ...facts];
|
|
351
|
+
: { kind: "associated", contractId }, context.columns), ...cwd, ...restraint, ...facts];
|
|
321
352
|
if (!callFailed(result.result)) {
|
|
322
353
|
const selector = result.result.alias.kind === "aliased" ? result.result.alias.alias.alias : result.result.akuma;
|
|
323
354
|
lines.push(`$ keiyaku -C ${posixShellArgument(result.world)} wait ${selector} --timeout 5m`);
|
|
@@ -327,7 +358,7 @@ function callText(result, context) {
|
|
|
327
358
|
if (result.result.observation.kind === "failed") {
|
|
328
359
|
return [...snapshotHeading(result.result.akuma, alias, contractId === undefined
|
|
329
360
|
? { kind: "none" }
|
|
330
|
-
: { kind: "associated", contractId }), ...cwd, ...restraint, ...facts, `! error ${safeText(result.result.observation.failure.diagnostic)}`].join("\n");
|
|
361
|
+
: { kind: "associated", contractId }, context.columns), ...cwd, ...restraint, ...facts, `! error ${safeText(result.result.observation.failure.diagnostic)}`].join("\n");
|
|
331
362
|
}
|
|
332
363
|
return snapshotText({
|
|
333
364
|
status: result.result.observation.status,
|
|
@@ -12,5 +12,9 @@ export function renderCatalogText(catalog) {
|
|
|
12
12
|
...(row.description === undefined ? [] : [` ${safeText(row.description)}`]),
|
|
13
13
|
]).join("\n");
|
|
14
14
|
}
|
|
15
|
-
return catalog.rows.map((row) =>
|
|
15
|
+
return catalog.rows.map((row) => {
|
|
16
|
+
if (!("lifeAt" in row))
|
|
17
|
+
return `${safeText(row.id)} - ${row.life}`;
|
|
18
|
+
return `${safeText(row.id)} - ${row.life} - runtime ${row.lifeAt ?? "-"} - last activity ${row.lastActivityAt ?? "-"}`;
|
|
19
|
+
}).join("\n");
|
|
16
20
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { renderAcceptedAudit } from "./audit.js";
|
|
2
|
-
import { appendHookPayload, cleanupLines, hookFailureSummary, leakLines, outcomeLines, receiptPayload, receiptRow, reuseLines, stopLines, titleLines, } from "./receipt.js";
|
|
2
|
+
import { appendHookPayload, cleanupLines, hookFailureSummary, leakLines, outcomeLines, receiptPayload, receiptRow, reuseLines, stopLines, stopSummary, titleLines, } from "./receipt.js";
|
|
3
3
|
import { gitShortStat, renderOpaqueBlock } from "./terminal.js";
|
|
4
4
|
const HANG = " ";
|
|
5
5
|
function isRecord(value) {
|
|
@@ -217,6 +217,28 @@ function movementLines(result, columns) {
|
|
|
217
217
|
receiptRow(lines, "~", "target", [{ text: `moved · re-integrated x${count}` }], columns);
|
|
218
218
|
return lines;
|
|
219
219
|
}
|
|
220
|
+
function continuationLines(result, columns) {
|
|
221
|
+
const report = result.continuation;
|
|
222
|
+
if (report === undefined)
|
|
223
|
+
return [];
|
|
224
|
+
const lines = [];
|
|
225
|
+
for (const contractId of report.claimed) {
|
|
226
|
+
receiptRow(lines, "✓", "continuation", [
|
|
227
|
+
{ text: "complete" },
|
|
228
|
+
{ text: contractId, opaque: true },
|
|
229
|
+
], columns);
|
|
230
|
+
}
|
|
231
|
+
for (const { contractId, stop } of report.stopped) {
|
|
232
|
+
const detail = "kind" in stop ? [{ text: stop.kind }] : stopSummary(stop, contractId);
|
|
233
|
+
receiptRow(lines, "!", "continuation", [
|
|
234
|
+
{ text: "blocked" },
|
|
235
|
+
{ text: contractId, opaque: true },
|
|
236
|
+
{ text: "·" },
|
|
237
|
+
...detail,
|
|
238
|
+
], columns);
|
|
239
|
+
}
|
|
240
|
+
return lines;
|
|
241
|
+
}
|
|
220
242
|
function renderAcceptedBind(result, columns) {
|
|
221
243
|
const lines = titleLines("✓", "bound", result.contract, columns);
|
|
222
244
|
receiptRow(lines, " ", "workspace", [
|
|
@@ -239,7 +261,7 @@ function renderAcceptedDeliver(result, columns) {
|
|
|
239
261
|
const complete = result.completion !== undefined;
|
|
240
262
|
const title = complete ? "delivered" : "deliver — not complete";
|
|
241
263
|
const lines = titleLines("✓", title, result.contract, columns);
|
|
242
|
-
lines.push(...movementLines(result, columns), ...completionLines(result, columns));
|
|
264
|
+
lines.push(...movementLines(result, columns), ...completionLines(result, columns), ...continuationLines(result, columns));
|
|
243
265
|
if (!complete)
|
|
244
266
|
receiptRow(lines, " ", "candidate", [{ text: "kept" }], columns);
|
|
245
267
|
if (result.verification !== undefined) {
|
|
@@ -258,7 +280,7 @@ function renderAcceptedDeliver(result, columns) {
|
|
|
258
280
|
function renderAcceptedReview(result, columns) {
|
|
259
281
|
const complete = result.completion !== undefined;
|
|
260
282
|
const lines = titleLines("✓", `review ${result.verdict} — ${complete ? "complete" : "not complete"}`, result.contract, columns);
|
|
261
|
-
lines.push(...movementLines(result, columns), ...completionLines(result, columns));
|
|
283
|
+
lines.push(...movementLines(result, columns), ...completionLines(result, columns), ...continuationLines(result, columns));
|
|
262
284
|
if (!complete)
|
|
263
285
|
receiptRow(lines, " ", "candidate", [{ text: "kept" }], columns);
|
|
264
286
|
if (result.placement !== undefined) {
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { displayColumns, renderTextBlock, safeText, tone } from "./terminal.js";
|
|
2
|
-
const RULE = "─";
|
|
3
|
-
const HORIZON = "───";
|
|
4
2
|
const PLUMB = " │ ";
|
|
5
3
|
const PATH_PREFIX = `${PLUMB}↳ `;
|
|
4
|
+
const MAX_VISIBLE_ROWS = 10;
|
|
5
|
+
const FIELD_WIDTH = 8;
|
|
6
6
|
function contractMark(row) {
|
|
7
7
|
if (row.phase === "claimed")
|
|
8
8
|
return "✓";
|
|
@@ -46,6 +46,13 @@ function gateMark(report) {
|
|
|
46
46
|
return "?";
|
|
47
47
|
return report.current.verdict === "satisfied" ? "✓" : "!";
|
|
48
48
|
}
|
|
49
|
+
function worldGateMark(report) {
|
|
50
|
+
if (report.current.kind === "stale")
|
|
51
|
+
return "~";
|
|
52
|
+
if (report.current.kind !== "attested")
|
|
53
|
+
return " ";
|
|
54
|
+
return report.current.verdict === "satisfied" ? "✓" : "✗";
|
|
55
|
+
}
|
|
49
56
|
function formatAge(source, observedAt) {
|
|
50
57
|
if (source === null || source === undefined)
|
|
51
58
|
return "—";
|
|
@@ -76,11 +83,11 @@ function targetFacts(row) {
|
|
|
76
83
|
return ["no target"];
|
|
77
84
|
const facts = [`target ${name}`];
|
|
78
85
|
if (row.targetLag.kind === "unknown")
|
|
79
|
-
facts.push(
|
|
86
|
+
facts.push(`commits behind ${name} unknown`);
|
|
80
87
|
if (row.targetLag.kind === "counted")
|
|
81
|
-
facts.push(
|
|
88
|
+
facts.push(`${row.targetLag.behind} commits behind ${name}`);
|
|
82
89
|
if (row.targetObservation?.drift === true)
|
|
83
|
-
facts.push("
|
|
90
|
+
facts.push("target moved");
|
|
84
91
|
return facts;
|
|
85
92
|
}
|
|
86
93
|
function workspaceState(row) {
|
|
@@ -140,10 +147,24 @@ function akumaHot(row) {
|
|
|
140
147
|
return true;
|
|
141
148
|
return row.contract?.observed === "missing" || row.contract?.observed === "unavailable";
|
|
142
149
|
}
|
|
143
|
-
function
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
150
|
+
function activitySnapshotLine(row) {
|
|
151
|
+
const snapshot = row.snapshot;
|
|
152
|
+
if (snapshot === undefined)
|
|
153
|
+
return undefined;
|
|
154
|
+
const entries = snapshot.entries.flatMap((entry) => entry.kind === "row" ? [entry.row] : []);
|
|
155
|
+
const latest = entries.at(-1);
|
|
156
|
+
if (latest === undefined) {
|
|
157
|
+
if (snapshot.kind === "idle" && snapshot.outcome !== undefined) {
|
|
158
|
+
return snapshot.outcome.outcome.kind === "answered" ? snapshot.outcome.outcome.answer : snapshot.outcome.outcome.diagnostic;
|
|
159
|
+
}
|
|
160
|
+
return "empty";
|
|
161
|
+
}
|
|
162
|
+
if (latest.kind === "said" || latest.kind === "thought" || latest.kind === "note" || latest.kind === "call" || latest.kind === "tell") {
|
|
163
|
+
return latest.text;
|
|
164
|
+
}
|
|
165
|
+
if (latest.kind === "tool")
|
|
166
|
+
return latest.name;
|
|
167
|
+
return "activity";
|
|
147
168
|
}
|
|
148
169
|
function plumbFacts(facts, columns) {
|
|
149
170
|
const clean = facts.map(safeText).filter((fact) => fact.length > 0);
|
|
@@ -185,11 +206,28 @@ function renderGates(reports, observedAt, columns, includeSummaries) {
|
|
|
185
206
|
}
|
|
186
207
|
return lines;
|
|
187
208
|
}
|
|
209
|
+
function fieldPrefix(label) {
|
|
210
|
+
return ` ${label.padEnd(FIELD_WIDTH, " ")}`;
|
|
211
|
+
}
|
|
212
|
+
function fieldLine(label, value) {
|
|
213
|
+
return `${fieldPrefix(label)}${safeText(value)}`;
|
|
214
|
+
}
|
|
215
|
+
function fieldBlock(label, value, columns) {
|
|
216
|
+
const continuation = " ".repeat(fieldPrefix(label).length);
|
|
217
|
+
return renderTextBlock(value, continuation, columns).map((line, index) => index === 0
|
|
218
|
+
? `${fieldPrefix(label)}${line.slice(continuation.length)}`
|
|
219
|
+
: line);
|
|
220
|
+
}
|
|
221
|
+
function renderWorldGates(reports) {
|
|
222
|
+
if (reports.length === 0)
|
|
223
|
+
return [];
|
|
224
|
+
return [fieldLine("GATES", reports.map((report) => `[${worldGateMark(report)}] ${report.gate}`).join(" "))];
|
|
225
|
+
}
|
|
188
226
|
function failure(name, section, context) {
|
|
189
|
-
return [
|
|
227
|
+
return [`[ ${name} ]`, "", `${tone(`! ${safeText(section.failure.message)}`, "alert", context.color)}`];
|
|
190
228
|
}
|
|
191
|
-
function sectionAbsent(name,
|
|
192
|
-
return [
|
|
229
|
+
function sectionAbsent(name, _columns) {
|
|
230
|
+
return [`[ ${name} ]`, "", `${PLUMB}${name.toLowerCase()} absent`];
|
|
193
231
|
}
|
|
194
232
|
function endpointFact(id, observed) {
|
|
195
233
|
if (observed === "missing")
|
|
@@ -249,22 +287,69 @@ function renderContractRow(row, report, context, selection) {
|
|
|
249
287
|
lines.push(...renderNamespaceTasks(row, context));
|
|
250
288
|
return lines;
|
|
251
289
|
}
|
|
290
|
+
function isTerminalContract(row) {
|
|
291
|
+
return row.phase === "claimed" || row.phase === "abandoned";
|
|
292
|
+
}
|
|
293
|
+
function isTerminalTask(row) {
|
|
294
|
+
return row.disposition === "done" || row.disposition === "drop";
|
|
295
|
+
}
|
|
296
|
+
function renderWorldContractRow(row, report, context) {
|
|
297
|
+
const hot = contractHot(row);
|
|
298
|
+
const title = row.title ?? "title unavailable";
|
|
299
|
+
const phase = `${row.phase} · ${formatAge(row.phaseAt, report.observedAt)}`;
|
|
300
|
+
if (!hot)
|
|
301
|
+
return [identityLine(contractMark(row), row.id, `· "${title}" · ${phase} · ${targetFacts(row).join(" · ")}`)];
|
|
302
|
+
const lines = [identityLine(contractMark(row), row.id)];
|
|
303
|
+
lines.push(...fieldBlock("TITLE", title, context.columns));
|
|
304
|
+
lines.push(fieldLine("STATE", phase));
|
|
305
|
+
lines.push(fieldLine("GIT", [...targetFacts(row), workspaceState(row)].join(" · ")));
|
|
306
|
+
if (showWorkspacePath(row, hot)
|
|
307
|
+
&& row.workspaceObservation.kind !== "unappointed"
|
|
308
|
+
&& row.workspaceObservation.kind !== "failed"
|
|
309
|
+
&& row.workspaceObservation.location.kind === "worktree") {
|
|
310
|
+
lines.push(fieldLine("DIR", row.workspaceObservation.location.path));
|
|
311
|
+
}
|
|
312
|
+
lines.push(...renderWorldGates(row.gates.reports));
|
|
313
|
+
const linked = [];
|
|
314
|
+
if (row.holder.kind === "held")
|
|
315
|
+
linked.push(row.holder.taskId);
|
|
316
|
+
if (row.holder.kind === "unavailable")
|
|
317
|
+
linked.push("holder unavailable");
|
|
318
|
+
for (const attached of row.fleet) {
|
|
319
|
+
const aliases = attached.aliases.length === 0 ? "" : ` (${attached.aliases.join(" ")})`;
|
|
320
|
+
linked.push(`${attached.id}${aliases}`);
|
|
321
|
+
}
|
|
322
|
+
if (linked.length > 0) {
|
|
323
|
+
lines.push(fieldLine("LINKED", linked[0]));
|
|
324
|
+
for (const item of linked.slice(1))
|
|
325
|
+
lines.push(` ${" ".repeat(FIELD_WIDTH)}${safeText(item)}`);
|
|
326
|
+
}
|
|
327
|
+
return lines;
|
|
328
|
+
}
|
|
329
|
+
function selectRows(rows, isHot) {
|
|
330
|
+
return [...rows.filter(isHot), ...rows.filter((row) => !isHot(row))].slice(0, MAX_VISIBLE_ROWS);
|
|
331
|
+
}
|
|
332
|
+
function sectionFooter(visible, total, unit, selector) {
|
|
333
|
+
if (visible === total)
|
|
334
|
+
return [` (all ${total} live ${unit} shown)`];
|
|
335
|
+
return [` + ${total - visible} more live ${unit} not shown`, ` keiyaku ls ${selector}/`];
|
|
336
|
+
}
|
|
337
|
+
function sectionBlock(name, unit, selector, rows, total) {
|
|
338
|
+
const lines = [`[ ${name} ] ${total} live`, ""];
|
|
339
|
+
for (const row of rows)
|
|
340
|
+
lines.push(...row);
|
|
341
|
+
lines.push("", ...sectionFooter(rows.length, total, unit, selector));
|
|
342
|
+
return lines;
|
|
343
|
+
}
|
|
252
344
|
function renderContracts(report, context) {
|
|
253
345
|
const section = report.contracts;
|
|
254
346
|
if (section.kind === "absent")
|
|
255
347
|
return sectionAbsent("KEIYAKU", context.columns);
|
|
256
348
|
if (section.kind === "failed")
|
|
257
349
|
return failure("KEIYAKU", section, context);
|
|
258
|
-
const
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
];
|
|
262
|
-
const lines = [aperture("KEIYAKU", context.columns)];
|
|
263
|
-
for (const row of rows)
|
|
264
|
-
lines.push(...renderContractRow(row, report, context, "world"));
|
|
265
|
-
const attention = rows.filter(contractHot).length;
|
|
266
|
-
lines.push(aperture(`${rows.length} keiyaku · ${attention} attention`, context.columns));
|
|
267
|
-
return lines;
|
|
350
|
+
const live = section.value.rows.filter((row) => !isTerminalContract(row));
|
|
351
|
+
const rows = selectRows(live, contractHot);
|
|
352
|
+
return sectionBlock("KEIYAKU", "keiyaku", "kei", rows.map((row) => renderWorldContractRow(row, report, context)), live.length);
|
|
268
353
|
}
|
|
269
354
|
function renderSelectedContract(report, context) {
|
|
270
355
|
const section = report.contracts;
|
|
@@ -281,32 +366,24 @@ function renderTasks(report, context) {
|
|
|
281
366
|
return sectionAbsent("TASK", context.columns);
|
|
282
367
|
if (section.kind === "failed")
|
|
283
368
|
return failure("TASK", section, context);
|
|
284
|
-
const
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
];
|
|
288
|
-
const lines = [aperture("TASK", context.columns)];
|
|
289
|
-
for (const row of rows) {
|
|
369
|
+
const live = section.value.rows.filter((row) => !isTerminalTask(row));
|
|
370
|
+
const rows = selectRows(live, taskHot);
|
|
371
|
+
const rowLines = rows.map((row) => {
|
|
290
372
|
const compact = !taskHot(row);
|
|
291
|
-
lines.push(identityLine(taskMark(row), row.id));
|
|
292
373
|
const relation = row.contract === undefined
|
|
293
374
|
? ["unbound"]
|
|
294
375
|
: [endpointFact(row.contract.id, row.contract.observed)];
|
|
295
|
-
const facts = [row.title, row.disposition, `P${row.priority}`, ...relation];
|
|
296
376
|
if (compact)
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
const attention = rows.filter(taskHot).length;
|
|
308
|
-
lines.push(aperture(`${rows.length} task · ${attention} attention`, context.columns));
|
|
309
|
-
return lines;
|
|
377
|
+
return [identityLine(taskMark(row), row.id, `· "${row.title}" · ${row.disposition} · P${row.priority} · ${relation.join(" · ")}`)];
|
|
378
|
+
const lines = [identityLine(taskMark(row), row.id)];
|
|
379
|
+
lines.push(...fieldBlock("TITLE", row.title, context.columns));
|
|
380
|
+
lines.push(fieldLine("STATE", `${row.disposition} · P${row.priority}`));
|
|
381
|
+
for (const blocker of row.blockers ?? [])
|
|
382
|
+
lines.push(fieldLine("BLOCKED", blocker.id));
|
|
383
|
+
lines.push(fieldLine("LINKED", relation.join(" · ")));
|
|
384
|
+
return lines;
|
|
385
|
+
});
|
|
386
|
+
return sectionBlock("TASK", "task", "task", rowLines, live.length);
|
|
310
387
|
}
|
|
311
388
|
function akumaLabel(row) {
|
|
312
389
|
const aliases = row.aliases ?? [];
|
|
@@ -318,13 +395,15 @@ function renderAkuma(report, context) {
|
|
|
318
395
|
return sectionAbsent("FLEET", context.columns);
|
|
319
396
|
if (section.kind === "failed")
|
|
320
397
|
return failure("FLEET", section, context);
|
|
321
|
-
const
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
398
|
+
const live = section.value.rows
|
|
399
|
+
.filter((row) => row.life === "running")
|
|
400
|
+
.sort((left, right) => {
|
|
401
|
+
const l = "lastActivityAt" in left && left.lastActivityAt !== null ? Date.parse(left.lastActivityAt) : -Infinity;
|
|
402
|
+
const r = "lastActivityAt" in right && right.lastActivityAt !== null ? Date.parse(right.lastActivityAt) : -Infinity;
|
|
403
|
+
return r - l;
|
|
404
|
+
});
|
|
405
|
+
const rows = live.slice(0, MAX_VISIBLE_ROWS);
|
|
406
|
+
const rowLines = rows.map((row) => {
|
|
328
407
|
const lifeAt = "lifeAt" in row ? row.lifeAt : null;
|
|
329
408
|
const life = `${row.life} · ${formatAge(lifeAt, report.observedAt)}`;
|
|
330
409
|
const activity = "lastActivityAt" in row && row.lastActivityAt !== null
|
|
@@ -336,57 +415,26 @@ function renderAkuma(report, context) {
|
|
|
336
415
|
const relation = row.contract === undefined
|
|
337
416
|
? ["unbound"]
|
|
338
417
|
: [endpointFact(row.contract.id, row.contract.observed)];
|
|
418
|
+
const snapshot = activitySnapshotLine(row);
|
|
339
419
|
if (!akumaHot(row))
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
return
|
|
349
|
-
}
|
|
350
|
-
function sectionCount(name, section) {
|
|
351
|
-
if (section.kind === "present")
|
|
352
|
-
return `${section.value.rows.length} ${name}`;
|
|
353
|
-
return `${section.kind} ${name}`;
|
|
354
|
-
}
|
|
355
|
-
function worldCoordinate(report) {
|
|
356
|
-
const project = report.root;
|
|
357
|
-
const branch = report.branch?.startsWith("refs/heads/") === true
|
|
358
|
-
? report.branch.slice("refs/heads/".length)
|
|
359
|
-
: report.branch;
|
|
360
|
-
const state = report.contracts.kind === "present" ? report.contracts.value.state ?? null : null;
|
|
361
|
-
return [project, branch, state].filter((part) => part !== null && part.length > 0).map(safeText).join(" ");
|
|
362
|
-
}
|
|
363
|
-
function splitHorizon(report, columns) {
|
|
364
|
-
if (report.root === null)
|
|
365
|
-
return ["✗ keiyaku world absent"];
|
|
366
|
-
const aggregate = [
|
|
367
|
-
sectionCount("keiyaku", report.contracts),
|
|
368
|
-
sectionCount("akuma", report.akuma),
|
|
369
|
-
sectionCount("task", report.tasks),
|
|
370
|
-
].join(" · ");
|
|
371
|
-
const world = worldCoordinate(report);
|
|
372
|
-
const minimum = `kanshi ${HORIZON} ${aggregate} ${HORIZON} ${world}`;
|
|
373
|
-
if (displayColumns(minimum) > columns)
|
|
374
|
-
return [minimum];
|
|
375
|
-
const fill = columns - displayColumns(minimum);
|
|
376
|
-
const left = Math.floor(fill / 2);
|
|
377
|
-
const right = Math.ceil(fill / 2);
|
|
378
|
-
return [`kanshi ${HORIZON}${RULE.repeat(left)} ${aggregate} ${HORIZON}${RULE.repeat(right)} ${world}`];
|
|
420
|
+
return [identityLine(akumaMark(row.life), row.id, `${akumaLabel(row)} · ${[...key, ...relation, ...(snapshot === undefined ? [] : [snapshot])].join(" · ")}`.trim())];
|
|
421
|
+
const lines = [identityLine(akumaMark(row.life), row.id, akumaLabel(row))];
|
|
422
|
+
lines.push(fieldLine("LIFE", key.join(" · ")));
|
|
423
|
+
if (snapshot !== undefined)
|
|
424
|
+
lines.push(fieldLine("SNAPSHOT", snapshot));
|
|
425
|
+
lines.push(fieldLine("LINKED", relation.join(" · ")));
|
|
426
|
+
return lines;
|
|
427
|
+
});
|
|
428
|
+
return sectionBlock("FLEET", "akuma", "aku", rowLines, live.length);
|
|
379
429
|
}
|
|
380
430
|
export function renderKanshiText(report, context = { columns: 80, color: false }, selection = "world") {
|
|
381
431
|
if (selection === "contract")
|
|
382
432
|
return renderSelectedContract(report, context).join("\n");
|
|
383
433
|
return [
|
|
384
|
-
...splitHorizon(report, context.columns),
|
|
385
|
-
"",
|
|
386
434
|
...renderContracts(report, context),
|
|
387
|
-
"",
|
|
388
|
-
...renderTasks(report, context),
|
|
389
|
-
"",
|
|
435
|
+
"", "",
|
|
390
436
|
...renderAkuma(report, context),
|
|
437
|
+
"", "",
|
|
438
|
+
...renderTasks(report, context),
|
|
391
439
|
].join("\n");
|
|
392
440
|
}
|
|
@@ -15,6 +15,7 @@ export declare function hookFailureSummary(failure: HookFailure): string;
|
|
|
15
15
|
export declare function appendHookPayload(lines: string[], failure: HookFailure): void;
|
|
16
16
|
export declare function reuseLines(reuse: VerificationReuse | undefined, columns: number): readonly string[];
|
|
17
17
|
export declare function stopLines(label: "verification" | "completion", stop: VerificationStop | PlacementStop, columns: number, addressed: string): readonly string[];
|
|
18
|
+
export declare function stopSummary(stop: VerificationStop | PlacementStop, addressed: string): readonly ReceiptSegment[];
|
|
18
19
|
export declare function cleanupLines(cleanup: NonNullable<AcceptedDeliverResult["cleanup"]>, columns: number): readonly string[];
|
|
19
20
|
export declare function leakLines(leak: NonNullable<AcceptedDeliverResult["leak"]>, columns: number): readonly string[];
|
|
20
21
|
export {};
|
|
@@ -86,6 +86,23 @@ function targetMovedDetail(stop) {
|
|
|
86
86
|
];
|
|
87
87
|
}
|
|
88
88
|
export function stopLines(label, stop, columns, addressed) {
|
|
89
|
+
const detail = stopSummary(stop, addressed);
|
|
90
|
+
const lines = [];
|
|
91
|
+
const state = label === "verification" ? "failed" : "blocked";
|
|
92
|
+
receiptRow(lines, "!", label, [{ text: state }, { text: "·" }, ...detail], columns);
|
|
93
|
+
lines.push(...prerequisiteRows(stop, columns));
|
|
94
|
+
if ("failure" in stop && stop.failure === "environment-failure" && "command" in stop) {
|
|
95
|
+
appendHookPayload(lines, stop.detail);
|
|
96
|
+
}
|
|
97
|
+
if ("retry" in stop && stop.retry?.kind === "publication-failed") {
|
|
98
|
+
receiptPayload(lines, "diagnostic", stop.retry.diagnostic);
|
|
99
|
+
}
|
|
100
|
+
else if ("failure" in stop && "diagnostic" in stop) {
|
|
101
|
+
receiptPayload(lines, "diagnostic", stop.diagnostic);
|
|
102
|
+
}
|
|
103
|
+
return lines;
|
|
104
|
+
}
|
|
105
|
+
export function stopSummary(stop, addressed) {
|
|
89
106
|
const detail = [];
|
|
90
107
|
if ("refusal" in stop && stop.refusal !== undefined) {
|
|
91
108
|
detail.push({ text: stop.refusal.kind });
|
|
@@ -105,20 +122,7 @@ export function stopLines(label, stop, columns, addressed) {
|
|
|
105
122
|
detail.push(...targetMovedDetail(stop));
|
|
106
123
|
}
|
|
107
124
|
}
|
|
108
|
-
|
|
109
|
-
const state = label === "verification" ? "failed" : "blocked";
|
|
110
|
-
receiptRow(lines, "!", label, [{ text: state }, { text: "·" }, ...detail], columns);
|
|
111
|
-
lines.push(...prerequisiteRows(stop, columns));
|
|
112
|
-
if ("failure" in stop && stop.failure === "environment-failure" && "command" in stop) {
|
|
113
|
-
appendHookPayload(lines, stop.detail);
|
|
114
|
-
}
|
|
115
|
-
if ("retry" in stop && stop.retry?.kind === "publication-failed") {
|
|
116
|
-
receiptPayload(lines, "diagnostic", stop.retry.diagnostic);
|
|
117
|
-
}
|
|
118
|
-
else if ("failure" in stop && "diagnostic" in stop) {
|
|
119
|
-
receiptPayload(lines, "diagnostic", stop.diagnostic);
|
|
120
|
-
}
|
|
121
|
-
return lines;
|
|
125
|
+
return detail;
|
|
122
126
|
}
|
|
123
127
|
export function cleanupLines(cleanup, columns) {
|
|
124
128
|
const lines = [];
|
|
@@ -52,6 +52,12 @@ export function renderRefusalFacts(refusal, indent, columns, addressed) {
|
|
|
52
52
|
}
|
|
53
53
|
if (refusal.kind === "dirty-workspace")
|
|
54
54
|
return renderDirtyRefusal(refusal, indent, columns, identity);
|
|
55
|
+
if (refusal.kind === "unmerged-paths") {
|
|
56
|
+
return [
|
|
57
|
+
...renderOpaqueBlock(refusalHead(refusal.kind, identity, []), indent, columns),
|
|
58
|
+
...collectionLines("paths", refusal.paths, indent, columns),
|
|
59
|
+
];
|
|
60
|
+
}
|
|
55
61
|
if (refusal.kind === "integration-failed") {
|
|
56
62
|
const lines = [
|
|
57
63
|
...renderOpaqueBlock(refusalHead(refusal.kind, identity, [`reason=${refusal.reason}`, `targetHead=${refusal.targetHead}`]), indent, columns),
|