@skrr-ai/cli 0.1.106 → 0.1.108

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.
@@ -44,7 +44,7 @@ class AgentOperatingPlan extends base_command_1.BaseCommand {
44
44
  this.log(` Evaluation: ${w.status} — ${w.reason}${w.nextAt ? `; next ${w.nextAt}` : ''}`);
45
45
  this.log(` Next intervention: ${r.lifecycle !== 'active' ? 'inactive; requires activation' : r.nextIntervention?.nextAt || r.nextIntervention?.status || 'not recorded'}`);
46
46
  if (r.blocker)
47
- this.log(` Blocker: ${r.blocker.code} — ${r.blocker.remediation || ''}`);
47
+ this.log(` Blocker: ${r.blocker.code} — ${r.blocker.remediation || r.blocker.detail || ''}`);
48
48
  }
49
49
  for (const line of (0, operating_plan_loops_1.planningLoopLines)(plan.planning.loops, plan.planning.todos.length))
50
50
  this.log(line);
@@ -16,7 +16,8 @@ function doctorVerdict(input) {
16
16
  const lifecycle = /^status_([a-z_]+)$/.exec(String(blocker.code));
17
17
  return {
18
18
  verdict: lifecycle ? lifecycle[1] : blocker.code === 'deleted' ? 'deleted' : 'blocked',
19
- next: String(blocker.remediation || 'Inspect the Commitment'),
19
+ // A typed blocker carries no remedy, only what happened (OSK-16230).
20
+ next: String(blocker.remediation || blocker.detail || 'Inspect the Commitment'),
20
21
  };
21
22
  }
