@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,133 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Service interfaces for dependency inversion
|
|
3
|
+
* Core service contracts
|
|
4
|
+
*
|
|
5
|
+
* DESIGN RATIONALE:
|
|
6
|
+
* - Depend on abstractions, not implementations
|
|
7
|
+
* - Clear contracts for services
|
|
8
|
+
* - Enables dependency injection
|
|
9
|
+
* - Facilitates testing with mocks
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import type { AppError } from '../functional/error-types.js';
|
|
13
|
+
import type { Result } from '../functional/result.js';
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Logger interface
|
|
17
|
+
*/
|
|
18
|
+
export interface ILogger {
|
|
19
|
+
debug(message: string, ...args: any[]): void;
|
|
20
|
+
info(message: string, ...args: any[]): void;
|
|
21
|
+
warn(message: string, ...args: any[]): void;
|
|
22
|
+
error(message: string, ...args: any[]): void;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Configuration service interface
|
|
27
|
+
*/
|
|
28
|
+
export interface IConfigService {
|
|
29
|
+
/**
|
|
30
|
+
* Get configuration value
|
|
31
|
+
*/
|
|
32
|
+
get<T>(key: string): Result<T, AppError>;
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Get configuration value with default
|
|
36
|
+
*/
|
|
37
|
+
getOrDefault<T>(key: string, defaultValue: T): T;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Set configuration value
|
|
41
|
+
*/
|
|
42
|
+
set<T>(key: string, value: T): Result<void, AppError>;
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Check if configuration key exists
|
|
46
|
+
*/
|
|
47
|
+
has(key: string): boolean;
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Load configuration from file
|
|
51
|
+
*/
|
|
52
|
+
load(path: string): Promise<Result<void, AppError>>;
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Save configuration to file
|
|
56
|
+
*/
|
|
57
|
+
save(path: string): Promise<Result<void, AppError>>;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* File service interface
|
|
62
|
+
*/
|
|
63
|
+
export interface IFileService {
|
|
64
|
+
/**
|
|
65
|
+
* Read file contents
|
|
66
|
+
*/
|
|
67
|
+
read(path: string): Promise<Result<string, AppError>>;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Write file contents
|
|
71
|
+
*/
|
|
72
|
+
write(path: string, content: string): Promise<Result<void, AppError>>;
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if file exists
|
|
76
|
+
*/
|
|
77
|
+
exists(path: string): Promise<Result<boolean, AppError>>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Delete file
|
|
81
|
+
*/
|
|
82
|
+
delete(path: string): Promise<Result<void, AppError>>;
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* List files in directory
|
|
86
|
+
*/
|
|
87
|
+
list(path: string): Promise<Result<string[], AppError>>;
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Create directory
|
|
91
|
+
*/
|
|
92
|
+
createDir(path: string, recursive?: boolean): Promise<Result<void, AppError>>;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Validation service interface
|
|
97
|
+
*/
|
|
98
|
+
export interface IValidationService<T> {
|
|
99
|
+
/**
|
|
100
|
+
* Validate data
|
|
101
|
+
*/
|
|
102
|
+
validate(data: unknown): Result<T, AppError>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Validate and return errors
|
|
106
|
+
*/
|
|
107
|
+
validateWithErrors(data: unknown): Result<T, string[]>;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Event emitter interface for event-driven architecture
|
|
112
|
+
*/
|
|
113
|
+
export interface IEventEmitter<EventMap extends Record<string, any>> {
|
|
114
|
+
/**
|
|
115
|
+
* Emit an event
|
|
116
|
+
*/
|
|
117
|
+
emit<K extends keyof EventMap>(event: K, data: EventMap[K]): void;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Subscribe to an event
|
|
121
|
+
*/
|
|
122
|
+
on<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void): () => void;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Subscribe to an event once
|
|
126
|
+
*/
|
|
127
|
+
once<K extends keyof EventMap>(event: K, handler: (data: EventMap[K]) => void): () => void;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Remove all listeners for an event
|
|
131
|
+
*/
|
|
132
|
+
removeAllListeners<K extends keyof EventMap>(event: K): void;
|
|
133
|
+
}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core service interfaces for dependency injection
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Logger interface
|
|
6
|
+
export interface ILogger {
|
|
7
|
+
info(message: string, ...args: unknown[]): void;
|
|
8
|
+
warn(message: string, ...args: unknown[]): void;
|
|
9
|
+
error(message: string, error?: Error | unknown, ...args: unknown[]): void;
|
|
10
|
+
debug(message: string, ...args: unknown[]): void;
|
|
11
|
+
success(message: string, ...args: unknown[]): void;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Configuration interface
|
|
15
|
+
export interface IConfiguration {
|
|
16
|
+
get<T = unknown>(key: string, defaultValue?: T): T;
|
|
17
|
+
getRequired<T = unknown>(key: string): T;
|
|
18
|
+
has(key: string): boolean;
|
|
19
|
+
set(key: string, value: unknown): void;
|
|
20
|
+
reload(): Promise<void>;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Database connection interface
|
|
24
|
+
export interface IDatabaseConnection {
|
|
25
|
+
connect(): Promise<void>;
|
|
26
|
+
disconnect(): Promise<void>;
|
|
27
|
+
healthCheck(): Promise<{ healthy: boolean; error?: string }>;
|
|
28
|
+
execute(sql: string, params?: unknown[]): Promise<DatabaseQueryResult>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Database query result
|
|
32
|
+
export interface DatabaseQueryResult {
|
|
33
|
+
rows: Record<string, unknown>[];
|
|
34
|
+
rowCount: number;
|
|
35
|
+
command: string;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// Storage interface for memory and cache
|
|
39
|
+
export interface IStorage<T = unknown> {
|
|
40
|
+
initialize(): Promise<void>;
|
|
41
|
+
get(key: string, namespace?: string): Promise<T | null>;
|
|
42
|
+
set(key: string, value: T, namespace?: string): Promise<void>;
|
|
43
|
+
delete(key: string, namespace?: string): Promise<void>;
|
|
44
|
+
clear(namespace?: string): Promise<void>;
|
|
45
|
+
list(namespace?: string): Promise<string[]>;
|
|
46
|
+
healthCheck(): Promise<{ healthy: boolean; error?: string }>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Search service interface
|
|
50
|
+
export interface ISearchService {
|
|
51
|
+
initialize(): Promise<void>;
|
|
52
|
+
searchCodebase(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
53
|
+
searchKnowledge(query: string, options?: SearchOptions): Promise<SearchResult[]>;
|
|
54
|
+
getStatus(): Promise<SearchServiceStatus>;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// Search options
|
|
58
|
+
export interface SearchOptions {
|
|
59
|
+
limit?: number;
|
|
60
|
+
offset?: number;
|
|
61
|
+
filters?: Record<string, unknown>;
|
|
62
|
+
sortBy?: string;
|
|
63
|
+
sortOrder?: 'asc' | 'desc';
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// Search result
|
|
67
|
+
export interface SearchResult {
|
|
68
|
+
id: string;
|
|
69
|
+
content: string;
|
|
70
|
+
score: number;
|
|
71
|
+
metadata: Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Search service status
|
|
75
|
+
export interface SearchServiceStatus {
|
|
76
|
+
indexed: number;
|
|
77
|
+
total: number;
|
|
78
|
+
lastIndexed: string;
|
|
79
|
+
healthy: boolean;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Target manager interface
|
|
83
|
+
export interface ITargetManager {
|
|
84
|
+
getTarget(id: string): Target | null;
|
|
85
|
+
getAllTargets(): Target[];
|
|
86
|
+
registerTarget(target: Target): void;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Target definition
|
|
90
|
+
export interface Target {
|
|
91
|
+
id: string;
|
|
92
|
+
name: string;
|
|
93
|
+
type: string;
|
|
94
|
+
config: Record<string, unknown>;
|
|
95
|
+
enabled: boolean;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Embedding provider interface
|
|
99
|
+
export interface IEmbeddingProvider {
|
|
100
|
+
name: string;
|
|
101
|
+
embed(text: string): Promise<number[]>;
|
|
102
|
+
isAvailable(): Promise<boolean>;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// MCP service interface
|
|
106
|
+
export interface IMCPService {
|
|
107
|
+
initialize(): Promise<void>;
|
|
108
|
+
installServers(serverIds: string[], options?: MCPServerInstallOptions): Promise<void>;
|
|
109
|
+
getAvailableServers(): MCPServerInfo[];
|
|
110
|
+
getInstalledServers(): MCPServerInfo[];
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// MCP server installation options
|
|
114
|
+
export interface MCPServerInstallOptions {
|
|
115
|
+
force?: boolean;
|
|
116
|
+
autoStart?: boolean;
|
|
117
|
+
config?: Record<string, unknown>;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// MCP server information
|
|
121
|
+
export interface MCPServerInfo {
|
|
122
|
+
id: string;
|
|
123
|
+
name: string;
|
|
124
|
+
version: string;
|
|
125
|
+
description?: string;
|
|
126
|
+
installed: boolean;
|
|
127
|
+
enabled: boolean;
|
|
128
|
+
config?: Record<string, unknown>;
|
|
129
|
+
}
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Loop Controller - Simple continuous execution
|
|
3
|
+
*
|
|
4
|
+
* Core concept: Keep executing the same task with context persistence
|
|
5
|
+
* - First run: Fresh start
|
|
6
|
+
* - Subsequent runs: Auto-continue (builds on previous work)
|
|
7
|
+
* - Stop: Manual (Ctrl+C) or max-runs limit
|
|
8
|
+
*
|
|
9
|
+
* Use case: "Keep working on X until I stop you"
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import chalk from 'chalk';
|
|
13
|
+
import type { FlowOptions } from '../commands/flow-command.js';
|
|
14
|
+
|
|
15
|
+
export interface LoopOptions {
|
|
16
|
+
enabled: boolean;
|
|
17
|
+
interval: number; // Wait time in seconds between runs (0 = no wait)
|
|
18
|
+
maxRuns?: number; // Optional max iterations (default: infinite)
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface LoopResult {
|
|
22
|
+
exitCode: number;
|
|
23
|
+
error?: Error;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface LoopState {
|
|
27
|
+
iteration: number;
|
|
28
|
+
startTime: Date;
|
|
29
|
+
successCount: number;
|
|
30
|
+
errorCount: number;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Controller for loop execution mode
|
|
35
|
+
*/
|
|
36
|
+
export class LoopController {
|
|
37
|
+
private state: LoopState;
|
|
38
|
+
private shouldStop: boolean = false;
|
|
39
|
+
|
|
40
|
+
constructor() {
|
|
41
|
+
this.state = {
|
|
42
|
+
iteration: 0,
|
|
43
|
+
startTime: new Date(),
|
|
44
|
+
successCount: 0,
|
|
45
|
+
errorCount: 0,
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
// Handle graceful shutdown
|
|
49
|
+
this.setupSignalHandlers();
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Execute task in loop mode
|
|
54
|
+
* Simple: Keep running same task until manual stop or max-runs
|
|
55
|
+
*/
|
|
56
|
+
async run(
|
|
57
|
+
executor: () => Promise<LoopResult>,
|
|
58
|
+
options: LoopOptions
|
|
59
|
+
): Promise<void> {
|
|
60
|
+
console.log(chalk.cyan.bold('\n━━━ 🔄 Loop Mode Activated\n'));
|
|
61
|
+
console.log(chalk.dim(` Wait time: ${options.interval}s`));
|
|
62
|
+
console.log(chalk.dim(` Max runs: ${options.maxRuns || '∞'}`));
|
|
63
|
+
console.log(chalk.dim(` Stop: Ctrl+C or max-runs limit\n`));
|
|
64
|
+
|
|
65
|
+
while (this.shouldContinue(options)) {
|
|
66
|
+
this.state.iteration++;
|
|
67
|
+
|
|
68
|
+
try {
|
|
69
|
+
await this.executeIteration(executor, options);
|
|
70
|
+
} catch (error) {
|
|
71
|
+
this.handleError(error as Error);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Wait for next iteration
|
|
75
|
+
if (this.shouldContinue(options)) {
|
|
76
|
+
await this.waitForNextRun(options);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
this.printSummary();
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Execute single iteration
|
|
85
|
+
* Simple: Run task, track success/error, continue
|
|
86
|
+
*/
|
|
87
|
+
private async executeIteration(
|
|
88
|
+
executor: () => Promise<LoopResult>,
|
|
89
|
+
options: LoopOptions
|
|
90
|
+
): Promise<void> {
|
|
91
|
+
const maxDisplay = options.maxRuns || '∞';
|
|
92
|
+
console.log(
|
|
93
|
+
chalk.cyan(
|
|
94
|
+
`\n🔄 Loop iteration ${this.state.iteration}/${maxDisplay}`
|
|
95
|
+
)
|
|
96
|
+
);
|
|
97
|
+
console.log(chalk.dim(`Started: ${new Date().toLocaleTimeString()}\n`));
|
|
98
|
+
|
|
99
|
+
const result = await executor();
|
|
100
|
+
|
|
101
|
+
// Update state (just count success/error)
|
|
102
|
+
if (result.error || result.exitCode !== 0) {
|
|
103
|
+
this.state.errorCount++;
|
|
104
|
+
console.log(chalk.yellow(`\n⚠️ Task encountered error (continuing...)`));
|
|
105
|
+
} else {
|
|
106
|
+
this.state.successCount++;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Handle execution error
|
|
112
|
+
* Simple: Log and continue (resilient)
|
|
113
|
+
*/
|
|
114
|
+
private handleError(error: Error): void {
|
|
115
|
+
this.state.errorCount++;
|
|
116
|
+
console.error(chalk.yellow('\n⚠️ Error occurred - continuing to next iteration'));
|
|
117
|
+
console.error(chalk.dim(`Error: ${error.message}\n`));
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Wait for next iteration
|
|
122
|
+
*/
|
|
123
|
+
private async waitForNextRun(options: LoopOptions): Promise<void> {
|
|
124
|
+
const maxDisplay = options.maxRuns || '∞';
|
|
125
|
+
const progress = `${this.state.iteration}/${maxDisplay}`;
|
|
126
|
+
|
|
127
|
+
console.log(
|
|
128
|
+
chalk.dim(
|
|
129
|
+
`\n⏳ Waiting ${options.interval}s until next run... (completed: ${progress})`
|
|
130
|
+
)
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
// Countdown display (optional, can be removed if too verbose)
|
|
134
|
+
const startTime = Date.now();
|
|
135
|
+
const endTime = startTime + options.interval * 1000;
|
|
136
|
+
|
|
137
|
+
while (Date.now() < endTime && !this.shouldStop) {
|
|
138
|
+
await this.sleep(1000);
|
|
139
|
+
|
|
140
|
+
const remaining = Math.ceil((endTime - Date.now()) / 1000);
|
|
141
|
+
if (remaining > 0 && remaining % 10 === 0) {
|
|
142
|
+
process.stdout.write(chalk.dim(`\r⏳ ${remaining}s remaining...`));
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
if (!this.shouldStop) {
|
|
147
|
+
process.stdout.write('\r' + ' '.repeat(50) + '\r'); // Clear line
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if should continue looping
|
|
153
|
+
* Simple: Stop only on manual interrupt or max-runs
|
|
154
|
+
*/
|
|
155
|
+
private shouldContinue(options: LoopOptions): boolean {
|
|
156
|
+
if (this.shouldStop) return false;
|
|
157
|
+
if (options.maxRuns && this.state.iteration >= options.maxRuns) return false;
|
|
158
|
+
return true;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Print execution summary
|
|
163
|
+
*/
|
|
164
|
+
private printSummary(): void {
|
|
165
|
+
const duration = Date.now() - this.state.startTime.getTime();
|
|
166
|
+
const minutes = Math.floor(duration / 60000);
|
|
167
|
+
const seconds = Math.floor((duration % 60000) / 1000);
|
|
168
|
+
|
|
169
|
+
console.log(chalk.cyan.bold('\n━━━ 🏁 Loop Summary\n'));
|
|
170
|
+
console.log(` Total iterations: ${this.state.iteration}`);
|
|
171
|
+
console.log(
|
|
172
|
+
` Successful: ${chalk.green(this.state.successCount.toString())}`
|
|
173
|
+
);
|
|
174
|
+
console.log(` Errors: ${chalk.red(this.state.errorCount.toString())}`);
|
|
175
|
+
console.log(` Duration: ${minutes}m ${seconds}s`);
|
|
176
|
+
console.log();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Setup signal handlers for graceful shutdown
|
|
181
|
+
*/
|
|
182
|
+
private setupSignalHandlers(): void {
|
|
183
|
+
const handler = () => {
|
|
184
|
+
console.log(
|
|
185
|
+
chalk.yellow('\n\n⚠️ Interrupt received - finishing current iteration...')
|
|
186
|
+
);
|
|
187
|
+
this.shouldStop = true;
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
process.on('SIGINT', handler);
|
|
191
|
+
process.on('SIGTERM', handler);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* Sleep helper
|
|
196
|
+
*/
|
|
197
|
+
private sleep(ms: number): Promise<void> {
|
|
198
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
199
|
+
}
|
|
200
|
+
}
|