@skrr-ai/cli 0.1.116 → 0.1.117

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 (63) hide show
  1. package/dist/base-command.d.ts +21 -0
  2. package/dist/base-command.js +95 -8
  3. package/dist/commands/actions/install.d.ts +1 -0
  4. package/dist/commands/actions/install.js +46 -1
  5. package/dist/commands/commitments/create.d.ts +21 -2
  6. package/dist/commands/commitments/create.js +74 -4
  7. package/dist/commands/convos/recovery.js +12 -4
  8. package/dist/commands/followups/plans/list.js +1 -1
  9. package/dist/commands/guide.d.ts +21 -0
  10. package/dist/commands/guide.js +46 -0
  11. package/dist/commands/instructions/guide.js +34 -52
  12. package/dist/commands/memory/maintain.js +1 -1
  13. package/dist/commands/skills/install.d.ts +1 -0
  14. package/dist/commands/skills/install.js +41 -2
  15. package/dist/commands/slack/send-as-agent.js +3 -3
  16. package/dist/commands/spaces/create.js +8 -2
  17. package/dist/commands/spaces/guide.d.ts +21 -0
  18. package/dist/commands/spaces/guide.js +40 -0
  19. package/dist/commands/spaces/index.d.ts +0 -1
  20. package/dist/commands/spaces/index.js +1 -6
  21. package/dist/commands/tasks/checkout.js +2 -3
  22. package/dist/commands/tasks/comments/add.js +3 -5
  23. package/dist/commands/tasks/complete.js +2 -3
  24. package/dist/commands/tasks/deliverable/add.d.ts +1 -1
  25. package/dist/commands/tasks/deliverable/add.js +4 -7
  26. package/dist/commands/tasks/expectations/assess.js +2 -2
  27. package/dist/commands/tasks/guide.d.ts +8 -0
  28. package/dist/commands/tasks/guide.js +23 -10
  29. package/dist/commands/tasks/report.js +9 -5
  30. package/dist/commands/tasks/resource/add.js +2 -3
  31. package/dist/commands/tasks/result/submit.js +2 -3
  32. package/dist/commands/tasks/resume/save.js +2 -3
  33. package/dist/commands/tasks/review.js +2 -3
  34. package/dist/commands/tasks/triage.js +2 -1
  35. package/dist/commands/tasks/updates/add.js +2 -3
  36. package/dist/generated/agent-guides.json +8290 -0
  37. package/dist/generated/managed-instruction-guides.json +1 -1
  38. package/dist/help.js +4 -3
  39. package/dist/hooks/command-not-found.js +1 -1
  40. package/dist/lib/agent-guide-reference-gate.d.ts +28 -0
  41. package/dist/lib/agent-guide-reference-gate.js +186 -0
  42. package/dist/lib/agent-guides.d.ts +51 -0
  43. package/dist/lib/agent-guides.js +170 -0
  44. package/dist/lib/command-miss.d.ts +13 -2
  45. package/dist/lib/command-miss.js +134 -8
  46. package/dist/lib/guide-pointer.d.ts +15 -0
  47. package/dist/lib/guide-pointer.js +51 -0
  48. package/dist/lib/lookbackPlans.d.ts +3 -1
  49. package/dist/lib/lookbackPlans.js +9 -0
  50. package/dist/lib/outbox.d.ts +0 -14
  51. package/dist/lib/outbox.js +0 -21
  52. package/dist/lib/roles.d.ts +1 -1
  53. package/dist/lib/task-batch.d.ts +10 -7
  54. package/dist/lib/task-batch.js +5 -20
  55. package/dist/lib/task-operations.d.ts +8 -20
  56. package/dist/lib/task-operations.js +9 -94
  57. package/dist/lib/task-preflight.js +70 -33
  58. package/dist/lib/usage-discovery.d.ts +4 -0
  59. package/dist/lib/usage-discovery.js +5 -0
  60. package/dist/lib/voice-call-intent.js +3 -1
  61. package/dist/node_modules/@skrr-ai/data-provider/index.js +6593 -6383
  62. package/oclif.manifest.json +228 -13
  63. package/package.json +4 -2
@@ -4,19 +4,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  const core_1 = require("@oclif/core");
7
+ const data_provider_1 = require("@skrr-ai/data-provider");
7
8
  const node_crypto_1 = require("node:crypto");
8
9
  const base_command_1 = require("../../base-command");
