@skrr-ai/cli 0.1.46 → 0.1.48

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 (305) hide show
  1. package/README.md +1 -1
  2. package/dist/base-command.d.ts +18 -0
  3. package/dist/base-command.js +61 -1
  4. package/dist/commands/agents/chat.d.ts +21 -0
  5. package/dist/commands/agents/chat.js +26 -9
  6. package/dist/commands/balance/usage/breakdown.d.ts +14 -0
  7. package/dist/commands/balance/usage/breakdown.js +68 -0
  8. package/dist/commands/balance/usage.js +24 -0
  9. package/dist/commands/code/handover.d.ts +2 -2
  10. package/dist/commands/code/handover.js +12 -13
  11. package/dist/commands/code/install.js +31 -0
  12. package/dist/commands/code/update.d.ts +46 -0
  13. package/dist/commands/code/update.js +242 -0
  14. package/dist/commands/commerce/entitlements/explain.d.ts +22 -0
  15. package/dist/commands/commerce/entitlements/explain.js +40 -0
  16. package/dist/commands/commerce/incidents/audit.d.ts +20 -0
  17. package/dist/commands/commerce/incidents/audit.js +37 -0
  18. package/dist/commands/commerce/incidents/list.d.ts +22 -0
  19. package/dist/commands/commerce/incidents/list.js +49 -0
  20. package/dist/commands/commerce/incidents/resolve.d.ts +23 -0
  21. package/dist/commands/commerce/incidents/resolve.js +45 -0
  22. package/dist/commands/commerce/incidents/retry.d.ts +22 -0
  23. package/dist/commands/commerce/incidents/retry.js +45 -0
  24. package/dist/commands/commerce/incidents/show.d.ts +13 -0
  25. package/dist/commands/commerce/incidents/show.js +30 -0
  26. package/dist/commands/commerce/offers/approve.d.ts +17 -0
  27. package/dist/commands/commerce/offers/approve.js +33 -0
  28. package/dist/commands/commerce/offers/create.d.ts +18 -0
  29. package/dist/commands/commerce/offers/create.js +34 -0
  30. package/dist/commands/commerce/offers/publish.d.ts +13 -0
  31. package/dist/commands/commerce/offers/publish.js +28 -0
  32. package/dist/commands/commerce/offers/retire.d.ts +17 -0
  33. package/dist/commands/commerce/offers/retire.js +32 -0
  34. package/dist/commands/commerce/offers/validate.d.ts +18 -0
  35. package/dist/commands/commerce/offers/validate.js +33 -0
  36. package/dist/commands/commerce/products/create.d.ts +10 -0
  37. package/dist/commands/commerce/products/create.js +25 -0
  38. package/dist/commands/commerce/promotions/redeem.d.ts +13 -0
  39. package/dist/commands/commerce/promotions/redeem.js +32 -0
  40. package/dist/commands/commerce/terms/accept.d.ts +18 -0
  41. package/dist/commands/commerce/terms/accept.js +39 -0
  42. package/dist/commands/commerce/terms/show.d.ts +10 -0
  43. package/dist/commands/commerce/terms/show.js +34 -0
  44. package/dist/commands/commerce/trials/conversion-preview.d.ts +18 -0
  45. package/dist/commands/commerce/trials/conversion-preview.js +39 -0
  46. package/dist/commands/commerce/trials/start.d.ts +23 -0
  47. package/dist/commands/commerce/trials/start.js +58 -0
  48. package/dist/commands/commitments/accounting.d.ts +39 -0
  49. package/dist/commands/commitments/accounting.js +116 -0
  50. package/dist/commands/commitments/action-history.d.ts +1 -0
  51. package/dist/commands/commitments/action-history.js +8 -1
  52. package/dist/commands/commitments/case.d.ts +1 -0
  53. package/dist/commands/commitments/case.js +19 -3
  54. package/dist/commands/commitments/cases.d.ts +1 -0
  55. package/dist/commands/commitments/cases.js +11 -1
  56. package/dist/commands/commitments/checks.d.ts +1 -0
  57. package/dist/commands/commitments/checks.js +5 -1
  58. package/dist/commands/commitments/cost.d.ts +62 -0
  59. package/dist/commands/commitments/cost.js +130 -0
  60. package/dist/commands/commitments/create.js +1 -1
  61. package/dist/commands/commitments/delete.d.ts +1 -0
  62. package/dist/commands/commitments/delete.js +56 -6
  63. package/dist/commands/commitments/feedback.js +9 -1
  64. package/dist/commands/commitments/handover.js +16 -0
  65. package/dist/commands/commitments/hypotheses.d.ts +1 -0
  66. package/dist/commands/commitments/hypotheses.js +17 -2
  67. package/dist/commands/commitments/links/list.js +4 -1
  68. package/dist/commands/commitments/liveness.d.ts +18 -0
  69. package/dist/commands/commitments/liveness.js +80 -0
  70. package/dist/commands/commitments/metrics.d.ts +1 -0
  71. package/dist/commands/commitments/metrics.js +8 -1
  72. package/dist/commands/commitments/next-tick.d.ts +39 -0
  73. package/dist/commands/commitments/next-tick.js +122 -0
  74. package/dist/commands/commitments/observations.d.ts +1 -0
  75. package/dist/commands/commitments/observations.js +19 -2
  76. package/dist/commands/commitments/pause.js +12 -0
  77. package/dist/commands/commitments/receipt.d.ts +2 -0
  78. package/dist/commands/commitments/receipt.js +16 -2
  79. package/dist/commands/commitments/remediation-proposal.js +6 -2
  80. package/dist/commands/commitments/remediation-proposals.js +5 -1
  81. package/dist/commands/commitments/restore.d.ts +25 -0
  82. package/dist/commands/commitments/restore.js +46 -0
  83. package/dist/commands/commitments/show.js +32 -0
  84. package/dist/commands/commitments/trigger-status.js +33 -0
  85. package/dist/commands/commitments/update.d.ts +1 -0
  86. package/dist/commands/commitments/update.js +31 -5
  87. package/dist/commands/commitments/wake-capabilities.d.ts +0 -1
  88. package/dist/commands/commitments/wake-capabilities.js +8 -4
  89. package/dist/commands/convos/handover.d.ts +1 -0
  90. package/dist/commands/convos/handover.js +4 -0
  91. package/dist/commands/daemon/index.js +2 -0
  92. package/dist/commands/daemon/update.d.ts +19 -0
  93. package/dist/commands/daemon/update.js +48 -0
  94. package/dist/commands/enterprise/budgets/create.d.ts +11 -0
  95. package/dist/commands/enterprise/budgets/create.js +27 -0
  96. package/dist/commands/enterprise/budgets/evaluate.d.ts +17 -0
  97. package/dist/commands/enterprise/budgets/evaluate.js +32 -0
  98. package/dist/commands/enterprise/budgets/publish.d.ts +14 -0
  99. package/dist/commands/enterprise/budgets/publish.js +30 -0
  100. package/dist/commands/enterprise/contracts/activate.d.ts +14 -0
  101. package/dist/commands/enterprise/contracts/activate.js +30 -0
  102. package/dist/commands/enterprise/contracts/create.d.ts +14 -0
  103. package/dist/commands/enterprise/contracts/create.js +32 -0
  104. package/dist/commands/enterprise/contracts/issue.d.ts +18 -0
  105. package/dist/commands/enterprise/contracts/issue.js +34 -0
  106. package/dist/commands/enterprise/domains/begin.d.ts +20 -0
  107. package/dist/commands/enterprise/domains/begin.js +39 -0
  108. package/dist/commands/enterprise/domains/verify.d.ts +14 -0
  109. package/dist/commands/enterprise/domains/verify.js +32 -0
  110. package/dist/commands/enterprise/external-config/activate.d.ts +22 -0
  111. package/dist/commands/enterprise/external-config/activate.js +49 -0
  112. package/dist/commands/enterprise/external-config/set.d.ts +25 -0
  113. package/dist/commands/enterprise/external-config/set.js +123 -0
  114. package/dist/commands/enterprise/external-config/show.d.ts +12 -0
  115. package/dist/commands/enterprise/external-config/show.js +39 -0
  116. package/dist/commands/enterprise/external-config/suspend.d.ts +13 -0
  117. package/dist/commands/enterprise/external-config/suspend.js +41 -0
  118. package/dist/commands/enterprise/finance/close-snapshot.d.ts +11 -0
  119. package/dist/commands/enterprise/finance/close-snapshot.js +28 -0
  120. package/dist/commands/enterprise/finance/record-document.d.ts +18 -0
  121. package/dist/commands/enterprise/finance/record-document.js +35 -0
  122. package/dist/commands/enterprise/profile.d.ts +20 -0
  123. package/dist/commands/enterprise/profile.js +38 -0
  124. package/dist/commands/login.d.ts +13 -0
  125. package/dist/commands/login.js +77 -0
  126. package/dist/commands/machines/dedicated/index.js +3 -0
  127. package/dist/commands/machines/dedicated/service/apply.d.ts +23 -0
  128. package/dist/commands/machines/dedicated/service/apply.js +99 -0
  129. package/dist/commands/machines/dedicated/service/approve.d.ts +23 -0
  130. package/dist/commands/machines/dedicated/service/approve.js +52 -0
  131. package/dist/commands/machines/dedicated/service/create.d.ts +55 -0
  132. package/dist/commands/machines/dedicated/service/create.js +222 -0
  133. package/dist/commands/machines/dedicated/service/edit.d.ts +30 -0
  134. package/dist/commands/machines/dedicated/service/edit.js +146 -0
  135. package/dist/commands/machines/dedicated/service/exec.d.ts +26 -0
  136. package/dist/commands/machines/dedicated/service/exec.js +92 -0
  137. package/dist/commands/machines/dedicated/service/export.d.ts +29 -0
  138. package/dist/commands/machines/dedicated/service/export.js +106 -0
  139. package/dist/commands/machines/dedicated/service/index.d.ts +14 -0
  140. package/dist/commands/machines/dedicated/service/index.js +49 -0
  141. package/dist/commands/machines/dedicated/service/logs.d.ts +33 -0
  142. package/dist/commands/machines/dedicated/service/logs.js +148 -0
  143. package/dist/commands/machines/dedicated/service/ls.d.ts +20 -0
  144. package/dist/commands/machines/dedicated/service/ls.js +80 -0
  145. package/dist/commands/machines/dedicated/service/restart.d.ts +14 -0
  146. package/dist/commands/machines/dedicated/service/restart.js +29 -0
  147. package/dist/commands/machines/dedicated/service/rm.d.ts +20 -0
  148. package/dist/commands/machines/dedicated/service/rm.js +33 -0
  149. package/dist/commands/machines/dedicated/service/secret/index.d.ts +14 -0
  150. package/dist/commands/machines/dedicated/service/secret/index.js +25 -0
  151. package/dist/commands/machines/dedicated/service/secret/rm.d.ts +22 -0
  152. package/dist/commands/machines/dedicated/service/secret/rm.js +54 -0
  153. package/dist/commands/machines/dedicated/service/secret/set.d.ts +22 -0
  154. package/dist/commands/machines/dedicated/service/secret/set.js +93 -0
  155. package/dist/commands/machines/dedicated/service/start.d.ts +14 -0
  156. package/dist/commands/machines/dedicated/service/start.js +29 -0
  157. package/dist/commands/machines/dedicated/service/status.d.ts +21 -0
  158. package/dist/commands/machines/dedicated/service/status.js +85 -0
  159. package/dist/commands/machines/dedicated/service/stop.d.ts +20 -0
  160. package/dist/commands/machines/dedicated/service/stop.js +35 -0
  161. package/dist/commands/machines/dedicated/service/top.d.ts +22 -0
  162. package/dist/commands/machines/dedicated/service/top.js +120 -0
  163. package/dist/commands/machines/dedicated/sign-in-status.d.ts +22 -0
  164. package/dist/commands/machines/dedicated/sign-in-status.js +79 -0
  165. package/dist/commands/machines/dedicated/ssh-config.d.ts +28 -0
  166. package/dist/commands/machines/dedicated/ssh-config.js +66 -0
  167. package/dist/commands/machines/dedicated/ssh-proxy.d.ts +22 -0
  168. package/dist/commands/machines/dedicated/ssh-proxy.js +62 -0
  169. package/dist/commands/machines/dedicated/ssh.d.ts +29 -0
  170. package/dist/commands/machines/dedicated/ssh.js +121 -0
  171. package/dist/commands/native/allow-all.d.ts +14 -0
  172. package/dist/commands/native/allow-all.js +25 -0
  173. package/dist/commands/native/allow.d.ts +24 -0
  174. package/dist/commands/native/allow.js +45 -0
  175. package/dist/commands/native/deny.d.ts +24 -0
  176. package/dist/commands/native/deny.js +43 -0
  177. package/dist/commands/native/disable.d.ts +17 -0
  178. package/dist/commands/native/disable.js +28 -0
  179. package/dist/commands/native/enable.d.ts +12 -0
  180. package/dist/commands/native/enable.js +29 -0
  181. package/dist/commands/native/list-apps.d.ts +19 -0
  182. package/dist/commands/native/list-apps.js +30 -0
  183. package/dist/commands/native/restrict-to.d.ts +22 -0
  184. package/dist/commands/native/restrict-to.js +41 -0
  185. package/dist/commands/native/status.d.ts +19 -0
  186. package/dist/commands/native/status.js +33 -0
  187. package/dist/commands/native.d.ts +39 -0
  188. package/dist/commands/native.js +76 -0
  189. package/dist/commands/service/index.d.ts +14 -0
  190. package/dist/commands/service/index.js +31 -0
  191. package/dist/commands/service/logs.d.ts +18 -0
  192. package/dist/commands/service/logs.js +53 -0
  193. package/dist/commands/service/ls.d.ts +13 -0
  194. package/dist/commands/service/ls.js +48 -0
  195. package/dist/commands/service/propose.d.ts +34 -0
  196. package/dist/commands/service/propose.js +113 -0
  197. package/dist/commands/service/status.d.ts +17 -0
  198. package/dist/commands/service/status.js +56 -0
  199. package/dist/commands/spaces/auto-executor/runs.d.ts +13 -0
  200. package/dist/commands/spaces/auto-executor/runs.js +56 -0
  201. package/dist/commands/spaces/briefs/get.js +8 -0
  202. package/dist/commands/spaces/briefs/list.js +2 -0
  203. package/dist/commands/spaces/briefs/set.d.ts +3 -0
  204. package/dist/commands/spaces/briefs/set.js +78 -13
  205. package/dist/commands/spaces/expertise-suggestions.d.ts +12 -0
  206. package/dist/commands/spaces/expertise-suggestions.js +71 -0
  207. package/dist/commands/spaces/members/add.d.ts +1 -0
  208. package/dist/commands/spaces/members/add.js +11 -3
  209. package/dist/commands/spaces/members/list.js +11 -0
  210. package/dist/commands/store/governance/artifacts.d.ts +25 -0
  211. package/dist/commands/store/governance/artifacts.js +55 -0
  212. package/dist/commands/store/governance/assign.d.ts +16 -0
  213. package/dist/commands/store/governance/assign.js +54 -0
  214. package/dist/commands/store/governance/create-license.d.ts +18 -0
  215. package/dist/commands/store/governance/create-license.js +35 -0
  216. package/dist/commands/store/governance/install-plan.d.ts +25 -0
  217. package/dist/commands/store/governance/install-plan.js +56 -0
  218. package/dist/commands/store/governance/license-policy.d.ts +30 -0
  219. package/dist/commands/store/governance/license-policy.js +69 -0
  220. package/dist/commands/store/governance/policy.d.ts +25 -0
  221. package/dist/commands/store/governance/policy.js +65 -0
  222. package/dist/commands/store/governance/publish-release.d.ts +17 -0
  223. package/dist/commands/store/governance/publish-release.js +34 -0
  224. package/dist/commands/tasks/assign.d.ts +9 -0
  225. package/dist/commands/tasks/assign.js +108 -1
  226. package/dist/commands/tasks/deliverable/add.d.ts +15 -0
  227. package/dist/commands/tasks/deliverable/add.js +174 -0
  228. package/dist/commands/tasks/deliverable/archive.d.ts +35 -0
  229. package/dist/commands/tasks/deliverable/archive.js +205 -0
  230. package/dist/commands/tasks/labels/detach.js +3 -0
  231. package/dist/commands/tasks/labels/suggest.d.ts +24 -0
  232. package/dist/commands/tasks/labels/suggest.js +69 -0
  233. package/dist/commands/tasks/list.d.ts +16 -2
  234. package/dist/commands/tasks/list.js +100 -24
  235. package/dist/commands/tasks/show.d.ts +23 -0
  236. package/dist/commands/tasks/show.js +51 -1
  237. package/dist/commands/wiki/rm.d.ts +22 -1
  238. package/dist/commands/wiki/rm.js +52 -3
  239. package/dist/lib/agentic-stream.js +10 -2
  240. package/dist/lib/assignee-resolver.d.ts +22 -0
  241. package/dist/lib/assignee-resolver.js +26 -0
  242. package/dist/lib/balance.d.ts +6 -1
  243. package/dist/lib/balance.js +15 -0
  244. package/dist/lib/code-handover.d.ts +0 -10
  245. package/dist/lib/code-handover.js +20 -5
  246. package/dist/lib/commerce-command.d.ts +48 -0
  247. package/dist/lib/commerce-command.js +70 -0
  248. package/dist/lib/commerce.d.ts +93 -0
  249. package/dist/lib/commerce.js +129 -0
  250. package/dist/lib/commitment-product.js +1 -1
  251. package/dist/lib/commitments.d.ts +202 -29
  252. package/dist/lib/commitments.js +300 -33
  253. package/dist/lib/dedicated-lease-command.js +10 -0
  254. package/dist/lib/dedicated-machines.d.ts +37 -0
  255. package/dist/lib/dedicated-machines.js +47 -0
  256. package/dist/lib/dedicated-service-command.d.ts +60 -0
  257. package/dist/lib/dedicated-service-command.js +182 -0
  258. package/dist/lib/dedicated-service.d.ts +342 -0
  259. package/dist/lib/dedicated-service.js +741 -0
  260. package/dist/lib/dedicated-ssh.d.ts +136 -0
  261. package/dist/lib/dedicated-ssh.js +387 -0
  262. package/dist/lib/delegated-cli.js +51 -13
  263. package/dist/lib/enterprise-commerce.d.ts +67 -0
  264. package/dist/lib/enterprise-commerce.js +112 -0
  265. package/dist/lib/enterprise-external-configuration.d.ts +60 -0
  266. package/dist/lib/enterprise-external-configuration.js +58 -0
  267. package/dist/lib/first-party-harness.js +17 -3
  268. package/dist/lib/governed-store.d.ts +110 -0
  269. package/dist/lib/governed-store.js +79 -0
  270. package/dist/lib/guest-service-command.d.ts +16 -0
  271. package/dist/lib/guest-service-command.js +32 -0
  272. package/dist/lib/guest-service.d.ts +67 -0
  273. package/dist/lib/guest-service.js +112 -0
  274. package/dist/lib/prompt.d.ts +1 -0
  275. package/dist/lib/prompt.js +1 -0
  276. package/dist/lib/task-asset-upload.d.ts +20 -0
  277. package/dist/lib/task-asset-upload.js +30 -8
  278. package/dist/lib/task-extras.d.ts +23 -0
  279. package/dist/lib/task-extras.js +6 -0
  280. package/dist/lib/update-check.d.ts +80 -7
  281. package/dist/lib/update-check.js +365 -33
  282. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.d.ts +9 -3
  283. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonScopes.js +10 -3
  284. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.d.ts +9 -1
  285. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/firstPartyHarness.js +8 -0
  286. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +1 -1
  287. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +6 -5
  288. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.d.ts +16 -0
  289. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/messages.js +21 -4
  290. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.d.ts +1 -1
  291. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/spawnEnv.js +24 -2
  292. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.d.ts +9 -3
  293. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonScopes.js +10 -3
  294. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.d.ts +9 -1
  295. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/firstPartyHarness.js +8 -0
  296. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +1 -1
  297. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +1 -1
  298. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.d.ts +16 -0
  299. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/messages.js +20 -4
  300. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.d.ts +1 -1
  301. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/spawnEnv.js +24 -2
  302. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  303. package/dist/node_modules/@skrr-ai/data-provider/index.js +5067 -4821
  304. package/oclif.manifest.json +34877 -28659
  305. package/package.json +43 -1
