@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
@@ -136,6 +136,12 @@ class TasksCreate extends base_command_1.BaseCommand {
136
136
  'dri-user': core_1.Flags.string({
137
137
  description: 'Explicit accountable human DRI (separate from assignees)',
138
138
  }),
139
+ expect: core_1.Flags.string({
140
+ multiple: true,
141
+ description: tasks_1.EXPECT_FLAG_HELP +
142
+ " Injected into the agent's instructions and checked against what the run produced. " +
143
+ 'Omit to inherit the space default.',
144
+ }),
139
145
  'execution-mode': core_1.Flags.string({
140
146
  description: 'Who executes the task: agent or human. Required when both Humans and Agents are ' +
141
147
  'assigned. Passing "human" with no assignee flags files the task against you, ' +
@@ -184,7 +190,25 @@ class TasksCreate extends base_command_1.BaseCommand {
184
190
  retryable: false,
185
191
  });
186
192
  }
187
- const payload = (0, tasks_1.buildTaskCreatePayload)(body, flags);
193
+ // Resolve --expect BEFORE any write. A bad spec must fail here rather than
194
+ // reaching the server as a silently-dropped field: a declaration that did
195
+ // not stick is worse than none, because the board then shows a contract
196
+ // nobody is enforcing.
197
+ const parsedExpectations = (0, tasks_1.parseExpectations)(flags.expect);
198
+ if (!parsedExpectations.ok) {
199
+ this.failWithCliError({
200
+ message: parsedExpectations.error,
201
+ code: 'INVALID_TASK_EXPECTATION',
202
+ exit: 1,
203
+ retryable: false,
204
+ });
205
+ }
206
+ const payload = (0, tasks_1.buildTaskCreatePayload)(body, {
207
+ ...flags,
208
+ expectations: parsedExpectations.ok && parsedExpectations.value.length > 0
209
+ ? parsedExpectations.value
210
+ : undefined,
211
+ });
188
212
  const isTemplateInstantiation = typeof payload.templateId === 'string' && payload.templateId.length > 0;
189
213
  // Template defaults (including its assigned agents) are part of the saved
190
214
  // resource. Do not let the ordinary "who owns an empty task?" policy
@@ -0,0 +1,23 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
4
+ *
5
+ * The command an agent runs to say what it produced. Deliberately small: one
6
+ * positional carrying a coordinate it already knows, because the measured
7
+ * behaviour is that agents emit a real commit sha 77% of the time when asked
8
+ * for it in a required field, and 0% of the time when asked for it in an
9
+ * optional structured one.
10
+ */
11
+ export default class TasksDeliverableAdd extends BaseCommand {
12
+ static description: string;
13
+ static examples: string[];
14
+ static args: {
15
+ task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
16
+ coordinate: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
17
+ };
18
+ static flags: {
19
+ lifecycle: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
20
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
21
+ };
22
+ run(): Promise<void>;
23
+ }
@@ -0,0 +1,70 @@
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 tasks_1 = require("../../../lib/tasks");
7
+ /**
8
+ * `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
9
+ *
10
+ * The command an agent runs to say what it produced. Deliberately small: one
11
+ * positional carrying a coordinate it already knows, because the measured
12
+ * behaviour is that agents emit a real commit sha 77% of the time when asked
13
+ * for it in a required field, and 0% of the time when asked for it in an
14
+ * optional structured one.
15
+ */
16
+ class TasksDeliverableAdd extends base_command_1.BaseCommand {
17
+ static description = 'Record something this task produced (a commit, a page, a URL, a file, a message, a dataset)';
18
+ static examples = [
19
+ '<%= config.bin %> tasks deliverable add <task-id> code:a1b2c3d@origin/main',
20
+ '<%= config.bin %> tasks deliverable add <task-id> page:d_92@Engineering/deploy',
21
+ '<%= config.bin %> tasks deliverable add <task-id> file/pdf:f_71@Drive/Reports',
22
+ '<%= config.bin %> tasks deliverable add <task-id> code:a1b2c3d --lifecycle in_review',
23
+ ];
24
+ static args = {
25
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
26
+ coordinate: core_1.Args.string({
27
+ description: '<kind>[/<format>]:<identity>[@<location>] — e.g. code:a1b2c3d@origin/main, ' +
28
+ 'file/pdf:report_92@Drive/Reports',
29
+ required: true,
30
+ ignoreStdin: true,
31
+ }),
32
+ };
33
+ static flags = {
34
+ lifecycle: core_1.Flags.string({
35
+ options: ['local', 'in_review', 'durable', 'blocked', 'superseded'],
36
+ description: 'How far along it is. Defaults to `local` — recording that a commit exists is not a ' +
37
+ 'claim that it landed. Only an external receipt should assert `durable`.',
38
+ }),
39
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
40
+ };
41
+ async run() {
42
+ this.requireAuth();
43
+ const { args, flags } = await this.parse(TasksDeliverableAdd);
44
+ const parsed = (0, tasks_1.parseDeliverableCoordinate)(args.coordinate);
45
+ if (!parsed.ok) {
46
+ // For an agent this message IS the documentation — it hits the error and
47
+ // corrects, so the refusal carries the whole form and vocabulary.
48
+ this.error(parsed.error, { exit: 1 });
49
+ return;
50
+ }
51
+ let result;
52
+ try {
53
+ result = await data_provider_1.dataService.addTaskDeliverable(args.task, {
54
+ ...parsed.value,
55
+ ...(flags.lifecycle ? { lifecycle: flags.lifecycle } : {}),
56
+ });
57
+ }
58
+ catch (err) {
59
+ this.handleApiError(err);
60
+ return;
61
+ }
62
+ if (flags.json) {
63
+ this.log(JSON.stringify(result, null, 2));
64
+ return;
65
+ }
66
+ const where = parsed.value.location ? ` at ${parsed.value.location}` : '';
67
+ this.log(`Recorded ${parsed.value.kind}:${parsed.value.identity}${where} (${flags.lifecycle || 'local'}).`);
68
+ }
69
+ }
70
+ exports.default = TasksDeliverableAdd;
@@ -15,7 +15,7 @@ export default class TasksEventsAppend extends BaseCommand {
15
15
  changed: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
16
16
  next: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
17
17
  evidence: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
- artifact: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
18
+ deliverable: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
19
19
  };
20
20
  run(): Promise<void>;
21
21
  }
