@skrr-ai/cli 0.1.15 → 0.1.18

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 (64) hide show
  1. package/README.md +10 -4
  2. package/dist/commands/commitments/action-proposals/decide.d.ts +13 -0
  3. package/dist/commands/commitments/action-proposals/decide.js +41 -0
  4. package/dist/commands/commitments/action-proposals/execute.d.ts +12 -0
  5. package/dist/commands/commitments/action-proposals/execute.js +29 -0
  6. package/dist/commands/commitments/action-proposals.d.ts +13 -0
  7. package/dist/commands/commitments/action-proposals.js +56 -0
  8. package/dist/commands/commitments/autonomy.d.ts +7 -11
  9. package/dist/commands/commitments/autonomy.js +16 -17
  10. package/dist/commands/commitments/bundle.d.ts +14 -0
  11. package/dist/commands/commitments/bundle.js +49 -0
  12. package/dist/commands/commitments/create.d.ts +33 -5
  13. package/dist/commands/commitments/create.js +234 -27
  14. package/dist/commands/commitments/delivery.d.ts +16 -0
  15. package/dist/commands/commitments/delivery.js +71 -0
  16. package/dist/commands/commitments/doctor.d.ts +20 -0
  17. package/dist/commands/commitments/doctor.js +88 -0
  18. package/dist/commands/commitments/effective-policy.d.ts +2 -0
  19. package/dist/commands/commitments/effective-policy.js +50 -5
  20. package/dist/commands/commitments/endpoints/connect.d.ts +3 -10
  21. package/dist/commands/commitments/endpoints/connect.js +6 -37
  22. package/dist/commands/commitments/endpoints/create.d.ts +2 -9
  23. package/dist/commands/commitments/endpoints/create.js +10 -23
  24. package/dist/commands/commitments/mode.d.ts +15 -0
  25. package/dist/commands/commitments/mode.js +57 -0
  26. package/dist/commands/commitments/pack/promote.d.ts +1 -1
  27. package/dist/commands/commitments/pack/promote.js +7 -3
  28. package/dist/commands/commitments/pack/shadow.d.ts +3 -2
  29. package/dist/commands/commitments/pack/shadow.js +6 -4
  30. package/dist/commands/commitments/patch.d.ts +14 -0
  31. package/dist/commands/commitments/patch.js +51 -0
  32. package/dist/commands/commitments/preflight.d.ts +5 -2
  33. package/dist/commands/commitments/preflight.js +47 -6
  34. package/dist/commands/commitments/receipt.d.ts +15 -0
  35. package/dist/commands/commitments/receipt.js +34 -0
  36. package/dist/commands/commitments/remediate.d.ts +0 -1
  37. package/dist/commands/commitments/remediate.js +5 -3
  38. package/dist/commands/commitments/reports.js +27 -5
  39. package/dist/commands/commitments/show.d.ts +1 -0
  40. package/dist/commands/commitments/show.js +22 -1
  41. package/dist/commands/commitments/update.d.ts +9 -2
  42. package/dist/commands/commitments/update.js +40 -4
  43. package/dist/commands/commitments/wake-capabilities.d.ts +5 -0
  44. package/dist/commands/commitments/wake-capabilities.js +23 -7
  45. package/dist/commands/spaces/bootstrap.js +1 -1
  46. package/dist/commands/tasks/complete.js +57 -0
  47. package/dist/commands/tasks/create.js +2 -1
  48. package/dist/commands/tasks/deliverable/confirm.d.ts +35 -0
  49. package/dist/commands/tasks/deliverable/confirm.js +77 -0
  50. package/dist/commands/tasks/expectations.js +20 -1
  51. package/dist/commands/tasks/report.js +24 -0
  52. package/dist/commands/tasks/trust.d.ts +35 -0
  53. package/dist/commands/tasks/trust.js +75 -0
  54. package/dist/commands/tasks/waive.js +1 -1
  55. package/dist/lib/commitment-endpoints.d.ts +3 -2
  56. package/dist/lib/commitment-endpoints.js +12 -9
  57. package/dist/lib/commitment-product.d.ts +14 -0
  58. package/dist/lib/commitment-product.js +78 -0
  59. package/dist/lib/commitments.d.ts +67 -20
  60. package/dist/lib/commitments.js +89 -39
  61. package/dist/lib/tasks.js +27 -1
  62. package/dist/node_modules/@skrr-ai/data-provider/index.js +3733 -3570
  63. package/oclif.manifest.json +9291 -8306
  64. package/package.json +1 -1
@@ -1,18 +1,142 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.hasEventCapableWatchedSource = hasEventCapableWatchedSource;
4
+ exports.assertEventDrivenWatchedSource = assertEventDrivenWatchedSource;
5
+ exports.createCadenceFromFlags = createCadenceFromFlags;
3
6
  exports.autonomyStagingNotice = autonomyStagingNotice;
4
7
  const core_1 = require("@oclif/core");
5
8
  const base_command_1 = require("../../base-command");
