toggle-oh-my 1.0.2 → 1.0.3
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/index.js +79 -109
- package/package.json +1 -1
package/index.js
CHANGED
|
@@ -2,23 +2,21 @@ import { readFileSync, writeFileSync, existsSync } from 'fs';
|
|
|
2
2
|
import { homedir, platform } from 'os';
|
|
3
3
|
import { join } from 'path';
|
|
4
4
|
|
|
5
|
+
// ============================================================
|
|
6
|
+
// Core toggle logic (shared between server and TUI plugins)
|
|
7
|
+
// ============================================================
|
|
8
|
+
|
|
5
9
|
const PLUGIN_NAMES = ['oh-my-openagent', 'oh-my-opencode'];
|
|
6
10
|
const DISABLED_PREFIX = 'disabled_';
|
|
7
|
-
const PLUGIN_DISPLAY = 'oh-my-openagent';
|
|
8
11
|
|
|
9
12
|
function escapeRegex(str) {
|
|
10
13
|
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
11
14
|
}
|
|
12
15
|
|
|
13
|
-
/**
|
|
14
|
-
* Find and read the opencode config file that contains any of the known plugin names.
|
|
15
|
-
* Checks project-level config first, then user-level config.
|
|
16
|
-
*/
|
|
17
16
|
function findConfig() {
|
|
18
17
|
const hasAnyPlugin = (raw) =>
|
|
19
18
|
PLUGIN_NAMES.some((n) => raw.includes(n) || raw.includes(DISABLED_PREFIX + n));
|
|
20
19
|
|
|
21
|
-
// project-level: <cwd>/.opencode/opencode.json
|
|
22
20
|
const projectCandidates = [
|
|
23
21
|
join(process.cwd(), '.opencode', 'opencode.json'),
|
|
24
22
|
join(process.cwd(), '.opencode', 'opencode.jsonc'),
|
|
@@ -26,27 +24,19 @@ function findConfig() {
|
|
|
26
24
|
for (const fp of projectCandidates) {
|
|
27
25
|
if (!existsSync(fp)) continue;
|
|
28
26
|
const raw = readFileSync(fp, 'utf-8');
|
|
29
|
-
if (hasAnyPlugin(raw)) {
|
|
30
|
-
return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
|
|
31
|
-
}
|
|
27
|
+
if (hasAnyPlugin(raw)) return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
|
|
32
28
|
}
|
|
33
29
|
|
|
34
|
-
// user-level configs: check ALL known platform paths
|
|
35
30
|
const home = homedir();
|
|
36
31
|
const userCandidates = [
|
|
37
|
-
// Windows paths (opencode on Windows may use any of these)
|
|
38
32
|
join(home, '.config', 'opencode', 'opencode.jsonc'),
|
|
39
33
|
join(home, '.config', 'opencode', 'opencode.json'),
|
|
40
34
|
join(home, '.opencode.json'),
|
|
41
35
|
];
|
|
42
|
-
// Windows: %APPDATA%/opencode/config.json
|
|
43
36
|
if (platform() === 'win32' && process.env.APPDATA) {
|
|
44
|
-
userCandidates.push(
|
|
45
|
-
|
|
46
|
-
join(process.env.APPDATA, 'opencode', 'opencode.jsonc'),
|
|
47
|
-
);
|
|
37
|
+
userCandidates.push(join(process.env.APPDATA, 'opencode', 'config.json'));
|
|
38
|
+
userCandidates.push(join(process.env.APPDATA, 'opencode', 'opencode.jsonc'));
|
|
48
39
|
}
|
|
49
|
-
// Linux/macOS: $XDG_CONFIG_HOME
|
|
50
40
|
if (platform() !== 'win32') {
|
|
51
41
|
const xdg = process.env.XDG_CONFIG_HOME || join(home, '.config');
|
|
52
42
|
userCandidates.push(join(xdg, 'opencode', 'opencode.jsonc'));
|
|
@@ -55,132 +45,112 @@ function findConfig() {
|
|
|
55
45
|
for (const fp of userCandidates) {
|
|
56
46
|
if (!existsSync(fp)) continue;
|
|
57
47
|
try {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
} catch { /* permission issue, try next */ }
|
|
48
|
+
return { path: fp, raw: readFileSync(fp, 'utf-8'), format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
|
|
49
|
+
} catch { /* try next */ }
|
|
61
50
|
}
|
|
62
|
-
|
|
63
51
|
return null;
|
|
64
52
|
}
|
|
65
53
|
|
|
66
|
-
/**
|
|
67
|
-
* Detect which plugin name is currently in the config (enabled or disabled).
|
|
68
|
-
* Returns the actual name found, or null.
|
|
69
|
-
*/
|
|
70
54
|
function detectActiveName(raw) {
|
|
71
55
|
for (const name of PLUGIN_NAMES) {
|
|
72
|
-
const
|
|
73
|
-
|
|
74
|
-
if (enabledRE.test(raw) || disabledRE.test(raw)) {
|
|
75
|
-
return name;
|
|
76
|
-
}
|
|
56
|
+
const re = new RegExp(`"(${escapeRegex(name)}|${escapeRegex(DISABLED_PREFIX)}${escapeRegex(name)})(?:@[^"]*)?"`);
|
|
57
|
+
if (re.test(raw)) return name;
|
|
77
58
|
}
|
|
78
59
|
return null;
|
|
79
60
|
}
|
|
80
61
|
|
|
81
62
|
/**
|
|
82
|
-
* Toggle the plugin in the
|
|
83
|
-
* Preserves version and original formatting.
|
|
63
|
+
* Toggle the plugin name in the config text.
|
|
64
|
+
* Preserves version and original formatting via regex replacement.
|
|
84
65
|
*/
|
|
85
66
|
function toggleInText(raw) {
|
|
86
67
|
const activeName = detectActiveName(raw);
|
|
87
|
-
if (!activeName) {
|
|
88
|
-
return { raw: null, newState: 'not-found', version: '' };
|
|
89
|
-
}
|
|
68
|
+
if (!activeName) return { raw: null, newState: 'not-found', version: '' };
|
|
90
69
|
|
|
91
|
-
const
|
|
92
|
-
|
|
93
|
-
'g'
|
|
94
|
-
);
|
|
95
|
-
const disabledPattern = new RegExp(
|
|
96
|
-
`"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(activeName)}(?:@[^"]*)?)"`,
|
|
97
|
-
'g'
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
let isCurrentlyEnabled = false;
|
|
101
|
-
let version = '';
|
|
102
|
-
|
|
103
|
-
let match;
|
|
104
|
-
enabledPattern.lastIndex = 0;
|
|
105
|
-
while ((match = enabledPattern.exec(raw)) !== null) {
|
|
106
|
-
isCurrentlyEnabled = true;
|
|
107
|
-
const atIdx = match[1].indexOf('@');
|
|
108
|
-
version = atIdx >= 0 ? match[1].slice(atIdx) : '';
|
|
109
|
-
break;
|
|
110
|
-
}
|
|
70
|
+
const enabledRE = new RegExp(`"(${escapeRegex(activeName)}(?:@[^"]*)?)"`, 'g');
|
|
71
|
+
const disabledRE = new RegExp(`"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(activeName)}(?:@[^"]*)?)"`, 'g');
|
|
111
72
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
disabledVersion = atIdx >= 0 ? match[1].slice(atIdx) : '';
|
|
118
|
-
break;
|
|
73
|
+
let m;
|
|
74
|
+
enabledRE.lastIndex = 0;
|
|
75
|
+
if ((m = enabledRE.exec(raw))) {
|
|
76
|
+
const version = m[1].includes('@') ? m[1].slice(m[1].indexOf('@')) : '';
|
|
77
|
+
return { raw: raw.replace(enabledRE, `"${DISABLED_PREFIX}${activeName}${version}"`), newState: 'disabled', version, activeName };
|
|
119
78
|
}
|
|
120
79
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
);
|
|
126
|
-
return { raw: toggledText, newState: 'disabled', version, activeName };
|
|
127
|
-
} else if (disabledVersion !== '') {
|
|
128
|
-
const toggledText = raw.replace(
|
|
129
|
-
disabledPattern,
|
|
130
|
-
`"${activeName}${disabledVersion}"`
|
|
131
|
-
);
|
|
132
|
-
return { raw: toggledText, newState: 'enabled', version: disabledVersion, activeName };
|
|
80
|
+
disabledRE.lastIndex = 0;
|
|
81
|
+
if ((m = disabledRE.exec(raw))) {
|
|
82
|
+
const version = m[1].includes('@') ? m[1].slice(m[1].indexOf('@')) : '';
|
|
83
|
+
return { raw: raw.replace(disabledRE, `"${activeName}${version}"`), newState: 'enabled', version, activeName };
|
|
133
84
|
}
|
|
134
85
|
|
|
135
86
|
return { raw: null, newState: 'not-found', version: '' };
|
|
136
87
|
}
|
|
137
88
|
|
|
89
|
+
// ============================================================
|
|
90
|
+
// Server plugin (hooks) — registers in command palette
|
|
91
|
+
// ============================================================
|
|
92
|
+
|
|
138
93
|
export const ToggleOhMyPlugin = async () => {
|
|
139
94
|
return {
|
|
140
95
|
config: async (opencodeConfig) => {
|
|
141
|
-
// Register the /toggle-oh-my command
|
|
142
96
|
opencodeConfig.command ??= {};
|
|
143
97
|
opencodeConfig.command['toggle-oh-my'] = {
|
|
144
98
|
template: '',
|
|
145
|
-
description: 'Toggle oh-my-
|
|
99
|
+
description: 'Toggle oh-my-openagent on/off (instant, no LLM needed)',
|
|
146
100
|
};
|
|
147
101
|
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
148
104
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
if (!configFile) {
|
|
154
|
-
output.parts = [{
|
|
155
|
-
type: 'text',
|
|
156
|
-
text: '❌ Could not find opencode config file. Checked:\n' +
|
|
157
|
-
' - .opencode/opencode.json\n' +
|
|
158
|
-
' - ~/.config/opencode/opencode.jsonc\n' +
|
|
159
|
-
' - ~/.opencode.json',
|
|
160
|
-
}];
|
|
161
|
-
return;
|
|
162
|
-
}
|
|
105
|
+
// ============================================================
|
|
106
|
+
// TUI plugin — instant command without LLM involvement
|
|
107
|
+
// Uses api.command.register to add a slash command with onSelect
|
|
108
|
+
// ============================================================
|
|
163
109
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
110
|
+
async function performToggle(api) {
|
|
111
|
+
const file = findConfig();
|
|
112
|
+
if (!file) {
|
|
113
|
+
try { api.ui?.toast?.({ title: 'toggle-oh-my', body: 'Config file not found', type: 'error' }); } catch {}
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
172
116
|
|
|
173
|
-
|
|
174
|
-
|
|
117
|
+
const r = toggleInText(file.raw);
|
|
118
|
+
if (r.newState === 'not-found') {
|
|
119
|
+
try { api.ui?.toast?.({ title: 'toggle-oh-my', body: 'Plugin not found in config', type: 'warning' }); } catch {}
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
175
122
|
|
|
176
|
-
|
|
177
|
-
const message = result.newState === 'enabled'
|
|
178
|
-
? `✅ **${name}${result.version}** is now **ENABLED**\n Restart opencode to apply.`
|
|
179
|
-
: `❌ **${name}${result.version}** is now **DISABLED**\n Restart opencode to apply.`;
|
|
123
|
+
writeFileSync(file.path, r.raw, 'utf-8');
|
|
180
124
|
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
};
|
|
184
|
-
};
|
|
125
|
+
const status = r.newState === 'enabled' ? 'ENABLED' : 'DISABLED';
|
|
126
|
+
const msg = `${r.activeName}${r.version} → ${status} (restart opencode to apply)`;
|
|
185
127
|
|
|
186
|
-
|
|
128
|
+
try {
|
|
129
|
+
api.ui?.toast?.({ title: 'toggle-oh-my', body: msg, type: 'info' });
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export default {
|
|
134
|
+
id: 'toggle-oh-my',
|
|
135
|
+
tui: async (api) => {
|
|
136
|
+
try {
|
|
137
|
+
const unregister = api.command.register(() => [
|
|
138
|
+
{
|
|
139
|
+
title: 'Toggle Oh My',
|
|
140
|
+
value: 'toggle-oh-my',
|
|
141
|
+
description: 'Toggle oh-my-openagent on/off instantly (restart to apply)',
|
|
142
|
+
category: 'Plugins',
|
|
143
|
+
slash: { name: 'toggle-oh-my' },
|
|
144
|
+
onSelect: () => performToggle(api),
|
|
145
|
+
},
|
|
146
|
+
]);
|
|
147
|
+
|
|
148
|
+
// Cleanup on dispose
|
|
149
|
+
if (typeof unregister === 'function') {
|
|
150
|
+
// store for later cleanup if needed
|
|
151
|
+
}
|
|
152
|
+
} catch (e) {
|
|
153
|
+
console.error('toggle-oh-my TUI registration failed:', e);
|
|
154
|
+
}
|
|
155
|
+
},
|
|
156
|
+
};
|