toggle-oh-my 1.0.1 → 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 +85 -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,155 +24,133 @@ 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: ~/.config/opencode/opencode.jsonc (or opencode.json)
|
|
35
30
|
const home = homedir();
|
|
36
31
|
const userCandidates = [
|
|
37
32
|
join(home, '.config', 'opencode', 'opencode.jsonc'),
|
|
38
33
|
join(home, '.config', 'opencode', 'opencode.json'),
|
|
39
34
|
join(home, '.opencode.json'),
|
|
40
35
|
];
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return { path: fp, raw, format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
|
|
36
|
+
if (platform() === 'win32' && process.env.APPDATA) {
|
|
37
|
+
userCandidates.push(join(process.env.APPDATA, 'opencode', 'config.json'));
|
|
38
|
+
userCandidates.push(join(process.env.APPDATA, 'opencode', 'opencode.jsonc'));
|
|
45
39
|
}
|
|
46
|
-
|
|
47
|
-
// Linux/macOS fallback: $XDG_CONFIG_HOME/opencode/opencode.jsonc
|
|
48
40
|
if (platform() !== 'win32') {
|
|
49
41
|
const xdg = process.env.XDG_CONFIG_HOME || join(home, '.config');
|
|
50
|
-
|
|
51
|
-
if (existsSync(fp)) {
|
|
52
|
-
const raw = readFileSync(fp, 'utf-8');
|
|
53
|
-
return { path: fp, raw, format: 'jsonc' };
|
|
54
|
-
}
|
|
42
|
+
userCandidates.push(join(xdg, 'opencode', 'opencode.jsonc'));
|
|
55
43
|
}
|
|
56
44
|
|
|
45
|
+
for (const fp of userCandidates) {
|
|
46
|
+
if (!existsSync(fp)) continue;
|
|
47
|
+
try {
|
|
48
|
+
return { path: fp, raw: readFileSync(fp, 'utf-8'), format: fp.endsWith('.jsonc') ? 'jsonc' : 'json' };
|
|
49
|
+
} catch { /* try next */ }
|
|
50
|
+
}
|
|
57
51
|
return null;
|
|
58
52
|
}
|
|
59
53
|
|
|
60
|
-
/**
|
|
61
|
-
* Detect which plugin name is currently in the config (enabled or disabled).
|
|
62
|
-
* Returns the actual name found, or null.
|
|
63
|
-
*/
|
|
64
54
|
function detectActiveName(raw) {
|
|
65
55
|
for (const name of PLUGIN_NAMES) {
|
|
66
|
-
const
|
|
67
|
-
|
|
68
|
-
if (enabledRE.test(raw) || disabledRE.test(raw)) {
|
|
69
|
-
return name;
|
|
70
|
-
}
|
|
56
|
+
const re = new RegExp(`"(${escapeRegex(name)}|${escapeRegex(DISABLED_PREFIX)}${escapeRegex(name)})(?:@[^"]*)?"`);
|
|
57
|
+
if (re.test(raw)) return name;
|
|
71
58
|
}
|
|
72
59
|
return null;
|
|
73
60
|
}
|
|
74
61
|
|
|
75
62
|
/**
|
|
76
|
-
* Toggle the plugin in the
|
|
77
|
-
* Preserves version and original formatting.
|
|
63
|
+
* Toggle the plugin name in the config text.
|
|
64
|
+
* Preserves version and original formatting via regex replacement.
|
|
78
65
|
*/
|
|
79
66
|
function toggleInText(raw) {
|
|
80
67
|
const activeName = detectActiveName(raw);
|
|
81
|
-
if (!activeName) {
|
|
82
|
-
return { raw: null, newState: 'not-found', version: '' };
|
|
83
|
-
}
|
|
68
|
+
if (!activeName) return { raw: null, newState: 'not-found', version: '' };
|
|
84
69
|
|
|
85
|
-
const
|
|
86
|
-
|
|
87
|
-
'g'
|
|
88
|
-
);
|
|
89
|
-
const disabledPattern = new RegExp(
|
|
90
|
-
`"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(activeName)}(?:@[^"]*)?)"`,
|
|
91
|
-
'g'
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
let isCurrentlyEnabled = false;
|
|
95
|
-
let version = '';
|
|
96
|
-
|
|
97
|
-
let match;
|
|
98
|
-
enabledPattern.lastIndex = 0;
|
|
99
|
-
while ((match = enabledPattern.exec(raw)) !== null) {
|
|
100
|
-
isCurrentlyEnabled = true;
|
|
101
|
-
const atIdx = match[1].indexOf('@');
|
|
102
|
-
version = atIdx >= 0 ? match[1].slice(atIdx) : '';
|
|
103
|
-
break;
|
|
104
|
-
}
|
|
70
|
+
const enabledRE = new RegExp(`"(${escapeRegex(activeName)}(?:@[^"]*)?)"`, 'g');
|
|
71
|
+
const disabledRE = new RegExp(`"(${escapeRegex(DISABLED_PREFIX)}${escapeRegex(activeName)}(?:@[^"]*)?)"`, 'g');
|
|
105
72
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
disabledVersion = atIdx >= 0 ? match[1].slice(atIdx) : '';
|
|
112
|
-
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 };
|
|
113
78
|
}
|
|
114
79
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
);
|
|
120
|
-
return { raw: toggledText, newState: 'disabled', version, activeName };
|
|
121
|
-
} else if (disabledVersion !== '') {
|
|
122
|
-
const toggledText = raw.replace(
|
|
123
|
-
disabledPattern,
|
|
124
|
-
`"${activeName}${disabledVersion}"`
|
|
125
|
-
);
|
|
126
|
-
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 };
|
|
127
84
|
}
|
|
128
85
|
|
|
129
86
|
return { raw: null, newState: 'not-found', version: '' };
|
|
130
87
|
}
|
|
131
88
|
|
|
89
|
+
// ============================================================
|
|
90
|
+
// Server plugin (hooks) — registers in command palette
|
|
91
|
+
// ============================================================
|
|
92
|
+
|
|
132
93
|
export const ToggleOhMyPlugin = async () => {
|
|
133
94
|
return {
|
|
134
95
|
config: async (opencodeConfig) => {
|
|
135
|
-
// Register the /toggle-oh-my command
|
|
136
96
|
opencodeConfig.command ??= {};
|
|
137
97
|
opencodeConfig.command['toggle-oh-my'] = {
|
|
138
98
|
template: '',
|
|
139
|
-
description: 'Toggle oh-my-
|
|
99
|
+
description: 'Toggle oh-my-openagent on/off (instant, no LLM needed)',
|
|
140
100
|
};
|
|
141
101
|
},
|
|
102
|
+
};
|
|
103
|
+
};
|
|
142
104
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
if (!configFile) {
|
|
148
|
-
output.parts = [{
|
|
149
|
-
type: 'text',
|
|
150
|
-
text: '❌ Could not find opencode config file. Checked:\n' +
|
|
151
|
-
' - .opencode/opencode.json\n' +
|
|
152
|
-
' - ~/.config/opencode/opencode.jsonc\n' +
|
|
153
|
-
' - ~/.opencode.json',
|
|
154
|
-
}];
|
|
155
|
-
return;
|
|
156
|
-
}
|
|
105
|
+
// ============================================================
|
|
106
|
+
// TUI plugin — instant command without LLM involvement
|
|
107
|
+
// Uses api.command.register to add a slash command with onSelect
|
|
108
|
+
// ============================================================
|
|
157
109
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
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
|
+
}
|
|
116
|
+
|
|
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
|
+
}
|
|
166
122
|
|
|
167
|
-
|
|
168
|
-
writeFileSync(configFile.path, result.raw, 'utf-8');
|
|
123
|
+
writeFileSync(file.path, r.raw, 'utf-8');
|
|
169
124
|
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
? `✅ **${name}${result.version}** is now **ENABLED**\n Restart opencode to apply.`
|
|
173
|
-
: `❌ **${name}${result.version}** is now **DISABLED**\n Restart opencode to apply.`;
|
|
125
|
+
const status = r.newState === 'enabled' ? 'ENABLED' : 'DISABLED';
|
|
126
|
+
const msg = `${r.activeName}${r.version} → ${status} (restart opencode to apply)`;
|
|
174
127
|
|
|
175
|
-
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
}
|
|
128
|
+
try {
|
|
129
|
+
api.ui?.toast?.({ title: 'toggle-oh-my', body: msg, type: 'info' });
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
179
132
|
|
|
180
|
-
export default
|
|
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
|
+
};
|