6
9
  const web_url_1 = require("../../lib/web-url");
7
10
  const prompt_1 = require("../../lib/prompt");
8
11
  const triggers_1 = require("../../lib/triggers");
12
+ const commitment_product_1 = require("../../lib/commitment-product");
9
13
  const commitments_1 = require("../../lib/commitments");
14
+ const DEFAULT_EVENT_DRIVEN_BACKSTOP_MS = 24 * 60 * 60 * 1000;
15
+ const DEFAULT_EVENT_SOURCES = new Set(['github', 'slack', 'email', 'calendar']);
16
+ function record(value) {
17
+ return value && typeof value === 'object' && !Array.isArray(value)
18
+ ? value
19
+ : undefined;
20
+ }
21
+ function configuredId(config, ...keys) {
22
+ return keys.some((key) => {
23
+ const value = config?.[key];
24
+ return typeof value === 'string' && value.trim().length > 0;
25
+ });
26
+ }
27
+ function hasStructurallyValidExplicitEvent(config) {
28
+ const events = config?.events;
29
+ if (!Array.isArray(events))
30
+ return false;
31
+ return events.some((value) => {
32
+ const event = record(value);
33
+ if (!event)
34
+ return false;
35
+ if (typeof event.entityType !== 'string' || !event.entityType.trim())
36
+ return false;
37
+ if (typeof event.eventType !== 'string' || !event.eventType.trim())
38
+ return false;
39
+ if (event.filterField === undefined)
40
+ return true;
41
+ if (typeof event.filterField !== 'string')
42
+ return false;
43
+ return !event.filterField
44
+ .split('.')
45
+ .some((part) => ['__proto__', 'constructor', 'prototype'].includes(part));
46
+ });
47
+ }
48
+ /**
49
+ * True only when the authored create body can materialize at least one event
50
+ * binding. A polling-only source must not make `--event-driven` look successful
51
+ * while the resulting Commitment still wakes exclusively on its backstop.
52
+ */
53
+ function hasEventCapableWatchedSource(payload) {
54
+ const goalId = typeof payload.goalId === 'string' && payload.goalId.trim().length > 0;
55
+ const watchedSources = Array.isArray(payload.watchedSources) ? payload.watchedSources : [];
56
+ return watchedSources.some((value) => {
57
+ const sourceEntry = record(value);
58
+ if (!sourceEntry || sourceEntry.enabled === false || typeof sourceEntry.source !== 'string') {
59
+ return false;
60
+ }
61
+ const source = sourceEntry.source;
62
+ const config = record(sourceEntry.config);
63
+ // For these four sources, TriggerSync owns a default event family. An
64
+ // explicit empty or malformed event list intentionally suppresses it.
65
+ if (DEFAULT_EVENT_SOURCES.has(source)) {
66
+ return Array.isArray(config?.events) ? hasStructurallyValidExplicitEvent(config) : true;
67
+ }
68
+ if (source === 'tasks') {
69
+ return (goalId || configuredId(config, 'taskId', 'goalId', 'keyResultId'));
70
+ }
71
+ if (source === 'goal') {
72
+ return goalId || configuredId(config, 'goalId', 'keyResultId');
73
+ }
74
+ if (source === 'space')
75
+ return configuredId(config, 'spaceId');
76
+ if (source === 'conversations')
77
+ return configuredId(config, 'conversationId');
78
+ if (source === 'messages')
79
+ return configuredId(config, 'groupChatId');
80
+ if (source === 'agent_initiatives') {
81
+ return configuredId(config, 'initiativeId', 'cloudCodingTaskId');
82
+ }
83
+ if (source === 'documents')
84
+ return configuredId(config, 'artifactIdentifier');
85
+ if (source === 'monitor')
86
+ return configuredId(config, 'monitorId');
87
+ // Custom bindings are still server-validated against the source-specific
88
+ // wake allowlist. At this boundary, require at least a structurally valid
89
+ // event row instead of treating any non-empty array as event capability.
90
+ return hasStructurallyValidExplicitEvent(config);
91
+ });
92
+ }
93
+ function assertEventDrivenWatchedSource(payload) {
94
+ if (record(payload.cadence)?.mode !== 'event_plus_interval' ||
95
+ hasEventCapableWatchedSource(payload)) {
96
+ return;
97
+ }
98
+ throw new Error('Event-driven cadence (--event-driven or --cadence-mode event_plus_interval) requires at least one event-capable watched source. ' +
99
+ 'Add --watch github (or another event-capable source), or choose an interval-only cadence.');
100
+ }
101
+ /**
102
+ * Resolve the create-time cadence flags into the API payload.
103
+ *
104
+ * `--event-driven` is deliberately a complete shortcut rather than an alias
105
+ * for the mode string: event delivery is an accelerator, so the contract must
106
+ * always retain a timed safety net even when the author omits `--cadence`.
107
+ */
108
+ function createCadenceFromFlags({ eventDriven, cadence: cadenceInput, cadenceMode, timezone, }) {
109
+ if (eventDriven) {
110
+ if (cadenceMode && cadenceMode !== 'event_plus_interval') {
111
+ throw new Error(`--event-driven cannot be combined with --cadence-mode ${cadenceMode}. ` +
112
+ 'Use --event-driven alone or --cadence-mode event_plus_interval.');
113
+ }
114
+ return {
115
+ mode: 'event_plus_interval',
116
+ intervalMs: cadenceInput
117
+ ? (0, commitments_1.parseIntervalToMs)(cadenceInput)
118
+ : DEFAULT_EVENT_DRIVEN_BACKSTOP_MS,
119
+ ...(timezone ? { timezone } : {}),
120
+ };
121
+ }
122
+ const cadence = {};
123
+ if (cadenceInput) {
124
+ cadence.intervalMs = (0, commitments_1.parseIntervalToMs)(cadenceInput);
125
+ cadence.mode = cadenceMode || 'interval';
126
+ }
127
+ else if (cadenceMode) {
128
+ cadence.mode = cadenceMode;
129
+ }
130
+ if (timezone)
131
+ cadence.timezone = timezone;
132
+ return Object.keys(cadence).length ? cadence : undefined;
133
+ }
10
134
  /**
11
- * Public commitment creation is intentionally trust-first: an action-capable
12
- * mode requested in the create payload is staged as suggestions_only until an
13
- * owner explicitly grants it. That safety boundary must be visible at the CLI
14
- * boundary; otherwise a successful create response reads like the requested
15
- * autonomy is live when it is not.
135
+ * Creation preserves the policy its author wrote, so the mode that comes back
136
+ * is normally the mode that was asked for. When it is not normalization
137
+ * rejected the value, or a governance layer sits lower the difference has to
138
+ * be said out loud at the CLI boundary; otherwise a successful create response
139
+ * reads like the requested autonomy is live when something else is running.
16
140
  */
