@sylphx/flow 2.1.3 → 2.1.5
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 +28 -0
- package/README.md +44 -0
- package/package.json +79 -73
- package/src/commands/flow/execute-v2.ts +37 -29
- package/src/commands/flow/prompt.ts +5 -3
- package/src/commands/flow/types.ts +0 -2
- package/src/commands/flow-command.ts +20 -13
- package/src/commands/hook-command.ts +1 -3
- package/src/commands/settings/checkbox-config.ts +128 -0
- package/src/commands/settings/index.ts +6 -0
- package/src/commands/settings-command.ts +84 -156
- package/src/config/ai-config.ts +60 -41
- package/src/core/agent-loader.ts +11 -6
- package/src/core/attach/file-attacher.ts +172 -0
- package/src/core/attach/index.ts +5 -0
- package/src/core/attach-manager.ts +117 -171
- package/src/core/backup-manager.ts +35 -29
- package/src/core/cleanup-handler.ts +11 -8
- package/src/core/error-handling.ts +23 -30
- package/src/core/flow-executor.ts +58 -76
- package/src/core/formatting/bytes.ts +2 -4
- package/src/core/functional/async.ts +5 -4
- package/src/core/functional/error-handler.ts +2 -2
- package/src/core/git-stash-manager.ts +21 -10
- package/src/core/installers/file-installer.ts +0 -1
- package/src/core/installers/mcp-installer.ts +0 -1
- package/src/core/project-manager.ts +24 -18
- package/src/core/secrets-manager.ts +54 -73
- package/src/core/session-manager.ts +20 -22
- package/src/core/state-detector.ts +139 -80
- package/src/core/template-loader.ts +13 -31
- package/src/core/upgrade-manager.ts +122 -69
- package/src/index.ts +8 -5
- package/src/services/auto-upgrade.ts +1 -1
- package/src/services/config-service.ts +41 -29
- package/src/services/global-config.ts +3 -3
- package/src/services/target-installer.ts +11 -26
- package/src/targets/claude-code.ts +35 -81
- package/src/targets/opencode.ts +28 -68
- package/src/targets/shared/index.ts +7 -0
- package/src/targets/shared/mcp-transforms.ts +132 -0
- package/src/targets/shared/target-operations.ts +135 -0
- package/src/types/cli.types.ts +2 -2
- package/src/types/provider.types.ts +1 -7
- package/src/types/session.types.ts +11 -11
- package/src/types/target.types.ts +3 -1
- package/src/types/todo.types.ts +1 -1
- package/src/types.ts +1 -1
- package/src/utils/__tests__/package-manager-detector.test.ts +6 -6
- package/src/utils/agent-enhancer.ts +4 -4
- package/src/utils/config/paths.ts +3 -1
- package/src/utils/config/target-utils.ts +2 -2
- package/src/utils/display/banner.ts +2 -2
- package/src/utils/display/notifications.ts +58 -45
- package/src/utils/display/status.ts +29 -12
- package/src/utils/files/file-operations.ts +1 -1
- package/src/utils/files/sync-utils.ts +38 -41
- package/src/utils/index.ts +19 -27
- package/src/utils/package-manager-detector.ts +15 -5
- package/src/utils/security/security.ts +8 -4
- package/src/utils/target-selection.ts +6 -8
- package/src/utils/version.ts +4 -2
- package/src/commands/flow-orchestrator.ts +0 -328
- package/src/commands/init-command.ts +0 -92
- package/src/commands/init-core.ts +0 -331
- package/src/core/agent-manager.ts +0 -174
- package/src/core/loop-controller.ts +0 -200
- package/src/core/rule-loader.ts +0 -147
- package/src/core/rule-manager.ts +0 -240
- package/src/services/claude-config-service.ts +0 -252
- package/src/services/first-run-setup.ts +0 -220
- package/src/services/smart-config-service.ts +0 -269
- package/src/types/api.types.ts +0 -9
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { fileURLToPath } from 'node:url';
|
|
4
|
-
import { projectSettings } from '../utils/config/settings.js';
|
|
5
|
-
import { targetManager } from './target-manager.js';
|
|
6
4
|
import { ConfigService } from '../services/config-service.js';
|
|
5
|
+
import type { Target } from '../types/target.types.js';
|
|
6
|
+
import { targetManager } from './target-manager.js';
|
|
7
7
|
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = path.dirname(__filename);
|
|
@@ -28,7 +28,13 @@ export interface ProjectState {
|
|
|
28
28
|
lastUpdated: Date | null;
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
-
export type RecommendedAction =
|
|
31
|
+
export type RecommendedAction =
|
|
32
|
+
| 'FULL_INIT'
|
|
33
|
+
| 'RUN_ONLY'
|
|
34
|
+
| 'REPAIR'
|
|
35
|
+
| 'UPGRADE'
|
|
36
|
+
| 'UPGRADE_TARGET'
|
|
37
|
+
| 'CLEAN_INIT';
|
|
32
38
|
|
|
33
39
|
export class StateDetector {
|
|
34
40
|
private projectPath: string;
|
|
@@ -37,6 +43,17 @@ export class StateDetector {
|
|
|
37
43
|
this.projectPath = projectPath;
|
|
38
44
|
}
|
|
39
45
|
|
|
46
|
+
/**
|
|
47
|
+
* Resolve target from ID string to Target object
|
|
48
|
+
*/
|
|
49
|
+
private resolveTarget(targetId: string): Target | null {
|
|
50
|
+
const targetOption = targetManager.getTarget(targetId);
|
|
51
|
+
if (targetOption._tag === 'None') {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
return targetOption.value;
|
|
55
|
+
}
|
|
56
|
+
|
|
40
57
|
async detect(): Promise<ProjectState> {
|
|
41
58
|
const state: ProjectState = {
|
|
42
59
|
initialized: false,
|
|
@@ -77,37 +94,16 @@ export class StateDetector {
|
|
|
77
94
|
state.outdated = this.isVersionOutdated(state.version, state.latestVersion);
|
|
78
95
|
}
|
|
79
96
|
|
|
80
|
-
//
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
await this.
|
|
86
|
-
// OpenCode doesn't have separate hooks directory (hooks config in opencode.jsonc)
|
|
87
|
-
state.components.hooks.installed = false;
|
|
88
|
-
// OpenCode appends output styles to AGENTS.md
|
|
89
|
-
state.components.outputStyles.installed = await this.checkOutputStylesInAGENTS();
|
|
90
|
-
await this.checkComponent('slashCommands', '.opencode/command', '*.md', state);
|
|
91
|
-
} else {
|
|
92
|
-
// Claude Code (default)
|
|
93
|
-
await this.checkComponent('agents', '.claude/agents', '*.md', state);
|
|
94
|
-
|
|
95
|
-
// Claude Code includes rules and output styles in agent files
|
|
96
|
-
// So we mark them as installed if agents are installed
|
|
97
|
-
state.components.rules.installed = state.components.agents.installed;
|
|
98
|
-
state.components.rules.count = state.components.agents.count;
|
|
99
|
-
|
|
100
|
-
state.components.outputStyles.installed = state.components.agents.installed;
|
|
101
|
-
|
|
102
|
-
// Check hooks (optional for Claude Code)
|
|
103
|
-
await this.checkComponent('hooks', '.claude/hooks', '*.js', state);
|
|
104
|
-
|
|
105
|
-
// Check slash commands
|
|
106
|
-
await this.checkComponent('slashCommands', '.claude/commands', '*.md', state);
|
|
97
|
+
// Resolve target to get config
|
|
98
|
+
const target = state.target ? this.resolveTarget(state.target) : null;
|
|
99
|
+
|
|
100
|
+
// Check components based on target config
|
|
101
|
+
if (target) {
|
|
102
|
+
await this.checkComponentsForTarget(target, state);
|
|
107
103
|
}
|
|
108
104
|
|
|
109
105
|
// Check MCP
|
|
110
|
-
const mcpConfig = await this.checkMCPConfig(
|
|
106
|
+
const mcpConfig = await this.checkMCPConfig(target);
|
|
111
107
|
state.components.mcp.installed = mcpConfig.exists;
|
|
112
108
|
state.components.mcp.serverCount = mcpConfig.serverCount;
|
|
113
109
|
state.components.mcp.version = mcpConfig.version;
|
|
@@ -121,14 +117,45 @@ export class StateDetector {
|
|
|
121
117
|
|
|
122
118
|
// Check corruption
|
|
123
119
|
state.corrupted = await this.checkCorruption(state);
|
|
124
|
-
|
|
125
|
-
} catch (error) {
|
|
120
|
+
} catch (_error) {
|
|
126
121
|
state.corrupted = true;
|
|
127
122
|
}
|
|
128
123
|
|
|
129
124
|
return state;
|
|
130
125
|
}
|
|
131
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Check components based on target configuration
|
|
129
|
+
*/
|
|
130
|
+
private async checkComponentsForTarget(target: Target, state: ProjectState): Promise<void> {
|
|
131
|
+
// Check agents using target's agentDir
|
|
132
|
+
await this.checkComponent('agents', target.config.agentDir, '*.md', state);
|
|
133
|
+
|
|
134
|
+
// Check rules based on target config
|
|
135
|
+
if (target.config.rulesFile) {
|
|
136
|
+
// Target has separate rules file (e.g., OpenCode's AGENTS.md)
|
|
137
|
+
await this.checkFileComponent('rules', target.config.rulesFile, state);
|
|
138
|
+
// Check output styles in rules file
|
|
139
|
+
state.components.outputStyles.installed = await this.checkOutputStylesInFile(
|
|
140
|
+
target.config.rulesFile
|
|
141
|
+
);
|
|
142
|
+
} else {
|
|
143
|
+
// Rules are included in agent files (e.g., Claude Code)
|
|
144
|
+
state.components.rules.installed = state.components.agents.installed;
|
|
145
|
+
state.components.rules.count = state.components.agents.count;
|
|
146
|
+
state.components.outputStyles.installed = state.components.agents.installed;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Check hooks - look for hooks directory in configDir
|
|
150
|
+
const hooksDir = path.join(target.config.configDir, 'hooks');
|
|
151
|
+
await this.checkComponent('hooks', hooksDir, '*.js', state);
|
|
152
|
+
|
|
153
|
+
// Check slash commands using target's slashCommandsDir
|
|
154
|
+
if (target.config.slashCommandsDir) {
|
|
155
|
+
await this.checkComponent('slashCommands', target.config.slashCommandsDir, '*.md', state);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
132
159
|
recommendAction(state: ProjectState): RecommendedAction {
|
|
133
160
|
if (!state.initialized) {
|
|
134
161
|
return 'FULL_INIT';
|
|
@@ -142,8 +169,11 @@ export class StateDetector {
|
|
|
142
169
|
return 'UPGRADE';
|
|
143
170
|
}
|
|
144
171
|
|
|
145
|
-
if (
|
|
146
|
-
|
|
172
|
+
if (
|
|
173
|
+
state.targetVersion &&
|
|
174
|
+
state.targetLatestVersion &&
|
|
175
|
+
this.isVersionOutdated(state.targetVersion, state.targetLatestVersion)
|
|
176
|
+
) {
|
|
147
177
|
return 'UPGRADE_TARGET';
|
|
148
178
|
}
|
|
149
179
|
|
|
@@ -170,8 +200,11 @@ export class StateDetector {
|
|
|
170
200
|
explanations.push('Run `bun dev:flow upgrade` to upgrade');
|
|
171
201
|
}
|
|
172
202
|
|
|
173
|
-
if (
|
|
174
|
-
|
|
203
|
+
if (
|
|
204
|
+
state.targetVersion &&
|
|
205
|
+
state.targetLatestVersion &&
|
|
206
|
+
this.isVersionOutdated(state.targetVersion, state.targetLatestVersion)
|
|
207
|
+
) {
|
|
175
208
|
explanations.push(`${state.target} update available`);
|
|
176
209
|
explanations.push(`Run \`bun dev:flow upgrade-target\` to upgrade`);
|
|
177
210
|
}
|
|
@@ -210,7 +243,10 @@ export class StateDetector {
|
|
|
210
243
|
): Promise<void> {
|
|
211
244
|
try {
|
|
212
245
|
const fullPath = path.join(this.projectPath, componentPath);
|
|
213
|
-
const exists = await fs
|
|
246
|
+
const exists = await fs
|
|
247
|
+
.access(fullPath)
|
|
248
|
+
.then(() => true)
|
|
249
|
+
.catch(() => false);
|
|
214
250
|
|
|
215
251
|
if (!exists) {
|
|
216
252
|
state.components[componentName].installed = false;
|
|
@@ -219,19 +255,30 @@ export class StateDetector {
|
|
|
219
255
|
|
|
220
256
|
// 计算文件数量
|
|
221
257
|
const files = await fs.readdir(fullPath).catch(() => []);
|
|
222
|
-
const count =
|
|
223
|
-
|
|
258
|
+
const count =
|
|
259
|
+
pattern === '*.js'
|
|
260
|
+
? files.filter((f) => f.endsWith('.js')).length
|
|
261
|
+
: pattern === '*.md'
|
|
262
|
+
? files.filter((f) => f.endsWith('.md')).length
|
|
263
|
+
: files.length;
|
|
224
264
|
|
|
225
265
|
// Component is only installed if it has files
|
|
226
266
|
state.components[componentName].installed = count > 0;
|
|
227
267
|
|
|
228
|
-
if (
|
|
268
|
+
if (
|
|
269
|
+
componentName === 'agents' ||
|
|
270
|
+
componentName === 'slashCommands' ||
|
|
271
|
+
componentName === 'rules'
|
|
272
|
+
) {
|
|
229
273
|
state.components[componentName].count = count;
|
|
230
274
|
}
|
|
231
275
|
|
|
232
276
|
// 这里可以读取版本信息(如果保存了的话)
|
|
233
277
|
const versionPath = path.join(fullPath, '.version');
|
|
234
|
-
const versionExists = await fs
|
|
278
|
+
const versionExists = await fs
|
|
279
|
+
.access(versionPath)
|
|
280
|
+
.then(() => true)
|
|
281
|
+
.catch(() => false);
|
|
235
282
|
if (versionExists) {
|
|
236
283
|
state.components[componentName].version = await fs.readFile(versionPath, 'utf-8');
|
|
237
284
|
}
|
|
@@ -247,7 +294,10 @@ export class StateDetector {
|
|
|
247
294
|
): Promise<void> {
|
|
248
295
|
try {
|
|
249
296
|
const fullPath = path.join(this.projectPath, filePath);
|
|
250
|
-
const exists = await fs
|
|
297
|
+
const exists = await fs
|
|
298
|
+
.access(fullPath)
|
|
299
|
+
.then(() => true)
|
|
300
|
+
.catch(() => false);
|
|
251
301
|
|
|
252
302
|
state.components[componentName].installed = exists;
|
|
253
303
|
|
|
@@ -260,53 +310,53 @@ export class StateDetector {
|
|
|
260
310
|
}
|
|
261
311
|
}
|
|
262
312
|
|
|
263
|
-
private async
|
|
313
|
+
private async checkOutputStylesInFile(filePath: string): Promise<boolean> {
|
|
264
314
|
try {
|
|
265
|
-
const
|
|
266
|
-
const exists = await fs
|
|
315
|
+
const fullPath = path.join(this.projectPath, filePath);
|
|
316
|
+
const exists = await fs
|
|
317
|
+
.access(fullPath)
|
|
318
|
+
.then(() => true)
|
|
319
|
+
.catch(() => false);
|
|
267
320
|
|
|
268
321
|
if (!exists) {
|
|
269
322
|
return false;
|
|
270
323
|
}
|
|
271
324
|
|
|
272
|
-
// Check if
|
|
273
|
-
const content = await fs.readFile(
|
|
325
|
+
// Check if file contains output styles section
|
|
326
|
+
const content = await fs.readFile(fullPath, 'utf-8');
|
|
274
327
|
return content.includes('# Output Styles');
|
|
275
328
|
} catch {
|
|
276
329
|
return false;
|
|
277
330
|
}
|
|
278
331
|
}
|
|
279
332
|
|
|
280
|
-
private async checkMCPConfig(
|
|
333
|
+
private async checkMCPConfig(
|
|
334
|
+
target?: Target | null
|
|
335
|
+
): Promise<{ exists: boolean; serverCount: number; version: string | null }> {
|
|
281
336
|
try {
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
if (target === 'opencode') {
|
|
286
|
-
// OpenCode uses opencode.jsonc with mcp key
|
|
287
|
-
mcpPath = path.join(this.projectPath, 'opencode.jsonc');
|
|
288
|
-
serversKey = 'mcp';
|
|
289
|
-
} else {
|
|
290
|
-
// Claude Code uses .mcp.json with mcpServers key
|
|
291
|
-
mcpPath = path.join(this.projectPath, '.mcp.json');
|
|
292
|
-
serversKey = 'mcpServers';
|
|
337
|
+
if (!target) {
|
|
338
|
+
return { exists: false, serverCount: 0, version: null };
|
|
293
339
|
}
|
|
294
340
|
|
|
295
|
-
|
|
341
|
+
// Use target config for MCP file path and servers key
|
|
342
|
+
const mcpPath = path.join(this.projectPath, target.config.configFile);
|
|
343
|
+
const serversKey = target.config.mcpConfigPath;
|
|
344
|
+
|
|
345
|
+
const exists = await fs
|
|
346
|
+
.access(mcpPath)
|
|
347
|
+
.then(() => true)
|
|
348
|
+
.catch(() => false);
|
|
296
349
|
|
|
297
350
|
if (!exists) {
|
|
298
351
|
return { exists: false, serverCount: 0, version: null };
|
|
299
352
|
}
|
|
300
353
|
|
|
301
|
-
// Use
|
|
354
|
+
// Use target's readConfig method for proper parsing (handles JSONC, etc.)
|
|
302
355
|
let content: any;
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
content = await fileUtils.readConfig(opencodeTarget.config, this.projectPath);
|
|
308
|
-
} else {
|
|
309
|
-
// Claude Code uses plain JSON
|
|
356
|
+
try {
|
|
357
|
+
content = await target.readConfig(this.projectPath);
|
|
358
|
+
} catch {
|
|
359
|
+
// Fallback to plain JSON parsing
|
|
310
360
|
content = JSON.parse(await fs.readFile(mcpPath, 'utf-8'));
|
|
311
361
|
}
|
|
312
362
|
|
|
@@ -322,19 +372,24 @@ export class StateDetector {
|
|
|
322
372
|
}
|
|
323
373
|
}
|
|
324
374
|
|
|
325
|
-
private async checkTargetVersion(
|
|
375
|
+
private async checkTargetVersion(
|
|
376
|
+
targetId: string
|
|
377
|
+
): Promise<{ version: string | null; latestVersion: string | null }> {
|
|
326
378
|
try {
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
379
|
+
const target = this.resolveTarget(targetId);
|
|
380
|
+
if (!target) {
|
|
381
|
+
return { version: null, latestVersion: null };
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
// Check if target has executeCommand (CLI-based target)
|
|
385
|
+
// Only CLI targets like claude-code have version checking capability
|
|
386
|
+
if (target.executeCommand && target.id === 'claude-code') {
|
|
331
387
|
const { exec } = await import('node:child_process');
|
|
332
388
|
const { promisify } = await import('node:util');
|
|
333
389
|
const execAsync = promisify(exec);
|
|
334
390
|
|
|
335
391
|
try {
|
|
336
392
|
const { stdout } = await execAsync('claude --version');
|
|
337
|
-
// 解析版本号
|
|
338
393
|
const match = stdout.match(/v?(\d+\.\d+\.\d+)/);
|
|
339
394
|
return {
|
|
340
395
|
version: match ? match[1] : null,
|
|
@@ -366,14 +421,18 @@ export class StateDetector {
|
|
|
366
421
|
}
|
|
367
422
|
|
|
368
423
|
private async checkCorruption(state: ProjectState): Promise<boolean> {
|
|
369
|
-
//
|
|
424
|
+
// Check for contradictory states
|
|
370
425
|
if (state.initialized && !state.target) {
|
|
371
|
-
return true; //
|
|
426
|
+
return true; // Initialized but no target
|
|
372
427
|
}
|
|
373
428
|
|
|
374
|
-
//
|
|
375
|
-
if (state.initialized && state.target
|
|
376
|
-
|
|
429
|
+
// Check required components based on target
|
|
430
|
+
if (state.initialized && state.target) {
|
|
431
|
+
const target = this.resolveTarget(state.target);
|
|
432
|
+
// CLI-based targets (category: 'cli') require agents to be installed
|
|
433
|
+
if (target && target.category === 'cli' && !state.components.agents.installed) {
|
|
434
|
+
return true; // CLI target initialized but no agents
|
|
435
|
+
}
|
|
377
436
|
}
|
|
378
437
|
|
|
379
438
|
return false;
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Template Loader
|
|
3
3
|
* Loads Flow templates from assets directory
|
|
4
|
-
* Supports
|
|
4
|
+
* Supports any target with consistent template structure
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
+
import { existsSync } from 'node:fs';
|
|
7
8
|
import fs from 'node:fs/promises';
|
|
8
9
|
import path from 'node:path';
|
|
9
|
-
import { existsSync } from 'node:fs';
|
|
10
10
|
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import type { Target } from '../types/target.types.js';
|
|
11
12
|
import type { FlowTemplates } from './attach-manager.js';
|
|
12
13
|
|
|
13
14
|
export class TemplateLoader {
|
|
@@ -24,7 +25,7 @@ export class TemplateLoader {
|
|
|
24
25
|
* Load all templates for target
|
|
25
26
|
* Uses flat assets directory structure (no target-specific subdirectories)
|
|
26
27
|
*/
|
|
27
|
-
async loadTemplates(
|
|
28
|
+
async loadTemplates(_target: Target | string): Promise<FlowTemplates> {
|
|
28
29
|
const templates: FlowTemplates = {
|
|
29
30
|
agents: [],
|
|
30
31
|
commands: [],
|
|
@@ -77,14 +78,14 @@ export class TemplateLoader {
|
|
|
77
78
|
/**
|
|
78
79
|
* Load agents from directory
|
|
79
80
|
*/
|
|
80
|
-
private async loadAgents(
|
|
81
|
-
agentsDir: string
|
|
82
|
-
): Promise<Array<{ name: string; content: string }>> {
|
|
81
|
+
private async loadAgents(agentsDir: string): Promise<Array<{ name: string; content: string }>> {
|
|
83
82
|
const agents = [];
|
|
84
83
|
const files = await fs.readdir(agentsDir);
|
|
85
84
|
|
|
86
85
|
for (const file of files) {
|
|
87
|
-
if (!file.endsWith('.md'))
|
|
86
|
+
if (!file.endsWith('.md')) {
|
|
87
|
+
continue;
|
|
88
|
+
}
|
|
88
89
|
|
|
89
90
|
const content = await fs.readFile(path.join(agentsDir, file), 'utf-8');
|
|
90
91
|
agents.push({ name: file, content });
|
|
@@ -103,7 +104,9 @@ export class TemplateLoader {
|
|
|
103
104
|
const files = await fs.readdir(commandsDir);
|
|
104
105
|
|
|
105
106
|
for (const file of files) {
|
|
106
|
-
if (!file.endsWith('.md'))
|
|
107
|
+
if (!file.endsWith('.md')) {
|
|
108
|
+
continue;
|
|
109
|
+
}
|
|
107
110
|
|
|
108
111
|
const content = await fs.readFile(path.join(commandsDir, file), 'utf-8');
|
|
109
112
|
commands.push({ name: file, content });
|
|
@@ -115,9 +118,7 @@ export class TemplateLoader {
|
|
|
115
118
|
/**
|
|
116
119
|
* Load MCP servers configuration
|
|
117
120
|
*/
|
|
118
|
-
private async loadMCPServers(
|
|
119
|
-
configPath: string
|
|
120
|
-
): Promise<Array<{ name: string; config: any }>> {
|
|
121
|
+
private async loadMCPServers(configPath: string): Promise<Array<{ name: string; config: any }>> {
|
|
121
122
|
const data = await fs.readFile(configPath, 'utf-8');
|
|
122
123
|
const config = JSON.parse(data);
|
|
123
124
|
|
|
@@ -129,25 +130,6 @@ export class TemplateLoader {
|
|
|
129
130
|
return servers;
|
|
130
131
|
}
|
|
131
132
|
|
|
132
|
-
/**
|
|
133
|
-
* Load hooks from directory
|
|
134
|
-
*/
|
|
135
|
-
private async loadHooks(
|
|
136
|
-
hooksDir: string
|
|
137
|
-
): Promise<Array<{ name: string; content: string }>> {
|
|
138
|
-
const hooks = [];
|
|
139
|
-
const files = await fs.readdir(hooksDir);
|
|
140
|
-
|
|
141
|
-
for (const file of files) {
|
|
142
|
-
if (!file.endsWith('.js')) continue;
|
|
143
|
-
|
|
144
|
-
const content = await fs.readFile(path.join(hooksDir, file), 'utf-8');
|
|
145
|
-
hooks.push({ name: file, content });
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
return hooks;
|
|
149
|
-
}
|
|
150
|
-
|
|
151
133
|
/**
|
|
152
134
|
* Load single files (CLAUDE.md, .cursorrules, etc.)
|
|
153
135
|
*/
|
|
@@ -180,7 +162,7 @@ export class TemplateLoader {
|
|
|
180
162
|
/**
|
|
181
163
|
* Check if templates exist (uses flat directory structure)
|
|
182
164
|
*/
|
|
183
|
-
async hasTemplates(
|
|
165
|
+
async hasTemplates(_target: Target | string): Promise<boolean> {
|
|
184
166
|
// Check if any template directories exist
|
|
185
167
|
const agentsDir = path.join(this.assetsDir, 'agents');
|
|
186
168
|
const commandsDir = path.join(this.assetsDir, 'slash-commands');
|