@sylphx/flow 1.0.2 → 1.0.4
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 +393 -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,444 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Hook command - Dynamic content loading for Claude Code hooks
|
|
4
|
+
*
|
|
5
|
+
* Purpose: Load rules and output styles dynamically via session hooks
|
|
6
|
+
* instead of installing them as static files
|
|
7
|
+
*
|
|
8
|
+
* DESIGN RATIONALE:
|
|
9
|
+
* - Single source of truth: assets/ directory
|
|
10
|
+
* - Dynamic loading: No static file maintenance
|
|
11
|
+
* - Flexible: Easy to extend for different hook types and targets
|
|
12
|
+
* - Consistent: Follows sysinfo command pattern
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { exec } from 'node:child_process';
|
|
16
|
+
import fsSync from 'node:fs';
|
|
17
|
+
import os from 'node:os';
|
|
18
|
+
import path from 'node:path';
|
|
19
|
+
import { promisify } from 'node:util';
|
|
20
|
+
import { Command } from 'commander';
|
|
21
|
+
import { cli } from '../utils/cli-output.js';
|
|
22
|
+
|
|
23
|
+
const execAsync = promisify(exec);
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Hook types supported
|
|
27
|
+
*/
|
|
28
|
+
type HookType = 'session' | 'message' | 'notification';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Target platforms supported
|
|
32
|
+
*/
|
|
33
|
+
type TargetPlatform = 'claude-code';
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Create the hook command
|
|
37
|
+
*/
|
|
38
|
+
export const hookCommand = new Command('hook')
|
|
39
|
+
.description('Load dynamic system information for Claude Code hooks')
|
|
40
|
+
.requiredOption('--type <type>', 'Hook type (session, message)')
|
|
41
|
+
.option('--target <target>', 'Target platform (claude-code)', 'claude-code')
|
|
42
|
+
.option('--verbose', 'Show verbose output', false)
|
|
43
|
+
.action(async (options) => {
|
|
44
|
+
try {
|
|
45
|
+
const hookType = options.type as HookType;
|
|
46
|
+
const target = options.target as TargetPlatform;
|
|
47
|
+
|
|
48
|
+
// Validate hook type
|
|
49
|
+
if (!['session', 'message', 'notification'].includes(hookType)) {
|
|
50
|
+
throw new Error(
|
|
51
|
+
`Invalid hook type: ${hookType}. Must be 'session', 'message', or 'notification'`
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
// Validate target
|
|
56
|
+
if (target !== 'claude-code') {
|
|
57
|
+
throw new Error(`Invalid target: ${target}. Only 'claude-code' is currently supported`);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Load and display content based on hook type
|
|
61
|
+
const content = await loadHookContent(hookType, target, options.verbose);
|
|
62
|
+
|
|
63
|
+
// Output the content (no extra formatting, just the content)
|
|
64
|
+
console.log(content);
|
|
65
|
+
|
|
66
|
+
// Explicitly exit to ensure process terminates
|
|
67
|
+
// REASON: Even with parseAsync(), the process may not exit due to:
|
|
68
|
+
// 1. Logger instances keeping event loop active
|
|
69
|
+
// 2. Other global resources (timers, listeners) not being cleaned up
|
|
70
|
+
// 3. This is a short-lived CLI command that should exit immediately after output
|
|
71
|
+
// Many CLI tools use process.exit() for this reason - it's the right pattern here
|
|
72
|
+
process.exit(0);
|
|
73
|
+
} catch (error) {
|
|
74
|
+
cli.error(
|
|
75
|
+
`Failed to load hook content: ${error instanceof Error ? error.message : String(error)}`
|
|
76
|
+
);
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Load content for a specific hook type and target
|
|
83
|
+
*/
|
|
84
|
+
async function loadHookContent(
|
|
85
|
+
hookType: HookType,
|
|
86
|
+
target: TargetPlatform,
|
|
87
|
+
verbose: boolean = false
|
|
88
|
+
): Promise<string> {
|
|
89
|
+
if (hookType === 'session') {
|
|
90
|
+
return await loadSessionContent(target, verbose);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (hookType === 'message') {
|
|
94
|
+
return await loadMessageContent(target, verbose);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (hookType === 'notification') {
|
|
98
|
+
return await sendNotification(verbose);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return '';
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Load content for session start hook
|
|
106
|
+
* Includes: system info only (rules and output styles are static files)
|
|
107
|
+
*/
|
|
108
|
+
async function loadSessionContent(_target: TargetPlatform, verbose: boolean): Promise<string> {
|
|
109
|
+
// Load system info for session
|
|
110
|
+
if (verbose) {
|
|
111
|
+
cli.info('Loading system info...');
|
|
112
|
+
}
|
|
113
|
+
return await getSystemInfo('session');
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Load content for message hook
|
|
118
|
+
* Includes: system status
|
|
119
|
+
*/
|
|
120
|
+
async function loadMessageContent(_target: TargetPlatform, verbose: boolean): Promise<string> {
|
|
121
|
+
if (verbose) {
|
|
122
|
+
cli.info('Loading system status...');
|
|
123
|
+
}
|
|
124
|
+
return await getSystemInfo('message');
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Get system information
|
|
129
|
+
*/
|
|
130
|
+
async function getSystemInfo(hookType: 'session' | 'message'): Promise<string> {
|
|
131
|
+
const currentTime = new Date().toISOString();
|
|
132
|
+
const tempDir = os.tmpdir();
|
|
133
|
+
|
|
134
|
+
// Get memory information
|
|
135
|
+
const totalMem = os.totalmem();
|
|
136
|
+
const freeMem = os.freemem();
|
|
137
|
+
const usedMem = totalMem - freeMem;
|
|
138
|
+
const memoryUsage = ((usedMem / totalMem) * 100).toFixed(1);
|
|
139
|
+
|
|
140
|
+
// Get CPU information
|
|
141
|
+
const cpus = os.cpus();
|
|
142
|
+
const cpuCores = cpus.length;
|
|
143
|
+
|
|
144
|
+
// Get CPU usage (using load average for fast detection)
|
|
145
|
+
const loadAvg = os.loadavg();
|
|
146
|
+
const cpuUsagePercent = Math.round((loadAvg[0] / cpuCores) * 100);
|
|
147
|
+
|
|
148
|
+
// Get platform information
|
|
149
|
+
const platform = os.platform();
|
|
150
|
+
const arch = os.arch();
|
|
151
|
+
|
|
152
|
+
if (hookType === 'session') {
|
|
153
|
+
// Session info includes project information
|
|
154
|
+
const projectInfo = await detectProjectInfo();
|
|
155
|
+
|
|
156
|
+
return `## Session Information
|
|
157
|
+
|
|
158
|
+
**Platform:** ${platform} (${arch})
|
|
159
|
+
**Working Directory:** ${process.cwd()}
|
|
160
|
+
**Temp Directory:** ${tempDir}
|
|
161
|
+
**CPU:** ${cpuCores} cores
|
|
162
|
+
**Total Memory:** ${formatBytes(totalMem)}
|
|
163
|
+
|
|
164
|
+
## Project Information
|
|
165
|
+
|
|
166
|
+
**Project Type:** ${projectInfo.type}
|
|
167
|
+
**Package Manager:** ${projectInfo.packageManager}${
|
|
168
|
+
projectInfo.name && projectInfo.name !== 'unnamed'
|
|
169
|
+
? `\n**Project:** ${projectInfo.name} (${projectInfo.version})`
|
|
170
|
+
: ''
|
|
171
|
+
}`;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Message info - just current status
|
|
175
|
+
return `## System Status
|
|
176
|
+
|
|
177
|
+
**Current Time:** ${new Date(currentTime).toLocaleString()}
|
|
178
|
+
**CPU:** ${cpuUsagePercent}%
|
|
179
|
+
**Memory:** ${memoryUsage}% used (${formatBytes(freeMem)} free)`;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Detect project information
|
|
184
|
+
*/
|
|
185
|
+
async function detectProjectInfo() {
|
|
186
|
+
const cwd = process.cwd();
|
|
187
|
+
const packageJsonPath = path.join(cwd, 'package.json');
|
|
188
|
+
|
|
189
|
+
// Check if package.json exists
|
|
190
|
+
if (!fsSync.existsSync(packageJsonPath)) {
|
|
191
|
+
return {
|
|
192
|
+
type: 'unknown',
|
|
193
|
+
packageManager: 'none',
|
|
194
|
+
description: 'No package.json found',
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
try {
|
|
199
|
+
// Read package.json
|
|
200
|
+
const packageJson = JSON.parse(fsSync.readFileSync(packageJsonPath, 'utf8'));
|
|
201
|
+
|
|
202
|
+
// Detect project type based on dependencies and scripts
|
|
203
|
+
const projectType = detectProjectType(packageJson);
|
|
204
|
+
|
|
205
|
+
// Detect package manager based on package.json field, then lock files
|
|
206
|
+
const packageManager = detectPackageManager(cwd, packageJson);
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
type: projectType,
|
|
210
|
+
packageManager: packageManager,
|
|
211
|
+
name: packageJson.name || 'unnamed',
|
|
212
|
+
version: packageJson.version || '0.0.0',
|
|
213
|
+
description: packageJson.description || '',
|
|
214
|
+
};
|
|
215
|
+
} catch (_error) {
|
|
216
|
+
return {
|
|
217
|
+
type: 'js/ts',
|
|
218
|
+
packageManager: 'unknown',
|
|
219
|
+
description: 'Invalid package.json',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Detect project type from package.json
|
|
226
|
+
*/
|
|
227
|
+
function detectProjectType(packageJson: Record<string, unknown>): string {
|
|
228
|
+
// Check for TypeScript
|
|
229
|
+
const hasTypescript =
|
|
230
|
+
packageJson.devDependencies?.typescript ||
|
|
231
|
+
packageJson.dependencies?.typescript ||
|
|
232
|
+
packageJson.devDependencies?.['@types/node'] ||
|
|
233
|
+
packageJson.scripts?.build?.includes('tsc') ||
|
|
234
|
+
packageJson.scripts?.dev?.includes('ts-node');
|
|
235
|
+
|
|
236
|
+
if (hasTypescript) {
|
|
237
|
+
return 'typescript';
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// Check for React
|
|
241
|
+
const hasReact =
|
|
242
|
+
packageJson.dependencies?.react ||
|
|
243
|
+
packageJson.devDependencies?.react ||
|
|
244
|
+
packageJson.scripts?.dev?.includes('vite') ||
|
|
245
|
+
packageJson.scripts?.build?.includes('vite');
|
|
246
|
+
|
|
247
|
+
if (hasReact) {
|
|
248
|
+
return 'react';
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Check for Next.js
|
|
252
|
+
const hasNext =
|
|
253
|
+
packageJson.dependencies?.next ||
|
|
254
|
+
packageJson.devDependencies?.next ||
|
|
255
|
+
packageJson.scripts?.dev === 'next dev' ||
|
|
256
|
+
packageJson.scripts?.build === 'next build';
|
|
257
|
+
|
|
258
|
+
if (hasNext) {
|
|
259
|
+
return 'next.js';
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Default to JavaScript
|
|
263
|
+
return 'javascript';
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
/**
|
|
267
|
+
* Detect package manager from lock files
|
|
268
|
+
*/
|
|
269
|
+
function detectPackageManager(cwd: string, packageJson?: any): string {
|
|
270
|
+
// First, check package.json for explicit packageManager field (most accurate)
|
|
271
|
+
if (packageJson?.packageManager) {
|
|
272
|
+
const packageManagerField = packageJson.packageManager;
|
|
273
|
+
// Extract manager name from "bun@1.3.1" format
|
|
274
|
+
const managerName = packageManagerField.split('@')[0];
|
|
275
|
+
if (['npm', 'yarn', 'pnpm', 'bun'].includes(managerName)) {
|
|
276
|
+
return managerName;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
// Fallback: Check for lock files in order of preference
|
|
281
|
+
const lockFiles = [
|
|
282
|
+
{ file: 'pnpm-lock.yaml', manager: 'pnpm' },
|
|
283
|
+
{ file: 'yarn.lock', manager: 'yarn' },
|
|
284
|
+
{ file: 'package-lock.json', manager: 'npm' },
|
|
285
|
+
{ file: 'bun.lockb', manager: 'bun' },
|
|
286
|
+
];
|
|
287
|
+
|
|
288
|
+
for (const { file, manager } of lockFiles) {
|
|
289
|
+
if (fsSync.existsSync(path.join(cwd, file))) {
|
|
290
|
+
return manager;
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return 'npm'; // Default to npm
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* Format bytes to human-readable string
|
|
299
|
+
*/
|
|
300
|
+
function formatBytes(bytes: number): string {
|
|
301
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
302
|
+
if (bytes === 0) {
|
|
303
|
+
return '0 Bytes';
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const i = Math.floor(Math.log(bytes) / Math.log(1024));
|
|
307
|
+
return `${Math.round((bytes / 1024 ** i) * 100) / 100} ${sizes[i]}`;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/**
|
|
311
|
+
* Send OS-level notification
|
|
312
|
+
*/
|
|
313
|
+
async function sendNotification(verbose: boolean): Promise<string> {
|
|
314
|
+
const title = '🔮 Sylphx Flow';
|
|
315
|
+
const message = 'Claude Code is ready';
|
|
316
|
+
const platform = os.platform();
|
|
317
|
+
|
|
318
|
+
if (verbose) {
|
|
319
|
+
cli.info(`Sending notification on ${platform}...`);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
try {
|
|
323
|
+
switch (platform) {
|
|
324
|
+
case 'darwin':
|
|
325
|
+
await sendMacNotification(title, message);
|
|
326
|
+
break;
|
|
327
|
+
case 'linux':
|
|
328
|
+
await sendLinuxNotification(title, message);
|
|
329
|
+
break;
|
|
330
|
+
case 'win32':
|
|
331
|
+
await sendWindowsNotification(title, message);
|
|
332
|
+
break;
|
|
333
|
+
default:
|
|
334
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return ''; // Notifications don't output to stdout
|
|
338
|
+
} catch (error) {
|
|
339
|
+
const errorMsg = error instanceof Error ? error.message : String(error);
|
|
340
|
+
if (verbose) {
|
|
341
|
+
cli.error(`Failed to send notification: ${errorMsg}`);
|
|
342
|
+
}
|
|
343
|
+
// Don't fail the hook, just silently skip notification
|
|
344
|
+
return '';
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Send notification on macOS using terminal-notifier or osascript
|
|
350
|
+
*/
|
|
351
|
+
async function sendMacNotification(title: string, message: string): Promise<void> {
|
|
352
|
+
const iconPath = '/Users/kyle/flow/assets/icons/flow-notification-icon.png';
|
|
353
|
+
|
|
354
|
+
// Try terminal-notifier if available (supports custom icons)
|
|
355
|
+
try {
|
|
356
|
+
await execAsync('which terminal-notifier');
|
|
357
|
+
await execAsync(
|
|
358
|
+
`terminal-notifier -message "${escapeForShell(message)}" -title "${escapeForShell(title)}" -appIcon "${iconPath}"`
|
|
359
|
+
);
|
|
360
|
+
} catch {
|
|
361
|
+
// Fallback to osascript if terminal-notifier not available
|
|
362
|
+
// Note: osascript doesn't support custom icons, will show Terminal app icon
|
|
363
|
+
const script = `display notification "${escapeForAppleScript(message)}" with title "${escapeForAppleScript(title)}"`;
|
|
364
|
+
await execAsync(`osascript -e '${script}'`);
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* Send notification on Linux using notify-send
|
|
370
|
+
*/
|
|
371
|
+
async function sendLinuxNotification(title: string, message: string): Promise<void> {
|
|
372
|
+
// Try to use notify-send, fail silently if not available
|
|
373
|
+
try {
|
|
374
|
+
await execAsync('which notify-send');
|
|
375
|
+
// Use Flow-themed spiral emoji as icon for Sylphx Flow
|
|
376
|
+
await execAsync(
|
|
377
|
+
`notify-send -i "🌀" "${escapeForShell(title)}" "${escapeForShell(message)}"`
|
|
378
|
+
);
|
|
379
|
+
} catch {
|
|
380
|
+
// notify-send not available, skip notification silently
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Send notification on Windows using PowerShell
|
|
386
|
+
*/
|
|
387
|
+
async function sendWindowsNotification(title: string, message: string): Promise<void> {
|
|
388
|
+
const script = `
|
|
389
|
+
[Windows.UI.Notifications.ToastNotificationManager, Windows.UI.Notifications, ContentType = WindowsRuntime] | Out-Null
|
|
390
|
+
[Windows.Data.Xml.Dom.XmlDocument, Windows.Data.Xml.Dom.XmlDocument, ContentType = WindowsRuntime] | Out-Null
|
|
391
|
+
|
|
392
|
+
$template = @"
|
|
393
|
+
<toast>
|
|
394
|
+
<visual>
|
|
395
|
+
<binding template="ToastImageAndText02">
|
|
396
|
+
<image id="1" src="%SystemRoot%\\System32\\Shell32.dll,-16739" alt="info icon"/>
|
|
397
|
+
<text id="1">${escapeForXml(title)}</text>
|
|
398
|
+
<text id="2">${escapeForXml(message)}</text>
|
|
399
|
+
</binding>
|
|
400
|
+
</visual>
|
|
401
|
+
</toast>
|
|
402
|
+
"@
|
|
403
|
+
|
|
404
|
+
$xml = New-Object Windows.Data.Xml.Dom.XmlDocument
|
|
405
|
+
$xml.LoadXml($template)
|
|
406
|
+
$toast = New-Object Windows.UI.Notifications.ToastNotification $xml
|
|
407
|
+
[Windows.UI.Notifications.ToastNotificationManager]::CreateToastNotifier("Claude Code").Show($toast)
|
|
408
|
+
`;
|
|
409
|
+
|
|
410
|
+
await execAsync(`powershell -Command "${escapeForPowerShell(script)}"`);
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* Escape string for AppleScript
|
|
415
|
+
*/
|
|
416
|
+
function escapeForAppleScript(str: string): string {
|
|
417
|
+
return str.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* Escape string for shell
|
|
422
|
+
*/
|
|
423
|
+
function escapeForShell(str: string): string {
|
|
424
|
+
return str.replace(/"/g, '\\"').replace(/\$/g, '\\$').replace(/`/g, '\\`');
|
|
425
|
+
}
|
|
426
|
+
|
|
427
|
+
/**
|
|
428
|
+
* Escape string for XML
|
|
429
|
+
*/
|
|
430
|
+
function escapeForXml(str: string): string {
|
|
431
|
+
return str
|
|
432
|
+
.replace(/&/g, '&')
|
|
433
|
+
.replace(/</g, '<')
|
|
434
|
+
.replace(/>/g, '>')
|
|
435
|
+
.replace(/"/g, '"')
|
|
436
|
+
.replace(/'/g, ''');
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
/**
|
|
440
|
+
* Escape string for PowerShell
|
|
441
|
+
*/
|
|
442
|
+
function escapeForPowerShell(str: string): string {
|
|
443
|
+
return str.replace(/"/g, '""');
|
|
444
|
+
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import boxen from 'boxen';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import gradient from 'gradient-string';
|
|
4
|
+
import {
|
|
5
|
+
selectAndValidateTarget,
|
|
6
|
+
previewDryRun,
|
|
7
|
+
installComponents,
|
|
8
|
+
type InitOptions,
|
|
9
|
+
} from './init-core.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Legacy init with full UI - used by setup command for backward compatibility
|
|
13
|
+
* The flow command uses init-core functions directly for better integration
|
|
14
|
+
*/
|
|
15
|
+
export async function runInit(options: InitOptions): Promise<void> {
|
|
16
|
+
// Create ASCII art title
|
|
17
|
+
const title = `
|
|
18
|
+
███████╗██╗ ██╗██╗ ██████╗ ██╗ ██╗██╗ ██╗ ███████╗██╗ ██████╗ ██╗ ██╗
|
|
19
|
+
██╔════╝╚██╗ ██╔╝██║ ██╔══██╗██║ ██║╚██╗██╔╝ ██╔════╝██║ ██╔═══██╗██║ ██║
|
|
20
|
+
███████╗ ╚████╔╝ ██║ ██████╔╝███████║ ╚███╔╝ █████╗ ██║ ██║ ██║██║ █╗ ██║
|
|
21
|
+
╚════██║ ╚██╔╝ ██║ ██╔═══╝ ██╔══██║ ██╔██╗ ██╔══╝ ██║ ██║ ██║██║███╗██║
|
|
22
|
+
███████║ ██║ ███████╗██║ ██║ ██║██╔╝ ██╗ ██║ ███████╗╚██████╔╝╚███╔███╔╝
|
|
23
|
+
╚══════╝ ╚═╝ ╚══════╝╚═╝ ╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚══════╝ ╚═════╝ ╚══╝╚══╝
|
|
24
|
+
`;
|
|
25
|
+
|
|
26
|
+
console.log(gradient(['cyan', 'blue'])(title));
|
|
27
|
+
console.log(chalk.dim.cyan(' Project Initialization\n'));
|
|
28
|
+
|
|
29
|
+
// Select and validate target using core function
|
|
30
|
+
const targetId = await selectAndValidateTarget(options);
|
|
31
|
+
|
|
32
|
+
// Dry run preview
|
|
33
|
+
if (options.dryRun) {
|
|
34
|
+
console.log(
|
|
35
|
+
boxen(
|
|
36
|
+
chalk.yellow('⚠ Dry Run Mode') + chalk.dim('\nNo changes will be made to your project'),
|
|
37
|
+
{
|
|
38
|
+
padding: 1,
|
|
39
|
+
margin: { top: 0, bottom: 1, left: 0, right: 0 },
|
|
40
|
+
borderStyle: 'round',
|
|
41
|
+
borderColor: 'yellow',
|
|
42
|
+
}
|
|
43
|
+
)
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
await previewDryRun(targetId, options);
|
|
47
|
+
|
|
48
|
+
console.log(
|
|
49
|
+
'\n' +
|
|
50
|
+
boxen(chalk.green.bold('✓ Dry run complete'), {
|
|
51
|
+
padding: { top: 0, bottom: 0, left: 2, right: 2 },
|
|
52
|
+
margin: 0,
|
|
53
|
+
borderStyle: 'round',
|
|
54
|
+
borderColor: 'green',
|
|
55
|
+
}) +
|
|
56
|
+
'\n'
|
|
57
|
+
);
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
console.log(chalk.cyan.bold('\n━━━ Installing Core Components ━━━\n'));
|
|
62
|
+
|
|
63
|
+
// Install components using core function
|
|
64
|
+
const result = await installComponents(targetId, options);
|
|
65
|
+
|
|
66
|
+
// Success summary
|
|
67
|
+
console.log(
|
|
68
|
+
'\n' +
|
|
69
|
+
boxen(
|
|
70
|
+
chalk.green.bold('✓ Setup complete!') +
|
|
71
|
+
'\n\n' +
|
|
72
|
+
chalk.dim(`Target: ${result.targetName}`) +
|
|
73
|
+
'\n\n' +
|
|
74
|
+
chalk.cyan('Ready to code with Sylphx Flow'),
|
|
75
|
+
{
|
|
76
|
+
padding: 1,
|
|
77
|
+
margin: 0,
|
|
78
|
+
borderStyle: 'round',
|
|
79
|
+
borderColor: 'green',
|
|
80
|
+
}
|
|
81
|
+
) +
|
|
82
|
+
'\n'
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* LEGACY: init command has been integrated into the flow command.
|
|
88
|
+
* Use `flow --init-only` instead of standalone `init` command.
|
|
89
|
+
*
|
|
90
|
+
* This export is kept for backward compatibility but will be removed in future versions.
|
|
91
|
+
* The runInit() function is the core implementation used by flow command.
|
|
92
|
+
*/
|