@skrr-ai/cli 0.1.13 → 0.1.14

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 (55) hide show
  1. package/dist/base-command.d.ts +1 -39
  2. package/dist/base-command.js +36 -10
  3. package/dist/commands/agents/list.d.ts +10 -0
  4. package/dist/commands/agents/list.js +16 -1
  5. package/dist/commands/browser.js +5 -5
  6. package/dist/commands/commitments/create.d.ts +2 -0
  7. package/dist/commands/commitments/create.js +30 -13
  8. package/dist/commands/commitments/update.d.ts +2 -0
  9. package/dist/commands/commitments/update.js +24 -4
  10. package/dist/commands/daemon/byok.js +5 -5
  11. package/dist/commands/daemon/index.js +3 -3
  12. package/dist/commands/daemon/install.js +8 -5
  13. package/dist/commands/daemon/login.js +5 -5
  14. package/dist/commands/daemon/logs.js +5 -5
  15. package/dist/commands/daemon/restart.js +5 -5
  16. package/dist/commands/daemon/start.js +5 -5
  17. package/dist/commands/daemon/status.js +5 -5
  18. package/dist/commands/daemon/stop.js +5 -5
  19. package/dist/commands/daemon/uninstall.js +5 -5
  20. package/dist/commands/daemon/unlock.js +5 -5
  21. package/dist/commands/daemon/usage.js +6 -6
  22. package/dist/commands/login.js +7 -2
  23. package/dist/commands/spaces/list.d.ts +13 -0
  24. package/dist/commands/spaces/list.js +20 -1
  25. package/dist/commands/tasks/list.d.ts +11 -0
  26. package/dist/commands/tasks/list.js +82 -11
  27. package/dist/commands/wiki/cat.js +2 -1
  28. package/dist/commands/wiki/mv.js +5 -1
  29. package/dist/commands/wiki/write.js +5 -1
  30. package/dist/lib/api-fetch.d.ts +2 -1
  31. package/dist/lib/api-fetch.js +9 -2
  32. package/dist/lib/commitments.d.ts +44 -0
  33. package/dist/lib/commitments.js +123 -0
  34. package/dist/lib/daemon-binding.d.ts +3 -4
  35. package/dist/lib/daemon-binding.js +11 -10
  36. package/dist/lib/daemon-handoff-opt-out.d.ts +8 -0
  37. package/dist/lib/daemon-handoff-opt-out.js +17 -0
  38. package/dist/lib/daemon-installer.d.ts +1 -1
  39. package/dist/lib/daemon-installer.js +2 -2
  40. package/dist/lib/daemon-setup.js +13 -7
  41. package/dist/lib/daemonHandoff.d.ts +1 -1
  42. package/dist/lib/daemonHandoff.js +20 -7
  43. package/dist/lib/delegated-cli.d.ts +0 -9
  44. package/dist/lib/delegated-cli.js +149 -6
  45. package/dist/lib/{exec-oversky.d.ts → exec-runtime-binary.d.ts} +15 -4
  46. package/dist/lib/{exec-oversky.js → exec-runtime-binary.js} +93 -37
  47. package/dist/lib/node-adapter.js +6 -0
  48. package/dist/lib/sky-code.d.ts +1 -1
  49. package/dist/lib/sky-code.js +1 -1
  50. package/dist/lib/usage-discovery.js +1 -1
  51. package/dist/lib/web-url.d.ts +16 -0
  52. package/dist/lib/web-url.js +25 -0
  53. package/dist/node_modules/@skrr-ai/data-provider/index.js +3931 -3884
  54. package/oclif.manifest.json +17600 -17572
  55. package/package.json +1 -1
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const exec_oversky_1 = require("../../lib/exec-oversky");
4
+ const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
5
5
  class DaemonStop extends core_1.Command {
6
6
  // `skrr daemons <sub>` — the plural is advertised as an alias for the
7
7
  // whole topic, and without a per-command alias only the bare topic
8
8
  // resolved: every subcommand under it failed.
9
9
  static aliases = ['daemons:stop'];
10
10
  static summary = 'Stop the local skrr daemon';
11
- static description = (0, exec_oversky_1.forwardedFlagsNote)('stop');
11
+ static description = (0, exec_runtime_binary_1.forwardedFlagsNote)('stop');
12
12
  static strict = false;
13
13
  async run() {
14
14
  // `this.argv`, NOT `this.parse()`. `strict = false` relaxes POSITIONAL
@@ -22,12 +22,12 @@ class DaemonStop extends core_1.Command {
22
22
  // mistake, one directory over.
23
23
  const argv = this.argv;
24
24
  try {
25
- const code = await (0, exec_oversky_1.execOversky)(['stop', ...argv]);
25
+ const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(['stop', ...argv]);
26
26
  this.exit(code);
27
27
  }
28
28
  catch (err) {
29
- if (err.message === 'oversky-not-installed') {
30
- this.log((0, exec_oversky_1.notInstalledMessage)());
29
+ if (err.message === 'runtime-binary-not-installed') {
30
+ this.log((0, exec_runtime_binary_1.notInstalledMessage)());
31
31
  this.exit(127);
32
32
  }
33
33
  throw err;
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const exec_oversky_1 = require("../../lib/exec-oversky");
4
+ const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
5
5
  /**
6
6
  * `skrr daemon uninstall` — uninstall the local runtime's OS service.
7
7
  *
@@ -20,7 +20,7 @@ class DaemonUninstall extends core_1.Command {
20
20
  // resolved: every subcommand under it failed.
21
21
  static aliases = ['daemons:uninstall'];
22
22
  static summary = "Remove the local skrr runtime's background service (local state is left alone)";
23
- static description = (0, exec_oversky_1.forwardedFlagsNote)('uninstall');
23
+ static description = (0, exec_runtime_binary_1.forwardedFlagsNote)('uninstall');
24
24
  static strict = false; // forward all args verbatim — see run()
25
25
  async run() {
26
26
  // `this.argv`, NOT `this.parse()`. `strict = false` relaxes POSITIONAL
@@ -34,12 +34,12 @@ class DaemonUninstall extends core_1.Command {
34
34
  // mistake, one directory over.
35
35
  const argv = this.argv;
36
36
  try {
37
- const code = await (0, exec_oversky_1.execOversky)(['uninstall', ...argv]);
37
+ const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(['uninstall', ...argv]);
38
38
  this.exit(code);
39
39
  }
40
40
  catch (err) {
41
- if (err.message === 'oversky-not-installed') {
42
- this.log((0, exec_oversky_1.notInstalledMessage)());
41
+ if (err.message === 'runtime-binary-not-installed') {
42
+ this.log((0, exec_runtime_binary_1.notInstalledMessage)());
43
43
  this.exit(127);
44
44
  }
45
45
  throw err;
@@ -1,14 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const exec_oversky_1 = require("../../lib/exec-oversky");
4
+ const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
5
5
  class DaemonUnlock extends core_1.Command {
6
6
  // `skrr daemons <sub>` — the plural is advertised as an alias for the
7
7
  // whole topic, and without a per-command alias only the bare topic
8
8
  // resolved: every subcommand under it failed.
9
9
  static aliases = ['daemons:unlock'];
10
10
  static summary = 'Clear a stranded singleton lock (daemon.lock) left by a crashed skrr daemon';
11
- static description = (0, exec_oversky_1.forwardedFlagsNote)('unlock');
11
+ static description = (0, exec_runtime_binary_1.forwardedFlagsNote)('unlock');
12
12
  static strict = false; // forward all args verbatim — see run()
13
13
  static examples = ['<%= config.bin %> daemon unlock'];
14
14
  async run() {
@@ -18,12 +18,12 @@ class DaemonUnlock extends core_1.Command {
18
18
  // the verb lives on a second binary you have to learn about first.
19
19
  const argv = this.argv;
20
20
  try {
21
- const code = await (0, exec_oversky_1.execOversky)(['unlock', ...argv]);
21
+ const code = await (0, exec_runtime_binary_1.execRuntimeBinary)(['unlock', ...argv]);
22
22
  this.exit(code);
23
23
  }
24
24
  catch (err) {
25
- if (err.message === 'oversky-not-installed') {
26
- this.log((0, exec_oversky_1.notInstalledMessage)());
25
+ if (err.message === 'runtime-binary-not-installed') {
26
+ this.log((0, exec_runtime_binary_1.notInstalledMessage)());
27
27
  this.exit(127);
28
28
  }
29
29
  throw err;
@@ -6,7 +6,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.supportsDaemonUsage = supportsDaemonUsage;
7
7
  const core_1 = require("@oclif/core");
8
8
  const node_child_process_1 = require("node:child_process");
9
- const exec_oversky_1 = require("../../lib/exec-oversky");
9
+ const exec_runtime_binary_1 = require("../../lib/exec-runtime-binary");
10
10
  const index_1 = __importDefault(require("./index"));
11
11
  /** Whether an `oversky --help` surface actually supports the usage command. */
12
12
  function supportsDaemonUsage(helpText) {
@@ -29,9 +29,9 @@ class DaemonUsage extends index_1.default {
29
29
  };
30
30
  async run() {
31
31
  const { flags } = await this.parse(DaemonUsage);
32
- const binary = (0, exec_oversky_1.findOverskyBinary)();
32
+ const binary = (0, exec_runtime_binary_1.findRuntimeBinary)();
33
33
  if (!binary) {
34
- this.log((0, exec_oversky_1.notInstalledMessage)());
34
+ this.log((0, exec_runtime_binary_1.notInstalledMessage)());
35
35
  this.exit(127);
36
36
  }
37
37
  // A stale PATH-first `oversky` (0.2.x is common on Homebrew installs) does
@@ -58,11 +58,11 @@ class DaemonUsage extends index_1.default {
58
58
  if (flags.json)
59
59
  args.push('--json');
60
60
  try {
61
- this.exit(await (0, exec_oversky_1.execOversky)(args));
61
+ this.exit(await (0, exec_runtime_binary_1.execRuntimeBinary)(args));
62
62
  }
63
63
  catch (error) {
64
- if (error.message === 'oversky-not-installed') {
65
- this.log((0, exec_oversky_1.notInstalledMessage)());
64
+ if (error.message === 'runtime-binary-not-installed') {
65
+ this.log((0, exec_runtime_binary_1.notInstalledMessage)());
66
66
  this.exit(127);
67
67
  }
68
68
  throw error;
@@ -38,7 +38,10 @@ const auth_storage_1 = require("../lib/auth-storage");
38
38
  * `cliLoginWithRefreshToken` for the full rationale.
39
39
  */
40
40
  class Login extends base_command_1.BaseCommand {
41
- static description = 'Sign in to a skrr instance (PKCE localhost callback; device-code fallback)';
41
+ static description = 'Sign in to a skrr instance (PKCE localhost callback; device-code fallback). ' +
42
+ 'On an interactive first login without a local runtime, skrr offers to install it as a ' +
43
+ 'background service. Choose no, or set SKRR_SKIP_DAEMON_HANDOFF=1 before running this ' +
44
+ 'command, to skip that optional setup.';
42
45
  static examples = [
43
46
  '<%= config.bin %> login',
44
47
  '<%= config.bin %> login --env prod',
@@ -186,7 +189,9 @@ class Login extends base_command_1.BaseCommand {
186
189
  // profile, and until this login there was no session to take them from.
187
190
  // Keyed off `reason` rather than the sentence in `detail`, which is
188
191
  // written for a person and expected to be reworded.
189
- if (handoff.reason === 'no-binary' || handoff.reason === 'no-service') {
192
+ if (handoff.reason === 'no-binary' ||
193
+ handoff.reason === 'legacy-binary' ||
194
+ handoff.reason === 'no-service') {
190
195
  const setup = await (0, daemon_setup_1.offerDaemonSetup)({
191
196
  binaryPresent: handoff.reason === 'no-service',
192
197
  log: (line) => this.log(line),
@@ -17,6 +17,19 @@ import { BaseCommand } from '../../base-command';
17
17
  * convention `describeResourceNotFound` follows in `base-command`.
18
18
  */
19
19
  export declare function noSpacesMatchedMessage(q: string, bin?: string): string;
20
+ /**
21
+ * What the unfiltered zero-row case is allowed to say.
22
+ *
23
+ * A bare `No spaces.` on a fresh account is technically true and operationally
24
+ * useless: it names no next step, so the user reading `GETTING_STARTED.md`
25
+ * step 3 hits three of these in a row and concludes the product is inert.
26
+ * `commitments/list.ts` learned this the hard way and left the reasoning in a
27
+ * comment; the fix is to follow the same pattern here — one line naming the
28
+ * command that fixes it.
29
+ *
30
+ * Pure and exported so the wording is testable without an oclif runtime.
31
+ */
32
+ export declare function noSpacesEmptyMessage(bin?: string): string;
20
33
  export default class SpacesList extends BaseCommand {
21
34
  static description: string;
22
35
  static examples: string[];
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.noSpacesMatchedMessage = noSpacesMatchedMessage;
4
+ exports.noSpacesEmptyMessage = noSpacesEmptyMessage;
4
5
  const core_1 = require("@oclif/core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
6
7
  const base_command_1 = require("../../base-command");
@@ -29,6 +30,22 @@ function noSpacesMatchedMessage(q, bin = 'skrr') {
29
30
  'not a tokenized search, so word order matters.\n' +
30
31
  ` → retry with one distinctive word, or list them all with \`${bin} spaces list\`.`);
31
32
  }
33
+ /**
34
+ * What the unfiltered zero-row case is allowed to say.
35
+ *
36
+ * A bare `No spaces.` on a fresh account is technically true and operationally
37
+ * useless: it names no next step, so the user reading `GETTING_STARTED.md`
38
+ * step 3 hits three of these in a row and concludes the product is inert.
39
+ * `commitments/list.ts` learned this the hard way and left the reasoning in a
40
+ * comment; the fix is to follow the same pattern here — one line naming the
41
+ * command that fixes it.
42
+ *
43
+ * Pure and exported so the wording is testable without an oclif runtime.
44
+ */
45
+ function noSpacesEmptyMessage(bin = 'skrr') {
46
+ return ('No spaces yet.\n' +
47
+ ` → Create your first one: \`${bin} spaces create --title "My workspace"\``);
48
+ }
32
49
  class SpacesList extends base_command_1.BaseCommand {
33
50
  static description = 'List spaces';
34
51
  static examples = [
@@ -95,7 +112,9 @@ class SpacesList extends base_command_1.BaseCommand {
95
112
  const r = response;
96
113
  const items = r?.data ?? [];
97
114
  if (items.length === 0) {
98
- this.log(flags.q ? noSpacesMatchedMessage(flags.q, this.config.bin) : 'No spaces.');
115
+ this.log(flags.q
116
+ ? noSpacesMatchedMessage(flags.q, this.config.bin)
117
+ : noSpacesEmptyMessage(this.config.bin));
99
118
  return;
100
119
  }
101
120
  const rows = items.map((s) => ({
@@ -1,6 +1,17 @@
1
1
  import type { TaskListParams } from '@skrr-ai/data-provider';
2
2
  import { BaseCommand } from '../../base-command';
3
3
  type ListFlags = Record<string, unknown>;
4
+ /**
5
+ * What the zero-row case is allowed to say. The bare `No tasks.` was step 3
6
+ * of `GETTING_STARTED.md` and named no next step; a new user reading it hit
7
+ * three of these in a row and concluded the product was empty. Follow the
8
+ * `commitments/list.ts` pattern: name the command that fixes THIS empty state,
9
+ * with the specific `--status in_progress` hint kept because a reaped run
10
+ * genuinely hides here (a real bug, not a first-run scenario).
11
+ *
12
+ * Exported and pure so the wording is testable without an oclif runtime.
13
+ */
14
+ export declare function emptyTasksMessage(flags: ListFlags, bin?: string): string;
4
15
  /**
5
16
  * The task-list request bag is shared with import/export so every CLI list
6
17
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emptyTasksMessage = emptyTasksMessage;
3
4
  exports.buildListParams = buildListParams;
4
5
  const core_1 = require("@oclif/core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
@@ -7,6 +8,86 @@ const base_command_1 = require("../../base-command");
7
8
  const web_url_1 = require("../../lib/web-url");
8
9
  const format_1 = require("../../lib/format");
9
10
  const tasks_1 = require("../../lib/tasks");
11
+ /**
12
+ * Names of `tasks list` flags that narrow the result set. Used to distinguish
13
+ * "no tasks anywhere" from "no tasks matching your filters" — the first case
14
+ * wants a create-command hint, the second wants a loosen-filters hint. Kept
15
+ * as a set so a new filter added to `buildListParams` shows up here at review
16
+ * time rather than silently changing the empty state.
17
+ */
18
+ const FILTER_FLAGS = new Set([
19
+ 'goal',
20
+ 'project',
21
+ 'cycle',
22
+ 'parent',
23
+ 'status',
24
+ 'status-type',
25
+ 'execution-state',
26
+ 'actionable-by',
27
+ 'actionable-for-agent',
28
+ 'needs-me',
29
+ 'actionability-state',
30
+ 'actionability-reason',
31
+ 'priority',
32
+ 'estimate',
33
+ 'min-estimate',
34
+ 'assignee-agent',
35
+ 'assignee-user',
36
+ 'dri',
37
+ 'dri-kind',
38
+ 'mine',
39
+ 'explicit-only',
40
+ 'subscribed',
41
+ 'include-snoozed',
42
+ 'external-key',
43
+ 'blocked-by',
44
+ 'blocks',
45
+ 'due-before',
46
+ 'due-after',
47
+ 'overdue',
48
+ 'tag',
49
+ 'label-id',
50
+ 'q',
51
+ ]);
52
+ /**
53
+ * What the zero-row case is allowed to say. The bare `No tasks.` was step 3
54
+ * of `GETTING_STARTED.md` and named no next step; a new user reading it hit
55
+ * three of these in a row and concluded the product was empty. Follow the
56
+ * `commitments/list.ts` pattern: name the command that fixes THIS empty state,
57
+ * with the specific `--status in_progress` hint kept because a reaped run
58
+ * genuinely hides here (a real bug, not a first-run scenario).
59
+ *
60
+ * Exported and pure so the wording is testable without an oclif runtime.
61
+ */
62
+ function emptyTasksMessage(flags, bin = 'skrr') {
63
+ const activeFilters = Array.from(FILTER_FLAGS).filter((name) => {
64
+ const value = flags[name];
65
+ if (value === undefined || value === null || value === false)
66
+ return false;
67
+ if (typeof value === 'string' && value.length === 0)
68
+ return false;
69
+ if (Array.isArray(value) && value.length === 0)
70
+ return false;
71
+ return true;
72
+ });
73
+ const lines = ['No tasks.'];
74
+ if (activeFilters.length === 0) {
75
+ lines.push('');
76
+ lines.push(` → Create your first one: \`${bin} tasks create --title "..." --space <space-id>\``);
77
+ lines.push(` → List spaces to find one: \`${bin} spaces list\``);
78
+ }
79
+ else if (flags.status === 'in_progress' && !flags['execution-state']) {
80
+ // `--status in_progress` filters the board column only. A run that was
81
+ // reaped/reset (e.g. a stuck-task timeout) is moved OFF `in_progress` but
82
+ // keeps its execution markers, so it hides here. Point the operator at
83
+ // the execution-state views that still surface it.
84
+ lines.push('');
85
+ lines.push('Note: `--status in_progress` shows only the board column. A reaped or reset run');
86
+ lines.push(' no longer has that status. Try `--execution-state active` (running now) or');
87
+ lines.push(` \`--execution-state recovered\` (reaped, startable), or \`${bin} tasks active\`.`);
88
+ }
89
+ return lines.join('\n');
90
+ }
10
91
  /**
11
92
  * The task-list request bag is shared with import/export so every CLI list
12
93
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -303,17 +384,7 @@ class TasksList extends base_command_1.BaseCommand {
303
384
  const r = response;
304
385
  const items = r?.data ?? [];
305
386
  if (items.length === 0) {
306
- this.log('No tasks.');
307
- // `--status in_progress` filters the board column only. A run that was
308
- // reaped/reset (e.g. a stuck-task timeout) is moved OFF `in_progress` but
309
- // keeps its execution markers, so it hides here. Point the operator at the
310
- // execution-state views that still surface it.
311
- if (flags.status === 'in_progress' && !flags['execution-state']) {
312
- this.log('');
313
- this.log('Note: `--status in_progress` shows only the board column. A reaped or reset run');
314
- this.log(' no longer has that status. Try `--execution-state active` (running now) or');
315
- this.log(' `--execution-state recovered` (reaped, startable), or `skrr tasks active`.');
316
- }
387
+ this.log(emptyTasksMessage(flags, this.config.bin));
317
388
  return;
318
389
  }
319
390
  const rows = items.map((t) => {
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
+ const web_url_1 = require("../../lib/web-url");
6
7
  class WikiCat extends base_command_1.BaseCommand {
7
8
  static description = 'Print a wiki page (Markdown by default)';
8
9
  static examples = [
@@ -41,7 +42,7 @@ class WikiCat extends base_command_1.BaseCommand {
41
42
  this.handleApiError(err);
42
43
  }
43
44
  if (flags.json) {
44
- this.log(JSON.stringify(page, null, 2));
45
+ this.log(JSON.stringify((0, web_url_1.withWikiPageUrl)(this.cliConfig.baseURL, page), null, 2));
45
46
  return;
46
47
  }
47
48
  // Body only — so `skrr wiki cat ... > page.md` produces a clean file and
@@ -6,6 +6,7 @@ const core_1 = require("@oclif/core");
6
6
  const data_provider_1 = require("@skrr-ai/data-provider");
7
7
  const base_command_1 = require("../../base-command");
8
8
  const http_error_1 = require("../../lib/http-error");
9
+ const web_url_1 = require("../../lib/web-url");
9
10
  const wiki_1 = require("../../lib/wiki");
10
11
  const write_1 = require("./write");
11
12
  /**
@@ -231,7 +232,10 @@ class WikiMv extends base_command_1.BaseCommand {
231
232
  });
232
233
  }
233
234
  if (flags.json) {
234
- this.log(JSON.stringify({ kind: 'page', ...moved }, null, 2));
235
+ this.log(JSON.stringify({
236
+ kind: 'page',
237
+ ...(0, web_url_1.withWikiPageUrl)(this.cliConfig.baseURL, moved),
238
+ }, null, 2));
235
239
  return;
236
240
  }
237
241
  this.log(`Moved page ${source} → ${moved.path ?? args.destination}.`);
@@ -8,6 +8,7 @@ const core_1 = require("@oclif/core");
8
8
  const data_provider_1 = require("@skrr-ai/data-provider");
9
9
  const base_command_1 = require("../../base-command");
10
10
  const http_error_1 = require("../../lib/http-error");
11
+ const web_url_1 = require("../../lib/web-url");
11
12
  const wiki_1 = require("../../lib/wiki");
12
13
  /**
13
14
  * The page lookup below goes through `dataService`, whose adapter throws a
@@ -248,8 +249,9 @@ class WikiWrite extends base_command_1.BaseCommand {
248
249
  `it creates. Retitle it with \`${this.config.bin} wiki update ${args.space} ${page.path ?? args.path} ` +
249
250
  `--title ${JSON.stringify(flags.title)}\` (the slug is kept, so stored paths stay valid).`);
250
251
  }
252
+ const withUrl = (0, web_url_1.withWikiPageUrl)(this.cliConfig.baseURL, page);
251
253
  if (flags.json) {
252
- this.log(JSON.stringify(page, null, 2));
254
+ this.log(JSON.stringify(withUrl, null, 2));
253
255
  return;
254
256
  }
255
257
  // Report what actually happened, not what we set out to do — a write that
@@ -260,6 +262,8 @@ class WikiWrite extends base_command_1.BaseCommand {
260
262
  verb = flags.append ? 'Appended to' : 'Updated';
261
263
  }
262
264
  this.log(`${verb} ${page.path ?? args.path} (${page.pageId}).`);
265
+ if (withUrl.url)
266
+ this.log(`URL: ${withUrl.url}`);
263
267
  }
264
268
  }
265
269
  exports.default = WikiWrite;
@@ -29,7 +29,8 @@ export declare class ApiFetchError extends Error {
29
29
  readonly status: number;
30
30
  readonly body?: string;
31
31
  readonly code?: string;
32
- constructor(message: string, status: number, body?: string, code?: string);
32
+ readonly method?: string;
33
+ constructor(message: string, status: number, body?: string, code?: string, method?: string);
33
34
  }
34
35
  /**
35
36
  * Call a skrr API endpoint with the current credential. Throws
@@ -46,12 +46,19 @@ class ApiFetchError extends Error {
46
46
  status;
47
47
  body;
48
48
  code;
49
- constructor(message, status, body, code) {
49
+ // The HTTP method of the failing request. The 403 branch of
50
+ // `handleApiError` needs to know whether the request even HAD a body before
51
+ // it can advise the user to "try again with less of the request body"
52
+ // (OSK-6765): `agents show` is a GET and had no body, and the advice was
53
+ // impossible to follow.
54
+ method;
55
+ constructor(message, status, body, code, method) {
50
56
  super(message);
51
57
  this.name = 'ApiFetchError';
52
58
  this.status = status;
53
59
  this.body = body;
54
60
  this.code = code;
61
+ this.method = method;
55
62
  }
56
63
  }
57
64
  exports.ApiFetchError = ApiFetchError;
@@ -189,7 +196,7 @@ async function apiFetch(pathOrUrl, opts = {}) {
189
196
  catch {
190
197
  /* ignore body read failures */
191
198
  }
192
- throw new ApiFetchError(`HTTP ${res.status} ${res.statusText} — ${method} ${pathOrUrl}`, res.status, text, code);
199
+ throw new ApiFetchError(`HTTP ${res.status} ${res.statusText} — ${method} ${pathOrUrl}`, res.status, text, code, method);
193
200
  }
194
201
  // 204/205 carry no body by definition; an empty body has no content-type
195
202
  // worth judging. Neither is an error.
@@ -362,6 +362,29 @@ export declare const COMMITMENT_CREATE_KEYS: readonly ["agentId", "kind", "compa
362
362
  */
363
363
  export declare function normalizeCreateBody(input: Record<string, unknown>): Record<string, unknown>;
364
364
  export declare function parseIntervalToMs(input: string): number;
365
+ /**
366
+ * Assemble the `watchedSources` array from CLI flags — one place, so `create`
367
+ * and `update` agree on which flags carry what.
368
+ *
369
+ * The `space` and `monitor` sources need a scope id (`config.spaceId` /
370
+ * `config.monitorId`) that the flat `--watch` enum cannot carry. The API
371
+ * rejects the whole write when one is missing (`SPACE_SOURCE_SCOPE_REQUIRED` /
372
+ * `INVALID_MONITOR_REFERENCE`) — the same shape that made this a finding: the
373
+ * CLI advertised the source but had no way to satisfy it.
374
+ *
375
+ * The companion flags (`--watch-space`, `--watch-monitor`) fill that gap and
376
+ * implicitly opt the source in, so the enum and its config cannot disagree.
377
+ * Space refs must be RESOLVED to canonical ids by the caller — this helper
378
+ * does not do the network round trip, so it is unit-testable on its own.
379
+ */
380
+ export type WatchFlagInputs = {
381
+ watch?: string[];
382
+ metricKey?: string[];
383
+ /** Already resolved to canonical space ids by the caller. */
384
+ spaceIds?: string[];
385
+ monitorIds?: string[];
386
+ };
387
+ export declare function buildWatchedSources(inputs: WatchFlagInputs): Array<Record<string, unknown>> | undefined;
365
388
  /** The evidence-health shape carried on a check's `inputs`. */
366
389
  export type EvidenceHealthView = {
367
390
  state?: string | null;
@@ -429,6 +452,27 @@ export declare function effectiveAutonomyMode(check?: {
429
452
  policyTrace?: Array<Record<string, unknown>>;
430
453
  } | null;
431
454
  } | null): string | undefined;
455
+ /**
456
+ * A `commitment_standstill` policyTrace entry, extracted from the latest
457
+ * check's decision. The arbiter appends it whenever a quiet posture is the
458
+ * ANSWER to "why nothing is happening" (`CommitmentControl/index.js` around
459
+ * the `record_only`/empty-action-plan branch). Every field except `reason` is
460
+ * optional, and old checks lack the entry entirely — so a missing entry means
461
+ * "the loop had nothing standstill-worthy to say", not "we don't know".
462
+ *
463
+ * Exported for tests.
464
+ */
465
+ export declare function extractStandstillFromCheck(check?: {
466
+ decision?: {
467
+ policyTrace?: Array<Record<string, unknown>>;
468
+ } | null;
469
+ } | null): {
470
+ reason: string;
471
+ layer?: string;
472
+ owner?: string;
473
+ fix?: string;
474
+ alsoConstrained?: Array<Record<string, unknown>>;
475
+ } | null;
432
476
  /**
433
477
  * The `Autonomy:` line — the stated mode, annotated when the latest check
434
478
  * actually ran at a STRICTER one (OSK-4881). Without the caveat, a commitment