@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,288 @@
1
+ /**
2
+ * Stream Handler
3
+ * Unified stream processing for both headless and TUI modes
4
+ */
5
+
6
+ import type { StreamChunk } from './ai-sdk.js';
7
+ import type { MessagePart, TokenUsage } from '../types/session.types.js';
8
+
9
+ /**
10
+ * Callbacks for stream events
11
+ */
12
+ export interface StreamCallbacks {
13
+ onTextStart?: () => void;
14
+ onTextDelta?: (text: string) => void;
15
+ onTextEnd?: () => void;
16
+ onReasoningStart?: () => void;
17
+ onReasoningDelta?: (text: string) => void;
18
+ onReasoningEnd?: (duration: number) => void;
19
+ onToolCall?: (toolCallId: string, toolName: string, args: unknown) => void;
20
+ onToolInputStart?: (toolCallId: string, toolName: string) => void;
21
+ onToolInputDelta?: (toolCallId: string, toolName: string, argsTextDelta: string) => void;
22
+ onToolInputEnd?: (toolCallId: string, toolName: string, args: unknown) => void;
23
+ onToolResult?: (toolCallId: string, toolName: string, result: unknown, duration: number) => void;
24
+ onToolError?: (toolCallId: string, toolName: string, error: string, duration: number) => void;
25
+ onAbort?: () => void;
26
+ onError?: (error: string) => void;
27
+ onFinish?: (usage: TokenUsage, finishReason: string) => void;
28
+ onComplete?: () => void;
29
+ }
30
+
31
+ /**
32
+ * Stream processing result
33
+ */
34
+ export interface StreamResult {
35
+ fullResponse: string;
36
+ messageParts: MessagePart[];
37
+ usage?: TokenUsage;
38
+ finishReason?: string;
39
+ }
40
+
41
+ /**
42
+ * Process AI stream and collect response with parts
43
+ */
44
+ export async function processStream(
45
+ stream: AsyncIterable<StreamChunk>,
46
+ callbacks: StreamCallbacks = {}
47
+ ): Promise<StreamResult> {
48
+ const { onTextStart, onTextDelta, onTextEnd, onReasoningStart, onReasoningDelta, onReasoningEnd, onToolCall, onToolInputStart, onToolInputDelta, onToolInputEnd, onToolResult, onToolError, onAbort, onError, onFinish, onComplete } = callbacks;
49
+
50
+ let fullResponse = '';
51
+ const messageParts: MessagePart[] = [];
52
+ const activeTools = new Map<string, { name: string; startTime: number; args: unknown }>();
53
+ let currentTextContent = '';
54
+ let currentReasoningContent = '';
55
+ let reasoningStartTime: number | null = null;
56
+ let usage: TokenUsage | undefined;
57
+ let finishReason: string | undefined;
58
+
59
+ for await (const chunk of stream) {
60
+ switch (chunk.type) {
61
+ case 'text-start': {
62
+ // Text generation started - notify immediately
63
+ onTextStart?.();
64
+ break;
65
+ }
66
+
67
+ case 'text-delta': {
68
+ fullResponse += chunk.textDelta;
69
+ currentTextContent += chunk.textDelta;
70
+ onTextDelta?.(chunk.textDelta);
71
+ break;
72
+ }
73
+
74
+ case 'text-end': {
75
+ // Text generation finished - save text part if any
76
+ if (currentTextContent) {
77
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
78
+ currentTextContent = '';
79
+ }
80
+ onTextEnd?.();
81
+ break;
82
+ }
83
+
84
+ case 'reasoning-start': {
85
+ // Save current text part if any
86
+ if (currentTextContent) {
87
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
88
+ currentTextContent = '';
89
+ }
90
+ reasoningStartTime = Date.now();
91
+ onReasoningStart?.();
92
+ break;
93
+ }
94
+
95
+ case 'reasoning-delta': {
96
+ currentReasoningContent += chunk.textDelta;
97
+ onReasoningDelta?.(chunk.textDelta);
98
+ break;
99
+ }
100
+
101
+ case 'reasoning-end': {
102
+ // Save reasoning part with duration
103
+ const duration = reasoningStartTime ? Date.now() - reasoningStartTime : 0;
104
+ if (currentReasoningContent || reasoningStartTime) {
105
+ messageParts.push({
106
+ type: 'reasoning',
107
+ content: currentReasoningContent,
108
+ status: 'completed', // All saved parts are completed
109
+ duration
110
+ });
111
+ currentReasoningContent = '';
112
+ reasoningStartTime = null;
113
+ }
114
+ // Pass duration to callback so UI can display it
115
+ onReasoningEnd?.(duration);
116
+ break;
117
+ }
118
+
119
+ case 'tool-call': {
120
+ // Save current text part if any
121
+ if (currentTextContent) {
122
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
123
+ currentTextContent = '';
124
+ }
125
+
126
+ // Add tool part (may not have complete args yet if streaming)
127
+ messageParts.push({
128
+ type: 'tool',
129
+ toolId: chunk.toolCallId,
130
+ name: chunk.toolName,
131
+ status: 'active', // Match MessagePart type
132
+ args: chunk.args,
133
+ });
134
+
135
+ // Track tool start time
136
+ activeTools.set(chunk.toolCallId, {
137
+ name: chunk.toolName,
138
+ startTime: Date.now(),
139
+ args: chunk.args,
140
+ });
141
+
142
+ onToolCall?.(chunk.toolCallId, chunk.toolName, chunk.args);
143
+ break;
144
+ }
145
+
146
+ case 'tool-input-start': {
147
+ // Tool input streaming started - notify callback
148
+ onToolInputStart?.(chunk.toolCallId, chunk.toolName);
149
+ break;
150
+ }
151
+
152
+ case 'tool-input-delta': {
153
+ // Update tool args as they stream in
154
+ // Find the active tool part and update its args
155
+ const toolPart = messageParts.find(
156
+ (p) => p.type === 'tool' && p.name === chunk.toolName && p.status === 'active'
157
+ );
158
+
159
+ if (toolPart && toolPart.type === 'tool') {
160
+ // Append args delta (args are streaming as JSON text)
161
+ const currentArgsText = typeof toolPart.args === 'string' ? toolPart.args : '';
162
+ toolPart.args = currentArgsText + chunk.argsTextDelta;
163
+ }
164
+
165
+ // Notify callback for real-time UI update
166
+ onToolInputDelta?.(chunk.toolCallId, chunk.toolName, chunk.argsTextDelta);
167
+ break;
168
+ }
169
+
170
+ case 'tool-input-end': {
171
+ // Tool input streaming complete - args are ready
172
+ // Find tool part to get final args
173
+ const toolPart = messageParts.find(
174
+ (p) => p.type === 'tool' && p.name === chunk.toolName && p.status === 'active'
175
+ );
176
+
177
+ if (toolPart && toolPart.type === 'tool') {
178
+ onToolInputEnd?.(chunk.toolCallId, chunk.toolName, toolPart.args);
179
+ }
180
+ break;
181
+ }
182
+
183
+ case 'tool-result': {
184
+ const tool = activeTools.get(chunk.toolCallId);
185
+ if (tool) {
186
+ const duration = Date.now() - tool.startTime;
187
+ activeTools.delete(chunk.toolCallId);
188
+
189
+ // Update tool part status and result
190
+ const toolPart = messageParts.find(
191
+ (p) => p.type === 'tool' && p.name === chunk.toolName && p.status === 'active'
192
+ );
193
+
194
+ if (toolPart && toolPart.type === 'tool') {
195
+ toolPart.status = 'completed';
196
+ toolPart.duration = duration;
197
+ toolPart.result = chunk.result;
198
+ }
199
+
200
+ onToolResult?.(chunk.toolCallId, chunk.toolName, chunk.result, duration);
201
+ }
202
+ break;
203
+ }
204
+
205
+ case 'tool-error': {
206
+ // Save current text part if any
207
+ if (currentTextContent) {
208
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
209
+ currentTextContent = '';
210
+ }
211
+
212
+ const tool = activeTools.get(chunk.toolCallId);
213
+ if (tool) {
214
+ const duration = Date.now() - tool.startTime;
215
+ activeTools.delete(chunk.toolCallId);
216
+
217
+ // Update tool part status and error
218
+ const toolPart = messageParts.find(
219
+ (p) => p.type === 'tool' && p.name === chunk.toolName && p.status === 'active'
220
+ );
221
+
222
+ if (toolPart && toolPart.type === 'tool') {
223
+ toolPart.status = 'error';
224
+ toolPart.duration = duration;
225
+ toolPart.error = chunk.error;
226
+ }
227
+
228
+ // Notify callback
229
+ onToolError?.(chunk.toolCallId, chunk.toolName, chunk.error, duration);
230
+ }
231
+ break;
232
+ }
233
+
234
+ case 'abort': {
235
+ // Save current text part if any
236
+ if (currentTextContent) {
237
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
238
+ currentTextContent = '';
239
+ }
240
+
241
+ // Mark all active parts as 'abort'
242
+ messageParts.forEach(part => {
243
+ if (part.status === 'active') {
244
+ part.status = 'abort';
245
+ }
246
+ });
247
+
248
+ // Notify callback
249
+ onAbort?.();
250
+ break;
251
+ }
252
+
253
+ case 'error': {
254
+ // Save current text part if any
255
+ if (currentTextContent) {
256
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
257
+ currentTextContent = '';
258
+ }
259
+
260
+ // Add error part
261
+ messageParts.push({ type: 'error', error: chunk.error, status: 'completed' });
262
+
263
+ // Notify callback
264
+ onError?.(chunk.error);
265
+ break;
266
+ }
267
+
268
+ case 'finish': {
269
+ usage = chunk.usage;
270
+ finishReason = chunk.finishReason;
271
+ onFinish?.(chunk.usage, chunk.finishReason);
272
+ break;
273
+ }
274
+ }
275
+ }
276
+
277
+ // Save final text part if any
278
+ if (currentTextContent) {
279
+ messageParts.push({ type: 'text', content: currentTextContent, status: 'completed' });
280
+ }
281
+
282
+ return {
283
+ fullResponse,
284
+ messageParts,
285
+ usage,
286
+ finishReason,
287
+ };
288
+ }
@@ -0,0 +1,161 @@
1
+ import inquirer from 'inquirer';
2
+ import {
3
+ getAllTargetIDs,
4
+ getAllTargets,
5
+ getDefaultTargetUnsafe,
6
+ getImplementedTargetIDs,
7
+ getImplementedTargets,
8
+ getTarget,
9
+ getTargetsWithMCPSupport,
10
+ isTargetImplemented,
11
+ } from '../config/targets.js';
12
+ import { getOrElse, isSome } from '../core/functional/option.js';
13
+ import { projectSettings } from '../utils/settings.js';
14
+
15
+ /**
16
+ * Target Manager interface
17
+ */
18
+ export interface TargetManager {
19
+ getAllTargets(): ReturnType<typeof getAllTargets>;
20
+ getImplementedTargets(): ReturnType<typeof getImplementedTargets>;
21
+ getTarget(id: string): ReturnType<typeof getTarget>;
22
+ promptForTargetSelection(): Promise<string>;
23
+ resolveTarget(options: { target?: string; allowSelection?: boolean }): Promise<string>;
24
+ isTargetImplemented(targetId: string): boolean;
25
+ getTargetsWithMCPSupport(): ReturnType<typeof getTargetsWithMCPSupport>;
26
+ getImplementedTargetIDs(): ReturnType<typeof getImplementedTargetIDs>;
27
+ getAllTargetIDs(): ReturnType<typeof getAllTargetIDs>;
28
+ }
29
+
30
+ /**
31
+ * Create a target manager instance
32
+ */
33
+ export function createTargetManager(): TargetManager {
34
+ /**
35
+ * Detect target from current environment
36
+ */
37
+ const detectTargetFromEnvironment = (): string | null => {
38
+ try {
39
+ const implementedTargets = getImplementedTargets();
40
+
41
+ // Prioritize non-default targets for detection
42
+ const nonDefaultTargets = implementedTargets.filter((target) => !target.isDefault);
43
+ const defaultTargets = implementedTargets.filter((target) => target.isDefault);
44
+
45
+ // Check non-default targets first
46
+ for (const target of nonDefaultTargets) {
47
+ const detected = target.detectFromEnvironment?.();
48
+ if (detected) {
49
+ return target.id;
50
+ }
51
+ }
52
+
53
+ // Then check default targets
54
+ for (const target of defaultTargets) {
55
+ const detected = target.detectFromEnvironment?.();
56
+ if (detected) {
57
+ return target.id;
58
+ }
59
+ }
60
+
61
+ return null;
62
+ } catch {
63
+ return null;
64
+ }
65
+ };
66
+
67
+ /**
68
+ * Prompt user to select a target platform
69
+ */
70
+ const promptForTargetSelection = async (): Promise<string> => {
71
+ const availableTargets = getImplementedTargetIDs();
72
+
73
+ // Try to get saved default target for default selection
74
+ let defaultTarget = getDefaultTargetUnsafe().id;
75
+ try {
76
+ const savedDefaultTarget = await projectSettings.getDefaultTarget();
77
+ if (savedDefaultTarget && isSome(getTarget(savedDefaultTarget))) {
78
+ defaultTarget = savedDefaultTarget;
79
+ }
80
+ } catch {
81
+ // Silently ignore errors reading project settings
82
+ }
83
+
84
+ const answer = await inquirer.prompt([
85
+ {
86
+ type: 'list',
87
+ name: 'target',
88
+ message: 'Select target platform:',
89
+ choices: availableTargets.map((id) => {
90
+ const targetOption = getTarget(id);
91
+ const target = getOrElse({ id, name: id } as any)(targetOption);
92
+ return {
93
+ name: target.name || id,
94
+ value: id,
95
+ };
96
+ }),
97
+ default: defaultTarget,
98
+ },
99
+ ]);
100
+
101
+ return answer.target;
102
+ };
103
+
104
+ /**
105
+ * Resolve target with fallback to default and detection
106
+ */
107
+ const resolveTarget = async (options: {
108
+ target?: string;
109
+ allowSelection?: boolean;
110
+ }): Promise<string> => {
111
+ // If target is explicitly specified, use it
112
+ if (options.target) {
113
+ if (!isSome(getTarget(options.target))) {
114
+ throw new Error(
115
+ `Unknown target: ${options.target}. Available targets: ${getAllTargetIDs().join(', ')}`
116
+ );
117
+ }
118
+ return options.target;
119
+ }
120
+
121
+ // Try to use saved project default target first
122
+ try {
123
+ const savedDefaultTarget = await projectSettings.getDefaultTarget();
124
+ if (savedDefaultTarget && isSome(getTarget(savedDefaultTarget))) {
125
+ return savedDefaultTarget;
126
+ }
127
+ } catch (_error) {
128
+ // Silently ignore errors reading project settings
129
+ }
130
+
131
+ // Try to detect target from environment
132
+ const detectedTarget = detectTargetFromEnvironment();
133
+ if (detectedTarget) {
134
+ return detectedTarget;
135
+ }
136
+
137
+ // If selection is allowed and no target found, prompt user
138
+ if (options.allowSelection) {
139
+ return await promptForTargetSelection();
140
+ }
141
+
142
+ // Fall back to system default target
143
+ const defaultTarget = getDefaultTargetUnsafe();
144
+ return defaultTarget.id;
145
+ };
146
+
147
+ return {
148
+ getAllTargets: () => getAllTargets(),
149
+ getImplementedTargets: () => getImplementedTargets(),
150
+ getTarget: (id: string) => getTarget(id),
151
+ promptForTargetSelection,
152
+ resolveTarget,
153
+ isTargetImplemented: (targetId: string) => isTargetImplemented(targetId),
154
+ getTargetsWithMCPSupport: () => getTargetsWithMCPSupport(),
155
+ getImplementedTargetIDs: () => getImplementedTargetIDs(),
156
+ getAllTargetIDs: () => getAllTargetIDs(),
157
+ };
158
+ }
159
+
160
+ // Singleton instance
161
+ export const targetManager = createTargetManager();