@skrr-ai/cli 0.1.13 → 0.1.15

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 (136) hide show
  1. package/dist/base-command.d.ts +12 -36
  2. package/dist/base-command.js +96 -11
  3. package/dist/commands/agents/create.d.ts +1 -0
  4. package/dist/commands/agents/create.js +6 -1
  5. package/dist/commands/agents/instructions/add.d.ts +21 -0
  6. package/dist/commands/agents/instructions/add.js +115 -0
  7. package/dist/commands/agents/instructions/disable.d.ts +13 -0
  8. package/dist/commands/agents/instructions/disable.js +50 -0
  9. package/dist/commands/agents/instructions/edit.d.ts +16 -0
  10. package/dist/commands/agents/instructions/edit.js +79 -0
  11. package/dist/commands/agents/instructions/enable.d.ts +13 -0
  12. package/dist/commands/agents/instructions/enable.js +50 -0
  13. package/dist/commands/agents/instructions/move.d.ts +15 -0
  14. package/dist/commands/agents/instructions/move.js +61 -0
  15. package/dist/commands/agents/instructions/preview.d.ts +13 -0
  16. package/dist/commands/agents/instructions/preview.js +59 -0
  17. package/dist/commands/agents/instructions/remove.d.ts +13 -0
  18. package/dist/commands/agents/instructions/remove.js +49 -0
  19. package/dist/commands/agents/instructions/set.d.ts +17 -0
  20. package/dist/commands/agents/instructions/set.js +79 -0
  21. package/dist/commands/agents/instructions/show.d.ts +15 -0
  22. package/dist/commands/agents/instructions/show.js +76 -0
  23. package/dist/commands/agents/instructions/upgrade.d.ts +16 -0
  24. package/dist/commands/agents/instructions/upgrade.js +77 -0
  25. package/dist/commands/agents/list.d.ts +10 -0
  26. package/dist/commands/agents/list.js +16 -1
  27. package/dist/commands/agents/show.js +5 -0
  28. package/dist/commands/agents/triggers/policy.js +8 -2
  29. package/dist/commands/agents/update.d.ts +1 -0
  30. package/dist/commands/agents/update.js +8 -2
  31. package/dist/commands/browser.js +5 -5
  32. package/dist/commands/commitments/create.d.ts +2 -0
  33. package/dist/commands/commitments/create.js +30 -13
  34. package/dist/commands/commitments/update.d.ts +2 -0
  35. package/dist/commands/commitments/update.js +24 -4
  36. package/dist/commands/daemon/byok.js +5 -5
  37. package/dist/commands/daemon/index.js +5 -3
  38. package/dist/commands/daemon/install.js +8 -5
  39. package/dist/commands/daemon/login.js +5 -5
  40. package/dist/commands/daemon/logs.js +5 -5
  41. package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
  42. package/dist/commands/daemon/recover-offline-queue.js +24 -0
  43. package/dist/commands/daemon/restart.js +5 -5
  44. package/dist/commands/daemon/start.js +34 -6
  45. package/dist/commands/daemon/status.js +5 -5
  46. package/dist/commands/daemon/stop.js +5 -5
  47. package/dist/commands/daemon/uninstall.js +5 -5
  48. package/dist/commands/daemon/unlock.js +5 -5
  49. package/dist/commands/daemon/usage.js +6 -6
  50. package/dist/commands/instruction-blocks/create.d.ts +19 -0
  51. package/dist/commands/instruction-blocks/create.js +94 -0
  52. package/dist/commands/instruction-blocks/delete.d.ts +13 -0
  53. package/dist/commands/instruction-blocks/delete.js +35 -0
  54. package/dist/commands/instruction-blocks/list.d.ts +13 -0
  55. package/dist/commands/instruction-blocks/list.js +71 -0
  56. package/dist/commands/instruction-blocks/show.d.ts +12 -0
  57. package/dist/commands/instruction-blocks/show.js +35 -0
  58. package/dist/commands/instruction-blocks/update.d.ts +23 -0
  59. package/dist/commands/instruction-blocks/update.js +94 -0
  60. package/dist/commands/login.js +7 -2
  61. package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
  62. package/dist/commands/spaces/auto-executor/preview.js +62 -3
  63. package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
  64. package/dist/commands/spaces/auto-executor/update.js +78 -7
  65. package/dist/commands/spaces/capacity.js +14 -2
  66. package/dist/commands/spaces/list.d.ts +13 -0
  67. package/dist/commands/spaces/list.js +20 -1
  68. package/dist/commands/spaces/update.d.ts +4 -0
  69. package/dist/commands/spaces/update.js +49 -2
  70. package/dist/commands/tasks/complete.d.ts +12 -5
  71. package/dist/commands/tasks/complete.js +50 -28
  72. package/dist/commands/tasks/continue-ready.js +9 -1
  73. package/dist/commands/tasks/create.d.ts +1 -0
  74. package/dist/commands/tasks/create.js +25 -1
  75. package/dist/commands/tasks/deliverable/add.d.ts +23 -0
  76. package/dist/commands/tasks/deliverable/add.js +70 -0
  77. package/dist/commands/tasks/events/append.d.ts +1 -1
  78. package/dist/commands/tasks/events/append.js +4 -4
  79. package/dist/commands/tasks/expectations.d.ts +12 -0
  80. package/dist/commands/tasks/expectations.js +92 -0
  81. package/dist/commands/tasks/judge.d.ts +29 -0
  82. package/dist/commands/tasks/judge.js +68 -0
  83. package/dist/commands/tasks/list.d.ts +11 -0
  84. package/dist/commands/tasks/list.js +82 -11
  85. package/dist/commands/tasks/report.d.ts +1 -1
  86. package/dist/commands/tasks/report.js +3 -3
  87. package/dist/commands/tasks/update.d.ts +1 -0
  88. package/dist/commands/tasks/update.js +34 -4
  89. package/dist/commands/tasks/waive.d.ts +25 -0
  90. package/dist/commands/tasks/waive.js +58 -0
  91. package/dist/commands/wiki/cat.js +2 -1
  92. package/dist/commands/wiki/mv.js +5 -1
  93. package/dist/commands/wiki/write.js +5 -1
  94. package/dist/lib/agent-config.js +5 -0
  95. package/dist/lib/agent-instructions.d.ts +15 -0
  96. package/dist/lib/agent-instructions.js +55 -0
  97. package/dist/lib/api-fetch.d.ts +2 -1
  98. package/dist/lib/api-fetch.js +9 -2
  99. package/dist/lib/command-hygiene.d.ts +6 -1
  100. package/dist/lib/command-hygiene.js +49 -29
  101. package/dist/lib/commitments.d.ts +44 -0
  102. package/dist/lib/commitments.js +123 -0
  103. package/dist/lib/daemon-binding.d.ts +3 -4
  104. package/dist/lib/daemon-binding.js +11 -10
  105. package/dist/lib/daemon-handoff-opt-out.d.ts +8 -0
  106. package/dist/lib/daemon-handoff-opt-out.js +17 -0
  107. package/dist/lib/daemon-installer.d.ts +1 -1
  108. package/dist/lib/daemon-installer.js +2 -2
  109. package/dist/lib/daemon-setup.js +13 -7
  110. package/dist/lib/daemonHandoff.d.ts +1 -1
  111. package/dist/lib/daemonHandoff.js +20 -7
  112. package/dist/lib/delegated-cli.d.ts +0 -9
  113. package/dist/lib/delegated-cli.js +149 -6
  114. package/dist/lib/{exec-oversky.d.ts → exec-runtime-binary.d.ts} +28 -4
  115. package/dist/lib/{exec-oversky.js → exec-runtime-binary.js} +138 -37
  116. package/dist/lib/git-task.d.ts +37 -0
  117. package/dist/lib/git-task.js +151 -0
  118. package/dist/lib/instruction-blocks.d.ts +29 -0
  119. package/dist/lib/instruction-blocks.js +159 -0
  120. package/dist/lib/node-adapter.js +6 -0
  121. package/dist/lib/runtime-start-verification.d.ts +49 -0
  122. package/dist/lib/runtime-start-verification.js +108 -0
  123. package/dist/lib/sky-code.d.ts +1 -1
  124. package/dist/lib/sky-code.js +1 -1
  125. package/dist/lib/task-execution.d.ts +18 -2
  126. package/dist/lib/task-execution.js +19 -14
  127. package/dist/lib/task-transcript.js +1 -1
  128. package/dist/lib/tasks.d.ts +50 -0
  129. package/dist/lib/tasks.js +66 -1
  130. package/dist/lib/usage-discovery.js +1 -1
  131. package/dist/lib/web-url.d.ts +16 -0
  132. package/dist/lib/web-url.js +25 -0
  133. package/dist/node_modules/@skrr-ai/data-provider/index.js +4075 -3905
  134. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  135. package/oclif.manifest.json +12364 -10488
  136. package/package.json +4 -1
