shark-ai 0.4.20 → 0.4.24

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/dist/bin/shark.js CHANGED
@@ -1,49 +1,14 @@
1
1
  #!/usr/bin/env node
2
- import {
3
- configCommand,
4
- loginCommand
5
- } from "../chunk-JLE7NOEG.js";
6
- import {
7
- AGENT_RESPONSE_JSON_SCHEMA,
8
- ProviderResolver,
9
- TechStackEnum,
10
- astAddClass,
11
- astAddDecorator,
12
- astAddFunction,
13
- astAddImport,
14
- astAddInterface,
15
- astAddMethod,
16
- astAddProperty,
17
- astAddTypeAlias,
18
- astGetMethod,
19
- astGetProperty,
20
- astGrepRewrite,
21
- astGrepSearch,
22
- astListStructure,
23
- astModifyMethod,
24
- astModifyProperty,
25
- astOrganizeImports,
26
- astRemoveFunction,
27
- astRemoveImport,
28
- astRemoveMethod,
29
- astRemoveProperty,
30
- conversationManager,
31
- handleListFiles,
32
- handleReadFile,
33
- handleRunCommand,
34
- handleSearchCode,
35
- handleSearchFile,
36
- interactiveDeveloperAgent,
37
- replaceLineRange,
38
- startSmartReplace,
39
- workflowManager
40
- } from "../chunk-O6YG3GZN.js";
41
2
  import {
42
3
  ConfigManager,
43
4
  FileLogger,
5
+ authenticate,
44
6
  colors,
7
+ configCommand,
8
+ loginCommand,
9
+ tokenStorage,
45
10
  tui
46
- } from "../chunk-CUUMQTCW.js";
11
+ } from "../chunk-NKE5GNDJ.js";
47
12
 
48
13
  // src/core/error/crash-handler.ts
49
14
  import fs from "fs";
@@ -102,10 +67,123 @@ Node: ${process.version}
102
67
  var crashHandler = CrashHandler.getInstance();
103
68
 
104
69
  // src/bin/shark.ts
105
- import { Command as Command6 } from "commander";
70
+ import { Command as Command7 } from "commander";
106
71
 
107
72
  // src/commands/init.ts
108
73
  import { Command } from "commander";
74
+
75
+ // src/core/workflow/workflow-manager.ts
76
+ import fs2 from "fs/promises";
77
+ import path2 from "path";
78
+
79
+ // src/core/workflow/shark-workflow.schema.ts
80
+ import { z } from "zod";
81
+ var TechStackEnum = z.enum([
82
+ "react",
83
+ "nextjs",
84
+ "angular",
85
+ "vue",
86
+ "node-ts",
87
+ "python",
88
+ "dotnet",
89
+ "java",
90
+ "unknown"
91
+ ]);
92
+ var WorkflowStageEnum = z.enum([
93
+ "business_analysis",
94
+ "specification",
95
+ "architecture",
96
+ "development",
97
+ "verification",
98
+ "deployment"
99
+ ]);
100
+ var StageStatusEnum = z.enum([
101
+ "pending",
102
+ "in_progress",
103
+ "completed",
104
+ "failed",
105
+ "skipped"
106
+ ]);
107
+ var WorkflowSchema = z.object({
108
+ projectId: z.string().uuid(),
109
+ projectName: z.string().min(1),
110
+ techStack: TechStackEnum.default("unknown"),
111
+ currentStage: WorkflowStageEnum.default("business_analysis"),
112
+ stageStatus: StageStatusEnum.default("pending"),
113
+ lastUpdated: z.string().datetime(),
114
+ // ISO 8601
115
+ conversationId: z.string().optional(),
116
+ conversations: z.record(z.string(), z.string()).optional(),
117
+ // agentType -> conversationId
118
+ artifacts: z.array(z.string()).default([]),
119
+ // Extensible metadata bag
120
+ metadata: z.record(z.unknown()).optional()
121
+ });
122
+
123
+ // src/core/workflow/workflow-manager.ts
124
+ var WorkflowManager = class _WorkflowManager {
125
+ static instance;
126
+ filename = "shark-workflow.json";
127
+ tmpFilename = ".shark-workflow.tmp";
128
+ constructor() {
129
+ }
130
+ static getInstance() {
131
+ if (!_WorkflowManager.instance) {
132
+ _WorkflowManager.instance = new _WorkflowManager();
133
+ }
134
+ return _WorkflowManager.instance;
135
+ }
136
+ getFilePath() {
137
+ return path2.join(process.cwd(), this.filename);
138
+ }
139
+ getTmpFilePath() {
140
+ return path2.join(process.cwd(), this.tmpFilename);
141
+ }
142
+ async save(state) {
143
+ const parsed = WorkflowSchema.safeParse(state);
144
+ if (!parsed.success) {
145
+ throw new Error(`Invalid workflow state: ${parsed.error.message}`);
146
+ }
147
+ const filePath = this.getFilePath();
148
+ const tmpPath = this.getTmpFilePath();
149
+ const data = JSON.stringify(parsed.data, null, 2);
150
+ try {
151
+ await fs2.writeFile(tmpPath, data, "utf-8");
152
+ await fs2.rename(tmpPath, filePath);
153
+ } catch (error) {
154
+ throw new Error(`Failed to save workflow state atomically: ${error.message}`);
155
+ }
156
+ }
157
+ async load() {
158
+ const filePath = this.getFilePath();
159
+ try {
160
+ try {
161
+ await fs2.access(filePath);
162
+ } catch {
163
+ return null;
164
+ }
165
+ const content = await fs2.readFile(filePath, "utf-8");
166
+ const json = JSON.parse(content);
167
+ const parsed = WorkflowSchema.safeParse(json);
168
+ if (parsed.success) {
169
+ return parsed.data;
170
+ } else {
171
+ console.warn(colors.warning(`\u26A0\uFE0F Corrupted workflow file detected: ${parsed.error.message}`));
172
+ return null;
173
+ }
174
+ } catch (error) {
175
+ console.warn(colors.warning(`\u26A0\uFE0F Failed to load workflow state: ${error.message}`));
176
+ return null;
177
+ }
178
+ }
179
+ // Helper to get current state or default if none exists
180
+ async getOrInitState() {
181
+ return await this.load();
182
+ }
183
+ };
184
+ var workflowManager = WorkflowManager.getInstance();
185
+
186
+ // src/commands/init.ts
109
187
  import { randomUUID } from "crypto";
110
188
  var initAction = async () => {
111
189
  tui.intro("Shark Project Initialization");
@@ -125,72 +203,4434 @@ var initAction = async () => {
125
203
  { value: "exit", label: "\u274C Exit" }
126
204
  ]
127
205
  });
