@skrr-ai/cli 0.1.14 → 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 (90) hide show
  1. package/dist/base-command.d.ts +14 -0
  2. package/dist/base-command.js +60 -1
  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/show.js +5 -0
  26. package/dist/commands/agents/triggers/policy.js +8 -2
  27. package/dist/commands/agents/update.d.ts +1 -0
  28. package/dist/commands/agents/update.js +8 -2
  29. package/dist/commands/daemon/index.js +2 -0
  30. package/dist/commands/daemon/recover-offline-queue.d.ts +9 -0
  31. package/dist/commands/daemon/recover-offline-queue.js +24 -0
  32. package/dist/commands/daemon/start.js +29 -1
  33. package/dist/commands/instruction-blocks/create.d.ts +19 -0
  34. package/dist/commands/instruction-blocks/create.js +94 -0
  35. package/dist/commands/instruction-blocks/delete.d.ts +13 -0
  36. package/dist/commands/instruction-blocks/delete.js +35 -0
  37. package/dist/commands/instruction-blocks/list.d.ts +13 -0
  38. package/dist/commands/instruction-blocks/list.js +71 -0
  39. package/dist/commands/instruction-blocks/show.d.ts +12 -0
  40. package/dist/commands/instruction-blocks/show.js +35 -0
  41. package/dist/commands/instruction-blocks/update.d.ts +23 -0
  42. package/dist/commands/instruction-blocks/update.js +94 -0
  43. package/dist/commands/spaces/auto-executor/preview.d.ts +1 -0
  44. package/dist/commands/spaces/auto-executor/preview.js +62 -3
  45. package/dist/commands/spaces/auto-executor/update.d.ts +4 -0
  46. package/dist/commands/spaces/auto-executor/update.js +78 -7
  47. package/dist/commands/spaces/capacity.js +14 -2
  48. package/dist/commands/spaces/update.d.ts +4 -0
  49. package/dist/commands/spaces/update.js +49 -2
  50. package/dist/commands/tasks/complete.d.ts +12 -5
  51. package/dist/commands/tasks/complete.js +50 -28
  52. package/dist/commands/tasks/continue-ready.js +9 -1
  53. package/dist/commands/tasks/create.d.ts +1 -0
  54. package/dist/commands/tasks/create.js +25 -1
  55. package/dist/commands/tasks/deliverable/add.d.ts +23 -0
  56. package/dist/commands/tasks/deliverable/add.js +70 -0
  57. package/dist/commands/tasks/events/append.d.ts +1 -1
  58. package/dist/commands/tasks/events/append.js +4 -4
  59. package/dist/commands/tasks/expectations.d.ts +12 -0
  60. package/dist/commands/tasks/expectations.js +92 -0
  61. package/dist/commands/tasks/judge.d.ts +29 -0
  62. package/dist/commands/tasks/judge.js +68 -0
  63. package/dist/commands/tasks/report.d.ts +1 -1
  64. package/dist/commands/tasks/report.js +3 -3
  65. package/dist/commands/tasks/update.d.ts +1 -0
  66. package/dist/commands/tasks/update.js +34 -4
  67. package/dist/commands/tasks/waive.d.ts +25 -0
  68. package/dist/commands/tasks/waive.js +58 -0
  69. package/dist/lib/agent-config.js +5 -0
  70. package/dist/lib/agent-instructions.d.ts +15 -0
  71. package/dist/lib/agent-instructions.js +55 -0
  72. package/dist/lib/command-hygiene.d.ts +6 -1
  73. package/dist/lib/command-hygiene.js +49 -29
  74. package/dist/lib/exec-runtime-binary.d.ts +13 -0
  75. package/dist/lib/exec-runtime-binary.js +45 -0
  76. package/dist/lib/git-task.d.ts +37 -0
  77. package/dist/lib/git-task.js +151 -0
  78. package/dist/lib/instruction-blocks.d.ts +29 -0
  79. package/dist/lib/instruction-blocks.js +159 -0
  80. package/dist/lib/runtime-start-verification.d.ts +49 -0
  81. package/dist/lib/runtime-start-verification.js +108 -0
  82. package/dist/lib/task-execution.d.ts +18 -2
  83. package/dist/lib/task-execution.js +19 -14
  84. package/dist/lib/task-transcript.js +1 -1
  85. package/dist/lib/tasks.d.ts +50 -0
  86. package/dist/lib/tasks.js +66 -1
  87. package/dist/node_modules/@skrr-ai/data-provider/index.js +2027 -1904
  88. package/dist/node_modules/@skrr-ai/inference-broker/package.json +1 -1
  89. package/oclif.manifest.json +13706 -11858
  90. package/package.json +4 -1