@@ -0,0 +1,25 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr tasks waive <task> <expectation-id> --reason "<why>"`
4
+ *
5
+ * Drop a requirement, on the record. The expectation is KEPT and marked, never
6
+ * deleted: deleting it expresses the same decision while destroying the only
7
+ * evidence that it was ever owed, which is what someone asking "why wasn't this
8
+ * done?" needs to read.
9
+ *
10
+ * The reason is required for the same purpose — a waiver without one is
11
+ * indistinguishable from having forgotten.
12
+ */
13
+ export default class TasksWaive extends BaseCommand {
14
+ static description: string;
15
+ static examples: string[];
16
+ static args: {
17
+ task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
18
+ expectation: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
19
+ };
20
+ static flags: {
21
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
22
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
23
+ };
24
+ run(): Promise<void>;
25
+ }
@@ -0,0 +1,58 @@
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
+ /**
7
+ * `skrr tasks waive <task> <expectation-id> --reason "<why>"`
8
+ *
9
+ * Drop a requirement, on the record. The expectation is KEPT and marked, never
10
+ * deleted: deleting it expresses the same decision while destroying the only
11
+ * evidence that it was ever owed, which is what someone asking "why wasn't this
12
+ * done?" needs to read.
13
+ *
14
+ * The reason is required for the same purpose — a waiver without one is
15
+ * indistinguishable from having forgotten.
16
+ */
17
+ class TasksWaive extends base_command_1.BaseCommand {
18
+ static description = 'Waive an expectation, with a stated reason';
19
+ static examples = [
20
+ '<%= config.bin %> tasks waive <task-id> c1 --reason "superseded by OSK-7000"',
21
+ ];
22
+ static args = {
23
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
24
+ expectation: core_1.Args.string({
25
+ description: 'Expectation id (see `tasks show`)',
26
+ required: true,
27
+ ignoreStdin: true,
28
+ }),
29
+ };
30
+ static flags = {
31
+ reason: core_1.Flags.string({
32
+ description: 'Why this is no longer required. Required — it is the record.',
33
+ required: true,
34
+ }),
35
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
36
+ };
37
+ async run() {
38
+ this.requireAuth();
39
+ const { args, flags } = await this.parse(TasksWaive);
40
+ let result;
41
+ try {
42
+ result = await data_provider_1.dataService.waiveTaskExpectation(args.task, {
43
+ expectationId: args.expectation,
44
+ reason: flags.reason,
45
+ });
46
+ }
47
+ catch (err) {
48
+ this.handleApiError(err);
49
+ return;
50
+ }
51
+ if (flags.json) {
52
+ this.log(JSON.stringify(result, null, 2));
53
+ return;
54
+ }
55
+ this.log(`Waived ${args.expectation}: ${flags.reason}`);
56
+ }
57
+ }
58
+ exports.default = TasksWaive;
@@ -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;
@@ -48,5 +48,10 @@ function mergeAgentConfigFlags(body, flags) {
48
48
  }
49
49
  if (flags['recursion-limit'] !== undefined)
50
50
  parsed.recursion_limit = flags['recursion-limit'];
51
+ // Cross-Space Task execution ceiling. `0` means unlimited, matching the
52
+ // Space limits' convention so a user learns one spelling of "no cap".
53
+ if (flags['max-concurrent-task-executions'] !== undefined) {
54
+ parsed.maxConcurrentTaskExecutions = flags['max-concurrent-task-executions'];
55
+ }
51
56
  return (0, prompt_1.mergeDefined)(body, parsed);
52
57
  }
@@ -0,0 +1,15 @@
1
+ import { dataService, type AgentPromptComposition } from '@skrr-ai/data-provider';
2
+ /** Positional and --agent forms agree with the existing `agents tone` UX. */
3
+ export declare function resolveInstructionAgentId(argument?: string, flag?: string): Promise<string>;
4
+ /** Fetch the edit-gated Agent projection used by the web composer. */
5
+ export declare function loadAgentInstructions(agentId: string): Promise<{
6
+ agent: Awaited<ReturnType<typeof dataService.getExpandedAgentById>>;
7
+ composition: AgentPromptComposition;
8
+ }>;
9
+ /**
10
+ * The Agent controller is intentionally the only persistence boundary. It
11
+ * resolves library versions, verifies bindings and materializes legacy
12
+ * `instructions` atomically, so a CLI never tries to compile a prompt itself.
13
+ */
14
+ export declare function saveAgentInstructions(agentId: string, composition: AgentPromptComposition): Promise<import("@skrr-ai/data-provider").Agent>;
15
+ export declare function renderCompositionLines(composition: AgentPromptComposition): string[];
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveInstructionAgentId = resolveInstructionAgentId;
4
+ exports.loadAgentInstructions = loadAgentInstructions;
5
+ exports.saveAgentInstructions = saveAgentInstructions;
6
+ exports.renderCompositionLines = renderCompositionLines;
7
+ const data_provider_1 = require("@skrr-ai/data-provider");
8
+ const agent_resolver_1 = require("./agent-resolver");
9
+ const instruction_blocks_1 = require("./instruction-blocks");
10
+ /** Positional and --agent forms agree with the existing `agents tone` UX. */
11
+ async function resolveInstructionAgentId(argument, flag) {
12
+ return (0, agent_resolver_1.resolveAgentId)((0, agent_resolver_1.mergeExplicitAgentInputs)([
13
+ { label: 'argument', value: argument },
14
+ { label: '--agent', value: flag },
15
+ ]));
16
+ }
17
+ /** Fetch the edit-gated Agent projection used by the web composer. */
18
+ async function loadAgentInstructions(agentId) {
19
+ const agent = await data_provider_1.dataService.getExpandedAgentById({ agent_id: agentId });
20
+ return { agent, composition: (0, instruction_blocks_1.compositionForAgent)(agent) };
21
+ }
22
+ /**
23
+ * The Agent controller is intentionally the only persistence boundary. It
24
+ * resolves library versions, verifies bindings and materializes legacy
25
+ * `instructions` atomically, so a CLI never tries to compile a prompt itself.
26
+ */
27
+ async function saveAgentInstructions(agentId, composition) {
28
+ return data_provider_1.dataService.updateAgent({
29
+ agent_id: agentId,
30
+ data: { promptComposition: composition },
31
+ });
32
+ }
33
+ function renderCompositionLines(composition) {
34
+ if (!composition.entries.length)
35
+ return ['(No custom instruction blocks.)'];
36
+ return composition.entries.flatMap((entry, index) => {
37
+ const state = entry.enabled ? 'on' : 'off';
38
+ if (entry.source.kind === 'inline') {
39
+ const preview = entry.source.body.trim().replace(/\s+/g, ' ').slice(0, 140);
40
+ return [
41
+ `${index + 1}. ${state.padEnd(3)} inline ${entry.source.title} (${entry.instanceId})`,
42
+ ` ${preview || '(empty)'}`,
43
+ ];
44
+ }
45
+ const label = entry.resolved?.name || entry.source.blockId;
46
+ const bindings = entry.bindings && Object.keys(entry.bindings).length
47
+ ? ` · bindings: ${Object.entries(entry.bindings)
48
+ .map(([key, value]) => `${key}=${value}`)
49
+ .join(', ')}`
50
+ : '';
51
+ return [
52
+ `${index + 1}. ${state.padEnd(3)} library ${label} · v${entry.source.version} (${entry.instanceId})${bindings}`,
53
+ ];
54
+ });
55
+ }
@@ -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.
@@ -30,5 +30,10 @@ export type CommandHygieneReport = {
30
30
  };
31
31
  export declare function auditCommands({ rootDir, manifest, }: {
32
32
  rootDir: string;
33
- manifest: CommandManifest;
33
+ /**
34
+ * The generated oclif catalogue is not present in a source checkout. Such
35
+ * callers can still run source-level checks, but cannot make claims about
36
+ * what a generated manifest contains.
37
+ */
38
+ manifest?: CommandManifest;
34
39
  }): CommandHygieneReport;
@@ -46,13 +46,14 @@ const AGENT_CRITICAL_COMMANDS = [
46
46
  ];
47
47
  function auditCommands({ rootDir, manifest, }) {
48
48
  const issues = [];
49
- const commands = manifest.commands ?? {};
49
+ const commands = manifest?.commands ?? {};
50
50
  const commandIds = Object.keys(commands);
51
+ const manifestForCatalog = manifest ?? {};
51
52
  // The command manifest is the only catalogue. The progressive `skrr usage`
52
53
  // surfaces are useful precisely because they do not make an agent load that
53
54
  // whole catalogue at once, so audit every generated continuation page too.
54
- const overview = (0, usage_discovery_1.buildUsageOverview)(manifest);
55
- const usageBudgetIssues = (0, usage_discovery_1.auditUsageBudgets)(manifest);
55
+ const overview = (0, usage_discovery_1.buildUsageOverview)(manifestForCatalog);
56
+ const usageBudgetIssues = (0, usage_discovery_1.auditUsageBudgets)(manifestForCatalog);
56
57
  for (const issue of usageBudgetIssues) {
57
58
  issues.push({
58
59
  code: 'USAGE_DISCOVERY_TOKEN_BUDGET_EXCEEDED',
@@ -62,14 +63,31 @@ function auditCommands({ rootDir, manifest, }) {
62
63
  `uses ${issue.estimatedTokens} estimated tokens; budget is ${issue.tokenBudget}.`,
63
64
  });
64
65
  }
65
- for (const id of AGENT_CRITICAL_COMMANDS) {
66
- if (!commands[id]) {
67
- issues.push({
68
- code: 'AGENT_CRITICAL_COMMAND_MISSING',
69
- severity: 'error',
70
- command: id,
71
- message: `Agent-critical command is missing from the manifest: ${id}`,
72
- });
66
+ if (!manifest) {
67
+ // Absence is a distinct answer from "missing", and it should be audible.
68
+ //
69
+ // Skipping silently means a source-only run reports a clean bill of health
70
+ // for a check it never performed. That is the shape that makes an audit
71
+ // untrustworthy — worse than a noisy one, because a green result stops
72
+ // meaning anything. A warning keeps the run green while saying which claim
73
+ // it is NOT making, and names the command that would let it.
74
+ issues.push({
75
+ code: 'AGENT_CRITICAL_COMMANDS_UNCHECKED',
76
+ severity: 'warning',
77
+ message: 'No command manifest was available, so the agent-critical command check did not run. ' +
78
+ 'Build the CLI (`npx tsc -p tsconfig.build.json && npx oclif manifest` in cli/) to audit it.',
79
+ });
80
+ }
81
+ if (manifest) {
82
+ for (const id of AGENT_CRITICAL_COMMANDS) {
83
+ if (!commands[id]) {
84
+ issues.push({
85
+ code: 'AGENT_CRITICAL_COMMAND_MISSING',
86
+ severity: 'error',
87
+ command: id,
88
+ message: `Agent-critical command is missing from the manifest: ${id}`,
89
+ });
90
+ }
73
91
  }
74
92
  }
75
93
  let sourceAvailable = false;
@@ -129,23 +147,25 @@ function auditCommands({ rootDir, manifest, }) {
129
147
  message: 'Source command files are unavailable; source-level hygiene checks were skipped.',
130
148
  });
131
149
  }
132
- for (const id of commandIds) {
133
- const command = commands[id];
134
- if (!command?.description?.trim()) {
135
- issues.push({
136
- code: 'COMMAND_DESCRIPTION_MISSING',
137
- severity: 'warning',
138
- command: id,
139
- message: 'Command is missing a description in the manifest.',
140
- });
141
- }
142
- if (!hasJsonFlag(command?.flags) && isMachineRelevantCommand(id)) {
143
- issues.push({
144
- code: 'JSON_SUCCESS_OUTPUT_MISSING',
145
- severity: 'warning',
146
- command: id,
147
- message: 'Machine-relevant command has no --json success-output flag. Keep human-only commands intentional.',
148
- });
150
+ if (manifest) {
151
+ for (const id of commandIds) {
152
+ const command = commands[id];
153
+ if (!command?.description?.trim()) {
154
+ issues.push({
155
+ code: 'COMMAND_DESCRIPTION_MISSING',
156
+ severity: 'warning',
157
+ command: id,
158
+ message: 'Command is missing a description in the manifest.',
159
+ });
160
+ }
161
+ if (!hasJsonFlag(command?.flags) && isMachineRelevantCommand(id)) {
162
+ issues.push({
163
+ code: 'JSON_SUCCESS_OUTPUT_MISSING',
164
+ severity: 'warning',
165
+ command: id,
166
+ message: 'Machine-relevant command has no --json success-output flag. Keep human-only commands intentional.',
167
+ });
168
+ }
149
169
  }
150
170
  }
151
171
  const errors = issues.filter((issue) => issue.severity === 'error').length;
@@ -167,7 +187,7 @@ function auditCommands({ rootDir, manifest, }) {
167
187
  usage: {
168
188
  overviewTokenBudget: overview.tokenBudget,
169
189
  overviewEstimatedTokens: overview.estimatedTokens,
170
- domains: (0, usage_discovery_1.usageDomains)(manifest).length,
190
+ domains: (0, usage_discovery_1.usageDomains)(manifestForCatalog).length,
171
191
  budgetErrors: usageBudgetIssues.length,
172
192
  },
173
193
  issues,
@@ -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
@@ -3,12 +3,14 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.COMMITMENT_CLI_EVALUATORS = exports.COMMITMENT_CREATE_KEYS = exports.COMMITMENT_REMEDIATION_ACTIONS = exports.COMMITMENT_FEEDBACK_KINDS = exports.commitmentApi = exports.commitmentPackApi = exports.MAX_COMMITMENT_INTERVAL_MS = exports.MIN_COMMITMENT_INTERVAL_MS = exports.COMMITMENT_AUTONOMY_MODES = exports.COMMITMENT_STATUSES = exports.COMMITMENT_CADENCE_MAX_TIMES = exports.COMMITMENT_CADENCE_TIME_PATTERN = exports.COMMITMENT_CADENCE_MODES = exports.COMMITMENT_TEMPLATE_IDS = exports.COMMITMENT_WATCHED_SOURCES = void 0;
4
4
  exports.normalizeCreateBody = normalizeCreateBody;
5
5
  exports.parseIntervalToMs = parseIntervalToMs;
6
+ exports.buildWatchedSources = buildWatchedSources;
6
7
  exports.describeEvidenceHealth = describeEvidenceHealth;
7
8
  exports.describeIntervalMs = describeIntervalMs;
8
9
  exports.buildSuccessCriteria = buildSuccessCriteria;
9
10
  exports.cadenceLabel = cadenceLabel;
10
11
  exports.renderCommitmentList = renderCommitmentList;
11
12
  exports.effectiveAutonomyMode = effectiveAutonomyMode;
13
+ exports.extractStandstillFromCheck = extractStandstillFromCheck;
12
14
  exports.describeAutonomyLine = describeAutonomyLine;
13
15
  exports.renderCommitmentSummary = renderCommitmentSummary;
14
16
  exports.describeWakeDecision = describeWakeDecision;
@@ -316,6 +318,66 @@ function parseIntervalToMs(input) {
316
318
  }
317
319
  return ms;
318
320
  }
321
+ function buildWatchedSources(inputs) {
322
+ const watched = (inputs.watch ?? []).filter(Boolean);
323
+ const metricKeys = (inputs.metricKey ?? []).filter(Boolean);
324
+ const spaceIds = (inputs.spaceIds ?? []).filter(Boolean);
325
+ const monitorIds = (inputs.monitorIds ?? []).filter(Boolean);
326
+ const effective = new Set(watched);
327
+ if (spaceIds.length)
328
+ effective.add('space');
329
+ if (monitorIds.length)
330
+ effective.add('monitor');
331
+ if (effective.has('space') && spaceIds.length === 0) {
332
+ throw new Error('--watch space needs at least one --watch-space <space-id-or-ref>. ' +
333
+ 'The `space` source requires a scope id, which the enum on its own cannot carry.');
334
+ }
335
+ if (effective.has('monitor') && monitorIds.length === 0) {
336
+ throw new Error('--watch monitor needs at least one --watch-monitor <monitor-id>. ' +
337
+ 'The `monitor` source requires a scope id, which the enum on its own cannot carry.');
338
+ }
339
+ if (metricKeys.length && !effective.has('metrics')) {
340
+ throw new Error('--metric-key requires --watch metrics.');
341
+ }
342
+ if (effective.size === 0)
343
+ return undefined;
344
+ // Preserve caller order: --watch entries first (deduped), then any source
345
+ // that was only implied by a companion flag.
346
+ const orderedSources = [];
347
+ const seen = new Set();
348
+ for (const s of watched) {
349
+ if (!seen.has(s)) {
350
+ orderedSources.push(s);
351
+ seen.add(s);
352
+ }
353
+ }
354
+ for (const implied of ['space', 'monitor']) {
355
+ if (effective.has(implied) && !seen.has(implied)) {
356
+ orderedSources.push(implied);
357
+ seen.add(implied);
358
+ }
359
+ }
360
+ const rows = [];
361
+ for (const source of orderedSources) {
362
+ if (source === 'space') {
363
+ for (const spaceId of spaceIds) {
364
+ rows.push({ source, enabled: true, config: { spaceId } });
365
+ }
366
+ }
367
+ else if (source === 'monitor') {
368
+ for (const monitorId of monitorIds) {
369
+ rows.push({ source, enabled: true, config: { monitorId } });
370
+ }
371
+ }
372
+ else if (source === 'metrics' && metricKeys.length) {
373
+ rows.push({ source, enabled: true, config: { metricKeys } });
374
+ }
375
+ else {
376
+ rows.push({ source, enabled: true });
377
+ }
378
+ }
379
+ return rows;
380
+ }
319
381
  /**
320
382
  * One line an author can act on, or nothing.
321
383
  *
@@ -480,6 +542,41 @@ function effectiveAutonomyMode(check) {
480
542
  }
481
543
  return undefined;
482
544
  }
545
+ /**
546
+ * A `commitment_standstill` policyTrace entry, extracted from the latest
547
+ * check's decision. The arbiter appends it whenever a quiet posture is the
548
+ * ANSWER to "why nothing is happening" (`CommitmentControl/index.js` around
549
+ * the `record_only`/empty-action-plan branch). Every field except `reason` is
550
+ * optional, and old checks lack the entry entirely — so a missing entry means
551
+ * "the loop had nothing standstill-worthy to say", not "we don't know".
552
+ *
553
+ * Exported for tests.
554
+ */
555
+ function extractStandstillFromCheck(check) {
556
+ const trace = check?.decision?.policyTrace;
557
+ if (!Array.isArray(trace))
558
+ return null;
559
+ for (const entry of trace) {
560
+ if (!entry || typeof entry !== 'object')
561
+ continue;
562
+ const e = entry;
563
+ if (e.rule !== 'commitment_standstill')
564
+ continue;
565
+ const reason = typeof e.reason === 'string' ? e.reason : '';
566
+ if (!reason)
567
+ continue;
568
+ return {
569
+ reason,
570
+ layer: typeof e.layer === 'string' ? e.layer : undefined,
571
+ owner: typeof e.owner === 'string' ? e.owner : undefined,
572
+ fix: typeof e.fix === 'string' ? e.fix : undefined,
573
+ alsoConstrained: Array.isArray(e.alsoConstrained)
574
+ ? e.alsoConstrained
575
+ : undefined,
576
+ };
577
+ }
578
+ return null;
579
+ }
483
580
  /**
484
581
  * The `Autonomy:` line — the stated mode, annotated when the latest check
485
582
  * actually ran at a STRICTER one (OSK-4881). Without the caveat, a commitment
@@ -538,6 +635,32 @@ latestCheck) {
538
635
  if (typeof commitment.state?.consecutiveUnresolvedChecks === 'number') {
539
636
  log(`Unresolved checks in a row:${` ${commitment.state.consecutiveUnresolvedChecks}`}`);
540
637
  }
638
+ // The load-bearing signal on a commitment that "reports active but never
639
+ // acts": the arbiter names WHY every recent check decided to do nothing, but
640
+ // that entry sat only in `policyTrace` on each check and was never rendered.
641
+ // Read at check time, unrolled here so a person reading the header sees the
642
+ // same conclusion the check already reached — and the `fix` string that was
643
+ // already written for them.
644
+ const standstill = extractStandstillFromCheck(latestCheck);
645
+ if (standstill) {
646
+ const streak = commitment.state?.consecutiveUnresolvedChecks;
647
+ const streakClause = typeof streak === 'number' && streak >= 2 ? `${streak} checks in a row: ` : '';
648
+ log(`Standstill:${` ${streakClause}${standstill.reason}`}`);
649
+ if (standstill.fix)
650
+ log(` Fix: ${standstill.fix}`);
651
+ if (standstill.alsoConstrained?.length) {
652
+ const also = standstill.alsoConstrained
653
+ .map((entry) => {
654
+ if (!entry || typeof entry !== 'object')
655
+ return null;
656
+ const reason = entry.reason;
657
+ return typeof reason === 'string' ? reason : null;
658
+ })
659
+ .filter((r) => Boolean(r));
660
+ if (also.length)
661
+ log(` Also constrained: ${also.join(', ')}`);
662
+ }
663
+ }
541
664
  const sources = (commitment.watchedSources || [])
542
665
  .filter((s) => s.enabled !== false)
543
666
  .map((s) => s.source);
@@ -21,9 +21,8 @@
21
21
  /** Whether `argv` already carries `--name` or `--name=value` (or `-alias`). */
22
22
  export declare function hasFlag(argv: readonly string[], name: string, alias?: string): boolean;
23
23
  /**
24
- * Append `--server` / `--profile` describing the CLI's own session, for any
25
- * the caller did not pass. An explicit flag always wins this only fills a
26
- * silence, so `skrr daemon install --env prod` still installs against prod
27
- * while the CLI sits on dev.
24
+ * Fill `--server` / `--profile` from the CLI's own session when the caller did
25
+ * not pass them. The runtime's profile is global, so normalize it before the
26
+ * subcommand even when the caller supplied it after one.
28
27
  */
29
28
  export declare function bindToCliSession(argv: readonly string[]): string[];