opensteer 0.5.0 → 0.5.2

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.
package/CHANGELOG.md CHANGED
@@ -36,6 +36,9 @@
36
36
  timeout/stale-target cases more accurately.
37
37
  - Cloud action failures now accept optional structured failure details and map
38
38
  them to `OpensteerActionError` when available.
39
+ - Added native skills installer commands (`opensteer skills install` and
40
+ `opensteer skills add`) that wrap the upstream `skills` CLI to install the
41
+ first-party `opensteer` skill pack without requiring separate global setup.
39
42
  - Docs: refreshed README and getting-started guidance to match current SDK/CLI
40
43
  behavior and env vars.
41
44
  - Docs: added CLI reference and docs index.
package/README.md CHANGED
@@ -1,128 +1,179 @@
1
1
  # Opensteer
2
2
 
3
- Open-source browser automation SDK for coding agents and deterministic replay.
3
+ Browser automation framework for developers and AI agents with deterministic replay.
4
4
 
5
- Opensteer combines descriptor-aware actions, resilient selector persistence,
6
- clean HTML snapshots, and first-class local or cloud runtime support.
5
+ Opensteer gives you one API for local and cloud runs, description-based actions,
6
+ structured extraction, and CUA agent workflows.
7
7
 
8
- ## Requirements
8
+ ## Install
9
9
 
10
- - Node.js `>=20`
11
- - A browser environment supported by Playwright
12
- - API key for your selected model provider if you use LLM resolve/extract
10
+ Main setup (recommended):
13
11
 
14
- ## Install
12
+ ```bash
13
+ npm i -g opensteer
14
+ opensteer skills install
15
+ ```
16
+
17
+ SDK package (when importing `Opensteer` in app code):
15
18
 
16
19
  ```bash
17
20
  # npm
18
21
  npm install opensteer
22
+
19
23
  # pnpm
20
24
  pnpm add opensteer
25
+
21
26
  # bun
22
27
  bun add opensteer
23
28
  ```
24
29
 
25
- Repo maintenance defaults to `pnpm`, with compatibility checks for `npm` and
26
- `bun` in CI.
30
+ ## Requirements
31
+
32
+ - Node.js `>=20`
33
+ - Playwright-supported browser runtime
34
+ - Model provider API key for LLM-powered resolution/extraction/CUA
27
35
 
28
- If your environment skips Playwright browser downloads, run:
36
+ If browser binaries are missing:
29
37
 
30
38
  ```bash
31
39
  npx playwright install chromium
32
40
  ```
33
41
 
34
- ## Quickstart (SDK)
42
+ ## What It Does
43
+
44
+ - Unified local/cloud execution with the same API surface
45
+ - Descriptor-aware actions with selector persistence for replay
46
+ - Structured extraction with typed schemas
47
+ - CUA agent support (`openai`, `anthropic`, `google`)
48
+
49
+ ## Quick Start: SDK
35
50
 
36
51
  ```ts
37
52
  import { Opensteer } from "opensteer";
38
53
 
39
- const opensteer = new Opensteer({ name: "my-scraper" });
40
- await opensteer.launch({ headless: false });
54
+ const opensteer = new Opensteer({ name: "quickstart" });
41
55
 
42
56
  try {
57
+ await opensteer.launch();
43
58
  await opensteer.goto("https://example.com");
44
- const html = await opensteer.snapshot();
45
- console.log(html.slice(0, 500));
46
59
 
47
- await opensteer.click({ description: "main call to action", element: 3 });
60
+ await opensteer.snapshot({ mode: "action" });
61
+ await opensteer.click({ description: "main call to action" });
62
+
63
+ await opensteer.snapshot({ mode: "extraction" });
64
+ const data = await opensteer.extract({
65
+ description: "hero section",
66
+ schema: { title: "string", href: "string" },
67
+ });
68
+
69
+ console.log(data);
48
70
  } finally {
49
71
  await opensteer.close();
50
72
  }
51
73
  ```
52
74
 
53
- ## CUA Agent
75
+ ## Quick Start: CUA Agent
54
76
 
