@skrr-ai/cli 0.1.134 → 0.1.136

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.
@@ -479,6 +479,7 @@ export declare abstract class BaseCommand extends Command {
479
479
  * bug (2026-09-18).
480
480
  */
481
481
  private lastApiRequestId?;
482
+ private jsonErrorWritten;
482
483
  /** E6: the server's `Retry-After` for the refused call, when it was a 429. */
483
484
  private lastRetryAfterMs?;
484
485
  private writeJsonCliError;
@@ -1352,6 +1352,13 @@ class BaseCommand extends core_1.Command {
1352
1352
  }
1353
1353
  async catch(err) {
1354
1354
  if (isOclifExitError(err)) {
1355
+ // oclif's default ExitError handler calls process.exit immediately.
1356
+ // A JSON error already written to a pipe may still be buffered, so let
1357
+ // Node drain stdout naturally while preserving the requested exit code.
1358
+ if (this.jsonErrorWritten) {
1359
+ process.exitCode = err.oclif?.exit ?? 1;
1360
+ return;
1361
+ }
1355
1362
  return super.catch(err);
1356
1363
  }
1357
1364
  // Canonical Task Model v1 removed several `tasks complete` flags outright,
@@ -2155,6 +2162,7 @@ class BaseCommand extends core_1.Command {
2155
2162
  * bug (2026-09-18).
2156
2163
  */
2157
2164
  lastApiRequestId;
2165
+ jsonErrorWritten = false;
2158
2166
  /** E6: the server's `Retry-After` for the refused call, when it was a 429. */
2159
2167
  lastRetryAfterMs;
2160
2168
  writeJsonCliError({ message, code, status, exit, retryable, details, requestId, }) {
@@ -2182,6 +2190,7 @@ class BaseCommand extends core_1.Command {
2182
2190
  ...(details ? { details } : {}),
2183
2191
  },
2184
2192
  }), null, 2));
2193
+ this.jsonErrorWritten = true;
2185
2194
  this.exit(exit);
2186
2195
  }
