@sylphx/flow 1.8.0 → 1.8.2
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 +72 -0
- package/assets/output-styles/silent.md +145 -8
- package/assets/rules/core.md +19 -2
- package/package.json +2 -12
- package/src/commands/flow/execute.ts +470 -0
- package/src/commands/flow/index.ts +11 -0
- package/src/commands/flow/prompt.ts +35 -0
- package/src/commands/flow/setup.ts +312 -0
- package/src/commands/flow/targets.ts +18 -0
- package/src/commands/flow/types.ts +47 -0
- package/src/commands/flow-command.ts +18 -967
- package/src/commands/flow-orchestrator.ts +14 -5
- package/src/commands/hook-command.ts +1 -1
- package/src/commands/init-core.ts +12 -3
- package/src/commands/run-command.ts +1 -1
- package/src/config/rules.ts +1 -1
- package/src/core/error-handling.ts +1 -1
- package/src/core/loop-controller.ts +1 -1
- package/src/core/state-detector.ts +1 -1
- package/src/core/target-manager.ts +1 -1
- package/src/index.ts +1 -1
- package/src/shared/files/index.ts +1 -1
- package/src/shared/processing/index.ts +1 -1
- package/src/targets/claude-code.ts +3 -3
- package/src/targets/opencode.ts +3 -3
- package/src/utils/agent-enhancer.ts +2 -2
- package/src/utils/{mcp-config.ts → config/mcp-config.ts} +4 -4
- package/src/utils/{paths.ts → config/paths.ts} +1 -1
- package/src/utils/{settings.ts → config/settings.ts} +1 -1
- package/src/utils/{target-config.ts → config/target-config.ts} +5 -5
- package/src/utils/{target-utils.ts → config/target-utils.ts} +3 -3
- package/src/utils/display/banner.ts +25 -0
- package/src/utils/display/status.ts +55 -0
- package/src/utils/{file-operations.ts → files/file-operations.ts} +2 -2
- package/src/utils/files/jsonc.ts +36 -0
- package/src/utils/{sync-utils.ts → files/sync-utils.ts} +3 -3
- package/src/utils/index.ts +42 -61
- package/src/utils/version.ts +47 -0
- package/src/components/benchmark-monitor.tsx +0 -331
- package/src/components/reindex-progress.tsx +0 -261
- package/src/composables/functional/index.ts +0 -14
- package/src/composables/functional/useEnvironment.ts +0 -171
- package/src/composables/functional/useFileSystem.ts +0 -139
- package/src/composables/index.ts +0 -4
- package/src/composables/useEnv.ts +0 -13
- package/src/composables/useRuntimeConfig.ts +0 -27
- package/src/core/ai-sdk.ts +0 -603
- package/src/core/app-factory.ts +0 -381
- package/src/core/builtin-agents.ts +0 -9
- package/src/core/command-system.ts +0 -550
- package/src/core/config-system.ts +0 -550
- package/src/core/connection-pool.ts +0 -390
- package/src/core/di-container.ts +0 -155
- package/src/core/headless-display.ts +0 -96
- package/src/core/interfaces/index.ts +0 -22
- package/src/core/interfaces/repository.interface.ts +0 -91
- package/src/core/interfaces/service.interface.ts +0 -133
- package/src/core/interfaces.ts +0 -96
- package/src/core/result.ts +0 -351
- package/src/core/service-config.ts +0 -252
- package/src/core/session-service.ts +0 -121
- package/src/core/storage-factory.ts +0 -115
- package/src/core/stream-handler.ts +0 -288
- package/src/core/type-utils.ts +0 -427
- package/src/core/unified-storage.ts +0 -456
- package/src/core/validation/limit.ts +0 -46
- package/src/core/validation/query.ts +0 -20
- package/src/db/auto-migrate.ts +0 -322
- package/src/db/base-database-client.ts +0 -144
- package/src/db/cache-db.ts +0 -218
- package/src/db/cache-schema.ts +0 -75
- package/src/db/database.ts +0 -70
- package/src/db/index.ts +0 -252
- package/src/db/memory-db.ts +0 -153
- package/src/db/memory-schema.ts +0 -29
- package/src/db/schema.ts +0 -289
- package/src/db/session-repository.ts +0 -733
- package/src/domains/index.ts +0 -6
- package/src/domains/utilities/index.ts +0 -6
- package/src/domains/utilities/time/index.ts +0 -5
- package/src/domains/utilities/time/tools.ts +0 -291
- package/src/services/agent-service.ts +0 -273
- package/src/services/evaluation-service.ts +0 -271
- package/src/services/functional/evaluation-logic.ts +0 -296
- package/src/services/functional/file-processor.ts +0 -273
- package/src/services/functional/index.ts +0 -12
- package/src/services/memory.service.ts +0 -476
- package/src/types/api/batch.ts +0 -108
- package/src/types/api/errors.ts +0 -118
- package/src/types/api/index.ts +0 -55
- package/src/types/api/requests.ts +0 -76
- package/src/types/api/responses.ts +0 -180
- package/src/types/api/websockets.ts +0 -85
- package/src/types/benchmark.ts +0 -49
- package/src/types/database.types.ts +0 -510
- package/src/types/memory-types.ts +0 -63
- package/src/utils/advanced-tokenizer.ts +0 -191
- package/src/utils/ai-model-fetcher.ts +0 -19
- package/src/utils/async-file-operations.ts +0 -516
- package/src/utils/audio-player.ts +0 -345
- package/src/utils/codebase-helpers.ts +0 -211
- package/src/utils/console-ui.ts +0 -79
- package/src/utils/database-errors.ts +0 -140
- package/src/utils/debug-logger.ts +0 -49
- package/src/utils/file-scanner.ts +0 -259
- package/src/utils/help.ts +0 -20
- package/src/utils/immutable-cache.ts +0 -106
- package/src/utils/jsonc.ts +0 -158
- package/src/utils/memory-tui.ts +0 -414
- package/src/utils/models-dev.ts +0 -91
- package/src/utils/parallel-operations.ts +0 -487
- package/src/utils/process-manager.ts +0 -155
- package/src/utils/prompts.ts +0 -120
- package/src/utils/search-tool-builder.ts +0 -214
- package/src/utils/session-manager.ts +0 -168
- package/src/utils/session-title.ts +0 -87
- package/src/utils/simplified-errors.ts +0 -410
- package/src/utils/template-engine.ts +0 -94
- package/src/utils/test-audio.ts +0 -71
- package/src/utils/todo-context.ts +0 -46
- package/src/utils/token-counter.ts +0 -288
- /package/src/utils/{cli-output.ts → display/cli-output.ts} +0 -0
- /package/src/utils/{logger.ts → display/logger.ts} +0 -0
- /package/src/utils/{notifications.ts → display/notifications.ts} +0 -0
- /package/src/utils/{secret-utils.ts → security/secret-utils.ts} +0 -0
- /package/src/utils/{security.ts → security/security.ts} +0 -0
|
@@ -1,331 +0,0 @@
|
|
|
1
|
-
import path from 'node:path';
|
|
2
|
-
import { Box, render, Text, useApp } from 'ink';
|
|
3
|
-
import React from 'react';
|
|
4
|
-
import type { AgentData, InitialInfo } from '../types/benchmark.js';
|
|
5
|
-
|
|
6
|
-
export interface InkMonitorProps {
|
|
7
|
-
monitor: InkMonitor;
|
|
8
|
-
onComplete: () => void;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export class InkMonitor {
|
|
12
|
-
private agents: Map<string, AgentData> = new Map();
|
|
13
|
-
private isRunning = false;
|
|
14
|
-
private workspaceDirs: string[] = [];
|
|
15
|
-
private uiInstance?: any;
|
|
16
|
-
private listeners = new Set<() => void>();
|
|
17
|
-
private initialInfo: InitialInfo;
|
|
18
|
-
|
|
19
|
-
constructor(initialInfo: InitialInfo) {
|
|
20
|
-
this.initialInfo = initialInfo;
|
|
21
|
-
this.setupSignalHandlers();
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// Subscribe to changes - proper React pattern
|
|
25
|
-
subscribe(listener: () => void) {
|
|
26
|
-
this.listeners.add(listener);
|
|
27
|
-
return () => {
|
|
28
|
-
this.listeners.delete(listener);
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
private triggerUpdate() {
|
|
33
|
-
for (const listener of this.listeners) {
|
|
34
|
-
listener();
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Getters for React component
|
|
39
|
-
getAgents() {
|
|
40
|
-
return this.agents;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getWorkspaceDirs() {
|
|
44
|
-
return this.workspaceDirs;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getInitialInfo() {
|
|
48
|
-
return this.initialInfo;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
start() {
|
|
52
|
-
this.isRunning = true;
|
|
53
|
-
|
|
54
|
-
// Force Ink to work by ensuring proper terminal detection
|
|
55
|
-
process.stdout.isTTY = true;
|
|
56
|
-
process.stderr.isTTY = true;
|
|
57
|
-
|
|
58
|
-
const uiInstance = render(
|
|
59
|
-
<BenchmarkMonitor
|
|
60
|
-
monitor={this}
|
|
61
|
-
onComplete={() => {
|
|
62
|
-
this.stop();
|
|
63
|
-
}}
|
|
64
|
-
/>,
|
|
65
|
-
{
|
|
66
|
-
// Enable Ink's full-screen mode with proper terminal control
|
|
67
|
-
debug: false,
|
|
68
|
-
patchConsole: true, // Let Ink control console output
|
|
69
|
-
exitOnCtrlC: false, // We'll handle CtrlC ourselves
|
|
70
|
-
}
|
|
71
|
-
);
|
|
72
|
-
|
|
73
|
-
this.uiInstance = uiInstance;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
setWorkspaceDirs(dirs: string[]) {
|
|
77
|
-
this.workspaceDirs = dirs;
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
addAgent(name: string) {
|
|
81
|
-
this.agents.set(name, {
|
|
82
|
-
status: 'idle',
|
|
83
|
-
output: [],
|
|
84
|
-
startTime: undefined,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
updateAgentStatus(name: string, status: AgentData['status']) {
|
|
89
|
-
const agent = this.agents.get(name);
|
|
90
|
-
if (agent) {
|
|
91
|
-
agent.status = status;
|
|
92
|
-
if (status === 'running' && !agent.startTime) {
|
|
93
|
-
agent.startTime = Date.now();
|
|
94
|
-
} else if (status === 'completed' || status === 'error') {
|
|
95
|
-
agent.endTime = Date.now();
|
|
96
|
-
}
|
|
97
|
-
// Trigger UI update through subscriber pattern
|
|
98
|
-
this.triggerUpdate();
|
|
99
|
-
}
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
setAgentPid(name: string, pid: number) {
|
|
103
|
-
const agent = this.agents.get(name);
|
|
104
|
-
if (agent) {
|
|
105
|
-
agent.pid = pid;
|
|
106
|
-
this.triggerUpdate();
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
addAgentOutput(name: string, output: string) {
|
|
111
|
-
const agent = this.agents.get(name);
|
|
112
|
-
if (agent) {
|
|
113
|
-
// Remove ANSI escape sequences that interfere with Ink
|
|
114
|
-
const cleanedOutput = output.replace(/\x1b\[[0-9;]*[A-Za-z]/g, '');
|
|
115
|
-
const lines = cleanedOutput.split('\n').filter((line) => line.trim());
|
|
116
|
-
agent.output = [...agent.output.slice(-20), ...lines]; // Keep last 20 lines
|
|
117
|
-
// Trigger UI update through subscriber pattern
|
|
118
|
-
this.triggerUpdate();
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
stop() {
|
|
123
|
-
this.isRunning = false;
|
|
124
|
-
if (this.uiInstance) {
|
|
125
|
-
this.uiInstance.unmount();
|
|
126
|
-
this.uiInstance = undefined;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private setupSignalHandlers() {
|
|
131
|
-
const shutdown = async (_signal: string) => {
|
|
132
|
-
if (!this.isRunning) {
|
|
133
|
-
return;
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
this.stop();
|
|
137
|
-
process.exit(0);
|
|
138
|
-
};
|
|
139
|
-
|
|
140
|
-
process.on('SIGINT', () => shutdown('SIGINT'));
|
|
141
|
-
process.on('SIGTERM', () => shutdown('SIGTERM'));
|
|
142
|
-
process.on('SIGHUP', () => shutdown('SIGHUP'));
|
|
143
|
-
}
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
// React Ink component for efficient real-time monitoring
|
|
147
|
-
const BenchmarkMonitor: React.FC<InkMonitorProps> = ({ monitor, onComplete }) => {
|
|
148
|
-
const { exit } = useApp();
|
|
149
|
-
|
|
150
|
-
// Subscribe to monitor changes using proper React state
|
|
151
|
-
const [_updateTrigger, setUpdateTrigger] = React.useState(0);
|
|
152
|
-
const [flashState, setFlashState] = React.useState(true);
|
|
153
|
-
|
|
154
|
-
React.useEffect(() => {
|
|
155
|
-
// Subscribe to the monitor's change notifications
|
|
156
|
-
const unsubscribe = monitor.subscribe(() => {
|
|
157
|
-
setUpdateTrigger((prev) => prev + 1);
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
return unsubscribe;
|
|
161
|
-
}, [monitor]);
|
|
162
|
-
|
|
163
|
-
// Flashing effect for running status + force frequent updates for real-time output
|
|
164
|
-
React.useEffect(() => {
|
|
165
|
-
const interval = setInterval(() => {
|
|
166
|
-
setFlashState((prev) => !prev);
|
|
167
|
-
setUpdateTrigger((prev) => prev + 1); // Force update every 800ms to refresh output display
|
|
168
|
-
}, 800); // Flash every 800ms for slow flashing
|
|
169
|
-
|
|
170
|
-
return () => clearInterval(interval);
|
|
171
|
-
}, []);
|
|
172
|
-
|
|
173
|
-
// Auto-exit when all agents complete
|
|
174
|
-
React.useEffect(() => {
|
|
175
|
-
const agents = monitor.getAgents();
|
|
176
|
-
const allCompleted = Array.from(agents.values()).every(
|
|
177
|
-
(agent) => agent.status === 'completed' || agent.status === 'error'
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
if (allCompleted && agents.size > 0) {
|
|
181
|
-
onComplete();
|
|
182
|
-
exit();
|
|
183
|
-
}
|
|
184
|
-
}, [monitor, onComplete, exit]);
|
|
185
|
-
|
|
186
|
-
const status = React.useMemo(() => {
|
|
187
|
-
const agents = monitor.getAgents();
|
|
188
|
-
return Array.from(agents.entries()).map(([name, agent]) => {
|
|
189
|
-
let runtime = 0;
|
|
190
|
-
if (agent.startTime) {
|
|
191
|
-
if (agent.endTime) {
|
|
192
|
-
runtime = Math.floor((agent.endTime - agent.startTime) / 1000);
|
|
193
|
-
} else if (agent.status === 'running') {
|
|
194
|
-
// Don't calculate runtime dynamically - store it in the agent data
|
|
195
|
-
runtime = agent.startTime ? Math.floor((Date.now() - agent.startTime) / 1000) : 0;
|
|
196
|
-
}
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Determine status display with flashing green dot for running
|
|
200
|
-
let statusDisplay = '';
|
|
201
|
-
let statusColor = '';
|
|
202
|
-
|
|
203
|
-
if (agent.status === 'running') {
|
|
204
|
-
statusDisplay = flashState ? '●' : ' ';
|
|
205
|
-
statusColor = 'green';
|
|
206
|
-
} else if (agent.status === 'completed') {
|
|
207
|
-
statusDisplay = '✓';
|
|
208
|
-
statusColor = 'green';
|
|
209
|
-
} else if (agent.status === 'error') {
|
|
210
|
-
statusDisplay = '✗';
|
|
211
|
-
statusColor = 'red';
|
|
212
|
-
} else {
|
|
213
|
-
statusDisplay = '◯';
|
|
214
|
-
statusColor = 'gray';
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
// Show actual runtime for agents
|
|
218
|
-
let runtimeText = '';
|
|
219
|
-
if (agent.startTime && agent.status === 'running') {
|
|
220
|
-
runtimeText = `${runtime}s`;
|
|
221
|
-
} else if (agent.startTime) {
|
|
222
|
-
runtimeText = `${runtime}s`;
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
// Get last output lines (show up to 5 most recent lines)
|
|
226
|
-
let lastOutputLines: string[] = [];
|
|
227
|
-
if (agent.output.length > 0) {
|
|
228
|
-
// Get the last 5 lines - simpler filtering to ensure real-time output shows
|
|
229
|
-
const recentLines = agent.output.slice(-5);
|
|
230
|
-
lastOutputLines = recentLines
|
|
231
|
-
.filter((line) => line && line.trim().length > 0)
|
|
232
|
-
.map((line) => {
|
|
233
|
-
const cleanLine = line.trim();
|
|
234
|
-
return cleanLine.length > 150 ? `${cleanLine.substring(0, 150)}...` : cleanLine;
|
|
235
|
-
});
|
|
236
|
-
}
|
|
237
|
-
|
|
238
|
-
// Show placeholder text only if no actual output exists
|
|
239
|
-
if (lastOutputLines.length === 0) {
|
|
240
|
-
if (agent.status === 'running') {
|
|
241
|
-
lastOutputLines.push('(working...)');
|
|
242
|
-
} else if (agent.status === 'idle') {
|
|
243
|
-
lastOutputLines.push('(waiting to start...)');
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return {
|
|
248
|
-
name,
|
|
249
|
-
statusDisplay,
|
|
250
|
-
statusColor,
|
|
251
|
-
status: agent.status.toUpperCase(),
|
|
252
|
-
runtime: runtimeText,
|
|
253
|
-
lastOutput: lastOutputLines,
|
|
254
|
-
pid: agent.pid,
|
|
255
|
-
};
|
|
256
|
-
});
|
|
257
|
-
}, [monitor, flashState]);
|
|
258
|
-
|
|
259
|
-
const workspaceDirs = monitor.getWorkspaceDirs();
|
|
260
|
-
const initialInfo = monitor.getInitialInfo();
|
|
261
|
-
|
|
262
|
-
// Create a mapping from agent name to workspace directory
|
|
263
|
-
const agentWorkspaceMap = new Map<string, string>();
|
|
264
|
-
for (const dir of workspaceDirs) {
|
|
265
|
-
const agentName = path.basename(dir);
|
|
266
|
-
agentWorkspaceMap.set(agentName, dir);
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
return (
|
|
270
|
-
<Box flexDirection="column" padding={1}>
|
|
271
|
-
{/* Initial Information Section */}
|
|
272
|
-
<Box marginBottom={1} flexDirection="column">
|
|
273
|
-
<Text bold>Agent Benchmark Monitor</Text>
|
|
274
|
-
{initialInfo?.initialInfo && (
|
|
275
|
-
<>
|
|
276
|
-
<Text color="gray">Output: {initialInfo.initialInfo.outputDir}</Text>
|
|
277
|
-
<Text color="gray">
|
|
278
|
-
Task:{' '}
|
|
279
|
-
{initialInfo.initialInfo.taskFile
|
|
280
|
-
? path.basename(initialInfo.initialInfo.taskFile)
|
|
281
|
-
: 'Unknown'}
|
|
282
|
-
</Text>
|
|
283
|
-
<Text color="gray">Agents: {initialInfo.initialInfo.agentCount}</Text>
|
|
284
|
-
<Text color="gray">
|
|
285
|
-
Concurrency: {initialInfo.initialInfo.concurrency}, Delay:{' '}
|
|
286
|
-
{initialInfo.initialInfo.delay}s
|
|
287
|
-
</Text>
|
|
288
|
-
</>
|
|
289
|
-
)}
|
|
290
|
-
{(!initialInfo || !initialInfo.initialInfo) && (
|
|
291
|
-
<Text color="gray">Initializing benchmark...</Text>
|
|
292
|
-
)}
|
|
293
|
-
</Box>
|
|
294
|
-
|
|
295
|
-
<Box flexDirection="column">
|
|
296
|
-
{status.map((agent) => (
|
|
297
|
-
<Box key={agent.name} marginBottom={1} flexDirection="column">
|
|
298
|
-
<Box>
|
|
299
|
-
<Text bold>
|
|
300
|
-
<Text color={agent.statusColor}>{agent.statusDisplay}</Text> {agent.name}
|
|
301
|
-
{agent.runtime ? ` ${agent.runtime}` : ''}
|
|
302
|
-
{agent.pid ? <Text color="gray"> (pid: {agent.pid})</Text> : ''}
|
|
303
|
-
</Text>
|
|
304
|
-
</Box>
|
|
305
|
-
|
|
306
|
-
{/* Show workspace directory under each agent */}
|
|
307
|
-
{agentWorkspaceMap.has(agent.name) && (
|
|
308
|
-
<Box paddingLeft={2}>
|
|
309
|
-
<Text color="gray">{agentWorkspaceMap.get(agent.name)}</Text>
|
|
310
|
-
</Box>
|
|
311
|
-
)}
|
|
312
|
-
|
|
313
|
-
{agent.lastOutput && agent.lastOutput.length > 0 && (
|
|
314
|
-
<Box paddingLeft={4} flexDirection="column" paddingBottom={1} marginTop={1}>
|
|
315
|
-
{agent.lastOutput.map((line, index) => (
|
|
316
|
-
<Text key={index} color="gray">
|
|
317
|
-
{line}
|
|
318
|
-
</Text>
|
|
319
|
-
))}
|
|
320
|
-
</Box>
|
|
321
|
-
)}
|
|
322
|
-
</Box>
|
|
323
|
-
))}
|
|
324
|
-
</Box>
|
|
325
|
-
|
|
326
|
-
<Box marginTop={1}>
|
|
327
|
-
<Text color="gray">Press Ctrl+C to exit</Text>
|
|
328
|
-
</Box>
|
|
329
|
-
</Box>
|
|
330
|
-
);
|
|
331
|
-
};
|
|
@@ -1,261 +0,0 @@
|
|
|
1
|
-
import { Box, render, Text, useApp } from 'ink';
|
|
2
|
-
import React from 'react';
|
|
3
|
-
|
|
4
|
-
export interface ReindexProgressData {
|
|
5
|
-
current: number;
|
|
6
|
-
total: number;
|
|
7
|
-
fileName: string;
|
|
8
|
-
status: 'processing' | 'completed' | 'skipped';
|
|
9
|
-
phase: 'tokenizing' | 'calculating' | 'completed';
|
|
10
|
-
mode?: 'tfidf-only' | 'semantic';
|
|
11
|
-
stats?: {
|
|
12
|
-
documentsProcessed: number;
|
|
13
|
-
uniqueTerms: number;
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export class ReindexMonitor {
|
|
18
|
-
private progress: ReindexProgressData = {
|
|
19
|
-
current: 0,
|
|
20
|
-
total: 0,
|
|
21
|
-
fileName: '',
|
|
22
|
-
status: 'processing',
|
|
23
|
-
phase: 'tokenizing',
|
|
24
|
-
};
|
|
25
|
-
private uiInstance?: any;
|
|
26
|
-
private listeners = new Set<() => void>();
|
|
27
|
-
private startTime = Date.now();
|
|
28
|
-
private lastPercentage = -1; // Track last rendered percentage
|
|
29
|
-
|
|
30
|
-
subscribe(listener: () => void) {
|
|
31
|
-
this.listeners.add(listener);
|
|
32
|
-
return () => {
|
|
33
|
-
this.listeners.delete(listener);
|
|
34
|
-
};
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
private triggerUpdate() {
|
|
38
|
-
for (const listener of this.listeners) {
|
|
39
|
-
listener();
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
getProgress() {
|
|
44
|
-
return this.progress;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getElapsedTime() {
|
|
48
|
-
return Math.floor((Date.now() - this.startTime) / 1000);
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
updateProgress(data: Partial<ReindexProgressData>) {
|
|
52
|
-
this.progress = { ...this.progress, ...data };
|
|
53
|
-
|
|
54
|
-
// Only trigger UI update when percentage changes (to reduce render overhead)
|
|
55
|
-
const currentPercentage =
|
|
56
|
-
this.progress.total > 0 ? Math.floor((this.progress.current / this.progress.total) * 100) : 0;
|
|
57
|
-
|
|
58
|
-
// Always trigger on phase change or when percentage actually changes
|
|
59
|
-
if (data.phase || currentPercentage !== this.lastPercentage) {
|
|
60
|
-
this.lastPercentage = currentPercentage;
|
|
61
|
-
this.triggerUpdate();
|
|
62
|
-
}
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
start(total: number) {
|
|
66
|
-
this.progress = {
|
|
67
|
-
current: 0,
|
|
68
|
-
total,
|
|
69
|
-
fileName: '',
|
|
70
|
-
status: 'processing',
|
|
71
|
-
phase: 'tokenizing',
|
|
72
|
-
};
|
|
73
|
-
this.startTime = Date.now();
|
|
74
|
-
|
|
75
|
-
// Force Ink to work by ensuring proper terminal detection
|
|
76
|
-
process.stdout.isTTY = true;
|
|
77
|
-
process.stderr.isTTY = true;
|
|
78
|
-
|
|
79
|
-
const uiInstance = render(<ReindexProgress monitor={this} />, {
|
|
80
|
-
debug: false,
|
|
81
|
-
patchConsole: false, // Don't patch console to allow direct stderr output
|
|
82
|
-
exitOnCtrlC: false,
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
this.uiInstance = uiInstance;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
stop() {
|
|
89
|
-
if (this.uiInstance) {
|
|
90
|
-
this.uiInstance.unmount();
|
|
91
|
-
this.uiInstance = undefined;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
interface ReindexProgressProps {
|
|
97
|
-
monitor: ReindexMonitor;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const ReindexProgress: React.FC<ReindexProgressProps> = ({ monitor }) => {
|
|
101
|
-
const { exit } = useApp();
|
|
102
|
-
// Use React state instead of reading from monitor directly
|
|
103
|
-
const [progress, setProgress] = React.useState<ReindexProgressData>(monitor.getProgress());
|
|
104
|
-
const [elapsedTime, setElapsedTime] = React.useState(0);
|
|
105
|
-
|
|
106
|
-
// Subscribe to progress updates and update React state
|
|
107
|
-
React.useEffect(() => {
|
|
108
|
-
const unsubscribe = monitor.subscribe(() => {
|
|
109
|
-
const newProgress = monitor.getProgress();
|
|
110
|
-
setProgress(newProgress);
|
|
111
|
-
setElapsedTime(monitor.getElapsedTime());
|
|
112
|
-
|
|
113
|
-
// Show progress updates (Ink UI is too slow to render, so we use console.error for immediate feedback)
|
|
114
|
-
if (newProgress.current % 20 === 0) {
|
|
115
|
-
const percentage = Math.floor((newProgress.current / newProgress.total) * 100);
|
|
116
|
-
console.error(`📦 ${newProgress.current}/${newProgress.total} files (${percentage}%)`);
|
|
117
|
-
}
|
|
118
|
-
});
|
|
119
|
-
return unsubscribe;
|
|
120
|
-
}, [monitor]);
|
|
121
|
-
|
|
122
|
-
// Update elapsed time every second (not every 100ms)
|
|
123
|
-
React.useEffect(() => {
|
|
124
|
-
const interval = setInterval(() => {
|
|
125
|
-
setElapsedTime(monitor.getElapsedTime());
|
|
126
|
-
}, 1000); // Every 1 second instead of 100ms
|
|
127
|
-
return () => clearInterval(interval);
|
|
128
|
-
}, [monitor]);
|
|
129
|
-
const percentage = progress.total > 0 ? Math.floor((progress.current / progress.total) * 100) : 0;
|
|
130
|
-
|
|
131
|
-
// Calculate estimated time remaining
|
|
132
|
-
const avgTimePerFile = progress.current > 0 ? elapsedTime / progress.current : 0;
|
|
133
|
-
const remainingFiles = progress.total - progress.current;
|
|
134
|
-
const estimatedRemaining = Math.ceil(avgTimePerFile * remainingFiles);
|
|
135
|
-
|
|
136
|
-
// Build progress bar
|
|
137
|
-
const barWidth = 40;
|
|
138
|
-
const filledWidth = Math.floor((percentage / 100) * barWidth);
|
|
139
|
-
const progressBar = '█'.repeat(filledWidth) + '░'.repeat(barWidth - filledWidth);
|
|
140
|
-
|
|
141
|
-
// Auto-exit when completed
|
|
142
|
-
React.useEffect(() => {
|
|
143
|
-
if (progress.phase === 'completed') {
|
|
144
|
-
setTimeout(() => {
|
|
145
|
-
exit();
|
|
146
|
-
}, 1000);
|
|
147
|
-
}
|
|
148
|
-
}, [progress.phase, exit]);
|
|
149
|
-
|
|
150
|
-
return (
|
|
151
|
-
<Box flexDirection="column" padding={1}>
|
|
152
|
-
{/* Header */}
|
|
153
|
-
<Box marginBottom={1}>
|
|
154
|
-
<Text bold color="cyan">
|
|
155
|
-
🔤 Reindexing Codebase
|
|
156
|
-
</Text>
|
|
157
|
-
{progress.mode === 'tfidf-only' && (
|
|
158
|
-
<Text color="yellow" dimColor>
|
|
159
|
-
{' '}
|
|
160
|
-
(TF-IDF mode - no API key)
|
|
161
|
-
</Text>
|
|
162
|
-
)}
|
|
163
|
-
{progress.mode === 'semantic' && (
|
|
164
|
-
<Text color="green" dimColor>
|
|
165
|
-
{' '}
|
|
166
|
-
(Semantic search enabled)
|
|
167
|
-
</Text>
|
|
168
|
-
)}
|
|
169
|
-
</Box>
|
|
170
|
-
|
|
171
|
-
{/* API key hint */}
|
|
172
|
-
{progress.mode === 'tfidf-only' && progress.phase === 'tokenizing' && (
|
|
173
|
-
<Box marginBottom={1}>
|
|
174
|
-
<Text color="gray" dimColor>
|
|
175
|
-
💡 Tip: Set OPENAI_API_KEY for semantic search with embeddings
|
|
176
|
-
</Text>
|
|
177
|
-
</Box>
|
|
178
|
-
)}
|
|
179
|
-
|
|
180
|
-
{/* Progress bar */}
|
|
181
|
-
<Box marginBottom={1}>
|
|
182
|
-
<Box width={barWidth + 2} marginRight={2}>
|
|
183
|
-
<Text color="blue">{progressBar}</Text>
|
|
184
|
-
</Box>
|
|
185
|
-
<Text color="cyan" bold>
|
|
186
|
-
{percentage}%
|
|
187
|
-
</Text>
|
|
188
|
-
</Box>
|
|
189
|
-
|
|
190
|
-
{/* Current file and stats */}
|
|
191
|
-
<Box flexDirection="column" marginBottom={1}>
|
|
192
|
-
<Box>
|
|
193
|
-
<Text color="gray">Progress: </Text>
|
|
194
|
-
<Text color="white">
|
|
195
|
-
{progress.current}/{progress.total} files
|
|
196
|
-
</Text>
|
|
197
|
-
</Box>
|
|
198
|
-
|
|
199
|
-
{progress.phase === 'tokenizing' && progress.fileName && (
|
|
200
|
-
<Box>
|
|
201
|
-
<Text color="gray">Current: </Text>
|
|
202
|
-
<Text color="yellow">{progress.fileName}</Text>
|
|
203
|
-
</Box>
|
|
204
|
-
)}
|
|
205
|
-
|
|
206
|
-
{progress.phase === 'calculating' && (
|
|
207
|
-
<Box>
|
|
208
|
-
<Text color="cyan">⚡ Calculating TF-IDF scores...</Text>
|
|
209
|
-
</Box>
|
|
210
|
-
)}
|
|
211
|
-
</Box>
|
|
212
|
-
|
|
213
|
-
{/* Timing info */}
|
|
214
|
-
<Box flexDirection="column" marginBottom={1}>
|
|
215
|
-
<Box>
|
|
216
|
-
<Text color="gray">Elapsed: </Text>
|
|
217
|
-
<Text color="white">{formatTime(elapsedTime)}</Text>
|
|
218
|
-
</Box>
|
|
219
|
-
|
|
220
|
-
{progress.phase === 'tokenizing' && remainingFiles > 0 && (
|
|
221
|
-
<Box>
|
|
222
|
-
<Text color="gray">Estimated: </Text>
|
|
223
|
-
<Text color="white">{formatTime(estimatedRemaining)} remaining</Text>
|
|
224
|
-
</Box>
|
|
225
|
-
)}
|
|
226
|
-
</Box>
|
|
227
|
-
|
|
228
|
-
{/* Final stats when completed */}
|
|
229
|
-
{progress.phase === 'completed' && progress.stats && (
|
|
230
|
-
<Box flexDirection="column" marginTop={1}>
|
|
231
|
-
<Text color="green" bold>
|
|
232
|
-
✓ Indexing Complete!
|
|
233
|
-
</Text>
|
|
234
|
-
<Box marginTop={1}>
|
|
235
|
-
<Text color="gray">Documents: </Text>
|
|
236
|
-
<Text color="white">{progress.stats.documentsProcessed}</Text>
|
|
237
|
-
</Box>
|
|
238
|
-
<Box>
|
|
239
|
-
<Text color="gray">Unique terms: </Text>
|
|
240
|
-
<Text color="white">{progress.stats.uniqueTerms.toLocaleString()}</Text>
|
|
241
|
-
</Box>
|
|
242
|
-
<Box>
|
|
243
|
-
<Text color="gray">Total time: </Text>
|
|
244
|
-
<Text color="white">{formatTime(elapsedTime)}</Text>
|
|
245
|
-
</Box>
|
|
246
|
-
</Box>
|
|
247
|
-
)}
|
|
248
|
-
</Box>
|
|
249
|
-
);
|
|
250
|
-
};
|
|
251
|
-
|
|
252
|
-
function formatTime(seconds: number): string {
|
|
253
|
-
if (seconds < 60) {
|
|
254
|
-
return `${seconds}s`;
|
|
255
|
-
}
|
|
256
|
-
const minutes = Math.floor(seconds / 60);
|
|
257
|
-
const secs = seconds % 60;
|
|
258
|
-
return `${minutes}m ${secs}s`;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
export { ReindexProgress };
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Functional composables
|
|
3
|
-
* Reusable abstractions following functional principles
|
|
4
|
-
*
|
|
5
|
-
* DESIGN RATIONALE:
|
|
6
|
-
* - Pure functions where possible
|
|
7
|
-
* - Side effects isolated and explicit
|
|
8
|
-
* - Result/Option types for error handling
|
|
9
|
-
* - Type-safe operations
|
|
10
|
-
* - Composable utilities
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
|
-
export * from './useEnvironment.js';
|
|
14
|
-
export * from './useFileSystem.js';
|