toggle-oh-my 1.0.0 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +5 -5
  2. package/index.js +63 -43
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -8,10 +8,10 @@ The plugin reads your opencode config (`~/.config/opencode/opencode.jsonc` or pr
8
8
 
9
9
  | State | plugin entry |
10
10
  |-------|-------------|
11
- | **ON** | `"oh-my-opencode@latest"` |
12
- | **OFF** | `"disabled_oh-my-opencode@latest"` |
11
+ | **ON** | `"oh-my-openagent@latest"` (or `oh-my-opencode@latest`) |
12
+ | **OFF** | `"disabled_oh-my-openagent@latest"` (or `disabled_oh-my-opencode@latest`) |
13
13
 
14
- The version string (`@latest`, `@1.2.3`, etc.) is preserved during the toggle, so you never lose the version pin.
14
+ Both `oh-my-openagent` and `oh-my-opencode` names are supported (they are the same npm package). The version string (`@latest`, `@1.2.3`, etc.) is preserved during the toggle, so you never lose the version pin.
15
15
 
16
16
  ## Usage
17
17
 
@@ -31,10 +31,10 @@ Then add to your `opencode.json` / `opencode.jsonc`:
31
31
 
32
32
  ```json
33
33
  {
34
- "plugin": ["toggle-oh-my", "oh-my-opencode@latest"]
34
+ "plugin": ["toggle-oh-my", "oh-my-openagent@latest"]
35
35
  }
36
36
  ```
37
37
 
38
38
  ## Compatibility
39
39
 
40
- Works with any version of oh-my-opencode. The version specifier (`@latest`, `@1.x`, etc.) is preserved through toggles.
40
+ Works with any version (oh-my-openagent, oh-my-opencode). The version specifier (`@latest`, `@1.x`, etc.) is preserved through toggles.
package/index.js CHANGED
@@ -1,16 +1,23 @@
1
1
  import { readFileSync, writeFileSync, existsSync } from 'fs';
2
2
  import { homedir, platform } from 'os';
3
- import { join, sep } from 'path';
3
+ import { join } from 'path';
4
4
 
5
- const PLUGIN_NAME = 'oh-my-opencode';
5
+ const PLUGIN_NAMES = ['oh-my-openagent', 'oh-my-opencode'];
6
6
  const DISABLED_PREFIX = 'disabled_';
7
- const PLUGIN_DISPLAY = 'oh-my-opencode';
7
+ const PLUGIN_DISPLAY = 'oh-my-openagent';
8
+
9
+ function escapeRegex(str) {
10
+ return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
11
+ }
8
12
 
9
13
  /**
10
- * Find and read the opencode config file that contains the plugin entry.
14
+ * Find and read the opencode config file that contains any of the known plugin names.
11
15
  * Checks project-level config first, then user-level config.
12
16
  */
13
17
  function findConfig() {
18
+ const hasAnyPlugin = (raw) =>
19
+ PLUGIN_NAMES.some((n) => raw.includes(n) || raw.includes(DISABLED_PREFIX + n));
20
+
14
21
  // project-level: <cwd>/.opencode/opencode.json
15
22
  const projectCandidates = [
16
23
  join(process.cwd(), '.opencode', 'opencode.json'),
@@ -19,69 +26,89 @@ function findConfig() {
19
26
  for (const fp of projectCandidates) {
20
27
  if (!existsSync(fp)) continue;
21
28
  const raw = readFileSync(fp, 'utf-8');
22
- if (raw.includes(PLUGIN_NAME) || raw.includes(DISABLED_PREFIX + PLUGIN_NAME)) {
29
+ if (hasAnyPlugin(raw)) {
23
30
  return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
24
31
  }
25
32
  }
26
33
 
27
- // user-level: ~/.config/opencode/opencode.jsonc (or opencode.json)
34
+ // user-level configs: check ALL known platform paths
28
35
  const home = homedir();
29
36
  const userCandidates = [
37
+ // Windows paths (opencode on Windows may use any of these)
30
38
  join(home, '.config', 'opencode', 'opencode.jsonc'),
31
39
  join(home, '.config', 'opencode', 'opencode.json'),
32
40
  join(home, '.opencode.json'),
33
41
  ];
34
- for (const fp of userCandidates) {
35
- if (!existsSync(fp)) continue;
36
- const raw = readFileSync(fp, 'utf-8');
37
- return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
42
+ // Windows: %APPDATA%/opencode/config.json
43
+ if (platform() === 'win32' && process.env.APPDATA) {
44
+ userCandidates.push(
45
+ join(process.env.APPDATA, 'opencode', 'config.json'),
46
+ join(process.env.APPDATA, 'opencode', 'opencode.jsonc'),
47
+ );
38
48
  }
39
-
40
- // Linux/macOS fallback: $XDG_CONFIG_HOME/opencode/opencode.jsonc
49
+ // Linux/macOS: $XDG_CONFIG_HOME
41
50
  if (platform() !== 'win32') {
42
51
  const xdg = process.env.XDG_CONFIG_HOME || join(home, '.config');
43
- const fp = join(xdg, 'opencode', 'opencode.jsonc');
44
- if (existsSync(fp)) {
52
+ userCandidates.push(join(xdg, 'opencode', 'opencode.jsonc'));
53
+ }
54
+
55
+ for (const fp of userCandidates) {
56
+ if (!existsSync(fp)) continue;
57
+ try {
45
58
  const raw = readFileSync(fp, 'utf-8');
46
- return { path: fp, raw, format: 'jsonc' };
47
- }
59
+ return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
60
+ } catch { /* permission issue, try next */ }
48
61
  }
49
62
 
50
63
  return null;
51
64
  }
52
65
 
53
66
  /**
54
- * Toggle oh-my-opencode in the raw config text.
55
- * Uses regex to find the plugin string and toggle its name.
56
- * Preserves original formatting, comments, and whitespace.
67
+ * Detect which plugin name is currently in the config (enabled or disabled).
68
+ * Returns the actual name found, or null.
69
+ */
70
+ function detectActiveName(raw) {
71
+ for (const name of PLUGIN_NAMES) {
72
+ const enabledRE = new RegExp(`"(${escapeRegex(name)}(?:@[^"]*)?)"`);
73
+ const disabledRE = new RegExp(`"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(name)}(?:@[^"]*)?)"`);
74
+ if (enabledRE.test(raw) || disabledRE.test(raw)) {
75
+ return name;
76
+ }
77
+ }
78
+ return null;
79
+ }
80
+
81
+ /**
82
+ * Toggle the plugin in the raw config text.
83
+ * Preserves version and original formatting.
57
84
  */
58
85
  function toggleInText(raw) {
59
- // Match: "oh-my-opencode@version" or "disabled_oh-my-opencode@version"
60
- // Captures: the full string including quotes, and the version part
86
+ const activeName = detectActiveName(raw);
87
+ if (!activeName) {
88
+ return { raw: null, newState: 'not-found', version: '' };
89
+ }
90
+
61
91
  const enabledPattern = new RegExp(
62
- `"(${escapeRegex(PLUGIN_NAME)}(?:@[^"]*)?)"`,
92
+ `"(${escapeRegex(activeName)}(?:@[^"]*)?)"`,
63
93
  'g'
64
94
  );
65
95
  const disabledPattern = new RegExp(
66
- `"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(PLUGIN_NAME)}(?:@[^"]*)?)"`,
96
+ `"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(activeName)}(?:@[^"]*)?)"`,
67
97
  'g'
68
98
  );
69
99
 
70
100
  let isCurrentlyEnabled = false;
71
101
  let version = '';
72
102
 
73
- // Check if currently enabled
74
103
  let match;
75
104
  enabledPattern.lastIndex = 0;
76
105
  while ((match = enabledPattern.exec(raw)) !== null) {
77
106
  isCurrentlyEnabled = true;
78
- // Extract version from the match
79
107
  const atIdx = match[1].indexOf('@');
80
108
  version = atIdx >= 0 ? match[1].slice(atIdx) : '';
81
109
  break;
82
110
  }
83
111
 
84
- // Check if currently disabled
85
112
  disabledPattern.lastIndex = 0;
86
113
  let disabledVersion = '';
87
114
  while ((match = disabledPattern.exec(raw)) !== null) {
@@ -91,31 +118,23 @@ function toggleInText(raw) {
91
118
  break;
92
119
  }
93
120
 
94
- let toggledText;
95
121
  if (isCurrentlyEnabled) {
96
- // Toggle OFF: add disabled_ prefix
97
- toggledText = raw.replace(
122
+ const toggledText = raw.replace(
98
123
  enabledPattern,
99
- `"${DISABLED_PREFIX}${PLUGIN_NAME}${version}"`
124
+ `"${DISABLED_PREFIX}${activeName}${version}"`
100
125
  );
101
- return { raw: toggledText, newState: 'disabled', version };
126
+ return { raw: toggledText, newState: 'disabled', version, activeName };
102
127
  } else if (disabledVersion !== '') {
103
- // Toggle ON: remove disabled_ prefix
104
- toggledText = raw.replace(
128
+ const toggledText = raw.replace(
105
129
  disabledPattern,
106
- `"${PLUGIN_NAME}${disabledVersion}"`
130
+ `"${activeName}${disabledVersion}"`
107
131
  );
108
- return { raw: toggledText, newState: 'enabled', version: disabledVersion };
132
+ return { raw: toggledText, newState: 'enabled', version: disabledVersion, activeName };
109
133
  }
110
134
 
111
- // Not found in any form
112
135
  return { raw: null, newState: 'not-found', version: '' };
113
136
  }
114
137
 
115
- function escapeRegex(str) {
116
- return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
117
- }
118
-
119
138
  export const ToggleOhMyPlugin = async () => {
120
139
  return {
121
140
  config: async (opencodeConfig) => {
@@ -146,7 +165,7 @@ export const ToggleOhMyPlugin = async () => {
146
165
  if (result.newState === 'not-found') {
147
166
  output.parts = [{
148
167
  type: 'text',
149
- text: `⚠️ "${PLUGIN_DISPLAY}" not found in ${configFile.path}. Nothing to toggle.`,
168
+ text: `⚠️ Plugin not found in ${configFile.path}. Nothing to toggle.`,
150
169
  }];
151
170
  return;
152
171
  }
@@ -154,9 +173,10 @@ export const ToggleOhMyPlugin = async () => {
154
173
  // Write the modified config back
155
174
  writeFileSync(configFile.path, result.raw, 'utf-8');
156
175
 
176
+ const name = result.activeName || PLUGIN_DISPLAY;
157
177
  const message = result.newState === 'enabled'
158
- ? `✅ **${PLUGIN_DISPLAY}${result.version}** is now **ENABLED**\n Restart opencode to apply.`
159
- : `❌ **${PLUGIN_DISPLAY}${result.version}** is now **DISABLED**\n Restart opencode to apply.`;
178
+ ? `✅ **${name}${result.version}** is now **ENABLED**\n Restart opencode to apply.`
179
+ : `❌ **${name}${result.version}** is now **DISABLED**\n Restart opencode to apply.`;
160
180
 
161
181
  output.parts = [{ type: 'text', text: message }];
162
182
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "toggle-oh-my",
3
- "version": "1.0.0",
3
+ "version": "1.0.2",
4
4
  "description": "Opencode plugin to toggle oh-my-opencode on/off with a /toggle-oh-my command",
5
5
  "type": "module",
6
6
  "main": "index.js",