oh-my-opencode-slim 1.0.7 โ†’ 1.1.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.
package/README.md CHANGED
@@ -39,8 +39,10 @@ bunx oh-my-opencode-slim@latest install
39
39
 
40
40
  The installer also registers the companion TUI plugin in OpenCode's
41
41
  `tui.json`, which adds a small sidebar showing specialist-agent status plus
42
- active/reusable task sessions. For manual setups, add `oh-my-opencode-slim` to
43
- the `plugin` array in both `opencode.json` and `tui.json`.
42
+ active/reusable task sessions. It also warms OpenCode's plugin cache so bunx
43
+ installs keep loading even after temporary directories are cleaned up. For
44
+ manual setups, add `oh-my-opencode-slim` to the `plugin` array in both
45
+ `opencode.json` and `tui.json`.
44
46
 
45
47
  ### Getting Started
46
48
 
@@ -488,7 +490,9 @@ Use this section as a map: start with installation, then jump to features, confi
488
490
  | **[Session Management](docs/session-management.md)** | Reuse recent child-agent sessions with short aliases instead of starting over |
489
491
  | **[Todo Continuation](docs/todo-continuation.md)** | Auto-continue orchestrator sessions with cooldowns and safety checks |
490
492
  | **[Preset Switching](docs/preset-switching.md)** | Switch agent model presets at runtime with `/preset` |
493
+ | **[Subtask](docs/subtask.md)** | Run a bounded child worker with `/subtask` and return a structured summary to the main session |
491
494
  | **[Codemap](docs/codemap.md)** | Generate hierarchical codemaps to understand large codebases faster |
495
+ | **[Clonedeps](docs/clonedeps.md)** | Clone selected dependency source into an ignored local workspace for inspection |
492
496
  | **[Interview](docs/interview.md)** | Turn rough ideas into a structured markdown spec through a browser-based Q&A flow |
493
497
  | **[Divoom Display](docs/divoom.md)** | Mirror orchestrator and specialist-agent activity to a Divoom MiniToo Bluetooth display |
494
498
 
@@ -498,7 +502,7 @@ Use this section as a map: start with installation, then jump to features, confi
498
502
  |-----|----------------|
499
503
  | **[Configuration](docs/configuration.md)** | Config file locations, JSONC support, prompt overrides, and full option reference |
500
504
  | **[Maintainer Guide](docs/maintainers.md)** | Issue triage rules, label meanings, support routing, and repo maintenance workflow |
501
- | **[Skills](docs/skills.md)** | Built-in and recommended skills such as `simplify`, `agent-browser`, and `codemap` |
505
+ | **[Skills](docs/skills.md)** | Built-in and recommended skills such as `simplify`, `agent-browser`, `codemap`, and `clonedeps` |
502
506
  | **[MCPs](docs/mcps.md)** | `websearch`, `context7`, `grep_app`, and how MCP permissions work per agent |
503
507
  | **[Tools](docs/tools.md)** | Built-in tool capabilities like `webfetch`, LSP tools, code search, and formatters |
504
508
 
@@ -519,7 +523,7 @@ Use this section as a map: start with installation, then jump to features, confi
519
523
  <p><sub>Every merged contribution leaves a mark on the realm.</sub></p>
520
524
 
521
525
  <!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
