oh-my-opencode-slim 2.0.0-beta.9 → 2.0.1

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 (49) hide show
  1. package/README.ja-JP.md +121 -47
  2. package/README.ko-KR.md +708 -0
  3. package/README.md +149 -58
  4. package/README.zh-CN.md +142 -64
  5. package/dist/cli/background-subagents.d.ts +13 -0
  6. package/dist/cli/companion.d.ts +4 -0
  7. package/dist/cli/index.d.ts +2 -1
  8. package/dist/cli/index.js +651 -82
  9. package/dist/cli/install.d.ts +6 -1
  10. package/dist/cli/providers.d.ts +2 -1
  11. package/dist/cli/types.d.ts +8 -0
  12. package/dist/companion/manager.d.ts +38 -0
  13. package/dist/config/constants.d.ts +4 -1
  14. package/dist/config/fallback-chains.d.ts +1 -0
  15. package/dist/config/schema.d.ts +46 -40
  16. package/dist/hooks/auto-update-checker/checker.d.ts +7 -1
  17. package/dist/hooks/auto-update-checker/constants.d.ts +1 -0
  18. package/dist/hooks/auto-update-checker/types.d.ts +10 -0
  19. package/dist/hooks/index.d.ts +0 -2
  20. package/dist/hooks/json-error-recovery/hook.d.ts +1 -1
  21. package/dist/hooks/phase-reminder/index.d.ts +1 -1
  22. package/dist/index.js +1861 -2149
  23. package/dist/mcp/grep-app.d.ts +1 -1
  24. package/dist/multiplexer/zellij/index.d.ts +17 -3
  25. package/dist/tools/cancel-task.d.ts +6 -0
  26. package/dist/tools/preset-manager.d.ts +6 -7
  27. package/dist/tui.js +56 -31
  28. package/dist/utils/background-job-board.d.ts +39 -1
  29. package/dist/utils/index.d.ts +0 -1
  30. package/dist/utils/task.d.ts +2 -0
  31. package/oh-my-opencode-slim.schema.json +34 -91
  32. package/package.json +4 -3
  33. package/src/skills/codemap.md +3 -1
  34. package/src/skills/deepwork/SKILL.md +25 -3
  35. package/src/skills/oh-my-opencode-slim/SKILL.md +326 -0
  36. package/dist/divoom/council.gif +0 -0
  37. package/dist/divoom/designer.gif +0 -0
  38. package/dist/divoom/explorer.gif +0 -0
  39. package/dist/divoom/fixer.gif +0 -0
  40. package/dist/divoom/input.gif +0 -0
  41. package/dist/divoom/intro.gif +0 -0
  42. package/dist/divoom/librarian.gif +0 -0
  43. package/dist/divoom/manager.d.ts +0 -57
  44. package/dist/divoom/oracle.gif +0 -0
  45. package/dist/divoom/orchestrator.gif +0 -0
  46. package/dist/hooks/goal/index.d.ts +0 -38
  47. package/dist/hooks/todo-continuation/index.d.ts +0 -55
  48. package/dist/hooks/todo-continuation/todo-hygiene.d.ts +0 -35
  49. package/dist/utils/session-manager.d.ts +0 -55
package/dist/cli/index.js CHANGED
@@ -1,32 +1,32 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
3
  import { createRequire } from "node:module";
4
+ var __defProp = Object.defineProperty;
5
+ var __returnValue = (v) => v;
6
+ function __exportSetter(name, newValue) {
7
+ this[name] = __returnValue.bind(null, newValue);
8
+ }
9
+ var __export = (target, all) => {
10
+ for (var name in all)
11
+ __defProp(target, name, {
12
+ get: all[name],
13
+ enumerable: true,
14
+ configurable: true,
15
+ set: __exportSetter.bind(all, name)
16
+ });
17
+ };
18
+ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
4
19
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
5
20
 
6
- // src/cli/doctor.ts
7
- import * as fs2 from "node:fs";
8
- import { z as z3 } from "zod";
9
-
10
- // src/config/loader.ts
11
- import * as fs from "node:fs";
12
- import * as path from "node:path";
13
-
14
- // src/cli/config-io.ts
15
- import {
16
- copyFileSync as copyFileSync2,
17
- existsSync as existsSync3,
18
- mkdirSync as mkdirSync3,
19
- readFileSync,
20
- renameSync,
21
- statSync as statSync2,
22
- writeFileSync
23
- } from "node:fs";
24
- import { homedir as homedir2 } from "node:os";
25
- import { dirname as dirname3, join as join3 } from "node:path";
26
-
27
21
  // src/utils/compat.ts
22
+ var exports_compat = {};
23
+ __export(exports_compat, {
24
+ isBun: () => isBun,
25
+ crossWrite: () => crossWrite,
26
+ crossSpawn: () => crossSpawn
27
+ });
28
28
  import { spawn as nodeSpawn } from "node:child_process";
29
- var isBun = typeof globalThis.Bun !== "undefined";
29
+ import { writeFile as fsWriteFile } from "node:fs/promises";
30
30
  function collectStream(stream) {
31
31
  if (!stream)
32
32
  return () => Promise.resolve("");
@@ -69,6 +69,124 @@ function crossSpawn(command, options) {
69
69
  }
70
70
  };
71
71
  }
