@skrr-ai/cli 0.1.37 → 0.1.39

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 (177) hide show
  1. package/bin/dev-fallback.js +175 -4
  2. package/dist/base-command.js +46 -2
  3. package/dist/commands/agents/actions/create.js +18 -7
  4. package/dist/commands/agents/actions/delete.d.ts +1 -0
  5. package/dist/commands/agents/actions/delete.js +7 -0
  6. package/dist/commands/agents/actions/update.js +5 -3
  7. package/dist/commands/agents/api-actions/add.js +5 -2
  8. package/dist/commands/agents/api-actions/index.d.ts +11 -0
  9. package/dist/commands/agents/api-actions/index.js +27 -1
  10. package/dist/commands/agents/avatar/default/index.d.ts +11 -0
  11. package/dist/commands/agents/avatar/default/index.js +17 -0
  12. package/dist/commands/agents/avatar/default/set.d.ts +2 -1
  13. package/dist/commands/agents/avatar/default/set.js +32 -7
  14. package/dist/commands/agents/avatar/upload.js +7 -1
  15. package/dist/commands/agents/categories/delete.js +8 -0
  16. package/dist/commands/agents/categories/index.d.ts +21 -5
  17. package/dist/commands/agents/categories/index.js +68 -15
  18. package/dist/commands/agents/chat.d.ts +21 -0
  19. package/dist/commands/agents/chat.js +94 -7
  20. package/dist/commands/agents/create.js +11 -4
  21. package/dist/commands/agents/list.d.ts +15 -0
  22. package/dist/commands/agents/list.js +31 -1
  23. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  24. package/dist/commands/agents/scene-background/upload.js +97 -0
  25. package/dist/commands/agents/show.js +13 -0
  26. package/dist/commands/agents/tools/calls.d.ts +23 -5
  27. package/dist/commands/agents/tools/calls.js +81 -18
  28. package/dist/commands/agents/update.js +15 -5
  29. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  30. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  31. package/dist/commands/convos/messages.d.ts +13 -0
  32. package/dist/commands/convos/messages.js +40 -16
  33. package/dist/commands/daemon/index.js +8 -1
  34. package/dist/commands/daemon/logs.d.ts +26 -0
  35. package/dist/commands/daemon/logs.js +56 -0
  36. package/dist/commands/daemon/uninstall.js +1 -1
  37. package/dist/commands/followups/act.js +3 -2
  38. package/dist/commands/followups/cancel.d.ts +6 -3
  39. package/dist/commands/followups/cancel.js +16 -6
  40. package/dist/commands/followups/observability.d.ts +23 -0
  41. package/dist/commands/followups/observability.js +114 -0
  42. package/dist/commands/followups/reschedule.js +1 -1
  43. package/dist/commands/followups/resolve.js +3 -3
  44. package/dist/commands/followups/show.js +2 -1
  45. package/dist/commands/followups/watch.js +9 -5
  46. package/dist/commands/harnesses/forget.js +28 -0
  47. package/dist/commands/harnesses/install.d.ts +41 -0
  48. package/dist/commands/harnesses/install.js +165 -0
  49. package/dist/commands/harnesses/installers.d.ts +45 -0
  50. package/dist/commands/harnesses/installers.js +155 -0
  51. package/dist/commands/harnesses/list.d.ts +82 -1
  52. package/dist/commands/harnesses/list.js +104 -12
  53. package/dist/commands/harnesses/models.js +10 -1
  54. package/dist/commands/harnesses/show.d.ts +15 -0
  55. package/dist/commands/harnesses/show.js +44 -3
  56. package/dist/commands/harnesses/update.js +1 -1
  57. package/dist/commands/harnesses/usage.d.ts +25 -0
  58. package/dist/commands/harnesses/usage.js +24 -2
  59. package/dist/commands/instructions/status.js +13 -1
  60. package/dist/commands/machines/dedicated/catalog.js +4 -15
  61. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  62. package/dist/commands/machines/dedicated/create.js +63 -2
  63. package/dist/commands/machines/dedicated/destroy.js +19 -5
  64. package/dist/commands/machines/dedicated/exec.js +3 -1
  65. package/dist/commands/machines/dedicated/list.js +9 -6
  66. package/dist/commands/machines/dedicated/price-book.d.ts +17 -0
  67. package/dist/commands/machines/dedicated/price-book.js +35 -0
  68. package/dist/commands/machines/hosted/list.js +12 -0
  69. package/dist/commands/machines/list.js +11 -0
  70. package/dist/commands/machines/show.d.ts +1 -0
  71. package/dist/commands/machines/show.js +40 -11
  72. package/dist/commands/profiles/list.d.ts +23 -0
  73. package/dist/commands/profiles/list.js +96 -0
  74. package/dist/commands/search/skills.d.ts +11 -1
  75. package/dist/commands/search/skills.js +26 -8
  76. package/dist/commands/skills/details.d.ts +2 -0
  77. package/dist/commands/skills/details.js +24 -1
  78. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  79. package/dist/commands/skills/import-as-actions.js +175 -0
  80. package/dist/commands/skills/install.d.ts +2 -0
  81. package/dist/commands/skills/install.js +24 -1
  82. package/dist/commands/skills/invocable.js +6 -2
  83. package/dist/commands/skills/local.d.ts +40 -0
  84. package/dist/commands/skills/local.js +160 -0
  85. package/dist/commands/skills/search.d.ts +11 -1
  86. package/dist/commands/skills/search.js +29 -8
  87. package/dist/commands/spaces/actions/add.js +23 -5
  88. package/dist/commands/spaces/actions/available.js +3 -0
  89. package/dist/commands/spaces/actions/list.d.ts +5 -0
  90. package/dist/commands/spaces/actions/list.js +49 -3
  91. package/dist/commands/spaces/actions/run.d.ts +28 -0
  92. package/dist/commands/spaces/actions/run.js +146 -0
  93. package/dist/commands/spaces/actions/update.js +27 -5
  94. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  95. package/dist/commands/spaces/label-summaries.js +64 -0
  96. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  97. package/dist/commands/spaces/task-rollups.js +15 -4
  98. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
  99. package/dist/commands/tasks/actions/attach.js +32 -7
  100. package/dist/commands/tasks/complete.js +13 -0
  101. package/dist/commands/tasks/create.js +1 -1
  102. package/dist/commands/tasks/events/append.js +29 -6
  103. package/dist/commands/tasks/expectations/assess.d.ts +1 -0
  104. package/dist/commands/tasks/expectations/assess.js +43 -5
  105. package/dist/commands/tasks/expectations.js +77 -16
  106. package/dist/commands/tasks/follow-up.js +2 -2
  107. package/dist/commands/tasks/lifecycle/definitions.js +21 -2
  108. package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
  109. package/dist/commands/tasks/lifecycle/detach.js +43 -3
  110. package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
  111. package/dist/commands/tasks/lifecycle/migrate.js +42 -3
  112. package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
  113. package/dist/commands/tasks/lifecycle/reopen.js +40 -3
  114. package/dist/commands/tasks/lifecycle/transition.js +33 -2
  115. package/dist/commands/tasks/list.js +16 -9
  116. package/dist/commands/tasks/move.js +11 -13
  117. package/dist/commands/tasks/review-queue.d.ts +15 -0
  118. package/dist/commands/tasks/review-queue.js +180 -31
  119. package/dist/commands/tasks/show.d.ts +11 -0
  120. package/dist/commands/tasks/show.js +105 -12
  121. package/dist/commands/tasks/trust.js +7 -2
  122. package/dist/commands/tasks/update.js +46 -10
  123. package/dist/commands/tasks/updates/add.js +1 -1
  124. package/dist/commands/token/list.d.ts +22 -0
  125. package/dist/commands/token/list.js +68 -0
  126. package/dist/commands/token/revoke.d.ts +23 -0
  127. package/dist/commands/token/revoke.js +53 -0
  128. package/dist/lib/agent-actions.d.ts +57 -2
  129. package/dist/lib/agent-actions.js +113 -21
  130. package/dist/lib/agent-pin-mute.d.ts +10 -5
  131. package/dist/lib/agent-pin-mute.js +17 -4
  132. package/dist/lib/agent-visual-refs.d.ts +27 -0
  133. package/dist/lib/agent-visual-refs.js +127 -2
  134. package/dist/lib/agentic-stream.d.ts +6 -0
  135. package/dist/lib/agentic-stream.js +34 -3
  136. package/dist/lib/assignee-resolver.d.ts +1 -1
  137. package/dist/lib/assignee-resolver.js +8 -0
  138. package/dist/lib/bulk-task-targets.d.ts +12 -0
  139. package/dist/lib/bulk-task-targets.js +34 -1
  140. package/dist/lib/cli-installers.d.ts +130 -0
  141. package/dist/lib/cli-installers.js +145 -0
  142. package/dist/lib/command-miss.js +55 -7
  143. package/dist/lib/dedicated-lease-command.d.ts +16 -1
  144. package/dist/lib/dedicated-lease-command.js +26 -3
  145. package/dist/lib/dedicated-machines.d.ts +135 -1
  146. package/dist/lib/dedicated-machines.js +307 -3
  147. package/dist/lib/failed-lookup.d.ts +3 -0
  148. package/dist/lib/failed-lookup.js +40 -0
  149. package/dist/lib/first-party-harness-agent.js +9 -2
  150. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  151. package/dist/lib/first-party-harness-doctor.js +126 -27
  152. package/dist/lib/followups.d.ts +14 -6
  153. package/dist/lib/followups.js +31 -6
  154. package/dist/lib/harnesses.d.ts +8 -0
  155. package/dist/lib/instruction-input.d.ts +19 -1
  156. package/dist/lib/instruction-input.js +52 -1
  157. package/dist/lib/label-scope.d.ts +15 -0
  158. package/dist/lib/label-scope.js +25 -5
  159. package/dist/lib/local-skills.d.ts +98 -0
  160. package/dist/lib/local-skills.js +195 -0
  161. package/dist/lib/machines.d.ts +10 -10
  162. package/dist/lib/machines.js +23 -1
  163. package/dist/lib/search-query.d.ts +19 -0
  164. package/dist/lib/search-query.js +35 -0
  165. package/dist/lib/space-custom-actions.d.ts +72 -0
  166. package/dist/lib/space-custom-actions.js +117 -0
  167. package/dist/lib/space-resolver.d.ts +1 -21
  168. package/dist/lib/space-resolver.js +19 -10
  169. package/dist/lib/task-closure.d.ts +3 -2
  170. package/dist/lib/task-closure.js +18 -20
  171. package/dist/lib/task-lifecycle-output.js +117 -9
  172. package/dist/lib/task-ref-resolver.d.ts +1 -17
  173. package/dist/lib/task-ref-resolver.js +18 -8
  174. package/dist/lib/tasks.js +9 -3
  175. package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
  176. package/oclif.manifest.json +15505 -14460
  177. package/package.json +9 -3
