@skrr-ai/cli 0.1.37 → 0.1.39

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 (177) hide show
  1. package/bin/dev-fallback.js +175 -4
  2. package/dist/base-command.js +46 -2
  3. package/dist/commands/agents/actions/create.js +18 -7
  4. package/dist/commands/agents/actions/delete.d.ts +1 -0
  5. package/dist/commands/agents/actions/delete.js +7 -0
  6. package/dist/commands/agents/actions/update.js +5 -3
  7. package/dist/commands/agents/api-actions/add.js +5 -2
  8. package/dist/commands/agents/api-actions/index.d.ts +11 -0
  9. package/dist/commands/agents/api-actions/index.js +27 -1
  10. package/dist/commands/agents/avatar/default/index.d.ts +11 -0
  11. package/dist/commands/agents/avatar/default/index.js +17 -0
  12. package/dist/commands/agents/avatar/default/set.d.ts +2 -1
  13. package/dist/commands/agents/avatar/default/set.js +32 -7
  14. package/dist/commands/agents/avatar/upload.js +7 -1
  15. package/dist/commands/agents/categories/delete.js +8 -0
  16. package/dist/commands/agents/categories/index.d.ts +21 -5
  17. package/dist/commands/agents/categories/index.js +68 -15
  18. package/dist/commands/agents/chat.d.ts +21 -0
  19. package/dist/commands/agents/chat.js +94 -7
  20. package/dist/commands/agents/create.js +11 -4
  21. package/dist/commands/agents/list.d.ts +15 -0
  22. package/dist/commands/agents/list.js +31 -1
  23. package/dist/commands/agents/scene-background/upload.d.ts +30 -0
  24. package/dist/commands/agents/scene-background/upload.js +97 -0
  25. package/dist/commands/agents/show.js +13 -0
  26. package/dist/commands/agents/tools/calls.d.ts +23 -5
  27. package/dist/commands/agents/tools/calls.js +81 -18
  28. package/dist/commands/agents/update.js +15 -5
  29. package/dist/commands/code/{oversky-agent.d.ts → skrr-agent.d.ts} +3 -3
  30. package/dist/commands/code/{oversky-agent.js → skrr-agent.js} +11 -8
  31. package/dist/commands/convos/messages.d.ts +13 -0
  32. package/dist/commands/convos/messages.js +40 -16
  33. package/dist/commands/daemon/index.js +8 -1
  34. package/dist/commands/daemon/logs.d.ts +26 -0
  35. package/dist/commands/daemon/logs.js +56 -0
  36. package/dist/commands/daemon/uninstall.js +1 -1
  37. package/dist/commands/followups/act.js +3 -2
  38. package/dist/commands/followups/cancel.d.ts +6 -3
  39. package/dist/commands/followups/cancel.js +16 -6
  40. package/dist/commands/followups/observability.d.ts +23 -0
  41. package/dist/commands/followups/observability.js +114 -0
  42. package/dist/commands/followups/reschedule.js +1 -1
  43. package/dist/commands/followups/resolve.js +3 -3
  44. package/dist/commands/followups/show.js +2 -1
  45. package/dist/commands/followups/watch.js +9 -5
  46. package/dist/commands/harnesses/forget.js +28 -0
  47. package/dist/commands/harnesses/install.d.ts +41 -0
  48. package/dist/commands/harnesses/install.js +165 -0
  49. package/dist/commands/harnesses/installers.d.ts +45 -0
  50. package/dist/commands/harnesses/installers.js +155 -0
  51. package/dist/commands/harnesses/list.d.ts +82 -1
  52. package/dist/commands/harnesses/list.js +104 -12
  53. package/dist/commands/harnesses/models.js +10 -1
  54. package/dist/commands/harnesses/show.d.ts +15 -0
  55. package/dist/commands/harnesses/show.js +44 -3
  56. package/dist/commands/harnesses/update.js +1 -1
  57. package/dist/commands/harnesses/usage.d.ts +25 -0
  58. package/dist/commands/harnesses/usage.js +24 -2
  59. package/dist/commands/instructions/status.js +13 -1
  60. package/dist/commands/machines/dedicated/catalog.js +4 -15
  61. package/dist/commands/machines/dedicated/create.d.ts +8 -0
  62. package/dist/commands/machines/dedicated/create.js +63 -2
  63. package/dist/commands/machines/dedicated/destroy.js +19 -5
  64. package/dist/commands/machines/dedicated/exec.js +3 -1
  65. package/dist/commands/machines/dedicated/list.js +9 -6
  66. package/dist/commands/machines/dedicated/price-book.d.ts +17 -0
  67. package/dist/commands/machines/dedicated/price-book.js +35 -0
  68. package/dist/commands/machines/hosted/list.js +12 -0
  69. package/dist/commands/machines/list.js +11 -0
  70. package/dist/commands/machines/show.d.ts +1 -0
  71. package/dist/commands/machines/show.js +40 -11
  72. package/dist/commands/profiles/list.d.ts +23 -0
  73. package/dist/commands/profiles/list.js +96 -0
  74. package/dist/commands/search/skills.d.ts +11 -1
  75. package/dist/commands/search/skills.js +26 -8
  76. package/dist/commands/skills/details.d.ts +2 -0
  77. package/dist/commands/skills/details.js +24 -1
  78. package/dist/commands/skills/import-as-actions.d.ts +32 -0
  79. package/dist/commands/skills/import-as-actions.js +175 -0
  80. package/dist/commands/skills/install.d.ts +2 -0
  81. package/dist/commands/skills/install.js +24 -1
  82. package/dist/commands/skills/invocable.js +6 -2
  83. package/dist/commands/skills/local.d.ts +40 -0
  84. package/dist/commands/skills/local.js +160 -0
  85. package/dist/commands/skills/search.d.ts +11 -1
  86. package/dist/commands/skills/search.js +29 -8
  87. package/dist/commands/spaces/actions/add.js +23 -5
  88. package/dist/commands/spaces/actions/available.js +3 -0
  89. package/dist/commands/spaces/actions/list.d.ts +5 -0
  90. package/dist/commands/spaces/actions/list.js +49 -3
  91. package/dist/commands/spaces/actions/run.d.ts +28 -0
  92. package/dist/commands/spaces/actions/run.js +146 -0
  93. package/dist/commands/spaces/actions/update.js +27 -5
  94. package/dist/commands/spaces/label-summaries.d.ts +27 -0
  95. package/dist/commands/spaces/label-summaries.js +64 -0
  96. package/dist/commands/spaces/task-rollups.d.ts +5 -0
  97. package/dist/commands/spaces/task-rollups.js +15 -4
  98. package/dist/commands/spaces/workflows/lifecycle-diagnostics.js +63 -3
  99. package/dist/commands/tasks/actions/attach.js +32 -7
  100. package/dist/commands/tasks/complete.js +13 -0
  101. package/dist/commands/tasks/create.js +1 -1
  102. package/dist/commands/tasks/events/append.js +29 -6
  103. package/dist/commands/tasks/expectations/assess.d.ts +1 -0
  104. package/dist/commands/tasks/expectations/assess.js +43 -5
  105. package/dist/commands/tasks/expectations.js +77 -16
  106. package/dist/commands/tasks/follow-up.js +2 -2
  107. package/dist/commands/tasks/lifecycle/definitions.js +21 -2
  108. package/dist/commands/tasks/lifecycle/detach.d.ts +7 -0
  109. package/dist/commands/tasks/lifecycle/detach.js +43 -3
  110. package/dist/commands/tasks/lifecycle/migrate.d.ts +2 -0
  111. package/dist/commands/tasks/lifecycle/migrate.js +42 -3
  112. package/dist/commands/tasks/lifecycle/reopen.d.ts +2 -0
  113. package/dist/commands/tasks/lifecycle/reopen.js +40 -3
  114. package/dist/commands/tasks/lifecycle/transition.js +33 -2
  115. package/dist/commands/tasks/list.js +16 -9
  116. package/dist/commands/tasks/move.js +11 -13
  117. package/dist/commands/tasks/review-queue.d.ts +15 -0
  118. package/dist/commands/tasks/review-queue.js +180 -31
  119. package/dist/commands/tasks/show.d.ts +11 -0
  120. package/dist/commands/tasks/show.js +105 -12
  121. package/dist/commands/tasks/trust.js +7 -2
  122. package/dist/commands/tasks/update.js +46 -10
  123. package/dist/commands/tasks/updates/add.js +1 -1
  124. package/dist/commands/token/list.d.ts +22 -0
  125. package/dist/commands/token/list.js +68 -0
  126. package/dist/commands/token/revoke.d.ts +23 -0
  127. package/dist/commands/token/revoke.js +53 -0
  128. package/dist/lib/agent-actions.d.ts +57 -2
  129. package/dist/lib/agent-actions.js +113 -21
  130. package/dist/lib/agent-pin-mute.d.ts +10 -5
  131. package/dist/lib/agent-pin-mute.js +17 -4
  132. package/dist/lib/agent-visual-refs.d.ts +27 -0
  133. package/dist/lib/agent-visual-refs.js +127 -2
  134. package/dist/lib/agentic-stream.d.ts +6 -0
  135. package/dist/lib/agentic-stream.js +34 -3
  136. package/dist/lib/assignee-resolver.d.ts +1 -1
  137. package/dist/lib/assignee-resolver.js +8 -0
  138. package/dist/lib/bulk-task-targets.d.ts +12 -0
  139. package/dist/lib/bulk-task-targets.js +34 -1
  140. package/dist/lib/cli-installers.d.ts +130 -0
  141. package/dist/lib/cli-installers.js +145 -0
  142. package/dist/lib/command-miss.js +55 -7
  143. package/dist/lib/dedicated-lease-command.d.ts +16 -1
  144. package/dist/lib/dedicated-lease-command.js +26 -3
  145. package/dist/lib/dedicated-machines.d.ts +135 -1
  146. package/dist/lib/dedicated-machines.js +307 -3
  147. package/dist/lib/failed-lookup.d.ts +3 -0
  148. package/dist/lib/failed-lookup.js +40 -0
  149. package/dist/lib/first-party-harness-agent.js +9 -2
  150. package/dist/lib/first-party-harness-doctor.d.ts +15 -0
  151. package/dist/lib/first-party-harness-doctor.js +126 -27
  152. package/dist/lib/followups.d.ts +14 -6
  153. package/dist/lib/followups.js +31 -6
  154. package/dist/lib/harnesses.d.ts +8 -0
  155. package/dist/lib/instruction-input.d.ts +19 -1
  156. package/dist/lib/instruction-input.js +52 -1
  157. package/dist/lib/label-scope.d.ts +15 -0
  158. package/dist/lib/label-scope.js +25 -5
  159. package/dist/lib/local-skills.d.ts +98 -0
  160. package/dist/lib/local-skills.js +195 -0
  161. package/dist/lib/machines.d.ts +10 -10
  162. package/dist/lib/machines.js +23 -1
  163. package/dist/lib/search-query.d.ts +19 -0
  164. package/dist/lib/search-query.js +35 -0
  165. package/dist/lib/space-custom-actions.d.ts +72 -0
  166. package/dist/lib/space-custom-actions.js +117 -0
  167. package/dist/lib/space-resolver.d.ts +1 -21
  168. package/dist/lib/space-resolver.js +19 -10
  169. package/dist/lib/task-closure.d.ts +3 -2
  170. package/dist/lib/task-closure.js +18 -20
  171. package/dist/lib/task-lifecycle-output.js +117 -9
  172. package/dist/lib/task-ref-resolver.d.ts +1 -17
  173. package/dist/lib/task-ref-resolver.js +18 -8
  174. package/dist/lib/tasks.js +9 -3
  175. package/dist/node_modules/@skrr-ai/data-provider/index.js +5076 -4947
  176. package/oclif.manifest.json +15505 -14460
  177. package/package.json +9 -3