@@ -0,0 +1,92 @@
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 expectations <task>` — what this task owes, and how far each got.
8
+ *
9
+ * Reads the SERVER's verdict rather than recomputing one. The judge and the
10
+ * completion gate are the same module, so a CLI that decided "met" for itself
11
+ * could disagree with the gate that refuses the task — and the reader would
12
+ * have no way to tell which was lying.
13
+ */
14
+ const STATE_LABEL = {
15
+ met: 'done',
16
+ waived: 'dropped',
17
+ blocked: 'stuck',
18
+ underway: 'on its way',
19
+ owed: 'not yet',
20
+ unjudged: 'needs a person',
21
+ };
22
+ function line(expectation) {
23
+ const label = STATE_LABEL[expectation.state] || expectation.state;
24
+ // A judged expectation is stated as NOT a gate. A list that reads like a
25
+ // contract while enforcing nothing is worse than no list, because it is
26
+ // trusted.
27
+ const gate = expectation.gating ? '' : ' (does not block completion)';
28
+ const from = expectation.source === 'space' ? ' [space default]' : '';
29
+ return ` ${expectation.id.padEnd(18)} ${label.padEnd(14)} ${expectation.sentence}${gate}${from}`;
30
+ }
31
+ class TasksExpectations extends base_command_1.BaseCommand {
32
+ static description = 'Show what a task must produce and how far each expectation got ("Done when")';
33
+ static examples = [
34
+ '<%= config.bin %> tasks expectations <task-id>',
35
+ '<%= config.bin %> tasks expectations <task-id> --json',
36
+ ];
37
+ static args = {
38
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
39
+ };
40
+ static flags = {
41
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
42
+ };
43
+ async run() {
44
+ this.requireAuth();
45
+ const { args, flags } = await this.parse(TasksExpectations);
46
+ let acceptance;
47
+ try {
48
+ acceptance = await data_provider_1.dataService.getTaskAcceptance(args.task);
49
+ }
50
+ catch (err) {
51
+ this.handleApiError(err);
52
+ return;
53
+ }
54
+ if (flags.json) {
55
+ this.log(JSON.stringify(acceptance, null, 2));
56
+ return;
57
+ }
58
+ if (acceptance.expectations.length === 0) {
59
+ // An empty contract is a real answer, not an unfilled field. Say so
60
+ // plainly rather than prompting for something most tasks do not owe.
61
+ this.log('Nothing durable is expected of this task.');
62
+ return;
63
+ }
64
+ // The evidence read fails OPEN by design, so "produced nothing" and "we
65
+ // could not look" are indistinguishable downstream unless the server says
66
+ // which happened. Never print the first when it might be the second.
67
+ if (!acceptance.evidenceRead) {
68
+ this.warn('Could not read what this task produced — the states below may understate it. ' +
69
+ 'This is not a claim that nothing was produced.');
70
+ }
71
+ this.log('Done when:');
72
+ for (const expectation of acceptance.expectations) {
73
+ this.log(line(expectation));
74
+ }
75
+ const gating = acceptance.expectations.filter((e) => e.gating);
76
+ const met = gating.filter((e) => e.met).length;
77
+ this.log('');
78
+ this.log(`${met} of ${gating.length} met · overall: ${acceptance.state}`);
79
+ // Only the ones actually stored on the task can be judged. A space default
80
+ // is synthesised per read, so pointing at `tasks judge` for one would send
81
+ // the reader at a command that can only refuse it.
82
+ const settleable = acceptance.expectations.filter((e) => e.state === 'unjudged' && e.source !== 'space').length;
83
+ if (settleable > 0) {
84
+ this.log(`${settleable} awaiting a person — settle with \`${this.config.bin} tasks judge\`.`);
85
+ }
86
+ const inherited = acceptance.unjudged - settleable;
87
+ if (inherited > 0) {
88
+ this.log(`${inherited} inherited from the space default — declare them on the task to settle them.`);
89
+ }
90
+ }
91
+ }
92
+ exports.default = TasksExpectations;
@@ -0,0 +1,29 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr tasks judge <task> <expectation-id> --verdict met --reason "<why>"`
4
+ *
5
+ * Settle a `judged` expectation — one whose text nothing here can evaluate.
6
+ *
7
+ * Only a judged one. A checkable expectation is settled by recording what was
8
+ * produced, and letting a person mark it met by hand would make the deliverables
9
+ * ledger optional, which is the same as not having one. The server refuses that
10
+ * case with an error naming the command to use instead.
11
+ *
12
+ * The reason is REQUIRED, for the same purpose a waiver's is: a verdict with no
13
+ * reason is indistinguishable from a click, and the entire value of a judged
14
+ * expectation is that somebody actually looked.
15
+ */
16
+ export default class TasksJudge extends BaseCommand {
17
+ static description: string;
18
+ static examples: string[];
19
+ static args: {
20
+ task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
21
+ expectation: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
22
+ };
23
+ static flags: {
24
+ verdict: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
26
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
27
+ };
28
+ run(): Promise<void>;
29
+ }
@@ -0,0 +1,68 @@
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 judge <task> <expectation-id> --verdict met --reason "<why>"`
8
+ *
9
+ * Settle a `judged` expectation — one whose text nothing here can evaluate.
10
+ *
11
+ * Only a judged one. A checkable expectation is settled by recording what was
12
+ * produced, and letting a person mark it met by hand would make the deliverables
13
+ * ledger optional, which is the same as not having one. The server refuses that
14
+ * case with an error naming the command to use instead.
15
+ *
16
+ * The reason is REQUIRED, for the same purpose a waiver's is: a verdict with no
17
+ * reason is indistinguishable from a click, and the entire value of a judged
18
+ * expectation is that somebody actually looked.
19
+ */
20
+ class TasksJudge extends base_command_1.BaseCommand {
21
+ static description = 'Settle a judged expectation with a verdict and a stated reason';
22
+ static examples = [
23
+ '<%= config.bin %> tasks judge <task-id> e2 --verdict met --reason "Read it end to end; a non-engineer follows it."',
24
+ '<%= config.bin %> tasks judge <task-id> e2 --verdict unmet --reason "Still assumes the reader knows the schema."',
25
+ ];
26
+ static args = {
27
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
28
+ expectation: core_1.Args.string({
29
+ description: 'Expectation id (see `tasks expectations`)',
30
+ required: true,
31
+ ignoreStdin: true,
32
+ }),
33
+ };
34
+ static flags = {
35
+ verdict: core_1.Flags.string({
36
+ options: ['met', 'unmet'],
37
+ required: true,
38
+ description: 'Whether the expectation was met.',
39
+ }),
40
+ reason: core_1.Flags.string({
41
+ required: true,
42
+ description: 'What you looked at. A verdict without one is not a judgement.',
43
+ }),
44
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
45
+ };
46
+ async run() {
47
+ this.requireAuth();
48
+ const { args, flags } = await this.parse(TasksJudge);
49
+ let result;
50
+ try {
51
+ result = await data_provider_1.dataService.judgeTaskExpectation(args.task, {
52
+ expectationId: args.expectation,
53
+ verdict: flags.verdict,
54
+ reason: flags.reason,
55
+ });
56
+ }
57
+ catch (err) {
58
+ this.handleApiError(err);
59
+ return;
60
+ }
61
+ if (flags.json) {
62
+ this.log(JSON.stringify(result, null, 2));
63
+ return;
64
+ }
65
+ this.log(`Recorded ${args.expectation} as ${flags.verdict}: ${flags.reason}`);
66
+ }
67
+ }
68
+ exports.default = TasksJudge;
@@ -18,7 +18,7 @@ export default class TasksReport extends BaseCommand {
18
18
  finding: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
19
  evidence: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
20
  next: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
- artifact: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
21
+ deliverable: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
22
22
  };