72
+ async function crossWrite(path, data) {
73
+ await fsWriteFile(path, Buffer.from(data));
74
+ }
75
+ var isBun;
76
+ var init_compat = __esm(() => {
77
+ isBun = typeof globalThis.Bun !== "undefined";
78
+ });
79
+
80
+ // src/utils/zip-extractor.ts
81
+ var exports_zip_extractor = {};
82
+ __export(exports_zip_extractor, {
83
+ extractZip: () => extractZip
84
+ });
85
+ import { spawnSync } from "node:child_process";
86
+ import { release } from "node:os";
87
+ function getWindowsBuildNumber() {
88
+ if (process.platform !== "win32")
89
+ return null;
90
+ const parts = release().split(".");
91
+ if (parts.length >= 3) {
92
+ const build = parseInt(parts[2], 10);
93
+ if (!Number.isNaN(build))
94
+ return build;
95
+ }
96
+ return null;
97
+ }
98
+ function isPwshAvailable() {
99
+ if (process.platform !== "win32")
100
+ return false;
101
+ const result = spawnSync("where", ["pwsh"], {
102
+ stdio: ["ignore", "pipe", "pipe"]
103
+ });
104
+ return result.status === 0;
105
+ }
106
+ function escapePowerShellPath(path2) {
107
+ return path2.replace(/'/g, "''");
108
+ }
109
+ function getWindowsZipExtractor() {
110
+ const buildNumber = getWindowsBuildNumber();
111
+ if (buildNumber !== null && buildNumber >= WINDOWS_BUILD_WITH_TAR) {
112
+ return "tar";
113
+ }
114
+ if (isPwshAvailable()) {
115
+ return "pwsh";
116
+ }
117
+ return "powershell";
118
+ }
119
+ async function extractZip(archivePath, destDir) {
120
+ let proc;
121
+ if (process.platform === "win32") {
122
+ const extractor = getWindowsZipExtractor();
123
+ switch (extractor) {
124
+ case "tar":
125
+ proc = crossSpawn(["tar", "-xf", archivePath, "-C", destDir], {
126
+ stdout: "ignore",
127
+ stderr: "pipe"
128
+ });
129
+ break;
130
+ case "pwsh":
131
+ proc = crossSpawn([
132
+ "pwsh",
133
+ "-Command",
134
+ `Expand-Archive -Path '${escapePowerShellPath(archivePath)}' -DestinationPath '${escapePowerShellPath(destDir)}' -Force`
135
+ ], {
136
+ stdout: "ignore",
137
+ stderr: "pipe"
138
+ });
139
+ break;
140
+ default:
141
+ proc = crossSpawn([
142
+ "powershell",
143
+ "-Command",
144
+ `Expand-Archive -Path '${escapePowerShellPath(archivePath)}' -DestinationPath '${escapePowerShellPath(destDir)}' -Force`
145
+ ], {
146
+ stdout: "ignore",
147
+ stderr: "pipe"
148
+ });
149
+ break;
150
+ }
151
+ } else {
152
+ proc = crossSpawn(["unzip", "-o", archivePath, "-d", destDir], {
153
+ stdout: "ignore",
154
+ stderr: "pipe"
155
+ });
156
+ }
157
+ const exitCode = await proc.exited;
158
+ if (exitCode !== 0) {
159
+ const stderr = await proc.stderr();
160
+ throw new Error(`zip extraction failed (exit ${exitCode}): ${stderr}`);
161
+ }
162
+ }
163
+ var WINDOWS_BUILD_WITH_TAR = 17134;
164
+ var init_zip_extractor = __esm(() => {
165
+ init_compat();
166
+ });
167
+
168
+ // src/cli/doctor.ts
169
+ import * as fs2 from "node:fs";
170
+ import { z as z3 } from "zod";
171
+
172
+ // src/config/loader.ts
173
+ import * as fs from "node:fs";
174
+ import * as path from "node:path";
175
+
176
+ // src/cli/config-io.ts
177
+ init_compat();
178
+ import {
179
+ copyFileSync as copyFileSync2,
180
+ existsSync as existsSync3,
181
+ mkdirSync as mkdirSync3,
182
+ readFileSync,
183
+ renameSync,
184
+ rmSync,
185
+ statSync as statSync2,
186
+ writeFileSync
187
+ } from "node:fs";
188
+ import { homedir as homedir2 } from "node:os";
189
+ import { dirname as dirname3, join as join3 } from "node:path";
72
190
 
73
191
  // src/cli/paths.ts
74
192
  import { existsSync, mkdirSync } from "node:fs";
@@ -100,7 +218,7 @@ function getConfigSearchDirs() {
100
218
  });
101
219
  }
102
220
  function getOpenCodeConfigPaths() {
103
- const configDir = getDefaultOpenCodeConfigDir();
221
+ const configDir = getConfigDir();
104
222
  return [join(configDir, "opencode.json"), join(configDir, "opencode.jsonc")];
105
223
  }