128
- if (tui.isCancel(action) || action === "exit") {
129
- tui.outro("See you later! \u{1F44B}");
130
- return;
206
+ if (tui.isCancel(action) || action === "exit") {
207
+ tui.outro("See you later! \u{1F44B}");
208
+ return;
209
+ }
210
+ if (action === "resume") {
211
+ tui.log.success(`Resuming work on ${colors.primary(existingState.projectName)}...`);
212
+ tui.outro(`To continue, run: "shark agent" (Context loaded)`);
213
+ return;
214
+ }
215
+ }
216
+ const projectName = await tui.text({
217
+ message: "What is the name of your project?",
218
+ placeholder: "e.g. My Awesome App",
219
+ validate: (value) => {
220
+ if (!value) return "Project name is required";
221
+ if (value.trim().length < 2) return "Project name must be at least 2 characters";
222
+ }
223
+ });
224
+ if (tui.isCancel(projectName)) {
225
+ tui.outro("Initialization cancelled.");
226
+ return;
227
+ }
228
+ const techStackOptions = TechStackEnum.options.map((stack) => ({
229
+ value: stack,
230
+ label: stack === "node-ts" ? "Node.js (TypeScript)" : stack === "nextjs" ? "Next.js" : stack.charAt(0).toUpperCase() + stack.slice(1)
231
+ }));
232
+ const techStack = await tui.select({
233
+ message: "Select your technology stack:",
234
+ options: techStackOptions
235
+ });
236
+ if (tui.isCancel(techStack)) {
237
+ tui.outro("Initialization cancelled.");
238
+ return;
239
+ }
240
+ const spinner = tui.spinner();
241
+ spinner.start("Initializing project workflow...");
242
+ try {
243
+ const newState = {
244
+ projectId: randomUUID(),
245
+ projectName,
246
+ techStack,
247
+ currentStage: "business_analysis",
248
+ stageStatus: "pending",
249
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
250
+ artifacts: [],
251
+ metadata: {
252
+ initializedBy: "shark-cli",
253
+ version: "0.0.1"
254
+ }
255
+ };
256
+ await workflowManager.save(newState);
257
+ spinner.stop("Project workflow created!");
258
+ tui.log.success(`Project ${colors.primary(projectName)} initialized successfully.`);
259
+ tui.log.message(`Your Project ID: ${colors.dim(newState.projectId)}`);
260
+ tui.outro('Ready to start! Run "shark agent" to begin analyzing requirements.');
261
+ } catch (error) {
262
+ spinner.stop("Initialization failed.", 1);
263
+ tui.log.error(error.message);
264
+ process.exit(1);
265
+ }
266
+ };
267
+ var initCommand = new Command("init").description("Initialize a new Shark project").action(initAction);
268
+
269
+ // src/commands/dev.ts
270
+ import { Command as Command2 } from "commander";
271
+
272
+ // src/core/agents/agent-response-parser.ts
273
+ import { z as z2 } from "zod";
274
+ var AgentActionSchema = z2.object({
275
+ type: z2.enum([
276
+ "create_file",
277
+ "modify_file",
278
+ "list_files",
279
+ "search_file",
280
+ "search_code",
281
+ "read_file",
282
+ "delete_file",
283
+ "list_structure",
284
+ "modify_ast",
285
+ "search_ast",
286
+ "run_command",
287
+ "talk_with_user",
288
+ "use_mcp_tool",
289
+ "activate_skill",
290
+ "define_subagent",
291
+ "invoke_subagent",
292
+ "send_message",
293
+ "manage_subagents",
294
+ "complete_task",
295
+ "wait",
296
+ "ast_list_structure",
297
+ "ast_get_method",
298
+ "ast_add_method",
299
+ "ast_modify_method",
300
+ "ast_remove_method",
301
+ "ast_add_class",
302
+ "ast_get_property",
303
+ "ast_add_property",
304
+ "ast_modify_property",
305
+ "ast_remove_property",
306
+ "ast_add_decorator",
307
+ "ast_add_interface",
308
+ "ast_add_type_alias",
309
+ "ast_add_function",
310
+ "ast_remove_function",
311
+ "ast_add_import",
312
+ "ast_remove_import",
313
+ "ast_organize_imports"
314
+ ]),
315
+ path: z2.string().nullable().optional(),
316
+ // Nullable for strict mode combatibility
317
+ content: z2.string().nullable().optional(),
318
+ line_range: z2.array(z2.number()).nullable().optional(),
319
+ target_content: z2.string().nullable().optional(),
320
+ command: z2.string().nullable().optional(),
321
+ tool_name: z2.string().nullable().optional(),
322
+ tool_args: z2.string().nullable().optional(),
323
+ // JSON string argument
324
+ // search_code fields
325
+ query: z2.string().nullable().optional(),
326
+ is_regex: z2.boolean().nullable().optional(),
327
+ // AST-Grep fields
328
+ pattern: z2.string().nullable().optional(),
329
+ fix: z2.string().nullable().optional(),
330
+ language: z2.string().nullable().optional(),
331
+ file_path: z2.string().nullable().optional(),
332
+ // Alias for path in ast-grep actions
333
+ // New AST Tool Specific Fields
334
+ class_name: z2.string().nullable().optional(),
335
+ method_name: z2.string().nullable().optional(),
336
+ method_code: z2.string().nullable().optional(),
337
+ property_name: z2.string().nullable().optional(),
338
+ property_code: z2.string().nullable().optional(),
339
+ extends_class: z2.string().nullable().optional(),
340
+ implements_interfaces: z2.array(z2.string()).nullable().optional(),
341
+ decorator_code: z2.string().nullable().optional(),
342
+ interface_code: z2.string().nullable().optional(),
343
+ type_code: z2.string().nullable().optional(),
344
+ function_name: z2.string().nullable().optional(),
345
+ function_code: z2.string().nullable().optional(),
346
+ import_statement: z2.string().nullable().optional(),
347
+ module_path: z2.string().nullable().optional(),
348
+ new_body: z2.string().nullable().optional(),
349
+ // Preview confirmation
350
+ confirmed: z2.boolean().nullable().optional(),
351
+ start_anchor: z2.string().nullable().optional(),
352
+ end_anchor: z2.string().nullable().optional(),
353
+ // Superpowers fields
354
+ skill_name: z2.string().nullable().optional(),
355
+ duration_seconds: z2.number().nullable().optional(),
356
+ Subagents: z2.array(z2.object({
357
+ TypeName: z2.string(),
358
+ Role: z2.string(),
359
+ Prompt: z2.string()
360
+ })).nullable().optional(),
361
+ Recipient: z2.string().nullable().optional(),
362
+ Message: z2.string().nullable().optional(),
363
+ Action: z2.enum(["list", "kill", "kill_all"]).nullable().optional(),
364
+ ConversationIds: z2.array(z2.string()).nullable().optional(),
365
+ // define_subagent fields
366
+ name: z2.string().nullable().optional(),
367
+ description: z2.string().nullable().optional(),
368
+ system_prompt: z2.string().nullable().optional(),
369
+ enable_write_tools: z2.boolean().nullable().optional(),
370
+ enable_subagent_tools: z2.boolean().nullable().optional(),
371
+ enable_mcp_tools: z2.boolean().nullable().optional()
372
+ });
373
+ var AgentCommandSchema = z2.object({
374
+ command: z2.string(),
375
+ description: z2.string(),
376
+ critical: z2.boolean()
377
+ });
378
+ var AgentResponseSchema = z2.object({
379
+ action: AgentActionSchema.nullable().optional(),
380
+ actions: z2.array(AgentActionSchema).default([]),
381
+ // Maintain backward compatibility
382
+ commands: z2.array(AgentCommandSchema).optional(),
383
+ // Maintain backward compatibility
384
+ summary: z2.string().optional(),
385
+ // Legacy fields handling for smooth transition/fallback
386
+ message: z2.string().optional(),
387
+ conversation_id: z2.string().optional()
388
+ });
389
+ function parseAgentResponse(rawResponse) {
390
+ FileLogger.log("PARSER", "Parsing Agent Response", { rawType: typeof rawResponse });
391
+ let parsedObj = {};
392
+ let conversation_id;
393
+ if (typeof rawResponse === "string") {
394
+ FileLogger.log("PARSER", "Type String", { length: rawResponse.length });
395
+ try {
396
+ parsedObj = extractFirstJson(rawResponse);
397
+ } catch (e) {
398
+ FileLogger.log("PARSER", "String Parse Failed", { error: e.message });
399
+ const errMsg = e.message;
400
+ const cleanRaw = rawResponse.trim();
401
+ if (cleanRaw === "") {
402
+ const systemMsg2 = `[SYSTEM ERROR]: O modelo retornou uma resposta vazia. Por favor, tente novamente e forne\xE7a uma a\xE7\xE3o JSON v\xE1lida.`;
403
+ return {
404
+ action: {
405
+ type: "talk_with_user",
406
+ content: systemMsg2,
407
+ path: ""
408
+ },
409
+ actions: [{
410
+ type: "talk_with_user",
411
+ content: systemMsg2,
412
+ path: ""
413
+ }],
414
+ message: systemMsg2
415
+ };
416
+ }
417
+ const looksLikeJson = cleanRaw.startsWith("{") || cleanRaw.startsWith("[");
418
+ if (!looksLikeJson) {
419
+ return {
420
+ action: {
421
+ type: "talk_with_user",
422
+ content: rawResponse,
423
+ path: ""
424
+ },
425
+ actions: [{
426
+ type: "talk_with_user",
427
+ content: rawResponse,
428
+ path: ""
429
+ }],
430
+ message: rawResponse
431
+ };
432
+ }
433
+ const isTruncated = errMsg.includes("Unterminated string") || errMsg.includes("Unexpected end of JSON input");
434
+ const charCount = rawResponse.length;
435
+ const safeLimit = Math.floor(charCount * 0.9);
436
+ const systemMsg = isTruncated ? `[SYSTEM ERROR]: Sua resposta anterior foi cortada/truncada antes do final devido ao limite m\xE1ximo de tokens de sa\xEDda (output token limit) ap\xF3s atingir ${charCount} caracteres. O JSON ficou incompleto: ${errMsg}. Por favor, envie uma nova resposta com formato JSON completo e v\xE1lido (n\xE3o tente apenas completar o JSON anterior). Continue o trabalho l\xF3gico da tarefa de forma mais curta ou incremental (ex: criando apenas o esqueleto/estrutura b\xE1sica ou escrevendo uma parte menor do arquivo de cada vez). Garanta que o tamanho total desta nova resposta JSON seja menor que ${safeLimit} caracteres para evitar novos cortes.` : `[SYSTEM ERROR]: Falha ao parsear o JSON de resposta: ${errMsg}.`;
437
+ return {
438
+ action: {
439
+ type: "talk_with_user",
440
+ content: systemMsg,
441
+ path: ""
442
+ },
443
+ actions: [{
444
+ type: "talk_with_user",
445
+ content: systemMsg,
446
+ path: ""
447
+ }],
448
+ message: systemMsg
449
+ };
450
+ }
451
+ } else if (typeof rawResponse === "object" && rawResponse !== null) {
452
+ const anyResp = rawResponse;
453
+ conversation_id = anyResp.conversation_id;
454
+ FileLogger.log("PARSER", "Type Object", {
455
+ hasContent: !!anyResp.content,
456
+ hasMessage: !!anyResp.message,
457
+ messageType: typeof anyResp.message
458
+ });
459
+ const stringContent = anyResp.content || anyResp.message;
460
+ if (stringContent && typeof stringContent === "string") {
461
+ try {
462
+ const parsedInside = extractFirstJson(stringContent);
463
+ if (typeof parsedInside === "object" && parsedInside !== null) {
464
+ parsedObj = parsedInside;
465
+ FileLogger.log("PARSER", "Inner JSON Parsed", { keys: Object.keys(parsedObj) });
466
+ } else {
467
+ parsedObj = rawResponse;
468
+ FileLogger.log("PARSER", "Inner JSON was primitive");
469
+ }
470
+ } catch (e) {
471
+ FileLogger.log("PARSER", "Inner JSON Parse Error", { error: e.message });
472
+ const errMsg = e.message;
473
+ const cleanContent = stringContent.trim();
474
+ const looksLikeJson = cleanContent.startsWith("{") || cleanContent.startsWith("[");
475
+ if (looksLikeJson) {
476
+ const isTruncated = errMsg.includes("Unterminated string") || errMsg.includes("Unexpected end of JSON input");
477
+ const charCount = stringContent.length;
478
+ const safeLimit = Math.floor(charCount * 0.9);
479
+ const systemMsg = isTruncated ? `[SYSTEM ERROR]: Sua resposta anterior foi cortada/truncada antes do final devido ao limite m\xE1ximo de tokens de sa\xEDda (output token limit) ap\xF3s atingir ${charCount} caracteres. O JSON ficou incompleto: ${errMsg}. Por favor, envie uma nova resposta com formato JSON completo e v\xE1lido (n\xE3o tente apenas completar o JSON anterior). Continue o trabalho l\xF3gico da tarefa de forma mais curta ou incremental (ex: criando apenas o esqueleto/estrutura b\xE1sica ou escrevendo uma parte menor do arquivo de cada vez). Garanta que o tamanho total desta nova resposta JSON seja menor que ${safeLimit} caracteres para evitar novos cortes.` : `[SYSTEM ERROR]: Falha ao parsear o JSON de resposta: ${errMsg}.`;
480
+ parsedObj = {
481
+ action: {
482
+ type: "talk_with_user",
483
+ content: systemMsg,
484
+ path: ""
485
+ },
486
+ actions: [{
487
+ type: "talk_with_user",
488
+ content: systemMsg,
489
+ path: ""
490
+ }],
491
+ summary: "Parsing failed due to truncated JSON response"
492
+ };
493
+ } else {
494
+ parsedObj = rawResponse;
495
+ }
496
+ }
497
+ } else {
498
+ parsedObj = rawResponse;
499
+ }
500
+ if (!parsedObj.actions && !parsedObj.action) {
501
+ parsedObj = rawResponse;
502
+ }
503
+ }
504
+ let normalizedAction = parsedObj.action;
505
+ let normalizedActions = parsedObj.actions;
506
+ if (!normalizedAction && (!normalizedActions || normalizedActions.length === 0) && parsedObj && typeof parsedObj === "object" && typeof parsedObj.type === "string") {
507
+ const validTypes = [
508
+ "create_file",
509
+ "modify_file",
510
+ "list_files",
511
+ "search_file",
512
+ "search_code",
513
+ "read_file",
514
+ "delete_file",
515
+ "talk_with_user",
516
+ "use_mcp_tool",
517
+ "list_structure",
518
+ "modify_ast",
519
+ "search_ast",
520
+ "run_command",
521
+ "activate_skill",
522
+ "define_subagent",
523
+ "invoke_subagent",
524
+ "send_message",
525
+ "manage_subagents",
526
+ "complete_task"
527
+ ];
528
+ if (validTypes.includes(parsedObj.type)) {
529
+ normalizedAction = parsedObj;
530
+ normalizedActions = [parsedObj];
531
+ }
532
+ }
533
+ if (!normalizedAction && normalizedActions && normalizedActions.length > 0) {
534
+ normalizedAction = normalizedActions[0];
535
+ } else if (normalizedAction && (!normalizedActions || normalizedActions.length === 0)) {
536
+ normalizedActions = [normalizedAction];
537
+ }
538
+ if (!normalizedAction && !normalizedActions) {
539
+ FileLogger.log("PARSER", "No Action/Actions Found - Constructing Default");
540
+ const content = parsedObj.message || (typeof parsedObj === "object" ? JSON.stringify(parsedObj) : String(parsedObj));
541
+ normalizedAction = {
542
+ type: "talk_with_user",
543
+ content,
544
+ path: ""
545
+ };
546
+ normalizedActions = [normalizedAction];
547
+ }
548
+ let normalizedCommands = [];
549
+ if (Array.isArray(parsedObj.commands)) {
550
+ normalizedCommands = parsedObj.commands.map((cmd) => {
551
+ if (typeof cmd === "string") {
552
+ return {
553
+ command: cmd,
554
+ description: `Execute ${cmd}`,
555
+ critical: false
556
+ };
557
+ }
558
+ if (cmd && typeof cmd === "object") {
559
+ return {
560
+ command: cmd.command || "",
561
+ description: cmd.description || `Execute ${cmd.command || ""}`,
562
+ critical: cmd.critical === true
563
+ };
564
+ }
565
+ return null;
566
+ }).filter(Boolean);
567
+ }
568
+ const result = {
569
+ action: normalizedAction,
570
+ actions: normalizedActions,
571
+ commands: normalizedCommands,
572
+ summary: parsedObj.summary || "",
573
+ conversation_id,
574
+ message: parsedObj.summary || "Agent Action"
575
+ // Backward compatibility
576
+ };
577
+ FileLogger.log("PARSER", "Final Result Constructed", { hasAction: !!result.action });
578
+ try {
579
+ return AgentResponseSchema.parse(result);
580
+ } catch (e) {
581
+ FileLogger.log("PARSER", "Schema Validation Failed", { error: e.message });
582
+ throw e;
583
+ }
584
+ }
585
+ function extractFirstJson(str) {
586
+ try {
587
+ return JSON.parse(str);
588
+ } catch (e) {
589
+ const firstOpen = str.indexOf("{");
590
+ if (firstOpen === -1) throw e;
591
+ let balance = 0;
592
+ let inString = false;
593
+ let escape = false;
594
+ for (let i = firstOpen; i < str.length; i++) {
595
+ const char = str[i];
596
+ if (escape) {
597
+ escape = false;
598
+ continue;
599
+ }
600
+ if (char === "\\") {
601
+ escape = true;
602
+ continue;
603
+ }
604
+ if (char === '"') {
605
+ inString = !inString;
606
+ continue;
607
+ }
608
+ if (!inString) {
609
+ if (char === "{") balance++;
610
+ else if (char === "}") {
611
+ balance--;
612
+ if (balance === 0) {
613
+ const potentialJson = str.substring(firstOpen, i + 1);
614
+ try {
615
+ return JSON.parse(potentialJson);
616
+ } catch (innerE) {
617
+ throw e;
618
+ }
619
+ }
620
+ }
621
+ }
622
+ }
623
+ throw e;
624
+ }
625
+ }
626
+
627
+ // src/core/api/stackspot-client.ts
628
+ var AuthError = class extends Error {
629
+ constructor(message) {
630
+ super(message);
631
+ this.name = "AuthError";
632
+ }
633
+ };
634
+ var STACKSPOT_AGENT_API_BASE = "https://genai-inference-app.stackspot.com";
635
+ async function ensureValidToken(realm) {
636
+ let creds = await tokenStorage.getCredentials(realm);
637
+ if (!creds?.accessToken) {
638
+ throw new AuthError(`Authentication required for realm '${realm}'.
639
+ Please run 'shark login' to authenticate.`);
640
+ }
641
+ const now = Math.floor(Date.now() / 1e3);
642
+ const buffer = 300;
643
+ if (creds.expiresAt && creds.clientId && creds.clientKey) {
644
+ if (now > creds.expiresAt - buffer) {
645
+ try {
646
+ const newTokens = await authenticate(realm, creds.clientId, creds.clientKey);
647
+ await tokenStorage.saveToken(
648
+ realm,
649
+ newTokens.access_token,
650
+ creds.clientId,
651
+ creds.clientKey,
652
+ newTokens.expires_in
653
+ );
654
+ return newTokens.access_token;
655
+ } catch (error) {
656
+ console.warn(colors.warning(`\u26A0\uFE0F Failed to auto-refresh token: ${error.message}`));
657
+ }
658
+ }
659
+ }
660
+ return creds.accessToken;
661
+ }
662
+
663
+ // src/core/api/sse-client.ts
664
+ var SSEClient = class {
665
+ /**
666
+ * Streams agent response using Server-Sent Events.
667
+ *
668
+ * @param url - The SSE endpoint URL
669
+ * @param requestPayload - The request payload to POST
670
+ * @param headers - Request headers (including Authorization)
671
+ * @param callbacks - Event callbacks for chunks, completion, and errors
672
+ */
673
+ async streamAgentResponse(url, requestPayload, headers, callbacks = {}) {
674
+ const { onChunk, onComplete, onError } = callbacks;
675
+ FileLogger.log("SSE", `Starting Request to ${url}`, {
676
+ headers,
677
+ payload: requestPayload
678
+ });
679
+ try {
680
+ const response = await fetch(url, {
681
+ method: "POST",
682
+ headers: {
683
+ ...headers,
684
+ "Content-Type": "application/json"
685
+ },
686
+ body: JSON.stringify(requestPayload)
687
+ });
688
+ FileLogger.log("SSE", `Response Status: ${response.status} ${response.statusText}`);
689
+ const responseHeaders = {};
690
+ response.headers.forEach((value, key) => {
691
+ responseHeaders[key] = value;
692
+ });
693
+ FileLogger.log("SSE", "Response Headers", responseHeaders);
694
+ if (!response.ok) {
695
+ const errorText = await response.text();
696
+ FileLogger.log("SSE", "Response Error Body", errorText);
697
+ throw new Error(`SSE request failed: ${response.status} ${response.statusText} - ${errorText}`);
698
+ }
699
+ if (!response.body) {
700
+ throw new Error("Response body is null");
701
+ }
702
+ const contentType = response.headers.get("content-type") || "";
703
+ const isJson = contentType.includes("application/json");
704
+ if (isJson) {
705
+ const jsonBody = await response.json();
706
+ FileLogger.log("SSE", "Received Non-Streaming JSON Response", { length: JSON.stringify(jsonBody).length });
707
+ let content = "";
708
+ if (typeof jsonBody === "string") content = jsonBody;
709
+ else if (jsonBody.message) content = jsonBody.message;
710
+ else if (jsonBody.choices?.[0]?.message?.content) content = jsonBody.choices[0].message.content;
711
+ else content = JSON.stringify(jsonBody);
712
+ if (onChunk) onChunk(content);
713
+ if (onComplete) onComplete(content, jsonBody);
714
+ return;
715
+ }
716
+ const reader = response.body.getReader();
717
+ const decoder = new TextDecoder();
718
+ let buffer = "";
719
+ let fullMessage = "";
720
+ let metadata = {};
721
+ while (true) {
722
+ const { done, value } = await reader.read();
723
+ if (done) {
724
+ break;
725
+ }
726
+ buffer += decoder.decode(value, { stream: true });
727
+ const lines = buffer.split("\n");
728
+ buffer = lines.pop() || "";
729
+ for (const line of lines) {
730
+ if (line.startsWith("data:")) {
731
+ const data = line.slice(5).trim();
732
+ if (data === "[DONE]") {
733
+ FileLogger.log("SSE", "Stream Complete [DONE]", { fullMessage, metadata });
734
+ if (onComplete) {
735
+ onComplete(fullMessage, metadata);
736
+ }
737
+ return;
738
+ }
739
+ try {
740
+ const parsed = JSON.parse(data);
741
+ const chunk = parsed.message || parsed.content || data;
742
+ fullMessage += chunk;
743
+ if (parsed.conversation_id) {
744
+ metadata.conversation_id = parsed.conversation_id;
745
+ }
746
+ if (onChunk) {
747
+ onChunk(chunk);
748
+ }
749
+ } catch (parseError) {
750
+ fullMessage += data;
751
+ if (onChunk) {
752
+ onChunk(data);
753
+ }
754
+ }
755
+ }
756
+ }
757
+ }
758
+ FileLogger.log("SSE", "Stream Ended Naturally", { fullMessage, metadata });
759
+ if (onComplete) {
760
+ onComplete(fullMessage, metadata);
761
+ }
762
+ } catch (error) {
763
+ FileLogger.log("SSE", "Stream Error", error);
764
+ if (onError) {
765
+ onError(error instanceof Error ? error : new Error(String(error)));
766
+ } else {
767
+ throw error;
768
+ }
769
+ }
770
+ }
771
+ };
772
+ var sseClient = new SSEClient();
773
+
774
+ // src/core/auth/get-active-realm.ts
775
+ async function getActiveRealm() {
776
+ const configManager = ConfigManager.getInstance();
777
+ const config = configManager.getConfig();
778
+ const realm = config.activeRealm;
779
+ if (!realm) {
780
+ throw new Error(
781
+ 'No active authentication found.\nPlease run "shark login" first to authenticate.'
782
+ );
783
+ }
784
+ return realm;
785
+ }
786
+
787
+ // src/core/api/prompts.ts
788
+ var UNIFIED_SYSTEM_PROMPT = `Voc\xEA \xE9 o Shark Dev, um agente de intelig\xEAncia artificial de desenvolvimento colaborativo no Shark AI.
789
+ Seu objetivo \xE9 ajudar o usu\xE1rio a analisar, especificar e implementar c\xF3digo de forma estruturada.
790
+
791
+ \u2139\uFE0F SISTEMA DE \xC2NCORAS PARA LEITURA/EDI\xC7\xC3O DE ARQUIVOS (Anchor System):
792
+ - Quando voc\xEA l\xEA um arquivo usando a a\xE7\xE3o 'read_file', cada linha do arquivo ser\xE1 retornada no formato: \`palavra_\xE2ncora\xA7conte\xFAdo_da_linha\`.
793
+ - Exemplo: \`apple\xA7const x = 10;\`
794
+ - Ao modificar um arquivo usando a a\xE7\xE3o 'modify_file', voc\xEA DEVE especificar:
795
+ - \`start_anchor\`: A palavra \xE2ncora (ex: \`apple\`) que marca o in\xEDcio do bloco a ser substitu\xEDdo.
796
+ - \`end_anchor\`: A palavra \xE2ncora (ex: \`apple\`) que marca o fim do bloco a ser substitu\xEDdo (inclusive).
797
+ - \`content\`: O novo conte\xFAdo que substituir\xE1 todo o bloco entre (e incluindo) as duas \xE2ncoras.
798
+ - Importante: Use APENAS a palavra \xE2ncora no campo \`start_anchor\` e \`end_anchor\` (por exemplo: \`apple\`), e N\xC3O a linha inteira ou o separador \`\xA7\`.
799
+
800
+ \u26A0\uFE0F REGRA GERAL PARA ARQUIVOS GRANDES (Evitar JSON truncado):
801
+ - Evite criar ou modificar arquivos grandes (como planos, documenta\xE7\xF5es ou c\xF3digos extensos) de uma \xFAnica vez.
802
+ - Limite de Sa\xEDda R\xEDgido: A API possui um limite m\xE1ximo de tokens de sa\xEDda. Para sua seguran\xE7a, garanta que o conte\xFAdo de cada resposta JSON sua tenha no m\xE1ximo 15.000 caracteres (cerca de 4.000 tokens). NUNCA gere respostas \xFAnicas maiores do que isso.
803
+ - Se a tarefa exigir criar ou modificar arquivos longos, siga estritamente esta l\xF3gica:
804
+ 1. Use 'create_file' para criar apenas a estrutura b\xE1sica ou esqueleto do arquivo (cabe\xE7alhos e se\xE7\xF5es vazias).
805
+ 2. Nas rodadas subsequentes, use 'modify_file' com o sistema de \xE2ncoras para preencher/atualizar o conte\xFAdo de forma incremental e em peda\xE7os menores (no m\xE1ximo 50 a 100 linhas por vez).
806
+ - Isso evita que a sua resposta JSON seja cortada no meio devido ao limite m\xE1ximo de tokens de sa\xEDda da API.
807
+
808
+ \u{1F6A8} REGRAS CR\xCDTICAS DE RESPOSTA (JSON):
809
+ - Voc\xEA DEVE responder APENAS com um objeto JSON v\xE1lido.
810
+ - N\xE3o inclua nenhuma introdu\xE7\xE3o, explica\xE7\xE3o ou bloco de markdown fora do JSON.
811
+ - Se precisar falar com o usu\xE1rio, use a action com type 'talk_with_user'.
812
+
813
+ SUA SA\xCDDA DEVE SEGUIR EXATAMENTE ESTE FORMATO JSON:
814
+ {
815
+ "action": {
816
+ "type": "create_file" | "modify_file" | "read_file" | "list_files" | "search_file" | "search_code" | "delete_file" | "run_command" | "talk_with_user" | "use_mcp_tool" | "activate_skill" | "define_subagent" | "invoke_subagent" | "send_message" | "manage_subagents" | "complete_task" | "wait",
817
+ "path": "caminho/relativo/do/arquivo (opcional)",
818
+ "content": "conte\xFAdo do arquivo ou mensagem para o usu\xE1rio (opcional)",
819
+ "start_anchor": "\xE2ncora de in\xEDcio de substitui\xE7\xE3o (modify_file apenas)",
820
+ "end_anchor": "\xE2ncora de fim de substitui\xE7\xE3o (modify_file apenas)",
821
+ "command": "comando bash a ser executado (run_command apenas)",
822
+ "query": "termo de busca (search_code apenas)",
823
+ "tool_name": "nome da ferramenta MCP (use_mcp_tool apenas)",
824
+ "tool_args": "argumentos em string JSON para MCP (use_mcp_tool apenas)",
825
+ "skill_name": "nome da habilidade a ativar (activate_skill apenas)",
826
+ "duration_seconds": "tempo m\xE1ximo em segundos para aguardar atualiza\xE7\xF5es (opcional, wait apenas)",
827
+ "Subagents": [
828
+ {
829
+ "TypeName": "tipo do subagente",
830
+ "Role": "papel do subagente",
831
+ "Prompt": "instru\xE7\xF5es de tarefa para o subagente"
832
+ }
833
+ ] (invoke_subagent apenas),
834
+ "Recipient": "ID da conversa de destino da mensagem (send_message apenas)",
835
+ "Message": "conte\xFAdo da mensagem a ser enviada (send_message apenas)",
836
+ "Action": "list" | "kill" | "kill_all" (manage_subagents apenas),
837
+ "ConversationIds": ["lista de IDs de conversa para cancelar"] (manage_subagents apenas, opcional),
838
+ "name": "nome do subagente (define_subagent apenas)",
839
+ "description": "descri\xE7\xE3o do subagente (define_subagent apenas)",
840
+ "system_prompt": "prompt de sistema customizado (define_subagent apenas)",
841
+ "enable_write_tools": true | false (define_subagent apenas, opcional),
842
+ "enable_subagent_tools": true | false (define_subagent apenas, opcional),
843
+ "enable_mcp_tools": true | false (define_subagent apenas, opcional)
844
+ },
845
+ "summary": "Resumo de 1 frase do que voc\xEA realizou nesta rodada."
846
+ }`;
847
+ var AGENT_RESPONSE_JSON_SCHEMA = {
848
+ "$schema": "http://json-schema.org/draft-07/schema#",
849
+ "title": "AgentResponse",
850
+ "type": "object",
851
+ "properties": {
852
+ "action": {
853
+ "type": "object",
854
+ "properties": {
855
+ "type": {
856
+ "type": "string",
857
+ "enum": [
858
+ "create_file",
859
+ "modify_file",
860
+ "read_file",
861
+ "list_files",
862
+ "search_file",
863
+ "search_code",
864
+ "delete_file",
865
+ "run_command",
866
+ "talk_with_user",
867
+ "use_mcp_tool",
868
+ "activate_skill",
869
+ "define_subagent",
870
+ "invoke_subagent",
871
+ "send_message",
872
+ "manage_subagents",
873
+ "complete_task",
874
+ "wait"
875
+ ]
876
+ },
877
+ "path": { "type": ["string", "null"] },
878
+ "content": { "type": ["string", "null"] },
879
+ "start_anchor": { "type": ["string", "null"] },
880
+ "end_anchor": { "type": ["string", "null"] },
881
+ "command": { "type": ["string", "null"] },
882
+ "query": { "type": ["string", "null"] },
883
+ "tool_name": { "type": ["string", "null"] },
884
+ "tool_args": { "type": ["string", "null"] },
885
+ "skill_name": { "type": ["string", "null"] },
886
+ "duration_seconds": {
887
+ "type": ["integer", "null"],
888
+ "description": "Tempo maximo em segundos para aguardar atualizacoes."
889
+ },
890
+ "Subagents": {
891
+ "type": ["array", "null"],
892
+ "items": {
893
+ "type": "object",
894
+ "properties": {
895
+ "TypeName": { "type": "string" },
896
+ "Role": { "type": "string" },
897
+ "Prompt": { "type": "string" }
898
+ },
899
+ "required": ["TypeName", "Role", "Prompt"]
900
+ }
901
+ },
902
+ "Recipient": { "type": ["string", "null"] },
903
+ "Message": { "type": ["string", "null"] },
904
+ "Action": { "type": ["string", "null"], "enum": ["list", "kill", "kill_all"] },
905
+ "ConversationIds": {
906
+ "type": ["array", "null"],
907
+ "items": { "type": "string" }
908
+ },
909
+ "name": { "type": ["string", "null"] },
910
+ "description": { "type": ["string", "null"] },
911
+ "system_prompt": { "type": ["string", "null"] },
912
+ "enable_write_tools": { "type": ["boolean", "null"] },
913
+ "enable_subagent_tools": { "type": ["boolean", "null"] },
914
+ "enable_mcp_tools": { "type": ["boolean", "null"] }
915
+ },
916
+ "required": ["type"]
917
+ },
918
+ "summary": { "type": "string" }
919
+ },
920
+ "required": ["action"]
921
+ };
922
+
923
+ // src/core/api/stackspot-provider.ts
924
+ var StackSpotProvider = class {
925
+ constructor(agentType) {
926
+ this.agentType = agentType;
927
+ const config = ConfigManager.getInstance().getConfig();
928
+ this.agentId = config.stackspot?.agentId;
929
+ }
930
+ agentId;
931
+ getAgentId() {
932
+ const config = ConfigManager.getInstance().getConfig();
933
+ const configKeyMapping = {
934
+ "developer_agent": "dev",
935
+ "business_analyst": "ba",
936
+ "specification_agent": "spec",
937
+ "qa_agent": "qa",
938
+ "scan_agent": "scan",
939
+ "code_review": "codeReview"
940
+ };
941
+ const mappedKey = configKeyMapping[this.agentType];
942
+ if (mappedKey && config.agents?.[mappedKey]) {
943
+ return config.agents[mappedKey];
944
+ }
945
+ const envIdMapping = {
946
+ "business_analyst": process.env.STACKSPOT_BA_AGENT_ID,
947
+ "developer_agent": process.env.STACKSPOT_DEV_AGENT_ID,
948
+ "qa_agent": process.env.STACKSPOT_QA_AGENT_ID,
949
+ "specification_agent": process.env.STACKSPOT_SPEC_AGENT_ID,
950
+ "scan_agent": process.env.STACKSPOT_SCAN_AGENT_ID,
951
+ "code_review": process.env.STACKSPOT_CODE_REVIEW_AGENT_ID
952
+ };
953
+ const envResolved = envIdMapping[this.agentType];
954
+ if (envResolved) {
955
+ return envResolved;
956
+ }
957
+ if (this.agentId && this.agentId !== "01KEQCGJ65YENRA4QBXVN1YFFX") {
958
+ return this.agentId;
959
+ }
960
+ const defaultIdMapping = {
961
+ "business_analyst": "01KEJ95G304TNNAKGH5XNEEBVD",
962
+ "developer_agent": "01KEQCGJ65YENRA4QBXVN1YFFX",
963
+ "qa_agent": "01KEQFJZ3Q3JER11NH22HEZX9X",
964
+ "specification_agent": "01KEPXTX37FTB4N672TZST4SGP",
965
+ "scan_agent": "01KEQ9AHWB550J2244YBH3QATN",
966
+ "code_review": ""
967
+ };
968
+ const resolved = defaultIdMapping[this.agentType];
969
+ if (this.agentType === "code_review") {
970
+ if (!resolved) {
971
+ throw new Error("Agent ID for 'code_review' is not configured.");
972
+ }
973
+ return resolved;
974
+ }
975
+ return resolved || "01KEQCGJ65YENRA4QBXVN1YFFX";
976
+ }
977
+ getAgentVersion() {
978
+ const config = ConfigManager.getInstance().getConfig();
979
+ const versionMapping = {
980
+ "business_analyst": config.agentVersions?.ba || process.env.STACKSPOT_BA_AGENT_VERSION,
981
+ "developer_agent": config.agentVersions?.dev || process.env.STACKSPOT_DEV_AGENT_VERSION,
982
+ "qa_agent": config.agentVersions?.qa || process.env.STACKSPOT_QA_AGENT_VERSION,
983
+ "specification_agent": config.agentVersions?.spec || process.env.STACKSPOT_SPEC_AGENT_VERSION,
984
+ "scan_agent": config.agentVersions?.scan || process.env.STACKSPOT_SCAN_AGENT_VERSION,
985
+ "code_review": config.agentVersions?.codeReview || process.env.STACKSPOT_CODE_REVIEW_AGENT_VERSION
986
+ };
987
+ return versionMapping[this.agentType];
988
+ }
989
+ async streamChat(prompt, options) {
990
+ const realm = await getActiveRealm();
991
+ let token = null;
992
+ try {
993
+ token = await ensureValidToken(realm);
994
+ } catch (error) {
995
+ token = await tokenStorage.getToken(realm);
996
+ }
997
+ if (!token) {
998
+ throw new Error(`No authentication token found for realm '${realm}'. Please run 'shark login'.`);
999
+ }
1000
+ const isFirstTurn = !options.conversationId;
1001
+ const finalPrompt = isFirstTurn ? `SYSTEM INSTRUCTIONS:
1002
+ ${UNIFIED_SYSTEM_PROMPT}
1003
+
1004
+ USER REQUEST:
1005
+ ${prompt}` : prompt;
1006
+ const requestPayload = {
1007
+ user_prompt: finalPrompt,
1008
+ streaming: true,
1009
+ stackspot_knowledge: false,
1010
+ return_ks_in_response: true,
1011
+ deep_search_ks: false,
1012
+ use_conversation: true,
1013
+ conversation_id: options.conversationId
1014
+ };
1015
+ const agentVersion = this.getAgentVersion();
1016
+ if (agentVersion) {
1017
+ requestPayload.agent_version_number = agentVersion;
1018
+ }
1019
+ const effectiveAgentId = this.getAgentId();
1020
+ const agentUrl = `${STACKSPOT_AGENT_API_BASE}/v1/agent/${effectiveAgentId}/chat`;
1021
+ const headers = {
1022
+ "Authorization": `Bearer ${token}`,
1023
+ "Content-Type": "application/json"
1024
+ };
1025
+ const sanitizedHeaders = { ...headers };
1026
+ if (sanitizedHeaders["Authorization"]) {
1027
+ sanitizedHeaders["Authorization"] = "Bearer ***";
1028
+ }
1029
+ FileLogger.log("PROVIDER_REQUEST", "Request payload sent to StackSpot API", {
1030
+ agentId: effectiveAgentId,
1031
+ url: agentUrl,
1032
+ headers: sanitizedHeaders,
1033
+ payload: requestPayload
1034
+ });
1035
+ let fullMessage = "";
1036
+ let rawResponse = {};
1037
+ await sseClient.streamAgentResponse(
1038
+ agentUrl,
1039
+ requestPayload,
1040
+ headers,
1041
+ {
1042
+ onChunk: (chunk) => {
1043
+ fullMessage += chunk;
1044
+ if (options.onChunk) {
1045
+ options.onChunk(chunk);
1046
+ }
1047
+ },
1048
+ onComplete: async (message, metadata) => {
1049
+ rawResponse = {
1050
+ message: message || fullMessage,
1051
+ conversation_id: metadata?.conversation_id || options.conversationId
1052
+ };
1053
+ },
1054
+ onError: (error) => {
1055
+ throw error;
1056
+ }
1057
+ }
1058
+ );
1059
+ FileLogger.log("PROVIDER_RESPONSE", "Raw response from StackSpot API", { rawResponse });
1060
+ const parsedResponse = parseAgentResponse(rawResponse);
1061
+ if (options.onComplete) {
1062
+ options.onComplete(parsedResponse);
1063
+ }
1064
+ return parsedResponse;
1065
+ }
1066
+ };
1067
+
1068
+ // src/core/workflow/history-manager.ts
1069
+ import fs3 from "fs";
1070
+ import path3 from "path";
1071
+ var HistoryManager = class {
1072
+ static getHistoryDir() {
1073
+ const dir = path3.resolve(process.cwd(), "_sharkrc", "history");
1074
+ if (!fs3.existsSync(dir)) {
1075
+ fs3.mkdirSync(dir, { recursive: true });
1076
+ }
1077
+ return dir;
1078
+ }
1079
+ static getFilePath(conversationId) {
1080
+ return path3.resolve(this.getHistoryDir(), `${conversationId}.json`);
1081
+ }
1082
+ static async getHistory(conversationId) {
1083
+ const filePath = this.getFilePath(conversationId);
1084
+ if (!fs3.existsSync(filePath)) {
1085
+ return [];
1086
+ }
1087
+ try {
1088
+ const raw = fs3.readFileSync(filePath, "utf-8");
1089
+ const parsed = JSON.parse(raw);
1090
+ return Array.isArray(parsed) ? parsed : [];
1091
+ } catch {
1092
+ return [];
1093
+ }
1094
+ }
1095
+ static async saveHistory(conversationId, messages) {
1096
+ const filePath = this.getFilePath(conversationId);
1097
+ fs3.writeFileSync(filePath, JSON.stringify(messages, null, 2), "utf-8");
1098
+ }
1099
+ static async appendMessage(conversationId, message) {
1100
+ const history = await this.getHistory(conversationId);
1101
+ history.push(message);
1102
+ await this.saveHistory(conversationId, history);
1103
+ }
1104
+ };
1105
+
1106
+ // src/core/api/openai-compatible-provider.ts
1107
+ import crypto from "crypto";
1108
+ var OpenAICompatibleProvider = class {
1109
+ constructor(options) {
1110
+ this.options = options;
1111
+ }
1112
+ getAgentSystemPrompt(agentType) {
1113
+ return UNIFIED_SYSTEM_PROMPT;
1114
+ }
1115
+ async streamChat(prompt, options) {
1116
+ const conversationId = options.conversationId || crypto.randomUUID();
1117
+ const history = await HistoryManager.getHistory(conversationId);
1118
+ if (history.length === 0) {
1119
+ history.push({
1120
+ role: "system",
1121
+ content: this.getAgentSystemPrompt(options.agentType)
1122
+ });
1123
+ }
1124
+ history.push({ role: "user", content: prompt });
1125
+ const requestPayload = {
1126
+ model: this.options.model,
1127
+ messages: history,
1128
+ stream: true,
1129
+ temperature: 0.2
1130
+ };
1131
+ if (this.options.useStructuredOutputs) {
1132
+ requestPayload.response_format = {
1133
+ type: "json_schema",
1134
+ json_schema: {
1135
+ name: "agent_response",
1136
+ strict: true,
1137
+ schema: {
1138
+ type: "object",
1139
+ properties: {
1140
+ action: {
1141
+ type: "object",
1142
+ properties: {
1143
+ type: {
1144
+ type: "string",
1145
+ enum: [
1146
+ "create_file",
1147
+ "modify_file",
1148
+ "read_file",
1149
+ "list_files",
1150
+ "search_file",
1151
+ "search_code",
1152
+ "delete_file",
1153
+ "run_command",
1154
+ "talk_with_user",
1155
+ "use_mcp_tool",
1156
+ "activate_skill",
1157
+ "define_subagent",
1158
+ "invoke_subagent",
1159
+ "send_message",
1160
+ "manage_subagents",
1161
+ "complete_task",
1162
+ "list_structure",
1163
+ "modify_ast",
1164
+ "search_ast",
1165
+ "ast_list_structure",
1166
+ "ast_get_method",
1167
+ "ast_add_method",
1168
+ "ast_modify_method",
1169
+ "ast_remove_method",
1170
+ "ast_add_class",
1171
+ "ast_get_property",
1172
+ "ast_add_property",
1173
+ "ast_modify_property",
1174
+ "ast_remove_property",
1175
+ "ast_add_decorator",
1176
+ "ast_add_interface",
1177
+ "ast_add_type_alias",
1178
+ "ast_add_function",
1179
+ "ast_remove_function",
1180
+ "ast_add_import",
1181
+ "ast_remove_import",
1182
+ "ast_organize_imports"
1183
+ ]
1184
+ },
1185
+ path: { type: ["string", "null"] },
1186
+ content: { type: ["string", "null"] },
1187
+ start_anchor: { type: ["string", "null"] },
1188
+ end_anchor: { type: ["string", "null"] },
1189
+ command: { type: ["string", "null"] },
1190
+ query: { type: ["string", "null"] },
1191
+ tool_name: { type: ["string", "null"] },
1192
+ tool_args: { type: ["string", "null"] },
1193
+ line_range: {
1194
+ type: ["array", "null"],
1195
+ items: { type: "number" }
1196
+ },
1197
+ target_content: { type: ["string", "null"] },
1198
+ is_regex: { type: ["boolean", "null"] },
1199
+ pattern: { type: ["string", "null"] },
1200
+ fix: { type: ["string", "null"] },
1201
+ language: { type: ["string", "null"] },
1202
+ file_path: { type: ["string", "null"] },
1203
+ class_name: { type: ["string", "null"] },
1204
+ method_name: { type: ["string", "null"] },
1205
+ method_code: { type: ["string", "null"] },
1206
+ property_name: { type: ["string", "null"] },
1207
+ property_code: { type: ["string", "null"] },
1208
+ extends_class: { type: ["string", "null"] },
1209
+ implements_interfaces: {
1210
+ type: ["array", "null"],
1211
+ items: { type: "string" }
1212
+ },
1213
+ decorator_code: { type: ["string", "null"] },
1214
+ interface_code: { type: ["string", "null"] },
1215
+ type_code: { type: ["string", "null"] },
1216
+ function_name: { type: ["string", "null"] },
1217
+ function_code: { type: ["string", "null"] },
1218
+ import_statement: { type: ["string", "null"] },
1219
+ module_path: { type: ["string", "null"] },
1220
+ new_body: { type: ["string", "null"] },
1221
+ confirmed: { type: ["boolean", "null"] },
1222
+ skill_name: { type: ["string", "null"] },
1223
+ Subagents: {
1224
+ type: ["array", "null"],
1225
+ items: {
1226
+ type: "object",
1227
+ properties: {
1228
+ TypeName: { type: "string" },
1229
+ Role: { type: "string" },
1230
+ Prompt: { type: "string" }
1231
+ },
1232
+ required: ["TypeName", "Role", "Prompt"],
1233
+ additionalProperties: false
1234
+ }
1235
+ },
1236
+ Recipient: { type: ["string", "null"] },
1237
+ Message: { type: ["string", "null"] },
1238
+ Action: { type: ["string", "null"] },
1239
+ ConversationIds: {
1240
+ type: ["array", "null"],
1241
+ items: { type: "string" }
1242
+ },
1243
+ name: { type: ["string", "null"] },
1244
+ description: { type: ["string", "null"] },
1245
+ system_prompt: { type: ["string", "null"] },
1246
+ enable_write_tools: { type: ["boolean", "null"] },
1247
+ enable_subagent_tools: { type: ["boolean", "null"] },
1248
+ enable_mcp_tools: { type: ["boolean", "null"] }
1249
+ },
1250
+ required: [
1251
+ "type",
1252
+ "path",
1253
+ "content",
1254
+ "start_anchor",
1255
+ "end_anchor",
1256
+ "command",
1257
+ "query",
1258
+ "tool_name",
1259
+ "tool_args",
1260
+ "line_range",
1261
+ "target_content",
1262
+ "is_regex",
1263
+ "pattern",
1264
+ "fix",
1265
+ "language",
1266
+ "file_path",
1267
+ "class_name",
1268
+ "method_name",
1269
+ "method_code",
1270
+ "property_name",
1271
+ "property_code",
1272
+ "extends_class",
1273
+ "implements_interfaces",
1274
+ "decorator_code",
1275
+ "interface_code",
1276
+ "type_code",
1277
+ "function_name",
1278
+ "function_code",
1279
+ "import_statement",
1280
+ "module_path",
1281
+ "new_body",
1282
+ "confirmed",
1283
+ "skill_name",
1284
+ "Subagents",
1285
+ "Recipient",
1286
+ "Message",
1287
+ "Action",
1288
+ "ConversationIds",
1289
+ "name",
1290
+ "description",
1291
+ "system_prompt",
1292
+ "enable_write_tools",
1293
+ "enable_subagent_tools",
1294
+ "enable_mcp_tools"
1295
+ ],
1296
+ additionalProperties: false
1297
+ },
1298
+ summary: { type: "string" }
1299
+ },
1300
+ required: ["action", "summary"],
1301
+ additionalProperties: false
1302
+ }
1303
+ }
1304
+ };
1305
+ } else {
1306
+ requestPayload.response_format = { type: "json_object" };
1307
+ }
1308
+ const headers = {
1309
+ "Content-Type": "application/json"
1310
+ };
1311
+ if (this.options.apiKey) {
1312
+ headers["Authorization"] = `Bearer ${this.options.apiKey}`;
1313
+ }
1314
+ const sanitizedHeaders = { ...headers };
1315
+ if (sanitizedHeaders["Authorization"]) {
1316
+ sanitizedHeaders["Authorization"] = "Bearer ***";
1317
+ }
1318
+ FileLogger.log("PROVIDER_REQUEST", "Request payload sent to OpenAI Compatible API", {
1319
+ baseURL: this.options.baseURL,
1320
+ headers: sanitizedHeaders,
1321
+ payload: requestPayload
1322
+ });
1323
+ const res = await fetch(`${this.options.baseURL}/chat/completions`, {
1324
+ method: "POST",
1325
+ headers,
1326
+ body: JSON.stringify(requestPayload)
1327
+ });
1328
+ if (!res.ok) {
1329
+ const errBody = await res.text();
1330
+ throw new Error(`OpenAI API request failed: ${res.status} ${res.statusText} - ${errBody}`);
1331
+ }
1332
+ const reader = res.body?.getReader();
1333
+ if (!reader) {
1334
+ throw new Error("Response body reader is undefined");
1335
+ }
1336
+ try {
1337
+ const decoder = new TextDecoder();
1338
+ let fullContent = "";
1339
+ let done = false;
1340
+ let buffer = "";
1341
+ while (!done) {
1342
+ const { value, done: doneReading } = await reader.read();
1343
+ done = doneReading;
1344
+ if (value) {
1345
+ buffer += decoder.decode(value, { stream: !done });
1346
+ const lines = buffer.split("\n");
1347
+ buffer = lines.pop() || "";
1348
+ for (const line of lines) {
1349
+ const clean = line.trim();
1350
+ if (!clean || clean === "data: [DONE]") continue;
1351
+ if (clean.startsWith("data: ")) {
1352
+ let parsed;
1353
+ try {
1354
+ parsed = JSON.parse(clean.substring(6));
1355
+ } catch {
1356
+ continue;
1357
+ }
1358
+ if (parsed && parsed.error) {
1359
+ throw new Error(`OpenAI Stream Error: ${JSON.stringify(parsed.error)}`);
1360
+ }
1361
+ const delta = parsed?.choices?.[0]?.delta?.content || "";
1362
+ if (delta) {
1363
+ fullContent += delta;
1364
+ if (options.onChunk) {
1365
+ options.onChunk(delta);
1366
+ }
1367
+ }
1368
+ }
1369
+ }
1370
+ }
1371
+ }
1372
+ if (buffer) {
1373
+ const clean = buffer.trim();
1374
+ if (clean && clean !== "data: [DONE]" && clean.startsWith("data: ")) {
1375
+ let parsed;
1376
+ try {
1377
+ parsed = JSON.parse(clean.substring(6));
1378
+ } catch {
1379
+ }
1380
+ if (parsed && parsed.error) {
1381
+ throw new Error(`OpenAI Stream Error: ${JSON.stringify(parsed.error)}`);
1382
+ }
1383
+ const delta = parsed?.choices?.[0]?.delta?.content || "";
1384
+ if (delta) {
1385
+ fullContent += delta;
1386
+ if (options.onChunk) {
1387
+ options.onChunk(delta);
1388
+ }
1389
+ }
1390
+ }
1391
+ }
1392
+ FileLogger.log("PROVIDER_RESPONSE", "Raw response from OpenAI Compatible API", { fullContent });
1393
+ const parsedResponse = parseAgentResponse(fullContent);
1394
+ parsedResponse.conversation_id = conversationId;
1395
+ history.push({ role: "assistant", content: JSON.stringify(parsedResponse) });
1396
+ await HistoryManager.saveHistory(conversationId, history);
1397
+ if (options.onComplete) {
1398
+ options.onComplete(parsedResponse);
1399
+ }
1400
+ return parsedResponse;
1401
+ } finally {
1402
+ if (typeof reader.releaseLock === "function") {
1403
+ reader.releaseLock();
1404
+ }
1405
+ }
1406
+ }
1407
+ };
1408
+
1409
+ // src/core/api/provider-resolver.ts
1410
+ var ProviderResolver = class {
1411
+ static getProvider(agentType) {
1412
+ const config = ConfigManager.getInstance().getConfig();
1413
+ if (config.provider === "openai-compatible") {
1414
+ const opt = config["openai-compatible"] || {};
1415
+ return new OpenAICompatibleProvider({
1416
+ baseURL: opt.baseURL || "http://localhost:11434/v1",
1417
+ apiKey: opt.apiKey || "ollama",
1418
+ model: opt.model || "llama3",
1419
+ useStructuredOutputs: opt.useStructuredOutputs ?? true
1420
+ });
1421
+ }
1422
+ return new StackSpotProvider(agentType);
1423
+ }
1424
+ };
1425
+
1426
+ // src/core/workflow/conversation-manager.ts
1427
+ var ConversationManager = class _ConversationManager {
1428
+ static instance;
1429
+ constructor() {
1430
+ }
1431
+ static getInstance() {
1432
+ if (!_ConversationManager.instance) {
1433
+ _ConversationManager.instance = new _ConversationManager();
1434
+ }
1435
+ return _ConversationManager.instance;
1436
+ }
1437
+ /**
1438
+ * Saves a conversation ID for a specific agent type.
1439
+ *
1440
+ * @param agentType - The type of agent (e.g., 'business_analyst', 'architect')
1441
+ * @param conversationId - The conversation ID from the agent response
1442
+ */
1443
+ async saveConversationId(agentType, conversationId) {
1444
+ const state = await workflowManager.load();
1445
+ if (!state) {
1446
+ throw new Error('No workflow state found. Please run "shark init" first.');
1447
+ }
1448
+ if (!state.conversations) {
1449
+ state.conversations = {};
1450
+ }
1451
+ state.conversations[agentType] = conversationId;
1452
+ await workflowManager.save(state);
1453
+ }
1454
+ /**
1455
+ * Retrieves the conversation ID for a specific agent type.
1456
+ *
1457
+ * @param agentType - The type of agent
1458
+ * @returns The conversation ID, or undefined if none exists
1459
+ */
1460
+ async getConversationId(agentType) {
1461
+ const state = await workflowManager.load();
1462
+ if (!state || !state.conversations) {
1463
+ return void 0;
1464
+ }
1465
+ return state.conversations[agentType];
1466
+ }
1467
+ /**
1468
+ * Clears the conversation ID for a specific agent type.
1469
+ *
1470
+ * @param agentType - The type of agent
1471
+ */
1472
+ async clearConversationId(agentType) {
1473
+ const state = await workflowManager.load();
1474
+ if (!state || !state.conversations) {
1475
+ return;
1476
+ }
1477
+ delete state.conversations[agentType];
1478
+ await workflowManager.save(state);
1479
+ }
1480
+ /**
1481
+ * Clears all conversation IDs.
1482
+ * Useful when transitioning to a new project or resetting state.
1483
+ */
1484
+ async clearAllConversations() {
1485
+ const state = await workflowManager.load();
1486
+ if (!state) {
1487
+ return;
1488
+ }
1489
+ state.conversations = {};
1490
+ await workflowManager.save(state);
1491
+ }
1492
+ };
1493
+ var conversationManager = ConversationManager.getInstance();
1494
+
1495
+ // src/core/workflow/anchor-state-manager.ts
1496
+ import fs4 from "fs";
1497
+ import path4 from "path";
1498
+ import { diffLines } from "diff";
1499
+ var AnchorStateManager = class {
1500
+ cache = /* @__PURE__ */ new Map();
1501
+ wordPool = [];
1502
+ constructor() {
1503
+ this.initializeWordPool();
1504
+ }
1505
+ initializeWordPool() {
1506
+ this.wordPool = [
1507
+ // group 1
1508
+ "apple",
1509
+ "beach",
1510
+ "cabin",
1511
+ "dust",
1512
+ "edge",
1513
+ "stone",
1514
+ "river",
1515
+ "tree",
1516
+ "leaf",
1517
+ "flower",
1518
+ "grass",
1519
+ "seed",
1520
+ "soil",
1521
+ "sand",
1522
+ "rock",
1523
+ "hill",
1524
+ "path",
1525
+ "road",
1526
+ "street",
1527
+ "house",
1528
+ "room",
1529
+ "door",
1530
+ "window",
1531
+ "wall",
1532
+ "roof",
1533
+ "floor",
1534
+ "desk",
1535
+ "chair",
1536
+ "table",
1537
+ "book",
1538
+ "pen",
1539
+ "paper",
1540
+ "bag",
1541
+ "box",
1542
+ "cup",
1543
+ "plate",
1544
+ "bowl",
1545
+ "spoon",
1546
+ "fork",
1547
+ "knife",
1548
+ "food",
1549
+ "bread",
1550
+ "milk",
1551
+ "water",
1552
+ "tea",
1553
+ "fruit",
1554
+ "pear",
1555
+ "peach",
1556
+ "plum",
1557
+ "grape",
1558
+ "melon",
1559
+ "berry",
1560
+ "nut",
1561
+ "bean",
1562
+ "corn",
1563
+ "rice",
1564
+ "oat",
1565
+ "wheat",
1566
+ "fish",
1567
+ "bird",
1568
+ "cat",
1569
+ "dog",
1570
+ "cow",
1571
+ "sheep",
1572
+ "goat",
1573
+ "pig",
1574
+ "horse",
1575
+ "deer",
1576
+ "bear",
1577
+ "wolf",
1578
+ "fox",
1579
+ "lion",
1580
+ "tiger",
1581
+ "seal",
1582
+ "whale",
1583
+ "crab",
1584
+ "frog",
1585
+ "toad",
1586
+ "snake",
1587
+ "worm",
1588
+ "bee",
1589
+ "ant",
1590
+ "fly",
1591
+ "spider",
1592
+ "moth",
1593
+ "fern",
1594
+ "moss",
1595
+ "pine",
1596
+ "oak",
1597
+ "maple",
1598
+ "birch",
1599
+ "willow",
1600
+ "ash",
1601
+ "elm",
1602
+ "palm",
1603
+ "rose",
1604
+ "lily",
1605
+ "daisy",
1606
+ "tulip",
1607
+ "iris",
1608
+ // group 2
1609
+ "about",
1610
+ "above",
1611
+ "actor",
1612
+ "acute",
1613
+ "admit",
1614
+ "adopt",
1615
+ "adult",
1616
+ "after",
1617
+ "again",
1618
+ "agent",
1619
+ "agree",
1620
+ "ahead",
1621
+ "alarm",
1622
+ "album",
1623
+ "alert",
1624
+ "alike",
1625
+ "alive",
1626
+ "allow",
1627
+ "alone",
1628
+ "along",
1629
+ "alter",
1630
+ "among",
1631
+ "anger",
1632
+ "angle",
1633
+ "angry",
1634
+ "apart",
1635
+ "appeal",
1636
+ "apron",
1637
+ "arena",
1638
+ "argue",
1639
+ "arise",
1640
+ "array",
1641
+ "arrow",
1642
+ "aside",
1643
+ "asset",
1644
+ "audio",
1645
+ "audit",
1646
+ "avoid",
1647
+ "award",
1648
+ "aware",
1649
+ "awful",
1650
+ "back",
1651
+ "bacon",
1652
+ "badge",
1653
+ "baker",
1654
+ "ball",
1655
+ "band",
1656
+ "bank",
1657
+ "base",
1658
+ "basic",
1659
+ "basil",
1660
+ "basin",
1661
+ "basis",
1662
+ "bath",
1663
+ "baton",
1664
+ "bayou",
1665
+ "bead",
1666
+ "beak",
1667
+ "beam",
1668
+ "beast",
1669
+ "beat",
1670
+ "beauty",
1671
+ "beef",
1672
+ "beer",
1673
+ "beet",
1674
+ "begin",
1675
+ "begun",
1676
+ "being",
1677
+ "belief",
1678
+ "bell",
1679
+ "belly",
1680
+ "below",
1681
+ "bench",
1682
+ "bend",
1683
+ "best",
1684
+ "bible",
1685
+ "bike",
1686
+ "bill",
1687
+ "bind",
1688
+ "bingo",
1689
+ "birth",
1690
+ "bite",
1691
+ "black",
1692
+ "blade",
1693
+ "blame",
1694
+ "blank",
1695
+ "blast",
1696
+ "blaze",
1697
+ "bleed",
1698
+ "blend",
1699
+ "blind",
1700
+ "blink",
1701
+ "block",
1702
+ "blond",
1703
+ "blood",
1704
+ "bloom",
1705
+ "blow",
1706
+ "blue",
1707
+ "blunt",
1708
+ "blush",
1709
+ // group 3
1710
+ "board",
1711
+ "boast",
1712
+ "boat",
1713
+ "body",
1714
+ "boil",
1715
+ "bold",
1716
+ "bolt",
1717
+ "bomb",
1718
+ "bond",
1719
+ "bone",
1720
+ "bonus",
1721
+ "boom",
1722
+ "boost",
1723
+ "boot",
1724
+ "booth",
1725
+ "border",
1726
+ "bore",
1727
+ "boss",
1728
+ "both",
1729
+ "bough",
1730
+ "bound",
1731
+ "boy",
1732
+ "brace",
1733
+ "brain",
1734
+ "brake",
1735
+ "branch",
1736
+ "brand",
1737
+ "brass",
1738
+ "brave",
1739
+ "break",
1740
+ "breast",
1741
+ "breath",
1742
+ "breezy",
1743
+ "brick",
1744
+ "bride",
1745
+ "bridge",
1746
+ "brief",
1747
+ "bright",
1748
+ "brim",
1749
+ "bring",
1750
+ "brisk",
1751
+ "broad",
1752
+ "broil",
1753
+ "broke",
1754
+ "bronze",
1755
+ "brook",
1756
+ "broom",
1757
+ "broth",
1758
+ "brown",
1759
+ "brush",
1760
+ "bubble",
1761
+ "bucket",
1762
+ "buckle",
1763
+ "bud",
1764
+ "budget",
1765
+ "buffet",
1766
+ "bugle",
1767
+ "build",
1768
+ "built",
1769
+ "bulb",
1770
+ "bulk",
1771
+ "bull",
1772
+ "bullet",
1773
+ "bump",
1774
+ "bunch",
1775
+ "bundle",
1776
+ "bunk",
1777
+ "bunny",
1778
+ "burden",
1779
+ "bureau",
1780
+ "burn",
1781
+ "burst",
1782
+ "bush",
1783
+ "busy",
1784
+ "butler",
1785
+ "butter",
1786
+ "button",
1787
+ "buyer",
1788
+ "buzz",
1789
+ "cable",
1790
+ "cactus",
1791
+ "cage",
1792
+ "cake",
1793
+ "calf",
1794
+ "calm",
1795
+ "came",
1796
+ "camel",
1797
+ "camera",
1798
+ "camp",
1799
+ "canal",
1800
+ "candle",
1801
+ "candy",
1802
+ "cane",
1803
+ "canoe",
1804
+ "canopy",
1805
+ "canvas",
1806
+ "canyon",
1807
+ "cape",
1808
+ "capital",
1809
+ "captain",
1810
+ // group 4
1811
+ "car",
1812
+ "card",
1813
+ "care",
1814
+ "cargo",
1815
+ "carol",
1816
+ "carpet",
1817
+ "carrot",
1818
+ "carry",
1819
+ "cart",
1820
+ "carve",
1821
+ "case",
1822
+ "cash",
1823
+ "cask",
1824
+ "cast",
1825
+ "castle",
1826
+ "catch",
1827
+ "cater",
1828
+ "cattle",
1829
+ "cause",
1830
+ "cave",
1831
+ "caviar",
1832
+ "cavity",
1833
+ "cedar",
1834
+ "celery",
1835
+ "cell",
1836
+ "cellar",
1837
+ "cello",
1838
+ "cement",
1839
+ "census",
1840
+ "center",
1841
+ "cereal",
1842
+ "chain",
1843
+ "chalk",
1844
+ "chamber",
1845
+ "chance",
1846
+ "change",
1847
+ "channel",
1848
+ "chapel",
1849
+ "chapter",
1850
+ "char",
1851
+ "charcoal",
1852
+ "charge",
1853
+ "charm",
1854
+ "chart",
1855
+ "chase",
1856
+ "chasm",
1857
+ "cheap",
1858
+ "cheat",
1859
+ "check",
1860
+ "cheek",
1861
+ "cheer",
1862
+ "cheese",
1863
+ "chef",
1864
+ "cherry",
1865
+ "chess",
1866
+ "chest",
1867
+ "chew",
1868
+ "chick",
1869
+ "chief",
1870
+ "child",
1871
+ "chili",
1872
+ "chill",
1873
+ "chime",
1874
+ "chin",
1875
+ "china",
1876
+ "chip",
1877
+ "chirp",
1878
+ "chisel",
1879
+ "choir",
1880
+ "choke",
1881
+ "choose",
1882
+ "chop",
1883
+ "chord",
1884
+ "chore",
1885
+ "chorus",
1886
+ "chose",
1887
+ "chrome",
1888
+ "chubby",
1889
+ "chuck",
1890
+ "chunk",
1891
+ "church",
1892
+ "cider",
1893
+ "cigar",
1894
+ "cinder",
1895
+ "circle",
1896
+ "circus",
1897
+ "cite",
1898
+ "citizen",
1899
+ "city",
1900
+ "civic",
1901
+ // group 5
1902
+ "civil",
1903
+ "clad",
1904
+ "claim",
1905
+ "clam",
1906
+ "clamp",
1907
+ "clan",
1908
+ "clap",
1909
+ "clasp",
1910
+ "class",
1911
+ "clause",
1912
+ "claw",
1913
+ "clay",
1914
+ "clean",
1915
+ "clear",
1916
+ "cleat",
1917
+ "cleft",
1918
+ "clerk",
1919
+ "clever",
1920
+ "click",
1921
+ "client",
1922
+ "cliff",
1923
+ "climate",
1924
+ "climb",
1925
+ "cling",
1926
+ "clinic",
1927
+ "clip",
1928
+ "cloak",
1929
+ "clock",
1930
+ "clod",
1931
+ "clog",
1932
+ "clone",
1933
+ "close",
1934
+ "closet",
1935
+ "cloth",
1936
+ "cloud",
1937
+ "clove",
1938
+ "clown",
1939
+ "club",
1940
+ "cluck",
1941
+ "clue",
1942
+ "clump",
1943
+ "clumsy",
1944
+ "clung",
1945
+ "cluster",
1946
+ "coach",
1947
+ "coal",
1948
+ "coast",
1949
+ "coat",
1950
+ "cobalt",
1951
+ "cobra",
1952
+ "cobweb",
1953
+ "cocoa",
1954
+ "coconut",
1955
+ "cod",
1956
+ "code",
1957
+ "coffee",
1958
+ "coffin",
1959
+ "cog",
1960
+ "coil",
1961
+ "coin",
1962
+ "coke",
1963
+ "cold",
1964
+ "collar",
1965
+ "collie",
1966
+ "colony",
1967
+ "color",
1968
+ "colt",
1969
+ "column",
1970
+ "comb",
1971
+ "combat",
1972
+ "come",
1973
+ "comedy",
1974
+ "comet",
1975
+ "comfort",
1976
+ "comic",
1977
+ "comma",
1978
+ "common",
1979
+ "compact",
1980
+ "company",
1981
+ "compare",
1982
+ // group 6
1983
+ "compass",
1984
+ "compel",
1985
+ "complex",
1986
+ "comply",
1987
+ "comrade",
1988
+ "concise",
1989
+ "concrete",
1990
+ "condor",
1991
+ "cone",
1992
+ "confer",
1993
+ "conga",
1994
+ "conic",
1995
+ "connect",
1996
+ "consul",
1997
+ "contest",
1998
+ "context",
1999
+ "contract",
2000
+ "control",
2001
+ "convert",
2002
+ "convex",
2003
+ "convey",
2004
+ "convoy",
2005
+ "cook",
2006
+ "cookie",
2007
+ "cool",
2008
+ "coop",
2009
+ "cope",
2010
+ "copper",
2011
+ "copy",
2012
+ "coral",
2013
+ "cord",
2014
+ "core",
2015
+ "cork",
2016
+ "corner",
2017
+ "cornet",
2018
+ "corps",
2019
+ "cosmic",
2020
+ "cost",
2021
+ "costume",
2022
+ "cottage",
2023
+ "cotton",
2024
+ "couch",
2025
+ "cough",
2026
+ "could",
2027
+ "council",
2028
+ "counsel",
2029
+ "count",
2030
+ "counter",
2031
+ "country",
2032
+ "county",
2033
+ "coup",
2034
+ "couple",
2035
+ "courage",
2036
+ "course",
2037
+ "court",
2038
+ "cousin",
2039
+ "cove",
2040
+ "cover",
2041
+ "covet",
2042
+ "coward",
2043
+ "coyote",
2044
+ "crack",
2045
+ "cradle",
2046
+ "craft",
2047
+ "crag",
2048
+ "cram",
2049
+ "cramp",
2050
+ "cranberry",
2051
+ "crane",
2052
+ "crank",
2053
+ "crash",
2054
+ "crate",
2055
+ "crater",
2056
+ "cravat",
2057
+ "crave",
2058
+ "craw",
2059
+ "crawl",
2060
+ "crayon",
2061
+ "craze",
2062
+ "crazy",
2063
+ "creak",
2064
+ "cream",
2065
+ "create",
2066
+ "credit",
2067
+ "creed",
2068
+ "creek",
2069
+ "creep",
2070
+ "crepe",
2071
+ "cress",
2072
+ "crest",
2073
+ // group 7
2074
+ "crew",
2075
+ "crib",
2076
+ "cricket",
2077
+ "cried",
2078
+ "crier",
2079
+ "crime",
2080
+ "crimson",
2081
+ "cringe",
2082
+ "cripple",
2083
+ "crisis",
2084
+ "crisp",
2085
+ "critic",
2086
+ "croak",
2087
+ "crock",
2088
+ "crocus",
2089
+ "crony",
2090
+ "crook",
2091
+ "crop",
2092
+ "cross",
2093
+ "croup",
2094
+ "crow",
2095
+ "crowd",
2096
+ "crown",
2097
+ "crude",
2098
+ "cruel",
2099
+ "crumb",
2100
+ "crumple",
2101
+ "crush",
2102
+ "crust",
2103
+ "crutch",
2104
+ "cry",
2105
+ "crypt",
2106
+ "crystal",
2107
+ "cub",
2108
+ "cube",
2109
+ "cuckoo",
2110
+ "cucumber",
2111
+ "cuff",
2112
+ "cult",
2113
+ "culture",
2114
+ "cupboard",
2115
+ "curb",
2116
+ "curd",
2117
+ "cure",
2118
+ "curfew",
2119
+ "curl",
2120
+ "currant",
2121
+ "current",
2122
+ "curry",
2123
+ "curse",
2124
+ "curve",
2125
+ "cushion",
2126
+ "custard",
2127
+ "custom",
2128
+ "cut",
2129
+ "cute",
2130
+ "cutter",
2131
+ "cycle",
2132
+ "cyclone",
2133
+ "cynic",
2134
+ "cypress",
2135
+ "dad",
2136
+ "dagger",
2137
+ "daily",
2138
+ "dairy",
2139
+ "dale",
2140
+ "dally",
2141
+ "dam",
2142
+ "damage",
2143
+ "dame",
2144
+ "damp",
2145
+ "dance",
2146
+ "dandy",
2147
+ "danger",
2148
+ "dapple",
2149
+ "dare",
2150
+ "dark",
2151
+ "darling",
2152
+ "darn",
2153
+ "dart",
2154
+ // group 8
2155
+ "dash",
2156
+ "date",
2157
+ "datum",
2158
+ "daub",
2159
+ "daughter",
2160
+ "dawn",
2161
+ "daze",
2162
+ "dazzle",
2163
+ "deacon",
2164
+ "dead",
2165
+ "deaf",
2166
+ "deal",
2167
+ "dealer",
2168
+ "dean",
2169
+ "dear",
2170
+ "death",
2171
+ "debar",
2172
+ "debate",
2173
+ "debit",
2174
+ "debris",
2175
+ "debt",
2176
+ "decade",
2177
+ "decay",
2178
+ "decent",
2179
+ "decide",
2180
+ "deck",
2181
+ "declare",
2182
+ "decline",
2183
+ "decor",
2184
+ "decoy",
2185
+ "decrease",
2186
+ "decree",
2187
+ "deduct",
2188
+ "deed",
2189
+ "deep",
2190
+ "defeat",
2191
+ "defect",
2192
+ "defend",
2193
+ "defer",
2194
+ "deficit",
2195
+ "defile",
2196
+ "define",
2197
+ "deform",
2198
+ "defray",
2199
+ "defy",
2200
+ "degree",
2201
+ "delay",
2202
+ "delegate",
2203
+ "delight",
2204
+ "deliver",
2205
+ "dell",
2206
+ "delta",
2207
+ "deluge",
2208
+ "delve",
2209
+ "demand",
2210
+ "demean",
2211
+ "demerit",
2212
+ "demise",
2213
+ "demo",
2214
+ "demote",
2215
+ "demur",
2216
+ "den",
2217
+ "denial",
2218
+ "denote",
2219
+ "denounce",
2220
+ "dense",
2221
+ "density",
2222
+ "dent",
2223
+ "dental",
2224
+ "dentist",
2225
+ "deny",
2226
+ "depart",
2227
+ "depend",
2228
+ "depict",
2229
+ "deplore",
2230
+ "deport",
2231
+ "depose",
2232
+ "deposit",
2233
+ // group 9
2234
+ "depot",
2235
+ "depth",
2236
+ "deputy",
2237
+ "derby",
2238
+ "derive",
2239
+ "dervish",
2240
+ "descend",
2241
+ "descent",
2242
+ "describe",
2243
+ "desert",
2244
+ "deserve",
2245
+ "design",
2246
+ "desire",
2247
+ "desolate",
2248
+ "despair",
2249
+ "despise",
2250
+ "despite",
2251
+ "despot",
2252
+ "dessert",
2253
+ "destiny",
2254
+ "destroy",
2255
+ "detach",
2256
+ "detail",
2257
+ "detain",
2258
+ "detect",
2259
+ "deter",
2260
+ "detest",
2261
+ "detour",
2262
+ "deuce",
2263
+ "develop",
2264
+ "deviate",
2265
+ "device",
2266
+ "devil",
2267
+ "devise",
2268
+ "devoid",
2269
+ "devote",
2270
+ "devour",
2271
+ "devout",
2272
+ "dew",
2273
+ "diagram",
2274
+ "dial",
2275
+ "dialect",
2276
+ "dialogue",
2277
+ "diameter",
2278
+ "diamond",
2279
+ "diary",
2280
+ "dice",
2281
+ "dictate",
2282
+ "diction",
2283
+ "dictionary",
2284
+ "did",
2285
+ "die",
2286
+ "diet",
2287
+ "differ",
2288
+ "difficult",
2289
+ "diffuse",
2290
+ "dig",
2291
+ "digest",
2292
+ "digger",
2293
+ "digit",
2294
+ "dignity",
2295
+ "dike",
2296
+ "dilute",
2297
+ "dim",
2298
+ "dime",
2299
+ "diminish",
2300
+ "dimple",
2301
+ "din",
2302
+ "dine",
2303
+ "diner",
2304
+ "dinghy",
2305
+ "dingle",
2306
+ "dinner",
2307
+ "dint",
2308
+ "dip",
2309
+ "diphthong",
2310
+ "diploma",
2311
+ "dire",
2312
+ "direct",
2313
+ "director",
2314
+ // group 10
2315
+ "dirge",
2316
+ "dirk",
2317
+ "dirt",
2318
+ "dirty",
2319
+ "disable",
2320
+ "disarm",
2321
+ "disaster",
2322
+ "disavow",
2323
+ "disband",
2324
+ "discard",
2325
+ "discern",
2326
+ "discharge",
2327
+ "disciple",
2328
+ "discipline",
2329
+ "disclose",
2330
+ "discomfit",
2331
+ "discord",
2332
+ "discount",
2333
+ "discourse",
2334
+ "discover",
2335
+ "discreet",
2336
+ "discrepant",
2337
+ "discretion",
2338
+ "discuss",
2339
+ "disdain",
2340
+ "disease",
2341
+ "disfavor",
2342
+ "disfigure",
2343
+ "disgrace",
2344
+ "disguise",
2345
+ "disgust",
2346
+ "dish",
2347
+ "dishevel",
2348
+ "dishonest",
2349
+ "dishonor",
2350
+ "disinfect",
2351
+ "disinherit",
2352
+ "disintegrate",
2353
+ "dislike",
2354
+ "dislocate",
2355
+ "dislodge",
2356
+ "disloyal",
2357
+ "dismal",
2358
+ "dismantle",
2359
+ "dismay",
2360
+ "dismember",
2361
+ "dismiss",
2362
+ "dismount",
2363
+ "disobey",
2364
+ "disorder",
2365
+ "disown",
2366
+ "disparage",
2367
+ "disparate",
2368
+ "disparity",
2369
+ "dispatch",
2370
+ "dispel",
2371
+ "dispense",
2372
+ "disperse",
2373
+ "displace",
2374
+ "display",
2375
+ "displease",
2376
+ "dispose",
2377
+ "disprove",
2378
+ "dispipe",
2379
+ "disqualify",
2380
+ "disquiet",
2381
+ "disregard",
2382
+ "disrepute",
2383
+ "disrespect",
2384
+ "disrobe",
2385
+ "disrupt",
2386
+ "dissatisfy",
2387
+ "dissect",
2388
+ "dissemble",
2389
+ "disseminate",
2390
+ "dissent",
2391
+ "dissertation",
2392
+ "disservice",
2393
+ "dissident",
2394
+ "dissimilar",
2395
+ "dissipate",
2396
+ "dissolve",
2397
+ "dissonant",
2398
+ "dissuade",
2399
+ "distance",
2400
+ "distant",
2401
+ "distaste",
2402
+ "distemper",
2403
+ "distend",
2404
+ "distich",
2405
+ "distill",
2406
+ "distinct",
2407
+ "distinguish",
2408
+ "distort",
2409
+ "distract",
2410
+ "distrain",
2411
+ "distress",
2412
+ "distribute",
2413
+ "district",
2414
+ "distrust",
2415
+ "disturb",
2416
+ "disunion",
2417
+ "disuse",
2418
+ "ditch",
2419
+ "ditty",
2420
+ "diurnal",
2421
+ "divan",
2422
+ "dive",
2423
+ "diverge",
2424
+ "diverse",
2425
+ "diversion",
2426
+ "diversity",
2427
+ "divert",
2428
+ "divest",
2429
+ "divide",
2430
+ "dividend",
2431
+ "divine",
2432
+ "diviner",
2433
+ "divinity",
2434
+ "divisible",
2435
+ "division",
2436
+ "divisor",
2437
+ "divorce",
2438
+ "divulge",
2439
+ "dizzy",
2440
+ "do",
2441
+ "docile",
2442
+ "dock",
2443
+ "doctor",
2444
+ "doctrine",
2445
+ "document",
2446
+ "dodge",
2447
+ "doe",
2448
+ "doer",
2449
+ "dogma",
2450
+ "dogmatic",
2451
+ "dole",
2452
+ "doll",
2453
+ "dollar",
2454
+ "domain",
2455
+ "dome",
2456
+ "domestic",
2457
+ "domicile",
2458
+ "dominant",
2459
+ "dominate",
2460
+ "domineer",
2461
+ "dominion",
2462
+ "domino",
2463
+ "don",
2464
+ "donation",
2465
+ "done",
2466
+ "donkey",
2467
+ "donor",
2468
+ "doom",
2469
+ "dormant",
2470
+ "dormitory",
2471
+ "dormouse",
2472
+ "dose",
2473
+ "dot",
2474
+ "double",
2475
+ "doublet",
2476
+ "doubt",
2477
+ "doubtful",
2478
+ "dough",
2479
+ "doughnut",
2480
+ "doughty",
2481
+ "dour",
2482
+ "douse",
2483
+ "dove",
2484
+ "dowager",
2485
+ "dowdy",
2486
+ "dower",
2487
+ "downcast",
2488
+ "downfall",
2489
+ "downright",
2490
+ "downy",
2491
+ "dowry"
2492
+ ];
2493
+ }
2494
+ allocateAnchor(usedAnchors) {
2495
+ for (const word of this.wordPool) {
2496
+ if (!usedAnchors.has(word)) {
2497
+ usedAnchors.add(word);
2498
+ return word;
2499
+ }
2500
+ }
2501
+ let counter = 1;
2502
+ while (true) {
2503
+ const word = `anchor_${counter}`;
2504
+ if (!usedAnchors.has(word)) {
2505
+ usedAnchors.add(word);
2506
+ return word;
2507
+ }
2508
+ counter++;
2509
+ }
2510
+ }
2511
+ getAnchoredContent(filePath) {
2512
+ const absolutePath = path4.resolve(filePath);
2513
+ let lineStates = this.cache.get(absolutePath);
2514
+ if (!lineStates) {
2515
+ const content = fs4.readFileSync(absolutePath, "utf8");
2516
+ const hasTrailingNewline = content.endsWith("\n");
2517
+ const lines = hasTrailingNewline ? content.slice(0, -1).split("\n") : content.split("\n");
2518
+ const usedAnchors = /* @__PURE__ */ new Set();
2519
+ lineStates = lines.map((line) => {
2520
+ const anchor = this.allocateAnchor(usedAnchors);
2521
+ return { anchor, text: line };
2522
+ });
2523
+ this.cache.set(absolutePath, lineStates);
2524
+ }
2525
+ return lineStates.map((ls) => `${ls.anchor}\xA7${ls.text}`).join("\n");
2526
+ }
2527
+ applyAnchoredEdit(filePath, startAnchor, endAnchor, content) {
2528
+ const absolutePath = path4.resolve(filePath);
2529
+ let lineStates = this.cache.get(absolutePath);
2530
+ if (!lineStates) {
2531
+ this.getAnchoredContent(absolutePath);
2532
+ lineStates = this.cache.get(absolutePath);
2533
+ }
2534
+ const startIndex = lineStates.findIndex((ls) => ls.anchor === startAnchor);
2535
+ if (startIndex === -1) {
2536
+ throw new Error(`Start anchor "${startAnchor}" not found`);
2537
+ }
2538
+ const endIndex = lineStates.findIndex((ls) => ls.anchor === endAnchor);
2539
+ if (endIndex === -1) {
2540
+ throw new Error(`End anchor "${endAnchor}" not found`);
2541
+ }
2542
+ if (startIndex > endIndex) {
2543
+ throw new Error(`Invalid range: start anchor "${startAnchor}" is after end anchor "${endAnchor}"`);
2544
+ }
2545
+ const originalContent = fs4.readFileSync(absolutePath, "utf8");
2546
+ const hasTrailingNewline = originalContent.endsWith("\n");
2547
+ const oldLines = lineStates.map((ls) => ls.text);
2548
+ const cleanContent = content.endsWith("\n") ? content.slice(0, -1) : content;
2549
+ const newEditLines = cleanContent.split("\n");
2550
+ const updatedLines = [
2551
+ ...oldLines.slice(0, startIndex),
2552
+ ...newEditLines,
2553
+ ...oldLines.slice(endIndex + 1)
2554
+ ];
2555
+ const fileContentToWrite = updatedLines.join("\n") + (hasTrailingNewline ? "\n" : "");
2556
+ fs4.writeFileSync(absolutePath, fileContentToWrite, "utf8");
2557
+ const usedAnchors = /* @__PURE__ */ new Set();
2558
+ for (let i = 0; i < lineStates.length; i++) {
2559
+ usedAnchors.add(lineStates[i].anchor);
2560
+ }
2561
+ const oldEditSegment = lineStates.slice(startIndex, endIndex + 1);
2562
+ const oldEditLines = oldEditSegment.map((ls) => ls.text);
2563
+ const diffs = diffLines(oldEditLines.join("\n"), cleanContent);
2564
+ const reconciledEditStates = [];
2565
+ let oldEditIndex = 0;
2566
+ for (const change of diffs) {
2567
+ const changeLines = change.value.split("\n");
2568
+ if (changeLines.length > 1 && changeLines[changeLines.length - 1] === "") {
2569
+ changeLines.pop();
2570
+ }
2571
+ if (change.removed) {
2572
+ oldEditIndex += changeLines.length;
2573
+ } else if (change.added) {
2574
+ for (const line of changeLines) {
2575
+ const anchor = this.allocateAnchor(usedAnchors);
2576
+ reconciledEditStates.push({ anchor, text: line });
2577
+ }
2578
+ } else {
2579
+ for (let i = 0; i < changeLines.length; i++) {
2580
+ const oldLs = oldEditSegment[oldEditIndex];
2581
+ if (oldLs) {
2582
+ reconciledEditStates.push({ anchor: oldLs.anchor, text: oldLs.text });
2583
+ usedAnchors.add(oldLs.anchor);
2584
+ } else {
2585
+ const anchor = this.allocateAnchor(usedAnchors);
2586
+ reconciledEditStates.push({ anchor, text: changeLines[i] });
2587
+ }
2588
+ oldEditIndex++;
2589
+ }
2590
+ }
2591
+ }
2592
+ const finalLineStates = [
2593
+ ...lineStates.slice(0, startIndex),
2594
+ ...reconciledEditStates,
2595
+ ...lineStates.slice(endIndex + 1)
2596
+ ];
2597
+ this.cache.set(absolutePath, finalLineStates);
2598
+ }
2599
+ };
2600
+
2601
+ // src/core/agents/developer-agent.ts
2602
+ import fs9 from "fs";
2603
+ import path10 from "path";
2604
+
2605
+ // src/core/agents/agent-tools.ts
2606
+ import fs6 from "fs";
2607
+ import path7 from "path";
2608
+ import fg from "fast-glob";
2609
+ import { exec } from "child_process";
2610
+ import { promisify } from "util";
2611
+ import { fileURLToPath } from "url";
2612
+
2613
+ // src/core/ast-editing/editors/code-editor-factory.ts
2614
+ import * as path6 from "path";
2615
+
2616
+ // src/core/ast-editing/editors/typescript-editor.ts
2617
+ import { Project, SyntaxKind } from "ts-morph";
2618
+ import * as path5 from "path";
2619
+ import * as fs5 from "fs";
2620
+ var TypeScriptEditor = class {
2621
+ project;
2622
+ constructor() {
2623
+ this.project = new Project({
2624
+ skipAddingFilesFromTsConfig: true,
2625
+ compilerOptions: {
2626
+ target: 99,
2627
+ // ESNext
2628
+ module: 99
2629
+ // ESNext
2630
+ }
2631
+ });
2632
+ }
2633
+ /**
2634
+ * Get or add source file to project
2635
+ */
2636
+ getSourceFile(filePath) {
2637
+ const absolutePath = path5.resolve(filePath);
2638
+ let sourceFile = this.project.getSourceFile(absolutePath);
2639
+ if (!sourceFile) {
2640
+ if (!fs5.existsSync(absolutePath)) {
2641
+ throw new Error(`File not found: ${absolutePath}`);
2642
+ }
2643
+ sourceFile = this.project.addSourceFileAtPath(absolutePath);
2644
+ }
2645
+ return sourceFile;
2646
+ }
2647
+ /**
2648
+ * Get class declaration by name
2649
+ */
2650
+ getClass(sourceFile, className) {
2651
+ const classDecl = sourceFile.getClass(className);
2652
+ if (!classDecl) {
2653
+ throw new Error(`Class "${className}" not found in ${sourceFile.getFilePath()}`);
2654
+ }
2655
+ return classDecl;
2656
+ }
2657
+ // ═══════════════════════════════════════════════════════
2658
+ // IMPLEMENTATION: listStructure
2659
+ // ═══════════════════════════════════════════════════════
2660
+ async listStructure(filePath) {
2661
+ const sourceFile = this.getSourceFile(filePath);
2662
+ const classes = sourceFile.getClasses().map((cls) => ({
2663
+ name: cls.getName() || "<anonymous>",
2664
+ methods: cls.getMethods().map((m) => this.extractMethodInfo(m)),
2665
+ properties: cls.getProperties().map((p) => this.extractPropertyInfo(p)),
2666
+ decorators: cls.getDecorators().map((d) => d.getText()),
2667
+ extendsClass: cls.getExtends()?.getText(),
2668
+ implementsInterfaces: cls.getImplements().map((i) => i.getText())
2669
+ }));
2670
+ const interfaces = sourceFile.getInterfaces().map((iface) => ({
2671
+ name: iface.getName(),
2672
+ properties: iface.getProperties().map((p) => this.extractPropertyInfo(p)),
2673
+ extends: iface.getExtends().map((e) => e.getText())
2674
+ }));
2675
+ const functions = sourceFile.getFunctions().map((fn) => ({
2676
+ name: fn.getName() || "<anonymous>",
2677
+ parameters: fn.getParameters().map((p) => ({
2678
+ name: p.getName(),
2679
+ type: p.getType().getText(),
2680
+ isOptional: p.isOptional()
2681
+ })),
2682
+ returnType: fn.getReturnType().getText(),
2683
+ isAsync: fn.isAsync(),
2684
+ isExported: fn.isExported()
2685
+ }));
2686
+ const imports = sourceFile.getImportDeclarations().map((imp) => ({
2687
+ modulePath: imp.getModuleSpecifierValue(),
2688
+ isDefault: !!imp.getDefaultImport(),
2689
+ namedImports: imp.getNamedImports().map((n) => n.getName()),
2690
+ namespaceImport: imp.getNamespaceImport()?.getText()
2691
+ }));
2692
+ const exports = sourceFile.getExportedDeclarations();
2693
+ const exportInfo = Array.from(exports.entries()).flatMap(
2694
+ ([name, declarations]) => declarations.map((decl) => ({
2695
+ name,
2696
+ type: this.getDeclarationType(decl),
2697
+ isDefault: sourceFile.getDefaultExportSymbol()?.getName() === name
2698
+ }))
2699
+ );
2700
+ return {
2701
+ classes,
2702
+ interfaces,
2703
+ functions,
2704
+ imports,
2705
+ exports: exportInfo
2706
+ };
2707
+ }
2708
+ extractMethodInfo(method) {
2709
+ return {
2710
+ name: method.getName(),
2711
+ parameters: method.getParameters().map((p) => ({
2712
+ name: p.getName(),
2713
+ type: p.getType().getText(),
2714
+ isOptional: p.isOptional()
2715
+ })),
2716
+ returnType: method.getReturnType().getText(),
2717
+ isAsync: method.isAsync(),
2718
+ isStatic: method.isStatic(),
2719
+ visibility: this.getVisibility(method),
2720
+ decorators: method.getDecorators().map((d) => d.getText())
2721
+ };
2722
+ }
2723
+ extractPropertyInfo(property) {
2724
+ return {
2725
+ name: property.getName(),
2726
+ type: property.getType()?.getText(),
2727
+ visibility: this.getVisibility(property),
2728
+ isReadonly: property.isReadonly(),
2729
+ initializer: property.getInitializer()?.getText()
2730
+ };
2731
+ }
2732
+ getVisibility(node) {
2733
+ if (node.hasModifier?.(SyntaxKind.PrivateKeyword)) return "private";
2734
+ if (node.hasModifier?.(SyntaxKind.ProtectedKeyword)) return "protected";
2735
+ return "public";
2736
+ }
2737
+ getDeclarationType(decl) {
2738
+ if (decl.getKind() === SyntaxKind.ClassDeclaration) return "class";
2739
+ if (decl.getKind() === SyntaxKind.FunctionDeclaration) return "function";
2740
+ if (decl.getKind() === SyntaxKind.InterfaceDeclaration) return "interface";
2741
+ if (decl.getKind() === SyntaxKind.TypeAliasDeclaration) return "type";
2742
+ return "const";
2743
+ }
2744
+ // ═══════════════════════════════════════════════════════
2745
+ // IMPLEMENTATION: Class Operations
2746
+ // ═══════════════════════════════════════════════════════
2747
+ async addClass(filePath, className, options) {
2748
+ try {
2749
+ const sourceFile = this.getSourceFile(filePath);
2750
+ sourceFile.addClass({
2751
+ name: className,
2752
+ extends: options?.extendsClass,
2753
+ implements: options?.implementsInterfaces,
2754
+ isExported: true
2755
+ });
2756
+ await sourceFile.save();
2757
+ return true;
2758
+ } catch (error) {
2759
+ console.error(`Failed to add class "${className}":`, error);
2760
+ return false;
2761
+ }
2762
+ }
2763
+ async addProperty(filePath, className, propertyCode) {
2764
+ try {
2765
+ const sourceFile = this.getSourceFile(filePath);
2766
+ const classDecl = this.getClass(sourceFile, className);
2767
+ const closeBrace = classDecl.getEnd() - 1;
2768
+ classDecl.insertText(closeBrace, `
2769
+ ${propertyCode}`);
2770
+ classDecl.formatText();
2771
+ await sourceFile.save();
2772
+ return true;
2773
+ } catch (error) {
2774
+ console.error(`Failed to add property to "${className}":`, error);
2775
+ return false;
2776
+ }
2777
+ }
2778
+ async getProperty(filePath, className, propertyName) {
2779
+ try {
2780
+ const sourceFile = this.getSourceFile(filePath);
2781
+ const classDecl = this.getClass(sourceFile, className);
2782
+ const property = classDecl.getProperty(propertyName);
2783
+ if (!property) {
2784
+ return void 0;
2785
+ }
2786
+ return property.getText();
2787
+ } catch (error) {
2788
+ console.error(`Failed to get property "${propertyName}":`, error);
2789
+ return void 0;
2790
+ }
2791
+ }
2792
+ async modifyProperty(filePath, className, propertyName, newCode) {
2793
+ try {
2794
+ const sourceFile = this.getSourceFile(filePath);
2795
+ const classDecl = this.getClass(sourceFile, className);
2796
+ const property = classDecl.getProperty(propertyName);
2797
+ if (!property) {
2798
+ throw new Error(`Property "${propertyName}" not found in class "${className}"`);
2799
+ }
2800
+ property.replaceWithText(newCode);
2801
+ classDecl.formatText();
2802
+ await sourceFile.save();
2803
+ return true;
2804
+ } catch (error) {
2805
+ console.error(`Failed to modify property "${propertyName}":`, error);
2806
+ return false;
2807
+ }
2808
+ }
2809
+ async removeProperty(filePath, className, propertyName) {
2810
+ try {
2811
+ const sourceFile = this.getSourceFile(filePath);
2812
+ const classDecl = this.getClass(sourceFile, className);
2813
+ const property = classDecl.getProperty(propertyName);
2814
+ if (!property) {
2815
+ throw new Error(`Property "${propertyName}" not found in class "${className}"`);
2816
+ }
2817
+ property.remove();
2818
+ await sourceFile.save();
2819
+ return true;
2820
+ } catch (error) {
2821
+ console.error(`Failed to remove property "${propertyName}":`, error);
2822
+ return false;
2823
+ }
2824
+ }
2825
+ async addMethod(filePath, className, methodCode) {
2826
+ try {
2827
+ const sourceFile = this.getSourceFile(filePath);
2828
+ const classDecl = this.getClass(sourceFile, className);
2829
+ const closeBrace = classDecl.getEnd() - 1;
2830
+ classDecl.insertText(closeBrace, `
2831
+ ${methodCode}
2832
+ `);
2833
+ classDecl.formatText();
2834
+ await sourceFile.save();
2835
+ return true;
2836
+ } catch (error) {
2837
+ console.error(`Failed to add method to "${className}":`, error);
2838
+ return false;
2839
+ }
2840
+ }
2841
+ async modifyMethod(filePath, className, methodName, newBody) {
2842
+ try {
2843
+ const sourceFile = this.getSourceFile(filePath);
2844
+ const classDecl = this.getClass(sourceFile, className);
2845
+ const method = classDecl.getMethod(methodName);
2846
+ if (!method) {
2847
+ throw new Error(`Method "${methodName}" not found in class "${className}"`);
2848
+ }
2849
+ method.setBodyText(newBody);
2850
+ method.formatText();
2851
+ await sourceFile.save();
2852
+ return true;
2853
+ } catch (error) {
2854
+ console.error(`Failed to modify method "${methodName}":`, error);
2855
+ return false;
2856
+ }
2857
+ }
2858
+ async removeMethod(filePath, className, methodName) {
2859
+ try {
2860
+ const sourceFile = this.getSourceFile(filePath);
2861
+ const classDecl = this.getClass(sourceFile, className);
2862
+ const method = classDecl.getMethod(methodName);
2863
+ if (!method) {
2864
+ throw new Error(`Method "${methodName}" not found in class "${className}"`);
2865
+ }
2866
+ method.remove();
2867
+ await sourceFile.save();
2868
+ return true;
2869
+ } catch (error) {
2870
+ console.error(`Failed to remove method "${methodName}":`, error);
2871
+ return false;
2872
+ }
2873
+ }
2874
+ async addDecorator(filePath, className, decoratorCode) {
2875
+ try {
2876
+ const sourceFile = this.getSourceFile(filePath);
2877
+ const classDecl = this.getClass(sourceFile, className);
2878
+ const start = classDecl.getStart();
2879
+ sourceFile.insertText(start, `${decoratorCode}
2880
+ `);
2881
+ sourceFile.formatText();
2882
+ await sourceFile.save();
2883
+ return true;
2884
+ } catch (error) {
2885
+ console.error(`Failed to add decorator to "${className}":`, error);
2886
+ return false;
2887
+ }
2888
+ }
2889
+ async getMethod(filePath, className, methodName) {
2890
+ try {
2891
+ const sourceFile = this.getSourceFile(filePath);
2892
+ const classDecl = this.getClass(sourceFile, className);
2893
+ const method = classDecl.getMethod(methodName);
2894
+ if (!method) {
2895
+ return void 0;
2896
+ }
2897
+ return method.getText();
2898
+ } catch (error) {
2899
+ console.error(`Failed to get method "${methodName}":`, error);
2900
+ return void 0;
2901
+ }
2902
+ }
2903
+ // ═══════════════════════════════════════════════════════
2904
+ // IMPLEMENTATION: Interface/Type Operations
2905
+ // ═══════════════════════════════════════════════════════
2906
+ async addInterface(filePath, interfaceCode) {
2907
+ try {
2908
+ const sourceFile = this.getSourceFile(filePath);
2909
+ sourceFile.insertText(sourceFile.getEnd(), `
2910
+
2911
+ ${interfaceCode}`);
2912
+ sourceFile.formatText();
2913
+ await sourceFile.save();
2914
+ return true;
2915
+ } catch (error) {
2916
+ console.error(`Failed to add interface:`, error);
2917
+ return false;
2918
+ }
2919
+ }
2920
+ async addTypeAlias(filePath, typeCode) {
2921
+ try {
2922
+ const sourceFile = this.getSourceFile(filePath);
2923
+ sourceFile.insertText(sourceFile.getEnd(), `
2924
+
2925
+ ${typeCode}`);
2926
+ sourceFile.formatText();
2927
+ await sourceFile.save();
2928
+ return true;
2929
+ } catch (error) {
2930
+ console.error(`Failed to add type alias:`, error);
2931
+ return false;
2932
+ }
2933
+ }
2934
+ // ═══════════════════════════════════════════════════════
2935
+ // IMPLEMENTATION: Function Operations
2936
+ // ═══════════════════════════════════════════════════════
2937
+ async addFunction(filePath, functionCode) {
2938
+ try {
2939
+ const sourceFile = this.getSourceFile(filePath);
2940
+ sourceFile.insertText(sourceFile.getEnd(), `
2941
+
2942
+ ${functionCode}`);
2943
+ sourceFile.formatText();
2944
+ await sourceFile.save();
2945
+ return true;
2946
+ } catch (error) {
2947
+ console.error(`Failed to add function:`, error);
2948
+ return false;
2949
+ }
2950
+ }
2951
+ async removeFunction(filePath, functionName) {
2952
+ try {
2953
+ const sourceFile = this.getSourceFile(filePath);
2954
+ const func = sourceFile.getFunction(functionName);
2955
+ if (!func) {
2956
+ throw new Error(`Function "${functionName}" not found`);
2957
+ }
2958
+ func.remove();
2959
+ await sourceFile.save();
2960
+ return true;
2961
+ } catch (error) {
2962
+ console.error(`Failed to remove function "${functionName}":`, error);
2963
+ return false;
2964
+ }
2965
+ }
2966
+ // ═══════════════════════════════════════════════════════
2967
+ // IMPLEMENTATION: Import/Export Operations
2968
+ // ═══════════════════════════════════════════════════════
2969
+ async addImport(filePath, importStatement) {
2970
+ try {
2971
+ const sourceFile = this.getSourceFile(filePath);
2972
+ const lastImport = sourceFile.getImportDeclarations().pop();
2973
+ const pos = lastImport ? lastImport.getEnd() : 0;
2974
+ sourceFile.insertText(pos, `
2975
+ ${importStatement}`);
2976
+ this.organizeImports(filePath);
2977
+ await sourceFile.save();
2978
+ return true;
2979
+ } catch (error) {
2980
+ console.error(`Failed to add import:`, error);
2981
+ return false;
2982
+ }
2983
+ }
2984
+ async removeImport(filePath, modulePath) {
2985
+ try {
2986
+ const sourceFile = this.getSourceFile(filePath);
2987
+ const importDecls = sourceFile.getImportDeclarations().filter((imp) => imp.getModuleSpecifierValue() === modulePath);
2988
+ if (importDecls.length === 0) {
2989
+ throw new Error(`Import from "${modulePath}" not found`);
2990
+ }
2991
+ importDecls.forEach((d) => d.remove());
2992
+ await sourceFile.save();
2993
+ return true;
2994
+ } catch (error) {
2995
+ console.error(`Failed to remove import from "${modulePath}":`, error);
2996
+ return false;
2997
+ }
2998
+ }
2999
+ async organizeImports(filePath) {
3000
+ try {
3001
+ const sourceFile = this.getSourceFile(filePath);
3002
+ sourceFile.organizeImports();
3003
+ const imports = sourceFile.getImportDeclarations();
3004
+ const importStructure = imports.map((i) => i.getStructure());
3005
+ importStructure.sort((a, b) => {
3006
+ return a.moduleSpecifier.localeCompare(b.moduleSpecifier);
3007
+ });
3008
+ imports.forEach((i) => i.remove());
3009
+ sourceFile.addImportDeclarations(importStructure);
3010
+ await sourceFile.save();
3011
+ return true;
3012
+ } catch (error) {
3013
+ console.error(`Failed to organize imports:`, error);
3014
+ return false;
3015
+ }
3016
+ }
3017
+ };
3018
+
3019
+ // src/core/ast-editing/editors/code-editor-factory.ts
3020
+ var CodeEditorFactory = class {
3021
+ static tsEditor = null;
3022
+ /**
3023
+ * Returns appropriate editor for the file, or null if AST editing not supported
3024
+ */
3025
+ static getEditor(filePath) {
3026
+ const ext = path6.extname(filePath).toLowerCase();
3027
+ if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
3028
+ if (!this.tsEditor) {
3029
+ this.tsEditor = new TypeScriptEditor();
3030
+ }
3031
+ return this.tsEditor;
3032
+ }
3033
+ return null;
3034
+ }
3035
+ /**
3036
+ * Clear cached editors (useful for testing)
3037
+ */
3038
+ static clearCache() {
3039
+ this.tsEditor = null;
3040
+ }
3041
+ };
3042
+
3043
+ // src/core/agents/agent-tools.ts
3044
+ var execAsync = promisify(exec);
3045
+ function detectLineEnding(content) {
3046
+ const crlf = content.split("\r\n").length - 1;
3047
+ const lf = content.split("\n").length - 1 - crlf;
3048
+ return crlf > lf ? "\r\n" : "\n";
3049
+ }
3050
+ function handleListFiles(dirPath) {
3051
+ try {
3052
+ const fullPath = path7.resolve(process.cwd(), dirPath);
3053
+ if (!fs6.existsSync(fullPath)) return `Error: Directory ${dirPath} does not exist.`;
3054
+ const items = fs6.readdirSync(fullPath, { withFileTypes: true });
3055
+ return items.map((item) => {
3056
+ return `${item.isDirectory() ? "[DIR]" : "[FILE]"} ${item.name}`;
3057
+ }).join("\n");
3058
+ } catch (e) {
3059
+ return `Error listing files: ${e.message}`;
3060
+ }
3061
+ }
3062
+ function handleReadFile(filePath, showLineNumbers = true) {
3063
+ try {
3064
+ const fullPath = path7.resolve(process.cwd(), filePath);
3065
+ if (!fs6.existsSync(fullPath)) return `Error: File ${filePath} does not exist.`;
3066
+ const stats = fs6.statSync(fullPath);
3067
+ if (stats.size > 100 * 1024) return `Error: File too large to read (${stats.size} bytes). Limit is 100KB.`;
3068
+ const content = fs6.readFileSync(fullPath, "utf-8");
3069
+ if (showLineNumbers) {
3070
+ const lines = content.split("\n");
3071
+ return lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n");
3072
+ }
3073
+ return content;
3074
+ } catch (e) {
3075
+ return `Error reading file: ${e.message}`;
3076
+ }
3077
+ }
3078
+ function replaceLineRange(filePath, startLine, endLine, newContent, tui2) {
3079
+ try {
3080
+ if (!fs6.existsSync(filePath)) {
3081
+ tui2.log.error(`\u274C File not found for modification: ${filePath}`);
3082
+ return false;
3083
+ }
3084
+ const currentFileContent = fs6.readFileSync(filePath, "utf-8");
3085
+ const lineEnding = detectLineEnding(currentFileContent);
3086
+ const lines = currentFileContent.split(lineEnding);
3087
+ if (startLine < 1 || startLine > lines.length) {
3088
+ tui2.log.error(`\u274C Invalid start line: ${startLine}. File has ${lines.length} lines.`);
3089
+ return false;
3090
+ }
3091
+ if (endLine < startLine || endLine > lines.length) {
3092
+ tui2.log.error(`\u274C Invalid end line: ${endLine}. Must be >= startLine and <= file length.`);
3093
+ return false;
3094
+ }
3095
+ const before = lines.slice(0, startLine - 1);
3096
+ const after = lines.slice(endLine);
3097
+ const newLines = newContent.split(lineEnding);
3098
+ const normalizedNewLines = newContent.replace(/\r\n/g, "\n").split("\n");
3099
+ const result = [...before, ...normalizedNewLines, ...after].join(lineEnding);
3100
+ const BOM = "\uFEFF";
3101
+ const finalContent = result.startsWith(BOM) ? result : BOM + result;
3102
+ fs6.writeFileSync(filePath, finalContent, { encoding: "utf-8" });
3103
+ tui2.log.success(`\u2705 Replaced lines ${startLine}-${endLine} in ${filePath}`);
3104
+ return true;
3105
+ } catch (e) {
3106
+ tui2.log.error(`\u274C Error replacing line range: ${e.message}`);
3107
+ return false;
3108
+ }
3109
+ }
3110
+ function handleSearchFile(pattern) {
3111
+ try {
3112
+ const entries = fg.sync(pattern, { dot: true });
3113
+ if (entries.length === 0) return "No files found matching pattern.";
3114
+ return entries.slice(0, 50).join("\n");
3115
+ } catch (e) {
3116
+ return `Error searching files: ${e.message}`;
3117
+ }
3118
+ }
3119
+ function handleSearchCode(globPattern, query, isRegex = false) {
3120
+ const MAX_MATCHES = 50;
3121
+ const MAX_FILE_SIZE_BYTES = 500 * 1024;
3122
+ try {
3123
+ const files = fg.sync(globPattern, { dot: true, absolute: false });
3124
+ if (files.length === 0) return `No files found matching pattern: "${globPattern}"`;
3125
+ let searchRegex;
3126
+ try {
3127
+ searchRegex = isRegex ? new RegExp(query, "gi") : new RegExp(query.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "gi");
3128
+ } catch {
3129
+ return `Error: Invalid regex pattern: "${query}"`;
3130
+ }
3131
+ const results = [];
3132
+ let totalMatches = 0;
3133
+ for (const filePath of files) {
3134
+ if (totalMatches >= MAX_MATCHES) break;
3135
+ try {
3136
+ const fullPath = path7.resolve(process.cwd(), filePath);
3137
+ const stats = fs6.statSync(fullPath);
3138
+ if (stats.size > MAX_FILE_SIZE_BYTES) continue;
3139
+ const content = fs6.readFileSync(fullPath, "utf-8");
3140
+ const lines = content.split("\n");
3141
+ for (let i = 0; i < lines.length; i++) {
3142
+ if (totalMatches >= MAX_MATCHES) break;
3143
+ searchRegex.lastIndex = 0;
3144
+ if (searchRegex.test(lines[i])) {
3145
+ results.push(`${filePath}:${i + 1}: ${lines[i].trim()}`);
3146
+ totalMatches++;
3147
+ }
3148
+ }
3149
+ } catch {
3150
+ }
3151
+ }
3152
+ if (results.length === 0) {
3153
+ return `No matches found for "${query}" in files matching "${globPattern}"`;
3154
+ }
3155
+ const limited = totalMatches >= MAX_MATCHES ? ` (limited to ${MAX_MATCHES})` : "";
3156
+ return `Found ${totalMatches} match(es) for "${query}" in "${globPattern}"${limited}:
3157
+ ${results.join("\n")}`;
3158
+ } catch (e) {
3159
+ return `Error searching code: ${e.message}`;
3160
+ }
3161
+ }
3162
+ function startSmartReplace(filePath, newContent, targetContent, tui2) {
3163
+ if (!fs6.existsSync(filePath)) {
3164
+ tui2.log.error(`\u274C File not found for modification: ${filePath}`);
3165
+ return false;
3166
+ }
3167
+ const currentFileContent = fs6.readFileSync(filePath, "utf-8");
3168
+ const normalizedTarget = targetContent.replace(/\r\n/g, "\n");
3169
+ const normalizedContent = currentFileContent.replace(/\r\n/g, "\n");
3170
+ if (!normalizedContent.includes(normalizedTarget)) {
3171
+ tui2.log.error(`\u274C Target content not found in ${filePath} (checked with normalized line endings). Modification aborted.`);
3172
+ console.log(colors.dim("--- Target Content Expected ---"));
3173
+ console.log(targetContent.substring(0, 200) + "...");
3174
+ return false;
3175
+ }
3176
+ const occurrences = currentFileContent.split(targetContent).length - 1;
3177
+ if (occurrences > 1) {
3178
+ tui2.log.error(`\u274C Ambiguous target: Found ${occurrences} occurrences in ${filePath}. Modification aborted.`);
3179
+ return false;
3180
+ }
3181
+ const BOM = "\uFEFF";
3182
+ const updatedContent = currentFileContent.replace(targetContent, newContent);
3183
+ const finalContent = updatedContent.startsWith(BOM) ? updatedContent : BOM + updatedContent;
3184
+ fs6.writeFileSync(filePath, finalContent, { encoding: "utf-8" });
3185
+ tui2.log.success(`\u2705 Smart Replace Applied: ${filePath}`);
3186
+ return true;
3187
+ }
3188
+ async function handleRunCommand(command) {
3189
+ const { spawn } = await import("child_process");
3190
+ try {
3191
+ tui.log.info(`\u{1F4BB} Executing: ${colors.dim(command)}`);
3192
+ return new Promise((resolve2) => {
3193
+ const child = spawn(command, {
3194
+ shell: true,
3195
+ stdio: ["ignore", "pipe", "pipe"],
3196
+ cwd: process.cwd()
3197
+ });
3198
+ let stdout = "";
3199
+ let stderr = "";
3200
+ const timer = setTimeout(() => {
3201
+ child.kill();
3202
+ resolve2(`Error: Command timed out after 5 minutes.
3203
+ Output so far:
3204
+ ${stdout}
3205
+ ${stderr}`);
3206
+ }, 5 * 60 * 1e3);
3207
+ child.stdout.on("data", (data) => {
3208
+ const chunk = data.toString();
3209
+ stdout += chunk;
3210
+ });
3211
+ child.stderr.on("data", (data) => {
3212
+ stderr += data.toString();
3213
+ });
3214
+ child.on("close", (code) => {
3215
+ clearTimeout(timer);
3216
+ if (code === 0) {
3217
+ resolve2(stdout.trim() || "Command executed successfully (no output).");
3218
+ } else {
3219
+ resolve2(`Command failed with exit code ${code}.
3220
+ STDERR:
3221
+ ${stderr}
3222
+ STDOUT:
3223
+ ${stdout}`);
3224
+ }
3225
+ });
3226
+ child.on("error", (err) => {
3227
+ clearTimeout(timer);
3228
+ resolve2(`Error executing command: ${err.message}`);
3229
+ });
3230
+ });
3231
+ } catch (e) {
3232
+ return `Error launching command: ${e.message}`;
3233
+ }
3234
+ }
3235
+ function resolveAstGrepCommand() {
3236
+ const isWin = process.platform === "win32";
3237
+ const binName = isWin ? "sg.cmd" : "sg";
3238
+ try {
3239
+ const currentFile = fileURLToPath(import.meta.url);
3240
+ let dir = path7.dirname(currentFile);
3241
+ for (let i = 0; i < 5; i++) {
3242
+ const candidate = path7.join(dir, "node_modules", ".bin", binName);
3243
+ if (fs6.existsSync(candidate)) {
3244
+ return `"${candidate}"`;
3245
+ }
3246
+ const parent = path7.dirname(dir);
3247
+ if (parent === dir) break;
3248
+ dir = parent;
3249
+ }
3250
+ } catch (e) {
3251
+ }
3252
+ const cwdBin = path7.resolve(process.cwd(), "node_modules", ".bin", binName);
3253
+ if (fs6.existsSync(cwdBin)) {
3254
+ return `"${cwdBin}"`;
3255
+ }
3256
+ return "npx sg";
3257
+ }
3258
+ async function astGrepSearch(pattern, filePath, language, tui2) {
3259
+ const { spawn } = await import("child_process");
3260
+ try {
3261
+ if (!fs6.existsSync(filePath)) {
3262
+ return `\u274C File not found: ${filePath}`;
3263
+ }
3264
+ const sgCmd = resolveAstGrepCommand();
3265
+ const cmd = `${sgCmd} run -p "${pattern}" -l ${language} --json ${filePath}`;
3266
+ tui2.log.info(`\u{1F50D} [AST-GREP] Searching: ${cmd}`);
3267
+ return new Promise((resolve2) => {
3268
+ const child = spawn(cmd, {
3269
+ shell: true,
3270
+ stdio: ["ignore", "pipe", "pipe"],
3271
+ cwd: process.cwd(),
3272
+ env: { ...process.env, NO_COLOR: "true" }
3273
+ // Avoid ANSI codes in JSON output
3274
+ });
3275
+ let stdout = "";
3276
+ let stderr = "";
3277
+ child.stdout.on("data", (data) => stdout += data.toString());
3278
+ child.stderr.on("data", (data) => stderr += data.toString());
3279
+ child.on("close", (code) => {
3280
+ if (code === 0 && stdout) {
3281
+ resolve2(stdout);
3282
+ } else if (code === 1 && !stderr) {
3283
+ resolve2("No structural matches found.");
3284
+ } else {
3285
+ if (!stdout && !stderr) resolve2("No structural matches found.");
3286
+ else {
3287
+ tui2.log.error(`\u274C ast-grep search error (code ${code}): ${stderr}`);
3288
+ resolve2(`Error executing ast-grep search: ${stderr || stdout}`);
3289
+ }
3290
+ }
3291
+ });
3292
+ child.on("error", (err) => {
3293
+ resolve2(`Error executing ast-grep search: ${err.message}`);
3294
+ });
3295
+ });
3296
+ } catch (e) {
3297
+ tui2.log.error(`\u274C ast-grep search exception: ${e.message}`);
3298
+ return `Error executing ast-grep search: ${e.message}`;
3299
+ }
3300
+ }
3301
+ async function astGrepRewrite(pattern, fix, filePath, language, tui2) {
3302
+ const { spawn } = await import("child_process");
3303
+ try {
3304
+ if (!fs6.existsSync(filePath)) {
3305
+ tui2.log.error(`\u274C File not found for AST modification: ${filePath}`);
3306
+ return false;
3307
+ }
3308
+ const sgCmd = resolveAstGrepCommand();
3309
+ const cmd = `${sgCmd} run -p "${pattern}" -r "${fix}" -l ${language} ${filePath} --update-all`;
3310
+ tui2.log.info(`\u270F\uFE0F [AST-GREP] Rewriting: pattern="${pattern}" fix="${fix.substring(0, 50)}..."`);
3311
+ return new Promise((resolve2) => {
3312
+ const child = spawn(cmd, {
3313
+ shell: true,
3314
+ stdio: ["ignore", "pipe", "pipe"],
3315
+ cwd: process.cwd()
3316
+ });
3317
+ let stderr = "";
3318
+ child.stderr.on("data", (data) => stderr += data.toString());
3319
+ child.on("close", (code) => {
3320
+ if (code === 0) {
3321
+ tui2.log.success(`\u2705 AST Rewrite applied to ${filePath}`);
3322
+ resolve2(true);
3323
+ } else {
3324
+ tui2.log.error(`\u274C AST Rewrite failed (code ${code}): ${stderr}`);
3325
+ resolve2(false);
3326
+ }
3327
+ });
3328
+ child.on("error", (err) => {
3329
+ tui2.log.error(`\u274C AST Rewrite spawn error: ${err.message}`);
3330
+ resolve2(false);
3331
+ });
3332
+ });
3333
+ } catch (e) {
3334
+ tui2.log.error(`\u274C Unexpected error in astGrepRewrite: ${e.message}`);
3335
+ return false;
3336
+ }
3337
+ }
3338
+ async function astListStructure(filePath) {
3339
+ try {
3340
+ const editor = CodeEditorFactory.getEditor(filePath);
3341
+ if (!editor) {
3342
+ return `[AST Error] File type not supported: ${filePath}. Use read_file instead.`;
3343
+ }
3344
+ const structure = await editor.listStructure(filePath);
3345
+ let output = `[AST Structure of ${filePath}]
3346
+
3347
+ `;
3348
+ if (structure.classes.length > 0) {
3349
+ output += `CLASSES:
3350
+ `;
3351
+ structure.classes.forEach((cls) => {
3352
+ output += ` - ${cls.name}`;
3353
+ if (cls.extendsClass) output += ` extends ${cls.extendsClass}`;
3354
+ if (cls.implementsInterfaces.length > 0) {
3355
+ output += ` implements ${cls.implementsInterfaces.join(", ")}`;
3356
+ }
3357
+ output += `
3358
+ `;
3359
+ if (cls.decorators.length > 0) {
3360
+ output += ` Decorators: ${cls.decorators.join(", ")}
3361
+ `;
3362
+ }
3363
+ if (cls.properties.length > 0) {
3364
+ output += ` Properties:
3365
+ `;
3366
+ cls.properties.forEach((prop) => {
3367
+ output += ` - ${prop.visibility} ${prop.name}: ${prop.type}
3368
+ `;
3369
+ });
3370
+ }
3371
+ if (cls.methods.length > 0) {
3372
+ output += ` Methods:
3373
+ `;
3374
+ cls.methods.forEach((method) => {
3375
+ const params = method.parameters.map((p) => `${p.name}: ${p.type}`).join(", ");
3376
+ output += ` - ${method.visibility} ${method.name}(${params}): ${method.returnType}
3377
+ `;
3378
+ });
3379
+ }
3380
+ output += `
3381
+ `;
3382
+ });
3383
+ }
3384
+ if (structure.interfaces.length > 0) {
3385
+ output += `INTERFACES:
3386
+ `;
3387
+ structure.interfaces.forEach((iface) => {
3388
+ output += ` - ${iface.name}
3389
+ `;
3390
+ iface.properties.forEach((prop) => {
3391
+ output += ` ${prop.name}: ${prop.type}
3392
+ `;
3393
+ });
3394
+ });
3395
+ output += `
3396
+ `;
3397
+ }
3398
+ if (structure.functions.length > 0) {
3399
+ output += `FUNCTIONS:
3400
+ `;
3401
+ structure.functions.forEach((fn) => {
3402
+ const params = fn.parameters.map((p) => `${p.name}: ${p.type}`).join(", ");
3403
+ output += ` - ${fn.name}(${params}): ${fn.returnType}
3404
+ `;
3405
+ });
3406
+ output += `
3407
+ `;
3408
+ }
3409
+ if (structure.imports.length > 0) {
3410
+ output += `IMPORTS:
3411
+ `;
3412
+ structure.imports.forEach((imp) => {
3413
+ output += ` - from "${imp.modulePath}": ${imp.namedImports.join(", ")}
3414
+ `;
3415
+ });
3416
+ }
3417
+ return output;
3418
+ } catch (error) {
3419
+ return `[AST Error] ${error.message}`;
3420
+ }
3421
+ }
3422
+ async function astAddMethod(filePath, className, methodCode) {
3423
+ const editor = CodeEditorFactory.getEditor(filePath);
3424
+ if (!editor) {
3425
+ throw new Error(`No AST editor available for ${filePath}`);
3426
+ }
3427
+ return await editor.addMethod(filePath, className, methodCode);
3428
+ }
3429
+ async function astGetMethod(filePath, className, methodName) {
3430
+ const editor = CodeEditorFactory.getEditor(filePath);
3431
+ if (!editor) {
3432
+ throw new Error(`No AST editor available for ${filePath}`);
3433
+ }
3434
+ const methodContent = await editor.getMethod(filePath, className, methodName);
3435
+ if (!methodContent) {
3436
+ return `Method "${methodName}" not found in class "${className}"`;
3437
+ }
3438
+ return methodContent;
3439
+ }
3440
+ async function astAddClass(filePath, className, extendsClass, implementsInterfaces) {
3441
+ const editor = CodeEditorFactory.getEditor(filePath);
3442
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3443
+ return await editor.addClass(filePath, className, { extendsClass, implementsInterfaces });
3444
+ }
3445
+ async function astAddProperty(filePath, className, propertyCode) {
3446
+ const editor = CodeEditorFactory.getEditor(filePath);
3447
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3448
+ return await editor.addProperty(filePath, className, propertyCode);
3449
+ }
3450
+ async function astGetProperty(filePath, className, propertyName) {
3451
+ const editor = CodeEditorFactory.getEditor(filePath);
3452
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3453
+ const propContent = await editor.getProperty(filePath, className, propertyName);
3454
+ if (!propContent) {
3455
+ return `Property "${propertyName}" not found in class "${className}"`;
3456
+ }
3457
+ return propContent;
3458
+ }
3459
+ async function astModifyProperty(filePath, className, propertyName, propertyCode) {
3460
+ const editor = CodeEditorFactory.getEditor(filePath);
3461
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3462
+ return await editor.modifyProperty(filePath, className, propertyName, propertyCode);
3463
+ }
3464
+ async function astRemoveProperty(filePath, className, propertyName) {
3465
+ const editor = CodeEditorFactory.getEditor(filePath);
3466
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3467
+ return await editor.removeProperty(filePath, className, propertyName);
3468
+ }
3469
+ async function astModifyMethod(filePath, className, methodName, newBody) {
3470
+ const editor = CodeEditorFactory.getEditor(filePath);
3471
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3472
+ return await editor.modifyMethod(filePath, className, methodName, newBody);
3473
+ }
3474
+ async function astRemoveMethod(filePath, className, methodName) {
3475
+ const editor = CodeEditorFactory.getEditor(filePath);
3476
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3477
+ return await editor.removeMethod(filePath, className, methodName);
3478
+ }
3479
+ async function astAddDecorator(filePath, className, decoratorCode) {
3480
+ const editor = CodeEditorFactory.getEditor(filePath);
3481
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3482
+ return await editor.addDecorator(filePath, className, decoratorCode);
3483
+ }
3484
+ async function astAddInterface(filePath, interfaceCode) {
3485
+ const editor = CodeEditorFactory.getEditor(filePath);
3486
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3487
+ return await editor.addInterface(filePath, interfaceCode);
3488
+ }
3489
+ async function astAddTypeAlias(filePath, typeCode) {
3490
+ const editor = CodeEditorFactory.getEditor(filePath);
3491
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3492
+ return await editor.addTypeAlias(filePath, typeCode);
3493
+ }
3494
+ async function astAddFunction(filePath, functionCode) {
3495
+ const editor = CodeEditorFactory.getEditor(filePath);
3496
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3497
+ return await editor.addFunction(filePath, functionCode);
3498
+ }
3499
+ async function astRemoveFunction(filePath, functionName) {
3500
+ const editor = CodeEditorFactory.getEditor(filePath);
3501
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3502
+ return await editor.removeFunction(filePath, functionName);
3503
+ }
3504
+ async function astAddImport(filePath, importStatement) {
3505
+ const editor = CodeEditorFactory.getEditor(filePath);
3506
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3507
+ return await editor.addImport(filePath, importStatement);
3508
+ }
3509
+ async function astRemoveImport(filePath, modulePath) {
3510
+ const editor = CodeEditorFactory.getEditor(filePath);
3511
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3512
+ return await editor.removeImport(filePath, modulePath);
3513
+ }
3514
+ async function astOrganizeImports(filePath) {
3515
+ const editor = CodeEditorFactory.getEditor(filePath);
3516
+ if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
3517
+ return await editor.organizeImports(filePath);
3518
+ }
3519
+
3520
+ // src/core/workflow/skill-manager.ts
3521
+ import fs7 from "fs/promises";
3522
+ import path8 from "path";
3523
+ import os2 from "os";
3524
+ var SkillManager = class {
3525
+ activeSkills = /* @__PURE__ */ new Set();
3526
+ skillPrompts = /* @__PURE__ */ new Map();
3527
+ async loadSkillFromFile(filePath) {
3528
+ const content = await fs7.readFile(filePath, "utf-8");
3529
+ const cleanContent = content.replace(/^---[\s\S]*?---\s*/, "");
3530
+ return cleanContent;
3531
+ }
3532
+ async activateSkill(skillName) {
3533
+ if (this.activeSkills.has(skillName)) {
3534
+ return `Skill ${skillName} is already active.`;
3535
+ }
3536
+ this.reset();
3537
+ const globalPath = path8.join(os2.homedir(), ".shark", "skills", skillName, "SKILL.md");
3538
+ const localPath = path8.join(process.cwd(), ".agents", "skills", skillName, "SKILL.md");
3539
+ let skillPath = "";
3540
+ try {
3541
+ await fs7.access(localPath);
3542
+ skillPath = localPath;
3543
+ } catch {
3544
+ try {
3545
+ await fs7.access(globalPath);
3546
+ skillPath = globalPath;
3547
+ } catch {
3548
+ throw new Error(`Skill '${skillName}' not found globally or locally.`);
3549
+ }
3550
+ }
3551
+ const prompt = await this.loadSkillFromFile(skillPath);
3552
+ this.activeSkills.add(skillName);
3553
+ this.skillPrompts.set(skillName, prompt);
3554
+ return prompt;
3555
+ }
3556
+ getSystemInstructionExtension() {
3557
+ if (this.activeSkills.size === 0) return "";
3558
+ let extension = "\n\n<EXTREMELY_IMPORTANT>\n";
3559
+ for (const [name, prompt] of this.skillPrompts.entries()) {
3560
+ extension += `
3561
+ --- ACTIVE SKILL: ${name} ---
3562
+ ${prompt}
3563
+ `;
3564
+ }
3565
+ extension += "\n</EXTREMELY_IMPORTANT>\n";
3566
+ return extension;
3567
+ }
3568
+ async listAvailableSkills() {
3569
+ const globalSkillsDir = path8.join(os2.homedir(), ".shark", "skills");
3570
+ const localSkillsDir = path8.join(process.cwd(), ".agents", "skills");
3571
+ const skillNames = /* @__PURE__ */ new Set();
3572
+ const readDir = async (dir) => {
3573
+ try {
3574
+ const entries = await fs7.readdir(dir, { withFileTypes: true });
3575
+ for (const entry of entries) {
3576
+ if (entry.isDirectory()) {
3577
+ const skillMdPath = path8.join(dir, entry.name, "SKILL.md");
3578
+ try {
3579
+ await fs7.access(skillMdPath);
3580
+ skillNames.add(entry.name);
3581
+ } catch {
3582
+ }
3583
+ }
3584
+ }
3585
+ } catch {
3586
+ }
3587
+ };
3588
+ await readDir(globalSkillsDir);
3589
+ await readDir(localSkillsDir);
3590
+ return Array.from(skillNames).sort();
3591
+ }
3592
+ reset() {
3593
+ this.activeSkills.clear();
3594
+ this.skillPrompts.clear();
3595
+ }
3596
+ };
3597
+ var skillManager = new SkillManager();
3598
+
3599
+ // src/core/workflow/subagent-manager.ts
3600
+ import crypto2 from "crypto";
3601
+ import fs8 from "fs";
3602
+ import path9 from "path";
3603
+ import { fork } from "child_process";
3604
+ import { fileURLToPath as fileURLToPath2 } from "url";
3605
+ var SubagentManager = class {
3606
+ subagents = /* @__PURE__ */ new Map();
3607
+ customTypes = /* @__PURE__ */ new Map();
3608
+ registerSubagent(id, type, role, parentId) {
3609
+ this.subagents.set(id, { id, type, role, status: "running", parentId });
3610
+ }
3611
+ terminateSubagent(id, success = true, isCancelled = false) {
3612
+ const state = this.subagents.get(id);
3613
+ if (state) {
3614
+ if (state.status === "cancelled") {
3615
+ return;
3616
+ }
3617
+ if (isCancelled) {
3618
+ state.status = "cancelled";
3619
+ } else {
3620
+ state.status = success ? "completed" : "failed";
3621
+ }
3622
+ }
3623
+ }
3624
+ isSubagentActive(id) {
3625
+ return this.subagents.get(id)?.status === "running";
3626
+ }
3627
+ hasSubagent(id) {
3628
+ return this.subagents.has(id);
3629
+ }
3630
+ getSubagentState(id) {
3631
+ return this.subagents.get(id);
3632
+ }
3633
+ updateSubagentSummary(id, summary) {
3634
+ const state = this.subagents.get(id);
3635
+ if (state) {
3636
+ state.summary = summary;
3637
+ }
3638
+ }
3639
+ sendMessage(recipient, message) {
3640
+ const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", recipient);
3641
+ fs8.mkdirSync(mailboxDir, { recursive: true });
3642
+ const filePath = path9.join(mailboxDir, `${Date.now()}-${crypto2.randomUUID()}.json`);
3643
+ fs8.writeFileSync(filePath, JSON.stringify({ message }), "utf-8");
3644
+ }
3645
+ retrieveMessages(id) {
3646
+ const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", id);
3647
+ if (!fs8.existsSync(mailboxDir)) {
3648
+ return [];
3649
+ }
3650
+ const files = fs8.readdirSync(mailboxDir);
3651
+ files.sort();
3652
+ const messages = [];
3653
+ for (const file of files) {
3654
+ const filePath = path9.join(mailboxDir, file);
3655
+ try {
3656
+ const content = fs8.readFileSync(filePath, "utf-8");
3657
+ const data = JSON.parse(content);
3658
+ if (data && typeof data.message === "string") {
3659
+ messages.push(data.message);
3660
+ }
3661
+ } catch (e) {
3662
+ }
3663
+ try {
3664
+ fs8.unlinkSync(filePath);
3665
+ } catch (e) {
3666
+ }
3667
+ }
3668
+ return messages;
3669
+ }
3670
+ peekMessages(id) {
3671
+ const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", id);
3672
+ if (!fs8.existsSync(mailboxDir)) {
3673
+ return [];
3674
+ }
3675
+ const files = fs8.readdirSync(mailboxDir);
3676
+ files.sort();
3677
+ const messages = [];
3678
+ for (const file of files) {
3679
+ const filePath = path9.join(mailboxDir, file);
3680
+ try {
3681
+ const content = fs8.readFileSync(filePath, "utf-8");
3682
+ const data = JSON.parse(content);
3683
+ if (data && typeof data.message === "string") {
3684
+ messages.push(data.message);
3685
+ }
3686
+ } catch (e) {
3687
+ }
3688
+ }
3689
+ return messages;
3690
+ }
3691
+ defineSubagentType(name, description, systemPrompt, options = {}) {
3692
+ this.customTypes.set(name, {
3693
+ name,
3694
+ description,
3695
+ systemPrompt,
3696
+ ...options
3697
+ });
3698
+ }
3699
+ getCustomSubagentType(name) {
3700
+ return this.customTypes.get(name);
3701
+ }
3702
+ getSubagentLogs(id, maxLines = 50) {
3703
+ if (!/^[a-zA-Z0-9-]+$/.test(id)) {
3704
+ throw new Error("Invalid subagent ID format");
3705
+ }
3706
+ const projectRoot = process.cwd();
3707
+ const logFile = path9.resolve(projectRoot, "_sharkrc", "history", `subagent-${id}-console.log`);
3708
+ if (!fs8.existsSync(logFile)) {
3709
+ return "No console logs found for this subagent.";
3710
+ }
3711
+ try {
3712
+ let content = fs8.readFileSync(logFile, "utf-8");
3713
+ if (content.endsWith("\n")) {
3714
+ content = content.slice(0, -1);
3715
+ }
3716
+ const lines = content.split("\n");
3717
+ const tail = lines.slice(-maxLines);
3718
+ return tail.join("\n");
3719
+ } catch (e) {
3720
+ return `Failed to read subagent logs: ${e.message}`;
3721
+ }
3722
+ }
3723
+ getActiveSubagents() {
3724
+ return Array.from(this.subagents.values()).filter((s) => s.status === "running");
3725
+ }
3726
+ getActiveSubagentsForParent(parentId) {
3727
+ return Array.from(this.subagents.values()).filter((s) => s.status === "running" && s.parentId === parentId);
3728
+ }
3729
+ killSubagent(id) {
3730
+ const state = this.subagents.get(id);
3731
+ if (state) {
3732
+ if (state.childProcess) {
3733
+ try {
3734
+ state.childProcess.kill("SIGTERM");
3735
+ } catch (e) {
3736
+ }
3737
+ }
3738
+ this.terminateSubagent(id, false, true);
3739
+ if (state.parentId) {
3740
+ const cancelMsg = `[Subagent Notification] Subagent ${state.role} (${id}) has finished with status: CANCELLED. Summary: Terminated by parent agent.`;
3741
+ this.sendMessage(state.parentId, cancelMsg);
3742
+ }
3743
+ }
3744
+ }
3745
+ killAllSubagents() {
3746
+ for (const [id, state] of this.subagents.entries()) {
3747
+ if (state.status === "running") {
3748
+ this.killSubagent(id);
3749
+ }
3750
+ }
3751
+ }
3752
+ async invokeSubagents(subagents, parentId, parentQueue) {
3753
+ const invoked = [];
3754
+ for (const sub of subagents) {
3755
+ const id = `subagent-${crypto2.randomUUID()}`;
3756
+ this.registerSubagent(id, sub.TypeName, sub.Role, parentId);
3757
+ const promise = (async () => {
3758
+ try {
3759
+ const projectRoot = process.cwd();
3760
+ const __filename2 = fileURLToPath2(import.meta.url);
3761
+ const __dirname2 = path9.dirname(__filename2);
3762
+ let packageRoot = __dirname2;
3763
+ while (true) {
3764
+ const pkgPath = path9.join(packageRoot, "package.json");
3765
+ if (fs8.existsSync(pkgPath)) {
3766
+ try {
3767
+ const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
3768
+ if (pkg && pkg.name === "shark-ai") {
3769
+ break;
3770
+ }
3771
+ } catch (e) {
3772
+ }
3773
+ }
3774
+ const parent = path9.dirname(packageRoot);
3775
+ if (parent === packageRoot) {
3776
+ break;
3777
+ }
3778
+ packageRoot = parent;
3779
+ }
3780
+ const pathToSharkJs = path9.resolve(packageRoot, "dist", "bin", "shark.js");
3781
+ const customType = this.customTypes.get(sub.TypeName);
3782
+ let customContext = `Voc\xEA est\xE1 executando em modo SUBAGENTE.
3783
+ `;
3784
+ customContext += `- Seu ID \xE9: ${id}
3785
+ `;
3786
+ customContext += `- O ID do seu Agente Pai \xE9: ${parentId}
3787
+ `;
3788
+ customContext += `- Voc\xEA N\xC3O tem um terminal interativo com o usu\xE1rio humano. N\xE3o use 'talk_with_user' para interagir.
3789
+ `;
3790
+ customContext += `- Para reportar progresso intermedi\xE1rio ou tirar d\xFAvidas com seu pai, use a a\xE7\xE3o 'send_message' com Recipient='${parentId}'.
3791
+ `;
3792
+ customContext += `- Para concluir a tarefa e enviar o resultado detalhado em markdown, use obrigatoriamente a a\xE7\xE3o 'complete_task' com suas descobertas no campo 'content'.
3793
+ `;
3794
+ if (customType) {
3795
+ customContext += `Custom Prompt: ${customType.systemPrompt}
3796
+ `;
3797
+ }
3798
+ const instruction = customContext + "\n\n" + sub.Prompt;
3799
+ const args = ["dev", "-t", instruction, "--taskId", id, "--auto"];
3800
+ const child = fork(pathToSharkJs, args, {
3801
+ cwd: projectRoot,
3802
+ silent: true,
3803
+ env: {
3804
+ ...process.env,
3805
+ SHARK_PARENT_ID: parentId,
3806
+ SHARK_SUBAGENT_ROLE: sub.Role
3807
+ }
3808
+ });
3809
+ const state2 = this.subagents.get(id);
3810
+ if (state2) {
3811
+ state2.childProcess = child;
3812
+ }
3813
+ const historyDir = path9.resolve(projectRoot, "_sharkrc", "history");
3814
+ fs8.mkdirSync(historyDir, { recursive: true });
3815
+ const consoleLogFile = path9.join(historyDir, `subagent-${id}-console.log`);
3816
+ const logStream = fs8.createWriteStream(consoleLogFile, { flags: "a" });
3817
+ if (child.stdout) {
3818
+ child.stdout.pipe(logStream);
3819
+ }
3820
+ if (child.stderr) {
3821
+ child.stderr.pipe(logStream);
3822
+ }
3823
+ const exitCode = await new Promise((resolve2) => {
3824
+ child.on("exit", (code) => {
3825
+ resolve2(code);
3826
+ });
3827
+ });
3828
+ logStream.end();
3829
+ const isCancelled = this.subagents.get(id)?.status === "cancelled";
3830
+ const success = exitCode === 0;
3831
+ this.terminateSubagent(id, success);
3832
+ if (isCancelled) {
3833
+ this.updateSubagentSummary(id, "Terminated by parent agent.");
3834
+ tui.log.message(`
3835
+ Subagent ${sub.Role} (${id}) cancelled.`);
3836
+ } else if (!success) {
3837
+ this.updateSubagentSummary(id, "Failed");
3838
+ const fallbackMsg = `[Subagent Notification] Subagent ${sub.Role} (${id}) has finished with status: FAILED. Summary: Subagent process exited with code ${exitCode}`;
3839
+ const mailboxDir = path9.resolve(projectRoot, ".shark", "mailbox", parentId);
3840
+ const hasMessages = fs8.existsSync(mailboxDir) && fs8.readdirSync(mailboxDir).length > 0;
3841
+ if (!hasMessages) {
3842
+ this.sendMessage(parentId, fallbackMsg);
3843
+ }
3844
+ tui.log.error(`Subagent ${sub.Role} (${id}) failed.`);
3845
+ } else {
3846
+ this.updateSubagentSummary(id, "Completed");
3847
+ const parentMsgs = this.peekMessages(parentId);
3848
+ const subagentMsg = parentMsgs.find((m) => m.includes(`(${id})`));
3849
+ if (subagentMsg) {
3850
+ tui.log.message(`
3851
+ ${subagentMsg}`);
3852
+ } else {
3853
+ tui.log.success(`Subagent ${sub.Role} (${id}) completed successfully.`);
3854
+ }
3855
+ }
3856
+ if (parentQueue) {
3857
+ let summaryContent = "Tarefa conclu\xEDda sem resumo.";
3858
+ let statusVal = success ? "completed" : "failed";
3859
+ if (isCancelled) {
3860
+ summaryContent = "Terminated by parent agent.";
3861
+ statusVal = "cancelled";
3862
+ } else if (success) {
3863
+ const parentMsgs = this.peekMessages(parentId);
3864
+ const subagentMsg = parentMsgs.find((m) => m.includes(`(${id})`));
3865
+ if (subagentMsg) {
3866
+ summaryContent = subagentMsg;
3867
+ }
3868
+ } else {
3869
+ summaryContent = `Subagente falhou com c\xF3digo de sa\xEDda ${exitCode}`;
3870
+ }
3871
+ parentQueue.push({
3872
+ type: "subagent_notification",
3873
+ content: summaryContent,
3874
+ timestamp: Date.now(),
3875
+ metadata: {
3876
+ subagentId: id,
3877
+ role: sub.Role,
3878
+ status: statusVal
3879
+ }
3880
+ });
3881
+ }
3882
+ } catch (error) {
3883
+ console.error(`Subagent ${id} failed to spawn:`, error);
3884
+ this.terminateSubagent(id, false);
3885
+ const errorMsg = error instanceof Error ? error.message : "Unknown error";
3886
+ this.sendMessage(
3887
+ parentId,
3888
+ `[Subagent Notification] Subagent ${sub.Role} (${id}) has finished with status: FAILED. Summary: Subagent spawn failed: ${errorMsg}`
3889
+ );
3890
+ tui.log.error(`Subagent ${sub.Role} (${id}) failed to spawn.`);
3891
+ if (parentQueue) {
3892
+ parentQueue.push({
3893
+ type: "subagent_notification",
3894
+ content: `Subagente falhou ao iniciar: ${errorMsg}`,
3895
+ timestamp: Date.now(),
3896
+ metadata: {
3897
+ subagentId: id,
3898
+ role: sub.Role,
3899
+ status: "failed"
3900
+ }
3901
+ });
3902
+ }
3903
+ }
3904
+ })();
3905
+ const state = this.subagents.get(id);
3906
+ if (state) {
3907
+ state.promise = promise;
3908
+ }
3909
+ invoked.push({ id, TypeName: sub.TypeName, Role: sub.Role });
3910
+ }
3911
+ return invoked;
3912
+ }
3913
+ };
3914
+ var subagentManager = new SubagentManager();
3915
+
3916
+ // src/core/workflow/message-queue.ts
3917
+ var MessageQueue = class {
3918
+ queue = [];
3919
+ pendingResolvers = [];
3920
+ push(message) {
3921
+ if (this.pendingResolvers.length > 0) {
3922
+ const resolve2 = this.pendingResolvers.shift();
3923
+ resolve2(message);
3924
+ } else {
3925
+ this.queue.push(message);
3926
+ }
3927
+ }
3928
+ async next() {
3929
+ if (this.queue.length > 0) {
3930
+ return this.queue.shift();
3931
+ }
3932
+ return new Promise((resolve2) => {
3933
+ this.pendingResolvers.push(resolve2);
3934
+ });
3935
+ }
3936
+ isEmpty() {
3937
+ return this.queue.length === 0;
3938
+ }
3939
+ };
3940
+
3941
+ // src/core/agents/developer-agent.ts
3942
+ async function promptUser(message, initialValue, placeholder, prefix = "") {
3943
+ let userReply = await tui.text({ message: `${prefix}${message}`, initialValue, placeholder });
3944
+ while (userReply === "/skills") {
3945
+ const availableSkills = await skillManager.listAvailableSkills();
3946
+ const options = availableSkills.map((name) => ({ value: name, label: name }));
3947
+ if (options.length === 0) {
3948
+ tui.log.warning("Nenhuma skill encontrada. Execute `shark super` para instalar as skills.");
3949
+ } else {
3950
+ const selectedSkill = await tui.select({
3951
+ message: "Selecione a Skill do Superpowers para ativar:",
3952
+ options
3953
+ });
3954
+ if (!tui.isCancel(selectedSkill)) {
3955
+ await skillManager.activateSkill(selectedSkill);
3956
+ tui.log.success(`\u2714 Skill '${selectedSkill}' ativada com sucesso!`);
3957
+ }
3958
+ }
3959
+ userReply = await tui.text({
3960
+ message: `${prefix}${message}`,
3961
+ initialValue,
3962
+ placeholder: "digite a instru\xE7\xE3o da tarefa..."
3963
+ });
3964
+ }
3965
+ return userReply;
3966
+ }
3967
+ async function waitForInputOrNotification(queue, promptMessage = "Your answer:", subagentPrefix = "", timeoutMs) {
3968
+ let cancelled = false;
3969
+ let resolvePromptPromise = null;
3970
+ let timerId = null;
3971
+ const promptPromise = new Promise((resolve2) => {
3972
+ resolvePromptPromise = resolve2;
3973
+ });
3974
+ const runPrompt = async () => {
3975
+ try {
3976
+ const userReply = await promptUser(promptMessage, void 0, void 0, subagentPrefix);
3977
+ if (!cancelled && resolvePromptPromise) {
3978
+ resolvePromptPromise({
3979
+ type: "user",
3980
+ content: userReply,
3981
+ timestamp: Date.now()
3982
+ });
3983
+ }
3984
+ } catch (e) {
3985
+ }
3986
+ };
3987
+ runPrompt();
3988
+ const queuePromise = queue.next();
3989
+ const promises = [promptPromise, queuePromise];
3990
+ if (timeoutMs !== void 0 && timeoutMs !== null) {
3991
+ const timeoutPromise = new Promise((resolve2) => {
3992
+ timerId = setTimeout(() => {
3993
+ resolve2({
3994
+ type: "timeout",
3995
+ content: "Wait timeout expired.",
3996
+ timestamp: Date.now()
3997
+ });
3998
+ }, timeoutMs);
3999
+ });
4000
+ promises.push(timeoutPromise);
4001
+ }
4002
+ const winner = await Promise.race(promises);
4003
+ if (timerId) {
4004
+ clearTimeout(timerId);
4005
+ }
4006
+ if (winner.type === "subagent_notification" || winner.type === "timeout") {
4007
+ cancelled = true;
4008
+ process.stdin.emit("data", "\r");
4009
+ await new Promise((r) => setTimeout(r, 50));
4010
+ if (process.stdout.isTTY) {
4011
+ process.stdout.write("\x1B[1A\x1B[2K\x1B[1A\x1B[2K");
131
4012
  }
132
- if (action === "resume") {
133
- tui.log.success(`Resuming work on ${colors.primary(existingState.projectName)}...`);
134
- tui.outro(`To continue, run: "shark agent" (Context loaded)`);
135
- return;
4013
+ }
4014
+ return winner;
4015
+ }
4016
+ function formatRoleForUI(role) {
4017
+ const limit = 20;
4018
+ if (role.length <= limit) return role;
4019
+ return role.substring(0, limit - 3) + "...";
4020
+ }
4021
+ async function interactiveDeveloperAgent(options = {}) {
4022
+ const isAuto = options.auto === true || process.argv.includes("--auto");
4023
+ const isSubagent = !!options.taskId && (options.taskId.startsWith("subagent-") || subagentManager.hasSubagent(options.taskId));
4024
+ const projectRoot = process.cwd();
4025
+ const messageQueue = new MessageQueue();
4026
+ let currentTask = options.taskInstruction;
4027
+ if (!currentTask) {
4028
+ if (isSubagent) {
4029
+ currentTask = "Subagent Task";
4030
+ } else {
4031
+ const userTask = await promptUser(
4032
+ "O que voc\xEA gostaria que o Shark Dev fizesse?",
4033
+ void 0,
4034
+ "ex: crie uma API REST simples ou digite /skills para ativar diretrizes"
4035
+ );
4036
+ if (tui.isCancel(userTask) || !userTask) {
4037
+ return { success: false, summary: "Task execution cancelled." };
4038
+ }
4039
+ currentTask = userTask;
136
4040
  }
137
4041
  }
138
- const projectName = await tui.text({
139
- message: "What is the name of your project?",
140
- placeholder: "e.g. My Awesome App",
141
- validate: (value) => {
142
- if (!value) return "Project name is required";
143
- if (value.trim().length < 2) return "Project name must be at least 2 characters";
4042
+ let subagentPrefix = "";
4043
+ if (options.taskId) {
4044
+ const subState = subagentManager.getSubagentState(options.taskId);
4045
+ if (subState) {
4046
+ subagentPrefix = `[Subagent: ${formatRoleForUI(subState.role)}] `;
144
4047
  }
145
- });
146
- if (tui.isCancel(projectName)) {
147
- tui.outro("Initialization cancelled.");
148
- return;
149
4048
  }
150
- const techStackOptions = TechStackEnum.options.map((stack) => ({
151
- value: stack,
152
- label: stack === "node-ts" ? "Node.js (TypeScript)" : stack === "nextjs" ? "Next.js" : stack.charAt(0).toUpperCase() + stack.slice(1)
153
- }));
154
- const techStack = await tui.select({
155
- message: "Select your technology stack:",
156
- options: techStackOptions
157
- });
158
- if (tui.isCancel(techStack)) {
159
- tui.outro("Initialization cancelled.");
160
- return;
4049
+ const log = {
4050
+ info: (msg) => tui.log.info(`${subagentPrefix}${msg}`),
4051
+ warning: (msg) => tui.log.warning(`${subagentPrefix}${msg}`),
4052
+ error: (msg) => tui.log.error(`${subagentPrefix}${msg}`),
4053
+ success: (msg) => tui.log.success(`${subagentPrefix}${msg}`),
4054
+ message: (msg) => tui.log.message(`${subagentPrefix}${msg}`)
4055
+ };
4056
+ let contextContent = "";
4057
+ const defaultContextPath = path10.resolve(projectRoot, "_sharkrc", "project-context.md");
4058
+ const specificContextPath = options.context ? path10.resolve(projectRoot, options.context) : defaultContextPath;
4059
+ if (fs9.existsSync(specificContextPath)) {
4060
+ try {
4061
+ contextContent = fs9.readFileSync(specificContextPath, "utf-8");
4062
+ } catch (e) {
4063
+ log.warning(`Failed to read context file: ${e}`);
4064
+ }
4065
+ }
4066
+ let basePrompt = ``;
4067
+ if (contextContent) {
4068
+ basePrompt += `
4069
+
4070
+ --- PROJECT CONTEXT ---
4071
+ ${contextContent}
4072
+ -----------------------
4073
+ `;
4074
+ }
4075
+ if (options.history) {
4076
+ basePrompt += `
4077
+
4078
+ --- PREVIOUS EXECUTION SUMMARY ---
4079
+ ${options.history}
4080
+ ----------------------------------
4081
+ `;
161
4082
  }
4083
+ basePrompt += `
4084
+
4085
+ \u{1F7E2} EXECUTION MODE
4086
+
4087
+ You are a highly skilled Developer Agent.
4088
+ \u{1F449} **CURRENT TASK**: "${currentTask}"
4089
+
4090
+ Your goal is to address the user's request:
4091
+ - If the request is a question, a request for explanation, or a discussion, answer the user using the 'talk_with_user' action. You can search the codebase or read files first to answer accurately. Once the explanation/discussion is complete, output a final response starting with "TASK_COMPLETED:" followed by a brief summary.
4092
+ - If the request is to implement changes, debug, or write code:
4093
+ 1. Implement the necessary changes.
4094
+ 2. Verify (compile/test).
4095
+ 3. When you are confident the task is done, output a final response starting with "TASK_COMPLETED:" followed by a brief technical summary of what you did.
4096
+ `;
4097
+ let nextPrompt = basePrompt;
4098
+ let keepGoing = true;
4099
+ let finalSummary = "";
4100
+ const conversationKey = options.taskId ? `dev_agent_${options.taskId}` : `dev_agent_${Date.now()}`;
4101
+ const anchorManager = new AnchorStateManager();
162
4102
  const spinner = tui.spinner();
163
- spinner.start("Initializing project workflow...");
4103
+ const handleCleanupSignal = (exitCode) => {
4104
+ const currentId = options.taskId || "parent";
4105
+ const active = subagentManager.getActiveSubagentsForParent(currentId);
4106
+ if (active.length > 0) {
4107
+ for (const sub of active) {
4108
+ subagentManager.killSubagent(sub.id);
4109
+ }
4110
+ }
4111
+ process.exit(exitCode);
4112
+ };
4113
+ const sigIntHandler = () => handleCleanupSignal(130);
4114
+ const sigTermHandler = () => handleCleanupSignal(143);
4115
+ process.on("SIGINT", sigIntHandler);
4116
+ process.on("SIGTERM", sigTermHandler);
164
4117
  try {
165
- const newState = {
166
- projectId: randomUUID(),
167
- projectName,
168
- techStack,
169
- currentStage: "business_analysis",
170
- stageStatus: "pending",
171
- lastUpdated: (/* @__PURE__ */ new Date()).toISOString(),
172
- artifacts: [],
173
- metadata: {
174
- initializedBy: "shark-cli",
175
- version: "0.0.1"
4118
+ while (keepGoing) {
4119
+ if (options.taskId && subagentManager.hasSubagent(options.taskId) && !subagentManager.isSubagentActive(options.taskId)) {
4120
+ log.warning(`Subagent ${options.taskId} was terminated.`);
4121
+ return { success: false, summary: "Subagent terminated by manager." };
176
4122
  }
177
- };
178
- await workflowManager.save(newState);
179
- spinner.stop("Project workflow created!");
180
- tui.log.success(`Project ${colors.primary(projectName)} initialized successfully.`);
181
- tui.log.message(`Your Project ID: ${colors.dim(newState.projectId)}`);
182
- tui.outro('Ready to start! Run "shark agent" to begin analyzing requirements.');
183
- } catch (error) {
184
- spinner.stop("Initialization failed.", 1);
185
- tui.log.error(error.message);
186
- process.exit(1);
4123
+ const recipientId = options.taskId || "parent";
4124
+ const mailboxMessages = subagentManager.retrieveMessages(recipientId);
4125
+ let currentTurnPrompt = nextPrompt;
4126
+ if (mailboxMessages.length > 0) {
4127
+ currentTurnPrompt += `
4128
+
4129
+ \u2709\uFE0F NEW MAILBOX MESSAGES:
4130
+ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4131
+ `;
4132
+ }
4133
+ const myId = options.taskId || "parent";
4134
+ const allSubagents = subagentManager.getActiveSubagentsForParent(myId);
4135
+ if (allSubagents.length > 0) {
4136
+ let panel = `
4137
+
4138
+ --- CURRENT ACTIVE SUBAGENTS ---
4139
+ `;
4140
+ panel += `You have ${allSubagents.length} active subagent(s) running in the background:
4141
+ `;
4142
+ for (const sub of allSubagents) {
4143
+ panel += `- ID: ${sub.id} | Role: ${sub.role} | Status: ${sub.status}
4144
+ `;
4145
+ }
4146
+ panel += `Use the 'wait' action if you have no other work and are waiting for these subagents to complete.
4147
+ `;
4148
+ panel += `--------------------------------
4149
+ `;
4150
+ currentTurnPrompt += panel;
4151
+ }
4152
+ const promptToSend = currentTurnPrompt + skillManager.getSystemInstructionExtension();
4153
+ try {
4154
+ const activeSubagents = subagentManager.getActiveSubagents();
4155
+ const activeCount = activeSubagents.length;
4156
+ const spinnerText = activeCount > 0 ? `\u{1F988} Shark Dev working... (Active subagents: ${activeCount})` : "\u{1F988} Shark Dev working...";
4157
+ spinner.start(spinnerText);
4158
+ const existingConversationId = await conversationManager.getConversationId(conversationKey);
4159
+ const provider = ProviderResolver.getProvider("developer_agent");
4160
+ const response = await provider.streamChat(promptToSend, {
4161
+ conversationId: existingConversationId,
4162
+ agentType: "developer_agent",
4163
+ onChunk: () => {
4164
+ }
4165
+ });
4166
+ if (response.conversation_id) {
4167
+ await conversationManager.saveConversationId(conversationKey, response.conversation_id);
4168
+ }
4169
+ spinner.stop("Response received");
4170
+ if (response.summary) {
4171
+ if (options.taskId) {
4172
+ subagentManager.updateSubagentSummary(options.taskId, response.summary);
4173
+ }
4174
+ log.info(`\u{1F4CC} Status: ${response.summary}`);
4175
+ }
4176
+ if (response.message && response.message.includes("TASK_COMPLETED:")) {
4177
+ finalSummary = response.message.split("TASK_COMPLETED:")[1].trim();
4178
+ log.success(`\u2714 Task Completed: ${finalSummary}`);
4179
+ if (options.taskId) {
4180
+ subagentManager.updateSubagentSummary(options.taskId, finalSummary);
4181
+ keepGoing = false;
4182
+ break;
4183
+ }
4184
+ if (!options.taskInstruction) {
4185
+ let nextMsg;
4186
+ if (!messageQueue.isEmpty()) {
4187
+ nextMsg = await messageQueue.next();
4188
+ } else {
4189
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
4190
+ }
4191
+ if (nextMsg.type === "user") {
4192
+ if (tui.isCancel(nextMsg.content)) {
4193
+ keepGoing = false;
4194
+ break;
4195
+ }
4196
+ }
4197
+ nextPrompt = nextMsg.content;
4198
+ continue;
4199
+ } else {
4200
+ keepGoing = false;
4201
+ break;
4202
+ }
4203
+ }
4204
+ if (response.message && response.message.includes("TASK_FAILED:")) {
4205
+ const failureReason = response.message.split("TASK_FAILED:")[1].trim();
4206
+ log.error(`\u274C Agent reported task failure: ${failureReason}`);
4207
+ if (options.taskId) {
4208
+ if (process.env.SHARK_PARENT_ID) {
4209
+ const parentId = process.env.SHARK_PARENT_ID;
4210
+ const role = process.env.SHARK_SUBAGENT_ROLE || "Subagent";
4211
+ subagentManager.sendMessage(
4212
+ parentId,
4213
+ `[Subagent Notification] Subagent ${role} (${options.taskId}) has finished with status: FAILED. Summary: ${failureReason}`
4214
+ );
4215
+ }
4216
+ return { success: false, summary: failureReason };
4217
+ }
4218
+ if (!options.taskInstruction) {
4219
+ let nextMsg;
4220
+ if (!messageQueue.isEmpty()) {
4221
+ nextMsg = await messageQueue.next();
4222
+ } else {
4223
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
4224
+ }
4225
+ if (nextMsg.type === "user") {
4226
+ if (tui.isCancel(nextMsg.content)) {
4227
+ return { success: false, summary: failureReason };
4228
+ }
4229
+ }
4230
+ nextPrompt = nextMsg.content;
4231
+ continue;
4232
+ } else {
4233
+ return { success: false, summary: failureReason };
4234
+ }
4235
+ }
4236
+ const action = response.action;
4237
+ if (!action) {
4238
+ if (isSubagent) {
4239
+ log.warning("No action returned by the subagent. Exiting loop.");
4240
+ keepGoing = false;
4241
+ break;
4242
+ }
4243
+ if (response.message) {
4244
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
4245
+ console.log(response.message);
4246
+ let nextMsg;
4247
+ if (!messageQueue.isEmpty()) {
4248
+ nextMsg = await messageQueue.next();
4249
+ } else {
4250
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
4251
+ }
4252
+ if (nextMsg.type === "user") {
4253
+ if (tui.isCancel(nextMsg.content)) {
4254
+ keepGoing = false;
4255
+ break;
4256
+ }
4257
+ }
4258
+ nextPrompt = nextMsg.content;
4259
+ } else {
4260
+ log.warning("No action or message returned by the agent.");
4261
+ let nextMsg;
4262
+ if (!messageQueue.isEmpty()) {
4263
+ nextMsg = await messageQueue.next();
4264
+ } else {
4265
+ nextMsg = await waitForInputOrNotification(messageQueue, "Agent returned empty response. Type a message to continue or press Ctrl+C to cancel:", subagentPrefix);
4266
+ }
4267
+ if (nextMsg.type === "user") {
4268
+ if (tui.isCancel(nextMsg.content)) {
4269
+ keepGoing = false;
4270
+ break;
4271
+ }
4272
+ }
4273
+ nextPrompt = nextMsg.content;
4274
+ }
4275
+ continue;
4276
+ }
4277
+ let resultMsg = "";
4278
+ if (action.type === "read_file") {
4279
+ const filePath = action.path || "";
4280
+ log.info(`\u{1F4D6} Reading (Anchored): ${colors.dim(filePath)}`);
4281
+ try {
4282
+ const content = anchorManager.getAnchoredContent(filePath);
4283
+ resultMsg = `[Action read_file(${filePath}) Success]:
4284
+ ${content}`;
4285
+ } catch (e) {
4286
+ resultMsg = `[Action read_file(${filePath}) Failed]: ${e.message}`;
4287
+ }
4288
+ } else if (action.type === "modify_file") {
4289
+ const filePath = action.path || "";
4290
+ log.warning(`\u{1F4DD} Modify (Anchored): ${colors.bold(filePath)}`);
4291
+ let approved = isAuto;
4292
+ if (!approved) {
4293
+ approved = await tui.confirm({ message: `Approve modify_file changes to ${filePath}?` });
4294
+ }
4295
+ if (approved) {
4296
+ try {
4297
+ anchorManager.applyAnchoredEdit(filePath, action.start_anchor || "", action.end_anchor || "", action.content || "");
4298
+ resultMsg = `[Action modify_file(${filePath}) Success]`;
4299
+ } catch (e) {
4300
+ resultMsg = `[Action modify_file(${filePath}) Failed]: ${e.message}`;
4301
+ }
4302
+ } else {
4303
+ resultMsg = `[Action modify_file(${filePath}) User Denied]`;
4304
+ }
4305
+ } else if (action.type === "create_file") {
4306
+ const filePath = action.path || "";
4307
+ log.warning(`\u{1F4DD} Create file: ${colors.bold(filePath)}`);
4308
+ let approved = isAuto;
4309
+ if (!approved) {
4310
+ approved = await tui.confirm({ message: `Approve create_file changes to ${filePath}?` });
4311
+ }
4312
+ if (approved) {
4313
+ try {
4314
+ const resolvedPath = path10.resolve(projectRoot, filePath);
4315
+ const dir = path10.dirname(resolvedPath);
4316
+ if (!fs9.existsSync(dir)) {
4317
+ fs9.mkdirSync(dir, { recursive: true });
4318
+ }
4319
+ fs9.writeFileSync(resolvedPath, action.content || "", "utf-8");
4320
+ resultMsg = `[Action create_file(${filePath}) Success]`;
4321
+ } catch (e) {
4322
+ resultMsg = `[Action create_file(${filePath}) Failed]: ${e.message}`;
4323
+ }
4324
+ } else {
4325
+ resultMsg = `[Action create_file(${filePath}) User Denied]`;
4326
+ }
4327
+ } else if (action.type === "delete_file") {
4328
+ const filePath = action.path || "";
4329
+ log.warning(`\u{1F5D1}\uFE0F Delete file: ${colors.bold(filePath)}`);
4330
+ let approved = isAuto;
4331
+ if (!approved) {
4332
+ approved = await tui.confirm({ message: `Approve delete_file changes to ${filePath}?` });
4333
+ }
4334
+ if (approved) {
4335
+ try {
4336
+ const resolvedPath = path10.resolve(projectRoot, filePath);
4337
+ if (fs9.existsSync(resolvedPath)) {
4338
+ fs9.rmSync(resolvedPath, { force: true });
4339
+ }
4340
+ resultMsg = `[Action delete_file(${filePath}) Success]`;
4341
+ } catch (e) {
4342
+ resultMsg = `[Action delete_file(${filePath}) Failed]: ${e.message}`;
4343
+ }
4344
+ } else {
4345
+ resultMsg = `[Action delete_file(${filePath}) User Denied]`;
4346
+ }
4347
+ } else if (action.type === "run_command") {
4348
+ const cmd = action.command || "";
4349
+ log.info(`\u{1F4BB} Executing: ${colors.dim(cmd)}`);
4350
+ let approved = isAuto;
4351
+ if (!approved) {
4352
+ approved = await tui.confirm({ message: `Execute run_command: ${cmd}?` });
4353
+ }
4354
+ if (approved) {
4355
+ try {
4356
+ const output = await handleRunCommand(cmd);
4357
+ resultMsg = `[Action run_command(${cmd}) Success]:
4358
+ ${output}`;
4359
+ } catch (e) {
4360
+ resultMsg = `[Action run_command(${cmd}) Failed]: ${e.message}`;
4361
+ }
4362
+ } else {
4363
+ resultMsg = `[Action run_command(${cmd}) User Denied]`;
4364
+ }
4365
+ } else if (action.type === "list_files") {
4366
+ const dirPath = action.path || ".";
4367
+ log.info(`\u{1F4C2} Scanning: ${colors.dim(dirPath)}`);
4368
+ try {
4369
+ const result = handleListFiles(dirPath);
4370
+ resultMsg = `[Action list_files(${dirPath}) Success]:
4371
+ ${result}`;
4372
+ } catch (e) {
4373
+ resultMsg = `[Action list_files(${dirPath}) Failed]: ${e.message}`;
4374
+ }
4375
+ } else if (action.type === "search_file") {
4376
+ const pattern = action.path || "";
4377
+ log.info(`\u{1F50D} Searching files: ${colors.dim(pattern)}`);
4378
+ try {
4379
+ const result = handleSearchFile(pattern);
4380
+ resultMsg = `[Action search_file(${pattern}) Success]:
4381
+ ${result}`;
4382
+ } catch (e) {
4383
+ resultMsg = `[Action search_file(${pattern}) Failed]: ${e.message}`;
4384
+ }
4385
+ } else if (action.type === "search_code") {
4386
+ const glob = action.path || "src/**/*";
4387
+ const query = action.query || "";
4388
+ const isRegex = action.is_regex === true;
4389
+ log.info(`\u{1F50E} Search code: ${colors.dim(`"${query}" in ${glob}`)}`);
4390
+ try {
4391
+ const result = handleSearchCode(glob, query, isRegex);
4392
+ resultMsg = `[Action search_code("${query}" in "${glob}") Success]:
4393
+ ${result}`;
4394
+ } catch (e) {
4395
+ resultMsg = `[Action search_code("${query}" in "${glob}") Failed]: ${e.message}`;
4396
+ }
4397
+ } else if (action.type === "use_mcp_tool") {
4398
+ resultMsg = `[Action use_mcp_tool Failed]: MCP tools are not configured/available in this agent.`;
4399
+ } else if (action.type === "activate_skill") {
4400
+ const name = action.skill_name || "";
4401
+ log.info(`\u26A1 Activating skill: ${colors.bold(name)}`);
4402
+ try {
4403
+ await skillManager.activateSkill(name);
4404
+ resultMsg = `[System]: Skill '${name}' activated successfully.`;
4405
+ } catch (e) {
4406
+ resultMsg = `[System]: Failed to activate skill '${name}': ${e.message}`;
4407
+ }
4408
+ } else if (action.type === "talk_with_user") {
4409
+ const isSystemError = action.content?.startsWith("[SYSTEM ERROR]");
4410
+ if (isSystemError) {
4411
+ log.error(`\u26A0\uFE0F Detectado erro na resposta do Agente (truncado ou inv\xE1lido).`);
4412
+ log.info(colors.dim(action.content || ""));
4413
+ if (isSubagent) {
4414
+ resultMsg = action.content || "";
4415
+ nextPrompt = resultMsg;
4416
+ continue;
4417
+ } else {
4418
+ let approved = isAuto;
4419
+ if (!approved) {
4420
+ approved = await tui.confirm({ message: `Enviar notifica\xE7\xE3o de erro para o agente tentar se recuperar automaticamente?` });
4421
+ }
4422
+ if (approved) {
4423
+ resultMsg = action.content || "";
4424
+ } else {
4425
+ let nextMsg;
4426
+ if (!messageQueue.isEmpty()) {
4427
+ nextMsg = await messageQueue.next();
4428
+ } else {
4429
+ nextMsg = await waitForInputOrNotification(messageQueue, "Seu prompt alternativo para o agente:", subagentPrefix);
4430
+ }
4431
+ if (nextMsg.type === "user") {
4432
+ if (tui.isCancel(nextMsg.content)) {
4433
+ keepGoing = false;
4434
+ break;
4435
+ }
4436
+ }
4437
+ resultMsg = nextMsg.content;
4438
+ }
4439
+ }
4440
+ } else {
4441
+ const contentStr = action.content || "";
4442
+ const hasCompleted = contentStr.includes("TASK_COMPLETED:");
4443
+ if (isSubagent) {
4444
+ const summary = hasCompleted ? contentStr.split("TASK_COMPLETED:")[1].trim() : contentStr;
4445
+ subagentManager.updateSubagentSummary(options.taskId, summary);
4446
+ finalSummary = summary;
4447
+ keepGoing = false;
4448
+ break;
4449
+ }
4450
+ if (hasCompleted) {
4451
+ finalSummary = contentStr.split("TASK_COMPLETED:")[1].trim();
4452
+ log.success(`\u2714 Task Completed: ${finalSummary}`);
4453
+ if (!options.taskInstruction) {
4454
+ let nextMsg2;
4455
+ if (!messageQueue.isEmpty()) {
4456
+ nextMsg2 = await messageQueue.next();
4457
+ } else {
4458
+ nextMsg2 = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
4459
+ }
4460
+ if (nextMsg2.type === "user") {
4461
+ if (tui.isCancel(nextMsg2.content)) {
4462
+ keepGoing = false;
4463
+ break;
4464
+ }
4465
+ }
4466
+ nextPrompt = nextMsg2.content;
4467
+ continue;
4468
+ } else {
4469
+ keepGoing = false;
4470
+ break;
4471
+ }
4472
+ }
4473
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
4474
+ console.log(contentStr);
4475
+ let nextMsg;
4476
+ if (!messageQueue.isEmpty()) {
4477
+ nextMsg = await messageQueue.next();
4478
+ } else {
4479
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
4480
+ }
4481
+ if (nextMsg.type === "user") {
4482
+ if (tui.isCancel(nextMsg.content)) {
4483
+ keepGoing = false;
4484
+ break;
4485
+ }
4486
+ resultMsg = `User Reply: ${nextMsg.content}`;
4487
+ } else {
4488
+ resultMsg = nextMsg.content;
4489
+ }
4490
+ }
4491
+ } else if (action.type === "define_subagent") {
4492
+ const name = action.name || "";
4493
+ const desc = action.description || "";
4494
+ const sysPrompt = action.system_prompt || "";
4495
+ const opts = {
4496
+ enableWriteTools: action.enable_write_tools ?? void 0,
4497
+ enableSubagentTools: action.enable_subagent_tools ?? void 0,
4498
+ enableMcpTools: action.enable_mcp_tools ?? void 0
4499
+ };
4500
+ log.info(`\u{1F6E0}\uFE0F Defining subagent type: ${colors.bold(name)}`);
4501
+ subagentManager.defineSubagentType(name, desc, sysPrompt, opts);
4502
+ resultMsg = `[Action define_subagent Success]: Defined subagent type '${name}'`;
4503
+ } else if (action.type === "invoke_subagent") {
4504
+ const subagentsToInvoke = action.Subagents || [];
4505
+ log.info(`\u{1F680} Invoking ${subagentsToInvoke.length} subagent(s)`);
4506
+ const parentId = options.taskId || "parent";
4507
+ const invoked = await subagentManager.invokeSubagents(subagentsToInvoke, parentId, messageQueue);
4508
+ resultMsg = `[Action invoke_subagent Success]: Invoked subagents:
4509
+ ${invoked.map((s) => `- ID: ${s.id}, Type: ${s.TypeName}, Role: ${s.Role}`).join("\n")}`;
4510
+ } else if (action.type === "send_message") {
4511
+ const recipient = action.Recipient || "";
4512
+ const message = action.Message || "";
4513
+ log.info(`\u2709\uFE0F Sending message to ${colors.bold(recipient)}`);
4514
+ subagentManager.sendMessage(recipient, message);
4515
+ resultMsg = `[Action send_message Success]: Message sent to '${recipient}'`;
4516
+ } else if (action.type === "manage_subagents") {
4517
+ const subAction = action.Action || "";
4518
+ const ids = action.ConversationIds || [];
4519
+ log.info(`\u2699\uFE0F Managing subagents. Action: ${colors.bold(subAction)}`);
4520
+ if (subAction === "list") {
4521
+ const active = subagentManager.getActiveSubagents();
4522
+ resultMsg = `[Action manage_subagents Success]: Active subagents:
4523
+ ${active.map((s) => `- ID: ${s.id}, Type: ${s.type}, Role: ${s.role}`).join("\n")}`;
4524
+ } else if (subAction === "read_logs") {
4525
+ const id = ids[0];
4526
+ if (!id) {
4527
+ resultMsg = `[Action manage_subagents Failed]: No subagent ID provided in ConversationIds.`;
4528
+ } else {
4529
+ try {
4530
+ const logs = subagentManager.getSubagentLogs(id);
4531
+ resultMsg = `[Action manage_subagents Success]: Last log lines for subagent ${id}:
4532
+ \`\`\`
4533
+ ${logs}
4534
+ \`\`\``;
4535
+ } catch (e) {
4536
+ resultMsg = `[Action manage_subagents Failed]: ${e.message}`;
4537
+ }
4538
+ }
4539
+ } else if (subAction === "kill") {
4540
+ for (const id of ids) {
4541
+ subagentManager.killSubagent(id);
4542
+ }
4543
+ resultMsg = `[Action manage_subagents Success]: Terminated subagents: ${ids.join(", ")}`;
4544
+ } else if (subAction === "kill_all") {
4545
+ subagentManager.killAllSubagents();
4546
+ resultMsg = `[Action manage_subagents Success]: Terminated all active subagents`;
4547
+ } else {
4548
+ resultMsg = `[Action manage_subagents Failed]: Unknown action '${subAction}'`;
4549
+ }
4550
+ } else if (action.type === "complete_task") {
4551
+ const detailedContent = action.content || "";
4552
+ const taskSummary = action.summary || "Task completed successfully.";
4553
+ if (isSubagent) {
4554
+ subagentManager.updateSubagentSummary(options.taskId, taskSummary);
4555
+ if (process.env.SHARK_PARENT_ID) {
4556
+ subagentManager.sendMessage(
4557
+ process.env.SHARK_PARENT_ID,
4558
+ `[Subagent Notification] Subagent ${process.env.SHARK_SUBAGENT_ROLE || "Subagent"} (${options.taskId}) completed.
4559
+ Result Details:
4560
+ ${detailedContent}`
4561
+ );
4562
+ }
4563
+ }
4564
+ finalSummary = taskSummary;
4565
+ keepGoing = false;
4566
+ break;
4567
+ } else if (action.type === "wait") {
4568
+ const durationSeconds = action.duration_seconds || 0;
4569
+ const durationMs = durationSeconds > 0 ? durationSeconds * 1e3 : void 0;
4570
+ log.info(`\u23F3 Waiting for updates (Timeout: ${durationSeconds || "infinite"}s)...`);
4571
+ let nextMsg;
4572
+ if (!messageQueue.isEmpty()) {
4573
+ nextMsg = await messageQueue.next();
4574
+ } else {
4575
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, durationMs);
4576
+ }
4577
+ if (nextMsg.type === "timeout") {
4578
+ resultMsg = `[System]: Wait duration of ${durationSeconds} seconds expired. No notifications received.`;
4579
+ } else if (nextMsg.type === "user") {
4580
+ if (tui.isCancel(nextMsg.content)) {
4581
+ keepGoing = false;
4582
+ break;
4583
+ }
4584
+ resultMsg = `User Reply: ${nextMsg.content}`;
4585
+ } else {
4586
+ resultMsg = nextMsg.content;
4587
+ }
4588
+ } else {
4589
+ resultMsg = `[Unsupported action type: ${action.type}]`;
4590
+ }
4591
+ FileLogger.log("TOOL_EXECUTION", `Action: ${action.type}`, { action, result: resultMsg });
4592
+ nextPrompt = resultMsg;
4593
+ } catch (e) {
4594
+ log.error(e.message);
4595
+ if (options.taskId && process.env.SHARK_PARENT_ID) {
4596
+ const parentId = process.env.SHARK_PARENT_ID;
4597
+ const role = process.env.SHARK_SUBAGENT_ROLE || "Subagent";
4598
+ subagentManager.sendMessage(
4599
+ parentId,
4600
+ `[Subagent Notification] Subagent ${role} (${options.taskId}) has finished with status: FAILED. Summary: Error: ${e.message}`
4601
+ );
4602
+ }
4603
+ keepGoing = false;
4604
+ return { success: false, summary: `Error: ${e.message}` };
4605
+ }
4606
+ }
4607
+ const finalResult = { success: true, summary: finalSummary || "Task completed without summary." };
4608
+ if (options.taskId && process.env.SHARK_PARENT_ID) {
4609
+ const parentId = process.env.SHARK_PARENT_ID;
4610
+ const role = process.env.SHARK_SUBAGENT_ROLE || "Subagent";
4611
+ subagentManager.sendMessage(
4612
+ parentId,
4613
+ `[Subagent Notification] Subagent ${role} (${options.taskId}) has finished with status: COMPLETED. Summary: ${finalResult.summary}`
4614
+ );
4615
+ }
4616
+ log.success("\u2705 Task Scope Completed");
4617
+ return finalResult;
4618
+ } finally {
4619
+ process.off("SIGINT", sigIntHandler);
4620
+ process.off("SIGTERM", sigTermHandler);
4621
+ const currentId = options.taskId || "parent";
4622
+ const myActiveSubagents = subagentManager.getActiveSubagentsForParent(currentId);
4623
+ if (myActiveSubagents.length > 0) {
4624
+ log.info(`\u{1F9F9} Terminating ${myActiveSubagents.length} active child subagent(s) before exit...`);
4625
+ for (const sub of myActiveSubagents) {
4626
+ subagentManager.killSubagent(sub.id);
4627
+ }
4628
+ }
187
4629
  }
188
- };
189
- var initCommand = new Command("init").description("Initialize a new Shark project").action(initAction);
4630
+ }
190
4631
 
191
4632
  // src/commands/dev.ts
192
- import { Command as Command2 } from "commander";
193
- var devCommand = new Command2("dev").description("Starts the Shark Developer Agent (Shark Dev Orchestration V2)").option("-t, --task <type>", "Initial task description (Quick Mode)").option("-c, --context <path>", "Path to custom context file").option("-y, --yes", "Automatically approve all actions without prompting").option("--auto", "Automatically approve all actions without prompting").option("--export-schema", "Export the agent response JSON schema").action(async (options) => {
4633
+ var devCommand = new Command2("dev").description("Starts the Shark Developer Agent (Shark Dev Orchestration V2)").option("-t, --task <type>", "Initial task description (Quick Mode)").option("-c, --context <path>", "Path to custom context file").option("-y, --yes", "Automatically approve all actions without prompting").option("--auto", "Automatically approve all actions without prompting").option("--export-schema", "Export the agent response JSON schema").option("--taskId <id>", "ID of the current subagent task").action(async (options) => {
194
4634
  if (options.exportSchema) {
195
4635
  console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
196
4636
  return;
@@ -199,7 +4639,8 @@ var devCommand = new Command2("dev").description("Starts the Shark Developer Age
199
4639
  const result = await interactiveDeveloperAgent({
200
4640
  taskInstruction: options.task,
201
4641
  context: options.context,
202
- auto: options.yes || options.auto
4642
+ auto: options.yes || options.auto,
4643
+ taskId: options.taskId
203
4644
  });
204
4645
  if (!result.success) {
205
4646
  console.error("Task execution failed:", result.summary);
@@ -215,8 +4656,8 @@ var devCommand = new Command2("dev").description("Starts the Shark Developer Age
215
4656
  import { Command as Command3 } from "commander";
216
4657
 
217
4658
  // src/core/agents/legacy-developer-agent.ts
218
- import fs2 from "fs";
219
- import path2 from "path";
4659
+ import fs10 from "fs";
4660
+ import path11 from "path";
220
4661
  var AGENT_TYPE = "developer_agent";
221
4662
  async function validateTypeScript(filePath) {
222
4663
  try {
@@ -245,11 +4686,11 @@ async function interactiveDeveloperAgent2(options = {}) {
245
4686
  }
246
4687
  const projectRoot = process.cwd();
247
4688
  let contextContent = "";
248
- const defaultContextPath = path2.resolve(projectRoot, "_sharkrc", "project-context.md");
249
- const specificContextPath = options.context ? path2.resolve(projectRoot, options.context) : defaultContextPath;
250
- if (fs2.existsSync(specificContextPath)) {
4689
+ const defaultContextPath = path11.resolve(projectRoot, "_sharkrc", "project-context.md");
4690
+ const specificContextPath = options.context ? path11.resolve(projectRoot, options.context) : defaultContextPath;
4691
+ if (fs10.existsSync(specificContextPath)) {
251
4692
  try {
252
- contextContent = fs2.readFileSync(specificContextPath, "utf-8");
4693
+ contextContent = fs10.readFileSync(specificContextPath, "utf-8");
253
4694
  } catch (e) {
254
4695
  tui.log.warning(`Failed to read context file: ${e}`);
255
4696
  }
@@ -391,9 +4832,9 @@ ${result}
391
4832
  }
392
4833
  if (approved) {
393
4834
  if (action.type === "create_file") {
394
- const dir = path2.dirname(path2.resolve(projectRoot, filePath));
395
- if (!fs2.existsSync(dir)) fs2.mkdirSync(dir, { recursive: true });
396
- fs2.writeFileSync(path2.resolve(projectRoot, filePath), action.content || "", "utf-8");
4835
+ const dir = path11.dirname(path11.resolve(projectRoot, filePath));
4836
+ if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
4837
+ fs10.writeFileSync(path11.resolve(projectRoot, filePath), action.content || "", "utf-8");
397
4838
  executionResults += `[Action create_file]: Success
398
4839
 
399
4840
  `;
@@ -407,7 +4848,7 @@ ${result}
407
4848
 
408
4849
  `;
409
4850
  }
410
- const val = await validateTypeScript(path2.resolve(projectRoot, filePath));
4851
+ const val = await validateTypeScript(path11.resolve(projectRoot, filePath));
411
4852
  if (!val.valid) executionResults += `[Validation Failed]: ${val.error}
412
4853
 
413
4854
  `;
@@ -549,23 +4990,23 @@ async function callDevAgentApi(prompt, onChunk, conversationKey = AGENT_TYPE) {
549
4990
  }
550
4991
 
551
4992
  // src/core/workflow/task-manager.ts
552
- import fs3 from "fs";
553
- import path3 from "path";
4993
+ import fs11 from "fs";
4994
+ import path12 from "path";
554
4995
  var TaskManager = class {
555
4996
  projectRoot;
556
4997
  specPath;
557
4998
  constructor(projectRoot = process.cwd()) {
558
4999
  this.projectRoot = projectRoot;
559
- this.specPath = path3.resolve(this.projectRoot, "_sharkrc", "tech-spec.md");
5000
+ this.specPath = path12.resolve(this.projectRoot, "_sharkrc", "tech-spec.md");
560
5001
  }
561
5002
  /**
562
5003
  * Reads the tech-spec.md file and analyzes its current state.
563
5004
  */
564
5005
  analyzeSpecState() {
565
- if (!fs3.existsSync(this.specPath)) {
5006
+ if (!fs11.existsSync(this.specPath)) {
566
5007
  return { status: "MISSING", allTasks: [] };
567
5008
  }
568
- const content = fs3.readFileSync(this.specPath, "utf-8");
5009
+ const content = fs11.readFileSync(this.specPath, "utf-8");
569
5010
  const lines = content.split("\n");
570
5011
  const tasks = [];
571
5012
  let taskIndex = 1;
@@ -631,7 +5072,7 @@ var TaskManager = class {
631
5072
  console.error(`Task ${taskId} not found.`);
632
5073
  return false;
633
5074
  }
634
- const content = fs3.readFileSync(this.specPath, "utf-8");
5075
+ const content = fs11.readFileSync(this.specPath, "utf-8");
635
5076
  const lines = content.split("\n");
636
5077
  const targetLine = lines[task.line_number];
637
5078
  if (!targetLine.includes(task.description)) {
@@ -640,7 +5081,7 @@ var TaskManager = class {
640
5081
  }
641
5082
  const newLine = targetLine.replace("- [ ]", "- [x]").replace("- [/]", "- [x]");
642
5083
  lines[task.line_number] = newLine;
643
- fs3.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
5084
+ fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
644
5085
  return true;
645
5086
  }
646
5087
  /**
@@ -650,19 +5091,19 @@ var TaskManager = class {
650
5091
  const state = this.analyzeSpecState();
651
5092
  const task = state.allTasks.find((t) => t.id === taskId);
652
5093
  if (!task) return false;
653
- const content = fs3.readFileSync(this.specPath, "utf-8");
5094
+ const content = fs11.readFileSync(this.specPath, "utf-8");
654
5095
  const lines = content.split("\n");
655
5096
  let targetLine = lines[task.line_number];
656
5097
  targetLine = targetLine.replace("- [ ]", "- [/]");
657
5098
  lines[task.line_number] = targetLine;
658
- fs3.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
5099
+ fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
659
5100
  return true;
660
5101
  }
661
5102
  /**
662
5103
  * Completely updates the spec file content (used by Spec Agent).
663
5104
  */
664
5105
  updateSpecContent(newContent) {
665
- fs3.writeFileSync(this.specPath, newContent, "utf-8");
5106
+ fs11.writeFileSync(this.specPath, newContent, "utf-8");
666
5107
  }
667
5108
  getSpecPath() {
668
5109
  return this.specPath;
@@ -670,17 +5111,17 @@ var TaskManager = class {
670
5111
  };
671
5112
 
672
5113
  // src/core/agents/specification-agent.ts
673
- import fs4 from "fs";
674
- import path4 from "path";
5114
+ import fs12 from "fs";
5115
+ import path13 from "path";
675
5116
  var AGENT_TYPE2 = "specification_agent";
676
5117
  async function interactiveSpecificationAgent(options = {}) {
677
5118
  FileLogger.init();
678
5119
  tui.intro("\u{1F3D7}\uFE0F Specification Agent (Template-Based)");
679
5120
  const projectRoot = process.cwd();
680
- const sharkRcDir = path4.resolve(projectRoot, "_sharkrc");
681
- if (!fs4.existsSync(sharkRcDir)) fs4.mkdirSync(sharkRcDir, { recursive: true });
682
- const outputFile = path4.resolve(sharkRcDir, "tech-spec.md");
683
- if (!fs4.existsSync(outputFile)) {
5121
+ const sharkRcDir = path13.resolve(projectRoot, "_sharkrc");
5122
+ if (!fs12.existsSync(sharkRcDir)) fs12.mkdirSync(sharkRcDir, { recursive: true });
5123
+ const outputFile = path13.resolve(sharkRcDir, "tech-spec.md");
5124
+ if (!fs12.existsSync(outputFile)) {
684
5125
  let initialContent = `# Technical Specification: {{PROJECT_NAME}}
685
5126
 
686
5127
  ## 1. Technology Stack
@@ -705,28 +5146,28 @@ async function interactiveSpecificationAgent(options = {}) {
705
5146
  ## 5. Implementation Steps
706
5147
  [TO BE FILLED - MUST BE CHECKBOXES]
707
5148
  `;
708
- const projectName = path4.basename(projectRoot);
5149
+ const projectName = path13.basename(projectRoot);
709
5150
  initialContent = initialContent.replace(/{{PROJECT_NAME}}/g, projectName);
710
5151
  const BOM = "\uFEFF";
711
- fs4.writeFileSync(outputFile, BOM + initialContent, { encoding: "utf-8" });
5152
+ fs12.writeFileSync(outputFile, BOM + initialContent, { encoding: "utf-8" });
712
5153
  tui.log.success(`\u2705 Created: ${colors.bold("_sharkrc/tech-spec.md")}`);
713
5154
  } else {
714
5155
  tui.log.info(`\u{1F4C4} Using existing ${colors.bold("_sharkrc/tech-spec.md")}`);
715
5156
  }
716
5157
  let contextContent = "";
717
- const contextPath = path4.resolve(projectRoot, "_sharkrc", "project-context.md");
718
- if (fs4.existsSync(contextPath)) {
719
- contextContent = fs4.readFileSync(contextPath, "utf-8");
5158
+ const contextPath = path13.resolve(projectRoot, "_sharkrc", "project-context.md");
5159
+ if (fs12.existsSync(contextPath)) {
5160
+ contextContent = fs12.readFileSync(contextPath, "utf-8");
720
5161
  tui.log.info(`\u{1F4D8} Context loaded.`);
721
5162
  }
722
5163
  let briefingContent = "";
723
- if (options.briefingPath && fs4.existsSync(options.briefingPath)) {
724
- briefingContent = fs4.readFileSync(options.briefingPath, "utf-8");
5164
+ if (options.briefingPath && fs12.existsSync(options.briefingPath)) {
5165
+ briefingContent = fs12.readFileSync(options.briefingPath, "utf-8");
725
5166
  tui.log.info(`\u{1F4C4} Briefing loaded from: ${colors.dim(options.briefingPath)}`);
726
5167
  } else {
727
- const standardBriefing = path4.resolve(projectRoot, "_sharkrc", "briefing.md");
728
- if (fs4.existsSync(standardBriefing)) {
729
- briefingContent = fs4.readFileSync(standardBriefing, "utf-8");
5168
+ const standardBriefing = path13.resolve(projectRoot, "_sharkrc", "briefing.md");
5169
+ if (fs12.existsSync(standardBriefing)) {
5170
+ briefingContent = fs12.readFileSync(standardBriefing, "utf-8");
730
5171
  tui.log.info(`\u{1F4C4} Briefing loaded.`);
731
5172
  }
732
5173
  }
@@ -787,8 +5228,8 @@ async function runSpecLoop(initialMessage, targetPath, overrideAgentId) {
787
5228
  const spinner = tui.spinner();
788
5229
  spinner.start(`\u{1F3D7}\uFE0F Spec Agent working (Step ${stepCount}/${MAX_STEPS})...`);
789
5230
  let pendingSections = [];
790
- if (fs4.existsSync(targetPath)) {
791
- const content = fs4.readFileSync(targetPath, "utf-8");
5231
+ if (fs12.existsSync(targetPath)) {
5232
+ const content = fs12.readFileSync(targetPath, "utf-8");
792
5233
  if (content.includes("[TO BE ANALYZED]")) pendingSections.push("Analysis Sections (Stack, Arch, Data, API)");
793
5234
  if (content.includes("[TO BE FILLED")) pendingSections.push("Implementation Steps");
794
5235
  }
@@ -863,11 +5304,11 @@ ${result}
863
5304
 
864
5305
  `;
865
5306
  } else if (["create_file", "modify_file"].includes(action.type)) {
866
- let actionPath = path4.resolve(action.path || "");
867
- const resolvedTargetPath = path4.resolve(targetPath);
5307
+ let actionPath = path13.resolve(action.path || "");
5308
+ const resolvedTargetPath = path13.resolve(targetPath);
868
5309
  let isTarget = actionPath === resolvedTargetPath;
869
- if (!isTarget && path4.basename(actionPath) === "tech-spec.md") {
870
- tui.log.warning(`Redirecting ${action.type} from ${action.path} to ${path4.relative(process.cwd(), targetPath)}`);
5310
+ if (!isTarget && path13.basename(actionPath) === "tech-spec.md") {
5311
+ tui.log.warning(`Redirecting ${action.type} from ${action.path} to ${path13.relative(process.cwd(), targetPath)}`);
871
5312
  action.path = targetPath;
872
5313
  actionPath = resolvedTargetPath;
873
5314
  isTarget = true;
@@ -883,7 +5324,7 @@ ${result}
883
5324
  try {
884
5325
  if (action.type === "create_file") {
885
5326
  const BOM = "\uFEFF";
886
- fs4.writeFileSync(action.path, BOM + (action.content || ""), "utf-8");
5327
+ fs12.writeFileSync(action.path, BOM + (action.content || ""), "utf-8");
887
5328
  tui.log.success(`\u2705 Created: ${action.path}`);
888
5329
  executionResults += `[Action create_file]: Success.
889
5330
  `;
@@ -949,7 +5390,7 @@ Voc\xEA completou a FASE 2 com sucesso.
949
5390
  Continue seu trabalho na Fase ${currentPhase} ou emita "PHASE_COMPLETED" se terminou esta etapa atual.`;
950
5391
  continue;
951
5392
  }
952
- const content = fs4.existsSync(targetPath) ? fs4.readFileSync(targetPath, "utf-8") : "";
5393
+ const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
953
5394
  if (content.includes("[TO BE")) {
954
5395
  const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
955
5396
  let missing = pendingMatches.length > 0 ? pendingMatches.join(", ") : "algumas se\xE7\xF5es";
@@ -981,7 +5422,7 @@ Voc\xEA \xE9 OBRIGADO a usar a action \`modify_file\` para preencher o conte\xFA
981
5422
 
982
5423
  User Reply: ${userReply}`;
983
5424
  } else if (executionResults) {
984
- const content = fs4.existsSync(targetPath) ? fs4.readFileSync(targetPath, "utf-8") : "";
5425
+ const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
985
5426
  let systemMsg = "Execu\xE7\xE3o da ferramenta conclu\xEDda.";
986
5427
  if (specUpdated) {
987
5428
  if (content.includes("[TO BE")) {
@@ -1169,42 +5610,49 @@ var exportSchemaCommand = new Command4("export-schema").description("Outputs the
1169
5610
  console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
1170
5611
  });
1171
5612
 
1172
- // src/commands/super.ts
5613
+ // src/commands/export-prompt.ts
1173
5614
  import { Command as Command5 } from "commander";
1174
- import { fileURLToPath } from "url";
1175
- import path5 from "path";
1176
- import os2 from "os";
1177
- import fs5 from "fs/promises";
5615
+ var exportPromptCommand = new Command5("export-prompt").description("Outputs the unified agent system prompt").action(() => {
5616
+ console.log(UNIFIED_SYSTEM_PROMPT);
5617
+ });
5618
+
5619
+ // src/commands/super.ts
5620
+ import { Command as Command6 } from "commander";
5621
+ import { fileURLToPath as fileURLToPath3 } from "url";
5622
+ import path14 from "path";
5623
+ import os3 from "os";
5624
+ import fs13 from "fs/promises";
1178
5625
  var superCommandAction = async (options = {}) => {
1179
- const __filename2 = fileURLToPath(import.meta.url);
1180
- const __dirname2 = path5.dirname(__filename2);
1181
- const packageRoot = path5.resolve(__dirname2, "../../");
1182
- const internalSkillsPath = path5.join(packageRoot, "skills");
1183
- const targetPath = options.local ? path5.join(process.cwd(), ".agents", "skills") : path5.join(os2.homedir(), ".shark", "skills");
5626
+ const __filename2 = fileURLToPath3(import.meta.url);
5627
+ const __dirname2 = path14.dirname(__filename2);
5628
+ const packageRoot = path14.resolve(__dirname2, "../../");
5629
+ const internalSkillsPath = path14.join(packageRoot, "skills");
5630
+ const targetPath = options.local ? path14.join(process.cwd(), ".agents", "skills") : path14.join(os3.homedir(), ".shark", "skills");
1184
5631
  try {
1185
5632
  try {
1186
- await fs5.rm(targetPath, { recursive: true, force: true });
5633
+ await fs13.rm(targetPath, { recursive: true, force: true });
1187
5634
  } catch {
1188
5635
  }
1189
- await fs5.mkdir(targetPath, { recursive: true });
1190
- await fs5.cp(internalSkillsPath, targetPath, { recursive: true, force: true });
5636
+ await fs13.mkdir(targetPath, { recursive: true });
5637
+ await fs13.cp(internalSkillsPath, targetPath, { recursive: true, force: true });
1191
5638
  console.log(`\u{1F680} Superpowers skills installed successfully to ${targetPath}`);
1192
5639
  } catch (error) {
1193
5640
  console.error(`\u274C Failed to install superpowers skills: ${error.message}`);
1194
5641
  process.exit(1);
1195
5642
  }
1196
5643
  };
1197
- var superCommand = new Command5("super").description("Install Superpowers skills globally or locally").option("-l, --local", "Install skills locally in the current project under .agents/skills").action(superCommandAction);
5644
+ var superCommand = new Command6("super").description("Install Superpowers skills globally or locally").option("-l, --local", "Install skills locally in the current project under .agents/skills").action(superCommandAction);
1198
5645
 
1199
5646
  // src/bin/shark.ts
1200
5647
  crashHandler.init();
1201
- var program = new Command6();
5648
+ var program = new Command7();
1202
5649
  program.name("shark").description("Shark CLI: AI-Native Collaborative Development Tool").version("0.0.1");
1203
5650
  program.addCommand(loginCommand);
1204
5651
  program.addCommand(initCommand);
1205
5652
  program.addCommand(devCommand);
1206
5653
  program.addCommand(legacyCommand);
1207
5654
  program.addCommand(exportSchemaCommand);
5655
+ program.addCommand(exportPromptCommand);
1208
5656
  program.addCommand(superCommand);
1209
5657
  program.command("config").description("Manage global configuration").action(configCommand.action);
1210
5658
  process.on("unhandledRejection", (err) => {