@skrr-ai/cli 0.1.15 → 0.1.17

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.
@@ -0,0 +1,77 @@
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 confirm <task> <kind>:<identity>[@<location>] --reason "<what you checked>"`
9
+ *
10
+ * Somebody looked, and it is there.
11
+ *
12
+ * WHY THIS IS A SEPARATE COMMAND from `deliverable add`. Recording and vouching
13
+ * are different claims. `add` says "this exists, and I am the one who made it";
14
+ * this says "I checked, independently of whoever made it". Five of the six
15
+ * outcome kinds have no automatic observer — nothing in the platform watches a
16
+ * Drive folder or a Slack channel — so without this rung an expectation for a
17
+ * PDF or a posted message could only ever be closed by waiving it, which
18
+ * records "we decided we did not need this" about work that was actually done.
19
+ *
20
+ * The reason is required, for the same purpose a waiver's and a judgement's
21
+ * are: the entire value of a confirmation is that a person actually looked, and
22
+ * a confirmation with nothing written down is indistinguishable from a click.
23
+ *
24
+ * It UPSERTS. Confirming something the executor never recorded is legitimate —
25
+ * the thing is there either way — and making a person file the row first before
26
+ * they may vouch for it is ceremony that buys nothing.
27
+ */
28
+ class TasksDeliverableConfirm extends base_command_1.BaseCommand {
29
+ static description = 'Confirm you checked a deliverable and it is there — the evidence an expectation needs when nothing observes it automatically';
30
+ static examples = [
31
+ '<%= config.bin %> tasks deliverable confirm <task-id> file/pdf:report_92@Drive/Reports --reason "Opened it; all four sections are there."',
32
+ '<%= config.bin %> tasks deliverable confirm <task-id> message:m_51@#general --reason "Read the thread; it went out at 09:04."',
33
+ ];
34
+ static args = {
35
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
36
+ coordinate: core_1.Args.string({
37
+ description: '<kind>[/<format>]:<identity>[@<location>] — the thing you checked',
38
+ required: true,
39
+ ignoreStdin: true,
40
+ }),
41
+ };
42
+ static flags = {
43
+ reason: core_1.Flags.string({
44
+ required: true,
45
+ description: 'What you looked at. A confirmation without one is not a confirmation.',
46
+ }),
47
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
48
+ };
49
+ async run() {
50
+ this.requireAuth();
51
+ const { args, flags } = await this.parse(TasksDeliverableConfirm);
52
+ const parsed = (0, tasks_1.parseDeliverableCoordinate)(args.coordinate);
53
+ if (!parsed.ok) {
54
+ // For an agent the refusal IS the documentation — it hits the error and
55
+ // corrects — so it carries the whole form and vocabulary.
56
+ this.error(parsed.error, { exit: 1 });
57
+ return;
58
+ }
59
+ let result;
60
+ try {
61
+ result = await data_provider_1.dataService.confirmTaskDeliverable(args.task, {
62
+ ...parsed.value,
63
+ reason: flags.reason,
64
+ });
65
+ }
66
+ catch (err) {
67
+ this.handleApiError(err);
68
+ return;
69
+ }
70
+ if (flags.json) {
71
+ this.log(JSON.stringify(result, null, 2));
72
+ return;
73
+ }
74
+ this.log(`Confirmed ${parsed.value.kind}:${parsed.value.identity}: ${flags.reason}`);
75
+ }
76
+ }
77
+ exports.default = TasksDeliverableConfirm;
@@ -15,6 +15,10 @@ const STATE_LABEL = {
15
15
  met: 'done',
16
16
  waived: 'dropped',
17
17
  blocked: 'stuck',
18
+ // Its own word, because its next action is its own: somebody looks, rather
19
+ // than somebody works. Reading it as 'not yet' sends people to redo finished
20
+ // work, which is precisely the confusion the state was added to remove.
21
+ unverified: 'needs a look',
18
22
  underway: 'on its way',
19
23
  owed: 'not yet',
20
24
  unjudged: 'needs a person',
@@ -26,7 +30,13 @@ function line(expectation) {
26
30
  // trusted.
27
31
  const gate = expectation.gating ? '' : ' (does not block completion)';
28
32
  const from = expectation.source === 'space' ? ' [space default]' : '';
29
- return ` ${expectation.id.padEnd(18)} ${label.padEnd(14)} ${expectation.sentence}${gate}${from}`;
33
+ // Only shown when the work exists and the evidence is short — stating the
34
+ // required authority on every row would be noise on the rows where it is
35
+ // already satisfied or not yet relevant.
36
+ const evidence = expectation.state === 'unverified'
37
+ ? ` (has ${expectation.evidenceAuthority}, needs ${expectation.requiredAuthority})`
38
+ : '';
39
+ return ` ${expectation.id.padEnd(18)} ${label.padEnd(14)} ${expectation.sentence}${evidence}${gate}${from}`;
30
40
  }
31
41
  class TasksExpectations extends base_command_1.BaseCommand {
32
42
  static description = 'Show what a task must produce and how far each expectation got ("Done when")';
@@ -83,6 +93,15 @@ class TasksExpectations extends base_command_1.BaseCommand {
83
93
  if (settleable > 0) {
84
94
  this.log(`${settleable} awaiting a person — settle with \`${this.config.bin} tasks judge\`.`);