@@ -5,6 +5,7 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../../base-command");
6
6
  const prompt_1 = require("../../../lib/prompt");
7
7
  const space_output_1 = require("../../../lib/space-output");
8
+ const space_custom_actions_1 = require("../../../lib/space-custom-actions");
8
9
  class SpacesActionsUpdate extends base_command_1.BaseCommand {
9
10
  static description = 'Update a custom action in a space';
10
11
  static args = {
@@ -14,10 +15,10 @@ class SpacesActionsUpdate extends base_command_1.BaseCommand {
14
15
  static flags = {
15
16
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
16
17
  'from-json': core_1.Flags.string({ description: 'JSON action patch' }),
17
- label: core_1.Flags.string({ description: 'Button label' }),
18
+ label: space_custom_actions_1.spaceCustomActionLabelFlag,
18
19
  prompt: core_1.Flags.string({ description: 'Prompt sent when the action runs' }),
19
20
  color: core_1.Flags.string({ description: 'Color hex' }),
20
- icon: core_1.Flags.string({ description: 'Lucide icon name' }),
21
+ icon: space_custom_actions_1.spaceCustomActionIconFlag,
21
22
  skill: core_1.Flags.string({ description: 'Optional skill ID' }),
22
23
  };
23
24
  async run() {
@@ -30,21 +31,39 @@ class SpacesActionsUpdate extends base_command_1.BaseCommand {
30
31
  catch (err) {
31
32
  this.error(err.message, { exit: 1 });
32
33
  }
33
- const payload = (0, prompt_1.mergeDefined)(body, {
34
+ const patch = (0, prompt_1.mergeDefined)(body, {
34
35
  label: flags.label,
35
36
  prompt: flags.prompt,
36
37
  color: flags.color,
37
38
  icon: flags.icon,
38
39
  skillId: flags.skill,
39
40
  });
40
- if (!Object.keys(payload).length)
41
+ if (!Object.keys(patch).length)
41
42
  this.error('Nothing to update.', { exit: 1 });
43
+ const iconProblem = (0, space_custom_actions_1.spaceCustomActionIconProblem)(patch.icon);
44
+ if (iconProblem)
45
+ this.error(iconProblem, { exit: 1 });
42
46
  let result;
47
+ let leadAgentId;
43
48
  try {
49
+ // The server replaces the WHOLE element (`$set: {'customActions.$': action}`)
50
+ // and validates a complete action — `id`, `label`, `prompt`, `color` — whose
51
+ // `id` must equal the path's. This command sent only the changed flags, so
52
+ // every call failed that validation: `--label` alone, `--prompt` alone, all
53
+ // of them together, 400 "Invalid request data" every time (OSK-9646). The
54
+ // web editor never hit it because it always sends the full action.
55
+ //
56
+ // So the patch is applied over the stored action here and sent whole.
57
+ const space = await data_provider_1.dataService.getSpace(args.space);
58
+ leadAgentId = space.leadAgentId;
59
+ const current = (space.customActions ?? []).find((action) => action.id === args.id);
60
+ if (!current) {
61
+ this.error(`No custom action ${args.id} in space ${args.space}. List them with \`spaces actions list ${args.space}\`.`, { exit: 1 });
62
+ }
44
63
  result = await data_provider_1.dataService.updateSpaceCustomAction({
45
64
  spaceId: args.space,
46
65
  actionId: args.id,
47
- action: payload,
66
+ action: { ...current, ...patch, id: args.id },
48
67
  });
49
68
  }
50
69
  catch (err) {
@@ -54,6 +73,9 @@ class SpacesActionsUpdate extends base_command_1.BaseCommand {
54
73
  this.log(JSON.stringify((0, space_output_1.withSpaceResourceUrls)(this.cliConfig.baseURL, args.space, 'actions', result, ['id']), null, 2));
55
74
  else
56
75
  this.log(`Updated custom action ${args.id}.`);
76
+ const unrunnable = (0, space_custom_actions_1.spaceActionsNotRunnableReason)({ id: args.space, leadAgentId }, this.config.bin);
77
+ if (unrunnable)
78
+ this.warn(unrunnable);
57
79
  }
58
80
  }
59
81
  exports.default = SpacesActionsUpdate;
@@ -0,0 +1,27 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr spaces label-summaries` — the label DEFINITIONS carried by every Space
4
+ * you can view, in one request.
5
+ *
6
+ * The third of the sidebar's batch reads, beside `spaces member-summaries` and
7
+ * `spaces task-rollups`, and the one that was never bound. `Space.labelIds`
8
+ * already says which Space carries which label, so what a caller holding a list
9
+ * of Spaces is missing is only the name and colour to resolve those ids
10
+ * against — and `GET /api/labels` takes exactly ONE (scope, scopeId) pair, so
11
+ * resolving them otherwise costs a request per row.
12
+ *
13
+ * Deliberately a flat list, matching the endpoint: one map, not one map per row.
14
+ *
15
+ * A label whose row is not returned is one the caller genuinely cannot read —
16
+ * another user's private label attached to a shared Space. It is reported as
17
+ * unresolved rather than hidden, because "this Space has a label you cannot
18
+ * see" and "this Space has no labels" are different facts.
19
+ */
20
+ export default class SpacesLabelSummaries extends BaseCommand {
21
+ static description: string;
22
+ static examples: string[];
23
+ static flags: {
24
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
25
+ };
26
+ run(): Promise<void>;
27
+ }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../base-command");
6
+ /**
7
+ * `skrr spaces label-summaries` — the label DEFINITIONS carried by every Space
8
+ * you can view, in one request.
9
+ *
10
+ * The third of the sidebar's batch reads, beside `spaces member-summaries` and
11
+ * `spaces task-rollups`, and the one that was never bound. `Space.labelIds`
12
+ * already says which Space carries which label, so what a caller holding a list
13
+ * of Spaces is missing is only the name and colour to resolve those ids
14
+ * against — and `GET /api/labels` takes exactly ONE (scope, scopeId) pair, so
15
+ * resolving them otherwise costs a request per row.
16
+ *
17
+ * Deliberately a flat list, matching the endpoint: one map, not one map per row.
18
+ *
19
+ * A label whose row is not returned is one the caller genuinely cannot read —
20
+ * another user's private label attached to a shared Space. It is reported as
21
+ * unresolved rather than hidden, because "this Space has a label you cannot
22
+ * see" and "this Space has no labels" are different facts.
23
+ */
24
+ class SpacesLabelSummaries extends base_command_1.BaseCommand {
25
+ static description = 'Show the label definitions used across every space you can view';
26
+ static examples = [
27
+ '<%= config.bin %> spaces label-summaries',
28
+ '<%= config.bin %> spaces label-summaries --json',
29
+ ];
30
+ static flags = {
31
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
32
+ };
33
+ async run() {
34
+ this.requireAuth();
35
+ const { flags } = await this.parse(SpacesLabelSummaries);
36
+ let response;
37
+ try {
38
+ response = await data_provider_1.dataService.listSpaceLabelSummaries();
39
+ }
40
+ catch (err) {
41
+ this.handleApiError(err);
42
+ return;
43
+ }
44
+ if (flags.json) {
45
+ this.log(JSON.stringify(response, null, 2));
46
+ return;
47
+ }
48
+ const labels = response.labels || [];
49
+ if (labels.length === 0) {
50
+ this.log('No labels on any space you can view.');
51
+ return;
52
+ }
53
+ const clip = (text, width) => text.length > width ? `${text.slice(0, width - 3)}...` : text;
54
+ this.log(`${'LABEL'.padEnd(28)} ${'SCOPE'.padEnd(10)} ${'COLOR'.padEnd(9)} ${'APPLIES TO'.padEnd(24)} ID`);
55
+ for (const label of labels) {
56
+ // `applicableTo` is what the label may be attached to, which is not the
57
+ // same as its scope — a workspace-scoped label can still be task-only.
58
+ const appliesTo = (label.applicableTo || []).join(', ') || '-';
59
+ this.log(`${clip(label.name, 28).padEnd(28)} ${String(label.scope ?? '-').padEnd(10)} ` +
60
+ `${String(label.color ?? '-').padEnd(9)} ${clip(appliesTo, 24).padEnd(24)} ${label.id}`);
61
+ }
62
+ }
63
+ }
64
+ exports.default = SpacesLabelSummaries;
@@ -3,6 +3,11 @@ import { BaseCommand } from '../../base-command';
3
3
  * `skrr spaces task-rollups` — task-status counts for every Space you can view,
4
4
  * in one request: the same rollup the Spaces sidebar row reads, counted by the
5
5
  * project-rollup rules (`done` is done only; `open` excludes cancelled).
6
+ *
7
+ * The SIGNAL column is the derived work band (`spaces/derive.ts`) — what the
8
+ * board is actually doing, as opposed to the status somebody declared on it.
9
+ * Sorting puts the boards that want a person first, which is the reason to run
10
+ * this from a terminal at all.
6
11
  */
7
12
  export default class SpacesTaskRollups extends BaseCommand {
8
13
  static description: string;
@@ -7,6 +7,11 @@ const base_command_1 = require("../../base-command");
7
7
  * `skrr spaces task-rollups` — task-status counts for every Space you can view,
8
8
  * in one request: the same rollup the Spaces sidebar row reads, counted by the
9
9
  * project-rollup rules (`done` is done only; `open` excludes cancelled).
10
+ *
11
+ * The SIGNAL column is the derived work band (`spaces/derive.ts`) — what the
12
+ * board is actually doing, as opposed to the status somebody declared on it.
13
+ * Sorting puts the boards that want a person first, which is the reason to run
14
+ * this from a terminal at all.
10
15
  */
11
16
  class SpacesTaskRollups extends base_command_1.BaseCommand {
12
17
  static description = 'Show task-status counts for every space you can view';
@@ -46,7 +51,11 @@ class SpacesTaskRollups extends base_command_1.BaseCommand {
46
51
  // A partial walk is not the whole picture; say so before the numbers.
47
52
  this.log(`Degraded: ${(response.warnings || []).join('; ') || 'some spaces were not counted'}`);
48
53
  }
49
- const rows = [...(response.rollups || [])].sort((a, b) => b.tasks.open - a.tasks.open);
54
+ // Most-urgent first: a parked agent, then a blocked board, then a stalled
55
+ // one, then the rest by open count. The band order is the shared one, so
56
+ // this list and the web sidebar rank the same boards the same way.
57
+ const bandRank = (rollup) => data_provider_1.SPACE_ACTIVITY_BAND_ORDER.indexOf((0, data_provider_1.spaceWorkBand)(rollup));
58
+ const rows = [...(response.rollups || [])].sort((a, b) => bandRank(a.tasks) - bandRank(b.tasks) || b.tasks.open - a.tasks.open);
50
59
  if (rows.length === 0) {
51
60
  this.log('No spaces with tasks.');
52
61
  return;
@@ -54,11 +63,13 @@ class SpacesTaskRollups extends base_command_1.BaseCommand {
54
63
  const label = (spaceId) => {
55
64
  const title = titles.get(spaceId);
56
65
  const text = title ? `${title} (${spaceId.slice(0, 8)})` : spaceId;
57
- return text.length > 48 ? `${text.slice(0, 45)}...` : text;
66
+ return text.length > 44 ? `${text.slice(0, 41)}...` : text;
58
67
  };
59
- this.log(`${'SPACE'.padEnd(48)} ${'OPEN'.padStart(5)} ${'DONE'.padStart(5)} ${'BLOCKED'.padStart(7)} ${'TOTAL'.padStart(5)}`);
68
+ this.log(`${'SPACE'.padEnd(44)} ${'SIGNAL'.padEnd(15)} ${'OPEN'.padStart(5)} ${'DONE'.padStart(5)} ${'BLOCKED'.padStart(7)} ${'TOTAL'.padStart(5)}`);
60
69
  for (const { spaceId, tasks } of rows) {
61
- this.log(`${label(spaceId).padEnd(48)} ${String(tasks.open).padStart(5)} ${String(tasks.done).padStart(5)} ` +
70
+ const band = (0, data_provider_1.spaceWorkBand)(tasks);
71
+ this.log(`${label(spaceId).padEnd(44)} ${(data_provider_1.SPACE_ACTIVITY_BAND_LABELS[band] ?? band).padEnd(15)} ` +
72
+ `${String(tasks.open).padStart(5)} ${String(tasks.done).padStart(5)} ` +
62
73
  `${String(tasks.blocked).padStart(7)} ${String(tasks.total).padStart(5)}`);
63
74
  }
64
75
  }
@@ -33,13 +33,73 @@ class SpacesWorkflowsLifecycleDiagnostics extends base_command_1.BaseCommand {
33
33
  `, ${response.stages.completed} completed` +
34
34
  ` (median ${Math.round(response.stages.durationMs.median / 1000)}s` +
35
35
  `, max ${Math.round(response.stages.durationMs.max / 1000)}s)`);
36
- this.log(` effects: ${response.effects.pending} pending` +
37
- ` (oldest ${Math.round(response.effects.oldestPendingMs / 1000)}s)` +
38
- `, ${response.effects.repairs} repaired`);
36
+ /*
37
+ * The server's effect counters NEST — `pending` is every effect still owed,
38
+ * `repairs` the owed ones that hit an error, `exhausted` the owed ones that
39
+ * stopped retrying — so they must be printed as parts of one number, never
40
+ * side by side. Side by side, one stopped effect read as "1 pending (oldest
41
+ * 8h), 1 repaired, 1 stopped retrying": three effects, one of them
42
+ * apparently fixed, when there was exactly one and nothing was fixed.
43
+ */
44
+ const { pending: owed, exhausted: stopped = 0, repairs: faulted } = response.effects;
45
+ const retrying = Math.max(0, owed - stopped);
46
+ this.log(owed
47
+ ? ` effects: ${owed} owed (oldest ${Math.round(response.effects.oldestPendingMs / 1000)}s)` +
48
+ ` — ${retrying} still retrying, ${stopped} stopped; ${faulted} hit an error`
49
+ : ' effects: none owed');
39
50
  this.log(` transitions: ${response.transitions.applied} applied` +
40
51
  `, ${response.transitions.gated} gated` +
41
52
  `, ${response.transitions.superseded} superseded`);
42
53
  this.log(` orphan stages: ${response.orphans}`);
54
+ /*
55
+ * WHAT TO DO. The counts above tell an operator how much; LC-13's bar is
56
+ * that each shape of "stuck" also names its repair, because "slow", "stuck"
57
+ * and "waiting for me" are three different facts and one undifferentiated
58
+ * number is not actionable. Only shapes that are actually present print,
59
+ * so a healthy Space says so instead of listing remedies for nothing.
60
+ */
61
+ // Diagnostics are aggregate by design, so every repair that needs a Task
62
+ // must say how to FIND that Task — and in the Space the operator just
63
+ // named, so the command runs as printed rather than as a template.
64
+ const space = response.spaceId;
65
+ // Every owed effect places the lifecycle's exceptional hold, so this one
66
+ // query finds the Tasks behind both retrying and stopped effects. Holds
67
+ // other authors placed show up too; the NEXT column says which is which.
68
+ const findHeld = `skrr tasks list --space ${space} --actionability-reason exceptional_hold`;
69
+ const actions = [];
70
+ const waitingOn = response.stages.waitingOn;
71
+ if (waitingOn?.human) {
72
+ actions.push(`${waitingOn.human} stage(s) waiting on a person — nothing is broken; a person owes a decision. ` +
73
+ `Find them: skrr tasks list --space ${space} --actionable-by human`);
74
+ }
75
+ if (waitingOn?.agent) {
76
+ actions.push(`${waitingOn.agent} stage(s) waiting on an agent — an agent owes a turn. ` +
77
+ `Find them: skrr tasks list --space ${space} --actionable-by agent`);
78
+ }
79
+ if (response.bindings.drifted) {
80
+ actions.push(`${response.bindings.drifted} binding(s) whose Task status disagrees with its stage — ` +
81
+ 'run "skrr tasks lifecycle reconcile <task-id>", or move the Task back to the stage\'s status');
82
+ }
83
+ if (stopped) {
84
+ actions.push(`${stopped} effect(s) stopped retrying — they will not run again on their own. Find them: ${findHeld}; ` +
85
+ '"skrr tasks lifecycle show <task-id>" names the error; fix the cause, then "skrr tasks lifecycle reconcile <task-id>"');
86
+ }
87
+ if (retrying) {
88
+ actions.push(`${retrying} effect(s) still retrying — usually transient. If they persist, find them: ${findHeld}; ` +
89
+ '"skrr tasks lifecycle show <task-id>" names the error');
90
+ }
91
+ if (response.orphans) {
92
+ actions.push(`${response.orphans} orphan stage(s) — a run waiting for a Task that moved on; ` +
93
+ '"skrr tasks lifecycle reconcile <task-id>" re-syncs it');
94
+ }
95
+ this.log('');
96
+ if (!actions.length) {
97
+ this.log('Nothing needs attention.');
98
+ return;
99
+ }
100
+ this.log('What to do:');
101
+ for (const action of actions)
102
+ this.log(` - ${action}`);
43
103
  }
44
104
  }
45
105
  exports.default = SpacesWorkflowsLifecycleDiagnostics;
@@ -4,10 +4,35 @@ const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../../base-command");
6
6
  const bulk_task_targets_1 = require("../../../lib/bulk-task-targets");
7
+ /**
8
+ * Parse `<agentId>:<actionId>`, splitting on the FIRST colon only.
9
+ *
10
+ * Agent ids never contain a colon; action ids can. This used to split on every
11
+ * colon and keep segments 0 and 1, so `<agent>:<action>:ANYTHING` silently
12
+ * attached `<agent>:<action>` — the caller asked for one thing and got another,
13
+ * exit 0 (OSK-9636). Keeping the remainder as the action id means an extra
14
+ * segment reaches the server as part of an id that does not exist, and is
15
+ * refused there.
16
+ *
17
+ * A space custom action's projected id is `space-custom:<id>`. Read as a ref it
18
+ * became agent `space-custom`, and the server correctly answered "Agent
19
+ * space-custom not found" — true of a string nobody meant as an agent id, and
20
+ * no help (OSK-9637). Those actions run from the space and are deliberately not
21
+ * attachable, so say that instead.
22
+ */
7
23
  function parseRef(input) {
8
- const [agentId, actionId] = input.split(':').map((s) => s.trim());
9
- if (!agentId || !actionId)
10
- return null;
24
+ const trimmed = input.trim();
25
+ const spaceCustom = `"${trimmed}" is a space custom action. Those run from the space and cannot be attached to a task — only agent quick actions can. List attachable ones with: skrr spaces actions available <space-id>`;
26
+ if ((0, data_provider_1.isSpaceCustomActionId)(trimmed))
27
+ return { problem: spaceCustom };
28
+ const colon = trimmed.indexOf(':');
29
+ const agentId = colon > 0 ? trimmed.slice(0, colon).trim() : '';
30
+ const actionId = colon > 0 ? trimmed.slice(colon + 1).trim() : '';
31
+ if (!agentId || !actionId) {
32
+ return { problem: `Invalid ref "${input}" — expected <agentId>:<actionId>.` };
33
+ }
34
+ if ((0, data_provider_1.isSpaceCustomActionId)(actionId))
35
+ return { problem: spaceCustom };
11
36
  return { agentId, actionId };
12
37
  }
13
38
  const refKey = (ref) => `${ref.agentId}:${ref.actionId}`;
@@ -55,11 +80,11 @@ class TasksActionsAttach extends base_command_1.BaseCommand {
55
80
  }
56
81
  const incoming = [];
57
82
  for (const raw of refPositionals) {
58
- const ref = parseRef(raw);
59
- if (!ref) {
60
- this.error(`Invalid ref "${raw}" — expected <agentId>:<actionId>.`, { exit: 1 });
83
+ const parsed = parseRef(raw);
84
+ if ('problem' in parsed) {
85
+ this.error(parsed.problem, { exit: 1 });
61
86
  }
62
- incoming.push(ref);
87
+ incoming.push(parsed);
63
88
  }
64
89
  if (incoming.length === 0) {
65
90
  this.error('At least one <agentId>:<actionId> ref is required. Discover refs with: skrr spaces actions available <space-id>', { exit: 1 });
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.describePendingReview = describePendingReview;
4
4
  const core_1 = require("@oclif/core");
5
5
  const base_command_1 = require("../../base-command");
6
+ const task_closure_1 = require("../../lib/task-closure");
6
7
  const api_fetch_1 = require("../../lib/api-fetch");
7
8
  const outbox_1 = require("../../lib/outbox");
8
9
  const prompt_1 = require("../../lib/prompt");
@@ -271,6 +272,18 @@ class TasksComplete extends base_command_1.BaseCommand {
271
272
  });
272
273
  if (pending)
273
274
  this.log(pending);
275
+ /*
276
+ * Say what was closed over, on the door that actually closes things.
277
+ *
278
+ * `tasks complete` reported nothing about the closure at all — no blockers,
279
+ * no `overrode` — so the person who forced past an owner-declared required
280
+ * Review was never told they had. Only `tasks move` read the field, and this
281
+ * is the more common way to finish a Task. Same renderer as `move` and
282
+ * `show`, so all three describe one close identically.
283
+ */
284
+ for (const sentence of (0, task_closure_1.closureSnapshotLines)(result.task?.resolution?.closure)) {
285
+ this.log(sentence);
286
+ }
274
287
  for (const advisory of result.advisories || [])
275
288
  this.warn(advisory.message);
276
289
  if (url)
@@ -240,7 +240,7 @@ class TasksCreate extends base_command_1.BaseCommand {
240
240
  'Omit to pin the definition’s current published version at create time.',
241
241
  }),
242
242
  'lifecycle-pack': core_1.Flags.string({
243
- description: 'Bind the created task to a built-in lifecycle pack (e.g. finding, doc-to-task). ' +
243
+ description: 'Bind the created task to a built-in lifecycle pack (finding, doc_to_task). ' +
244
244
  'Alternative to --lifecycle-definition.',
245
245
  }),
246
246
  };
@@ -16,10 +16,20 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
16
16
  // the retired vocabulary read as the intended path — which is how it kept
17
17
  // getting exercised.
18
18
  static summary = 'DEPRECATED compatibility spelling for `tasks updates add` — append a legacy task event';
19
+ // The FULL mapping, not a sample of it. The previous description named
20
+ // `handoff`/`checkpoint` and `started|lifecycle` and stopped — so a caller
21
+ // passing `--kind evidence` had no way to learn, before writing, that it
22
+ // becomes a `verification` Update. Those are different claims: `evidence`
23
+ // says a flow was exercised, `verification` says a claim was CHECKED, and the
24
+ // typed-Update vocabulary exists precisely so the two cannot be confused.
25
+ // Mirrors `LEGACY_KIND_TO_UPDATE_TYPE` in `api/server/services/TaskWorkEvent.js`.
19
26
  static description = 'DEPRECATED compatibility adapter for the historical Event vocabulary. Every kind is ' +
20
- 'stored as a canonical Update, so `--kind handoff` and `--kind checkpoint` both become ' +
21
- '`progress`, and `--kind started|lifecycle` writes only to the Activity ledger and will ' +
22
- 'NOT appear in `tasks updates list`. Use `tasks updates add` instead.';
27
+ 'stored as a canonical Update of a DIFFERENT name, so read this before relying on one: ' +
28
+ '`evidence` and `verification` both become `verification`; `checkpoint`, `changed`, ' +
29
+ '`completed` and `handoff` all become `progress`; `input_requested` and `failed` become ' +
30
+ '`blocker`; `decision` stays `decision`. `--kind started|lifecycle` writes only to the ' +
31
+ 'Activity ledger and will NOT appear in `tasks updates list`. Use `tasks updates add` ' +
32
+ 'instead, which writes the type you name.';
23
33
  static examples = [
24
34
  '<%= config.bin %> tasks events append <task-id> --kind checkpoint --idempotency-key "<task-id>:first-pass" --summary "Parser handles nested fences"',
25
35
  '<%= config.bin %> tasks events append <task-id> --kind decision --idempotency-key "<task-id>:storage-choice" --summary "Chose the CRDT path" --evidence "A direct contentHtml write is reverted by the next render tick"',
@@ -53,7 +63,7 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
53
63
  // `Invalid request data` / HTTP_400. (OSK-4888)
54
64
  kind: core_1.Flags.string({
55
65
  required: true,
56
- description: 'Event kind at a meaningful work boundary',
66
+ description: 'Legacy event kind. Stored as a canonical Update of a different name — see the mapping in the description above, and prefer `tasks updates add`',
57
67
  options: [...tasks_1.TASK_WORK_EVENT_KINDS],
58
68
  }),
59
69
  summary: core_1.Flags.string({ required: true, description: 'What is newly true, in one sentence' }),
@@ -108,8 +118,21 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
108
118
  if (written.canonicalType) {
109
119
  this.log(`Recorded on ${task.id} as a ${written.canonicalType} Update` +
110
120
  `${written.id ? ` (${written.id})` : ''}.`);
111
- this.log(`\`tasks events append\` is a compatibility spelling; \`${this.config.bin} tasks updates add\` ` +
112
- `is the canonical command, and \`${this.config.bin} tasks updates list\` reads them back.`);
121
+ /*
122
+ * Scope the claim to this KIND.
123
+ *
124
+ * "a compatibility spelling" reads as "same command, older name" — so a
125
+ * caller who has just used it successfully reaches for it again with a
126
+ * kind it does not take. The two vocabularies overlap in only three of
127
+ * fourteen values (`decision`, `blocker`, `verification`), and this line
128
+ * is printed on exactly those — confirming the general claim precisely
129
+ * where it happens to be true. `finding` and `progress` exist only on the
130
+ * canonical command; `evidence`, `checkpoint` and the rest only here.
131
+ */
132
+ this.log(`\`tasks events append --kind ${written.canonicalType}\` maps onto ` +
133
+ `\`${this.config.bin} tasks updates add --type ${written.canonicalType}\`, which is the ` +
134
+ 'canonical command. Their vocabularies differ otherwise — `--type ' +
135
+ 'progress|finding` exist only there — and `tasks updates list` reads them back.');
113
136
  return;
114
137
  }
115
138
  // `--kind started|lifecycle` produces no Update at all: it writes a
@@ -16,6 +16,7 @@ export default class TasksExpectationsAssess extends BaseCommand {
16
16
  'spec-version': import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
17
  'idempotency-key': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
18
  all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
19
+ 'include-unmet': import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
19
20
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
20
21
  };
21
22
  /**
@@ -43,7 +43,12 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
43
43
  'idempotency-key': core_1.Flags.string({ description: 'Stable retry key' }),
44
44
  all: core_1.Flags.boolean({
45
45
  description: 'Assess every Expectation still awaiting one on this Task, with the same decision ' +
46
- 'and reason. Already-settled Expectations are left alone.',
46
+ 'and reason. Already-settled Expectations are left alone, and ones already judged ' +
47
+ 'unmet are skipped unless --include-unmet.',
48
+ }),
49
+ 'include-unmet': core_1.Flags.boolean({
50
+ description: 'With --all, also re-assess Expectations somebody judged unmet — for after the work ' +
51
+ 'was fixed. Overwrites a considered rejection, so it is never implied.',
47
52
  }),
48
53
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
49
54
  };
@@ -56,11 +61,26 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
56
61
  * batch verb must never do. Overriding an existing assessment stays a
57
62
  * deliberate, single, named act.
58
63
  */
59
- async awaitingIds(taskId) {
64
+ async awaitingIds(taskId, { includeUnmet = false } = {}) {
60
65
  const acceptance = await data_provider_1.dataService.getTaskAcceptance(taskId);
61
- return acceptance.expectations
62
- .filter((expectation) => expectation.gating && !expectation.met && !expectation.assessmentId)
66
+ const gatingUnsettled = acceptance.expectations.filter((expectation) => expectation.gating && !expectation.met);
67
+ /*
68
+ * A row somebody judged `unmet` is SKIPPED by default and NAMED either way.
69
+ *
70
+ * Skipping is right: a blanket `--all --decision met` that swept up a
71
+ * reviewer's considered "no" is the one thing a batch verb must never do.
72
+ * Saying nothing about it was not: the row displayed as "needs assessment"
73
+ * (it no longer does), `--all` reported the Task settled, and the board
74
+ * could never be cleared because the only bulk verb silently declined to
75
+ * touch the only rows left.
76
+ */
77
+ const rejected = gatingUnsettled
78
+ .filter((expectation) => expectation.state === 'unmet')
63
79
  .map((expectation) => expectation.id);
80
+ const ids = gatingUnsettled
81
+ .filter((expectation) => !expectation.assessmentId || (includeUnmet && expectation.state === 'unmet'))
82
+ .map((expectation) => expectation.id);
83
+ return { ids, rejected };
64
84
  }
65
85
  async run() {
66
86
  this.requireAuth();
@@ -123,17 +143,35 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
123
143
  };
124
144
  if (flags.all) {
125
145
  let ids;
146
+ let rejected;
126
147
  try {
127
- ids = await this.awaitingIds(task.id);
148
+ ({ ids, rejected } = await this.awaitingIds(task.id, {
149
+ includeUnmet: flags['include-unmet'],
150
+ }));
128
151
  }
129
152
  catch (error) {
130
153
  this.handleApiError(error);
131
154
  return;
132
155
  }
156
+ const skippedRejections = flags['include-unmet'] ? [] : rejected;
133
157
  if (ids.length === 0) {
158
+ if (skippedRejections.length > 0) {
159
+ // "Nothing is awaiting assessment" was true of what this command
160
+ // WOULD touch and false about the Task, which is the distinction that
161
+ // made the board unclearable.
162
+ this.log(`Nothing is awaiting a first assessment, but ${skippedRejections.length} ` +
163
+ `Expectation(s) were judged unmet: ${skippedRejections.join(', ')}.`);
164
+ this.log(`Fix the work and re-assess ${skippedRejections.length === 1 ? 'it' : 'them'} by id, ` +
165
+ 'or re-run with --include-unmet to overwrite the rejection.');
166
+ return;
167
+ }
134
168
  this.log('Nothing is awaiting assessment on this Task.');
135
169
  return;
136
170
  }
171
+ if (skippedRejections.length > 0) {
172
+ this.log(`Skipping ${skippedRejections.length} Expectation(s) judged unmet ` +
173
+ `(${skippedRejections.join(', ')}) — pass --include-unmet to re-assess them.`);
174
+ }
137
175
  // Each write is reported on its own. A partial failure must leave the
138
176
  // caller knowing exactly which Expectations were settled, because the
139
177
  // remedy is to re-run for the rest — a single rolled-up "failed" would