@skrr-ai/cli 0.1.116 → 0.1.117

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 (63) hide show
  1. package/dist/base-command.d.ts +21 -0
  2. package/dist/base-command.js +95 -8
  3. package/dist/commands/actions/install.d.ts +1 -0
  4. package/dist/commands/actions/install.js +46 -1
  5. package/dist/commands/commitments/create.d.ts +21 -2
  6. package/dist/commands/commitments/create.js +74 -4
  7. package/dist/commands/convos/recovery.js +12 -4
  8. package/dist/commands/followups/plans/list.js +1 -1
  9. package/dist/commands/guide.d.ts +21 -0
  10. package/dist/commands/guide.js +46 -0
  11. package/dist/commands/instructions/guide.js +34 -52
  12. package/dist/commands/memory/maintain.js +1 -1
  13. package/dist/commands/skills/install.d.ts +1 -0
  14. package/dist/commands/skills/install.js +41 -2
  15. package/dist/commands/slack/send-as-agent.js +3 -3
  16. package/dist/commands/spaces/create.js +8 -2
  17. package/dist/commands/spaces/guide.d.ts +21 -0
  18. package/dist/commands/spaces/guide.js +40 -0
  19. package/dist/commands/spaces/index.d.ts +0 -1
  20. package/dist/commands/spaces/index.js +1 -6
  21. package/dist/commands/tasks/checkout.js +2 -3
  22. package/dist/commands/tasks/comments/add.js +3 -5
  23. package/dist/commands/tasks/complete.js +2 -3
  24. package/dist/commands/tasks/deliverable/add.d.ts +1 -1
  25. package/dist/commands/tasks/deliverable/add.js +4 -7
  26. package/dist/commands/tasks/expectations/assess.js +2 -2
  27. package/dist/commands/tasks/guide.d.ts +8 -0
  28. package/dist/commands/tasks/guide.js +23 -10
  29. package/dist/commands/tasks/report.js +9 -5
  30. package/dist/commands/tasks/resource/add.js +2 -3
  31. package/dist/commands/tasks/result/submit.js +2 -3
  32. package/dist/commands/tasks/resume/save.js +2 -3
  33. package/dist/commands/tasks/review.js +2 -3
  34. package/dist/commands/tasks/triage.js +2 -1
  35. package/dist/commands/tasks/updates/add.js +2 -3
  36. package/dist/generated/agent-guides.json +8290 -0
  37. package/dist/generated/managed-instruction-guides.json +1 -1
  38. package/dist/help.js +4 -3
  39. package/dist/hooks/command-not-found.js +1 -1
  40. package/dist/lib/agent-guide-reference-gate.d.ts +28 -0
  41. package/dist/lib/agent-guide-reference-gate.js +186 -0
  42. package/dist/lib/agent-guides.d.ts +51 -0
  43. package/dist/lib/agent-guides.js +170 -0
  44. package/dist/lib/command-miss.d.ts +13 -2
  45. package/dist/lib/command-miss.js +134 -8
  46. package/dist/lib/guide-pointer.d.ts +15 -0
  47. package/dist/lib/guide-pointer.js +51 -0
  48. package/dist/lib/lookbackPlans.d.ts +3 -1
  49. package/dist/lib/lookbackPlans.js +9 -0
  50. package/dist/lib/outbox.d.ts +0 -14
  51. package/dist/lib/outbox.js +0 -21
  52. package/dist/lib/roles.d.ts +1 -1
  53. package/dist/lib/task-batch.d.ts +10 -7
  54. package/dist/lib/task-batch.js +5 -20
  55. package/dist/lib/task-operations.d.ts +8 -20
  56. package/dist/lib/task-operations.js +9 -94
  57. package/dist/lib/task-preflight.js +70 -33
  58. package/dist/lib/usage-discovery.d.ts +4 -0
  59. package/dist/lib/usage-discovery.js +5 -0
  60. package/dist/lib/voice-call-intent.js +3 -1
  61. package/dist/node_modules/@skrr-ai/data-provider/index.js +6593 -6383
  62. package/oclif.manifest.json +228 -13
  63. package/package.json +4 -2
@@ -477,6 +477,12 @@ export interface ParsedGlobals {
477
477
  workspace?: string;
478
478
  }
479
479
  export declare function parseGlobalFlags(argv: readonly string[]): ParsedGlobals;
480
+ /**
481
+ * The typed refusal code an API error body carries, wherever this codebase has
482
+ * put it: canonical `code` (or `errorType`) first, then the legacy
483
+ * code-spelled-in-`error` shape.
484
+ */
485
+ export declare function refusalCodeOf(body: Record<string, unknown> | null | undefined): string | undefined;
480
486
  /**
481
487
  * The typed `code` and message an API refusal carried, read the way
482
488
  * `handleApiError` reads them, for a command that answers one code itself
@@ -621,6 +627,21 @@ export declare function errorBodyMessage(body: unknown): string | undefined;
621
627
  * from there, so no caller reads two places. A lifted `details.message` cannot replace it.
622
628
  */
