@sylphx/flow 1.2.0 → 1.3.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/CHANGELOG.md +20 -0
- package/package.json +1 -1
- package/src/commands/flow-command.ts +28 -0
- package/src/utils/sync-utils.ts +106 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
1
1
|
# @sylphx/flow
|
|
2
2
|
|
|
3
|
+
## 1.3.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- Enhanced --sync with MCP registry checking:
|
|
8
|
+
- Detect servers not in Flow registry (removed or custom)
|
|
9
|
+
- Interactive selection for removal
|
|
10
|
+
- Clean removal from .mcp.json
|
|
11
|
+
- Flow: sync templates → check MCP → remove selected
|
|
12
|
+
|
|
13
|
+
## 1.2.1
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- Apply MEP principles to workspace documentation rule:
|
|
18
|
+
- Condensed from verbose instructions to condition→action format
|
|
19
|
+
- Removed step-by-step teaching and command examples
|
|
20
|
+
- Embedded verification in directives
|
|
21
|
+
- 31% reduction while maintaining clarity
|
|
22
|
+
|
|
3
23
|
## 1.2.0
|
|
4
24
|
|
|
5
25
|
### Minor Changes
|
package/package.json
CHANGED
|
@@ -405,6 +405,20 @@ async function executeSetupPhase(prompt: string | undefined, options: FlowOption
|
|
|
405
405
|
|
|
406
406
|
const deletedCount = await executeSyncDelete(manifest);
|
|
407
407
|
console.log(chalk.green(`\n✓ Deleted ${deletedCount} files\n`));
|
|
408
|
+
|
|
409
|
+
// Check MCP servers
|
|
410
|
+
const { checkMCPServers, showNonRegistryServers, selectServersToRemove, removeMCPServers } = await import('../utils/sync-utils.js');
|
|
411
|
+
const nonRegistryServers = await checkMCPServers(process.cwd());
|
|
412
|
+
|
|
413
|
+
if (nonRegistryServers.length > 0) {
|
|
414
|
+
showNonRegistryServers(nonRegistryServers);
|
|
415
|
+
const serversToRemove = await selectServersToRemove(nonRegistryServers);
|
|
416
|
+
|
|
417
|
+
if (serversToRemove.length > 0) {
|
|
418
|
+
const removedCount = await removeMCPServers(process.cwd(), serversToRemove);
|
|
419
|
+
console.log(chalk.green(`\n✓ Removed ${removedCount} MCP server(s)\n`));
|
|
420
|
+
}
|
|
421
|
+
}
|
|
408
422
|
} else if (!options.sync) {
|
|
409
423
|
const targetId = await selectAndValidateTarget(initOptions);
|
|
410
424
|
selectedTarget = targetId;
|
|
@@ -720,6 +734,20 @@ async function executeFlowOnce(prompt: string | undefined, options: FlowOptions)
|
|
|
720
734
|
|
|
721
735
|
const deletedCount = await executeSyncDelete(manifest);
|
|
722
736
|
console.log(chalk.green(`\n✓ Deleted ${deletedCount} files\n`));
|
|
737
|
+
|
|
738
|
+
// Check MCP servers
|
|
739
|
+
const { checkMCPServers, showNonRegistryServers, selectServersToRemove, removeMCPServers } = await import('../utils/sync-utils.js');
|
|
740
|
+
const nonRegistryServers = await checkMCPServers(process.cwd());
|
|
741
|
+
|
|
742
|
+
if (nonRegistryServers.length > 0) {
|
|
743
|
+
showNonRegistryServers(nonRegistryServers);
|
|
744
|
+
const serversToRemove = await selectServersToRemove(nonRegistryServers);
|
|
745
|
+
|
|
746
|
+
if (serversToRemove.length > 0) {
|
|
747
|
+
const removedCount = await removeMCPServers(process.cwd(), serversToRemove);
|
|
748
|
+
console.log(chalk.green(`\n✓ Removed ${removedCount} MCP server(s)\n`));
|
|
749
|
+
}
|
|
750
|
+
}
|
|
723
751
|
} else {
|
|
724
752
|
// Select and validate target (will use existing in repair mode, or prompt if needed)
|
|
725
753
|
const targetId = await selectAndValidateTarget(initOptions);
|
package/src/utils/sync-utils.ts
CHANGED
|
@@ -2,6 +2,7 @@ import fs from 'node:fs';
|
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import chalk from 'chalk';
|
|
4
4
|
import type { Target } from '../types.js';
|
|
5
|
+
import { MCP_SERVER_REGISTRY } from '../config/servers.js';
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Files to delete during sync for each target
|
|
@@ -157,3 +158,108 @@ export async function confirmSync(): Promise<boolean> {
|
|
|
157
158
|
]);
|
|
158
159
|
return confirm;
|
|
159
160
|
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Check MCP servers - find servers not in Flow registry
|
|
164
|
+
*/
|
|
165
|
+
export async function checkMCPServers(cwd: string): Promise<string[]> {
|
|
166
|
+
const mcpPath = path.join(cwd, '.mcp.json');
|
|
167
|
+
|
|
168
|
+
if (!fs.existsSync(mcpPath)) {
|
|
169
|
+
return [];
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
try {
|
|
173
|
+
const content = await fs.promises.readFile(mcpPath, 'utf-8');
|
|
174
|
+
const mcpConfig = JSON.parse(content);
|
|
175
|
+
|
|
176
|
+
if (!mcpConfig.mcpServers) {
|
|
177
|
+
return [];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const installedServers = Object.keys(mcpConfig.mcpServers);
|
|
181
|
+
const registryServers = Object.keys(MCP_SERVER_REGISTRY);
|
|
182
|
+
|
|
183
|
+
// Find servers not in registry
|
|
184
|
+
return installedServers.filter(id => !registryServers.includes(id));
|
|
185
|
+
} catch (error) {
|
|
186
|
+
console.warn(chalk.yellow('⚠ Failed to read .mcp.json'));
|
|
187
|
+
return [];
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Show non-registry servers
|
|
193
|
+
*/
|
|
194
|
+
export function showNonRegistryServers(servers: string[]): void {
|
|
195
|
+
if (servers.length === 0) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
console.log(chalk.cyan.bold('\n📋 MCP Registry Check\n'));
|
|
200
|
+
console.log(chalk.yellow('⚠️ 以下 MCP servers 唔係 Flow registry 入面:\n'));
|
|
201
|
+
|
|
202
|
+
servers.forEach(server => {
|
|
203
|
+
console.log(chalk.dim(` - ${server}`));
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
console.log(chalk.dim('\n可能原因:'));
|
|
207
|
+
console.log(chalk.dim(' 1. Flow registry 已移除'));
|
|
208
|
+
console.log(chalk.dim(' 2. 你自己手動安裝\n'));
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Select servers to remove
|
|
213
|
+
*/
|
|
214
|
+
export async function selectServersToRemove(servers: string[]): Promise<string[]> {
|
|
215
|
+
const { default: inquirer } = await import('inquirer');
|
|
216
|
+
const { selected } = await inquirer.prompt([
|
|
217
|
+
{
|
|
218
|
+
type: 'checkbox',
|
|
219
|
+
name: 'selected',
|
|
220
|
+
message: '選擇要刪除既 servers:',
|
|
221
|
+
choices: servers.map(s => ({ name: s, value: s })),
|
|
222
|
+
},
|
|
223
|
+
]);
|
|
224
|
+
return selected;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Remove MCP servers from .mcp.json
|
|
229
|
+
*/
|
|
230
|
+
export async function removeMCPServers(cwd: string, serversToRemove: string[]): Promise<number> {
|
|
231
|
+
if (serversToRemove.length === 0) {
|
|
232
|
+
return 0;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
const mcpPath = path.join(cwd, '.mcp.json');
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const content = await fs.promises.readFile(mcpPath, 'utf-8');
|
|
239
|
+
const mcpConfig = JSON.parse(content);
|
|
240
|
+
|
|
241
|
+
if (!mcpConfig.mcpServers) {
|
|
242
|
+
return 0;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
let removedCount = 0;
|
|
246
|
+
for (const server of serversToRemove) {
|
|
247
|
+
if (mcpConfig.mcpServers[server]) {
|
|
248
|
+
delete mcpConfig.mcpServers[server];
|
|
249
|
+
removedCount++;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
// Write back
|
|
254
|
+
await fs.promises.writeFile(
|
|
255
|
+
mcpPath,
|
|
256
|
+
JSON.stringify(mcpConfig, null, 2) + '\n',
|
|
257
|
+
'utf-8'
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
return removedCount;
|
|
261
|
+
} catch (error) {
|
|
262
|
+
console.warn(chalk.yellow('⚠ Failed to update .mcp.json'));
|
|
263
|
+
return 0;
|
|
264
|
+
}
|
|
265
|
+
}
|