opencode-skills-collection 4.0.74 → 4.0.76

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 (53) hide show
  1. package/bundled-skills/.antigravity-install-manifest.json +16 -1
  2. package/bundled-skills/claude-api/SKILL.md +4 -4
  3. package/bundled-skills/claude-api/curl/examples.md +5 -5
  4. package/bundled-skills/claude-api/java/claude-api.md +1 -1
  5. package/bundled-skills/claude-api/php/claude-api.md +2 -2
  6. package/bundled-skills/claude-api/python/claude-api/README.md +13 -13
  7. package/bundled-skills/claude-api/python/claude-api/batches.md +3 -3
  8. package/bundled-skills/claude-api/python/claude-api/files-api.md +3 -3
  9. package/bundled-skills/claude-api/python/claude-api/streaming.md +6 -6
  10. package/bundled-skills/claude-api/python/claude-api/tool-use.md +19 -19
  11. package/bundled-skills/claude-api/ruby/claude-api.md +3 -3
  12. package/bundled-skills/claude-api/shared/error-codes.md +2 -2
  13. package/bundled-skills/claude-api/shared/live-sources.md +1 -1
  14. package/bundled-skills/claude-api/shared/models.md +4 -4
  15. package/bundled-skills/claude-api/shared/tool-use-concepts.md +2 -2
  16. package/bundled-skills/claude-api/typescript/claude-api/README.md +12 -12
  17. package/bundled-skills/claude-api/typescript/claude-api/batches.md +2 -2
  18. package/bundled-skills/claude-api/typescript/claude-api/files-api.md +1 -1
  19. package/bundled-skills/claude-api/typescript/claude-api/streaming.md +4 -4
  20. package/bundled-skills/claude-api/typescript/claude-api/tool-use.md +14 -14
  21. package/bundled-skills/electron-drive-skill/LICENSE +21 -0
  22. package/bundled-skills/electron-drive-skill/README.md +172 -0
  23. package/bundled-skills/electron-drive-skill/SKILL.md +237 -0
  24. package/bundled-skills/electron-drive-skill/drive.config.json.example +10 -0
  25. package/bundled-skills/electron-drive-skill/package.json +28 -0
  26. package/bundled-skills/electron-drive-skill/scripts/daemon.mjs +352 -0
  27. package/bundled-skills/electron-drive-skill/scripts/drive.mjs +250 -0
  28. package/bundled-skills/electron-drive-skill/scripts/lib.mjs +165 -0
  29. package/bundled-skills/electron-drive-skill/scripts/lib.test.mjs +56 -0
  30. package/bundled-skills/jev-social/SKILL.md +5 -5
  31. package/bundled-skills/lintlang-audit/SKILL.md +100 -0
  32. package/bundled-skills/mcp-dependency-drift-audit/SKILL.md +169 -0
  33. package/bundled-skills/nexrad-mosaic-access/SKILL.md +297 -0
  34. package/bundled-skills/nexrad-mosaic-construction/SKILL.md +277 -0
  35. package/bundled-skills/nexrad-product-access/SKILL.md +258 -0
  36. package/bundled-skills/nexrad-radar-visualization/SKILL.md +318 -0
  37. package/bundled-skills/radar-satellite-analysis/SKILL.md +314 -0
  38. package/bundled-skills/system-prompt-lookup/SKILL.md +188 -0
  39. package/bundled-skills/traderspy-market-briefing/SKILL.md +132 -0
  40. package/bundled-skills/traderspy-market-screener/SKILL.md +182 -0
  41. package/bundled-skills/traderspy-market-screener/references/condition-cookbook.md +70 -0
  42. package/bundled-skills/traderspy-position-check/SKILL.md +130 -0
  43. package/bundled-skills/traderspy-position-check/references/risk-math.md +60 -0
  44. package/bundled-skills/traderspy-smart-money/SKILL.md +151 -0
  45. package/bundled-skills/traderspy-smart-money/references/exchange-notes.md +40 -0
  46. package/bundled-skills/traderspy-technical-analysis/SKILL.md +162 -0
  47. package/bundled-skills/traderspy-technical-analysis/references/derivatives-guide.md +57 -0
  48. package/bundled-skills/traderspy-technical-analysis/references/indicator-guide.md +47 -0
  49. package/bundled-skills/traderspy-trading-signals/SKILL.md +156 -0
  50. package/bundled-skills/traderspy-trading-signals/references/signal-fields.md +64 -0
  51. package/bundled-skills/unified-ai-gateway/SKILL.md +58 -16
  52. package/package.json +1 -1
  53. package/skills_index.json +657 -358