522
- [![All Contributors](https://img.shields.io/badge/all_contributors-45-orange.svg?style=flat-square)](#contributors-)
526
+ [![All Contributors](https://img.shields.io/badge/all_contributors-46-orange.svg?style=flat-square)](#contributors-)
523
527
  <!-- ALL-CONTRIBUTORS-BADGE:END -->
524
528
  </div>
525
529
 
@@ -590,6 +594,7 @@ Use this section as a map: start with installation, then jump to features, confi
590
594
  <td align="center" valign="top" width="16.66%"><a href="https://github.com/gustavocaiano"><img src="https://avatars.githubusercontent.com/u/104129313?v=4?s=100" width="100px;" alt="Gustavo Caiano"/><br /><sub><b>Gustavo Caiano</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=gustavocaiano" title="Code">๐Ÿ’ป</a></td>
591
595
  <td align="center" valign="top" width="16.66%"><a href="https://github.com/ThomasMldr"><img src="https://avatars.githubusercontent.com/u/6631765?v=4?s=100" width="100px;" alt="Thomas Mulder"/><br /><sub><b>Thomas Mulder</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=ThomasMldr" title="Code">๐Ÿ’ป</a></td>
592
596
  <td align="center" valign="top" width="16.66%"><a href="https://github.com/maou-shonen"><img src="https://avatars.githubusercontent.com/u/22576780?v=4?s=100" width="100px;" alt="้ญ”็Ž‹ๅฐ‘ๅนด(maou shonen)"/><br /><sub><b>้ญ”็Ž‹ๅฐ‘ๅนด(maou shonen)</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=maou-shonen" title="Code">๐Ÿ’ป</a></td>
597
+ <td align="center" valign="top" width="16.66%"><a href="https://github.com/jelasin"><img src="https://avatars.githubusercontent.com/u/97788570?v=4?s=100" width="100px;" alt=" Jelasin"/><br /><sub><b> Jelasin</b></sub></a><br /><a href="https://github.com/alvinunreal/oh-my-opencode-slim/commits?author=jelasin" title="Code">๐Ÿ’ป</a></td>
593
598
  </tr>
594
599
  </tbody>
595
600
  </table>
@@ -1,4 +1,5 @@
1
1
  import type { ConfigMergeResult, DetectedConfig, InstallConfig, OpenCodeConfig } from './types';
2
+ export declare function warmOpenCodePluginCache(): Promise<ConfigMergeResult | null>;
2
3
  /**
3
4
  * Strip JSON comments (single-line // and multi-line) and trailing commas for JSONC support.
4
5
  */
package/dist/cli/index.js CHANGED
@@ -15,13 +15,61 @@ import * as path from "node:path";
15
15
  import {
16
16
  copyFileSync as copyFileSync2,
17
17
  existsSync as existsSync3,
18
+ mkdirSync as mkdirSync3,
18
19
  readFileSync,
19
20
  renameSync,
20
21
  statSync as statSync2,
21
22
  writeFileSync
22
23
  } from "node:fs";
24
+ import { homedir as homedir2 } from "node:os";
23
25
  import { dirname as dirname3, join as join3 } from "node:path";
24
26
 
27
+ // src/utils/compat.ts
28
+ import { spawn as nodeSpawn } from "node:child_process";
29
+ var isBun = typeof globalThis.Bun !== "undefined";
30
+ function collectStream(stream) {
31
+ if (!stream)
32
+ return () => Promise.resolve("");
33
+ const chunks = [];
34
+ stream.on("data", (chunk) => chunks.push(chunk));
35
+ return () => new Promise((resolve, reject) => {
36
+ if (!stream.readable) {
37
+ resolve(Buffer.concat(chunks).toString("utf-8"));
38
+ return;
39
+ }
40
+ stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
41
+ stream.on("error", reject);
42
+ });
43
+ }
44
+ function crossSpawn(command, options) {
45
+ const [cmd, ...args] = command;
46
+ const proc = nodeSpawn(cmd, args, {
47
+ stdio: [
48
+ options?.stdin ?? "ignore",
49
+ options?.stdout ?? "pipe",
50
+ options?.stderr ?? "pipe"
51
+ ],
52
+ cwd: options?.cwd,
53
+ env: options?.env
54
+ });
55
+ const stdoutCollector = collectStream(proc.stdout);
56
+ const stderrCollector = collectStream(proc.stderr);
57
+ const exited = new Promise((resolve, reject) => {
58
+ proc.on("error", reject);
59
+ proc.on("close", (code) => resolve(code ?? 1));
60
+ });
61
+ return {
62
+ proc,
63
+ stdout: stdoutCollector,
64
+ stderr: stderrCollector,
65
+ exited,
66
+ kill: (signal) => proc.kill(signal),
67
+ get exitCode() {
68
+ return proc.exitCode;
69
+ }
70
+ };
71
+ }
72
+
25
73
  // src/cli/paths.ts
26
74
  import { existsSync, mkdirSync } from "node:fs";
27
75
  import { homedir } from "node:os";
@@ -419,6 +467,12 @@ var CUSTOM_SKILLS = [
419
467
  description: "Repository understanding and hierarchical codemap generation",
420
468
  allowedAgents: ["orchestrator"],
421
469
  sourcePath: "src/skills/codemap"
470
+ },
471
+ {
472
+ name: "clonedeps",
473
+ description: "Clone important dependency source for local inspection",
474
+ allowedAgents: ["orchestrator"],
475
+ sourcePath: "src/skills/clonedeps"
422
476
  }
423
477
  ];
424
478
  function getCustomSkillsDir() {
@@ -658,10 +712,6 @@ function findPackageRoot(startPath) {
658
712
  currentPath = parentPath;
659
713
  }
660
714
  }
661
- function isPackageManagerInstall(path) {
662
- const normalizedPath = normalizePathForMatch(path);
663
- return normalizedPath.includes(`/node_modules/${PACKAGE_NAME}`);
664
- }
665
715
  function isLocalPackageRootEntry(entry) {
666
716
  if (!entry || entry.startsWith("file://")) {
667
717
  return false;
@@ -677,6 +727,10 @@ function isLocalPackageRootEntry(entry) {
677
727
  return false;
678
728
  }
679
729
  }
730
+ function isPackageManagerInstall(path) {
731
+ const normalizedPath = normalizePathForMatch(path);
732
+ return normalizedPath.includes(`/node_modules/${PACKAGE_NAME}`);
733
+ }
680
734
  function isPluginEntry(entry) {
681
735
  return entry === PACKAGE_NAME || entry.startsWith(`${PACKAGE_NAME}@`) || entry.startsWith("file://") && entry.includes(PACKAGE_NAME) || isLocalPackageRootEntry(entry);
682
736
  }
@@ -699,6 +753,104 @@ function getPluginEntry() {
699
753
  return PACKAGE_NAME;
700
754
  }
701
755
  }
756
+ function getOpenCodePluginCacheDir() {
757
+ const cacheDir = process.env.XDG_CACHE_HOME?.trim() || join3(homedir2(), ".cache");
758
+ return join3(cacheDir, "opencode", "packages", `${PACKAGE_NAME}@latest`);
759
+ }
760
+ function writeOpenCodePluginCacheManifest(cacheDir) {
761
+ try {
762
+ writeFileSync(join3(cacheDir, "package.json"), JSON.stringify({
763
+ name: `${PACKAGE_NAME}-cache`,
764
+ private: true,
765
+ dependencies: {
766
+ [PACKAGE_NAME]: "latest"
767
+ }
768
+ }, null, 2));
769
+ return null;
770
+ } catch (err) {
771
+ return {
772
+ success: false,
773
+ configPath: cacheDir,
774
+ error: `Failed to write cache package.json: ${err}`
775
+ };
776
+ }
777
+ }
778
+ function verifyOpenCodePluginCache(cacheDir) {
779
+ const pluginPackageJsonPath = join3(cacheDir, "node_modules", PACKAGE_NAME, "package.json");
780
+ if (!existsSync3(pluginPackageJsonPath)) {
781
+ return {
782
+ success: false,
783
+ configPath: cacheDir,
784
+ error: `Cached plugin package not found at ${pluginPackageJsonPath}`
785
+ };
786
+ }
787
+ try {
788
+ const packageJson = JSON.parse(readFileSync(pluginPackageJsonPath, "utf-8"));
789
+ if (packageJson.name !== PACKAGE_NAME) {
790
+ return {
791
+ success: false,
792
+ configPath: cacheDir,
793
+ error: `Cached plugin package has unexpected name: ${packageJson.name}`
794
+ };
795
+ }
796
+ } catch (err) {
797
+ return {
798
+ success: false,
799
+ configPath: cacheDir,
800
+ error: `Failed to verify cached plugin package: ${err}`
801
+ };
802
+ }
803
+ return null;
804
+ }
805
+ async function warmOpenCodePluginCache() {
806
+ const cliEntryPath = process.argv[1];
807
+ if (!cliEntryPath) {
808
+ return null;
809
+ }
810
+ const packageRoot = findPackageRoot(cliEntryPath);
811
+ if (!packageRoot || !isPackageManagerInstall(packageRoot)) {
812
+ return null;
813
+ }
814
+ const cacheDir = getOpenCodePluginCacheDir();
815
+ try {
816
+ mkdirSync3(cacheDir, { recursive: true });
817
+ } catch (err) {
818
+ return {
819
+ success: false,
820
+ configPath: cacheDir,
821
+ error: `Failed to create OpenCode cache directory: ${err}`
822
+ };
823
+ }
824
+ const manifestError = writeOpenCodePluginCacheManifest(cacheDir);
825
+ if (manifestError)
826
+ return manifestError;
827
+ try {
828
+ const proc = crossSpawn(["bun", "install", "--ignore-scripts"], {
829
+ cwd: cacheDir,
830
+ stdout: "pipe",
831
+ stderr: "pipe"
832
+ });
833
+ await proc.exited;
834
+ if (proc.exitCode !== 0) {
835
+ const stderr = (await proc.stderr()).trim();
836
+ return {
837
+ success: false,
838
+ configPath: cacheDir,
839
+ error: stderr || `bun install exited with code ${proc.exitCode}`
840
+ };
841
+ }
842
+ const verificationError = verifyOpenCodePluginCache(cacheDir);
843
+ if (verificationError)
844
+ return verificationError;
845
+ return { success: true, configPath: cacheDir };
846
+ } catch (err) {
847
+ return {
848
+ success: false,
849
+ configPath: cacheDir,
850
+ error: `Failed to warm OpenCode cache: ${err}`
851
+ };
852
+ }
853
+ }
702
854
  function stripJsonComments(json) {
703
855
  const commentPattern = /\\"|"(?:\\"|[^"])*"|(\/\/.*|\/\*[\s\S]*?\*\/)/g;
704
856
  const trailingCommaPattern = /\\"|"(?:\\"|[^"])*"|(,)(\s*[}\]])/g;
@@ -1205,54 +1357,6 @@ import { createInterface } from "node:readline/promises";
1205
1357
  // src/cli/system.ts
1206
1358
  import { spawnSync as spawnSync2 } from "node:child_process";
1207
1359
  import { statSync as statSync4 } from "node:fs";
1208
-
1209
- // src/utils/compat.ts
1210
- import { spawn as nodeSpawn } from "node:child_process";
1211
- var isBun = typeof globalThis.Bun !== "undefined";
1212
- function collectStream(stream) {
1213
- if (!stream)
1214
- return () => Promise.resolve("");
1215
- const chunks = [];
1216
- stream.on("data", (chunk) => chunks.push(chunk));
1217
- return () => new Promise((resolve, reject) => {
1218
- if (!stream.readable) {
1219
- resolve(Buffer.concat(chunks).toString("utf-8"));
1220
- return;
1221
- }
1222
- stream.on("end", () => resolve(Buffer.concat(chunks).toString("utf-8")));
1223
- stream.on("error", reject);
1224
- });
1225
- }
1226
- function crossSpawn(command, options) {
1227
- const [cmd, ...args] = command;
1228
- const proc = nodeSpawn(cmd, args, {
1229
- stdio: [
1230
- options?.stdin ?? "ignore",
1231
- options?.stdout ?? "pipe",
1232
- options?.stderr ?? "pipe"
1233
- ],
1234
- cwd: options?.cwd,
1235
- env: options?.env
1236
- });
1237
- const stdoutCollector = collectStream(proc.stdout);
1238
- const stderrCollector = collectStream(proc.stderr);
1239
- const exited = new Promise((resolve, reject) => {
1240
- proc.on("error", reject);
1241
- proc.on("close", (code) => resolve(code ?? 1));
1242
- });
1243
- return {
1244
- proc,
1245
- stdout: stdoutCollector,
1246
- stderr: stderrCollector,
1247
- exited,
1248
- kill: (signal) => proc.kill(signal),
1249
- get exitCode() {
1250
- return proc.exitCode;
1251
- }
1252
- };
1253
- }
1254
-
1255
- // src/cli/system.ts
1256
1360
  var cachedOpenCodePath = null;
1257
1361
  function resolvePathCommand(command) {
1258
1362
  try {
@@ -1478,6 +1582,7 @@ async function runInstall(config) {
1478
1582
  totalSteps += 1;
1479
1583
  if (config.installCustomSkills)
1480
1584
  totalSteps += 1;
1585
+ totalSteps += 1;
1481
1586
  let step = 1;
1482
1587
  printStep(step++, totalSteps, "Checking OpenCode installation...");
1483
1588
  if (config.dryRun) {
@@ -1506,6 +1611,19 @@ async function runInstall(config) {
1506
1611
  handleStepResult(tuiResult, "TUI badge added");
1507
1612
  }
1508
1613
  }
1614
+ printStep(step++, totalSteps, "Warming OpenCode plugin cache...");
1615
+ if (config.dryRun) {
1616
+ printInfo("Dry run mode - skipping cache warm-up");
1617
+ } else {
1618
+ const cacheResult = await warmOpenCodePluginCache();
1619
+ if (cacheResult === null) {
1620
+ printInfo("Local development install - cache warm-up not required");
1621
+ } else if (!cacheResult.success) {
1622
+ printInfo(`Skipped cache warm-up: ${cacheResult.error}`);
1623
+ } else {
1624
+ handleStepResult(cacheResult, "OpenCode cache warmed");
1625
+ }
1626
+ }
1509
1627
  printStep(step++, totalSteps, "Disabling OpenCode default agents...");
1510
1628
  if (config.dryRun) {
1511
1629
  printInfo("Dry run mode - skipping agent disabling");