thoth-agents 0.1.18 → 0.2.0

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 (37) hide show
  1. package/README.md +55 -12
  2. package/dist/agents/prompt-dialects.d.ts +9 -0
  3. package/dist/{chunk-6K3ZXIMC.js → chunk-3NOVCFN7.js} +88 -29
  4. package/dist/chunk-4WYCZ5Z7.js +698 -0
  5. package/dist/{chunk-SOT5ZY53.js → chunk-WH3F3GWE.js} +1498 -350
  6. package/dist/cli/claude-code-install.d.ts +53 -0
  7. package/dist/cli/claude-code-paths.d.ts +31 -0
  8. package/dist/cli/codex-install.d.ts +1 -5
  9. package/dist/cli/commands.d.ts +1 -1
  10. package/dist/cli/index.js +85 -27
  11. package/dist/cli/managed-state-io.d.ts +16 -0
  12. package/dist/cli/operations/claude-code.d.ts +21 -0
  13. package/dist/cli/tui/index.js +87 -9
  14. package/dist/cli/tui/operations.d.ts +2 -0
  15. package/dist/cli/types.d.ts +3 -3
  16. package/dist/config/index.d.ts +1 -1
  17. package/dist/config/schema.d.ts +11 -0
  18. package/dist/config/utils.d.ts +5 -0
  19. package/dist/harness/adapters/claude-code.d.ts +24 -0
  20. package/dist/harness/core/memory-governance.d.ts +39 -4
  21. package/dist/harness/core/package-version.d.ts +7 -0
  22. package/dist/harness/types.d.ts +1 -1
  23. package/dist/harness/writers/claude-code-plugin-package.d.ts +32 -0
  24. package/dist/harness/writers/claude-code-skill-layout.d.ts +16 -0
  25. package/dist/harness/writers/claude-code-subagent.d.ts +26 -0
  26. package/dist/harness/writers/fs-skill-collect.d.ts +7 -0
  27. package/dist/hooks/phase-reminder/index.d.ts +2 -0
  28. package/dist/hooks/thoth-mem/protocol.d.ts +2 -2
  29. package/dist/index.js +54 -512
  30. package/package.json +1 -1
  31. package/src/skills/_shared/persistence-contract.md +18 -14
  32. package/src/skills/_shared/thoth-mem-convention.md +18 -18
  33. package/src/skills/executing-plans/SKILL.md +6 -4
  34. package/src/skills/plan-reviewer/SKILL.md +4 -2
  35. package/src/skills/thoth-mem-agents/SKILL.md +3 -0
  36. package/thoth-agents.schema.json +16 -0
  37. package/dist/chunk-DYGVRAMS.js +0 -182