55
77
  ```ts
56
78
  import { Opensteer } from "opensteer";
57
79
 
58
- const opensteer = new Opensteer({
59
- model: "openai/computer-use-preview",
60
- });
80
+ const opensteer = new Opensteer({ model: "openai/computer-use-preview" });
61
81
 
62
- await opensteer.launch();
82
+ try {
83
+ await opensteer.launch();
84
+ const agent = opensteer.agent({ mode: "cua" });
85
+ const result = await agent.execute({
86
+ instruction: "Go to Hacker News and open the top story.",
87
+ maxSteps: 20,
88
+ });
89
+ console.log(result.message);
90
+ } finally {
91
+ await opensteer.close();
92
+ }
93
+ ```
63
94
 
64
- const agent = opensteer.agent({
65
- mode: "cua",
66
- });
95
+ ## Quick Start: CLI
67
96
 
68
- const result = await agent.execute({
69
- instruction: "Go to Hacker News and open the top story.",
70
- maxSteps: 20,
71
- highlightCursor: true,
72
- });
97
+ ```bash
98
+ # Open a browser session and bind a selector namespace
99
+ opensteer open https://example.com --session demo --name quickstart
73
100
 
74
- console.log(result.message);
75
- await opensteer.close();
101
+ # Action snapshot + interaction
102
+ opensteer snapshot action --session demo
103
+ opensteer click --description "main call to action" --session demo
104
+
105
+ # Extraction snapshot + structured extract
106
+ opensteer snapshot extraction --session demo
107
+ opensteer extract '{"title":"string","href":"string"}' --description "hero section" --session demo
108
+
109
+ # Close session
110
+ opensteer close --session demo
76
111
  ```
77
112
 
78
- Supported CUA providers in V1: `openai`, `anthropic`, `google`.
113
+ For non-interactive runs, set `OPENSTEER_SESSION` or `OPENSTEER_CLIENT_ID`.
114
+
115
+ ## For AI Agents
79
116
 
80
- ## Quickstart (CLI)
117
+ Use this workflow to keep scripts replayable and maintainable:
81
118
 
82
- Opensteer CLI separates runtime routing from selector namespace routing.
119
+ 1. Use Opensteer APIs (`goto`, `snapshot`, `click`, `input`, `extract`) instead of raw Playwright calls.
120
+ 2. Keep namespace consistent: SDK `name` must match CLI `--name`.
121
+ 3. Take `snapshot({ mode: "action" })` before actions and `snapshot({ mode: "extraction" })` before extraction.
122
+ 4. Prefer `description` targeting for persistence and deterministic reruns.
123
+ 5. Always wrap runs in `try/finally` and call `close()`.
83
124
 
84
- - Runtime routing: `--session` or `OPENSTEER_SESSION`
85
- - Selector namespace: `--name` or `OPENSTEER_NAME` (used by `open`)
125
+ First-party skills:
126
+
127
+ - [skills/opensteer/SKILL.md](skills/opensteer/SKILL.md)
128
+ - [skills/electron/SKILL.md](skills/electron/SKILL.md)
129
+ - [skills/README.md](skills/README.md)
130
+
131
+ Install the Opensteer skill pack:
86
132
 
87
133
  ```bash
88
- opensteer open https://example.com --session agent-a --name product-scraper
89
- opensteer snapshot --session agent-a
90
- opensteer click 3 --session agent-a
91
- opensteer status --session agent-a
92
- opensteer close --session agent-a
134
+ opensteer skills install
93
135
  ```
94
136
 
95
- In non-interactive environments, set `OPENSTEER_SESSION` or
96
- `OPENSTEER_CLIENT_ID` explicitly.
97
-
98
- ## Resolution and Replay Model
137
+ Fallback (direct upstream `skills` CLI):
99
138
 
100
- For descriptor-aware actions (`click`, `input`, `hover`, `select`, `scroll`):
139
+ ```bash
140
+ npx skills add https://github.com/steerlabs/opensteer-skills --skill opensteer
141
+ ```
101
142
 