@@ -0,0 +1,96 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const node_fs_1 = __importDefault(require("node:fs"));
7
+ const node_path_1 = __importDefault(require("node:path"));
8
+ const core_1 = require("@oclif/core");
9
+ const base_command_1 = require("../../base-command");
10
+ const config_1 = require("../../lib/config");
11
+ const keychain_1 = require("../../lib/keychain");
12
+ /**
13
+ * Which stored sessions exist on this machine.
14
+ *
15
+ * Every command advertises `--profile` and nothing could tell you what to pass
16
+ * it. Profiles are additive and stored on disk — the default at
17
+ * `<root>/cli-config.json`, named ones at `<root>/profiles/<name>/cli-config.json`
18
+ * — so the list was always knowable; there was simply no way to ask.
19
+ *
20
+ * That is not only a discoverability nit. A second profile is how one machine
21
+ * holds a second identity, which is what any test of a NON-OWNER path needs. A
22
+ * dogfood run of the `force`-past-a-required-Review flow stalled on exactly
23
+ * this: the flag was there, the second identity was reachable, and nothing named
24
+ * it.
25
+ */
26
+ class ProfilesList extends base_command_1.BaseCommand {
27
+ static description = 'List the stored sessions (profiles) on this machine';
28
+ static examples = [
29
+ '<%= config.bin %> profiles list',
30
+ '<%= config.bin %> profiles list --json',
31
+ '<%= config.bin %> login --profile reviewer',
32
+ ];
33
+ static flags = {
34
+ json: core_1.Flags.boolean({ description: 'Output as JSON' }),
35
+ };
36
+ async run() {
37
+ const { flags } = await this.parse(ProfilesList);
38
+ const root = (0, config_1.configRoot)();
39
+ const active = (0, keychain_1.getActiveProfile)() || keychain_1.DEFAULT_PROFILE;
40
+ const rows = [];
41
+ const seen = new Set();
42
+ const describe = (name, configFile) => {
43
+ /*
44
+ * Deliberately NOT a "signed in" column.
45
+ *
46
+ * The credential does not live in `cli-config.json` — it is in the
47
+ * keychain — so this file can only report the deployment. A first draft
48
+ * inferred sign-in from a `token` key that is never there and printed
49
+ * `no` for the profile the caller was actively authenticated as: a
50
+ * confident wrong answer, which is worse than an absent one and exactly
51
+ * the failure this whole surface keeps making. Read the server, say the
52
+ * server.
53
+ */
54
+ if (seen.has(name))
55
+ return;
56
+ seen.add(name);
57
+ let server;
58
+ try {
59
+ const raw = JSON.parse(node_fs_1.default.readFileSync(configFile, 'utf8'));
60
+ server = typeof raw.baseURL === 'string' ? raw.baseURL : undefined;
61
+ }
62
+ catch {
63
+ // A profile directory with no readable config is still a profile; say
64
+ // so rather than hiding it, because a half-written one is exactly what
65
+ // someone would be trying to diagnose.
66
+ }
67
+ rows.push({ name, active: name === active, server });
68
+ };
69
+ describe(keychain_1.DEFAULT_PROFILE, node_path_1.default.join(root, 'cli-config.json'));
70
+ try {
71
+ for (const entry of node_fs_1.default.readdirSync(node_path_1.default.join(root, 'profiles'), { withFileTypes: true })) {
72
+ if (!entry.isDirectory())
73
+ continue;
74
+ describe(entry.name, node_path_1.default.join(root, 'profiles', entry.name, 'cli-config.json'));
75
+ }
76
+ }
77
+ catch {
78
+ // No `profiles/` directory means no named profiles — not an error.
79
+ }
80
+ if (flags.json) {
81
+ this.log(JSON.stringify({ root, active, profiles: rows }, null, 2));
82
+ return;
83
+ }
84
+ // Width from the DATA, so a long profile name cannot run into the next
85
+ // column — `df-memory-verify-isolated` did exactly that at a fixed 24.
86
+ const width = Math.max(4, ...rows.map((row) => row.name.length)) + 2;
87
+ this.log('NAME'.padEnd(width) + 'ACTIVE'.padEnd(9) + 'SERVER');
88
+ for (const row of rows) {
89
+ this.log(row.name.padEnd(width) + (row.active ? 'yes' : '-').padEnd(9) + (row.server || '-'));
90
+ }
91
+ this.log('');
92
+ this.log(`Add one: ${this.config.bin} login --profile <name>`);
93
+ this.log(`Use one: ${this.config.bin} <command> --profile <name> (or OVERSKY_PROFILE)`);
94
+ }
95
+ }
96
+ exports.default = ProfilesList;
@@ -2,9 +2,19 @@ import { BaseCommand } from '../../base-command';
2
2
  export default class SearchSkills extends BaseCommand {
3
3
  static description: string;
4
4
  static examples: string[];
5
+ /**
6
+ * `skills search` and `search skills` are the SAME search, and both spellings
7
+ * stay. Neither said so, their one-liners differed, and only one carried short
8
+ * flags — so a reader picked one at random with no way to tell whether they had
9
+ * picked a deprecated spelling. Duplication that is deliberate should say so;
10
+ * duplication that is silent reads as a mistake either way.
11
+ */
12
+ static args: {
13
+ query: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
14
+ };
5
15
  static flags: {
6
16
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
7
- query: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
17
+ query: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
8
18
  category: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
9
19
  limit: import("@oclif/core/lib/interfaces").OptionFlag<number, import("@oclif/core/lib/interfaces").CustomOptions>;
10
20
  };
@@ -4,19 +4,36 @@ 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 format_1 = require("../../lib/format");
7
+ const search_query_1 = require("../../lib/search-query");
7
8
  class SearchSkills extends base_command_1.BaseCommand {
8
- static description = 'Search skills in the skrr store';
9
+ static description = 'Search skills in the skrr store by keyword (same as `skills search`)';
9
10
  static examples = [
10
- '<%= config.bin %> search skills --query "web scraping"',
11
- '<%= config.bin %> search skills --query summarize --category productivity',
12
- '<%= config.bin %> search skills --query translate --limit 10 --json',
11
+ '<%= config.bin %> search skills "web scraping"',
12
+ '<%= config.bin %> search skills summarize --category productivity',
13
+ '<%= config.bin %> search skills translate --limit 10 --json',
14
+ '<%= config.bin %> search skills -q translate',
13
15
  ];
16
+ /**
17
+ * `skills search` and `search skills` are the SAME search, and both spellings
18
+ * stay. Neither said so, their one-liners differed, and only one carried short
19
+ * flags — so a reader picked one at random with no way to tell whether they had
20
+ * picked a deprecated spelling. Duplication that is deliberate should say so;
21
+ * duplication that is silent reads as a mistake either way.
22
+ */
23
+ static args = {
24
+ query: core_1.Args.string({
25
+ description: 'Search term (same value as --query; pass either, not both)',
26
+ required: false,
27
+ ignoreStdin: true,
28
+ }),
29
+ };
14
30
  static flags = {
15
31
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
32
+ // See `skills search` — the positional satisfies it and the flag stays so
33
+ // existing scripts keep working.
16
34
  query: core_1.Flags.string({
17
35
  char: 'q',
18
36
  description: 'Search query',
19
- required: true,
20
37
  }),
21
38
  category: core_1.Flags.string({
22
39
  char: 'c',
@@ -29,11 +46,12 @@ class SearchSkills extends base_command_1.BaseCommand {
29
46
  };
30
47
  async run() {
31
48
  this.requireAuth();
32
- const { flags } = await this.parse(SearchSkills);
49
+ const { args, flags } = await this.parse(SearchSkills);
50
+ const query = (0, search_query_1.resolveSearchQuery)(this, args.query, flags.query, 'search skills');
33
51
  let response;
34
52
  try {
35
53
  response = await data_provider_1.dataService.getStoreSkills({
36
- q: flags.query,
54
+ q: query,
37
55
  category: flags.category,
38
56
  limit: flags.limit,
39
57
  });
@@ -48,7 +66,7 @@ class SearchSkills extends base_command_1.BaseCommand {
48
66
  const r = response;
49
67
  const items = Array.isArray(r) ? r : (r?.data ?? []);
50
68
  if (items.length === 0) {
51
- this.log('No skills found.');
69
+ this.log(`No skills found matching '${query}'.`);
52
70
  return;
53
71
  }
54
72
  const rows = items.map((s) => ({
@@ -8,5 +8,7 @@ export default class SkillsDetails extends BaseCommand {
8
8
  static flags: {
9
9
  json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
10
10
  };
11
+ /** A store 404 for a name that may be a skill on the caller's machine. */
12
+ private failOnStoreMiss;
11
13
  run(): Promise<void>;
12
14
  }
@@ -3,6 +3,7 @@ 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 local_skills_1 = require("../../lib/local-skills");
6
7
  class SkillsDetails extends base_command_1.BaseCommand {
7
8
  static description = 'Show full details for a skill';
8
9
  static examples = [
@@ -15,15 +16,37 @@ class SkillsDetails extends base_command_1.BaseCommand {
15
16
  static flags = {
16
17
  json: core_1.Flags.boolean({ description: 'Output as JSON' }),
17
18
  };
19
+ /** A store 404 for a name that may be a skill on the caller's machine. */
20
+ async failOnStoreMiss(err, id) {
21
+ if (err?.status === 404) {
22
+ this.failWithCliError({
23
+ message: (0, local_skills_1.describeStoreSkillMiss)(id, await (0, local_skills_1.findLocalSkillsNamed)(id), 'details'),
24
+ code: 'NOT_FOUND',
25
+ status: 404,
26
+ exit: 1,
27
+ retryable: false,
28
+ });
29
+ }
30
+ this.handleApiError(err);
31
+ }
18
32
  async run() {
19
33
  this.requireAuth();
20
34
  const { args, flags } = await this.parse(SkillsDetails);
35
+ // A path is never a store id: say so before asking the store (OSK-9412).
36
+ if ((0, local_skills_1.looksLikeSkillPath)(args.id)) {
37
+ this.failWithCliError({
38
+ message: (0, local_skills_1.describeStoreSkillMiss)(args.id, [], 'details'),
39
+ code: 'STORE_SKILL_ID_EXPECTED',
40
+ exit: 2,
41
+ retryable: false,
42
+ });
43
+ }
21
44
  let skill;
22
45
  try {
23
46
  skill = await data_provider_1.dataService.getSkill(args.id);
24
47
  }
25
48
  catch (err) {
26
- this.handleApiError(err);
49
+ await this.failOnStoreMiss(err, args.id);
27
50
  }
28
51
  if (flags.json) {
29
52
  this.log(JSON.stringify(skill, null, 2));
@@ -0,0 +1,32 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * Import a machine's local skills into an agent, each as a quick action.
4
+ *
5
+ * `skrr skills local` listed these skills and nothing in the CLI could act on
6
+ * them: the server route and the web dialog existed, and a headless operator or
7
+ * a locally-running coding agent had no way in (OSK-9659).
8
+ *
9
+ * The whole selection goes in ONE request, as the route requires — the agent's
10
+ * action library is one document field, and one request per skill is the
11
+ * lost-update race the route was built to avoid. Each skill comes back with its
12
+ * own status, because partial success is the normal result.
13
+ */
14
+ export default class SkillsImportAsActions extends BaseCommand {
15
+ static description: string;
16
+ static examples: string[];
17
+ static strict: boolean;
18
+ static args: {
19
+ skills: import("@oclif/core/lib/interfaces").Arg<string | undefined, Record<string, unknown>>;
20
+ };
21
+ static flags: {
22
+ agent: import("@oclif/core/lib/interfaces").OptionFlag<string, import("@oclif/core/lib/interfaces").CustomOptions>;
23
+ daemon: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
24
+ scope: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
25
+ space: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
26
+ label: import("@oclif/core/lib/interfaces").OptionFlag<string[] | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
27
+ outcome: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
28
+ 'attach-to-space': import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
29
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
30
+ };
31
+ run(): Promise<void>;
32
+ }
@@ -0,0 +1,175 @@
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_actions_1 = require("../../lib/agent-actions");
7
+ const format_1 = require("../../lib/format");
8
+ const local_skills_1 = require("../../lib/local-skills");
9
+ /**
10
+ * Import a machine's local skills into an agent, each as a quick action.
11
+ *
12
+ * `skrr skills local` listed these skills and nothing in the CLI could act on
13
+ * them: the server route and the web dialog existed, and a headless operator or
14
+ * a locally-running coding agent had no way in (OSK-9659).
15
+ *
16
+ * The whole selection goes in ONE request, as the route requires — the agent's
17
+ * action library is one document field, and one request per skill is the
18
+ * lost-update race the route was built to avoid. Each skill comes back with its
19
+ * own status, because partial success is the normal result.
20
+ */
21
+ class SkillsImportAsActions extends base_command_1.BaseCommand {
22
+ static description = "Import skills from a machine into an agent, each as a quick action (discover them with 'skills local')";
23
+ static examples = [
24
+ '<%= config.bin %> skills import-as-actions dogfood db-performance --agent <agent-id>',
25
+ '<%= config.bin %> skills import-as-actions dogfood --agent <agent-id> --space <space-id> --outcome durable',
26
+ '<%= config.bin %> skills import-as-actions dogfood --agent <agent-id> --label dogfood',
27
+ ];
28
+ static strict = false;
29
+ static args = {
30
+ skills: core_1.Args.string({
31
+ description: 'Skill name(s) as `skills local` lists them. Repeatable, 1-20.',
32
+ required: false,
33
+ ignoreStdin: true,
34
+ }),
35
+ };
36
+ static flags = {
37
+ agent: core_1.Flags.string({
38
+ description: 'Agent that will own the actions (needs EDIT)',
39
+ required: true,
40
+ }),
41
+ daemon: core_1.Flags.string({
42
+ description: 'Which machine to import from. Defaults to your only connected daemon.',
43
+ }),
44
+ scope: core_1.Flags.string({
45
+ description: 'Where the actions are offered. Implied by --space / --label.',
46
+ options: [...agent_actions_1.AGENT_ACTION_SCOPE_CHOICES],
47
+ }),
48
+ space: core_1.Flags.string({ description: 'Offer only in this space (implies --scope space)' }),
49
+ label: core_1.Flags.string({
50
+ description: 'Offer in every space carrying this Label — id or name (repeatable, any-of; implies --scope label)',
51
+ multiple: true,
52
+ }),
53
+ outcome: core_1.Flags.string({
54
+ description: "What a run leaves behind: 'durable' keeps its task on the board",
55
+ options: [...agent_actions_1.AGENT_ACTION_OUTCOME_CHOICES],
56
+ }),
57
+ 'attach-to-space': core_1.Flags.string({
58
+ description: "Also add each imported skill to this space's skill library (needs EDIT)",
59
+ }),
60
+ json: core_1.Flags.boolean({ description: 'Output the per-skill results as JSON' }),
61
+ };
62
+ async run() {
63
+ this.requireAuth();
64
+ const { argv, flags } = await this.parse(SkillsImportAsActions);
65
+ const names = [...new Set(argv.map((name) => name.trim()).filter(Boolean))];
66
+ if (names.length === 0) {
67
+ this.error('Name at least one skill. List them with: skills local', { exit: 1 });
68
+ }
69
+ if (names.length > 20) {
70
+ this.error(`At most 20 skills per import (got ${names.length}).`, { exit: 1 });
71
+ }
72
+ let daemonId = flags.daemon;
73
+ if (!daemonId) {
74
+ let daemons;
75
+ try {
76
+ daemons = await data_provider_1.dataService.getDaemons();
77
+ }
78
+ catch (err) {
79
+ this.handleApiError(err);
80
+ return;
81
+ }
82
+ const choice = (0, local_skills_1.chooseDaemon)(daemons.active ?? []);
83
+ if (!('daemonId' in choice)) {
84
+ const listed = choice.candidates.map((d) => ` --daemon ${d.daemonId}${d.deviceName ? ` (${d.deviceName})` : ''}`);
85
+ this.error([choice.problem, ...listed].join('\n'), { exit: 2 });
86
+ }
87
+ daemonId = choice.daemonId;
88
+ }
89
+ // Resolve each name against the machine's own listing. The listing's
90
+ // `pathHash` round-trips to the route so a skill renamed or replaced since
91
+ // it was listed is refused rather than silently importing something else,
92
+ // and an unknown name fails here with the list, not as a per-skill error.
93
+ let listing;
94
+ let version;
95
+ try {
96
+ const response = await data_provider_1.dataService.getDaemonSkills(daemonId);
97
+ listing = response.skills ?? [];
98
+ version = response.version;
99
+ }
100
+ catch (err) {
101
+ this.handleApiError(err);
102
+ return;
103
+ }
104
+ const byName = new Map(listing.map((skill) => [String(skill.name), skill]));
105
+ const unknown = names.filter((name) => !byName.has(name));
106
+ if (unknown.length > 0) {
107
+ const tooOld = (0, local_skills_1.daemonPredates)(version, local_skills_1.SKILL_CONVENTION_DISCOVERY_SINCE);
108
+ this.error(tooOld
109
+ ? `This daemon (${version}) cannot see skills in the conventions — discovery arrived in ${local_skills_1.SKILL_CONVENTION_DISCOVERY_SINCE}. Update it, run \`${this.config.bin} daemon restart\`, then try again.`
110
+ : `Not on ${daemonId}: ${unknown.join(', ')}. List what is there with: ${this.config.bin} skills local --daemon ${daemonId}`, { exit: 1 });
111
+ }
112
+ let scopeParams = {};
113
+ try {
114
+ const scope = await (0, agent_actions_1.resolveAgentActionScopeFlags)(flags);
115
+ if (scope?.kind === 'space')
116
+ scopeParams = { scope: 'space', spaceId: scope.spaceId };
117
+ else if (scope?.kind === 'label')
118
+ scopeParams = { scope: 'label', labelIds: scope.labelIds };
119
+ else if (scope?.kind === 'general')
120
+ scopeParams = { scope: 'general' };
121
+ }
122
+ catch (err) {
123
+ this.error(err.message, { exit: 1 });
124
+ }
125
+ let result;
126
+ try {
127
+ result = await data_provider_1.dataService.importDaemonSkillsAsActions({
128
+ daemonId,
129
+ targetAgentId: flags.agent,
130
+ skills: names.map((name) => {
131
+ const pathHash = byName.get(name)?.pathHash;
132
+ return pathHash ? { name, pathHash: String(pathHash) } : { name };
133
+ }),
134
+ ...scopeParams,
135
+ ...(flags.outcome ? { outcome: flags.outcome } : {}),
136
+ ...(flags['attach-to-space'] ? { attachToSpaceId: flags['attach-to-space'] } : {}),
137
+ });
138
+ }
139
+ catch (err) {
140
+ this.handleApiError(err);
141
+ return;
142
+ }
143
+ const failed = result.results.filter((r) => r.status === 'failed');
144
+ if (flags.json) {
145
+ this.log(JSON.stringify(result, null, 2));
146
+ }
147
+ else {
148
+ (0, format_1.renderTable)(result.results.map((r) => ({
149
+ skill: r.name,
150
+ import: r.status,
151
+ action: r.actionCreated
152
+ ? 'created'
153
+ : (r.actionSkippedReason ??
154
+ r.actionError ??
155
+ (r.status === 'failed' ? '-' : 'not added')),
156
+ detail: r.error ?? '',
157
+ })), [
158
+ { key: 'skill', header: 'SKILL' },
159
+ { key: 'import', header: 'IMPORT' },
160
+ { key: 'action', header: 'ACTION' },
161
+ { key: 'detail', header: 'DETAIL' },
162
+ ], (line) => this.log(line));
163
+ this.log(`${result.actionsCreated} action(s) created on ${result.targetAgentId}; ` +
164
+ `${failed.length} skill(s) failed.`);
165
+ if (result.actionsError) {
166
+ this.log(`The skills imported, but their actions could not be added: ${result.actionsError.message}`);
167
+ }
168
+ }
169
+ // Partial success is normal and reported above; exit non-zero only when
170
+ // something the caller asked for did not happen.
171
+ if (failed.length > 0 || result.actionsError)
172
+ this.exit(1);
173
+ }
174
+ }
175
+ exports.default = SkillsImportAsActions;
@@ -10,5 +10,7 @@ export default class SkillsInstall extends BaseCommand {
10
10
  agent: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
11
11
  service: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
12
12
  };
13
+ /** A store 404 for a name that may be a skill on the caller's machine. */
14
+ private failOnStoreMiss;
13
15
  run(): Promise<void>;
14
16
  }
@@ -3,6 +3,7 @@ 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 local_skills_1 = require("../../lib/local-skills");
6
7
  class SkillsInstall extends base_command_1.BaseCommand {
7
8
  static description = 'Install a skill from the store to a service (agent)';
8
9
  static examples = [
@@ -25,6 +26,19 @@ class SkillsInstall extends base_command_1.BaseCommand {
25
26
  hidden: true,
26
27
  }),
27
28
  };
29
+ /** A store 404 for a name that may be a skill on the caller's machine. */
30
+ async failOnStoreMiss(err, id) {
31
+ if (err?.status === 404) {
32
+ this.failWithCliError({
33
+ message: (0, local_skills_1.describeStoreSkillMiss)(id, await (0, local_skills_1.findLocalSkillsNamed)(id), 'install'),
34
+ code: 'NOT_FOUND',
35
+ status: 404,
36
+ exit: 1,
37
+ retryable: false,
38
+ });
39
+ }
40
+ this.handleApiError(err);
41
+ }
28
42
  async run() {
29
43
  this.requireAuth();
30
44
  const { args, flags } = await this.parse(SkillsInstall);
@@ -33,6 +47,15 @@ class SkillsInstall extends base_command_1.BaseCommand {
33
47
  process.stderr.write('[skrr] --service is deprecated for `skills install`; use --agent instead.\n');
34
48
  }
35
49
  const targetAgentId = flags.agent ?? flags.service ?? null;
50
+ // A path is never a store id: say so before asking the store (OSK-9412).
51
+ if ((0, local_skills_1.looksLikeSkillPath)(args.id)) {
52
+ this.failWithCliError({
53
+ message: (0, local_skills_1.describeStoreSkillMiss)(args.id, [], 'install'),
54
+ code: 'STORE_SKILL_ID_EXPECTED',
55
+ exit: 2,
56
+ retryable: false,
57
+ });
58
+ }
36
59
  let result;
37
60
  try {
38
61
  result = await data_provider_1.dataService.installSkill({
@@ -41,7 +64,7 @@ class SkillsInstall extends base_command_1.BaseCommand {
41
64
  });
42
65
  }
43
66
  catch (err) {
44
- this.handleApiError(err);
67
+ await this.failOnStoreMiss(err, args.id);
45
68
  }
46
69
  if (flags.json) {
47
70
  this.log(JSON.stringify(result, null, 2));
@@ -15,7 +15,7 @@ const format_1 = require("../../lib/format");
15
15
  * The latter is what a chat user actually sees.
16
16
  */
17
17
  class SkillsInvocable extends base_command_1.BaseCommand {
18
- static description = 'List skills callable via /skill-name (attached + accessible globals; broader than `agents skills list`)';
18
+ static description = 'List library skills callable via /skill-name (attached + accessible globals). Skills on your machine: `skrr skills local`';
19
19
  static examples = [
20
20
  '<%= config.bin %> skills invocable',
21
21
  '<%= config.bin %> skills invocable --agent <agent-id>',
@@ -51,7 +51,11 @@ class SkillsInvocable extends base_command_1.BaseCommand {
51
51
  const scope = flags.agent ? `agent ${flags.agent}` : 'your global scope';
52
52
  const attached = items.filter((s) => s.agentId != null).length;
53
53
  const globals = items.length - attached;
54
- this.log(`Invocable set for ${scope}: ${items.length} total (${attached} attached + ${globals} accessible globals).`);
54
+ this.log(`Invocable library skills for ${scope}: ${items.length} total (${attached} attached + ${globals} accessible globals).`);
55
+ // Two kinds of skill answer to `/name`, and this counts only one. Said on
56
+ // every run, not just at zero: a non-empty library list read as "all my
57
+ // skills" hides the other kind just as well (OSK-9478).
58
+ this.log('Skills in your machine’s .claude/skills and sibling roots are listed by `skrr skills local`.');
55
59
  if (items.length === 0)
56
60
  return;
57
61
  const rows = items.map((s) => ({
@@ -0,0 +1,40 @@
1
+ import { BaseCommand } from '../../base-command';
2
+ /**
3
+ * The skills that are ON THIS MACHINE.
4
+ *
5
+ * Every other command under `skills` is about the STORE or a cloud scope —
6
+ * `browse`, `library`, `install`, `invocable`. Not one of them reads the
7
+ * filesystem, and the topic's own description ("Browse and install skills from
8
+ * the skill store") names the store, so the one topic named after the thing was
9
+ * about something else entirely. A machine with 79 discovered skills across
10
+ * seven roots could not show one line of them through `skrr`, and
11
+ * `skills invocable` — the command whose description promises breadth —
12
+ * answered `0 total` beside them.
13
+ *
14
+ * The inventory already existed and was already published: the daemon walks its
15
+ * roots at registration (`reportableSkills`), the row lands in
16
+ * `daemon:active:*`, and `GET /api/daemons/:id/skills` serves it. This reads
17
+ * that. Nothing new is collected.
18
+ *
19
+ * It is also the repo's own rule rather than a preference: "the CLI is how a
20
+ * headless operator and a locally-running coding agent touch the platform, so
21
+ * an endpoint the API exposes must be reachable from `skrr`."
22
+ *
23
+ * WHAT THIS IS NOT: the list a `/`-menu selection resolves against. That walk
24
+ * happens at session start from the SESSION's effective cwd, which may be a
25
+ * worktree or an ephemeral workspace; this one is the daemon's own cwd. They
26
+ * usually agree and are not required to, so the header says which machine and
27
+ * directory the answer is about rather than implying it is the only one.
28
+ */
29
+ export default class SkillsLocal extends BaseCommand {
30
+ static description: string;
31
+ static examples: string[];
32
+ static flags: {
33
+ json: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
34
+ quiet: import("@oclif/core/lib/interfaces").BooleanFlag<boolean>;
35
+ daemon: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
36
+ root: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
37
+ triggers: import("@oclif/core/lib/interfaces").OptionFlag<string | undefined, import("@oclif/core/lib/interfaces").CustomOptions>;
38
+ };
39
+ run(): Promise<void>;
40
+ }