@skrr-ai/cli 0.1.23 → 0.1.24

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 (67) hide show
  1. package/bin/run.js +54 -1
  2. package/dist/base-command.js +74 -1
  3. package/dist/commands/harnesses/leases/list.js +1 -1
  4. package/dist/commands/harnesses/products.js +1 -0
  5. package/dist/commands/machines/dedicated/create.d.ts +18 -0
  6. package/dist/commands/machines/dedicated/create.js +90 -0
  7. package/dist/commands/machines/dedicated/destroy.d.ts +14 -0
  8. package/dist/commands/machines/dedicated/destroy.js +56 -0
  9. package/dist/commands/machines/dedicated/grow.d.ts +13 -0
  10. package/dist/commands/machines/dedicated/grow.js +46 -0
  11. package/dist/commands/machines/dedicated/health-check.d.ts +12 -0
  12. package/dist/commands/machines/dedicated/health-check.js +42 -0
  13. package/dist/commands/machines/dedicated/incident-close.d.ts +12 -0
  14. package/dist/commands/machines/dedicated/incident-close.js +42 -0
  15. package/dist/commands/machines/dedicated/index.d.ts +6 -0
  16. package/dist/commands/machines/dedicated/index.js +39 -0
  17. package/dist/commands/machines/dedicated/list.d.ts +11 -0
  18. package/dist/commands/machines/dedicated/list.js +58 -0
  19. package/dist/commands/machines/dedicated/restart.d.ts +12 -0
  20. package/dist/commands/machines/dedicated/restart.js +42 -0
  21. package/dist/commands/machines/dedicated/show.d.ts +12 -0
  22. package/dist/commands/machines/dedicated/show.js +58 -0
  23. package/dist/commands/machines/dedicated/snapshot.d.ts +12 -0
  24. package/dist/commands/machines/dedicated/snapshot.js +42 -0
  25. package/dist/commands/machines/dedicated/start.d.ts +12 -0
  26. package/dist/commands/machines/dedicated/start.js +42 -0
  27. package/dist/commands/machines/dedicated/stop.d.ts +12 -0
  28. package/dist/commands/machines/dedicated/stop.js +42 -0
  29. package/dist/commands/machines/hosted/index.js +4 -1
  30. package/dist/commands/spaces/update.js +16 -12
  31. package/dist/commands/spaces/work-sync/reconcile.d.ts +38 -0
  32. package/dist/commands/spaces/work-sync/reconcile.js +90 -0
  33. package/dist/commands/spaces/work-sync/telemetry.d.ts +29 -0
  34. package/dist/commands/spaces/work-sync/telemetry.js +96 -0
  35. package/dist/commands/tasks/activity.js +5 -1
  36. package/dist/commands/tasks/complete.d.ts +5 -1
  37. package/dist/commands/tasks/complete.js +17 -3
  38. package/dist/commands/tasks/create.d.ts +1 -0
  39. package/dist/commands/tasks/create.js +7 -0
  40. package/dist/commands/tasks/deliverable/add.js +22 -4
  41. package/dist/commands/tasks/deliverable/list.js +23 -2
  42. package/dist/commands/tasks/events/append.js +31 -3
  43. package/dist/commands/tasks/expectations/assess.d.ts +1 -1
  44. package/dist/commands/tasks/expectations/assess.js +33 -7
  45. package/dist/commands/tasks/expectations.js +110 -10
  46. package/dist/commands/tasks/output.d.ts +0 -1
  47. package/dist/commands/tasks/output.js +21 -1
  48. package/dist/commands/tasks/report.js +3 -1
  49. package/dist/commands/tasks/result/show.js +20 -2
  50. package/dist/commands/tasks/resume/save.js +11 -1
  51. package/dist/commands/tasks/self-schedule.js +7 -4
  52. package/dist/commands/tasks/show.js +19 -0
  53. package/dist/commands/tasks/timeline.js +9 -1
  54. package/dist/commands/tasks/updates/add.js +23 -2
  55. package/dist/lib/dedicated-machines.d.ts +66 -0
  56. package/dist/lib/dedicated-machines.js +161 -0
  57. package/dist/lib/harnesses.d.ts +5 -0
  58. package/dist/lib/harnesses.js +5 -2
  59. package/dist/lib/task-legacy.d.ts +24 -0
  60. package/dist/lib/task-legacy.js +48 -0
  61. package/dist/lib/tasks.d.ts +32 -0
  62. package/dist/lib/tasks.js +47 -1
  63. package/dist/lib/work-sync.d.ts +70 -0
  64. package/dist/lib/work-sync.js +29 -0
  65. package/dist/node_modules/@skrr-ai/data-provider/index.js +2079 -2055
  66. package/oclif.manifest.json +12229 -11213
  67. package/package.json +1 -1