623
629
  export declare function envelopeDetails(body: Record<string, unknown> | null | undefined): unknown;
630
+ type UnsupportedFlagRemedyTarget = {
631
+ command: string;
632
+ flags: readonly string[];
633
+ };
634
+ type UnsupportedFlagRemedy = {
635
+ fix: string;
636
+ targets: readonly UnsupportedFlagRemedyTarget[];
637
+ };
638
+ /**
639
+ * Specific corrections for habitual flags that look plausible but do not belong
640
+ * to the command that received them. Keep the replacement command and every
641
+ * flag it names explicit: the spec checks this table against the shipped
642
+ * manifest, so a useful refusal cannot slowly point at a removed form.
643
+ */
644
+ export declare const UNSUPPORTED_FLAG_REMEDIES: Readonly<Record<string, Readonly<Record<string, UnsupportedFlagRemedy>>>>;
624
645
  /**
625
646
  * The hint for a `Nonexistent flags:` refusal, or null. Two cases:
626
647
  * - a flag retired from THIS command, which this command does not define again: where it went;
@@ -1,8 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.Flags = exports.BaseCommand = exports.resolveProfileFromArgv = void 0;
3
+ exports.Flags = exports.UNSUPPORTED_FLAG_REMEDIES = exports.BaseCommand = exports.resolveProfileFromArgv = void 0;
4
4
  exports.assigneePayloadNeedsResolution = assigneePayloadNeedsResolution;
5
5
  exports.parseGlobalFlags = parseGlobalFlags;
6
+ exports.refusalCodeOf = refusalCodeOf;
6
7
  exports.apiRefusal = apiRefusal;
7
8
  exports.withSubject = withSubject;
8
9
  exports.describeHarnessUnavailable = describeHarnessUnavailable;
@@ -31,6 +32,7 @@ const json_mode_1 = require("./lib/json-mode");
31
32
  const failed_lookup_1 = require("./lib/failed-lookup");
32
33
  const internal_id_1 = require("./lib/internal-id");
33
34
  const http_error_1 = require("./lib/http-error");
35
+ const guide_pointer_1 = require("./lib/guide-pointer");
34
36
  // Re-exported from its home in `lib/keychain`, beside the other profile
35
37
  // functions. It lived here while `init()` was its only caller; a lib module
36
38
  // needing it would otherwise have to import the command base class, and with
@@ -1279,6 +1281,10 @@ class BaseCommand extends core_1.Command {
1279
1281
  if (retiredHint) {
1280
1282
  err.message = `${err.message}\n\n${retiredHint}`;
1281
1283
  }
1284
+ const unsupportedFlagHint = unsupportedFlagRemedyForMessage(err.message || '', this.id);
1285
+ if (unsupportedFlagHint && !err.message.includes(unsupportedFlagHint.fix)) {
1286
+ err.message = `${err.message}\n\n${unsupportedFlagHint.fix}`;
1287
+ }
1282
1288
  // Sibling domains spell one lifecycle differently (`in_progress` for a
1283
1289
  // space, `active` for a goal), and oclif's enum refusal named neither the
1284
1290
  // difference nor the value to use (OSK-9735).
@@ -1296,13 +1302,19 @@ class BaseCommand extends core_1.Command {
1296
1302
  const httpBody = (0, http_error_1.httpBodyOf)(err);
1297
1303
  const httpStatus = (0, http_error_1.httpStatusOf)(err);
1298
1304
  if (httpBody && httpStatus !== undefined && httpStatus >= 400) {
1299
- const bodyCode = stringProp(httpBody, 'code') || stringProp(httpBody, 'errorType');
1305
+ const bodyCode = refusalCodeOf(httpBody);
1300
1306
  const bodyMessage = stringProp(httpBody, 'message') || stringProp(httpBody, 'error');
1301
1307
  err.code ??= bodyCode || `HTTP_${httpStatus}`;
1302
1308
  if (bodyMessage && !err.message.includes(bodyMessage)) {
1303
1309
  err.message = `${bodyMessage} — ${err.message}`;
1304
1310
  }
1305
1311
  }
1312
+ // A refusal a guide section explains points the reader at it (OSK-17977):
1313
+ // text gets a `Guide:` line as the message's last line, --json a `guide`
1314
+ // field. Resolved offline from the bundled registry — never a request on
1315
+ // an already-failing path — and absent for a code nothing explains.
1316
+ const code = err.code ?? parseCliErrorCode(err);
1317
+ const guide = (0, guide_pointer_1.agentGuidePointerFor)(code);
1306
1318
  if (this.wantsJsonOutput()) {
1307
1319
  const exit = err.exitCode ?? err.oclif?.exit ?? 1;
1308
1320
  process.exitCode = exit;
@@ -1310,17 +1322,23 @@ class BaseCommand extends core_1.Command {
1310
1322
  this.log(JSON.stringify(this.jsonOperationError({
1311
1323
  ok: false,
1312
1324
  error: {
1313
- code: err.code ?? parseCliErrorCode(err),
1325
+ code,
1314
1326
  message: err.message || String(err),
1315
1327
  retryable: err.retryable ?? false,
1316
1328
  command: this.id ?? null,
1317
1329
  server: this.cliConfig?.baseURL ?? null,
1318
1330
  ...parseErrorRemedy(err, this.id, this.config?.bin),
1331
+ ...(guide ? { guide } : {}),
1319
1332
  ...(details ? { details } : {}),
1320
1333
  },
1321
1334
  }), null, 2));
1322
1335
  return;
1323
1336
  }
1337
+ if (guide) {
1338
+ const line = (0, guide_pointer_1.agentGuidePointerLine)(guide);
1339
+ if (!err.message.includes(line))
1340
+ err.message = `${err.message}\n\n${line}`;
1341
+ }
1324
1342
  return super.catch(err);
1325
1343
  }
1326
1344
  error(input, options = {}) {
@@ -1390,7 +1408,7 @@ class BaseCommand extends core_1.Command {
1390
1408
  this.lastApiRequestId = e.requestId;
1391
1409
  this.lastRetryAfterMs = e.retryAfterMs;
1392
1410
  const body = parseErrorBody(e.body);
1393
- const bodyCode = stringProp(body, 'code') || stringProp(body, 'errorType');
1411
+ const bodyCode = refusalCodeOf(body);
1394
1412
  // A body path the person never typed is read back as the flag that wrote it (OSK-15313).
1395
1413
  // The server answers every error with `message` (OSK-16986); `errorBodyMessage` keeps the
1396
1414
  // older `error` string and the nested `/v1` shape as fallbacks for a server without it.
@@ -1987,6 +2005,9 @@ class BaseCommand extends core_1.Command {
1987
2005
  /** E6: the server's `Retry-After` for the refused call, when it was a 429. */
