@syntesseraai/opencode-feature-factory 0.6.7 → 0.6.8
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/bin/ff-deploy.js +81 -7
- package/dist/agent-config.js +17 -51
- package/dist/mcp-config.js +17 -51
- package/dist/opencode-global-config.d.ts +9 -0
- package/dist/opencode-global-config.js +79 -0
- package/dist/plugin-config.js +17 -54
- package/package.json +1 -1
package/bin/ff-deploy.js
CHANGED
|
@@ -51,6 +51,40 @@ const DEFAULT_MCP_SERVERS = {
|
|
|
51
51
|
},
|
|
52
52
|
};
|
|
53
53
|
|
|
54
|
+
const DEFAULT_PLUGINS = [
|
|
55
|
+
'@syntesseraai/opencode-feature-factory@latest',
|
|
56
|
+
'@nick-vi/opencode-type-inject@latest',
|
|
57
|
+
'@franlol/opencode-md-table-formatter@latest',
|
|
58
|
+
'@spoons-and-mirrors/subtask2@latest',
|
|
59
|
+
'opencode-pty@latest',
|
|
60
|
+
'@angdrew/opencode-hashline-plugin@latest',
|
|
61
|
+
];
|
|
62
|
+
|
|
63
|
+
function getPackageBaseName(plugin) {
|
|
64
|
+
if (plugin.startsWith('@')) {
|
|
65
|
+
const slashIndex = plugin.indexOf('/');
|
|
66
|
+
if (slashIndex !== -1) {
|
|
67
|
+
const versionAt = plugin.indexOf('@', slashIndex + 1);
|
|
68
|
+
if (versionAt !== -1) {
|
|
69
|
+
return plugin.substring(0, versionAt);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return plugin;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const atIndex = plugin.indexOf('@');
|
|
76
|
+
if (atIndex !== -1) {
|
|
77
|
+
return plugin.substring(0, atIndex);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return plugin;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function hasPlugin(existing, plugin) {
|
|
84
|
+
const baseName = getPackageBaseName(plugin);
|
|
85
|
+
return existing.some((entry) => getPackageBaseName(entry) === baseName);
|
|
86
|
+
}
|
|
87
|
+
|
|
54
88
|
async function ensureDir(dir) {
|
|
55
89
|
try {
|
|
56
90
|
await fs.mkdir(dir, { recursive: true });
|
|
@@ -127,11 +161,30 @@ async function updateMCPConfig() {
|
|
|
127
161
|
try {
|
|
128
162
|
// Read existing config if it exists
|
|
129
163
|
let existingConfig = {};
|
|
164
|
+
let hasExistingConfigFile = false;
|
|
130
165
|
try {
|
|
131
166
|
const configContent = await fs.readFile(GLOBAL_CONFIG_FILE, 'utf8');
|
|
167
|
+
hasExistingConfigFile = true;
|
|
132
168
|
existingConfig = JSON.parse(configContent);
|
|
133
|
-
|
|
134
|
-
|
|
169
|
+
if (!existingConfig || typeof existingConfig !== 'object' || Array.isArray(existingConfig)) {
|
|
170
|
+
if (isInteractive) {
|
|
171
|
+
console.warn('\n⚠️ Existing opencode.json is not a JSON object. Skipping update.');
|
|
172
|
+
}
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (error && error.code === 'ENOENT') {
|
|
177
|
+
// No existing config, will create new
|
|
178
|
+
} else if (error instanceof SyntaxError) {
|
|
179
|
+
if (isInteractive) {
|
|
180
|
+
console.warn(
|
|
181
|
+
'\n⚠️ Existing opencode.json is invalid JSON. Skipping update to avoid overwriting user config.'
|
|
182
|
+
);
|
|
183
|
+
}
|
|
184
|
+
return;
|
|
185
|
+
} else {
|
|
186
|
+
throw error;
|
|
187
|
+
}
|
|
135
188
|
}
|
|
136
189
|
|
|
137
190
|
// Check which MCP servers need to be added
|
|
@@ -157,15 +210,33 @@ async function updateMCPConfig() {
|
|
|
157
210
|
}
|
|
158
211
|
}
|
|
159
212
|
|
|
160
|
-
|
|
213
|
+
const existingPlugins = Array.isArray(existingConfig.plugin)
|
|
214
|
+
? existingConfig.plugin.filter((entry) => typeof entry === 'string')
|
|
215
|
+
: [];
|
|
216
|
+
const pluginsToAdd = [];
|
|
217
|
+
|
|
218
|
+
for (const plugin of DEFAULT_PLUGINS) {
|
|
219
|
+
if (hasPlugin(existingPlugins, plugin)) {
|
|
220
|
+
if (isInteractive) {
|
|
221
|
+
console.log(` ⏭️ ${plugin}: already exists, skipping`);
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
pluginsToAdd.push(plugin);
|
|
225
|
+
if (isInteractive) {
|
|
226
|
+
console.log(` ✅ ${plugin}: will be added`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
if (serversAdded === 0 && pluginsToAdd.length === 0) {
|
|
161
232
|
if (isInteractive) {
|
|
162
|
-
console.log('\n✅
|
|
233
|
+
console.log('\n✅ Required MCP servers and plugins already configured. No changes needed.');
|
|
163
234
|
}
|
|
164
235
|
return;
|
|
165
236
|
}
|
|
166
237
|
|
|
167
238
|
// Backup existing config if it exists
|
|
168
|
-
if (existingConfig && Object.keys(existingConfig).length > 0) {
|
|
239
|
+
if (hasExistingConfigFile && existingConfig && Object.keys(existingConfig).length > 0) {
|
|
169
240
|
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
170
241
|
const backupFile = `${GLOBAL_CONFIG_FILE}.backup.${timestamp}`;
|
|
171
242
|
await fs.writeFile(backupFile, JSON.stringify(existingConfig, null, 2));
|
|
@@ -181,6 +252,7 @@ async function updateMCPConfig() {
|
|
|
181
252
|
...existingMcp,
|
|
182
253
|
...serversToAdd,
|
|
183
254
|
},
|
|
255
|
+
plugin: [...existingPlugins, ...pluginsToAdd],
|
|
184
256
|
};
|
|
185
257
|
|
|
186
258
|
// Write updated config
|
|
@@ -188,11 +260,13 @@ async function updateMCPConfig() {
|
|
|
188
260
|
await fs.writeFile(GLOBAL_CONFIG_FILE, JSON.stringify(updatedConfig, null, 2));
|
|
189
261
|
|
|
190
262
|
if (isInteractive) {
|
|
191
|
-
console.log(`\n✅
|
|
263
|
+
console.log(`\n✅ Updated OpenCode config: ${GLOBAL_CONFIG_FILE}`);
|
|
264
|
+
console.log(` Added ${serversAdded} MCP server(s)`);
|
|
265
|
+
console.log(` Added ${pluginsToAdd.length} plugin(s)`);
|
|
192
266
|
if (serversSkipped > 0) {
|
|
193
267
|
console.log(` Skipped ${serversSkipped} existing server(s)`);
|
|
194
268
|
}
|
|
195
|
-
console.log('\n📝 Note: Restart OpenCode to load new
|
|
269
|
+
console.log('\n📝 Note: Restart OpenCode to load new configuration');
|
|
196
270
|
}
|
|
197
271
|
} catch (error) {
|
|
198
272
|
if (isInteractive) {
|
package/dist/agent-config.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
const GLOBAL_OPENCODE_DIR = join(homedir(), '.config', 'opencode');
|
|
5
|
-
const GLOBAL_OPENCODE_CONFIG_PATH = join(GLOBAL_OPENCODE_DIR, 'opencode.json');
|
|
1
|
+
import { isRecord, updateGlobalOpenCodeConfigBlock } from './opencode-global-config.js';
|
|
6
2
|
/**
|
|
7
3
|
* Built-in opencode agents that should be disabled when the Feature Factory
|
|
8
4
|
* plugin is active. These are the standard opencode agents that overlap with
|
|
@@ -42,50 +38,20 @@ export function mergeAgentConfigs(existing, defaults) {
|
|
|
42
38
|
* @param $ - Bun shell instance
|
|
43
39
|
*/
|
|
44
40
|
export async function updateAgentConfig($) {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
};
|
|
62
|
-
// Ensure global config directory exists
|
|
63
|
-
try {
|
|
64
|
-
await $ `mkdir -p ${GLOBAL_OPENCODE_DIR}`.quiet();
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
// Directory might already exist, ignore
|
|
68
|
-
}
|
|
69
|
-
// Backup existing global config if it exists and has content
|
|
70
|
-
if (globalJson && Object.keys(globalJson).length > 0) {
|
|
71
|
-
try {
|
|
72
|
-
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
73
|
-
const backupPath = `${GLOBAL_OPENCODE_CONFIG_PATH}.backup.${timestamp}`;
|
|
74
|
-
const backupContent = JSON.stringify(globalJson, null, 2);
|
|
75
|
-
await $ `echo ${backupContent} > ${backupPath}`.quiet();
|
|
76
|
-
}
|
|
77
|
-
catch (error) {
|
|
78
|
-
// Backup failed, but continue anyway
|
|
79
|
-
console.warn('[feature-factory] Could not create backup:', error);
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// Write updated config to global opencode.json
|
|
83
|
-
const configContent = JSON.stringify(updatedGlobalConfig, null, 2);
|
|
84
|
-
try {
|
|
85
|
-
await $ `echo ${configContent} > ${GLOBAL_OPENCODE_CONFIG_PATH}`.quiet();
|
|
86
|
-
}
|
|
87
|
-
catch (error) {
|
|
88
|
-
// Silently fail - don't block if we can't write the config
|
|
89
|
-
console.warn('[feature-factory] Could not update agent config:', error);
|
|
90
|
-
}
|
|
41
|
+
void $;
|
|
42
|
+
await updateGlobalOpenCodeConfigBlock({
|
|
43
|
+
blockName: 'agent',
|
|
44
|
+
warningLabel: 'agent config',
|
|
45
|
+
update: (existingBlock) => {
|
|
46
|
+
const existingAgentConfigs = isRecord(existingBlock)
|
|
47
|
+
? existingBlock
|
|
48
|
+
: undefined;
|
|
49
|
+
const updatedAgentConfigs = mergeAgentConfigs(existingAgentConfigs, DEFAULT_DISABLED_AGENTS);
|
|
50
|
+
const hasChanges = Object.keys(DEFAULT_DISABLED_AGENTS).some((agentName) => !existingAgentConfigs?.[agentName]);
|
|
51
|
+
return {
|
|
52
|
+
nextBlock: updatedAgentConfigs,
|
|
53
|
+
changed: hasChanges,
|
|
54
|
+
};
|
|
55
|
+
},
|
|
56
|
+
});
|
|
91
57
|
}
|
package/dist/mcp-config.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
const GLOBAL_OPENCODE_DIR = join(homedir(), '.config', 'opencode');
|
|
5
|
-
const GLOBAL_OPENCODE_CONFIG_PATH = join(GLOBAL_OPENCODE_DIR, 'opencode.json');
|
|
1
|
+
import { isRecord, updateGlobalOpenCodeConfigBlock } from './opencode-global-config.js';
|
|
6
2
|
/**
|
|
7
3
|
* Default MCP server configuration to be added by the plugin
|
|
8
4
|
* These servers will be merged into the global OpenCode config.
|
|
@@ -54,50 +50,20 @@ export function mergeMCPServers(existing, defaults) {
|
|
|
54
50
|
* @param $ - Bun shell instance
|
|
55
51
|
*/
|
|
56
52
|
export async function updateMCPConfig($) {
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
};
|
|
74
|
-
// Ensure global config directory exists
|
|
75
|
-
try {
|
|
76
|
-
await $ `mkdir -p ${GLOBAL_OPENCODE_DIR}`.quiet();
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
// Directory might already exist, ignore
|
|
80
|
-
}
|
|
81
|
-
// Backup existing global config if it exists and has content
|
|
82
|
-
if (globalJson && Object.keys(globalJson).length > 0) {
|
|
83
|
-
try {
|
|
84
|
-
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
85
|
-
const backupPath = `${GLOBAL_OPENCODE_CONFIG_PATH}.backup.${timestamp}`;
|
|
86
|
-
const backupContent = JSON.stringify(globalJson, null, 2);
|
|
87
|
-
await $ `echo ${backupContent} > ${backupPath}`.quiet();
|
|
88
|
-
}
|
|
89
|
-
catch (error) {
|
|
90
|
-
// Backup failed, but continue anyway
|
|
91
|
-
console.warn('[feature-factory] Could not create backup:', error);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
// Write updated config to global opencode.json
|
|
95
|
-
const configContent = JSON.stringify(updatedGlobalConfig, null, 2);
|
|
96
|
-
try {
|
|
97
|
-
await $ `echo ${configContent} > ${GLOBAL_OPENCODE_CONFIG_PATH}`.quiet();
|
|
98
|
-
}
|
|
99
|
-
catch (error) {
|
|
100
|
-
// Silently fail - don't block if we can't write the config
|
|
101
|
-
console.warn('[feature-factory] Could not update MCP config:', error);
|
|
102
|
-
}
|
|
53
|
+
void $;
|
|
54
|
+
await updateGlobalOpenCodeConfigBlock({
|
|
55
|
+
blockName: 'mcp',
|
|
56
|
+
warningLabel: 'MCP config',
|
|
57
|
+
update: (existingBlock) => {
|
|
58
|
+
const existingMcpServers = isRecord(existingBlock)
|
|
59
|
+
? existingBlock
|
|
60
|
+
: undefined;
|
|
61
|
+
const updatedMcpServers = mergeMCPServers(existingMcpServers, DEFAULT_MCP_SERVERS);
|
|
62
|
+
const hasChanges = Object.keys(DEFAULT_MCP_SERVERS).some((serverName) => !existingMcpServers?.[serverName]);
|
|
63
|
+
return {
|
|
64
|
+
nextBlock: updatedMcpServers,
|
|
65
|
+
changed: hasChanges,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
});
|
|
103
69
|
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function isRecord(value: unknown): value is Record<string, unknown>;
|
|
2
|
+
export declare function updateGlobalOpenCodeConfigBlock<T>(options: {
|
|
3
|
+
blockName: string;
|
|
4
|
+
warningLabel: string;
|
|
5
|
+
update: (existingBlock: unknown) => {
|
|
6
|
+
nextBlock: T;
|
|
7
|
+
changed: boolean;
|
|
8
|
+
};
|
|
9
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
2
|
+
import { homedir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
const GLOBAL_OPENCODE_DIR = join(homedir(), '.config', 'opencode');
|
|
5
|
+
const GLOBAL_OPENCODE_CONFIG_PATH = join(GLOBAL_OPENCODE_DIR, 'opencode.json');
|
|
6
|
+
export function isRecord(value) {
|
|
7
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
8
|
+
}
|
|
9
|
+
async function loadGlobalOpenCodeConfig() {
|
|
10
|
+
try {
|
|
11
|
+
const configContent = await readFile(GLOBAL_OPENCODE_CONFIG_PATH, 'utf8');
|
|
12
|
+
const parsed = JSON.parse(configContent);
|
|
13
|
+
if (!isRecord(parsed)) {
|
|
14
|
+
return {
|
|
15
|
+
config: {},
|
|
16
|
+
hasExistingFile: true,
|
|
17
|
+
parseError: true,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
return {
|
|
21
|
+
config: parsed,
|
|
22
|
+
hasExistingFile: true,
|
|
23
|
+
parseError: false,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
catch (error) {
|
|
27
|
+
if (typeof error === 'object' &&
|
|
28
|
+
error !== null &&
|
|
29
|
+
'code' in error &&
|
|
30
|
+
error.code === 'ENOENT') {
|
|
31
|
+
return {
|
|
32
|
+
config: {},
|
|
33
|
+
hasExistingFile: false,
|
|
34
|
+
parseError: false,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
if (error instanceof SyntaxError) {
|
|
38
|
+
return {
|
|
39
|
+
config: {},
|
|
40
|
+
hasExistingFile: true,
|
|
41
|
+
parseError: true,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
throw error;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
export async function updateGlobalOpenCodeConfigBlock(options) {
|
|
48
|
+
const { blockName, warningLabel, update } = options;
|
|
49
|
+
const loaded = await loadGlobalOpenCodeConfig();
|
|
50
|
+
if (loaded.parseError) {
|
|
51
|
+
console.warn(`[feature-factory] Could not update ${warningLabel}: existing opencode.json is not valid JSON`);
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
const { nextBlock, changed } = update(loaded.config[blockName]);
|
|
55
|
+
if (!changed) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
const updatedConfig = {
|
|
59
|
+
...loaded.config,
|
|
60
|
+
[blockName]: nextBlock,
|
|
61
|
+
};
|
|
62
|
+
await mkdir(GLOBAL_OPENCODE_DIR, { recursive: true });
|
|
63
|
+
if (loaded.hasExistingFile && Object.keys(loaded.config).length > 0) {
|
|
64
|
+
try {
|
|
65
|
+
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
66
|
+
const backupPath = `${GLOBAL_OPENCODE_CONFIG_PATH}.backup.${timestamp}`;
|
|
67
|
+
await writeFile(backupPath, JSON.stringify(loaded.config, null, 2));
|
|
68
|
+
}
|
|
69
|
+
catch (error) {
|
|
70
|
+
console.warn('[feature-factory] Could not create backup:', error);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
try {
|
|
74
|
+
await writeFile(GLOBAL_OPENCODE_CONFIG_PATH, JSON.stringify(updatedConfig, null, 2));
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.warn(`[feature-factory] Could not update ${warningLabel}:`, error);
|
|
78
|
+
}
|
|
79
|
+
}
|
package/dist/plugin-config.js
CHANGED
|
@@ -1,8 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { homedir } from 'node:os';
|
|
3
|
-
import { join } from 'node:path';
|
|
4
|
-
const GLOBAL_OPENCODE_DIR = join(homedir(), '.config', 'opencode');
|
|
5
|
-
const GLOBAL_OPENCODE_CONFIG_PATH = join(GLOBAL_OPENCODE_DIR, 'opencode.json');
|
|
1
|
+
import { updateGlobalOpenCodeConfigBlock } from './opencode-global-config.js';
|
|
6
2
|
/**
|
|
7
3
|
* Default plugins that should be present in the global OpenCode config.
|
|
8
4
|
* These will be merged into the existing plugin array without removing
|
|
@@ -88,53 +84,20 @@ export function mergePlugins(existing, defaults) {
|
|
|
88
84
|
* @param $ - Bun shell instance
|
|
89
85
|
*/
|
|
90
86
|
export async function updatePluginConfig($) {
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
const updatedGlobalConfig = {
|
|
108
|
-
...(globalJson ?? {}),
|
|
109
|
-
plugin: updatedPlugins,
|
|
110
|
-
};
|
|
111
|
-
// Ensure global config directory exists
|
|
112
|
-
try {
|
|
113
|
-
await $ `mkdir -p ${GLOBAL_OPENCODE_DIR}`.quiet();
|
|
114
|
-
}
|
|
115
|
-
catch {
|
|
116
|
-
// Directory might already exist, ignore
|
|
117
|
-
}
|
|
118
|
-
// Backup existing global config if it exists and has content
|
|
119
|
-
if (globalJson && Object.keys(globalJson).length > 0) {
|
|
120
|
-
try {
|
|
121
|
-
const timestamp = new Date().toISOString().split('T')[0].replace(/-/g, '');
|
|
122
|
-
const backupPath = `${GLOBAL_OPENCODE_CONFIG_PATH}.backup.${timestamp}`;
|
|
123
|
-
const backupContent = JSON.stringify(globalJson, null, 2);
|
|
124
|
-
await $ `echo ${backupContent} > ${backupPath}`.quiet();
|
|
125
|
-
}
|
|
126
|
-
catch (error) {
|
|
127
|
-
// Backup failed, but continue anyway
|
|
128
|
-
console.warn('[feature-factory] Could not create backup:', error);
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
// Write updated config to global opencode.json
|
|
132
|
-
const configContent = JSON.stringify(updatedGlobalConfig, null, 2);
|
|
133
|
-
try {
|
|
134
|
-
await $ `echo ${configContent} > ${GLOBAL_OPENCODE_CONFIG_PATH}`.quiet();
|
|
135
|
-
}
|
|
136
|
-
catch (error) {
|
|
137
|
-
// Silently fail - don't block if we can't write the config
|
|
138
|
-
console.warn('[feature-factory] Could not update plugin config:', error);
|
|
139
|
-
}
|
|
87
|
+
void $;
|
|
88
|
+
await updateGlobalOpenCodeConfigBlock({
|
|
89
|
+
blockName: 'plugin',
|
|
90
|
+
warningLabel: 'plugin config',
|
|
91
|
+
update: (existingBlock) => {
|
|
92
|
+
const existingPlugins = Array.isArray(existingBlock)
|
|
93
|
+
? existingBlock.filter((entry) => typeof entry === 'string')
|
|
94
|
+
: undefined;
|
|
95
|
+
const existingArray = existingPlugins ?? [];
|
|
96
|
+
const hasChanges = DEFAULT_PLUGINS.some((plugin) => !hasPlugin(existingArray, plugin));
|
|
97
|
+
return {
|
|
98
|
+
nextBlock: mergePlugins(existingPlugins, DEFAULT_PLUGINS),
|
|
99
|
+
changed: hasChanges,
|
|
100
|
+
};
|
|
101
|
+
},
|
|
102
|
+
});
|
|
140
103
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"$schema": "https://json.schemastore.org/package.json",
|
|
3
3
|
"name": "@syntesseraai/opencode-feature-factory",
|
|
4
|
-
"version": "0.6.
|
|
4
|
+
"version": "0.6.8",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "OpenCode plugin for Feature Factory agents - provides sub-agents and skills for validation, review, security, and architecture assessment",
|
|
7
7
|
"license": "MIT",
|