@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.
Files changed (229) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/package.json +10 -9
  3. package/src/commands/codebase-command.ts +168 -0
  4. package/src/commands/flow-command.ts +1137 -0
  5. package/src/commands/flow-orchestrator.ts +296 -0
  6. package/src/commands/hook-command.ts +444 -0
  7. package/src/commands/init-command.ts +92 -0
  8. package/src/commands/init-core.ts +322 -0
  9. package/src/commands/knowledge-command.ts +161 -0
  10. package/src/commands/run-command.ts +120 -0
  11. package/src/components/benchmark-monitor.tsx +331 -0
  12. package/src/components/reindex-progress.tsx +261 -0
  13. package/src/composables/functional/index.ts +14 -0
  14. package/src/composables/functional/useEnvironment.ts +171 -0
  15. package/src/composables/functional/useFileSystem.ts +139 -0
  16. package/src/composables/index.ts +5 -0
  17. package/src/composables/useEnv.ts +13 -0
  18. package/src/composables/useRuntimeConfig.ts +27 -0
  19. package/src/composables/useTargetConfig.ts +45 -0
  20. package/src/config/ai-config.ts +376 -0
  21. package/src/config/constants.ts +35 -0
  22. package/src/config/index.ts +27 -0
  23. package/src/config/rules.ts +43 -0
  24. package/src/config/servers.ts +371 -0
  25. package/src/config/targets.ts +126 -0
  26. package/src/core/agent-loader.ts +141 -0
  27. package/src/core/agent-manager.ts +174 -0
  28. package/src/core/ai-sdk.ts +603 -0
  29. package/src/core/app-factory.ts +381 -0
  30. package/src/core/builtin-agents.ts +9 -0
  31. package/src/core/command-system.ts +550 -0
  32. package/src/core/config-system.ts +550 -0
  33. package/src/core/connection-pool.ts +390 -0
  34. package/src/core/di-container.ts +155 -0
  35. package/src/core/error-handling.ts +519 -0
  36. package/src/core/formatting/bytes.test.ts +115 -0
  37. package/src/core/formatting/bytes.ts +64 -0
  38. package/src/core/functional/async.ts +313 -0
  39. package/src/core/functional/either.ts +109 -0
  40. package/src/core/functional/error-handler.ts +135 -0
  41. package/src/core/functional/error-types.ts +311 -0
  42. package/src/core/functional/index.ts +19 -0
  43. package/src/core/functional/option.ts +142 -0
  44. package/src/core/functional/pipe.ts +189 -0
  45. package/src/core/functional/result.ts +204 -0
  46. package/src/core/functional/validation.ts +138 -0
  47. package/src/core/headless-display.ts +96 -0
  48. package/src/core/index.ts +6 -0
  49. package/src/core/installers/file-installer.ts +303 -0
  50. package/src/core/installers/mcp-installer.ts +213 -0
  51. package/src/core/interfaces/index.ts +22 -0
  52. package/src/core/interfaces/repository.interface.ts +91 -0
  53. package/src/core/interfaces/service.interface.ts +133 -0
  54. package/src/core/interfaces.ts +129 -0
  55. package/src/core/loop-controller.ts +200 -0
  56. package/src/core/result.ts +351 -0
  57. package/src/core/rule-loader.ts +147 -0
  58. package/src/core/rule-manager.ts +240 -0
  59. package/src/core/service-config.ts +252 -0
  60. package/src/core/session-service.ts +121 -0
  61. package/src/core/state-detector.ts +389 -0
  62. package/src/core/storage-factory.ts +115 -0
  63. package/src/core/stream-handler.ts +288 -0
  64. package/src/core/target-manager.ts +161 -0
  65. package/src/core/type-utils.ts +427 -0
  66. package/src/core/unified-storage.ts +456 -0
  67. package/src/core/upgrade-manager.ts +300 -0
  68. package/src/core/validation/limit.test.ts +155 -0
  69. package/src/core/validation/limit.ts +46 -0
  70. package/src/core/validation/query.test.ts +44 -0
  71. package/src/core/validation/query.ts +20 -0
  72. package/src/db/auto-migrate.ts +322 -0
  73. package/src/db/base-database-client.ts +144 -0
  74. package/src/db/cache-db.ts +218 -0
  75. package/src/db/cache-schema.ts +75 -0
  76. package/src/db/database.ts +70 -0
  77. package/src/db/index.ts +252 -0
  78. package/src/db/memory-db.ts +153 -0
  79. package/src/db/memory-schema.ts +29 -0
  80. package/src/db/schema.ts +289 -0
  81. package/src/db/session-repository.ts +733 -0
  82. package/src/domains/codebase/index.ts +5 -0
  83. package/src/domains/codebase/tools.ts +139 -0
  84. package/src/domains/index.ts +8 -0
  85. package/src/domains/knowledge/index.ts +10 -0
  86. package/src/domains/knowledge/resources.ts +537 -0
  87. package/src/domains/knowledge/tools.ts +174 -0
  88. package/src/domains/utilities/index.ts +6 -0
  89. package/src/domains/utilities/time/index.ts +5 -0
  90. package/src/domains/utilities/time/tools.ts +291 -0
  91. package/src/index.ts +211 -0
  92. package/src/services/agent-service.ts +273 -0
  93. package/src/services/claude-config-service.ts +252 -0
  94. package/src/services/config-service.ts +258 -0
  95. package/src/services/evaluation-service.ts +271 -0
  96. package/src/services/functional/evaluation-logic.ts +296 -0
  97. package/src/services/functional/file-processor.ts +273 -0
  98. package/src/services/functional/index.ts +12 -0
  99. package/src/services/index.ts +13 -0
  100. package/src/services/mcp-service.ts +432 -0
  101. package/src/services/memory.service.ts +476 -0
  102. package/src/services/search/base-indexer.ts +156 -0
  103. package/src/services/search/codebase-indexer-types.ts +38 -0
  104. package/src/services/search/codebase-indexer.ts +647 -0
  105. package/src/services/search/embeddings-provider.ts +455 -0
  106. package/src/services/search/embeddings.ts +316 -0
  107. package/src/services/search/functional-indexer.ts +323 -0
  108. package/src/services/search/index.ts +27 -0
  109. package/src/services/search/indexer.ts +380 -0
  110. package/src/services/search/knowledge-indexer.ts +422 -0
  111. package/src/services/search/semantic-search.ts +244 -0
  112. package/src/services/search/tfidf.ts +559 -0
  113. package/src/services/search/unified-search-service.ts +888 -0
  114. package/src/services/smart-config-service.ts +385 -0
  115. package/src/services/storage/cache-storage.ts +487 -0
  116. package/src/services/storage/drizzle-storage.ts +581 -0
  117. package/src/services/storage/index.ts +15 -0
  118. package/src/services/storage/lancedb-vector-storage.ts +494 -0
  119. package/src/services/storage/memory-storage.ts +268 -0
  120. package/src/services/storage/separated-storage.ts +467 -0
  121. package/src/services/storage/vector-storage.ts +13 -0
  122. package/src/shared/agents/index.ts +63 -0
  123. package/src/shared/files/index.ts +99 -0
  124. package/src/shared/index.ts +32 -0
  125. package/src/shared/logging/index.ts +24 -0
  126. package/src/shared/processing/index.ts +153 -0
  127. package/src/shared/types/index.ts +25 -0
  128. package/src/targets/claude-code.ts +574 -0
  129. package/src/targets/functional/claude-code-logic.ts +185 -0
  130. package/src/targets/functional/index.ts +6 -0
  131. package/src/targets/opencode.ts +529 -0
  132. package/src/types/agent.types.ts +32 -0
  133. package/src/types/api/batch.ts +108 -0
  134. package/src/types/api/errors.ts +118 -0
  135. package/src/types/api/index.ts +55 -0
  136. package/src/types/api/requests.ts +76 -0
  137. package/src/types/api/responses.ts +180 -0
  138. package/src/types/api/websockets.ts +85 -0
  139. package/src/types/api.types.ts +9 -0
  140. package/src/types/benchmark.ts +49 -0
  141. package/src/types/cli.types.ts +87 -0
  142. package/src/types/common.types.ts +35 -0
  143. package/src/types/database.types.ts +510 -0
  144. package/src/types/mcp-config.types.ts +448 -0
  145. package/src/types/mcp.types.ts +69 -0
  146. package/src/types/memory-types.ts +63 -0
  147. package/src/types/provider.types.ts +28 -0
  148. package/src/types/rule.types.ts +24 -0
  149. package/src/types/session.types.ts +214 -0
  150. package/src/types/target-config.types.ts +295 -0
  151. package/src/types/target.types.ts +140 -0
  152. package/src/types/todo.types.ts +25 -0
  153. package/src/types.ts +40 -0
  154. package/src/utils/advanced-tokenizer.ts +191 -0
  155. package/src/utils/agent-enhancer.ts +114 -0
  156. package/src/utils/ai-model-fetcher.ts +19 -0
  157. package/src/utils/async-file-operations.ts +516 -0
  158. package/src/utils/audio-player.ts +345 -0
  159. package/src/utils/cli-output.ts +266 -0
  160. package/src/utils/codebase-helpers.ts +211 -0
  161. package/src/utils/console-ui.ts +79 -0
  162. package/src/utils/database-errors.ts +140 -0
  163. package/src/utils/debug-logger.ts +49 -0
  164. package/src/utils/error-handler.ts +53 -0
  165. package/src/utils/file-operations.ts +310 -0
  166. package/src/utils/file-scanner.ts +259 -0
  167. package/src/utils/functional/array.ts +355 -0
  168. package/src/utils/functional/index.ts +15 -0
  169. package/src/utils/functional/object.ts +279 -0
  170. package/src/utils/functional/string.ts +281 -0
  171. package/src/utils/functional.ts +543 -0
  172. package/src/utils/help.ts +20 -0
  173. package/src/utils/immutable-cache.ts +106 -0
  174. package/src/utils/index.ts +78 -0
  175. package/src/utils/jsonc.ts +158 -0
  176. package/src/utils/logger.ts +396 -0
  177. package/src/utils/mcp-config.ts +249 -0
  178. package/src/utils/memory-tui.ts +414 -0
  179. package/src/utils/models-dev.ts +91 -0
  180. package/src/utils/notifications.ts +169 -0
  181. package/src/utils/object-utils.ts +51 -0
  182. package/src/utils/parallel-operations.ts +487 -0
  183. package/src/utils/paths.ts +143 -0
  184. package/src/utils/process-manager.ts +155 -0
  185. package/src/utils/prompts.ts +120 -0
  186. package/src/utils/search-tool-builder.ts +214 -0
  187. package/src/utils/secret-utils.ts +179 -0
  188. package/src/utils/security.ts +537 -0
  189. package/src/utils/session-manager.ts +168 -0
  190. package/src/utils/session-title.ts +87 -0
  191. package/src/utils/settings.ts +182 -0
  192. package/src/utils/simplified-errors.ts +410 -0
  193. package/src/utils/sync-utils.ts +159 -0
  194. package/src/utils/target-config.ts +570 -0
  195. package/src/utils/target-utils.ts +394 -0
  196. package/src/utils/template-engine.ts +94 -0
  197. package/src/utils/test-audio.ts +71 -0
  198. package/src/utils/todo-context.ts +46 -0
  199. package/src/utils/token-counter.ts +288 -0
  200. package/dist/index.d.ts +0 -10
  201. package/dist/index.js +0 -59554
  202. package/dist/lancedb.linux-x64-gnu-b7f0jgsz.node +0 -0
  203. package/dist/lancedb.linux-x64-musl-tgcv22rx.node +0 -0
  204. package/dist/shared/chunk-25dwp0dp.js +0 -89
  205. package/dist/shared/chunk-3pjb6063.js +0 -208
  206. package/dist/shared/chunk-4d6ydpw7.js +0 -2854
  207. package/dist/shared/chunk-4wjcadjk.js +0 -225
  208. package/dist/shared/chunk-5j4w74t6.js +0 -30
  209. package/dist/shared/chunk-5j8m3dh3.js +0 -58
  210. package/dist/shared/chunk-5thh3qem.js +0 -91
  211. package/dist/shared/chunk-6g9xy73m.js +0 -252
  212. package/dist/shared/chunk-7eq34c42.js +0 -23
  213. package/dist/shared/chunk-c2gwgx3r.js +0 -115
  214. package/dist/shared/chunk-cjd3mk4c.js +0 -1320
  215. package/dist/shared/chunk-g5cv6703.js +0 -368
  216. package/dist/shared/chunk-hpkhykhq.js +0 -574
  217. package/dist/shared/chunk-m2322pdk.js +0 -122
  218. package/dist/shared/chunk-nd5fdvaq.js +0 -26
  219. package/dist/shared/chunk-pgd3m6zf.js +0 -108
  220. package/dist/shared/chunk-qk8n91hw.js +0 -494
  221. package/dist/shared/chunk-rkkn8szp.js +0 -16855
  222. package/dist/shared/chunk-t16rfxh0.js +0 -61
  223. package/dist/shared/chunk-t4fbfa5v.js +0 -19
  224. package/dist/shared/chunk-t77h86w6.js +0 -276
  225. package/dist/shared/chunk-v0ez4aef.js +0 -71
  226. package/dist/shared/chunk-v29j2r3s.js +0 -32051
  227. package/dist/shared/chunk-vfbc6ew5.js +0 -765
  228. package/dist/shared/chunk-vmeqwm1c.js +0 -204
  229. 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
+ }