@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
@@ -0,0 +1,160 @@
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
+ const format_1 = require("../../lib/format");
7
+ const local_skills_1 = require("../../lib/local-skills");
8
+ /**
9
+ * The skills that are ON THIS MACHINE.
10
+ *
11
+ * Every other command under `skills` is about the STORE or a cloud scope —
12
+ * `browse`, `library`, `install`, `invocable`. Not one of them reads the
13
+ * filesystem, and the topic's own description ("Browse and install skills from
14
+ * the skill store") names the store, so the one topic named after the thing was
15
+ * about something else entirely. A machine with 79 discovered skills across
16
+ * seven roots could not show one line of them through `skrr`, and
17
+ * `skills invocable` — the command whose description promises breadth —
18
+ * answered `0 total` beside them.
19
+ *
20
+ * The inventory already existed and was already published: the daemon walks its
21
+ * roots at registration (`reportableSkills`), the row lands in
22
+ * `daemon:active:*`, and `GET /api/daemons/:id/skills` serves it. This reads
23
+ * that. Nothing new is collected.
24
+ *
25
+ * It is also the repo's own rule rather than a preference: "the CLI is how a
26
+ * headless operator and a locally-running coding agent touch the platform, so
27
+ * an endpoint the API exposes must be reachable from `skrr`."
28
+ *
29
+ * WHAT THIS IS NOT: the list a `/`-menu selection resolves against. That walk
30
+ * happens at session start from the SESSION's effective cwd, which may be a
31
+ * worktree or an ephemeral workspace; this one is the daemon's own cwd. They
32
+ * usually agree and are not required to, so the header says which machine and
33
+ * directory the answer is about rather than implying it is the only one.
34
+ */
35
+ class SkillsLocal extends base_command_1.BaseCommand {
36
+ static description = 'List the skills discovered on a machine, from its daemon';
37
+ static examples = [
38
+ '<%= config.bin %> skills local',
39
+ '<%= config.bin %> skills local --daemon <daemon-id>',
40
+ '<%= config.bin %> skills local --root .claude/skills',
41
+ '<%= config.bin %> skills local --json',
42
+ ];
43
+ static flags = {
44
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
45
+ quiet: core_1.Flags.boolean({ description: 'Suppress non-essential output' }),
46
+ daemon: core_1.Flags.string({
47
+ description: 'Which machine to ask. Defaults to your only connected daemon.',
48
+ }),
49
+ root: core_1.Flags.string({
50
+ description: 'Only skills found under this convention root, e.g. .claude/skills',
51
+ }),
52
+ triggers: core_1.Flags.string({
53
+ description: 'Only skills with this trigger',
54
+ options: ['user', 'model'],
55
+ }),
56
+ };
57
+ async run() {
58
+ this.requireAuth();
59
+ const { flags } = await this.parse(SkillsLocal);
60
+ let daemonId = flags.daemon;
61
+ if (!daemonId) {
62
+ // Resolve rather than guess. Naming the ambiguity beats picking one of
63
+ // several machines and reporting its skills as "the" answer.
64
+ let daemons;
65
+ try {
66
+ daemons = await data_provider_1.dataService.getDaemons();
67
+ }
68
+ catch (err) {
69
+ this.handleApiError(err);
70
+ return;
71
+ }
72
+ const choice = (0, local_skills_1.chooseDaemon)(daemons.active ?? []);
73
+ if (!('daemonId' in choice)) {
74
+ // A usage error, on stderr: printed to stdout it was the whole output of
75
+ // `--json`, which a caller then failed to parse.
76
+ const listed = choice.candidates.map((d) => ` --daemon ${d.daemonId}${d.deviceName ? ` (${d.deviceName})` : ''}`);
77
+ this.error([choice.problem, ...listed].join('\n'), { exit: 2 });
78
+ }
79
+ daemonId = choice.daemonId;
80
+ }
81
+ let response;
82
+ try {
83
+ response = await data_provider_1.dataService.getDaemonSkills(daemonId);
84
+ }
85
+ catch (err) {
86
+ this.handleApiError(err);
87
+ return;
88
+ }
89
+ let skills = response.skills ?? [];
90
+ if (flags.root)
91
+ skills = skills.filter((s) => s.source === flags.root);
92
+ if (flags.triggers) {
93
+ const want = flags.triggers;
94
+ // A skill with no declared triggers is invocable both ways — that is the
95
+ // walker's own default when frontmatter is silent, not an absence.
96
+ skills = skills.filter((s) => want === 'user' ? (0, local_skills_1.isUserInvocable)(s) : !s.triggers || s.triggers.includes(want));
97
+ }
98
+ if (flags.json) {
99
+ this.log(JSON.stringify({
100
+ daemonId,
101
+ online: response.online,
102
+ version: response.version ?? null,
103
+ // An empty `data` from a daemon too old to look is not "no skills".
104
+ discoversConventions: response.version
105
+ ? !(0, local_skills_1.daemonPredates)(response.version, local_skills_1.SKILL_CONVENTION_DISCOVERY_SINCE)
106
+ : null,
107
+ data: skills,
108
+ }, null, 2));
109
+ return;
110
+ }
111
+ if (flags.quiet)
112
+ return;
113
+ if (skills.length === 0) {
114
+ // Three different states, and a bare "none" would collapse them.
115
+ if (!response.online) {
116
+ this.log(`${daemonId} is not connected, so its inventory may be out of date. None listed.`);
117
+ }
118
+ else if ((0, local_skills_1.daemonPredates)(response.version, local_skills_1.SKILL_CONVENTION_DISCOVERY_SINCE)) {
119
+ // Checked before the filter and "add a SKILL.md" branches: from a daemon
120
+ // this old, an empty list says nothing about what is on the machine, and
121
+ // telling the user to add files it cannot see sent them in a circle
122
+ // (OSK-9630 — 20 SKILL.md files on disk, "No skills discovered").
123
+ this.log(`No skills listed — but this daemon (${response.version}) cannot see them. ` +
124
+ `Discovery from .claude/skills, .agents/skills, .devin/skills, .windsurf/skills and ` +
125
+ `~/.claude/skills arrived in ${local_skills_1.SKILL_CONVENTION_DISCOVERY_SINCE}. Update the daemon ` +
126
+ `(one that came with the desktop app updates with the app), then run ` +
127
+ `\`${this.config.bin} daemon restart\`.`);
128
+ }
129
+ else if (flags.root || flags.triggers) {
130
+ this.log('No skills match that filter on this machine.');
131
+ }
132
+ else {
133
+ this.log('No skills discovered on this machine. Put a SKILL.md in one of the conventions ' +
134
+ '(.claude/skills, .agents/skills, .devin/skills, .windsurf/skills) and reconnect.');
135
+ }
136
+ return;
137
+ }
138
+ const rows = skills.map((s) => ({
139
+ name: String(s.name),
140
+ // Where it came from. Two skills of the same name in different roots are
141
+ // otherwise indistinguishable, which is the same gap the `/` menu had.
142
+ root: String(s.source ?? '-'),
143
+ scope: String(s.scope ?? '-'),
144
+ // `user` belongs in the `/` menu; `model` is the agent's to reach for.
145
+ triggers: (s.triggers ?? ['user', 'model']).join('+'),
146
+ description: String(s.description ?? ''),
147
+ }));
148
+ this.log(`${skills.length} skill${skills.length === 1 ? '' : 's'} on ${daemonId}` +
149
+ (response.deviceName ? ` (${response.deviceName})` : '') +
150
+ (response.online ? '' : ' — NOT CONNECTED, this inventory may be stale'));
151
+ (0, format_1.renderTable)(rows, [
152
+ { key: 'name', header: 'NAME' },
153
+ { key: 'root', header: 'ROOT' },
154
+ { key: 'scope', header: 'SCOPE' },
155
+ { key: 'triggers', header: 'TRIGGERS' },
156
+ { key: 'description', header: 'DESCRIPTION', maxWidth: 52 },
157
+ ], (line) => this.log(line));
158
+ }
159
+ }
160
+ exports.default = SkillsLocal;
@@ -2,9 +2,19 @@ import { BaseCommand } from '../../base-command';
2
2
  export default class SkillsSearch extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