@@ -24,7 +24,7 @@ const client = new Anthropic({ apiKey: "your-api-key" });
24
24
 
25
25
  ```typescript
26
26
  const response = await client.messages.create({
27
- model: "claude-opus-4-6",
27
+ model: "claude-opus-5",
28
28
  max_tokens: 1024,
29
29
  messages: [{ role: "user", content: "What is the capital of France?" }],
30
30
  });
@@ -37,7 +37,7 @@ console.log(response.content[0].text);
37
37
 
38
38
  ```typescript
39
39
  const response = await client.messages.create({
40
- model: "claude-opus-4-6",
40
+ model: "claude-opus-5",
41
41
  max_tokens: 1024,
42
42
  system:
43
43
  "You are a helpful coding assistant. Always provide examples in Python.",
@@ -53,7 +53,7 @@ const response = await client.messages.create({
53
53
 
54
54
  ```typescript
55
55
  const response = await client.messages.create({
56
- model: "claude-opus-4-6",
56
+ model: "claude-opus-5",
57
57
  max_tokens: 1024,
58
58
  messages: [
59
59
  {
@@ -78,7 +78,7 @@ import fs from "fs";
78
78
  const imageData = fs.readFileSync("image.png").toString("base64");
79
79
 
80
80
  const response = await client.messages.create({
81
- model: "claude-opus-4-6",
81
+ model: "claude-opus-5",
82
82
  max_tokens: 1024,
83
83
  messages: [
84
84
  {
@@ -105,7 +105,7 @@ Use top-level `cache_control` to automatically cache the last cacheable block in
105
105
 
106
106
  ```typescript
107
107
  const response = await client.messages.create({
108
- model: "claude-opus-4-6",
108
+ model: "claude-opus-5",
109
109
  max_tokens: 1024,
110
110
  cache_control: { type: "ephemeral" }, // auto-caches the last cacheable block
111
111
  system: "You are an expert on this large document...",
@@ -119,7 +119,7 @@ For fine-grained control, add `cache_control` to specific content blocks:
119
119
 
120
120
  ```typescript
121
121
  const response = await client.messages.create({
122
- model: "claude-opus-4-6",
122
+ model: "claude-opus-5",
123
123
  max_tokens: 1024,
124
124
  system: [
125
125
  {
@@ -133,7 +133,7 @@ const response = await client.messages.create({
133
133
 
134
134
  // With explicit TTL (time-to-live)
135
135
  const response2 = await client.messages.create({
136
- model: "claude-opus-4-6",
136
+ model: "claude-opus-5",
137
137
  max_tokens: 1024,
138
138
  system: [
139
139
  {
@@ -156,7 +156,7 @@ const response2 = await client.messages.create({
156
156
  ```typescript
157
157
  // Opus 4.6: adaptive thinking (recommended)
158
158
  const response = await client.messages.create({
159
- model: "claude-opus-4-6",
159
+ model: "claude-opus-5",
160
160
  max_tokens: 16000,
161
161
  thinking: { type: "adaptive" },
162
162
  output_config: { effort: "high" }, // low | medium | high | max
@@ -214,7 +214,7 @@ const messages: Anthropic.MessageParam[] = [
214
214
  ];
215
215
 
216
216
  const response = await client.messages.create({
217
- model: "claude-opus-4-6",
217
+ model: "claude-opus-5",
218
218
  max_tokens: 1024,
219
219
  messages: messages,
220
220
  });
@@ -243,7 +243,7 @@ async function chat(userMessage: string): Promise<string> {
243
243
 
244
244
  const response = await client.beta.messages.create({
245
245
  betas: ["compact-2026-01-12"],
246
- model: "claude-opus-4-6",
246
+ model: "claude-opus-5",
247
247
  max_tokens: 4096,
248
248
  messages,
249
249
  context_management: {
@@ -288,7 +288,7 @@ The `stop_reason` field in the response indicates why the model stopped generati
288
288
  ```typescript
289
289
  // Automatic caching (simplest — caches the last cacheable block)
290
290
  const response = await client.messages.create({
291
- model: "claude-opus-4-6",
291
+ model: "claude-opus-5",
292
292
  max_tokens: 1024,
293
293
  cache_control: { type: "ephemeral" },
294
294
  system: largeDocumentText, // e.g., 50KB of context
@@ -303,7 +303,7 @@ const response = await client.messages.create({
303
303
 
304
304
  ```typescript
305
305
  const countResponse = await client.messages.countTokens({
306
- model: "claude-opus-4-6",
306
+ model: "claude-opus-5",
307
307
  messages: messages,
308
308
  system: system,
309
309
  });
@@ -24,7 +24,7 @@ const messageBatch = await client.messages.batches.create({
24
24
  {
25
25
  custom_id: "request-1",
26
26
  params: {
27
- model: "claude-opus-4-6",
27
+ model: "claude-opus-5",
28
28
  max_tokens: 1024,
29
29
  messages: [
30
30
  { role: "user", content: "Summarize climate change impacts" },
@@ -34,7 +34,7 @@ const messageBatch = await client.messages.batches.create({
34
34
  {
35
35
  custom_id: "request-2",
36
36
  params: {
37
- model: "claude-opus-4-6",
37
+ model: "claude-opus-5",
38
38
  max_tokens: 1024,
39
39
  messages: [
40
40
  { role: "user", content: "Explain quantum computing basics" },
@@ -41,7 +41,7 @@ console.log(`Size: ${uploaded.size_bytes} bytes`);
41
41
 
42
42
  ```typescript
43
43
  const response = await client.beta.messages.create({
44
- model: "claude-opus-4-6",
44
+ model: "claude-opus-5",
45
45
  max_tokens: 1024,
46
46
  messages: [
47
47
  {
@@ -4,7 +4,7 @@
4
4
 
5
5
  ```typescript
6
6
  const stream = client.messages.stream({
7
- model: "claude-opus-4-6",
7
+ model: "claude-opus-5",
8
8
  max_tokens: 1024,
9
9
  messages: [{ role: "user", content: "Write a story" }],
10
10
  });
@@ -27,7 +27,7 @@ for await (const event of stream) {
27
27
 
28
28
  ```typescript
29
29
  const stream = client.messages.stream({
30
- model: "claude-opus-4-6",
30
+ model: "claude-opus-5",
31
31
  max_tokens: 16000,
32
32
  thinking: { type: "adaptive" },
33
33
  messages: [{ role: "user", content: "Analyze this problem" }],
@@ -82,7 +82,7 @@ const getWeather = betaZodTool({
82
82
  });
83
83
 
84
84
  const runner = client.beta.messages.toolRunner({
85
- model: "claude-opus-4-6",
85
+ model: "claude-opus-5",
86
86
  max_tokens: 4096,
87
87
  tools: [getWeather],
88
88
  messages: [
@@ -117,7 +117,7 @@ for await (const messageStream of runner) {
117
117
 
118
118
  ```typescript
119
119
  const stream = client.messages.stream({
120
- model: "claude-opus-4-6",
120
+ model: "claude-opus-5",
121
121
  max_tokens: 1024,
122
122
  messages: [{ role: "user", content: "Hello" }],
123
123
  });
@@ -30,7 +30,7 @@ const getWeather = betaZodTool({
30
30
 
31
31
  // The tool runner handles the agentic loop and returns the final message
32
32
  const finalMessage = await client.beta.messages.toolRunner({
33
- model: "claude-opus-4-6",
33
+ model: "claude-opus-5",
34
34
  max_tokens: 4096,
35
35
  tools: [getWeather],
36
36
  messages: [{ role: "user", content: "What's the weather in Paris?" }],
@@ -61,7 +61,7 @@ let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
61
61
 
62
62
  while (true) {
63
63
  const response = await client.messages.create({
64
- model: "claude-opus-4-6",
64
+ model: "claude-opus-5",
65
65
  max_tokens: 4096,
66
66
  tools: tools,
67
67
  messages: messages,
@@ -111,7 +111,7 @@ let messages: Anthropic.MessageParam[] = [{ role: "user", content: userInput }];
111
111
 
112
112
  while (true) {
113
113
  const stream = client.messages.stream({
114
- model: "claude-opus-4-6",
114
+ model: "claude-opus-5",
115
115
  max_tokens: 4096,
116
116
  tools,
117
117
  messages,
@@ -169,7 +169,7 @@ while (true) {
169
169
 
170
170
  ```typescript
171
171
  const response = await client.messages.create({
172
- model: "claude-opus-4-6",
172
+ model: "claude-opus-5",
173
173
  max_tokens: 1024,
174
174
  tools: tools,
175
175
  messages: [{ role: "user", content: "What's the weather in Paris?" }],
@@ -180,7 +180,7 @@ for (const block of response.content) {
180
180
  const result = await executeTool(block.name, block.input);
181
181
 
182
182
  const followup = await client.messages.create({
183
- model: "claude-opus-4-6",
183
+ model: "claude-opus-5",
184
184
  max_tokens: 1024,
185
185
  tools: tools,
186
186
  messages: [
@@ -204,7 +204,7 @@ for (const block of response.content) {
204
204
 
205
205
  ```typescript
206
206
  const response = await client.messages.create({
207
- model: "claude-opus-4-6",
207
+ model: "claude-opus-5",
208
208
  max_tokens: 1024,
209
209
  tools: tools,
210
210
  tool_choice: { type: "tool", name: "get_weather" },
@@ -224,7 +224,7 @@ import Anthropic from "@anthropic-ai/sdk";
224
224
  const client = new Anthropic();
225
225
 
226
226
  const response = await client.messages.create({
227
- model: "claude-opus-4-6",
227
+ model: "claude-opus-5",
228
228
  max_tokens: 4096,
229
229
  messages: [
230
230
  {
@@ -257,7 +257,7 @@ const uploaded = await client.beta.files.upload({
257
257
  // Code execution is GA; Files API is still beta (pass via RequestOptions)
258
258
  const response = await client.messages.create(
259
259
  {
260
- model: "claude-opus-4-6",
260
+ model: "claude-opus-5",
261
261
  max_tokens: 4096,
262
262
  messages: [
263
263
  {
@@ -317,7 +317,7 @@ for (const block of response.content) {
317
317
  ```typescript
318
318
  // First request: set up environment
319
319
  const response1 = await client.messages.create({
320
- model: "claude-opus-4-6",
320
+ model: "claude-opus-5",
321
321
  max_tokens: 4096,
322
322
  messages: [
323
323
  {
@@ -333,7 +333,7 @@ const containerId = response1.container.id;
333
333
 
334
334
  const response2 = await client.messages.create({
335
335
  container: containerId,
336
- model: "claude-opus-4-6",
336
+ model: "claude-opus-5",
337
337
  max_tokens: 4096,
338
338
  messages: [
339
339
  {
@@ -353,7 +353,7 @@ const response2 = await client.messages.create({
353
353
 
354
354
  ```typescript
355
355
  const response = await client.messages.create({
356
- model: "claude-opus-4-6",
356
+ model: "claude-opus-5",
357
357
  max_tokens: 2048,
358
358
  messages: [
359
359
  {
@@ -387,7 +387,7 @@ const handlers: MemoryToolHandlers = {
387
387
  const memory = betaMemoryTool(handlers);
388
388
 
389
389
  const runner = client.beta.messages.toolRunner({
390
- model: "claude-opus-4-6",
390
+ model: "claude-opus-5",
391
391
  max_tokens: 2048,
392
392
  tools: [memory],
393
393
  messages: [{ role: "user", content: "Remember my preferences" }],
@@ -424,7 +424,7 @@ const ContactInfoSchema = z.object({
424
424
  const client = new Anthropic();
425
425
 
426
426
  const response = await client.messages.parse({
427
- model: "claude-opus-4-6",
427
+ model: "claude-opus-5",
428
428
  max_tokens: 1024,
429
429
  messages: [
430
430
  {
@@ -445,7 +445,7 @@ console.log(response.parsed_output.name); // "Jane Doe"
445
445
 
446
446
  ```typescript
447
447
  const response = await client.messages.create({
448
- model: "claude-opus-4-6",
448
+ model: "claude-opus-5",
449
449
  max_tokens: 1024,
450
450
  messages: [
451
451
  {
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Search 3D, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,172 @@
1
+ # electron-drive-skill
2
+
3
+ An agentic skill that lets an agent drive
4
+ your Electron app the way a person would: start and stop it, read the screen,
5
+ click, type, take screenshots, run code in the renderer or main process, and
6
+ read logs. The agent can then check a UI change or reproduce a bug in the
7
+ real app, not just in unit tests.
8
+
9
+ |Tested On|Status|
10
+ |--|--|
11
+ |Claude Code|y|
12
+ |Muse Code|y|
13
+
14
+ ```text
15
+ $ drive start --build
16
+ Using the build from 0 minute(s) ago. Pass --build to rebuild.
17
+ { "mode": "built", "profile": "default", "running": true, "window": { "title": "My App", ... } }
18
+ $ drive snapshot
19
+ - button "Get started"
20
+ $ drive click 'role=button[name="Get started"]'
21
+ clicked role=button[name="Get started"]
22
+ $ drive stop
23
+ quit via app.quit()
24
+ ```
25
+
26
+ The app runs under Playwright in a background daemon, so each command is a
27
+ separate, fast shell call. Every launch uses a scratch profile
28
+ (`--user-data-dir` under your temp directory), so the agent never touches your
29
+ real app data.
30
+
31
+ ## Requirements
32
+
33
+ - Node.js 18 or later
34
+ - `electron` and `playwright-core` 1.49 or later, installed in your project:
35
+ ```bash
36
+ npm i -D playwright-core
37
+ ```
38
+ No browsers need to be downloaded; the driver uses your project's Electron.
39
+ - macOS or Linux. On Windows, run it as `node scripts/drive.mjs`. The
40
+ process-tree kill that `stop` falls back to is not implemented there.
41
+
42
+ ## Install
43
+
44
+ Clone it into your project's skills directory:
45
+
46
+ ```bash
47
+ git clone https://github.com/Search-3D/electron-drive-skill .claude/skills/electron-drive-skill
48
+ ```
49
+
50
+ Or clone it into `~/.claude/skills/electron-drive-skill` to use it in every
51
+ project. The driver always uses the Electron and Playwright of the project you
52
+ run it in. Keep the folder name: it must match the `name` in `SKILL.md`.
53
+
54
+ The examples here call it `drive`. Nothing installs that command; to run it by
55
+ hand, add an alias:
56
+
57
+ ```bash
58
+ alias drive="node .claude/skills/electron-drive-skill/scripts/drive.mjs"
59
+ ```
60
+
61
+ > [!IMPORTANT]
62
+ > Make sure you have installed playwright in your project's developer dependencies, as outlined in the steps above.
63
+
64
+ ## Configure
65
+
66
+ With no configuration, `start` runs `electron .` on whatever is already built.
67
+ For anything else, add `drive.config.json` at your project root (copy
68
+ [`drive.config.json.example`](drive.config.json.example) as a starting point).
69
+ Every field is optional:
70
+
71
+ ```json
72
+ {
73
+ "build": "npm run build",
74
+ "args": ["."],
75
+ "env": { "APP_DATA_DIR": "{profile}/data" },
76
+ "logFile": "logs/main.log",
77
+ "dev": {
78
+ "args": ["."],
79
+ "env": { "ELECTRON_RENDERER_URL": "http://localhost:5173" },
80
+ "url": "http://localhost:5173"
81
+ }
82
+ }
83
+ ```
84
+
85
+ | Field | Meaning |
86
+ |---|---|
87
+ | `build` | Shell command that `start --build` runs first. |
88
+ | `args` | Arguments to Electron: the app directory (its `package.json` `main` must be the built entry point) or the entry file. For example, electron-react-boilerplate uses `["release/app"]`. |
89
+ | `env` | Added to the app's environment. `{profile}` becomes the scratch profile directory; it is replaced only in `env` values, not in `args`. Use it to redirect any data the app keeps outside `userData` (see below). |
90
+ | `logFile` | A log file, relative to the profile directory, that `logs` shows along with the console output. |
91
+ | `dev.args`, `dev.env` | Used by `start --dev` instead of `args` (plus `env`). |
92
+ | `dev.url` | The renderer dev server. `start --dev` refuses to launch if it is not up. |
93
+
94
+ ### Redirecting app data with `env`
95
+
96
+ Every launch gets `--user-data-dir` pointed at a scratch profile, which moves
97
+ `app.getPath('userData')` and everything Electron stores there. Data your app
98
+ keeps somewhere else, such as a database, a library folder or a cache path, is
99
+ **not** moved, so the agent would work on your real files.
100
+
101
+ If your app keeps data elsewhere, give it an environment variable that
102
+ overrides the location, and set that variable in `env`:
103
+
104
+ ```ts
105
+ // main process
106
+ const dataDir = process.env.APP_DATA_DIR ?? defaultDataDir()
107
+ ```
108
+
109
+ ```json
110
+ { "env": { "APP_DATA_DIR": "{profile}/data" } }
111
+ ```
112
+
113
+ `APP_DATA_DIR` is only an example name. The driver just sets the variable, so
114
+ it has an effect only if your app reads it. Use whatever name your app
115
+ already checks.
116
+
117
+ For `--dev`, start your renderer dev server yourself, and make sure it does not
118
+ also launch Electron.
119
+
120
+ ## Commands
121
+
122
+ ```text
123
+ start [--dev] [--build] [--profile <name>] [--fresh]
124
+ status whether the app is up, its profile and window
125
+ snapshot accessibility tree of the window
126
+ screenshot [--out <png>] [--selector <css>]
127
+ click <target> Playwright selector: 'text=Settings', 'role=button[name="Save"]', css
128
+ fill <target> <text>
129
+ select <target> <value|label>
130
+ press <key> e.g. Enter, Escape, Control+K
131
+ wait <target> [--timeout <ms>]
132
+ eval <js | -> in the renderer; '-' reads stdin
133
+ main <js | -> in the main process; 'electron' and 'process' are in scope
134
+ logs [--lines <n>]
135
+ stop
136
+ ```
137
+
138
+ Put `--` before text that starts with dashes, so it is not read as a flag:
139
+ `drive fill 'role=textbox[name="Args"]' -- --verbose`.
140
+
141
+ `eval` and `main` return their result as JSON. A DOM node, a function or
142
+ anything else that does not serialize comes back as `undefined` or `{}`;
143
+ return plain data instead (`el.textContent`, not `el`).
144
+
145
+ Run the script directly instead of through a `yarn`/`npm` script alias. yarn 1
146
+ re-splits arguments and drops inner quotes, which silently breaks most
147
+ JavaScript passed to `eval` and `main`.
148
+
149
+ ## Limitations
150
+
151
+ - **One window.** Commands act on the first window that is not DevTools.
152
+ There is no way to pick another one, so a settings window or a second
153
+ document window cannot be driven. An app with a splash screen is driven
154
+ once the splash closes and the main window is the only one left; check
155
+ `status` to see which window is current.
156
+ - **One app at a time** per project: `start` refuses while one is running.
157
+ - **Windows:** run it as `node scripts/drive.mjs`; the process-tree kill that
158
+ `stop` falls back to is POSIX only.
159
+
160
+ ## How it works
161
+
162
+ - `scripts/drive.mjs`: the CLI. `start` spawns the daemon and waits for it.
163
+ Every other command is one HTTP request to it.
164
+ - `scripts/daemon.mjs`: launches Electron through Playwright's `_electron`
165
+ API and serves commands on `127.0.0.1` with a random token. Its state file
166
+ and log are in `$TMPDIR/electron-drive/<project>-<hash>/`.
167
+ - `scripts/lib.mjs`: config, argument parsing and paths, with no side
168
+ effects. Tests: `node --test scripts/`.
169
+
170
+ ## License
171
+
172
+ [MIT](LICENSE)