@skrr-ai/cli 0.1.128 → 0.1.130

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 (103) hide show
  1. package/dist/base-command.d.ts +12 -0
  2. package/dist/base-command.js +65 -0
  3. package/dist/commands/agenda/trigger/create.js +9 -1
  4. package/dist/commands/agenda/trigger/list.d.ts +2 -1
  5. package/dist/commands/agenda/trigger/list.js +55 -6
  6. package/dist/commands/agents/create.js +3 -1
  7. package/dist/commands/agents/reflect.d.ts +6 -0
  8. package/dist/commands/agents/reflect.js +45 -6
  9. package/dist/commands/agents/schedule-overview.d.ts +17 -0
  10. package/dist/commands/agents/schedule-overview.js +33 -9
  11. package/dist/commands/agents/triggers/policy.js +3 -1
  12. package/dist/commands/browser/persistent-profile.d.ts +22 -0
  13. package/dist/commands/browser/persistent-profile.js +46 -0
  14. package/dist/commands/commitments/create.js +3 -1
  15. package/dist/commands/commitments/effective-policy.js +6 -0
  16. package/dist/commands/commitments/explain.js +10 -1
  17. package/dist/commands/commitments/plan.d.ts +14 -0
  18. package/dist/commands/commitments/plan.js +32 -0
  19. package/dist/commands/commitments/preflight.d.ts +1 -1
  20. package/dist/commands/commitments/preflight.js +12 -4
  21. package/dist/commands/commitments/show.d.ts +9 -0
  22. package/dist/commands/commitments/show.js +14 -1
  23. package/dist/commands/compass/revisions.d.ts +6 -2
  24. package/dist/commands/compass/revisions.js +13 -5
  25. package/dist/commands/compass/tactic.d.ts +5 -0
  26. package/dist/commands/compass/tactic.js +14 -0
  27. package/dist/commands/followups/plans/create.js +1 -1
  28. package/dist/commands/harnesses/list.js +1 -1
  29. package/dist/commands/northstars/create.js +6 -1
  30. package/dist/commands/northstars/governance/configure.js +1 -1
  31. package/dist/commands/northstars/intents/decide.js +2 -3
  32. package/dist/commands/northstars/list.js +1 -1
  33. package/dist/commands/northstars/review-now.js +5 -0
  34. package/dist/commands/northstars/reviews.js +15 -3
  35. package/dist/commands/northstars/show.js +1 -1
  36. package/dist/commands/spaces/auto-executor/runs.d.ts +9 -1
  37. package/dist/commands/spaces/auto-executor/runs.js +18 -1
  38. package/dist/commands/spaces/heartbeats/create.js +7 -5
  39. package/dist/commands/spaces/heartbeats/status.js +10 -0
  40. package/dist/commands/spaces/heartbeats/update.js +5 -3
  41. package/dist/commands/store/artifacts/releases.d.ts +20 -0
  42. package/dist/commands/store/artifacts/releases.js +85 -0
  43. package/dist/commands/store/library.d.ts +14 -0
  44. package/dist/commands/store/library.js +71 -0
  45. package/dist/commands/tasks/archive.js +6 -0
  46. package/dist/commands/tasks/list.d.ts +48 -0
  47. package/dist/commands/tasks/list.js +141 -9
  48. package/dist/commands/tasks/pause-schedule.js +1 -1
  49. package/dist/commands/tasks/remind.js +23 -11
  50. package/dist/commands/tasks/resume-schedule.js +4 -1
  51. package/dist/commands/tasks/retry-schedule.js +6 -3
  52. package/dist/commands/tasks/schedule.js +35 -8
  53. package/dist/commands/tasks/self-schedule.d.ts +56 -0
  54. package/dist/commands/tasks/self-schedule.js +179 -21
  55. package/dist/commands/tasks/show.js +4 -0
  56. package/dist/lib/agent-trigger-listing.d.ts +26 -0
  57. package/dist/lib/agent-trigger-listing.js +63 -0
  58. package/dist/lib/agentic-stream.js +138 -41
  59. package/dist/lib/commitment-ledgers.js +3 -1
  60. package/dist/lib/commitments.d.ts +24 -0
  61. package/dist/lib/commitments.js +64 -4
  62. package/dist/lib/compasses.d.ts +2 -0
  63. package/dist/lib/iso-instant.d.ts +52 -0
  64. package/dist/lib/iso-instant.js +162 -0
  65. package/dist/lib/northstars.d.ts +87 -4
  66. package/dist/lib/northstars.js +226 -94
  67. package/dist/lib/proposals.js +6 -2
  68. package/dist/lib/reflection.d.ts +58 -0
  69. package/dist/lib/reflection.js +115 -6
  70. package/dist/lib/store-artifact-releases.d.ts +39 -0
  71. package/dist/lib/store-artifact-releases.js +12 -0
  72. package/dist/lib/store-library.d.ts +37 -0
  73. package/dist/lib/store-library.js +12 -0
  74. package/dist/lib/task-calendar.d.ts +19 -0
  75. package/dist/lib/task-calendar.js +72 -0
  76. package/dist/lib/task-operations.js +2 -0
  77. package/dist/lib/task-recurrence-outcome.js +3 -1
  78. package/dist/lib/task-recurrence-summary.d.ts +15 -0
  79. package/dist/lib/task-recurrence-summary.js +228 -0
  80. package/dist/lib/task-reminder.d.ts +28 -0
  81. package/dist/lib/task-reminder.js +50 -0
  82. package/dist/lib/task-schedule-notes.d.ts +46 -0
  83. package/dist/lib/task-schedule-notes.js +71 -0
  84. package/dist/lib/triggers.d.ts +31 -2
  85. package/dist/lib/triggers.js +38 -18
  86. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/daemonWire.js +11 -1
  87. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.d.ts +9 -0
  88. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/deviceIdentityBridge.js +22 -0
  89. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.d.ts +123 -0
  90. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/executorStopReceipt.js +160 -0
  91. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.d.ts +2 -1
  92. package/dist/node_modules/@skrr-ai/auth-core/dist/cjs/index.js +19 -0
  93. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/daemonWire.js +11 -1
  94. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.d.ts +9 -0
  95. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/deviceIdentityBridge.js +28 -0
  96. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.d.ts +123 -0
  97. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/executorStopReceipt.js +173 -0
  98. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.d.ts +2 -1
  99. package/dist/node_modules/@skrr-ai/auth-core/dist/esm/index.js +3 -1
  100. package/dist/node_modules/@skrr-ai/auth-core/package.json +1 -1
  101. package/dist/node_modules/@skrr-ai/data-provider/index.js +6772 -6675
  102. package/oclif.manifest.json +282 -40
  103. package/package.json +1 -1