102
- 1. Reuse persisted path for `description`
103
- 2. Use `element` counter from snapshot
104
- 3. Use explicit CSS `selector`
105
- 4. Use built-in LLM resolution (`description` required)
106
- 5. Throw actionable error
143
+ Claude Code marketplace plugin:
107
144
 
108
- When steps 2-4 succeed and `description` is present, Opensteer persists the
109
- path for deterministic replay in `.opensteer/selectors/<namespace>`.
145
+ ```text
146
+ /plugin marketplace add steerlabs/opensteer
147
+ /plugin install opensteer@opensteer-marketplace
148
+ ```
110
149
 
111
150
  ## Cloud Mode
112
151
 
113
- Opensteer defaults to local mode.
152
+ Opensteer defaults to local mode. Enable cloud mode with env or constructor options:
114
153
 
115
- - `OPENSTEER_MODE=local|cloud`
116
- - `OPENSTEER_API_KEY` or `cloud.apiKey` required in cloud mode
117
- - `OPENSTEER_BASE_URL` or `cloud.baseUrl` to override the default cloud host
118
- - `OPENSTEER_AUTH_SCHEME` or `cloud.authScheme` for auth header mode
119
- (`api-key` or `bearer`)
154
+ ```bash
155
+ OPENSTEER_MODE=cloud
156
+ OPENSTEER_API_KEY=<your_api_key>
157
+ ```
158
+
159
+ - `OPENSTEER_BASE_URL` overrides the default cloud host
160
+ - `OPENSTEER_AUTH_SCHEME` supports `api-key` (default) or `bearer`
120
161
  - `cloud: true` or a `cloud` options object overrides `OPENSTEER_MODE`
162
+ - Cloud mode is fail-fast (no automatic fallback to local)
163
+ - `Opensteer.from(page)`, `uploadFile`, `exportCookies`, and `importCookies` are local-only
164
+
165
+ ## Resolution and Replay
166
+
167
+ For descriptor-aware actions (`click`, `input`, `hover`, `select`, `scroll`):
121
168
 
122
- `.env` files are auto-loaded from `storage.rootDir` (default `process.cwd()`)
123
- in this order: `.env.<NODE_ENV>.local`, `.env.local` (except in test),
124
- `.env.<NODE_ENV>`, `.env`. Existing `process.env` values are not overwritten.
125
- Set `OPENSTEER_DISABLE_DOTENV_AUTOLOAD=true` to disable.
169
+ 1. Reuse persisted selector path from `description`
170
+ 2. Try snapshot counter (`element`)
171
+ 3. Try explicit CSS selector (`selector`)
172
+ 4. Use LLM resolution (`description` required)
173
+ 5. Return actionable error
174
+
175
+ When step 2-4 succeeds and `description` is present, selector paths are cached
176
+ in `.opensteer/selectors/<namespace>` for deterministic replay.
126
177
 
127
178
  ## Docs
128
179
 
@@ -133,13 +184,23 @@ Set `OPENSTEER_DISABLE_DOTENV_AUTOLOAD=true` to disable.
133
184
  - [Selectors and Storage](docs/selectors.md)
134
185
  - [HTML Cleaning and Snapshot Modes](docs/html-cleaning.md)
135
186
  - [Live Web Validation Suite](docs/live-web-tests.md)
187
+ - [Skills](docs/skills.md)
188
+
189
+ ## Development
190
+
191
+ ```bash
192
+ pnpm install
193
+ pnpm typecheck
194
+ pnpm test
195
+ pnpm build
196
+ ```
136
197
 
137
198
  ## Community
138
199
 
139
- - [Contributing Guide](CONTRIBUTING.md)
200
+ - [Contributing](CONTRIBUTING.md)
140
201
  - [Code of Conduct](CODE_OF_CONDUCT.md)
141
202
  - [Security Policy](SECURITY.md)
