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 +1197 -2270
- package/dist/bin/shark.js.map +1 -1
- package/dist/{chunk-NKE5GNDJ.js → chunk-LQ2PEYRD.js} +172 -75
- package/dist/chunk-LQ2PEYRD.js.map +1 -0
- package/dist/chunk-WJUQF54U.js +752 -0
- package/dist/chunk-WJUQF54U.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/membox-manager-ZDKXDZPF.js +8 -0
- package/dist/membox-manager-ZDKXDZPF.js.map +1 -0
- package/package.json +4 -4
- package/skills/writing-plans/SKILL.md +1 -15
- package/skills/writing-plans/plan-document-reviewer-prompt.md +1 -1
- package/dist/chunk-NKE5GNDJ.js.map +0 -1
package/dist/bin/shark.js
CHANGED
|
@@ -8,7 +8,10 @@ import {
|
|
|
8
8
|
loginCommand,
|
|
9
9
|
tokenStorage,
|
|
10
10
|
tui
|
|
11
|
-
} from "../chunk-
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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": {
|
|
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
|
-
|
|
934
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1002
|
-
|
|
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}
|
|
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:
|
|
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 ||
|
|
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
|
|
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 ||
|
|
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:
|
|
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
|
|
1324
|
-
|
|
1325
|
-
|
|
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
|
|
1497
|
-
import
|
|
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 =
|
|
2613
|
+
const absolutePath = path5.resolve(filePath);
|
|
2513
2614
|
let lineStates = this.cache.get(absolutePath);
|
|
2514
2615
|
if (!lineStates) {
|
|
2515
|
-
const content =
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
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
|
|
2603
|
-
import
|
|
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
|
|
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 =
|
|
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 =
|
|
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
|
-
|
|
3236
|
-
|
|
3237
|
-
|
|
3238
|
-
|
|
3239
|
-
|
|
3240
|
-
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
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
|
-
|
|
3253
|
-
|
|
3254
|
-
|
|
2851
|
+
isSubagentActive(id) {
|
|
2852
|
+
return this.subagents.get(id)?.status === "running";
|
|
2853
|
+
}
|
|
2854
|
+
hasSubagent(id) {
|
|
2855
|
+
return this.subagents.has(id);
|
|
3255
2856
|
}
|
|
3256
|
-
|
|
3257
|
-
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
3261
|
-
if (
|
|
3262
|
-
|
|
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
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
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
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
const
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
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
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
})
|
|
3332
|
-
|
|
3333
|
-
|
|
3334
|
-
|
|
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
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
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
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
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 =
|
|
3708
|
-
if (!
|
|
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 =
|
|
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-${
|
|
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
|
|
3761
|
-
const
|
|
3762
|
-
let packageRoot =
|
|
2988
|
+
const __filename3 = fileURLToPath(import.meta.url);
|
|
2989
|
+
const __dirname3 = path7.dirname(__filename3);
|
|
2990
|
+
let packageRoot = __dirname3;
|
|
3763
2991
|
while (true) {
|
|
3764
|
-
const pkgPath =
|
|
3765
|
-
if (
|
|
2992
|
+
const pkgPath = path7.join(packageRoot, "package.json");
|
|
2993
|
+
if (fs7.existsSync(pkgPath)) {
|
|
3766
2994
|
try {
|
|
3767
|
-
const pkg = JSON.parse(
|
|
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 =
|
|
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 =
|
|
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 =
|
|
3814
|
-
|
|
3815
|
-
const consoleLogFile =
|
|
3816
|
-
const logStream =
|
|
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 =
|
|
3840
|
-
const hasMessages =
|
|
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
|
|
3945
|
-
|
|
3946
|
-
|
|
3947
|
-
|
|
3948
|
-
|
|
3949
|
-
|
|
3950
|
-
const
|
|
3951
|
-
|
|
3952
|
-
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
await
|
|
3956
|
-
|
|
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
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
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
|
-
}
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3224
|
+
};
|
|
3225
|
+
runPrompt();
|
|
3226
|
+
promises2.push(promptPromise);
|
|
3227
|
+
}
|
|
3988
3228
|
const queuePromise = queue.next();
|
|
3989
|
-
|
|
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
|
-
|
|
3243
|
+
promises2.push(timeoutPromise);
|
|
4001
3244
|
}
|
|
4002
|
-
const winner = await Promise.race(
|
|
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
|
-
|
|
4009
|
-
|
|
4010
|
-
|
|
4011
|
-
process.stdout.
|
|
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 =
|
|
4058
|
-
const specificContextPath = options.context ?
|
|
4059
|
-
if (
|
|
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 =
|
|
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,
|
|
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,
|
|
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
|
|
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.
|
|
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.
|
|
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 =
|
|
4315
|
-
const dir =
|
|
4316
|
-
if (!
|
|
4317
|
-
|
|
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
|
-
|
|
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 =
|
|
4337
|
-
if (
|
|
4338
|
-
|
|
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.
|
|
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
|
-
|
|
4565
|
-
|
|
4566
|
-
|
|
4567
|
-
|
|
4568
|
-
|
|
4569
|
-
|
|
4570
|
-
|
|
4571
|
-
|
|
4572
|
-
|
|
4573
|
-
|
|
4574
|
-
|
|
4575
|
-
|
|
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
|
-
|
|
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/
|
|
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/
|
|
4659
|
-
import
|
|
4660
|
-
|
|
4661
|
-
|
|
4662
|
-
|
|
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
|
-
|
|
4694
|
-
} catch
|
|
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
|
-
|
|
4699
|
-
|
|
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
|
-
|
|
4704
|
-
|
|
4705
|
-
|
|
4706
|
-
|
|
4707
|
-
|
|
4708
|
-
|
|
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
|
-
|
|
4712
|
-
|
|
4713
|
-
|
|
4714
|
-
|
|
4715
|
-
|
|
4716
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4721
|
-
|
|
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">×</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
|
-
|
|
4724
|
-
|
|
4725
|
-
|
|
4726
|
-
|
|
4727
|
-
|
|
4728
|
-
|
|
4729
|
-
|
|
4730
|
-
|
|
4731
|
-
|
|
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
|
-
|
|
4782
|
-
|
|
4783
|
-
|
|
4784
|
-
|
|
4785
|
-
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
|
|
4789
|
-
|
|
4790
|
-
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
|
|
4798
|
-
|
|
4799
|
-
|
|
4800
|
-
|
|
4801
|
-
|
|
4802
|
-
|
|
4803
|
-
|
|
4804
|
-
|
|
4805
|
-
|
|
4806
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
|
|
4822
|
-
|
|
4823
|
-
|
|
4824
|
-
|
|
4825
|
-
|
|
4826
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4842
|
-
|
|
4843
|
-
|
|
4844
|
-
|
|
4845
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4926
|
-
|
|
4927
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4939
|
-
|
|
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
|
-
|
|
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
|
-
|
|
4948
|
-
|
|
4949
|
-
|
|
4950
|
-
|
|
4951
|
-
|
|
4952
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
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
|
-
|
|
5143
|
-
[TO BE ANALYZED - API]
|
|
5144
|
-
[Main endpoints or CLI commands]
|
|
4329
|
+
document.getElementById('btnCloseSidebar').addEventListener('click', hideSidebar);
|
|
5145
4330
|
|
|
5146
|
-
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
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
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
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
|
-
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
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
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
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
|
-
|
|
5214
|
-
|
|
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
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
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 (
|
|
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
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
|
|
5252
|
-
|
|
5253
|
-
|
|
5254
|
-
|
|
5255
|
-
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
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
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
5280
|
-
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
|
|
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
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
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
|
-
}
|
|
5343
|
-
|
|
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
|
-
}
|
|
5459
|
-
|
|
5460
|
-
keepGoing = false;
|
|
4509
|
+
} catch (e) {
|
|
4510
|
+
console.error("Error parsing memory graph files:", e);
|
|
5461
4511
|
}
|
|
5462
|
-
|
|
5463
|
-
|
|
5464
|
-
|
|
5465
|
-
|
|
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
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
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
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5576
|
-
|
|
5577
|
-
|
|
5578
|
-
|
|
5579
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
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);
|