+ /**
6
+ * `skills search` and `search skills` are the SAME search, and both spellings
7
+ * stay. Neither said so, their one-liners differed, and only one carried short
8
+ * flags — so a reader picked one at random with no way to tell whether they had
9
+ * picked a deprecated spelling. Duplication that is deliberate should say so;
10
+ * duplication that is silent reads as a mistake either way.
11
+ */
12
+ static args: {
13
+ query: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
5
15
  static flags: {
6
16
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
- query: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
17
+ query: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
18
  category: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
19
  limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
10
20
  };
@@ -4,26 +4,47 @@ 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 format_1 = require("../../lib/format");
7
+ const search_query_1 = require("../../lib/search-query");
7
8
  class SkillsSearch extends base_command_1.BaseCommand {
8
- static description = 'Search skills in the store by keyword';
9
+ static description = 'Search skills in the skrr store by keyword (same as `search skills`)';
9
10
  static examples = [
10
- '<%= config.bin %> skills search --query "summarize"',
11
- '<%= config.bin %> skills search --query "image" --category creative',
12
- '<%= config.bin %> skills search --query "data" --limit 10 --json',
11
+ '<%= config.bin %> skills search summarize',
12
+ '<%= config.bin %> skills search image --category creative',
13
+ '<%= config.bin %> skills search data --limit 10 --json',
14
+ '<%= config.bin %> skills search --query summarize',
13
15
  ];
16
+ /**
17
+ * `skills search` and `search skills` are the SAME search, and both spellings
18
+ * stay. Neither said so, their one-liners differed, and only one carried short
19
+ * flags — so a reader picked one at random with no way to tell whether they had
20
+ * picked a deprecated spelling. Duplication that is deliberate should say so;
21
+ * duplication that is silent reads as a mistake either way.
22
+ */
23
+ static args = {
24
+ query: core_1.Args.string({
25
+ description: 'Search term (same value as --query; pass either, not both)',
26
+ required: false,
27
+ ignoreStdin: true,
28
+ }),
29
+ };
14
30
  static flags = {
15
31
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
16
- query: core_1.Flags.string({ description: 'Search query', required: true }),
32
+ // No longer `required` — the positional satisfies it, and keeping the flag
33
+ // means every existing script and doc line stays valid. Same rule
34
+ // `messages list` adopted after `command messages:list:<id> not found`
35
+ // read like the command did not exist.
36
+ query: core_1.Flags.string({ char: 'q', description: 'Search query' }),
17
37
  category: core_1.Flags.string({ description: 'Filter by category' }),
18
38
  limit: core_1.Flags.integer({ description: 'Maximum number of results', default: 20 }),
19
39
  };
20
40
  async run() {
21
41
  this.requireAuth();
22
- const { flags } = await this.parse(SkillsSearch);
42
+ const { args, flags } = await this.parse(SkillsSearch);
43
+ const query = (0, search_query_1.resolveSearchQuery)(this, args.query, flags.query, 'skills search');
23
44
  let response;
24
45
  try {
25
46
  response = await data_provider_1.dataService.getStoreSkills({
26
- q: flags.query,
47
+ q: query,
27
48
  category: flags.category,
28
49
  limit: flags.limit,
29
50
  });
@@ -38,7 +59,7 @@ class SkillsSearch extends base_command_1.BaseCommand {
38
59
  const r = response;
39
60
  const items = Array.isArray(r) ? r : (r?.data ?? []);
40
61
  if (items.length === 0) {
41
- this.log(`No skills found matching '${flags.query}'.`);
62
+ this.log(`No skills found matching '${query}'.`);
42
63
  return;
43
64
  }
44
65
  const rows = items.map((s) => ({
@@ -1,10 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
6
7
  const prompt_1 = require("../../../lib/prompt");
7
8
  const space_output_1 = require("../../../lib/space-output");
9
+ const space_custom_actions_1 = require("../../../lib/space-custom-actions");
8
10
  class SpacesActionsAdd extends base_command_1.BaseCommand {
9
11
  static description = 'Add a custom action to a space';
10
12
  static args = {
@@ -13,11 +15,11 @@ class SpacesActionsAdd extends base_command_1.BaseCommand {
13
15
  static flags = {
14
16
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
15
17
  'from-json': core_1.Flags.string({ description: 'JSON action body' }),
16
- id: core_1.Flags.string({ description: 'Action ID' }),
17
- label: core_1.Flags.string({ description: 'Button label' }),
18
+ id: core_1.Flags.string({ description: 'Action ID (generated when omitted)' }),
19
+ label: space_custom_actions_1.spaceCustomActionLabelFlag,
18
20
  prompt: core_1.Flags.string({ description: 'Prompt sent when the action runs' }),
19
21
  color: core_1.Flags.string({ description: 'Color hex' }),
20
- icon: core_1.Flags.string({ description: 'Lucide icon name' }),
22
+ icon: space_custom_actions_1.spaceCustomActionIconFlag,
21
23
  skill: core_1.Flags.string({ description: 'Optional skill ID' }),
22
24
  };
23
25
  async run() {
@@ -31,7 +33,10 @@ class SpacesActionsAdd extends base_command_1.BaseCommand {
31
33
  this.error(err.message, { exit: 1 });
32
34
  }
33
35
  const payload = (0, prompt_1.mergeDefined)(body, {
34
- id: flags.id,
36
+ // Generated when omitted, as `agents actions create` does. Demanding the
37
+ // caller invent an id — with only "Action id is required." to go on — was
38
+ // the one required field nothing else could supply.
39
+ id: flags.id ?? (typeof body.id === 'string' && body.id ? body.id : `act_${(0, node_crypto_1.randomUUID)()}`),
35
40
  label: flags.label,
36
41
  prompt: flags.prompt,
37
42
  color: flags.color,
@@ -41,6 +46,9 @@ class SpacesActionsAdd extends base_command_1.BaseCommand {
41
46
  for (const field of ['id', 'label', 'prompt', 'color'])
42
47
  if (!payload[field] || typeof payload[field] !== 'string')
43
48
  this.error(`Action ${field} is required.`, { exit: 1 });
49
+ const iconProblem = (0, space_custom_actions_1.spaceCustomActionIconProblem)(payload.icon);
50
+ if (iconProblem)
51
+ this.error(iconProblem, { exit: 1 });
44
52
  let result;
45
53
  try {
46
54
  result = await data_provider_1.dataService.addSpaceCustomAction({
@@ -51,10 +59,20 @@ class SpacesActionsAdd extends base_command_1.BaseCommand {
51
59
  catch (err) {
52
60
  this.handleApiError(err);
53
61
  }
62
+ // `--json` returns the action that was added, not the whole Space. The
63
+ // server answers with the full Space document — 117 lines in which the
64
+ // five-field action is buried — while `agents actions create --json`
65
+ // returns just the action. A create returns the thing it created.
66
+ const stored = (result?.customActions ?? []).find((action) => action.id === payload.id) ?? payload;
54
67
  if (flags.json)
55
- this.log(JSON.stringify((0, space_output_1.withSpaceResourceUrls)(this.cliConfig.baseURL, args.id, 'actions', result, ['id']), null, 2));
68
+ this.log(JSON.stringify((0, space_output_1.withSpaceResourceUrls)(this.cliConfig.baseURL, args.id, 'actions', stored, ['id']), null, 2));
56
69
  else
57
70
  this.log(`Added custom action ${String(payload.id)}.`);
71
+ // On stderr, so `--json` stays parseable: the action was saved, and still
72
+ // cannot run until the space has a Leader (OSK-9641).
73
+ const unrunnable = (0, space_custom_actions_1.spaceActionsNotRunnableReason)({ id: args.id, leadAgentId: result?.leadAgentId }, this.config.bin);
74
+ if (unrunnable)
75
+ this.warn(unrunnable);
58
76
  }
59
77
  }
60
78
  exports.default = SpacesActionsAdd;
@@ -51,7 +51,10 @@ class SpacesActionsAvailable extends base_command_1.BaseCommand {
51
51
  { key: 'ref', header: 'REF (agentId:actionId)' },
52
52
  ], (line) => this.log(line));
53
53
  this.log('');
54
+ // The REF is for two commands, and they need different rights: attaching
55
+ // works for any space member, running needs VIEW on the agent (OSK-9663).
54
56
  this.log('Attach to a task: skrr tasks actions attach <task-id> <agentId>:<actionId>');
57
+ this.log(`Run one in this space: skrr spaces actions run ${args.id} <agentId>:<actionId> (needs access to the agent; see what you can run with: skrr spaces actions list ${args.id} --all)`);
55
58
  }
56
59
  }
57
60
  exports.default = SpacesActionsAvailable;
@@ -4,8 +4,13 @@ export default class SpacesActionsList extends BaseCommand {
4
4
  static args: {
5
5
  id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
6
6
  };
7
+ static examples: string[];
7
8
  static flags: {
8
9
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ all: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
9
11
  };
10
12
  run(): Promise<void>;
13
+ /** Say, on stderr, when none of the listed space actions can run. */
14
+ private noteUnrunnable;
15
+ private listAll;
11
16
  }
@@ -4,13 +4,23 @@ 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 format_1 = require("../../../lib/format");
7
+ const space_custom_actions_1 = require("../../../lib/space-custom-actions");
7
8
  const space_output_1 = require("../../../lib/space-output");
8
9
  class SpacesActionsList extends base_command_1.BaseCommand {
9
10
  static description = 'List custom actions configured for a space';
10
11
  static args = {
11
12
  id: core_1.Args.string({ description: 'Space ID', required: true, ignoreStdin: true }),
12
13
  };
13
- static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
14
+ static examples = [
15
+ '<%= config.bin %> spaces actions list <space-id>',
16
+ '<%= config.bin %> spaces actions list <space-id> --all',
17
+ ];
18
+ static flags = {
19
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
20
+ all: core_1.Flags.boolean({
21
+ description: "Everything the space's action panel offers: its own actions and those of member agents you can view, each with the command that runs it",
22
+ }),
23
+ };
14
24
  async run() {
15
25
  this.requireAuth();
16
26
  const { args, flags } = await this.parse(SpacesActionsList);
@@ -22,12 +32,17 @@ class SpacesActionsList extends base_command_1.BaseCommand {
22
32
  this.handleApiError(err);
23
33
  }
24
34
  const actions = space.customActions ?? [];
35
+ if (flags.all)
36
+ return this.listAll(space, flags.json);
25
37
  if (flags.json)
26
38
  return void this.log(JSON.stringify((0, space_output_1.withSpaceResourceUrls)(this.cliConfig.baseURL, args.id, 'actions', { data: actions }, [
27
39
  'id',
28
40
  ]), null, 2));
29
- if (!actions.length)
30
- return void this.log('No custom actions.');
41
+ if (!actions.length) {
42
+ this.log('No custom actions.');
43
+ this.log(`Agent actions offered here: ${this.config.bin} spaces actions list ${args.id} --all`);
44
+ return;
45
+ }
31
46
  (0, format_1.renderTable)(actions.map((a) => ({
32
47
  id: a.id,
33
48
  label: a.label,
@@ -41,6 +56,37 @@ class SpacesActionsList extends base_command_1.BaseCommand {
41
56
  { key: 'skill', header: 'SKILL' },
42
57
  { key: 'id', header: 'ID' },
43
58
  ], (line) => this.log(line));
59
+ this.noteUnrunnable(space);
60
+ this.log(`Agent actions offered here too: ${this.config.bin} spaces actions list ${args.id} --all`);
61
+ }
62
+ /** Say, on stderr, when none of the listed space actions can run. */
63
+ noteUnrunnable(space) {
64
+ const reason = (0, space_custom_actions_1.spaceActionsNotRunnableReason)(space, this.config.bin);
65
+ if (reason)
66
+ this.warn(reason);
67
+ }
68
+ async listAll(space, json) {
69
+ let members;
70
+ try {
71
+ members = await data_provider_1.dataService.listSpaceMemberActions(space.id);
72
+ }
73
+ catch (err) {
74
+ this.handleApiError(err);
75
+ }
76
+ const rows = (0, space_custom_actions_1.spaceQuickActionRows)(space, members.agents, this.config.bin);
77
+ if (json)
78
+ return void this.log(JSON.stringify({ spaceId: space.id, data: rows }, null, 2));
79
+ if (!rows.length)
80
+ return void this.log('This space offers no quick actions.');
81
+ (0, format_1.renderTable)(rows, [
82
+ { key: 'source', header: 'SOURCE' },
83
+ { key: 'title', header: 'TITLE', maxWidth: 36 },
84
+ { key: 'owner', header: 'RUNS AS', maxWidth: 24 },
85
+ { key: 'type', header: 'TYPE' },
86
+ { key: 'run', header: 'RUN' },
87
+ ], (line) => this.log(line));
88
+ if (rows.some((row) => row.source === 'space'))
89
+ this.noteUnrunnable(space);
44
90
  }
45
91
  }
46
92
  exports.default = SpacesActionsList;
@@ -0,0 +1,28 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * Fire one of a space's quick actions, the way its Actions tab does.
4
+ *
5
+ * `spaces actions add` could create a space action and nothing on the CLI could
6
+ * run it (OSK-9641), and a space member handed `agentId:actionId` refs by
7
+ * `spaces actions available` had only `agents actions run`, which answers a
8
+ * member with 403 and opens a chat rather than working in the space (OSK-9663).
9
+ *
10
+ * The routing is the web runner's (`useSpaceQuickActionRunner`), not a new one:
11
+ * a prompt action runs as a task in this space, assigned to the Space Leader for
12
+ * a space's own action and to its agent for an agent's; a built-in action runs
13
+ * through the agent-action route. The task text comes from the same
14
+ * `spaceQuickActionTaskSpec` the web uses, so both surfaces hand the agent the
15
+ * same work.
16
+ */
17
+ export default class SpacesActionsRun extends BaseCommand {
18
+ static description: string;
19
+ static examples: string[];
20
+ static args: {
21
+ space: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
22
+ action: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
23
+ };
24
+ static flags: {
25
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
+ };
27
+ run(): Promise<void>;
28
+ }
@@ -0,0 +1,146 @@
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
+ const space_custom_actions_1 = require("../../../lib/space-custom-actions");
7
+ const web_url_1 = require("../../../lib/web-url");
8
+ /**
9
+ * Fire one of a space's quick actions, the way its Actions tab does.
10
+ *
11
+ * `spaces actions add` could create a space action and nothing on the CLI could
12
+ * run it (OSK-9641), and a space member handed `agentId:actionId` refs by
13
+ * `spaces actions available` had only `agents actions run`, which answers a
14
+ * member with 403 and opens a chat rather than working in the space (OSK-9663).
15
+ *
16
+ * The routing is the web runner's (`useSpaceQuickActionRunner`), not a new one:
17
+ * a prompt action runs as a task in this space, assigned to the Space Leader for
18
+ * a space's own action and to its agent for an agent's; a built-in action runs
19
+ * through the agent-action route. The task text comes from the same
20
+ * `spaceQuickActionTaskSpec` the web uses, so both surfaces hand the agent the
21
+ * same work.
22
+ */
23
+ class SpacesActionsRun extends base_command_1.BaseCommand {
24
+ static description = "Run a space quick action: the space's own (by id) or a member agent's (agentId:actionId)";
25
+ static examples = [
26
+ '<%= config.bin %> spaces actions run <space-id> <action-id>',
27
+ '<%= config.bin %> spaces actions run <space-id> <agent-id>:<action-id> --json',
28
+ ];
29
+ static args = {
30
+ space: core_1.Args.string({ description: 'Space ID', required: true, ignoreStdin: true }),
31
+ action: core_1.Args.string({
32
+ description: "A space action id from 'spaces actions list', or agentId:actionId, both as 'spaces actions list --all' prints them",
33
+ required: true,
34
+ ignoreStdin: true,
35
+ }),
36
+ };
37
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
38
+ async run() {
39
+ this.requireAuth();
40
+ const { args, flags } = await this.parse(SpacesActionsRun);
41
+ const bin = this.config.bin;
42
+ let space;
43
+ try {
44
+ space = await data_provider_1.dataService.getSpace(args.space);
45
+ }
46
+ catch (err) {
47
+ this.handleApiError(err);
48
+ }
49
+ const colon = args.action.indexOf(':');
50
+ const agentRef = colon > 0 && !args.action.startsWith('space-custom:')
51
+ ? { agentId: args.action.slice(0, colon), actionId: args.action.slice(colon + 1) }
52
+ : null;
53
+ let action;
54
+ let assigneeAgentId;
55
+ if (!agentRef) {
56
+ const found = (0, space_custom_actions_1.findSpaceCustomAction)(space, args.action);
57
+ if ('problem' in found)
58
+ this.error(found.problem, { exit: 4 });
59
+ const leaderProblem = (0, space_custom_actions_1.spaceActionsNotRunnableReason)(space, bin);
60
+ if (leaderProblem || !space.leadAgentId)
61
+ this.error(leaderProblem ?? '', { exit: 1 });
62
+ action = (0, data_provider_1.spaceCustomActionToAgentAction)(found.customAction, space, found.index);
63
+ assigneeAgentId = space.leadAgentId;
64
+ }
65
+ else {
66
+ let members;
67
+ try {
68
+ members = await data_provider_1.dataService.listSpaceMemberActions(args.space);
69
+ }
70
+ catch (err) {
71
+ this.handleApiError(err);
72
+ }
73
+ const member = members.agents.find(({ agent }) => agent.id === agentRef.agentId);
74
+ if (!member) {
75
+ this.error(`Agent ${agentRef.agentId} is not a member of this space that you can view, so you cannot ` +
76
+ `run its actions here. Attaching one to a task needs only the space: ` +
77
+ `\`${bin} tasks actions attach <task-id> ${args.action}\`. To run it, its owner has to share the agent with you.`, { exit: 1 });
78
+ }
79
+ const found = (member.agent.agentActions ?? []).find((a) => a.id === agentRef.actionId);
80
+ if (!found) {
81
+ this.error(`No action ${agentRef.actionId} on agent ${agentRef.agentId}. List what this space offers: \`${bin} spaces actions list ${args.space} --all\`.`, { exit: 4 });
82
+ }
83
+ if (!(0, data_provider_1.isAgentActionAvailableInSpace)(found, { spaceId: space.id, labelIds: space.labelIds })) {
84
+ this.error(`Action ${found.title} is not offered in this space (its scope is another space or a Label this space does not carry).`, { exit: 1 });
85
+ }
86
+ action = found;
87
+ assigneeAgentId = agentRef.agentId;
88
+ }
89
+ if ((0, data_provider_1.isBuiltinAgentAction)(action)) {
90
+ // A built-in is a typed platform operation owned by its agent, and runs
91
+ // through the agent-action route exactly as the web runner sends it.
92
+ let result;
93
+ try {
94
+ result = await data_provider_1.dataService.runAgentAction({
95
+ agent_id: assigneeAgentId,
96
+ action_id: action.id,
97
+ });
98
+ }
99
+ catch (err) {
100
+ this.handleApiError(err);
101
+ }
102
+ if (flags.json)
103
+ return void this.log(JSON.stringify(result, null, 2));
104
+ if (!result.ok)
105
+ this.error(result.error || result.reason || 'Action failed.', { exit: 1 });
106
+ this.log(`${action.title} ran.`);
107
+ return;
108
+ }
109
+ const outcome = (0, data_provider_1.agentActionOutcome)(action);
110
+ const spec = (0, data_provider_1.spaceQuickActionTaskSpec)(action, { id: space.id, title: space.title });
111
+ let created;
112
+ try {
113
+ created = await data_provider_1.dataService.createAndStartTask({
114
+ ...spec,
115
+ spaceId: space.id,
116
+ assigneeAgentIds: [assigneeAgentId],
117
+ });
118
+ }
119
+ catch (err) {
120
+ this.handleApiError(err, { subject: `Action ${action.title}` });
121
+ }
122
+ const taskUrl = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, created.task);
123
+ if (flags.json) {
124
+ this.log(JSON.stringify({ ...created, outcome, assigneeAgentId, ...(taskUrl ? { taskUrl } : {}) }, null, 2));
125
+ }
126
+ else {
127
+ const identifier = created.task.identifier ?? created.task.id;
128
+ this.log(created.started
129
+ ? `Started ${action.title} as task ${identifier}.`
130
+ : `Created task ${identifier} for ${action.title}, but it did not start: ${created.startError ?? 'no reason given'}.`);
131
+ if (taskUrl)
132
+ this.log(`Task: ${taskUrl}`);
133
+ if (created.conversationId)
134
+ this.log(`Conversation: ${created.conversationId}`);
135
+ if (created.started && outcome === 'ephemeral') {
136
+ // The web hides an ephemeral action's task when the run finishes, from
137
+ // the tab that fired it. The CLI does not wait for the run, so say what
138
+ // will be different rather than let the task's staying look like a bug.
139
+ this.log(`This action is ephemeral. The web app hides its task when the run finishes; from the CLI it stays until you hide it: \`${bin} tasks move ${created.task.id} --status hidden\`.`);
140
+ }
141
+ }
142
+ if (!created.started)
143
+ this.exit(1);
144
+ }
145
+ }
146
+ exports.default = SpacesActionsRun;