shark-ai 0.4.24 → 0.4.27

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
@@ -8,7 +8,10 @@ import {
8
8
  loginCommand,
9
9
  tokenStorage,
10
10
  tui
11
- } from "../chunk-NKE5GNDJ.js";
11
+ } from "../chunk-LQ2PEYRD.js";
12
+ import {
13
+ MemboxManager
14
+ } from "../chunk-WJUQF54U.js";
12
15
 
13
16
  // src/core/error/crash-handler.ts
14
17
  import fs from "fs";
@@ -209,7 +212,7 @@ var initAction = async () => {
209
212
  }
210
213
  if (action === "resume") {
211
214
  tui.log.success(`Resuming work on ${colors.primary(existingState.projectName)}...`);
212
- tui.outro(`To continue, run: "shark agent" (Context loaded)`);
215
+ tui.outro(`To continue, run: "shark dev" (Context loaded)`);
213
216
  return;
214
217
  }
215
218
  }
@@ -257,7 +260,7 @@ var initAction = async () => {
257
260
  spinner.stop("Project workflow created!");
258
261
  tui.log.success(`Project ${colors.primary(projectName)} initialized successfully.`);
259
262
  tui.log.message(`Your Project ID: ${colors.dim(newState.projectId)}`);
260
- tui.outro('Ready to start! Run "shark agent" to begin analyzing requirements.');
263
+ tui.outro('Ready to start! Run "shark dev" to begin developing features.');
261
264
  } catch (error) {
262
265
  spinner.stop("Initialization failed.", 1);
263
266
  tui.log.error(error.message);
@@ -271,7 +274,21 @@ import { Command as Command2 } from "commander";
271
274
 
272
275
  // src/core/agents/agent-response-parser.ts
273
276
  import { z as z2 } from "zod";
274
- var AgentActionSchema = z2.object({
277
+ import { jsonrepair } from "jsonrepair";
278
+ var AgentActionSchema = z2.preprocess((val) => {
279
+ if (val && typeof val === "object") {
280
+ if (typeof val.type === "string") {
281
+ val.type = val.type.trim();
282
+ }
283
+ if (typeof val.Action === "string") {
284
+ val.Action = val.Action.trim();
285
+ }
286
+ if (val.Action !== void 0 && (val.type !== "manage_subagents" || val.Action === "")) {
287
+ val.Action = null;
288
+ }
289
+ }
290
+ return val;
291
+ }, z2.object({
275
292
  type: z2.enum([
276
293
  "create_file",
277
294
  "modify_file",
@@ -293,6 +310,7 @@ var AgentActionSchema = z2.object({
293
310
  "manage_subagents",
294
311
  "complete_task",
295
312
  "wait",
313
+ "notify_user",
296
314
  "ast_list_structure",
297
315
  "ast_get_method",
298
316
  "ast_add_method",
@@ -369,7 +387,7 @@ var AgentActionSchema = z2.object({
369
387
  enable_write_tools: z2.boolean().nullable().optional(),
370
388
  enable_subagent_tools: z2.boolean().nullable().optional(),
371
389
  enable_mcp_tools: z2.boolean().nullable().optional()
372
- });
390
+ }));
373
391
  var AgentCommandSchema = z2.object({
374
392
  command: z2.string(),
375
393
  description: z2.string(),
@@ -523,7 +541,9 @@ function parseAgentResponse(rawResponse) {
523
541
  "invoke_subagent",
524
542
  "send_message",
525
543
  "manage_subagents",
526
- "complete_task"
544
+ "complete_task",
545
+ "wait",
546
+ "notify_user"
527
547
  ];
528
548
  if (validTypes.includes(parsedObj.type)) {
529
549
  normalizedAction = parsedObj;
@@ -535,6 +555,17 @@ function parseAgentResponse(rawResponse) {
535
555
  } else if (normalizedAction && (!normalizedActions || normalizedActions.length === 0)) {
536
556
  normalizedActions = [normalizedAction];
537
557
  }
558
+ if (normalizedAction && typeof normalizedAction.content === "object" && normalizedAction.content !== null) {
559
+ normalizedAction.content = JSON.stringify(normalizedAction.content);
560
+ }
561
+ if (Array.isArray(normalizedActions)) {
562
+ normalizedActions = normalizedActions.map((act) => {
563
+ if (act && act.content && typeof act.content === "object" && act.content !== null) {
564
+ return { ...act, content: JSON.stringify(act.content) };
565
+ }
566
+ return act;
567
+ });
568
+ }
538
569
  if (!normalizedAction && !normalizedActions) {
539
570
  FileLogger.log("PARSER", "No Action/Actions Found - Constructing Default");
540
571
  const content = parsedObj.message || (typeof parsedObj === "object" ? JSON.stringify(parsedObj) : String(parsedObj));
@@ -586,8 +617,25 @@ function extractFirstJson(str) {
586
617
  try {
587
618
  return JSON.parse(str);
588
619
  } catch (e) {
620
+ const isValidStructure = (val) => {
621
+ return val && typeof val === "object" && !Array.isArray(val) && ("action" in val || "actions" in val || "summary" in val || "type" in val);
622
+ };
623
+ try {
624
+ const repaired = JSON.parse(jsonrepair(str));
625
+ if (isValidStructure(repaired)) {
626
+ return repaired;
627
+ }
628
+ } catch {
629
+ }
589
630
  const firstOpen = str.indexOf("{");
590
631
  if (firstOpen === -1) throw e;
632
+ try {
633
+ const repaired = JSON.parse(jsonrepair(str.substring(firstOpen)));
634
+ if (isValidStructure(repaired)) {
635
+ return repaired;
636
+ }
637
+ } catch {
638
+ }
591
639
  let balance = 0;
592
640
  let inString = false;
593
641
  let escape = false;
@@ -614,7 +662,11 @@ function extractFirstJson(str) {
614
662
  try {
615
663
  return JSON.parse(potentialJson);
616
664
  } catch (innerE) {
617
- throw e;
665
+ try {
666
+ return JSON.parse(jsonrepair(potentialJson));
667
+ } catch {
668
+ throw e;
669
+ }
618
670
  }
619
671
  }
620
672
  }
@@ -808,12 +860,14 @@ Seu objetivo \xE9 ajudar o usu\xE1rio a analisar, especificar e implementar c\xF
808
860
  \u{1F6A8} REGRAS CR\xCDTICAS DE RESPOSTA (JSON):
809
861
  - Voc\xEA DEVE responder APENAS com um objeto JSON v\xE1lido.
810
862
  - 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'.
863
+ - Se precisar falar com o usu\xE1rio e aguardar uma resposta dele, use a action com type 'talk_with_user'.
864
+ - Se voc\xEA quiser apenas enviar uma mensagem informativa ou relat\xF3rio detalhado para o usu\xE1rio sem bloquear ou parar a execu\xE7\xE3o para receber resposta, use a action 'notify_user'.
865
+
812
866
 
813
867
  SUA SA\xCDDA DEVE SEGUIR EXATAMENTE ESTE FORMATO JSON:
814
868
  {
815
869
  "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",
870
+ "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" | "notify_user",
817
871
  "path": "caminho/relativo/do/arquivo (opcional)",
818
872
  "content": "conte\xFAdo do arquivo ou mensagem para o usu\xE1rio (opcional)",
819
873
  "start_anchor": "\xE2ncora de in\xEDcio de substitui\xE7\xE3o (modify_file apenas)",
@@ -871,7 +925,8 @@ var AGENT_RESPONSE_JSON_SCHEMA = {
871
925
  "send_message",
872
926
  "manage_subagents",
873
927
  "complete_task",
874
- "wait"
928
+ "wait",
929
+ "notify_user"
875
930
  ]
876
931
  },
877
932
  "path": { "type": ["string", "null"] },
@@ -915,76 +970,176 @@ var AGENT_RESPONSE_JSON_SCHEMA = {
915
970
  },
916
971
  "required": ["type"]
917
972
  },
918
- "summary": { "type": "string" }
973
+ "summary": {
974
+ "type": "string",
975
+ "description": "Resumo de uma \xFAnica frase muito curta e sucinta do que voc\xEA realizou nesta rodada. Evite explica\xE7\xF5es longas."
976
+ }
919
977
  },
920
978
  "required": ["action"]
921
979
  };
922
980
 
981
+ // src/core/workflow/history-manager.ts
982
+ import fs3 from "fs";
983
+ import path3 from "path";
984
+ var HistoryManager = class {
985
+ static getHistoryDir() {
986
+ const dir = path3.resolve(process.cwd(), "_sharkrc", "history");
987
+ if (!fs3.existsSync(dir)) {
988
+ fs3.mkdirSync(dir, { recursive: true });
989
+ }
990
+ return dir;
991
+ }
992
+ static getFilePath(conversationId) {
993
+ return path3.resolve(this.getHistoryDir(), `${conversationId}.json`);
994
+ }
995
+ static async getHistory(conversationId) {
996
+ const filePath = this.getFilePath(conversationId);
997
+ if (!fs3.existsSync(filePath)) {
998
+ return [];
999
+ }
1000
+ try {
1001
+ const raw = fs3.readFileSync(filePath, "utf-8");
1002
+ const parsed = JSON.parse(raw);
1003
+ return Array.isArray(parsed) ? parsed : [];
1004
+ } catch {
1005
+ return [];
1006
+ }
1007
+ }
1008
+ static async saveHistory(conversationId, messages) {
1009
+ const filePath = this.getFilePath(conversationId);
1010
+ fs3.writeFileSync(filePath, JSON.stringify(messages, null, 2), "utf-8");
1011
+ }
1012
+ static async deleteHistory(conversationId) {
1013
+ const filePath = this.getFilePath(conversationId);
1014
+ if (fs3.existsSync(filePath)) {
1015
+ fs3.unlinkSync(filePath);
1016
+ }
1017
+ }
1018
+ static async appendMessage(conversationId, message) {
1019
+ const history = await this.getHistory(conversationId);
1020
+ history.push(message);
1021
+ await this.saveHistory(conversationId, history);
1022
+ }
1023
+ static getRawHistoryPath(conversationId) {
1024
+ return this.getFilePath(conversationId);
1025
+ }
1026
+ static async getRawHistory(conversationId) {
1027
+ return this.getHistory(conversationId);
1028
+ }
1029
+ static async saveRawHistory(conversationId, history) {
1030
+ await this.saveHistory(conversationId, history);
1031
+ }
1032
+ };
1033
+
1034
+ // src/core/api/stackspot-provider.ts
1035
+ import crypto from "crypto";
1036
+
1037
+ // src/core/workflow/skill-manager.ts
1038
+ import fs4 from "fs/promises";
1039
+ import path4 from "path";
1040
+ import os2 from "os";
1041
+ var SkillManager = class {
1042
+ activeSkills = /* @__PURE__ */ new Set();
1043
+ skillPrompts = /* @__PURE__ */ new Map();
1044
+ async loadSkillFromFile(filePath) {
1045
+ const content = await fs4.readFile(filePath, "utf-8");
1046
+ const cleanContent = content.replace(/^---[\s\S]*?---\s*/, "");
1047
+ return cleanContent;
1048
+ }
1049
+ async activateSkill(skillName) {
1050
+ if (this.activeSkills.has(skillName)) {
1051
+ return `Skill ${skillName} is already active.`;
1052
+ }
1053
+ this.reset();
1054
+ const globalPath = path4.join(os2.homedir(), ".shark", "skills", skillName, "SKILL.md");
1055
+ const localPath = path4.join(process.cwd(), ".agents", "skills", skillName, "SKILL.md");
1056
+ let skillPath = "";
1057
+ try {
1058
+ await fs4.access(localPath);
1059
+ skillPath = localPath;
1060
+ } catch {
1061
+ try {
1062
+ await fs4.access(globalPath);
1063
+ skillPath = globalPath;
1064
+ } catch {
1065
+ throw new Error(`Skill '${skillName}' not found globally or locally.`);
1066
+ }
1067
+ }
1068
+ const prompt = await this.loadSkillFromFile(skillPath);
1069
+ this.activeSkills.add(skillName);
1070
+ this.skillPrompts.set(skillName, prompt);
1071
+ return prompt;
1072
+ }
1073
+ getSystemInstructionExtension() {
1074
+ if (this.activeSkills.size === 0) return "";
1075
+ let extension = "\n\n<EXTREMELY_IMPORTANT>\n";
1076
+ for (const [name, prompt] of this.skillPrompts.entries()) {
1077
+ extension += `
1078
+ --- ACTIVE SKILL: ${name} ---
1079
+ ${prompt}
1080
+ `;
1081
+ }
1082
+ extension += "\n</EXTREMELY_IMPORTANT>\n";
1083
+ return extension;
1084
+ }
1085
+ async listAvailableSkills() {
1086
+ const globalSkillsDir = path4.join(os2.homedir(), ".shark", "skills");
1087
+ const localSkillsDir = path4.join(process.cwd(), ".agents", "skills");
1088
+ const skillNames = /* @__PURE__ */ new Set();
1089
+ const readDir = async (dir) => {
1090
+ try {
1091
+ const entries = await fs4.readdir(dir, { withFileTypes: true });
1092
+ for (const entry of entries) {
1093
+ if (entry.isDirectory()) {
1094
+ const skillMdPath = path4.join(dir, entry.name, "SKILL.md");
1095
+ try {
1096
+ await fs4.access(skillMdPath);
1097
+ skillNames.add(entry.name);
1098
+ } catch {
1099
+ }
1100
+ }
1101
+ }
1102
+ } catch {
1103
+ }
1104
+ };
1105
+ await readDir(globalSkillsDir);
1106
+ await readDir(localSkillsDir);
1107
+ return Array.from(skillNames).sort();
1108
+ }
1109
+ reset() {
1110
+ this.activeSkills.clear();
1111
+ this.skillPrompts.clear();
1112
+ }
1113
+ };
1114
+ var skillManager = new SkillManager();
1115
+
923
1116
  // src/core/api/stackspot-provider.ts
924
1117
  var StackSpotProvider = class {
925
1118
  constructor(agentType) {
926
1119
  this.agentType = agentType;
927
1120
  const config = ConfigManager.getInstance().getConfig();
928
1121
  this.agentId = config.stackspot?.agentId;
1122
+ this.useServerConversation = config.stackspot?.useServerConversation !== false;
929
1123
  }
930
1124
  agentId;
1125
+ useServerConversation = true;
931
1126
  getAgentId() {
932
1127
  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];
1128
+ if (config.agents?.dev) {
1129
+ return config.agents.dev;
944
1130
  }
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];
1131
+ const envResolved = process.env.STACKSPOT_DEV_AGENT_ID;
954
1132
  if (envResolved) {
955
1133
  return envResolved;
956
1134
  }
957
1135
  if (this.agentId && this.agentId !== "01KEQCGJ65YENRA4QBXVN1YFFX") {
958
1136
  return this.agentId;
959
1137
  }
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";
1138
+ return "01KEQCGJ65YENRA4QBXVN1YFFX";
976
1139
  }
977
1140
  getAgentVersion() {
978
1141
  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];
1142
+ return config.agentVersions?.dev || process.env.STACKSPOT_DEV_AGENT_VERSION;
988
1143
  }
989
1144
  async streamChat(prompt, options) {
990
1145
  const realm = await getActiveRealm();
@@ -997,20 +1152,81 @@ var StackSpotProvider = class {
997
1152
  if (!token) {
998
1153
  throw new Error(`No authentication token found for realm '${realm}'. Please run 'shark login'.`);
999
1154
  }
1155
+ let systemPrompt = UNIFIED_SYSTEM_PROMPT;
1156
+ let retrievedContext = "";
1157
+ const isHelperCall = options.conversationId?.startsWith("membox-");
1158
+ if (!isHelperCall) {
1159
+ const { MemboxManager: MemboxManager2 } = await import("../membox-manager-ZDKXDZPF.js");
1160
+ const memboxManager = new MemboxManager2();
1161
+ const query = options?.searchQuery || prompt;
1162
+ retrievedContext = await memboxManager.retrieveContext(query, []);
1163
+ if (retrievedContext) {
1164
+ systemPrompt = systemPrompt + "\n" + retrievedContext;
1165
+ }
1166
+ }
1000
1167
  const isFirstTurn = !options.conversationId;
1001
- const finalPrompt = isFirstTurn ? `SYSTEM INSTRUCTIONS:
1002
- ${UNIFIED_SYSTEM_PROMPT}
1168
+ let finalPrompt = prompt;
1169
+ const conversationId = options.conversationId || crypto.randomUUID();
1170
+ let history = [];
1171
+ if (!this.useServerConversation) {
1172
+ history = await HistoryManager.getHistory(conversationId);
1173
+ if (history.length === 0) {
1174
+ history.push({
1175
+ role: "system",
1176
+ content: systemPrompt
1177
+ });
1178
+ }
1179
+ history.push({ role: "user", content: prompt });
1180
+ let compiledPrompt = "";
1181
+ for (const msg of history) {
1182
+ if (msg.role === "system") {
1183
+ const skillExtension = skillManager.getSystemInstructionExtension();
1184
+ const fullSystemPrompt = skillExtension ? msg.content + "\n" + skillExtension : msg.content;
1185
+ compiledPrompt += `SYSTEM INSTRUCTIONS:
1186
+ ${fullSystemPrompt}
1187
+
1188
+ `;
1189
+ } else if (msg.role === "user") {
1190
+ compiledPrompt += `USER REQUEST:
1191
+ ${msg.content}
1192
+
1193
+ `;
1194
+ } else if (msg.role === "assistant") {
1195
+ compiledPrompt += `ASSISTANT RESPONSE:
1196
+ ${msg.content}
1197
+
1198
+ `;
1199
+ }
1200
+ }
1201
+ finalPrompt = compiledPrompt;
1202
+ } else {
1203
+ const skillExtension = skillManager.getSystemInstructionExtension();
1204
+ if (isFirstTurn) {
1205
+ const fullSystemPrompt = skillExtension ? systemPrompt + "\n" + skillExtension : systemPrompt;
1206
+ finalPrompt = `SYSTEM INSTRUCTIONS:
1207
+ ${fullSystemPrompt}
1003
1208
 
1004
1209
  USER REQUEST:
1005
- ${prompt}` : prompt;
1210
+ ${prompt}`;
1211
+ } else {
1212
+ finalPrompt = skillExtension ? prompt + "\n" + skillExtension : prompt;
1213
+ if (retrievedContext) {
1214
+ finalPrompt = `[MEM\xD3RIA E CONTEXTO RECUPERADOS]
1215
+ ${retrievedContext}
1216
+
1217
+ [MENSAGEM DO USU\xC1RIO]
1218
+ ${finalPrompt}`;
1219
+ }
1220
+ }
1221
+ }
1006
1222
  const requestPayload = {
1007
1223
  user_prompt: finalPrompt,
1008
1224
  streaming: true,
1009
1225
  stackspot_knowledge: false,
1010
1226
  return_ks_in_response: true,
1011
1227
  deep_search_ks: false,
1012
- use_conversation: true,
1013
- conversation_id: options.conversationId
1228
+ use_conversation: this.useServerConversation,
1229
+ conversation_id: this.useServerConversation ? options.conversationId : conversationId
1014
1230
  };
1015
1231
  const agentVersion = this.getAgentVersion();
1016
1232
  if (agentVersion) {
@@ -1048,7 +1264,7 @@ ${prompt}` : prompt;
1048
1264
  onComplete: async (message, metadata) => {
1049
1265
  rawResponse = {
1050
1266
  message: message || fullMessage,
1051
- conversation_id: metadata?.conversation_id || options.conversationId
1267
+ conversation_id: metadata?.conversation_id || conversationId
1052
1268
  };
1053
1269
  },
1054
1270
  onError: (error) => {
@@ -1058,6 +1274,11 @@ ${prompt}` : prompt;
1058
1274
  );
1059
1275
  FileLogger.log("PROVIDER_RESPONSE", "Raw response from StackSpot API", { rawResponse });
1060
1276
  const parsedResponse = parseAgentResponse(rawResponse);
1277
+ if (!this.useServerConversation) {
1278
+ parsedResponse.conversation_id = conversationId;
1279
+ history.push({ role: "assistant", content: JSON.stringify(parsedResponse) });
1280
+ await HistoryManager.saveHistory(conversationId, history);
1281
+ }
1061
1282
  if (options.onComplete) {
1062
1283
  options.onComplete(parsedResponse);
1063
1284
  }
@@ -1065,46 +1286,8 @@ ${prompt}` : prompt;
1065
1286
  }
1066
1287
  };
1067
1288
 
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
1289
  // src/core/api/openai-compatible-provider.ts
1107
- import crypto from "crypto";
1290
+ import crypto2 from "crypto";
1108
1291
  var OpenAICompatibleProvider = class {
1109
1292
  constructor(options) {
1110
1293
  this.options = options;
@@ -1113,7 +1296,7 @@ var OpenAICompatibleProvider = class {
1113
1296
  return UNIFIED_SYSTEM_PROMPT;
1114
1297
  }
1115
1298
  async streamChat(prompt, options) {
1116
- const conversationId = options.conversationId || crypto.randomUUID();
1299
+ const conversationId = options.conversationId || crypto2.randomUUID();
1117
1300
  const history = await HistoryManager.getHistory(conversationId);
1118
1301
  if (history.length === 0) {
1119
1302
  history.push({
@@ -1122,9 +1305,32 @@ var OpenAICompatibleProvider = class {
1122
1305
  });
1123
1306
  }
1124
1307
  history.push({ role: "user", content: prompt });
1308
+ const requestMessages = history.map((msg) => ({ ...msg }));
1309
+ const isHelperCall = conversationId.startsWith("membox-");
1310
+ if (!isHelperCall) {
1311
+ let systemMsg = requestMessages.find((m) => m.role === "system");
1312
+ if (!systemMsg) {
1313
+ systemMsg = {
1314
+ role: "system",
1315
+ content: this.getAgentSystemPrompt(options.agentType)
1316
+ };
1317
+ requestMessages.unshift(systemMsg);
1318
+ }
1319
+ const { MemboxManager: MemboxManager2 } = await import("../membox-manager-ZDKXDZPF.js");
1320
+ const memboxManager = new MemboxManager2();
1321
+ const query = options?.searchQuery || prompt;
1322
+ const retrievedContext = await memboxManager.retrieveContext(query, history);
1323
+ if (retrievedContext) {
1324
+ systemMsg.content = systemMsg.content + "\n" + retrievedContext;
1325
+ }
1326
+ const skillExtension = skillManager.getSystemInstructionExtension();
1327
+ if (skillExtension) {
1328
+ systemMsg.content = systemMsg.content + "\n" + skillExtension;
1329
+ }
1330
+ }
1125
1331
  const requestPayload = {
1126
1332
  model: this.options.model,
1127
- messages: history,
1333
+ messages: requestMessages,
1128
1334
  stream: true,
1129
1335
  temperature: 0.2
1130
1336
  };
@@ -1134,172 +1340,7 @@ var OpenAICompatibleProvider = class {
1134
1340
  json_schema: {
1135
1341
  name: "agent_response",
1136
1342
  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
- }
1343
+ schema: toStrictOpenAISchema(AGENT_RESPONSE_JSON_SCHEMA)
1303
1344
  }
1304
1345
  };
1305
1346
  } else {
@@ -1320,20 +1361,26 @@ var OpenAICompatibleProvider = class {
1320
1361
  headers: sanitizedHeaders,
1321
1362
  payload: requestPayload
1322
1363
  });
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
- }
1364
+ const controller = new AbortController();
1365
+ const timeoutId = setTimeout(() => controller.abort(), 3e5);
1366
+ let reader = void 0;
1336
1367
  try {
1368
+ const res = await fetch(`${this.options.baseURL}/chat/completions`, {
1369
+ method: "POST",
1370
+ headers,
1371
+ body: JSON.stringify(requestPayload),
1372
+ signal: controller.signal
1373
+ });
1374
+ if (!res.ok) {
1375
+ clearTimeout(timeoutId);
1376
+ const errBody = await res.text();
1377
+ throw new Error(`OpenAI API request failed: ${res.status} ${res.statusText} - ${errBody}`);
1378
+ }
1379
+ reader = res.body?.getReader();
1380
+ if (!reader) {
1381
+ clearTimeout(timeoutId);
1382
+ throw new Error("Response body reader is undefined");
1383
+ }
1337
1384
  const decoder = new TextDecoder();
1338
1385
  let fullContent = "";
1339
1386
  let done = false;
@@ -1389,6 +1436,7 @@ var OpenAICompatibleProvider = class {
1389
1436
  }
1390
1437
  }
1391
1438
  }
1439
+ clearTimeout(timeoutId);
1392
1440
  FileLogger.log("PROVIDER_RESPONSE", "Raw response from OpenAI Compatible API", { fullContent });
1393
1441
  const parsedResponse = parseAgentResponse(fullContent);
1394
1442
  parsedResponse.conversation_id = conversationId;
@@ -1398,13 +1446,66 @@ var OpenAICompatibleProvider = class {
1398
1446
  options.onComplete(parsedResponse);
1399
1447
  }
1400
1448
  return parsedResponse;
1449
+ } catch (error) {
1450
+ clearTimeout(timeoutId);
1451
+ if (error.name === "AbortError") {
1452
+ throw new Error(`OpenAI API request timed out after 5 minutes.`);
1453
+ }
1454
+ throw error;
1401
1455
  } finally {
1402
- if (typeof reader.releaseLock === "function") {
1456
+ if (reader && typeof reader.releaseLock === "function") {
1403
1457
  reader.releaseLock();
1404
1458
  }
1405
1459
  }
1406
1460
  }
1407
1461
  };
1462
+ function toStrictOpenAISchema(schema) {
1463
+ if (!schema || typeof schema !== "object") {
1464
+ return schema;
1465
+ }
1466
+ const cloned = JSON.parse(JSON.stringify(schema));
1467
+ delete cloned.$schema;
1468
+ delete cloned.title;
1469
+ function processNode(node) {
1470
+ if (!node || typeof node !== "object") {
1471
+ return;
1472
+ }
1473
+ if (node.type === "object") {
1474
+ node.additionalProperties = false;
1475
+ if (node.properties) {
1476
+ const originalRequired = node.required || [];
1477
+ const allProperties = Object.keys(node.properties);
1478
+ node.required = allProperties;
1479
+ for (const key of allProperties) {
1480
+ const prop = node.properties[key];
1481
+ if (!originalRequired.includes(key)) {
1482
+ if (prop.type) {
1483
+ if (Array.isArray(prop.type)) {
1484
+ if (!prop.type.includes("null")) {
1485
+ prop.type.push("null");
1486
+ }
1487
+ } else if (typeof prop.type === "string") {
1488
+ if (prop.type !== "null") {
1489
+ prop.type = [prop.type, "null"];
1490
+ }
1491
+ }
1492
+ }
1493
+ }
1494
+ if (prop.enum && Array.isArray(prop.enum)) {
1495
+ if (!prop.enum.includes(null)) {
1496
+ prop.enum.push(null);
1497
+ }
1498
+ }
1499
+ processNode(prop);
1500
+ }
1501
+ }
1502
+ } else if (node.type === "array" && node.items) {
1503
+ processNode(node.items);
1504
+ }
1505
+ }
1506
+ processNode(cloned);
1507
+ return cloned;
1508
+ }
1408
1509
 
1409
1510
  // src/core/api/provider-resolver.ts
1410
1511
  var ProviderResolver = class {
@@ -1493,8 +1594,8 @@ var ConversationManager = class _ConversationManager {
1493
1594
  var conversationManager = ConversationManager.getInstance();
1494
1595
 
1495
1596
  // src/core/workflow/anchor-state-manager.ts
1496
- import fs4 from "fs";
1497
- import path4 from "path";
1597
+ import fs5 from "fs";
1598
+ import path5 from "path";
1498
1599
  import { diffLines } from "diff";
1499
1600
  var AnchorStateManager = class {
1500
1601
  cache = /* @__PURE__ */ new Map();
@@ -2509,10 +2610,10 @@ var AnchorStateManager = class {
2509
2610
  }
2510
2611
  }
2511
2612
  getAnchoredContent(filePath) {
2512
- const absolutePath = path4.resolve(filePath);
2613
+ const absolutePath = path5.resolve(filePath);
2513
2614
  let lineStates = this.cache.get(absolutePath);
2514
2615
  if (!lineStates) {
2515
- const content = fs4.readFileSync(absolutePath, "utf8");
2616
+ const content = fs5.readFileSync(absolutePath, "utf8");
2516
2617
  const hasTrailingNewline = content.endsWith("\n");
2517
2618
  const lines = hasTrailingNewline ? content.slice(0, -1).split("\n") : content.split("\n");
2518
2619
  const usedAnchors = /* @__PURE__ */ new Set();
@@ -2525,7 +2626,7 @@ var AnchorStateManager = class {
2525
2626
  return lineStates.map((ls) => `${ls.anchor}\xA7${ls.text}`).join("\n");
2526
2627
  }
2527
2628
  applyAnchoredEdit(filePath, startAnchor, endAnchor, content) {
2528
- const absolutePath = path4.resolve(filePath);
2629
+ const absolutePath = path5.resolve(filePath);
2529
2630
  let lineStates = this.cache.get(absolutePath);
2530
2631
  if (!lineStates) {
2531
2632
  this.getAnchoredContent(absolutePath);
@@ -2542,7 +2643,7 @@ var AnchorStateManager = class {
2542
2643
  if (startIndex > endIndex) {
2543
2644
  throw new Error(`Invalid range: start anchor "${startAnchor}" is after end anchor "${endAnchor}"`);
2544
2645
  }
2545
- const originalContent = fs4.readFileSync(absolutePath, "utf8");
2646
+ const originalContent = fs5.readFileSync(absolutePath, "utf8");
2546
2647
  const hasTrailingNewline = originalContent.endsWith("\n");
2547
2648
  const oldLines = lineStates.map((ls) => ls.text);
2548
2649
  const cleanContent = content.endsWith("\n") ? content.slice(0, -1) : content;
@@ -2553,7 +2654,7 @@ var AnchorStateManager = class {
2553
2654
  ...oldLines.slice(endIndex + 1)
2554
2655
  ];
2555
2656
  const fileContentToWrite = updatedLines.join("\n") + (hasTrailingNewline ? "\n" : "");
2556
- fs4.writeFileSync(absolutePath, fileContentToWrite, "utf8");
2657
+ fs5.writeFileSync(absolutePath, fileContentToWrite, "utf8");
2557
2658
  const usedAnchors = /* @__PURE__ */ new Set();
2558
2659
  for (let i = 0; i < lineStates.length; i++) {
2559
2660
  usedAnchors.add(lineStates[i].anchor);
@@ -2599,457 +2700,19 @@ var AnchorStateManager = class {
2599
2700
  };
2600
2701
 
2601
2702
  // src/core/agents/developer-agent.ts
2602
- import fs9 from "fs";
2603
- import path10 from "path";
2703
+ import fs8 from "fs";
2704
+ import path8 from "path";
2604
2705
 
2605
2706
  // src/core/agents/agent-tools.ts
2606
2707
  import fs6 from "fs";
2607
- import path7 from "path";
2708
+ import path6 from "path";
2608
2709
  import fg from "fast-glob";
2609
2710
  import { exec } from "child_process";
2610
2711
  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
2712
  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
2713
  function handleListFiles(dirPath) {
3051
2714
  try {
3052
- const fullPath = path7.resolve(process.cwd(), dirPath);
2715
+ const fullPath = path6.resolve(process.cwd(), dirPath);
3053
2716
  if (!fs6.existsSync(fullPath)) return `Error: Directory ${dirPath} does not exist.`;
3054
2717
  const items = fs6.readdirSync(fullPath, { withFileTypes: true });
3055
2718
  return items.map((item) => {
@@ -3059,54 +2722,6 @@ function handleListFiles(dirPath) {
3059
2722
  return `Error listing files: ${e.message}`;
3060
2723
  }
3061
2724
  }
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
2725
  function handleSearchFile(pattern) {
3111
2726
  try {
3112
2727
  const entries = fg.sync(pattern, { dot: true });
@@ -3133,7 +2748,7 @@ function handleSearchCode(globPattern, query, isRegex = false) {
3133
2748
  for (const filePath of files) {
3134
2749
  if (totalMatches >= MAX_MATCHES) break;
3135
2750
  try {
3136
- const fullPath = path7.resolve(process.cwd(), filePath);
2751
+ const fullPath = path6.resolve(process.cwd(), filePath);
3137
2752
  const stats = fs6.statSync(fullPath);
3138
2753
  if (stats.size > MAX_FILE_SIZE_BYTES) continue;
3139
2754
  const content = fs6.readFileSync(fullPath, "utf-8");
@@ -3159,32 +2774,6 @@ ${results.join("\n")}`;
3159
2774
  return `Error searching code: ${e.message}`;
3160
2775
  }
3161
2776
  }
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
2777
  async function handleRunCommand(command) {
3189
2778
  const { spawn } = await import("child_process");
3190
2779
  try {
@@ -3232,456 +2821,95 @@ ${stdout}`);
3232
2821
  return `Error launching command: ${e.message}`;
3233
2822
  }
3234
2823
  }
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}"`;
2824
+
2825
+ // src/core/workflow/subagent-manager.ts
2826
+ import crypto3 from "crypto";
2827
+ import fs7 from "fs";
2828
+ import path7 from "path";
2829
+ import { fork } from "child_process";
2830
+ import { fileURLToPath } from "url";
2831
+ var SubagentManager = class {
2832
+ subagents = /* @__PURE__ */ new Map();
2833
+ customTypes = /* @__PURE__ */ new Map();
2834
+ messageSeq = 0;
2835
+ registerSubagent(id, type, role, parentId) {
2836
+ this.subagents.set(id, { id, type, role, status: "running", parentId });
2837
+ }
2838
+ terminateSubagent(id, success = true, isCancelled = false) {
2839
+ const state = this.subagents.get(id);
2840
+ if (state) {
2841
+ if (state.status === "cancelled") {
2842
+ return;
2843
+ }
2844
+ if (isCancelled) {
2845
+ state.status = "cancelled";
2846
+ } else {
2847
+ state.status = success ? "completed" : "failed";
3245
2848
  }
3246
- const parent = path7.dirname(dir);
3247
- if (parent === dir) break;
3248
- dir = parent;
3249
2849
  }
3250
- } catch (e) {
3251
2850
  }
3252
- const cwdBin = path7.resolve(process.cwd(), "node_modules", ".bin", binName);
3253
- if (fs6.existsSync(cwdBin)) {
3254
- return `"${cwdBin}"`;
2851
+ isSubagentActive(id) {
2852
+ return this.subagents.get(id)?.status === "running";
2853
+ }
2854
+ hasSubagent(id) {
2855
+ return this.subagents.has(id);
3255
2856
  }
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}`;
2857
+ getSubagentState(id) {
2858
+ return this.subagents.get(id);
2859
+ }
2860
+ updateSubagentSummary(id, summary) {
2861
+ const state = this.subagents.get(id);
2862
+ if (state) {
2863
+ state.summary = summary;
3263
2864
  }
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
2865
  }
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;
2866
+ sendMessage(recipient, message) {
2867
+ const mailboxDir = path7.resolve(process.cwd(), ".shark", "mailbox", recipient);
2868
+ fs7.mkdirSync(mailboxDir, { recursive: true });
2869
+ const seq = (this.messageSeq++).toString().padStart(6, "0");
2870
+ const filePath = path7.join(mailboxDir, `${Date.now()}-${seq}-${crypto3.randomUUID()}.json`);
2871
+ fs7.writeFileSync(filePath, JSON.stringify({ message }), "utf-8");
2872
+ }
2873
+ retrieveMessages(id) {
2874
+ const mailboxDir = path7.resolve(process.cwd(), ".shark", "mailbox", id);
2875
+ if (!fs7.existsSync(mailboxDir)) {
2876
+ return [];
3307
2877
  }
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);
2878
+ const files = fs7.readdirSync(mailboxDir);
2879
+ files.sort();
2880
+ const messages = [];
2881
+ for (const file of files) {
2882
+ const filePath = path7.join(mailboxDir, file);
2883
+ try {
2884
+ const content = fs7.readFileSync(filePath, "utf-8");
2885
+ const data = JSON.parse(content);
2886
+ if (data && typeof data.message === "string") {
2887
+ messages.push(data.message);
3326
2888
  }
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;
2889
+ } catch (e) {
2890
+ }
2891
+ try {
2892
+ fs7.unlinkSync(filePath);
2893
+ } catch (e) {
2894
+ }
2895
+ }
2896
+ return messages;
3336
2897
  }
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.`;
2898
+ peekMessages(id) {
2899
+ const mailboxDir = path7.resolve(process.cwd(), ".shark", "mailbox", id);
2900
+ if (!fs7.existsSync(mailboxDir)) {
2901
+ return [];
3343
2902
  }
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);
2903
+ const files = fs7.readdirSync(mailboxDir);
2904
+ files.sort();
2905
+ const messages = [];
2906
+ for (const file of files) {
2907
+ const filePath = path7.join(mailboxDir, file);
2908
+ try {
2909
+ const content = fs7.readFileSync(filePath, "utf-8");
2910
+ const data = JSON.parse(content);
2911
+ if (data && typeof data.message === "string") {
2912
+ messages.push(data.message);
3685
2913
  }
3686
2914
  } catch (e) {
3687
2915
  }
@@ -3704,12 +2932,12 @@ var SubagentManager = class {
3704
2932
  throw new Error("Invalid subagent ID format");
3705
2933
  }
3706
2934
  const projectRoot = process.cwd();
3707
- const logFile = path9.resolve(projectRoot, "_sharkrc", "history", `subagent-${id}-console.log`);
3708
- if (!fs8.existsSync(logFile)) {
2935
+ const logFile = path7.resolve(projectRoot, "_sharkrc", "history", `subagent-${id}-console.log`);
2936
+ if (!fs7.existsSync(logFile)) {
3709
2937
  return "No console logs found for this subagent.";
3710
2938
  }
3711
2939
  try {
3712
- let content = fs8.readFileSync(logFile, "utf-8");
2940
+ let content = fs7.readFileSync(logFile, "utf-8");
3713
2941
  if (content.endsWith("\n")) {
3714
2942
  content = content.slice(0, -1);
3715
2943
  }
@@ -3752,32 +2980,32 @@ var SubagentManager = class {
3752
2980
  async invokeSubagents(subagents, parentId, parentQueue) {
3753
2981
  const invoked = [];
3754
2982
  for (const sub of subagents) {
3755
- const id = `subagent-${crypto2.randomUUID()}`;
2983
+ const id = `subagent-${crypto3.randomUUID()}`;
3756
2984
  this.registerSubagent(id, sub.TypeName, sub.Role, parentId);
3757
2985
  const promise = (async () => {
3758
2986
  try {
3759
2987
  const projectRoot = process.cwd();
3760
- const __filename2 = fileURLToPath2(import.meta.url);
3761
- const __dirname2 = path9.dirname(__filename2);
3762
- let packageRoot = __dirname2;
2988
+ const __filename3 = fileURLToPath(import.meta.url);
2989
+ const __dirname3 = path7.dirname(__filename3);
2990
+ let packageRoot = __dirname3;
3763
2991
  while (true) {
3764
- const pkgPath = path9.join(packageRoot, "package.json");
3765
- if (fs8.existsSync(pkgPath)) {
2992
+ const pkgPath = path7.join(packageRoot, "package.json");
2993
+ if (fs7.existsSync(pkgPath)) {
3766
2994
  try {
3767
- const pkg = JSON.parse(fs8.readFileSync(pkgPath, "utf-8"));
2995
+ const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
3768
2996
  if (pkg && pkg.name === "shark-ai") {
3769
2997
  break;
3770
2998
  }
3771
2999
  } catch (e) {
3772
3000
  }
3773
3001
  }
3774
- const parent = path9.dirname(packageRoot);
3002
+ const parent = path7.dirname(packageRoot);
3775
3003
  if (parent === packageRoot) {
3776
3004
  break;
3777
3005
  }
3778
3006
  packageRoot = parent;
3779
3007
  }
3780
- const pathToSharkJs = path9.resolve(packageRoot, "dist", "bin", "shark.js");
3008
+ const pathToSharkJs = path7.resolve(packageRoot, "dist", "bin", "shark.js");
3781
3009
  const customType = this.customTypes.get(sub.TypeName);
3782
3010
  let customContext = `Voc\xEA est\xE1 executando em modo SUBAGENTE.
3783
3011
  `;
@@ -3810,10 +3038,10 @@ var SubagentManager = class {
3810
3038
  if (state2) {
3811
3039
  state2.childProcess = child;
3812
3040
  }
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" });
3041
+ const historyDir = path7.resolve(projectRoot, "_sharkrc", "history");
3042
+ fs7.mkdirSync(historyDir, { recursive: true });
3043
+ const consoleLogFile = path7.join(historyDir, `subagent-${id}-console.log`);
3044
+ const logStream = fs7.createWriteStream(consoleLogFile, { flags: "a" });
3817
3045
  if (child.stdout) {
3818
3046
  child.stdout.pipe(logStream);
3819
3047
  }
@@ -3836,8 +3064,8 @@ Subagent ${sub.Role} (${id}) cancelled.`);
3836
3064
  } else if (!success) {
3837
3065
  this.updateSubagentSummary(id, "Failed");
3838
3066
  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;
3067
+ const mailboxDir = path7.resolve(projectRoot, ".shark", "mailbox", parentId);
3068
+ const hasMessages = fs7.existsSync(mailboxDir) && fs7.readdirSync(mailboxDir).length > 0;
3841
3069
  if (!hasMessages) {
3842
3070
  this.sendMessage(parentId, fallbackMsg);
3843
3071
  }
@@ -3939,22 +3167,30 @@ var MessageQueue = class {
3939
3167
  };
3940
3168
 
3941
3169
  // src/core/agents/developer-agent.ts
3170
+ var activeOnCommandHandler = void 0;
3942
3171
  async function promptUser(message, initialValue, placeholder, prefix = "") {
3943
3172
  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!`);
3173
+ while (userReply && userReply.startsWith("/")) {
3174
+ let handled = false;
3175
+ if (activeOnCommandHandler) {
3176
+ handled = await activeOnCommandHandler(userReply);
3177
+ }
3178
+ if (!handled && userReply === "/skills") {
3179
+ const availableSkills = await skillManager.listAvailableSkills();
3180
+ const options = availableSkills.map((name) => ({ value: name, label: name }));
3181
+ if (options.length === 0) {
3182
+ tui.log.warning("Nenhuma skill encontrada. Execute `shark super` para instalar as skills.");
3183
+ } else {
3184
+ const selectedSkill = await tui.select({
3185
+ message: "Selecione a Skill do Superpowers para ativar:",
3186
+ options
3187
+ });
3188
+ if (!tui.isCancel(selectedSkill)) {
3189
+ await skillManager.activateSkill(selectedSkill);
3190
+ tui.log.success(`\u2714 Skill '${selectedSkill}' ativada com sucesso!`);
3191
+ }
3957
3192
  }
3193
+ handled = true;
3958
3194
  }
3959
3195
  userReply = await tui.text({
3960
3196
  message: `${prefix}${message}`,
@@ -3964,32 +3200,39 @@ async function promptUser(message, initialValue, placeholder, prefix = "") {
3964
3200
  }
3965
3201
  return userReply;
3966
3202
  }
3967
- async function waitForInputOrNotification(queue, promptMessage = "Your answer:", subagentPrefix = "", timeoutMs) {
3203
+ async function waitForInputOrNotification(queue, promptMessage = "Your answer:", subagentPrefix = "", timeoutMs, isAuto = false) {
3968
3204
  let cancelled = false;
3969
3205
  let resolvePromptPromise = null;
3970
3206
  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
- });
3207
+ const promises2 = [];
3208
+ if (!isAuto) {
3209
+ const promptPromise = new Promise((resolve2) => {
3210
+ resolvePromptPromise = resolve2;
3211
+ });
3212
+ const runPrompt = async () => {
3213
+ try {
3214
+ const userReply = await promptUser(promptMessage, void 0, void 0, subagentPrefix);
3215
+ if (!cancelled && resolvePromptPromise) {
3216
+ resolvePromptPromise({
3217
+ type: "user",
3218
+ content: userReply,
3219
+ timestamp: Date.now()
3220
+ });
3221
+ }
3222
+ } catch (e) {
3983
3223
  }
3984
- } catch (e) {
3985
- }
3986
- };
3987
- runPrompt();
3224
+ };
3225
+ runPrompt();
3226
+ promises2.push(promptPromise);
3227
+ }
3988
3228
  const queuePromise = queue.next();
3989
- const promises = [promptPromise, queuePromise];
3229
+ promises2.push(queuePromise);
3990
3230
  if (timeoutMs !== void 0 && timeoutMs !== null) {
3991
3231
  const timeoutPromise = new Promise((resolve2) => {
3992
3232
  timerId = setTimeout(() => {
3233
+ if (resolvePromptPromise) {
3234
+ cancelled = true;
3235
+ }
3993
3236
  resolve2({
3994
3237
  type: "timeout",
3995
3238
  content: "Wait timeout expired.",
@@ -3997,18 +3240,20 @@ async function waitForInputOrNotification(queue, promptMessage = "Your answer:",
3997
3240
  });
3998
3241
  }, timeoutMs);
3999
3242
  });
4000
- promises.push(timeoutPromise);
3243
+ promises2.push(timeoutPromise);
4001
3244
  }
4002
- const winner = await Promise.race(promises);
3245
+ const winner = await Promise.race(promises2);
4003
3246
  if (timerId) {
4004
3247
  clearTimeout(timerId);
4005
3248
  }
4006
3249
  if (winner.type === "subagent_notification" || winner.type === "timeout") {
4007
3250
  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");
3251
+ if (!isAuto) {
3252
+ process.stdin.emit("data", "\r");
3253
+ await new Promise((r) => setTimeout(r, 50));
3254
+ if (process.stdout.isTTY) {
3255
+ process.stdout.write("\x1B[1A\x1B[2K\x1B[1A\x1B[2K");
3256
+ }
4012
3257
  }
4013
3258
  }
4014
3259
  return winner;
@@ -4054,11 +3299,11 @@ async function interactiveDeveloperAgent(options = {}) {
4054
3299
  message: (msg) => tui.log.message(`${subagentPrefix}${msg}`)
4055
3300
  };
4056
3301
  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)) {
3302
+ const defaultContextPath = path8.resolve(projectRoot, "_sharkrc", "project-context.md");
3303
+ const specificContextPath = options.context ? path8.resolve(projectRoot, options.context) : defaultContextPath;
3304
+ if (fs8.existsSync(specificContextPath)) {
4060
3305
  try {
4061
- contextContent = fs9.readFileSync(specificContextPath, "utf-8");
3306
+ contextContent = fs8.readFileSync(specificContextPath, "utf-8");
4062
3307
  } catch (e) {
4063
3308
  log.warning(`Failed to read context file: ${e}`);
4064
3309
  }
@@ -4088,17 +3333,57 @@ You are a highly skilled Developer Agent.
4088
3333
  \u{1F449} **CURRENT TASK**: "${currentTask}"
4089
3334
 
4090
3335
  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.
3336
+ - 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, execute the 'complete_task' action with a brief summary in the 'summary' field and the full explanation in the 'content' field.
4092
3337
  - If the request is to implement changes, debug, or write code:
4093
3338
  1. Implement the necessary changes.
4094
3339
  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.
3340
+ 3. When you are confident the task is done, execute the 'complete_task' action with a brief technical summary of what you did in the 'summary' field and any additional details in the 'content' field.
3341
+
3342
+ - Handling Subagent Notifications:
3343
+ - When you receive notifications about subagent progress or completion in your mailbox, do NOT invoke the 'talk_with_user' action just to relay this information to the user if you still have other subagents running, or if you have further steps to execute yourself.
3344
+ - Instead, process the subagent's output, update your task progress in the 'summary' field of your next action, and proceed with executing your next planned steps (or use the 'wait' action to continue waiting for other running subagents).
3345
+ - Only use 'talk_with_user' if you genuinely require the user's input/decision to proceed, or when the entire task is ready for final discussion.
4096
3346
  `;
4097
3347
  let nextPrompt = basePrompt;
4098
3348
  let keepGoing = true;
4099
3349
  let finalSummary = "";
4100
3350
  const conversationKey = options.taskId ? `dev_agent_${options.taskId}` : `dev_agent_${Date.now()}`;
4101
3351
  const anchorManager = new AnchorStateManager();
3352
+ const onCommandHandler = async (command) => {
3353
+ if (command === "/compact") {
3354
+ tui.log.info("\u{1F988} Compactando mem\xF3ria de forma manual...");
3355
+ const memboxManager = new MemboxManager();
3356
+ const existingConversationId = await conversationManager.getConversationId(conversationKey);
3357
+ if (existingConversationId) {
3358
+ try {
3359
+ const rawHistory = await HistoryManager.getRawHistory(existingConversationId);
3360
+ const provider = ProviderResolver.getProvider("developer_agent");
3361
+ const truncatedHistory = await memboxManager.compactHistory(rawHistory, provider, existingConversationId, true);
3362
+ await HistoryManager.saveRawHistory(existingConversationId, truncatedHistory);
3363
+ tui.log.success("\u2714 Mem\xF3ria compactada e truncada com sucesso!");
3364
+ } catch (error) {
3365
+ tui.log.error(`Erro durante a compacta\xE7\xE3o: ${error.message}`);
3366
+ }
3367
+ } else {
3368
+ tui.log.warning("Nenhuma conversa\xE7\xE3o ativa para compactar.");
3369
+ }
3370
+ return true;
3371
+ }
3372
+ if (command === "/context") {
3373
+ const existingConversationId = await conversationManager.getConversationId(conversationKey);
3374
+ if (existingConversationId) {
3375
+ const rawHistory = await HistoryManager.getRawHistory(existingConversationId);
3376
+ const totalTokensEst = Math.ceil(JSON.stringify(rawHistory).length / 4);
3377
+ tui.log.info(`\u{1F4CA} Hist\xF3rico ativo: ${rawHistory.length} mensagens`);
3378
+ tui.log.info(`\u{1F4CA} Tamanho estimado: ${totalTokensEst} / 8000 tokens (${Math.round(totalTokensEst / 8e3 * 100)}% do limite)`);
3379
+ } else {
3380
+ tui.log.warning("Nenhuma conversa\xE7\xE3o ativa para analisar.");
3381
+ }
3382
+ return true;
3383
+ }
3384
+ return false;
3385
+ };
3386
+ activeOnCommandHandler = onCommandHandler;
4102
3387
  const spinner = tui.spinner();
4103
3388
  const handleCleanupSignal = (exitCode) => {
4104
3389
  const currentId = options.taskId || "parent";
@@ -4149,17 +3434,35 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4149
3434
  `;
4150
3435
  currentTurnPrompt += panel;
4151
3436
  }
4152
- const promptToSend = currentTurnPrompt + skillManager.getSystemInstructionExtension();
3437
+ const promptToSend = currentTurnPrompt;
4153
3438
  try {
4154
3439
  const activeSubagents = subagentManager.getActiveSubagents();
4155
3440
  const activeCount = activeSubagents.length;
4156
3441
  const spinnerText = activeCount > 0 ? `\u{1F988} Shark Dev working... (Active subagents: ${activeCount})` : "\u{1F988} Shark Dev working...";
4157
3442
  spinner.start(spinnerText);
4158
3443
  const existingConversationId = await conversationManager.getConversationId(conversationKey);
3444
+ if (existingConversationId) {
3445
+ const rawHistory = await HistoryManager.getRawHistory(existingConversationId);
3446
+ const totalTokensEst = Math.ceil(JSON.stringify(rawHistory).length / 4);
3447
+ const compactionTokenLimit = ConfigManager.getInstance().getConfig().memory?.compactionTokenLimit ?? 8e3;
3448
+ if (totalTokensEst >= compactionTokenLimit) {
3449
+ try {
3450
+ log.info("\u{1F988} Limite de context/tokens atingido. Iniciando compacta\xE7\xE3o autom\xE1tica...");
3451
+ const memboxManager = new MemboxManager();
3452
+ const providerInstance = ProviderResolver.getProvider("developer_agent");
3453
+ const truncatedHistory = await memboxManager.compactHistory(rawHistory, providerInstance, existingConversationId);
3454
+ await HistoryManager.saveRawHistory(existingConversationId, truncatedHistory);
3455
+ log.success("\u2714 Compacta\xE7\xE3o autom\xE1tica conclu\xEDda!");
3456
+ } catch (error) {
3457
+ log.error(`\u26A0\uFE0F Falha na compacta\xE7\xE3o autom\xE1tica: ${error.message}. Prosseguindo sem compacta\xE7\xE3o.`);
3458
+ }
3459
+ }
3460
+ }
4159
3461
  const provider = ProviderResolver.getProvider("developer_agent");
4160
3462
  const response = await provider.streamChat(promptToSend, {
4161
3463
  conversationId: existingConversationId,
4162
3464
  agentType: "developer_agent",
3465
+ searchQuery: nextPrompt,
4163
3466
  onChunk: () => {
4164
3467
  }
4165
3468
  });
@@ -4174,19 +3477,32 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4174
3477
  log.info(`\u{1F4CC} Status: ${response.summary}`);
4175
3478
  }
4176
3479
  if (response.message && response.message.includes("TASK_COMPLETED:")) {
3480
+ const mainContent = response.message.split("TASK_COMPLETED:")[0].trim();
3481
+ if (mainContent) {
3482
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
3483
+ console.log(mainContent);
3484
+ }
4177
3485
  finalSummary = response.message.split("TASK_COMPLETED:")[1].trim();
4178
3486
  log.success(`\u2714 Task Completed: ${finalSummary}`);
4179
3487
  if (options.taskId) {
4180
3488
  subagentManager.updateSubagentSummary(options.taskId, finalSummary);
3489
+ if (process.env.SHARK_PARENT_ID) {
3490
+ subagentManager.sendMessage(
3491
+ process.env.SHARK_PARENT_ID,
3492
+ `[Subagent Notification] Subagent ${process.env.SHARK_SUBAGENT_ROLE || "Subagent"} (${options.taskId}) completed.
3493
+ Result Details:
3494
+ ${mainContent || finalSummary}`
3495
+ );
3496
+ }
4181
3497
  keepGoing = false;
4182
3498
  break;
4183
3499
  }
4184
- if (!options.taskInstruction) {
3500
+ if (!options.auto || subagentManager.getActiveSubagentsForParent(myId).length > 0) {
4185
3501
  let nextMsg;
4186
3502
  if (!messageQueue.isEmpty()) {
4187
3503
  nextMsg = await messageQueue.next();
4188
3504
  } else {
4189
- nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
3505
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
4190
3506
  }
4191
3507
  if (nextMsg.type === "user") {
4192
3508
  if (tui.isCancel(nextMsg.content)) {
@@ -4202,6 +3518,11 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4202
3518
  }
4203
3519
  }
4204
3520
  if (response.message && response.message.includes("TASK_FAILED:")) {
3521
+ const mainContent = response.message.split("TASK_FAILED:")[0].trim();
3522
+ if (mainContent) {
3523
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
3524
+ console.log(mainContent);
3525
+ }
4205
3526
  const failureReason = response.message.split("TASK_FAILED:")[1].trim();
4206
3527
  log.error(`\u274C Agent reported task failure: ${failureReason}`);
4207
3528
  if (options.taskId) {
@@ -4215,12 +3536,12 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4215
3536
  }
4216
3537
  return { success: false, summary: failureReason };
4217
3538
  }
4218
- if (!options.taskInstruction) {
3539
+ if (!options.auto || subagentManager.getActiveSubagentsForParent(myId).length > 0) {
4219
3540
  let nextMsg;
4220
3541
  if (!messageQueue.isEmpty()) {
4221
3542
  nextMsg = await messageQueue.next();
4222
3543
  } else {
4223
- nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
3544
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
4224
3545
  }
4225
3546
  if (nextMsg.type === "user") {
4226
3547
  if (tui.isCancel(nextMsg.content)) {
@@ -4247,7 +3568,7 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4247
3568
  if (!messageQueue.isEmpty()) {
4248
3569
  nextMsg = await messageQueue.next();
4249
3570
  } else {
4250
- nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
3571
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
4251
3572
  }
4252
3573
  if (nextMsg.type === "user") {
4253
3574
  if (tui.isCancel(nextMsg.content)) {
@@ -4262,7 +3583,7 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
4262
3583
  if (!messageQueue.isEmpty()) {
4263
3584
  nextMsg = await messageQueue.next();
4264
3585
  } else {
4265
- nextMsg = await waitForInputOrNotification(messageQueue, "Agent returned empty response. Type a message to continue or press Ctrl+C to cancel:", subagentPrefix);
3586
+ nextMsg = await waitForInputOrNotification(messageQueue, "Agent returned empty response. Type a message to continue or press Ctrl+C to cancel:", subagentPrefix, void 0, isAuto);
4266
3587
  }
4267
3588
  if (nextMsg.type === "user") {
4268
3589
  if (tui.isCancel(nextMsg.content)) {
@@ -4311,12 +3632,12 @@ ${content}`;
4311
3632
  }
4312
3633
  if (approved) {
4313
3634
  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 });
3635
+ const resolvedPath = path8.resolve(projectRoot, filePath);
3636
+ const dir = path8.dirname(resolvedPath);
3637
+ if (!fs8.existsSync(dir)) {
3638
+ fs8.mkdirSync(dir, { recursive: true });
4318
3639
  }
4319
- fs9.writeFileSync(resolvedPath, action.content || "", "utf-8");
3640
+ fs8.writeFileSync(resolvedPath, action.content || "", "utf-8");
4320
3641
  resultMsg = `[Action create_file(${filePath}) Success]`;
4321
3642
  } catch (e) {
4322
3643
  resultMsg = `[Action create_file(${filePath}) Failed]: ${e.message}`;
@@ -4333,9 +3654,9 @@ ${content}`;
4333
3654
  }
4334
3655
  if (approved) {
4335
3656
  try {
4336
- const resolvedPath = path10.resolve(projectRoot, filePath);
4337
- if (fs9.existsSync(resolvedPath)) {
4338
- fs9.rmSync(resolvedPath, { force: true });
3657
+ const resolvedPath = path8.resolve(projectRoot, filePath);
3658
+ if (fs8.existsSync(resolvedPath)) {
3659
+ fs8.rmSync(resolvedPath, { force: true });
4339
3660
  }
4340
3661
  resultMsg = `[Action delete_file(${filePath}) Success]`;
4341
3662
  } catch (e) {
@@ -4426,7 +3747,7 @@ ${result}`;
4426
3747
  if (!messageQueue.isEmpty()) {
4427
3748
  nextMsg = await messageQueue.next();
4428
3749
  } else {
4429
- nextMsg = await waitForInputOrNotification(messageQueue, "Seu prompt alternativo para o agente:", subagentPrefix);
3750
+ nextMsg = await waitForInputOrNotification(messageQueue, "Seu prompt alternativo para o agente:", subagentPrefix, void 0, isAuto);
4430
3751
  }
4431
3752
  if (nextMsg.type === "user") {
4432
3753
  if (tui.isCancel(nextMsg.content)) {
@@ -4443,19 +3764,33 @@ ${result}`;
4443
3764
  if (isSubagent) {
4444
3765
  const summary = hasCompleted ? contentStr.split("TASK_COMPLETED:")[1].trim() : contentStr;
4445
3766
  subagentManager.updateSubagentSummary(options.taskId, summary);
3767
+ if (process.env.SHARK_PARENT_ID) {
3768
+ const mainContent = hasCompleted ? contentStr.split("TASK_COMPLETED:")[0].trim() : contentStr;
3769
+ subagentManager.sendMessage(
3770
+ process.env.SHARK_PARENT_ID,
3771
+ `[Subagent Notification] Subagent ${process.env.SHARK_SUBAGENT_ROLE || "Subagent"} (${options.taskId}) completed.
3772
+ Result Details:
3773
+ ${mainContent}`
3774
+ );
3775
+ }
4446
3776
  finalSummary = summary;
4447
3777
  keepGoing = false;
4448
3778
  break;
4449
3779
  }
4450
3780
  if (hasCompleted) {
3781
+ const mainContent = contentStr.split("TASK_COMPLETED:")[0].trim();
3782
+ if (mainContent) {
3783
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
3784
+ console.log(mainContent);
3785
+ }
4451
3786
  finalSummary = contentStr.split("TASK_COMPLETED:")[1].trim();
4452
3787
  log.success(`\u2714 Task Completed: ${finalSummary}`);
4453
- if (!options.taskInstruction) {
3788
+ if (!options.auto || subagentManager.getActiveSubagentsForParent(myId).length > 0) {
4454
3789
  let nextMsg2;
4455
3790
  if (!messageQueue.isEmpty()) {
4456
3791
  nextMsg2 = await messageQueue.next();
4457
3792
  } else {
4458
- nextMsg2 = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
3793
+ nextMsg2 = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
4459
3794
  }
4460
3795
  if (nextMsg2.type === "user") {
4461
3796
  if (tui.isCancel(nextMsg2.content)) {
@@ -4476,7 +3811,7 @@ ${result}`;
4476
3811
  if (!messageQueue.isEmpty()) {
4477
3812
  nextMsg = await messageQueue.next();
4478
3813
  } else {
4479
- nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix);
3814
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
4480
3815
  }
4481
3816
  if (nextMsg.type === "user") {
4482
3817
  if (tui.isCancel(nextMsg.content)) {
@@ -4560,19 +3895,46 @@ Result Details:
4560
3895
  ${detailedContent}`
4561
3896
  );
4562
3897
  }
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);
3898
+ finalSummary = taskSummary;
3899
+ keepGoing = false;
3900
+ break;
3901
+ } else {
3902
+ if (detailedContent) {
3903
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
3904
+ console.log(detailedContent);
3905
+ }
3906
+ log.success(`\u2714 Task Completed: ${taskSummary}`);
3907
+ if (!options.auto || subagentManager.getActiveSubagentsForParent(myId).length > 0) {
3908
+ let nextMsg;
3909
+ if (!messageQueue.isEmpty()) {
3910
+ nextMsg = await messageQueue.next();
3911
+ } else {
3912
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, void 0, isAuto);
3913
+ }
3914
+ if (nextMsg.type === "user") {
3915
+ if (tui.isCancel(nextMsg.content)) {
3916
+ finalSummary = taskSummary;
3917
+ keepGoing = false;
3918
+ break;
3919
+ }
3920
+ }
3921
+ nextPrompt = nextMsg.content;
3922
+ continue;
3923
+ } else {
3924
+ finalSummary = taskSummary;
3925
+ keepGoing = false;
3926
+ break;
3927
+ }
3928
+ }
3929
+ } else if (action.type === "wait") {
3930
+ const durationSeconds = action.duration_seconds || 0;
3931
+ const durationMs = durationSeconds > 0 ? durationSeconds * 1e3 : void 0;
3932
+ log.info(`\u23F3 Waiting for updates (Timeout: ${durationSeconds || "infinite"}s)...`);
3933
+ let nextMsg;
3934
+ if (!messageQueue.isEmpty()) {
3935
+ nextMsg = await messageQueue.next();
3936
+ } else {
3937
+ nextMsg = await waitForInputOrNotification(messageQueue, "Your answer:", subagentPrefix, durationMs, isAuto);
4576
3938
  }
4577
3939
  if (nextMsg.type === "timeout") {
4578
3940
  resultMsg = `[System]: Wait duration of ${durationSeconds} seconds expired. No notifications received.`;
@@ -4585,6 +3947,13 @@ ${detailedContent}`
4585
3947
  } else {
4586
3948
  resultMsg = nextMsg.content;
4587
3949
  }
3950
+ } else if (action.type === "notify_user") {
3951
+ const messageContent = action.content || "";
3952
+ if (messageContent) {
3953
+ log.info(colors.primary("\u{1F916} Shark Dev:"));
3954
+ console.log(messageContent);
3955
+ }
3956
+ resultMsg = `[Action notify_user Success]: Notifica\xE7\xE3o exibida com sucesso para o usu\xE1rio.`;
4588
3957
  } else {
4589
3958
  resultMsg = `[Unsupported action type: ${action.type}]`;
4590
3959
  }
@@ -4616,6 +3985,7 @@ ${detailedContent}`
4616
3985
  log.success("\u2705 Task Scope Completed");
4617
3986
  return finalResult;
4618
3987
  } finally {
3988
+ activeOnCommandHandler = void 0;
4619
3989
  process.off("SIGINT", sigIntHandler);
4620
3990
  process.off("SIGTERM", sigTermHandler);
4621
3991
  const currentId = options.taskId || "parent";
@@ -4630,11 +4000,30 @@ ${detailedContent}`
4630
4000
  }
4631
4001
 
4632
4002
  // src/commands/dev.ts
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) => {
4003
+ import { exec as exec2 } from "child_process";
4004
+ import * as path9 from "path";
4005
+ import * as fs9 from "fs";
4006
+ import { fileURLToPath as fileURLToPath2 } from "url";
4007
+ var __filename2 = fileURLToPath2(import.meta.url);
4008
+ var __dirname2 = path9.dirname(__filename2);
4009
+ 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").option("--graph", "Enable real-time memory graph visualization", false).action(async (options) => {
4634
4010
  if (options.exportSchema) {
4635
4011
  console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
4636
4012
  return;
4637
4013
  }
4014
+ if (options.graph) {
4015
+ try {
4016
+ const runDir = path9.join(process.cwd(), ".shark", "membox");
4017
+ const configPath = path9.join(runDir, "graph-server.json");
4018
+ if (fs9.existsSync(configPath)) {
4019
+ fs9.unlinkSync(configPath);
4020
+ }
4021
+ } catch {
4022
+ }
4023
+ const cliPath = path9.resolve(__dirname2, "..", "bin", "shark.js");
4024
+ const child = exec2(`node "${cliPath}" graph`);
4025
+ child.unref();
4026
+ }
4638
4027
  try {
4639
4028
  const result = await interactiveDeveloperAgent({
4640
4029
  taskInstruction: options.task,
@@ -4652,996 +4041,534 @@ var devCommand = new Command2("dev").description("Starts the Shark Developer Age
4652
4041
  }
4653
4042
  });
4654
4043
 
4655
- // src/commands/legacy.ts
4044
+ // src/commands/export-schema.ts
4656
4045
  import { Command as Command3 } from "commander";
4046
+ var exportSchemaCommand = new Command3("export-schema").description("Outputs the agent response JSON Schema").action(() => {
4047
+ console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
4048
+ });
4657
4049
 
4658
- // src/core/agents/legacy-developer-agent.ts
4659
- import fs10 from "fs";
4660
- import path11 from "path";
4661
- var AGENT_TYPE = "developer_agent";
4662
- async function validateTypeScript(filePath) {
4050
+ // src/commands/export-prompt.ts
4051
+ import { Command as Command4 } from "commander";
4052
+ var exportPromptCommand = new Command4("export-prompt").description("Outputs the unified agent system prompt").action(() => {
4053
+ console.log(UNIFIED_SYSTEM_PROMPT);
4054
+ });
4055
+
4056
+ // src/commands/super.ts
4057
+ import { Command as Command5 } from "commander";
4058
+ import { fileURLToPath as fileURLToPath3 } from "url";
4059
+ import path10 from "path";
4060
+ import os3 from "os";
4061
+ import fs10 from "fs/promises";
4062
+ var superCommandAction = async (options = {}) => {
4063
+ const __filename3 = fileURLToPath3(import.meta.url);
4064
+ const __dirname3 = path10.dirname(__filename3);
4065
+ const packageRoot = path10.resolve(__dirname3, "../../");
4066
+ const internalSkillsPath = path10.join(packageRoot, "skills");
4067
+ const targetPath = options.local ? path10.join(process.cwd(), ".agents", "skills") : path10.join(os3.homedir(), ".shark", "skills");
4663
4068
  try {
4664
- const result = await handleRunCommand(`npx tsc --noEmit --skipLibCheck ${filePath}`);
4665
- if (result.trim() === "" || !result.includes("error TS")) {
4666
- return { valid: true };
4667
- }
4668
- return { valid: false, error: result };
4669
- } catch (e) {
4670
- return { valid: false, error: e.message || "TypeScript validation failed" };
4671
- }
4672
- }
4673
- function getAgentId(overrideId) {
4674
- if (overrideId) return overrideId;
4675
- const config = ConfigManager.getInstance().getConfig();
4676
- if (config.agents?.dev) return config.agents.dev;
4677
- if (process.env.STACKSPOT_DEV_AGENT_ID) return process.env.STACKSPOT_DEV_AGENT_ID;
4678
- return "01KEQCGJ65YENRA4QBXVN1YFFX";
4679
- }
4680
- async function interactiveDeveloperAgent2(options = {}) {
4681
- FileLogger.init();
4682
- const agentId = getAgentId();
4683
- if (agentId === "PENDING_CONFIGURATION") {
4684
- tui.log.error("\u274C STACKSPOT_DEV_AGENT_ID not configured in .env");
4685
- return { success: false, summary: "Missing configuration." };
4686
- }
4687
- const projectRoot = process.cwd();
4688
- let contextContent = "";
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)) {
4692
4069
  try {
4693
- contextContent = fs10.readFileSync(specificContextPath, "utf-8");
4694
- } catch (e) {
4695
- tui.log.warning(`Failed to read context file: ${e}`);
4070
+ await fs10.rm(targetPath, { recursive: true, force: true });
4071
+ } catch {
4696
4072
  }
4073
+ await fs10.mkdir(targetPath, { recursive: true });
4074
+ await fs10.cp(internalSkillsPath, targetPath, { recursive: true, force: true });
4075
+ console.log(`\u{1F680} Superpowers skills installed successfully to ${targetPath}`);
4076
+ } catch (error) {
4077
+ console.error(`\u274C Failed to install superpowers skills: ${error.message}`);
4078
+ process.exit(1);
4697
4079
  }
4698
- const currentTask = options.taskInstruction || "Analyze the project and fix pending issues.";
4699
- let basePrompt = ``;
4700
- if (contextContent) {
4701
- basePrompt += `
4080
+ };
4081
+ 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);
4702
4082
 
4703
- --- PROJECT CONTEXT ---
4704
- ${contextContent}
4705
- -----------------------
4706
- `;
4707
- }
4708
- if (options.history) {
4709
- basePrompt += `
4083
+ // src/commands/graph.ts
4084
+ import { Command as Command6, Argument } from "commander";
4085
+ import * as http from "http";
4086
+ import * as fs11 from "fs";
4087
+ import * as path11 from "path";
4088
+ import { exec as exec3 } from "child_process";
4710
4089
 
4711
- --- PREVIOUS EXECUTION SUMMARY ---
4712
- ${options.history}
4713
- ----------------------------------
4714
- `;
4715
- }
4716
- basePrompt += `
4090
+ // src/commands/graph-html.ts
4091
+ var GRAPH_HTML_TEMPLATE = `
4092
+ <!DOCTYPE html>
4093
+ <html lang="en">
4094
+ <head>
4095
+ <meta charset="UTF-8">
4096
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
4097
+ <title>Shark Dev Memory Graph</title>
4098
+ <script src="https://cdn.tailwindcss.com"></script>
4099
+ <script type="text/javascript" src="https://unpkg.com/vis-network/standalone/umd/vis-network.min.js"></script>
4100
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&family=JetBrains+Mono&display=swap" rel="stylesheet">
4101
+ <style>
4102
+ body { font-family: 'Inter', sans-serif; background-color: #0b0f19; color: #f3f4f6; }
4103
+ .glass { background: rgba(15, 23, 42, 0.75); backdrop-filter: blur(12px); border: 1px solid rgba(255, 255, 255, 0.08); }
4104
+ </style>
4105
+ </head>
4106
+ <body class="h-screen w-screen overflow-hidden flex flex-col">
4107
+ <!-- Header -->
4108
+ <header class="h-16 px-6 border-b border-white/5 flex items-center justify-between glass z-10">
4109
+ <div class="flex items-center gap-3">
4110
+ <span class="text-2xl">\u{1F988}</span>
4111
+ <h1 class="text-lg font-semibold tracking-wide">Shark Memory Graph</h1>
4112
+ </div>
4113
+ <div class="flex items-center gap-4">
4114
+ <input type="text" id="searchInput" placeholder="Search topics/events..." class="px-4 py-2 bg-white/5 border border-white/10 rounded-lg text-sm focus:outline-none focus:border-blue-500/50 w-64">
4115
+ <div class="flex bg-white/5 rounded-lg p-0.5 border border-white/5">
4116
+ <button id="btnBoxes" class="px-4 py-1.5 rounded-md text-xs font-medium bg-blue-600 text-white transition-all">Boxes Network</button>
4117
+ <button id="btnTimeline" class="px-4 py-1.5 rounded-md text-xs font-medium text-gray-400 hover:text-white transition-all">Event Flow</button>
4118
+ </div>
4119
+ </div>
4120
+ </header>
4717
4121
 
4718
- \u{1F7E2} EXECUTION MODE
4122
+ <!-- Main Content -->
4123
+ <div class="flex-1 flex relative">
4124
+ <!-- Canvas -->
4125
+ <div id="network" class="flex-1 h-full"></div>
4719
4126
 
4720
- You are a highly skilled Developer Agent.
4721
- \u{1F449} **CURRENT TASK**: "${currentTask}"
4127
+ <!-- Sidebar -->
4128
+ <aside id="sidebar" class="w-96 glass border-l border-white/5 flex flex-col translate-x-full transition-transform duration-300 absolute right-0 top-0 bottom-0 z-20">
4129
+ <div class="p-6 border-b border-white/5 flex items-center justify-between">
4130
+ <h2 id="sidebarTitle" class="text-lg font-semibold text-blue-400">Node Details</h2>
4131
+ <button id="btnCloseSidebar" class="text-gray-400 hover:text-white text-lg">&times;</button>
4132
+ </div>
4133
+ <div id="sidebarContent" class="flex-1 overflow-y-auto p-6 space-y-4">
4134
+ <!-- Filled dynamically -->
4135
+ </div>
4136
+ </aside>
4722
4137
 
4723
- Your goal is to COMPLETE this specific task and then STOP.
4724
- 1. Implement the necessary changes.
4725
- 2. Verify (compile/test).
4726
- 3. **MANDATORY**: When you are confident the task is done, output a final message starting with "TASK_COMPLETED:" followed by a brief technical summary of what you did.
4727
- `;
4728
- let nextPrompt = basePrompt;
4729
- let keepGoing = true;
4730
- const spinner = tui.spinner();
4731
- let finalSummary = "";
4732
- let isTaskCompleted = false;
4733
- const conversationKey = options.taskId ? `dev_agent_${options.taskId}` : `dev_agent_${Date.now()}`;
4734
- let autoApprovals = {
4735
- files: false,
4736
- commands: false
4737
- };
4738
- while (keepGoing) {
4739
- try {
4740
- spinner.start("\u{1F988} Shark Dev working...");
4741
- const lastResponse = await callDevAgentApi(nextPrompt, (chunk) => {
4742
- }, conversationKey);
4743
- spinner.stop("Response received");
4744
- if (lastResponse) {
4745
- const response = lastResponse;
4746
- const actions = response.actions || [];
4747
- if (response.message && response.message.includes("TASK_COMPLETED:")) {
4748
- isTaskCompleted = true;
4749
- finalSummary = response.message.split("TASK_COMPLETED:")[1].trim();
4750
- keepGoing = false;
4751
- }
4752
- if (response.message && response.message.includes("TASK_FAILED:")) {
4753
- const failureReason = response.message.split("TASK_FAILED:")[1].trim();
4754
- tui.log.error(`\u274C Agent reported task failure: ${failureReason}`);
4755
- return { success: false, summary: failureReason };
4756
- }
4757
- if (actions.length === 0 && response.message && !isTaskCompleted) {
4758
- tui.log.info(colors.primary("\u{1F916} Shark Dev:"));
4759
- console.log(response.message);
4760
- const userReply = await tui.text({ message: "Your answer:" });
4761
- if (tui.isCancel(userReply)) {
4762
- keepGoing = false;
4763
- break;
4764
- }
4765
- nextPrompt = userReply;
4766
- }
4767
- let executionResults = "";
4768
- let waitingForUser = false;
4769
- for (const action of actions) {
4770
- if (action.type === "talk_with_user") {
4771
- tui.log.info(colors.primary("\u{1F916} Shark Dev:"));
4772
- console.log(action.content);
4773
- if (!isTaskCompleted) waitingForUser = true;
4774
- } else if (action.type === "list_files") {
4775
- tui.log.info(`\u{1F4C2} Scanning: ${colors.dim(action.path || ".")}`);
4776
- const result = handleListFiles(action.path || ".");
4777
- executionResults += `[Action list_files(${action.path}) Result]:
4778
- ${result}
4138
+ <!-- Empty State overlay -->
4139
+ <div id="emptyState" class="absolute inset-0 bg-[#0b0f19]/90 flex items-center justify-center hidden z-30">
4140
+ <div class="max-w-md p-8 rounded-2xl glass text-center space-y-4">
4141
+ <div class="text-4xl">\u{1F4AD}</div>
4142
+ <h3 class="text-xl font-bold">Awaiting Memories...</h3>
4143
+ <p class="text-sm text-gray-400">Start a chat session using <code class="bg-black/30 px-1.5 py-0.5 rounded text-blue-400">shark dev --graph</code> in another terminal to generate memory boxes.</p>
4144
+ </div>
4145
+ </div>
4146
+ </div>
4779
4147
 
4780
- `;
4781
- } else if (action.type === "read_file") {
4782
- tui.log.info(`\u{1F4D6} Reading: ${colors.dim(action.path || "")}`);
4783
- const result = handleReadFile(action.path || "");
4784
- executionResults += `[Action read_file(${action.path}) Result]:
4785
- ${result}
4786
-
4787
- `;
4788
- } else if (action.type === "search_file") {
4789
- const result = handleSearchFile(action.path || "");
4790
- executionResults += `[Action search_file(${action.path}) Result]:
4791
- ${result}
4792
-
4793
- `;
4794
- } else if (action.type === "run_command") {
4795
- const cmd = action.command || "";
4796
- tui.log.info(`\u{1F4BB} Executing: ${colors.dim(cmd)}`);
4797
- let approved = autoApprovals.commands;
4798
- if (!approved) {
4799
- const choice = await tui.select({
4800
- message: `Execute: ${cmd}?`,
4801
- options: [{ value: "yes", label: "Yes" }, { value: "always", label: "Yes (Auto-Approve Session)" }, { value: "no", label: "No" }]
4802
- });
4803
- if (choice === "always") {
4804
- autoApprovals.commands = true;
4805
- approved = true;
4806
- } else if (choice === "yes") approved = true;
4148
+ <script>
4149
+ let network = null;
4150
+ let currentMode = 'boxes';
4151
+ let graphData = { nodes: [], edges: [] };
4152
+ let lastDataString = '';
4153
+
4154
+ async function fetchGraphData() {
4155
+ try {
4156
+ const res = await fetch(\`/api/graph?mode=\${currentMode}\`);
4157
+ const data = await res.json();
4158
+
4159
+ const container = document.getElementById('emptyState');
4160
+ if (data.nodes.length === 0) {
4161
+ container.classList.remove('hidden');
4162
+ return;
4163
+ }
4164
+ container.classList.add('hidden');
4165
+
4166
+ const currentDataString = JSON.stringify({ nodes: data.nodes, edges: data.edges });
4167
+ if (currentDataString === lastDataString) {
4168
+ highlightActiveNodes(data.activeNodes);
4169
+ return;
4170
+ }
4171
+ lastDataString = currentDataString;
4172
+
4173
+ updateNetwork(data);
4174
+ } catch (err) {
4175
+ console.error("Error fetching graph data:", err);
4807
4176
  }
4808
- if (approved) {
4809
- const result = await handleRunCommand(cmd);
4810
- executionResults += `[Action run_command(${cmd}) Result]:
4811
- ${result}
4177
+ }
4812
4178
 
4813
- `;
4179
+ function getOptionsForMode(mode) {
4180
+ if (mode === 'timeline') {
4181
+ return {
4182
+ layout: {
4183
+ hierarchical: {
4184
+ enabled: true,
4185
+ direction: 'LR',
4186
+ sortMethod: 'directed',
4187
+ nodeSpacing: 200,
4188
+ levelSeparation: 250
4189
+ }
4190
+ },
4191
+ physics: {
4192
+ enabled: false
4193
+ },
4194
+ interaction: { hover: true }
4195
+ };
4814
4196
  } else {
4815
- executionResults += `[Action run_command]: User blocked execution.
4816
-
4817
- `;
4818
- }
4819
- } else if (["create_file", "modify_file"].includes(action.type)) {
4820
- const filePath = action.path || "";
4821
- tui.log.warning(`\u{1F4DD} ${action.type === "create_file" ? "CREATE" : "MODIFY"}: ${colors.bold(filePath)}`);
4822
- let approved = autoApprovals.files;
4823
- if (!approved) {
4824
- const choice = await tui.select({
4825
- message: `Approve changes to ${filePath}?`,
4826
- options: [{ value: "yes", label: "Yes" }, { value: "always", label: "Yes (Auto-Approve Session)" }, { value: "no", label: "No" }]
4827
- });
4828
- if (choice === "always") {
4829
- autoApprovals.files = true;
4830
- approved = true;
4831
- } else if (choice === "yes") approved = true;
4197
+ return {
4198
+ layout: {
4199
+ hierarchical: {
4200
+ enabled: false
4201
+ }
4202
+ },
4203
+ physics: {
4204
+ enabled: true,
4205
+ solver: 'forceAtlas2Based',
4206
+ forceAtlas2Based: { gravitationalConstant: -50, centralGravity: 0.01, springLength: 100 }
4207
+ },
4208
+ interaction: { hover: true }
4209
+ };
4832
4210
  }
4833
- if (approved) {
4834
- if (action.type === "create_file") {
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");
4838
- executionResults += `[Action create_file]: Success
4211
+ }
4839
4212
 
4840
- `;
4841
- } else {
4842
- let success = false;
4843
- if (action.line_range) success = replaceLineRange(filePath, action.line_range[0], action.line_range[1], action.content || "", tui);
4844
- else if (action.target_content) success = startSmartReplace(filePath, action.content || "", action.target_content, tui);
4845
- executionResults += success ? `[Action modify_file]: Success
4213
+ function updateNetwork(data) {
4214
+ graphData = data;
4215
+ const container = document.getElementById('network');
4216
+
4217
+ const visNodes = new vis.DataSet(data.nodes.map(n => {
4218
+ const isActive = data.activeNodes && data.activeNodes.includes(n.id);
4219
+ return {
4220
+ id: n.id,
4221
+ label: n.label,
4222
+ size: n.size || 15,
4223
+ shape: n.type === 'box' ? 'dot' : 'circle',
4224
+ color: {
4225
+ background: isActive ? '#d97706' : (n.type === 'box' ? '#1e40af' : '#065f46'),
4226
+ border: isActive ? '#fbbf24' : (n.type === 'box' ? '#3b82f6' : '#10b981'),
4227
+ highlight: { background: '#2563eb', border: '#60a5fa' }
4228
+ },
4229
+ font: { color: '#f3f4f6', face: 'Inter', size: 12 }
4230
+ };
4231
+ }));
4846
4232
 
4847
- ` : `[Action modify_file]: Failed
4233
+ const visEdges = new vis.DataSet(data.edges.map(e => {
4234
+ // Similarity-based edge thickness (e.similarity from 0.0 to 1.0)
4235
+ const width = e.similarity ? Math.max(1, Math.round(e.similarity * 6)) : 2;
4236
+ const isWeak = e.similarity && e.similarity < 0.6;
4237
+ return {
4238
+ from: e.from,
4239
+ to: e.to,
4240
+ label: e.label || '',
4241
+ arrows: e.arrows || '',
4242
+ width: width,
4243
+ dashes: isWeak ? true : false,
4244
+ color: { color: '#4b5563', highlight: '#9ca3af' },
4245
+ font: { color: '#9ca3af', face: 'Inter', size: 10 }
4246
+ };
4247
+ }));
4848
4248
 
4849
- `;
4850
- }
4851
- const val = await validateTypeScript(path11.resolve(projectRoot, filePath));
4852
- if (!val.valid) executionResults += `[Validation Failed]: ${val.error}
4853
-
4854
- `;
4249
+ if (network) {
4250
+ network.setData({ nodes: visNodes, edges: visEdges });
4251
+ network.setOptions(getOptionsForMode(currentMode));
4855
4252
  } else {
4856
- executionResults += `[Action ${action.type}]: User Denied.
4857
-
4858
- `;
4253
+ network = new vis.Network(container, { nodes: visNodes, edges: visEdges }, getOptionsForMode(currentMode));
4254
+
4255
+ network.on('click', function(params) {
4256
+ if (params.nodes.length > 0) {
4257
+ showSidebar(params.nodes[0]);
4258
+ } else {
4259
+ hideSidebar();
4260
+ }
4261
+ });
4859
4262
  }
4860
- } else if (action.type.startsWith("ast_")) {
4861
- try {
4862
- let result = "";
4863
- if (action.type === "ast_list_structure") {
4864
- result = await astListStructure(action.path || "");
4865
- } else if (action.type === "ast_get_method") {
4866
- result = await astGetMethod(action.path || "", action.class_name || "", action.method_name || "");
4867
- } else if (action.type === "ast_add_method") {
4868
- const success = await astAddMethod(action.path || "", action.class_name || "", action.method_code || "");
4869
- result = success ? "Method added successfully." : "Failed to add method.";
4870
- } else if (action.type === "ast_modify_method") {
4871
- const success = await astModifyMethod(action.path || "", action.class_name || "", action.method_name || "", action.new_body || "");
4872
- result = success ? "Method modified successfully." : "Failed to modify method.";
4873
- } else if (action.type === "ast_remove_method") {
4874
- const success = await astRemoveMethod(action.path || "", action.class_name || "", action.method_name || "");
4875
- result = success ? "Method removed successfully." : "Failed to remove method.";
4876
- } else if (action.type === "ast_add_class") {
4877
- const success = await astAddClass(action.path || "", action.class_name || "", action.extends_class || void 0, action.implements_interfaces || void 0);
4878
- result = success ? "Class added successfully." : "Failed to add class.";
4879
- } else if (action.type === "ast_get_property") {
4880
- tui.log.info(`\u{1F50D} Reading property ${colors.gray(action.property_name || "")} in ${colors.gray(action.class_name || "")}`);
4881
- const propContent = await astGetProperty(action.path || "", action.class_name || "", action.property_name || "");
4882
- result = `[AST Get Property] Content:
4883
- ${propContent}`;
4884
- } else if (action.type === "ast_add_property") {
4885
- const success = await astAddProperty(action.path || "", action.class_name || "", action.property_code || "");
4886
- result = success ? "Property added successfully." : "Failed to add property.";
4887
- } else if (action.type === "ast_modify_property") {
4888
- tui.log.info(`\u270F\uFE0F Modifying property ${colors.gray(action.property_name || "")} in ${colors.gray(action.class_name || "")}`);
4889
- const success = await astModifyProperty(action.path || "", action.class_name || "", action.property_name || "", action.property_code || "");
4890
- result = success ? "Property modified successfully." : "Failed to modify property.";
4891
- } else if (action.type === "ast_remove_property") {
4892
- const success = await astRemoveProperty(action.path || "", action.class_name || "", action.property_name || "");
4893
- result = success ? "Property removed successfully." : "Failed to remove property.";
4894
- } else if (action.type === "ast_add_decorator") {
4895
- const success = await astAddDecorator(action.path || "", action.class_name || "", action.decorator_code || "");
4896
- result = success ? "Decorator added successfully." : "Failed to add decorator.";
4897
- } else if (action.type === "ast_add_interface") {
4898
- const success = await astAddInterface(action.path || "", action.interface_code || "");
4899
- result = success ? "Interface added successfully." : "Failed to add interface.";
4900
- } else if (action.type === "ast_add_type_alias") {
4901
- const success = await astAddTypeAlias(action.path || "", action.type_code || "");
4902
- result = success ? "Type alias added successfully." : "Failed to add type alias.";
4903
- } else if (action.type === "ast_add_function") {
4904
- const success = await astAddFunction(action.path || "", action.function_code || "");
4905
- result = success ? "Function added successfully." : "Failed to add function.";
4906
- } else if (action.type === "ast_remove_function") {
4907
- const success = await astRemoveFunction(action.path || "", action.function_name || "");
4908
- result = success ? "Function removed successfully." : "Failed to remove function.";
4909
- } else if (action.type === "ast_add_import") {
4910
- const success = await astAddImport(action.path || "", action.import_statement || "");
4911
- result = success ? "Import added successfully." : "Failed to add import.";
4912
- } else if (action.type === "ast_remove_import") {
4913
- const success = await astRemoveImport(action.path || "", action.module_path || "");
4914
- result = success ? "Import removed successfully." : "Failed to remove import.";
4915
- } else if (action.type === "ast_organize_imports") {
4916
- const success = await astOrganizeImports(action.path || "");
4917
- result = success ? "Imports organized successfully." : "Failed to organize imports.";
4918
- } else {
4919
- result = `Unknown AST action: ${action.type}`;
4920
- }
4921
- executionResults += `[Action ${action.type} Result]:
4922
- ${result}
4263
+ }
4923
4264
 
4924
- `;
4925
- tui.log.info(`\u26A1 AST Action ${colors.dim(action.type)}: ${result}`);
4926
- } catch (e) {
4927
- executionResults += `[Action ${action.type} Failed]: ${e.message}
4265
+ function highlightActiveNodes(activeNodeIds) {
4266
+ if (!network || !activeNodeIds) return;
4267
+ const nodesDataset = network.body.data.nodes;
4268
+ graphData.nodes.forEach(n => {
4269
+ const isActive = activeNodeIds.includes(n.id);
4270
+ nodesDataset.update({
4271
+ id: n.id,
4272
+ color: {
4273
+ background: isActive ? '#d97706' : (n.type === 'box' ? '#1e40af' : '#065f46'),
4274
+ border: isActive ? '#fbbf24' : (n.type === 'box' ? '#3b82f6' : '#10b981')
4275
+ }
4276
+ });
4277
+ });
4278
+ }
4928
4279
 
4929
- `;
4930
- tui.log.error(`\u274C AST Action Error: ${e.message}`);
4931
- }
4932
- } else if (action.type === "search_ast") {
4933
- const result = await astGrepSearch(action.pattern || "", action.path || "", action.language || "typescript", tui);
4934
- executionResults += `[Action search_ast Result]:
4935
- ${result}
4280
+ function showSidebar(nodeId) {
4281
+ const node = graphData.nodes.find(n => n.id === nodeId);
4282
+ if (!node || !node.details) return;
4936
4283
 
4937
- `;
4938
- } else if (action.type === "modify_ast") {
4939
- const success = await astGrepRewrite(action.pattern || "", action.fix || "", action.path || "", action.language || "typescript", tui);
4940
- executionResults += success ? `[Action modify_ast]: Success
4284
+ document.getElementById('sidebarTitle').innerText = node.label;
4285
+ const content = document.getElementById('sidebarContent');
4286
+ content.innerHTML = '';
4941
4287
 
4942
- ` : `[Action modify_ast]: Failed
4288
+ if (node.type === 'box') {
4289
+ const detail = node.details;
4290
+ if (detail.keywords && detail.keywords.length > 0) {
4291
+ const tagContainer = document.createElement('div');
4292
+ tagContainer.className = 'flex flex-wrap gap-2';
4293
+ detail.keywords.forEach(kw => {
4294
+ const span = document.createElement('span');
4295
+ span.className = 'px-2 py-0.5 bg-blue-500/10 text-blue-400 rounded text-xs border border-blue-500/20';
4296
+ span.innerText = kw;
4297
+ tagContainer.appendChild(span);
4298
+ });
4299
+ content.appendChild(tagContainer);
4300
+ }
4943
4301
 
4944
- `;
4945
- }
4946
- }
4947
- if (executionResults) {
4948
- if (waitingForUser) {
4949
- const userReply = await tui.text({ message: "Your answer:" });
4950
- if (tui.isCancel(userReply)) {
4951
- keepGoing = false;
4952
- break;
4302
+ if (detail.history) {
4303
+ const historyTitle = document.createElement('h4');
4304
+ historyTitle.className = 'text-sm font-semibold border-b border-white/5 pb-2 mt-4';
4305
+ historyTitle.innerText = 'Dialogue Logs';
4306
+ content.appendChild(historyTitle);
4307
+
4308
+ detail.history.forEach(msg => {
4309
+ const bubble = document.createElement('div');
4310
+ bubble.className = \`p-3 rounded-lg text-sm \${msg.role === 'user' ? 'bg-white/5 border border-white/5' : 'bg-blue-600/10 border border-blue-600/20'}\`;
4311
+ bubble.innerHTML = \`<strong class="block text-xs text-gray-400 mb-1">\${msg.role.toUpperCase()}</strong>\${msg.content}\`;
4312
+ content.appendChild(bubble);
4313
+ });
4314
+ }
4315
+ } else {
4316
+ const eventDesc = document.createElement('p');
4317
+ eventDesc.className = 'text-sm text-gray-300';
4318
+ eventDesc.innerText = node.details.event || '';
4319
+ content.appendChild(eventDesc);
4953
4320
  }
4954
- nextPrompt = `${executionResults}
4955
- User Reply: ${userReply}`;
4956
- } else {
4957
- nextPrompt = `${executionResults}
4958
- [System]: Continue execution. If finished, output "TASK_COMPLETED: summary".`;
4959
- tui.log.info(colors.dim("Processing results..."));
4960
- }
4961
- } else if (!keepGoing) {
4962
- } else if (waitingForUser) {
4963
- } else {
4964
- if (!isTaskCompleted && actions.length > 0) nextPrompt = "Please continue.";
4965
- }
4966
- } else {
4967
- tui.log.warning("No response received from agent.");
4968
- }
4969
- } catch (e) {
4970
- tui.log.error(e.message);
4971
- keepGoing = false;
4972
- return { success: false, summary: `Error: ${e.message}` };
4973
- }
4974
- }
4975
- tui.log.success("\u2705 Task Scope Completed");
4976
- return { success: true, summary: finalSummary || "Task completed without summary." };
4977
- }
4978
- async function callDevAgentApi(prompt, onChunk, conversationKey = AGENT_TYPE) {
4979
- const existingConversationId = await conversationManager.getConversationId(conversationKey);
4980
- const provider = ProviderResolver.getProvider("developer_agent");
4981
- const parsedResponse = await provider.streamChat(prompt, {
4982
- conversationId: existingConversationId,
4983
- agentType: "developer_agent",
4984
- onChunk
4985
- });
4986
- if (parsedResponse.conversation_id) {
4987
- await conversationManager.saveConversationId(conversationKey, parsedResponse.conversation_id);
4988
- }
4989
- return parsedResponse;
4990
- }
4991
4321
 
4992
- // src/core/workflow/task-manager.ts
4993
- import fs11 from "fs";
4994
- import path12 from "path";
4995
- var TaskManager = class {
4996
- projectRoot;
4997
- specPath;
4998
- constructor(projectRoot = process.cwd()) {
4999
- this.projectRoot = projectRoot;
5000
- this.specPath = path12.resolve(this.projectRoot, "_sharkrc", "tech-spec.md");
5001
- }
5002
- /**
5003
- * Reads the tech-spec.md file and analyzes its current state.
5004
- */
5005
- analyzeSpecState() {
5006
- if (!fs11.existsSync(this.specPath)) {
5007
- return { status: "MISSING", allTasks: [] };
5008
- }
5009
- const content = fs11.readFileSync(this.specPath, "utf-8");
5010
- const lines = content.split("\n");
5011
- const tasks = [];
5012
- let taskIndex = 1;
5013
- for (let i = 0; i < lines.length; i++) {
5014
- const line = lines[i];
5015
- const trimmed = line.trim();
5016
- const pendingMatch = trimmed.match(/^- \[ \] (.*)/);
5017
- const completedMatch = trimmed.match(/^- \[x\] (.*)/i);
5018
- const progressMatch = trimmed.match(/^- \[\/\] (.*)/);
5019
- let currentTask = null;
5020
- let status2 = null;
5021
- let description = "";
5022
- if (pendingMatch) {
5023
- description = pendingMatch[1].trim();
5024
- status2 = "PENDING";
5025
- } else if (completedMatch) {
5026
- description = completedMatch[1].trim();
5027
- status2 = "COMPLETED";
5028
- } else if (progressMatch) {
5029
- description = progressMatch[1].trim();
5030
- status2 = "IN_PROGRESS";
5031
- }
5032
- if (status2 && description) {
5033
- let j = i + 1;
5034
- while (j < lines.length) {
5035
- const nextLine = lines[j];
5036
- const nextTrimmed = nextLine.trim();
5037
- if (!nextTrimmed || nextTrimmed.match(/^- \[[ x\/]\]/) || nextTrimmed.startsWith("#")) {
5038
- break;
5039
- }
5040
- description += "\n" + nextTrimmed;
5041
- j++;
4322
+ document.getElementById('sidebar').classList.remove('translate-x-full');
5042
4323
  }
5043
- currentTask = {
5044
- id: `task-${taskIndex++}`,
5045
- description,
5046
- status: status2,
5047
- line_number: i
5048
- };
5049
- tasks.push(currentTask);
5050
- }
5051
- }
5052
- let nextTask = tasks.find((t) => t.status === "IN_PROGRESS");
5053
- if (!nextTask) {
5054
- nextTask = tasks.find((t) => t.status === "PENDING");
5055
- }
5056
- const status = !nextTask && tasks.length > 0 && tasks.every((t) => t.status === "COMPLETED") ? "COMPLETED" : "PENDING";
5057
- return {
5058
- status: tasks.length === 0 ? "MISSING" : status,
5059
- // Empty file is effectively "pending creation" but we treat as missing content logic elsewhere
5060
- nextTask,
5061
- allTasks: tasks
5062
- };
5063
- }
5064
- /**
5065
- * Marks a specific task as COMPLETED in the file.
5066
- * Uses line-based replacement to be safe.
5067
- */
5068
- markTaskAsDone(taskId) {
5069
- const state = this.analyzeSpecState();
5070
- const task = state.allTasks.find((t) => t.id === taskId);
5071
- if (!task) {
5072
- console.error(`Task ${taskId} not found.`);
5073
- return false;
5074
- }
5075
- const content = fs11.readFileSync(this.specPath, "utf-8");
5076
- const lines = content.split("\n");
5077
- const targetLine = lines[task.line_number];
5078
- if (!targetLine.includes(task.description)) {
5079
- console.error(`Concurrency Error: Task line content mistmatch. Expected "${task.description}" at line ${task.line_number}.`);
5080
- return false;
5081
- }
5082
- const newLine = targetLine.replace("- [ ]", "- [x]").replace("- [/]", "- [x]");
5083
- lines[task.line_number] = newLine;
5084
- fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
5085
- return true;
5086
- }
5087
- /**
5088
- * Marks a task as IN_PROGRESS.
5089
- */
5090
- markTaskInProgress(taskId) {
5091
- const state = this.analyzeSpecState();
5092
- const task = state.allTasks.find((t) => t.id === taskId);
5093
- if (!task) return false;
5094
- const content = fs11.readFileSync(this.specPath, "utf-8");
5095
- const lines = content.split("\n");
5096
- let targetLine = lines[task.line_number];
5097
- targetLine = targetLine.replace("- [ ]", "- [/]");
5098
- lines[task.line_number] = targetLine;
5099
- fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
5100
- return true;
5101
- }
5102
- /**
5103
- * Completely updates the spec file content (used by Spec Agent).
5104
- */
5105
- updateSpecContent(newContent) {
5106
- fs11.writeFileSync(this.specPath, newContent, "utf-8");
5107
- }
5108
- getSpecPath() {
5109
- return this.specPath;
5110
- }
5111
- };
5112
-
5113
- // src/core/agents/specification-agent.ts
5114
- import fs12 from "fs";
5115
- import path13 from "path";
5116
- var AGENT_TYPE2 = "specification_agent";
5117
- async function interactiveSpecificationAgent(options = {}) {
5118
- FileLogger.init();
5119
- tui.intro("\u{1F3D7}\uFE0F Specification Agent (Template-Based)");
5120
- const projectRoot = process.cwd();
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)) {
5125
- let initialContent = `# Technical Specification: {{PROJECT_NAME}}
5126
-
5127
- ## 1. Technology Stack
5128
- [TO BE ANALYZED - STACK]
5129
- - Language: [e.g. TypeScript]
5130
- - Framework: [e.g. Node.js / React]
5131
- - Database: [e.g. SQLite / PostgreSQL]
5132
- - Key Libraries: [Top 5 dependencies]
5133
4324
 
5134
- ## 2. Architecture Overview
5135
- [TO BE ANALYZED - ARCHITECTURE]
5136
- [Brief description of architectural pattern]
5137
-
5138
- ## 3. Data Model
5139
- [TO BE ANALYZED - DATA MODEL]
5140
- [Schema/ERD definitions]
4325
+ function hideSidebar() {
4326
+ document.getElementById('sidebar').classList.add('translate-x-full');
4327
+ }
5141
4328
 
5142
- ## 4. API / Interface Contracts
5143
- [TO BE ANALYZED - API]
5144
- [Main endpoints or CLI commands]
4329
+ document.getElementById('btnCloseSidebar').addEventListener('click', hideSidebar);
5145
4330
 
5146
- ## 5. Implementation Steps
5147
- [TO BE FILLED - MUST BE CHECKBOXES]
5148
- `;
5149
- const projectName = path13.basename(projectRoot);
5150
- initialContent = initialContent.replace(/{{PROJECT_NAME}}/g, projectName);
5151
- const BOM = "\uFEFF";
5152
- fs12.writeFileSync(outputFile, BOM + initialContent, { encoding: "utf-8" });
5153
- tui.log.success(`\u2705 Created: ${colors.bold("_sharkrc/tech-spec.md")}`);
5154
- } else {
5155
- tui.log.info(`\u{1F4C4} Using existing ${colors.bold("_sharkrc/tech-spec.md")}`);
5156
- }
5157
- let contextContent = "";
5158
- const contextPath = path13.resolve(projectRoot, "_sharkrc", "project-context.md");
5159
- if (fs12.existsSync(contextPath)) {
5160
- contextContent = fs12.readFileSync(contextPath, "utf-8");
5161
- tui.log.info(`\u{1F4D8} Context loaded.`);
5162
- }
5163
- let briefingContent = "";
5164
- if (options.briefingPath && fs12.existsSync(options.briefingPath)) {
5165
- briefingContent = fs12.readFileSync(options.briefingPath, "utf-8");
5166
- tui.log.info(`\u{1F4C4} Briefing loaded from: ${colors.dim(options.briefingPath)}`);
5167
- } else {
5168
- const standardBriefing = path13.resolve(projectRoot, "_sharkrc", "briefing.md");
5169
- if (fs12.existsSync(standardBriefing)) {
5170
- briefingContent = fs12.readFileSync(standardBriefing, "utf-8");
5171
- tui.log.info(`\u{1F4C4} Briefing loaded.`);
5172
- }
5173
- }
5174
- let initialPrompt = `
5175
- Voc\xEA \xE9 o **Shark Spec**, um Arquiteto de Software S\xEAnior e Tech Lead.
5176
- Seu objetivo final \xE9 produzir uma especifica\xE7\xE3o t\xE9cnica precisa para a tarefa no arquivo \`_sharkrc/tech-spec.md\`.
5177
-
5178
- \u26A0\uFE0F O SEU WORKFLOW \xC9 GUIADO POR FASES.
5179
- N\xC3O TENTE ADIANTAR O TRABALHO (ex: investigar c\xF3digo ou preencher template agora).
4331
+ document.getElementById('btnBoxes').addEventListener('click', () => {
4332
+ currentMode = 'boxes';
4333
+ lastDataString = '';
4334
+ document.getElementById('btnBoxes').className = 'px-4 py-1.5 rounded-md text-xs font-medium bg-blue-600 text-white transition-all';
4335
+ document.getElementById('btnTimeline').className = 'px-4 py-1.5 rounded-md text-xs font-medium text-gray-400 hover:text-white transition-all';
4336
+ fetchGraphData();
4337
+ });
5180
4338
 
5181
- **VOC\xCA EST\xC1 NA FASE 1: ENTENDIMENTO DA TAREFA**
5182
- - Use \`talk_with_user\` para perguntar ao usu\xE1rio qual tarefa espec\xEDfica, funcionalidade ou bug ele precisa especificar.
5183
- - Confirme o escopo e os limites com o usu\xE1rio.
5184
- - Se o escopo estiver perfeitamente claro e confirmado (com o usu\xE1rio), emita "PHASE_COMPLETED" no campo "summary" do JSON para avan\xE7ar.
4339
+ document.getElementById('btnTimeline').addEventListener('click', () => {
4340
+ currentMode = 'timeline';
4341
+ lastDataString = '';
4342
+ document.getElementById('btnTimeline').className = 'px-4 py-1.5 rounded-md text-xs font-medium bg-blue-600 text-white transition-all';
4343
+ document.getElementById('btnBoxes').className = 'px-4 py-1.5 rounded-md text-xs font-medium text-gray-400 hover:text-white transition-all';
4344
+ fetchGraphData();
4345
+ });
5185
4346
 
5186
- IMPORTANTE: Toda a sua comunica\xE7\xE3o DEVE ser em Portugu\xEAs.
5187
- `;
5188
- if (briefingContent) {
5189
- initialPrompt += `
5190
- \u2139\uFE0F Um documento de briefing foi encontrado. Ele define parcialmente a tarefa para a Fase 1.
5191
- Confirme seu entendimento com o usu\xE1rio via \`talk_with_user\` antes de prosseguir para a Fase 2.
4347
+ document.getElementById('searchInput').addEventListener('input', (e) => {
4348
+ const val = e.target.value.toLowerCase();
4349
+ if (!val) return;
4350
+ const node = graphData.nodes.find(n => n.label.toLowerCase().includes(val));
4351
+ if (node && network) {
4352
+ network.focus(node.id, { scale: 1.2, animation: true });
4353
+ showSidebar(node.id);
4354
+ }
4355
+ });
5192
4356
 
5193
- --- BRIEFING ---
5194
- ${briefingContent}
5195
- ----------------
5196
- `;
5197
- } else {
5198
- initialPrompt += `
5199
- \u2139\uFE0F Nenhum documento de briefing foi encontrado. Inicie a Fase 1 imediatamente: use \`talk_with_user\` para perguntar ao usu\xE1rio o que precisa ser especificado.
5200
- `;
5201
- }
5202
- if (options.initialContext) {
5203
- initialPrompt += `
5204
- --- CONTEXTO DE EXECU\xC7\xC3O ANTERIOR (HANDOVER/FEEDBACK) ---
5205
- ${options.initialContext}
5206
- -----------------------------------------------------
4357
+ fetchGraphData();
4358
+ setInterval(fetchGraphData, 3000);
4359
+ </script>
4360
+ </body>
4361
+ </html>
5207
4362
  `;
5208
- }
5209
- if (contextContent) {
5210
- initialPrompt += `
5211
- \u2139\uFE0F O contexto do projeto est\xE1 dispon\xEDvel para refer\xEAncia. Use-o na Fase 2 para se alinhar com os padr\xF5es de arquitetura existentes, mas N\xC3O o use para preencher as se\xE7\xF5es de forma gen\xE9rica.
5212
4363
 
5213
- --- PROJECT CONTEXT ---
5214
- ${contextContent}
5215
- -----------------------
5216
- `;
4364
+ // src/commands/graph.ts
4365
+ var graphCommand = new Command6("graph").description("Visualize episodic memory and trace graphs in your browser").addArgument(new Argument("[mode]", "Default view mode (boxes or timeline)").choices(["boxes", "timeline"]).default("boxes")).option("-p, --port <number>", "Port to host the server", "4200").action(async (mode, options) => {
4366
+ let port = parseInt(options.port, 10);
4367
+ if (isNaN(port)) {
4368
+ console.error(colors.error("\u274C Invalid port number."));
4369
+ process.exit(1);
5217
4370
  }
5218
- await runSpecLoop(initialPrompt.trim(), outputFile, options.agentId);
5219
- }
5220
- async function runSpecLoop(initialMessage, targetPath, overrideAgentId) {
5221
- let nextPrompt = initialMessage;
5222
- let keepGoing = true;
5223
- let stepCount = 0;
5224
- let currentPhase = 1;
5225
- const MAX_STEPS = 30;
5226
- while (keepGoing && stepCount < MAX_STEPS) {
5227
- stepCount++;
5228
- const spinner = tui.spinner();
5229
- spinner.start(`\u{1F3D7}\uFE0F Spec Agent working (Step ${stepCount}/${MAX_STEPS})...`);
5230
- let pendingSections = [];
5231
- if (fs12.existsSync(targetPath)) {
5232
- const content = fs12.readFileSync(targetPath, "utf-8");
5233
- if (content.includes("[TO BE ANALYZED]")) pendingSections.push("Analysis Sections (Stack, Arch, Data, API)");
5234
- if (content.includes("[TO BE FILLED")) pendingSections.push("Implementation Steps");
4371
+ let activeNodes = [];
4372
+ let activeNodesTimestamp = 0;
4373
+ const server = http.createServer(async (req, res) => {
4374
+ const url = new URL(req.url || "", `http://localhost:${port}`);
4375
+ if (url.pathname === "/") {
4376
+ res.writeHead(200, { "Content-Type": "text/html; charset=utf-8" });
4377
+ res.end(GRAPH_HTML_TEMPLATE);
4378
+ return;
5235
4379
  }
5236
- if (pendingSections.length === 0 && stepCount > 1) {
4380
+ if (req.method === "POST" && url.pathname === "/api/active-nodes") {
4381
+ let body = "";
4382
+ req.on("data", (chunk) => {
4383
+ body += chunk;
4384
+ });
4385
+ req.on("end", () => {
4386
+ try {
4387
+ const data = JSON.parse(body);
4388
+ activeNodes = data.nodeIds || [];
4389
+ activeNodesTimestamp = Date.now();
4390
+ res.writeHead(200, { "Content-Type": "application/json" });
4391
+ res.end(JSON.stringify({ success: true }));
4392
+ } catch {
4393
+ res.writeHead(400);
4394
+ res.end("Bad Request");
4395
+ }
4396
+ });
4397
+ return;
5237
4398
  }
5238
- let responseText = "";
5239
- let lastResponse = null;
5240
- try {
5241
- lastResponse = await callSpecAgentApi(nextPrompt, (chunk) => {
5242
- responseText += chunk;
5243
- }, overrideAgentId);
5244
- spinner.stop("Response received");
5245
- if (lastResponse && lastResponse.actions) {
5246
- let executionResults = "";
5247
- let waitingForUser = false;
5248
- let specUpdated = false;
5249
- let hasSystemError = false;
5250
- let systemErrorContent = "";
5251
- for (const action of lastResponse.actions) {
5252
- if (action.type === "talk_with_user") {
5253
- const isSystemError = action.content?.startsWith("[SYSTEM ERROR]");
5254
- if (isSystemError) {
5255
- tui.log.error(`\u26A0\uFE0F Detectado erro na resposta do Agente (truncado ou inv\xE1lido).`);
5256
- tui.log.info(colors.dim(action.content || ""));
5257
- const approved = await tui.confirm({ message: `Enviar notifica\xE7\xE3o de erro para o agente tentar se recuperar automaticamente?` });
5258
- if (approved) {
5259
- hasSystemError = true;
5260
- systemErrorContent = action.content || "";
5261
- } else {
5262
- const userReply = await tui.text({ message: "Seu prompt alternativo para o agente:" });
5263
- if (tui.isCancel(userReply)) {
5264
- keepGoing = false;
5265
- return;
4399
+ if (url.pathname === "/api/graph") {
4400
+ const graphMode = url.searchParams.get("mode") || "boxes";
4401
+ const runDir = path11.join(process.cwd(), ".shark", "membox");
4402
+ const boxesFile = path11.join(runDir, "boxes.jsonl");
4403
+ const tracesFile = path11.join(runDir, "traces.jsonl");
4404
+ const nodes = [];
4405
+ const edges = [];
4406
+ try {
4407
+ if (fs11.existsSync(boxesFile)) {
4408
+ const fileContent = await fs11.promises.readFile(boxesFile, "utf-8");
4409
+ const lines = fileContent.trim().split("\n").filter(Boolean);
4410
+ lines.forEach((line) => {
4411
+ try {
4412
+ const box = JSON.parse(line);
4413
+ if (graphMode === "boxes") {
4414
+ const history = [];
4415
+ if (box.content_text) {
4416
+ const turns = box.content_text.split(/\n(?=user:|assistant:)/i);
4417
+ turns.forEach((turn) => {
4418
+ const match = turn.match(/^(user|assistant):\s*([\s\S]*)$/i);
4419
+ if (match) {
4420
+ const role = match[1].toLowerCase();
4421
+ let content = match[2].trim();
4422
+ if (content.startsWith("{")) {
4423
+ try {
4424
+ const parsed = JSON.parse(content);
4425
+ if (parsed.action && parsed.action.content) {
4426
+ content = parsed.action.content;
4427
+ } else if (parsed.summary) {
4428
+ content = parsed.summary;
4429
+ }
4430
+ } catch {
4431
+ }
4432
+ }
4433
+ history.push({ role, content: content.replace(/\n/g, "<br>") });
4434
+ } else {
4435
+ history.push({ role: "log", content: turn.trim().replace(/\n/g, "<br>") });
4436
+ }
4437
+ });
5266
4438
  }
5267
- hasSystemError = true;
5268
- systemErrorContent = userReply;
5269
- }
5270
- } else {
5271
- tui.log.info(colors.primary("\u{1F916} Architect:"));
5272
- console.log(action.content);
5273
- waitingForUser = true;
5274
- }
5275
- } else if (action.type === "list_files") {
5276
- tui.log.info(`\u{1F4C2} Scanning: ${colors.dim(action.path || ".")}`);
5277
- const result = handleListFiles(action.path || ".");
5278
- executionResults += `[Action list_files(${action.path}) Result]:
5279
- ${result}
5280
-
5281
- `;
5282
- } else if (action.type === "read_file") {
5283
- tui.log.info(`\u{1F4D6} Reading: ${colors.dim(action.path || "")}`);
5284
- const result = handleReadFile(action.path || "");
5285
- executionResults += `[Action read_file(${action.path}) Result]:
5286
- ${result}
5287
-
5288
- `;
5289
- } else if (action.type === "search_file") {
5290
- tui.log.info(`\u{1F50D} Searching: ${colors.dim(action.path || "")}`);
5291
- const result = handleSearchFile(action.path || "");
5292
- executionResults += `[Action search_file(${action.path}) Result]:
5293
- ${result}
5294
-
5295
- `;
5296
- } else if (action.type === "search_code") {
5297
- const glob = action.path || "src/**/*";
5298
- const query = action.query || "";
5299
- const isRegex = action.is_regex === true;
5300
- tui.log.info(`\u{1F50E} Search code: ${colors.dim(`"${query}" in ${glob}`)}`);
5301
- const result = handleSearchCode(glob, query, isRegex);
5302
- executionResults += `[Action search_code("${query}" in "${glob}") Result]:
5303
- ${result}
5304
-
5305
- `;
5306
- } else if (["create_file", "modify_file"].includes(action.type)) {
5307
- let actionPath = path13.resolve(action.path || "");
5308
- const resolvedTargetPath = path13.resolve(targetPath);
5309
- let isTarget = actionPath === resolvedTargetPath;
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)}`);
5312
- action.path = targetPath;
5313
- actionPath = resolvedTargetPath;
5314
- isTarget = true;
5315
- }
5316
- if (!isTarget && action.type === "create_file") {
5317
- const confirm = await tui.confirm({ message: `Agent wants to create ${action.path}. Allow?` });
5318
- if (!confirm) {
5319
- executionResults += `[Action create_file]: User denied.
5320
- `;
5321
- continue;
4439
+ nodes.push({
4440
+ id: `box_${box.box_id}`,
4441
+ label: box.features?.topic || `Topic ${box.box_id}`,
4442
+ type: "box",
4443
+ size: 15 + Math.min((box.features?.events?.length || 0) * 2, 20),
4444
+ details: {
4445
+ topic: box.features?.topic,
4446
+ keywords: box.features?.keywords,
4447
+ history
4448
+ }
4449
+ });
4450
+ } else {
4451
+ box.features?.events?.forEach((ev, idx) => {
4452
+ nodes.push({
4453
+ id: `ev_${box.box_id}_${idx}`,
4454
+ label: ev.length > 30 ? ev.substring(0, 30) + "..." : ev,
4455
+ type: "event",
4456
+ details: { event: ev }
4457
+ });
4458
+ });
5322
4459
  }
4460
+ } catch (e) {
5323
4461
  }
4462
+ });
4463
+ }
4464
+ if (fs11.existsSync(tracesFile)) {
4465
+ const fileContent = await fs11.promises.readFile(tracesFile, "utf-8");
4466
+ const lines = fileContent.trim().split("\n").filter(Boolean);
4467
+ lines.forEach((line) => {
5324
4468
  try {
5325
- if (action.type === "create_file") {
5326
- const BOM = "\uFEFF";
5327
- fs12.writeFileSync(action.path, BOM + (action.content || ""), "utf-8");
5328
- tui.log.success(`\u2705 Created: ${action.path}`);
5329
- executionResults += `[Action create_file]: Success.
5330
- `;
5331
- } else if (action.type === "modify_file") {
5332
- if (action.target_content) {
5333
- const success = startSmartReplace(action.path, action.content || "", action.target_content, tui);
5334
- if (success) {
5335
- executionResults += `[Action modify_file]: Success.
5336
- `;
5337
- specUpdated = true;
5338
- } else {
5339
- executionResults += `[Action modify_file]: Failed. Target content not found or ambiguous.
5340
- `;
4469
+ const trace = JSON.parse(line);
4470
+ if (graphMode === "boxes") {
4471
+ const boxIds = trace.box_ids || [];
4472
+ const entries = trace.entries || [];
4473
+ for (let i = 0; i < boxIds.length - 1; i++) {
4474
+ const entry = entries.find((e) => e.box_id === boxIds[i + 1]);
4475
+ edges.push({
4476
+ from: `box_${boxIds[i]}`,
4477
+ to: `box_${boxIds[i + 1]}`,
4478
+ label: `Trace ${trace.trace_id}`,
4479
+ arrows: "to",
4480
+ similarity: entry?.similarity ?? 1
4481
+ });
4482
+ }
4483
+ } else {
4484
+ const traceEventIds = [];
4485
+ const entries = trace.entries || [];
4486
+ const eventSimilarities = [];
4487
+ entries.forEach((entry) => {
4488
+ if (entry?.events) {
4489
+ entry.events.forEach((_, idx) => {
4490
+ traceEventIds.push(`ev_${entry.box_id}_${idx}`);
4491
+ eventSimilarities.push(entry.similarity ?? 1);
4492
+ });
5341
4493
  }
5342
- } else {
5343
- executionResults += `[Action modify_file]: Failed. 'target_content' is required.
5344
- `;
4494
+ });
4495
+ for (let i = 0; i < traceEventIds.length - 1; i++) {
4496
+ edges.push({
4497
+ from: traceEventIds[i],
4498
+ to: traceEventIds[i + 1],
4499
+ label: `Trace ${trace.trace_id}`,
4500
+ arrows: "to",
4501
+ similarity: eventSimilarities[i + 1] ?? 1
4502
+ });
5345
4503
  }
5346
4504
  }
5347
4505
  } catch (e) {
5348
- executionResults += `[Action ${action.type}]: Error: ${e.message}
5349
- `;
5350
4506
  }
5351
- }
5352
- }
5353
- if (lastResponse.message && lastResponse.message.includes("PHASE_COMPLETED")) {
5354
- const extraContext = executionResults ? `
5355
-
5356
- Resultados das \xFAltimas a\xE7\xF5es executadas antes da conclus\xE3o:
5357
- ${executionResults}` : "";
5358
- if (currentPhase === 1) {
5359
- currentPhase = 2;
5360
- tui.log.success(`\u2705 Fase 1 Conclu\xEDda. Iniciando Fase 2 (Investiga\xE7\xE3o).`);
5361
- nextPrompt = `[System Message]
5362
- Voc\xEA completou a FASE 1 com sucesso.
5363
-
5364
- **VOC\xCA AGORA EST\xC1 NA FASE 2: INVESTIGA\xC7\xC3O**
5365
- - Use \`search_code\` e \`list_files\` para explorar os arquivos relevantes \xE0 tarefa.
5366
- - Prefira \`search_code\` em vez de \`read_file\` para buscar c\xF3digo sem inflar o contexto.
5367
- - N\xC3O leia o projeto inteiro de forma gen\xE9rica.
5368
- - REGRA DE OURO (READ-FIRST): Voc\xEA N\xC3O PODE referenciar um arquivo na especifica\xE7\xE3o t\xE9cnica que n\xE3o tenha investigado nesta fase.
5369
- - Quando achar que possui toda a clareza t\xE9cnica sobre onde e o que deve ser feito no c\xF3digo, emita "PHASE_COMPLETED" no summary.${extraContext}`;
5370
- continue;
5371
- } else if (currentPhase === 2) {
5372
- currentPhase = 3;
5373
- tui.log.success(`\u2705 Fase 2 Conclu\xEDda. Iniciando Fase 3 (Preenchimento).`);
5374
- nextPrompt = `[System Message]
5375
- Voc\xEA completou a FASE 2 com sucesso.
5376
-
5377
- **VOC\xCA AGORA EST\xC1 NA FASE 3: PREENCHIMENTO DO TEMPLATE**
5378
- - Use \`modify_file\` no arquivo \`${targetPath}\` para substituir os placeholders pelo conte\xFAdo real levantado na fase de investiga\xE7\xE3o.
5379
- - As se\xE7\xF5es 1-4 devem descrever o contexto da TAREFA, e n\xE3o o projeto como um todo.
5380
- - Passos de Implementa\xE7\xE3o (Implementation Steps): APENAS checkboxes markdown: \`- [ ] [Verbo de A\xE7\xE3o] [O Que] em [Caminho Relativo]\`.
5381
- - Quando TODOS os placeholders ([TO BE ANALYZED...] ou [TO BE FILLED]) forem substitu\xEDdos e o trabalho conclu\xEDdo, emita "SPEC_UPDATED: Complete" no summary para finalizar.${extraContext}`;
5382
- continue;
5383
- }
5384
- }
5385
- if (lastResponse.message && lastResponse.message.includes("SPEC_UPDATED:")) {
5386
- if (currentPhase < 3) {
5387
- tui.log.warning(`O agente tentou finalizar prematuramente. For\xE7ando retorno para a fase atual...`);
5388
- nextPrompt = `[System Error]: Voc\xEA tentou finalizar a especifica\xE7\xE3o prematuramente emitindo SPEC_UPDATED, mas ainda est\xE1 na Fase ${currentPhase}. Voc\xEA s\xF3 pode finalizar quando estiver na Fase 3.
5389
-
5390
- Continue seu trabalho na Fase ${currentPhase} ou emita "PHASE_COMPLETED" se terminou esta etapa atual.`;
5391
- continue;
5392
- }
5393
- const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
5394
- if (content.includes("[TO BE")) {
5395
- const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
5396
- let missing = pendingMatches.length > 0 ? pendingMatches.join(", ") : "algumas se\xE7\xF5es";
5397
- tui.log.warning(`O agente tentou concluir prematuramente, mas h\xE1 placeholders pendentes. For\xE7ando retorno...`);
5398
- nextPrompt = `[System Error]: A valida\xE7\xE3o falhou e o bloqueio autom\xE1tico foi acionado.
5399
- Voc\xEA tentou concluir a tarefa, mas o arquivo AINDA possui placeholders '[TO BE ANALYZED...]' ou '[TO BE FILLED]'.
5400
- As seguintes se\xE7\xF5es ainda cont\xEAm estes placeholders: ${missing}.
5401
- Voc\xEA \xE9 OBRIGADO a usar a action \`modify_file\` para preencher o conte\xFAdo de cada uma dessas se\xE7\xF5es. Use o placeholder exato no campo \`target_content\`. N\xC3O repita a conclus\xE3o da tarefa at\xE9 corrigir todas as pend\xEAncias.`;
5402
- continue;
5403
- } else {
5404
- const updateSummary = lastResponse.message.split("SPEC_UPDATED:")[1].trim();
5405
- tui.log.success(`\u2705 Spec Finalized: ${updateSummary}`);
5406
- return;
5407
- }
5408
- }
5409
- if (executionResults) {
5410
- FileLogger.log("TOOL_EXECUTION", "Specification Agent Actions Output", { executionResults });
5411
- }
5412
- if (hasSystemError) {
5413
- nextPrompt = systemErrorContent;
5414
- } else if (waitingForUser) {
5415
- const userReply = await tui.text({ message: "Your answer", placeholder: "Type your answer..." });
5416
- if (tui.isCancel(userReply)) {
5417
- keepGoing = false;
5418
- return;
5419
- }
5420
- FileLogger.log("USER_INPUT", "User response to specification agent", { userReply });
5421
- nextPrompt = `${executionResults}
5422
-
5423
- User Reply: ${userReply}`;
5424
- } else if (executionResults) {
5425
- const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
5426
- let systemMsg = "Execu\xE7\xE3o da ferramenta conclu\xEDda.";
5427
- if (specUpdated) {
5428
- if (content.includes("[TO BE")) {
5429
- const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
5430
- let missing = pendingMatches.length > 0 ? pendingMatches.join(", ") : "v\xE1rias se\xE7\xF5es";
5431
- systemMsg += `
5432
- [System]: Se\xE7\xE3o atualizada com sucesso. A valida\xE7\xE3o detectou que AINDA H\xC1 placeholders pendentes ('[TO BE...]') nas seguintes se\xE7\xF5es: ${missing}.
5433
- Por favor, envie uma nova action \`modify_file\` focada em uma destas se\xE7\xF5es obrigatoriamente. USE o respectivo placeholder no campo \`target_content\` para que o replace funcione.`;
5434
- } else {
5435
- systemMsg += "\n[System]: O arquivo parece completo! Se estiver satisfeito e possuir TODAS as implementa\xE7\xF5es descritas, retorne 'SPEC_UPDATED: Complete'.";
5436
- }
5437
- } else {
5438
- systemMsg += "\n[System]: A modifica\xE7\xE3o do arquivo falhou. Verifique se o `target_content` que voc\xEA usou existe EXATAMENTE como no arquivo e se ele \xE9 \xDANICO na hora de usar a action `modify_file`.";
5439
- }
5440
- nextPrompt = `${executionResults}
5441
-
5442
- ${systemMsg}`;
5443
- } else {
5444
- if (lastResponse.message) {
5445
- tui.log.info(colors.primary("\u{1F916} Architect (Message only):"));
5446
- console.log(lastResponse.message);
5447
- const userReply = await tui.text({ message: "Your answer:" });
5448
- if (tui.isCancel(userReply)) {
5449
- keepGoing = false;
5450
- break;
5451
- }
5452
- FileLogger.log("USER_INPUT", "User response to specification agent (Message only)", { userReply });
5453
- nextPrompt = userReply;
5454
- } else {
5455
- keepGoing = false;
5456
- }
4507
+ });
5457
4508
  }
5458
- } else {
5459
- tui.log.warning("No actions received.");
5460
- keepGoing = false;
4509
+ } catch (e) {
4510
+ console.error("Error parsing memory graph files:", e);
5461
4511
  }
5462
- } catch (error) {
5463
- spinner.stop("Error");
5464
- tui.log.error(error.message);
5465
- keepGoing = false;
4512
+ const activeNodeList = Date.now() - activeNodesTimestamp < 15e3 ? activeNodes : [];
4513
+ res.writeHead(200, { "Content-Type": "application/json" });
4514
+ res.end(JSON.stringify({ nodes, edges, activeNodes: activeNodeList }));
4515
+ return;
5466
4516
  }
5467
- }
5468
- }
5469
- async function callSpecAgentApi(prompt, onChunk, agentId) {
5470
- const conversationId = await conversationManager.getConversationId(AGENT_TYPE2);
5471
- FileLogger.log("AGENT", "Calling Agent API", { agentId, conversationId });
5472
- const provider = ProviderResolver.getProvider("specification_agent");
5473
- if (agentId && "agentId" in provider) {
5474
- provider.agentId = agentId;
5475
- }
5476
- const parsed = await provider.streamChat(prompt, {
5477
- conversationId,
5478
- agentType: "specification_agent",
5479
- onChunk
4517
+ res.writeHead(404, { "Content-Type": "text/plain" });
4518
+ res.end("Not Found");
5480
4519
  });
5481
- if (parsed.conversation_id) {
5482
- await conversationManager.saveConversationId(AGENT_TYPE2, parsed.conversation_id);
5483
- }
5484
- return parsed;
5485
- }
5486
-
5487
- // src/commands/legacy.ts
5488
- var legacyCommand = new Command3("legacy").description("Starts the Legacy Developer Agent task orchestration loop").option("-t, --task <type>", "Initial task description (Quick Mode)").option("-c, --context <path>", "Path to custom context file").action(async (options) => {
5489
- const taskManager = new TaskManager(process.cwd());
5490
- let state = taskManager.analyzeSpecState();
5491
- if (state.status === "MISSING") {
5492
- tui.log.warning("\u{1F4CB} No tech-spec.md found.");
5493
- const confirm = await tui.confirm({ message: "Create a new Specification (Tech Spec)?" });
5494
- if (confirm) {
5495
- await interactiveSpecificationAgent({
5496
- briefingPath: options.task ? void 0 : void 0
5497
- });
5498
- state = taskManager.analyzeSpecState();
5499
- if (state.status === "MISSING") {
5500
- tui.log.error("\u274C Spec creation aborted or failed.");
5501
- return;
4520
+ const startServer = (p) => {
4521
+ server.listen(p, () => {
4522
+ const url = `http://localhost:${p}/?mode=${mode}`;
4523
+ console.log(colors.success(`\u{1F680} Memory Graph Server active at: ${url}`));
4524
+ const runDir = path11.join(process.cwd(), ".shark", "membox");
4525
+ if (!fs11.existsSync(runDir)) {
4526
+ fs11.mkdirSync(runDir, { recursive: true });
5502
4527
  }
5503
- } else {
5504
- return;
5505
- }
5506
- }
5507
- let keepOrchestrating = true;
5508
- let burnMode = false;
5509
- let contextHistory = "";
5510
- tui.intro("\u{1F988} Shark Legacy Orchestrator");
5511
- while (keepOrchestrating) {
5512
- state = taskManager.analyzeSpecState();
5513
- if (state.status === "COMPLETED") {
5514
- tui.log.success("\u{1F389} All tasks in tech-spec.md are COMPLETED!");
5515
- const choice = await tui.select({
5516
- message: "What next?",
5517
- options: [
5518
- { value: "exit", label: "Exit" },
5519
- { value: "new_spec", label: "New Specification (Reset)" }
5520
- ]
5521
- });
5522
- if (choice === "new_spec") {
5523
- await interactiveSpecificationAgent();
5524
- contextHistory = "";
5525
- continue;
4528
+ fs11.writeFileSync(
4529
+ path11.join(runDir, "graph-server.json"),
4530
+ JSON.stringify({ active: true, port: p, timestamp: Date.now() }),
4531
+ "utf-8"
4532
+ );
4533
+ if (process.platform === "win32") {
4534
+ exec3(`start "" "${url}"`);
4535
+ } else if (process.platform === "darwin") {
4536
+ exec3(`open "${url}"`);
5526
4537
  } else {
5527
- keepOrchestrating = false;
5528
- break;
4538
+ exec3(`xdg-open "${url}"`);
5529
4539
  }
5530
- }
5531
- const currentTask = state.nextTask;
5532
- if (!currentTask) {
5533
- tui.log.error("Something went wrong. Status is not completed but no next task found.");
5534
- break;
5535
- }
5536
- tui.log.info(`
5537
- \u{1F449} **NEXT TASK**: ${colors.bold(currentTask.description)}`);
5538
- if (!burnMode) {
5539
- const action = await tui.select({
5540
- message: "Orchestration Checkpoint:",
5541
- options: [
5542
- { value: "execute", label: "\u{1F680} Execute Task (Start)" },
5543
- { value: "burn", label: "\u{1F525} Burn Mode (Auto-Execute Remaining)" },
5544
- { value: "pivot", label: "\u{1F527} Pivot/Correct (Edit Spec)" },
5545
- { value: "skip", label: "\u23ED\uFE0F Skip Task (Mark Done without Executing)" },
5546
- { value: "stop", label: "\u{1F6D1} Stop Session" }
5547
- ]
5548
- });
5549
- if (action === "stop") {
5550
- keepOrchestrating = false;
5551
- break;
5552
- } else if (action === "burn") {
5553
- burnMode = true;
5554
- } else if (action === "skip") {
5555
- taskManager.markTaskAsDone(currentTask.id);
5556
- tui.log.info("Task skipped.");
5557
- continue;
5558
- } else if (action === "pivot") {
5559
- tui.log.info("Transferring control to Specification Agent...");
5560
- await interactiveSpecificationAgent({
5561
- initialContext: `User requested pivot after tasks:
5562
- ${contextHistory}`
5563
- });
5564
- continue;
5565
- }
5566
- }
5567
- taskManager.markTaskInProgress(currentTask.id);
5568
- tui.log.info(`\u26A1 Starting Micro-Context for Task: "${currentTask.description}"`);
5569
- const result = await interactiveDeveloperAgent2({
5570
- taskId: currentTask.id,
5571
- taskInstruction: currentTask.description,
5572
- history: contextHistory,
5573
- context: options.context
5574
4540
  });
5575
- if (result.success) {
5576
- tui.log.success(`\u2705 Task Completed: ${currentTask.description}`);
5577
- taskManager.markTaskAsDone(currentTask.id);
5578
- contextHistory += `
5579
- [Task "${currentTask.description}" completed]: ${result.summary}`;
4541
+ };
4542
+ server.on("error", (err) => {
4543
+ if (err.code === "EADDRINUSE") {
4544
+ console.log(colors.secondary(`\u26A0\uFE0F Port ${port} is busy. Retrying next port...`));
4545
+ port++;
4546
+ startServer(port);
5580
4547
  } else {
5581
- tui.log.error(`\u274C Task Failed: ${result.summary}`);
5582
- burnMode = false;
5583
- const recovery = await tui.select({
5584
- message: "Task Failed. Recovery Action:",
5585
- options: [
5586
- { value: "retry", label: "Retry (Run Agent Again)" },
5587
- { value: "pivot", label: "Pivot (Adjust Spec/Instructions)" },
5588
- { value: "ignore", label: "Ignore (Mark Done anyway)" },
5589
- { value: "stop", label: "Stop" }
5590
- ]
5591
- });
5592
- if (recovery === "stop") break;
5593
- if (recovery === "ignore") taskManager.markTaskAsDone(currentTask.id);
5594
- if (recovery === "pivot") {
5595
- await interactiveSpecificationAgent({
5596
- initialContext: `Task "${currentTask.description}" FAILED.
5597
- Error: ${result.summary}
5598
- History:
5599
- ${contextHistory}`
5600
- });
5601
- }
4548
+ console.error(colors.error("\u274C Server startup error:"), err);
5602
4549
  }
5603
- }
5604
- tui.outro("\u{1F988} Legacy Orchestration Finished.");
5605
- });
5606
-
5607
- // src/commands/export-schema.ts
5608
- import { Command as Command4 } from "commander";
5609
- var exportSchemaCommand = new Command4("export-schema").description("Outputs the agent response JSON Schema").action(() => {
5610
- console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
5611
- });
5612
-
5613
- // src/commands/export-prompt.ts
5614
- import { Command as Command5 } from "commander";
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";
5625
- var superCommandAction = async (options = {}) => {
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");
5631
- try {
4550
+ });
4551
+ const cleanup = () => {
5632
4552
  try {
5633
- await fs13.rm(targetPath, { recursive: true, force: true });
4553
+ const runDir = path11.join(process.cwd(), ".shark", "membox");
4554
+ const file = path11.join(runDir, "graph-server.json");
4555
+ if (fs11.existsSync(file)) {
4556
+ fs11.unlinkSync(file);
4557
+ }
5634
4558
  } catch {
5635
4559
  }
5636
- await fs13.mkdir(targetPath, { recursive: true });
5637
- await fs13.cp(internalSkillsPath, targetPath, { recursive: true, force: true });
5638
- console.log(`\u{1F680} Superpowers skills installed successfully to ${targetPath}`);
5639
- } catch (error) {
5640
- console.error(`\u274C Failed to install superpowers skills: ${error.message}`);
5641
- process.exit(1);
5642
- }
5643
- };
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);
4560
+ };
4561
+ process.on("exit", cleanup);
4562
+ process.on("SIGINT", () => {
4563
+ cleanup();
4564
+ process.exit(0);
4565
+ });
4566
+ process.on("SIGTERM", () => {
4567
+ cleanup();
4568
+ process.exit(0);
4569
+ });
4570
+ startServer(port);
4571
+ });
5645
4572
 
5646
4573
  // src/bin/shark.ts
5647
4574
  crashHandler.init();
@@ -5650,10 +4577,10 @@ program.name("shark").description("Shark CLI: AI-Native Collaborative Developmen
5650
4577
  program.addCommand(loginCommand);
5651
4578
  program.addCommand(initCommand);
5652
4579
  program.addCommand(devCommand);
5653
- program.addCommand(legacyCommand);
5654
4580
  program.addCommand(exportSchemaCommand);
5655
4581
  program.addCommand(exportPromptCommand);
5656
4582
  program.addCommand(superCommand);
4583
+ program.addCommand(graphCommand);
5657
4584
  program.command("config").description("Manage global configuration").action(configCommand.action);
5658
4585
  process.on("unhandledRejection", (err) => {
5659
4586
  console.error(colors.error("\u274C Unhandled Error:"), err);