22
23
  if (operatingState.status === 'waiting_approval') {
@@ -1,3 +1,4 @@
1
+ import type { ProposalView } from '@skrr-ai/data-provider';
1
2
  import { BaseCommand } from '../../base-command';
2
3
  /**
3
4
  * What an empty page means. With a filter AND a next cursor it does NOT mean "none": the inbox reads a
@@ -6,6 +7,12 @@ import { BaseCommand } from '../../base-command';
6
7
  * Without a cursor the inbox is exhausted and "none" is true.
7
8
  */
8
9
  export declare function emptyPageLines(narrowed: boolean, nextCursor: string | null | undefined): string[];
10
+ /**
11
+ * What the row is ABOUT: the Commitment and the Agent behind it, in the order the web inbox shows them. 51 distinct titles covered 747 decidable
12
+ * initiatives, so the title alone could not tell two rows apart (OSK-15378). A dash is "not named" (an id the
13
+ * server could not read for this viewer is in `--json`), never "none".
14
+ */
15
+ export declare function aboutFor(row: ProposalView): string;
9
16
  export default class ProposalsList extends BaseCommand {
10
17
  static description: string;
11
18
  static flags: {
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.emptyPageLines = emptyPageLines;
4
+ exports.aboutFor = aboutFor;
4
5
  const core_1 = require("@oclif/core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
6
7
  const base_command_1 = require("../../base-command");
@@ -23,6 +24,14 @@ function emptyPageLines(narrowed, nextCursor) {
23
24
  ]
24
25
  : ['No proposals on this page; the cursor below continues.'];
25
26
  }
27
+ /**
28
+ * What the row is ABOUT: the Commitment and the Agent behind it, in the order the web inbox shows them. 51 distinct titles covered 747 decidable
29
+ * initiatives, so the title alone could not tell two rows apart (OSK-15378). A dash is "not named" (an id the
30
+ * server could not read for this viewer is in `--json`), never "none".
31
+ */
32
+ function aboutFor(row) {
33
+ return [row.commitmentTitle, row.agent?.name].filter(Boolean).join(' · ') || '—';
34
+ }
26
35
  /** A snoozed proposal reads `proposed` in its disposition; the wake time is what tells them apart. */
27
36
  function noteFor(row) {
28
37
  if (row.sourceStatus === 'snoozed' && row.snoozedUntil)
@@ -77,6 +86,7 @@ class ProposalsList extends base_command_1.BaseCommand {
77
86
  (0, format_1.renderTable)(result.data.map((row) => ({
78
87
  id: row.id,
79
88
  title: row.title,
89
+ about: aboutFor(row),
80
90
  disposition: row.disposition,
81
91
  // Most proposals carry no estimate; "unknown" read as a failure to determine one (OSK-15377).
82
92
  basis: row.economics?.basis || '—',
@@ -85,6 +95,7 @@ class ProposalsList extends base_command_1.BaseCommand {
85
95
  })), [
86
96
  { key: 'id', header: 'PROPOSAL', verbatim: true },
87
97
  { key: 'title', header: 'TITLE' },
98
+ { key: 'about', header: 'ABOUT', maxWidth: 48 },
88
99
  { key: 'disposition', header: 'DISPOSITION' },
89
100
  { key: 'basis', header: 'BASIS' },
90
101
  { key: 'deadline', header: 'DEADLINE' },
@@ -58,13 +58,31 @@ function renderExecutionReceipt(receipt, log, verbose = false) {
58
58
  log(` ${evidence.label}: ${location}`);
59
59
  }
60
60
  }
61
+ // "Next" names an action only when the receipt has one. A reason that only
62
+ // restates what happened (a typed blocker's summary, which servers before
63
+ // OSK-16429 sent as the reason) is narrative, not something to do: the actor
64
+ // is printed without it and the narrative stays with the blocker.
65
+ const narrative = [receipt.summary, receipt.blocker?.summary].filter(Boolean);
66
+ const action = receipt.nextActor?.reason && !narrative.includes(receipt.nextActor.reason)
67
+ ? receipt.nextActor.reason
68
+ : '';
61
69
  if (receipt.nextActor)
62
- log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${receipt.nextActor.reason ? ` — ${receipt.nextActor.reason}` : ''}`);
70
+ log(`Next: ${receipt.nextActor.label || receipt.nextActor.kind}${action ? ` — ${action}` : ''}`);
63
71
  if (receipt.blocker) {
64
72
  log(`Blocker: ${receipt.blocker.code}`);
73
+ // The headline already carries a typed blocker's summary; say it again only
74
+ // when it differs.
75
+ if (receipt.blocker.summary && receipt.blocker.summary !== receipt.summary)
76
+ log(` Detail: ${receipt.blocker.summary}`);
65
77
  for (const evidenceRef of receipt.blocker.evidenceRefs || [])
66
78
  log(` Evidence: ${evidenceRef}`);
67
79
  }
80
+ // A source gap beside this episode, not the reason it stopped (OSK-16230).
81
+ if (receipt.standingBlocker) {
82
+ log(`Standing: ${receipt.standingBlocker.code} — ${receipt.standingBlocker.summary} (${receipt.standingBlocker.owner.label})`);
83
+ for (const evidenceRef of receipt.standingBlocker.evidenceRefs || [])
84
+ log(` Evidence: ${evidenceRef}`);
85
+ }
68
86
  if (verbose)
69
87
  log(`Diagnostics:\n${JSON.stringify({ id: receipt.id, revision: receipt.revision, evidence: receipt.evidence, diagnostics: receipt.diagnostics }, null, 2)}`);
70
88
  }
@@ -818,8 +818,16 @@ export declare function describeOperation(operation: string | null | undefined,
818
818
  * already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
819
819
  * printed while the operation is ready or working, where a fix would contradict
820
820
  * the line above it.
821
- */
822
- export declare function describeOperationFix(commitment: CommitmentView, operation: string | null | undefined): string | null;
821
+ *
822
+ * Returns one or two lines. `Fix:` only when the blocker carries an action; a
823
+ * typed blocker an agent returned carries only what happened, which printed
824
+ * under "Fix:" read as a remedy nobody wrote (OSK-16230). That narrative is
825
+ * labelled `Blocker:` instead, and no remedy is made up for it.
826
+ */
827
+ export declare function describeOperationFix(commitment: CommitmentView, operation: string | null | undefined): Array<{
828
+ label: 'Fix' | 'Blocker';
829
+ text: string;
830
+ }>;
823
831
  /**
824
832
  * Who stopped a Commitment, and when and why, in one line. The server stores it as `stoppedBy`;
825
833
  * `show` printed neither (OSK-15423), so a Commitment you paused did not say you had.
@@ -955,20 +955,32 @@ function describeOperation(operation, reason) {
955
955
  * already composed sat only in `--json` and `doctor` (OSK-16212). Nothing is
956
956
  * printed while the operation is ready or working, where a fix would contradict
957
957
  * the line above it.
958
+ *
959
+ * Returns one or two lines. `Fix:` only when the blocker carries an action; a
960
+ * typed blocker an agent returned carries only what happened, which printed
961
+ * under "Fix:" read as a remedy nobody wrote (OSK-16230). That narrative is
962
+ * labelled `Blocker:` instead, and no remedy is made up for it.
958
963
  */
959
964
  function describeOperationFix(commitment, operation) {
960
965
  if (operation === 'ready' || operation === 'working')
961
- return null;
966
+ return [];
962
967
  const blocker = commitment.firstActionableBlocker;
963
- const remediation = blocker && typeof blocker.remediation === 'string' ? blocker.remediation.trim() : '';
964
- if (!remediation)
965
- return null;
966
- const who = typeof blocker?.ownerLabel === 'string' && blocker.ownerLabel
968
+ if (!blocker)
969
+ return [];
970
+ const remediation = typeof blocker.remediation === 'string' ? blocker.remediation.trim() : '';
971
+ const detail = typeof blocker.detail === 'string' ? blocker.detail.trim() : '';
972
+ const who = typeof blocker.ownerLabel === 'string' && blocker.ownerLabel
967
973
  ? blocker.ownerLabel
968
- : typeof blocker?.owner === 'string' && blocker.owner
974
+ : typeof blocker.owner === 'string' && blocker.owner
969
975
  ? blocker.owner
970
976
  : '';
971
- return `${remediation}${who ? ` (${who})` : ''}`;
977
+ const lines = [];
978
+ // The detail is shown only when the fix does not already say it.
979
+ if (detail && detail !== remediation)
980
+ lines.push({ label: 'Blocker', text: `${detail}${!remediation && who ? ` (${who})` : ''}` });
981
+ if (remediation)
982
+ lines.push({ label: 'Fix', text: `${remediation}${who ? ` (${who})` : ''}` });
983
+ return lines;
972
984
  }
973
985
  /**
974
986
  * Who stopped a Commitment, and when and why, in one line. The server stores it as `stoppedBy`;
@@ -1082,9 +1094,8 @@ scheduleIssues) {
1082
1094
  log(`Assessment: ${assessment?.label ?? 'Unknown'}`);
1083
1095
  log(`Evidence: ${assessment?.evidence ?? 'unavailable'}`);
1084
1096
  log(`Operation: ${describeOperation(assessment?.operation, assessment?.operationReason)}`);
1085
- const fix = describeOperationFix(commitment, assessment?.operation);
1086
- if (fix)
1087
- log(` Fix: ${fix}`);
1097
+ for (const line of describeOperationFix(commitment, assessment?.operation))
1098
+ log(` ${line.label}: ${line.text}`);
1088
1099
  if (commitment.state?.lastCheckedAt)
1089
1100
  log(`Checked: ${commitment.state.lastCheckedAt}`);
1090
1101
  if (typeof commitment.state?.consecutiveUnresolvedChecks === 'number') {