@astrosheep/keiyaku 4.6.14 → 4.6.16

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 (56) hide show
  1. package/build/plugins/claude/skills/keiyaku/SKILL.md +1 -1
  2. package/build/plugins/claude/skills/keiyaku-akuma/SKILL.md +13 -8
  3. package/build/plugins/codex/skills/keiyaku/SKILL.md +1 -1
  4. package/build/plugins/codex/skills/keiyaku-akuma/SKILL.md +13 -8
  5. package/build/plugins/opencode/skills/keiyaku/SKILL.md +1 -1
  6. package/build/plugins/opencode/skills/keiyaku-akuma/SKILL.md +13 -8
  7. package/build/skills/keiyaku/SKILL.md +1 -1
  8. package/build/skills/keiyaku-akuma/SKILL.md +13 -8
  9. package/build/src/akuma/archetype.js +2 -2
  10. package/build/src/cli/commands/akuma-grammar.js +18 -14
  11. package/build/src/cli/commands/akuma.d.ts +2 -1
  12. package/build/src/cli/commands/akuma.js +24 -25
  13. package/build/src/cli/commands/contract-help.d.ts +11 -11
  14. package/build/src/cli/commands/contract-help.js +16 -16
  15. package/build/src/cli/commands/contract.d.ts +3 -2
  16. package/build/src/cli/commands/contract.js +45 -65
  17. package/build/src/cli/commands/debt-grammar.js +8 -15
  18. package/build/src/cli/commands/debt.d.ts +2 -1
  19. package/build/src/cli/commands/debt.js +22 -22
  20. package/build/src/cli/commands/install.d.ts +2 -1
  21. package/build/src/cli/commands/install.js +4 -3
  22. package/build/src/cli/commands/whoami.d.ts +46 -0
  23. package/build/src/cli/commands/whoami.js +127 -0
  24. package/build/src/cli/main.js +5 -4
  25. package/build/src/cli/parse.d.ts +7 -1
  26. package/build/src/cli/parse.js +23 -3
  27. package/build/src/cli/render/akuma-activity.d.ts +10 -23
  28. package/build/src/cli/render/akuma-activity.js +178 -212
  29. package/build/src/cli/render/akuma.d.ts +7 -6
  30. package/build/src/cli/render/akuma.js +22 -18
  31. package/build/src/cli/render/catalog.js +4 -3
  32. package/build/src/cli/render/contract-history.js +5 -16
  33. package/build/src/cli/render/debt.d.ts +2 -1
  34. package/build/src/cli/render/debt.js +6 -6
  35. package/build/src/cli/render/nuke.d.ts +2 -1
  36. package/build/src/cli/render/nuke.js +4 -2
  37. package/build/src/cli/render/receipt.js +8 -3
  38. package/build/src/cli/render/reconcile.js +8 -7
  39. package/build/src/cli/render/refusal.js +7 -3
  40. package/build/src/cli/render/terminal.d.ts +5 -2
  41. package/build/src/cli/render/terminal.js +74 -12
  42. package/build/src/cli/render/whoami.d.ts +2 -0
  43. package/build/src/cli/render/whoami.js +56 -0
  44. package/build/src/cli/runtime.d.ts +2 -1
  45. package/build/src/cli/runtime.js +59 -58
  46. package/build/src/cli/streams.d.ts +11 -1
  47. package/build/src/cli/streams.js +6 -2
  48. package/build/src/cli/usage.d.ts +1 -0
  49. package/build/src/cli/usage.js +7 -6
  50. package/build/src/contract-worktree.js +1 -1
  51. package/build/src/library/selection.d.ts +1 -0
  52. package/build/src/library/selection.js +1 -1
  53. package/build/src/runtime/proc/windows-launch.exe +0 -0
  54. package/package.json +2 -2
  55. package/skills/keiyaku/SKILL.md +1 -1
  56. package/skills/keiyaku-akuma/SKILL.md +13 -8
@@ -102,7 +102,7 @@ export function renderCallText(result, streamed, context) {
102
102
  evidence: callEvidence(result, head),
103
103
  }),
104
104
  ...(admittedTell === undefined ? [] : tellPayloadLines(admittedTell.row.text, context)),
105
- ...subordinateLines([`! error ${safeText(result.observation.failure.diagnostic)}`]),
105
+ ...subordinateLines([`! ${safeText(result.observation.failure.diagnostic)}`]),
106
106
  ].join("\n");