@@ -0,0 +1,85 @@
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 format_1 = require("../../../lib/format");
6
+ const list_output_1 = require("../../../lib/list-output");
7
+ const store_artifact_releases_1 = require("../../../lib/store-artifact-releases");
8
+ /**
9
+ * Your own publication history for one skill, newest first, with the admission
10
+ * state a curator left on each release. `store artifacts receipt` reads one
11
+ * release in full.
12
+ */
13
+ class StoreArtifactsReleases extends base_command_1.BaseCommand {
14
+ static description = 'List the releases you published for your own skill, newest first, with each release’s admission state and review note';
15
+ static examples = [
16
+ '<%= config.bin %> store artifacts releases skill <skill-id>',
17
+ '<%= config.bin %> store artifacts releases skill <skill-id> --limit 50 --json',
18
+ '<%= config.bin %> store artifacts releases skill <skill-id> --before 7',
19
+ ];
20
+ static args = {
21
+ // The history route is served for skills only. An action release is read
22
+ // one at a time with `store artifacts receipt action <id> <releaseId>`.
23
+ entityType: core_1.Args.string({
24
+ required: true,
25
+ ignoreStdin: true,
26
+ options: ['skill'],
27
+ description: 'Only skills have a release history read; for an action use `receipt`',
28
+ }),
29
+ entityId: core_1.Args.string({ required: true, ignoreStdin: true, description: 'Skill id' }),
30
+ };
31
+ static flags = {
32
+ limit: core_1.Flags.integer({ description: 'How many releases (1-50)', min: 1, max: 50 }),
33
+ before: core_1.Flags.integer({
34
+ description: 'Only releases numbered below this one (the page cursor printed with a page)',
35
+ min: 1,
36
+ }),
37
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
38
+ };
39
+ async run() {
40
+ this.requireAuth();
41
+ const { args, flags } = await this.parse(StoreArtifactsReleases);
42
+ let history;
43
+ try {
44
+ history = await (0, store_artifact_releases_1.listSkillArtifactReleases)(args.entityId, {
45
+ limit: flags.limit,
46
+ before: flags.before,
47
+ });
48
+ }
49
+ catch (error) {
50
+ this.handleApiError(error);
51
+ return;
52
+ }
53
+ const releases = history?.data ?? [];
54
+ const nextBefore = history?.nextBefore ?? null;
55
+ if (flags.json) {
56
+ this.printJsonResult((0, list_output_1.listResult)({ data: releases, next_cursor: nextBefore === null ? null : String(nextBefore) }, 'data'));
57
+ return;
58
+ }
59
+ if (releases.length === 0) {
60
+ this.log('No releases published for this skill.');
61
+ return;
62
+ }
63
+ (0, format_1.renderTable)(releases.map((release) => ({
64
+ number: String(release.releaseNumber),
65
+ release: release.releaseId,
66
+ version: release.versionLabel ?? '-',
67
+ status: release.status ?? '-',
68
+ admission: release.admission?.state ?? '-',
69
+ note: release.admission?.review?.note ?? '-',
70
+ published: release.publishedAt ? String(release.publishedAt).slice(0, 10) : '-',
71
+ })), [
72
+ { key: 'number', header: '#' },
73
+ { key: 'release', header: 'RELEASE' },
74
+ { key: 'version', header: 'VERSION' },
75
+ { key: 'status', header: 'STATUS' },
76
+ { key: 'admission', header: 'ADMISSION' },
77
+ { key: 'note', header: 'REVIEW NOTE', maxWidth: 40 },
78
+ { key: 'published', header: 'PUBLISHED' },
79
+ ], (line) => this.log(line));
80
+ if (nextBefore !== null) {
81
+ this.log(`More: run again with --before ${nextBefore}`);
82
+ }
83
+ }
84
+ }
85
+ exports.default = StoreArtifactsReleases;
@@ -0,0 +1,14 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * The Skill, Action and Service copies you bought, and whether each can still
4
+ * be used (the web Library page). `store installs` lists the Agent purchases;
5
+ * this is the other three kinds.
6
+ */
7
+ export default class StoreLibrary extends BaseCommand {
8
+ static description: string;
9
+ static examples: string[];
10
+ static flags: {
11
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -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 format_1 = require("../../lib/format");
6
+ const store_library_1 = require("../../lib/store-library");
7
+ const workspaces_1 = require("../../lib/workspaces");
8
+ const KINDS = [
9
+ ['skill', 'skills'],
10
+ ['action', 'actions'],
11
+ ['service', 'services'],
12
+ ];
13
+ /**
14
+ * The Skill, Action and Service copies you bought, and whether each can still
15
+ * be used (the web Library page). `store installs` lists the Agent purchases;
16
+ * this is the other three kinds.
17
+ */
18
+ class StoreLibrary extends base_command_1.BaseCommand {
19
+ static description = 'List the Skill, Action and Service copies you bought from the Store and whether each is still usable (Agent purchases: `store installs`)';
20
+ static examples = [
21
+ '<%= config.bin %> store library',
22
+ '<%= config.bin %> store library --json',
23
+ ];
24
+ static flags = {
25
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
26
+ };
27
+ async run() {
28
+ this.requireAuth();
29
+ const { flags } = await this.parse(StoreLibrary);
30
+ const workspaceId = await (0, workspaces_1.ambientWorkspaceId)();
31
+ let library;
32
+ try {
33
+ library = await (0, store_library_1.getStoreLibraryItems)(workspaceId);
34
+ }
35
+ catch (error) {
36
+ this.handleApiError(error);
37
+ return;
38
+ }
39
+ if (flags.json) {
40
+ this.printJsonResult({ workspaceId: workspaceId ?? null, ...library });
41
+ return;
42
+ }
43
+ const rows = KINDS.flatMap(([kind, key]) => (library?.[key] ?? []).map((item) => ({
44
+ kind,
45
+ source: item.sourceId,
46
+ copy: item.copyId ?? '-',
47
+ name: item.name ?? '-',
48
+ // `copyAvailable` is the server's verdict; the states say why when it is false.
49
+ state: item.copyAvailable ? 'available' : 'unavailable',
50
+ reason: item.copyAvailable
51
+ ? '-'
52
+ : [item.entitlementState, item.fulfillmentState].filter(Boolean).join(' / ') ||
53
+ 'copy missing',
54
+ })));
55
+ if (rows.length === 0) {
56
+ this.log(workspaceId
57
+ ? `No Skill, Action or Service purchases found in workspace ${workspaceId}.`
58
+ : 'No Skill, Action or Service purchases found; select a workspace with --workspace to check another scope.');
59
+ return;
60
+ }
61
+ (0, format_1.renderTable)(rows, [
62
+ { key: 'kind', header: 'KIND' },
63
+ { key: 'source', header: 'SOURCE' },
64
+ { key: 'copy', header: 'YOUR COPY' },
65
+ { key: 'name', header: 'NAME', maxWidth: 32 },
66
+ { key: 'state', header: 'STATE' },
67
+ { key: 'reason', header: 'REASON', maxWidth: 36 },
68
+ ], (line) => this.log(line));
69
+ }
70
+ }
71
+ exports.default = StoreLibrary;
@@ -4,6 +4,7 @@ const core_1 = require("@oclif/core");
4
4
  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
+ const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
7
8
  class TasksArchive extends base_command_1.BaseCommand {
8
9
  static description = 'Archive a task (reversible — use --unarchive to restore)';
9
10
  static examples = [
@@ -37,6 +38,11 @@ class TasksArchive extends base_command_1.BaseCommand {
37
38
  }
38
39
  const action = flags.unarchive ? 'unarchived' : 'archived';
39
40
  this.log(`Task ${args.id} ${action}`);
41
+ const lines = flags.unarchive
42
+ ? (0, task_schedule_notes_1.unarchiveResultLines)(result, String(args.id), this.config.bin)
43
+ : (0, task_schedule_notes_1.archiveResultLines)(result);
44
+ for (const line of lines)
45
+ this.log(line);
40
46
  }
41
47
  }
42
48
  exports.default = TasksArchive;
@@ -2,6 +2,40 @@ import type { TaskListParams } from '@skrr-ai/data-provider';
2
2
  import { BaseCommand } from '../../base-command';
3
3
  import { type ListWarning } from '../../lib/list-output';
4
4
  type ListFlags = Record<string, unknown>;
5
+ /** Which workspace a list reads, and why (OSK-21284). */
6
+ export type ListWorkspaceScope = {
7
+ source: 'all';
8
+ } | {
9
+ source: 'default';
10
+ workspaceId: string;
11
+ workspaceName?: string;
12
+ workspaceCount: number;
13
+ };
14
+ /**
15
+ * A plain `tasks list` named no workspace, and the server answers that with a list across EVERY
16
+ * workspace the person belongs to. Its access filter has branches that belong to no single
17
+ * workspace, so no index can serve it in order: on production it timed out after about 6 s while
18
+ * the same list naming a workspace answered in about a second. `skrr workspaces list` already says
19
+ * an unselected session "resolves to your default workspace"; a list now reads it too.
20
+ *
21
+ * --all-workspaces the old, wider list, asked for by name (`workspaceScope=all`)
22
+ * a selected workspace, `--workspace`, or `skrr workspaces use` untouched: the choice stands
23
+ * an autonomous Agent session untouched: a workspace-less Agent session stays personal
24
+ * `--space`, `--calendar` untouched: a Space names its own scope
25
+ *
26
+ * Returns nothing when there is nothing to apply, and `source: 'default'` when the caller's
27
+ * default workspace should be used. `lookupDefault` is injected so the rule is tested without the
28
+ * network; a lookup that fails or finds no default changes nothing.
29
+ */
30
+ export declare function resolveListWorkspace(flags: {
31
+ 'all-workspaces'?: boolean;
32
+ space?: string;
33
+ calendar?: string;
34
+ }, env: NodeJS.ProcessEnv, lookupDefault: () => Promise<{
35
+ id: string;
36
+ name?: string;
37
+ count: number;
38
+ } | null>): Promise<ListWorkspaceScope | undefined>;
5
39
  /**
6
40
  * E12: inside an agent session an unfiltered `tasks list --json` returned the
7
41
  * first page of EVERYTHING, which in practice was cancelled test tasks. When the
@@ -51,6 +85,19 @@ export declare function nextColumnWidth(rows: Array<{
51
85
  * is the one name an agent scanning 43 keys does not guess (OSK-10692).
52
86
  */
53
87
  export declare function taskRef(t: Record<string, unknown>): string;
88
+ /**
89
+ * A calendar row in `--json`: the Task it places, by the names `tasks show` resolves.
90
+ *
91
+ * The server's occurrence `id` is `<taskId>:<kind>:<instant>`, a key for the occurrence, and the
92
+ * list shaping every other `tasks list` row gets (`ref` and `url` read off the row itself, and
93
+ * `nextAction` off its actionability) turned it into an `id`, a `url` and a `ref` that named
94
+ * nothing: `tasks show` refused the first two and the third was "-" (OSK-21586, OSK-21587). So the
95
+ * row's `id`, `ref` and `url` are the Task's, and the occurrence key is `occurrenceId`. A missing
96
+ * answer is `null`, never a placeholder.
97
+ */
98
+ export declare function calendarJsonRow(baseURL: string, row: Record<string, unknown>): Record<string, unknown>;
99
+ /** The calendar response with its rows made addressable; everything else stays as the server sent it. */
100
+ export declare function calendarJsonResponse(baseURL: string, response: unknown): unknown;
54
101
  /** Evidence about managed execution only; unmanaged work is outside this projection. */
55
102
  export declare function taskExecution(t: Record<string, unknown>): Record<string, unknown>;
56
103
  export declare function taskPageSummary(items: Array<Record<string, unknown>>, hasMore: boolean): {
@@ -123,6 +170,7 @@ export default class TasksList extends BaseCommand {
123
170
  'explicit-only': import("@oclif/core/interfaces").BooleanFlag<boolean>;
124
171
  subscribed: import("@oclif/core/interfaces").BooleanFlag<boolean>;
125
172
  'include-snoozed': import("@oclif/core/interfaces").BooleanFlag<boolean>;
173
+ 'all-workspaces': import("@oclif/core/interfaces").BooleanFlag<boolean>;
126
174
  'external-key': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
127
175
  'external-key-prefix': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
128
176
  'description-format': import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
@@ -1,11 +1,14 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.resolveListWorkspace = resolveListWorkspace;
3
4
  exports.agentSessionOpenDefault = agentSessionOpenDefault;
4
5
  exports.emptyTasksMessage = emptyTasksMessage;
5
6
  exports.externalKeyPrefixWarnings = externalKeyPrefixWarnings;
6
7
  exports.nextActionText = nextActionText;
7
8
  exports.nextColumnWidth = nextColumnWidth;
8
9
  exports.taskRef = taskRef;
10
+ exports.calendarJsonRow = calendarJsonRow;
11
+ exports.calendarJsonResponse = calendarJsonResponse;
9
12
  exports.taskExecution = taskExecution;
10
13
  exports.taskPageSummary = taskPageSummary;
11
14
  exports.taskExecutionText = taskExecutionText;
@@ -18,7 +21,9 @@ const web_url_1 = require("../../lib/web-url");
18
21
  const format_1 = require("../../lib/format");
19
22
  const list_output_1 = require("../../lib/list-output");
20
23
  const tasks_1 = require("../../lib/tasks");
24
+ const workspaces_1 = require("../../lib/workspaces");
21
25
  const task_economics_1 = require("../../lib/task-economics");
26
+ const task_calendar_1 = require("../../lib/task-calendar");
22
27
  /**
23
28
  * Names of `tasks list` flags that narrow the result set. Used to distinguish
24
29
  * "no tasks anywhere" from "no tasks matching your filters" — the first case
@@ -63,6 +68,55 @@ const FILTER_FLAGS = new Set([
63
68
  'q',
64
69
  'view',
65
70
  ]);
71
+ /**
72
+ * A plain `tasks list` named no workspace, and the server answers that with a list across EVERY
73
+ * workspace the person belongs to. Its access filter has branches that belong to no single
74
+ * workspace, so no index can serve it in order: on production it timed out after about 6 s while
75
+ * the same list naming a workspace answered in about a second. `skrr workspaces list` already says
76
+ * an unselected session "resolves to your default workspace"; a list now reads it too.
77
+ *
78
+ * --all-workspaces the old, wider list, asked for by name (`workspaceScope=all`)
79
+ * a selected workspace, `--workspace`, or `skrr workspaces use` untouched: the choice stands
80
+ * an autonomous Agent session untouched: a workspace-less Agent session stays personal
81
+ * `--space`, `--calendar` untouched: a Space names its own scope
82
+ *
83
+ * Returns nothing when there is nothing to apply, and `source: 'default'` when the caller's
84
+ * default workspace should be used. `lookupDefault` is injected so the rule is tested without the
85
+ * network; a lookup that fails or finds no default changes nothing.
86
+ */
87
+ async function resolveListWorkspace(flags, env, lookupDefault) {
88
+ if (flags['all-workspaces'])
89
+ return { source: 'all' };
90
+ if (env.OVERSKY_WORKSPACE_ID?.trim())
91
+ return undefined;
92
+ if (env.OVERSKY_AUTONOMOUS_SESSION === '1')
93
+ return undefined;
94
+ if (flags.space || flags.calendar)
95
+ return undefined;
96
+ let found = null;
97
+ try {
98
+ found = await lookupDefault();
99
+ }
100
+ catch {
101
+ return undefined;
102
+ }
103
+ if (!found?.id)
104
+ return undefined;
105
+ return {
106
+ source: 'default',
107
+ workspaceId: found.id,
108
+ ...(found.name ? { workspaceName: found.name } : {}),
109
+ workspaceCount: found.count,
110
+ };
111
+ }
112
+ /** The default workspace the signed-in person's `skrr workspaces list` reports. */
113
+ async function lookupDefaultWorkspace() {
114
+ const listing = await (0, workspaces_1.listWorkspaceListing)();
115
+ if (!listing.defaultWorkspaceId)
116
+ return null;
117
+ const row = listing.rows.find((entry) => entry.id === listing.defaultWorkspaceId);
118
+ return { id: listing.defaultWorkspaceId, ...(row?.name ? { name: row.name } : {}), count: listing.rows.length };
119
+ }
66
120
  /**
67
121
  * E12: inside an agent session an unfiltered `tasks list --json` returned the
68
122
  * first page of EVERYTHING, which in practice was cancelled test tasks. When the
@@ -234,6 +288,43 @@ function nextColumnWidth(rows) {
234
288
  function taskRef(t) {
235
289
  return String(t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '-'));
236
290
  }
291
+ /**
292
+ * A calendar row in `--json`: the Task it places, by the names `tasks show` resolves.
293
+ *
294
+ * The server's occurrence `id` is `<taskId>:<kind>:<instant>`, a key for the occurrence, and the
295
+ * list shaping every other `tasks list` row gets (`ref` and `url` read off the row itself, and
296
+ * `nextAction` off its actionability) turned it into an `id`, a `url` and a `ref` that named
297
+ * nothing: `tasks show` refused the first two and the third was "-" (OSK-21586, OSK-21587). So the
298
+ * row's `id`, `ref` and `url` are the Task's, and the occurrence key is `occurrenceId`. A missing
299
+ * answer is `null`, never a placeholder.
300
+ */
301
+ function calendarJsonRow(baseURL, row) {
302
+ const task = (row.task ?? {});
303
+ const taskId = (row.taskId ?? task.id);
304
+ const next = nextActionText(task);
305
+ return {
306
+ ...row,
307
+ id: taskId ?? row.id,
308
+ occurrenceId: row.id,
309
+ ref: task.identifier ?? null,
310
+ nextAction: next === '-' ? null : next,
311
+ url: taskId ? (0, web_url_1.taskUrlFrom)(baseURL, { id: taskId, spaceId: task.spaceId }) : null,
312
+ };
313
+ }
314
+ /** The calendar response with its rows made addressable; everything else stays as the server sent it. */
315
+ function calendarJsonResponse(baseURL, response) {
316
+ if (!response || typeof response !== 'object')
317
+ return response;
318
+ const calendar = response;
319
+ if (!Array.isArray(calendar.data))
320
+ return response;
321
+ return {
322
+ ...calendar,
323
+ data: calendar.data.map((row) => row && typeof row === 'object'
324
+ ? calendarJsonRow(baseURL, row)
325
+ : row),
326
+ };
327
+ }
237
328
  /** Evidence about managed execution only; unmanaged work is outside this projection. */
238
329
  function taskExecution(t) {
239
330
  const actionability = t.actionability;
@@ -351,6 +442,7 @@ function buildListParams(flags, assigneeUserId) {
351
442
  ...(flag('explicit-only') ? { explicitOnly: true } : {}),
352
443
  ...(flag('subscribed') ? { subscribed: true } : {}),
353
444
  ...(flag('include-snoozed') ? { includeSnoozed: true } : {}),
445
+ ...(flag('all-workspaces') ? { workspaceScope: 'all' } : {}),
354
446
  ...(flag('external-key')
355
447
  ? { externalKey: flag('external-key') }
356
448
  : {}),
@@ -430,6 +522,7 @@ class TasksList extends base_command_1.BaseCommand {
430
522
  '<%= config.bin %> tasks list --dri me',
431
523
  '<%= config.bin %> tasks list --view <task-view-id>',
432
524
  '<%= config.bin %> tasks list --calendar 2026-08-01..2026-08-31 --json',
525
+ '<%= config.bin %> tasks list --calendar 2026-12-06T16:00:00Z..2026-12-07T15:59:59Z',
433
526
  '<%= config.bin %> tasks list --open --compact --json',
434
527
  '<%= config.bin %> tasks list --space <space-id> --fields id,ref,title,status --json',
435
528
  '<%= config.bin %> tasks list --open --ndjson | jq -r .ref',
@@ -543,6 +636,10 @@ class TasksList extends base_command_1.BaseCommand {
543
636
  'include-snoozed': core_1.Flags.boolean({
544
637
  description: 'Include tasks you have personally snoozed (hidden by default)',
545
638
  }),
639
+ 'all-workspaces': core_1.Flags.boolean({
640
+ description: 'List across every workspace you belong to. Without it, a list with no workspace selected ' +
641
+ 'reads your default workspace; a list across all of them is slow and may time out.',
642
+ }),
546
643
  'external-key': core_1.Flags.string({ description: 'Filter by external key' }),
547
644
  'external-key-prefix': core_1.Flags.string({
548
645
  description: 'Filter to tasks whose external key starts with this literal prefix, e.g. every task of one tree written under --external-key-prefix. Combine with --space; see also `tasks create-tree --verify`.',
@@ -581,7 +678,11 @@ class TasksList extends base_command_1.BaseCommand {
581
678
  description: 'Exit 2 when --status names a value that is not a built-in state id and matches nothing (instead of an empty success).',
582
679
  }),
583
680
  calendar: core_1.Flags.string({
584
- description: 'Expand the human task calendar for an inclusive range (YYYY-MM-DD..YYYY-MM-DD or ISO instants). Includes due dates and agent schedule occurrences.',
681
+ description: 'Expand the human task calendar for an inclusive range, <from>..<to>, at most 93 days. ' +
682
+ 'A date-only value (YYYY-MM-DD) is a whole UTC day, 00:00:00.000Z to 23:59:59.999Z, not your local day: ' +
683
+ 'for a local day pass ISO instants, e.g. 2026-12-06T16:00:00Z..2026-12-07T15:59:59Z. ' +
684
+ 'Rows print in UTC, and the human output names the resolved window. ' +
685
+ 'Includes due dates and agent schedule occurrences; a result cut short by a limit says which limit.',
585
686
  }),
586
687
  };
587
688
  async run() {
@@ -633,6 +734,16 @@ class TasksList extends base_command_1.BaseCommand {
633
734
  const filterApplied = agentSessionOpenDefault(flags);
634
735
  if (filterApplied)
635
736
  flags.open = true;
737
+ // Which workspace this list reads. A default is applied the way `--workspace` is: the
738
+ // request header follows OVERSKY_WORKSPACE_ID, set for this process only.
739
+ const workspaceScope = await resolveListWorkspace(flags, process.env, lookupDefaultWorkspace);
740
+ if (workspaceScope?.source === 'default') {
741
+ process.env.OVERSKY_WORKSPACE_ID = workspaceScope.workspaceId;
742
+ if (!flags.json && !flags.ndjson && workspaceScope.workspaceCount > 1) {
743
+ this.logToStderr(`Workspace: ${workspaceScope.workspaceName ? `"${workspaceScope.workspaceName}"` : workspaceScope.workspaceId} ` +
744
+ `(your default). Every workspace you belong to: ${this.config.bin} tasks list --all-workspaces`);
745
+ }
746
+ }
636
747
  let response;
637
748
  try {
638
749
  response = calendarRange
@@ -687,19 +798,24 @@ class TasksList extends base_command_1.BaseCommand {
687
798
  // `identifier` and `actionability` — under `--json` an agent scanning 43
688
799
  // keys could not find either (OSK-10692). Alias them to the names the
689
800
  // table prints so the machine view answers the same questions.
690
- const enriched = list && Array.isArray(list.data)
801
+ const enriched = list && Array.isArray(list.data) && !calendarRange
691
802
  ? {
692
803
  ...list,
693
- ...(!calendarRange ? { summary: taskPageSummary(list.data, Boolean(list.has_more ?? list.next_cursor)) } : {}),
804
+ summary: taskPageSummary(list.data, Boolean(list.has_more ?? list.next_cursor)),
694
805
  data: list.data.map((t) => ({
695
806
  ...t,
696
807
  ref: taskRef(t),
697
808
  nextAction: nextActionText(t),
698
- ...(!calendarRange ? { execution: taskExecution(t) } : {}),
809
+ execution: taskExecution(t),
699
810
  })),
700
811
  }
701
812
  : response;
702
- const withUrls = (0, web_url_1.withDataUrls)(this.cliConfig.baseURL, enriched, web_url_1.taskUrlFrom);
813
+ // A calendar row is an occurrence, not a Task: it gets the Task's id, ref and url and the
814
+ // occurrence key as `occurrenceId`, instead of the Task list's treatment, which rebuilt the
815
+ // url and the ref from the occurrence's own id (OSK-21586).
816
+ const withUrls = calendarRange
817
+ ? calendarJsonResponse(this.cliConfig.baseURL, response)
818
+ : (0, web_url_1.withDataUrls)(this.cliConfig.baseURL, enriched, web_url_1.taskUrlFrom);
703
819
  // The calendar response is occurrences, not tasks: leave its shape alone.
704
820
  const out = calendarRange
705
821
  ? withUrls
@@ -723,14 +839,27 @@ class TasksList extends base_command_1.BaseCommand {
723
839
  }
724
840
  : {}),
725
841
  });
726
- this.printList(out, Boolean(flags.ndjson));
842
+ // Say which workspace was read, as data, when the CLI chose it (or the caller asked for all):
843
+ // an agent reading JSON cannot see the stderr note.
844
+ this.printList(workspaceScope && out && typeof out === 'object' && !Array.isArray(out)
845
+ ? { ...out, workspace_scope: workspaceScope }
846
+ : out, Boolean(flags.ndjson));
727
847
  return;
728
848
  }
729
849
  if (calendarRange) {
730
850
  const calendar = response;
731
851
  const occurrences = calendar.data ?? [];
852
+ const notices = (0, task_calendar_1.calendarNoticeLines)(calendar);
853
+ // The resolved window goes to stderr: it is about the read, not a row, so a piped table
854
+ // stays a table. A date-only range is a whole UTC day (OSK-21529).
855
+ this.logToStderr((0, task_calendar_1.calendarWindowLine)(calendar));
732
856
  if (occurrences.length === 0) {
733
- this.log('No calendar occurrences.');
857
+ // "No calendar occurrences." is only true of a complete read (OSK-21382).
858
+ this.log(notices.length === 0
859
+ ? 'No calendar occurrences.'
860
+ : 'No calendar occurrences were listed, but the read was cut short.');
861
+ for (const line of notices)
862
+ this.log(line);
734
863
  return;
735
864
  }
736
865
  (0, format_1.renderTable)(occurrences.map((item) => ({
@@ -746,8 +875,11 @@ class TasksList extends base_command_1.BaseCommand {
746
875
  { key: 'title', header: 'TITLE', maxWidth: 50 },
747
876
  { key: 'status', header: 'STATUS' },
748
877
  ], (line) => this.log(line));
749
- if (calendar.truncated)
750
- this.log('\nNote: calendar occurrence expansion reached its safety cap.');
878
+ if (notices.length > 0) {
879
+ this.log('');
880
+ for (const line of notices)
881
+ this.log(line);
882
+ }
751
883
  return;
752
884
  }
753
885
  const r = response;
@@ -5,7 +5,7 @@ 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
  class TasksPauseSchedule extends base_command_1.BaseCommand {
8
- static description = 'Pause future recurring runs; current runs and already queued work are unchanged';
8
+ static description = 'Pause future recurring runs; current runs and already queued work are unchanged. A run that comes due while paused is skipped, not replayed, when you resume';
9
9
  static examples = [
10
10
  '<%= config.bin %> tasks pause-schedule <task-id>',
11
11
  '<%= config.bin %> tasks pause-schedule <task-id> --json',
@@ -3,6 +3,8 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  const data_provider_1 = require("@skrr-ai/data-provider");
5
5
  const base_command_1 = require("../../base-command");
6
+ const iso_instant_1 = require("../../lib/iso-instant");
7
+ const task_reminder_1 = require("../../lib/task-reminder");
6
8
  /** Set a one-time human reminder without changing the task's visibility or state. */
7
9
  class TasksRemind extends base_command_1.BaseCommand {
8
10
  static description = 'Set a one-time inbox reminder on a task. The task stays visible and unchanged.';
@@ -18,24 +20,27 @@ class TasksRemind extends base_command_1.BaseCommand {
18
20
  }),
19
21
  };
20
22
  static flags = {
21
- at: core_1.Flags.string({ description: 'Reminder time as an ISO-8601 UTC instant', required: true }),
23
+ at: core_1.Flags.string({
24
+ description: 'Reminder time as an ISO-8601 UTC instant ending in Z (e.g. 2026-08-25T16:00:00Z); a time with no Z or a numeric offset such as +08:00 is refused',
25
+ required: true,
26
+ }),
22
27
  json: core_1.Flags.boolean({ description: 'Output the TriggerDefinition as JSON' }),
23
28
  };
24
29
  async run() {
25
30
  this.requireAuth();
26
31
  const { args, flags } = await this.parse(TasksRemind);
32
+ // Before any lookup: a bad time costs no round trip and creates nothing. Never `new Date(flags.at)`,
33
+ // which reads a zoneless time in THIS machine's zone and rolls 2027-02-30 into March (OSK-21504/21506).
34
+ const parsed = (0, iso_instant_1.parseUtcInstant)(flags.at, '--at');
35
+ if (!parsed.ok)
36
+ this.error(parsed.message, { exit: 2 });
37
+ const at = parsed.date;
27
38
  const taskRef = await this.resolveEntityRef('task', args.taskId);
28
- const at = new Date(flags.at);
29
- if (Number.isNaN(at.getTime()) || !/^\d{4}-\d{2}-\d{2}T/.test(flags.at)) {
30
- this.error('--at must be an ISO-8601 instant, for example 2026-08-25T16:00:00Z.', {
31
- exit: 2,
32
- });
33
- }
34
39
  let task;
35
40
  let reminder;
36
41
  try {
37
42
  task = (await data_provider_1.dataService.getTask(taskRef, { enrich: false }));
38
- reminder = await data_provider_1.request.post('/api/triggers', {
43
+ reminder = (await data_provider_1.request.post('/api/triggers', {
39
44
  scope: {
40
45
  kind: 'entity_reminder',
41
46
  refs: {
@@ -47,8 +52,8 @@ class TasksRemind extends base_command_1.BaseCommand {
47
52
  kind: 'once',
48
53
  label: `Reminder: ${task.title || task.identifier || task.id}`.slice(0, 120),
49
54
  execution: { mode: 'notify_user', promptTemplate: '' },
50
- once: { runAt: at.toISOString() },
51
- });
55
+ once: { runAt: parsed.iso },
56
+ }));
52
57
  }
53
58
  catch (err) {
54
59
  this.handleApiError(err);
@@ -58,7 +63,14 @@ class TasksRemind extends base_command_1.BaseCommand {
58
63
  this.log(JSON.stringify(reminder, null, 2));
59
64
  return;
60
65
  }
61
- this.log(`Reminder set for ${task.title || task.identifier || task.id} at ${at.toLocaleString()}.`);
66
+ for (const line of (0, task_reminder_1.reminderReceiptLines)({
67
+ title: task.title || task.identifier || task.id,
68
+ trigger: reminder,
69
+ requested: at,
70
+ bin: this.config.bin,
71
+ })) {
72
+ this.log(line);
73
+ }
62
74
  }
63
75
  }
64
76
  exports.default = TasksRemind;
@@ -4,8 +4,9 @@ const core_1 = require("@oclif/core");
4
4
  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
+ const task_schedule_notes_1 = require("../../lib/task-schedule-notes");
7
8
  class TasksResumeSchedule extends base_command_1.BaseCommand {
8
- static description = 'Resume a paused recurring task — server re-computes the next fire from the cron pattern';
9
+ static description = 'Resume a paused recurring task. A run that came due while it was paused is skipped (not replayed); the next run comes from the cron pattern';
9
10
  static examples = [
10
11
  '<%= config.bin %> tasks resume-schedule <task-id>',
11
12
  '<%= config.bin %> tasks resume-schedule <task-id> --json',
@@ -34,6 +35,8 @@ class TasksResumeSchedule extends base_command_1.BaseCommand {
34
35
  const t = result;
35
36
  const ref = t.identifier ?? (t.taskNumber ? `#${t.taskNumber}` : '');
36
37
  this.log(`Resumed schedule on task ${t.id ?? t._id ?? taskRef}${ref ? ` (${ref})` : ''}`);
38
+ for (const line of (0, task_schedule_notes_1.resumeResultLines)(t))
39
+ this.log(line);
37
40
  }
38
41
  }
39
42
  exports.default = TasksResumeSchedule;
@@ -5,10 +5,12 @@ 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
  class TasksRetrySchedule extends base_command_1.BaseCommand {
8
- static description = 'Re-arm a stale/failed schedule. Optionally pin a new first-fire time';
8
+ static description = 'Re-arm a stale/failed schedule. After a failed or skipped start, pass --at with a new first-fire time: ' +
9
+ 'the failed fire cleared the scheduledFor, so there is nothing left to retry against. ' +
10
+ 'The Task must have an assigned agent, as the start will fail the same way otherwise.';
9
11
  static examples = [
10
- '<%= config.bin %> tasks retry-schedule <task-id>',
11
12
  '<%= config.bin %> tasks retry-schedule <task-id> --at 2026-06-01T15:30:00Z',
13
+ '<%= config.bin %> tasks retry-schedule <task-id>',
12
14
  ];
13
15
  static args = {
14
16
  id: core_1.Args.string({ description: 'Task ID', required: true, ignoreStdin: true }),
@@ -16,7 +18,8 @@ class TasksRetrySchedule extends base_command_1.BaseCommand {
16
18
  static flags = {
17
19
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
18
20
  at: core_1.Flags.string({
19
- description: 'Optional new first-fire time as ISO-8601 UTC. Omit to retry against the current scheduledFor.',
21
+ description: 'New first-fire time as ISO-8601 UTC. Required after a failed or skipped start (the failed fire cleared scheduledFor). ' +
22
+ 'Omit it only to re-sync a schedule whose scheduledFor is still in the future.',
20
23
  }),
21
24
  };
22
25
  async run() {