shark-ai 0.4.26 → 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 +1063 -2224
- 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",
|
|
@@ -370,7 +387,7 @@ var AgentActionSchema = z2.object({
|
|
|
370
387
|
enable_write_tools: z2.boolean().nullable().optional(),
|
|
371
388
|
enable_subagent_tools: z2.boolean().nullable().optional(),
|
|
372
389
|
enable_mcp_tools: z2.boolean().nullable().optional()
|
|
373
|
-
});
|
|
390
|
+
}));
|
|
374
391
|
var AgentCommandSchema = z2.object({
|
|
375
392
|
command: z2.string(),
|
|
376
393
|
description: z2.string(),
|
|
@@ -538,6 +555,17 @@ function parseAgentResponse(rawResponse) {
|
|
|
538
555
|
} else if (normalizedAction && (!normalizedActions || normalizedActions.length === 0)) {
|
|
539
556
|
normalizedActions = [normalizedAction];
|
|
540
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
|
+
}
|
|
541
569
|
if (!normalizedAction && !normalizedActions) {
|
|
542
570
|
FileLogger.log("PARSER", "No Action/Actions Found - Constructing Default");
|
|
543
571
|
const content = parsedObj.message || (typeof parsedObj === "object" ? JSON.stringify(parsedObj) : String(parsedObj));
|
|
@@ -589,8 +617,25 @@ function extractFirstJson(str) {
|
|
|
589
617
|
try {
|
|
590
618
|
return JSON.parse(str);
|
|
591
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
|
+
}
|
|
592
630
|
const firstOpen = str.indexOf("{");
|
|
593
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
|
+
}
|
|
594
639
|
let balance = 0;
|
|
595
640
|
let inString = false;
|
|
596
641
|
let escape = false;
|
|
@@ -617,7 +662,11 @@ function extractFirstJson(str) {
|
|
|
617
662
|
try {
|
|
618
663
|
return JSON.parse(potentialJson);
|
|
619
664
|
} catch (innerE) {
|
|
620
|
-
|
|
665
|
+
try {
|
|
666
|
+
return JSON.parse(jsonrepair(potentialJson));
|
|
667
|
+
} catch {
|
|
668
|
+
throw e;
|
|
669
|
+
}
|
|
621
670
|
}
|
|
622
671
|
}
|
|
623
672
|
}
|
|
@@ -921,76 +970,176 @@ var AGENT_RESPONSE_JSON_SCHEMA = {
|
|
|
921
970
|
},
|
|
922
971
|
"required": ["type"]
|
|
923
972
|
},
|
|
924
|
-
"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
|
+
}
|
|
925
977
|
},
|
|
926
978
|
"required": ["action"]
|
|
927
979
|
};
|
|
928
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
|
+
|
|
929
1116
|
// src/core/api/stackspot-provider.ts
|
|
930
1117
|
var StackSpotProvider = class {
|
|
931
1118
|
constructor(agentType) {
|
|
932
1119
|
this.agentType = agentType;
|
|
933
1120
|
const config = ConfigManager.getInstance().getConfig();
|
|
934
1121
|
this.agentId = config.stackspot?.agentId;
|
|
1122
|
+
this.useServerConversation = config.stackspot?.useServerConversation !== false;
|
|
935
1123
|
}
|
|
936
1124
|
agentId;
|
|
1125
|
+
useServerConversation = true;
|
|
937
1126
|
getAgentId() {
|
|
938
1127
|
const config = ConfigManager.getInstance().getConfig();
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
"business_analyst": "ba",
|
|
942
|
-
"specification_agent": "spec",
|
|
943
|
-
"qa_agent": "qa",
|
|
944
|
-
"scan_agent": "scan",
|
|
945
|
-
"code_review": "codeReview"
|
|
946
|
-
};
|
|
947
|
-
const mappedKey = configKeyMapping[this.agentType];
|
|
948
|
-
if (mappedKey && config.agents?.[mappedKey]) {
|
|
949
|
-
return config.agents[mappedKey];
|
|
1128
|
+
if (config.agents?.dev) {
|
|
1129
|
+
return config.agents.dev;
|
|
950
1130
|
}
|
|
951
|
-
const
|
|
952
|
-
"business_analyst": process.env.STACKSPOT_BA_AGENT_ID,
|
|
953
|
-
"developer_agent": process.env.STACKSPOT_DEV_AGENT_ID,
|
|
954
|
-
"qa_agent": process.env.STACKSPOT_QA_AGENT_ID,
|
|
955
|
-
"specification_agent": process.env.STACKSPOT_SPEC_AGENT_ID,
|
|
956
|
-
"scan_agent": process.env.STACKSPOT_SCAN_AGENT_ID,
|
|
957
|
-
"code_review": process.env.STACKSPOT_CODE_REVIEW_AGENT_ID
|
|
958
|
-
};
|
|
959
|
-
const envResolved = envIdMapping[this.agentType];
|
|
1131
|
+
const envResolved = process.env.STACKSPOT_DEV_AGENT_ID;
|
|
960
1132
|
if (envResolved) {
|
|
961
1133
|
return envResolved;
|
|
962
1134
|
}
|
|
963
1135
|
if (this.agentId && this.agentId !== "01KEQCGJ65YENRA4QBXVN1YFFX") {
|
|
964
1136
|
return this.agentId;
|
|
965
1137
|
}
|
|
966
|
-
|
|
967
|
-
"business_analyst": "01KEJ95G304TNNAKGH5XNEEBVD",
|
|
968
|
-
"developer_agent": "01KEQCGJ65YENRA4QBXVN1YFFX",
|
|
969
|
-
"qa_agent": "01KEQFJZ3Q3JER11NH22HEZX9X",
|
|
970
|
-
"specification_agent": "01KEPXTX37FTB4N672TZST4SGP",
|
|
971
|
-
"scan_agent": "01KEQ9AHWB550J2244YBH3QATN",
|
|
972
|
-
"code_review": ""
|
|
973
|
-
};
|
|
974
|
-
const resolved = defaultIdMapping[this.agentType];
|
|
975
|
-
if (this.agentType === "code_review") {
|
|
976
|
-
if (!resolved) {
|
|
977
|
-
throw new Error("Agent ID for 'code_review' is not configured.");
|
|
978
|
-
}
|
|
979
|
-
return resolved;
|
|
980
|
-
}
|
|
981
|
-
return resolved || "01KEQCGJ65YENRA4QBXVN1YFFX";
|
|
1138
|
+
return "01KEQCGJ65YENRA4QBXVN1YFFX";
|
|
982
1139
|
}
|
|
983
1140
|
getAgentVersion() {
|
|
984
1141
|
const config = ConfigManager.getInstance().getConfig();
|
|
985
|
-
|
|
986
|
-
"business_analyst": config.agentVersions?.ba || process.env.STACKSPOT_BA_AGENT_VERSION,
|
|
987
|
-
"developer_agent": config.agentVersions?.dev || process.env.STACKSPOT_DEV_AGENT_VERSION,
|
|
988
|
-
"qa_agent": config.agentVersions?.qa || process.env.STACKSPOT_QA_AGENT_VERSION,
|
|
989
|
-
"specification_agent": config.agentVersions?.spec || process.env.STACKSPOT_SPEC_AGENT_VERSION,
|
|
990
|
-
"scan_agent": config.agentVersions?.scan || process.env.STACKSPOT_SCAN_AGENT_VERSION,
|
|
991
|
-
"code_review": config.agentVersions?.codeReview || process.env.STACKSPOT_CODE_REVIEW_AGENT_VERSION
|
|
992
|
-
};
|
|
993
|
-
return versionMapping[this.agentType];
|
|
1142
|
+
return config.agentVersions?.dev || process.env.STACKSPOT_DEV_AGENT_VERSION;
|
|
994
1143
|
}
|
|
995
1144
|
async streamChat(prompt, options) {
|
|
996
1145
|
const realm = await getActiveRealm();
|
|
@@ -1003,20 +1152,81 @@ var StackSpotProvider = class {
|
|
|
1003
1152
|
if (!token) {
|
|
1004
1153
|
throw new Error(`No authentication token found for realm '${realm}'. Please run 'shark login'.`);
|
|
1005
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
|
+
}
|
|
1006
1167
|
const isFirstTurn = !options.conversationId;
|
|
1007
|
-
|
|
1008
|
-
|
|
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}
|
|
1009
1208
|
|
|
1010
1209
|
USER REQUEST:
|
|
1011
|
-
${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
|
+
}
|
|
1012
1222
|
const requestPayload = {
|
|
1013
1223
|
user_prompt: finalPrompt,
|
|
1014
1224
|
streaming: true,
|
|
1015
1225
|
stackspot_knowledge: false,
|
|
1016
1226
|
return_ks_in_response: true,
|
|
1017
1227
|
deep_search_ks: false,
|
|
1018
|
-
use_conversation:
|
|
1019
|
-
conversation_id: options.conversationId
|
|
1228
|
+
use_conversation: this.useServerConversation,
|
|
1229
|
+
conversation_id: this.useServerConversation ? options.conversationId : conversationId
|
|
1020
1230
|
};
|
|
1021
1231
|
const agentVersion = this.getAgentVersion();
|
|
1022
1232
|
if (agentVersion) {
|
|
@@ -1054,7 +1264,7 @@ ${prompt}` : prompt;
|
|
|
1054
1264
|
onComplete: async (message, metadata) => {
|
|
1055
1265
|
rawResponse = {
|
|
1056
1266
|
message: message || fullMessage,
|
|
1057
|
-
conversation_id: metadata?.conversation_id ||
|
|
1267
|
+
conversation_id: metadata?.conversation_id || conversationId
|
|
1058
1268
|
};
|
|
1059
1269
|
},
|
|
1060
1270
|
onError: (error) => {
|
|
@@ -1064,6 +1274,11 @@ ${prompt}` : prompt;
|
|
|
1064
1274
|
);
|
|
1065
1275
|
FileLogger.log("PROVIDER_RESPONSE", "Raw response from StackSpot API", { rawResponse });
|
|
1066
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
|
+
}
|
|
1067
1282
|
if (options.onComplete) {
|
|
1068
1283
|
options.onComplete(parsedResponse);
|
|
1069
1284
|
}
|
|
@@ -1071,46 +1286,8 @@ ${prompt}` : prompt;
|
|
|
1071
1286
|
}
|
|
1072
1287
|
};
|
|
1073
1288
|
|
|
1074
|
-
// src/core/workflow/history-manager.ts
|
|
1075
|
-
import fs3 from "fs";
|
|
1076
|
-
import path3 from "path";
|
|
1077
|
-
var HistoryManager = class {
|
|
1078
|
-
static getHistoryDir() {
|
|
1079
|
-
const dir = path3.resolve(process.cwd(), "_sharkrc", "history");
|
|
1080
|
-
if (!fs3.existsSync(dir)) {
|
|
1081
|
-
fs3.mkdirSync(dir, { recursive: true });
|
|
1082
|
-
}
|
|
1083
|
-
return dir;
|
|
1084
|
-
}
|
|
1085
|
-
static getFilePath(conversationId) {
|
|
1086
|
-
return path3.resolve(this.getHistoryDir(), `${conversationId}.json`);
|
|
1087
|
-
}
|
|
1088
|
-
static async getHistory(conversationId) {
|
|
1089
|
-
const filePath = this.getFilePath(conversationId);
|
|
1090
|
-
if (!fs3.existsSync(filePath)) {
|
|
1091
|
-
return [];
|
|
1092
|
-
}
|
|
1093
|
-
try {
|
|
1094
|
-
const raw = fs3.readFileSync(filePath, "utf-8");
|
|
1095
|
-
const parsed = JSON.parse(raw);
|
|
1096
|
-
return Array.isArray(parsed) ? parsed : [];
|
|
1097
|
-
} catch {
|
|
1098
|
-
return [];
|
|
1099
|
-
}
|
|
1100
|
-
}
|
|
1101
|
-
static async saveHistory(conversationId, messages) {
|
|
1102
|
-
const filePath = this.getFilePath(conversationId);
|
|
1103
|
-
fs3.writeFileSync(filePath, JSON.stringify(messages, null, 2), "utf-8");
|
|
1104
|
-
}
|
|
1105
|
-
static async appendMessage(conversationId, message) {
|
|
1106
|
-
const history = await this.getHistory(conversationId);
|
|
1107
|
-
history.push(message);
|
|
1108
|
-
await this.saveHistory(conversationId, history);
|
|
1109
|
-
}
|
|
1110
|
-
};
|
|
1111
|
-
|
|
1112
1289
|
// src/core/api/openai-compatible-provider.ts
|
|
1113
|
-
import
|
|
1290
|
+
import crypto2 from "crypto";
|
|
1114
1291
|
var OpenAICompatibleProvider = class {
|
|
1115
1292
|
constructor(options) {
|
|
1116
1293
|
this.options = options;
|
|
@@ -1119,7 +1296,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1119
1296
|
return UNIFIED_SYSTEM_PROMPT;
|
|
1120
1297
|
}
|
|
1121
1298
|
async streamChat(prompt, options) {
|
|
1122
|
-
const conversationId = options.conversationId ||
|
|
1299
|
+
const conversationId = options.conversationId || crypto2.randomUUID();
|
|
1123
1300
|
const history = await HistoryManager.getHistory(conversationId);
|
|
1124
1301
|
if (history.length === 0) {
|
|
1125
1302
|
history.push({
|
|
@@ -1128,9 +1305,32 @@ var OpenAICompatibleProvider = class {
|
|
|
1128
1305
|
});
|
|
1129
1306
|
}
|
|
1130
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
|
+
}
|
|
1131
1331
|
const requestPayload = {
|
|
1132
1332
|
model: this.options.model,
|
|
1133
|
-
messages:
|
|
1333
|
+
messages: requestMessages,
|
|
1134
1334
|
stream: true,
|
|
1135
1335
|
temperature: 0.2
|
|
1136
1336
|
};
|
|
@@ -1140,172 +1340,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1140
1340
|
json_schema: {
|
|
1141
1341
|
name: "agent_response",
|
|
1142
1342
|
strict: true,
|
|
1143
|
-
schema:
|
|
1144
|
-
type: "object",
|
|
1145
|
-
properties: {
|
|
1146
|
-
action: {
|
|
1147
|
-
type: "object",
|
|
1148
|
-
properties: {
|
|
1149
|
-
type: {
|
|
1150
|
-
type: "string",
|
|
1151
|
-
enum: [
|
|
1152
|
-
"create_file",
|
|
1153
|
-
"modify_file",
|
|
1154
|
-
"read_file",
|
|
1155
|
-
"list_files",
|
|
1156
|
-
"search_file",
|
|
1157
|
-
"search_code",
|
|
1158
|
-
"delete_file",
|
|
1159
|
-
"run_command",
|
|
1160
|
-
"talk_with_user",
|
|
1161
|
-
"use_mcp_tool",
|
|
1162
|
-
"activate_skill",
|
|
1163
|
-
"define_subagent",
|
|
1164
|
-
"invoke_subagent",
|
|
1165
|
-
"send_message",
|
|
1166
|
-
"manage_subagents",
|
|
1167
|
-
"complete_task",
|
|
1168
|
-
"list_structure",
|
|
1169
|
-
"modify_ast",
|
|
1170
|
-
"search_ast",
|
|
1171
|
-
"ast_list_structure",
|
|
1172
|
-
"ast_get_method",
|
|
1173
|
-
"ast_add_method",
|
|
1174
|
-
"ast_modify_method",
|
|
1175
|
-
"ast_remove_method",
|
|
1176
|
-
"ast_add_class",
|
|
1177
|
-
"ast_get_property",
|
|
1178
|
-
"ast_add_property",
|
|
1179
|
-
"ast_modify_property",
|
|
1180
|
-
"ast_remove_property",
|
|
1181
|
-
"ast_add_decorator",
|
|
1182
|
-
"ast_add_interface",
|
|
1183
|
-
"ast_add_type_alias",
|
|
1184
|
-
"ast_add_function",
|
|
1185
|
-
"ast_remove_function",
|
|
1186
|
-
"ast_add_import",
|
|
1187
|
-
"ast_remove_import",
|
|
1188
|
-
"ast_organize_imports"
|
|
1189
|
-
]
|
|
1190
|
-
},
|
|
1191
|
-
path: { type: ["string", "null"] },
|
|
1192
|
-
content: { type: ["string", "null"] },
|
|
1193
|
-
start_anchor: { type: ["string", "null"] },
|
|
1194
|
-
end_anchor: { type: ["string", "null"] },
|
|
1195
|
-
command: { type: ["string", "null"] },
|
|
1196
|
-
query: { type: ["string", "null"] },
|
|
1197
|
-
tool_name: { type: ["string", "null"] },
|
|
1198
|
-
tool_args: { type: ["string", "null"] },
|
|
1199
|
-
line_range: {
|
|
1200
|
-
type: ["array", "null"],
|
|
1201
|
-
items: { type: "number" }
|
|
1202
|
-
},
|
|
1203
|
-
target_content: { type: ["string", "null"] },
|
|
1204
|
-
is_regex: { type: ["boolean", "null"] },
|
|
1205
|
-
pattern: { type: ["string", "null"] },
|
|
1206
|
-
fix: { type: ["string", "null"] },
|
|
1207
|
-
language: { type: ["string", "null"] },
|
|
1208
|
-
file_path: { type: ["string", "null"] },
|
|
1209
|
-
class_name: { type: ["string", "null"] },
|
|
1210
|
-
method_name: { type: ["string", "null"] },
|
|
1211
|
-
method_code: { type: ["string", "null"] },
|
|
1212
|
-
property_name: { type: ["string", "null"] },
|
|
1213
|
-
property_code: { type: ["string", "null"] },
|
|
1214
|
-
extends_class: { type: ["string", "null"] },
|
|
1215
|
-
implements_interfaces: {
|
|
1216
|
-
type: ["array", "null"],
|
|
1217
|
-
items: { type: "string" }
|
|
1218
|
-
},
|
|
1219
|
-
decorator_code: { type: ["string", "null"] },
|
|
1220
|
-
interface_code: { type: ["string", "null"] },
|
|
1221
|
-
type_code: { type: ["string", "null"] },
|
|
1222
|
-
function_name: { type: ["string", "null"] },
|
|
1223
|
-
function_code: { type: ["string", "null"] },
|
|
1224
|
-
import_statement: { type: ["string", "null"] },
|
|
1225
|
-
module_path: { type: ["string", "null"] },
|
|
1226
|
-
new_body: { type: ["string", "null"] },
|
|
1227
|
-
confirmed: { type: ["boolean", "null"] },
|
|
1228
|
-
skill_name: { type: ["string", "null"] },
|
|
1229
|
-
Subagents: {
|
|
1230
|
-
type: ["array", "null"],
|
|
1231
|
-
items: {
|
|
1232
|
-
type: "object",
|
|
1233
|
-
properties: {
|
|
1234
|
-
TypeName: { type: "string" },
|
|
1235
|
-
Role: { type: "string" },
|
|
1236
|
-
Prompt: { type: "string" }
|
|
1237
|
-
},
|
|
1238
|
-
required: ["TypeName", "Role", "Prompt"],
|
|
1239
|
-
additionalProperties: false
|
|
1240
|
-
}
|
|
1241
|
-
},
|
|
1242
|
-
Recipient: { type: ["string", "null"] },
|
|
1243
|
-
Message: { type: ["string", "null"] },
|
|
1244
|
-
Action: { type: ["string", "null"] },
|
|
1245
|
-
ConversationIds: {
|
|
1246
|
-
type: ["array", "null"],
|
|
1247
|
-
items: { type: "string" }
|
|
1248
|
-
},
|
|
1249
|
-
name: { type: ["string", "null"] },
|
|
1250
|
-
description: { type: ["string", "null"] },
|
|
1251
|
-
system_prompt: { type: ["string", "null"] },
|
|
1252
|
-
enable_write_tools: { type: ["boolean", "null"] },
|
|
1253
|
-
enable_subagent_tools: { type: ["boolean", "null"] },
|
|
1254
|
-
enable_mcp_tools: { type: ["boolean", "null"] }
|
|
1255
|
-
},
|
|
1256
|
-
required: [
|
|
1257
|
-
"type",
|
|
1258
|
-
"path",
|
|
1259
|
-
"content",
|
|
1260
|
-
"start_anchor",
|
|
1261
|
-
"end_anchor",
|
|
1262
|
-
"command",
|
|
1263
|
-
"query",
|
|
1264
|
-
"tool_name",
|
|
1265
|
-
"tool_args",
|
|
1266
|
-
"line_range",
|
|
1267
|
-
"target_content",
|
|
1268
|
-
"is_regex",
|
|
1269
|
-
"pattern",
|
|
1270
|
-
"fix",
|
|
1271
|
-
"language",
|
|
1272
|
-
"file_path",
|
|
1273
|
-
"class_name",
|
|
1274
|
-
"method_name",
|
|
1275
|
-
"method_code",
|
|
1276
|
-
"property_name",
|
|
1277
|
-
"property_code",
|
|
1278
|
-
"extends_class",
|
|
1279
|
-
"implements_interfaces",
|
|
1280
|
-
"decorator_code",
|
|
1281
|
-
"interface_code",
|
|
1282
|
-
"type_code",
|
|
1283
|
-
"function_name",
|
|
1284
|
-
"function_code",
|
|
1285
|
-
"import_statement",
|
|
1286
|
-
"module_path",
|
|
1287
|
-
"new_body",
|
|
1288
|
-
"confirmed",
|
|
1289
|
-
"skill_name",
|
|
1290
|
-
"Subagents",
|
|
1291
|
-
"Recipient",
|
|
1292
|
-
"Message",
|
|
1293
|
-
"Action",
|
|
1294
|
-
"ConversationIds",
|
|
1295
|
-
"name",
|
|
1296
|
-
"description",
|
|
1297
|
-
"system_prompt",
|
|
1298
|
-
"enable_write_tools",
|
|
1299
|
-
"enable_subagent_tools",
|
|
1300
|
-
"enable_mcp_tools"
|
|
1301
|
-
],
|
|
1302
|
-
additionalProperties: false
|
|
1303
|
-
},
|
|
1304
|
-
summary: { type: "string" }
|
|
1305
|
-
},
|
|
1306
|
-
required: ["action", "summary"],
|
|
1307
|
-
additionalProperties: false
|
|
1308
|
-
}
|
|
1343
|
+
schema: toStrictOpenAISchema(AGENT_RESPONSE_JSON_SCHEMA)
|
|
1309
1344
|
}
|
|
1310
1345
|
};
|
|
1311
1346
|
} else {
|
|
@@ -1326,20 +1361,26 @@ var OpenAICompatibleProvider = class {
|
|
|
1326
1361
|
headers: sanitizedHeaders,
|
|
1327
1362
|
payload: requestPayload
|
|
1328
1363
|
});
|
|
1329
|
-
const
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
body: JSON.stringify(requestPayload)
|
|
1333
|
-
});
|
|
1334
|
-
if (!res.ok) {
|
|
1335
|
-
const errBody = await res.text();
|
|
1336
|
-
throw new Error(`OpenAI API request failed: ${res.status} ${res.statusText} - ${errBody}`);
|
|
1337
|
-
}
|
|
1338
|
-
const reader = res.body?.getReader();
|
|
1339
|
-
if (!reader) {
|
|
1340
|
-
throw new Error("Response body reader is undefined");
|
|
1341
|
-
}
|
|
1364
|
+
const controller = new AbortController();
|
|
1365
|
+
const timeoutId = setTimeout(() => controller.abort(), 3e5);
|
|
1366
|
+
let reader = void 0;
|
|
1342
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
|
+
}
|
|
1343
1384
|
const decoder = new TextDecoder();
|
|
1344
1385
|
let fullContent = "";
|
|
1345
1386
|
let done = false;
|
|
@@ -1395,6 +1436,7 @@ var OpenAICompatibleProvider = class {
|
|
|
1395
1436
|
}
|
|
1396
1437
|
}
|
|
1397
1438
|
}
|
|
1439
|
+
clearTimeout(timeoutId);
|
|
1398
1440
|
FileLogger.log("PROVIDER_RESPONSE", "Raw response from OpenAI Compatible API", { fullContent });
|
|
1399
1441
|
const parsedResponse = parseAgentResponse(fullContent);
|
|
1400
1442
|
parsedResponse.conversation_id = conversationId;
|
|
@@ -1404,13 +1446,66 @@ var OpenAICompatibleProvider = class {
|
|
|
1404
1446
|
options.onComplete(parsedResponse);
|
|
1405
1447
|
}
|
|
1406
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;
|
|
1407
1455
|
} finally {
|
|
1408
|
-
if (typeof reader.releaseLock === "function") {
|
|
1456
|
+
if (reader && typeof reader.releaseLock === "function") {
|
|
1409
1457
|
reader.releaseLock();
|
|
1410
1458
|
}
|
|
1411
1459
|
}
|
|
1412
1460
|
}
|
|
1413
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
|
+
}
|
|
1414
1509
|
|
|
1415
1510
|
// src/core/api/provider-resolver.ts
|
|
1416
1511
|
var ProviderResolver = class {
|
|
@@ -1499,8 +1594,8 @@ var ConversationManager = class _ConversationManager {
|
|
|
1499
1594
|
var conversationManager = ConversationManager.getInstance();
|
|
1500
1595
|
|
|
1501
1596
|
// src/core/workflow/anchor-state-manager.ts
|
|
1502
|
-
import
|
|
1503
|
-
import
|
|
1597
|
+
import fs5 from "fs";
|
|
1598
|
+
import path5 from "path";
|
|
1504
1599
|
import { diffLines } from "diff";
|
|
1505
1600
|
var AnchorStateManager = class {
|
|
1506
1601
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -2515,10 +2610,10 @@ var AnchorStateManager = class {
|
|
|
2515
2610
|
}
|
|
2516
2611
|
}
|
|
2517
2612
|
getAnchoredContent(filePath) {
|
|
2518
|
-
const absolutePath =
|
|
2613
|
+
const absolutePath = path5.resolve(filePath);
|
|
2519
2614
|
let lineStates = this.cache.get(absolutePath);
|
|
2520
2615
|
if (!lineStates) {
|
|
2521
|
-
const content =
|
|
2616
|
+
const content = fs5.readFileSync(absolutePath, "utf8");
|
|
2522
2617
|
const hasTrailingNewline = content.endsWith("\n");
|
|
2523
2618
|
const lines = hasTrailingNewline ? content.slice(0, -1).split("\n") : content.split("\n");
|
|
2524
2619
|
const usedAnchors = /* @__PURE__ */ new Set();
|
|
@@ -2531,7 +2626,7 @@ var AnchorStateManager = class {
|
|
|
2531
2626
|
return lineStates.map((ls) => `${ls.anchor}\xA7${ls.text}`).join("\n");
|
|
2532
2627
|
}
|
|
2533
2628
|
applyAnchoredEdit(filePath, startAnchor, endAnchor, content) {
|
|
2534
|
-
const absolutePath =
|
|
2629
|
+
const absolutePath = path5.resolve(filePath);
|
|
2535
2630
|
let lineStates = this.cache.get(absolutePath);
|
|
2536
2631
|
if (!lineStates) {
|
|
2537
2632
|
this.getAnchoredContent(absolutePath);
|
|
@@ -2548,7 +2643,7 @@ var AnchorStateManager = class {
|
|
|
2548
2643
|
if (startIndex > endIndex) {
|
|
2549
2644
|
throw new Error(`Invalid range: start anchor "${startAnchor}" is after end anchor "${endAnchor}"`);
|
|
2550
2645
|
}
|
|
2551
|
-
const originalContent =
|
|
2646
|
+
const originalContent = fs5.readFileSync(absolutePath, "utf8");
|
|
2552
2647
|
const hasTrailingNewline = originalContent.endsWith("\n");
|
|
2553
2648
|
const oldLines = lineStates.map((ls) => ls.text);
|
|
2554
2649
|
const cleanContent = content.endsWith("\n") ? content.slice(0, -1) : content;
|
|
@@ -2559,7 +2654,7 @@ var AnchorStateManager = class {
|
|
|
2559
2654
|
...oldLines.slice(endIndex + 1)
|
|
2560
2655
|
];
|
|
2561
2656
|
const fileContentToWrite = updatedLines.join("\n") + (hasTrailingNewline ? "\n" : "");
|
|
2562
|
-
|
|
2657
|
+
fs5.writeFileSync(absolutePath, fileContentToWrite, "utf8");
|
|
2563
2658
|
const usedAnchors = /* @__PURE__ */ new Set();
|
|
2564
2659
|
for (let i = 0; i < lineStates.length; i++) {
|
|
2565
2660
|
usedAnchors.add(lineStates[i].anchor);
|
|
@@ -2605,457 +2700,19 @@ var AnchorStateManager = class {
|
|
|
2605
2700
|
};
|
|
2606
2701
|
|
|
2607
2702
|
// src/core/agents/developer-agent.ts
|
|
2608
|
-
import
|
|
2609
|
-
import
|
|
2703
|
+
import fs8 from "fs";
|
|
2704
|
+
import path8 from "path";
|
|
2610
2705
|
|
|
2611
2706
|
// src/core/agents/agent-tools.ts
|
|
2612
2707
|
import fs6 from "fs";
|
|
2613
|
-
import
|
|
2708
|
+
import path6 from "path";
|
|
2614
2709
|
import fg from "fast-glob";
|
|
2615
2710
|
import { exec } from "child_process";
|
|
2616
2711
|
import { promisify } from "util";
|
|
2617
|
-
import { fileURLToPath } from "url";
|
|
2618
|
-
|
|
2619
|
-
// src/core/ast-editing/editors/code-editor-factory.ts
|
|
2620
|
-
import * as path6 from "path";
|
|
2621
|
-
|
|
2622
|
-
// src/core/ast-editing/editors/typescript-editor.ts
|
|
2623
|
-
import { Project, SyntaxKind } from "ts-morph";
|
|
2624
|
-
import * as path5 from "path";
|
|
2625
|
-
import * as fs5 from "fs";
|
|
2626
|
-
var TypeScriptEditor = class {
|
|
2627
|
-
project;
|
|
2628
|
-
constructor() {
|
|
2629
|
-
this.project = new Project({
|
|
2630
|
-
skipAddingFilesFromTsConfig: true,
|
|
2631
|
-
compilerOptions: {
|
|
2632
|
-
target: 99,
|
|
2633
|
-
// ESNext
|
|
2634
|
-
module: 99
|
|
2635
|
-
// ESNext
|
|
2636
|
-
}
|
|
2637
|
-
});
|
|
2638
|
-
}
|
|
2639
|
-
/**
|
|
2640
|
-
* Get or add source file to project
|
|
2641
|
-
*/
|
|
2642
|
-
getSourceFile(filePath) {
|
|
2643
|
-
const absolutePath = path5.resolve(filePath);
|
|
2644
|
-
let sourceFile = this.project.getSourceFile(absolutePath);
|
|
2645
|
-
if (!sourceFile) {
|
|
2646
|
-
if (!fs5.existsSync(absolutePath)) {
|
|
2647
|
-
throw new Error(`File not found: ${absolutePath}`);
|
|
2648
|
-
}
|
|
2649
|
-
sourceFile = this.project.addSourceFileAtPath(absolutePath);
|
|
2650
|
-
}
|
|
2651
|
-
return sourceFile;
|
|
2652
|
-
}
|
|
2653
|
-
/**
|
|
2654
|
-
* Get class declaration by name
|
|
2655
|
-
*/
|
|
2656
|
-
getClass(sourceFile, className) {
|
|
2657
|
-
const classDecl = sourceFile.getClass(className);
|
|
2658
|
-
if (!classDecl) {
|
|
2659
|
-
throw new Error(`Class "${className}" not found in ${sourceFile.getFilePath()}`);
|
|
2660
|
-
}
|
|
2661
|
-
return classDecl;
|
|
2662
|
-
}
|
|
2663
|
-
// ═══════════════════════════════════════════════════════
|
|
2664
|
-
// IMPLEMENTATION: listStructure
|
|
2665
|
-
// ═══════════════════════════════════════════════════════
|
|
2666
|
-
async listStructure(filePath) {
|
|
2667
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2668
|
-
const classes = sourceFile.getClasses().map((cls) => ({
|
|
2669
|
-
name: cls.getName() || "<anonymous>",
|
|
2670
|
-
methods: cls.getMethods().map((m) => this.extractMethodInfo(m)),
|
|
2671
|
-
properties: cls.getProperties().map((p) => this.extractPropertyInfo(p)),
|
|
2672
|
-
decorators: cls.getDecorators().map((d) => d.getText()),
|
|
2673
|
-
extendsClass: cls.getExtends()?.getText(),
|
|
2674
|
-
implementsInterfaces: cls.getImplements().map((i) => i.getText())
|
|
2675
|
-
}));
|
|
2676
|
-
const interfaces = sourceFile.getInterfaces().map((iface) => ({
|
|
2677
|
-
name: iface.getName(),
|
|
2678
|
-
properties: iface.getProperties().map((p) => this.extractPropertyInfo(p)),
|
|
2679
|
-
extends: iface.getExtends().map((e) => e.getText())
|
|
2680
|
-
}));
|
|
2681
|
-
const functions = sourceFile.getFunctions().map((fn) => ({
|
|
2682
|
-
name: fn.getName() || "<anonymous>",
|
|
2683
|
-
parameters: fn.getParameters().map((p) => ({
|
|
2684
|
-
name: p.getName(),
|
|
2685
|
-
type: p.getType().getText(),
|
|
2686
|
-
isOptional: p.isOptional()
|
|
2687
|
-
})),
|
|
2688
|
-
returnType: fn.getReturnType().getText(),
|
|
2689
|
-
isAsync: fn.isAsync(),
|
|
2690
|
-
isExported: fn.isExported()
|
|
2691
|
-
}));
|
|
2692
|
-
const imports = sourceFile.getImportDeclarations().map((imp) => ({
|
|
2693
|
-
modulePath: imp.getModuleSpecifierValue(),
|
|
2694
|
-
isDefault: !!imp.getDefaultImport(),
|
|
2695
|
-
namedImports: imp.getNamedImports().map((n) => n.getName()),
|
|
2696
|
-
namespaceImport: imp.getNamespaceImport()?.getText()
|
|
2697
|
-
}));
|
|
2698
|
-
const exports = sourceFile.getExportedDeclarations();
|
|
2699
|
-
const exportInfo = Array.from(exports.entries()).flatMap(
|
|
2700
|
-
([name, declarations]) => declarations.map((decl) => ({
|
|
2701
|
-
name,
|
|
2702
|
-
type: this.getDeclarationType(decl),
|
|
2703
|
-
isDefault: sourceFile.getDefaultExportSymbol()?.getName() === name
|
|
2704
|
-
}))
|
|
2705
|
-
);
|
|
2706
|
-
return {
|
|
2707
|
-
classes,
|
|
2708
|
-
interfaces,
|
|
2709
|
-
functions,
|
|
2710
|
-
imports,
|
|
2711
|
-
exports: exportInfo
|
|
2712
|
-
};
|
|
2713
|
-
}
|
|
2714
|
-
extractMethodInfo(method) {
|
|
2715
|
-
return {
|
|
2716
|
-
name: method.getName(),
|
|
2717
|
-
parameters: method.getParameters().map((p) => ({
|
|
2718
|
-
name: p.getName(),
|
|
2719
|
-
type: p.getType().getText(),
|
|
2720
|
-
isOptional: p.isOptional()
|
|
2721
|
-
})),
|
|
2722
|
-
returnType: method.getReturnType().getText(),
|
|
2723
|
-
isAsync: method.isAsync(),
|
|
2724
|
-
isStatic: method.isStatic(),
|
|
2725
|
-
visibility: this.getVisibility(method),
|
|
2726
|
-
decorators: method.getDecorators().map((d) => d.getText())
|
|
2727
|
-
};
|
|
2728
|
-
}
|
|
2729
|
-
extractPropertyInfo(property) {
|
|
2730
|
-
return {
|
|
2731
|
-
name: property.getName(),
|
|
2732
|
-
type: property.getType()?.getText(),
|
|
2733
|
-
visibility: this.getVisibility(property),
|
|
2734
|
-
isReadonly: property.isReadonly(),
|
|
2735
|
-
initializer: property.getInitializer()?.getText()
|
|
2736
|
-
};
|
|
2737
|
-
}
|
|
2738
|
-
getVisibility(node) {
|
|
2739
|
-
if (node.hasModifier?.(SyntaxKind.PrivateKeyword)) return "private";
|
|
2740
|
-
if (node.hasModifier?.(SyntaxKind.ProtectedKeyword)) return "protected";
|
|
2741
|
-
return "public";
|
|
2742
|
-
}
|
|
2743
|
-
getDeclarationType(decl) {
|
|
2744
|
-
if (decl.getKind() === SyntaxKind.ClassDeclaration) return "class";
|
|
2745
|
-
if (decl.getKind() === SyntaxKind.FunctionDeclaration) return "function";
|
|
2746
|
-
if (decl.getKind() === SyntaxKind.InterfaceDeclaration) return "interface";
|
|
2747
|
-
if (decl.getKind() === SyntaxKind.TypeAliasDeclaration) return "type";
|
|
2748
|
-
return "const";
|
|
2749
|
-
}
|
|
2750
|
-
// ═══════════════════════════════════════════════════════
|
|
2751
|
-
// IMPLEMENTATION: Class Operations
|
|
2752
|
-
// ═══════════════════════════════════════════════════════
|
|
2753
|
-
async addClass(filePath, className, options) {
|
|
2754
|
-
try {
|
|
2755
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2756
|
-
sourceFile.addClass({
|
|
2757
|
-
name: className,
|
|
2758
|
-
extends: options?.extendsClass,
|
|
2759
|
-
implements: options?.implementsInterfaces,
|
|
2760
|
-
isExported: true
|
|
2761
|
-
});
|
|
2762
|
-
await sourceFile.save();
|
|
2763
|
-
return true;
|
|
2764
|
-
} catch (error) {
|
|
2765
|
-
console.error(`Failed to add class "${className}":`, error);
|
|
2766
|
-
return false;
|
|
2767
|
-
}
|
|
2768
|
-
}
|
|
2769
|
-
async addProperty(filePath, className, propertyCode) {
|
|
2770
|
-
try {
|
|
2771
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2772
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2773
|
-
const closeBrace = classDecl.getEnd() - 1;
|
|
2774
|
-
classDecl.insertText(closeBrace, `
|
|
2775
|
-
${propertyCode}`);
|
|
2776
|
-
classDecl.formatText();
|
|
2777
|
-
await sourceFile.save();
|
|
2778
|
-
return true;
|
|
2779
|
-
} catch (error) {
|
|
2780
|
-
console.error(`Failed to add property to "${className}":`, error);
|
|
2781
|
-
return false;
|
|
2782
|
-
}
|
|
2783
|
-
}
|
|
2784
|
-
async getProperty(filePath, className, propertyName) {
|
|
2785
|
-
try {
|
|
2786
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2787
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2788
|
-
const property = classDecl.getProperty(propertyName);
|
|
2789
|
-
if (!property) {
|
|
2790
|
-
return void 0;
|
|
2791
|
-
}
|
|
2792
|
-
return property.getText();
|
|
2793
|
-
} catch (error) {
|
|
2794
|
-
console.error(`Failed to get property "${propertyName}":`, error);
|
|
2795
|
-
return void 0;
|
|
2796
|
-
}
|
|
2797
|
-
}
|
|
2798
|
-
async modifyProperty(filePath, className, propertyName, newCode) {
|
|
2799
|
-
try {
|
|
2800
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2801
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2802
|
-
const property = classDecl.getProperty(propertyName);
|
|
2803
|
-
if (!property) {
|
|
2804
|
-
throw new Error(`Property "${propertyName}" not found in class "${className}"`);
|
|
2805
|
-
}
|
|
2806
|
-
property.replaceWithText(newCode);
|
|
2807
|
-
classDecl.formatText();
|
|
2808
|
-
await sourceFile.save();
|
|
2809
|
-
return true;
|
|
2810
|
-
} catch (error) {
|
|
2811
|
-
console.error(`Failed to modify property "${propertyName}":`, error);
|
|
2812
|
-
return false;
|
|
2813
|
-
}
|
|
2814
|
-
}
|
|
2815
|
-
async removeProperty(filePath, className, propertyName) {
|
|
2816
|
-
try {
|
|
2817
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2818
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2819
|
-
const property = classDecl.getProperty(propertyName);
|
|
2820
|
-
if (!property) {
|
|
2821
|
-
throw new Error(`Property "${propertyName}" not found in class "${className}"`);
|
|
2822
|
-
}
|
|
2823
|
-
property.remove();
|
|
2824
|
-
await sourceFile.save();
|
|
2825
|
-
return true;
|
|
2826
|
-
} catch (error) {
|
|
2827
|
-
console.error(`Failed to remove property "${propertyName}":`, error);
|
|
2828
|
-
return false;
|
|
2829
|
-
}
|
|
2830
|
-
}
|
|
2831
|
-
async addMethod(filePath, className, methodCode) {
|
|
2832
|
-
try {
|
|
2833
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2834
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2835
|
-
const closeBrace = classDecl.getEnd() - 1;
|
|
2836
|
-
classDecl.insertText(closeBrace, `
|
|
2837
|
-
${methodCode}
|
|
2838
|
-
`);
|
|
2839
|
-
classDecl.formatText();
|
|
2840
|
-
await sourceFile.save();
|
|
2841
|
-
return true;
|
|
2842
|
-
} catch (error) {
|
|
2843
|
-
console.error(`Failed to add method to "${className}":`, error);
|
|
2844
|
-
return false;
|
|
2845
|
-
}
|
|
2846
|
-
}
|
|
2847
|
-
async modifyMethod(filePath, className, methodName, newBody) {
|
|
2848
|
-
try {
|
|
2849
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2850
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2851
|
-
const method = classDecl.getMethod(methodName);
|
|
2852
|
-
if (!method) {
|
|
2853
|
-
throw new Error(`Method "${methodName}" not found in class "${className}"`);
|
|
2854
|
-
}
|
|
2855
|
-
method.setBodyText(newBody);
|
|
2856
|
-
method.formatText();
|
|
2857
|
-
await sourceFile.save();
|
|
2858
|
-
return true;
|
|
2859
|
-
} catch (error) {
|
|
2860
|
-
console.error(`Failed to modify method "${methodName}":`, error);
|
|
2861
|
-
return false;
|
|
2862
|
-
}
|
|
2863
|
-
}
|
|
2864
|
-
async removeMethod(filePath, className, methodName) {
|
|
2865
|
-
try {
|
|
2866
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2867
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2868
|
-
const method = classDecl.getMethod(methodName);
|
|
2869
|
-
if (!method) {
|
|
2870
|
-
throw new Error(`Method "${methodName}" not found in class "${className}"`);
|
|
2871
|
-
}
|
|
2872
|
-
method.remove();
|
|
2873
|
-
await sourceFile.save();
|
|
2874
|
-
return true;
|
|
2875
|
-
} catch (error) {
|
|
2876
|
-
console.error(`Failed to remove method "${methodName}":`, error);
|
|
2877
|
-
return false;
|
|
2878
|
-
}
|
|
2879
|
-
}
|
|
2880
|
-
async addDecorator(filePath, className, decoratorCode) {
|
|
2881
|
-
try {
|
|
2882
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2883
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2884
|
-
const start = classDecl.getStart();
|
|
2885
|
-
sourceFile.insertText(start, `${decoratorCode}
|
|
2886
|
-
`);
|
|
2887
|
-
sourceFile.formatText();
|
|
2888
|
-
await sourceFile.save();
|
|
2889
|
-
return true;
|
|
2890
|
-
} catch (error) {
|
|
2891
|
-
console.error(`Failed to add decorator to "${className}":`, error);
|
|
2892
|
-
return false;
|
|
2893
|
-
}
|
|
2894
|
-
}
|
|
2895
|
-
async getMethod(filePath, className, methodName) {
|
|
2896
|
-
try {
|
|
2897
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2898
|
-
const classDecl = this.getClass(sourceFile, className);
|
|
2899
|
-
const method = classDecl.getMethod(methodName);
|
|
2900
|
-
if (!method) {
|
|
2901
|
-
return void 0;
|
|
2902
|
-
}
|
|
2903
|
-
return method.getText();
|
|
2904
|
-
} catch (error) {
|
|
2905
|
-
console.error(`Failed to get method "${methodName}":`, error);
|
|
2906
|
-
return void 0;
|
|
2907
|
-
}
|
|
2908
|
-
}
|
|
2909
|
-
// ═══════════════════════════════════════════════════════
|
|
2910
|
-
// IMPLEMENTATION: Interface/Type Operations
|
|
2911
|
-
// ═══════════════════════════════════════════════════════
|
|
2912
|
-
async addInterface(filePath, interfaceCode) {
|
|
2913
|
-
try {
|
|
2914
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2915
|
-
sourceFile.insertText(sourceFile.getEnd(), `
|
|
2916
|
-
|
|
2917
|
-
${interfaceCode}`);
|
|
2918
|
-
sourceFile.formatText();
|
|
2919
|
-
await sourceFile.save();
|
|
2920
|
-
return true;
|
|
2921
|
-
} catch (error) {
|
|
2922
|
-
console.error(`Failed to add interface:`, error);
|
|
2923
|
-
return false;
|
|
2924
|
-
}
|
|
2925
|
-
}
|
|
2926
|
-
async addTypeAlias(filePath, typeCode) {
|
|
2927
|
-
try {
|
|
2928
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2929
|
-
sourceFile.insertText(sourceFile.getEnd(), `
|
|
2930
|
-
|
|
2931
|
-
${typeCode}`);
|
|
2932
|
-
sourceFile.formatText();
|
|
2933
|
-
await sourceFile.save();
|
|
2934
|
-
return true;
|
|
2935
|
-
} catch (error) {
|
|
2936
|
-
console.error(`Failed to add type alias:`, error);
|
|
2937
|
-
return false;
|
|
2938
|
-
}
|
|
2939
|
-
}
|
|
2940
|
-
// ═══════════════════════════════════════════════════════
|
|
2941
|
-
// IMPLEMENTATION: Function Operations
|
|
2942
|
-
// ═══════════════════════════════════════════════════════
|
|
2943
|
-
async addFunction(filePath, functionCode) {
|
|
2944
|
-
try {
|
|
2945
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2946
|
-
sourceFile.insertText(sourceFile.getEnd(), `
|
|
2947
|
-
|
|
2948
|
-
${functionCode}`);
|
|
2949
|
-
sourceFile.formatText();
|
|
2950
|
-
await sourceFile.save();
|
|
2951
|
-
return true;
|
|
2952
|
-
} catch (error) {
|
|
2953
|
-
console.error(`Failed to add function:`, error);
|
|
2954
|
-
return false;
|
|
2955
|
-
}
|
|
2956
|
-
}
|
|
2957
|
-
async removeFunction(filePath, functionName) {
|
|
2958
|
-
try {
|
|
2959
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2960
|
-
const func = sourceFile.getFunction(functionName);
|
|
2961
|
-
if (!func) {
|
|
2962
|
-
throw new Error(`Function "${functionName}" not found`);
|
|
2963
|
-
}
|
|
2964
|
-
func.remove();
|
|
2965
|
-
await sourceFile.save();
|
|
2966
|
-
return true;
|
|
2967
|
-
} catch (error) {
|
|
2968
|
-
console.error(`Failed to remove function "${functionName}":`, error);
|
|
2969
|
-
return false;
|
|
2970
|
-
}
|
|
2971
|
-
}
|
|
2972
|
-
// ═══════════════════════════════════════════════════════
|
|
2973
|
-
// IMPLEMENTATION: Import/Export Operations
|
|
2974
|
-
// ═══════════════════════════════════════════════════════
|
|
2975
|
-
async addImport(filePath, importStatement) {
|
|
2976
|
-
try {
|
|
2977
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2978
|
-
const lastImport = sourceFile.getImportDeclarations().pop();
|
|
2979
|
-
const pos = lastImport ? lastImport.getEnd() : 0;
|
|
2980
|
-
sourceFile.insertText(pos, `
|
|
2981
|
-
${importStatement}`);
|
|
2982
|
-
this.organizeImports(filePath);
|
|
2983
|
-
await sourceFile.save();
|
|
2984
|
-
return true;
|
|
2985
|
-
} catch (error) {
|
|
2986
|
-
console.error(`Failed to add import:`, error);
|
|
2987
|
-
return false;
|
|
2988
|
-
}
|
|
2989
|
-
}
|
|
2990
|
-
async removeImport(filePath, modulePath) {
|
|
2991
|
-
try {
|
|
2992
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
2993
|
-
const importDecls = sourceFile.getImportDeclarations().filter((imp) => imp.getModuleSpecifierValue() === modulePath);
|
|
2994
|
-
if (importDecls.length === 0) {
|
|
2995
|
-
throw new Error(`Import from "${modulePath}" not found`);
|
|
2996
|
-
}
|
|
2997
|
-
importDecls.forEach((d) => d.remove());
|
|
2998
|
-
await sourceFile.save();
|
|
2999
|
-
return true;
|
|
3000
|
-
} catch (error) {
|
|
3001
|
-
console.error(`Failed to remove import from "${modulePath}":`, error);
|
|
3002
|
-
return false;
|
|
3003
|
-
}
|
|
3004
|
-
}
|
|
3005
|
-
async organizeImports(filePath) {
|
|
3006
|
-
try {
|
|
3007
|
-
const sourceFile = this.getSourceFile(filePath);
|
|
3008
|
-
sourceFile.organizeImports();
|
|
3009
|
-
const imports = sourceFile.getImportDeclarations();
|
|
3010
|
-
const importStructure = imports.map((i) => i.getStructure());
|
|
3011
|
-
importStructure.sort((a, b) => {
|
|
3012
|
-
return a.moduleSpecifier.localeCompare(b.moduleSpecifier);
|
|
3013
|
-
});
|
|
3014
|
-
imports.forEach((i) => i.remove());
|
|
3015
|
-
sourceFile.addImportDeclarations(importStructure);
|
|
3016
|
-
await sourceFile.save();
|
|
3017
|
-
return true;
|
|
3018
|
-
} catch (error) {
|
|
3019
|
-
console.error(`Failed to organize imports:`, error);
|
|
3020
|
-
return false;
|
|
3021
|
-
}
|
|
3022
|
-
}
|
|
3023
|
-
};
|
|
3024
|
-
|
|
3025
|
-
// src/core/ast-editing/editors/code-editor-factory.ts
|
|
3026
|
-
var CodeEditorFactory = class {
|
|
3027
|
-
static tsEditor = null;
|
|
3028
|
-
/**
|
|
3029
|
-
* Returns appropriate editor for the file, or null if AST editing not supported
|
|
3030
|
-
*/
|
|
3031
|
-
static getEditor(filePath) {
|
|
3032
|
-
const ext = path6.extname(filePath).toLowerCase();
|
|
3033
|
-
if ([".ts", ".tsx", ".js", ".jsx"].includes(ext)) {
|
|
3034
|
-
if (!this.tsEditor) {
|
|
3035
|
-
this.tsEditor = new TypeScriptEditor();
|
|
3036
|
-
}
|
|
3037
|
-
return this.tsEditor;
|
|
3038
|
-
}
|
|
3039
|
-
return null;
|
|
3040
|
-
}
|
|
3041
|
-
/**
|
|
3042
|
-
* Clear cached editors (useful for testing)
|
|
3043
|
-
*/
|
|
3044
|
-
static clearCache() {
|
|
3045
|
-
this.tsEditor = null;
|
|
3046
|
-
}
|
|
3047
|
-
};
|
|
3048
|
-
|
|
3049
|
-
// src/core/agents/agent-tools.ts
|
|
3050
2712
|
var execAsync = promisify(exec);
|
|
3051
|
-
function detectLineEnding(content) {
|
|
3052
|
-
const crlf = content.split("\r\n").length - 1;
|
|
3053
|
-
const lf = content.split("\n").length - 1 - crlf;
|
|
3054
|
-
return crlf > lf ? "\r\n" : "\n";
|
|
3055
|
-
}
|
|
3056
2713
|
function handleListFiles(dirPath) {
|
|
3057
2714
|
try {
|
|
3058
|
-
const fullPath =
|
|
2715
|
+
const fullPath = path6.resolve(process.cwd(), dirPath);
|
|
3059
2716
|
if (!fs6.existsSync(fullPath)) return `Error: Directory ${dirPath} does not exist.`;
|
|
3060
2717
|
const items = fs6.readdirSync(fullPath, { withFileTypes: true });
|
|
3061
2718
|
return items.map((item) => {
|
|
@@ -3065,54 +2722,6 @@ function handleListFiles(dirPath) {
|
|
|
3065
2722
|
return `Error listing files: ${e.message}`;
|
|
3066
2723
|
}
|
|
3067
2724
|
}
|
|
3068
|
-
function handleReadFile(filePath, showLineNumbers = true) {
|
|
3069
|
-
try {
|
|
3070
|
-
const fullPath = path7.resolve(process.cwd(), filePath);
|
|
3071
|
-
if (!fs6.existsSync(fullPath)) return `Error: File ${filePath} does not exist.`;
|
|
3072
|
-
const stats = fs6.statSync(fullPath);
|
|
3073
|
-
if (stats.size > 100 * 1024) return `Error: File too large to read (${stats.size} bytes). Limit is 100KB.`;
|
|
3074
|
-
const content = fs6.readFileSync(fullPath, "utf-8");
|
|
3075
|
-
if (showLineNumbers) {
|
|
3076
|
-
const lines = content.split("\n");
|
|
3077
|
-
return lines.map((line, idx) => `${idx + 1}: ${line}`).join("\n");
|
|
3078
|
-
}
|
|
3079
|
-
return content;
|
|
3080
|
-
} catch (e) {
|
|
3081
|
-
return `Error reading file: ${e.message}`;
|
|
3082
|
-
}
|
|
3083
|
-
}
|
|
3084
|
-
function replaceLineRange(filePath, startLine, endLine, newContent, tui2) {
|
|
3085
|
-
try {
|
|
3086
|
-
if (!fs6.existsSync(filePath)) {
|
|
3087
|
-
tui2.log.error(`\u274C File not found for modification: ${filePath}`);
|
|
3088
|
-
return false;
|
|
3089
|
-
}
|
|
3090
|
-
const currentFileContent = fs6.readFileSync(filePath, "utf-8");
|
|
3091
|
-
const lineEnding = detectLineEnding(currentFileContent);
|
|
3092
|
-
const lines = currentFileContent.split(lineEnding);
|
|
3093
|
-
if (startLine < 1 || startLine > lines.length) {
|
|
3094
|
-
tui2.log.error(`\u274C Invalid start line: ${startLine}. File has ${lines.length} lines.`);
|
|
3095
|
-
return false;
|
|
3096
|
-
}
|
|
3097
|
-
if (endLine < startLine || endLine > lines.length) {
|
|
3098
|
-
tui2.log.error(`\u274C Invalid end line: ${endLine}. Must be >= startLine and <= file length.`);
|
|
3099
|
-
return false;
|
|
3100
|
-
}
|
|
3101
|
-
const before = lines.slice(0, startLine - 1);
|
|
3102
|
-
const after = lines.slice(endLine);
|
|
3103
|
-
const newLines = newContent.split(lineEnding);
|
|
3104
|
-
const normalizedNewLines = newContent.replace(/\r\n/g, "\n").split("\n");
|
|
3105
|
-
const result = [...before, ...normalizedNewLines, ...after].join(lineEnding);
|
|
3106
|
-
const BOM = "\uFEFF";
|
|
3107
|
-
const finalContent = result.startsWith(BOM) ? result : BOM + result;
|
|
3108
|
-
fs6.writeFileSync(filePath, finalContent, { encoding: "utf-8" });
|
|
3109
|
-
tui2.log.success(`\u2705 Replaced lines ${startLine}-${endLine} in ${filePath}`);
|
|
3110
|
-
return true;
|
|
3111
|
-
} catch (e) {
|
|
3112
|
-
tui2.log.error(`\u274C Error replacing line range: ${e.message}`);
|
|
3113
|
-
return false;
|
|
3114
|
-
}
|
|
3115
|
-
}
|
|
3116
2725
|
function handleSearchFile(pattern) {
|
|
3117
2726
|
try {
|
|
3118
2727
|
const entries = fg.sync(pattern, { dot: true });
|
|
@@ -3139,7 +2748,7 @@ function handleSearchCode(globPattern, query, isRegex = false) {
|
|
|
3139
2748
|
for (const filePath of files) {
|
|
3140
2749
|
if (totalMatches >= MAX_MATCHES) break;
|
|
3141
2750
|
try {
|
|
3142
|
-
const fullPath =
|
|
2751
|
+
const fullPath = path6.resolve(process.cwd(), filePath);
|
|
3143
2752
|
const stats = fs6.statSync(fullPath);
|
|
3144
2753
|
if (stats.size > MAX_FILE_SIZE_BYTES) continue;
|
|
3145
2754
|
const content = fs6.readFileSync(fullPath, "utf-8");
|
|
@@ -3165,32 +2774,6 @@ ${results.join("\n")}`;
|
|
|
3165
2774
|
return `Error searching code: ${e.message}`;
|
|
3166
2775
|
}
|
|
3167
2776
|
}
|
|
3168
|
-
function startSmartReplace(filePath, newContent, targetContent, tui2) {
|
|
3169
|
-
if (!fs6.existsSync(filePath)) {
|
|
3170
|
-
tui2.log.error(`\u274C File not found for modification: ${filePath}`);
|
|
3171
|
-
return false;
|
|
3172
|
-
}
|
|
3173
|
-
const currentFileContent = fs6.readFileSync(filePath, "utf-8");
|
|
3174
|
-
const normalizedTarget = targetContent.replace(/\r\n/g, "\n");
|
|
3175
|
-
const normalizedContent = currentFileContent.replace(/\r\n/g, "\n");
|
|
3176
|
-
if (!normalizedContent.includes(normalizedTarget)) {
|
|
3177
|
-
tui2.log.error(`\u274C Target content not found in ${filePath} (checked with normalized line endings). Modification aborted.`);
|
|
3178
|
-
console.log(colors.dim("--- Target Content Expected ---"));
|
|
3179
|
-
console.log(targetContent.substring(0, 200) + "...");
|
|
3180
|
-
return false;
|
|
3181
|
-
}
|
|
3182
|
-
const occurrences = currentFileContent.split(targetContent).length - 1;
|
|
3183
|
-
if (occurrences > 1) {
|
|
3184
|
-
tui2.log.error(`\u274C Ambiguous target: Found ${occurrences} occurrences in ${filePath}. Modification aborted.`);
|
|
3185
|
-
return false;
|
|
3186
|
-
}
|
|
3187
|
-
const BOM = "\uFEFF";
|
|
3188
|
-
const updatedContent = currentFileContent.replace(targetContent, newContent);
|
|
3189
|
-
const finalContent = updatedContent.startsWith(BOM) ? updatedContent : BOM + updatedContent;
|
|
3190
|
-
fs6.writeFileSync(filePath, finalContent, { encoding: "utf-8" });
|
|
3191
|
-
tui2.log.success(`\u2705 Smart Replace Applied: ${filePath}`);
|
|
3192
|
-
return true;
|
|
3193
|
-
}
|
|
3194
2777
|
async function handleRunCommand(command) {
|
|
3195
2778
|
const { spawn } = await import("child_process");
|
|
3196
2779
|
try {
|
|
@@ -3238,458 +2821,95 @@ ${stdout}`);
|
|
|
3238
2821
|
return `Error launching command: ${e.message}`;
|
|
3239
2822
|
}
|
|
3240
2823
|
}
|
|
3241
|
-
|
|
3242
|
-
|
|
3243
|
-
|
|
3244
|
-
|
|
3245
|
-
|
|
3246
|
-
|
|
3247
|
-
|
|
3248
|
-
|
|
3249
|
-
|
|
3250
|
-
|
|
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";
|
|
3251
2848
|
}
|
|
3252
|
-
const parent = path7.dirname(dir);
|
|
3253
|
-
if (parent === dir) break;
|
|
3254
|
-
dir = parent;
|
|
3255
2849
|
}
|
|
3256
|
-
} catch (e) {
|
|
3257
2850
|
}
|
|
3258
|
-
|
|
3259
|
-
|
|
3260
|
-
|
|
2851
|
+
isSubagentActive(id) {
|
|
2852
|
+
return this.subagents.get(id)?.status === "running";
|
|
2853
|
+
}
|
|
2854
|
+
hasSubagent(id) {
|
|
2855
|
+
return this.subagents.has(id);
|
|
3261
2856
|
}
|
|
3262
|
-
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
|
|
3266
|
-
|
|
3267
|
-
if (
|
|
3268
|
-
|
|
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;
|
|
3269
2864
|
}
|
|
3270
|
-
const sgCmd = resolveAstGrepCommand();
|
|
3271
|
-
const cmd = `${sgCmd} run -p "${pattern}" -l ${language} --json ${filePath}`;
|
|
3272
|
-
tui2.log.info(`\u{1F50D} [AST-GREP] Searching: ${cmd}`);
|
|
3273
|
-
return new Promise((resolve2) => {
|
|
3274
|
-
const child = spawn(cmd, {
|
|
3275
|
-
shell: true,
|
|
3276
|
-
stdio: ["ignore", "pipe", "pipe"],
|
|
3277
|
-
cwd: process.cwd(),
|
|
3278
|
-
env: { ...process.env, NO_COLOR: "true" }
|
|
3279
|
-
// Avoid ANSI codes in JSON output
|
|
3280
|
-
});
|
|
3281
|
-
let stdout = "";
|
|
3282
|
-
let stderr = "";
|
|
3283
|
-
child.stdout.on("data", (data) => stdout += data.toString());
|
|
3284
|
-
child.stderr.on("data", (data) => stderr += data.toString());
|
|
3285
|
-
child.on("close", (code) => {
|
|
3286
|
-
if (code === 0 && stdout) {
|
|
3287
|
-
resolve2(stdout);
|
|
3288
|
-
} else if (code === 1 && !stderr) {
|
|
3289
|
-
resolve2("No structural matches found.");
|
|
3290
|
-
} else {
|
|
3291
|
-
if (!stdout && !stderr) resolve2("No structural matches found.");
|
|
3292
|
-
else {
|
|
3293
|
-
tui2.log.error(`\u274C ast-grep search error (code ${code}): ${stderr}`);
|
|
3294
|
-
resolve2(`Error executing ast-grep search: ${stderr || stdout}`);
|
|
3295
|
-
}
|
|
3296
|
-
}
|
|
3297
|
-
});
|
|
3298
|
-
child.on("error", (err) => {
|
|
3299
|
-
resolve2(`Error executing ast-grep search: ${err.message}`);
|
|
3300
|
-
});
|
|
3301
|
-
});
|
|
3302
|
-
} catch (e) {
|
|
3303
|
-
tui2.log.error(`\u274C ast-grep search exception: ${e.message}`);
|
|
3304
|
-
return `Error executing ast-grep search: ${e.message}`;
|
|
3305
2865
|
}
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
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 [];
|
|
3313
2877
|
}
|
|
3314
|
-
const
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
const
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
child.stderr.on("data", (data) => stderr += data.toString());
|
|
3325
|
-
child.on("close", (code) => {
|
|
3326
|
-
if (code === 0) {
|
|
3327
|
-
tui2.log.success(`\u2705 AST Rewrite applied to ${filePath}`);
|
|
3328
|
-
resolve2(true);
|
|
3329
|
-
} else {
|
|
3330
|
-
tui2.log.error(`\u274C AST Rewrite failed (code ${code}): ${stderr}`);
|
|
3331
|
-
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);
|
|
3332
2888
|
}
|
|
3333
|
-
})
|
|
3334
|
-
|
|
3335
|
-
|
|
3336
|
-
|
|
3337
|
-
})
|
|
3338
|
-
|
|
3339
|
-
|
|
3340
|
-
|
|
3341
|
-
return false;
|
|
2889
|
+
} catch (e) {
|
|
2890
|
+
}
|
|
2891
|
+
try {
|
|
2892
|
+
fs7.unlinkSync(filePath);
|
|
2893
|
+
} catch (e) {
|
|
2894
|
+
}
|
|
2895
|
+
}
|
|
2896
|
+
return messages;
|
|
3342
2897
|
}
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
if (!editor) {
|
|
3348
|
-
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 [];
|
|
3349
2902
|
}
|
|
3350
|
-
const
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
|
|
3355
|
-
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
|
|
3360
|
-
if (cls.implementsInterfaces.length > 0) {
|
|
3361
|
-
output += ` implements ${cls.implementsInterfaces.join(", ")}`;
|
|
3362
|
-
}
|
|
3363
|
-
output += `
|
|
3364
|
-
`;
|
|
3365
|
-
if (cls.decorators.length > 0) {
|
|
3366
|
-
output += ` Decorators: ${cls.decorators.join(", ")}
|
|
3367
|
-
`;
|
|
3368
|
-
}
|
|
3369
|
-
if (cls.properties.length > 0) {
|
|
3370
|
-
output += ` Properties:
|
|
3371
|
-
`;
|
|
3372
|
-
cls.properties.forEach((prop) => {
|
|
3373
|
-
output += ` - ${prop.visibility} ${prop.name}: ${prop.type}
|
|
3374
|
-
`;
|
|
3375
|
-
});
|
|
3376
|
-
}
|
|
3377
|
-
if (cls.methods.length > 0) {
|
|
3378
|
-
output += ` Methods:
|
|
3379
|
-
`;
|
|
3380
|
-
cls.methods.forEach((method) => {
|
|
3381
|
-
const params = method.parameters.map((p) => `${p.name}: ${p.type}`).join(", ");
|
|
3382
|
-
output += ` - ${method.visibility} ${method.name}(${params}): ${method.returnType}
|
|
3383
|
-
`;
|
|
3384
|
-
});
|
|
3385
|
-
}
|
|
3386
|
-
output += `
|
|
3387
|
-
`;
|
|
3388
|
-
});
|
|
3389
|
-
}
|
|
3390
|
-
if (structure.interfaces.length > 0) {
|
|
3391
|
-
output += `INTERFACES:
|
|
3392
|
-
`;
|
|
3393
|
-
structure.interfaces.forEach((iface) => {
|
|
3394
|
-
output += ` - ${iface.name}
|
|
3395
|
-
`;
|
|
3396
|
-
iface.properties.forEach((prop) => {
|
|
3397
|
-
output += ` ${prop.name}: ${prop.type}
|
|
3398
|
-
`;
|
|
3399
|
-
});
|
|
3400
|
-
});
|
|
3401
|
-
output += `
|
|
3402
|
-
`;
|
|
3403
|
-
}
|
|
3404
|
-
if (structure.functions.length > 0) {
|
|
3405
|
-
output += `FUNCTIONS:
|
|
3406
|
-
`;
|
|
3407
|
-
structure.functions.forEach((fn) => {
|
|
3408
|
-
const params = fn.parameters.map((p) => `${p.name}: ${p.type}`).join(", ");
|
|
3409
|
-
output += ` - ${fn.name}(${params}): ${fn.returnType}
|
|
3410
|
-
`;
|
|
3411
|
-
});
|
|
3412
|
-
output += `
|
|
3413
|
-
`;
|
|
3414
|
-
}
|
|
3415
|
-
if (structure.imports.length > 0) {
|
|
3416
|
-
output += `IMPORTS:
|
|
3417
|
-
`;
|
|
3418
|
-
structure.imports.forEach((imp) => {
|
|
3419
|
-
output += ` - from "${imp.modulePath}": ${imp.namedImports.join(", ")}
|
|
3420
|
-
`;
|
|
3421
|
-
});
|
|
3422
|
-
}
|
|
3423
|
-
return output;
|
|
3424
|
-
} catch (error) {
|
|
3425
|
-
return `[AST Error] ${error.message}`;
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
async function astAddMethod(filePath, className, methodCode) {
|
|
3429
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3430
|
-
if (!editor) {
|
|
3431
|
-
throw new Error(`No AST editor available for ${filePath}`);
|
|
3432
|
-
}
|
|
3433
|
-
return await editor.addMethod(filePath, className, methodCode);
|
|
3434
|
-
}
|
|
3435
|
-
async function astGetMethod(filePath, className, methodName) {
|
|
3436
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3437
|
-
if (!editor) {
|
|
3438
|
-
throw new Error(`No AST editor available for ${filePath}`);
|
|
3439
|
-
}
|
|
3440
|
-
const methodContent = await editor.getMethod(filePath, className, methodName);
|
|
3441
|
-
if (!methodContent) {
|
|
3442
|
-
return `Method "${methodName}" not found in class "${className}"`;
|
|
3443
|
-
}
|
|
3444
|
-
return methodContent;
|
|
3445
|
-
}
|
|
3446
|
-
async function astAddClass(filePath, className, extendsClass, implementsInterfaces) {
|
|
3447
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3448
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3449
|
-
return await editor.addClass(filePath, className, { extendsClass, implementsInterfaces });
|
|
3450
|
-
}
|
|
3451
|
-
async function astAddProperty(filePath, className, propertyCode) {
|
|
3452
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3453
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3454
|
-
return await editor.addProperty(filePath, className, propertyCode);
|
|
3455
|
-
}
|
|
3456
|
-
async function astGetProperty(filePath, className, propertyName) {
|
|
3457
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3458
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3459
|
-
const propContent = await editor.getProperty(filePath, className, propertyName);
|
|
3460
|
-
if (!propContent) {
|
|
3461
|
-
return `Property "${propertyName}" not found in class "${className}"`;
|
|
3462
|
-
}
|
|
3463
|
-
return propContent;
|
|
3464
|
-
}
|
|
3465
|
-
async function astModifyProperty(filePath, className, propertyName, propertyCode) {
|
|
3466
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3467
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3468
|
-
return await editor.modifyProperty(filePath, className, propertyName, propertyCode);
|
|
3469
|
-
}
|
|
3470
|
-
async function astRemoveProperty(filePath, className, propertyName) {
|
|
3471
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3472
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3473
|
-
return await editor.removeProperty(filePath, className, propertyName);
|
|
3474
|
-
}
|
|
3475
|
-
async function astModifyMethod(filePath, className, methodName, newBody) {
|
|
3476
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3477
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3478
|
-
return await editor.modifyMethod(filePath, className, methodName, newBody);
|
|
3479
|
-
}
|
|
3480
|
-
async function astRemoveMethod(filePath, className, methodName) {
|
|
3481
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3482
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3483
|
-
return await editor.removeMethod(filePath, className, methodName);
|
|
3484
|
-
}
|
|
3485
|
-
async function astAddDecorator(filePath, className, decoratorCode) {
|
|
3486
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3487
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3488
|
-
return await editor.addDecorator(filePath, className, decoratorCode);
|
|
3489
|
-
}
|
|
3490
|
-
async function astAddInterface(filePath, interfaceCode) {
|
|
3491
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3492
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3493
|
-
return await editor.addInterface(filePath, interfaceCode);
|
|
3494
|
-
}
|
|
3495
|
-
async function astAddTypeAlias(filePath, typeCode) {
|
|
3496
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3497
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3498
|
-
return await editor.addTypeAlias(filePath, typeCode);
|
|
3499
|
-
}
|
|
3500
|
-
async function astAddFunction(filePath, functionCode) {
|
|
3501
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3502
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3503
|
-
return await editor.addFunction(filePath, functionCode);
|
|
3504
|
-
}
|
|
3505
|
-
async function astRemoveFunction(filePath, functionName) {
|
|
3506
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3507
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3508
|
-
return await editor.removeFunction(filePath, functionName);
|
|
3509
|
-
}
|
|
3510
|
-
async function astAddImport(filePath, importStatement) {
|
|
3511
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3512
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3513
|
-
return await editor.addImport(filePath, importStatement);
|
|
3514
|
-
}
|
|
3515
|
-
async function astRemoveImport(filePath, modulePath) {
|
|
3516
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3517
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3518
|
-
return await editor.removeImport(filePath, modulePath);
|
|
3519
|
-
}
|
|
3520
|
-
async function astOrganizeImports(filePath) {
|
|
3521
|
-
const editor = CodeEditorFactory.getEditor(filePath);
|
|
3522
|
-
if (!editor) throw new Error(`AST editing not supported for: ${filePath}`);
|
|
3523
|
-
return await editor.organizeImports(filePath);
|
|
3524
|
-
}
|
|
3525
|
-
|
|
3526
|
-
// src/core/workflow/skill-manager.ts
|
|
3527
|
-
import fs7 from "fs/promises";
|
|
3528
|
-
import path8 from "path";
|
|
3529
|
-
import os2 from "os";
|
|
3530
|
-
var SkillManager = class {
|
|
3531
|
-
activeSkills = /* @__PURE__ */ new Set();
|
|
3532
|
-
skillPrompts = /* @__PURE__ */ new Map();
|
|
3533
|
-
async loadSkillFromFile(filePath) {
|
|
3534
|
-
const content = await fs7.readFile(filePath, "utf-8");
|
|
3535
|
-
const cleanContent = content.replace(/^---[\s\S]*?---\s*/, "");
|
|
3536
|
-
return cleanContent;
|
|
3537
|
-
}
|
|
3538
|
-
async activateSkill(skillName) {
|
|
3539
|
-
if (this.activeSkills.has(skillName)) {
|
|
3540
|
-
return `Skill ${skillName} is already active.`;
|
|
3541
|
-
}
|
|
3542
|
-
this.reset();
|
|
3543
|
-
const globalPath = path8.join(os2.homedir(), ".shark", "skills", skillName, "SKILL.md");
|
|
3544
|
-
const localPath = path8.join(process.cwd(), ".agents", "skills", skillName, "SKILL.md");
|
|
3545
|
-
let skillPath = "";
|
|
3546
|
-
try {
|
|
3547
|
-
await fs7.access(localPath);
|
|
3548
|
-
skillPath = localPath;
|
|
3549
|
-
} catch {
|
|
3550
|
-
try {
|
|
3551
|
-
await fs7.access(globalPath);
|
|
3552
|
-
skillPath = globalPath;
|
|
3553
|
-
} catch {
|
|
3554
|
-
throw new Error(`Skill '${skillName}' not found globally or locally.`);
|
|
3555
|
-
}
|
|
3556
|
-
}
|
|
3557
|
-
const prompt = await this.loadSkillFromFile(skillPath);
|
|
3558
|
-
this.activeSkills.add(skillName);
|
|
3559
|
-
this.skillPrompts.set(skillName, prompt);
|
|
3560
|
-
return prompt;
|
|
3561
|
-
}
|
|
3562
|
-
getSystemInstructionExtension() {
|
|
3563
|
-
if (this.activeSkills.size === 0) return "";
|
|
3564
|
-
let extension = "\n\n<EXTREMELY_IMPORTANT>\n";
|
|
3565
|
-
for (const [name, prompt] of this.skillPrompts.entries()) {
|
|
3566
|
-
extension += `
|
|
3567
|
-
--- ACTIVE SKILL: ${name} ---
|
|
3568
|
-
${prompt}
|
|
3569
|
-
`;
|
|
3570
|
-
}
|
|
3571
|
-
extension += "\n</EXTREMELY_IMPORTANT>\n";
|
|
3572
|
-
return extension;
|
|
3573
|
-
}
|
|
3574
|
-
async listAvailableSkills() {
|
|
3575
|
-
const globalSkillsDir = path8.join(os2.homedir(), ".shark", "skills");
|
|
3576
|
-
const localSkillsDir = path8.join(process.cwd(), ".agents", "skills");
|
|
3577
|
-
const skillNames = /* @__PURE__ */ new Set();
|
|
3578
|
-
const readDir = async (dir) => {
|
|
3579
|
-
try {
|
|
3580
|
-
const entries = await fs7.readdir(dir, { withFileTypes: true });
|
|
3581
|
-
for (const entry of entries) {
|
|
3582
|
-
if (entry.isDirectory()) {
|
|
3583
|
-
const skillMdPath = path8.join(dir, entry.name, "SKILL.md");
|
|
3584
|
-
try {
|
|
3585
|
-
await fs7.access(skillMdPath);
|
|
3586
|
-
skillNames.add(entry.name);
|
|
3587
|
-
} catch {
|
|
3588
|
-
}
|
|
3589
|
-
}
|
|
3590
|
-
}
|
|
3591
|
-
} catch {
|
|
3592
|
-
}
|
|
3593
|
-
};
|
|
3594
|
-
await readDir(globalSkillsDir);
|
|
3595
|
-
await readDir(localSkillsDir);
|
|
3596
|
-
return Array.from(skillNames).sort();
|
|
3597
|
-
}
|
|
3598
|
-
reset() {
|
|
3599
|
-
this.activeSkills.clear();
|
|
3600
|
-
this.skillPrompts.clear();
|
|
3601
|
-
}
|
|
3602
|
-
};
|
|
3603
|
-
var skillManager = new SkillManager();
|
|
3604
|
-
|
|
3605
|
-
// src/core/workflow/subagent-manager.ts
|
|
3606
|
-
import crypto2 from "crypto";
|
|
3607
|
-
import fs8 from "fs";
|
|
3608
|
-
import path9 from "path";
|
|
3609
|
-
import { fork } from "child_process";
|
|
3610
|
-
import { fileURLToPath as fileURLToPath2 } from "url";
|
|
3611
|
-
var SubagentManager = class {
|
|
3612
|
-
subagents = /* @__PURE__ */ new Map();
|
|
3613
|
-
customTypes = /* @__PURE__ */ new Map();
|
|
3614
|
-
messageSeq = 0;
|
|
3615
|
-
registerSubagent(id, type, role, parentId) {
|
|
3616
|
-
this.subagents.set(id, { id, type, role, status: "running", parentId });
|
|
3617
|
-
}
|
|
3618
|
-
terminateSubagent(id, success = true, isCancelled = false) {
|
|
3619
|
-
const state = this.subagents.get(id);
|
|
3620
|
-
if (state) {
|
|
3621
|
-
if (state.status === "cancelled") {
|
|
3622
|
-
return;
|
|
3623
|
-
}
|
|
3624
|
-
if (isCancelled) {
|
|
3625
|
-
state.status = "cancelled";
|
|
3626
|
-
} else {
|
|
3627
|
-
state.status = success ? "completed" : "failed";
|
|
3628
|
-
}
|
|
3629
|
-
}
|
|
3630
|
-
}
|
|
3631
|
-
isSubagentActive(id) {
|
|
3632
|
-
return this.subagents.get(id)?.status === "running";
|
|
3633
|
-
}
|
|
3634
|
-
hasSubagent(id) {
|
|
3635
|
-
return this.subagents.has(id);
|
|
3636
|
-
}
|
|
3637
|
-
getSubagentState(id) {
|
|
3638
|
-
return this.subagents.get(id);
|
|
3639
|
-
}
|
|
3640
|
-
updateSubagentSummary(id, summary) {
|
|
3641
|
-
const state = this.subagents.get(id);
|
|
3642
|
-
if (state) {
|
|
3643
|
-
state.summary = summary;
|
|
3644
|
-
}
|
|
3645
|
-
}
|
|
3646
|
-
sendMessage(recipient, message) {
|
|
3647
|
-
const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", recipient);
|
|
3648
|
-
fs8.mkdirSync(mailboxDir, { recursive: true });
|
|
3649
|
-
const seq = (this.messageSeq++).toString().padStart(6, "0");
|
|
3650
|
-
const filePath = path9.join(mailboxDir, `${Date.now()}-${seq}-${crypto2.randomUUID()}.json`);
|
|
3651
|
-
fs8.writeFileSync(filePath, JSON.stringify({ message }), "utf-8");
|
|
3652
|
-
}
|
|
3653
|
-
retrieveMessages(id) {
|
|
3654
|
-
const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", id);
|
|
3655
|
-
if (!fs8.existsSync(mailboxDir)) {
|
|
3656
|
-
return [];
|
|
3657
|
-
}
|
|
3658
|
-
const files = fs8.readdirSync(mailboxDir);
|
|
3659
|
-
files.sort();
|
|
3660
|
-
const messages = [];
|
|
3661
|
-
for (const file of files) {
|
|
3662
|
-
const filePath = path9.join(mailboxDir, file);
|
|
3663
|
-
try {
|
|
3664
|
-
const content = fs8.readFileSync(filePath, "utf-8");
|
|
3665
|
-
const data = JSON.parse(content);
|
|
3666
|
-
if (data && typeof data.message === "string") {
|
|
3667
|
-
messages.push(data.message);
|
|
3668
|
-
}
|
|
3669
|
-
} catch (e) {
|
|
3670
|
-
}
|
|
3671
|
-
try {
|
|
3672
|
-
fs8.unlinkSync(filePath);
|
|
3673
|
-
} catch (e) {
|
|
3674
|
-
}
|
|
3675
|
-
}
|
|
3676
|
-
return messages;
|
|
3677
|
-
}
|
|
3678
|
-
peekMessages(id) {
|
|
3679
|
-
const mailboxDir = path9.resolve(process.cwd(), ".shark", "mailbox", id);
|
|
3680
|
-
if (!fs8.existsSync(mailboxDir)) {
|
|
3681
|
-
return [];
|
|
3682
|
-
}
|
|
3683
|
-
const files = fs8.readdirSync(mailboxDir);
|
|
3684
|
-
files.sort();
|
|
3685
|
-
const messages = [];
|
|
3686
|
-
for (const file of files) {
|
|
3687
|
-
const filePath = path9.join(mailboxDir, file);
|
|
3688
|
-
try {
|
|
3689
|
-
const content = fs8.readFileSync(filePath, "utf-8");
|
|
3690
|
-
const data = JSON.parse(content);
|
|
3691
|
-
if (data && typeof data.message === "string") {
|
|
3692
|
-
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);
|
|
3693
2913
|
}
|
|
3694
2914
|
} catch (e) {
|
|
3695
2915
|
}
|
|
@@ -3712,12 +2932,12 @@ var SubagentManager = class {
|
|
|
3712
2932
|
throw new Error("Invalid subagent ID format");
|
|
3713
2933
|
}
|
|
3714
2934
|
const projectRoot = process.cwd();
|
|
3715
|
-
const logFile =
|
|
3716
|
-
if (!
|
|
2935
|
+
const logFile = path7.resolve(projectRoot, "_sharkrc", "history", `subagent-${id}-console.log`);
|
|
2936
|
+
if (!fs7.existsSync(logFile)) {
|
|
3717
2937
|
return "No console logs found for this subagent.";
|
|
3718
2938
|
}
|
|
3719
2939
|
try {
|
|
3720
|
-
let content =
|
|
2940
|
+
let content = fs7.readFileSync(logFile, "utf-8");
|
|
3721
2941
|
if (content.endsWith("\n")) {
|
|
3722
2942
|
content = content.slice(0, -1);
|
|
3723
2943
|
}
|
|
@@ -3760,32 +2980,32 @@ var SubagentManager = class {
|
|
|
3760
2980
|
async invokeSubagents(subagents, parentId, parentQueue) {
|
|
3761
2981
|
const invoked = [];
|
|
3762
2982
|
for (const sub of subagents) {
|
|
3763
|
-
const id = `subagent-${
|
|
2983
|
+
const id = `subagent-${crypto3.randomUUID()}`;
|
|
3764
2984
|
this.registerSubagent(id, sub.TypeName, sub.Role, parentId);
|
|
3765
2985
|
const promise = (async () => {
|
|
3766
2986
|
try {
|
|
3767
2987
|
const projectRoot = process.cwd();
|
|
3768
|
-
const
|
|
3769
|
-
const
|
|
3770
|
-
let packageRoot =
|
|
2988
|
+
const __filename3 = fileURLToPath(import.meta.url);
|
|
2989
|
+
const __dirname3 = path7.dirname(__filename3);
|
|
2990
|
+
let packageRoot = __dirname3;
|
|
3771
2991
|
while (true) {
|
|
3772
|
-
const pkgPath =
|
|
3773
|
-
if (
|
|
2992
|
+
const pkgPath = path7.join(packageRoot, "package.json");
|
|
2993
|
+
if (fs7.existsSync(pkgPath)) {
|
|
3774
2994
|
try {
|
|
3775
|
-
const pkg = JSON.parse(
|
|
2995
|
+
const pkg = JSON.parse(fs7.readFileSync(pkgPath, "utf-8"));
|
|
3776
2996
|
if (pkg && pkg.name === "shark-ai") {
|
|
3777
2997
|
break;
|
|
3778
2998
|
}
|
|
3779
2999
|
} catch (e) {
|
|
3780
3000
|
}
|
|
3781
3001
|
}
|
|
3782
|
-
const parent =
|
|
3002
|
+
const parent = path7.dirname(packageRoot);
|
|
3783
3003
|
if (parent === packageRoot) {
|
|
3784
3004
|
break;
|
|
3785
3005
|
}
|
|
3786
3006
|
packageRoot = parent;
|
|
3787
3007
|
}
|
|
3788
|
-
const pathToSharkJs =
|
|
3008
|
+
const pathToSharkJs = path7.resolve(packageRoot, "dist", "bin", "shark.js");
|
|
3789
3009
|
const customType = this.customTypes.get(sub.TypeName);
|
|
3790
3010
|
let customContext = `Voc\xEA est\xE1 executando em modo SUBAGENTE.
|
|
3791
3011
|
`;
|
|
@@ -3818,10 +3038,10 @@ var SubagentManager = class {
|
|
|
3818
3038
|
if (state2) {
|
|
3819
3039
|
state2.childProcess = child;
|
|
3820
3040
|
}
|
|
3821
|
-
const historyDir =
|
|
3822
|
-
|
|
3823
|
-
const consoleLogFile =
|
|
3824
|
-
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" });
|
|
3825
3045
|
if (child.stdout) {
|
|
3826
3046
|
child.stdout.pipe(logStream);
|
|
3827
3047
|
}
|
|
@@ -3844,8 +3064,8 @@ Subagent ${sub.Role} (${id}) cancelled.`);
|
|
|
3844
3064
|
} else if (!success) {
|
|
3845
3065
|
this.updateSubagentSummary(id, "Failed");
|
|
3846
3066
|
const fallbackMsg = `[Subagent Notification] Subagent ${sub.Role} (${id}) has finished with status: FAILED. Summary: Subagent process exited with code ${exitCode}`;
|
|
3847
|
-
const mailboxDir =
|
|
3848
|
-
const hasMessages =
|
|
3067
|
+
const mailboxDir = path7.resolve(projectRoot, ".shark", "mailbox", parentId);
|
|
3068
|
+
const hasMessages = fs7.existsSync(mailboxDir) && fs7.readdirSync(mailboxDir).length > 0;
|
|
3849
3069
|
if (!hasMessages) {
|
|
3850
3070
|
this.sendMessage(parentId, fallbackMsg);
|
|
3851
3071
|
}
|
|
@@ -3947,22 +3167,30 @@ var MessageQueue = class {
|
|
|
3947
3167
|
};
|
|
3948
3168
|
|
|
3949
3169
|
// src/core/agents/developer-agent.ts
|
|
3170
|
+
var activeOnCommandHandler = void 0;
|
|
3950
3171
|
async function promptUser(message, initialValue, placeholder, prefix = "") {
|
|
3951
3172
|
let userReply = await tui.text({ message: `${prefix}${message}`, initialValue, placeholder });
|
|
3952
|
-
while (userReply
|
|
3953
|
-
|
|
3954
|
-
|
|
3955
|
-
|
|
3956
|
-
|
|
3957
|
-
|
|
3958
|
-
const
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
await
|
|
3964
|
-
|
|
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
|
+
}
|
|
3965
3192
|
}
|
|
3193
|
+
handled = true;
|
|
3966
3194
|
}
|
|
3967
3195
|
userReply = await tui.text({
|
|
3968
3196
|
message: `${prefix}${message}`,
|
|
@@ -3976,7 +3204,7 @@ async function waitForInputOrNotification(queue, promptMessage = "Your answer:",
|
|
|
3976
3204
|
let cancelled = false;
|
|
3977
3205
|
let resolvePromptPromise = null;
|
|
3978
3206
|
let timerId = null;
|
|
3979
|
-
const
|
|
3207
|
+
const promises2 = [];
|
|
3980
3208
|
if (!isAuto) {
|
|
3981
3209
|
const promptPromise = new Promise((resolve2) => {
|
|
3982
3210
|
resolvePromptPromise = resolve2;
|
|
@@ -3995,10 +3223,10 @@ async function waitForInputOrNotification(queue, promptMessage = "Your answer:",
|
|
|
3995
3223
|
}
|
|
3996
3224
|
};
|
|
3997
3225
|
runPrompt();
|
|
3998
|
-
|
|
3226
|
+
promises2.push(promptPromise);
|
|
3999
3227
|
}
|
|
4000
3228
|
const queuePromise = queue.next();
|
|
4001
|
-
|
|
3229
|
+
promises2.push(queuePromise);
|
|
4002
3230
|
if (timeoutMs !== void 0 && timeoutMs !== null) {
|
|
4003
3231
|
const timeoutPromise = new Promise((resolve2) => {
|
|
4004
3232
|
timerId = setTimeout(() => {
|
|
@@ -4012,9 +3240,9 @@ async function waitForInputOrNotification(queue, promptMessage = "Your answer:",
|
|
|
4012
3240
|
});
|
|
4013
3241
|
}, timeoutMs);
|
|
4014
3242
|
});
|
|
4015
|
-
|
|
3243
|
+
promises2.push(timeoutPromise);
|
|
4016
3244
|
}
|
|
4017
|
-
const winner = await Promise.race(
|
|
3245
|
+
const winner = await Promise.race(promises2);
|
|
4018
3246
|
if (timerId) {
|
|
4019
3247
|
clearTimeout(timerId);
|
|
4020
3248
|
}
|
|
@@ -4071,11 +3299,11 @@ async function interactiveDeveloperAgent(options = {}) {
|
|
|
4071
3299
|
message: (msg) => tui.log.message(`${subagentPrefix}${msg}`)
|
|
4072
3300
|
};
|
|
4073
3301
|
let contextContent = "";
|
|
4074
|
-
const defaultContextPath =
|
|
4075
|
-
const specificContextPath = options.context ?
|
|
4076
|
-
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)) {
|
|
4077
3305
|
try {
|
|
4078
|
-
contextContent =
|
|
3306
|
+
contextContent = fs8.readFileSync(specificContextPath, "utf-8");
|
|
4079
3307
|
} catch (e) {
|
|
4080
3308
|
log.warning(`Failed to read context file: ${e}`);
|
|
4081
3309
|
}
|
|
@@ -4121,6 +3349,41 @@ Your goal is to address the user's request:
|
|
|
4121
3349
|
let finalSummary = "";
|
|
4122
3350
|
const conversationKey = options.taskId ? `dev_agent_${options.taskId}` : `dev_agent_${Date.now()}`;
|
|
4123
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;
|
|
4124
3387
|
const spinner = tui.spinner();
|
|
4125
3388
|
const handleCleanupSignal = (exitCode) => {
|
|
4126
3389
|
const currentId = options.taskId || "parent";
|
|
@@ -4171,17 +3434,35 @@ ${mailboxMessages.map((m) => `- ${m}`).join("\n")}
|
|
|
4171
3434
|
`;
|
|
4172
3435
|
currentTurnPrompt += panel;
|
|
4173
3436
|
}
|
|
4174
|
-
const promptToSend = currentTurnPrompt
|
|
3437
|
+
const promptToSend = currentTurnPrompt;
|
|
4175
3438
|
try {
|
|
4176
3439
|
const activeSubagents = subagentManager.getActiveSubagents();
|
|
4177
3440
|
const activeCount = activeSubagents.length;
|
|
4178
3441
|
const spinnerText = activeCount > 0 ? `\u{1F988} Shark Dev working... (Active subagents: ${activeCount})` : "\u{1F988} Shark Dev working...";
|
|
4179
3442
|
spinner.start(spinnerText);
|
|
4180
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
|
+
}
|
|
4181
3461
|
const provider = ProviderResolver.getProvider("developer_agent");
|
|
4182
3462
|
const response = await provider.streamChat(promptToSend, {
|
|
4183
3463
|
conversationId: existingConversationId,
|
|
4184
3464
|
agentType: "developer_agent",
|
|
3465
|
+
searchQuery: nextPrompt,
|
|
4185
3466
|
onChunk: () => {
|
|
4186
3467
|
}
|
|
4187
3468
|
});
|
|
@@ -4351,12 +3632,12 @@ ${content}`;
|
|
|
4351
3632
|
}
|
|
4352
3633
|
if (approved) {
|
|
4353
3634
|
try {
|
|
4354
|
-
const resolvedPath =
|
|
4355
|
-
const dir =
|
|
4356
|
-
if (!
|
|
4357
|
-
|
|
3635
|
+
const resolvedPath = path8.resolve(projectRoot, filePath);
|
|
3636
|
+
const dir = path8.dirname(resolvedPath);
|
|
3637
|
+
if (!fs8.existsSync(dir)) {
|
|
3638
|
+
fs8.mkdirSync(dir, { recursive: true });
|
|
4358
3639
|
}
|
|
4359
|
-
|
|
3640
|
+
fs8.writeFileSync(resolvedPath, action.content || "", "utf-8");
|
|
4360
3641
|
resultMsg = `[Action create_file(${filePath}) Success]`;
|
|
4361
3642
|
} catch (e) {
|
|
4362
3643
|
resultMsg = `[Action create_file(${filePath}) Failed]: ${e.message}`;
|
|
@@ -4373,9 +3654,9 @@ ${content}`;
|
|
|
4373
3654
|
}
|
|
4374
3655
|
if (approved) {
|
|
4375
3656
|
try {
|
|
4376
|
-
const resolvedPath =
|
|
4377
|
-
if (
|
|
4378
|
-
|
|
3657
|
+
const resolvedPath = path8.resolve(projectRoot, filePath);
|
|
3658
|
+
if (fs8.existsSync(resolvedPath)) {
|
|
3659
|
+
fs8.rmSync(resolvedPath, { force: true });
|
|
4379
3660
|
}
|
|
4380
3661
|
resultMsg = `[Action delete_file(${filePath}) Success]`;
|
|
4381
3662
|
} catch (e) {
|
|
@@ -4704,6 +3985,7 @@ ${detailedContent}`
|
|
|
4704
3985
|
log.success("\u2705 Task Scope Completed");
|
|
4705
3986
|
return finalResult;
|
|
4706
3987
|
} finally {
|
|
3988
|
+
activeOnCommandHandler = void 0;
|
|
4707
3989
|
process.off("SIGINT", sigIntHandler);
|
|
4708
3990
|
process.off("SIGTERM", sigTermHandler);
|
|
4709
3991
|
const currentId = options.taskId || "parent";
|
|
@@ -4718,11 +4000,30 @@ ${detailedContent}`
|
|
|
4718
4000
|
}
|
|
4719
4001
|
|
|
4720
4002
|
// src/commands/dev.ts
|
|
4721
|
-
|
|
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) => {
|
|
4722
4010
|
if (options.exportSchema) {
|
|
4723
4011
|
console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
|
|
4724
4012
|
return;
|
|
4725
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
|
+
}
|
|
4726
4027
|
try {
|
|
4727
4028
|
const result = await interactiveDeveloperAgent({
|
|
4728
4029
|
taskInstruction: options.task,
|
|
@@ -4740,996 +4041,534 @@ var devCommand = new Command2("dev").description("Starts the Shark Developer Age
|
|
|
4740
4041
|
}
|
|
4741
4042
|
});
|
|
4742
4043
|
|
|
4743
|
-
// src/commands/
|
|
4044
|
+
// src/commands/export-schema.ts
|
|
4744
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
|
+
});
|
|
4049
|
+
|
|
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
|
+
});
|
|
4745
4055
|
|
|
4746
|
-
// src/
|
|
4747
|
-
import
|
|
4748
|
-
import
|
|
4749
|
-
|
|
4750
|
-
|
|
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");
|
|
4751
4068
|
try {
|
|
4752
|
-
const result = await handleRunCommand(`npx tsc --noEmit --skipLibCheck ${filePath}`);
|
|
4753
|
-
if (result.trim() === "" || !result.includes("error TS")) {
|
|
4754
|
-
return { valid: true };
|
|
4755
|
-
}
|
|
4756
|
-
return { valid: false, error: result };
|
|
4757
|
-
} catch (e) {
|
|
4758
|
-
return { valid: false, error: e.message || "TypeScript validation failed" };
|
|
4759
|
-
}
|
|
4760
|
-
}
|
|
4761
|
-
function getAgentId(overrideId) {
|
|
4762
|
-
if (overrideId) return overrideId;
|
|
4763
|
-
const config = ConfigManager.getInstance().getConfig();
|
|
4764
|
-
if (config.agents?.dev) return config.agents.dev;
|
|
4765
|
-
if (process.env.STACKSPOT_DEV_AGENT_ID) return process.env.STACKSPOT_DEV_AGENT_ID;
|
|
4766
|
-
return "01KEQCGJ65YENRA4QBXVN1YFFX";
|
|
4767
|
-
}
|
|
4768
|
-
async function interactiveDeveloperAgent2(options = {}) {
|
|
4769
|
-
FileLogger.init();
|
|
4770
|
-
const agentId = getAgentId();
|
|
4771
|
-
if (agentId === "PENDING_CONFIGURATION") {
|
|
4772
|
-
tui.log.error("\u274C STACKSPOT_DEV_AGENT_ID not configured in .env");
|
|
4773
|
-
return { success: false, summary: "Missing configuration." };
|
|
4774
|
-
}
|
|
4775
|
-
const projectRoot = process.cwd();
|
|
4776
|
-
let contextContent = "";
|
|
4777
|
-
const defaultContextPath = path11.resolve(projectRoot, "_sharkrc", "project-context.md");
|
|
4778
|
-
const specificContextPath = options.context ? path11.resolve(projectRoot, options.context) : defaultContextPath;
|
|
4779
|
-
if (fs10.existsSync(specificContextPath)) {
|
|
4780
4069
|
try {
|
|
4781
|
-
|
|
4782
|
-
} catch
|
|
4783
|
-
tui.log.warning(`Failed to read context file: ${e}`);
|
|
4070
|
+
await fs10.rm(targetPath, { recursive: true, force: true });
|
|
4071
|
+
} catch {
|
|
4784
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);
|
|
4785
4079
|
}
|
|
4786
|
-
|
|
4787
|
-
|
|
4788
|
-
if (contextContent) {
|
|
4789
|
-
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);
|
|
4790
4082
|
|
|
4791
|
-
|
|
4792
|
-
|
|
4793
|
-
|
|
4794
|
-
|
|
4795
|
-
|
|
4796
|
-
|
|
4797
|
-
basePrompt += `
|
|
4798
|
-
|
|
4799
|
-
--- PREVIOUS EXECUTION SUMMARY ---
|
|
4800
|
-
${options.history}
|
|
4801
|
-
----------------------------------
|
|
4802
|
-
`;
|
|
4803
|
-
}
|
|
4804
|
-
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";
|
|
4805
4089
|
|
|
4806
|
-
|
|
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>
|
|
4807
4121
|
|
|
4808
|
-
|
|
4809
|
-
|
|
4122
|
+
<!-- Main Content -->
|
|
4123
|
+
<div class="flex-1 flex relative">
|
|
4124
|
+
<!-- Canvas -->
|
|
4125
|
+
<div id="network" class="flex-1 h-full"></div>
|
|
4810
4126
|
|
|
4811
|
-
|
|
4812
|
-
|
|
4813
|
-
|
|
4814
|
-
|
|
4815
|
-
|
|
4816
|
-
|
|
4817
|
-
|
|
4818
|
-
|
|
4819
|
-
|
|
4820
|
-
|
|
4821
|
-
const conversationKey = options.taskId ? `dev_agent_${options.taskId}` : `dev_agent_${Date.now()}`;
|
|
4822
|
-
let autoApprovals = {
|
|
4823
|
-
files: false,
|
|
4824
|
-
commands: false
|
|
4825
|
-
};
|
|
4826
|
-
while (keepGoing) {
|
|
4827
|
-
try {
|
|
4828
|
-
spinner.start("\u{1F988} Shark Dev working...");
|
|
4829
|
-
const lastResponse = await callDevAgentApi(nextPrompt, (chunk) => {
|
|
4830
|
-
}, conversationKey);
|
|
4831
|
-
spinner.stop("Response received");
|
|
4832
|
-
if (lastResponse) {
|
|
4833
|
-
const response = lastResponse;
|
|
4834
|
-
const actions = response.actions || [];
|
|
4835
|
-
if (response.message && response.message.includes("TASK_COMPLETED:")) {
|
|
4836
|
-
isTaskCompleted = true;
|
|
4837
|
-
finalSummary = response.message.split("TASK_COMPLETED:")[1].trim();
|
|
4838
|
-
keepGoing = false;
|
|
4839
|
-
}
|
|
4840
|
-
if (response.message && response.message.includes("TASK_FAILED:")) {
|
|
4841
|
-
const failureReason = response.message.split("TASK_FAILED:")[1].trim();
|
|
4842
|
-
tui.log.error(`\u274C Agent reported task failure: ${failureReason}`);
|
|
4843
|
-
return { success: false, summary: failureReason };
|
|
4844
|
-
}
|
|
4845
|
-
if (actions.length === 0 && response.message && !isTaskCompleted) {
|
|
4846
|
-
tui.log.info(colors.primary("\u{1F916} Shark Dev:"));
|
|
4847
|
-
console.log(response.message);
|
|
4848
|
-
const userReply = await tui.text({ message: "Your answer:" });
|
|
4849
|
-
if (tui.isCancel(userReply)) {
|
|
4850
|
-
keepGoing = false;
|
|
4851
|
-
break;
|
|
4852
|
-
}
|
|
4853
|
-
nextPrompt = userReply;
|
|
4854
|
-
}
|
|
4855
|
-
let executionResults = "";
|
|
4856
|
-
let waitingForUser = false;
|
|
4857
|
-
for (const action of actions) {
|
|
4858
|
-
if (action.type === "talk_with_user") {
|
|
4859
|
-
tui.log.info(colors.primary("\u{1F916} Shark Dev:"));
|
|
4860
|
-
console.log(action.content);
|
|
4861
|
-
if (!isTaskCompleted) waitingForUser = true;
|
|
4862
|
-
} else if (action.type === "list_files") {
|
|
4863
|
-
tui.log.info(`\u{1F4C2} Scanning: ${colors.dim(action.path || ".")}`);
|
|
4864
|
-
const result = handleListFiles(action.path || ".");
|
|
4865
|
-
executionResults += `[Action list_files(${action.path}) Result]:
|
|
4866
|
-
${result}
|
|
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>
|
|
4867
4137
|
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
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>
|
|
4874
4147
|
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
|
|
4883
|
-
|
|
4884
|
-
|
|
4885
|
-
|
|
4886
|
-
|
|
4887
|
-
|
|
4888
|
-
|
|
4889
|
-
|
|
4890
|
-
|
|
4891
|
-
|
|
4892
|
-
|
|
4893
|
-
|
|
4894
|
-
|
|
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);
|
|
4895
4176
|
}
|
|
4896
|
-
|
|
4897
|
-
const result = await handleRunCommand(cmd);
|
|
4898
|
-
executionResults += `[Action run_command(${cmd}) Result]:
|
|
4899
|
-
${result}
|
|
4177
|
+
}
|
|
4900
4178
|
|
|
4901
|
-
|
|
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
|
+
};
|
|
4902
4196
|
} else {
|
|
4903
|
-
|
|
4904
|
-
|
|
4905
|
-
|
|
4906
|
-
|
|
4907
|
-
|
|
4908
|
-
|
|
4909
|
-
|
|
4910
|
-
|
|
4911
|
-
|
|
4912
|
-
|
|
4913
|
-
|
|
4914
|
-
|
|
4915
|
-
|
|
4916
|
-
if (choice === "always") {
|
|
4917
|
-
autoApprovals.files = true;
|
|
4918
|
-
approved = true;
|
|
4919
|
-
} 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
|
+
};
|
|
4920
4210
|
}
|
|
4921
|
-
|
|
4922
|
-
if (action.type === "create_file") {
|
|
4923
|
-
const dir = path11.dirname(path11.resolve(projectRoot, filePath));
|
|
4924
|
-
if (!fs10.existsSync(dir)) fs10.mkdirSync(dir, { recursive: true });
|
|
4925
|
-
fs10.writeFileSync(path11.resolve(projectRoot, filePath), action.content || "", "utf-8");
|
|
4926
|
-
executionResults += `[Action create_file]: Success
|
|
4927
|
-
|
|
4928
|
-
`;
|
|
4929
|
-
} else {
|
|
4930
|
-
let success = false;
|
|
4931
|
-
if (action.line_range) success = replaceLineRange(filePath, action.line_range[0], action.line_range[1], action.content || "", tui);
|
|
4932
|
-
else if (action.target_content) success = startSmartReplace(filePath, action.content || "", action.target_content, tui);
|
|
4933
|
-
executionResults += success ? `[Action modify_file]: Success
|
|
4211
|
+
}
|
|
4934
4212
|
|
|
4935
|
-
|
|
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
|
+
}));
|
|
4936
4232
|
|
|
4937
|
-
|
|
4938
|
-
|
|
4939
|
-
|
|
4940
|
-
|
|
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
|
+
}));
|
|
4941
4248
|
|
|
4942
|
-
|
|
4249
|
+
if (network) {
|
|
4250
|
+
network.setData({ nodes: visNodes, edges: visEdges });
|
|
4251
|
+
network.setOptions(getOptionsForMode(currentMode));
|
|
4943
4252
|
} else {
|
|
4944
|
-
|
|
4945
|
-
|
|
4946
|
-
|
|
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
|
+
});
|
|
4947
4262
|
}
|
|
4948
|
-
|
|
4949
|
-
try {
|
|
4950
|
-
let result = "";
|
|
4951
|
-
if (action.type === "ast_list_structure") {
|
|
4952
|
-
result = await astListStructure(action.path || "");
|
|
4953
|
-
} else if (action.type === "ast_get_method") {
|
|
4954
|
-
result = await astGetMethod(action.path || "", action.class_name || "", action.method_name || "");
|
|
4955
|
-
} else if (action.type === "ast_add_method") {
|
|
4956
|
-
const success = await astAddMethod(action.path || "", action.class_name || "", action.method_code || "");
|
|
4957
|
-
result = success ? "Method added successfully." : "Failed to add method.";
|
|
4958
|
-
} else if (action.type === "ast_modify_method") {
|
|
4959
|
-
const success = await astModifyMethod(action.path || "", action.class_name || "", action.method_name || "", action.new_body || "");
|
|
4960
|
-
result = success ? "Method modified successfully." : "Failed to modify method.";
|
|
4961
|
-
} else if (action.type === "ast_remove_method") {
|
|
4962
|
-
const success = await astRemoveMethod(action.path || "", action.class_name || "", action.method_name || "");
|
|
4963
|
-
result = success ? "Method removed successfully." : "Failed to remove method.";
|
|
4964
|
-
} else if (action.type === "ast_add_class") {
|
|
4965
|
-
const success = await astAddClass(action.path || "", action.class_name || "", action.extends_class || void 0, action.implements_interfaces || void 0);
|
|
4966
|
-
result = success ? "Class added successfully." : "Failed to add class.";
|
|
4967
|
-
} else if (action.type === "ast_get_property") {
|
|
4968
|
-
tui.log.info(`\u{1F50D} Reading property ${colors.gray(action.property_name || "")} in ${colors.gray(action.class_name || "")}`);
|
|
4969
|
-
const propContent = await astGetProperty(action.path || "", action.class_name || "", action.property_name || "");
|
|
4970
|
-
result = `[AST Get Property] Content:
|
|
4971
|
-
${propContent}`;
|
|
4972
|
-
} else if (action.type === "ast_add_property") {
|
|
4973
|
-
const success = await astAddProperty(action.path || "", action.class_name || "", action.property_code || "");
|
|
4974
|
-
result = success ? "Property added successfully." : "Failed to add property.";
|
|
4975
|
-
} else if (action.type === "ast_modify_property") {
|
|
4976
|
-
tui.log.info(`\u270F\uFE0F Modifying property ${colors.gray(action.property_name || "")} in ${colors.gray(action.class_name || "")}`);
|
|
4977
|
-
const success = await astModifyProperty(action.path || "", action.class_name || "", action.property_name || "", action.property_code || "");
|
|
4978
|
-
result = success ? "Property modified successfully." : "Failed to modify property.";
|
|
4979
|
-
} else if (action.type === "ast_remove_property") {
|
|
4980
|
-
const success = await astRemoveProperty(action.path || "", action.class_name || "", action.property_name || "");
|
|
4981
|
-
result = success ? "Property removed successfully." : "Failed to remove property.";
|
|
4982
|
-
} else if (action.type === "ast_add_decorator") {
|
|
4983
|
-
const success = await astAddDecorator(action.path || "", action.class_name || "", action.decorator_code || "");
|
|
4984
|
-
result = success ? "Decorator added successfully." : "Failed to add decorator.";
|
|
4985
|
-
} else if (action.type === "ast_add_interface") {
|
|
4986
|
-
const success = await astAddInterface(action.path || "", action.interface_code || "");
|
|
4987
|
-
result = success ? "Interface added successfully." : "Failed to add interface.";
|
|
4988
|
-
} else if (action.type === "ast_add_type_alias") {
|
|
4989
|
-
const success = await astAddTypeAlias(action.path || "", action.type_code || "");
|
|
4990
|
-
result = success ? "Type alias added successfully." : "Failed to add type alias.";
|
|
4991
|
-
} else if (action.type === "ast_add_function") {
|
|
4992
|
-
const success = await astAddFunction(action.path || "", action.function_code || "");
|
|
4993
|
-
result = success ? "Function added successfully." : "Failed to add function.";
|
|
4994
|
-
} else if (action.type === "ast_remove_function") {
|
|
4995
|
-
const success = await astRemoveFunction(action.path || "", action.function_name || "");
|
|
4996
|
-
result = success ? "Function removed successfully." : "Failed to remove function.";
|
|
4997
|
-
} else if (action.type === "ast_add_import") {
|
|
4998
|
-
const success = await astAddImport(action.path || "", action.import_statement || "");
|
|
4999
|
-
result = success ? "Import added successfully." : "Failed to add import.";
|
|
5000
|
-
} else if (action.type === "ast_remove_import") {
|
|
5001
|
-
const success = await astRemoveImport(action.path || "", action.module_path || "");
|
|
5002
|
-
result = success ? "Import removed successfully." : "Failed to remove import.";
|
|
5003
|
-
} else if (action.type === "ast_organize_imports") {
|
|
5004
|
-
const success = await astOrganizeImports(action.path || "");
|
|
5005
|
-
result = success ? "Imports organized successfully." : "Failed to organize imports.";
|
|
5006
|
-
} else {
|
|
5007
|
-
result = `Unknown AST action: ${action.type}`;
|
|
5008
|
-
}
|
|
5009
|
-
executionResults += `[Action ${action.type} Result]:
|
|
5010
|
-
${result}
|
|
4263
|
+
}
|
|
5011
4264
|
|
|
5012
|
-
|
|
5013
|
-
|
|
5014
|
-
|
|
5015
|
-
|
|
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
|
+
}
|
|
5016
4279
|
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
} else if (action.type === "search_ast") {
|
|
5021
|
-
const result = await astGrepSearch(action.pattern || "", action.path || "", action.language || "typescript", tui);
|
|
5022
|
-
executionResults += `[Action search_ast Result]:
|
|
5023
|
-
${result}
|
|
4280
|
+
function showSidebar(nodeId) {
|
|
4281
|
+
const node = graphData.nodes.find(n => n.id === nodeId);
|
|
4282
|
+
if (!node || !node.details) return;
|
|
5024
4283
|
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
executionResults += success ? `[Action modify_ast]: Success
|
|
4284
|
+
document.getElementById('sidebarTitle').innerText = node.label;
|
|
4285
|
+
const content = document.getElementById('sidebarContent');
|
|
4286
|
+
content.innerHTML = '';
|
|
5029
4287
|
|
|
5030
|
-
|
|
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
|
+
}
|
|
5031
4301
|
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
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);
|
|
5041
4320
|
}
|
|
5042
|
-
nextPrompt = `${executionResults}
|
|
5043
|
-
User Reply: ${userReply}`;
|
|
5044
|
-
} else {
|
|
5045
|
-
nextPrompt = `${executionResults}
|
|
5046
|
-
[System]: Continue execution. If finished, output "TASK_COMPLETED: summary".`;
|
|
5047
|
-
tui.log.info(colors.dim("Processing results..."));
|
|
5048
|
-
}
|
|
5049
|
-
} else if (!keepGoing) {
|
|
5050
|
-
} else if (waitingForUser) {
|
|
5051
|
-
} else {
|
|
5052
|
-
if (!isTaskCompleted && actions.length > 0) nextPrompt = "Please continue.";
|
|
5053
|
-
}
|
|
5054
|
-
} else {
|
|
5055
|
-
tui.log.warning("No response received from agent.");
|
|
5056
|
-
}
|
|
5057
|
-
} catch (e) {
|
|
5058
|
-
tui.log.error(e.message);
|
|
5059
|
-
keepGoing = false;
|
|
5060
|
-
return { success: false, summary: `Error: ${e.message}` };
|
|
5061
|
-
}
|
|
5062
|
-
}
|
|
5063
|
-
tui.log.success("\u2705 Task Scope Completed");
|
|
5064
|
-
return { success: true, summary: finalSummary || "Task completed without summary." };
|
|
5065
|
-
}
|
|
5066
|
-
async function callDevAgentApi(prompt, onChunk, conversationKey = AGENT_TYPE) {
|
|
5067
|
-
const existingConversationId = await conversationManager.getConversationId(conversationKey);
|
|
5068
|
-
const provider = ProviderResolver.getProvider("developer_agent");
|
|
5069
|
-
const parsedResponse = await provider.streamChat(prompt, {
|
|
5070
|
-
conversationId: existingConversationId,
|
|
5071
|
-
agentType: "developer_agent",
|
|
5072
|
-
onChunk
|
|
5073
|
-
});
|
|
5074
|
-
if (parsedResponse.conversation_id) {
|
|
5075
|
-
await conversationManager.saveConversationId(conversationKey, parsedResponse.conversation_id);
|
|
5076
|
-
}
|
|
5077
|
-
return parsedResponse;
|
|
5078
|
-
}
|
|
5079
4321
|
|
|
5080
|
-
|
|
5081
|
-
import fs11 from "fs";
|
|
5082
|
-
import path12 from "path";
|
|
5083
|
-
var TaskManager = class {
|
|
5084
|
-
projectRoot;
|
|
5085
|
-
specPath;
|
|
5086
|
-
constructor(projectRoot = process.cwd()) {
|
|
5087
|
-
this.projectRoot = projectRoot;
|
|
5088
|
-
this.specPath = path12.resolve(this.projectRoot, "_sharkrc", "tech-spec.md");
|
|
5089
|
-
}
|
|
5090
|
-
/**
|
|
5091
|
-
* Reads the tech-spec.md file and analyzes its current state.
|
|
5092
|
-
*/
|
|
5093
|
-
analyzeSpecState() {
|
|
5094
|
-
if (!fs11.existsSync(this.specPath)) {
|
|
5095
|
-
return { status: "MISSING", allTasks: [] };
|
|
5096
|
-
}
|
|
5097
|
-
const content = fs11.readFileSync(this.specPath, "utf-8");
|
|
5098
|
-
const lines = content.split("\n");
|
|
5099
|
-
const tasks = [];
|
|
5100
|
-
let taskIndex = 1;
|
|
5101
|
-
for (let i = 0; i < lines.length; i++) {
|
|
5102
|
-
const line = lines[i];
|
|
5103
|
-
const trimmed = line.trim();
|
|
5104
|
-
const pendingMatch = trimmed.match(/^- \[ \] (.*)/);
|
|
5105
|
-
const completedMatch = trimmed.match(/^- \[x\] (.*)/i);
|
|
5106
|
-
const progressMatch = trimmed.match(/^- \[\/\] (.*)/);
|
|
5107
|
-
let currentTask = null;
|
|
5108
|
-
let status2 = null;
|
|
5109
|
-
let description = "";
|
|
5110
|
-
if (pendingMatch) {
|
|
5111
|
-
description = pendingMatch[1].trim();
|
|
5112
|
-
status2 = "PENDING";
|
|
5113
|
-
} else if (completedMatch) {
|
|
5114
|
-
description = completedMatch[1].trim();
|
|
5115
|
-
status2 = "COMPLETED";
|
|
5116
|
-
} else if (progressMatch) {
|
|
5117
|
-
description = progressMatch[1].trim();
|
|
5118
|
-
status2 = "IN_PROGRESS";
|
|
5119
|
-
}
|
|
5120
|
-
if (status2 && description) {
|
|
5121
|
-
let j = i + 1;
|
|
5122
|
-
while (j < lines.length) {
|
|
5123
|
-
const nextLine = lines[j];
|
|
5124
|
-
const nextTrimmed = nextLine.trim();
|
|
5125
|
-
if (!nextTrimmed || nextTrimmed.match(/^- \[[ x\/]\]/) || nextTrimmed.startsWith("#")) {
|
|
5126
|
-
break;
|
|
5127
|
-
}
|
|
5128
|
-
description += "\n" + nextTrimmed;
|
|
5129
|
-
j++;
|
|
4322
|
+
document.getElementById('sidebar').classList.remove('translate-x-full');
|
|
5130
4323
|
}
|
|
5131
|
-
currentTask = {
|
|
5132
|
-
id: `task-${taskIndex++}`,
|
|
5133
|
-
description,
|
|
5134
|
-
status: status2,
|
|
5135
|
-
line_number: i
|
|
5136
|
-
};
|
|
5137
|
-
tasks.push(currentTask);
|
|
5138
|
-
}
|
|
5139
|
-
}
|
|
5140
|
-
let nextTask = tasks.find((t) => t.status === "IN_PROGRESS");
|
|
5141
|
-
if (!nextTask) {
|
|
5142
|
-
nextTask = tasks.find((t) => t.status === "PENDING");
|
|
5143
|
-
}
|
|
5144
|
-
const status = !nextTask && tasks.length > 0 && tasks.every((t) => t.status === "COMPLETED") ? "COMPLETED" : "PENDING";
|
|
5145
|
-
return {
|
|
5146
|
-
status: tasks.length === 0 ? "MISSING" : status,
|
|
5147
|
-
// Empty file is effectively "pending creation" but we treat as missing content logic elsewhere
|
|
5148
|
-
nextTask,
|
|
5149
|
-
allTasks: tasks
|
|
5150
|
-
};
|
|
5151
|
-
}
|
|
5152
|
-
/**
|
|
5153
|
-
* Marks a specific task as COMPLETED in the file.
|
|
5154
|
-
* Uses line-based replacement to be safe.
|
|
5155
|
-
*/
|
|
5156
|
-
markTaskAsDone(taskId) {
|
|
5157
|
-
const state = this.analyzeSpecState();
|
|
5158
|
-
const task = state.allTasks.find((t) => t.id === taskId);
|
|
5159
|
-
if (!task) {
|
|
5160
|
-
console.error(`Task ${taskId} not found.`);
|
|
5161
|
-
return false;
|
|
5162
|
-
}
|
|
5163
|
-
const content = fs11.readFileSync(this.specPath, "utf-8");
|
|
5164
|
-
const lines = content.split("\n");
|
|
5165
|
-
const targetLine = lines[task.line_number];
|
|
5166
|
-
if (!targetLine.includes(task.description)) {
|
|
5167
|
-
console.error(`Concurrency Error: Task line content mistmatch. Expected "${task.description}" at line ${task.line_number}.`);
|
|
5168
|
-
return false;
|
|
5169
|
-
}
|
|
5170
|
-
const newLine = targetLine.replace("- [ ]", "- [x]").replace("- [/]", "- [x]");
|
|
5171
|
-
lines[task.line_number] = newLine;
|
|
5172
|
-
fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
|
|
5173
|
-
return true;
|
|
5174
|
-
}
|
|
5175
|
-
/**
|
|
5176
|
-
* Marks a task as IN_PROGRESS.
|
|
5177
|
-
*/
|
|
5178
|
-
markTaskInProgress(taskId) {
|
|
5179
|
-
const state = this.analyzeSpecState();
|
|
5180
|
-
const task = state.allTasks.find((t) => t.id === taskId);
|
|
5181
|
-
if (!task) return false;
|
|
5182
|
-
const content = fs11.readFileSync(this.specPath, "utf-8");
|
|
5183
|
-
const lines = content.split("\n");
|
|
5184
|
-
let targetLine = lines[task.line_number];
|
|
5185
|
-
targetLine = targetLine.replace("- [ ]", "- [/]");
|
|
5186
|
-
lines[task.line_number] = targetLine;
|
|
5187
|
-
fs11.writeFileSync(this.specPath, lines.join("\n"), "utf-8");
|
|
5188
|
-
return true;
|
|
5189
|
-
}
|
|
5190
|
-
/**
|
|
5191
|
-
* Completely updates the spec file content (used by Spec Agent).
|
|
5192
|
-
*/
|
|
5193
|
-
updateSpecContent(newContent) {
|
|
5194
|
-
fs11.writeFileSync(this.specPath, newContent, "utf-8");
|
|
5195
|
-
}
|
|
5196
|
-
getSpecPath() {
|
|
5197
|
-
return this.specPath;
|
|
5198
|
-
}
|
|
5199
|
-
};
|
|
5200
|
-
|
|
5201
|
-
// src/core/agents/specification-agent.ts
|
|
5202
|
-
import fs12 from "fs";
|
|
5203
|
-
import path13 from "path";
|
|
5204
|
-
var AGENT_TYPE2 = "specification_agent";
|
|
5205
|
-
async function interactiveSpecificationAgent(options = {}) {
|
|
5206
|
-
FileLogger.init();
|
|
5207
|
-
tui.intro("\u{1F3D7}\uFE0F Specification Agent (Template-Based)");
|
|
5208
|
-
const projectRoot = process.cwd();
|
|
5209
|
-
const sharkRcDir = path13.resolve(projectRoot, "_sharkrc");
|
|
5210
|
-
if (!fs12.existsSync(sharkRcDir)) fs12.mkdirSync(sharkRcDir, { recursive: true });
|
|
5211
|
-
const outputFile = path13.resolve(sharkRcDir, "tech-spec.md");
|
|
5212
|
-
if (!fs12.existsSync(outputFile)) {
|
|
5213
|
-
let initialContent = `# Technical Specification: {{PROJECT_NAME}}
|
|
5214
|
-
|
|
5215
|
-
## 1. Technology Stack
|
|
5216
|
-
[TO BE ANALYZED - STACK]
|
|
5217
|
-
- Language: [e.g. TypeScript]
|
|
5218
|
-
- Framework: [e.g. Node.js / React]
|
|
5219
|
-
- Database: [e.g. SQLite / PostgreSQL]
|
|
5220
|
-
- Key Libraries: [Top 5 dependencies]
|
|
5221
4324
|
|
|
5222
|
-
|
|
5223
|
-
|
|
5224
|
-
|
|
5225
|
-
|
|
5226
|
-
## 3. Data Model
|
|
5227
|
-
[TO BE ANALYZED - DATA MODEL]
|
|
5228
|
-
[Schema/ERD definitions]
|
|
4325
|
+
function hideSidebar() {
|
|
4326
|
+
document.getElementById('sidebar').classList.add('translate-x-full');
|
|
4327
|
+
}
|
|
5229
4328
|
|
|
5230
|
-
|
|
5231
|
-
[TO BE ANALYZED - API]
|
|
5232
|
-
[Main endpoints or CLI commands]
|
|
4329
|
+
document.getElementById('btnCloseSidebar').addEventListener('click', hideSidebar);
|
|
5233
4330
|
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
tui.log.success(`\u2705 Created: ${colors.bold("_sharkrc/tech-spec.md")}`);
|
|
5242
|
-
} else {
|
|
5243
|
-
tui.log.info(`\u{1F4C4} Using existing ${colors.bold("_sharkrc/tech-spec.md")}`);
|
|
5244
|
-
}
|
|
5245
|
-
let contextContent = "";
|
|
5246
|
-
const contextPath = path13.resolve(projectRoot, "_sharkrc", "project-context.md");
|
|
5247
|
-
if (fs12.existsSync(contextPath)) {
|
|
5248
|
-
contextContent = fs12.readFileSync(contextPath, "utf-8");
|
|
5249
|
-
tui.log.info(`\u{1F4D8} Context loaded.`);
|
|
5250
|
-
}
|
|
5251
|
-
let briefingContent = "";
|
|
5252
|
-
if (options.briefingPath && fs12.existsSync(options.briefingPath)) {
|
|
5253
|
-
briefingContent = fs12.readFileSync(options.briefingPath, "utf-8");
|
|
5254
|
-
tui.log.info(`\u{1F4C4} Briefing loaded from: ${colors.dim(options.briefingPath)}`);
|
|
5255
|
-
} else {
|
|
5256
|
-
const standardBriefing = path13.resolve(projectRoot, "_sharkrc", "briefing.md");
|
|
5257
|
-
if (fs12.existsSync(standardBriefing)) {
|
|
5258
|
-
briefingContent = fs12.readFileSync(standardBriefing, "utf-8");
|
|
5259
|
-
tui.log.info(`\u{1F4C4} Briefing loaded.`);
|
|
5260
|
-
}
|
|
5261
|
-
}
|
|
5262
|
-
let initialPrompt = `
|
|
5263
|
-
Voc\xEA \xE9 o **Shark Spec**, um Arquiteto de Software S\xEAnior e Tech Lead.
|
|
5264
|
-
Seu objetivo final \xE9 produzir uma especifica\xE7\xE3o t\xE9cnica precisa para a tarefa no arquivo \`_sharkrc/tech-spec.md\`.
|
|
5265
|
-
|
|
5266
|
-
\u26A0\uFE0F O SEU WORKFLOW \xC9 GUIADO POR FASES.
|
|
5267
|
-
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
|
+
});
|
|
5268
4338
|
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
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
|
+
});
|
|
5273
4346
|
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
|
|
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
|
+
});
|
|
5280
4356
|
|
|
5281
|
-
|
|
5282
|
-
|
|
5283
|
-
|
|
5284
|
-
|
|
5285
|
-
|
|
5286
|
-
initialPrompt += `
|
|
5287
|
-
\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.
|
|
5288
|
-
`;
|
|
5289
|
-
}
|
|
5290
|
-
if (options.initialContext) {
|
|
5291
|
-
initialPrompt += `
|
|
5292
|
-
--- CONTEXTO DE EXECU\xC7\xC3O ANTERIOR (HANDOVER/FEEDBACK) ---
|
|
5293
|
-
${options.initialContext}
|
|
5294
|
-
-----------------------------------------------------
|
|
4357
|
+
fetchGraphData();
|
|
4358
|
+
setInterval(fetchGraphData, 3000);
|
|
4359
|
+
</script>
|
|
4360
|
+
</body>
|
|
4361
|
+
</html>
|
|
5295
4362
|
`;
|
|
5296
|
-
}
|
|
5297
|
-
if (contextContent) {
|
|
5298
|
-
initialPrompt += `
|
|
5299
|
-
\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.
|
|
5300
4363
|
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
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);
|
|
5305
4370
|
}
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
while (keepGoing && stepCount < MAX_STEPS) {
|
|
5315
|
-
stepCount++;
|
|
5316
|
-
const spinner = tui.spinner();
|
|
5317
|
-
spinner.start(`\u{1F3D7}\uFE0F Spec Agent working (Step ${stepCount}/${MAX_STEPS})...`);
|
|
5318
|
-
let pendingSections = [];
|
|
5319
|
-
if (fs12.existsSync(targetPath)) {
|
|
5320
|
-
const content = fs12.readFileSync(targetPath, "utf-8");
|
|
5321
|
-
if (content.includes("[TO BE ANALYZED]")) pendingSections.push("Analysis Sections (Stack, Arch, Data, API)");
|
|
5322
|
-
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;
|
|
5323
4379
|
}
|
|
5324
|
-
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;
|
|
5325
4398
|
}
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
|
|
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
|
+
});
|
|
5354
4438
|
}
|
|
5355
|
-
|
|
5356
|
-
|
|
5357
|
-
|
|
5358
|
-
|
|
5359
|
-
|
|
5360
|
-
|
|
5361
|
-
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5373
|
-
|
|
5374
|
-
|
|
5375
|
-
|
|
5376
|
-
`;
|
|
5377
|
-
} else if (action.type === "search_file") {
|
|
5378
|
-
tui.log.info(`\u{1F50D} Searching: ${colors.dim(action.path || "")}`);
|
|
5379
|
-
const result = handleSearchFile(action.path || "");
|
|
5380
|
-
executionResults += `[Action search_file(${action.path}) Result]:
|
|
5381
|
-
${result}
|
|
5382
|
-
|
|
5383
|
-
`;
|
|
5384
|
-
} else if (action.type === "search_code") {
|
|
5385
|
-
const glob = action.path || "src/**/*";
|
|
5386
|
-
const query = action.query || "";
|
|
5387
|
-
const isRegex = action.is_regex === true;
|
|
5388
|
-
tui.log.info(`\u{1F50E} Search code: ${colors.dim(`"${query}" in ${glob}`)}`);
|
|
5389
|
-
const result = handleSearchCode(glob, query, isRegex);
|
|
5390
|
-
executionResults += `[Action search_code("${query}" in "${glob}") Result]:
|
|
5391
|
-
${result}
|
|
5392
|
-
|
|
5393
|
-
`;
|
|
5394
|
-
} else if (["create_file", "modify_file"].includes(action.type)) {
|
|
5395
|
-
let actionPath = path13.resolve(action.path || "");
|
|
5396
|
-
const resolvedTargetPath = path13.resolve(targetPath);
|
|
5397
|
-
let isTarget = actionPath === resolvedTargetPath;
|
|
5398
|
-
if (!isTarget && path13.basename(actionPath) === "tech-spec.md") {
|
|
5399
|
-
tui.log.warning(`Redirecting ${action.type} from ${action.path} to ${path13.relative(process.cwd(), targetPath)}`);
|
|
5400
|
-
action.path = targetPath;
|
|
5401
|
-
actionPath = resolvedTargetPath;
|
|
5402
|
-
isTarget = true;
|
|
5403
|
-
}
|
|
5404
|
-
if (!isTarget && action.type === "create_file") {
|
|
5405
|
-
const confirm = await tui.confirm({ message: `Agent wants to create ${action.path}. Allow?` });
|
|
5406
|
-
if (!confirm) {
|
|
5407
|
-
executionResults += `[Action create_file]: User denied.
|
|
5408
|
-
`;
|
|
5409
|
-
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
|
+
});
|
|
5410
4459
|
}
|
|
4460
|
+
} catch (e) {
|
|
5411
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) => {
|
|
5412
4468
|
try {
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5417
|
-
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
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
|
+
});
|
|
5429
4493
|
}
|
|
5430
|
-
}
|
|
5431
|
-
|
|
5432
|
-
|
|
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
|
+
});
|
|
5433
4503
|
}
|
|
5434
4504
|
}
|
|
5435
4505
|
} catch (e) {
|
|
5436
|
-
executionResults += `[Action ${action.type}]: Error: ${e.message}
|
|
5437
|
-
`;
|
|
5438
4506
|
}
|
|
5439
|
-
}
|
|
5440
|
-
}
|
|
5441
|
-
if (lastResponse.message && lastResponse.message.includes("PHASE_COMPLETED")) {
|
|
5442
|
-
const extraContext = executionResults ? `
|
|
5443
|
-
|
|
5444
|
-
Resultados das \xFAltimas a\xE7\xF5es executadas antes da conclus\xE3o:
|
|
5445
|
-
${executionResults}` : "";
|
|
5446
|
-
if (currentPhase === 1) {
|
|
5447
|
-
currentPhase = 2;
|
|
5448
|
-
tui.log.success(`\u2705 Fase 1 Conclu\xEDda. Iniciando Fase 2 (Investiga\xE7\xE3o).`);
|
|
5449
|
-
nextPrompt = `[System Message]
|
|
5450
|
-
Voc\xEA completou a FASE 1 com sucesso.
|
|
5451
|
-
|
|
5452
|
-
**VOC\xCA AGORA EST\xC1 NA FASE 2: INVESTIGA\xC7\xC3O**
|
|
5453
|
-
- Use \`search_code\` e \`list_files\` para explorar os arquivos relevantes \xE0 tarefa.
|
|
5454
|
-
- Prefira \`search_code\` em vez de \`read_file\` para buscar c\xF3digo sem inflar o contexto.
|
|
5455
|
-
- N\xC3O leia o projeto inteiro de forma gen\xE9rica.
|
|
5456
|
-
- 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.
|
|
5457
|
-
- 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}`;
|
|
5458
|
-
continue;
|
|
5459
|
-
} else if (currentPhase === 2) {
|
|
5460
|
-
currentPhase = 3;
|
|
5461
|
-
tui.log.success(`\u2705 Fase 2 Conclu\xEDda. Iniciando Fase 3 (Preenchimento).`);
|
|
5462
|
-
nextPrompt = `[System Message]
|
|
5463
|
-
Voc\xEA completou a FASE 2 com sucesso.
|
|
5464
|
-
|
|
5465
|
-
**VOC\xCA AGORA EST\xC1 NA FASE 3: PREENCHIMENTO DO TEMPLATE**
|
|
5466
|
-
- Use \`modify_file\` no arquivo \`${targetPath}\` para substituir os placeholders pelo conte\xFAdo real levantado na fase de investiga\xE7\xE3o.
|
|
5467
|
-
- As se\xE7\xF5es 1-4 devem descrever o contexto da TAREFA, e n\xE3o o projeto como um todo.
|
|
5468
|
-
- Passos de Implementa\xE7\xE3o (Implementation Steps): APENAS checkboxes markdown: \`- [ ] [Verbo de A\xE7\xE3o] [O Que] em [Caminho Relativo]\`.
|
|
5469
|
-
- 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}`;
|
|
5470
|
-
continue;
|
|
5471
|
-
}
|
|
5472
|
-
}
|
|
5473
|
-
if (lastResponse.message && lastResponse.message.includes("SPEC_UPDATED:")) {
|
|
5474
|
-
if (currentPhase < 3) {
|
|
5475
|
-
tui.log.warning(`O agente tentou finalizar prematuramente. For\xE7ando retorno para a fase atual...`);
|
|
5476
|
-
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.
|
|
5477
|
-
|
|
5478
|
-
Continue seu trabalho na Fase ${currentPhase} ou emita "PHASE_COMPLETED" se terminou esta etapa atual.`;
|
|
5479
|
-
continue;
|
|
5480
|
-
}
|
|
5481
|
-
const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
|
|
5482
|
-
if (content.includes("[TO BE")) {
|
|
5483
|
-
const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
|
|
5484
|
-
let missing = pendingMatches.length > 0 ? pendingMatches.join(", ") : "algumas se\xE7\xF5es";
|
|
5485
|
-
tui.log.warning(`O agente tentou concluir prematuramente, mas h\xE1 placeholders pendentes. For\xE7ando retorno...`);
|
|
5486
|
-
nextPrompt = `[System Error]: A valida\xE7\xE3o falhou e o bloqueio autom\xE1tico foi acionado.
|
|
5487
|
-
Voc\xEA tentou concluir a tarefa, mas o arquivo AINDA possui placeholders '[TO BE ANALYZED...]' ou '[TO BE FILLED]'.
|
|
5488
|
-
As seguintes se\xE7\xF5es ainda cont\xEAm estes placeholders: ${missing}.
|
|
5489
|
-
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.`;
|
|
5490
|
-
continue;
|
|
5491
|
-
} else {
|
|
5492
|
-
const updateSummary = lastResponse.message.split("SPEC_UPDATED:")[1].trim();
|
|
5493
|
-
tui.log.success(`\u2705 Spec Finalized: ${updateSummary}`);
|
|
5494
|
-
return;
|
|
5495
|
-
}
|
|
5496
|
-
}
|
|
5497
|
-
if (executionResults) {
|
|
5498
|
-
FileLogger.log("TOOL_EXECUTION", "Specification Agent Actions Output", { executionResults });
|
|
5499
|
-
}
|
|
5500
|
-
if (hasSystemError) {
|
|
5501
|
-
nextPrompt = systemErrorContent;
|
|
5502
|
-
} else if (waitingForUser) {
|
|
5503
|
-
const userReply = await tui.text({ message: "Your answer", placeholder: "Type your answer..." });
|
|
5504
|
-
if (tui.isCancel(userReply)) {
|
|
5505
|
-
keepGoing = false;
|
|
5506
|
-
return;
|
|
5507
|
-
}
|
|
5508
|
-
FileLogger.log("USER_INPUT", "User response to specification agent", { userReply });
|
|
5509
|
-
nextPrompt = `${executionResults}
|
|
5510
|
-
|
|
5511
|
-
User Reply: ${userReply}`;
|
|
5512
|
-
} else if (executionResults) {
|
|
5513
|
-
const content = fs12.existsSync(targetPath) ? fs12.readFileSync(targetPath, "utf-8") : "";
|
|
5514
|
-
let systemMsg = "Execu\xE7\xE3o da ferramenta conclu\xEDda.";
|
|
5515
|
-
if (specUpdated) {
|
|
5516
|
-
if (content.includes("[TO BE")) {
|
|
5517
|
-
const pendingMatches = [...content.matchAll(/## ([^\n]+)[\s\S]*?\[TO BE/g)].map((m) => m[1]);
|
|
5518
|
-
let missing = pendingMatches.length > 0 ? pendingMatches.join(", ") : "v\xE1rias se\xE7\xF5es";
|
|
5519
|
-
systemMsg += `
|
|
5520
|
-
[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}.
|
|
5521
|
-
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.`;
|
|
5522
|
-
} else {
|
|
5523
|
-
systemMsg += "\n[System]: O arquivo parece completo! Se estiver satisfeito e possuir TODAS as implementa\xE7\xF5es descritas, retorne 'SPEC_UPDATED: Complete'.";
|
|
5524
|
-
}
|
|
5525
|
-
} else {
|
|
5526
|
-
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`.";
|
|
5527
|
-
}
|
|
5528
|
-
nextPrompt = `${executionResults}
|
|
5529
|
-
|
|
5530
|
-
${systemMsg}`;
|
|
5531
|
-
} else {
|
|
5532
|
-
if (lastResponse.message) {
|
|
5533
|
-
tui.log.info(colors.primary("\u{1F916} Architect (Message only):"));
|
|
5534
|
-
console.log(lastResponse.message);
|
|
5535
|
-
const userReply = await tui.text({ message: "Your answer:" });
|
|
5536
|
-
if (tui.isCancel(userReply)) {
|
|
5537
|
-
keepGoing = false;
|
|
5538
|
-
break;
|
|
5539
|
-
}
|
|
5540
|
-
FileLogger.log("USER_INPUT", "User response to specification agent (Message only)", { userReply });
|
|
5541
|
-
nextPrompt = userReply;
|
|
5542
|
-
} else {
|
|
5543
|
-
keepGoing = false;
|
|
5544
|
-
}
|
|
4507
|
+
});
|
|
5545
4508
|
}
|
|
5546
|
-
}
|
|
5547
|
-
|
|
5548
|
-
keepGoing = false;
|
|
4509
|
+
} catch (e) {
|
|
4510
|
+
console.error("Error parsing memory graph files:", e);
|
|
5549
4511
|
}
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
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;
|
|
5554
4516
|
}
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
async function callSpecAgentApi(prompt, onChunk, agentId) {
|
|
5558
|
-
const conversationId = await conversationManager.getConversationId(AGENT_TYPE2);
|
|
5559
|
-
FileLogger.log("AGENT", "Calling Agent API", { agentId, conversationId });
|
|
5560
|
-
const provider = ProviderResolver.getProvider("specification_agent");
|
|
5561
|
-
if (agentId && "agentId" in provider) {
|
|
5562
|
-
provider.agentId = agentId;
|
|
5563
|
-
}
|
|
5564
|
-
const parsed = await provider.streamChat(prompt, {
|
|
5565
|
-
conversationId,
|
|
5566
|
-
agentType: "specification_agent",
|
|
5567
|
-
onChunk
|
|
4517
|
+
res.writeHead(404, { "Content-Type": "text/plain" });
|
|
4518
|
+
res.end("Not Found");
|
|
5568
4519
|
});
|
|
5569
|
-
|
|
5570
|
-
|
|
5571
|
-
|
|
5572
|
-
|
|
5573
|
-
|
|
5574
|
-
|
|
5575
|
-
|
|
5576
|
-
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) => {
|
|
5577
|
-
const taskManager = new TaskManager(process.cwd());
|
|
5578
|
-
let state = taskManager.analyzeSpecState();
|
|
5579
|
-
if (state.status === "MISSING") {
|
|
5580
|
-
tui.log.warning("\u{1F4CB} No tech-spec.md found.");
|
|
5581
|
-
const confirm = await tui.confirm({ message: "Create a new Specification (Tech Spec)?" });
|
|
5582
|
-
if (confirm) {
|
|
5583
|
-
await interactiveSpecificationAgent({
|
|
5584
|
-
briefingPath: options.task ? void 0 : void 0
|
|
5585
|
-
});
|
|
5586
|
-
state = taskManager.analyzeSpecState();
|
|
5587
|
-
if (state.status === "MISSING") {
|
|
5588
|
-
tui.log.error("\u274C Spec creation aborted or failed.");
|
|
5589
|
-
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 });
|
|
5590
4527
|
}
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
state = taskManager.analyzeSpecState();
|
|
5601
|
-
if (state.status === "COMPLETED") {
|
|
5602
|
-
tui.log.success("\u{1F389} All tasks in tech-spec.md are COMPLETED!");
|
|
5603
|
-
const choice = await tui.select({
|
|
5604
|
-
message: "What next?",
|
|
5605
|
-
options: [
|
|
5606
|
-
{ value: "exit", label: "Exit" },
|
|
5607
|
-
{ value: "new_spec", label: "New Specification (Reset)" }
|
|
5608
|
-
]
|
|
5609
|
-
});
|
|
5610
|
-
if (choice === "new_spec") {
|
|
5611
|
-
await interactiveSpecificationAgent();
|
|
5612
|
-
contextHistory = "";
|
|
5613
|
-
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}"`);
|
|
5614
4537
|
} else {
|
|
5615
|
-
|
|
5616
|
-
break;
|
|
5617
|
-
}
|
|
5618
|
-
}
|
|
5619
|
-
const currentTask = state.nextTask;
|
|
5620
|
-
if (!currentTask) {
|
|
5621
|
-
tui.log.error("Something went wrong. Status is not completed but no next task found.");
|
|
5622
|
-
break;
|
|
5623
|
-
}
|
|
5624
|
-
tui.log.info(`
|
|
5625
|
-
\u{1F449} **NEXT TASK**: ${colors.bold(currentTask.description)}`);
|
|
5626
|
-
if (!burnMode) {
|
|
5627
|
-
const action = await tui.select({
|
|
5628
|
-
message: "Orchestration Checkpoint:",
|
|
5629
|
-
options: [
|
|
5630
|
-
{ value: "execute", label: "\u{1F680} Execute Task (Start)" },
|
|
5631
|
-
{ value: "burn", label: "\u{1F525} Burn Mode (Auto-Execute Remaining)" },
|
|
5632
|
-
{ value: "pivot", label: "\u{1F527} Pivot/Correct (Edit Spec)" },
|
|
5633
|
-
{ value: "skip", label: "\u23ED\uFE0F Skip Task (Mark Done without Executing)" },
|
|
5634
|
-
{ value: "stop", label: "\u{1F6D1} Stop Session" }
|
|
5635
|
-
]
|
|
5636
|
-
});
|
|
5637
|
-
if (action === "stop") {
|
|
5638
|
-
keepOrchestrating = false;
|
|
5639
|
-
break;
|
|
5640
|
-
} else if (action === "burn") {
|
|
5641
|
-
burnMode = true;
|
|
5642
|
-
} else if (action === "skip") {
|
|
5643
|
-
taskManager.markTaskAsDone(currentTask.id);
|
|
5644
|
-
tui.log.info("Task skipped.");
|
|
5645
|
-
continue;
|
|
5646
|
-
} else if (action === "pivot") {
|
|
5647
|
-
tui.log.info("Transferring control to Specification Agent...");
|
|
5648
|
-
await interactiveSpecificationAgent({
|
|
5649
|
-
initialContext: `User requested pivot after tasks:
|
|
5650
|
-
${contextHistory}`
|
|
5651
|
-
});
|
|
5652
|
-
continue;
|
|
4538
|
+
exec3(`xdg-open "${url}"`);
|
|
5653
4539
|
}
|
|
5654
|
-
}
|
|
5655
|
-
taskManager.markTaskInProgress(currentTask.id);
|
|
5656
|
-
tui.log.info(`\u26A1 Starting Micro-Context for Task: "${currentTask.description}"`);
|
|
5657
|
-
const result = await interactiveDeveloperAgent2({
|
|
5658
|
-
taskId: currentTask.id,
|
|
5659
|
-
taskInstruction: currentTask.description,
|
|
5660
|
-
history: contextHistory,
|
|
5661
|
-
context: options.context
|
|
5662
4540
|
});
|
|
5663
|
-
|
|
5664
|
-
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
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);
|
|
5668
4547
|
} else {
|
|
5669
|
-
|
|
5670
|
-
burnMode = false;
|
|
5671
|
-
const recovery = await tui.select({
|
|
5672
|
-
message: "Task Failed. Recovery Action:",
|
|
5673
|
-
options: [
|
|
5674
|
-
{ value: "retry", label: "Retry (Run Agent Again)" },
|
|
5675
|
-
{ value: "pivot", label: "Pivot (Adjust Spec/Instructions)" },
|
|
5676
|
-
{ value: "ignore", label: "Ignore (Mark Done anyway)" },
|
|
5677
|
-
{ value: "stop", label: "Stop" }
|
|
5678
|
-
]
|
|
5679
|
-
});
|
|
5680
|
-
if (recovery === "stop") break;
|
|
5681
|
-
if (recovery === "ignore") taskManager.markTaskAsDone(currentTask.id);
|
|
5682
|
-
if (recovery === "pivot") {
|
|
5683
|
-
await interactiveSpecificationAgent({
|
|
5684
|
-
initialContext: `Task "${currentTask.description}" FAILED.
|
|
5685
|
-
Error: ${result.summary}
|
|
5686
|
-
History:
|
|
5687
|
-
${contextHistory}`
|
|
5688
|
-
});
|
|
5689
|
-
}
|
|
4548
|
+
console.error(colors.error("\u274C Server startup error:"), err);
|
|
5690
4549
|
}
|
|
5691
|
-
}
|
|
5692
|
-
|
|
5693
|
-
});
|
|
5694
|
-
|
|
5695
|
-
// src/commands/export-schema.ts
|
|
5696
|
-
import { Command as Command4 } from "commander";
|
|
5697
|
-
var exportSchemaCommand = new Command4("export-schema").description("Outputs the agent response JSON Schema").action(() => {
|
|
5698
|
-
console.log(JSON.stringify(AGENT_RESPONSE_JSON_SCHEMA, null, 2));
|
|
5699
|
-
});
|
|
5700
|
-
|
|
5701
|
-
// src/commands/export-prompt.ts
|
|
5702
|
-
import { Command as Command5 } from "commander";
|
|
5703
|
-
var exportPromptCommand = new Command5("export-prompt").description("Outputs the unified agent system prompt").action(() => {
|
|
5704
|
-
console.log(UNIFIED_SYSTEM_PROMPT);
|
|
5705
|
-
});
|
|
5706
|
-
|
|
5707
|
-
// src/commands/super.ts
|
|
5708
|
-
import { Command as Command6 } from "commander";
|
|
5709
|
-
import { fileURLToPath as fileURLToPath3 } from "url";
|
|
5710
|
-
import path14 from "path";
|
|
5711
|
-
import os3 from "os";
|
|
5712
|
-
import fs13 from "fs/promises";
|
|
5713
|
-
var superCommandAction = async (options = {}) => {
|
|
5714
|
-
const __filename2 = fileURLToPath3(import.meta.url);
|
|
5715
|
-
const __dirname2 = path14.dirname(__filename2);
|
|
5716
|
-
const packageRoot = path14.resolve(__dirname2, "../../");
|
|
5717
|
-
const internalSkillsPath = path14.join(packageRoot, "skills");
|
|
5718
|
-
const targetPath = options.local ? path14.join(process.cwd(), ".agents", "skills") : path14.join(os3.homedir(), ".shark", "skills");
|
|
5719
|
-
try {
|
|
4550
|
+
});
|
|
4551
|
+
const cleanup = () => {
|
|
5720
4552
|
try {
|
|
5721
|
-
|
|
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
|
+
}
|
|
5722
4558
|
} catch {
|
|
5723
4559
|
}
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
5728
|
-
|
|
5729
|
-
|
|
5730
|
-
|
|
5731
|
-
|
|
5732
|
-
|
|
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
|
+
});
|
|
5733
4572
|
|
|
5734
4573
|
// src/bin/shark.ts
|
|
5735
4574
|
crashHandler.init();
|
|
@@ -5738,10 +4577,10 @@ program.name("shark").description("Shark CLI: AI-Native Collaborative Developmen
|
|
|
5738
4577
|
program.addCommand(loginCommand);
|
|
5739
4578
|
program.addCommand(initCommand);
|
|
5740
4579
|
program.addCommand(devCommand);
|
|
5741
|
-
program.addCommand(legacyCommand);
|
|
5742
4580
|
program.addCommand(exportSchemaCommand);
|
|
5743
4581
|
program.addCommand(exportPromptCommand);
|
|
5744
4582
|
program.addCommand(superCommand);
|
|
4583
|
+
program.addCommand(graphCommand);
|
|
5745
4584
|
program.command("config").description("Manage global configuration").action(configCommand.action);
|
|
5746
4585
|
process.on("unhandledRejection", (err) => {
|
|
5747
4586
|
console.error(colors.error("\u274C Unhandled Error:"), err);
|