107
107
  }
108
108
  const inputObservation = result.observation;
@@ -152,32 +152,36 @@ export function renderKillText(result, alias, context) {
152
152
  void context;
153
153
  return result.results.map((member) => killResultText(member.id, member.evidence, alias)).join("\n");
154
154
  }
155
- export function callExitCode(result) {
155
+ export function callOutcome(result) {
156
156
  if (result.dispatch.kind === "failed" || result.alias.kind === "failed" || result.observation.kind === "failed")
157
- return 2;
157
+ return "failed";
158
158
  if (result.observation.kind !== "observed")
159
- return 0;
159
+ return "accepted";
160
160
  return result.observation.observation.reason === "failed" ||
161
161
  result.observation.observation.reason === "invalid-output"
162
- ? 2
163
- : 0;
162
+ ? "failed"
163
+ : "accepted";
164
164
  }
165
- export function killExitCode(result) {
165
+ export function killOutcome(result) {
166
166
  return result.results.some((member) => member.evidence === "unavailable" || member.evidence === "hung" || member.evidence === "untidy")
167
- ? 1
168
- : 0;
167
+ ? "failed"
168
+ : "accepted";
169
169
  }
170
- export function tellExitCode(result) {
171
- return result.tell.wake.kind === "failed" ? 2 : 0;
170
+ export function tellOutcome(result) {
171
+ return result.tell.wake.kind === "failed" ? "failed" : "accepted";
172
172
  }
173
- export function askExitCode(result) {
174
- return result.observation.reason === "failed" || result.observation.reason === "invalid-output" ? 2 : 0;
173
+ export function askOutcome(result) {
174
+ return result.observation.reason === "failed" || result.observation.reason === "invalid-output"
175
+ ? "failed"
176
+ : "accepted";
175
177
  }
176
- export function forkExitCode(receipt) {
177
- return receipt.kind === "forked" ? 0 : receipt.kind === "upstream-forked" ? 2 : 1;
178
+ export function forkOutcome(receipt) {
179
+ if (receipt.kind === "forked")
180
+ return receipt.dispatch.kind === "failed" ? "failed" : "accepted";
181
+ return receipt.kind === "upstream-forked" || receipt.kind === "fork-failed" ? "failed" : "refused";
178
182
  }
179
- export function historyExitCode(result, id) {
183
+ export function historyOutcome(result, id) {
180
184
  if (id === undefined)
181
- return 0;
182
- return result.kind === "exact" ? 0 : 1;
185
+ return "accepted";
186
+ return result.kind === "exact" ? "accepted" : "refused";
183
187
  }
@@ -58,6 +58,7 @@ export function renderContractCatalogue(list) {
58
58
  : row.phase === "abandoned"
59
59
  ? row.abandonNote
60
60
  : undefined;
61
+ const testimonyLead = ` ${row.phase === "claimed" ? "review" : "note"} `;
61
62
  const lines = [
62
63
  `${contractMark(row)} ${safeText(row.id)} · ${formatAge(row.phaseAt, list.observedAt)} · ${safeText(row.title ?? "title unavailable")}`,
63
64
  ` ${terminal ? outcome : progressStrip(row)}`,
@@ -65,11 +66,11 @@ export function renderContractCatalogue(list) {
65
66
  ? []
66
67
  : renderBoundedPayload({
67
68
  text: testimony,
68
- first: ` ${row.phase === "claimed" ? "review" : "note"} `,
69
- continuation: " │ ",
69
+ first: testimonyLead,
70
+ continuation: " ".repeat(testimonyLead.length),
70
71
  columns: 100,
71
72
  maxLines: 3,
72
- quote: "“",
73
+ quote: "",
73
74
  truncated: false,
74
75
  })),
75
76
  ];
@@ -1,4 +1,3 @@
1
- import { parseAkuId } from "../../akuma/identity.js";
2
1
  import { gateDisplayName, lifecycleWord, shortGitId } from "./contract-observation.js";
3
2
  import { receiptPayload } from "./receipt.js";
4
3
  import { renderOpaqueBlock, DEFAULT_CLI_COLUMNS } from "./terminal.js";
@@ -68,9 +67,7 @@ function journalBody(fact, workspace) {
68
67
  const lines = [
69
68
  ` gate ${fact.data.gate}`,
70
69
  ` verdict ${fact.data.verdict}`,
71
- snapshot === undefined
72
- ? " subject verification"
73
- : ` subject verification · snapshot ${shortGitId(snapshot)}`,
70
+ ...(snapshot === undefined ? [] : [` snapshot ${shortGitId(snapshot)}`]),
74
71
  ];
75
72
  if (fact.data.summary !== undefined) {
76
73
  const clipped = fact.data.summary.length > 4096 ? `${fact.data.summary.slice(0, 4096)}\n[truncated]` : fact.data.summary;
@@ -84,7 +81,7 @@ function journalBody(fact, workspace) {
84
81
  const lines = [` sequence ${String(fact.data.seq)}`, ` title ${fact.data.title}`];
85
82
  const body = fact.data.body.replace(/^(?:\r?\n)+/u, "").trimEnd();
86
83
  if (body.length > 0)
87
- lines.push(" body", ...renderOpaqueBlock(body, " │ ", DEFAULT_CLI_COLUMNS));
84
+ lines.push(" body", ...renderOpaqueBlock(body, " ", DEFAULT_CLI_COLUMNS));
88
85
  return lines;
89
86
  }
90
87
  case "abandoned": {
@@ -127,16 +124,8 @@ function bindLine(bind, dispatch) {
127
124
  const target = bind.data.coordinates.target?.replace(/^refs\/heads\//u, "") ?? "targetless";
128
125
  const gates = bind.data.terms.gates.map(gateDisplayName);
129
126
  const gateFact = gates.length === 0 ? "" : ` · ${gates.length === 1 ? "gate" : "gates"} ${gates.join(" · ")}`;
130
- let alias = "";
131
- if (dispatch?.source === "dispatch") {
132
- try {
133
- alias = ` · @${parseAkuId(dispatch.dispatch.akuId).archetype}`;
134
- }
135
- catch {
136
- alias = "";
137
- }
138
- }
139
- return `bound to ${target} @ ${shortGitId(bind.data.coordinates.start)}${gateFact}${alias}`;
127
+ const caller = dispatch?.source === "dispatch" ? ` · ${dispatch.dispatch.akuId}` : "";
128
+ return `bound to ${target} @ ${shortGitId(bind.data.coordinates.start)}${gateFact}${caller}`;
140
129
  }
141
130
  /**
142
131
  * The skeleton row is itself the first line of the beat's full form; the fold names the rest.
@@ -196,7 +185,7 @@ function skeletonBeatLines(beat, history) {
196
185
  }
197
186
  case "arc": {
198
187
  const body = fact.data.body.replace(/^(?:\r?\n)+/u, "").trimEnd();
199
- const folded = body.length === 0 ? [] : renderOpaqueBlock(body, " │ ", DEFAULT_CLI_COLUMNS);
188
+ const folded = body.length === 0 ? [] : renderOpaqueBlock(body, " ", DEFAULT_CLI_COLUMNS);
200
189
  return [`${clock(first)} arc ${fact.data.title}${payloadSuffix(folded)}`];
201
190
  }
202
191
  case "claimed":
@@ -1,3 +1,4 @@
1
+ import type { CliOutcome } from "../streams.js";
1
2
  import type { BlockedDebtList, DebtBatchResult, DebtContextResult, DebtDecompositionTree, DebtDetail, DebtDoctorReport, DebtList, DebtMutationResult, DebtQueryResult, DebtUpdateResult } from "../../debt/index.js";
2
3
  import { type TextRenderContext } from "./terminal.js";
3
4
  export { debtMark } from "./marks.js";
@@ -25,4 +26,4 @@ export declare function renderDebtContext(result: DebtContextResult, context?: T
25
26
  export declare function renderDebtMutation(verb: string, result: DebtMutationResult, context?: TextRenderContext): string;
26
27
  /** One lifecycle answer: a single mutation or a plural batch, both native shapes. */
27
28
  export declare function renderDebtLifecycle(verb: string, result: DebtMutationResult | DebtBatchResult, context?: TextRenderContext): string;
28
- export declare function debtExitCode(result: DebtNativeResult): number;
29
+ export declare function debtOutcome(result: DebtNativeResult): CliOutcome;
@@ -240,18 +240,18 @@ export function renderDebtMutation(verb, result, context = DEFAULT_CONTEXT) {
240
240
  export function renderDebtLifecycle(verb, result, context = DEFAULT_CONTEXT) {
241
241
  return "items" in result ? renderBatch(verb, result, context.columns) : renderMutation(verb, result, context.columns);
242
242
  }
243
- export function debtExitCode(result) {
243
+ export function debtOutcome(result) {
244
244
  if ("issues" in result)
245
- return result.issues.length === 0 ? 0 : 1;
245
+ return result.issues.length === 0 ? "accepted" : "failed";
246
246
  if ("items" in result) {
247
247
  const kinds = result.items.map((item) => item.outcome.kind);
248
- return kinds.includes("retry") ? 2 : kinds.includes("refused") ? 1 : 0;
248
+ return kinds.includes("retry") ? "retryable" : kinds.includes("refused") ? "refused" : "accepted";
249
249
  }
250
250
  if ("kind" in result) {
251
251
  if (result.kind === "retry")
252
- return 2;
252
+ return "retryable";
253
253
  if (result.kind === "refused")
254
- return 1;
254
+ return "refused";
255
255
  }
256
- return 0;
256
+ return "accepted";
257
257
  }
@@ -1,3 +1,4 @@
1
+ import type { CliOutcome } from "../streams.js";
1
2
  import type { NukeResult } from "../../index.js";
2
3
  export declare function renderNukeText(result: NukeResult): string;
3
- export declare function nukeExitCode(result: NukeResult): number;
4
+ export declare function nukeOutcome(result: NukeResult): CliOutcome;
@@ -18,6 +18,8 @@ export function renderNukeText(result) {
18
18
  }
19
19
  return "";
20
20
  }
21
- export function nukeExitCode(result) {
22
- return result.kind === "refused" ? 1 : result.pending.some((surface) => surface.required) ? 2 : 0;
21
+ export function nukeOutcome(result) {
22
+ if (result.kind === "refused")
23
+ return "refused";
24
+ return result.pending.some((surface) => surface.required) ? "retryable" : "accepted";
23
25
  }
@@ -102,7 +102,7 @@ export function gatesAwaitLines(placement) {
102
102
  .map((report) => gateDisplayName(report.gate));
103
103
  if (events.length === 0)
104
104
  return [];
105
- return [`⧗ awaiting ${events.length > 3 ? `${events.length} gates` : events.join(", ")}`];
105
+ return [`⧗ awaiting ${events.join(", ")}`];
106
106
  }
107
107
  function targetMovedDetail(stop) {
108
108
  if ("integratedAt" in stop) {
@@ -192,8 +192,13 @@ function gatesUnsatisfiedStopLines(stop, columns, dependent) {
192
192
  // carry the story, and the caller closes the receipt with the awaiting margin line.
193
193
  const lines = [];
194
194
  const target = stop.refusal.target;
195
- if (dependent !== undefined)
196
- receiptRow(lines, "⧗", dependent, [{ text: "·" }, { text: "gates unmet" }], columns);
195
+ if (dependent !== undefined) {
196
+ const awaiting = gatesAwaitLines(stop);
197
+ if (awaiting.length === 0)
198
+ receiptRow(lines, " ", "dependent", [{ text: dependent, opaque: true }], columns);
199
+ for (const line of awaiting)
200
+ receiptRow(lines, "⧗", dependent, [{ text: "·" }, { text: line.slice(2) }], columns);
201
+ }
197
202
  else if (target !== undefined)
198
203
  receiptRow(lines, " ", "target", [{ text: target, opaque: true }, { text: "· unchanged" }], columns);
199
204
  lines.push(...gateAlarmRows(stop, columns));
@@ -121,12 +121,7 @@ export function reconcileHasFailure(report) {
121
121
  if (isRepoReport(report)) {
122
122
  if (report.kind === "world-observation-failed")
123
123
  return true;
124
- const completed = report;
125
- return completed.contracts.some((item) => {
126
- return (item.report.lag.some((lag) => reconcileLagIsFailure(lag)) ||
127
- item.report.settlement.lags.length > 0 ||
128
- (item.report.settlement.seatClose?.length ?? 0) > 0);
129
- });
124
+ return report.contracts.some((item) => reconcileHasFailure(item.report));
130
125
  }
131
126
  return (report.lag.some((lag) => reconcileLagIsFailure(lag)) ||
132
127
  (report.settlement?.lags.length ?? 0) > 0 ||
@@ -134,7 +129,13 @@ export function reconcileHasFailure(report) {
134
129
  }
135
130
  export function renderReconcile(report, context) {
136
131
  const columns = context?.columns ?? DEFAULT_CLI_COLUMNS;
137
- const lines = ["✓ reconcile"];
132
+ const lines = [
133
+ isRepoReport(report) && report.kind === "world-observation-failed"
134
+ ? "! reconcile failed"
135
+ : reconcileHasFailure(report)
136
+ ? "! reconcile incomplete"
137
+ : "✓ reconcile",
138
+ ];
138
139
  const seen = new Set();
139
140
  if (isRepoReport(report)) {
140
141
  if (report.kind === "world-observation-failed") {
@@ -33,11 +33,15 @@ function renderDirtyRefusal(refusal, indent, columns, identity) {
33
33
  lines.push(...collectionLines(name, refusal[name], indent));
34
34
  }
35
35
  wrap(lines, gitShortStat(refusal.shortStat), indent, columns);
36
- if (refusal.option?.available === false) {
37
- lines.push(`${indent}option --include-dirty · unavailable with submodule changes`);
36
+ if (refusal.option === undefined) {
37
+ if (refusal.submodules.length > 0)
38
+ wrap(lines, "reason submodule changes cannot be captured for review", indent, columns);
39
+ }
40
+ else if (!refusal.option.available) {
41
+ lines.push(`${indent}option ${refusal.option.flag} · unavailable with submodule changes`);
38
42
  }
39
43
  else {
40
- lines.push(`${indent}option --include-dirty · captures complete non-ignored worktree bytes`);
44
+ lines.push(`${indent}option ${refusal.option.flag} · captures complete non-ignored worktree bytes`);
41
45
  lines.push(`${indent}capture index private · real index unchanged, including any unmerged entries`);
42
46
  lines.push(`${indent}staging not required for --include-dirty`);
43
47
  }
@@ -31,7 +31,7 @@ export declare function renderBoundedTextBlock(value: string, input: Readonly<{
31
31
  lines?: number;
32
32
  truncated?: boolean;
33
33
  }>): readonly string[];
34
- export declare function renderBoundedPayload(input: Readonly<{
34
+ type BoundedPayload = Readonly<{
35
35
  text: string;
36
36
  first: string;
37
37
  continuation: string;
@@ -39,7 +39,10 @@ export declare function renderBoundedPayload(input: Readonly<{
39
39
  maxLines: number;
40
40
  quote: string;
41
41
  truncated: boolean;
42
- }>): readonly string[];
42
+ /** Keep source line breaks and indentation; single-line previews fold whitespace instead. */
43
+ preserveLines?: true;
44
+ }>;
45
+ export declare function renderBoundedPayload(input: BoundedPayload): readonly string[];
43
46
  export declare function quotedText(value: string): string;
44
47
  export declare function safeText(value: string): string;
45
48
  type CheckoutNotFollowable = Readonly<{
@@ -138,18 +138,8 @@ export function truncateMiddleDisplayText(value, maximum) {
138
138
  const headColumns = Math.ceil(available / 2);
139
139
  const tailColumns = available - headColumns;
140
140
  const head = takeDisplayColumns(clean, headColumns).text.replace(/…+$/u, "");
141
- const characters = [...GRAPHEMES.segment(clean)].map(({ segment }) => segment);
142
- let tailStart = characters.length;
143
- let used = 0;
144
- while (tailStart > 0) {
145
- const candidate = characters[tailStart - 1];
146
- const width = displayColumns(candidate);
147
- if (used + width > tailColumns)
148
- break;
149
- used += width;
150
- tailStart -= 1;
151
- }
152
- return `${head}…${characters.slice(tailStart).join("").replace(/^…+/u, "")}`;
141
+ const tail = takeDisplayColumnsFromEnd(clean, tailColumns).text.replace(/^…+/u, "");
142
+ return `${head}…${tail}`;
153
143
  }
154
144
  export function renderBoundedTextBlock(value, input) {
155
145
  const maximumLines = input.lines ?? 3;
@@ -179,6 +169,78 @@ export function renderBoundedTextBlock(value, input) {
179
169
  return lines.length === 0 ? [input.first.trimEnd()] : lines;
180
170
  }
181
171
  export function renderBoundedPayload(input) {
172
+ return input.preserveLines === true ? renderPreservedPayload(input) : renderFoldedPayload(input);
173
+ }
174
+ /**
175
+ * Paragraph-preserving payload: source line breaks and leading indentation stay,
176
+ * each line wraps at a word boundary where one exists, and the last rendered
177
+ * line carries either the closing quote or the one truncation mark.
178
+ */
179
+ function renderPreservedPayload(input) {
180
+ const closing = input.quote === "" ? "" : input.quote === "“" ? "”" : input.quote;
181
+ if (input.text.length === 0)
182
+ return [input.quote === "" ? input.first.trimEnd() : `${input.first}${input.quote}${closing}`];
183
+ const source = input.text.split(/\r?\n/u).map((line) => safeText(line).replace(/\s+$/u, ""));
184
+ const lines = [];
185
+ let truncated = input.truncated;
186
+ let done = false;
187
+ /** Paragraph breaks are deferred so they never spend the preview's last usable line. */
188
+ let pendingBlanks = 0;
189
+ for (const line of source) {
190
+ if (done)
191
+ break;
192
+ if (line.length === 0) {
193
+ if (lines.length > 0)
194
+ pendingBlanks += 1;
195
+ continue;
196
+ }
197
+ // A paragraph break never spends the preview's last line: content wins over a blank.
198
+ const room = input.maxLines - lines.length - 1;
199
+ for (let count = 0; count < Math.min(pendingBlanks, Math.max(0, room)); count += 1)
200
+ lines.push(input.continuation.trimEnd());
201
+ pendingBlanks = 0;
202
+ const indent = /^ */u.exec(line)[0];
203
+ let rest = line.slice(indent.length);
204
+ while (rest.length > 0) {
205
+ if (lines.length >= input.maxLines) {
206
+ truncated = true;
207
+ done = true;
208
+ break;
209
+ }
210
+ const prefix = lines.length === 0 ? input.first : input.continuation;
211
+ const opening = input.quote !== "" && lines.length === 0 ? input.quote : "";
212
+ // One cell stays reserved for a closing quote or an omission mark.
213
+ const budget = Math.max(1, input.columns - displayColumns(prefix) - displayColumns(opening) - 1);
214
+ const fragment = wrapFragment(rest, budget);
215
+ if (fragment.body.length === 0) {
216
+ truncated = true;
217
+ done = true;
218
+ break;
219
+ }
220
+ lines.push(`${prefix}${opening}${indent}${fragment.body}`);
221
+ rest = fragment.remaining;
222
+ }
223
+ }
224
+ if (lines.length === 0)
225
+ return [input.first.trimEnd()];
226
+ if (truncated)
227
+ lines[lines.length - 1] = truncateDisplayText(`${lines[lines.length - 1]}…`, input.columns);
228
+ else if (closing !== "")
229
+ lines[lines.length - 1] += closing;
230
+ return lines;
231
+ }
232
+ /** One wrapped fragment: a word boundary is preferred over splitting a word. */
233
+ function wrapFragment(rest, budget) {
234
+ const taken = takeDisplayColumns(rest, budget);
235
+ if (taken.rest.length === 0)
236
+ return { body: taken.text, remaining: "" };
237
+ const split = taken.text.lastIndexOf(" ");
238
+ if (split <= 0 || split < Math.floor(taken.text.length / 2))
239
+ return { body: taken.text, remaining: taken.rest };
240
+ return { body: taken.text.slice(0, split), remaining: `${taken.text.slice(split + 1)}${taken.rest}` };
241
+ }
242
+ /** Single-line and bounded summaries fold whitespace into one grapheme-safe stream. */
243
+ function renderFoldedPayload(input) {
182
244
  let rest = safeText(input.text).replace(/\s+/gu, " ").trim();
183
245
  const lines = [];
184
246
  for (let index = 0; index < input.maxLines; index += 1) {
@@ -0,0 +1,2 @@
1
+ import type { WhoamiFacts } from "../commands/whoami.js";
2
+ export declare function renderWhoami(facts: WhoamiFacts): string;
@@ -0,0 +1,56 @@
1
+ import { panelHead } from "./akuma-activity.js";
2
+ import { safeText } from "./terminal.js";
3
+ function callerText(caller) {
4
+ if (caller.kind === "unknown")
5
+ return "unknown";
6
+ return caller.kind === "akuma" ? `${caller.akuId} · world ${caller.world}` : `${caller.integration}:${caller.id}`;
7
+ }
8
+ function originText(origin) {
9
+ return origin.kind === "request"
10
+ ? `request · ${origin.parent} · ${origin.requestId}`
11
+ : `fork · ${origin.parent} · ${origin.at}`;
12
+ }
13
+ function selfHead(facts, dispatch, workdir) {
14
+ const self = facts.self;
15
+ if (self.kind === "akuma") {
16
+ const aliases = facts.profile?.kind === "available" ? facts.profile.aliases : [];
17
+ const identity = `${self.akuId}${aliases.length ? ` (${aliases.join(" · ")})` : ""}`;
18
+ return panelHead(identity, undefined, dispatch === undefined ? undefined : { kind: "associated", contractId: dispatch }, workdir == null ? [] : [workdir]).map(safeText);
19
+ }
20
+ const value = self.kind === "none" ? "none" : self.kind === "invalid" ? `invalid · ${self.reason}` : callerText(self);
21
+ return [safeText(`${value} (not an akuma)`)];
22
+ }
23
+ function contextText(contract) {
24
+ if (contract.kind === "resolved")
25
+ return contract.id;
26
+ if (contract.kind === "unavailable")
27
+ return `unavailable · ${contract.reason}`;
28
+ return contract.kind;
29
+ }
30
+ export function renderWhoami(facts) {
31
+ const row = (label, value) => ` ${label.padEnd(11)} ${safeText(value)}`;
32
+ const self = facts.self;
33
+ const profile = self.kind === "akuma" && facts.profile?.kind === "available" ? facts.profile : undefined;
34
+ const dispatch = profile?.contracts.length === 1 ? profile.contracts[0] : undefined;
35
+ const lines = selfHead(facts, dispatch, profile?.workdir);
36
+ if (self.kind === "akuma") {
37
+ lines.push(row("initiator", facts.initiatorUnavailable === undefined
38
+ ? (facts.initiator ?? "none")
39
+ : `unavailable · ${facts.initiatorUnavailable}`));
40
+ if (profile !== undefined)
41
+ lines.push(row("parent", callerText(profile.birth.caller)));
42
+ }
43
+ lines.push(row("world", facts.world ?? "none"));
44
+ const context = contextText(facts.contract);
45
+ if (context !== dispatch)
46
+ lines.push(row("context", context));
47
+ lines.push(row("requests", facts.requests));
48
+ if (self.kind === "akuma" && facts.profile?.kind === "unavailable")
49
+ lines.push(row("profile", `unavailable · ${facts.profile.reason}`));
50
+ if (profile !== undefined) {
51
+ lines.push(row("runtime", `${profile.provider ?? "unknown"} · ${profile.model ?? "unknown"}`));
52
+ if (profile.birth.origin.kind !== "direct")
53
+ lines.push(row("origin", originText(profile.birth.origin)));
54
+ }
55
+ return lines.join("\n");
56
+ }
@@ -2,6 +2,7 @@ import { type LibraryExecution } from "../akuma/requests.js";
2
2
  import { type ActorId } from "../index.js";
3
3
  import type { ExecutionObserver } from "../protocol/execution-observation.js";
4
4
  import { type ParsedCommand, type ParsedInvocation } from "./parse.js";
5
+ import { type CliOutcome } from "./streams.js";
5
6
  export type ParsedCommandInvocation = Extract<ParsedInvocation, {
6
7
  command: ParsedCommand;
7
8
  }>;
@@ -30,7 +31,7 @@ export type CliRuntimeInput = Readonly<{
30
31
  export declare function writeExecutionProgress(stream?: NodeJS.WritableStream): Promise<ExecutionProgressDriver>;
31
32
  type AkumaFailureProjection = Readonly<{
32
33
  body: string;
33
- exitCode: 1 | 3;
34
+ outcome: CliOutcome;
34
35
  }>;
35
36
  /** Projection of one native Akuma addressing/observation failure into its stated refusal body. */
36
37
  export declare function akumaFailureProjection(error: unknown, command: ParsedCommand): Promise<AkumaFailureProjection | undefined>;