@@ -0,0 +1,698 @@
1
+ import {
2
+ DEFAULT_MODELS,
3
+ OPENCODE_PROMPT_DIALECT,
4
+ SUBAGENT_NAMES,
5
+ appendPromptSections,
6
+ composeAgentPrompt,
7
+ createOrchestratorPromptSections,
8
+ createReadOnlySpecialistPromptSections,
9
+ createWriteCapableSpecialistPromptSections,
10
+ detectModelFamily,
11
+ getAgentOverride,
12
+ getModelFamilyPromptSection,
13
+ getStepBudgetPromptSection,
14
+ loadAgentPrompt,
15
+ renderRolePrompt
16
+ } from "./chunk-3NOVCFN7.js";
17
+
18
+ // src/agents/deep.ts
19
+ var DEEP_PROMPT = renderRolePrompt(
20
+ createWriteCapableSpecialistPromptSections("deep"),
21
+ OPENCODE_PROMPT_DIALECT
22
+ );
23
+ function createDeepAgent(model, customPrompt, customAppendPrompt) {
24
+ const prompt = composeAgentPrompt({
25
+ basePrompt: DEEP_PROMPT,
26
+ customPrompt,
27
+ customAppendPrompt: appendPromptSections(
28
+ getModelFamilyPromptSection("deep", model),
29
+ customAppendPrompt
30
+ )
31
+ });
32
+ return {
33
+ name: "deep",
34
+ description: "Synchronous write-capable implementation agent optimized for thorough context analysis, edge cases, and correctness \u2014 not for bulk mechanical changes.",
35
+ config: {
36
+ model,
37
+ temperature: 0.1,
38
+ prompt,
39
+ color: "secondary"
40
+ // steps: 80,
41
+ }
42
+ };
43
+ }
44
+
45
+ // src/agents/designer.ts
46
+ var DESIGNER_PROMPT = renderRolePrompt(
47
+ createWriteCapableSpecialistPromptSections("designer"),
48
+ OPENCODE_PROMPT_DIALECT
49
+ );
50
+ function createDesignerAgent(model, customPrompt, customAppendPrompt) {
51
+ const prompt = composeAgentPrompt({
52
+ basePrompt: DESIGNER_PROMPT,
53
+ customPrompt,
54
+ customAppendPrompt: appendPromptSections(
55
+ getModelFamilyPromptSection("designer", model),
56
+ customAppendPrompt
57
+ )
58
+ });
59
+ return {
60
+ name: "designer",
61
+ description: "Synchronous write-capable UI/UX implementation agent with ownership of approach, execution, and visual verification.",
62
+ config: {
63
+ model,
64
+ temperature: 0.4,
65
+ prompt,
66
+ color: "accent"
67
+ // steps: 50,
68
+ }
69
+ };
70
+ }
71
+
72
+ // src/agents/explorer.ts
73
+ var EXPLORER_PROMPT = renderRolePrompt(
74
+ createReadOnlySpecialistPromptSections("explorer"),
75
+ OPENCODE_PROMPT_DIALECT
76
+ );
77
+ function createExplorerAgent(model, customPrompt, customAppendPrompt) {
78
+ const prompt = composeAgentPrompt({
79
+ basePrompt: EXPLORER_PROMPT,
80
+ customPrompt,
81
+ customAppendPrompt: appendPromptSections(
82
+ getModelFamilyPromptSection("explorer", model),
83
+ customAppendPrompt
84
+ )
85
+ });
86
+ return {
87
+ name: "explorer",
88
+ description: "Read-only local discovery agent for fast codebase search, references, and repository mapping.",
89
+ config: {
90
+ model,
91
+ temperature: 0.1,
92
+ prompt,
93
+ color: "info"
94
+ }
95
+ };
96
+ }
97
+
98
+ // src/agents/librarian.ts
99
+ var LIBRARIAN_PROMPT = renderRolePrompt(
100
+ createReadOnlySpecialistPromptSections("librarian"),
101
+ OPENCODE_PROMPT_DIALECT
102
+ );
103
+ function createLibrarianAgent(model, customPrompt, customAppendPrompt) {
104
+ const prompt = composeAgentPrompt({
105
+ basePrompt: LIBRARIAN_PROMPT,
106
+ customPrompt,
107
+ customAppendPrompt: appendPromptSections(
108
+ getModelFamilyPromptSection("librarian", model),
109
+ customAppendPrompt
110
+ )
111
+ });
112
+ return {
113
+ name: "librarian",
114
+ description: "Read-only research agent for official docs, public examples, and externally sourced implementation guidance.",
115
+ config: {
116
+ model,
117
+ temperature: 0.1,
118
+ prompt,
119
+ color: "info"
120
+ }
121
+ };
122
+ }
123
+
124
+ // src/agents/oracle.ts
125
+ var ORACLE_PROMPT = renderRolePrompt(
126
+ createReadOnlySpecialistPromptSections("oracle"),
127
+ OPENCODE_PROMPT_DIALECT
128
+ );
129
+ function createOracleAgent(model, customPrompt, customAppendPrompt) {
130
+ const prompt = composeAgentPrompt({
131
+ basePrompt: ORACLE_PROMPT,
132
+ customPrompt,
133
+ customAppendPrompt: appendPromptSections(
134
+ getModelFamilyPromptSection("oracle", model),
135
+ customAppendPrompt
136
+ )
137
+ });
138
+ return {
139
+ name: "oracle",
140
+ description: "Synchronous read-only strategic advisor for debugging, architecture, code review, and SDD plan review.",
141
+ config: {
142
+ model,
143
+ temperature: 0.1,
144
+ prompt,
145
+ color: "warning"
146
+ }
147
+ };
148
+ }
149
+
150
+ // src/agents/orchestrator.ts
151
+ var OPENCODE_RUNTIME_SECTION = `<opencode-runtime>
152
+ In the OpenCode harness, an automatic \`<reminder>...</reminder>\` workflow block followed by a \`\\n\\n---\\n\\n\` separator is prepended to your user messages as harness scaffolding, not user input.
153
+ When saving the user prompt via mem_save(kind="prompt"), you MUST exclude that injected \`<reminder>\` block and the \`---\` separator, and persist only the real user request text that follows.
154
+ </opencode-runtime>`;
155
+ var ORCHESTRATOR_PROMPT = appendPromptSections(
156
+ renderRolePrompt(createOrchestratorPromptSections(), OPENCODE_PROMPT_DIALECT),
157
+ OPENCODE_RUNTIME_SECTION
158
+ );
159
+ function createOrchestratorAgent(model, customPrompt, customAppendPrompt) {
160
+ const prompt = composeAgentPrompt({
161
+ basePrompt: ORCHESTRATOR_PROMPT,
162
+ customPrompt,
163
+ customAppendPrompt: appendPromptSections(
164
+ getModelFamilyPromptSection("orchestrator", model),
165
+ customAppendPrompt
166
+ )
167
+ });
168
+ const definition = {
169
+ name: "orchestrator",
170
+ description: "Delegate-first coordinator for SDD workflow, specialist dispatch, and root-session memory ownership.",
171
+ config: {
172
+ temperature: 0.1,
173
+ prompt,
174
+ color: "primary"
175
+ // steps: 100,
176
+ }
177
+ };
178
+ if (Array.isArray(model)) {
179
+ definition._modelArray = model.map(
180
+ (entry) => typeof entry === "string" ? { id: entry } : entry
181
+ );
182
+ } else if (typeof model === "string" && model) {
183
+ definition.config.model = model;
184
+ }
185
+ return definition;
186
+ }
187
+
188
+ // src/agents/quick.ts
189
+ var QUICK_PROMPT = renderRolePrompt(
190
+ createWriteCapableSpecialistPromptSections("quick"),
191
+ OPENCODE_PROMPT_DIALECT
192
+ );
193
+ function createQuickAgent(model, customPrompt, customAppendPrompt) {
194
+ const prompt = composeAgentPrompt({
195
+ basePrompt: QUICK_PROMPT,
196
+ customPrompt,
197
+ customAppendPrompt: appendPromptSections(
198
+ getModelFamilyPromptSection("quick", model),
199
+ customAppendPrompt
200
+ )
201
+ });
202
+ return {
203
+ name: "quick",
204
+ description: "Synchronous write-capable implementation agent optimized for fast, mechanical, well-bounded changes \u2014 including uniform patterns across multiple files.",
205
+ config: {
206
+ model,
207
+ temperature: 0.2,
208
+ prompt,
209
+ color: "success"
210
+ // steps: 30,
211
+ }
212
+ };
213
+ }
214
+
215
+ // src/agents/index.ts
216
+ var GEMINI_DEFAULT_STEPS = {
217
+ explorer: 120,
218
+ librarian: 80,
219
+ oracle: 80,
220
+ designer: 80,
221
+ quick: 40,
222
+ deep: 120
223
+ };
224
+ var BUILTIN_PERMISSION_PRESETS = {
225
+ orchestrator: {
226
+ read: "allow",
227
+ edit: "allow",
228
+ write: "allow",
229
+ glob: "allow",
230
+ grep: "allow",
231
+ list: "allow",
232
+ bash: "allow",
233
+ codesearch: "allow",
234
+ lsp: "allow",
235
+ skill: "allow",
236
+ question: "allow",
237
+ webfetch: "allow",
238
+ exa: "allow",
239
+ todowrite: "allow",
240
+ task: "allow",
241
+ external_directory: "allow"
242
+ },
243
+ explorer: {
244
+ read: "allow",
245
+ glob: "allow",
246
+ grep: "allow",
247
+ list: "allow",
248
+ codesearch: "allow",
249
+ lsp: "allow",
250
+ external_directory: "allow",
251
+ bash: "allow",
252
+ question: "allow",
253
+ skill: "allow",
254
+ edit: "deny",
255
+ todowrite: "deny",
256
+ task: "deny"
257
+ },
258
+ librarian: {
259
+ read: "allow",
260
+ glob: "allow",
261
+ grep: "allow",
262
+ external_directory: "allow",
263
+ bash: "allow",
264
+ webfetch: "allow",
265
+ exa: "allow",
266
+ codesearch: "allow",
267
+ question: "allow",
268
+ skill: "allow",
269
+ edit: "deny",
270
+ todowrite: "deny",
271
+ task: "deny"
272
+ },
273
+ oracle: {
274
+ read: "allow",
275
+ glob: "allow",
276
+ grep: "allow",
277
+ list: "allow",
278
+ lsp: "allow",
279
+ codesearch: "allow",
280
+ webfetch: "allow",
281
+ exa: "allow",
282
+ external_directory: "allow",
283
+ bash: "allow",
284
+ question: "allow",
285
+ skill: "allow",
286
+ edit: "deny",
287
+ todowrite: "deny",
288
+ task: "deny"
289
+ },
290
+ designer: {
291
+ read: "allow",
292
+ edit: "allow",
293
+ glob: "allow",
294
+ grep: "allow",
295
+ list: "allow",
296
+ bash: "allow",
297
+ codesearch: "allow",
298
+ lsp: "allow",
299
+ skill: "allow",
300
+ question: "allow",
301
+ todowrite: "deny",
302
+ task: "deny",
303
+ external_directory: {
304
+ "~/.config/opencode/skills/**": "allow"
305
+ }
306
+ },
307
+ quick: {
308
+ read: "allow",
309
+ edit: "allow",
310
+ glob: "allow",
311
+ grep: "allow",
312
+ list: "allow",
313
+ bash: "allow",
314
+ question: "allow",
315
+ codesearch: "allow",
316
+ lsp: "allow",
317
+ skill: "allow",
318
+ todowrite: "deny",
319
+ task: "deny",
320
+ external_directory: {
321
+ "~/.config/opencode/skills/**": "allow"
322
+ }
323
+ },
324
+ deep: {
325
+ read: "allow",
326
+ edit: "allow",
327
+ glob: "allow",
328
+ grep: "allow",
329
+ list: "allow",
330
+ bash: "allow",
331
+ codesearch: "allow",
332
+ lsp: "allow",
333
+ skill: "allow",
334
+ question: "allow",
335
+ webfetch: "allow",
336
+ exa: "allow",
337
+ todowrite: "deny",
338
+ task: "deny",
339
+ external_directory: {
340
+ "~/.config/opencode/skills/**": "allow"
341
+ }
342
+ }
343
+ };
344
+ function normalizeModelArray(model) {
345
+ return model.map(
346
+ (entry) => typeof entry === "string" ? { id: entry } : entry
347
+ );
348
+ }
349
+ function applyOverrides(agent, override) {
350
+ if (override.model) {
351
+ if (Array.isArray(override.model)) {
352
+ agent._modelArray = normalizeModelArray(override.model);
353
+ agent.config.model = void 0;
354
+ } else {
355
+ agent.config.model = override.model;
356
+ }
357
+ }
358
+ if (override.variant) {
359
+ agent.config.variant = override.variant;
360
+ }
361
+ if (override.temperature !== void 0) {
362
+ agent.config.temperature = override.temperature;
363
+ }
364
+ if (override.steps !== void 0) {
365
+ agent.config.steps = override.steps;
366
+ }
367
+ }
368
+ function applyStepBudgetPrompt(agent) {
369
+ const stepBudgetPrompt = getStepBudgetPromptSection(agent.config.steps);
370
+ if (!stepBudgetPrompt) {
371
+ return;
372
+ }
373
+ agent.config.prompt = appendPromptSections(
374
+ agent.config.prompt,
375
+ stepBudgetPrompt
376
+ );
377
+ }
378
+ function applyGeminiDefaultSteps(agent) {
379
+ if (!isSubagent(agent.name) || agent.config.steps !== void 0) {
380
+ return;
381
+ }
382
+ if (detectModelFamily(agent._modelArray ?? agent.config.model) !== "gemini") {
383
+ return;
384
+ }
385
+ agent.config.steps = GEMINI_DEFAULT_STEPS[agent.name];
386
+ }
387
+ function clonePermissionConfig(permission) {
388
+ if (typeof permission === "string") {
389
+ return permission;
390
+ }
391
+ return Object.fromEntries(
392
+ Object.entries(permission).map(([key, value]) => [
393
+ key,
394
+ value && typeof value === "object" && !Array.isArray(value) ? { ...value } : value
395
+ ])
396
+ );
397
+ }
398
+ function getBuiltinPermissionPreset(name) {
399
+ return clonePermissionConfig(BUILTIN_PERMISSION_PRESETS[name]);
400
+ }
401
+ function getExplicitPermissionOverride(override) {
402
+ return override?.permission;
403
+ }
404
+ function getPrimaryModelForPrompt(model) {
405
+ if (Array.isArray(model)) {
406
+ const first = model[0];
407
+ return typeof first === "string" ? first : first?.id;
408
+ }
409
+ return model;
410
+ }
411
+ function isSubagent(name) {
412
+ return SUBAGENT_NAMES.includes(name);
413
+ }
414
+ var SUBAGENT_FACTORIES = {
415
+ explorer: createExplorerAgent,
416
+ librarian: createLibrarianAgent,
417
+ oracle: createOracleAgent,
418
+ designer: createDesignerAgent,
419
+ quick: createQuickAgent,
420
+ deep: createDeepAgent
421
+ };
422
+ function createAgents(config) {
423
+ const protoSubAgents = Object.entries(SUBAGENT_FACTORIES).map(([name, factory]) => {
424
+ const override = getAgentOverride(config, name);
425
+ const prompts = loadAgentPrompt(name, config?.preset);
426
+ const model = getPrimaryModelForPrompt(override?.model) ?? DEFAULT_MODELS[name];
427
+ return factory(model, prompts.prompt, prompts.appendPrompt);
428
+ });
429
+ const allSubAgents = protoSubAgents.map((agent) => {
430
+ const override = getAgentOverride(config, agent.name);
431
+ if (override) {
432
+ applyOverrides(agent, override);
433
+ }
434
+ applyGeminiDefaultSteps(agent);
435
+ applyStepBudgetPrompt(agent);
436
+ return agent;
437
+ });
438
+ const orchestratorOverride = getAgentOverride(config, "orchestrator");
439
+ const orchestratorPrompts = loadAgentPrompt("orchestrator", config?.preset);
440
+ const orchestrator = createOrchestratorAgent(
441
+ orchestratorOverride?.model ?? DEFAULT_MODELS.orchestrator,
442
+ orchestratorPrompts.prompt,
443
+ orchestratorPrompts.appendPrompt
444
+ );
445
+ if (orchestratorOverride) {
446
+ applyOverrides(orchestrator, orchestratorOverride);
447
+ }
448
+ applyStepBudgetPrompt(orchestrator);
449
+ return [orchestrator, ...allSubAgents];
450
+ }
451
+ function getAgentConfigs(config) {
452
+ const agents = createAgents(config);
453
+ return Object.fromEntries(
454
+ agents.map((agent) => {
455
+ const override = getAgentOverride(config, agent.name);
456
+ const sdkConfig = {
457
+ ...agent.config,
458
+ description: agent.description
459
+ };
460
+ const builtinPermission = isSubagent(agent.name) ? getBuiltinPermissionPreset(agent.name) : agent.name === "orchestrator" ? getBuiltinPermissionPreset("orchestrator") : void 0;
461
+ const explicitPermissionOverride = getExplicitPermissionOverride(override);
462
+ sdkConfig.permission = explicitPermissionOverride ?? agent.config.permission ?? builtinPermission;
463
+ if (isSubagent(agent.name)) {
464
+ sdkConfig.mode = "subagent";
465
+ } else if (agent.name === "orchestrator") {
466
+ sdkConfig.mode = "primary";
467
+ }
468
+ return [agent.name, sdkConfig];
469
+ })
470
+ );
471
+ }
472
+
473
+ // src/harness/adapters/opencode.ts
474
+ var OPENCODE_CAPABILITIES = {
475
+ agentDefinitions: "supported",
476
+ delegatedExecution: "supported",
477
+ parallelDelegation: "supported",
478
+ runtimeHooks: "supported",
479
+ mcpConfiguration: "supported",
480
+ skillPackaging: "supported",
481
+ rolePermissions: "supported",
482
+ parentContextInjection: "supported",
483
+ memoryGovernanceEnforcement: "supported"
484
+ };
485
+ function renderOpenCodeAgentConfigs(config) {
486
+ return getAgentConfigs(config);
487
+ }
488
+ function hasOpenCodeConfig(context) {
489
+ return "config" in context;
490
+ }
491
+ var opencodeAdapter = {
492
+ id: "opencode",
493
+ displayName: "OpenCode",
494
+ capabilities: OPENCODE_CAPABILITIES,
495
+ render(context) {
496
+ const config = hasOpenCodeConfig(context) ? context.config : void 0;
497
+ const agents = renderOpenCodeAgentConfigs(config);
498
+ return {
499
+ harness: "opencode",
500
+ artifacts: [
501
+ {
502
+ harness: "opencode",
503
+ kind: "agent-config",
504
+ path: "opencode.agent.config.json",
505
+ description: "Current OpenCode AgentConfig output rendered through the harness adapter boundary.",
506
+ content: JSON.stringify(agents, null, 2)
507
+ }
508
+ ],
509
+ diagnostics: []
510
+ };
511
+ }
512
+ };
513
+
514
+ // src/utils/subprocess.ts
515
+ import {
516
+ spawn as nodeSpawn,
517
+ spawnSync as nodeSpawnSync
518
+ } from "child_process";
519
+ import { Readable } from "stream";
520
+ function emptyReadableStream() {
521
+ return new ReadableStream({
522
+ start(controller) {
523
+ controller.close();
524
+ }
525
+ });
526
+ }
527
+ function toWebReadable(stream) {
528
+ if (!stream) {
529
+ return emptyReadableStream();
530
+ }
531
+ return Readable.toWeb(
532
+ stream
533
+ );
534
+ }
535
+ function fallbackStdin() {
536
+ return {
537
+ write: () => void 0,
538
+ end: () => void 0
539
+ };
540
+ }
541
+ function spawn(command, options = {}) {
542
+ const child = nodeSpawn(command[0], command.slice(1), {
543
+ cwd: options.cwd,
544
+ env: options.env,
545
+ stdio: [
546
+ options.stdin ?? "pipe",
547
+ options.stdout ?? "pipe",
548
+ options.stderr ?? "pipe"
549
+ ]
550
+ });
551
+ const managed = {
552
+ stdin: child.stdin ?? fallbackStdin(),
553
+ stdout: toWebReadable(child.stdout),
554
+ stderr: toWebReadable(child.stderr),
555
+ exited: new Promise((resolve) => {
556
+ child.on("exit", (code) => {
557
+ managed.exitCode = code;
558
+ resolve(code ?? 1);
559
+ });
560
+ child.on("error", () => {
561
+ managed.exitCode = 1;
562
+ resolve(1);
563
+ });
564
+ }),
565
+ exitCode: child.exitCode,
566
+ kill: () => {
567
+ child.kill();
568
+ }
569
+ };
570
+ return managed;
571
+ }
572
+ function spawnSync(command, options = {}) {
573
+ const result = nodeSpawnSync(command[0], command.slice(1), {
574
+ cwd: options.cwd,
575
+ env: options.env,
576
+ stdio: [
577
+ options.stdin ?? "ignore",
578
+ options.stdout ?? "pipe",
579
+ options.stderr ?? "pipe"
580
+ ]
581
+ });
582
+ return { exitCode: result.status };
583
+ }
584
+
585
+ // src/cli/system.ts
586
+ import { statSync } from "fs";
587
+ var cachedOpenCodePath = null;
588
+ function getOpenCodePaths() {
589
+ const home = process.env.HOME || process.env.USERPROFILE || "";
590
+ return [
591
+ // PATH (try this first)
592
+ "opencode",
593
+ // User local installations (Linux & macOS)
594
+ `${home}/.local/bin/opencode`,
595
+ `${home}/.opencode/bin/opencode`,
596
+ `${home}/bin/opencode`,
597
+ // System-wide installations
598
+ "/usr/local/bin/opencode",
599
+ "/opt/opencode/bin/opencode",
600
+ "/usr/bin/opencode",
601
+ "/bin/opencode",
602
+ // macOS specific
603
+ "/Applications/OpenCode.app/Contents/MacOS/opencode",
604
+ `${home}/Applications/OpenCode.app/Contents/MacOS/opencode`,
605
+ // Homebrew (macOS & Linux)
606
+ "/opt/homebrew/bin/opencode",
607
+ "/home/linuxbrew/.linuxbrew/bin/opencode",
608
+ `${home}/homebrew/bin/opencode`,
609
+ // macOS user Library
610
+ `${home}/Library/Application Support/opencode/bin/opencode`,
611
+ // Snap (Linux)
612
+ "/snap/bin/opencode",
613
+ "/var/snap/opencode/current/bin/opencode",
614
+ // Flatpak (Linux)
615
+ "/var/lib/flatpak/exports/bin/ai.opencode.OpenCode",
616
+ `${home}/.local/share/flatpak/exports/bin/ai.opencode.OpenCode`,
617
+ // Nix (Linux/macOS)
618
+ "/nix/store/opencode/bin/opencode",
619
+ `${home}/.nix-profile/bin/opencode`,
620
+ "/run/current-system/sw/bin/opencode",
621
+ // Cargo (Rust toolchain)
622
+ `${home}/.cargo/bin/opencode`,
623
+ // npm/npx global
624
+ `${home}/.npm-global/bin/opencode`,
625
+ "/usr/local/lib/node_modules/opencode/bin/opencode",
626
+ // Yarn global
627
+ `${home}/.yarn/bin/opencode`,
628
+ // PNPM
629
+ `${home}/.pnpm-global/bin/opencode`
630
+ ];
631
+ }
632
+ function resolveOpenCodePath() {
633
+ if (cachedOpenCodePath) {
634
+ return cachedOpenCodePath;
635
+ }
636
+ const paths = getOpenCodePaths();
637
+ for (const opencodePath of paths) {
638
+ if (opencodePath === "opencode") continue;
639
+ try {
640
+ const stat = statSync(opencodePath);
641
+ if (stat.isFile()) {
642
+ cachedOpenCodePath = opencodePath;
643
+ return opencodePath;
644
+ }
645
+ } catch {
646
+ }
647
+ }
648
+ return "opencode";
649
+ }
650
+ async function isOpenCodeInstalled() {
651
+ const paths = getOpenCodePaths();
652
+ for (const opencodePath of paths) {
653
+ try {
654
+ const proc = spawn([opencodePath, "--version"], {
655
+ stdout: "pipe",
656
+ stderr: "pipe"
657
+ });
658
+ await proc.exited;
659
+ if (proc.exitCode === 0) {
660
+ cachedOpenCodePath = opencodePath;
661
+ return true;
662
+ }
663
+ } catch {
664
+ }
665
+ }
666
+ return false;
667
+ }
668
+ async function getOpenCodeVersion() {
669
+ const opencodePath = resolveOpenCodePath();
670
+ try {
671
+ const proc = spawn([opencodePath, "--version"], {
672
+ stdout: "pipe",
673
+ stderr: "pipe"
674
+ });
675
+ const output = await new Response(proc.stdout).text();
676
+ await proc.exited;
677
+ if (proc.exitCode === 0) {
678
+ return output.trim();
679
+ }
680
+ } catch {
681
+ }
682
+ return null;
683
+ }
684
+ function getOpenCodePath() {
685
+ const path = resolveOpenCodePath();
686
+ return path === "opencode" ? null : path;
687
+ }
688
+
689
+ export {
690
+ createAgents,
691
+ renderOpenCodeAgentConfigs,
692
+ opencodeAdapter,
693
+ spawn,
694
+ spawnSync,
695
+ isOpenCodeInstalled,
696
+ getOpenCodeVersion,
697
+ getOpenCodePath
698
+ };