@@ -0,0 +1,69 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../../base-command");
5
+ const task_extras_1 = require("../../../lib/task-extras");
6
+ const format_1 = require("../../../lib/format");
7
+ /**
8
+ * Ask the lightweight model which of the DECLARED labels fit this task —
9
+ * `POST /api/tasks/:taskId/labels/suggest`, the on-demand half of the
10
+ * create-time `_labelGuidance` hint. The server keeps the suggestion off the
11
+ * task-create path (the hottest write in the product); this command is how a
12
+ * caller pays for one suggestion when it actually wants it.
13
+ *
14
+ * Same contract as create: the model may only CHOOSE from the declared
15
+ * vocabulary, a label the task already wears is never suggested, and a
16
+ * suggestion is a proposal, not a write — attach is still the caller's
17
+ * decision (`tasks labels attach --label <id>`).
18
+ */
19
+ class TasksLabelsSuggest extends base_command_1.BaseCommand {
20
+ static description = 'Suggest labels for a task from the declared vocabulary (advisory, never writes)';
21
+ static examples = [
22
+ '<%= config.bin %> tasks labels suggest <task-id>',
23
+ '<%= config.bin %> tasks labels suggest <task-id> --json',
24
+ ];
25
+ static args = {
26
+ id: core_1.Args.string({ description: 'Task ID', required: true, ignoreStdin: true }),
27
+ };
28
+ static flags = {
29
+ json: core_1.Flags.boolean({ description: 'Output the raw suggestion response as JSON' }),
30
+ };
31
+ async run() {
32
+ this.requireAuth();
33
+ const { args, flags } = await this.parse(TasksLabelsSuggest);
34
+ let response;
35
+ try {
36
+ // The URL lives in the lib — a command that builds its own `/api/...`
37
+ // string is invisible to the CLI↔API parity check.
38
+ response = await (0, task_extras_1.taskLabelSuggestion)(args.id);
39
+ }
40
+ catch (err) {
41
+ this.handleApiError(err);
42
+ }
43
+ if (flags.json) {
44
+ this.log(JSON.stringify(response ?? {}, null, 2));
45
+ return;
46
+ }
47
+ const suggestions = (response?.suggestions ?? []).filter((s) => Boolean(s?.id && s?.name));
48
+ const ref = response?.identifier ? ` (${response.identifier})` : '';
49
+ if (suggestions.length === 0) {
50
+ this.log(`No label suggestions for task ${args.id}${ref} — ` +
51
+ // The server counts the vocabulary AFTER excluding the labels this
52
+ // task already wears, so 0 means nothing left to propose, not that
53
+ // the registry is empty.
54
+ (response?.vocabularySize === 0
55
+ ? 'the declared vocabulary is empty, or every label in it is already attached.'
56
+ : 'nothing in the declared vocabulary clearly applies.') +
57
+ ' "No label" is a correct answer.');
58
+ return;
59
+ }
60
+ this.log(`Suggested labels for task ${args.id}${ref}:`);
61
+ (0, format_1.renderTable)(suggestions.map((s) => ({ id: s.id, name: s.name })), [
62
+ { key: 'id', header: 'ID' },
63
+ { key: 'name', header: 'NAME' },
64
+ ], (line) => this.log(line));
65
+ this.log(`A suggestion is a proposal, not a write — attach with ` +
66
+ `\`skrr tasks labels attach ${args.id} --label <id>\` if one fits.`);
67
+ }
68
+ }
69
+ exports.default = TasksLabelsSuggest;
@@ -12,6 +12,19 @@ type ListFlags = Record<string, unknown>;
12
12
  * Exported and pure so the wording is testable without an oclif runtime.