23
23
  run(): Promise<void>;
24
24
  }
@@ -18,7 +18,7 @@ class TasksReport extends base_command_1.BaseCommand {
18
18
  static description = 'Record a self-contained outcome, findings, evidence, and next actions';
19
19
  static examples = [
20
20
  '<%= config.bin %> tasks report <task-id> --outcome "Adopt the event ledger" --finding "The task page omits work-event details" --evidence "Task detail reads only legacy activity" --next "Add an Outcome section"',
21
- '<%= config.bin %> tasks report <task-id> --kind handoff --outcome "Ready for review" --finding "All acceptance criteria are met" --artifact "src/task.ts" --json',
21
+ '<%= config.bin %> tasks report <task-id> --kind handoff --outcome "Ready for review" --finding "All acceptance criteria are met" --deliverable "src/task.ts" --json',
22
22
  ];
23
23
  static args = {
24
24
  id: core_1.Args.string({
@@ -48,7 +48,7 @@ class TasksReport extends base_command_1.BaseCommand {
48
48
  multiple: true,
49
49
  }),
50
50
  next: core_1.Flags.string({ description: 'A follow-up or next action (repeatable)', multiple: true }),
51
- artifact: core_1.Flags.string({
51
+ deliverable: core_1.Flags.string({
52
52
  description: 'Durable file, commit, PR, or URL (repeatable)',
53
53
  multiple: true,
54
54
  }),
@@ -92,7 +92,7 @@ class TasksReport extends base_command_1.BaseCommand {
92
92
  idempotencyKey: (0, outbox_1.idempotencyKeyFor)('task-report', task.id, JSON.stringify({ report, kind: flags.kind })),
93
93
  kind,
94
94
  ...(0, task_report_1.taskReportEventFields)(report),
95
- artifacts: flags.artifact?.map((ref) => ({ label: 'artifact', ref })),
95
+ artifacts: flags.deliverable?.map((ref) => ({ label: 'deliverable', ref })),
96
96
  };
97
97
  const sessionPath = await (0, session_task_endpoints_1.sessionScopedTaskPath)('event');
98
98
  let result;
@@ -10,6 +10,7 @@ export default class TasksUpdate extends BaseCommand {
10
10
  task: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
11
  'from-json': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
12
  title: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ expect: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
14
  description: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
15
  'description-format': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
15
16
  priority: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
@@ -5,8 +5,9 @@ const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
6
  const web_url_1 = require("../../lib/web-url");
7
7
  const prompt_1 = require("../../lib/prompt");
8
- const task_resolver_1 = require("../../lib/task-resolver");
9
8
  const tasks_1 = require("../../lib/tasks");
9
+ const task_resolver_1 = require("../../lib/task-resolver");
10
+ const tasks_2 = require("../../lib/tasks");
10
11
  const create_1 = require("./create");
11
12
  class TasksUpdate extends base_command_1.BaseCommand {
12
13
  static description = 'Update an existing task (partial patch). Status and PROJECT changes go through ' +
@@ -41,6 +42,11 @@ class TasksUpdate extends base_command_1.BaseCommand {
41
42
  description: 'Path to a JSON file (or "-" for stdin) to use as the patch body; may include taskId',
42
43
  }),
43
44
  title: core_1.Flags.string({ description: 'New title' }),
45
+ expect: core_1.Flags.string({
46
+ multiple: true,
47
+ description: tasks_1.EXPECT_FLAG_HELP +
48
+ ' Replaces the whole list; pass "clear" to drop it and inherit the space default.',
49
+ }),
44
50
  description: core_1.Flags.string({ description: 'New description' }),
45
51
  'description-format': core_1.Flags.string({
46
52
  description: 'Input format for --description or from-json description',
@@ -48,7 +54,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
48
54
  }),
49
55
  priority: core_1.Flags.string({
50
56
  description: 'New priority',
51
- options: [...tasks_1.TASK_PRIORITIES],
57
+ options: [...tasks_2.TASK_PRIORITIES],
52
58
  }),
53
59
  'start-date': core_1.Flags.string({ description: 'New planned start date (ISO string)' }),
54
60
  'due-date': core_1.Flags.string({ description: 'New due date (ISO string)' }),
@@ -198,8 +204,32 @@ class TasksUpdate extends base_command_1.BaseCommand {
198
204
  details: { suggestion: failure.suggestion, context: failure.details },
199
205
  });
200
206
  }
207
+ // `--expect clear` is an explicit empty list, which the API turns into $unset —
208
+ // distinct from omitting the flag, which leaves the declaration untouched.
209
+ // Without the sentinel there is no way to say "stop requiring an output"
210
+ // short of hand-writing --from-json.
211
+ // `--expect clear` empties the list; otherwise the flags REPLACE it, because
212
+ // a partial patch of a list has no honest meaning — "add one" and "this is
213
+ // now the whole contract" are different requests and the flag can only be
214
+ // one of them.
215
+ let expectationsPatch;
216
+ if (flags.expect !== undefined && flags.expect.length > 0) {
217
+ if (flags.expect.length === 1 && flags.expect[0].trim() === 'clear') {
218
+ expectationsPatch = null;
219
+ }
220
+ else {
221
+ const parsed = (0, tasks_1.parseExpectations)(flags.expect);
222
+ if (!parsed.ok) {
223
+ this.error(parsed.error, { exit: 1 });
224
+ }
225
+ else {
226
+ expectationsPatch = parsed.value;
227
+ }
228
+ }
229
+ }
201
230
  const payload = (0, prompt_1.mergeDefined)((0, task_resolver_1.stripTaskIdentityFields)(body), {
202
231
  title: flags.title,
232
+ expectations: expectationsPatch === null ? [] : (expectationsPatch ?? undefined),
203
233
  description: flags.description,
204
234
  descriptionFormat: flags['description-format'],
205
235
  priority: flags.priority,
@@ -231,7 +261,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
231
261
  labelIds: flags['label-id'],
232
262
  // Worktree isolation override. --worktree ⇒ 'ephemeral', --no-worktree ⇒
233
263
  // 'shared', omitted ⇒ unchanged. (Clear-to-inherit via --from-json.)
234
- executionIsolation: (0, tasks_1.executionIsolationFromWorktreeFlag)(flags.worktree),
264
+ executionIsolation: (0, tasks_2.executionIsolationFromWorktreeFlag)(flags.worktree),
235
265
  // A mutation option, not a field — `undefined` unless explicitly asked
236
266
  // for, so it never counts toward the "nothing to update" check below.
237
267
  repairReleasePins: flags['repair-release-pins'] ? true : undefined,
@@ -253,7 +283,7 @@ class TasksUpdate extends base_command_1.BaseCommand {
253
283
  retryable: false,
254
284
  });
255
285
  }
256
- const mutationGuardFailure = (0, tasks_1.taskUpdateMutationGuard)(payload);
286
+ const mutationGuardFailure = (0, tasks_2.taskUpdateMutationGuard)(payload);
257
287
  if (mutationGuardFailure) {
258
288
  this.failWithCliError({
259
289
  message: mutationGuardFailure.message,
@@ -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;
@@ -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
+ }
@@ -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,
@@ -72,6 +72,19 @@ export declare function notInstalledMessage(): string;
72
72
  * may depend on.
73
73
  */
74
74
  export declare function execRuntimeBinary(args: string[]): Promise<number>;
75
+ /** Output from a short-lived runtime probe such as `skrrd status --json`. */
76
+ export type RuntimeBinaryOutput = {
77
+ code: number;
78
+ stdout: string;
79
+ stderr: string;
80
+ };
81
+ /**
82
+ * Invoke the installed runtime without inheriting its streams. The regular
83
+ * lifecycle command still owns the terminal; this is only for the wrapper's
84
+ * post-condition probes, where output is evidence rather than user-facing
85
+ * command output.
86
+ */
87
+ export declare function execRuntimeBinaryCapture(args: string[]): Promise<RuntimeBinaryOutput>;
75
88
  /**
76
89
  * Check an installed daemon's top-level help before forwarding a command that
77
90
  * was added after older daemon releases shipped. A false result is useful to