@sylphx/flow 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/CHANGELOG.md +12 -0
- package/package.json +10 -9
- package/src/commands/codebase-command.ts +168 -0
- package/src/commands/flow-command.ts +1137 -0
- package/src/commands/flow-orchestrator.ts +296 -0
- package/src/commands/hook-command.ts +444 -0
- package/src/commands/init-command.ts +92 -0
- package/src/commands/init-core.ts +322 -0
- package/src/commands/knowledge-command.ts +161 -0
- package/src/commands/run-command.ts +120 -0
- package/src/components/benchmark-monitor.tsx +331 -0
- package/src/components/reindex-progress.tsx +261 -0
- package/src/composables/functional/index.ts +14 -0
- package/src/composables/functional/useEnvironment.ts +171 -0
- package/src/composables/functional/useFileSystem.ts +139 -0
- package/src/composables/index.ts +5 -0
- package/src/composables/useEnv.ts +13 -0
- package/src/composables/useRuntimeConfig.ts +27 -0
- package/src/composables/useTargetConfig.ts +45 -0
- package/src/config/ai-config.ts +376 -0
- package/src/config/constants.ts +35 -0
- package/src/config/index.ts +27 -0
- package/src/config/rules.ts +43 -0
- package/src/config/servers.ts +371 -0
- package/src/config/targets.ts +126 -0
- package/src/core/agent-loader.ts +141 -0
- package/src/core/agent-manager.ts +174 -0
- package/src/core/ai-sdk.ts +603 -0
- package/src/core/app-factory.ts +381 -0
- package/src/core/builtin-agents.ts +9 -0
- package/src/core/command-system.ts +550 -0
- package/src/core/config-system.ts +550 -0
- package/src/core/connection-pool.ts +390 -0
- package/src/core/di-container.ts +155 -0
- package/src/core/error-handling.ts +519 -0
- package/src/core/formatting/bytes.test.ts +115 -0
- package/src/core/formatting/bytes.ts +64 -0
- package/src/core/functional/async.ts +313 -0
- package/src/core/functional/either.ts +109 -0
- package/src/core/functional/error-handler.ts +135 -0
- package/src/core/functional/error-types.ts +311 -0
- package/src/core/functional/index.ts +19 -0
- package/src/core/functional/option.ts +142 -0
- package/src/core/functional/pipe.ts +189 -0
- package/src/core/functional/result.ts +204 -0
- package/src/core/functional/validation.ts +138 -0
- package/src/core/headless-display.ts +96 -0
- package/src/core/index.ts +6 -0
- package/src/core/installers/file-installer.ts +303 -0
- package/src/core/installers/mcp-installer.ts +213 -0
- package/src/core/interfaces/index.ts +22 -0
- package/src/core/interfaces/repository.interface.ts +91 -0
- package/src/core/interfaces/service.interface.ts +133 -0
- package/src/core/interfaces.ts +129 -0
- package/src/core/loop-controller.ts +200 -0
- package/src/core/result.ts +351 -0
- package/src/core/rule-loader.ts +147 -0
- package/src/core/rule-manager.ts +240 -0
- package/src/core/service-config.ts +252 -0
- package/src/core/session-service.ts +121 -0
- package/src/core/state-detector.ts +389 -0
- package/src/core/storage-factory.ts +115 -0
- package/src/core/stream-handler.ts +288 -0
- package/src/core/target-manager.ts +161 -0
- package/src/core/type-utils.ts +427 -0
- package/src/core/unified-storage.ts +456 -0
- package/src/core/upgrade-manager.ts +300 -0
- package/src/core/validation/limit.test.ts +155 -0
- package/src/core/validation/limit.ts +46 -0
- package/src/core/validation/query.test.ts +44 -0
- package/src/core/validation/query.ts +20 -0
- package/src/db/auto-migrate.ts +322 -0
- package/src/db/base-database-client.ts +144 -0
- package/src/db/cache-db.ts +218 -0
- package/src/db/cache-schema.ts +75 -0
- package/src/db/database.ts +70 -0
- package/src/db/index.ts +252 -0
- package/src/db/memory-db.ts +153 -0
- package/src/db/memory-schema.ts +29 -0
- package/src/db/schema.ts +289 -0
- package/src/db/session-repository.ts +733 -0
- package/src/domains/codebase/index.ts +5 -0
- package/src/domains/codebase/tools.ts +139 -0
- package/src/domains/index.ts +8 -0
- package/src/domains/knowledge/index.ts +10 -0
- package/src/domains/knowledge/resources.ts +537 -0
- package/src/domains/knowledge/tools.ts +174 -0
- package/src/domains/utilities/index.ts +6 -0
- package/src/domains/utilities/time/index.ts +5 -0
- package/src/domains/utilities/time/tools.ts +291 -0
- package/src/index.ts +211 -0
- package/src/services/agent-service.ts +273 -0
- package/src/services/claude-config-service.ts +252 -0
- package/src/services/config-service.ts +258 -0
- package/src/services/evaluation-service.ts +271 -0
- package/src/services/functional/evaluation-logic.ts +296 -0
- package/src/services/functional/file-processor.ts +273 -0
- package/src/services/functional/index.ts +12 -0
- package/src/services/index.ts +13 -0
- package/src/services/mcp-service.ts +432 -0
- package/src/services/memory.service.ts +476 -0
- package/src/services/search/base-indexer.ts +156 -0
- package/src/services/search/codebase-indexer-types.ts +38 -0
- package/src/services/search/codebase-indexer.ts +647 -0
- package/src/services/search/embeddings-provider.ts +455 -0
- package/src/services/search/embeddings.ts +316 -0
- package/src/services/search/functional-indexer.ts +323 -0
- package/src/services/search/index.ts +27 -0
- package/src/services/search/indexer.ts +380 -0
- package/src/services/search/knowledge-indexer.ts +422 -0
- package/src/services/search/semantic-search.ts +244 -0
- package/src/services/search/tfidf.ts +559 -0
- package/src/services/search/unified-search-service.ts +888 -0
- package/src/services/smart-config-service.ts +385 -0
- package/src/services/storage/cache-storage.ts +487 -0
- package/src/services/storage/drizzle-storage.ts +581 -0
- package/src/services/storage/index.ts +15 -0
- package/src/services/storage/lancedb-vector-storage.ts +494 -0
- package/src/services/storage/memory-storage.ts +268 -0
- package/src/services/storage/separated-storage.ts +467 -0
- package/src/services/storage/vector-storage.ts +13 -0
- package/src/shared/agents/index.ts +63 -0
- package/src/shared/files/index.ts +99 -0
- package/src/shared/index.ts +32 -0
- package/src/shared/logging/index.ts +24 -0
- package/src/shared/processing/index.ts +153 -0
- package/src/shared/types/index.ts +25 -0
- package/src/targets/claude-code.ts +574 -0
- package/src/targets/functional/claude-code-logic.ts +185 -0
- package/src/targets/functional/index.ts +6 -0
- package/src/targets/opencode.ts +529 -0
- package/src/types/agent.types.ts +32 -0
- package/src/types/api/batch.ts +108 -0
- package/src/types/api/errors.ts +118 -0
- package/src/types/api/index.ts +55 -0
- package/src/types/api/requests.ts +76 -0
- package/src/types/api/responses.ts +180 -0
- package/src/types/api/websockets.ts +85 -0
- package/src/types/api.types.ts +9 -0
- package/src/types/benchmark.ts +49 -0
- package/src/types/cli.types.ts +87 -0
- package/src/types/common.types.ts +35 -0
- package/src/types/database.types.ts +510 -0
- package/src/types/mcp-config.types.ts +448 -0
- package/src/types/mcp.types.ts +69 -0
- package/src/types/memory-types.ts +63 -0
- package/src/types/provider.types.ts +28 -0
- package/src/types/rule.types.ts +24 -0
- package/src/types/session.types.ts +214 -0
- package/src/types/target-config.types.ts +295 -0
- package/src/types/target.types.ts +140 -0
- package/src/types/todo.types.ts +25 -0
- package/src/types.ts +40 -0
- package/src/utils/advanced-tokenizer.ts +191 -0
- package/src/utils/agent-enhancer.ts +114 -0
- package/src/utils/ai-model-fetcher.ts +19 -0
- package/src/utils/async-file-operations.ts +516 -0
- package/src/utils/audio-player.ts +345 -0
- package/src/utils/cli-output.ts +266 -0
- package/src/utils/codebase-helpers.ts +211 -0
- package/src/utils/console-ui.ts +79 -0
- package/src/utils/database-errors.ts +140 -0
- package/src/utils/debug-logger.ts +49 -0
- package/src/utils/error-handler.ts +53 -0
- package/src/utils/file-operations.ts +310 -0
- package/src/utils/file-scanner.ts +259 -0
- package/src/utils/functional/array.ts +355 -0
- package/src/utils/functional/index.ts +15 -0
- package/src/utils/functional/object.ts +279 -0
- package/src/utils/functional/string.ts +281 -0
- package/src/utils/functional.ts +543 -0
- package/src/utils/help.ts +20 -0
- package/src/utils/immutable-cache.ts +106 -0
- package/src/utils/index.ts +78 -0
- package/src/utils/jsonc.ts +158 -0
- package/src/utils/logger.ts +396 -0
- package/src/utils/mcp-config.ts +249 -0
- package/src/utils/memory-tui.ts +414 -0
- package/src/utils/models-dev.ts +91 -0
- package/src/utils/notifications.ts +169 -0
- package/src/utils/object-utils.ts +51 -0
- package/src/utils/parallel-operations.ts +487 -0
- package/src/utils/paths.ts +143 -0
- package/src/utils/process-manager.ts +155 -0
- package/src/utils/prompts.ts +120 -0
- package/src/utils/search-tool-builder.ts +214 -0
- package/src/utils/secret-utils.ts +179 -0
- package/src/utils/security.ts +537 -0
- package/src/utils/session-manager.ts +168 -0
- package/src/utils/session-title.ts +87 -0
- package/src/utils/settings.ts +182 -0
- package/src/utils/simplified-errors.ts +410 -0
- package/src/utils/sync-utils.ts +159 -0
- package/src/utils/target-config.ts +570 -0
- package/src/utils/target-utils.ts +394 -0
- package/src/utils/template-engine.ts +94 -0
- package/src/utils/test-audio.ts +71 -0
- package/src/utils/todo-context.ts +46 -0
- package/src/utils/token-counter.ts +288 -0
- package/dist/index.d.ts +0 -10
- package/dist/index.js +0 -59554
- package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
- package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
- package/dist/shared/chunk-25dwp0dp.js +0 -89
- package/dist/shared/chunk-3pjb6063.js +0 -208
- package/dist/shared/chunk-4d6ydpw7.js +0 -2854
- package/dist/shared/chunk-4wjcadjk.js +0 -225
- package/dist/shared/chunk-5j4w74t6.js +0 -30
- package/dist/shared/chunk-5j8m3dh3.js +0 -58
- package/dist/shared/chunk-5thh3qem.js +0 -91
- package/dist/shared/chunk-6g9xy73m.js +0 -252
- package/dist/shared/chunk-7eq34c42.js +0 -23
- package/dist/shared/chunk-c2gwgx3r.js +0 -115
- package/dist/shared/chunk-cjd3mk4c.js +0 -1320
- package/dist/shared/chunk-g5cv6703.js +0 -368
- package/dist/shared/chunk-hpkhykhq.js +0 -574
- package/dist/shared/chunk-m2322pdk.js +0 -122
- package/dist/shared/chunk-nd5fdvaq.js +0 -26
- package/dist/shared/chunk-pgd3m6zf.js +0 -108
- package/dist/shared/chunk-qk8n91hw.js +0 -494
- package/dist/shared/chunk-rkkn8szp.js +0 -16855
- package/dist/shared/chunk-t16rfxh0.js +0 -61
- package/dist/shared/chunk-t4fbfa5v.js +0 -19
- package/dist/shared/chunk-t77h86w6.js +0 -276
- package/dist/shared/chunk-v0ez4aef.js +0 -71
- package/dist/shared/chunk-v29j2r3s.js +0 -32051
- package/dist/shared/chunk-vfbc6ew5.js +0 -765
- package/dist/shared/chunk-vmeqwm1c.js +0 -204
- package/dist/shared/chunk-x66eh37x.js +0 -137
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import fsPromises from 'node:fs/promises';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { FileInstaller } from '../core/installers/file-installer.js';
|
|
6
|
+
import { MCPInstaller } from '../core/installers/mcp-installer.js';
|
|
7
|
+
import type { AgentMetadata } from '../types/target-config.types.js';
|
|
8
|
+
import type { CommonOptions, MCPServerConfigUnion, SetupResult, Target } from '../types.js';
|
|
9
|
+
import { CLIError } from '../utils/error-handler.js';
|
|
10
|
+
import { getAgentsDir, getSlashCommandsDir } from '../utils/paths.js';
|
|
11
|
+
import { sanitize } from '../utils/security.js';
|
|
12
|
+
import { fileUtils, generateHelpText, pathUtils, yamlUtils } from '../utils/target-utils.js';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Claude Code target - composition approach with all original functionality
|
|
16
|
+
*/
|
|
17
|
+
export const claudeCodeTarget: Target = {
|
|
18
|
+
id: 'claude-code',
|
|
19
|
+
name: 'Claude Code',
|
|
20
|
+
description: 'Claude Code CLI with YAML front matter agents (.claude/agents/*.md)',
|
|
21
|
+
category: 'cli',
|
|
22
|
+
isImplemented: true,
|
|
23
|
+
isDefault: false,
|
|
24
|
+
|
|
25
|
+
mcpServerConfig: {
|
|
26
|
+
disableTime: true,
|
|
27
|
+
disableKnowledge: false,
|
|
28
|
+
disableCodebase: true,
|
|
29
|
+
},
|
|
30
|
+
|
|
31
|
+
config: {
|
|
32
|
+
agentDir: '.claude/agents',
|
|
33
|
+
agentExtension: '.md',
|
|
34
|
+
agentFormat: 'yaml-frontmatter',
|
|
35
|
+
stripYaml: false,
|
|
36
|
+
flatten: false,
|
|
37
|
+
configFile: '.mcp.json',
|
|
38
|
+
configSchema: null,
|
|
39
|
+
mcpConfigPath: 'mcpServers',
|
|
40
|
+
rulesFile: undefined, // Rules are included in agent files
|
|
41
|
+
outputStylesDir: undefined, // Output styles are included in agent files
|
|
42
|
+
slashCommandsDir: '.claude/commands',
|
|
43
|
+
installation: {
|
|
44
|
+
createAgentDir: true,
|
|
45
|
+
createConfigFile: true,
|
|
46
|
+
useSecretFiles: false,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Transform agent content for Claude Code
|
|
52
|
+
* Convert OpenCode format to Claude Code format with proper name/description extraction
|
|
53
|
+
*/
|
|
54
|
+
async transformAgentContent(
|
|
55
|
+
content: string,
|
|
56
|
+
metadata?: AgentMetadata,
|
|
57
|
+
sourcePath?: string
|
|
58
|
+
): Promise<string> {
|
|
59
|
+
const { metadata: existingMetadata, content: baseContent } =
|
|
60
|
+
await yamlUtils.extractFrontMatter(content);
|
|
61
|
+
|
|
62
|
+
// Convert OpenCode format to Claude Code format
|
|
63
|
+
const claudeCodeMetadata = convertToClaudeCodeFormat(existingMetadata, baseContent, sourcePath);
|
|
64
|
+
|
|
65
|
+
// If additional metadata is provided, merge it
|
|
66
|
+
if (metadata) {
|
|
67
|
+
Object.assign(claudeCodeMetadata, metadata);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return yamlUtils.addFrontMatter(baseContent, claudeCodeMetadata);
|
|
71
|
+
},
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Transform MCP server configuration for Claude Code
|
|
75
|
+
* Convert from various formats to Claude Code's optimal format
|
|
76
|
+
*/
|
|
77
|
+
transformMCPConfig(config: MCPServerConfigUnion, _serverId?: string): Record<string, unknown> {
|
|
78
|
+
// Handle legacy OpenCode 'local' type
|
|
79
|
+
if (config.type === 'local') {
|
|
80
|
+
// Convert OpenCode 'local' array command to Claude Code format
|
|
81
|
+
const [command, ...args] = config.command;
|
|
82
|
+
return {
|
|
83
|
+
type: 'stdio',
|
|
84
|
+
command,
|
|
85
|
+
...(args && args.length > 0 && { args }),
|
|
86
|
+
...(config.environment && { env: config.environment }),
|
|
87
|
+
};
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Handle new stdio format (already optimized for Claude Code)
|
|
91
|
+
if (config.type === 'stdio') {
|
|
92
|
+
return {
|
|
93
|
+
type: 'stdio',
|
|
94
|
+
command: config.command,
|
|
95
|
+
...(config.args && config.args.length > 0 && { args: config.args }),
|
|
96
|
+
...(config.env && { env: config.env }),
|
|
97
|
+
};
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// Handle legacy OpenCode 'remote' type
|
|
101
|
+
if (config.type === 'remote') {
|
|
102
|
+
return {
|
|
103
|
+
type: 'http',
|
|
104
|
+
url: config.url,
|
|
105
|
+
...(config.headers && { headers: config.headers }),
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Handle new http format (already optimized for Claude Code)
|
|
110
|
+
if (config.type === 'http') {
|
|
111
|
+
return {
|
|
112
|
+
type: 'http',
|
|
113
|
+
url: config.url,
|
|
114
|
+
...(config.headers && { headers: config.headers }),
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
return config;
|
|
119
|
+
},
|
|
120
|
+
|
|
121
|
+
getConfigPath: (cwd: string) =>
|
|
122
|
+
Promise.resolve(fileUtils.getConfigPath(claudeCodeTarget.config, cwd)),
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Read Claude Code configuration with structure normalization
|
|
126
|
+
*/
|
|
127
|
+
async readConfig(cwd: string): Promise<any> {
|
|
128
|
+
const config = await fileUtils.readConfig(claudeCodeTarget.config, cwd);
|
|
129
|
+
|
|
130
|
+
// Ensure the config has the expected structure for Claude Code
|
|
131
|
+
if (!config.mcpServers) {
|
|
132
|
+
config.mcpServers = {};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
return config;
|
|
136
|
+
},
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Write Claude Code configuration with structure normalization
|
|
140
|
+
*/
|
|
141
|
+
async writeConfig(cwd: string, config: Record<string, unknown>): Promise<void> {
|
|
142
|
+
// Ensure the config has the expected structure for Claude Code
|
|
143
|
+
if (!config.mcpServers) {
|
|
144
|
+
config.mcpServers = {};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await fileUtils.writeConfig(claudeCodeTarget.config, cwd, config);
|
|
148
|
+
},
|
|
149
|
+
|
|
150
|
+
validateRequirements: (cwd: string) =>
|
|
151
|
+
fileUtils.validateRequirements(claudeCodeTarget.config, cwd),
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Get detailed Claude Code-specific help text
|
|
155
|
+
*/
|
|
156
|
+
getHelpText(): string {
|
|
157
|
+
let help = generateHelpText(claudeCodeTarget.config);
|
|
158
|
+
|
|
159
|
+
help += 'Claude Code-Specific Information:\n';
|
|
160
|
+
help += ' Configuration File: .mcp.json\n';
|
|
161
|
+
help += ' Agent Format: Markdown with YAML front matter\n';
|
|
162
|
+
help += ' MCP Integration: Full server support\n\n';
|
|
163
|
+
|
|
164
|
+
help += 'Example Agent Structure:\n';
|
|
165
|
+
help += ' ---\n';
|
|
166
|
+
help += ` name: "code-reviewer"\n`;
|
|
167
|
+
help += ` description: "Expert code review specialist"\n`;
|
|
168
|
+
help += ' ---\n\n';
|
|
169
|
+
help += ' Agent content here...\n\n';
|
|
170
|
+
help += `Note: Only 'name' and 'description' fields are supported.\n`;
|
|
171
|
+
help += 'Tools field omitted to allow all tools by default (whitelist model limitation).\n';
|
|
172
|
+
help += 'Unsupported fields (mode, temperature, etc.) are automatically removed.\n\n';
|
|
173
|
+
|
|
174
|
+
help += 'Example MCP Configuration:\n';
|
|
175
|
+
help += ' {\n';
|
|
176
|
+
help += ` "mcpServers": {\n`;
|
|
177
|
+
help += ` "filesystem": {\n`;
|
|
178
|
+
help += ` "type": "stdio",\n`;
|
|
179
|
+
help += ` "command": "npx",\n`;
|
|
180
|
+
help += ` "args": ["@modelcontextprotocol/server-filesystem", "/path/to/allowed/dir"]\n`;
|
|
181
|
+
help += ' },\n';
|
|
182
|
+
help += ` "git": {\n`;
|
|
183
|
+
help += ` "type": "stdio",\n`;
|
|
184
|
+
help += ` "command": "npx",\n`;
|
|
185
|
+
help += ` "args": ["@modelcontextprotocol/server-git", "."],\n`;
|
|
186
|
+
help += ` "env": {\n`;
|
|
187
|
+
help += ` "GIT_TRACE": "1"\n`;
|
|
188
|
+
help += ' }\n';
|
|
189
|
+
help += ' },\n';
|
|
190
|
+
help += ` "api-server": {\n`;
|
|
191
|
+
help += ` "type": "http",\n`;
|
|
192
|
+
help += ` "url": "https://api.example.com/mcp",\n`;
|
|
193
|
+
help += ` "headers": {\n`;
|
|
194
|
+
help += ` "Authorization": "Bearer $API_KEY"\n`;
|
|
195
|
+
help += ' }\n';
|
|
196
|
+
help += ' }\n';
|
|
197
|
+
help += ' }\n';
|
|
198
|
+
help += ' }\n\n';
|
|
199
|
+
help +=
|
|
200
|
+
'Note: Environment variables can be expanded in command, args, env, url, and headers.\n\n';
|
|
201
|
+
|
|
202
|
+
return help;
|
|
203
|
+
},
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Execute command using Claude Code with system prompt and user prompt
|
|
207
|
+
*/
|
|
208
|
+
async executeCommand(
|
|
209
|
+
systemPrompt: string,
|
|
210
|
+
userPrompt: string,
|
|
211
|
+
options: { verbose?: boolean; dryRun?: boolean; print?: boolean; continue?: boolean } = {}
|
|
212
|
+
): Promise<void> {
|
|
213
|
+
// Sanitize and validate inputs
|
|
214
|
+
const sanitizedSystemPrompt = sanitize.yamlContent(systemPrompt);
|
|
215
|
+
// Remove dangerous control characters but don't limit length for user prompts
|
|
216
|
+
const sanitizedUserPrompt = userPrompt.replace(/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/g, '');
|
|
217
|
+
|
|
218
|
+
// Add summary request to system prompt
|
|
219
|
+
const enhancedSystemPrompt = `${sanitizedSystemPrompt}
|
|
220
|
+
|
|
221
|
+
Please begin your response with a comprehensive summary of all the instructions and context provided above.`;
|
|
222
|
+
|
|
223
|
+
if (options.dryRun) {
|
|
224
|
+
// Build the command for display
|
|
225
|
+
const dryRunArgs = ['claude', '--dangerously-skip-permissions'];
|
|
226
|
+
if (options.print) dryRunArgs.push('-p');
|
|
227
|
+
if (options.continue) dryRunArgs.push('-c');
|
|
228
|
+
dryRunArgs.push('--system-prompt', '"<agent content>"');
|
|
229
|
+
if (sanitizedUserPrompt.trim() !== '') {
|
|
230
|
+
dryRunArgs.push(`"${sanitizedUserPrompt}"`);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
console.log(chalk.cyan('Dry run - Would execute:'));
|
|
234
|
+
console.log(chalk.bold(dryRunArgs.join(' ')));
|
|
235
|
+
console.log(chalk.dim(`System prompt: ${enhancedSystemPrompt.length} characters`));
|
|
236
|
+
console.log(chalk.dim(`User prompt: ${sanitizedUserPrompt.length} characters`));
|
|
237
|
+
console.log('✓ Dry run completed successfully');
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
// Build arguments
|
|
243
|
+
const args = ['--dangerously-skip-permissions'];
|
|
244
|
+
|
|
245
|
+
// Add print and continue flags
|
|
246
|
+
if (options.print) {
|
|
247
|
+
args.push('-p');
|
|
248
|
+
}
|
|
249
|
+
if (options.continue) {
|
|
250
|
+
args.push('-c');
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
args.push('--system-prompt', enhancedSystemPrompt);
|
|
254
|
+
if (options.verbose) {
|
|
255
|
+
console.log('🚀 Executing Claude Code');
|
|
256
|
+
console.log(`📝 System prompt length: ${enhancedSystemPrompt.length} characters`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (sanitizedUserPrompt.trim() !== '') {
|
|
260
|
+
args.push(sanitizedUserPrompt);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (options.verbose) {
|
|
264
|
+
console.log(`📝 User prompt length: ${sanitizedUserPrompt.length} characters`);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// Use child_process directly to bypass security validation for this specific case
|
|
268
|
+
// This is safe because we're controlling the command and just passing the prompt as an argument
|
|
269
|
+
|
|
270
|
+
if (options.verbose) {
|
|
271
|
+
console.log(`🚀 Executing: claude ${args.join(' ')}`);
|
|
272
|
+
console.log('');
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
await new Promise<void>((resolve, reject) => {
|
|
276
|
+
const child = spawn('claude', args, {
|
|
277
|
+
stdio: 'inherit',
|
|
278
|
+
shell: false,
|
|
279
|
+
env: process.env, // Pass environment variables including ANTHROPIC_BASE_URL and ANTHROPIC_API_KEY
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
child.on('spawn', () => {
|
|
283
|
+
if (options.verbose) {
|
|
284
|
+
console.log('✓ Claude Code started');
|
|
285
|
+
}
|
|
286
|
+
});
|
|
287
|
+
|
|
288
|
+
child.on('close', (code) => {
|
|
289
|
+
if (code === 0) {
|
|
290
|
+
resolve();
|
|
291
|
+
} else {
|
|
292
|
+
const error = new Error(`Claude Code exited with code ${code}`) as any;
|
|
293
|
+
error.code = code;
|
|
294
|
+
reject(error);
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
child.on('error', (error) => {
|
|
299
|
+
if (options.verbose) {
|
|
300
|
+
console.error('✗ Error spawning Claude:', error);
|
|
301
|
+
}
|
|
302
|
+
reject(error);
|
|
303
|
+
});
|
|
304
|
+
});
|
|
305
|
+
} catch (error: any) {
|
|
306
|
+
if (error.code === 'ENOENT') {
|
|
307
|
+
throw new CLIError('Claude Code not found. Please install it first.', 'CLAUDE_NOT_FOUND');
|
|
308
|
+
}
|
|
309
|
+
if (error.code) {
|
|
310
|
+
throw new CLIError(`Claude Code exited with code ${error.code}`, 'CLAUDE_ERROR');
|
|
311
|
+
}
|
|
312
|
+
throw new CLIError(`Failed to execute Claude Code: ${error.message}`, 'CLAUDE_ERROR');
|
|
313
|
+
}
|
|
314
|
+
},
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Detect if this target is being used in the current environment
|
|
318
|
+
*/
|
|
319
|
+
detectFromEnvironment(): boolean {
|
|
320
|
+
try {
|
|
321
|
+
const cwd = process.cwd();
|
|
322
|
+
return fs.existsSync(path.join(cwd, '.mcp.json'));
|
|
323
|
+
} catch {
|
|
324
|
+
return false;
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
|
|
328
|
+
/**
|
|
329
|
+
* Update .claude/settings.local.json to approve MCP servers
|
|
330
|
+
*/
|
|
331
|
+
async approveMCPServers(cwd: string, serverNames: string[]): Promise<void> {
|
|
332
|
+
const settingsPath = path.join(cwd, '.claude', 'settings.local.json');
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
// Read existing settings or create new
|
|
336
|
+
let settings: Record<string, unknown> = {};
|
|
337
|
+
|
|
338
|
+
try {
|
|
339
|
+
const content = await fsPromises.readFile(settingsPath, 'utf8');
|
|
340
|
+
settings = JSON.parse(content);
|
|
341
|
+
} catch (error: any) {
|
|
342
|
+
if (error.code !== 'ENOENT') {
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
// File doesn't exist, will create new
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
// Get existing approved servers
|
|
349
|
+
const existingServers = Array.isArray(settings.enabledMcpjsonServers)
|
|
350
|
+
? settings.enabledMcpjsonServers
|
|
351
|
+
: [];
|
|
352
|
+
|
|
353
|
+
// Merge with new servers (deduplicate)
|
|
354
|
+
const allServers = [...new Set([...existingServers, ...serverNames])];
|
|
355
|
+
|
|
356
|
+
// Update settings
|
|
357
|
+
settings.enabledMcpjsonServers = allServers;
|
|
358
|
+
|
|
359
|
+
// Ensure .claude directory exists
|
|
360
|
+
await fsPromises.mkdir(path.dirname(settingsPath), { recursive: true });
|
|
361
|
+
|
|
362
|
+
// Write updated settings
|
|
363
|
+
await fsPromises.writeFile(settingsPath, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
|
|
364
|
+
} catch (error) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Failed to approve MCP servers: ${error instanceof Error ? error.message : String(error)}`
|
|
367
|
+
);
|
|
368
|
+
}
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
/**
|
|
372
|
+
* Transform rules content for Claude Code
|
|
373
|
+
* Claude Code doesn't need front matter in rules files (CLAUDE.md)
|
|
374
|
+
*/
|
|
375
|
+
async transformRulesContent(content: string): Promise<string> {
|
|
376
|
+
return yamlUtils.stripFrontMatter(content);
|
|
377
|
+
},
|
|
378
|
+
|
|
379
|
+
/**
|
|
380
|
+
* Setup hooks for Claude Code
|
|
381
|
+
* Configure session and prompt hooks for system information display
|
|
382
|
+
*/
|
|
383
|
+
async setupHooks(cwd: string, _options: CommonOptions): Promise<SetupResult> {
|
|
384
|
+
const { processSettings, generateHookCommands } = await import('./functional/claude-code-logic.js');
|
|
385
|
+
const { pathExists, createDirectory, readFile, writeFile } = await import(
|
|
386
|
+
'../composables/functional/useFileSystem.js'
|
|
387
|
+
);
|
|
388
|
+
|
|
389
|
+
const claudeConfigDir = path.join(cwd, '.claude');
|
|
390
|
+
const settingsPath = path.join(claudeConfigDir, 'settings.json');
|
|
391
|
+
|
|
392
|
+
// Ensure .claude directory exists
|
|
393
|
+
const dirExistsResult = await pathExists(claudeConfigDir);
|
|
394
|
+
if (dirExistsResult._tag === 'Success' && !dirExistsResult.value) {
|
|
395
|
+
const createResult = await createDirectory(claudeConfigDir, { recursive: true });
|
|
396
|
+
if (createResult._tag === 'Failure') {
|
|
397
|
+
throw new Error(`Failed to create .claude directory: ${createResult.error.message}`);
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
// Read existing settings or null if doesn't exist
|
|
402
|
+
let existingContent: string | null = null;
|
|
403
|
+
const fileExistsResult = await pathExists(settingsPath);
|
|
404
|
+
if (fileExistsResult._tag === 'Success' && fileExistsResult.value) {
|
|
405
|
+
const readResult = await readFile(settingsPath);
|
|
406
|
+
if (readResult._tag === 'Success') {
|
|
407
|
+
existingContent = readResult.value;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
// Generate hooks based on how CLI was invoked
|
|
412
|
+
const hookCommands = await generateHookCommands('claude-code');
|
|
413
|
+
|
|
414
|
+
// Process settings using pure functions
|
|
415
|
+
const settingsResult = processSettings(existingContent, hookCommands);
|
|
416
|
+
|
|
417
|
+
if (settingsResult._tag === 'Failure') {
|
|
418
|
+
throw new Error(`Failed to process settings: ${settingsResult.error.message}`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Write updated settings
|
|
422
|
+
const writeResult = await writeFile(settingsPath, settingsResult.value);
|
|
423
|
+
if (writeResult._tag === 'Failure') {
|
|
424
|
+
throw new Error(`Failed to write settings: ${writeResult.error.message}`);
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
// Return 3 hooks configured (SessionStart + UserPromptSubmit + Notification)
|
|
428
|
+
return {
|
|
429
|
+
count: 3,
|
|
430
|
+
message: 'Configured session, message, and notification hooks',
|
|
431
|
+
};
|
|
432
|
+
},
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* Setup agents for Claude Code
|
|
436
|
+
* Install agents to .claude/agents/ directory with rules and output styles appended
|
|
437
|
+
*/
|
|
438
|
+
async setupAgents(cwd: string, options: CommonOptions): Promise<SetupResult> {
|
|
439
|
+
const { enhanceAgentContent } = await import('../utils/agent-enhancer.js');
|
|
440
|
+
const installer = new FileInstaller();
|
|
441
|
+
const agentsDir = path.join(cwd, this.config.agentDir);
|
|
442
|
+
|
|
443
|
+
const results = await installer.installToDirectory(
|
|
444
|
+
getAgentsDir(),
|
|
445
|
+
agentsDir,
|
|
446
|
+
async (content, sourcePath) => {
|
|
447
|
+
// Transform agent content (add YAML front matter, etc.)
|
|
448
|
+
const transformed = await this.transformAgentContent(content, undefined, sourcePath);
|
|
449
|
+
|
|
450
|
+
// Extract rules from frontmatter to pass to enhancer
|
|
451
|
+
const { metadata } = await yamlUtils.extractFrontMatter(transformed);
|
|
452
|
+
const rules = metadata.rules as string[] | undefined;
|
|
453
|
+
|
|
454
|
+
// Enhance with rules and output styles
|
|
455
|
+
const enhanced = await enhanceAgentContent(transformed, rules);
|
|
456
|
+
|
|
457
|
+
return enhanced;
|
|
458
|
+
},
|
|
459
|
+
{
|
|
460
|
+
...options,
|
|
461
|
+
showProgress: false, // UI handled by init-command
|
|
462
|
+
}
|
|
463
|
+
);
|
|
464
|
+
|
|
465
|
+
return { count: results.length };
|
|
466
|
+
},
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Setup output styles for Claude Code
|
|
470
|
+
* Output styles are appended to each agent file
|
|
471
|
+
*/
|
|
472
|
+
async setupOutputStyles(_cwd: string, _options: CommonOptions): Promise<SetupResult> {
|
|
473
|
+
// Output styles are appended to each agent file during setupAgents
|
|
474
|
+
// No separate installation needed
|
|
475
|
+
return {
|
|
476
|
+
count: 0,
|
|
477
|
+
message: 'Output styles included in agent files',
|
|
478
|
+
};
|
|
479
|
+
},
|
|
480
|
+
|
|
481
|
+
/**
|
|
482
|
+
* Setup rules for Claude Code
|
|
483
|
+
* Rules are appended to each agent file
|
|
484
|
+
*/
|
|
485
|
+
async setupRules(_cwd: string, _options: CommonOptions): Promise<SetupResult> {
|
|
486
|
+
// Rules are appended to each agent file during setupAgents
|
|
487
|
+
// No separate CLAUDE.md file needed
|
|
488
|
+
return {
|
|
489
|
+
count: 0,
|
|
490
|
+
message: 'Rules included in agent files',
|
|
491
|
+
};
|
|
492
|
+
},
|
|
493
|
+
|
|
494
|
+
/**
|
|
495
|
+
* Setup MCP servers for Claude Code
|
|
496
|
+
* Select, configure, install, and approve MCP servers
|
|
497
|
+
*/
|
|
498
|
+
async setupMCP(cwd: string, options: CommonOptions): Promise<SetupResult> {
|
|
499
|
+
const installer = new MCPInstaller(this);
|
|
500
|
+
const result = await installer.setupMCP({ ...options, quiet: true });
|
|
501
|
+
|
|
502
|
+
// Approve servers in Claude Code settings
|
|
503
|
+
if (result.selectedServers.length > 0 && !options.dryRun) {
|
|
504
|
+
if (this.approveMCPServers) {
|
|
505
|
+
await this.approveMCPServers(cwd, result.selectedServers);
|
|
506
|
+
}
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
return { count: result.selectedServers.length };
|
|
510
|
+
},
|
|
511
|
+
|
|
512
|
+
/**
|
|
513
|
+
* Setup slash commands for Claude Code
|
|
514
|
+
* Install slash command templates to .claude/commands/ directory
|
|
515
|
+
*/
|
|
516
|
+
async setupSlashCommands(cwd: string, options: CommonOptions): Promise<SetupResult> {
|
|
517
|
+
if (!this.config.slashCommandsDir) {
|
|
518
|
+
return { count: 0 };
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
const installer = new FileInstaller();
|
|
522
|
+
const slashCommandsDir = path.join(cwd, this.config.slashCommandsDir);
|
|
523
|
+
|
|
524
|
+
const results = await installer.installToDirectory(
|
|
525
|
+
getSlashCommandsDir(),
|
|
526
|
+
slashCommandsDir,
|
|
527
|
+
async (content) => {
|
|
528
|
+
// Slash commands are plain markdown with front matter - no transformation needed
|
|
529
|
+
return content;
|
|
530
|
+
},
|
|
531
|
+
{
|
|
532
|
+
...options,
|
|
533
|
+
showProgress: false, // UI handled by init-command
|
|
534
|
+
}
|
|
535
|
+
);
|
|
536
|
+
|
|
537
|
+
return { count: results.length };
|
|
538
|
+
},
|
|
539
|
+
};
|
|
540
|
+
|
|
541
|
+
/**
|
|
542
|
+
* Convert OpenCode frontmatter to Claude Code format
|
|
543
|
+
*/
|
|
544
|
+
function convertToClaudeCodeFormat(
|
|
545
|
+
openCodeMetadata: any,
|
|
546
|
+
content: string,
|
|
547
|
+
sourcePath?: string
|
|
548
|
+
): any {
|
|
549
|
+
// Use explicit name from metadata if available, otherwise extract from content or path
|
|
550
|
+
const agentName =
|
|
551
|
+
openCodeMetadata.name || pathUtils.extractAgentName(content, openCodeMetadata, sourcePath);
|
|
552
|
+
|
|
553
|
+
// Extract description from metadata or content
|
|
554
|
+
const description = openCodeMetadata.description || pathUtils.extractDescription(content);
|
|
555
|
+
|
|
556
|
+
// Only keep supported fields for Claude Code
|
|
557
|
+
const result: any = {
|
|
558
|
+
name: agentName,
|
|
559
|
+
description: description,
|
|
560
|
+
};
|
|
561
|
+
|
|
562
|
+
// Only add model if it exists and is not 'inherit' (default)
|
|
563
|
+
if (openCodeMetadata.model && openCodeMetadata.model !== 'inherit') {
|
|
564
|
+
result.model = openCodeMetadata.model;
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
// Remove unsupported fields that might cause issues
|
|
568
|
+
// - tools: removed to allow all tools by default
|
|
569
|
+
// - mode: not supported by Claude Code
|
|
570
|
+
// - temperature: not supported by Claude Code
|
|
571
|
+
// - Other custom fields should also be removed for compatibility
|
|
572
|
+
|
|
573
|
+
return result;
|
|
574
|
+
}
|