13
13
  */
14
14
  export declare function emptyTasksMessage(flags: ListFlags, bin?: string): string;
15
+ /**
16
+ * The NEXT column's text, computed once for the table and the `--json`
17
+ * `nextAction` alias. `reason` is the sentence the server wrote for a person;
18
+ * `reasonCode` is the enum it wrote for a machine — printing the code rendered
19
+ * every stage of every lifecycle as `human:lifecycle_stage` while the sentence
20
+ * naming the stage sat unread in the same payload.
21
+ */
22
+ export declare function nextActionText(t: Record<string, unknown>): string;
23
+ /**
24
+ * The REF column's value, aliased to `ref` under `--json` because `identifier`
25
+ * is the one name an agent scanning 43 keys does not guess (OSK-10692).
26
+ */
27
+ export declare function taskRef(t: Record<string, unknown>): string;
15
28
  /**
16
29
  * The task-list request bag is shared with import/export so every CLI list
17
30
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -28,8 +41,9 @@ export default class TasksList extends BaseCommand {
28
41
  project: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
29
42
  cycle: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
30
43
  parent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
31
- status: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
32
- 'status-type': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
44
+ status: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
45
+ open: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
46
+ 'status-type': import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
33
47
  'execution-state': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
34
48
  'actionable-by': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
35
49
  'actionable-for-agent': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.emptyTasksMessage = emptyTasksMessage;
4
+ exports.nextActionText = nextActionText;
5
+ exports.taskRef = taskRef;
4
6
  exports.buildListParams = buildListParams;
5
7
  const core_1 = require("@oclif/core");
6
8
  const data_provider_1 = require("@skrr-ai/data-provider");
@@ -22,6 +24,7 @@ const FILTER_FLAGS = new Set([
22
24
  'parent',
23
25
  'status',
24
26
  'status-type',
27
+ 'open',
25
28
  'execution-state',
26
29
  'actionable-by',
27
30
  'actionable-for-agent',
@@ -77,7 +80,7 @@ function emptyTasksMessage(flags, bin = 'skrr') {
77
80
  lines.push(` → Create your first one: \`${bin} tasks create --title "..." --space <space-id>\``);
78
81
  lines.push(` → List spaces to find one: \`${bin} spaces list\``);
79
82
  }
80
- else if (flags.status === 'in_progress' && !flags['execution-state']) {
83
+ else if (statusList(flags).includes('in_progress') && !flags['execution-state']) {
81
84
  // `--status in_progress` filters the board column only. A run that was
82
85
  // reaped/reset (e.g. a stuck-task timeout) is moved OFF `in_progress` but
83
86
  // keeps its execution markers, so it hides here. Point the operator at
@@ -87,9 +90,7 @@ function emptyTasksMessage(flags, bin = 'skrr') {
87
90
  lines.push(' no longer has that status. Try `--execution-state active` (running now) or');
88
91
  lines.push(` \`--execution-state recovered\` (reaped, startable), or \`${bin} tasks active\`.`);
89
92
  }
90
- else if (typeof flags.status === 'string' &&
91
- flags.status.length > 0 &&
92
- !flags['execution-state']) {
93
+ else if (statusList(flags).length > 0 && !flags['execution-state']) {
93
94
  // A filter that matches nothing and a board that IS empty print the same
94
95
  // thing, and the caller cannot tell which they are looking at. Observed:
95
96
  // `--status todo` on a board whose open column is `backlog` said `No tasks.`
@@ -106,16 +107,54 @@ function emptyTasksMessage(flags, bin = 'skrr') {
106
107
  // string the branch above deliberately produces for that combination. The
107
108
  // same ambiguity exists there, but overturning a pinned decision is not
108
109
  // this change's business.
110
+ const wanted = statusList(flags);
109
111
  lines.push('');
110
- lines.push(`Note: nothing matched \`--status ${flags.status}\`. A status is a workflow-state id the`);
111
- lines.push(' space owns, so a board need not use this one at all.');
112
+ lines.push(`Note: nothing matched \`--status ${wanted.join(' --status ')}\`. A status is a`);
113
+ lines.push(' workflow-state id the space owns, so a board need not use this one at all.');
112
114
  lines.push(' → See what this scope actually holds: the same command without `--status`.');
113
- if (!data_provider_1.TASK_STATUSES.includes(flags.status)) {
115
+ if (wanted.some((s) => !data_provider_1.TASK_STATUSES.includes(s))) {
114
116
  lines.push(` (built-in ids: ${data_provider_1.TASK_STATUSES.join(', ')})`);
115
117
  }
116
118
  }
117
119
  return lines.join('\n');
118
120
  }
121
+ /**
122
+ * `--status` is repeatable, so the flag value is a list; callers that predate
123
+ * that and tests that pass a bare string both normalize through here.
124
+ */
125
+ function statusList(flags) {
126
+ const value = flags.status;
127
+ if (Array.isArray(value))
128
+ return value.filter((s) => typeof s === 'string');
129
+ return typeof value === 'string' && value.length > 0 ? [value] : [];
130
+ }
131
+ /** Same normalization for the repeatable `--status-type`. */
132
+ function statusTypeList(flags) {
133
+ const value = flags['status-type'];
134
+ if (Array.isArray(value))
135
+ return value.filter((s) => typeof s === 'string');
136
+ return typeof value === 'string' && value.length > 0 ? [value] : [];
137
+ }
138
+ /**
139
+ * The NEXT column's text, computed once for the table and the `--json`
140
+ * `nextAction` alias. `reason` is the sentence the server wrote for a person;
141
+ * `reasonCode` is the enum it wrote for a machine — printing the code rendered
142
+ * every stage of every lifecycle as `human:lifecycle_stage` while the sentence
143
+ * naming the stage sat unread in the same payload.
144
+ */
145
+ function nextActionText(t) {
146
+ const actionability = t.actionability;
147
+ return actionability
148
+ ? `${String(actionability.nextActor ?? '-')}: ${String(actionability.reason || actionability.reasonCode || actionability.state || '-')}`
149
+ : String(t.executionMode ?? '-');
150
+ }
151
+ /**
152
+ * The REF column's value, aliased to `ref` under `--json` because `identifier`
153
+ * is the one name an agent scanning 43 keys does not guess (OSK-10692).
154
+ */
155
+ function taskRef(t) {
156
+ return String(t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '-'));
157
+ }
119
158
  /**
120
159
  * The task-list request bag is shared with import/export so every CLI list
121
160
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -129,8 +168,25 @@ function buildListParams(flags, assigneeUserId) {
129
168
  projectId: flag('project'),
130
169
  cycleId: flag('cycle'),
131
170
  parentId: flag('parent'),
132
- ...(flag('status') ? { status: flag('status') } : {}),
133
- ...(flag('status-type') ? { statusType: flag('status-type') } : {}),
171
+ // Scalar for a single value, `$in` array once the caller ORs — keeps the
172
+ // wire shape identical to the pre-multiple flag for the common case.
173
+ ...(statusList(flags).length === 1
174
+ ? { status: statusList(flags)[0] }
175
+ : statusList(flags).length > 1
176
+ ? { status: statusList(flags) }
177
+ : {}),
178
+ // `--open` sends the DECLARED band rather than a string the CLI invented:
179
+ // TASK_WORKFLOW_TYPE_BANDS.open is the same list `--overdue` already
180
+ // filters by server-side, so the two cannot drift apart. The endpoint
181
+ // types statusType as `string | string[]` and the model does `$in`, so
182
+ // nothing below the CLI had to change.
183
+ ...(flag('open')
184
+ ? { statusType: [...data_provider_1.TASK_WORKFLOW_TYPE_BANDS.open] }
185
+ : statusTypeList(flags).length === 1
186
+ ? { statusType: statusTypeList(flags)[0] }
187
+ : statusTypeList(flags).length > 1
188
+ ? { statusType: statusTypeList(flags) }
189
+ : {}),
134
190
  ...(flag('execution-state')
135
191
  ? { executionState: flag('execution-state') }
136
192
  : {}),
@@ -206,6 +262,8 @@ class TasksList extends base_command_1.BaseCommand {
206
262
  '<%= config.bin %> tasks list --include-snoozed',
207
263
  '<%= config.bin %> tasks list --actionable-by agent --actionable-for-agent <agent-id>',
208
264
  '<%= config.bin %> tasks list --needs-me',
265
+ '<%= config.bin %> tasks list --open',
266
+ '<%= config.bin %> tasks list --status-type blocked --status-type review',
209
267
  '<%= config.bin %> tasks list --dri me',
210
268
  '<%= config.bin %> tasks list --view <task-view-id>',
211
269
  '<%= config.bin %> tasks list --calendar 2026-08-01..2026-08-31 --json',
@@ -219,9 +277,17 @@ class TasksList extends base_command_1.BaseCommand {
219
277
  description: 'Filter by planning cycle ID, or "current" / "next" (requires --space)',
220
278
  }),
221
279
  parent: core_1.Flags.string({ description: 'Filter by parent task ID' }),
222
- status: core_1.Flags.string({ description: 'Filter by task status' }),
280
+ // Repeatable: each occurrence ORs into one `$in` clause server-side, so
281
+ // "backlog or in review" is one call, not one call per band (OSK-10693).
282
+ status: core_1.Flags.string({ description: 'Filter by task status (repeatable, OR)', multiple: true }),
283
+ open: core_1.Flags.boolean({
284
+ description: 'Everything still open — the declared open band (backlog, unstarted, started, review, ' +
285
+ 'triage, failed, blocked). One call instead of one per band, and it survives a Space ' +
286
+ 'adding its own workflow state names.',
287
+ exclusive: ['status-type', 'status'],
288
+ }),
223
289
  'status-type': core_1.Flags.string({
224
- description: 'Filter by fixed workflow TYPE (independent of Space state names)',
290
+ description: 'Filter by fixed workflow TYPE, independent of Space state names (repeatable, OR)',
225
291
  options: [
226
292
  'backlog',
227
293
  'unstarted',
@@ -234,6 +300,7 @@ class TasksList extends base_command_1.BaseCommand {
234
300
  'blocked',
235
301
  'hidden',
236
302
  ],
303
+ multiple: true,
237
304
  }),
238
305
  'execution-state': core_1.Flags.string({
239
306
  description: 'Filter by execution state, not board status: "active" (running now — in_progress OR checked out OR live session) or "recovered" (startable again after a reaped/failed run). Surfaces reaped runs that --status in_progress hides.',
@@ -257,7 +324,7 @@ class TasksList extends base_command_1.BaseCommand {
257
324
  'due-before': core_1.Flags.string({ description: 'Only tasks due before an ISO timestamp or "now"' }),
258
325
  'due-after': core_1.Flags.string({ description: 'Only tasks due after an ISO timestamp or "now"' }),
259
326
  overdue: core_1.Flags.boolean({
260
- description: 'Alias for --due-before now --status-type open; uses the shared legacy open-status band until configurable workflow states land',
327
+ description: 'Alias for --due-before now --open; uses the shared legacy open-status band until configurable workflow states land',
261
328
  }),
262
329
  'blocked-by': core_1.Flags.string({ description: 'Only tasks blocked by this task ID' }),
263
330
  blocks: core_1.Flags.string({ description: 'Only tasks that block this task ID' }),
@@ -382,7 +449,9 @@ class TasksList extends base_command_1.BaseCommand {
382
449
  ...calendarRange,
383
450
  ...(flags.space ? { spaceId: flags.space } : {}),
384
451
  ...(flags.project ? { projectId: flags.project } : {}),
385
- ...(flags.status ? { status: flags.status } : {}),
452
+ ...(statusList(flags).length > 0
453
+ ? { status: statusList(flags) }
454
+ : {}),
386
455
  })
387
456
  : await data_provider_1.dataService.listTasks(buildListParams(flags, assigneeUserId));
388
457
  }
@@ -390,7 +459,22 @@ class TasksList extends base_command_1.BaseCommand {
390
459
  this.handleApiError(err);
391
460
  }
392
461
  if (flags.json) {
393
- this.log(JSON.stringify((0, web_url_1.withDataUrls)(this.cliConfig.baseURL, response, web_url_1.taskUrlFrom), null, 2));
462
+ // The table renders REF and NEXT, but the raw payload names them
463
+ // `identifier` and `actionability` — under `--json` an agent scanning 43
464
+ // keys could not find either (OSK-10692). Alias them to the names the
465
+ // table prints so the machine view answers the same questions.
466
+ const list = response;
467
+ const enriched = list && Array.isArray(list.data)
468
+ ? {
469
+ ...list,
470
+ data: list.data.map((t) => ({
471
+ ...t,
472
+ ref: taskRef(t),
473
+ nextAction: nextActionText(t),
474
+ })),
475
+ }
476
+ : response;
477
+ this.log(JSON.stringify((0, web_url_1.withDataUrls)(this.cliConfig.baseURL, enriched, web_url_1.taskUrlFrom), null, 2));
394
478
  return;
395
479
  }
396
480
  if (calendarRange) {
@@ -427,23 +511,15 @@ class TasksList extends base_command_1.BaseCommand {
427
511
  const agents = Array.isArray(t.assigneeAgentIds) ? t.assigneeAgentIds.length : 0;
428
512
  const users = Array.isArray(t.assigneeUserIds) ? t.assigneeUserIds.length : 0;
429
513
  const assignees = agents + users > 0 ? `${agents}a/${users}u` : '-';
430
- const actionability = t.actionability;
431
514
  return {
432
515
  id: String(t.id ?? t._id ?? '-'),
433
- ident: String(t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '-')),
516
+ ident: taskRef(t),
434
517
  title: String(t.title ?? '-'),
435
518
  status: String(t.status ?? '-'),
436
519
  priority: String(t.priority ?? '-'),
437
520
  estimate: t.estimate === null || t.estimate === undefined ? '-' : String(t.estimate),
438
521
  assignees,
439
- // `reason` is the sentence the server wrote for a person; `reasonCode`
440
- // is the enum it wrote for a machine. Printing the code rendered every
441
- // stage of every lifecycle as `human:lifecycle_stage` — six different
442
- // waits, one string — while the sentence naming the stage sat unread in
443
- // the same payload.
444
- next: actionability
445
- ? `${String(actionability.nextActor ?? '-')}: ${String(actionability.reason || actionability.reasonCode || actionability.state || '-')}`
446
- : String(t.executionMode ?? '-'),
522
+ next: nextActionText(t),
447
523
  match: String(t._matchField ?? '-'),
448
524
  };
449
525
  });
@@ -27,6 +27,29 @@ export declare function taskPointerLines(t: Record<string, unknown>, ref: string
27
27
  * newest `blocker` Update and hands its payload here.
28
28
  */