2187
2196
  /**
@@ -133,7 +133,7 @@ class AgentMessageSend extends base_command_1.BaseCommand {
133
133
  description: 'Path to a canonical intent JSON payload, or "-" for stdin',
134
134
  }),
135
135
  'idempotency-key': core_1.Flags.string({
136
- description: 'Stable retry key for this intent; auto-derived when omitted',
136
+ description: 'Stable retry key; overrides the key in --from-json, or is auto-derived when neither is supplied',
137
137
  }),
138
138
  kind: core_1.Flags.string({
139
139
  description: 'Semantic kind; prefer milestone, status, decision, blocker, or result (legacy progress/question accepted)',
@@ -113,6 +113,7 @@ class AgentsScheduleTrace extends base_command_1.BaseCommand {
113
113
  { key: 'status', header: 'STATUS' },
114
114
  { key: 'summary', header: 'SUMMARY', maxWidth: 48 },
115
115
  ], (line) => this.log(line));
116
+ (0, schedule_1.renderTodoPolicyTraceDetails)(events, (line) => this.log(line));
116
117
  // Which collection each column came from — the trace reconciles four
117
118
  // sources and the reader should be able to go check one.
118
119
  if (trace.sourcePolicy) {
@@ -1,10 +1,13 @@
1
1
  import { BaseCommand } from '../../base-command';
2
- import { type ApplyProactiveAgentResponse } from '../../lib/proactiveApi';
2
+ import { type ProactiveAgentSpec, type ApplyProactiveAgentResponse } from '../../lib/proactiveApi';
3
3
  export type ProactiveInstallReadiness = {
4
4
  status: 'ready' | 'degraded';
5
5
  message: string;
6
6
  repairCommand?: string;
7
7
  };
8
+ export declare function workerPolicyWindowLines(policy: NonNullable<ProactiveAgentSpec['worker']>['policy']): string[];
9
+ /** JSON previews show the effective disabled state without mutating the input spec. */
10
+ export declare function specWithEffectiveWindowFlags(spec: ProactiveAgentSpec): ProactiveAgentSpec;
8
11
  /**
9
12
  * Agent identity creation is intentionally forward-only, but an Agent URL must
10
13
  * not masquerade as a fully installed proactive service when a watcher or
@@ -1,5 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.workerPolicyWindowLines = workerPolicyWindowLines;
4
+ exports.specWithEffectiveWindowFlags = specWithEffectiveWindowFlags;
3
5
  exports.proactiveInstallReadiness = proactiveInstallReadiness;
4
6
  const core_1 = require("@oclif/core");
5
7
  const base_command_1 = require("../../base-command");
@@ -7,6 +9,40 @@ const prompt_1 = require("../../lib/prompt");
7
9
  const format_1 = require("../../lib/format");
8
10
  const web_url_1 = require("../../lib/web-url");
9
11
  const proactiveApi_1 = require("../../lib/proactiveApi");
12
+ function workerPolicyWindowLines(policy) {
13
+ const lines = [];
14
+ for (const [label, window] of [
15
+ ['Working hours', policy?.workingHours],
16
+ ['Quiet hours', policy?.quietHours],
17
+ ]) {
18
+ if (!window)
19
+ continue;
20
+ const state = window.enabled === undefined
21
+ ? 'disabled; enabled not specified'
22
+ : window.enabled ? 'enabled' : 'disabled';
23
+ lines.push(` ${label}: ${window.start}–${window.end} ${window.timezone ?? '(no zone recorded)'} (${state})`);
24
+ }
25
+ return lines;
26
+ }
27
+ /** JSON previews show the effective disabled state without mutating the input spec. */
28
+ function specWithEffectiveWindowFlags(spec) {
29
+ const policy = spec.worker?.policy;
30
+ if (!spec.worker || !policy)
31
+ return spec;
32
+ const workingHours = policy.workingHours && { ...policy.workingHours, enabled: policy.workingHours.enabled ?? false };
33
+ const quietHours = policy.quietHours && { ...policy.quietHours, enabled: policy.quietHours.enabled ?? false };
34
+ return {
35
+ ...spec,
36
+ worker: {
37
+ ...spec.worker,
38
+ policy: {
39
+ ...policy,
40
+ ...(workingHours ? { workingHours } : {}),
41
+ ...(quietHours ? { quietHours } : {}),
42
+ },
43
+ },
44
+ };
45
+ }
10
46
  /**
11
47
  * Agent identity creation is intentionally forward-only, but an Agent URL must
12
48
  * not masquerade as a fully installed proactive service when a watcher or
@@ -23,6 +59,18 @@ function proactiveInstallReadiness(result, bin) {
23
59
  message: 'The Agent is off. Review the setup warnings and enable Always-On explicitly if you want it to run.',
24
60
  };
25
61
  }
62
+ if (result.warnings.some((warning) => warning.code === 'compass_existing_not_replaced')) {
63
+ return {
64
+ status: 'degraded',
65
+ message: 'The requested standing direction was not installed. Review the existing Compass and the setup warnings.',
66
+ };
67
+ }
68
+ if (result.warnings.some((warning) => warning.code === 'policy_window_enabled_unspecified')) {
69
+ return {
70
+ status: 'degraded',
71
+ message: 'Some worker time windows are disabled because their enablement was not specified. Review the warnings.',
72
+ };
73
+ }
26
74
  // A full trigger budget must not make a new persona-only Agent look broken
27
75
  // when its core system commitments, local execution policy, and Always-On
28
76
  // state installed successfully. The optional backlog watcher is deferred;
@@ -135,7 +183,7 @@ class AgentsSpawn extends base_command_1.BaseCommand {
135
183
  const spec = (body.spec ?? body);
136
184
  if (!flags.approve) {
137
185
  if (flags.json) {
138
- this.log(JSON.stringify(spec, null, 2));
186
+ this.log(JSON.stringify(specWithEffectiveWindowFlags(spec), null, 2));
139
187
  return;
140
188
  }
141
189
  this.renderSpec(spec);
@@ -192,7 +240,7 @@ class AgentsSpawn extends base_command_1.BaseCommand {
192
240
  const spec = plan.spec;
193
241
  if (!flags.approve) {
194
242
  if (flags.json) {
195
- this.log(JSON.stringify(plan, null, 2));
243
+ this.log(JSON.stringify({ ...plan, spec: specWithEffectiveWindowFlags(spec) }, null, 2));
196
244
  return;
197
245
  }
198
246
  this.renderSpec(spec);
@@ -244,6 +292,8 @@ class AgentsSpawn extends base_command_1.BaseCommand {
244
292
  if (spec.worker) {
245
293
  this.log('');
246
294
  this.log(`Worker (event-reactive) — policy: ${spec.worker.policy?.mode ?? 'ask_first'}`);
295
+ for (const line of workerPolicyWindowLines(spec.worker.policy))
296
+ this.log(line);
247
297
  (0, format_1.renderTable)(spec.worker.watchers.map((w) => ({
248
298
  source: w.source,
249
299
  every: `${w.intervalMinutes ?? '-'}m`,
@@ -6,7 +6,7 @@ const scoped_memory_command_1 = require("../../lib/scoped-memory-command");
6
6
  const format_1 = require("../../lib/format");
7
7
  const memory_scope_1 = require("../../lib/memory-scope");
8
8
  class MemorySearch extends scoped_memory_command_1.ScopedMemoryCommand {
9
- static description = 'Semantic (hybrid vector + text) search over a memory pool — the same ranking the agent sees';
9
+ static description = 'Search a compatibility memory pool, or specify --scope or --store for canonical Memory';
10
10
  static examples = [
11
11
  '<%= config.bin %> memory search "dark mode preference"',
12
12
  '<%= config.bin %> memory search "billing" --agent agent_abc123',
@@ -68,13 +68,18 @@ class MemorySearch extends scoped_memory_command_1.ScopedMemoryCommand {
68
68
  catch (err) {
69
69
  this.handleApiError(err);
70
70
  }
71
+ const canonicalHint = flags.agent
72
+ ? 'Canonical Agent Memory is separate. Search with the same --agent and --scope agent:<agent-id>.'
73
+ : 'This is a compatibility pool. Search canonical Memory with --agent and --scope agent:<agent-id> or space:<space-id>.';
71
74
  if (flags.json) {
72
- this.log(JSON.stringify(response, null, 2));
75
+ this.log(JSON.stringify({ ...response, memorySource: 'compatibility_pool', canonicalSearchHint: canonicalHint }, null, 2));
73
76
  return;
74
77
  }
75
78
  const results = response.results ?? [];
79
+ this.log('Compatibility-pool memory search:');
80
+ this.log(canonicalHint);
76
81
  if (results.length === 0) {
77
- this.log(`No memories matched "${args.query}".`);
82
+ this.log(`No compatibility-pool memories matched "${args.query}".`);
78
83
  return;
79
84
  }
80
85
  const rows = results.map((r) => ({
@@ -12,7 +12,9 @@ class MemoryVersionsShow extends scoped_memory_command_1.ScopedMemoryCommand {
12
12
  this.requireAuth();
13
13
  const { args, flags } = await this.parse(MemoryVersionsShow);
14
14
  const result = await this.memoryHistoryApi(flags, { versionId: args.id });
15
- this.print(result, flags.json, `${result.id} ${result.operation}\n${JSON.stringify(result.payloadSnapshot, null, 2)}`);
15
+ this.print(result, flags.json, `${result.id} ${result.operation}\n${JSON.stringify(result.payloadSnapshot, null, 2)}${result.projection?.kind === 'legacy_episode_omitted'
16
+ ? '\nAn invalid historical episode reference was omitted; the content hash identifies the original stored payload.'
17
+ : ''}`);
16
18
  }
17
19
  }
18
20
  exports.default = MemoryVersionsShow;
@@ -0,0 +1,11 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class SpaceAutoDreamStatus extends BaseCommand {
3
+ static description: string;
4
+ static args: {
5
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
6
+ };
7
+ static flags: {
8
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
9
+ };
10
+ run(): Promise<void>;
11
+ }
@@ -0,0 +1,44 @@
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 api_fetch_1 = require("../../../lib/api-fetch");
6
+ class SpaceAutoDreamStatus extends base_command_1.BaseCommand {
7
+ static description = 'Show current Space AutoDream eligibility and the most recent recorded cycle';
8
+ static args = { id: core_1.Args.string({ description: 'Space ID or title', required: true, ignoreStdin: true }) };
9
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
10
+ async run() {
11
+ this.requireAuth();
12
+ const { args, flags } = await this.parse(SpaceAutoDreamStatus);
13
+ const spaceId = await this.resolveSpaceReference(args.id);
14
+ let result;
15
+ try {
16
+ result = await (0, api_fetch_1.apiFetch)(`/api/spaces/${encodeURIComponent(spaceId)}/autodream`, {
17
+ credential: this.resolvedCredential,
18
+ });
19
+ }
20
+ catch (error) {
21
+ this.handleApiError(error, { subject: `space ${args.id}` });
22
+ }
23
+ if (flags.json) {
24
+ this.printJsonResult(result);
25
+ return;
26
+ }
27
+ const eligibility = {
28
+ principal_missing_or_revoked: 'No active execution principal is configured.',
29
+ principal_lead_mismatch: 'The execution principal no longer matches the Space lead.',
30
+ principal_configured_unverified: 'An execution principal is configured; its current access has not been verified by this status check.',
31
+ }[result.currentEligibility] || 'Current eligibility is unknown.';
32
+ this.log(`Current eligibility: ${eligibility}`);
33
+ this.log(`Checked at: ${result.checkedAt}`);
34
+ const cycle = result.latestRecordedCycle;
35
+ if (cycle) {
36
+ this.log(`Most recent recorded cycle: ${cycle.window} — ${cycle.outcome}${cycle.reason ? ` (${cycle.reason})` : ''}; completed ${cycle.completedAt}`);
37
+ }
38
+ else {
39
+ this.log('No recorded cycle. This does not establish whether a cycle was scheduled.');
40
+ }
41
+ this.log('Cycle receipts are best-effort; an unrecorded later cycle may exist.');
42
+ }
43
+ }
44
+ exports.default = SpaceAutoDreamStatus;
@@ -62,6 +62,10 @@ class SpacesProposalsList extends base_command_1.BaseCommand {
62
62
  if (!result.data.length) {
63
63
  for (const line of (0, space_proposals_1.emptySpaceInboxLines)(result.nextCursor))
64
64
  this.log(line);
65
+ if (!result.nextCursor) {
66
+ const bin = this.config?.bin || 'skrr';
67
+ this.log(`For AutoDream status, run: ${bin} spaces autodream status ${spaceId}`);
68
+ }
65
69
  }
66
70
  (0, format_1.renderTable)(result.data.map(space_proposals_1.spaceProposalRow), [
67
71
  { key: 'id', header: 'PROPOSAL', verbatim: true },
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ export default class TriggersAuthorization extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ id: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ revoke: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ restore: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,45 @@
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 triggers_1 = require("../../lib/triggers");
6
+ class TriggersAuthorization extends base_command_1.BaseCommand {
7
+ static description = 'Revoke or restore a trigger’s user-directed authorization grant';
8
+ static examples = [
9
+ '<%= config.bin %> triggers authorization <trigger-id> --revoke',
10
+ '<%= config.bin %> triggers authorization <trigger-id> --restore --json',
11
+ ];
12
+ static args = {
13
+ id: core_1.Args.string({ description: 'Trigger ID', required: true, ignoreStdin: true }),
14
+ };
15
+ static flags = {
16
+ revoke: core_1.Flags.boolean({ description: 'Withdraw the existing grant', exclusive: ['restore'] }),
17
+ restore: core_1.Flags.boolean({ description: 'Restore the existing grant', exclusive: ['revoke'] }),
18
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
19
+ };
20
+ async run() {
21
+ this.requireAuth();
22
+ const { args, flags } = await this.parse(TriggersAuthorization);
23
+ if (!flags.revoke && !flags.restore) {
24
+ this.failWithCliError({
25
+ message: 'Choose --revoke or --restore for the existing authorization grant.',
26
+ code: 'AUTHORIZATION_ACTION_REQUIRED',
27
+ exit: 2,
28
+ retryable: false,
29
+ });
30
+ }
31
+ let result;
32
+ try {
33
+ result = await triggers_1.triggerApi.setAuthorizationRevoked(args.id, flags.revoke === true);
34
+ }
35
+ catch (err) {
36
+ this.handleApiError(err);
37
+ }
38
+ if (flags.json) {
39
+ this.log(JSON.stringify(result, null, 2));
40
+ return;
41
+ }
42
+ this.log(flags.revoke ? 'Authorization revoked.' : 'Authorization restored.');
43
+ }
44
+ }
45
+ exports.default = TriggersAuthorization;
@@ -78,6 +78,7 @@ export default class TriggersCreate extends BaseCommand {
78
78
  'allow-unknown-event': import("@oclif/core/interfaces").BooleanFlag<boolean>;
79
79
  };
80
80
  run(): Promise<void>;
81
+ private warnMissingSelfSchedule;
81
82
  /**
82
83
  * The server creates the trigger and then says whether something already
83
84
  * watches the same thing. Printed AFTER the summary, and the sentence itself
@@ -324,6 +324,7 @@ class TriggersCreate extends base_command_1.BaseCommand {
324
324
  }
325
325
  if (trigger.dryRun) {
326
326
  this.log('Trigger accepted by dry run.');
327
+ this.warnMissingSelfSchedule(trigger);
327
328
  return;
328
329
  }
329
330
  this.log('Created trigger:');
@@ -331,6 +332,15 @@ class TriggersCreate extends base_command_1.BaseCommand {
331
332
  if (url)
332
333
  this.log(`View it at ${url}`);
333
334
  this.warnDuplicateSubscriptions(trigger);
335
+ this.warnMissingSelfSchedule(trigger);
336
+ }
337
+ warnMissingSelfSchedule(trigger) {
338
+ const warnings = trigger?.warnings;
339
+ if (!Array.isArray(warnings))
340
+ return;
341
+ const warning = warnings.find((item) => item && typeof item === 'object' && item.code === 'self_scheduled_without_next_run');
342
+ if (typeof warning?.message === 'string')
343
+ this.warn(warning.message);
334
344
  }
335
345
  /**
336
346
  * The server creates the trigger and then says whether something already
@@ -25,10 +25,31 @@ function describeFireAuditDetail(detail, fireId, triggerId) {
25
25
  lines.push(`Reason: ${reason}`);
26
26
  if (message && message !== reason)
27
27
  lines.push(`${reason ? 'Detail:' : 'Reason:'} ${message}`);
28
+ const quietHours = (0, triggers_1.quietHoursAuditExplanation)(detail);
29
+ if (quietHours) {
30
+ lines.push(`Explanation: ${quietHours.summary}. ${quietHours.detail}`);
31
+ }
28
32
  if (detail.durationMs !== undefined)
29
33
  lines.push(`Duration: ${String(detail.durationMs)}ms`);
30
34
  if (detail.conversationId)
31
35
  lines.push(`Conversation: ${String(detail.conversationId)}`);
36
+ const metadata = detail.metadata;
37
+ if (metadata && typeof metadata === 'object' && !Array.isArray(metadata)) {
38
+ const record = metadata;
39
+ if (record.recordKind === 'schedule_end_marker') {
40
+ lines.push('Note: Schedule ended after the prior fire; this is not another occurrence.');
41
+ if (typeof record.afterOccurrenceId === 'string')
42
+ lines.push(`After fire: ${record.afterOccurrenceId}`);
43
+ }
44
+ const debounce = record.debounce;
45
+ if (debounce && typeof debounce === 'object' && !Array.isArray(debounce)) {
46
+ const { retryAt, windowMs } = debounce;
47
+ if (typeof retryAt === 'string' && retryAt.trim())
48
+ lines.push(`Retry at: ${retryAt}`);
49
+ if (typeof windowMs === 'number' && Number.isFinite(windowMs))
50
+ lines.push(`Debounce: ${windowMs}ms`);
51
+ }
52
+ }
32
53
  // Tool calls refused during a turn that was delivered: `triggers show` points here for the facts.
33
54
  lines.push(...(0, triggers_1.fireRefusalLines)(detail.metadata));
34
55
  return lines;
@@ -80,8 +80,18 @@ class TriggersPrune extends base_command_1.BaseCommand {
80
80
  // number that sizes the sweep, and `perUser` the cap it frees.
81
81
  const reclaimable = ownerWide ? quota.perUserReclaimable : quota.reclaimable;
82
82
  if (flags['dry-run']) {
83
+ let preview;
84
+ try {
85
+ preview = await triggers_1.triggerApi.prunePreview({
86
+ ...target,
87
+ ...(flags.limit ? { limit: flags.limit } : {}),
88
+ });
89
+ }
90
+ catch (err) {
91
+ this.handleApiError(err);
92
+ }
83
93
  if (flags.json) {
84
- this.log(JSON.stringify({ dryRun: true, ownerWide, wouldDelete: reclaimable, quota }, null, 2));
94
+ this.log(JSON.stringify({ ...preview, quota }, null, 2));
85
95
  return;
86
96
  }
87
97
  if (ownerWide) {
@@ -92,9 +102,11 @@ class TriggersPrune extends base_command_1.BaseCommand {
92
102
  this.log(`Scope: ${quota.scopeKind}`);
93
103
  this.log(`Active: ${quota.active} / ${quota.limit} (${quota.remaining} free)`);
94
104
  }
95
- this.log(`Reclaimable: ${reclaimable} disabled trigger(s)`);
105
+ this.log(`Would delete: ${preview.wouldDelete} disabled trigger(s)`);
106
+ for (const id of preview.ids)
107
+ this.log(` ${id}`);
96
108
  this.log('');
97
- this.log('Dry run — nothing was deleted.');
109
+ this.log('Dry run — nothing was deleted. Candidates can change before pruning.');
98
110
  return;
99
111
  }
100
112
  if (reclaimable === 0) {