17
141
  function autonomyStagingNotice({ payload, commitment, bin, }) {
18
142
  const requestedPolicy = payload.policy && typeof payload.policy === 'object' && !Array.isArray(payload.policy)
@@ -21,14 +145,21 @@ function autonomyStagingNotice({ payload, commitment, bin, }) {
21
145
  const returnedPolicy = commitment.policy && typeof commitment.policy === 'object' && !Array.isArray(commitment.policy)
22
146
  ? commitment.policy
23
147
  : {};
24
- const requested = typeof requestedPolicy.autonomyMode === 'string' ? requestedPolicy.autonomyMode : '';
148
+ const requested = typeof payload.mode === 'string'
149
+ ? payload.mode
150
+ : typeof requestedPolicy.autonomyMode === 'string'
151
+ ? requestedPolicy.autonomyMode
152
+ : '';
25
153
  const admitted = typeof returnedPolicy.autonomyMode === 'string' ? returnedPolicy.autonomyMode : '';
26
- if (!requested || requested === admitted || requested === 'off')
154
+ if (!requested ||
155
+ (0, commitment_product_1.productMode)(requested) === (0, commitment_product_1.productMode)(admitted || 'suggestions_only') ||
156
+ requested === 'off') {
27
157
  return undefined;
158
+ }
28
159
  const id = typeof commitment.id === 'string' ? commitment.id : '<commitment-id>';
29
160
  return [
30
- `Requested autonomy ${requested} was staged as ${admitted || 'suggestions_only'} for review; it is not active yet.`,
31
- `To grant it explicitly: ${bin} commitments autonomy ${id} --mode ${requested} --reason "<why this scope is safe>"`,
161
+ `Requested mode ${(0, commitment_product_1.productModeLabel)(requested)} was not applied; this commitment is running as ${(0, commitment_product_1.productModeLabel)(admitted || 'suggestions_only')}.`,
162
+ `To set it explicitly: ${bin} commitments mode ${id} ${(0, commitment_product_1.productMode)(requested)}`,
32
163
  ].join('\n');
33
164
  }
34
165
  /**
@@ -45,9 +176,11 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
45
176
  static description = 'Create a commitment (a durable agent contract)';
46
177
  static examples = [
47
178
  '<%= config.bin %> commitments create --agent <agent-id> --title "Ship v2" --outcome "v2 is live for all users" --cadence 6h',
48
- '<%= config.bin %> commitments create --agent <agent-id> --title "Hiring loop" --outcome "Every candidate hears back in 48h" --watch tasks --watch email --cadence 12h --status active',
179
+ '<%= config.bin %> commitments create --agent <agent-id> --title "Hiring loop" --outcome "Every candidate hears back in 48h" --watch tasks --watch email --cadence 12h',
49
180
  '<%= config.bin %> commitments create --agent <agent-id> --title "SLA" --outcome "p95 under 200ms" --criterion "p95 latency confirmed" --evaluator metric --measure <measure-id>',
181
+ '<%= config.bin %> commitments create --agent <agent-id> --title "AWS cost watch" --outcome "Cost regressions are investigated" --agent-directive-file ./aws-cost-policy.md',
50
182
  '<%= config.bin %> commitments create --from-json commitment.json --json',
183
+ '<%= config.bin %> commitments create --agent <agent-id> --title "Keep CI healthy" --outcome "Failed checks are repaired" --watch github --event-driven --mode execute --delivery draft-pr --repo owner/repo --base main --status draft',
51
184
  ];
52
185
  static flags = {
53
186
  json: core_1.Flags.boolean({ description: 'Output the created commitment as JSON' }),
@@ -64,10 +197,16 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
64
197
  description: 'The outcome to hold true — required when setting any target field',
65
198
  }),
66
199
  description: core_1.Flags.string({ description: 'Longer description' }),
200
+ 'agent-directive': core_1.Flags.string({
201
+ description: 'Per-Commitment domain policy (max 8,000 characters). Mutually exclusive with --agent-directive-file.',
202
+ }),
203
+ 'agent-directive-file': core_1.Flags.string({
204
+ description: 'Read the per-Commitment domain policy from a text file, or - for stdin. Mutually exclusive with --agent-directive.',
205
+ }),
67
206
  goal: core_1.Flags.string({ description: 'Link to a goal ID' }),
68
207
  status: core_1.Flags.string({
69
- description: 'Create as draft (default) or active',
70
- options: ['draft', 'active'],
208
+ description: 'Create as a draft (default); activate later with commitments resume',
209
+ options: ['draft'],
71
210
  }),
72
211
  deadline: core_1.Flags.string({ description: 'ISO-8601 deadline for an `achieve` commitment' }),
73
212
  criterion: core_1.Flags.string({
@@ -108,16 +247,45 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
108
247
  multiple: true,
109
248
  }),
110
249
  cadence: core_1.Flags.string({
111
- description: 'Check interval, e.g. "15m", "6h", "1d" (1m..30d). Implies --cadence-mode interval.',
250
+ description: 'Check interval, e.g. "15m", "6h", "30d" (1m..365d). Defaults to interval mode; with --event-driven, this is the timed backstop.',
251
+ }),
252
+ 'event-driven': core_1.Flags.boolean({
253
+ description: 'Wake on matching watched-source events, with a timed backstop from --cadence (default 24h). Requires an event-capable watched source and cannot be combined with a different --cadence-mode.',
112
254
  }),
113
255
  'cadence-mode': core_1.Flags.string({
114
- description: 'Cadence mode (default: interval when --cadence is set, else manual)',
115
- options: [...commitments_1.COMMITMENT_CADENCE_MODES],
256
+ description: 'Advanced cadence mode (default: interval when --cadence is set, else manual). Prefer --event-driven for event_plus_interval with a 24h default backstop.',
257
+ options: [...commitments_1.COMMITMENT_AUTHORABLE_CADENCE_MODES],
116
258
  }),
117
259
  timezone: core_1.Flags.string({ description: 'IANA timezone for the cadence' }),
260
+ mode: core_1.Flags.string({
261
+ description: 'How the commitment progresses (default Monitor)',
262
+ options: [...commitment_product_1.COMMITMENT_PRODUCT_MODES],
263
+ }),
264
+ delivery: core_1.Flags.string({
265
+ description: 'Where Prepare or Execute coding work is delivered; Monitor is report-only',
266
+ options: [...commitment_product_1.COMMITMENT_DELIVERY_CHOICES],
267
+ }),
268
+ repo: core_1.Flags.string({ description: 'Delivery repository, owner/repo' }),
269
+ base: core_1.Flags.string({ description: 'Delivery target branch' }),
118
270
  autonomy: core_1.Flags.string({
119
- description: 'How much the commitment may act without asking (default suggestions_only)',
271
+ // `--help` is where someone decides what to type, so the ceiling that can
272
+ // still bind has to be stated HERE and not only in the notice that
273
+ // corrects them afterwards. `commitments autonomy --mode` learned the
274
+ // same lesson: what it must not keep is the silence.
275
+ description: 'Compatibility flag. Prefer --mode monitor|prepare|execute. Workspace administrator rules remain binding.',
120
276
  options: [...commitments_1.COMMITMENT_AUTONOMY_MODES],
277
+ hidden: true,
278
+ }),
279
+ isolation: core_1.Flags.string({
280
+ // Omitting this is not the same as passing `shared`: an omitted value
281
+ // leaves the daemon's own isolation heuristic in force, while `shared` is
282
+ // an explicit contract that suppresses it. Say so here, because `--help`
283
+ // is where someone decides which of the two they mean.
284
+ description: 'Where autonomous runs do their work. "ephemeral" cuts a one-shot git worktree per run ' +
285
+ 'and destroys it after; "shared" lands them on the daemon working tree, in place. ' +
286
+ 'Omit to leave the runtime default. Cannot be combined with a remediation capability.',
287
+ options: [...commitments_1.COMMITMENT_EXECUTION_ISOLATION_MODES],
288
+ hidden: true,
121
289
  }),
122
290
  'max-nudges': core_1.Flags.integer({ description: 'Max user nudges per day (0..20)' }),
123
291
  };
@@ -125,6 +293,7 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
125
293
  this.requireAuth();
126
294
  const { flags } = await this.parse(CommitmentsCreate);
127
295
  let payload;
296
+ let deliveryNormalizedForMonitor = false;
128
297
  try {
129
298
  // `createBodySchema` is a z.strictObject, so unwrap a `plan --json`
130
299
  // envelope and drop any key it does not declare (a contract dumped from
@@ -138,6 +307,12 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
138
307
  payload.title = flags.title;
139
308
  if (flags.description !== undefined)
140
309
  payload.description = flags.description;
310
+ const agentDirective = await (0, commitments_1.readCommitmentAgentDirective)({
311
+ directive: flags['agent-directive'],
312
+ file: flags['agent-directive-file'],
313
+ });
314
+ if (agentDirective !== undefined)
315
+ payload.agentDirective = agentDirective;
141
316
  if (flags.goal)
142
317
  payload.goalId = flags.goal;
143
318
  if (flags.status)
@@ -182,25 +357,47 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
182
357
  });
183
358
  if (built)
184
359
  payload.watchedSources = built;
185
- const cadence = {};
186
- if (flags.cadence) {
187
- cadence.intervalMs = (0, commitments_1.parseIntervalToMs)(flags.cadence);
188
- cadence.mode = flags['cadence-mode'] || 'interval';
189
- }
190
- else if (flags['cadence-mode']) {
191
- cadence.mode = flags['cadence-mode'];
192
- }
193
- if (flags.timezone)
194
- cadence.timezone = flags.timezone;
195
- if (Object.keys(cadence).length)
360
+ const cadence = createCadenceFromFlags({
361
+ eventDriven: flags['event-driven'],
362
+ cadence: flags.cadence,
363
+ cadenceMode: flags['cadence-mode'],
364
+ timezone: flags.timezone,
365
+ });
366
+ if (cadence)
196
367
  payload.cadence = cadence;
368
+ assertEventDrivenWatchedSource(payload);
197
369
  const policy = {};
198
370
  if (flags.autonomy)
199
371
  policy.autonomyMode = flags.autonomy;
372
+ if (flags.mode && flags.autonomy)
373
+ throw new Error('Use --mode or the compatibility --autonomy flag, not both.');
374
+ if (flags.mode)
375
+ payload.mode = (0, commitment_product_1.productMode)(flags.mode);
376
+ if (flags.delivery)
377
+ payload.delivery = (0, commitment_product_1.deliveryInput)(flags.delivery, flags);
378
+ else if (flags.repo || flags.base)
379
+ throw new Error('--repo and --base require --delivery.');
380
+ if (flags.isolation)
381
+ policy.executionIsolation = flags.isolation;
200
382
  if (flags['max-nudges'] !== undefined)
201
383
  policy.maxUserNudgesPerDay = flags['max-nudges'];
202
384
  if (Object.keys(policy).length)
203
385
  payload.policy = policy;
386
+ const payloadPolicy = payload.policy && typeof payload.policy === 'object' && !Array.isArray(payload.policy)
387
+ ? payload.policy
388
+ : {};
389
+ const selectedMode = typeof payload.mode === 'string'
390
+ ? payload.mode
391
+ : typeof payloadPolicy.autonomyMode === 'string'
392
+ ? payloadPolicy.autonomyMode
393
+ : 'monitor';
394
+ if (payload.delivery && typeof payload.delivery === 'object' && !Array.isArray(payload.delivery)) {
395
+ const declared = payload.delivery;
396
+ const coherent = (0, commitment_product_1.deliveryForProductMode)(selectedMode, declared);
397
+ deliveryNormalizedForMonitor =
398
+ (0, commitment_product_1.productMode)(selectedMode) === 'monitor' && declared.mode !== 'report_only';
399
+ payload.delivery = coherent;
400
+ }
204
401
  }
205
402
  catch (err) {
206
403
  this.error(err.message, { exit: 1 });
@@ -215,6 +412,9 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
215
412
  exit: 1,
216
413
  });
217
414
  }
415
+ // Creation is authoring only. Activation is a separate resume boundary
416
+ // that runs the current preflight; JSON imports cannot bypass it.
417
+ payload.status = 'draft';
218
418
  let commitment;
219
419
  try {
220
420
  commitment = await commitments_1.commitmentApi.create(payload);
@@ -231,6 +431,9 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
231
431
  });
232
432
  if (notice)
233
433
  this.warn(notice);
434
+ if (deliveryNormalizedForMonitor) {
435
+ this.warn('Monitor is report-only, so the requested code-delivery destination was not stored. Use --mode prepare or --mode execute to deliver code.');
436
+ }
234
437
  return;
235
438
  }
236
439
  this.log('Created commitment:');
@@ -244,6 +447,10 @@ class CommitmentsCreate extends base_command_1.BaseCommand {
244
447
  this.log('');
245
448
  this.log(notice);
246
449
  }
450
+ if (deliveryNormalizedForMonitor) {
451
+ this.log('');
452
+ this.log('Monitor is report-only, so delivery was set to Report only. Choose Prepare or Execute to deliver code.');
453
+ }
247
454
  if (commitment.status === 'draft') {
248
455
  this.log('');
249
456
  this.log(`Activate it with: ${this.config.bin} commitments resume ${commitment.id}`);
@@ -0,0 +1,16 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class CommitmentsDelivery extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ mode: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
8
+ };
9
+ static flags: {
10
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
+ repo: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
+ base: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
13
+ reason: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
14
+ };
15
+ run(): Promise<void>;
16
+ }
@@ -0,0 +1,71 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const base_command_1 = require("../../base-command");
5
+ const commitments_1 = require("../../lib/commitments");
6
+ const commitment_product_1 = require("../../lib/commitment-product");
7
+ const web_url_1 = require("../../lib/web-url");
8
+ class CommitmentsDelivery extends base_command_1.BaseCommand {
9
+ static description = 'Declare where Prepare or Execute delivers work; Monitor is always report-only';
10
+ static examples = [
11
+ '<%= config.bin %> commitments delivery <id> draft-pr --repo owner/repo --base main',
12
+ ];
13
+ static args = {
14
+ id: core_1.Args.string({ required: true, ignoreStdin: true }),
15
+ mode: core_1.Args.string({
16
+ required: true,
17
+ description: 'Delivery destination. Code destinations require Prepare or Execute (or a Pack Shadow promotion target).',
18
+ options: [
19
+ ...commitment_product_1.COMMITMENT_DELIVERY_CHOICES,
20
+ ...commitment_product_1.COMMITMENT_DELIVERY_CHOICES.map((mode) => mode.replace(/-/g, '_')),
21
+ ],
22
+ }),
23
+ };
24
+ static flags = {
25
+ json: core_1.Flags.boolean({ description: 'Output the complete contract as JSON' }),
26
+ repo: core_1.Flags.string({ description: 'GitHub repository, owner/repo' }),
27
+ base: core_1.Flags.string({ description: 'Delivery target branch, such as main' }),
28
+ reason: core_1.Flags.string({ description: 'Reason recorded with this contract change' }),
29
+ };
30
+ async run() {
31
+ this.requireAuth();
32
+ const { args, flags } = await this.parse(CommitmentsDelivery);
33
+ try {
34
+ const current = (await commitments_1.commitmentApi.get(args.id)).commitment;
35
+ const requested = (0, commitment_product_1.deliveryInput)(args.mode, flags);
36
+ const stagingForPromotion = current.pack?.phase === 'shadow';
37
+ const targetPolicy = stagingForPromotion
38
+ ? current.pack?.policyBeforeShadow || current.policy
39
+ : current.policy;
40
+ const productContract = targetPolicy?.workPromotionModel === 'monitor_prepare_execute_v1';
41
+ if (productContract &&
42
+ (0, commitment_product_1.productMode)(targetPolicy?.autonomyMode) === 'monitor' &&
43
+ requested.mode !== 'report_only') {
44
+ throw new Error(stagingForPromotion
45
+ ? 'This Pack promotion target is Monitor, so its delivery must be report-only. Set the commitment mode to Prepare or Execute before staging code delivery.'
46
+ : 'Monitor is report-only. Set the commitment mode to Prepare or Execute before selecting code delivery.');
47
+ }
48
+ const delivery = { ...current.delivery, ...requested };
49
+ const result = await commitments_1.commitmentApi.delivery(args.id, {
50
+ delivery,
51
+ expectedVersion: current.version,
52
+ ...(flags.reason ? { reason: flags.reason } : {}),
53
+ });
54
+ const linked = (0, web_url_1.withCommitmentUrl)(this.cliConfig.baseURL, result);
55
+ if (flags.json)
56
+ return this.log(JSON.stringify(linked, null, 2));
57
+ const renderedDelivery = stagingForPromotion
58
+ ? result.pack?.deliveryBeforeShadow || delivery
59
+ : result.delivery;
60
+ this.log(`${stagingForPromotion ? 'Promotion delivery staged' : 'Delivery'}: ${(0, commitment_product_1.deliveryLabel)(renderedDelivery?.mode)}${renderedDelivery?.repo ? ` · ${renderedDelivery.repo}` : ''}${renderedDelivery?.baseBranch ? ` → ${renderedDelivery.baseBranch}` : ''}`);
61
+ if (stagingForPromotion) {
62
+ this.log('Live Shadow delivery remains Report only until explicit promotion.');
63
+ }
64
+ this.log(`URL: ${linked.url}`);
65
+ }
66
+ catch (error) {
67
+ this.handleApiError(error);
68
+ }
69
+ }
70
+ }
71
+ exports.default = CommitmentsDelivery;
@@ -0,0 +1,20 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export declare function doctorVerdict(input: {
3
+ commitment?: Record<string, unknown>;
4
+ preflight?: Record<string, unknown>;
5
+ operatingState?: Record<string, unknown>;
6
+ }): {
7
+ verdict: string;
8
+ next: string;
9
+ };
10
+ export default class CommitmentsDoctor extends BaseCommand {
11
+ static description: string;
12
+ static args: {
13
+ id: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
17
+ strict: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ run(): Promise<void>;
20
+ }
@@ -0,0 +1,88 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.doctorVerdict = doctorVerdict;
4
+ const core_1 = require("@oclif/core");
5
+ const base_command_1 = require("../../base-command");
6
+ const commitments_1 = require("../../lib/commitments");
7
+ function doctorVerdict(input) {
8
+ const commitment = input.commitment || {};
9
+ const preflight = input.preflight || {};
10
+ const operatingState = input.operatingState || {};
11
+ const blocker = operatingState.firstActionableBlocker;
12
+ if (blocker?.code) {
13
+ return { verdict: 'blocked', next: String(blocker.remediation || 'Inspect the Commitment') };
14
+ }
15
+ if (operatingState.status === 'waiting_approval') {
16
+ return { verdict: 'waiting_approval', next: 'Open the pending prepared action for review' };
17
+ }
18
+ if (operatingState.coverage && operatingState.coverage.complete === false) {
19
+ return { verdict: 'degraded', next: 'Retry doctor; one or more operating-state projections are unavailable' };
20
+ }
21
+ if (operatingState.status && commitment.status === 'active') {
22
+ const status = String(operatingState.status);
23
+ return {
24
+ verdict: ['on_track', 'healthy'].includes(status) ? 'active_healthy' : status,
25
+ next: 'skrr commitments trace <id> --limit 50',
26
+ };
27
+ }
28
+ if (commitment.status !== 'active')
29
+ return { verdict: 'blocked', next: 'skrr commitments resume <id>' };
30
+ if (preflight.verdict === 'blocked')
31
+ return { verdict: 'blocked', next: 'skrr commitments preflight <id> --json' };
32
+ const attention = preflight.attention;
33
+ if (attention?.allowed === false)
34
+ return { verdict: 'attention_suppressed', next: 'skrr commitments effective-policy <id>' };
35
+ return { verdict: 'active_healthy', next: 'skrr commitments trace <id> --limit 50' };
36
+ }
37
+ class CommitmentsDoctor extends base_command_1.BaseCommand {
38
+ static description = 'Summarize commitment readiness and the next operator action';
39
+ static args = {
40
+ id: core_1.Args.string({ description: 'Commitment ID', required: true, ignoreStdin: true }),
41
+ };
42
+ static flags = {
43
+ json: core_1.Flags.boolean({ description: 'Output the composed health report as JSON' }),
44
+ strict: core_1.Flags.boolean({
45
+ description: 'Exit non-zero unless the commitment is active and healthy',
46
+ }),
47
+ };
48
+ async run() {
49
+ this.requireAuth();
50
+ const { args, flags } = await this.parse(CommitmentsDoctor);
51
+ let commitment;
52
+ let shown;
53
+ try {
54
+ shown = (await commitments_1.commitmentApi.get(args.id));
55
+ commitment = (shown.commitment || {});
56
+ }
57
+ catch (error) {
58
+ this.handleApiError(error);
59
+ return;
60
+ }
61
+ const operatingState = (shown.operatingState || {});
62
+ const diagnosis = doctorVerdict({ commitment, operatingState });
63
+ const report = {
64
+ ...diagnosis,
65
+ commitment,
66
+ abilities: shown.abilities,
67
+ operatingState,
68
+ observedAt: new Date().toISOString(),
69
+ };
70
+ if (flags.json)
71
+ this.log(JSON.stringify(report, null, 2));
72
+ else {
73
+ this.log(`Verdict: ${diagnosis.verdict}`);
74
+ this.log(`Next: ${diagnosis.next.replace('<id>', args.id)}`);
75
+ const autonomy = operatingState.autonomy;
76
+ if (autonomy) {
77
+ this.log(`Mode: ${(0, commitments_1.commitmentAutonomyLabel)(String(autonomy.declaredMode || ''))} → ${(0, commitments_1.commitmentAutonomyLabel)(String(autonomy.effectiveMode || ''))}`);
78
+ }
79
+ const approvals = operatingState.approvals;
80
+ if (approvals?.pendingCount)
81
+ this.log(`Waiting approval: ${String(approvals.pendingCount)}`);
82
+ }
83
+ if (flags.strict && !['healthy', 'active_healthy'].includes(diagnosis.verdict)) {
84
+ process.exitCode = 1;
85
+ }
86
+ }
87
+ }
88
+ exports.default = CommitmentsDoctor;
@@ -1,4 +1,5 @@
1
1
  import { BaseCommand } from '../../base-command';
2
+ export declare function effectivePolicyLines(policy: Record<string, any>): string[];
2
3
  /** Show the policy actually governing a Commitment without changing it. */
3
4
  export default class CommitmentsEffectivePolicy extends BaseCommand {
4
5
  static description: string;
@@ -8,6 +9,7 @@ export default class CommitmentsEffectivePolicy extends BaseCommand {
8
9
  };
9
10
  static flags: {
10
11
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
12
+ verbose: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
11
13
  };
12
14
  run(): Promise<void>;
13
15
  }
@@ -1,8 +1,46 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.effectivePolicyLines = effectivePolicyLines;
3
4
  const core_1 = require("@oclif/core");
4
5
  const base_command_1 = require("../../base-command");
5
6
  const commitments_1 = require("../../lib/commitments");
7
+ const commitment_product_1 = require("../../lib/commitment-product");
8
+ function effectivePolicyLines(policy) {
9
+ const governance = policy.governance || {};
10
+ const owner = policy.owner || {};
11
+ const lines = [`Mode in force: ${(0, commitment_product_1.productModeLabel)(governance.effectiveAutonomyMode)}`];
12
+ if (governance.blocked) {
13
+ lines.push(`Workspace status: blocked${governance.blockedReason ? ` — ${governance.blockedReason}` : ''}`);
14
+ }
15
+ else {
16
+ lines.push('Workspace status: allowed');
17
+ }
18
+ if (governance.workspaceMaxMode) {
19
+ lines.push(`Workspace Mode ceiling: ${(0, commitment_product_1.productModeLabel)(governance.workspaceMaxMode)}`);
20
+ }
21
+ if (Array.isArray(governance.workspaceModeOverrides)) {
22
+ for (const override of governance.workspaceModeOverrides) {
23
+ if (override?.effectiveMode) {
24
+ lines.push(`Workspace override: ${(0, commitment_product_1.productModeLabel)(override.effectiveMode)} — ${override.policy || 'configured administrator rule'}${override.configuredValue ? ` (${override.configuredValue})` : ''}`);
25
+ }
26
+ }
27
+ }
28
+ if (Array.isArray(governance.allowedCommitmentDeliveryModes)) {
29
+ lines.push(`Workspace delivery allowlist: ${governance.allowedCommitmentDeliveryModes.length ? governance.allowedCommitmentDeliveryModes.map(commitment_product_1.deliveryLabel).join(', ') : 'none'}`);
30
+ }
31
+ if (Array.isArray(governance.requireAttestedAccessForDeliveryModes) && governance.requireAttestedAccessForDeliveryModes.length) {
32
+ lines.push(`Runtime attestation required for: ${governance.requireAttestedAccessForDeliveryModes.map(commitment_product_1.deliveryLabel).join(', ')}`);
33
+ }
34
+ const runLimit = owner.actionRunsPerDay;
35
+ if (runLimit) {
36
+ lines.push(`Owner action-run limit: ${runLimit.mode === 'unbounded' ? 'Unlimited' : `${runLimit.value}/day`}`);
37
+ }
38
+ const cost = policy.billing;
39
+ if (cost?.effectiveCostLimitUsd != null) {
40
+ lines.push(`Effective daily cost limit: $${Number(cost.effectiveCostLimitUsd).toFixed(2)}`);
41
+ }
42
+ return lines;
43
+ }
6
44
  /** Show the policy actually governing a Commitment without changing it. */
7
45
  class CommitmentsEffectivePolicy extends base_command_1.BaseCommand {
8
46
  static description = 'Show the owner, workspace, and infrastructure policy currently governing a commitment';
@@ -15,6 +53,7 @@ class CommitmentsEffectivePolicy extends base_command_1.BaseCommand {
15
53
  };
16
54
  static flags = {
17
55
  json: core_1.Flags.boolean({ description: 'Output the complete effective policy as JSON' }),
56
+ verbose: core_1.Flags.boolean({ description: 'Append raw compatibility and diagnostic policy JSON' }),
18
57
  };
19
58
  async run() {
20
59
  this.requireAuth();
@@ -27,11 +66,17 @@ class CommitmentsEffectivePolicy extends base_command_1.BaseCommand {
27
66
  this.handleApiError(err);
28
67
  return;
29
68
  }
30
- // The response is deliberately provenance-rich and evolves with each policy
31
- // layer. Preserve that shape rather than flattening it into a lossy table.
32
- if (!flags.json)
33
- this.log('Effective policy:');
34
- this.log(JSON.stringify(policy, null, 2));
69
+ if (flags.json) {
70
+ this.log(JSON.stringify(policy, null, 2));
71
+ return;
72
+ }
73
+ this.log('Effective policy:');
74
+ for (const line of effectivePolicyLines(policy))
75
+ this.log(` ${line}`);
76
+ if (flags.verbose) {
77
+ this.log('\nRaw compatibility diagnostics:');
78
+ this.log(JSON.stringify(policy, null, 2));
79
+ }
35
80
  }
36
81
  }
37
82
  exports.default = CommitmentsEffectivePolicy;