29
29
  export declare function blockerDetailLines(detail: Record<string, unknown> | undefined): string[];
30
+ /**
31
+ * The answer this command leads with: is it done?
32
+ *
33
+ * Everything below it in `tasks show` is still there — Status, Resolution,
34
+ * Result, Review, the Expectation counts. This does not replace them; it
35
+ * answers, in one sentence, the question a reader had to assemble from eleven
36
+ * concepts before. The projection is computed on the SERVER
37
+ * (`deriveTaskCompletion`), so this function renders and never decides: if the
38
+ * CLI derived it here, the board and a phone would each derive their own and
39
+ * the three would disagree about one Task.
40
+ *
41
+ * A read that FAILED prints its own line rather than nothing, because silence
42
+ * here is indistinguishable from "we had no concerns" — which is the exact
43
+ * collapse this whole projection exists to undo.
44
+ *
45
+ * Pure and exported so the wording is testable without an oclif runtime.
46
+ */
47
+ export declare function completionLines(projection: {
48
+ sentence?: unknown;
49
+ alsoOutstanding?: unknown;
50
+ } | null | undefined, { read }?: {
51
+ read?: boolean;
52
+ }): string[];
30
53
  export declare function labelsLine(labelIds?: string[], nameById?: Map<string, string>): string;