142
- - [Support](SUPPORT.md)
203
+ - [Discussions](https://github.com/steerlabs/opensteer/discussions)
143
204
  - [Changelog](CHANGELOG.md)
144
205
 
145
206
  ## License
package/bin/opensteer.mjs CHANGED
@@ -14,10 +14,38 @@ import {
14
14
  import { connect } from 'net'
15
15
  import { tmpdir } from 'os'
16
16
  import { dirname, join } from 'path'
17
- import { fileURLToPath } from 'url'
17
+ import { fileURLToPath, pathToFileURL } from 'url'
18
18
 
19
19
  const __dirname = dirname(fileURLToPath(import.meta.url))
20
20
  const SERVER_SCRIPT = join(__dirname, '..', 'dist', 'cli', 'server.js')
21
+ const SKILLS_INSTALLER_SCRIPT = join(
22
+ __dirname,
23
+ '..',
24
+ 'dist',
25
+ 'cli',
26
+ 'skills-installer.js'
27
+ )
28
+ const SKILLS_HELP_TEXT = `Usage: opensteer skills <install|add> [options]
29
+
30
+ Installs the first-party Opensteer skill using the upstream "skills" CLI.
31
+
32
+ Commands:
33
+ install Install the opensteer skill
34
+ add Alias for install
35
+
36
+ Supported Options:
37
+ -a, --agent <agents...> Target specific agent(s)
38
+ -g, --global Install globally
39
+ -y, --yes Skip confirmations
40
+ --copy Copy files instead of symlinking
41
+ --all Install to all agents
42
+ -h, --help Show this help
43
+
44
+ Examples:
45
+ opensteer skills install
46
+ opensteer skills add --agent codex --global --yes
47
+ opensteer skills install --all --yes
48
+ `
21
49
 
22
50
  const CONNECT_TIMEOUT = 15000
23
51
  const POLL_INTERVAL = 100
@@ -735,6 +763,46 @@ function toObject(value) {
735
763
  return value
736
764
  }
737
765
 
766
+ function isSkillsHelpRequest(args) {
767
+ if (args.length === 0) return true
768
+
769
+ const [subcommand, ...rest] = args
770
+ if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
771
+ return true
772
+ }
773
+
774
+ if (subcommand !== 'install' && subcommand !== 'add') {
775
+ return false
776
+ }
777
+
778
+ return rest.includes('--help') || rest.includes('-h')
779
+ }
780
+
781
+ function printSkillsHelp() {
782
+ process.stdout.write(SKILLS_HELP_TEXT)
783
+ }
784
+
785
+ async function runSkillsSubcommand(args) {
786
+ if (isSkillsHelpRequest(args)) {
787
+ printSkillsHelp()
788
+ return
789
+ }
790
+
791
+ if (!existsSync(SKILLS_INSTALLER_SCRIPT)) {
792
+ throw new Error(
793
+ `Skills installer module not found: ${SKILLS_INSTALLER_SCRIPT}. Run the build script first.`
794
+ )
795
+ }
796
+
797
+ const moduleUrl = pathToFileURL(SKILLS_INSTALLER_SCRIPT).href
798
+ const { runOpensteerSkillsInstaller } = await import(moduleUrl)
799
+
800
+ const exitCode = await runOpensteerSkillsInstaller(args)
801
+ if (exitCode !== 0) {
802
+ process.exit(exitCode)
803
+ }
804
+ }
805
+
738
806
  function printHelp() {
739
807
  console.log(`Usage: opensteer <command> [options]
740
808
 
@@ -794,6 +862,11 @@ Utility:
794
862
  wait-selector <selector> Wait for selector
795
863
  extract <schema-json> Extract structured data
796
864
 
865
+ Skills:
866
+ skills install [options] Install Opensteer skill pack for supported agents
867
+ skills add [options] Alias for "skills install"
868
+ skills --help Show skills installer help
869
+
797
870
  Global Flags:
798
871
  --session <id> Runtime session id for daemon/browser routing
799
872
  --name <namespace> Selector namespace for cache storage on 'open'
@@ -820,6 +893,19 @@ Environment:
820
893
  }
821
894
 
822
895
  async function main() {
896
+ const rawArgs = process.argv.slice(2)
897
+ if (rawArgs[0] === 'skills') {
898
+ try {
899
+ await runSkillsSubcommand(rawArgs.slice(1))
900
+ } catch (err) {
901
+ const message =
902
+ err instanceof Error ? err.message : 'Failed to run skills command'
903
+ process.stderr.write(`${message}\n`)
904
+ process.exit(1)
905
+ }
906
+ return
907
+ }
908
+
823
909
  const { command, flags, positional } = parseArgs(process.argv)
824
910
 
825
911
  if (command === 'sessions') {
@@ -0,0 +1,230 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/cli/skills-installer.ts
21
+ var skills_installer_exports = {};
22
+ __export(skills_installer_exports, {
23
+ createSkillsInstallInvocation: () => createSkillsInstallInvocation,
24
+ parseOpensteerSkillsArgs: () => parseOpensteerSkillsArgs,
25
+ resolveLocalSkillSourcePath: () => resolveLocalSkillSourcePath,
26
+ resolveSkillsCliPath: () => resolveSkillsCliPath,
27
+ runOpensteerSkillsInstaller: () => runOpensteerSkillsInstaller
28
+ });
29
+ module.exports = __toCommonJS(skills_installer_exports);
30
+ var import_node_child_process = require("child_process");
31
+ var import_node_module = require("module");
32
+ var import_node_fs = require("fs");
33
+ var import_node_path = require("path");
34
+ var HELP_TEXT = `Usage: opensteer skills <install|add> [options]
35
+
36
+ Installs the first-party Opensteer skill using the upstream "skills" CLI.
37
+
38
+ Commands:
39
+ install Install the opensteer skill
40
+ add Alias for install
41
+
42
+ Supported Options:
43
+ -a, --agent <agents...> Target specific agent(s)
44
+ -g, --global Install globally
45
+ -y, --yes Skip confirmations
46
+ --copy Copy files instead of symlinking
47
+ --all Install to all agents
48
+ -h, --help Show this help
49
+
50
+ Examples:
51
+ opensteer skills install
52
+ opensteer skills add --agent codex --global --yes
53
+ opensteer skills install --all --yes
54
+ `;
55
+ function parseOpensteerSkillsArgs(rawArgs) {
56
+ if (rawArgs.length === 0) {
57
+ return { mode: "help", passthroughArgs: [] };
58
+ }
59
+ const [subcommand, ...rest] = rawArgs;
60
+ if (subcommand === "help" || subcommand === "--help" || subcommand === "-h") {
61
+ return { mode: "help", passthroughArgs: [] };
62
+ }
63
+ if (subcommand !== "install" && subcommand !== "add") {
64
+ return {
65
+ mode: "error",
66
+ passthroughArgs: [],
67
+ error: `Unsupported skills subcommand "${subcommand}". Use "install" or "add".`
68
+ };
69
+ }
70
+ const passthroughArgs = [];
71
+ for (let i = 0; i < rest.length; i += 1) {
72
+ const arg = rest[i];
73
+ if (arg === "--help" || arg === "-h") {
74
+ return { mode: "help", passthroughArgs: [] };
75
+ }
76
+ if (arg === "--global" || arg === "-g") {
77
+ passthroughArgs.push(arg);
78
+ continue;
79
+ }
80
+ if (arg === "--yes" || arg === "-y") {
81
+ passthroughArgs.push(arg);
82
+ continue;
83
+ }
84
+ if (arg === "--copy" || arg === "--all") {
85
+ passthroughArgs.push(arg);
86
+ continue;
87
+ }
88
+ if (arg === "--agent" || arg === "-a") {
89
+ passthroughArgs.push(arg);
90
+ if (i + 1 >= rest.length || rest[i + 1]?.startsWith("-")) {
91
+ return {
92
+ mode: "error",
93
+ passthroughArgs: [],
94
+ error: `${arg} requires at least one value.`
95
+ };
96
+ }
97
+ while (i + 1 < rest.length && !rest[i + 1]?.startsWith("-")) {
98
+ i += 1;
99
+ const agent = rest[i];
100
+ if (agent) {
101
+ passthroughArgs.push(agent);
102
+ }
103
+ }
104
+ continue;
105
+ }
106
+ if (arg.startsWith("-")) {
107
+ return {
108
+ mode: "error",
109
+ passthroughArgs: [],
110
+ error: `Unsupported option "${arg}" for "opensteer skills".`
111
+ };
112
+ }
113
+ return {
114
+ mode: "error",
115
+ passthroughArgs: [],
116
+ error: `Unexpected argument "${arg}".`
117
+ };
118
+ }
119
+ return {
120
+ mode: "install",
121
+ passthroughArgs
122
+ };
123
+ }
124
+ function resolveLocalSkillSourcePath() {
125
+ const packageRoot = resolvePackageRoot();
126
+ const sourcePath = (0, import_node_path.join)(packageRoot, "skills");
127
+ if (!(0, import_node_fs.existsSync)(sourcePath)) {
128
+ throw new Error(
129
+ `Opensteer skill source was not found at "${sourcePath}".`
130
+ );
131
+ }
132
+ return sourcePath;
133
+ }
134
+ function resolveSkillsCliPath() {
135
+ const require2 = (0, import_node_module.createRequire)(resolveCliEntrypointPath());
136
+ const skillsPackagePath = require2.resolve("skills/package.json");
137
+ const skillsPackageDir = (0, import_node_path.dirname)(skillsPackagePath);
138
+ const cliPath = (0, import_node_path.join)(skillsPackageDir, "bin", "cli.mjs");
139
+ if (!(0, import_node_fs.existsSync)(cliPath)) {
140
+ throw new Error(`skills CLI entrypoint was not found at "${cliPath}".`);
141
+ }
142
+ return cliPath;
143
+ }
144
+ function resolveCliEntrypointPath() {
145
+ const cliEntrypoint = process.argv[1];
146
+ if (!cliEntrypoint) {
147
+ throw new Error("Unable to resolve CLI entrypoint path for skills installer.");
148
+ }
149
+ return (0, import_node_fs.realpathSync)(cliEntrypoint);
150
+ }
151
+ function resolvePackageRoot() {
152
+ const cliEntrypointPath = resolveCliEntrypointPath();
153
+ const binDir = (0, import_node_path.dirname)(cliEntrypointPath);
154
+ return (0, import_node_path.resolve)(binDir, "..");
155
+ }
156
+ function createSkillsInstallInvocation(args) {
157
+ return {
158
+ cliPath: args.skillsCliPath,
159
+ cliArgs: [
160
+ "add",
161
+ args.localSkillSourcePath,
162
+ "--skill",
163
+ "opensteer",
164
+ ...args.passthroughArgs
165
+ ]
166
+ };
167
+ }
168
+ async function spawnInvocation(invocation) {
169
+ return await new Promise((resolvePromise, rejectPromise) => {
170
+ const child = (0, import_node_child_process.spawn)(process.execPath, [invocation.cliPath, ...invocation.cliArgs], {
171
+ stdio: "inherit",
172
+ env: process.env,
173
+ cwd: process.cwd()
174
+ });
175
+ child.once("error", (error) => {
176
+ rejectPromise(error);
177
+ });
178
+ child.once("exit", (code) => {
179
+ if (typeof code === "number") {
180
+ resolvePromise(code);
181
+ return;
182
+ }
183
+ resolvePromise(1);
184
+ });
185
+ });
186
+ }
187
+ function createDefaultDeps() {
188
+ return {
189
+ resolveSkillsCliPath,
190
+ resolveLocalSkillSourcePath,
191
+ spawnInvocation,
192
+ writeStdout(message) {
193
+ process.stdout.write(message);
194
+ },
195
+ writeStderr(message) {
196
+ process.stderr.write(message);
197
+ }
198
+ };
199
+ }
200
+ async function runOpensteerSkillsInstaller(rawArgs, overrideDeps = {}) {
201
+ const deps = {
202
+ ...createDefaultDeps(),
203
+ ...overrideDeps
204
+ };
205
+ const parsed = parseOpensteerSkillsArgs(rawArgs);
206
+ if (parsed.mode === "help") {
207
+ deps.writeStdout(HELP_TEXT);
208
+ return 0;
209
+ }
210
+ if (parsed.mode === "error") {
211
+ deps.writeStderr(`${parsed.error}
212
+ `);
213
+ deps.writeStderr('Run "opensteer skills --help" for usage.\n');
214
+ return 1;
215
+ }
216
+ const invocation = createSkillsInstallInvocation({
217
+ localSkillSourcePath: deps.resolveLocalSkillSourcePath(),
218
+ passthroughArgs: parsed.passthroughArgs,
219
+ skillsCliPath: deps.resolveSkillsCliPath()
220
+ });
221
+ return await deps.spawnInvocation(invocation);
222
+ }
223
+ // Annotate the CommonJS export names for ESM import in node:
224
+ 0 && (module.exports = {
225
+ createSkillsInstallInvocation,
226
+ parseOpensteerSkillsArgs,
227
+ resolveLocalSkillSourcePath,
228
+ resolveSkillsCliPath,
229
+ runOpensteerSkillsInstaller
230
+ });
@@ -0,0 +1,28 @@
1
+ type ParseMode = 'help' | 'install' | 'error';
2
+ interface ParsedSkillsArgs {
3
+ mode: ParseMode;
4
+ passthroughArgs: string[];
5
+ error?: string;
6
+ }
7
+ interface SkillsInstallInvocation {
8
+ cliPath: string;
9
+ cliArgs: string[];
10
+ }
11
+ interface SkillsInstallerDeps {
12
+ resolveSkillsCliPath: () => string;
13
+ resolveLocalSkillSourcePath: () => string;
14
+ spawnInvocation: (invocation: SkillsInstallInvocation) => Promise<number>;
15
+ writeStdout: (message: string) => void;
16
+ writeStderr: (message: string) => void;
17
+ }
18
+ declare function parseOpensteerSkillsArgs(rawArgs: string[]): ParsedSkillsArgs;
19
+ declare function resolveLocalSkillSourcePath(): string;
20
+ declare function resolveSkillsCliPath(): string;
21
+ declare function createSkillsInstallInvocation(args: {
22
+ localSkillSourcePath: string;
23
+ passthroughArgs: string[];
24
+ skillsCliPath: string;
25
+ }): SkillsInstallInvocation;
26
+ declare function runOpensteerSkillsInstaller(rawArgs: string[], overrideDeps?: Partial<SkillsInstallerDeps>): Promise<number>;
27
+
28
+ export { createSkillsInstallInvocation, parseOpensteerSkillsArgs, resolveLocalSkillSourcePath, resolveSkillsCliPath, runOpensteerSkillsInstaller };
@@ -0,0 +1,28 @@
1
+ type ParseMode = 'help' | 'install' | 'error';
2
+ interface ParsedSkillsArgs {
3
+ mode: ParseMode;
4
+ passthroughArgs: string[];
5
+ error?: string;
6
+ }
7
+ interface SkillsInstallInvocation {
8
+ cliPath: string;
9
+ cliArgs: string[];
10
+ }
11
+ interface SkillsInstallerDeps {
12
+ resolveSkillsCliPath: () => string;
13
+ resolveLocalSkillSourcePath: () => string;
14
+ spawnInvocation: (invocation: SkillsInstallInvocation) => Promise<number>;
15
+ writeStdout: (message: string) => void;
16
+ writeStderr: (message: string) => void;
17
+ }
18
+ declare function parseOpensteerSkillsArgs(rawArgs: string[]): ParsedSkillsArgs;
19
+ declare function resolveLocalSkillSourcePath(): string;
20
+ declare function resolveSkillsCliPath(): string;
21
+ declare function createSkillsInstallInvocation(args: {
22
+ localSkillSourcePath: string;
23
+ passthroughArgs: string[];
24
+ skillsCliPath: string;
25
+ }): SkillsInstallInvocation;
26
+ declare function runOpensteerSkillsInstaller(rawArgs: string[], overrideDeps?: Partial<SkillsInstallerDeps>): Promise<number>;
27
+
28
+ export { createSkillsInstallInvocation, parseOpensteerSkillsArgs, resolveLocalSkillSourcePath, resolveSkillsCliPath, runOpensteerSkillsInstaller };