@@ -15,7 +15,7 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
15
15
  static examples = [
16
16
  '<%= config.bin %> tasks events append <task-id> --kind checkpoint --idempotency-key "<task-id>:first-pass" --summary "Parser handles nested fences"',
17
17
  '<%= config.bin %> tasks events append <task-id> --kind decision --idempotency-key "<task-id>:storage-choice" --summary "Chose the CRDT path" --evidence "A direct contentHtml write is reverted by the next render tick"',
18
- '<%= config.bin %> tasks events append <task-id> --kind verification --idempotency-key "<task-id>:verified" --summary "Repro no longer reproduces" --artifact <commit-sha>',
18
+ '<%= config.bin %> tasks events append <task-id> --kind verification --idempotency-key "<task-id>:verified" --summary "Repro no longer reproduces" --deliverable <commit-sha>',
19
19
  ];
20
20
  static args = {
21
21
  id: core_1.Args.string({
@@ -54,9 +54,9 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
54
54
  evidence: core_1.Flags.string({
55
55
  description: 'Why it is true — an observation, command output, or a before → after',
56
56
  }),
57
- artifact: core_1.Flags.string({
57
+ deliverable: core_1.Flags.string({
58
58
  multiple: true,
59
- description: 'Durable reference: a file path, commit sha, or URL. Repeatable. "in the conversation" is not an artifact',
59
+ description: 'Durable reference: a file path, commit sha, or URL. Repeatable. "in the conversation" is not a deliverable',
60
60
  }),
61
61
  };
62
62
  async run() {
@@ -70,7 +70,7 @@ class TasksEventsAppend extends base_command_1.BaseCommand {
70
70
  changed: flags.changed,
71
71
  next: flags.next,
72
72
  evidence: flags.evidence,
73
- artifacts: flags.artifact?.map((ref) => ({ label: 'artifact', ref })),
73
+ artifacts: flags.deliverable?.map((ref) => ({ label: 'deliverable', ref })),
74
74
  };
75
75
  const path = await (0, session_task_endpoints_1.sessionScopedTaskPath)('event');
76
76
  let result;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class TasksExpectations extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -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;
@@ -1,6 +1,17 @@
1
1
  import type { TaskListParams } from '@skrr-ai/data-provider';
2
2
  import { BaseCommand } from '../../base-command';
3
3
  type ListFlags = Record<string, unknown>;
4
+ /**
5
+ * What the zero-row case is allowed to say. The bare `No tasks.` was step 3
6
+ * of `GETTING_STARTED.md` and named no next step; a new user reading it hit
7
+ * three of these in a row and concluded the product was empty. Follow the
8
+ * `commitments/list.ts` pattern: name the command that fixes THIS empty state,
9
+ * with the specific `--status in_progress` hint kept because a reaped run
10
+ * genuinely hides here (a real bug, not a first-run scenario).
11
+ *
12
+ * Exported and pure so the wording is testable without an oclif runtime.
13
+ */
14
+ export declare function emptyTasksMessage(flags: ListFlags, bin?: string): string;
4
15
  /**
5
16
  * The task-list request bag is shared with import/export so every CLI list
6
17
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.emptyTasksMessage = emptyTasksMessage;
3
4
  exports.buildListParams = buildListParams;
4
5
  const core_1 = require("@oclif/core");
5
6
  const data_provider_1 = require("@skrr-ai/data-provider");
@@ -7,6 +8,86 @@ const base_command_1 = require("../../base-command");
7
8
  const web_url_1 = require("../../lib/web-url");
8
9
  const format_1 = require("../../lib/format");
9
10
  const tasks_1 = require("../../lib/tasks");
11
+ /**
12
+ * Names of `tasks list` flags that narrow the result set. Used to distinguish
13
+ * "no tasks anywhere" from "no tasks matching your filters" — the first case
14
+ * wants a create-command hint, the second wants a loosen-filters hint. Kept
15
+ * as a set so a new filter added to `buildListParams` shows up here at review
16
+ * time rather than silently changing the empty state.
17
+ */
18
+ const FILTER_FLAGS = new Set([
19
+ 'goal',
20
+ 'project',
21
+ 'cycle',
22
+ 'parent',
23
+ 'status',
24
+ 'status-type',
25
+ 'execution-state',
26
+ 'actionable-by',
27
+ 'actionable-for-agent',
28
+ 'needs-me',
29
+ 'actionability-state',
30
+ 'actionability-reason',
31
+ 'priority',
32
+ 'estimate',
33
+ 'min-estimate',
34
+ 'assignee-agent',
35
+ 'assignee-user',
36
+ 'dri',
37
+ 'dri-kind',
38
+ 'mine',
39
+ 'explicit-only',
40
+ 'subscribed',
41
+ 'include-snoozed',
42
+ 'external-key',
43
+ 'blocked-by',
44
+ 'blocks',
45
+ 'due-before',
46
+ 'due-after',
47
+ 'overdue',
48
+ 'tag',
49
+ 'label-id',
50
+ 'q',
51
+ ]);
52
+ /**
53
+ * What the zero-row case is allowed to say. The bare `No tasks.` was step 3
54
+ * of `GETTING_STARTED.md` and named no next step; a new user reading it hit
55
+ * three of these in a row and concluded the product was empty. Follow the
56
+ * `commitments/list.ts` pattern: name the command that fixes THIS empty state,
57
+ * with the specific `--status in_progress` hint kept because a reaped run
58
+ * genuinely hides here (a real bug, not a first-run scenario).
59
+ *
60
+ * Exported and pure so the wording is testable without an oclif runtime.
61
+ */
62
+ function emptyTasksMessage(flags, bin = 'skrr') {
63
+ const activeFilters = Array.from(FILTER_FLAGS).filter((name) => {
64
+ const value = flags[name];
65
+ if (value === undefined || value === null || value === false)
66
+ return false;
67
+ if (typeof value === 'string' && value.length === 0)
68
+ return false;
69
+ if (Array.isArray(value) && value.length === 0)
70
+ return false;
71
+ return true;
72
+ });
73
+ const lines = ['No tasks.'];
74
+ if (activeFilters.length === 0) {
75
+ lines.push('');
76
+ lines.push(` → Create your first one: \`${bin} tasks create --title "..." --space <space-id>\``);
77
+ lines.push(` → List spaces to find one: \`${bin} spaces list\``);
78
+ }
79
+ else if (flags.status === 'in_progress' && !flags['execution-state']) {
80
+ // `--status in_progress` filters the board column only. A run that was
81
+ // reaped/reset (e.g. a stuck-task timeout) is moved OFF `in_progress` but
82
+ // keeps its execution markers, so it hides here. Point the operator at
83
+ // the execution-state views that still surface it.
84
+ lines.push('');
85
+ lines.push('Note: `--status in_progress` shows only the board column. A reaped or reset run');
86
+ lines.push(' no longer has that status. Try `--execution-state active` (running now) or');
87
+ lines.push(` \`--execution-state recovered\` (reaped, startable), or \`${bin} tasks active\`.`);
88
+ }
89
+ return lines.join('\n');
90
+ }
10
91
  /**
11
92
  * The task-list request bag is shared with import/export so every CLI list
12
93
  * surface serializes the same server-side filters. Keep user-id resolution in
@@ -303,17 +384,7 @@ class TasksList extends base_command_1.BaseCommand {
303
384
  const r = response;
304
385
  const items = r?.data ?? [];
305
386
  if (items.length === 0) {
306
- this.log('No tasks.');
307
- // `--status in_progress` filters the board column only. A run that was
308
- // reaped/reset (e.g. a stuck-task timeout) is moved OFF `in_progress` but
309
- // keeps its execution markers, so it hides here. Point the operator at the
310
- // execution-state views that still surface it.
311
- if (flags.status === 'in_progress' && !flags['execution-state']) {
312
- this.log('');
313
- this.log('Note: `--status in_progress` shows only the board column. A reaped or reset run');
314
- this.log(' no longer has that status. Try `--execution-state active` (running now) or');
315
- this.log(' `--execution-state recovered` (reaped, startable), or `skrr tasks active`.');
316
- }
387
+ this.log(emptyTasksMessage(flags, this.config.bin));
317
388
  return;
318
389
  }
319
390
  const rows = items.map((t) => {
@@ -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,