@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,195 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SKILL_CONVENTION_DISCOVERY_SINCE = void 0;
4
+ exports.chooseDaemon = chooseDaemon;
5
+ exports.isUserInvocable = isUserInvocable;
6
+ exports.resolveLocalSkillRefs = resolveLocalSkillRefs;
7
+ exports.withSkillInvocations = withSkillInvocations;
8
+ exports.looksLikeSkillPath = looksLikeSkillPath;
9
+ exports.findLocalSkillsNamed = findLocalSkillsNamed;
10
+ exports.describeStoreSkillMiss = describeStoreSkillMiss;
11
+ exports.daemonPredates = daemonPredates;
12
+ const data_provider_1 = require("@skrr-ai/data-provider");
13
+ /**
14
+ * Which machine to ask, or why there is no single answer.
15
+ *
16
+ * Resolves rather than guesses: picking one of several machines and reporting
17
+ * its skills as "the" answer is the same silent narrowing the listing exists
18
+ * to remove.
19
+ */
20
+ function chooseDaemon(active) {
21
+ if (active.length === 1)
22
+ return { daemonId: active[0].daemonId };
23
+ if (active.length === 0) {
24
+ return {
25
+ problem: 'No connected machine. Start one with `skrr daemon start`, then try again.',
26
+ candidates: [],
27
+ };
28
+ }
29
+ return {
30
+ problem: `${active.length} machines are connected. Pick one with --daemon:`,
31
+ candidates: [...active],
32
+ };
33
+ }
34
+ /** A skill with no declared triggers is invocable both ways — the walker's default. */
35
+ function isUserInvocable(skill) {
36
+ return !skill.triggers || skill.triggers.includes('user');
37
+ }
38
+ /**
39
+ * Turn requested names into the references the server resolves.
40
+ *
41
+ * A request is `name`, or `<root>/<name>` when the same name exists under more
42
+ * than one root (`.claude/skills/review` vs `.agents/skills/review`). Each
43
+ * problem is reported in words that say what to type instead; nothing is
44
+ * dropped, because the server's own rule is that a skill the user picked and
45
+ * the turn did not load must fail the turn rather than run without it.
46
+ */
47
+ function resolveLocalSkillRefs(inventory, requested) {
48
+ const refs = [];
49
+ const problems = [];
50
+ const seen = new Set();
51
+ for (const raw of requested) {
52
+ const request = raw.trim().replace(/^\//, '');
53
+ const slash = request.lastIndexOf('/');
54
+ const root = slash > 0 ? request.slice(0, slash) : null;
55
+ const name = slash > 0 ? request.slice(slash + 1) : request;
56
+ const named = inventory.filter((skill) => skill.name === name);
57
+ const matches = root ? named.filter((skill) => skill.source === root) : named;
58
+ if (matches.length === 0) {
59
+ problems.push(named.length > 0
60
+ ? `No skill "${name}" under ${root}. It is under: ${named.map((s) => s.source ?? '?').join(', ')}.`
61
+ : `No skill named "${name}" on this machine. See \`skrr skills local\`.`);
62
+ continue;
63
+ }
64
+ if (matches.length > 1) {
65
+ problems.push(`"${name}" exists under ${matches.length} roots. Name one: ` +
66
+ matches.map((s) => `--local-skill ${s.source ?? '?'}/${name}`).join(' | '));
67
+ continue;
68
+ }
69
+ const [skill] = matches;
70
+ if (!isUserInvocable(skill)) {
71
+ problems.push(`"${name}" declares it is for the agent to reach for, not to invoke (triggers: model).`);
72
+ continue;
73
+ }
74
+ if (seen.has(skill.pathHash))
75
+ continue;
76
+ seen.add(skill.pathHash);
77
+ refs.push({ name: skill.name, pathHash: skill.pathHash });
78
+ }
79
+ return { refs, problems };
80
+ }
81
+ /**
82
+ * The prompt with a `/name` for every invoked skill it does not already name.
83
+ *
84
+ * The web composer sends the `/name` in the text beside the reference, and
85
+ * drops a reference whose `/name` is gone; a CLI turn carries the same pair so
86
+ * the agent reads the same invocation either way.
87
+ */
88
+ function withSkillInvocations(prompt, refs) {
89
+ const missing = refs.filter((ref) => !new RegExp(`(^|\\s)/${ref.name.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}(?=\\s|$)`).test(prompt));
90
+ if (missing.length === 0)
91
+ return prompt;
92
+ return `${missing.map((ref) => `/${ref.name}`).join(' ')} ${prompt}`;
93
+ }
94
+ /** Whether a skill argument names a place on disk rather than a store id. */
95
+ function looksLikeSkillPath(arg) {
96
+ return /[\\/]/.test(arg) || arg.startsWith('.') || arg.startsWith('~');
97
+ }
98
+ /**
99
+ * Where a skill with this name exists on the caller's connected machines.
100
+ *
101
+ * Best-effort by design: it runs only to word an error that is already going
102
+ * to be reported, so a machine that cannot be asked contributes nothing rather
103
+ * than replacing the real error with a second one.
104
+ */
105
+ async function findLocalSkillsNamed(name) {
106
+ const bare = name.replace(/^\//, '');
107
+ let active = [];
108
+ try {
109
+ active = (await data_provider_1.dataService.getDaemons()).active ?? [];
110
+ }
111
+ catch {
112
+ return [];
113
+ }
114
+ const sightings = await Promise.all(active.map(async (daemon) => {
115
+ try {
116
+ const { skills } = await data_provider_1.dataService.getDaemonSkills(daemon.daemonId);
117
+ return (skills ?? [])
118
+ .filter((skill) => skill.name === bare)
119
+ .map((skill) => ({
120
+ daemonId: daemon.daemonId,
121
+ deviceName: daemon.deviceName,
122
+ source: skill.source,
123
+ }));
124
+ }
125
+ catch {
126
+ return [];
127
+ }
128
+ }));
129
+ return sightings.flat();
130
+ }
131
+ /**
132
+ * The error for a skill argument a STORE command could not use.
133
+ *
134
+ * `skills install <name>` and `skills details <name>` answered a local skill's
135
+ * name or path with "Skill not found / NOT_FOUND": true of the store, and no
136
+ * help to someone the product had just told it reads their `.claude/skills`
137
+ * (OSK-9412). The answer depends on what the argument was, so it says which:
138
+ * a path (never a store id), a name found on a connected machine (needs no
139
+ * install), or neither (look the id up in the store).
140
+ */
141
+ function describeStoreSkillMiss(arg, sightings, command) {
142
+ const verb = command === 'install' ? 'installs' : 'shows';
143
+ if (looksLikeSkillPath(arg)) {
144
+ return [
145
+ `\`skills ${command}\` ${verb} a skill from the store by its id; "${arg}" is a path.`,
146
+ "A skill in one of your machine's skill roots needs no install.",
147
+ '→ See what your machine has: skrr skills local',
148
+ '→ Use one on a turn: skrr agents chat <agent-id> --local-skill <name>',
149
+ ].join('\n');
150
+ }
151
+ if (sightings.length > 0) {
152
+ const where = sightings
153
+ .map((s) => `${s.daemonId}${s.deviceName ? ` (${s.deviceName})` : ''}${s.source ? `, ${s.source}` : ''}`)
154
+ .join('; ');
155
+ return [
156
+ `No store skill has the id "${arg}", but a skill named "${arg}" is on your machine: ${where}.`,
157
+ 'It needs no install.',
158
+ `→ Use it on a turn: skrr agents chat <agent-id> --local-skill ${arg.replace(/^\//, '')}`,
159
+ ].join('\n');
160
+ }
161
+ return [
162
+ `No store skill has the id "${arg}".`,
163
+ '→ Find a store id: skrr skills search <term>',
164
+ '→ Skills on your machine are not in the store: skrr skills local',
165
+ ].join('\n');
166
+ }
167
+ /**
168
+ * The first daemon release whose walker scans the skill conventions — the
169
+ * project's `.claude/skills`, `.agents/skills`, `.devin/skills`,
170
+ * `.windsurf/skills` and the global `~/.claude/skills`. It arrived in
171
+ * `e9dfa732cd` and first shipped in 0.8.44; an older daemon scans only
172
+ * `claudeSession.skillsDir`, which is empty unless someone set it by hand.
173
+ */
174
+ exports.SKILL_CONVENTION_DISCOVERY_SINCE = '0.8.44';
175
+ /**
176
+ * Whether a daemon's reported version is older than `floor`.
177
+ *
178
+ * Unknown or unparseable versions answer false: this decides only whether to
179
+ * add an explanation, and claiming an unknown daemon is old would be a guess.
180
+ */
181
+ function daemonPredates(version, floor) {
182
+ const parse = (value) => {
183
+ const match = /^(\d+)\.(\d+)\.(\d+)/.exec(String(value ?? '').trim());
184
+ return match ? match.slice(1).map(Number) : null;
185
+ };
186
+ const have = parse(version);
187
+ const need = parse(floor);
188
+ if (!have || !need)
189
+ return false;
190
+ for (let i = 0; i < 3; i += 1) {
191
+ if (have[i] !== need[i])
192
+ return have[i] < need[i];
193
+ }
194
+ return false;
195
+ }
@@ -59,6 +59,16 @@ export interface Machine {
59
59
  identityKind: MachineIdentityKind;
60
60
  name: string;
61
61
  status: 'online' | 'offline' | 'degraded';
62
+ /**
63
+ * One sentence naming what decided the status, or null when nothing needs
64
+ * explaining. Optional because the CLI ships separately from the server.
65
+ *
66
+ * `degraded` alone is unactionable, and on a laptop it is usually permanent —
67
+ * one offline row for a provider that was uninstalled holds the verdict
68
+ * forever, because supersession needs a NEWER row for the same provider and
69
+ * none will ever arrive.
70
+ */
71
+ statusReason?: string | null;
62
72
  machineUuid: string | null;
63
73
  stableDaemonId: string | null;
64
74
  daemonId: string | null;
@@ -135,14 +145,4 @@ export declare function listMachines(): Promise<Machine[]>;
135
145
  export declare function getMachine(machineId: string): Promise<Machine | null>;
136
146
  export declare function updateMachine(machineId: string, patch: MachinePreferencesPatch): Promise<Machine | null>;
137
147
  export declare function disconnectMachine(machineId: string): Promise<Record<string, unknown> | null>;
138
- /**
139
- * Resolve a user-supplied reference to exactly one machine.
140
- *
141
- * A machine is addressable by several ids (`id`, `machineUuid`,
142
- * `stableDaemonId`, `daemonId`) and `skrr harnesses list` prints a TRUNCATED
143
- * uuid, so the id a user has to hand is often a prefix of the one the API
144
- * wants. Resolving here keeps every command accepting the same references, and
145
- * makes an ambiguous prefix an error rather than a silent pick of the first
146
- * match.
147
- */
148
148
  export declare function resolveMachineRef(machines: Machine[], ref: string): Machine;
@@ -93,13 +93,35 @@ async function disconnectMachine(machineId) {
93
93
  * makes an ambiguous prefix an error rather than a silent pick of the first
94
94
  * match.
95
95
  */
96
+ /**
97
+ * The daemon ids a hosted machine's sandbox id can appear inside.
98
+ *
99
+ * `machines hosted list` prints `sbs_…`; the machine row carries
100
+ * `hosted-machine-sbs_…` as its daemon id. Composed rather than matched by
101
+ * substring so a longer id cannot be resolved by a shorter one's text.
102
+ */
103
+ function hostedSandboxDaemonIds(ref) {
104
+ if (!ref.startsWith('sbs_'))
105
+ return [];
106
+ return [`hosted-machine-${ref}`, `hosted-job-machine-${ref}`];
107
+ }
96
108
  function resolveMachineRef(machines, ref) {
97
109
  const needle = ref.trim();
98
110
  const lower = needle.toLowerCase();
99
111
  const exact = machines.filter((m) => m.id === needle ||
100
112
  m.machineUuid === needle ||
101
113
  m.stableDaemonId === needle ||
102
- m.daemonId === needle);
114
+ m.daemonId === needle ||
115
+ // The sandbox id, which is the ONLY id `machines hosted list` prints.
116
+ // One hosted machine carries five identifiers and, before this, the one
117
+ // its own list showed you was the one `machines show` refused: the
118
+ // sandbox id appears here only INSIDE the daemon id, as
119
+ // `hosted-machine-<sbs_…>`, while `machines list --all` shows the same
120
+ // box as `lease:rtl_…` — a different random string entirely. Two lists,
121
+ // two unrelated-looking ids, no way on screen to tell they are one
122
+ // machine. Exact composition, not a substring search, so this cannot
123
+ // match a machine that merely contains the text.
124
+ hostedSandboxDaemonIds(needle).some((candidate) => m.daemonId === candidate));
103
125
  if (exact.length === 1)
104
126
  return exact[0];
105
127
  const byName = machines.filter((m) => m.name?.toLowerCase() === lower);
@@ -0,0 +1,19 @@
1
+ /**
2
+ * The search term, from a positional or `--query`, resolved the same way by
3
+ * both spellings of the same search.
4
+ *
5
+ * `skills search` and `search skills` run the identical store search. Neither
6
+ * accepted the obvious invocation — `skrr skills search git` — so the single
7
+ * most natural way to call a search command failed on both, and `--query` was
8
+ * `required` on both, which is what made it fail rather than prompt.
9
+ *
10
+ * Shared rather than copied because the two spellings staying in step is the
11
+ * whole point: the reason a reader could not tell them apart was that they had
12
+ * drifted (different one-liners, short flags on one of them), and a second copy
13
+ * of this is how that starts again.
14
+ */
15
+ export declare function resolveSearchQuery(cmd: {
16
+ error: (message: string, options?: {
17
+ exit?: number;
18
+ }) => never;
19
+ }, positional: string | undefined, flag: string | undefined, command: string): string;
@@ -0,0 +1,35 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveSearchQuery = resolveSearchQuery;
4
+ /**
5
+ * The search term, from a positional or `--query`, resolved the same way by
6
+ * both spellings of the same search.
7
+ *
8
+ * `skills search` and `search skills` run the identical store search. Neither
9
+ * accepted the obvious invocation — `skrr skills search git` — so the single
10
+ * most natural way to call a search command failed on both, and `--query` was
11
+ * `required` on both, which is what made it fail rather than prompt.
12
+ *
13
+ * Shared rather than copied because the two spellings staying in step is the
14
+ * whole point: the reason a reader could not tell them apart was that they had
15
+ * drifted (different one-liners, short flags on one of them), and a second copy
16
+ * of this is how that starts again.
17
+ */
18
+ function resolveSearchQuery(cmd, positional, flag, command) {
19
+ const fromPositional = positional?.trim();
20
+ const fromFlag = flag?.trim();
21
+ // Both given and DIFFERENT is a real ambiguity — refuse rather than pick.
22
+ // Both given and identical is a script that passes belt and braces; that is
23
+ // not an error and silently accepting it costs nobody anything.
24
+ if (fromPositional && fromFlag && fromPositional !== fromFlag) {
25
+ cmd.error(`Two different search terms: "${fromPositional}" and --query "${fromFlag}". Pass one.`, { exit: 2 });
26
+ }
27
+ const query = fromPositional || fromFlag;
28
+ if (!query) {
29
+ // `--query` used to be `required`, so oclif refused with its own generic
30
+ // sentence. Naming the shape people actually type is the point of the
31
+ // change, so the error names it too.
32
+ cmd.error(`What should I search for? Try \`skrr ${command} <term>\`.`, { exit: 2 });
33
+ }
34
+ return query;
35
+ }
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Shared shape for `skrr spaces actions add|update`.
3
+ *
4
+ * Both commands describe the same stored object, and they had drifted from the
5
+ * server that validates it (`spaceCustomActionSchema` in api/server/routes/spaces.js).
6
+ */
7
+ import type { SpaceCustomAction, SpaceMemberActionsResponse } from '@skrr-ai/data-provider';
8
+ /**
9
+ * The server's limit on `icon`, which is `z.string().max(4)`.
10
+ *
11
+ * The flag used to be documented as "Lucide icon name". It is not one: the web
12
+ * settings page renders `action.icon` as literal text, and almost no Lucide name
13
+ * fits in four characters, so following the help produced a 400 with a raw Zod
14
+ * message and no hint that an emoji was wanted (OSK-9631).
15
+ *
16
+ * Zod's `max` counts UTF-16 code units, not visible characters, so the check
17
+ * below uses `String.prototype.length` to agree with it exactly. Counting
18
+ * graphemes instead would accept a family emoji the server then refuses.
19
+ */
20
+ export declare const SPACE_CUSTOM_ACTION_ICON_MAX = 4;
21
+ export declare const spaceCustomActionIconFlag: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ export declare const spaceCustomActionLabelFlag: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ /** Refuse an icon the server would refuse, with a message that says what fits. */
24
+ export declare function spaceCustomActionIconProblem(icon: unknown): string | null;
25
+ type SpaceWithActions = {
26
+ id: string;
27
+ title?: string;
28
+ leadAgentId?: string | null;
29
+ customActions?: SpaceCustomAction[] | null;
30
+ };
31
+ /**
32
+ * Why a space's OWN actions cannot run, or null when they can.
33
+ *
34
+ * A space custom action runs as a task assigned to the Space Leader, so on a
35
+ * space with no Leader every one of them is dead on arrival. That fact used to
36
+ * live only in a tooltip on a disabled web button: `spaces actions add` reported
37
+ * success, `spaces actions list` listed the action happily, and nothing on the
38
+ * CLI said it could never run (OSK-9641).
39
+ */
40
+ export declare function spaceActionsNotRunnableReason(space: Pick<SpaceWithActions, 'id' | 'leadAgentId'>, bin: string): string | null;
41
+ /**
42
+ * Find a space custom action by the id `spaces actions list` prints, or by the
43
+ * `space-custom:<id>` form the projected quick-action surfaces use.
44
+ */
45
+ export declare function findSpaceCustomAction(space: SpaceWithActions, ref: string): {
46
+ customAction: SpaceCustomAction;
47
+ index: number;
48
+ } | {
49
+ problem: string;
50
+ };
51
+ export type SpaceQuickActionRow = {
52
+ source: 'space' | 'agent';
53
+ title: string;
54
+ owner: string;
55
+ type: string;
56
+ ref: string;
57
+ run: string;
58
+ };
59
+ /**
60
+ * Everything a space's action panel offers, in the order the web shows it: the
61
+ * space's own actions first, then the actions of member agents the caller can
62
+ * view, filtered by scope with the same predicate the web and server use.
63
+ *
64
+ * `spaces actions list` showed only the first half and `spaces actions
65
+ * available` a wider second half (every member agent, including ones the caller
66
+ * cannot run), so "what can I run here?" had no answer on the CLI (OSK-9641).
67
+ * Every row's `run` is a command `spaces actions run` accepts.
68
+ */
69
+ export declare function spaceQuickActionRows(space: SpaceWithActions & {
70
+ labelIds?: string[] | null;
71
+ }, members: SpaceMemberActionsResponse['agents'], bin: string): SpaceQuickActionRow[];
72
+ export {};
@@ -0,0 +1,117 @@
1
+ "use strict";
2
+ /**
3
+ * Shared shape for `skrr spaces actions add|update`.
4
+ *
5
+ * Both commands describe the same stored object, and they had drifted from the
6
+ * server that validates it (`spaceCustomActionSchema` in api/server/routes/spaces.js).
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.spaceCustomActionLabelFlag = exports.spaceCustomActionIconFlag = exports.SPACE_CUSTOM_ACTION_ICON_MAX = void 0;
10
+ exports.spaceCustomActionIconProblem = spaceCustomActionIconProblem;
11
+ exports.spaceActionsNotRunnableReason = spaceActionsNotRunnableReason;
12
+ exports.findSpaceCustomAction = findSpaceCustomAction;
13
+ exports.spaceQuickActionRows = spaceQuickActionRows;
14
+ const core_1 = require("@oclif/core");
15
+ const data_provider_1 = require("@skrr-ai/data-provider");
16
+ /**
17
+ * The server's limit on `icon`, which is `z.string().max(4)`.
18
+ *
19
+ * The flag used to be documented as "Lucide icon name". It is not one: the web
20
+ * settings page renders `action.icon` as literal text, and almost no Lucide name
21
+ * fits in four characters, so following the help produced a 400 with a raw Zod
22
+ * message and no hint that an emoji was wanted (OSK-9631).
23
+ *
24
+ * Zod's `max` counts UTF-16 code units, not visible characters, so the check
25
+ * below uses `String.prototype.length` to agree with it exactly. Counting
26
+ * graphemes instead would accept a family emoji the server then refuses.
27
+ */
28
+ exports.SPACE_CUSTOM_ACTION_ICON_MAX = 4;
29
+ exports.spaceCustomActionIconFlag = core_1.Flags.string({
30
+ description: `Emoji shown on the button, e.g. ✨ (at most ${exports.SPACE_CUSTOM_ACTION_ICON_MAX} UTF-16 units). Not an icon name.`,
31
+ });
32
+ exports.spaceCustomActionLabelFlag = core_1.Flags.string({
33
+ description: 'Button text shown in the space. Not a Label entity — to offer an agent action in every space carrying a Label, use `agents actions create --label`.',
34
+ });
35
+ /** Refuse an icon the server would refuse, with a message that says what fits. */
36
+ function spaceCustomActionIconProblem(icon) {
37
+ if (icon === undefined || icon === null)
38
+ return null;
39
+ if (typeof icon !== 'string')
40
+ return '--icon must be text.';
41
+ if (icon.length > exports.SPACE_CUSTOM_ACTION_ICON_MAX) {
42
+ return (`--icon "${icon}" is too long: it is shown as text on the button and must be an emoji ` +
43
+ `(at most ${exports.SPACE_CUSTOM_ACTION_ICON_MAX} UTF-16 units), e.g. --icon ✨. ` +
44
+ 'Icon names such as "Sparkles" are not accepted here.');
45
+ }
46
+ return null;
47
+ }
48
+ /**
49
+ * Why a space's OWN actions cannot run, or null when they can.
50
+ *
51
+ * A space custom action runs as a task assigned to the Space Leader, so on a
52
+ * space with no Leader every one of them is dead on arrival. That fact used to
53
+ * live only in a tooltip on a disabled web button: `spaces actions add` reported
54
+ * success, `spaces actions list` listed the action happily, and nothing on the
55
+ * CLI said it could never run (OSK-9641).
56
+ */
57
+ function spaceActionsNotRunnableReason(space, bin) {
58
+ if (space.leadAgentId)
59
+ return null;
60
+ return (`This space has no Space Leader, so its own actions cannot run: each runs as a task ` +
61
+ `assigned to the Leader. Set one with \`${bin} spaces update ${space.id} --lead-agent <agent-id>\`.`);
62
+ }
63
+ /**
64
+ * Find a space custom action by the id `spaces actions list` prints, or by the
65
+ * `space-custom:<id>` form the projected quick-action surfaces use.
66
+ */
67
+ function findSpaceCustomAction(space, ref) {
68
+ const actions = space.customActions ?? [];
69
+ const id = (0, data_provider_1.spaceCustomActionSourceId)(ref) ?? ref;
70
+ const index = actions.findIndex((action) => action.id === id);
71
+ if (index >= 0)
72
+ return { customAction: actions[index], index };
73
+ if (actions.length === 0) {
74
+ return { problem: `Space ${space.id} has no custom actions.` };
75
+ }
76
+ return {
77
+ problem: `No custom action ${ref} in space ${space.id}. It has: ` +
78
+ actions.map((action) => `${action.id} (${action.label})`).join(', ') +
79
+ '.',
80
+ };
81
+ }
82
+ /**
83
+ * Everything a space's action panel offers, in the order the web shows it: the
84
+ * space's own actions first, then the actions of member agents the caller can
85
+ * view, filtered by scope with the same predicate the web and server use.
86
+ *
87
+ * `spaces actions list` showed only the first half and `spaces actions
88
+ * available` a wider second half (every member agent, including ones the caller
89
+ * cannot run), so "what can I run here?" had no answer on the CLI (OSK-9641).
90
+ * Every row's `run` is a command `spaces actions run` accepts.
91
+ */
92
+ function spaceQuickActionRows(space, members, bin) {
93
+ const subject = { spaceId: space.id, labelIds: space.labelIds ?? [] };
94
+ const leaderMissing = !space.leadAgentId;
95
+ return [
96
+ ...(space.customActions ?? []).map((action) => ({
97
+ source: 'space',
98
+ title: action.label,
99
+ owner: 'Space Leader',
100
+ type: 'prompt',
101
+ ref: action.id,
102
+ run: leaderMissing
103
+ ? '(needs a Space Leader)'
104
+ : `${bin} spaces actions run ${space.id} ${action.id}`,
105
+ })),
106
+ ...members.flatMap(({ agent }) => (agent.agentActions ?? [])
107
+ .filter((action) => (0, data_provider_1.isAgentActionAvailableInSpace)(action, subject))
108
+ .map((action) => ({
109
+ source: 'agent',
110
+ title: action.title,
111
+ owner: agent.name ?? agent.id,
112
+ type: (0, data_provider_1.isBuiltinAgentAction)(action) ? `builtin:${action.builtinType}` : 'prompt',
113
+ ref: `${agent.id}:${action.id}`,
114
+ run: `${bin} spaces actions run ${space.id} ${agent.id}:${action.id}`,
115
+ }))),
116
+ ];
117
+ }
@@ -1,23 +1,3 @@
1
- /**
2
- * Space reference resolution — turn what a person typed into exactly one
3
- * space id, or an actionable failure.
4
- *
5
- * Every `--space` flag in the CLI (35 commands at last count) took a raw UUID
6
- * and nothing else. Passing a name did not error — it filtered on a UUID that
7
- * matches no space and returned an empty result:
8
- *
9
- * skrr tasks list --space "Always on" → "No tasks."
10
- *
11
- * A person retries. An agent concludes the space is empty and proceeds on a
12
- * false premise, which is the failure mode this module exists to remove. The
13
- * contract is deliberately narrow: a value that already looks like an id is
14
- * forwarded untouched (no request, no behavior change for existing scripts);
15
- * anything else must resolve to exactly one space or the command fails.
16
- *
17
- * Modeled on `assignee-resolver.ts`, which already does this for people and
18
- * agents — `--assignee-user frank` has worked for a while. Spaces were the
19
- * outlier, not the exception.
20
- */
21
1
  export type SpaceRef = {
22
2
  /** The space UUID — what every `--space` flag ultimately wants. */
23
3
  id: string;
@@ -36,7 +16,7 @@ export type SpaceResolutionFailure = {
36
16
  * hides. Distinct from NOT_FOUND because the fix is different:
37
17
  * pass --include-archived (or the id), not a different name.
38
18
  */
39
- code: 'NOT_FOUND' | 'AMBIGUOUS' | 'ARCHIVED';
19
+ code: 'NOT_FOUND' | 'AMBIGUOUS' | 'ARCHIVED' | 'NOT_A_REFERENCE';
40
20
  query: string;
41
21
  candidates: SpaceRef[];
42
22
  };
@@ -1,4 +1,15 @@
1
1
  "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SPACE_CLI_ERROR_CODE = void 0;
4
+ exports.looksLikeSpaceId = looksLikeSpaceId;
5
+ exports.looksLikeCanonicalSpaceId = looksLikeCanonicalSpaceId;
6
+ exports.normalizeSpaceRef = normalizeSpaceRef;
7
+ exports.spaceRefFrom = spaceRefFrom;
8
+ exports.resolveSpaceRef = resolveSpaceRef;
9
+ exports.describeSpace = describeSpace;
10
+ exports.describeSpaceCandidates = describeSpaceCandidates;
11
+ exports.formatSpaceResolutionFailure = formatSpaceResolutionFailure;
12
+ const failed_lookup_1 = require("./failed-lookup");
2
13
  /**
3
14
  * Space reference resolution — turn what a person typed into exactly one
4
15
  * space id, or an actionable failure.
@@ -19,16 +30,6 @@
19
30
  * agents — `--assignee-user frank` has worked for a while. Spaces were the
20
31
  * outlier, not the exception.
21
32
  */
22
- Object.defineProperty(exports, "__esModule", { value: true });
23
- exports.SPACE_CLI_ERROR_CODE = void 0;
24
- exports.looksLikeSpaceId = looksLikeSpaceId;
25
- exports.looksLikeCanonicalSpaceId = looksLikeCanonicalSpaceId;
26
- exports.normalizeSpaceRef = normalizeSpaceRef;
27
- exports.spaceRefFrom = spaceRefFrom;
28
- exports.resolveSpaceRef = resolveSpaceRef;
29
- exports.describeSpace = describeSpace;
30
- exports.describeSpaceCandidates = describeSpaceCandidates;
31
- exports.formatSpaceResolutionFailure = formatSpaceResolutionFailure;
32
33
  const UUID_RE = /^[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}$/;
33
34
  const OBJECT_ID_RE = /^[0-9a-fA-F]{24}$/;
34
35
  /**
@@ -106,6 +107,11 @@ function resolveSpaceRef(query, spaces) {
106
107
  if (!q) {
107
108
  return { ok: false, code: 'NOT_FOUND', query: raw, candidates: spaces };
108
109
  }
110
+ // A failed lookup's output is refused before the name tiers can match it.
111
+ // See `failed-lookup.ts`.
112
+ if ((0, failed_lookup_1.isFailedLookupValue)(raw)) {
113
+ return { ok: false, code: 'NOT_A_REFERENCE', query: raw, candidates: [] };
114
+ }
109
115
  // An exact id or ref wins over everything, archived or not: both are
110
116
  // explicit and unambiguous, so there is nothing to protect the caller from.
111
117
  // Names are matched against live spaces only — see the ARCHIVED branch.
@@ -159,6 +165,8 @@ function describeSpaceCandidates(candidates, limit = 10) {
159
165
  /** One human-readable block per failure, suitable for a CLI error body. */
160
166
  function formatSpaceResolutionFailure(failure, bin = 'skrr') {
161
167
  switch (failure.code) {
168
+ case 'NOT_A_REFERENCE':
169
+ return (0, failed_lookup_1.describeFailedLookupValue)(failure.query, 'Space');
162
170
  case 'AMBIGUOUS':
163
171
  return (`"${failure.query}" matches ${failure.candidates.length} spaces:\n` +
164
172
  `${describeSpaceCandidates(failure.candidates)}\n` +
@@ -178,4 +186,5 @@ exports.SPACE_CLI_ERROR_CODE = {
178
186
  AMBIGUOUS: 'SPACE_AMBIGUOUS',
179
187
  ARCHIVED: 'SPACE_ARCHIVED',
180
188
  NOT_FOUND: 'SPACE_NOT_FOUND',
189
+ NOT_A_REFERENCE: 'SPACE_NOT_A_REFERENCE',
181
190
  };
@@ -28,5 +28,6 @@ export type ClosureLike = TaskClosureEvaluation | {
28
28
  * the whole point is to be able to act without a second lookup.
29
29
  */
30
30
  export declare function closureReport(closure: ClosureLike): string[];
31
- /** `met (agent)` vs `met (you)` — whose judgement settled it, never hidden. */
32
- export declare function describeAssessor(kind: string | null | undefined): string;
31
+ /** `met (agent)` vs `met (person)` — shared with the web through data-provider. */
32
+ export { describeAssessorKind as describeAssessor } from '@skrr-ai/data-provider';
33
+ export { describeClosure as closureSnapshotLines, type TaskClosureSnapshot as ClosureSnapshot, } from '@skrr-ai/data-provider';