9
10
  const managed_instruction_guides_json_1 = __importDefault(require("../../generated/managed-instruction-guides.json"));
10
- const api_fetch_1 = require("../../lib/api-fetch");
11
- const auth_core_init_1 = require("../../lib/auth-core-init");
12
- const credential_resolver_1 = require("../../lib/credential-resolver");
13
- const harness_context_1 = require("../../lib/harness-context");
14
- const keychain_1 = require("../../lib/keychain");
11
+ const agent_guides_1 = require("../../lib/agent-guides");
12
+ // The installed v20 instructions still name this command; the guide body they
13
+ // ask for now lives in the tasks card. The card also dropped the membership
14
+ // and scheduling rules, so one trailing line keeps an agent pointed at where
15
+ // those went.
16
+ const TASKS_GUIDE_POINTER = 'Membership and scheduling now live in `skrr guide spaces access` for Space membership ' +
17
+ 'and `skrr guide tasks followups` for follow-ups and scheduling.';
15
18
  // Documentation stays available when a daemon-issued Agent credential is
16
19
  // unavailable. Do not run BaseCommand's mandatory credential initialization.
17
20
  class InstructionsGuide extends core_1.Command {
18
21
  static baseFlags = base_command_1.BaseCommand.baseFlags;
19
- static description = 'Read the detailed task-management guide on demand';
22
+ static description = 'Kept for installed instructions — prints the tasks guide. `skrr guide` reads every guide.';
20
23
  static args = {
21
24
  name: core_1.Args.string({
22
25
  description: 'Guide to read',
@@ -37,55 +40,34 @@ class InstructionsGuide extends core_1.Command {
37
40
  ];
38
41
  async run() {
39
42
  const { flags } = await this.parse(InstructionsGuide);
40
- let guide = managed_instruction_guides_json_1.default['oversky-task-management'];
41
- let source = 'bundled';
42
- let reason = flags.offline ? 'offline-requested' : 'no-session';
43
- if (!flags.offline) {
44
- try {
45
- (0, keychain_1.setActiveProfile)(flags.profile ?? process.env.OVERSKY_PROFILE ?? '');
46
- const autonomous = (0, credential_resolver_1.resolveTrustedAutonomousCredential)();
47
- if (!autonomous && !flags.bare && !(0, harness_context_1.isHarnessCliContext)())
48
- await (0, auth_core_init_1.prefetchCliAuth)();
49
- const credential = autonomous ??
50
- (await (0, credential_resolver_1.resolveCredential)({
51
- flagToken: flags.token,
52
- bareMode: flags.bare,
53
- allowUnreadableStoredCredential: true,
54
- }));
55
- if (credential.token) {
56
- reason = 'server-unavailable';
57
- const response = await (0, api_fetch_1.apiFetch)('/api/instructions/presets/oversky-task-management/guide', { credential, signal: AbortSignal.timeout(3_000) });
58
- if ((0, node_crypto_1.createHash)('sha256').update(response.body).digest('hex') !== response.hash) {
59
- // An integrity failure, not a network failure: the server
60
- // answered, but the body it sent does not match its own hash.
61
- // Give it its own reason before the catch below can flatten it
62
- // into 'server-unavailable', which describes the opposite world.
63
- reason = 'server-hash-mismatch';
64
- throw new Error('The server guide hash does not match its body.');
65
- }
66
- guide = response;
67
- source = 'server';
68
- reason = undefined;
69
- }
70
- }
71
- catch {
72
- // A missing/unreadable auth envelope or delegated daemon cannot make
73
- // a read-only local guide unavailable, and must never emit auth JSON.
74
- reason ??= 'server-unavailable';
75
- }
76
- }
77
- if (flags.json)
43
+ const read = await (0, agent_guides_1.readAgentGuide)({ id: 'tasks' }, flags);
44
+ if (!read.found)
45
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
46
+ const body = (0, data_provider_1.renderAgentGuideText)(read.response);
47
+ if (flags.json) {
48
+ // The ManagedInstructionGuide shape is the contract older callers read:
49
+ // keep its keys, but `body` is the tasks card text and `hash` its sha256,
50
+ // so a consumer that verifies hash against body still succeeds.
51
+ const preset = managed_instruction_guides_json_1.default['oversky-task-management'];
78
52
  this.log(JSON.stringify({
79
- ...guide,
80
- provenance: { source, fallback: source === 'bundled', ...(reason ? { reason } : {}) },
53
+ key: preset.key,
54
+ name: preset.name,
55
+ templateVersion: preset.templateVersion,
56
+ body,
57
+ hash: (0, node_crypto_1.createHash)('sha256').update(body, 'utf8').digest('hex'),
58
+ redirectedTo: 'tasks',
59
+ provenance: {
60
+ source: read.source,
61
+ fallback: read.source === 'bundled',
62
+ ...(read.reason ? { reason: read.reason } : {}),
63
+ },
81
64
  }, null, 2));
82
- else {
83
- if (source === 'bundled')
84
- this.logToStderr(reason === 'server-hash-mismatch'
85
- ? `The server guide’s body does not match its hash; using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`
86
- : `Using the CLI’s bundled task-management guide (template v${guide.templateVersion}).`);
87
- this.log(guide.body);
65
+ return;
88
66
  }
67
+ const notice = (0, agent_guides_1.agentGuideFallbackNotice)(read);
68
+ if (notice)
69
+ process.stderr.write(`${notice}\n`);
70
+ this.log(`${body}\n${TASKS_GUIDE_POINTER}`);
89
71
  }
90
72
  }
91
73
  exports.default = InstructionsGuide;
@@ -16,7 +16,7 @@ class MemoryMaintain extends base_command_1.BaseCommand {
16
16
  static description = 'Apply an auditable canonical memory maintenance operation';
17
17
  static examples = [
18
18
  '<%= config.bin %> memory maintain remember \'{"path":"/notes","claim":"Ship notes on Fridays."}\' --agent agent_abc123',
19
- '<%= config.bin %> memory maintain archive \'{"factId":"fact_abc"}\' --agent agent_abc123',
19
+ '<%= config.bin %> memory maintain archive \'{"path":"/notes/old-note"}\' --agent agent_abc123',
20
20
  ];
21
21
  static args = {
22
22
  operation: core_1.Args.string({ required: true, options: [...OPERATIONS], ignoreStdin: true }),
@@ -9,6 +9,7 @@ export default class SkillsInstall extends BaseCommand {
9
9
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
10
  agent: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
11
  service: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
+ yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
13
14
  /** A store 404 for a name that may be a skill on the caller's machine. */
14
15
  private failOnStoreMiss;
@@ -3,12 +3,14 @@ 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 prompt_1 = require("../../lib/prompt");
6
7
  const local_skills_1 = require("../../lib/local-skills");
7
8
  class SkillsInstall extends base_command_1.BaseCommand {
8
- static description = 'Install a skill from the store to a service (agent)';
9
+ static description = 'Install a skill from the store to a service (agent). A paid skill shows its price and asks you to confirm first.';
9
10
  static examples = [
10
11
  '<%= config.bin %> skills install <skill-id>',
11
12
  '<%= config.bin %> skills install <skill-id> --agent <agent-id>',
13
+ '<%= config.bin %> skills install <skill-id> --yes',
12
14
  '<%= config.bin %> skills install <skill-id> --json',
13
15
  ];
14
16
  static args = {
@@ -25,6 +27,10 @@ class SkillsInstall extends base_command_1.BaseCommand {
25
27
  description: '[DEPRECATED] Use --agent. Same meaning, kept for script compatibility.',
26
28
  hidden: true,
27
29
  }),
30
+ yes: core_1.Flags.boolean({
31
+ char: 'y',
32
+ description: 'Confirm a paid install without prompting. The price is still printed.',
33
+ }),
28
34
  };
29
35
  /** A store 404 for a name that may be a skill on the caller's machine. */
30
36
  async failOnStoreMiss(err, id) {
@@ -56,15 +62,48 @@ class SkillsInstall extends base_command_1.BaseCommand {
56
62
  retryable: false,
57
63
  });
58
64
  }
65
+ // What installing would charge NOW, read before the install sends it.
66
+ // A paid skill shows its price and asks first; the install then pins what
67
+ // was shown, so a listing whose price moved in between is refused rather
68
+ // than charged (OSK-18042 — same convention as `store install`, OSK-12240).
69
+ let listing;
70
+ try {
71
+ listing = await data_provider_1.dataService.getSkill(args.id);
72
+ }
73
+ catch (err) {
74
+ return await this.failOnStoreMiss(err, args.id);
75
+ }
76
+ const charge = Number(listing.price) || 0;
77
+ if (charge > 0) {
78
+ const name = listing.displayName || listing.name || args.id;
79
+ // Printed before any prompt and even with --yes, so a scripted purchase
80
+ // still leaves the price it agreed to in its own output.
81
+ this.logToStderr(`${name} is a paid skill.`);
82
+ this.logToStderr(` Price: ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)} USD`);
83
+ if (listing.offerVersionId) {
84
+ this.logToStderr(` Offer: ${listing.offerVersionId}`);
85
+ }
86
+ const confirmed = await (0, prompt_1.confirmDestructive)(`Buy ${name} for ${(0, data_provider_1.formatStoreAcquisitionPrice)(charge)}?`, flags.yes).catch((err) => this.error(err.message, { exit: err.exit ?? 1 }));
87
+ if (!confirmed) {
88
+ this.log('Cancelled. Nothing was bought.');
89
+ return;
90
+ }
91
+ }
59
92
  let result;
60
93
  try {
61
94
  result = await data_provider_1.dataService.installSkill({
62
95
  skillId: args.id,
63
96
  targetAgentId,
97
+ // Pin what the person was shown: the charge always, the offer when
98
+ // there is one to charge under.
99
+ expectedPrice: charge,
100
+ ...(charge > 0 && listing.offerVersionId
101
+ ? { expectedOfferVersionId: listing.offerVersionId }
102
+ : {}),
64
103
  });
65
104
  }
66
105
  catch (err) {
67
- await this.failOnStoreMiss(err, args.id);
106
+ return await this.failOnStoreMiss(err, args.id);
68
107
  }
69
108
  if (flags.json) {
70
109
  this.log(JSON.stringify(result, null, 2));
@@ -181,14 +181,14 @@ class SlackSendAsAgent extends base_command_1.BaseCommand {
181
181
  exports.default = SlackSendAsAgent;
182
182
  function generatedSessionIdempotencyKey(agentId, channelId, text) {
183
183
  const requestId = process.env.OVERSKY_SESSION_REQUEST_ID || process.env.OVERSKY_REQUEST_ID;
184
- if (requestId)
185
- return `session:slack:${requestId}`;
186
184
  const digest = crypto
187
185
  .createHash('sha256')
188
186
  .update(JSON.stringify({ agentId, channelId, text }))
189
187
  .digest('hex')
190
188
  .slice(0, 32);
191
- return `session:slack:${digest}`;
189
+ if (requestId)
190
+ return `session:slack:${requestId}:${digest}`;
191
+ return `session:slack:${crypto.randomUUID()}`;
192
192
  }
193
193
  function formatSessionSlackResult(result, agentId, channelId) {
194
194
  const status = result.status || 'submitted';
@@ -2,7 +2,7 @@
2
2
  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
- const outbox_1 = require("../../lib/outbox");
5
+ const node_crypto_1 = require("node:crypto");
6
6
  const api_fetch_1 = require("../../lib/api-fetch");
7
7
  const base_command_1 = require("../../base-command");
8
8
  const label_ref_1 = require("../../lib/label-ref");
@@ -124,7 +124,13 @@ class SpacesCreate extends base_command_1.BaseCommand {
124
124
  // replay must collapse onto the write that already landed. A key sent only
125
125
  // on the replay would create a second Space instead — the exact duplicate
126
126
  // the outbox exists to prevent.
127
- const idempotencyKey = (0, outbox_1.idempotencyKeyFor)('space-create', String(payload.title), JSON.stringify(payload));
127
+ //
128
+ // The key is fresh per invocation and travels with the queued write; it is
129
+ // never derived from the payload. A payload-derived key made a second
130
+ // `spaces create` with the same title and parent replay the first for 24
131
+ // hours, so a person who archived "Billing migration" and asked for a new
132
+ // one got the archived Space back, and their new work was filed inside it.
133
+ const idempotencyKey = `space-create_${(0, node_crypto_1.randomUUID)()}`;
128
134
  let created;
129
135
  try {
130
136
  // apiFetch rather than `dataService.createSpace`: the shared transport
@@ -0,0 +1,21 @@
1
+ import { Command } from '@oclif/core';
2
+ /** Local documentation must remain readable without credentials or a daemon. */
3
+ export default class SpacesGuide extends Command {
4
+ static baseFlags: {
5
+ bare: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ raw: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
11
+ static description: string;
12
+ static args: {
13
+ section: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
15
+ static flags: {
16
+ json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
17
+ offline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
18
+ };
19
+ static examples: string[];
20
+ run(): Promise<void>;
21
+ }
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const core_1 = require("@oclif/core");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../base-command");
6
+ const agent_guides_1 = require("../../lib/agent-guides");
7
+ /** Local documentation must remain readable without credentials or a daemon. */
8
+ class SpacesGuide extends core_1.Command {
9
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
10
+ static description = 'Alias of `skrr guide spaces views` — the Space perspective map. `skrr guide spaces` reads the whole guide.';
11
+ static args = {
12
+ section: core_1.Args.string({
13
+ description: 'A section of the spaces guide to read instead of `views`',
14
+ ignoreStdin: true,
15
+ }),
16
+ };
17
+ static flags = {
18
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
19
+ offline: core_1.Flags.boolean({
20
+ description: 'Read the bundled release guides without checking credentials or the server',
21
+ }),
22
+ };
23
+ static examples = [
24
+ '<%= config.bin %> spaces guide',
25
+ '<%= config.bin %> spaces guide goals',
26
+ '<%= config.bin %> spaces guide --json',
27
+ ];
28
+ async run() {
29
+ const { args, flags } = await this.parse(SpacesGuide);
30
+ const read = await (0, agent_guides_1.readAgentGuide)({ id: 'spaces', section: args.section ?? 'views' }, flags);
31
+ if (!read.found)
32
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
33
+ if (flags.json) {
34
+ process.stdout.write((0, agent_guides_1.agentGuideJson)(read));
35
+ return;
36
+ }
37
+ process.stdout.write((0, data_provider_1.renderAgentGuideText)(read.response));
38
+ }
39
+ }
40
+ exports.default = SpacesGuide;
@@ -9,7 +9,6 @@ declare const PERSPECTIVES: SpacePerspective[];
9
9
  export default class Spaces extends BaseCommand {
10
10
  static description: string;
11
11
  static examples: string[];
12
- static aliases: string[];
13
12
  static flags: {
14
13
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
14
  };
@@ -102,12 +102,7 @@ const PERSPECTIVES = [
102
102
  exports.PERSPECTIVES = PERSPECTIVES;
103
103
  class Spaces extends base_command_1.BaseCommand {
104
104
  static description = 'Discover space perspectives and the CLI commands for each one';
105
- static examples = [
106
- '<%= config.bin %> spaces',
107
- '<%= config.bin %> spaces --json',
108
- '<%= config.bin %> spaces guide',
109
- ];
110
- static aliases = ['spaces guide'];
105
+ static examples = ['<%= config.bin %> spaces', '<%= config.bin %> spaces --json'];
111
106
  static flags = {
112
107
  json: core_1.Flags.boolean({ description: 'Output the perspective guide as JSON' }),
113
108
  };
@@ -61,11 +61,10 @@ class TasksCheckout extends base_command_1.BaseCommand {
61
61
  result = await data_provider_1.dataService.checkoutTask({ taskId: taskRef, runId });
62
62
  }
63
63
  catch (err) {
64
- const e = err;
65
- if (e.status === 409) {
64
+ if ((0, base_command_1.apiRefusal)(err).code === 'TASK_ALREADY_CHECKED_OUT') {
66
65
  // Dedicated exit code for "conflict" so scripts can distinguish
67
66
  // "already checked out" from other failures without string-matching
68
- // the message.
67
+ // the message. Other 409s have their own server-provided reason.
69
68
  this.error('Task is already checked out by another run.', { exit: 9 });
70
69
  }
71
70
  this.handleApiError(err);
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const node_crypto_1 = require("node:crypto");
4
4
  const core_1 = require("@oclif/core");
5
5
  const base_command_1 = require("../../../base-command");
6
- const outbox_1 = require("../../../lib/outbox");
7
6
  const api_fetch_1 = require("../../../lib/api-fetch");
8
7
  const credential_resolver_1 = require("../../../lib/credential-resolver");
9
8
  const unsupported_managed_task_write_1 = require("../../../lib/unsupported-managed-task-write");
@@ -188,7 +187,7 @@ class TasksCommentsAdd extends base_command_1.BaseCommand {
188
187
  // ONE key for the original attempt and any later replay — see the comment
189
188
  // in `spaces create`. A key sent only on the replay would post a second
190
189
  // comment whenever the original landed and its response was lost.
191
- const idempotencyKey = (0, outbox_1.idempotencyKeyFor)('task-comment', taskRef, JSON.stringify(data));
190
+ const idempotencyKey = `task-comment_${(0, node_crypto_1.randomUUID)()}`;
192
191
  let result;
193
192
  try {
194
193
  // apiFetch rather than `dataService.addTaskComment`: the shared transport
@@ -225,9 +224,8 @@ class TasksCommentsAdd extends base_command_1.BaseCommand {
225
224
  }
226
225
  // A comment body is authored content: losing it to a 503 costs the
227
226
  // writing, not just a retry. Queue it durably instead (OSK-2806). The
228
- // key is derived from the task + body so replaying the SAME comment
229
- // collapses server-side rather than posting it twice; a genuinely
230
- // different comment on the same task hashes differently and still posts.
227
+ // key is kept from the original attempt, so replaying this queued intent
228
+ // collapses server-side rather than posting it twice.
231
229
  this.queueWriteOrFail(err, {
232
230
  command: 'tasks comments add',
233
231
  summary: `comment on task ${taskRef}`,
@@ -1,12 +1,12 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.describePendingReview = describePendingReview;
4
+ const node_crypto_1 = require("node:crypto");
4
5
  const core_1 = require("@oclif/core");
5
6
  const base_command_1 = require("../../base-command");
6
7
  const task_closure_1 = require("../../lib/task-closure");
7
8
  const api_fetch_1 = require("../../lib/api-fetch");
8
9
  const task_recurrence_outcome_1 = require("../../lib/task-recurrence-outcome");
9
- const outbox_1 = require("../../lib/outbox");
10
10
  const prompt_1 = require("../../lib/prompt");
11
11
  const session_task_write_1 = require("../../lib/session-task-write");
12
12
  const task_resolver_1 = require("../../lib/task-resolver");
@@ -278,8 +278,7 @@ class TasksComplete extends base_command_1.BaseCommand {
278
278
  expectedSpecVersion: typeof input.expectedSpecVersion === 'number' ? input.expectedSpecVersion : undefined,
279
279
  ...(input.force === true ? { force: true } : {}),
280
280
  };
281
- const idempotencyKey = optionalString(input.idempotencyKey) ||
282
- (0, outbox_1.idempotencyKeyFor)('task-complete', identity.id, JSON.stringify(payload));
281
+ const idempotencyKey = optionalString(input.idempotencyKey) || `task-complete_${(0, node_crypto_1.randomUUID)()}`;
283
282
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('completion', identity.id);
284
283
  let taskId = identity.id;
285
284
  if (sessionWrite)
@@ -1,5 +1,5 @@
1
1
  import { BaseCommand } from '../../../base-command';
2
- export declare function defaultDeliverableIdempotencyKey(task: string, payload: Record<string, unknown>): string;
2
+ export declare function defaultDeliverableIdempotencyKey(): string;
3
3
  /**
4
4
  * `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
5
5
  *
@@ -37,18 +37,16 @@ exports.defaultDeliverableIdempotencyKey = defaultDeliverableIdempotencyKey;
37
37
  exports.statusNotApplied = statusNotApplied;
38
38
  const fs = __importStar(require("node:fs"));
39
39
  const path = __importStar(require("node:path"));
40
+ const node_crypto_1 = require("node:crypto");
40
41
  const core_1 = require("@oclif/core");
41
42
  const data_provider_1 = require("@skrr-ai/data-provider");
42
43
  const base_command_1 = require("../../../base-command");
43
44
  const task_asset_upload_1 = require("../../../lib/task-asset-upload");
44
- const outbox_1 = require("../../../lib/outbox");
45
45
  const api_fetch_1 = require("../../../lib/api-fetch");
46
46
  const session_task_write_1 = require("../../../lib/session-task-write");
47
47
  const tasks_1 = require("../../../lib/tasks");
48
- function defaultDeliverableIdempotencyKey(task, payload) {
49
- // A retry of one exact observation keeps its key. A new version, provider,
50
- // digest, or expectation binding is a different observation.
51
- return (0, outbox_1.idempotencyKeyFor)('task-deliverable', task, JSON.stringify(payload));
48
+ function defaultDeliverableIdempotencyKey() {
49
+ return `task-deliverable_${(0, node_crypto_1.randomUUID)()}`;
52
50
  }
53
51
  /**
54
52
  * `skrr tasks deliverable add <task> <kind>:<identity>[@<location>]`
@@ -192,8 +190,7 @@ class TasksDeliverableAdd extends base_command_1.BaseCommand {
192
190
  };
193
191
  const payload = {
194
192
  ...observation,
195
- idempotencyKey: flags['idempotency-key'] ||
196
- defaultDeliverableIdempotencyKey(args.task, observation),
193
+ idempotencyKey: flags['idempotency-key'] || defaultDeliverableIdempotencyKey(),
197
194
  };
198
195
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('deliverable', args.task);
199
196
  result = sessionWrite
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
6
- const outbox_1 = require("../../../lib/outbox");
7
7
  const task_resolver_1 = require("../../../lib/task-resolver");
8
8
  const api_fetch_1 = require("../../../lib/api-fetch");
9
9
  const session_task_write_1 = require("../../../lib/session-task-write");
@@ -135,7 +135,7 @@ class TasksExpectationsAssess extends base_command_1.BaseCommand {
135
135
  // settling exactly one Expectation while reporting several.
136
136
  idempotencyKey: reuseCallerKey
137
137
  ? flags['idempotency-key']
138
- : (0, outbox_1.idempotencyKeyFor)('task-expectation-assessment', task.id, JSON.stringify({ expectationId: id, ...payload })),
138
+ : `task-expectation-assessment_${(0, node_crypto_1.randomUUID)()}`,
139
139
  };
140
140
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('assessment', task.id);
141
141
  return sessionWrite
@@ -1,10 +1,18 @@
1
1
  import { Command } from '@oclif/core';
2
2
  /** Local documentation must remain readable without credentials or a daemon. */
3
3
  export default class TasksGuide extends Command {
4
+ static baseFlags: {
5
+ bare: import("@oclif/core/interfaces").BooleanFlag<boolean>;
6
+ token: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ workspace: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
8
+ profile: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
+ raw: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ };
4
11
  static description: string;
5
12
  static examples: string[];
6
13
  static flags: {
7
14
  json: import("@oclif/core/interfaces").BooleanFlag<boolean>;
15
+ offline: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
16
  };
9
17
  run(): Promise<void>;
10
18
  }
@@ -1,20 +1,33 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const core_1 = require("@oclif/core");
4
- const task_operations_1 = require("../../lib/task-operations");
5
- const json_output_1 = require("../../lib/json-output");
4
+ const data_provider_1 = require("@skrr-ai/data-provider");
5
+ const base_command_1 = require("../../base-command");
6
+ const agent_guides_1 = require("../../lib/agent-guides");
6
7
  /** Local documentation must remain readable without credentials or a daemon. */
7
8
  class TasksGuide extends core_1.Command {
8
- static description = 'Find the commands for Task planning, dependencies, preflight, execution, and results';
9
- static examples = ['<%= config.bin %> tasks guide', '<%= config.bin %> tasks guide --json'];
10
- static flags = { json: core_1.Flags.boolean({ description: 'Output the local operations guide as JSON' }) };
9
+ static baseFlags = base_command_1.BaseCommand.baseFlags;
10
+ static description = 'Alias of `skrr guide tasks commands` — the Task command map. `skrr guide tasks` reads the whole guide.';
11
+ static examples = [
12
+ '<%= config.bin %> tasks guide',
13
+ '<%= config.bin %> tasks guide --json',
14
+ ];
15
+ static flags = {
16
+ json: core_1.Flags.boolean({ description: 'Output the guide and source provenance as JSON' }),
17
+ offline: core_1.Flags.boolean({
18
+ description: 'Read the bundled release guides without checking credentials or the server',
19
+ }),
20
+ };
11
21
  async run() {
12
22
  const { flags } = await this.parse(TasksGuide);
13
- if (flags.json)
14
- this.log(JSON.stringify((0, json_output_1.withOk)((0, task_operations_1.taskOperationsGuide)(this.config.bin)), null, 2));
15
- else
16
- for (const line of (0, task_operations_1.taskOperationsGuideLines)(this.config.bin))
17
- this.log(line);
23
+ const read = await (0, agent_guides_1.readAgentGuide)({ id: 'tasks', section: 'commands' }, flags);
24
+ if (!read.found)
25
+ return (0, agent_guides_1.writeAgentGuideMiss)(this, read.miss, flags.json);
26
+ if (flags.json) {
27
+ process.stdout.write((0, agent_guides_1.agentGuideJson)(read));
28
+ return;
29
+ }
30
+ process.stdout.write((0, data_provider_1.renderAgentGuideText)(read.response));
18
31
  }
19
32
  }
20
33
  exports.default = TasksGuide;
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../base-command");
6
- const outbox_1 = require("../../lib/outbox");
7
7
  const task_report_1 = require("../../lib/task-report");
8
8
  const task_resolver_1 = require("../../lib/task-resolver");
9
9
  const tasks_1 = require("../../lib/tasks");
@@ -110,11 +110,13 @@ class TasksReport extends base_command_1.BaseCommand {
110
110
  continue;
111
111
  }
112
112
  try {
113
- const data = { ...parsed.value, idempotencyKey: (0, outbox_1.idempotencyKeyFor)('task-deliverable', task.id, ref) };
113
+ const data = { ...parsed.value, idempotencyKey: `task-deliverable_${(0, node_crypto_1.randomUUID)()}` };
114
114
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('deliverable', task.id);
115
115
  const recorded = sessionWrite
116
116
  ? await (0, api_fetch_1.apiFetch)(sessionWrite.path, {
117
- method: 'POST', body: data, credential: sessionWrite.credential,
117
+ method: 'POST',
118
+ body: data,
119
+ credential: sessionWrite.credential,
118
120
  })
119
121
  : await data_provider_1.dataService.addTaskDeliverable(task.id, data);
120
122
  resourceLinkIds.push(recorded.link.id);
@@ -153,12 +155,14 @@ class TasksReport extends base_command_1.BaseCommand {
153
155
  try {
154
156
  const data = {
155
157
  ...payload,
156
- idempotencyKey: (0, outbox_1.idempotencyKeyFor)('task-result', task.id, JSON.stringify(payload)),
158
+ idempotencyKey: `task-result_${(0, node_crypto_1.randomUUID)()}`,
157
159
  };
158
160
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('result', task.id);
159
161
  result = sessionWrite
160
162
  ? await (0, api_fetch_1.apiFetch)(sessionWrite.path, {
161
- method: 'POST', body: data, credential: sessionWrite.credential,
163
+ method: 'POST',
164
+ body: data,
165
+ credential: sessionWrite.credential,
162
166
  })
163
167
  : await data_provider_1.dataService.submitTaskResult({ taskId: task.id, data });
164
168
  }
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
6
- const outbox_1 = require("../../../lib/outbox");
7
7
  const tasks_1 = require("../../../lib/tasks");
8
8
  const RESOURCE_ROLES = ['input', 'reference', 'evidence', 'attachment', 'deliverable'];
9
9
  class TasksResourceAdd extends base_command_1.BaseCommand {
@@ -58,8 +58,7 @@ class TasksResourceAdd extends base_command_1.BaseCommand {
58
58
  expectationIds: flags.expectation,
59
59
  resultId: flags['result-id'],
60
60
  runId: flags['run-id'],
61
- idempotencyKey: flags['idempotency-key'] ||
62
- (0, outbox_1.idempotencyKeyFor)('task-resource', args.task, JSON.stringify({ role: args.role, coordinate: parsed.value, status: flags.status })),
61
+ idempotencyKey: flags['idempotency-key'] || `task-resource_${(0, node_crypto_1.randomUUID)()}`,
63
62
  };
64
63
  let result;
65
64
  try {
@@ -1,9 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ const node_crypto_1 = require("node:crypto");
3
4
  const core_1 = require("@oclif/core");
4
5
  const data_provider_1 = require("@skrr-ai/data-provider");
5
6
  const base_command_1 = require("../../../base-command");
6
- const outbox_1 = require("../../../lib/outbox");
7
7
  const task_resolver_1 = require("../../../lib/task-resolver");
8
8
  const web_url_1 = require("../../../lib/web-url");
9
9
  const api_fetch_1 = require("../../../lib/api-fetch");
@@ -148,8 +148,7 @@ class TasksResultSubmit extends base_command_1.BaseCommand {
148
148
  try {
149
149
  const data = {
150
150
  ...payload,
151
- idempotencyKey: flags['idempotency-key'] ||
152
- (0, outbox_1.idempotencyKeyFor)('task-result', task.id, JSON.stringify(payload)),
151
+ idempotencyKey: flags['idempotency-key'] || `task-result_${(0, node_crypto_1.randomUUID)()}`,
153
152
  };
154
153
  const sessionWrite = await (0, session_task_write_1.sessionTaskWrite)('result', task.id);
155
154
  result = sessionWrite