1988
2006
  lastRetryAfterMs;
1989
2007
  writeJsonCliError({ message, code, status, exit, retryable, details, requestId, }) {
2008
+ // The guide section that explains this code, when one does — the `guide`
2009
+ // half of the `Guide:` line the text path appends.
2010
+ const guide = (0, guide_pointer_1.agentGuidePointerFor)(code);
1990
2011
  this.log(JSON.stringify(this.jsonOperationError({
1991
2012
  ok: false,
1992
2013
  error: {
@@ -1996,6 +2017,7 @@ class BaseCommand extends core_1.Command {
1996
2017
  retryable,
1997
2018
  command: this.id ?? null,
1998
2019
  server: this.cliConfig?.baseURL ?? null,
2020
+ ...(guide ? { guide } : {}),
1999
2021
  // The id the server logged this refusal against. Without it, a
2000
2022
  // refused call can only be matched to a log line by guessing from
2001
2023
  // timestamps — which, with several clients on one account,
@@ -2105,6 +2127,27 @@ function stringProp(obj, key) {
2105
2127
  const value = obj?.[key];
2106
2128
  return typeof value === 'string' && value.trim() ? value.trim() : undefined;
2107
2129
  }
2130
+ /**
2131
+ * Lower_snake is how the scoped-Memory REST surface (and older routes like it)
2132
+ * spelled a refusal code in `error`, where the canonical envelope expects human
2133
+ * text (OSK-18250). A released server still answers that way, so a code-shaped
2134
+ * `error` is read as the code — reported in the upper-case spelling the code
2135
+ * vocabulary and `parseMemoryApiError` already use. Anything else in `error`
2136
+ * (a sentence, a bare word) is text and stays out of `code`.
2137
+ */
2138
+ const LEGACY_ERROR_FIELD_CODE = /^[a-z][a-z0-9]*(?:_[a-z0-9]+)+$/;
2139
+ /**
2140
+ * The typed refusal code an API error body carries, wherever this codebase has
2141
+ * put it: canonical `code` (or `errorType`) first, then the legacy
2142
+ * code-spelled-in-`error` shape.
2143
+ */
2144
+ function refusalCodeOf(body) {
2145
+ const direct = stringProp(body ?? null, 'code') || stringProp(body ?? null, 'errorType');
2146
+ if (direct)
2147
+ return direct;
2148
+ const legacy = stringProp(body ?? null, 'error');
2149
+ return legacy && LEGACY_ERROR_FIELD_CODE.test(legacy) ? legacy.toUpperCase() : undefined;
2150
+ }
2108
2151
  /**
2109
2152
  * The typed `code` and message an API refusal carried, read the way
2110
2153
  * `handleApiError` reads them, for a command that answers one code itself
@@ -2115,7 +2158,7 @@ function apiRefusal(err) {
2115
2158
  return {};
2116
2159
  const body = parseErrorBody(err.body);
2117
2160
  return {
2118
- code: stringProp(body, 'code') || stringProp(body, 'errorType'),
2161
+ code: refusalCodeOf(body),
2119
2162
  message: errorBodyMessage(body),
2120
2163
  ...(body ? { body } : {}),
2121
2164
  };
@@ -2432,7 +2475,7 @@ function describeDaemonScopedCredential(bin, scope = 'daemon', source) {
2432
2475
  function isTypedServerRefusal(body) {
2433
2476
  if (!body || typeof body !== 'object')
2434
2477
  return false;
2435
- const code = stringProp(body, 'code') || stringProp(body, 'errorType');
2478
+ const code = refusalCodeOf(body);
2436
2479
  if (!code)
2437
2480
  return false;
2438
2481
  return (typeof body.cause === 'string' || typeof body.remedy === 'string' || body.retryable === false);
@@ -2455,7 +2498,7 @@ function isTypedServerRefusal(body) {
2455
2498
  function isApplicationCodedUnavailable(status, body) {
2456
2499
  if (status !== 502 && status !== 503 && status !== 504)
2457
2500
  return false;
2458
- return Boolean(stringProp(body, 'code') || stringProp(body, 'errorType'));
2501
+ return Boolean(refusalCodeOf(body));
2459
2502
  }
2460
2503
  /**
2461
2504
  * The sentence `redactServerErrorResponses` substitutes for a 5xx message in
@@ -2470,7 +2513,7 @@ function isRetryableFailure(status, body) {
2470
2513
  const declared = body?.retryable;
2471
2514
  if (typeof declared === 'boolean')
2472
2515
  return declared;
2473
- const code = stringProp(body, 'code') || stringProp(body, 'errorType');
2516
+ const code = refusalCodeOf(body);
2474
2517
  if (code && TERMINAL_ERROR_CODES.has(code))
2475
2518
  return false;
2476
2519
  return status === 408 || status === 425 || status === 429 || !!(status && status >= 500);
@@ -2600,6 +2643,41 @@ function envelopeDetails(body) {
2600
2643
  * "--outcome is now --summary" (OSK-16494). A rename is a fact about one command.
2601
2644
  */
2602
2645
  const RETIRED_FLAG_COMMANDS = new Set(['tasks:complete', 'tasks:result:submit']);
2646
+ /**
2647
+ * Specific corrections for habitual flags that look plausible but do not belong
2648
+ * to the command that received them. Keep the replacement command and every
2649
+ * flag it names explicit: the spec checks this table against the shipped
2650
+ * manifest, so a useful refusal cannot slowly point at a removed form.
2651
+ */
2652
+ exports.UNSUPPORTED_FLAG_REMEDIES = {
2653
+ 'spaces:create': {
2654
+ agent: {
2655
+ fix: 'A Space is created for the authenticated caller. Add an Agent after creation with ' +
2656
+ '`skrr spaces members add <space-id> --agent <agent-id>`.',
2657
+ targets: [{ command: 'spaces:members:add', flags: ['agent'] }],
2658
+ },
2659
+ },
2660
+ 'tasks:create': {
2661
+ agent: {
2662
+ fix: 'Assign the Task with `--assignee-agent <agent-id>`; `--agent` does not set Task assignees.',
2663
+ targets: [{ command: 'tasks:create', flags: ['assignee-agent'] }],
2664
+ },
2665
+ },
2666
+ 'followups:checkin': {
2667
+ check: {
2668
+ fix: 'A check-in is only a timed return. To check a condition, use ' +
2669
+ '`skrr followups watch --check <check> --success <condition> --abort <condition> --wait <kind>`.',
2670
+ targets: [{ command: 'followups:watch', flags: ['check', 'success', 'abort', 'wait'] }],
2671
+ },
2672
+ },
2673
+ };
2674
+ function unsupportedFlagRemedy(commandId, flag) {
2675
+ return commandId ? exports.UNSUPPORTED_FLAG_REMEDIES[commandId]?.[flag] : undefined;
2676
+ }
2677
+ function unsupportedFlagRemedyForMessage(message, commandId) {
2678
+ const match = /Nonexistent flag[s]?:?\s*(-{1,2}[\w-]+)/i.exec(message);
2679
+ return match ? unsupportedFlagRemedy(commandId, match[1].replace(/^-+/, '')) : undefined;
2680
+ }
2603
2681
  /**
2604
2682
  * The hint for a `Nonexistent flags:` refusal, or null. Two cases:
2605
2683
  * - a flag retired from THIS command, which this command does not define again: where it went;
@@ -2651,6 +2729,15 @@ function parseErrorRemedy(err, commandId, bin = 'skrr') {
2651
2729
  };
2652
2730
  }
2653
2731
  const nonexistent = /Nonexistent flag[s]?:?\s*(-{1,2}[\w-]+)(?:[ =]([^\s,][^,\n]*))?/i.exec(message);
2732
+ const unsupportedFlagHint = nonexistent
2733
+ ? unsupportedFlagRemedy(commandId, nonexistent[1].replace(/^-+/, ''))
2734
+ : undefined;
2735
+ if (unsupportedFlagHint) {
2736
+ return {
2737
+ flag: nonexistent[1].replace(/^-+/, ''),
2738
+ fix: unsupportedFlagHint.fix,
2739
+ };
2740
+ }
2654
2741
  if (nonexistent?.[2]) {
2655
2742
  // `"--outcome failed"` arrived as ONE argument: the flag may well exist, so
2656
2743
  // "remove it" would be wrong advice. Same case retiredFlagHint explains in text.
@@ -8,6 +8,7 @@ export default class ActionsInstall extends BaseCommand {
8
8
  static flags: {
9
9
  agent: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
10
10
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
12
  };
12
13
  run(): Promise<void>;
13
14
  }
@@ -3,11 +3,13 @@ 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 prompt_1 = require("../../lib/prompt");
6
7
  const agent_actions_1 = require("../../lib/agent-actions");
7
8
  class ActionsInstall extends base_command_1.BaseCommand {
8
- static description = 'Install an action listing into an agent';
9
+ static description = 'Install an action listing into an agent. A paid listing shows its price and asks you to confirm first.';
9
10
  static examples = [
10
11
  '<%= config.bin %> actions install <listing-id> --agent <agent-id>',
12
+ '<%= config.bin %> actions install <listing-id> --agent <agent-id> --yes',
11
13
  '<%= config.bin %> actions install <listing-id> --agent <agent-id> --json',
12
14
  ];
13
15
  static args = {
@@ -16,15 +18,58 @@ class ActionsInstall extends base_command_1.BaseCommand {
16
18
  static flags = {
17
19
  agent: core_1.Flags.string({ description: 'Target agent ID', required: true }),
18
20
  json: core_1.Flags.boolean({ description: 'Output install response as JSON' }),
21
+ yes: core_1.Flags.boolean({
22
+ char: 'y',
23
+ description: 'Confirm a paid install without prompting. The price is still printed.',
24
+ }),
19
25
  };
20
26
  async run() {
21
27
  this.requireAuth();
22
28
  const { args, flags } = await this.parse(ActionsInstall);
29
+ // What installing would charge NOW, read before the install sends it.
30
+ // A paid listing shows its price and asks first; the install then pins
31
+ // what was shown, so a listing whose price moved in between is refused
32
+ // rather than charged (OSK-18042 — same convention as `store install`,
33
+ // OSK-12240).
34
+ let listing;
35
+ try {
36
+ listing = await data_provider_1.dataService.getActionListing(args.id);
37
+ }
38
+ catch (err) {
39
+ this.handleApiError(err);
40
+ }
41
+ let charge = Number(listing.price) || 0;
42
+ if (charge > 0 && String(listing.author) === (await this.resolveCurrentUserId())) {
43
+ // Installing a listing you published is never charged — pin what the
44
+ // server would actually charge, not the sticker price.
45
+ charge = 0;
46
+ }
47
+ if (charge > 0) {
48
+ const name = listing.displayName || listing.name || args.id;
49
+ // Printed before any prompt and even with --yes, so a scripted purchase
50
+ // still leaves the price it agreed to in its own output.
51
+ this.logToStderr(`${name} is a paid action.`);
52
+ this.logToStderr(` Price: ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)} USD`);
53
+ if (listing.offerVersionId) {
54
+ this.logToStderr(` Offer: ${listing.offerVersionId}`);
55
+ }
56
+ const confirmed = await (0, prompt_1.confirmDestructive)(`Buy ${name} for ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)}?`, flags.yes).catch((err) => this.error(err.message, { exit: err.exit ?? 1 }));
57
+ if (!confirmed) {
58
+ this.log('Cancelled. Nothing was bought.');
59
+ return;
60
+ }
61
+ }
23
62
  let response;
24
63
  try {
25
64
  response = await data_provider_1.dataService.installActionListing({
26
65
  listingId: args.id,
27
66
  targetAgentId: flags.agent,
67
+ // Pin what the person was shown: the charge always, the offer when
68
+ // there is one to charge under.
69
+ expectedPrice: charge,
70
+ ...(charge > 0 && listing.offerVersionId
71
+ ? { expectedOfferVersionId: listing.offerVersionId }
72
+ : {}),
28
73
  });
29
74
  }
30
75
  catch (err) {
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ import { type AgentGuidePointer } from '../../lib/guide-pointer';
2
3
  /**
3
4
  * True only when the authored create body can materialize at least one event
4
5
  * binding. A polling-only source must not make `--event-driven` look successful
@@ -26,13 +27,30 @@ export declare function createCadenceFromFlags({ eventDriven, cadence: cadenceIn
26
27
  * be said out loud at the CLI boundary; otherwise a successful create response
27
28
  * reads like the requested autonomy is live when something else is running.
28
29
  */
30
+ /**
31
+ * A refused `--activate` leaves a draft, and the draft's id is the answer the
32
+ * caller needs — so the refusal is reported on the result, never thrown over
33
+ * it. `{code, reason, fix}` mirrors the MCP surface's refusal; `guide` points
34
+ * at the section that explains the code when one does (resolved offline).
35
+ */
36
+ export declare function commitmentActivationRefusal(err: unknown, { id, bin }: {
37
+ id: string;
38
+ bin: string;
39
+ }): {
40
+ code: string;
41
+ reason: string;
42
+ fix: string;
43
+ guide?: AgentGuidePointer;
44
+ };
29
45
  /**
30
46
  * `skrr commitments create` — author a durable commitment contract.
31
47
  *
32
48
  * A Commitment is a standing commitment ("hold this outcome true"), watched on a
33
49
  * cadence by its own control loop — not a task. Create it `draft` first and
34
- * activate once the contract reads right; an `active` commitment with a non-manual
35
- * cadence starts spending compute on every check.
50
+ * activate once the contract reads right — or pass `--activate` to turn it on
51
+ * in the same call (the same resume + Preflight boundary `commitments resume`
52
+ * runs; a refusal keeps the draft and says why). An `active` commitment with a
53
+ * non-manual cadence starts spending compute on every check.
36
54
  *
37
55
  * For a natural-language draft instead of flags, use `skrr commitments plan`.
38
56
  */
@@ -51,6 +69,7 @@ export default class CommitmentsCreate extends BaseCommand {
51
69
  'agent-directive-file': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
52
70
  goal: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
53
71
  status: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
72
+ activate: import("@oclif/core/interfaces").BooleanFlag<boolean>;
54
73
  deadline: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
55
74
  criterion: import("@oclif/core/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/interfaces").CustomOptions>;
56
75
  evaluator: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -3,9 +3,11 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.hasEventCapableWatchedSource = hasEventCapableWatchedSource;
4
4
  exports.assertEventDrivenWatchedSource = assertEventDrivenWatchedSource;
5
5
  exports.createCadenceFromFlags = createCadenceFromFlags;
6
+ exports.commitmentActivationRefusal = commitmentActivationRefusal;
6
7
  const data_provider_1 = require("@skrr-ai/data-provider");
7
8
  const core_1 = require("@oclif/core");
8
9
  const base_command_1 = require("../../base-command");
10
+ const guide_pointer_1 = require("../../lib/guide-pointer");
9
11
  const web_url_1 = require("../../lib/web-url");
10
12
  const prompt_1 = require("../../lib/prompt");
11
13
  const triggers_1 = require("../../lib/triggers");
@@ -145,13 +147,37 @@ function createCadenceFromFlags({ eventDriven, cadence: cadenceInput, cadenceMod
145
147
  * be said out loud at the CLI boundary; otherwise a successful create response
146
148
  * reads like the requested autonomy is live when something else is running.
147
149
  */
150
+ /**
151
+ * A refused `--activate` leaves a draft, and the draft's id is the answer the
152
+ * caller needs — so the refusal is reported on the result, never thrown over
153
+ * it. `{code, reason, fix}` mirrors the MCP surface's refusal; `guide` points
154
+ * at the section that explains the code when one does (resolved offline).
155
+ */
156
+ function commitmentActivationRefusal(err, { id, bin }) {
157
+ const refusal = (0, base_command_1.apiRefusal)(err);
158
+ const errorField = refusal.body?.error;
159
+ const code = refusal.code ||
160
+ (typeof errorField === 'string' && /^[A-Za-z][A-Za-z0-9_]*$/.test(errorField)
161
+ ? errorField
162
+ : undefined) ||
163
+ 'COMMITMENT_ACTIVATION_REFUSED';
164
+ const guide = (0, guide_pointer_1.agentGuidePointerFor)(code);
165
+ return {
166
+ code,
167
+ reason: refusal.message || (err instanceof Error ? err.message : 'Activation was refused.'),
168
+ fix: (0, data_provider_1.describeCommitmentActivationFix)(code === 'COMMITMENT_ACTIVATION_REFUSED' ? undefined : code, { turnOn: `run \`${bin} commitments resume ${id}\`` }),
169
+ ...(guide ? { guide } : {}),
170
+ };
171
+ }
148
172
  /**
149
173
  * `skrr commitments create` — author a durable commitment contract.
150
174
  *
151
175
  * A Commitment is a standing commitment ("hold this outcome true"), watched on a
152
176
  * cadence by its own control loop — not a task. Create it `draft` first and
153
- * activate once the contract reads right; an `active` commitment with a non-manual
154
- * cadence starts spending compute on every check.
177
+ * activate once the contract reads right — or pass `--activate` to turn it on
178
+ * in the same call (the same resume + Preflight boundary `commitments resume`
179
+ * runs; a refusal keeps the draft and says why). An `active` commitment with a
180
+ * non-manual cadence starts spending compute on every check.
155
181
  *
156
182
  * For a natural-language draft instead of flags, use `skrr commitments plan`.
157
183
  */
@@ -159,7 +185,7 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
159
185
  static description = 'Create a commitment (a durable agent contract). Its cadence is manual, an interval or daily clock times; ' +
160
186
  'it has no weekday, holiday, business-day or trading-calendar fields';
161
187
  static examples = [
162
- '<%= config.bin %> commitments create --agent <agent-id> --title "Ship v2" --outcome "v2 is live for all users" --cadence 6h',
188
+ '<%= config.bin %> commitments create --agent <agent-id> --title "Ship v2" --outcome "v2 is live for all users" --cadence 6h --activate',
163
189
  '<%= config.bin %> commitments create --agent <agent-id> --title "Hiring loop" --outcome "Every candidate hears back in 48h" --watch tasks --watch email --cadence 12h',
164
190
  '<%= config.bin %> commitments create --agent <agent-id> --title "SLA" --outcome "p95 under 200ms" --criterion "p95 latency confirmed" --evaluator metric --measure <measure-id>',
165
191
  '<%= config.bin %> commitments create --agent <agent-id> --title "AWS cost watch" --outcome "Cost regressions are investigated" --agent-directive-file ./aws-cost-policy.md',
@@ -218,6 +244,11 @@ JSON
218
244
  description: 'Create as a draft (default); activate later with commitments resume',
219
245
  options: ['draft'],
220
246
  }),
247
+ activate: core_1.Flags.boolean({
248
+ description: 'Turn the commitment on right after creating it — the same resume + Preflight boundary ' +
249
+ "as `commitments resume`. A refused activation keeps the draft and reports the refusal's " +
250
+ 'code, reason and fix.',
251
+ }),
221
252
  deadline: core_1.Flags.string({ description: 'ISO-8601 deadline for an `achieve` commitment' }),
222
253
  criterion: core_1.Flags.string({
223
254
  description: 'Success criterion label (repeatable). Evaluated manually unless --evaluator/--measure is set.',
@@ -457,17 +488,56 @@ JSON
457
488
  catch (err) {
458
489
  this.handleApiError(err);
459
490
  }
491
+ // `--activate` is the same resume + Preflight boundary `commitments
492
+ // resume` runs, composed onto the create so the caller gets one answer.
493
+ // A refusal is reported on the result — never thrown over the draft's id.
494
+ let activation = null;
495
+ if (flags.activate) {
496
+ try {
497
+ commitment = await commitments_1.commitmentApi.resume(String(commitment.id));
498
+ activation = { activated: true };
499
+ }
500
+ catch (err) {
501
+ activation = {
502
+ activated: false,
503
+ refusal: commitmentActivationRefusal(err, {
504
+ id: String(commitment.id),
505
+ bin: this.config.bin,
506
+ }),
507
+ };
508
+ // Report the row as it now is: a failed materialization rolls the
509
+ // activation back to paused, and printing the create-time snapshot
510
+ // would claim "draft" of a paused Commitment.
511
+ try {
512
+ const live = await commitments_1.commitmentApi.get(String(commitment.id));
513
+ if (live?.commitment)
514
+ commitment = live.commitment;
515
+ }
516
+ catch {
517
+ /* the refusal is the news; the create-time row is the fallback */
518
+ }
519
+ }
520
+ }
460
521
  const warnings = (0, commitments_1.commitmentWriteWarnings)(commitment, { payload });
461
522
  if (flags.json) {
462
523
  this.log(JSON.stringify((0, web_url_1.withCommitmentUrl)(this.cliConfig.baseURL, {
463
524
  ...commitment,
525
+ ...(activation ?? {}),
464
526
  ...(warnings.length ? { warnings } : {}),
465
527
  }), null, 2));
466
528
  return;
467
529
  }
468
- this.log('Created commitment:');
530
+ this.log(activation?.activated ? 'Created and activated commitment:' : 'Created commitment:');
469
531
  (0, commitments_1.renderCommitmentSummary)(commitment, (line) => this.log(line));
470
532
  (0, commitments_1.renderCommitmentWriteWarnings)(warnings, (line) => this.warn(line));
533
+ if (activation && !activation.activated) {
534
+ const { refusal } = activation;
535
+ this.warn(`Activation refused (${refusal.code}): ${refusal.reason}`);
536
+ if (refusal.fix)
537
+ this.warn(`Fix: ${refusal.fix}`);
538
+ if (refusal.guide)
539
+ this.warn(`Guide: ${refusal.guide.command}`);
540
+ }
471
541
  }
472
542
  }
473
543
  exports.default = CommitmentsCreate;
@@ -7,7 +7,11 @@ class ConvosRecovery extends base_command_1.BaseCommand {
7
7
  static description = 'Inspect interrupted native work, continue once, or dismiss its recovery offer';
8
8
  static args = {
9
9
  id: core_1.Args.string({ description: 'Conversation ID', required: true, ignoreStdin: true }),
10
- action: core_1.Args.string({ options: ['list', 'continue', 'dismiss'], default: 'list' }),
10
+ action: core_1.Args.string({
11
+ options: ['list', 'continue', 'dismiss'],
12
+ default: 'list',
13
+ ignoreStdin: true,
14
+ }),
11
15
  };
12
16
  static flags = {
13
17
  json: core_1.Flags.boolean({ description: 'Output JSON' }),
@@ -27,11 +31,15 @@ class ConvosRecovery extends base_command_1.BaseCommand {
27
31
  try {
28
32
  const path = `/api/convos/${encodeURIComponent(args.id)}/recovery`;
29
33
  if (args.action === 'continue') {
30
- result = await (0, api_fetch_1.apiFetch)('/api/agentic/chat', { method: 'POST',
31
- body: { conversationId: args.id, recoveryOfferId: flags.offer } });
34
+ result = await (0, api_fetch_1.apiFetch)('/api/agentic/chat', {
35
+ method: 'POST',
36
+ body: { conversationId: args.id, recoveryOfferId: flags.offer },
37
+ });
32
38
  }
33
39
  else if (args.action === 'dismiss') {
34
- result = await (0, api_fetch_1.apiFetch)(`${path}/${encodeURIComponent(flags.offer)}/dismiss`, { method: 'POST' });
40
+ result = await (0, api_fetch_1.apiFetch)(`${path}/${encodeURIComponent(flags.offer)}/dismiss`, {
41
+ method: 'POST',
42
+ });
35
43
  }
36
44
  else {
37
45
  result = await (0, api_fetch_1.apiFetch)(path);
@@ -34,7 +34,7 @@ class LookbackPlansList extends base_command_1.BaseCommand {
34
34
  return;
35
35
  }
36
36
  if (!result.plans.length) {
37
- this.log('No lookback plans.');
37
+ this.log((0, lookbackPlans_1.lookbackPlansEmptyMessage)(result.scope));
38
38
  return;
39
39
  }
40
40
  for (const plan of result.plans)
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Guide extends Command {
3
+ static baseFlags: {
4
+ bare: import("@oclif/core/interfaces").BooleanFlag<boolean>;
5
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
6
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ raw: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ static description: string;
11
+ static args: {
12
+ id: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
13
+ section: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ offline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ static examples: string[];
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../base-command");
6
+ const agent_guides_1 = require("../lib/agent-guides");
7
+ // Documentation stays available when a daemon-issued Agent credential is
8
+ // unavailable. Do not run BaseCommand's mandatory credential initialization.
9
+ class Guide extends core_1.Command {
10
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
11
+ static description = 'Read Skrr operation guides on demand';
12
+ static args = {
13
+ id: core_1.Args.string({ description: 'Guide id', ignoreStdin: true }),
14
+ section: core_1.Args.string({ description: 'Section id', ignoreStdin: true }),
15
+ };
16
+ static flags = {
17
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
18
+ offline: core_1.Flags.boolean({
19
+ description: 'Read the bundled release guides without checking credentials or the server',
20
+ }),
21
+ };
22
+ static examples = [
23
+ '<%= config.bin %> guide',
24
+ '<%= config.bin %> guide tasks',
25
+ '<%= config.bin %> guide tasks updates --offline',
26
+ ];
27
+ async run() {
28
+ const { args, flags } = await this.parse(Guide);
29
+ const request = {
30
+ ...(args.id ? { id: args.id } : {}),
31
+ ...(args.section ? { section: args.section } : {}),
32
+ };
33
+ const read = await (0, agent_guides_1.readAgentGuide)(request, flags);
34
+ if (!read.found)
35
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
36
+ if (flags.json) {
37
+ process.stdout.write((0, agent_guides_1.agentGuideJson)(read));
38
+ return;
39
+ }
40
+ const notice = (0, agent_guides_1.agentGuideFallbackNotice)(read);
41
+ if (notice)
42
+ process.stderr.write(`${notice}\n`);
43
+ process.stdout.write((0, data_provider_1.renderAgentGuideText)(read.response));
44
+ }
45
+ }
46
+ exports.default = Guide;