106
224
  function getConfigJson() {
@@ -328,11 +446,13 @@ var MultiplexerLayoutSchema = z2.enum([
328
446
  "even-horizontal",
329
447
  "even-vertical"
330
448
  ]);
449
+ var ZellijPaneModeSchema = z2.enum(["agent-tab", "current-tab"]);
331
450
  var TmuxLayoutSchema = MultiplexerLayoutSchema;
332
451
  var MultiplexerConfigSchema = z2.object({
333
452
  type: MultiplexerTypeSchema.default("none"),
334
453
  layout: MultiplexerLayoutSchema.default("main-vertical"),
335
- main_pane_size: z2.number().min(20).max(80).default(60)
454
+ main_pane_size: z2.number().min(20).max(80).default(60),
455
+ zellij_pane_mode: ZellijPaneModeSchema.default("agent-tab")
336
456
  });
337
457
  var TmuxConfigSchema = z2.object({
338
458
  enabled: z2.boolean().default(false),
@@ -343,7 +463,7 @@ var PresetSchema = z2.record(z2.string(), AgentOverrideConfigSchema);
343
463
  var WebsearchConfigSchema = z2.object({
344
464
  provider: z2.enum(["exa", "tavily"]).default("exa")
345
465
  });
346
- var McpNameSchema = z2.enum(["websearch", "context7", "grep_app"]);
466
+ var McpNameSchema = z2.enum(["websearch", "context7", "gh_grep"]);
347
467
  var InterviewConfigSchema = z2.object({
348
468
  maxQuestions: z2.number().int().min(1).max(10).default(2),
349
469
  outputFolder: z2.string().min(1).default("interview"),
@@ -351,28 +471,11 @@ var InterviewConfigSchema = z2.object({
351
471
  port: z2.number().int().min(0).max(65535).default(0),
352
472
  dashboard: z2.boolean().default(false)
353
473
  });
354
- var SessionManagerConfigSchema = z2.object({
474
+ var BackgroundJobsConfigSchema = z2.object({
355
475
  maxSessionsPerAgent: z2.number().int().min(1).max(10).default(2),
356
476
  readContextMinLines: z2.number().int().min(0).max(1000).default(10),
357
477
  readContextMaxFiles: z2.number().int().min(0).max(50).default(8)
358
478
  });
359
- var DivoomConfigSchema = z2.object({
360
- enabled: z2.boolean().default(false),
361
- python: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/.venv/bin/python"),
362
- script: z2.string().min(1).default("/Applications/Divoom MiniToo.app/Contents/Resources/tools/divoom_send.py"),
363
- size: z2.number().int().min(1).max(1024).default(128),
364
- fps: z2.number().int().min(1).max(60).default(8),
365
- speed: z2.number().int().min(1).max(1e4).default(125),
366
- maxFrames: z2.number().int().min(1).max(500).default(24),
367
- posterizeBits: z2.number().int().min(1).max(8).default(3),
368
- gifs: z2.record(z2.string(), z2.string().min(1)).optional()
369
- });
370
- var TodoContinuationConfigSchema = z2.object({
371
- maxContinuations: z2.number().int().min(1).max(50).default(5).describe("Maximum consecutive auto-continuations before stopping to ask user"),
372
- cooldownMs: z2.number().int().min(0).max(30000).default(3000).describe("Delay in ms before auto-continuing (gives user time to abort)"),
373
- autoEnable: z2.boolean().default(false).describe("Automatically enable auto-continue when the orchestrator session has enough todos"),
374
- autoEnableThreshold: z2.number().int().min(1).max(50).default(4).describe("Number of todos that triggers auto-enable (only used when autoEnable is true)")
375
- });
376
479
  var FailoverConfigSchema = z2.object({
377
480
  enabled: z2.boolean().default(true),
378
481
  timeoutMs: z2.number().min(0).default(15000),
@@ -380,6 +483,11 @@ var FailoverConfigSchema = z2.object({
380
483
  chains: FallbackChainsSchema.default({}),
381
484
  retry_on_empty: z2.boolean().default(true).describe("When true (default), empty provider responses are treated as failures, " + "triggering fallback/retry. Set to false to treat them as successes.")
382
485
  });
486
+ var CompanionConfigSchema = z2.object({
487
+ enabled: z2.boolean().optional(),
488
+ position: z2.enum(["bottom-right", "bottom-left", "top-right", "top-left"]).optional(),
489
+ size: z2.enum(["small", "medium", "large"]).optional()
490
+ });
383
491
  function validateCustomOnlyPromptFields(overrides, ctx, pathPrefix) {
384
492
  for (const [name, override] of Object.entries(overrides)) {
385
493
  const isBuiltInOrAlias = ALL_AGENT_NAMES.includes(name) || AGENT_ALIASES[name] !== undefined;
@@ -417,11 +525,10 @@ var PluginConfigSchema = z2.object({
417
525
  tmux: TmuxConfigSchema.optional(),
418
526
  websearch: WebsearchConfigSchema.optional(),
419
527
  interview: InterviewConfigSchema.optional(),
420
- sessionManager: SessionManagerConfigSchema.optional(),
421
- divoom: DivoomConfigSchema.optional(),
422
- todoContinuation: TodoContinuationConfigSchema.optional(),
528
+ backgroundJobs: BackgroundJobsConfigSchema.optional(),
423
529
  fallback: FailoverConfigSchema.optional(),
424
- council: CouncilConfigSchema.optional()
530
+ council: CouncilConfigSchema.optional(),
531
+ companion: CompanionConfigSchema.optional()
425
532
  }).superRefine((value, ctx) => {
426
533
  if (value.agents) {
427
534
  validateCustomOnlyPromptFields(value.agents, ctx, ["agents"]);
@@ -437,7 +544,7 @@ var DEFAULT_AGENT_MCPS = {
437
544
  orchestrator: ["*", "!context7"],
438
545
  designer: [],
439
546
  oracle: [],
440
- librarian: ["websearch", "context7", "grep_app"],
547
+ librarian: ["websearch", "context7", "gh_grep"],
441
548
  explorer: [],
442
549
  fixer: [],
443
550
  observer: [],
@@ -479,6 +586,12 @@ var CUSTOM_SKILLS = [
479
586
  description: "Heavy/complex coding sessions and large modifications workflow",
480
587
  allowedAgents: ["orchestrator"],
481
588
  sourcePath: "src/skills/deepwork"
589
+ },
590
+ {
591
+ name: "oh-my-opencode-slim",
592
+ description: "Configure, customize, and safely improve oh-my-opencode-slim setups",
593
+ allowedAgents: ["orchestrator"],
594
+ sourcePath: "src/skills/oh-my-opencode-slim"
482
595
  }
483
596
  ];
484
597
  function getCustomSkillsDir() {
@@ -526,12 +639,12 @@ var SCHEMA_URL = "https://unpkg.com/oh-my-opencode-slim@latest/oh-my-opencode-sl
526
639
  var GENERATED_PRESETS = ["openai", "opencode-go"];
527
640
  var MODEL_MAPPINGS = {
528
641
  openai: {
529
- orchestrator: { model: "openai/gpt-5.5" },
642
+ orchestrator: { model: "openai/gpt-5.5", variant: "medium" },
530
643
  oracle: { model: "openai/gpt-5.5", variant: "high" },
531
644
  librarian: { model: "openai/gpt-5.4-mini", variant: "low" },
532
645
  explorer: { model: "openai/gpt-5.4-mini", variant: "low" },
533
646
  designer: { model: "openai/gpt-5.4-mini", variant: "medium" },
534
- fixer: { model: "openai/gpt-5.4-mini", variant: "low" }
647
+ fixer: { model: "openai/gpt-5.5", variant: "low" }
535
648
  },
536
649
  kimi: {
537
650
  orchestrator: { model: "kimi-for-coding/k2p5" },
@@ -620,11 +733,24 @@ function generateLiteConfig(installConfig) {
620
733
  main_pane_size: 60
621
734
  };
622
735
  }
736
+ if (installConfig.companion === "yes") {
737
+ config.companion = {
738
+ enabled: true,
739
+ position: "bottom-right",
740
+ size: "medium"
741
+ };
742
+ }
623
743
  return config;
624
744
  }
625
745
 
626
746
  // src/cli/config-io.ts
627
747
  var PACKAGE_NAME = "oh-my-opencode-slim";
748
+ var DEFAULT_OPENCODE_AGENTS_TO_DISABLE = [
749
+ "build",
750
+ "explore",
751
+ "general",
752
+ "plan"
753
+ ];
628
754
  function isString(value) {
629
755
  return typeof value === "string";
630
756
  }
@@ -705,17 +831,63 @@ function getPluginEntry() {
705
831
  return PACKAGE_NAME;
706
832
  }
707
833
  }
708
- function getOpenCodePluginCacheDir() {
834
+ function getPinnedVersionFromConfig() {
835
+ try {
836
+ const { config } = parseConfig(getExistingConfigPath());
837
+ if (!config)
838
+ return;
839
+ for (const entry of getPlugins(config)) {
840
+ const spec = getPluginSpec(entry);
841
+ if (!spec)
842
+ continue;
843
+ if (spec === PACKAGE_NAME)
844
+ return;
845
+ if (spec.startsWith(`${PACKAGE_NAME}@`)) {
846
+ const version = spec.slice(PACKAGE_NAME.length + 1);
847
+ if (version && version !== "latest")
848
+ return version;
849
+ }
850
+ }
851
+ } catch {}
852
+ return;
853
+ }
854
+ function getRequestedPackageTag(packageRoot) {
855
+ const normalizedPath = normalizePathForMatch(packageRoot);
856
+ const marker = `/bunx-`;
857
+ const markerIndex = normalizedPath.lastIndexOf(marker);
858
+ if (markerIndex === -1)
859
+ return;
860
+ const bunxSegment = normalizedPath.slice(markerIndex + marker.length).split("/")[0];
861
+ const packagePrefix = `${PACKAGE_NAME}@`;
862
+ const packageIndex = bunxSegment.lastIndexOf(packagePrefix);
863
+ if (packageIndex === -1)
864
+ return;
865
+ const tag = bunxSegment.slice(packageIndex + packagePrefix.length);
866
+ return tag || undefined;
867
+ }
868
+ function getVersionFromPackageRoot(packageRoot) {
869
+ try {
870
+ const packageJsonPath = join3(packageRoot, "package.json");
871
+ if (!existsSync3(packageJsonPath))
872
+ return;
873
+ const pkg = JSON.parse(readFileSync(packageJsonPath, "utf-8"));
874
+ return pkg.version;
875
+ } catch {
876
+ return;
877
+ }
878
+ }
879
+ function getOpenCodePluginCacheDir(version) {
709
880
  const cacheDir = process.env.XDG_CACHE_HOME?.trim() || join3(homedir2(), ".cache");
710
- return join3(cacheDir, "opencode", "packages", `${PACKAGE_NAME}@latest`);
881
+ const suffix = version ? `${PACKAGE_NAME}@${version}` : `${PACKAGE_NAME}@latest`;
882
+ return join3(cacheDir, "opencode", "packages", suffix);
711
883
  }
712
- function writeOpenCodePluginCacheManifest(cacheDir) {
884
+ function writeOpenCodePluginCacheManifest(cacheDir, version = "latest") {
713
885
  try {
714
886
  writeFileSync(join3(cacheDir, "package.json"), JSON.stringify({
715
887
  name: `${PACKAGE_NAME}-cache`,
716
888
  private: true,
717
889
  dependencies: {
718
- [PACKAGE_NAME]: "latest"
890
+ [PACKAGE_NAME]: version
719
891
  }
720
892
  }, null, 2));
721
893
  return null;
@@ -727,6 +899,14 @@ function writeOpenCodePluginCacheManifest(cacheDir) {
727
899
  };
728
900
  }
729
901
  }
902
+ function removeOpenCodePluginCacheArtifacts(cacheDir) {
903
+ rmSync(join3(cacheDir, "node_modules", PACKAGE_NAME), {
904
+ recursive: true,
905
+ force: true
906
+ });
907
+ rmSync(join3(cacheDir, "bun.lock"), { force: true });
908
+ rmSync(join3(cacheDir, "bun.lockb"), { force: true });
909
+ }
730
910
  function verifyOpenCodePluginCache(cacheDir) {
731
911
  const pluginPackageJsonPath = join3(cacheDir, "node_modules", PACKAGE_NAME, "package.json");
732
912
  if (!existsSync3(pluginPackageJsonPath)) {
@@ -763,7 +943,11 @@ async function warmOpenCodePluginCache() {
763
943
  if (!packageRoot || !isPackageManagerInstall(packageRoot)) {
764
944
  return null;
765
945
  }
766
- const cacheDir = getOpenCodePluginCacheDir();
946
+ const pinnedVersion = getPinnedVersionFromConfig();
947
+ const runningVersion = getVersionFromPackageRoot(packageRoot);
948
+ const requestedTag = getRequestedPackageTag(packageRoot);
949
+ const cacheVersion = pinnedVersion ?? requestedTag ?? runningVersion;
950
+ const cacheDir = getOpenCodePluginCacheDir(cacheVersion);
767
951
  try {
768
952
  mkdirSync3(cacheDir, { recursive: true });
769
953
  } catch (err) {
@@ -773,9 +957,10 @@ async function warmOpenCodePluginCache() {
773
957
  error: `Failed to create OpenCode cache directory: ${err}`
774
958
  };
775
959
  }
776
- const manifestError = writeOpenCodePluginCacheManifest(cacheDir);
960
+ const manifestError = writeOpenCodePluginCacheManifest(cacheDir, cacheVersion);
777
961
  if (manifestError)
778
962
  return manifestError;
963
+ removeOpenCodePluginCacheArtifacts(cacheDir);
779
964
  try {
780
965
  const proc = crossSpawn(["bun", "install", "--ignore-scripts"], {
781
966
  cwd: cacheDir,
@@ -956,8 +1141,13 @@ function disableDefaultAgents() {
956
1141
  }
957
1142
  const config = parsedConfig ?? {};
958
1143
  const agent = config.agent ?? {};
959
- agent.explore = { disable: true };
960
- agent.general = { disable: true };
1144
+ for (const agentName of DEFAULT_OPENCODE_AGENTS_TO_DISABLE) {
1145
+ const existing = agent[agentName];
1146
+ agent[agentName] = {
1147
+ ...existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {},
1148
+ disable: true
1149
+ };
1150
+ }
961
1151
  config.agent = agent;
962
1152
  writeConfig(configPath, config);
963
1153
  return { success: true, configPath };
@@ -1085,10 +1275,10 @@ function mergePluginConfigs(base, override) {
1085
1275
  tmux: deepMerge(base.tmux, override.tmux),
1086
1276
  multiplexer: deepMerge(base.multiplexer, override.multiplexer),
1087
1277
  interview: deepMerge(base.interview, override.interview),
1088
- sessionManager: deepMerge(base.sessionManager, override.sessionManager),
1089
- divoom: deepMerge(base.divoom, override.divoom),
1278
+ backgroundJobs: deepMerge(base.backgroundJobs, override.backgroundJobs),
1090
1279
  fallback: deepMerge(base.fallback, override.fallback),
1091
- council: deepMerge(base.council, override.council)
1280
+ council: deepMerge(base.council, override.council),
1281
+ companion: deepMerge(base.companion, override.companion)
1092
1282
  };
1093
1283
  }
1094
1284
  function deepMerge(base, override) {
@@ -1304,16 +1494,253 @@ Options:
1304
1494
  }
1305
1495
 
1306
1496
  // src/cli/install.ts
1307
- import { existsSync as existsSync5 } from "node:fs";
1497
+ import { existsSync as existsSync7 } from "node:fs";
1308
1498
  import { createInterface } from "node:readline/promises";
1499
+
1500
+ // src/cli/background-subagents.ts
1501
+ import { existsSync as existsSync5, mkdirSync as mkdirSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "node:fs";
1502
+ import { homedir as homedir3 } from "node:os";
1503
+ import { dirname as dirname4, join as join5 } from "node:path";
1504
+ var ENV_NAME = "OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS";
1505
+ var START_MARKER = "# >>> oh-my-opencode-slim background subagents >>>";
1506
+ var END_MARKER = "# <<< oh-my-opencode-slim background subagents <<<";
1507
+ function isBackgroundSubagentsEnabled(value) {
1508
+ if (!value)
1509
+ return false;
1510
+ const normalized = value.trim().toLowerCase();
1511
+ return normalized !== "" && !["0", "false", "no", "off"].includes(normalized);
1512
+ }
1513
+ function detectShellKind(shell) {
1514
+ const name = shell?.split("/").at(-1);
1515
+ if (name === "zsh" || name === "bash" || name === "fish")
1516
+ return name;
1517
+ return;
1518
+ }
1519
+ function detectBackgroundSubagentsTarget(env = process.env) {
1520
+ const shell = detectShellKind(env.SHELL);
1521
+ const home = env.HOME || homedir3();
1522
+ if (shell === "zsh")
1523
+ return join5(home, ".zshrc");
1524
+ if (shell === "bash")
1525
+ return join5(home, ".bashrc");
1526
+ if (shell === "fish") {
1527
+ const configHome = env.XDG_CONFIG_HOME || join5(home, ".config");
1528
+ return join5(configHome, "fish", "conf.d", "opencode-background-subagents.fish");
1529
+ }
1530
+ return;
1531
+ }
1532
+ function getBackgroundSubagentsBlock(targetPath) {
1533
+ const isFish = targetPath.endsWith(".fish");
1534
+ const command = isFish ? `set -gx ${ENV_NAME} true` : `export ${ENV_NAME}=true`;
1535
+ return `${START_MARKER}
1536
+ ${command}
1537
+ ${END_MARKER}`;
1538
+ }
1539
+ function manualBackgroundSubagentsInstructions(options) {
1540
+ const shell = options?.shell ?? (options?.targetPath?.endsWith(".fish") ? "fish" : undefined) ?? detectShellKind(options?.targetPath);
1541
+ const bashZshSnippet = `export ${ENV_NAME}=true`;
1542
+ const fishSnippet = `set -gx ${ENV_NAME} true`;
1543
+ if (shell === "fish") {
1544
+ return `Start OpenCode with background subagents enabled:
1545
+ env ${ENV_NAME}=true opencode
1546
+
1547
+ Or add this to your fish startup file:
1548
+ ${fishSnippet}`;
1549
+ }
1550
+ if (shell === "bash" || shell === "zsh") {
1551
+ return `Start OpenCode with background subagents enabled:
1552
+ ${ENV_NAME}=true opencode
1553
+
1554
+ Or add this to your shell startup file:
1555
+ ${bashZshSnippet}`;
1556
+ }
1557
+ return `Start OpenCode with background subagents enabled:
1558
+ ${ENV_NAME}=true opencode
1559
+
1560
+ Or add one of these to your shell startup file:
1561
+ bash/zsh: ${bashZshSnippet}
1562
+ fish: ${fishSnippet}`;
1563
+ }
1564
+ function expandHomePath(targetPath) {
1565
+ if (targetPath === "~")
1566
+ return homedir3();
1567
+ if (targetPath.startsWith("~/"))
1568
+ return join5(homedir3(), targetPath.slice(2));
1569
+ return targetPath;
1570
+ }
1571
+ function upsertBackgroundSubagentsBlock(content, block) {
1572
+ const start = content.indexOf(START_MARKER);
1573
+ const end = content.indexOf(END_MARKER);
1574
+ if (start !== -1 && end !== -1 && end > start) {
1575
+ const afterEnd = end + END_MARKER.length;
1576
+ return `${content.slice(0, start)}${block}${content.slice(afterEnd)}`;
1577
+ }
1578
+ const separator = content.length > 0 && !content.endsWith(`
1579
+ `) ? `
1580
+
1581
+ ` : "";
1582
+ const prefix = content.length > 0 && content.endsWith(`
1583
+ `) ? `
1584
+ ` : separator;
1585
+ return `${content}${prefix}${block}
1586
+ `;
1587
+ }
1588
+ function writeBackgroundSubagentsBlock(targetPath) {
1589
+ const block = getBackgroundSubagentsBlock(targetPath);
1590
+ const content = existsSync5(targetPath) ? readFileSync4(targetPath, "utf8") : "";
1591
+ const nextContent = upsertBackgroundSubagentsBlock(content, block);
1592
+ mkdirSync4(dirname4(targetPath), { recursive: true });
1593
+ writeFileSync2(targetPath, nextContent);
1594
+ }
1595
+
1596
+ // src/cli/companion.ts
1597
+ import {
1598
+ chmodSync,
1599
+ copyFileSync as copyFileSync3,
1600
+ existsSync as existsSync6,
1601
+ mkdirSync as mkdirSync5,
1602
+ mkdtempSync,
1603
+ renameSync as renameSync2,
1604
+ rmSync as rmSync2,
1605
+ writeFileSync as writeFileSync3
1606
+ } from "node:fs";
1607
+ import { homedir as homedir4, tmpdir } from "node:os";
1608
+ import * as path2 from "node:path";
1609
+ var COMPANION_VERSION = "0.1.0";
1610
+ var COMPANION_TAG = "companion-v0.1.0";
1611
+ var GITHUB_REPO = "alvinunreal/oh-my-opencode-slim";
1612
+ function getCompanionTarget() {
1613
+ const p = process.platform;
1614
+ const a = process.arch;
1615
+ if (p === "darwin") {
1616
+ if (a === "arm64")
1617
+ return "aarch64-apple-darwin";
1618
+ if (a === "x64")
1619
+ return "x86_64-apple-darwin";
1620
+ } else if (p === "linux") {
1621
+ if (a === "x64")
1622
+ return "x86_64-unknown-linux-gnu";
1623
+ if (a === "arm64")
1624
+ return "aarch64-unknown-linux-gnu";
1625
+ } else if (p === "win32") {
1626
+ if (a === "x64")
1627
+ return "x86_64-pc-windows-msvc";
1628
+ }
1629
+ return null;
1630
+ }
1631
+ function getCompanionBinaryPath() {
1632
+ const xdg = process.env.XDG_DATA_HOME?.trim();
1633
+ const base = xdg && path2.isAbsolute(xdg) ? xdg : path2.join(homedir4(), ".local", "share");
1634
+ return path2.join(base, "opencode", "storage", "oh-my-opencode-slim", "bin", process.platform === "win32" ? "oh-my-opencode-slim-companion.exe" : "oh-my-opencode-slim-companion");
1635
+ }
1636
+ async function installCompanion(config) {
1637
+ const target = getCompanionTarget();
1638
+ const finalBinaryPath = getCompanionBinaryPath();
1639
+ if (!target) {
1640
+ return {
1641
+ success: false,
1642
+ configPath: finalBinaryPath,
1643
+ error: `Unsupported platform/architecture: ${process.platform} ${process.arch}`
1644
+ };
1645
+ }
1646
+ const isWindows = process.platform === "win32";
1647
+ const ext = isWindows ? "zip" : "tar.gz";
1648
+ const archiveName = `oh-my-opencode-slim-companion-v${COMPANION_VERSION}-${target}.${ext}`;
1649
+ const downloadUrl = `https://github.com/${GITHUB_REPO}/releases/download/${COMPANION_TAG}/${archiveName}`;
1650
+ if (config.dryRun) {
1651
+ console.log(` [dry-run] Detected companion target: ${target}`);
1652
+ console.log(` [dry-run] Would download archive: ${downloadUrl}`);
1653
+ console.log(` [dry-run] Would extract and install to: ${finalBinaryPath}`);
1654
+ return {
1655
+ success: true,
1656
+ configPath: finalBinaryPath
1657
+ };
1658
+ }
1659
+ let buffer;
1660
+ try {
1661
+ const res = await fetch(downloadUrl);
1662
+ if (!res.ok) {
1663
+ return {
1664
+ success: false,
1665
+ configPath: finalBinaryPath,
1666
+ error: `Failed to download companion binary (HTTP ${res.status}): ${res.statusText}`
1667
+ };
1668
+ }
1669
+ buffer = await res.arrayBuffer();
1670
+ } catch (err) {
1671
+ return {
1672
+ success: false,
1673
+ configPath: finalBinaryPath,
1674
+ error: `Failed to fetch companion archive: ${err instanceof Error ? err.message : String(err)}`
1675
+ };
1676
+ }
1677
+ let tempDir = "";
1678
+ try {
1679
+ tempDir = mkdtempSync(path2.join(tmpdir(), "companion-install-"));
1680
+ const archivePath = path2.join(tempDir, archiveName);
1681
+ writeFileSync3(archivePath, Buffer.from(buffer));
1682
+ const extractedDir = path2.join(tempDir, "extracted");
1683
+ mkdirSync5(extractedDir, { recursive: true });
1684
+ if (isWindows) {
1685
+ const { extractZip: extractZip2 } = await Promise.resolve().then(() => (init_zip_extractor(), exports_zip_extractor));
1686
+ await extractZip2(archivePath, extractedDir);
1687
+ } else {
1688
+ const { crossSpawn: crossSpawn2 } = await Promise.resolve().then(() => (init_compat(), exports_compat));
1689
+ const proc = crossSpawn2(["tar", "-xzf", archivePath, "-C", extractedDir]);
1690
+ const exitCode = await proc.exited;
1691
+ if (exitCode !== 0) {
1692
+ const stderr = await proc.stderr();
1693
+ return {
1694
+ success: false,
1695
+ configPath: finalBinaryPath,
1696
+ error: `Archive extraction failed (tar exited with ${exitCode}): ${stderr}`
1697
+ };
1698
+ }
1699
+ }
1700
+ const binaryName = isWindows ? "oh-my-opencode-slim-companion.exe" : "oh-my-opencode-slim-companion";
1701
+ const extractedBinaryPath = path2.join(extractedDir, binaryName);
1702
+ if (!existsSync6(extractedBinaryPath)) {
1703
+ return {
1704
+ success: false,
1705
+ configPath: finalBinaryPath,
1706
+ error: `Binary ${binaryName} not found in extracted archive`
1707
+ };
1708
+ }
1709
+ const binDir = path2.dirname(finalBinaryPath);
1710
+ mkdirSync5(binDir, { recursive: true });
1711
+ const tmpFinalPath = `${finalBinaryPath}.tmp`;
1712
+ copyFileSync3(extractedBinaryPath, tmpFinalPath);
1713
+ if (!isWindows) {
1714
+ chmodSync(tmpFinalPath, 493);
1715
+ }
1716
+ renameSync2(tmpFinalPath, finalBinaryPath);
1717
+ return {
1718
+ success: true,
1719
+ configPath: finalBinaryPath
1720
+ };
1721
+ } catch (err) {
1722
+ return {
1723
+ success: false,
1724
+ configPath: finalBinaryPath,
1725
+ error: `Failed to install companion: ${err instanceof Error ? err.message : String(err)}`
1726
+ };
1727
+ } finally {
1728
+ if (tempDir) {
1729
+ try {
1730
+ rmSync2(tempDir, { recursive: true, force: true });
1731
+ } catch {}
1732
+ }
1733
+ }
1734
+ }
1309
1735
  // src/cli/system.ts
1310
- import { spawnSync } from "node:child_process";
1736
+ init_compat();
1737
+ import { spawnSync as spawnSync2 } from "node:child_process";
1311
1738
  import { statSync as statSync4 } from "node:fs";
1312
1739
  var cachedOpenCodePath = null;
1313
1740
  function resolvePathCommand(command) {
1314
1741
  try {
1315
1742
  const resolver = process.platform === "win32" ? "where" : "which";
1316
- const result = spawnSync(resolver, [command], {
1743
+ const result = spawnSync2(resolver, [command], {
1317
1744
  encoding: "utf-8",
1318
1745
  stdio: ["ignore", "pipe", "ignore"]
1319
1746
  });
@@ -1328,7 +1755,7 @@ function resolvePathCommand(command) {
1328
1755
  }
1329
1756
  function canExecute(command, args) {
1330
1757
  try {
1331
- const result = spawnSync(command, args, {
1758
+ const result = spawnSync2(command, args, {
1332
1759
  stdio: "ignore"
1333
1760
  });
1334
1761
  return result.status === 0;
@@ -1430,8 +1857,8 @@ async function getOpenCodeVersion() {
1430
1857
  return null;
1431
1858
  }
1432
1859
  function getOpenCodePath() {
1433
- const path2 = resolveOpenCodePath();
1434
- return path2 === "opencode" ? null : path2;
1860
+ const path3 = resolveOpenCodePath();
1861
+ return path3 === "opencode" ? null : path3;
1435
1862
  }
1436
1863
  // src/cli/install.ts
1437
1864
  var GREEN = "\x1B[32m";
@@ -1450,8 +1877,8 @@ var SYMBOLS = {
1450
1877
  warn: `${YELLOW}[!]${RESET}`,
1451
1878
  star: `${YELLOW}★${RESET}`
1452
1879
  };
1453
- var GITHUB_REPO = "alvinunreal/oh-my-opencode-slim";
1454
- var GITHUB_URL = `https://github.com/${GITHUB_REPO}`;
1880
+ var GITHUB_REPO2 = "alvinunreal/oh-my-opencode-slim";
1881
+ var GITHUB_URL = `https://github.com/${GITHUB_REPO2}`;
1455
1882
  function printHeader(isUpdate) {
1456
1883
  console.log();
1457
1884
  console.log(`${BOLD}oh-my-opencode-slim ${isUpdate ? "Update" : "Install"}${RESET}`);
@@ -1467,6 +1894,9 @@ function printSuccess(message) {
1467
1894
  function printError(message) {
1468
1895
  console.log(`${SYMBOLS.cross} ${RED}${message}${RESET}`);
1469
1896
  }
1897
+ function printWarning(message) {
1898
+ console.log(`${SYMBOLS.warn} ${YELLOW}${message}${RESET}`);
1899
+ }
1470
1900
  function printInfo(message) {
1471
1901
  console.log(`${SYMBOLS.info} ${message}`);
1472
1902
  }
@@ -1491,7 +1921,7 @@ async function askToStarRepo(config) {
1491
1921
  return;
1492
1922
  try {
1493
1923
  const { execFileSync } = await import("node:child_process");
1494
- execFileSync("gh", ["api", "--silent", "--method", "PUT", `/user/starred/${GITHUB_REPO}`], { stdio: "ignore", timeout: 1e4 });
1924
+ execFileSync("gh", ["api", "--silent", "--method", "PUT", `/user/starred/${GITHUB_REPO2}`], { stdio: "ignore", timeout: 1e4 });
1495
1925
  printSuccess("Thanks for starring! ★");
1496
1926
  } catch {
1497
1927
  printInfo(`Couldn't star automatically. You can star manually:
@@ -1511,11 +1941,88 @@ async function checkOpenCodeInstalled() {
1511
1941
  return { ok: false };
1512
1942
  }
1513
1943
  const version = await getOpenCodeVersion();
1514
- const path2 = getOpenCodePath();
1944
+ const path3 = getOpenCodePath();
1515
1945
  const detectedVersion = version ?? "";
1516
- const pathInfo = path2 ? ` (${DIM}${path2}${RESET})` : "";
1946
+ const pathInfo = path3 ? ` (${DIM}${path3}${RESET})` : "";
1517
1947
  printSuccess(`OpenCode ${detectedVersion} detected${pathInfo}`);
1518
- return { ok: true, version: version ?? undefined, path: path2 ?? undefined };
1948
+ return { ok: true, version: version ?? undefined, path: path3 ?? undefined };
1949
+ }
1950
+ async function configureBackgroundSubagents(config) {
1951
+ if (isBackgroundSubagentsEnabled(process.env.OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS)) {
1952
+ printSuccess("OpenCode background subagents already enabled in environment");
1953
+ return { enabledNow: true };
1954
+ }
1955
+ const target = config.backgroundSubagentsTarget !== undefined ? expandHomePath(config.backgroundSubagentsTarget) : detectBackgroundSubagentsTarget();
1956
+ if (config.backgroundSubagents === "no") {
1957
+ printInfo("OpenCode background subagents shell setup skipped.");
1958
+ console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
1959
+ return { enabledNow: false };
1960
+ }
1961
+ if (!target) {
1962
+ printInfo("No safe shell startup file detected.");
1963
+ console.log(manualBackgroundSubagentsInstructions());
1964
+ return { enabledNow: false };
1965
+ }
1966
+ const block = getBackgroundSubagentsBlock(target);
1967
+ if (config.dryRun) {
1968
+ printInfo("Dry run mode - background subagents block that would be written:");
1969
+ console.log(`Target: ${target}`);
1970
+ console.log(`
1971
+ ${block}
1972
+ `);
1973
+ return { enabledNow: false, configuredTarget: target };
1974
+ }
1975
+ if (config.backgroundSubagents === "ask") {
1976
+ if (!process.stdin.isTTY) {
1977
+ printInfo("Skipped background subagents shell setup in non-TTY mode.");
1978
+ console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
1979
+ return { enabledNow: false };
1980
+ }
1981
+ console.log();
1982
+ printInfo("V2 requires OpenCode background subagents for default orchestration.");
1983
+ printInfo(`The installer can add the required environment export to ${target}.`);
1984
+ const shouldWrite = await confirm("Add OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true now?", true);
1985
+ if (!shouldWrite) {
1986
+ printInfo("Skipped background subagents shell setup.");
1987
+ console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
1988
+ return { enabledNow: false };
1989
+ }
1990
+ }
1991
+ try {
1992
+ writeBackgroundSubagentsBlock(target);
1993
+ } catch (error) {
1994
+ const message = error instanceof Error ? error.message : String(error);
1995
+ printError(`Could not write background subagents shell config: ${message}`);
1996
+ printInfo("Add the setting manually instead:");
1997
+ console.log(manualBackgroundSubagentsInstructions({ targetPath: target }));
1998
+ return { enabledNow: false };
1999
+ }
2000
+ printSuccess(`Background subagents enabled ${SYMBOLS.arrow} ${DIM}${target}${RESET}`);
2001
+ return { enabledNow: false, configuredTarget: target };
2002
+ }
2003
+ async function shouldInstallCompanion(config) {
2004
+ if (config.companion === "yes")
2005
+ return true;
2006
+ if (config.companion === "no")
2007
+ return false;
2008
+ if (config.dryRun) {
2009
+ printInfo("Dry run mode - would ask to install the desktop companion (default: yes).");
2010
+ config.companion = "yes";
2011
+ return true;
2012
+ }
2013
+ if (!process.stdin.isTTY) {
2014
+ printInfo("Skipped desktop companion prompt in non-TTY mode. Use --companion=yes to install it.");
2015
+ config.companion = "no";
2016
+ return false;
2017
+ }
2018
+ console.log();
2019
+ printInfo("The optional desktop companion shows live agent activity.");
2020
+ const shouldInstall = await confirm("Install and enable the desktop companion?", true);
2021
+ config.companion = shouldInstall ? "yes" : "no";
2022
+ if (!shouldInstall) {
2023
+ printInfo("Desktop companion install skipped.");
2024
+ }
2025
+ return shouldInstall;
1519
2026
  }
1520
2027
  function handleStepResult(result, successMsg) {
1521
2028
  if (!result.success) {
@@ -1525,13 +2032,24 @@ function handleStepResult(result, successMsg) {
1525
2032
  printSuccess(`${successMsg} ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`);
1526
2033
  return true;
1527
2034
  }
2035
+ function handleOptionalCompanionResult(result) {
2036
+ if (result.success) {
2037
+ printSuccess(`Companion installed ${SYMBOLS.arrow} ${DIM}${result.configPath}${RESET}`);
2038
+ return;
2039
+ }
2040
+ printWarning(`Desktop companion install skipped: ${result.error}`);
2041
+ printInfo("The desktop companion is optional; continuing plugin installation without it.");
2042
+ }
1528
2043
  async function runInstall(config) {
1529
2044
  const detected = detectCurrentConfig();
1530
2045
  const isUpdate = detected.isInstalled;
1531
2046
  printHeader(isUpdate);
1532
- let totalSteps = 6;
2047
+ const companionInstall = await shouldInstallCompanion(config);
2048
+ let totalSteps = 7;
1533
2049
  if (config.installCustomSkills)
1534
2050
  totalSteps += 1;
2051
+ if (companionInstall)
2052
+ totalSteps += 1;
1535
2053
  totalSteps += 1;
1536
2054
  let step = 1;
1537
2055
  printStep(step++, totalSteps, "Checking OpenCode installation...");
@@ -1590,6 +2108,15 @@ async function runInstall(config) {
1590
2108
  if (!handleStepResult(lspResult, "LSP enabled"))
1591
2109
  return 1;
1592
2110
  }
2111
+ printStep(step++, totalSteps, "Configuring OpenCode background subagents...");
2112
+ const backgroundSubagents = await configureBackgroundSubagents(config);
2113
+ if (companionInstall) {
2114
+ printStep(step++, totalSteps, "Installing desktop companion binary...");
2115
+ const companionResult = await installCompanion(config);
2116
+ handleOptionalCompanionResult(companionResult);
2117
+ if (!companionResult.success)
2118
+ config.companion = "no";
2119
+ }
1593
2120
  printStep(step++, totalSteps, "Writing oh-my-opencode-slim configuration...");
1594
2121
  if (config.dryRun) {
1595
2122
  const liteConfig = generateLiteConfig(config);
@@ -1599,7 +2126,7 @@ ${JSON.stringify(liteConfig, null, 2)}
1599
2126
  `);
1600
2127
  } else {
1601
2128
  const configPath2 = getExistingLiteConfigPath();
1602
- const configExists = existsSync5(configPath2);
2129
+ const configExists = existsSync7(configPath2);
1603
2130
  if (configExists && !config.reset) {
1604
2131
  printInfo(`Configuration already exists at ${configPath2}. Use --reset to overwrite.`);
1605
2132
  } else {
@@ -1646,7 +2173,15 @@ ${JSON.stringify(liteConfig, null, 2)}
1646
2173
  console.log(` ${BLUE}${configPath}${RESET}`);
1647
2174
  console.log();
1648
2175
  console.log(" 4. Start OpenCode:");
1649
- console.log(` ${BLUE}$ opencode${RESET}`);
2176
+ if (backgroundSubagents.enabledNow) {
2177
+ console.log(` ${BLUE}$ opencode${RESET}`);
2178
+ } else if (backgroundSubagents.configuredTarget) {
2179
+ console.log(` ${BLUE}$ source ${backgroundSubagents.configuredTarget}${RESET}`);
2180
+ console.log(` ${BLUE}$ opencode${RESET}`);
2181
+ console.log(` ${DIM}Or restart your terminal before running opencode.${RESET}`);
2182
+ } else {
2183
+ console.log(` ${BLUE}$ OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true opencode${RESET}`);
2184
+ }
1650
2185
  console.log();
1651
2186
  console.log(" 5. Verify the agents are responding:");
1652
2187
  console.log(` ${BLUE}> ping all agents${RESET}`);
@@ -1668,7 +2203,10 @@ async function install(args) {
1668
2203
  preset: args.preset,
1669
2204
  promptForStar: args.tui,
1670
2205
  dryRun: args.dryRun,
1671
- reset: args.reset ?? false
2206
+ reset: args.reset ?? false,
2207
+ backgroundSubagents: args.backgroundSubagents ?? "ask",
2208
+ backgroundSubagentsTarget: args.backgroundSubagentsTarget,
2209
+ companion: args.companion
1672
2210
  };
1673
2211
  return runInstall(config);
1674
2212
  }
@@ -1686,13 +2224,21 @@ function getGeneratedPresetNames2() {
1686
2224
  function parseArgs(args) {
1687
2225
  const result = {
1688
2226
  tui: true,
1689
- skills: "yes"
2227
+ skills: "yes",
2228
+ companion: "ask"
1690
2229
  };
1691
2230
  for (const arg of args) {
1692
2231
  if (arg === "--no-tui") {
1693
2232
  result.tui = false;
1694
2233
  } else if (arg.startsWith("--skills=")) {
1695
2234
  result.skills = arg.split("=")[1];
2235
+ } else if (arg.startsWith("--companion=")) {
2236
+ const mode = arg.split("=")[1];
2237
+ if (!["ask", "yes", "no"].includes(mode)) {
2238
+ console.error("Unsupported --companion value: use ask, yes, or no");
2239
+ process.exit(1);
2240
+ }
2241
+ result.companion = mode;
1696
2242
  } else if (arg.startsWith("--preset=")) {
1697
2243
  const preset = arg.split("=")[1];
1698
2244
  if (!isGeneratedPresetName2(preset)) {
@@ -1700,6 +2246,15 @@ function parseArgs(args) {
1700
2246
  process.exit(1);
1701
2247
  }
1702
2248
  result.preset = preset;
2249
+ } else if (arg.startsWith("--background-subagents=")) {
2250
+ const mode = arg.split("=")[1];
2251
+ if (!["ask", "yes", "no"].includes(mode)) {
2252
+ console.error("Unsupported --background-subagents value: use ask, yes, or no");
2253
+ process.exit(1);
2254
+ }
2255
+ result.backgroundSubagents = mode;
2256
+ } else if (arg.startsWith("--background-subagents-target=")) {
2257
+ result.backgroundSubagentsTarget = arg.split("=")[1];
1703
2258
  } else if (arg === "--dry-run") {
1704
2259
  result.dryRun = true;
1705
2260
  } else if (arg === "--reset") {
@@ -1709,6 +2264,7 @@ function parseArgs(args) {
1709
2264
  process.exit(0);
1710
2265
  }
1711
2266
  }
2267
+ result.backgroundSubagents ??= "ask";
1712
2268
  return result;
1713
2269
  }
1714
2270
  function printHelp() {
@@ -1721,7 +2277,14 @@ Usage:
1721
2277
 
1722
2278
  Options:
1723
2279
  --skills=yes|no Install bundled skills (default: yes)
2280
+ --companion=ask|yes|no Install desktop companion binary and enable config
2281
+ (default: ask; prompt defaults to yes)
1724
2282
  --preset=<name> Active generated config preset (default: openai)
2283
+ --background-subagents=ask|yes|no
2284
+ Persist required OpenCode background subagent env
2285
+ (default: ask; prompt defaults to yes)
2286
+ --background-subagents-target=<path>
2287
+ Shell startup file to update
1725
2288
  --no-tui Non-interactive mode
1726
2289
  --dry-run Simulate install without writing files
1727
2290
  --reset Force overwrite of existing configuration
@@ -1739,6 +2302,7 @@ For the full config reference, see docs/configuration.md.
1739
2302
  Examples:
1740
2303
  bunx oh-my-opencode-slim install
1741
2304
  bunx oh-my-opencode-slim install --no-tui --skills=yes
2305
+ bunx oh-my-opencode-slim install --background-subagents=yes
1742
2306
  bunx oh-my-opencode-slim install --preset=opencode-go
1743
2307
  bunx oh-my-opencode-slim install --reset
1744
2308
  bunx oh-my-opencode-slim doctor
@@ -1764,7 +2328,12 @@ async function main() {
1764
2328
  process.exit(1);
1765
2329
  }
1766
2330
  }
1767
- main().catch((err) => {
1768
- console.error("Fatal error:", err);
1769
- process.exit(1);
1770
- });
2331
+ if (__require.main == __require.module) {
2332
+ main().catch((err) => {
2333
+ console.error("Fatal error:", err);
2334
+ process.exit(1);
2335
+ });
2336
+ }
2337
+ export {
2338
+ parseArgs
2339
+ };