85
95
  }
96
+ // Named separately from `unjudged`, and with a different command, because
97
+ // the two look alike on the board and are not: one needs a verdict on prose,
98
+ // the other needs somebody to look at a thing that already exists.
99
+ const unverified = acceptance.expectations.filter((e) => e.state === 'unverified').length;
100
+ if (unverified > 0) {
101
+ this.log(`${unverified} produced but not vouched for — confirm with ` +
102
+ `\`${this.config.bin} tasks deliverable confirm\`, or lower the bar with ` +
103
+ `\`${this.config.bin} tasks trust\`.`);
104
+ }
86
105
  const inherited = acceptance.unjudged - settleable;
87
106
  if (inherited > 0) {
88
107
  this.log(`${inherited} inherited from the space default — declare them on the task to settle them.`);
@@ -7,6 +7,7 @@ const api_fetch_1 = require("../../lib/api-fetch");
7
7
  const outbox_1 = require("../../lib/outbox");
8
8
  const task_report_1 = require("../../lib/task-report");
9
9
  const task_resolver_1 = require("../../lib/task-resolver");
10
+ const tasks_1 = require("../../lib/tasks");
10
11
  const session_task_endpoints_1 = require("../../lib/session-task-endpoints");
11
12
  const web_url_1 = require("../../lib/web-url");
12
13
  /**
@@ -94,6 +95,29 @@ class TasksReport extends base_command_1.BaseCommand {
94
95
  ...(0, task_report_1.taskReportEventFields)(report),
95
96
  artifacts: flags.deliverable?.map((ref) => ({ label: 'deliverable', ref })),
96
97
  };
98
+ // THE SAME BRIDGE `tasks complete` CARRIES, for the same reason.
99
+ //
100
+ // `--deliverable` here is prose for a work event's `artifacts` list, and
101
+ // the ledger the completion gate reads is written by `tasks deliverable
102
+ // add`. Three commands in this CLI answer to the word and only one of them
103
+ // writes the ledger — and the project's own operating instructions teach
104
+ // this spelling. So a value that is unambiguously a coordinate is forwarded
105
+ // to the ledger too; prose is left exactly as it was, because guessing a
106
+ // coordinate out of prose fills the ledger with rows nobody can look up.
107
+ for (const ref of flags.deliverable ?? []) {
108
+ const parsed = (0, tasks_1.parseDeliverableCoordinate)(ref);
109
+ if (!parsed.ok) {
110
+ continue;
111
+ }
112
+ try {
113
+ await data_provider_1.dataService.addTaskDeliverable(task.id, parsed.value);
114
+ }
115
+ catch {
116
+ // Best-effort: a report must still be posted when the ledger write
117
+ // fails, and the completion gate's refusal names the command that
118
+ // writes it.
119
+ }
120
+ }
97
121
  const sessionPath = await (0, session_task_endpoints_1.sessionScopedTaskPath)('event');
98
122
  let result;
99
123
  try {
@@ -0,0 +1,35 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * `skrr tasks trust <task> <expectation-id> --level <authority> --reason "<why>"`
4
+ *
5
+ * Move how much evidence one expectation demands.
6
+ *
7
+ * A DECISION, not an edit — which is why it is its own command rather than a
8
+ * field on `tasks update`. It is the one control that turns a refusal into a
9
+ * pass with no new work done, so a reader later has to be able to see that it
10
+ * happened and why. Buried in a list patch it would record neither.
11
+ *
12
+ * Lowering is the common direction and it is legitimate: a contract that cannot
13
+ * express "an agent's word is enough for this one" pushes its authors back to
14
+ * declaring nothing at all, which is the failure the whole feature exists to
15
+ * end. Raising takes the same reason and costs nothing to allow.
16
+ *
17
+ * The three levels, weakest first:
18
+ * self_reported whoever did the work says it is done
19
+ * human_confirmed someone else looked and said so
20
+ * observation_backed the platform saw it itself (a merge webhook, an upload)
21
+ */
22
+ export default class TasksTrust extends BaseCommand {
23
+ static description: string;
24
+ static examples: string[];
25
+ static args: {
26
+ task: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
27
+ expectation: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
28
+ };
29
+ static flags: {
30
+ level: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
31
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
32
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
33
+ };
34
+ run(): Promise<void>;
35
+ }
@@ -0,0 +1,75 @@
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 trust <task> <expectation-id> --level <authority> --reason "<why>"`
8
+ *
9
+ * Move how much evidence one expectation demands.
10
+ *
11
+ * A DECISION, not an edit — which is why it is its own command rather than a
12
+ * field on `tasks update`. It is the one control that turns a refusal into a
13
+ * pass with no new work done, so a reader later has to be able to see that it
14
+ * happened and why. Buried in a list patch it would record neither.
15
+ *
16
+ * Lowering is the common direction and it is legitimate: a contract that cannot
17
+ * express "an agent's word is enough for this one" pushes its authors back to
18
+ * declaring nothing at all, which is the failure the whole feature exists to
19
+ * end. Raising takes the same reason and costs nothing to allow.
20
+ *
21
+ * The three levels, weakest first:
22
+ * self_reported whoever did the work says it is done
23
+ * human_confirmed someone else looked and said so
24
+ * observation_backed the platform saw it itself (a merge webhook, an upload)
25
+ */
26
+ class TasksTrust extends base_command_1.BaseCommand {
27
+ static description = 'Set how much evidence one expectation demands, with a stated reason';
28
+ static examples = [
29
+ '<%= config.bin %> tasks trust <task-id> e1 --level self_reported --reason "Internal draft; nobody else needs to check it."',
30
+ '<%= config.bin %> tasks trust <task-id> e2 --level observation_backed --reason "Customer-facing; I want the platform to see it land."',
31
+ ];
32
+ static args = {
33
+ task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
34
+ expectation: core_1.Args.string({
35
+ description: 'Expectation id (see `tasks expectations`)',
36
+ required: true,
37
+ ignoreStdin: true,
38
+ }),
39
+ };
40
+ static flags = {
41
+ level: core_1.Flags.string({
42
+ options: [...data_provider_1.DELIVERABLE_AUTHORITIES],
43
+ required: true,
44
+ description: 'How well evidenced the answer must be: self_reported (the doer says so), ' +
45
+ 'human_confirmed (someone else looked), observation_backed (the platform saw it).',
46
+ }),
47
+ reason: core_1.Flags.string({
48
+ required: true,
49
+ description: 'Why this bar. Required — it is the record.',
50
+ }),
51
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
52
+ };
53
+ async run() {
54
+ this.requireAuth();
55
+ const { args, flags } = await this.parse(TasksTrust);
56
+ let result;
57
+ try {
58
+ result = await data_provider_1.dataService.setTaskExpectationTrust(args.task, {
59
+ expectationId: args.expectation,
60
+ level: flags.level,
61
+ reason: flags.reason,
62
+ });
63
+ }
64
+ catch (err) {
65
+ this.handleApiError(err);
66
+ return;
67
+ }
68
+ if (flags.json) {
69
+ this.log(JSON.stringify(result, null, 2));
70
+ return;
71
+ }
72
+ this.log(`${args.expectation} now needs ${flags.level}: ${flags.reason}`);
73
+ }
74
+ }
75
+ exports.default = TasksTrust;
@@ -22,7 +22,7 @@ class TasksWaive extends base_command_1.BaseCommand {
22
22
  static args = {
23
23
  task: core_1.Args.string({ description: 'Task id or ref', required: true, ignoreStdin: true }),
24
24
  expectation: core_1.Args.string({
25
- description: 'Expectation id (see `tasks show`)',
25
+ description: 'Expectation id (see `tasks expectations`)',
26
26
  required: true,
27
27
  ignoreStdin: true,
28
28
  }),
@@ -16,7 +16,7 @@
16
16
  * Two `kind`s: `achieve` (bounded, deadline-bearing) and `maintain` (perpetual
17
17
  * "hold this state", derived from a Compass invariant — not authored here).
18
18
  */
19
- import { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES } from '@skrr-ai/data-provider';
19
+ import { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, COMMITMENT_AUTONOMY_MODES, COMMITMENT_EXECUTION_ISOLATION_MODES } from '@skrr-ai/data-provider';
20
20
  import type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentListResponse, CommitmentPlanResponse, CommitmentStatusResponse, CommitmentWorkLink } from '@skrr-ai/data-provider';
21
21
  import { withQuery } from './triggers';
22
22
  export { COMMITMENT_WATCHED_SOURCES, COMMITMENT_TEMPLATE_IDS, COMMITMENT_CADENCE_MODES, COMMITMENT_CADENCE_TIME_PATTERN, COMMITMENT_CADENCE_MAX_TIMES, };
@@ -28,8 +28,20 @@ export type { CommitmentCheck, CommitmentChecksResponse, Commitment, CommitmentL
28
28
  * `_Exhaustive*` guards fail to resolve if a canonical value is missing.
29
29
  */
30
30
  export declare const COMMITMENT_STATUSES: readonly ["draft", "active", "paused", "completed", "cancelled", "archived"];
31
- /** Autonomy ladder — how much the commitment may act without asking. */
32
- export declare const COMMITMENT_AUTONOMY_MODES: readonly ["off", "suggestions_only", "ask_first", "auto_run_limited", "auto_run"];
31
+ /**
32
+ * Autonomy ladder how much the commitment may act without asking.
33
+ *
34
+ * Re-exported, not restated: `@skrr-ai/data-provider` owns the vocabulary, and
35
+ * a second literal here is how the CLI would silently drift from the server
36
+ * that rejects its values.
37
+ */
38
+ export { COMMITMENT_AUTONOMY_MODES };
39
+ /**
40
+ * Where a Commitment's autonomous runs do their work. Re-exported for the same
41
+ * reason as the ladder above: the vocabulary is the server's, and a picker that
42
+ * offers a value the API rejects is a silent drift.
43
+ */
44
+ export { COMMITMENT_EXECUTION_ISOLATION_MODES };
33
45
  /**
34
46
  * Interval bounds for `cadence.intervalMs`.
35
47
  *
@@ -210,6 +222,7 @@ export declare const commitmentApi: {
210
222
  get: (id: string) => Promise<CommitmentStatusResponse>;
211
223
  create: (body: Record<string, unknown>) => Promise<Commitment>;
212
224
  update: (id: string, body: Record<string, unknown>) => Promise<Commitment>;
225
+ draftPreflight: (body: Record<string, unknown>) => Promise<CommitmentPreflightReport>;
213
226
  workLinks: (id: string, includeInactive?: boolean) => Promise<{
214
227
  object: "commitment_work_link_list";
215
228
  data: CommitmentWorkLink[];
@@ -347,6 +360,18 @@ export declare const COMMITMENT_FEEDBACK_KINDS: readonly ["useful", "noisy", "wr
347
360
  export declare const COMMITMENT_REMEDIATION_ACTIONS: readonly ["pause_commitment", "mute_user_nudges", "reduce_nudge_frequency", "tighten_policy", "mark_noop"];
348
361
  /** The only keys `createBodySchema` (a `z.strictObject`) accepts. */
349
362
  export declare const COMMITMENT_CREATE_KEYS: readonly ["agentId", "kind", "compassId", "compassOrigin", "goalId", "title", "description", "agentDirective", "status", "target", "watchedSources", "policy", "cadence", "subject", "sensors", "grants", "reports", "createdVia"];
363
+ /**
364
+ * Read the per-Commitment domain policy from an inline flag or a text file.
365
+ *
366
+ * This deliberately uses a separate pair of names from Agent-level instruction
367
+ * commands: the result configures one Commitment run, not the Agent's global
368
+ * prompt. The explicit error also keeps shell users from accidentally masking
369
+ * a reviewed file with a short inline experiment.
370
+ */
371
+ export declare function readCommitmentAgentDirective({ directive, file, }: {
372
+ directive?: string;
373
+ file?: string;
374
+ }): Promise<string | undefined>;
350
375
  /**
351
376
  * Normalize a `--from-json` body into something the create endpoint accepts.
352
377
  *
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
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;
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_EXECUTION_ISOLATION_MODES = 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
+ exports.readCommitmentAgentDirective = readCommitmentAgentDirective;
4
5
  exports.normalizeCreateBody = normalizeCreateBody;
5
6
  exports.parseIntervalToMs = parseIntervalToMs;
6
7
  exports.buildWatchedSources = buildWatchedSources;
@@ -45,6 +46,9 @@ Object.defineProperty(exports, "COMMITMENT_TEMPLATE_IDS", { enumerable: true, ge
45
46
  Object.defineProperty(exports, "COMMITMENT_CADENCE_MODES", { enumerable: true, get: function () { return data_provider_1.COMMITMENT_CADENCE_MODES; } });
46
47
  Object.defineProperty(exports, "COMMITMENT_CADENCE_TIME_PATTERN", { enumerable: true, get: function () { return data_provider_1.COMMITMENT_CADENCE_TIME_PATTERN; } });
47
48
  Object.defineProperty(exports, "COMMITMENT_CADENCE_MAX_TIMES", { enumerable: true, get: function () { return data_provider_1.COMMITMENT_CADENCE_MAX_TIMES; } });
49
+ Object.defineProperty(exports, "COMMITMENT_AUTONOMY_MODES", { enumerable: true, get: function () { return data_provider_1.COMMITMENT_AUTONOMY_MODES; } });
50
+ Object.defineProperty(exports, "COMMITMENT_EXECUTION_ISOLATION_MODES", { enumerable: true, get: function () { return data_provider_1.COMMITMENT_EXECUTION_ISOLATION_MODES; } });
51
+ const instruction_input_1 = require("./instruction-input");
48
52
  const format_1 = require("./format");
49
53
  const triggers_1 = require("./triggers");
50
54
  /**
@@ -63,16 +67,6 @@ exports.COMMITMENT_STATUSES = [
63
67
  ];
64
68
  const _statusDriftGuard = true;
65
69
  void _statusDriftGuard;
66
- /** Autonomy ladder — how much the commitment may act without asking. */
67
- exports.COMMITMENT_AUTONOMY_MODES = [
68
- 'off',
69
- 'suggestions_only',
70
- 'ask_first',
71
- 'auto_run_limited',
72
- 'auto_run',
73
- ];
74
- const _autonomyDriftGuard = true;
75
- void _autonomyDriftGuard;
76
70
  /**
77
71
  * Interval bounds for `cadence.intervalMs`.
78
72
  *
@@ -116,6 +110,7 @@ exports.commitmentApi = {
116
110
  get: (id) => data_provider_1.request.get(base(id)),
117
111
  create: (body) => data_provider_1.request.post('/api/commitments', body),
118
112
  update: (id, body) => data_provider_1.request.patch(base(id), body),
113
+ draftPreflight: (body) => data_provider_1.request.post('/api/commitments/preflight', body),
119
114
  workLinks: (id, includeInactive = false) => data_provider_1.request.get((0, triggers_1.withQuery)(`${base(id)}/links`, { ...(includeInactive ? { includeInactive: true } : {}) })),
120
115
  createWorkLink: (id, body) => data_provider_1.request.post(`${base(id)}/links`, body),
121
116
  updateWorkLink: (id, linkId, body) => data_provider_1.request.patch(`${base(id)}/links/${encodeURIComponent(linkId)}`, body),
@@ -238,6 +233,20 @@ exports.COMMITMENT_CREATE_KEYS = [
238
233
  'reports',
239
234
  'createdVia',
240
235
  ];
236
+ /**
237
+ * Read the per-Commitment domain policy from an inline flag or a text file.
238
+ *
239
+ * This deliberately uses a separate pair of names from Agent-level instruction
240
+ * commands: the result configures one Commitment run, not the Agent's global
241
+ * prompt. The explicit error also keeps shell users from accidentally masking
242
+ * a reviewed file with a short inline experiment.
243
+ */
244
+ async function readCommitmentAgentDirective({ directive, file, }) {
245
+ if (directive !== undefined && file !== undefined) {
246
+ throw new Error('Use either --agent-directive or --agent-directive-file, not both.');
247
+ }
248
+ return (0, instruction_input_1.readInstructionBody)({ body: directive, file });
249
+ }
241
250
  /**
242
251
  * Normalize a `--from-json` body into something the create endpoint accepts.
243
252
  *
package/dist/lib/tasks.js CHANGED
@@ -170,8 +170,34 @@ function parseDeliverableCoordinate(spec) {
170
170
  }
171
171
  /** Lifecycle values a caller may assert, for flag validation. */
172
172
  exports.DELIVERABLE_LIFECYCLE_VALUES = data_provider_1.DELIVERABLE_LIFECYCLES;
173
+ /**
174
+ * `--from-json` takes stored API field names, while the interactive flags use
175
+ * shorter names such as `--project` and `--spawned-by`. Normalize the obvious
176
+ * camel-case forms before a permissive API route can silently drop them.
177
+ */
178
+ function normalizeTaskCreateJsonAliases(body) {
179
+ const normalized = { ...body };
180
+ const aliases = [
181
+ ['parent', 'parentId'],
182
+ ['space', 'spaceId'],
183
+ ['goal', 'goalId'],
184
+ ['commitment', 'commitmentId'],
185
+ ['project', 'projectId'],
186
+ ['cycle', 'cycleId'],
187
+ ['spawnedBy', 'spawnedByTaskId'],
188
+ ];
189
+ for (const [alias, canonical] of aliases) {
190
+ if (Object.prototype.hasOwnProperty.call(normalized, canonical))
191
+ continue;
192
+ if (!Object.prototype.hasOwnProperty.call(normalized, alias))
193
+ continue;
194
+ normalized[canonical] = normalized[alias];
195
+ delete normalized[alias];
196
+ }
197
+ return normalized;
198
+ }
173
199
  function buildTaskCreatePayload(body, flags = {}) {
174
- const payload = (0, prompt_1.mergeDefined)(body, {
200
+ const payload = (0, prompt_1.mergeDefined)(normalizeTaskCreateJsonAliases(body), {
175
201
  templateId: flags.template,
176
202
  title: flags.title,
177
203
  description: flags.description,