31
54
  /**
32
55
  * The `Executor:` value — who does this task, and whether anyone said so.
@@ -2,6 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.taskPointerLines = taskPointerLines;
4
4
  exports.blockerDetailLines = blockerDetailLines;
5
+ exports.completionLines = completionLines;
5
6
  exports.labelsLine = labelsLine;
6
7
  exports.taskExecutorLabel = taskExecutorLabel;
7
8
  exports.taskFailureLines = taskFailureLines;
@@ -76,6 +77,35 @@ function blockerDetailLines(detail) {
76
77
  }
77
78
  return lines;
78
79
  }
80
+ /**
81
+ * The answer this command leads with: is it done?
82
+ *
83
+ * Everything below it in `tasks show` is still there — Status, Resolution,
84
+ * Result, Review, the Expectation counts. This does not replace them; it
85
+ * answers, in one sentence, the question a reader had to assemble from eleven
86
+ * concepts before. The projection is computed on the SERVER
87
+ * (`deriveTaskCompletion`), so this function renders and never decides: if the
88
+ * CLI derived it here, the board and a phone would each derive their own and
89
+ * the three would disagree about one Task.
90
+ *
91
+ * A read that FAILED prints its own line rather than nothing, because silence
92
+ * here is indistinguishable from "we had no concerns" — which is the exact
93
+ * collapse this whole projection exists to undo.
94
+ *
95
+ * Pure and exported so the wording is testable without an oclif runtime.
96
+ */
97
+ function completionLines(projection, { read = true } = {}) {
98
+ if (!read) {
99
+ return ['Done? could not be worked out just now — that check did not answer.'];
100
+ }
101
+ const sentence = typeof projection?.sentence === 'string' ? projection.sentence.trim() : '';
102
+ if (!sentence)
103
+ return [];
104
+ const also = Array.isArray(projection?.alsoOutstanding)
105
+ ? projection.alsoOutstanding.filter((row) => typeof row === 'string' && row.trim().length > 0)
106
+ : [];
107
+ return [`Done? ${sentence}`, ...also.map((row) => ` also: ${row.trim()}`)];
108
+ }
79
109
  function labelsLine(labelIds, nameById) {
80
110
  const ids = (labelIds ?? []).filter((id) => typeof id === 'string' && id.length > 0);
81
111
  if (ids.length === 0)
@@ -254,10 +284,30 @@ class TasksShow extends base_command_1.BaseCommand {
254
284
  }
255
285
  const t = task;
256
286
  const url = (0, web_url_1.taskUrlFrom)(this.cliConfig.baseURL, t);
287
+ /*
288
+ * The three-state answer, read from the server rather than worked out here.
289
+ *
290
+ * One extra request on a DETAIL view, for the line the whole command is
291
+ * read for. A failure is reported as a failure: `completionLines` prints
292
+ * that it could not be worked out, because a missing headline reads exactly
293
+ * like a clean one.
294
+ */
295
+ let completion = null;
296
+ let completionRead = true;
297
+ try {
298
+ const response = await data_provider_1.dataService.getTaskCompletion(String(t.id ?? taskId));
299
+ completion = (response?.completion ?? null);
300
+ completionRead = Boolean(completion);
301
+ }
302
+ catch {
303
+ completionRead = false;
304
+ }
257
305
  if (flags.json) {
258
- this.log(JSON.stringify({ ...t, url }, null, 2));
306
+ this.log(JSON.stringify({ ...t, url, completion }, null, 2));
259
307
  return;
260
308
  }
309
+ for (const line of completionLines(completion, { read: completionRead }))
310
+ this.log(line);
261
311
  const agents = Array.isArray(t.assigneeAgentIds) ? t.assigneeAgentIds : [];
262
312
  const users = Array.isArray(t.assigneeUserIds) ? t.assigneeUserIds : [];
263
313
  this.log(`ID: ${t.id ?? t._id ?? '-'}`);
@@ -1,13 +1,34 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * What to say when a page path does not resolve.
4
+ *
5
+ * A path is a SLUG, and renaming a page deliberately does not move it — that
6
+ * is what keeps a stored `skrr wiki` address working. The consequence is that
7
+ * a caller who types the page's TITLE is always wrong, and every page created
8
+ * in the web UI is `untitled`, `untitled-2`, … The old message named the
9
+ * failure and stopped, which leaves the one question the caller now has
10
+ * unanswered: where are the real paths? (OSK-10694)
11
+ *
12
+ * Exported so the wording is testable without driving the command.
13
+ */
14
+ export declare function missingPagePathError({ path, spaceId, binName, }: {
15
+ path: string;
16
+ spaceId: string;
17
+ binName: string;
18
+ }): {
19
+ message: string;
20
+ code: string;
21
+ };
2
22
  export default class WikiRm extends BaseCommand {
3
23
  static description: string;
4
24
  static examples: string[];
5
25
  static args: {
6
26
  space: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
- path: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
27
+ path: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
8
28
  };
9
29
  static flags: {
10
30
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
31
+ 'page-id': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
32
  force: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
33
  purge: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
13
34
  };
@@ -1,11 +1,32 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.missingPagePathError = missingPagePathError;
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 http_error_1 = require("../../lib/http-error");
7
8
  const prompt_1 = require("../../lib/prompt");
8
9
  const wiki_1 = require("../../lib/wiki");
10
+ /**
11
+ * What to say when a page path does not resolve.
12
+ *
13
+ * A path is a SLUG, and renaming a page deliberately does not move it — that
14
+ * is what keeps a stored `skrr wiki` address working. The consequence is that
15
+ * a caller who types the page's TITLE is always wrong, and every page created
16
+ * in the web UI is `untitled`, `untitled-2`, … The old message named the
17
+ * failure and stopped, which leaves the one question the caller now has
18
+ * unanswered: where are the real paths? (OSK-10694)
19
+ *
20
+ * Exported so the wording is testable without driving the command.
21
+ */
22
+ function missingPagePathError({ path, spaceId, binName, }) {
23
+ return {
24
+ message: `No page at path '${path}'. Paths are slugs and do not follow a page's title, ` +
25
+ `so list them with \`${binName} wiki ls ${spaceId}\` (or \`wiki tree\`), ` +
26
+ `or address the page directly with --page-id.`,
27
+ code: 'NOT_FOUND',
28
+ };
29
+ }
9
30
  class WikiRm extends base_command_1.BaseCommand {
10
31
  // "recoverable" promised something no command can deliver: the row is
11
32
  // archived rather than destroyed, but nothing in `skrr wiki` — or in the model
@@ -19,10 +40,17 @@ class WikiRm extends base_command_1.BaseCommand {
19
40
  ];
20
41
  static args = {
21
42
  space: core_1.Args.string({ description: 'Space ID', required: true, ignoreStdin: true }),
22
- path: core_1.Args.string({ description: 'Page path', required: true, ignoreStdin: true }),
43
+ path: core_1.Args.string({ description: 'Page path', required: false, ignoreStdin: true }),
23
44
  };
24
45
  static flags = {
25
46
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
47
+ // `wiki cat` has had this since it shipped; the two commands that MUTATE a
48
+ // page did not, which is backwards. Every create and every rm hands the
49
+ // caller a page id — rm's own success line tells you to read the archived
50
+ // page back with `wiki cat --page-id` — so an agent holding an id could
51
+ // read the page and not remove it without first translating the id into a
52
+ // slug through `wiki tree` (OSK-10694).
53
+ 'page-id': core_1.Flags.string({ description: 'Address the page by id instead of path' }),
26
54
  // `--yes` is the CLI-wide name for this (62 of 64 destructive commands use
27
55
  // it) and is what the non-TTY guard tells a script to pass. `--force`/`-f`
28
56
  // predates that convention here and stays for back-compat.
@@ -47,16 +75,37 @@ class WikiRm extends base_command_1.BaseCommand {
47
75
  async run() {
48
76
  this.requireAuth();
49
77
  const { args, flags } = await this.parse(WikiRm);
78
+ if (!args.path && !flags['page-id']) {
79
+ this.failWithCliError({
80
+ message: 'Provide a page path or --page-id.',
81
+ code: 'WIKI_PATH_REQUIRED',
82
+ exit: 1,
83
+ retryable: false,
84
+ });
85
+ }
50
86
  let page;
51
87
  try {
52
- page = await data_provider_1.dataService.getSpaceWikiPageByPath(args.space, args.path);
88
+ page = flags['page-id']
89
+ ? await data_provider_1.dataService.getSpaceWikiPage(args.space, flags['page-id'])
90
+ : await data_provider_1.dataService.getSpaceWikiPageByPath(args.space, args.path);
53
91
  }
54
92
  catch (err) {
55
93
  // `rm` addresses pages. Pointing a folder path at it used to report the
56
94
  // bare "No page at path 'X'", which reads as "nothing is there" when in
57
95
  // fact a folder is — send the caller to the verb that removes one.
58
- if ((0, http_error_1.isHttpNotFound)(err)) {
96
+ if ((0, http_error_1.isHttpNotFound)(err) && args.path) {
59
97
  const folderId = await (0, wiki_1.resolveWikiFolderId)(args.space, args.path, data_provider_1.dataService.listSpaceWikiDirectory, http_error_1.isHttpNotFound).catch(() => null);
98
+ if (!folderId) {
99
+ this.failWithCliError({
100
+ ...missingPagePathError({
101
+ path: args.path,
102
+ spaceId: args.space,
103
+ binName: this.config.bin,
104
+ }),
105
+ exit: 1,
106
+ retryable: false,
107
+ });
108
+ }
60
109
  if (folderId) {
61
110
  this.failWithCliError({
62
111
  message: `'${args.path}' is a folder, not a page. Remove it with \`${this.config.bin} wiki rmdir ${args.space} ${args.path}\`.`,
@@ -662,8 +662,16 @@ class AgenticStreamClient {
662
662
  this.finish('failed', event);
663
663
  });
664
664
  socket.on('permission:request', (event) => {
665
- if (match(event))
666
- void this.handlePermission(event);
665
+ if (!match(event))
666
+ return;
667
+ // One ask, one answer (OSK-11796). The same `permission:request` can
668
+ // arrive twice — a `session:join` replay overlapping the live emit, or
669
+ // fan-out through a second room — and each copy used to emit its own
670
+ // `permission.requested` AND send its own approve/deny, so a duplicate
671
+ // delivery produced a duplicate prompt and a racing second decision.
672
+ if (!this.firstTimeSeen('permission', event.toolUseId))
673
+ return;
674
+ void this.handlePermission(event);
667
675
  });
668
676
  socket.on('question:ask', (event) => {
669
677
  if (match(event))
@@ -25,6 +25,28 @@ export type SpaceMember = {
25
25
  name?: string;
26
26
  email?: string;
27
27
  };
28
+ export type ExpertiseSuggestion = {
29
+ member: SpaceMember;
30
+ /** Expertise entries that exactly match a task tag (strong match). */
31
+ matchedTags: string[];
32
+ /** Expertise entries that appear in the task title/description (weak match). */
33
+ matchedText: string[];
34
+ };
35
+ /**
36
+ * Deterministic advisory lookup: which space members' declared `expertise`
37
+ * intersects this task's tags (strong) or appears in its title/description
38
+ * (weak). Not a routing decision — the caller still picks the assignee; this
39
+ * just surfaces the declared contract. Ranked: more tag matches first, then
40
+ * more text matches.
41
+ */
42
+ export declare function suggestMembersByExpertise({ members, expertiseByMemberId, taskTags, taskText, }: {
43
+ members: SpaceMember[];
44
+ /** member.id → declared expertise labels */
45
+ expertiseByMemberId: Map<string, string[]>;
46
+ taskTags: string[];
47
+ /** Task title + description, any case — normalized internally. */
48
+ taskText: string;
49
+ }): ExpertiseSuggestion[];
28
50
  export type ResolutionFailure = {
29
51
  ok: false;
30
52
  /**
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.suggestMembersByExpertise = suggestMembersByExpertise;
3
4
  exports.looksLikeRawId = looksLikeRawId;
4
5
  exports.membersFromPrincipals = membersFromPrincipals;
5
6
  exports.resolveMember = resolveMember;
@@ -9,6 +10,31 @@ exports.resolveAssigneeReferences = resolveAssigneeReferences;
9
10
  exports.formatResolutionFailure = formatResolutionFailure;
10
11
  const failed_lookup_1 = require("./failed-lookup");
11
12
  const data_provider_1 = require("@skrr-ai/data-provider");
13
+ /**
14
+ * Deterministic advisory lookup: which space members' declared `expertise`
15
+ * intersects this task's tags (strong) or appears in its title/description
16
+ * (weak). Not a routing decision — the caller still picks the assignee; this
17
+ * just surfaces the declared contract. Ranked: more tag matches first, then
18
+ * more text matches.
19
+ */
20
+ function suggestMembersByExpertise({ members, expertiseByMemberId, taskTags, taskText, }) {
21
+ const normTag = (v) => v.trim().toLowerCase();
22
+ const tags = new Set(taskTags.map(normTag));
23
+ const text = taskText.toLowerCase();
24
+ const out = [];
25
+ for (const member of members) {
26
+ const expertise = expertiseByMemberId.get(member.id);
27
+ if (!expertise || expertise.length === 0)
28
+ continue;
29
+ const matchedTags = expertise.filter((e) => tags.has(normTag(e)));
30
+ const matchedText = expertise.filter((e) => !matchedTags.includes(e) && normTag(e).length > 0 && text.includes(normTag(e)));
31
+ if (matchedTags.length > 0 || matchedText.length > 0) {
32
+ out.push({ member, matchedTags, matchedText });
33
+ }
34
+ }
35
+ out.sort((a, b) => b.matchedTags.length - a.matchedTags.length || b.matchedText.length - a.matchedText.length);
36
+ return out;
37
+ }
12
38
  const OBJECT_ID_RE = /^[0-9a-fA-F]{24}$/;
13
39
  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}$/;
14
40
  // Agent ids are transitioning from public UUIDs to opaque `agent_…` ids.