@@ -0,0 +1,58 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
7
+ class DedicatedMachinesList extends base_command_1.BaseCommand {
8
+ static description = 'List Dedicated Runtime leases';
9
+ static examples = [
10
+ '<%= config.bin %> machines dedicated list',
11
+ '<%= config.bin %> machines dedicated list --json',
12
+ ];
13
+ static flags = {
14
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
15
+ state: core_1.Flags.string({ description: 'Filter by lease state' }),
16
+ limit: core_1.Flags.integer({ description: 'Max leases to return', default: 50 }),
17
+ };
18
+ async run() {
19
+ this.requireAuth();
20
+ const { flags } = await this.parse(DedicatedMachinesList);
21
+ let response;
22
+ try {
23
+ response = await (0, dedicated_machines_1.listDedicatedRuntimes)({
24
+ limit: flags.limit,
25
+ state: flags.state,
26
+ });
27
+ }
28
+ catch (err) {
29
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
30
+ if (message)
31
+ this.error(message, { exit: 1 });
32
+ this.handleApiError(err);
33
+ }
34
+ if (flags.json) {
35
+ this.log(JSON.stringify(response, null, 2));
36
+ return;
37
+ }
38
+ const leases = response?.leases ?? [];
39
+ if (leases.length === 0) {
40
+ this.log('No Dedicated Runtime leases.');
41
+ return;
42
+ }
43
+ (0, format_1.renderTable)(leases.map((lease) => ({
44
+ id: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseId)(lease)),
45
+ status: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseState)(lease)),
46
+ name: (0, format_1.formatCell)((0, dedicated_machines_1.dedicatedLeaseDisplayName)(lease)),
47
+ retention: (0, format_1.formatCell)(lease.storage?.retentionClass || '-'),
48
+ backup: (0, format_1.formatCell)(lease.storage?.lastBackupAt || '-'),
49
+ })), [
50
+ { key: 'id', header: 'ID' },
51
+ { key: 'status', header: 'STATUS' },
52
+ { key: 'name', header: 'NAME', maxWidth: 28 },
53
+ { key: 'retention', header: 'RETENTION', maxWidth: 14 },
54
+ { key: 'backup', header: 'LAST BACKUP', maxWidth: 24 },
55
+ ], (line) => this.log(line));
56
+ }
57
+ }
58
+ exports.default = DedicatedMachinesList;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class DedicatedMachinesRestart extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,42 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ class DedicatedMachinesRestart extends base_command_1.BaseCommand {
7
+ static description = 'Restart a Dedicated Runtime lease';
8
+ static examples = [
9
+ '<%= config.bin %> machines dedicated restart <lease-id>',
10
+ '<%= config.bin %> machines dedicated restart <lease-id> --json',
11
+ ];
12
+ static args = {
13
+ lease: core_1.Args.string({
14
+ description: 'Dedicated Runtime lease id',
15
+ required: true,
16
+ ignoreStdin: true,
17
+ }),
18
+ };
19
+ static flags = {
20
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
21
+ };
22
+ async run() {
23
+ this.requireAuth();
24
+ const { args, flags } = await this.parse(DedicatedMachinesRestart);
25
+ let response;
26
+ try {
27
+ response = await (0, dedicated_machines_1.performDedicatedLeaseAction)(args.lease, 'restart');
28
+ }
29
+ catch (err) {
30
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
31
+ if (message)
32
+ this.error(message, { exit: 1 });
33
+ this.handleApiError(err);
34
+ }
35
+ if (flags.json) {
36
+ this.log(JSON.stringify(response, null, 2));
37
+ return;
38
+ }
39
+ this.log(`Dedicated Runtime ${(0, dedicated_machines_1.dedicatedLeaseId)(response?.lease)} ${(0, dedicated_machines_1.dedicatedLeaseState)(response?.lease)}.`);
40
+ }
41
+ }
42
+ exports.default = DedicatedMachinesRestart;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class DedicatedMachinesShow extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,58 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ class DedicatedMachinesShow extends base_command_1.BaseCommand {
7
+ static description = 'Show one Dedicated Runtime lease';
8
+ static examples = [
9
+ '<%= config.bin %> machines dedicated show <lease-id>',
10
+ '<%= config.bin %> machines dedicated show <lease-id> --json',
11
+ ];
12
+ static args = {
13
+ lease: core_1.Args.string({
14
+ ignoreStdin: true,
15
+ description: 'Dedicated Runtime lease id',
16
+ required: true,
17
+ }),
18
+ };
19
+ static flags = { json: core_1.Flags.boolean({ description: 'Output as JSON' }) };
20
+ async run() {
21
+ this.requireAuth();
22
+ const { args, flags } = await this.parse(DedicatedMachinesShow);
23
+ let response;
24
+ try {
25
+ response = await (0, dedicated_machines_1.getDedicatedRuntime)(args.lease);
26
+ }
27
+ catch (err) {
28
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
29
+ if (message)
30
+ this.error(message, { exit: 1 });
31
+ this.handleApiError(err);
32
+ }
33
+ const lease = response?.lease;
34
+ if (!lease) {
35
+ this.error('Dedicated Runtime lease not found.', { exit: 1 });
36
+ }
37
+ if (flags.json) {
38
+ this.log(JSON.stringify(response, null, 2));
39
+ return;
40
+ }
41
+ this.log((0, dedicated_machines_1.dedicatedLeaseDisplayName)(lease));
42
+ this.log(` id ${(0, dedicated_machines_1.dedicatedLeaseId)(lease)}`);
43
+ this.log(` status ${(0, dedicated_machines_1.dedicatedLeaseState)(lease)}`);
44
+ if (lease.storage?.retentionClass) {
45
+ this.log(` retention ${lease.storage.retentionClass}`);
46
+ }
47
+ if (lease.storage?.backupProfile) {
48
+ this.log(` backup ${lease.storage.backupProfile}`);
49
+ }
50
+ if (lease.storage?.lastBackupAt) {
51
+ this.log(` last backup ${lease.storage.lastBackupAt}`);
52
+ }
53
+ if (lease.billing?.monthlySpendLimitCents != null) {
54
+ this.log(` monthly cap ${lease.billing.monthlySpendLimitCents} cents`);
55
+ }
56
+ }
57
+ }
58
+ exports.default = DedicatedMachinesShow;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class DedicatedMachinesSnapshot extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,42 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ class DedicatedMachinesSnapshot extends base_command_1.BaseCommand {
7
+ static description = 'Snapshot a Dedicated Runtime lease workspace';
8
+ static examples = [
9
+ '<%= config.bin %> machines dedicated snapshot <lease-id>',
10
+ '<%= config.bin %> machines dedicated snapshot <lease-id> --json',
11
+ ];
12
+ static args = {
13
+ lease: core_1.Args.string({
14
+ description: 'Dedicated Runtime lease id',
15
+ required: true,
16
+ ignoreStdin: true,
17
+ }),
18
+ };
19
+ static flags = {
20
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
21
+ };
22
+ async run() {
23
+ this.requireAuth();
24
+ const { args, flags } = await this.parse(DedicatedMachinesSnapshot);
25
+ let response;
26
+ try {
27
+ response = await (0, dedicated_machines_1.performDedicatedLeaseAction)(args.lease, 'snapshot');
28
+ }
29
+ catch (err) {
30
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
31
+ if (message)
32
+ this.error(message, { exit: 1 });
33
+ this.handleApiError(err);
34
+ }
35
+ if (flags.json) {
36
+ this.log(JSON.stringify(response, null, 2));
37
+ return;
38
+ }
39
+ this.log(`Dedicated Runtime ${(0, dedicated_machines_1.dedicatedLeaseId)(response?.lease)} ${(0, dedicated_machines_1.dedicatedLeaseState)(response?.lease)}.`);
40
+ }
41
+ }
42
+ exports.default = DedicatedMachinesSnapshot;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class DedicatedMachinesStart extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,42 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ class DedicatedMachinesStart extends base_command_1.BaseCommand {
7
+ static description = 'Start a Dedicated Runtime lease';
8
+ static examples = [
9
+ '<%= config.bin %> machines dedicated start <lease-id>',
10
+ '<%= config.bin %> machines dedicated start <lease-id> --json',
11
+ ];
12
+ static args = {
13
+ lease: core_1.Args.string({
14
+ description: 'Dedicated Runtime lease id',
15
+ required: true,
16
+ ignoreStdin: true,
17
+ }),
18
+ };
19
+ static flags = {
20
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
21
+ };
22
+ async run() {
23
+ this.requireAuth();
24
+ const { args, flags } = await this.parse(DedicatedMachinesStart);
25
+ let response;
26
+ try {
27
+ response = await (0, dedicated_machines_1.performDedicatedLeaseAction)(args.lease, 'start');
28
+ }
29
+ catch (err) {
30
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
31
+ if (message)
32
+ this.error(message, { exit: 1 });
33
+ this.handleApiError(err);
34
+ }
35
+ if (flags.json) {
36
+ this.log(JSON.stringify(response, null, 2));
37
+ return;
38
+ }
39
+ this.log(`Dedicated Runtime ${(0, dedicated_machines_1.dedicatedLeaseId)(response?.lease)} ${(0, dedicated_machines_1.dedicatedLeaseState)(response?.lease)}.`);
40
+ }
41
+ }
42
+ exports.default = DedicatedMachinesStart;
@@ -0,0 +1,12 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ export default class DedicatedMachinesStop extends BaseCommand {
3
+ static description: string;
4
+ static examples: string[];
5
+ static args: {
6
+ lease: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
7
+ };
8
+ static flags: {
9
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ run(): Promise<void>;
12
+ }
@@ -0,0 +1,42 @@
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 dedicated_machines_1 = require("../../../lib/dedicated-machines");
6
+ class DedicatedMachinesStop extends base_command_1.BaseCommand {
7
+ static description = 'Stop a Dedicated Runtime lease';
8
+ static examples = [
9
+ '<%= config.bin %> machines dedicated stop <lease-id>',
10
+ '<%= config.bin %> machines dedicated stop <lease-id> --json',
11
+ ];
12
+ static args = {
13
+ lease: core_1.Args.string({
14
+ description: 'Dedicated Runtime lease id',
15
+ required: true,
16
+ ignoreStdin: true,
17
+ }),
18
+ };
19
+ static flags = {
20
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
21
+ };
22
+ async run() {
23
+ this.requireAuth();
24
+ const { args, flags } = await this.parse(DedicatedMachinesStop);
25
+ let response;
26
+ try {
27
+ response = await (0, dedicated_machines_1.performDedicatedLeaseAction)(args.lease, 'stop');
28
+ }
29
+ catch (err) {
30
+ const message = (0, dedicated_machines_1.formatDedicatedRuntimeApiError)(err);
31
+ if (message)
32
+ this.error(message, { exit: 1 });
33
+ this.handleApiError(err);
34
+ }
35
+ if (flags.json) {
36
+ this.log(JSON.stringify(response, null, 2));
37
+ return;
38
+ }
39
+ this.log(`Dedicated Runtime ${(0, dedicated_machines_1.dedicatedLeaseId)(response?.lease)} ${(0, dedicated_machines_1.dedicatedLeaseState)(response?.lease)}.`);
40
+ }
41
+ }
42
+ exports.default = DedicatedMachinesStop;
@@ -2,7 +2,10 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
4
  class Machines extends core_1.Command {
5
- static description = 'Manage skrr Hosted Machine workspaces';
5
+ // Paired with `machines dedicated`, and the pairing is the point: this one
6
+ // resumes, that one restarts. Stated here so the choice can be made without
7
+ // opening either topic.
8
+ static description = 'A managed workspace that resumes — files, memory and running processes survive pause';
6
9
  static examples = [
7
10
  '<%= config.bin %> machines hosted start --name scratch --wait',
8
11
  '<%= config.bin %> machines hosted list',
@@ -76,7 +76,9 @@ class SpacesUpdate extends base_command_1.BaseCommand {
76
76
  description: tasks_1.EXPECT_FLAG_HELP +
77
77
  ' This is the space DEFAULT, and it is where the contract actually gets set: nobody ' +
78
78
  'declares one per task, but a space default is configured once and then holds. A ' +
79
- 'per-task --expect wins. Merged into deliveryPolicy without clobbering its other fields.',
79
+ 'per-task --expect wins. Merged into deliveryPolicy without clobbering its other ' +
80
+ 'fields. Replaces the whole list; pass "clear" to remove the default so new tasks ' +
81
+ 'inherit nothing.',
80
82
  }),
81
83
  'delivery-mode': core_1.Flags.string({
82
84
  options: ['none', 'branch', 'pr', 'pr_automerge', 'direct'],
@@ -132,21 +134,23 @@ class SpacesUpdate extends base_command_1.BaseCommand {
132
134
  // Parse before any write: a bad spec must fail here, not reach the server
133
135
  // as a silently-dropped field. A space default nobody is enforcing is worse
134
136
  // than none, because every task under it then shows a contract that isn't real.
135
- let defaultExpectations;
136
- if (flags['default-expect'] !== undefined && flags['default-expect'].length > 0) {
137
- const parsed = (0, tasks_1.parseExpectations)(flags['default-expect']);
138
- if (!parsed.ok) {
139
- this.error(parsed.error, { exit: 1 });
140
- }
141
- else {
142
- defaultExpectations = parsed.value;
143
- }
137
+ const resolvedDefaults = (0, tasks_1.resolveDefaultExpectations)(flags['default-expect']);
138
+ if (!resolvedDefaults.ok) {
139
+ this.error(resolvedDefaults.error, { exit: 1 });
144
140
  }
141
+ const defaultExpectations = resolvedDefaults.ok ? resolvedDefaults.value : undefined;
145
142
  const isolation = flags['worktree-isolation'];
146
143
  const deliveryMode = flags['delivery-mode'];
147
144
  const integrationBranch = flags['integration-branch'];
148
145
  const autoMergeTier = flags['auto-merge-tier'];
149
- if (isolation || defaultExpectations || deliveryMode || integrationBranch || autoMergeTier) {
146
+ // `defaultExpectations === null` is a real instruction (remove the default), so
147
+ // it must reach the policy merge — a truthiness test dropped it silently and
148
+ // made `clear` a no-op that printed success.
149
+ if (isolation ||
150
+ defaultExpectations !== undefined ||
151
+ deliveryMode ||
152
+ integrationBranch ||
153
+ autoMergeTier) {
150
154
  let basePolicy = {};
151
155
  if (payload.deliveryPolicy && typeof payload.deliveryPolicy === 'object') {
152
156
  basePolicy = { ...payload.deliveryPolicy };
@@ -169,7 +173,7 @@ class SpacesUpdate extends base_command_1.BaseCommand {
169
173
  ...(deliveryMode ? { mode: deliveryMode } : {}),
170
174
  ...(integrationBranch ? { integrationBranch } : {}),
171
175
  ...(autoMergeTier ? { autoMergeTrustTier: autoMergeTier } : {}),
172
- ...(defaultExpectations ? { defaultExpectations } : {}),
176
+ ...(defaultExpectations !== undefined ? { defaultExpectations } : {}),
173
177
  };
174
178
  }
175
179
  // Goal links are NOT part of the PATCH body — the server rejects a PATCH
@@ -0,0 +1,38 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * `skrr spaces work-sync reconcile <space>` — answer the divergence question
4
+ * without a browser, and repair what it finds.
5
+ *
6
+ * Root `CLAUDE.md` records that two Work Sync exit criteria were knowingly
7
+ * overridden, and that "an unexplained board divergence is a live hypothesis, not
8
+ * an impossibility, and is the first thing to suspect." `work-sync telemetry`
9
+ * reports whether the browser's convergence detector has ever RUN. This answers
10
+ * the underlying question directly: it compares every published projection entry
11
+ * against authoritative task truth server-side, and `upserts` is the count that
12
+ * disagreed.
13
+ *
14
+ * That matters because the detector only runs where a board is open. A Space
15
+ * nobody has visited since the detector shipped reports zero comparisons forever,
16
+ * which is indistinguishable from health in the telemetry and is not health. This
17
+ * command needs no board, no browser, and no visitor.
18
+ *
19
+ * IT WRITES, and the help says so plainly rather than burying it. The endpoint is
20
+ * editor-gated because it republishes projection rows that clients will see. It
21
+ * never mutates task truth — the projection is derived, and a republished card is
22
+ * a corrected copy of a Task nobody touched — and on a healthy scope it finds
23
+ * nothing and changes nothing, so the verification and the repair are the same
24
+ * call. There is deliberately no dry-run flag to add: a read-only variant would be
25
+ * a second implementation of the comparison, which is how the two answers start
26
+ * disagreeing.
27
+ */
28
+ export default class SpacesWorkSyncReconcile extends BaseCommand {
29
+ static description: string;
30
+ static examples: string[];
31
+ static args: {
32
+ space: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
33
+ };
34
+ static flags: {
35
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
36
+ };
37
+ run(): Promise<void>;
38
+ }
@@ -0,0 +1,90 @@
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 work_sync_1 = require("../../../lib/work-sync");
6
+ /**
7
+ * `skrr spaces work-sync reconcile <space>` — answer the divergence question
8
+ * without a browser, and repair what it finds.
9
+ *
10
+ * Root `CLAUDE.md` records that two Work Sync exit criteria were knowingly
11
+ * overridden, and that "an unexplained board divergence is a live hypothesis, not
12
+ * an impossibility, and is the first thing to suspect." `work-sync telemetry`
13
+ * reports whether the browser's convergence detector has ever RUN. This answers
14
+ * the underlying question directly: it compares every published projection entry
15
+ * against authoritative task truth server-side, and `upserts` is the count that
16
+ * disagreed.
17
+ *
18
+ * That matters because the detector only runs where a board is open. A Space
19
+ * nobody has visited since the detector shipped reports zero comparisons forever,
20
+ * which is indistinguishable from health in the telemetry and is not health. This
21
+ * command needs no board, no browser, and no visitor.
22
+ *
23
+ * IT WRITES, and the help says so plainly rather than burying it. The endpoint is
24
+ * editor-gated because it republishes projection rows that clients will see. It
25
+ * never mutates task truth — the projection is derived, and a republished card is
26
+ * a corrected copy of a Task nobody touched — and on a healthy scope it finds
27
+ * nothing and changes nothing, so the verification and the repair are the same
28
+ * call. There is deliberately no dry-run flag to add: a read-only variant would be
29
+ * a second implementation of the comparison, which is how the two answers start
30
+ * disagreeing.
31
+ */
32
+ class SpacesWorkSyncReconcile extends base_command_1.BaseCommand {
33
+ static description = 'Compare a Space Work Sync projection against task truth and repair what diverged (writes)';
34
+ static examples = [
35
+ '<%= config.bin %> spaces work-sync reconcile <space-id>',
36
+ '<%= config.bin %> spaces work-sync reconcile <space-id> --json',
37
+ ];
38
+ static args = {
39
+ space: core_1.Args.string({ description: 'Space ID', required: true, ignoreStdin: true }),
40
+ };
41
+ static flags = {
42
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
43
+ };
44
+ async run() {
45
+ this.requireAuth();
46
+ const { args, flags } = await this.parse(SpacesWorkSyncReconcile);
47
+ let result;
48
+ try {
49
+ result = await (0, work_sync_1.reconcileWorkSyncScope)(args.space);
50
+ }
51
+ catch (err) {
52
+ this.handleApiError(err);
53
+ return;
54
+ }
55
+ if (flags.json) {
56
+ this.log(JSON.stringify(result, null, 2));
57
+ return;
58
+ }
59
+ const diverged = (result.upserts || 0) +
60
+ (result.removes || 0) +
61
+ (result.summaryUpserts || 0) +
62
+ (result.summaryRemoves || 0);
63
+ // Lead with the verdict, because it is the whole reason to run this. A table
64
+ // of four zeros under no summary line makes the reader do the addition.
65
+ if (diverged === 0) {
66
+ this.log('No divergence: every published projection entry matches task truth.');
67
+ }
68
+ else {
69
+ this.log(`Divergence found and repaired: ${diverged} ${diverged === 1 ? 'entry' : 'entries'}.`);
70
+ this.log('');
71
+ this.log(` stale task cards republished ${result.upserts || 0}`);
72
+ this.log(` task cards removed ${result.removes || 0}`);
73
+ this.log(` stale summaries republished ${result.summaryUpserts || 0}`);
74
+ this.log(` summaries removed ${result.summaryRemoves || 0}`);
75
+ }
76
+ if (result.backlog > 0) {
77
+ this.log('');
78
+ this.log(`${result.backlog} repair ${result.backlog === 1 ? 'row was' : 'rows were'} already queued ` +
79
+ 'by earlier publish failures. A successful run settles them; one that failed again is kept.');
80
+ }
81
+ if (result.truncated) {
82
+ this.log('');
83
+ // Never let a partial pass read as a clean one. The scope read is capped at
84
+ // 1000 rows, so a larger Space was only checked as far as the cap.
85
+ this.log('PARTIAL: this Space is larger than the 1000-row reconciliation cap, so the ' +
86
+ 'verdict above covers a prefix of it and not the whole Space.');
87
+ }
88
+ }
89
+ }
90
+ exports.default = SpacesWorkSyncReconcile;
@@ -0,0 +1,29 @@
1
+ import { BaseCommand } from '../../../base-command';
2
+ /**
3
+ * `skrr spaces work-sync telemetry <space>` — Work Sync's own exit criteria.
4
+ *
5
+ * Work Sync v1 is ON at 100% (owner decision, 2026-09-08) with two criteria
6
+ * knowingly overridden rather than satisfied. The server has computed the verdict
7
+ * the whole time, and nothing outside the API could read it — so checking the
8
+ * criteria meant querying the production database by hand, which is why nobody
9
+ * did. An exit criterion that can only be read by hand is one that does not get
10
+ * read.
11
+ *
12
+ * The verdict is printed as the server decided it. This command does not
13
+ * re-derive it: the same rule that keeps `tasks expectations` reading the server's
14
+ * judgement rather than computing its own applies here, and for the same reason —
15
+ * a CLI that reached a different conclusion from the service would leave the
16
+ * reader unable to tell which was lying.
17
+ */
18
+ export default class SpacesWorkSyncTelemetry extends BaseCommand {
19
+ static description: string;
20
+ static examples: string[];
21
+ static args: {
22
+ space: import("@oclif/core/lib/interfaces").Arg<string, Record<string, unknown>>;
23
+ };
24
+ static flags: {
25
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
26
+ limit: import("@oclif/core/lib/interfaces").OptionFlag<number | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
27
+ };
28
+ run(): Promise<void>;
29
+ }