@pikaa-ai/pikaa 0.3.13 → 0.3.15
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/cli.js +775 -355
- package/dist/index.js +709 -319
- package/package.json +1 -1
- package/templates/base/groupy_prompt.md +28 -2
package/dist/index.js
CHANGED
|
@@ -1119,17 +1119,139 @@ class ExecutionPolicyError extends GroupyError {
|
|
|
1119
1119
|
}
|
|
1120
1120
|
}
|
|
1121
1121
|
// src/auth/store.ts
|
|
1122
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
|
|
1123
|
-
import { resolve } from "path";
|
|
1122
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2, unlinkSync } from "fs";
|
|
1123
|
+
import { resolve as resolve2 } from "path";
|
|
1124
|
+
|
|
1125
|
+
// src/config/paths.ts
|
|
1126
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
1127
|
+
import { resolve, join } from "path";
|
|
1124
1128
|
import { homedir } from "os";
|
|
1129
|
+
function getPikaaHomeDir() {
|
|
1130
|
+
const envDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME;
|
|
1131
|
+
if (envDir) {
|
|
1132
|
+
return resolve(envDir);
|
|
1133
|
+
}
|
|
1134
|
+
return resolve(homedir(), ".pikaa");
|
|
1135
|
+
}
|
|
1136
|
+
function getLegacyGroupyHomeDir() {
|
|
1137
|
+
if (process.env.GROUPY_HOME) {
|
|
1138
|
+
return resolve(process.env.GROUPY_HOME);
|
|
1139
|
+
}
|
|
1140
|
+
return resolve(homedir(), ".groupy");
|
|
1141
|
+
}
|
|
1142
|
+
var hasMigrated = false;
|
|
1143
|
+
function copyDirRecursiveSync(src, dest) {
|
|
1144
|
+
if (!existsSync(src))
|
|
1145
|
+
return;
|
|
1146
|
+
if (!existsSync(dest)) {
|
|
1147
|
+
mkdirSync(dest, { recursive: true });
|
|
1148
|
+
}
|
|
1149
|
+
const entries = readdirSync(src);
|
|
1150
|
+
for (const entry of entries) {
|
|
1151
|
+
const srcPath = join(src, entry);
|
|
1152
|
+
const destPath = join(dest, entry);
|
|
1153
|
+
const stat = statSync(srcPath);
|
|
1154
|
+
if (stat.isDirectory()) {
|
|
1155
|
+
copyDirRecursiveSync(srcPath, destPath);
|
|
1156
|
+
} else if (!existsSync(destPath)) {
|
|
1157
|
+
try {
|
|
1158
|
+
copyFileSync(srcPath, destPath);
|
|
1159
|
+
} catch {}
|
|
1160
|
+
}
|
|
1161
|
+
}
|
|
1162
|
+
}
|
|
1163
|
+
function ensurePikaaHomeMigrated(force = false) {
|
|
1164
|
+
const pikaaHome = getPikaaHomeDir();
|
|
1165
|
+
const legacyHome = getLegacyGroupyHomeDir();
|
|
1166
|
+
try {
|
|
1167
|
+
if (!existsSync(pikaaHome)) {
|
|
1168
|
+
mkdirSync(pikaaHome, { recursive: true });
|
|
1169
|
+
}
|
|
1170
|
+
} catch {}
|
|
1171
|
+
if (hasMigrated && !force) {
|
|
1172
|
+
return pikaaHome;
|
|
1173
|
+
}
|
|
1174
|
+
hasMigrated = true;
|
|
1175
|
+
try {
|
|
1176
|
+
if (existsSync(legacyHome) && legacyHome !== pikaaHome) {
|
|
1177
|
+
const legacyCreds = join(legacyHome, "credentials.json");
|
|
1178
|
+
const pikaaCreds = join(pikaaHome, "credentials.json");
|
|
1179
|
+
if (existsSync(legacyCreds) && !existsSync(pikaaCreds)) {
|
|
1180
|
+
copyFileSync(legacyCreds, pikaaCreds);
|
|
1181
|
+
}
|
|
1182
|
+
const legacyThreads = join(legacyHome, "groupy_threads.db");
|
|
1183
|
+
const pikaaThreads = join(pikaaHome, "pikaa_threads.db");
|
|
1184
|
+
if (existsSync(legacyThreads) && !existsSync(pikaaThreads)) {
|
|
1185
|
+
copyFileSync(legacyThreads, pikaaThreads);
|
|
1186
|
+
}
|
|
1187
|
+
const legacyRules = join(legacyHome, "groupy_rules.db");
|
|
1188
|
+
const pikaaRules = join(pikaaHome, "pikaa_rules.db");
|
|
1189
|
+
if (existsSync(legacyRules) && !existsSync(pikaaRules)) {
|
|
1190
|
+
copyFileSync(legacyRules, pikaaRules);
|
|
1191
|
+
}
|
|
1192
|
+
const legacyGraph = join(legacyHome, "agent_graph.db");
|
|
1193
|
+
const pikaaGraph = join(pikaaHome, "agent_graph.db");
|
|
1194
|
+
if (existsSync(legacyGraph) && !existsSync(pikaaGraph)) {
|
|
1195
|
+
copyFileSync(legacyGraph, pikaaGraph);
|
|
1196
|
+
}
|
|
1197
|
+
const legacyMemories = join(legacyHome, "memories.md");
|
|
1198
|
+
const pikaaMemories = join(pikaaHome, "memories.md");
|
|
1199
|
+
if (existsSync(legacyMemories) && !existsSync(pikaaMemories)) {
|
|
1200
|
+
copyFileSync(legacyMemories, pikaaMemories);
|
|
1201
|
+
}
|
|
1202
|
+
copyDirRecursiveSync(join(legacyHome, "skills"), join(pikaaHome, "skills"));
|
|
1203
|
+
copyDirRecursiveSync(join(legacyHome, "templates"), join(pikaaHome, "templates"));
|
|
1204
|
+
}
|
|
1205
|
+
} catch {}
|
|
1206
|
+
return pikaaHome;
|
|
1207
|
+
}
|
|
1208
|
+
function getCredentialsPath() {
|
|
1209
|
+
ensurePikaaHomeMigrated();
|
|
1210
|
+
return join(getPikaaHomeDir(), "credentials.json");
|
|
1211
|
+
}
|
|
1212
|
+
function getThreadsDbPath() {
|
|
1213
|
+
ensurePikaaHomeMigrated();
|
|
1214
|
+
return join(getPikaaHomeDir(), "pikaa_threads.db");
|
|
1215
|
+
}
|
|
1216
|
+
function getPrefixRulesDbPath() {
|
|
1217
|
+
ensurePikaaHomeMigrated();
|
|
1218
|
+
return join(getPikaaHomeDir(), "pikaa_rules.db");
|
|
1219
|
+
}
|
|
1220
|
+
function getAgentGraphDbPath() {
|
|
1221
|
+
ensurePikaaHomeMigrated();
|
|
1222
|
+
return join(getPikaaHomeDir(), "agent_graph.db");
|
|
1223
|
+
}
|
|
1224
|
+
function getGlobalSkillsDir() {
|
|
1225
|
+
ensurePikaaHomeMigrated();
|
|
1226
|
+
return join(getPikaaHomeDir(), "skills");
|
|
1227
|
+
}
|
|
1228
|
+
function getGlobalTemplatesDir() {
|
|
1229
|
+
ensurePikaaHomeMigrated();
|
|
1230
|
+
return join(getPikaaHomeDir(), "templates");
|
|
1231
|
+
}
|
|
1232
|
+
function getGlobalMemoriesPath() {
|
|
1233
|
+
ensurePikaaHomeMigrated();
|
|
1234
|
+
return join(getPikaaHomeDir(), "memories.md");
|
|
1235
|
+
}
|
|
1236
|
+
function getProjectsDir() {
|
|
1237
|
+
ensurePikaaHomeMigrated();
|
|
1238
|
+
const dir = join(getPikaaHomeDir(), "projects");
|
|
1239
|
+
if (!existsSync(dir)) {
|
|
1240
|
+
try {
|
|
1241
|
+
mkdirSync(dir, { recursive: true });
|
|
1242
|
+
} catch {}
|
|
1243
|
+
}
|
|
1244
|
+
return dir;
|
|
1245
|
+
}
|
|
1125
1246
|
|
|
1247
|
+
// src/auth/store.ts
|
|
1126
1248
|
class CredentialsStore {
|
|
1127
1249
|
filePath;
|
|
1128
1250
|
constructor(customPath) {
|
|
1129
|
-
this.filePath = customPath ||
|
|
1251
|
+
this.filePath = customPath || getCredentialsPath();
|
|
1130
1252
|
}
|
|
1131
1253
|
load() {
|
|
1132
|
-
if (!
|
|
1254
|
+
if (!existsSync2(this.filePath))
|
|
1133
1255
|
return null;
|
|
1134
1256
|
try {
|
|
1135
1257
|
const raw = readFileSync(this.filePath, "utf8");
|
|
@@ -1160,14 +1282,14 @@ class CredentialsStore {
|
|
|
1160
1282
|
return creds?.user;
|
|
1161
1283
|
}
|
|
1162
1284
|
save(credentials) {
|
|
1163
|
-
const dir =
|
|
1164
|
-
if (!
|
|
1165
|
-
|
|
1285
|
+
const dir = resolve2(this.filePath, "..");
|
|
1286
|
+
if (!existsSync2(dir)) {
|
|
1287
|
+
mkdirSync2(dir, { recursive: true });
|
|
1166
1288
|
}
|
|
1167
1289
|
writeFileSync(this.filePath, JSON.stringify(credentials, null, 2), "utf8");
|
|
1168
1290
|
}
|
|
1169
1291
|
clear() {
|
|
1170
|
-
if (
|
|
1292
|
+
if (existsSync2(this.filePath)) {
|
|
1171
1293
|
try {
|
|
1172
1294
|
unlinkSync(this.filePath);
|
|
1173
1295
|
return true;
|
|
@@ -1616,9 +1738,9 @@ class ToolRouter {
|
|
|
1616
1738
|
}
|
|
1617
1739
|
}
|
|
1618
1740
|
// src/tools/handlers/apply-patch.ts
|
|
1619
|
-
import { existsSync as
|
|
1620
|
-
import { resolve as
|
|
1621
|
-
import { mkdirSync as
|
|
1741
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "fs";
|
|
1742
|
+
import { resolve as resolve3, dirname as dirname2 } from "path";
|
|
1743
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1622
1744
|
var applyPatchTool = {
|
|
1623
1745
|
name: "apply_patch",
|
|
1624
1746
|
description: "Apply precise multi-line modifications to an existing file or create a new file. TargetContent must match the file content exactly.",
|
|
@@ -1645,7 +1767,7 @@ var applyPatchTool = {
|
|
|
1645
1767
|
if (!rawPath) {
|
|
1646
1768
|
return { output: "Error: 'path' parameter is required", isError: true };
|
|
1647
1769
|
}
|
|
1648
|
-
const filePath =
|
|
1770
|
+
const filePath = resolve3(ctx.cwd, rawPath);
|
|
1649
1771
|
const targetContent = typeof args.targetContent === "string" ? args.targetContent : "";
|
|
1650
1772
|
const replacementContent = String(args.replacementContent ?? "");
|
|
1651
1773
|
if (ctx.execPolicy) {
|
|
@@ -1664,7 +1786,7 @@ var applyPatchTool = {
|
|
|
1664
1786
|
}
|
|
1665
1787
|
}
|
|
1666
1788
|
}
|
|
1667
|
-
if (!
|
|
1789
|
+
if (!existsSync3(filePath)) {
|
|
1668
1790
|
if (targetContent) {
|
|
1669
1791
|
return {
|
|
1670
1792
|
output: `Error: Target file '${rawPath}' does not exist, but targetContent was provided.`,
|
|
@@ -1672,7 +1794,7 @@ var applyPatchTool = {
|
|
|
1672
1794
|
};
|
|
1673
1795
|
}
|
|
1674
1796
|
try {
|
|
1675
|
-
|
|
1797
|
+
mkdirSync3(dirname2(filePath), { recursive: true });
|
|
1676
1798
|
writeFileSync2(filePath, replacementContent, "utf8");
|
|
1677
1799
|
return { output: `Successfully created new file '${rawPath}'` };
|
|
1678
1800
|
} catch (err) {
|
|
@@ -1879,7 +2001,7 @@ class WindowsSandbox {
|
|
|
1879
2001
|
}
|
|
1880
2002
|
|
|
1881
2003
|
// src/security/kernel/linux.ts
|
|
1882
|
-
import { existsSync as
|
|
2004
|
+
import { existsSync as existsSync4 } from "fs";
|
|
1883
2005
|
|
|
1884
2006
|
class LinuxSandbox {
|
|
1885
2007
|
hasBwrap = false;
|
|
@@ -1890,7 +2012,7 @@ class LinuxSandbox {
|
|
|
1890
2012
|
if (process.platform !== "linux") {
|
|
1891
2013
|
return;
|
|
1892
2014
|
}
|
|
1893
|
-
this.hasBwrap =
|
|
2015
|
+
this.hasBwrap = existsSync4("/usr/bin/bwrap") || existsSync4("/bin/bwrap") || existsSync4("/usr/local/bin/bwrap");
|
|
1894
2016
|
}
|
|
1895
2017
|
wrapCommand(cmd, profile) {
|
|
1896
2018
|
if (!this.hasBwrap || profile.kind === "danger-unrestricted") {
|
|
@@ -1924,7 +2046,7 @@ class LinuxSandbox {
|
|
|
1924
2046
|
}
|
|
1925
2047
|
|
|
1926
2048
|
// src/security/kernel/macos.ts
|
|
1927
|
-
import { existsSync as
|
|
2049
|
+
import { existsSync as existsSync5 } from "fs";
|
|
1928
2050
|
|
|
1929
2051
|
class MacOSSandbox {
|
|
1930
2052
|
hasSandboxExec = false;
|
|
@@ -1935,7 +2057,7 @@ class MacOSSandbox {
|
|
|
1935
2057
|
if (process.platform !== "darwin") {
|
|
1936
2058
|
return;
|
|
1937
2059
|
}
|
|
1938
|
-
this.hasSandboxExec =
|
|
2060
|
+
this.hasSandboxExec = existsSync5("/usr/bin/sandbox-exec");
|
|
1939
2061
|
}
|
|
1940
2062
|
generateProfile(profile) {
|
|
1941
2063
|
const rules = [
|
|
@@ -1974,7 +2096,7 @@ class MacOSSandbox {
|
|
|
1974
2096
|
}
|
|
1975
2097
|
|
|
1976
2098
|
// src/security/kernel/manager.ts
|
|
1977
|
-
import { resolve as
|
|
2099
|
+
import { resolve as resolve4, normalize } from "path";
|
|
1978
2100
|
|
|
1979
2101
|
class KernelSandboxManager {
|
|
1980
2102
|
windowsSandbox;
|
|
@@ -1996,10 +2118,10 @@ class KernelSandboxManager {
|
|
|
1996
2118
|
};
|
|
1997
2119
|
}
|
|
1998
2120
|
buildDefaultProfile(cwd, allowNetwork = true) {
|
|
1999
|
-
const normCwd = normalize(
|
|
2121
|
+
const normCwd = normalize(resolve4(cwd));
|
|
2000
2122
|
return {
|
|
2001
2123
|
kind: "workspace-write",
|
|
2002
|
-
readableRoots: [normCwd,
|
|
2124
|
+
readableRoots: [normCwd, resolve4(process.cwd())],
|
|
2003
2125
|
writableRoots: [normCwd],
|
|
2004
2126
|
allowNetwork,
|
|
2005
2127
|
limits: {
|
|
@@ -2045,21 +2167,19 @@ var globalKernelSandbox = new KernelSandboxManager;
|
|
|
2045
2167
|
|
|
2046
2168
|
// src/storage/prefix-rules-store.ts
|
|
2047
2169
|
import { Database } from "bun:sqlite";
|
|
2048
|
-
import { existsSync as
|
|
2049
|
-
import { dirname as
|
|
2050
|
-
import { homedir as homedir2 } from "os";
|
|
2051
|
-
|
|
2170
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4 } from "fs";
|
|
2171
|
+
import { dirname as dirname3, resolve as resolve5 } from "path";
|
|
2052
2172
|
class PrefixRulesStore {
|
|
2053
2173
|
db;
|
|
2054
2174
|
constructor(dbOrPath) {
|
|
2055
2175
|
if (dbOrPath instanceof Database) {
|
|
2056
2176
|
this.db = dbOrPath;
|
|
2057
2177
|
} else {
|
|
2058
|
-
const effectivePath = dbOrPath ||
|
|
2178
|
+
const effectivePath = dbOrPath || getPrefixRulesDbPath();
|
|
2059
2179
|
if (effectivePath !== ":memory:") {
|
|
2060
|
-
const dir =
|
|
2061
|
-
if (!
|
|
2062
|
-
|
|
2180
|
+
const dir = dirname3(effectivePath);
|
|
2181
|
+
if (!existsSync6(dir)) {
|
|
2182
|
+
mkdirSync4(dir, { recursive: true });
|
|
2063
2183
|
}
|
|
2064
2184
|
}
|
|
2065
2185
|
this.db = new Database(effectivePath);
|
|
@@ -2082,7 +2202,7 @@ class PrefixRulesStore {
|
|
|
2082
2202
|
addRule(workspacePath, prefixTokens) {
|
|
2083
2203
|
if (!prefixTokens || prefixTokens.length === 0)
|
|
2084
2204
|
return;
|
|
2085
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
2205
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve5(workspacePath);
|
|
2086
2206
|
const tokensJson = JSON.stringify(prefixTokens);
|
|
2087
2207
|
const id = `${normalizedWs}:${tokensJson}`;
|
|
2088
2208
|
const query = this.db.prepare(`
|
|
@@ -2099,7 +2219,7 @@ class PrefixRulesStore {
|
|
|
2099
2219
|
isApproved(workspacePath, commandTokens) {
|
|
2100
2220
|
if (!commandTokens || commandTokens.length === 0)
|
|
2101
2221
|
return false;
|
|
2102
|
-
const normalizedWs =
|
|
2222
|
+
const normalizedWs = resolve5(workspacePath);
|
|
2103
2223
|
const query = this.db.prepare(`
|
|
2104
2224
|
SELECT prefix_tokens FROM approved_prefix_rules
|
|
2105
2225
|
WHERE workspace_path = $ws OR workspace_path = '*'
|
|
@@ -2118,7 +2238,7 @@ class PrefixRulesStore {
|
|
|
2118
2238
|
listRules(workspacePath) {
|
|
2119
2239
|
let rows;
|
|
2120
2240
|
if (workspacePath) {
|
|
2121
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
2241
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve5(workspacePath);
|
|
2122
2242
|
const query = this.db.prepare(`
|
|
2123
2243
|
SELECT prefix_tokens FROM approved_prefix_rules
|
|
2124
2244
|
WHERE workspace_path = $ws OR workspace_path = '*'
|
|
@@ -2137,7 +2257,7 @@ class PrefixRulesStore {
|
|
|
2137
2257
|
}).filter((r) => r.length > 0);
|
|
2138
2258
|
}
|
|
2139
2259
|
removeRule(workspacePath, prefixTokens) {
|
|
2140
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
2260
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve5(workspacePath);
|
|
2141
2261
|
const tokensJson = JSON.stringify(prefixTokens);
|
|
2142
2262
|
const id = `${normalizedWs}:${tokensJson}`;
|
|
2143
2263
|
const query = this.db.prepare(`
|
|
@@ -2270,7 +2390,7 @@ function createShellTool(policy = new ExecPolicy) {
|
|
|
2270
2390
|
} catch {}
|
|
2271
2391
|
});
|
|
2272
2392
|
}
|
|
2273
|
-
const timeoutPromise = new Promise((
|
|
2393
|
+
const timeoutPromise = new Promise((resolve6) => setTimeout(() => resolve6({ isTimeout: true }), timeoutMs));
|
|
2274
2394
|
const result = await Promise.race([
|
|
2275
2395
|
proc.exited.then(async (code) => {
|
|
2276
2396
|
const stdout = await new Response(proc.stdout).text();
|
|
@@ -2314,8 +2434,8 @@ ${result.stderr.trim()}`);
|
|
|
2314
2434
|
}
|
|
2315
2435
|
var shellTool = createShellTool();
|
|
2316
2436
|
// src/tools/handlers/file-ops.ts
|
|
2317
|
-
import { readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as
|
|
2318
|
-
import { resolve as
|
|
2437
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync3, existsSync as existsSync7, statSync as statSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
2438
|
+
import { resolve as resolve6, dirname as dirname4 } from "path";
|
|
2319
2439
|
var readFileTool = {
|
|
2320
2440
|
name: "read_file",
|
|
2321
2441
|
description: "Read the full text content of a file.",
|
|
@@ -2327,8 +2447,8 @@ var readFileTool = {
|
|
|
2327
2447
|
required: ["path"]
|
|
2328
2448
|
},
|
|
2329
2449
|
async execute(args, ctx) {
|
|
2330
|
-
const filePath =
|
|
2331
|
-
if (!
|
|
2450
|
+
const filePath = resolve6(ctx.cwd, String(args.path || ""));
|
|
2451
|
+
if (!existsSync7(filePath)) {
|
|
2332
2452
|
return { output: `Error: File not found: '${args.path}'`, isError: true };
|
|
2333
2453
|
}
|
|
2334
2454
|
try {
|
|
@@ -2349,15 +2469,15 @@ var listDirTool = {
|
|
|
2349
2469
|
}
|
|
2350
2470
|
},
|
|
2351
2471
|
async execute(args, ctx) {
|
|
2352
|
-
const dirPath =
|
|
2353
|
-
if (!
|
|
2472
|
+
const dirPath = resolve6(ctx.cwd, String(args.path || "."));
|
|
2473
|
+
if (!existsSync7(dirPath)) {
|
|
2354
2474
|
return { output: `Error: Directory not found: '${args.path}'`, isError: true };
|
|
2355
2475
|
}
|
|
2356
2476
|
try {
|
|
2357
|
-
const entries =
|
|
2477
|
+
const entries = readdirSync2(dirPath);
|
|
2358
2478
|
const formatted = entries.map((entry) => {
|
|
2359
|
-
const full =
|
|
2360
|
-
const isDir =
|
|
2479
|
+
const full = resolve6(dirPath, entry);
|
|
2480
|
+
const isDir = statSync2(full).isDirectory();
|
|
2361
2481
|
return `${isDir ? "[DIR]" : "[FILE]"} ${entry}`;
|
|
2362
2482
|
});
|
|
2363
2483
|
return { output: formatted.join(`
|
|
@@ -2380,7 +2500,7 @@ var writeFileTool = {
|
|
|
2380
2500
|
},
|
|
2381
2501
|
async execute(args, ctx) {
|
|
2382
2502
|
const rawPath = String(args.path || "");
|
|
2383
|
-
const filePath =
|
|
2503
|
+
const filePath = resolve6(ctx.cwd, rawPath);
|
|
2384
2504
|
if (ctx.execPolicy) {
|
|
2385
2505
|
const evalResult = ctx.execPolicy.shouldPromptFileEdit(rawPath);
|
|
2386
2506
|
if (evalResult.isPlanBlocked || ctx.mode === "plan") {
|
|
@@ -2398,7 +2518,7 @@ var writeFileTool = {
|
|
|
2398
2518
|
}
|
|
2399
2519
|
}
|
|
2400
2520
|
try {
|
|
2401
|
-
|
|
2521
|
+
mkdirSync5(dirname4(filePath), { recursive: true });
|
|
2402
2522
|
writeFileSync3(filePath, String(args.content ?? ""), "utf8");
|
|
2403
2523
|
return { output: `Successfully wrote to '${args.path}'` };
|
|
2404
2524
|
} catch (err) {
|
|
@@ -2521,8 +2641,8 @@ var updatePlanTool = {
|
|
|
2521
2641
|
}
|
|
2522
2642
|
};
|
|
2523
2643
|
// src/search/engine.ts
|
|
2524
|
-
import { readdirSync as
|
|
2525
|
-
import { resolve as
|
|
2644
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync4, statSync as statSync3, existsSync as existsSync8 } from "fs";
|
|
2645
|
+
import { resolve as resolve7, relative, join as join2, extname } from "path";
|
|
2526
2646
|
var DEFAULT_IGNORE_DIRS = new Set([
|
|
2527
2647
|
".git",
|
|
2528
2648
|
"node_modules",
|
|
@@ -2567,8 +2687,8 @@ var BINARY_EXTENSIONS = new Set([
|
|
|
2567
2687
|
|
|
2568
2688
|
class FileSearchEngine {
|
|
2569
2689
|
grep(cwd, options) {
|
|
2570
|
-
const searchRoot =
|
|
2571
|
-
if (!
|
|
2690
|
+
const searchRoot = resolve7(cwd, options.path || ".");
|
|
2691
|
+
if (!existsSync8(searchRoot)) {
|
|
2572
2692
|
return { matches: [], totalMatches: 0, truncated: false };
|
|
2573
2693
|
}
|
|
2574
2694
|
const maxResults = options.maxResults || 50;
|
|
@@ -2617,8 +2737,8 @@ class FileSearchEngine {
|
|
|
2617
2737
|
return { matches, totalMatches, truncated };
|
|
2618
2738
|
}
|
|
2619
2739
|
findFiles(cwd, options) {
|
|
2620
|
-
const searchRoot =
|
|
2621
|
-
if (!
|
|
2740
|
+
const searchRoot = resolve7(cwd, options.path || ".");
|
|
2741
|
+
if (!existsSync8(searchRoot))
|
|
2622
2742
|
return [];
|
|
2623
2743
|
const maxResults = options.maxResults || 100;
|
|
2624
2744
|
const gitignoreRules = this.loadGitignoreRules(searchRoot);
|
|
@@ -2657,8 +2777,8 @@ class FileSearchEngine {
|
|
|
2657
2777
|
}
|
|
2658
2778
|
loadGitignoreRules(root) {
|
|
2659
2779
|
const rules = new Set;
|
|
2660
|
-
const gitignorePath =
|
|
2661
|
-
if (
|
|
2780
|
+
const gitignorePath = join2(root, ".gitignore");
|
|
2781
|
+
if (existsSync8(gitignorePath)) {
|
|
2662
2782
|
try {
|
|
2663
2783
|
const lines = readFileSync4(gitignorePath, "utf8").split(`
|
|
2664
2784
|
`);
|
|
@@ -2675,7 +2795,7 @@ class FileSearchEngine {
|
|
|
2675
2795
|
collectFiles(dir, root, gitignoreRules, includePattern) {
|
|
2676
2796
|
const results = [];
|
|
2677
2797
|
try {
|
|
2678
|
-
const stat =
|
|
2798
|
+
const stat = statSync3(dir);
|
|
2679
2799
|
if (!stat.isDirectory()) {
|
|
2680
2800
|
if (!this.isBinary(dir)) {
|
|
2681
2801
|
results.push(dir);
|
|
@@ -2689,9 +2809,9 @@ class FileSearchEngine {
|
|
|
2689
2809
|
while (queue.length > 0) {
|
|
2690
2810
|
const currentDir = queue.shift();
|
|
2691
2811
|
try {
|
|
2692
|
-
const entries =
|
|
2812
|
+
const entries = readdirSync3(currentDir, { withFileTypes: true });
|
|
2693
2813
|
for (const entry of entries) {
|
|
2694
|
-
const fullPath =
|
|
2814
|
+
const fullPath = join2(currentDir, entry.name);
|
|
2695
2815
|
const relToRoot = relative(root, fullPath).replace(/\\/g, "/");
|
|
2696
2816
|
if (this.isIgnored(entry.name, relToRoot, gitignoreRules)) {
|
|
2697
2817
|
continue;
|
|
@@ -3088,6 +3208,111 @@ ${loaded.instructions}`
|
|
|
3088
3208
|
}
|
|
3089
3209
|
|
|
3090
3210
|
// src/memories/tool.ts
|
|
3211
|
+
function createSaveMemoryTool(store) {
|
|
3212
|
+
return {
|
|
3213
|
+
name: "save_memory",
|
|
3214
|
+
description: "Save a persistent memory note to the project's Auto-Memory bank. Categories: 'user' (role, workflow style, tooling preferences), 'feedback' (user corrections, guidelines), 'project' (external context, environments, deadlines), 'reference' (links, issue trackers, dashboards). Do NOT save facts easily discovered in code or git history.",
|
|
3215
|
+
parameters: {
|
|
3216
|
+
type: "object",
|
|
3217
|
+
properties: {
|
|
3218
|
+
category: {
|
|
3219
|
+
type: "string",
|
|
3220
|
+
description: "Category of memory: 'user', 'feedback', 'project', or 'reference'.",
|
|
3221
|
+
enum: ["user", "feedback", "project", "reference"]
|
|
3222
|
+
},
|
|
3223
|
+
name: {
|
|
3224
|
+
type: "string",
|
|
3225
|
+
description: "Short, descriptive snake_case identifier for this memory topic (e.g. 'testing_strategy', 'preferred_framework', 'staging_api')."
|
|
3226
|
+
},
|
|
3227
|
+
description: {
|
|
3228
|
+
type: "string",
|
|
3229
|
+
description: "One-line summary to display in the MEMORY.md index (e.g. 'Prefers Vitest without database mocks')."
|
|
3230
|
+
},
|
|
3231
|
+
content: {
|
|
3232
|
+
type: "string",
|
|
3233
|
+
description: "Detailed description of the fact, preference, or learned correction."
|
|
3234
|
+
}
|
|
3235
|
+
},
|
|
3236
|
+
required: ["category", "name", "content"]
|
|
3237
|
+
},
|
|
3238
|
+
async execute(args, context) {
|
|
3239
|
+
const category = args.category || "project";
|
|
3240
|
+
const name = String(args.name || `topic_${Date.now()}`);
|
|
3241
|
+
const content = String(args.content || "").trim();
|
|
3242
|
+
const description = args.description ? String(args.description).trim() : undefined;
|
|
3243
|
+
if (!content) {
|
|
3244
|
+
return { output: "Error: memory content cannot be empty", isError: true };
|
|
3245
|
+
}
|
|
3246
|
+
const entry = store.saveTopicMemory({
|
|
3247
|
+
category,
|
|
3248
|
+
name,
|
|
3249
|
+
description,
|
|
3250
|
+
content,
|
|
3251
|
+
cwd: context.cwd
|
|
3252
|
+
});
|
|
3253
|
+
return {
|
|
3254
|
+
output: `\u2713 Saved Auto-Memory topic: [${entry.category}] "${entry.name}" -> ${entry.filePath}`
|
|
3255
|
+
};
|
|
3256
|
+
}
|
|
3257
|
+
};
|
|
3258
|
+
}
|
|
3259
|
+
function createReadMemoryTool(store) {
|
|
3260
|
+
return {
|
|
3261
|
+
name: "read_memory",
|
|
3262
|
+
description: "Read the full details of a specific Auto-Memory topic file recorded in the project's memory index.",
|
|
3263
|
+
parameters: {
|
|
3264
|
+
type: "object",
|
|
3265
|
+
properties: {
|
|
3266
|
+
topic: {
|
|
3267
|
+
type: "string",
|
|
3268
|
+
description: "Name or filename of the memory topic to read (e.g. 'testing_strategy' or 'feedback_testing.md')."
|
|
3269
|
+
}
|
|
3270
|
+
},
|
|
3271
|
+
required: ["topic"]
|
|
3272
|
+
},
|
|
3273
|
+
async execute(args, context) {
|
|
3274
|
+
const topic = String(args.topic || "").trim();
|
|
3275
|
+
if (!topic) {
|
|
3276
|
+
return { output: "Error: topic name is required", isError: true };
|
|
3277
|
+
}
|
|
3278
|
+
const memory = store.readTopicMemory(topic, context.cwd);
|
|
3279
|
+
if (!memory) {
|
|
3280
|
+
return {
|
|
3281
|
+
output: `No memory topic found matching '${topic}' in this project.`,
|
|
3282
|
+
isError: true
|
|
3283
|
+
};
|
|
3284
|
+
}
|
|
3285
|
+
return {
|
|
3286
|
+
output: `# Topic: ${memory.name} (${memory.type})
|
|
3287
|
+
Modified: ${memory.modified}
|
|
3288
|
+
|
|
3289
|
+
${memory.content}`
|
|
3290
|
+
};
|
|
3291
|
+
}
|
|
3292
|
+
};
|
|
3293
|
+
}
|
|
3294
|
+
function createListMemoriesTool(store) {
|
|
3295
|
+
return {
|
|
3296
|
+
name: "list_memories",
|
|
3297
|
+
description: "List all persistent memory topics and index for the current project repository.",
|
|
3298
|
+
parameters: {
|
|
3299
|
+
type: "object",
|
|
3300
|
+
properties: {}
|
|
3301
|
+
},
|
|
3302
|
+
async execute(_args, context) {
|
|
3303
|
+
const topics = store.listProjectMemories(context.cwd);
|
|
3304
|
+
if (topics.length === 0) {
|
|
3305
|
+
return { output: "No persistent Auto-Memories have been recorded for this project yet." };
|
|
3306
|
+
}
|
|
3307
|
+
const lines = topics.map((t) => `\u2022 [${t.type}] **${t.name}**: ${t.description || t.content.split(`
|
|
3308
|
+
`)[0]} (file: ${t.filePath})`);
|
|
3309
|
+
return { output: `Project Auto-Memories (${topics.length} topics):
|
|
3310
|
+
|
|
3311
|
+
${lines.join(`
|
|
3312
|
+
`)}` };
|
|
3313
|
+
}
|
|
3314
|
+
};
|
|
3315
|
+
}
|
|
3091
3316
|
function createRememberTool(store) {
|
|
3092
3317
|
return {
|
|
3093
3318
|
name: "remember",
|
|
@@ -3098,16 +3323,15 @@ function createRememberTool(store) {
|
|
|
3098
3323
|
category: {
|
|
3099
3324
|
type: "string",
|
|
3100
3325
|
description: "Category of the memory.",
|
|
3101
|
-
enum: ["preference", "guideline", "architecture", "note"]
|
|
3326
|
+
enum: ["preference", "guideline", "architecture", "note", "user", "feedback", "project", "reference"]
|
|
3102
3327
|
},
|
|
3103
3328
|
content: {
|
|
3104
3329
|
type: "string",
|
|
3105
3330
|
description: "The concise rule, preference, or fact to remember permanently."
|
|
3106
3331
|
},
|
|
3107
|
-
|
|
3332
|
+
name: {
|
|
3108
3333
|
type: "string",
|
|
3109
|
-
description: "
|
|
3110
|
-
enum: ["global", "workspace"]
|
|
3334
|
+
description: "Optional topic name."
|
|
3111
3335
|
}
|
|
3112
3336
|
},
|
|
3113
3337
|
required: ["category", "content"]
|
|
@@ -3115,22 +3339,30 @@ function createRememberTool(store) {
|
|
|
3115
3339
|
async execute(args, context) {
|
|
3116
3340
|
const category = args.category || "preference";
|
|
3117
3341
|
const content = String(args.content || "");
|
|
3118
|
-
const
|
|
3342
|
+
const name = args.name ? String(args.name) : undefined;
|
|
3119
3343
|
if (!content) {
|
|
3120
3344
|
return { output: "Error: memory content cannot be empty", isError: true };
|
|
3121
3345
|
}
|
|
3122
3346
|
const entry = store.addMemory({
|
|
3123
3347
|
category,
|
|
3124
3348
|
content,
|
|
3125
|
-
|
|
3349
|
+
name,
|
|
3126
3350
|
cwd: context.cwd
|
|
3127
3351
|
});
|
|
3128
3352
|
return {
|
|
3129
|
-
output: `Successfully saved to
|
|
3353
|
+
output: `Successfully saved to Auto-Memory bank: [${entry.category}] "${entry.name || entry.content}"`
|
|
3130
3354
|
};
|
|
3131
3355
|
}
|
|
3132
3356
|
};
|
|
3133
3357
|
}
|
|
3358
|
+
function createAutoMemoryTools(store) {
|
|
3359
|
+
return [
|
|
3360
|
+
createSaveMemoryTool(store),
|
|
3361
|
+
createReadMemoryTool(store),
|
|
3362
|
+
createListMemoriesTool(store),
|
|
3363
|
+
createRememberTool(store)
|
|
3364
|
+
];
|
|
3365
|
+
}
|
|
3134
3366
|
|
|
3135
3367
|
// src/worktree/tools.ts
|
|
3136
3368
|
function createWorktreeTools(manager) {
|
|
@@ -3265,7 +3497,9 @@ function createDefaultTools(options = {}) {
|
|
|
3265
3497
|
router2.register(createSkillTool(options.skillsLoader));
|
|
3266
3498
|
}
|
|
3267
3499
|
if (options.memoryStore) {
|
|
3268
|
-
|
|
3500
|
+
for (const tool of createAutoMemoryTools(options.memoryStore)) {
|
|
3501
|
+
router2.register(tool);
|
|
3502
|
+
}
|
|
3269
3503
|
}
|
|
3270
3504
|
if (options.worktreeManager) {
|
|
3271
3505
|
for (const tool of createWorktreeTools(options.worktreeManager)) {
|
|
@@ -3275,21 +3509,21 @@ function createDefaultTools(options = {}) {
|
|
|
3275
3509
|
return router2;
|
|
3276
3510
|
}
|
|
3277
3511
|
// src/security/sandbox.ts
|
|
3278
|
-
import { resolve as
|
|
3512
|
+
import { resolve as resolve8, normalize as normalize2, relative as relative2 } from "path";
|
|
3279
3513
|
class PathSandbox {
|
|
3280
3514
|
allowedRoots;
|
|
3281
3515
|
constructor(workspaceRoots) {
|
|
3282
|
-
this.allowedRoots = workspaceRoots.map((root) => normalize2(
|
|
3516
|
+
this.allowedRoots = workspaceRoots.map((root) => normalize2(resolve8(root)));
|
|
3283
3517
|
}
|
|
3284
3518
|
isPathAllowed(targetPath) {
|
|
3285
|
-
const normalized = normalize2(
|
|
3519
|
+
const normalized = normalize2(resolve8(targetPath));
|
|
3286
3520
|
return this.allowedRoots.some((root) => {
|
|
3287
3521
|
const rel = relative2(root, normalized);
|
|
3288
|
-
return !rel.startsWith("..") && !
|
|
3522
|
+
return !rel.startsWith("..") && !resolve8(root, rel).startsWith("..");
|
|
3289
3523
|
});
|
|
3290
3524
|
}
|
|
3291
3525
|
assertPathAllowed(targetPath) {
|
|
3292
|
-
const normalized = normalize2(
|
|
3526
|
+
const normalized = normalize2(resolve8(targetPath));
|
|
3293
3527
|
if (!this.isPathAllowed(normalized)) {
|
|
3294
3528
|
throw new ExecutionPolicyError(`Path access denied: '${targetPath}' is outside the allowed workspace boundaries (${this.allowedRoots.join(", ")})`);
|
|
3295
3529
|
}
|
|
@@ -3297,8 +3531,8 @@ class PathSandbox {
|
|
|
3297
3531
|
}
|
|
3298
3532
|
}
|
|
3299
3533
|
// src/security/scanner.ts
|
|
3300
|
-
import { existsSync as
|
|
3301
|
-
import { join as
|
|
3534
|
+
import { existsSync as existsSync9, readdirSync as readdirSync4, readFileSync as readFileSync5, statSync as statSync4 } from "fs";
|
|
3535
|
+
import { join as join3, relative as relative3, resolve as resolve9 } from "path";
|
|
3302
3536
|
var SECURITY_RULES = [
|
|
3303
3537
|
{
|
|
3304
3538
|
id: "SEC-001",
|
|
@@ -3430,27 +3664,27 @@ var IGNORED_FILES = new Set([
|
|
|
3430
3664
|
]);
|
|
3431
3665
|
async function runSecurityScan(targetDir, options = {}) {
|
|
3432
3666
|
const startTime = performance.now();
|
|
3433
|
-
const root =
|
|
3667
|
+
const root = resolve9(targetDir);
|
|
3434
3668
|
const isDirectTestDir = targetDir.includes("test") || Boolean(options.includeTests);
|
|
3435
3669
|
const maxFiles = options.maxFiles || 2000;
|
|
3436
3670
|
const findings = [];
|
|
3437
3671
|
let scannedCount = 0;
|
|
3438
3672
|
function walk(current) {
|
|
3439
|
-
if (scannedCount >= maxFiles || !
|
|
3673
|
+
if (scannedCount >= maxFiles || !existsSync9(current))
|
|
3440
3674
|
return;
|
|
3441
3675
|
let entries;
|
|
3442
3676
|
try {
|
|
3443
|
-
entries =
|
|
3677
|
+
entries = readdirSync4(current);
|
|
3444
3678
|
} catch {
|
|
3445
3679
|
return;
|
|
3446
3680
|
}
|
|
3447
3681
|
for (const entry of entries) {
|
|
3448
3682
|
if (scannedCount >= maxFiles)
|
|
3449
3683
|
break;
|
|
3450
|
-
const fullPath =
|
|
3684
|
+
const fullPath = join3(current, entry);
|
|
3451
3685
|
let stat;
|
|
3452
3686
|
try {
|
|
3453
|
-
stat =
|
|
3687
|
+
stat = statSync4(fullPath);
|
|
3454
3688
|
} catch {
|
|
3455
3689
|
continue;
|
|
3456
3690
|
}
|
|
@@ -3571,14 +3805,13 @@ function formatWorldStatePrompt(state) {
|
|
|
3571
3805
|
`);
|
|
3572
3806
|
}
|
|
3573
3807
|
// src/prompts/loader.ts
|
|
3574
|
-
import { existsSync as
|
|
3575
|
-
import { resolve as
|
|
3576
|
-
import { homedir as
|
|
3577
|
-
|
|
3808
|
+
import { existsSync as existsSync10, readFileSync as readFileSync6 } from "fs";
|
|
3809
|
+
import { resolve as resolve10, join as join4 } from "path";
|
|
3810
|
+
import { homedir as homedir2 } from "os";
|
|
3578
3811
|
class PromptTemplateLoader {
|
|
3579
3812
|
builtInTemplatesDir;
|
|
3580
3813
|
constructor(builtInDir) {
|
|
3581
|
-
this.builtInTemplatesDir = builtInDir ||
|
|
3814
|
+
this.builtInTemplatesDir = builtInDir || resolve10(join4(import.meta.dir, "..", "..", "templates"));
|
|
3582
3815
|
}
|
|
3583
3816
|
loadTemplate(relativePath, variables = {}, cwd) {
|
|
3584
3817
|
const rawContent = this.resolveTemplateContent(relativePath, cwd);
|
|
@@ -3587,21 +3820,27 @@ class PromptTemplateLoader {
|
|
|
3587
3820
|
resolveTemplateContent(relativePath, cwd) {
|
|
3588
3821
|
const normalizedRel = relativePath.replace(/^\/+/, "");
|
|
3589
3822
|
if (cwd) {
|
|
3590
|
-
const workspacePath =
|
|
3591
|
-
if (
|
|
3823
|
+
const workspacePath = join4(cwd, ".agents", "templates", normalizedRel);
|
|
3824
|
+
if (existsSync10(workspacePath)) {
|
|
3592
3825
|
try {
|
|
3593
3826
|
return readFileSync6(workspacePath, "utf-8");
|
|
3594
3827
|
} catch {}
|
|
3595
3828
|
}
|
|
3596
3829
|
}
|
|
3597
|
-
const globalPath =
|
|
3598
|
-
if (
|
|
3830
|
+
const globalPath = join4(getGlobalTemplatesDir(), normalizedRel);
|
|
3831
|
+
if (existsSync10(globalPath)) {
|
|
3599
3832
|
try {
|
|
3600
3833
|
return readFileSync6(globalPath, "utf-8");
|
|
3601
3834
|
} catch {}
|
|
3602
3835
|
}
|
|
3603
|
-
const
|
|
3604
|
-
if (
|
|
3836
|
+
const legacyGlobalPath = join4(homedir2(), ".groupy", "templates", normalizedRel);
|
|
3837
|
+
if (existsSync10(legacyGlobalPath)) {
|
|
3838
|
+
try {
|
|
3839
|
+
return readFileSync6(legacyGlobalPath, "utf-8");
|
|
3840
|
+
} catch {}
|
|
3841
|
+
}
|
|
3842
|
+
const builtInPath = join4(this.builtInTemplatesDir, normalizedRel);
|
|
3843
|
+
if (existsSync10(builtInPath)) {
|
|
3605
3844
|
try {
|
|
3606
3845
|
return readFileSync6(builtInPath, "utf-8");
|
|
3607
3846
|
} catch {}
|
|
@@ -3618,8 +3857,8 @@ class PromptTemplateLoader {
|
|
|
3618
3857
|
var globalPromptLoader = new PromptTemplateLoader;
|
|
3619
3858
|
|
|
3620
3859
|
// src/prompts/agents-md.ts
|
|
3621
|
-
import { existsSync as
|
|
3622
|
-
import { resolve as
|
|
3860
|
+
import { existsSync as existsSync11, readFileSync as readFileSync7 } from "fs";
|
|
3861
|
+
import { resolve as resolve11, join as join5, dirname as dirname5 } from "path";
|
|
3623
3862
|
var DEFAULT_AGENTS_MD_FILENAMES = [
|
|
3624
3863
|
"AGENTS.override.md",
|
|
3625
3864
|
"AGENTS.md",
|
|
@@ -3634,28 +3873,28 @@ var AGENTS_MD_SEPARATOR = `
|
|
|
3634
3873
|
|
|
3635
3874
|
class AgentsMdLoader {
|
|
3636
3875
|
findProjectRoot(startDir) {
|
|
3637
|
-
let current =
|
|
3876
|
+
let current = resolve11(startDir);
|
|
3638
3877
|
while (true) {
|
|
3639
|
-
if (
|
|
3878
|
+
if (existsSync11(join5(current, ".git"))) {
|
|
3640
3879
|
return current;
|
|
3641
3880
|
}
|
|
3642
|
-
const parent =
|
|
3881
|
+
const parent = dirname5(current);
|
|
3643
3882
|
if (parent === current) {
|
|
3644
|
-
return
|
|
3883
|
+
return resolve11(startDir);
|
|
3645
3884
|
}
|
|
3646
3885
|
current = parent;
|
|
3647
3886
|
}
|
|
3648
3887
|
}
|
|
3649
3888
|
collectDirectoryHierarchy(targetDir, rootDir) {
|
|
3650
3889
|
const hierarchy = [];
|
|
3651
|
-
let current =
|
|
3652
|
-
const normalizedRoot =
|
|
3890
|
+
let current = resolve11(targetDir);
|
|
3891
|
+
const normalizedRoot = resolve11(rootDir);
|
|
3653
3892
|
while (true) {
|
|
3654
3893
|
hierarchy.unshift(current);
|
|
3655
3894
|
if (current === normalizedRoot) {
|
|
3656
3895
|
break;
|
|
3657
3896
|
}
|
|
3658
|
-
const parent =
|
|
3897
|
+
const parent = dirname5(current);
|
|
3659
3898
|
if (parent === current) {
|
|
3660
3899
|
break;
|
|
3661
3900
|
}
|
|
@@ -3670,8 +3909,8 @@ class AgentsMdLoader {
|
|
|
3670
3909
|
const sourcePaths = [];
|
|
3671
3910
|
for (const dir of dirHierarchy) {
|
|
3672
3911
|
for (const filename of fallbackFilenames) {
|
|
3673
|
-
const filePath =
|
|
3674
|
-
if (
|
|
3912
|
+
const filePath = join5(dir, filename);
|
|
3913
|
+
if (existsSync11(filePath)) {
|
|
3675
3914
|
try {
|
|
3676
3915
|
const content = readFileSync7(filePath, "utf-8").trim();
|
|
3677
3916
|
if (content) {
|
|
@@ -4024,6 +4263,17 @@ async function runTurn(session, turnContext, input) {
|
|
|
4024
4263
|
}
|
|
4025
4264
|
continue;
|
|
4026
4265
|
}
|
|
4266
|
+
if (!currentAgentText.trim() && toolCallRequests.length === 0) {
|
|
4267
|
+
if (iteration === 1 && iteration < turnContext.maxIterations) {
|
|
4268
|
+
session.addHistoryItem({
|
|
4269
|
+
id: `msg_nudge_${Date.now()}`,
|
|
4270
|
+
type: "user_message",
|
|
4271
|
+
content: "Please proceed with executing the task. Provide your complete analysis or call the required tools now.",
|
|
4272
|
+
createdAt: Date.now()
|
|
4273
|
+
});
|
|
4274
|
+
continue;
|
|
4275
|
+
}
|
|
4276
|
+
}
|
|
4027
4277
|
break;
|
|
4028
4278
|
}
|
|
4029
4279
|
const totalContextTokens = estimateTotalTokens(session.getHistory()) + Math.ceil(effectiveSystemPrompt.length / 4);
|
|
@@ -4249,13 +4499,13 @@ class Session {
|
|
|
4249
4499
|
type: "StatusChanged",
|
|
4250
4500
|
status: "waiting_approval"
|
|
4251
4501
|
});
|
|
4252
|
-
return new Promise((
|
|
4502
|
+
return new Promise((resolve12) => {
|
|
4253
4503
|
this.pendingApprovals.set(params.approvalId, (approved) => {
|
|
4254
4504
|
this.emitEvent({
|
|
4255
4505
|
type: "StatusChanged",
|
|
4256
4506
|
status: "running"
|
|
4257
4507
|
});
|
|
4258
|
-
|
|
4508
|
+
resolve12(approved);
|
|
4259
4509
|
});
|
|
4260
4510
|
});
|
|
4261
4511
|
}
|
|
@@ -4278,13 +4528,13 @@ class Session {
|
|
|
4278
4528
|
type: "StatusChanged",
|
|
4279
4529
|
status: "waiting_user_input"
|
|
4280
4530
|
});
|
|
4281
|
-
return new Promise((
|
|
4531
|
+
return new Promise((resolve12) => {
|
|
4282
4532
|
this.pendingUserQuestions.set(params.questionId, (answer) => {
|
|
4283
4533
|
this.emitEvent({
|
|
4284
4534
|
type: "StatusChanged",
|
|
4285
4535
|
status: "running"
|
|
4286
4536
|
});
|
|
4287
|
-
|
|
4537
|
+
resolve12(answer);
|
|
4288
4538
|
});
|
|
4289
4539
|
});
|
|
4290
4540
|
}
|
|
@@ -4314,7 +4564,7 @@ class Session {
|
|
|
4314
4564
|
return handleTurnInput(this, { text, images });
|
|
4315
4565
|
}
|
|
4316
4566
|
async promptAndWait(text, images, timeoutMs = 30000) {
|
|
4317
|
-
return new Promise((
|
|
4567
|
+
return new Promise((resolve12, reject) => {
|
|
4318
4568
|
const timer = setTimeout(() => {
|
|
4319
4569
|
unsub();
|
|
4320
4570
|
reject(new Error(`Turn timed out after ${timeoutMs}ms`));
|
|
@@ -4323,7 +4573,7 @@ class Session {
|
|
|
4323
4573
|
if (event.msg.type === "TurnCompleted") {
|
|
4324
4574
|
clearTimeout(timer);
|
|
4325
4575
|
unsub();
|
|
4326
|
-
|
|
4576
|
+
resolve12();
|
|
4327
4577
|
} else if (event.msg.type === "Error") {
|
|
4328
4578
|
clearTimeout(timer);
|
|
4329
4579
|
unsub();
|
|
@@ -4342,8 +4592,8 @@ class Session {
|
|
|
4342
4592
|
if (this.submissionQueue.length > 0) {
|
|
4343
4593
|
yield this.submissionQueue.shift();
|
|
4344
4594
|
} else {
|
|
4345
|
-
const nextSub = await new Promise((
|
|
4346
|
-
this.submissionResolvers.push(
|
|
4595
|
+
const nextSub = await new Promise((resolve12) => {
|
|
4596
|
+
this.submissionResolvers.push(resolve12);
|
|
4347
4597
|
});
|
|
4348
4598
|
yield nextSub;
|
|
4349
4599
|
}
|
|
@@ -4607,13 +4857,13 @@ class StdioTransport {
|
|
|
4607
4857
|
if (this.isClosed || !this.proc || !this.proc.stdin) {
|
|
4608
4858
|
throw new GroupyError("MCP Stdio transport is closed");
|
|
4609
4859
|
}
|
|
4610
|
-
return new Promise((
|
|
4860
|
+
return new Promise((resolve12, reject) => {
|
|
4611
4861
|
const timeoutMs = 30000;
|
|
4612
4862
|
const timer = setTimeout(() => {
|
|
4613
4863
|
this.pendingRequests.delete(request.id);
|
|
4614
4864
|
reject(new GroupyError(`MCP request timed out after ${timeoutMs}ms (method: ${request.method})`));
|
|
4615
4865
|
}, timeoutMs);
|
|
4616
|
-
this.pendingRequests.set(request.id, { resolve:
|
|
4866
|
+
this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
|
|
4617
4867
|
try {
|
|
4618
4868
|
const payload = JSON.stringify(request) + `
|
|
4619
4869
|
`;
|
|
@@ -4746,12 +4996,12 @@ class SseTransport {
|
|
|
4746
4996
|
if (!this.messageUrl) {
|
|
4747
4997
|
this.messageUrl = this.endpointUrl;
|
|
4748
4998
|
}
|
|
4749
|
-
return new Promise((
|
|
4999
|
+
return new Promise((resolve12, reject) => {
|
|
4750
5000
|
const timer = setTimeout(() => {
|
|
4751
5001
|
this.pendingRequests.delete(request.id);
|
|
4752
5002
|
reject(new GroupyError(`MCP SSE request timed out (method: ${request.method})`));
|
|
4753
5003
|
}, 30000);
|
|
4754
|
-
this.pendingRequests.set(request.id, { resolve:
|
|
5004
|
+
this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
|
|
4755
5005
|
fetch(this.messageUrl, {
|
|
4756
5006
|
method: "POST",
|
|
4757
5007
|
headers: {
|
|
@@ -4988,8 +5238,8 @@ class McpClient {
|
|
|
4988
5238
|
}
|
|
4989
5239
|
}
|
|
4990
5240
|
// src/mcp/manager.ts
|
|
4991
|
-
import { existsSync as
|
|
4992
|
-
import { resolve as
|
|
5241
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
|
|
5242
|
+
import { resolve as resolve12, dirname as dirname6, join as join6 } from "path";
|
|
4993
5243
|
class McpManager {
|
|
4994
5244
|
clients = new Map;
|
|
4995
5245
|
serverConfigs = new Map;
|
|
@@ -5023,8 +5273,8 @@ class McpManager {
|
|
|
5023
5273
|
}
|
|
5024
5274
|
}
|
|
5025
5275
|
async loadConfigFile(filePath) {
|
|
5026
|
-
const fullPath =
|
|
5027
|
-
if (!
|
|
5276
|
+
const fullPath = resolve12(filePath);
|
|
5277
|
+
if (!existsSync12(fullPath))
|
|
5028
5278
|
return;
|
|
5029
5279
|
this.loadedConfigFiles.add(fullPath);
|
|
5030
5280
|
try {
|
|
@@ -5277,13 +5527,13 @@ class McpManager {
|
|
|
5277
5527
|
`);
|
|
5278
5528
|
}
|
|
5279
5529
|
saveServerToConfigFile(filePath, name, config) {
|
|
5280
|
-
const fullPath =
|
|
5281
|
-
const dir =
|
|
5282
|
-
if (!
|
|
5283
|
-
|
|
5530
|
+
const fullPath = resolve12(filePath);
|
|
5531
|
+
const dir = dirname6(fullPath);
|
|
5532
|
+
if (!existsSync12(dir)) {
|
|
5533
|
+
mkdirSync6(dir, { recursive: true });
|
|
5284
5534
|
}
|
|
5285
5535
|
let existing = { mcpServers: {} };
|
|
5286
|
-
if (
|
|
5536
|
+
if (existsSync12(fullPath)) {
|
|
5287
5537
|
try {
|
|
5288
5538
|
const content = readFileSync8(fullPath, "utf8");
|
|
5289
5539
|
existing = JSON.parse(content);
|
|
@@ -5297,8 +5547,8 @@ class McpManager {
|
|
|
5297
5547
|
this.loadedConfigFiles.add(fullPath);
|
|
5298
5548
|
}
|
|
5299
5549
|
removeServerFromConfigFile(filePath, name) {
|
|
5300
|
-
const fullPath =
|
|
5301
|
-
if (!
|
|
5550
|
+
const fullPath = resolve12(filePath);
|
|
5551
|
+
if (!existsSync12(fullPath))
|
|
5302
5552
|
return false;
|
|
5303
5553
|
try {
|
|
5304
5554
|
const content = readFileSync8(fullPath, "utf8");
|
|
@@ -5328,11 +5578,11 @@ class McpManager {
|
|
|
5328
5578
|
}
|
|
5329
5579
|
}
|
|
5330
5580
|
getDefaultConfigFile(cwd = process.cwd()) {
|
|
5331
|
-
const workspaceConfig =
|
|
5332
|
-
if (
|
|
5581
|
+
const workspaceConfig = join6(cwd, ".mcp.json");
|
|
5582
|
+
if (existsSync12(workspaceConfig))
|
|
5333
5583
|
return workspaceConfig;
|
|
5334
|
-
const altConfig =
|
|
5335
|
-
if (
|
|
5584
|
+
const altConfig = join6(cwd, "mcp_config.json");
|
|
5585
|
+
if (existsSync12(altConfig))
|
|
5336
5586
|
return altConfig;
|
|
5337
5587
|
return workspaceConfig;
|
|
5338
5588
|
}
|
|
@@ -5351,11 +5601,11 @@ class McpManager {
|
|
|
5351
5601
|
}
|
|
5352
5602
|
}
|
|
5353
5603
|
// src/mcp/servers/chrome-devtools/index.ts
|
|
5354
|
-
import { resolve as
|
|
5604
|
+
import { resolve as resolve14 } from "path";
|
|
5355
5605
|
|
|
5356
5606
|
// src/mcp/servers/chrome-devtools/launcher.ts
|
|
5357
|
-
import { existsSync as
|
|
5358
|
-
import { join as
|
|
5607
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync7, rmSync } from "fs";
|
|
5608
|
+
import { join as join7 } from "path";
|
|
5359
5609
|
import { tmpdir } from "os";
|
|
5360
5610
|
class BrowserLauncher {
|
|
5361
5611
|
proc = null;
|
|
@@ -5363,21 +5613,21 @@ class BrowserLauncher {
|
|
|
5363
5613
|
wsDebuggerUrl = null;
|
|
5364
5614
|
port = 0;
|
|
5365
5615
|
static findBrowserExecutable() {
|
|
5366
|
-
if (process.env.CHROME_PATH &&
|
|
5616
|
+
if (process.env.CHROME_PATH && existsSync13(process.env.CHROME_PATH)) {
|
|
5367
5617
|
return process.env.CHROME_PATH;
|
|
5368
5618
|
}
|
|
5369
5619
|
if (process.platform === "win32") {
|
|
5370
5620
|
const candidates = [
|
|
5371
5621
|
"C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe",
|
|
5372
5622
|
"C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe",
|
|
5373
|
-
|
|
5623
|
+
join7(process.env.LOCALAPPDATA || "", "Google\\Chrome\\Application\\chrome.exe"),
|
|
5374
5624
|
"C:\\Program Files (x86)\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
5375
5625
|
"C:\\Program Files\\Microsoft\\Edge\\Application\\msedge.exe",
|
|
5376
5626
|
"C:\\Program Files\\BraveSoftware\\Brave-Browser\\Application\\brave.exe",
|
|
5377
|
-
|
|
5627
|
+
join7(process.env.LOCALAPPDATA || "", "BraveSoftware\\Brave-Browser\\Application\\brave.exe")
|
|
5378
5628
|
];
|
|
5379
5629
|
for (const path of candidates) {
|
|
5380
|
-
if (path &&
|
|
5630
|
+
if (path && existsSync13(path))
|
|
5381
5631
|
return path;
|
|
5382
5632
|
}
|
|
5383
5633
|
} else if (process.platform === "darwin") {
|
|
@@ -5388,7 +5638,7 @@ class BrowserLauncher {
|
|
|
5388
5638
|
"/Applications/Brave Browser.app/Contents/MacOS/Brave Browser"
|
|
5389
5639
|
];
|
|
5390
5640
|
for (const path of candidates) {
|
|
5391
|
-
if (
|
|
5641
|
+
if (existsSync13(path))
|
|
5392
5642
|
return path;
|
|
5393
5643
|
}
|
|
5394
5644
|
} else {
|
|
@@ -5401,7 +5651,7 @@ class BrowserLauncher {
|
|
|
5401
5651
|
"/usr/bin/microsoft-edge"
|
|
5402
5652
|
];
|
|
5403
5653
|
for (const path of candidates) {
|
|
5404
|
-
if (
|
|
5654
|
+
if (existsSync13(path))
|
|
5405
5655
|
return path;
|
|
5406
5656
|
}
|
|
5407
5657
|
}
|
|
@@ -5413,8 +5663,8 @@ class BrowserLauncher {
|
|
|
5413
5663
|
throw new Error("No supported browser (Google Chrome, Chromium, MS Edge, Brave) found on this machine. Please install Chrome or specify CHROME_PATH.");
|
|
5414
5664
|
}
|
|
5415
5665
|
this.port = options.port || 9200 + Math.floor(Math.random() * 500);
|
|
5416
|
-
this.tempUserDataDir = options.userDataDir ||
|
|
5417
|
-
|
|
5666
|
+
this.tempUserDataDir = options.userDataDir || join7(tmpdir(), `groupy_chrome_${Date.now()}_${Math.random().toString(36).slice(2)}`);
|
|
5667
|
+
mkdirSync7(this.tempUserDataDir, { recursive: true });
|
|
5418
5668
|
const isHeadless = options.headless ?? true;
|
|
5419
5669
|
const launchArgs = [
|
|
5420
5670
|
executable,
|
|
@@ -5491,7 +5741,7 @@ class BrowserLauncher {
|
|
|
5491
5741
|
}
|
|
5492
5742
|
this.proc = null;
|
|
5493
5743
|
}
|
|
5494
|
-
if (this.tempUserDataDir &&
|
|
5744
|
+
if (this.tempUserDataDir && existsSync13(this.tempUserDataDir)) {
|
|
5495
5745
|
try {
|
|
5496
5746
|
rmSync(this.tempUserDataDir, { recursive: true, force: true });
|
|
5497
5747
|
} catch {}
|
|
@@ -5513,7 +5763,7 @@ class CdpSession {
|
|
|
5513
5763
|
this.wsUrl = wsUrl;
|
|
5514
5764
|
}
|
|
5515
5765
|
async connect() {
|
|
5516
|
-
return new Promise((
|
|
5766
|
+
return new Promise((resolve13, reject) => {
|
|
5517
5767
|
try {
|
|
5518
5768
|
this.ws = new WebSocket(this.wsUrl);
|
|
5519
5769
|
const onOpen = async () => {
|
|
@@ -5529,7 +5779,7 @@ class CdpSession {
|
|
|
5529
5779
|
await this.send("Network.enable").catch(() => {});
|
|
5530
5780
|
await this.send("Page.setLifecycleEventsEnabled", { enabled: true }).catch(() => {});
|
|
5531
5781
|
this.setupEventHandlers();
|
|
5532
|
-
|
|
5782
|
+
resolve13();
|
|
5533
5783
|
} catch (err) {
|
|
5534
5784
|
reject(err);
|
|
5535
5785
|
}
|
|
@@ -5651,13 +5901,13 @@ class CdpSession {
|
|
|
5651
5901
|
throw new Error("CDP WebSocket is not connected");
|
|
5652
5902
|
}
|
|
5653
5903
|
const id = this.nextId++;
|
|
5654
|
-
return new Promise((
|
|
5904
|
+
return new Promise((resolve13, reject) => {
|
|
5655
5905
|
const timeoutMs = 30000;
|
|
5656
5906
|
const timer = setTimeout(() => {
|
|
5657
5907
|
this.pending.delete(id);
|
|
5658
5908
|
reject(new Error(`CDP command '${method}' timed out after ${timeoutMs}ms`));
|
|
5659
5909
|
}, timeoutMs);
|
|
5660
|
-
this.pending.set(id, { resolve:
|
|
5910
|
+
this.pending.set(id, { resolve: resolve13, reject, timer });
|
|
5661
5911
|
try {
|
|
5662
5912
|
this.ws?.send(JSON.stringify({ id, method, params }));
|
|
5663
5913
|
} catch (err) {
|
|
@@ -5852,8 +6102,8 @@ class DomSnapshotEngine {
|
|
|
5852
6102
|
}
|
|
5853
6103
|
}
|
|
5854
6104
|
// src/mcp/servers/chrome-devtools/controller.ts
|
|
5855
|
-
import { writeFileSync as writeFileSync5, mkdirSync as
|
|
5856
|
-
import { dirname as
|
|
6105
|
+
import { writeFileSync as writeFileSync5, mkdirSync as mkdirSync8 } from "fs";
|
|
6106
|
+
import { dirname as dirname7, resolve as resolve13 } from "path";
|
|
5857
6107
|
class ChromeDevToolsController {
|
|
5858
6108
|
launcher = new BrowserLauncher;
|
|
5859
6109
|
sessions = new Map;
|
|
@@ -5981,12 +6231,12 @@ class ChromeDevToolsController {
|
|
|
5981
6231
|
} else if (navType === "reload") {
|
|
5982
6232
|
await cdp.send("Page.reload", { ignoreCache: params.ignoreCache });
|
|
5983
6233
|
}
|
|
5984
|
-
await new Promise((
|
|
5985
|
-
const timer = setTimeout(
|
|
6234
|
+
await new Promise((resolve14) => {
|
|
6235
|
+
const timer = setTimeout(resolve14, params.timeout || 3000);
|
|
5986
6236
|
const unsub = cdp.on("Page.loadEventFired", () => {
|
|
5987
6237
|
clearTimeout(timer);
|
|
5988
6238
|
unsub();
|
|
5989
|
-
|
|
6239
|
+
resolve14();
|
|
5990
6240
|
});
|
|
5991
6241
|
});
|
|
5992
6242
|
const evalRes = await cdp.send("Runtime.evaluate", {
|
|
@@ -6002,8 +6252,8 @@ class ChromeDevToolsController {
|
|
|
6002
6252
|
const { cdp } = this.getSession(params.pageId);
|
|
6003
6253
|
const snapshot = await DomSnapshotEngine.captureSnapshot(cdp, params.verbose);
|
|
6004
6254
|
if (params.filePath) {
|
|
6005
|
-
const fullPath =
|
|
6006
|
-
|
|
6255
|
+
const fullPath = resolve13(params.filePath);
|
|
6256
|
+
mkdirSync8(dirname7(fullPath), { recursive: true });
|
|
6007
6257
|
writeFileSync5(fullPath, snapshot.textSnapshot, "utf8");
|
|
6008
6258
|
return `Snapshot saved to ${params.filePath} (${snapshot.elementsCount} indexed elements)`;
|
|
6009
6259
|
}
|
|
@@ -6031,8 +6281,8 @@ class ChromeDevToolsController {
|
|
|
6031
6281
|
});
|
|
6032
6282
|
const base64Data = res.data;
|
|
6033
6283
|
if (params.filePath) {
|
|
6034
|
-
const fullPath =
|
|
6035
|
-
|
|
6284
|
+
const fullPath = resolve13(params.filePath);
|
|
6285
|
+
mkdirSync8(dirname7(fullPath), { recursive: true });
|
|
6036
6286
|
writeFileSync5(fullPath, Buffer.from(base64Data, "base64"));
|
|
6037
6287
|
return { format, filePath: params.filePath };
|
|
6038
6288
|
}
|
|
@@ -6176,8 +6426,8 @@ class ChromeDevToolsController {
|
|
|
6176
6426
|
}
|
|
6177
6427
|
const value = res.result?.value;
|
|
6178
6428
|
if (params.filePath) {
|
|
6179
|
-
const fullPath =
|
|
6180
|
-
|
|
6429
|
+
const fullPath = resolve13(params.filePath);
|
|
6430
|
+
mkdirSync8(dirname7(fullPath), { recursive: true });
|
|
6181
6431
|
writeFileSync5(fullPath, JSON.stringify(value, null, 2), "utf8");
|
|
6182
6432
|
return `Script output saved to ${params.filePath}`;
|
|
6183
6433
|
}
|
|
@@ -6670,9 +6920,9 @@ if (false) {}
|
|
|
6670
6920
|
|
|
6671
6921
|
// src/mcp/servers/chrome-devtools/index.ts
|
|
6672
6922
|
var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/chrome-devtools";
|
|
6673
|
-
var CHROME_DEVTOOLS_MCP_SERVER_PATH =
|
|
6923
|
+
var CHROME_DEVTOOLS_MCP_SERVER_PATH = resolve14(__dirname, "server.ts");
|
|
6674
6924
|
// src/mcp/servers/web-search/index.ts
|
|
6675
|
-
import { resolve as
|
|
6925
|
+
import { resolve as resolve15 } from "path";
|
|
6676
6926
|
|
|
6677
6927
|
// src/mcp/servers/web-search/html-to-markdown.ts
|
|
6678
6928
|
class HtmlToMarkdownConverter {
|
|
@@ -7324,14 +7574,14 @@ if (false) {}
|
|
|
7324
7574
|
|
|
7325
7575
|
// src/mcp/servers/web-search/index.ts
|
|
7326
7576
|
var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/web-search";
|
|
7327
|
-
var WEB_SEARCH_MCP_SERVER_PATH =
|
|
7577
|
+
var WEB_SEARCH_MCP_SERVER_PATH = resolve15(__dirname, "server.ts");
|
|
7328
7578
|
// src/mcp/servers/sqlite/index.ts
|
|
7329
|
-
import { resolve as
|
|
7579
|
+
import { resolve as resolve17 } from "path";
|
|
7330
7580
|
|
|
7331
7581
|
// src/mcp/servers/sqlite/db-engine.ts
|
|
7332
7582
|
import { Database as Database2 } from "bun:sqlite";
|
|
7333
|
-
import { resolve as
|
|
7334
|
-
import { readdirSync as
|
|
7583
|
+
import { resolve as resolve16, isAbsolute } from "path";
|
|
7584
|
+
import { readdirSync as readdirSync5 } from "fs";
|
|
7335
7585
|
|
|
7336
7586
|
class SqliteEngine {
|
|
7337
7587
|
connections = new Map;
|
|
@@ -7353,17 +7603,17 @@ class SqliteEngine {
|
|
|
7353
7603
|
this.defaultDbPath = discovered;
|
|
7354
7604
|
return discovered;
|
|
7355
7605
|
}
|
|
7356
|
-
const fallback =
|
|
7606
|
+
const fallback = resolve16(process.cwd(), "dev.sqlite");
|
|
7357
7607
|
this.defaultDbPath = fallback;
|
|
7358
7608
|
return fallback;
|
|
7359
7609
|
}
|
|
7360
|
-
return isAbsolute(dbPath) ? dbPath :
|
|
7610
|
+
return isAbsolute(dbPath) ? dbPath : resolve16(process.cwd(), dbPath);
|
|
7361
7611
|
}
|
|
7362
7612
|
autoDiscoverDatabase() {
|
|
7363
7613
|
try {
|
|
7364
|
-
const files =
|
|
7614
|
+
const files = readdirSync5(process.cwd());
|
|
7365
7615
|
const dbFile = files.find((f) => f.endsWith(".sqlite") || f.endsWith(".sqlite3") || f.endsWith(".db"));
|
|
7366
|
-
return dbFile ?
|
|
7616
|
+
return dbFile ? resolve16(process.cwd(), dbFile) : null;
|
|
7367
7617
|
} catch {
|
|
7368
7618
|
return null;
|
|
7369
7619
|
}
|
|
@@ -7822,7 +8072,7 @@ if (false) {}
|
|
|
7822
8072
|
|
|
7823
8073
|
// src/mcp/servers/sqlite/index.ts
|
|
7824
8074
|
var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
|
|
7825
|
-
var SQLITE_MCP_SERVER_PATH =
|
|
8075
|
+
var SQLITE_MCP_SERVER_PATH = resolve17(__dirname, "server.ts");
|
|
7826
8076
|
// src/agents/identity.ts
|
|
7827
8077
|
import { generateKeyPairSync, sign, verify } from "crypto";
|
|
7828
8078
|
function createAgentIdentity(parentId, harnessId = "groupy-harness-v1") {
|
|
@@ -7865,8 +8115,8 @@ function verifyTaskAction(assertion, payload) {
|
|
|
7865
8115
|
}
|
|
7866
8116
|
}
|
|
7867
8117
|
// src/agents/roles.ts
|
|
7868
|
-
import { existsSync as
|
|
7869
|
-
import { resolve as
|
|
8118
|
+
import { existsSync as existsSync15, readdirSync as readdirSync6, readFileSync as readFileSync9 } from "fs";
|
|
8119
|
+
import { resolve as resolve18, join as join8 } from "path";
|
|
7870
8120
|
|
|
7871
8121
|
class AgentRoleRegistry {
|
|
7872
8122
|
roles = new Map;
|
|
@@ -7950,14 +8200,14 @@ class AgentRoleRegistry {
|
|
|
7950
8200
|
return cycle === 0 ? base : `${base}_${cycle + 1}`;
|
|
7951
8201
|
}
|
|
7952
8202
|
loadRolesFromDir(dirPath) {
|
|
7953
|
-
const fullPath =
|
|
7954
|
-
if (!
|
|
8203
|
+
const fullPath = resolve18(dirPath);
|
|
8204
|
+
if (!existsSync15(fullPath))
|
|
7955
8205
|
return;
|
|
7956
|
-
const entries =
|
|
8206
|
+
const entries = readdirSync6(fullPath);
|
|
7957
8207
|
for (const entry of entries) {
|
|
7958
8208
|
if (entry.endsWith(".json")) {
|
|
7959
8209
|
try {
|
|
7960
|
-
const content = readFileSync9(
|
|
8210
|
+
const content = readFileSync9(join8(fullPath, entry), "utf8");
|
|
7961
8211
|
const parsed = JSON.parse(content);
|
|
7962
8212
|
if (parsed.name && parsed.systemPrompt) {
|
|
7963
8213
|
this.registerRole(parsed);
|
|
@@ -7986,21 +8236,19 @@ class AgentRoleRegistry {
|
|
|
7986
8236
|
}
|
|
7987
8237
|
// src/agents/graph-store.ts
|
|
7988
8238
|
import { Database as Database3 } from "bun:sqlite";
|
|
7989
|
-
import { resolve as
|
|
7990
|
-
import { existsSync as
|
|
7991
|
-
import { homedir as homedir4 } from "os";
|
|
7992
|
-
|
|
8239
|
+
import { resolve as resolve19 } from "path";
|
|
8240
|
+
import { existsSync as existsSync16, mkdirSync as mkdirSync9 } from "fs";
|
|
7993
8241
|
class AgentGraphStore {
|
|
7994
8242
|
db;
|
|
7995
8243
|
constructor(dbPathOrDb) {
|
|
7996
8244
|
if (dbPathOrDb instanceof Database3) {
|
|
7997
8245
|
this.db = dbPathOrDb;
|
|
7998
8246
|
} else {
|
|
7999
|
-
const dbPath = dbPathOrDb ||
|
|
8247
|
+
const dbPath = dbPathOrDb || getAgentGraphDbPath();
|
|
8000
8248
|
if (dbPath !== ":memory:") {
|
|
8001
|
-
const dir =
|
|
8002
|
-
if (!
|
|
8003
|
-
|
|
8249
|
+
const dir = resolve19(dbPath, "..");
|
|
8250
|
+
if (!existsSync16(dir)) {
|
|
8251
|
+
mkdirSync9(dir, { recursive: true });
|
|
8004
8252
|
}
|
|
8005
8253
|
}
|
|
8006
8254
|
this.db = new Database3(dbPath);
|
|
@@ -8122,8 +8370,8 @@ Your nickname is ${nickname}. Your assigned task is: '${params.taskName}'. Focus
|
|
|
8122
8370
|
});
|
|
8123
8371
|
let resolvePromise;
|
|
8124
8372
|
let rejectPromise;
|
|
8125
|
-
const taskPromise = new Promise((
|
|
8126
|
-
resolvePromise =
|
|
8373
|
+
const taskPromise = new Promise((resolve20, reject) => {
|
|
8374
|
+
resolvePromise = resolve20;
|
|
8127
8375
|
rejectPromise = reject;
|
|
8128
8376
|
});
|
|
8129
8377
|
const handle = {
|
|
@@ -8422,17 +8670,16 @@ function registerMultiAgentTools(router2, spawner) {
|
|
|
8422
8670
|
}
|
|
8423
8671
|
// src/storage/sqlite-store.ts
|
|
8424
8672
|
import { Database as Database4 } from "bun:sqlite";
|
|
8425
|
-
import { existsSync as
|
|
8426
|
-
import { dirname as
|
|
8427
|
-
import { homedir as homedir5 } from "os";
|
|
8673
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync10 } from "fs";
|
|
8674
|
+
import { dirname as dirname8 } from "path";
|
|
8428
8675
|
class SqliteThreadStore {
|
|
8429
8676
|
db;
|
|
8430
8677
|
constructor(dbPath) {
|
|
8431
8678
|
const effectivePath = dbPath || this.getDefaultDbPath();
|
|
8432
8679
|
if (effectivePath !== ":memory:") {
|
|
8433
|
-
const dir =
|
|
8434
|
-
if (!
|
|
8435
|
-
|
|
8680
|
+
const dir = dirname8(effectivePath);
|
|
8681
|
+
if (!existsSync17(dir)) {
|
|
8682
|
+
mkdirSync10(dir, { recursive: true });
|
|
8436
8683
|
}
|
|
8437
8684
|
}
|
|
8438
8685
|
this.db = new Database4(effectivePath);
|
|
@@ -8441,7 +8688,7 @@ class SqliteThreadStore {
|
|
|
8441
8688
|
this.initSchema();
|
|
8442
8689
|
}
|
|
8443
8690
|
getDefaultDbPath() {
|
|
8444
|
-
return
|
|
8691
|
+
return getThreadsDbPath();
|
|
8445
8692
|
}
|
|
8446
8693
|
initSchema() {
|
|
8447
8694
|
this.db.exec(`
|
|
@@ -8664,9 +8911,9 @@ class SessionPersistenceManager {
|
|
|
8664
8911
|
}
|
|
8665
8912
|
}
|
|
8666
8913
|
// src/skills/loader.ts
|
|
8667
|
-
import { existsSync as
|
|
8668
|
-
import { resolve as resolve20, join as
|
|
8669
|
-
import { homedir as
|
|
8914
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync10 } from "fs";
|
|
8915
|
+
import { resolve as resolve20, join as join9 } from "path";
|
|
8916
|
+
import { homedir as homedir3 } from "os";
|
|
8670
8917
|
var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
|
|
8671
8918
|
|
|
8672
8919
|
class SkillsLoader {
|
|
@@ -8736,16 +8983,16 @@ class SkillsLoader {
|
|
|
8736
8983
|
resolve20(cwd, "skills")
|
|
8737
8984
|
];
|
|
8738
8985
|
for (const cand of candidates) {
|
|
8739
|
-
if (
|
|
8986
|
+
if (existsSync18(cand) && !roots.includes(cand)) {
|
|
8740
8987
|
roots.push(cand);
|
|
8741
8988
|
}
|
|
8742
8989
|
}
|
|
8743
8990
|
}
|
|
8744
8991
|
if (this.includeGlobal) {
|
|
8745
|
-
roots.push(
|
|
8992
|
+
roots.push(getGlobalSkillsDir(), resolve20(homedir3(), ".gemini", "config", "skills"));
|
|
8746
8993
|
}
|
|
8747
8994
|
roots.push(...this.customRoots.map((r) => resolve20(r)));
|
|
8748
|
-
return roots.filter((r) =>
|
|
8995
|
+
return roots.filter((r) => existsSync18(r));
|
|
8749
8996
|
}
|
|
8750
8997
|
discoverSkills(cwd, options) {
|
|
8751
8998
|
return this.listSkills(cwd, options);
|
|
@@ -8761,12 +9008,12 @@ class SkillsLoader {
|
|
|
8761
9008
|
const discovered = new Map;
|
|
8762
9009
|
for (const root of roots) {
|
|
8763
9010
|
try {
|
|
8764
|
-
const entries =
|
|
9011
|
+
const entries = readdirSync7(root, { withFileTypes: true });
|
|
8765
9012
|
for (const entry of entries) {
|
|
8766
9013
|
if (entry.isDirectory()) {
|
|
8767
|
-
const skillDir =
|
|
8768
|
-
const skillFilePath =
|
|
8769
|
-
if (
|
|
9014
|
+
const skillDir = join9(root, entry.name);
|
|
9015
|
+
const skillFilePath = join9(skillDir, "SKILL.md");
|
|
9016
|
+
if (existsSync18(skillFilePath)) {
|
|
8770
9017
|
const meta = this.parseSkillFrontmatter(skillFilePath, entry.name, root, cwd);
|
|
8771
9018
|
if (meta && !discovered.has(meta.name)) {
|
|
8772
9019
|
meta.enabled = !this.isSkillDisabled(meta.name);
|
|
@@ -8888,143 +9135,282 @@ When tackling complex specialized tasks that match any of these skills, autonomo
|
|
|
8888
9135
|
}
|
|
8889
9136
|
}
|
|
8890
9137
|
// src/memories/store.ts
|
|
8891
|
-
import { existsSync as
|
|
8892
|
-
import { resolve as resolve21, dirname as
|
|
8893
|
-
import {
|
|
8894
|
-
|
|
9138
|
+
import { existsSync as existsSync19, readFileSync as readFileSync11, writeFileSync as writeFileSync6, mkdirSync as mkdirSync11, readdirSync as readdirSync8 } from "fs";
|
|
9139
|
+
import { resolve as resolve21, join as join10, basename, dirname as dirname9 } from "path";
|
|
9140
|
+
import { createHash } from "crypto";
|
|
8895
9141
|
class MemoryStore {
|
|
8896
9142
|
globalPath;
|
|
8897
9143
|
customWorkspacePath;
|
|
8898
9144
|
constructor(options = {}) {
|
|
8899
|
-
this.globalPath = options.globalPath ||
|
|
9145
|
+
this.globalPath = options.globalPath || getGlobalMemoriesPath();
|
|
8900
9146
|
this.customWorkspacePath = options.workspacePath;
|
|
8901
9147
|
}
|
|
8902
|
-
|
|
8903
|
-
|
|
9148
|
+
findProjectRoot(cwd) {
|
|
9149
|
+
let current = resolve21(cwd);
|
|
9150
|
+
while (true) {
|
|
9151
|
+
if (existsSync19(join10(current, ".git"))) {
|
|
9152
|
+
return current;
|
|
9153
|
+
}
|
|
9154
|
+
const parent = dirname9(current);
|
|
9155
|
+
if (parent === current) {
|
|
9156
|
+
return resolve21(cwd);
|
|
9157
|
+
}
|
|
9158
|
+
current = parent;
|
|
9159
|
+
}
|
|
8904
9160
|
}
|
|
8905
|
-
|
|
8906
|
-
const
|
|
8907
|
-
const
|
|
8908
|
-
const
|
|
8909
|
-
|
|
8910
|
-
mkdirSync10(dir, { recursive: true });
|
|
8911
|
-
}
|
|
8912
|
-
const existingEntries = this.readMemoryFile(targetFile, scope);
|
|
8913
|
-
const normalized = params.content.trim();
|
|
8914
|
-
const duplicate = existingEntries.find((e) => e.category === params.category && e.content.toLowerCase() === normalized.toLowerCase());
|
|
8915
|
-
if (duplicate) {
|
|
8916
|
-
return duplicate;
|
|
8917
|
-
}
|
|
8918
|
-
const newEntry = {
|
|
8919
|
-
id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
8920
|
-
category: params.category,
|
|
8921
|
-
content: normalized,
|
|
8922
|
-
scope,
|
|
8923
|
-
createdAt: Date.now()
|
|
8924
|
-
};
|
|
8925
|
-
existingEntries.push(newEntry);
|
|
8926
|
-
this.writeMemoryFile(targetFile, existingEntries);
|
|
8927
|
-
return newEntry;
|
|
9161
|
+
getProjectSlug(cwd) {
|
|
9162
|
+
const root = this.findProjectRoot(cwd);
|
|
9163
|
+
const folderName = basename(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
|
|
9164
|
+
const hash = createHash("sha256").update(resolve21(root)).digest("hex").slice(0, 6);
|
|
9165
|
+
return `${folderName}-${hash}`;
|
|
8928
9166
|
}
|
|
8929
|
-
|
|
8930
|
-
|
|
8931
|
-
|
|
8932
|
-
|
|
8933
|
-
|
|
9167
|
+
getProjectMemoryDir(cwd) {
|
|
9168
|
+
if (this.customWorkspacePath) {
|
|
9169
|
+
const dir2 = resolve21(this.customWorkspacePath);
|
|
9170
|
+
if (!existsSync19(dir2)) {
|
|
9171
|
+
try {
|
|
9172
|
+
mkdirSync11(dir2, { recursive: true });
|
|
9173
|
+
} catch {}
|
|
9174
|
+
}
|
|
9175
|
+
return dir2;
|
|
9176
|
+
}
|
|
9177
|
+
const slug = this.getProjectSlug(cwd);
|
|
9178
|
+
const dir = join10(getProjectsDir(), slug, "memory");
|
|
9179
|
+
if (!existsSync19(dir)) {
|
|
9180
|
+
try {
|
|
9181
|
+
mkdirSync11(dir, { recursive: true });
|
|
9182
|
+
} catch {}
|
|
9183
|
+
}
|
|
9184
|
+
return dir;
|
|
9185
|
+
}
|
|
9186
|
+
getMemoryIndexPath(cwd) {
|
|
9187
|
+
return join10(this.getProjectMemoryDir(cwd), "MEMORY.md");
|
|
9188
|
+
}
|
|
9189
|
+
normalizeCategory(raw) {
|
|
9190
|
+
const cat = raw.toLowerCase().trim();
|
|
9191
|
+
if (cat === "user" || cat === "preference")
|
|
9192
|
+
return "user";
|
|
9193
|
+
if (cat === "feedback" || cat === "guideline")
|
|
9194
|
+
return "feedback";
|
|
9195
|
+
if (cat === "project" || cat === "architecture")
|
|
9196
|
+
return "project";
|
|
9197
|
+
if (cat === "reference" || cat === "note")
|
|
9198
|
+
return "reference";
|
|
9199
|
+
return "project";
|
|
9200
|
+
}
|
|
9201
|
+
saveTopicMemory(params) {
|
|
9202
|
+
const type = this.normalizeCategory(params.category);
|
|
9203
|
+
const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
|
|
9204
|
+
const memoryDir = this.getProjectMemoryDir(params.cwd);
|
|
9205
|
+
const fileName = `${type}_${sanitizedName}.md`;
|
|
9206
|
+
const filePath = join10(memoryDir, fileName);
|
|
9207
|
+
const nowIso = new Date().toISOString();
|
|
9208
|
+
const cleanContent = params.content.trim();
|
|
9209
|
+
const desc = (params.description || cleanContent.split(`
|
|
9210
|
+
`)[0] || sanitizedName).replace(/[\r\n]+/g, " ");
|
|
9211
|
+
const frontmatter = [
|
|
9212
|
+
"---",
|
|
9213
|
+
`type: ${type}`,
|
|
9214
|
+
`name: ${sanitizedName}`,
|
|
9215
|
+
`description: ${desc}`,
|
|
9216
|
+
`modified: ${nowIso}`,
|
|
9217
|
+
"---",
|
|
9218
|
+
"",
|
|
9219
|
+
`# ${sanitizedName.replace(/_/g, " ").toUpperCase()}`,
|
|
9220
|
+
"",
|
|
9221
|
+
cleanContent,
|
|
9222
|
+
""
|
|
9223
|
+
].join(`
|
|
9224
|
+
`);
|
|
9225
|
+
writeFileSync6(filePath, frontmatter, "utf8");
|
|
9226
|
+
this.syncMemoryIndex(params.cwd);
|
|
9227
|
+
return {
|
|
9228
|
+
id: `mem_${sanitizedName}`,
|
|
9229
|
+
category: type,
|
|
9230
|
+
name: sanitizedName,
|
|
9231
|
+
description: desc,
|
|
9232
|
+
content: cleanContent,
|
|
9233
|
+
scope: "project",
|
|
9234
|
+
createdAt: Date.now(),
|
|
9235
|
+
modifiedAt: Date.now(),
|
|
9236
|
+
filePath
|
|
9237
|
+
};
|
|
8934
9238
|
}
|
|
8935
|
-
|
|
8936
|
-
|
|
8937
|
-
|
|
9239
|
+
readTopicMemory(topicNameOrFile, cwd) {
|
|
9240
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
9241
|
+
let targetPath = join10(memoryDir, topicNameOrFile);
|
|
9242
|
+
if (!existsSync19(targetPath)) {
|
|
9243
|
+
if (!topicNameOrFile.endsWith(".md")) {
|
|
9244
|
+
targetPath = join10(memoryDir, `${topicNameOrFile}.md`);
|
|
9245
|
+
}
|
|
9246
|
+
}
|
|
9247
|
+
if (!existsSync19(targetPath)) {
|
|
9248
|
+
const files = readdirSync8(memoryDir);
|
|
9249
|
+
const match = files.find((f) => f.includes(topicNameOrFile));
|
|
9250
|
+
if (match) {
|
|
9251
|
+
targetPath = join10(memoryDir, match);
|
|
9252
|
+
} else {
|
|
9253
|
+
return null;
|
|
9254
|
+
}
|
|
9255
|
+
}
|
|
8938
9256
|
try {
|
|
8939
|
-
const
|
|
8940
|
-
|
|
9257
|
+
const raw = readFileSync11(targetPath, "utf8");
|
|
9258
|
+
return this.parseTopicFile(raw, targetPath);
|
|
9259
|
+
} catch {
|
|
9260
|
+
return null;
|
|
9261
|
+
}
|
|
9262
|
+
}
|
|
9263
|
+
parseTopicFile(raw, filePath) {
|
|
9264
|
+
const lines = raw.split(`
|
|
8941
9265
|
`);
|
|
8942
|
-
|
|
8943
|
-
|
|
8944
|
-
|
|
8945
|
-
|
|
8946
|
-
|
|
8947
|
-
|
|
8948
|
-
|
|
8949
|
-
|
|
8950
|
-
|
|
8951
|
-
|
|
8952
|
-
|
|
8953
|
-
|
|
8954
|
-
|
|
8955
|
-
|
|
8956
|
-
|
|
8957
|
-
|
|
8958
|
-
|
|
8959
|
-
|
|
8960
|
-
|
|
8961
|
-
|
|
8962
|
-
|
|
8963
|
-
|
|
8964
|
-
|
|
9266
|
+
let inFm = false;
|
|
9267
|
+
let type = "project";
|
|
9268
|
+
let name = basename(filePath, ".md");
|
|
9269
|
+
let description;
|
|
9270
|
+
let modified = new Date().toISOString();
|
|
9271
|
+
const bodyLines = [];
|
|
9272
|
+
for (let i = 0;i < lines.length; i++) {
|
|
9273
|
+
const line = lines[i];
|
|
9274
|
+
if (i === 0 && line.trim() === "---") {
|
|
9275
|
+
inFm = true;
|
|
9276
|
+
continue;
|
|
9277
|
+
}
|
|
9278
|
+
if (inFm) {
|
|
9279
|
+
if (line.trim() === "---") {
|
|
9280
|
+
inFm = false;
|
|
9281
|
+
continue;
|
|
9282
|
+
}
|
|
9283
|
+
const colonIdx = line.indexOf(":");
|
|
9284
|
+
if (colonIdx !== -1) {
|
|
9285
|
+
const key = line.slice(0, colonIdx).trim();
|
|
9286
|
+
const val = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
9287
|
+
if (key === "type")
|
|
9288
|
+
type = this.normalizeCategory(val);
|
|
9289
|
+
else if (key === "name")
|
|
9290
|
+
name = val;
|
|
9291
|
+
else if (key === "description")
|
|
9292
|
+
description = val;
|
|
9293
|
+
else if (key === "modified")
|
|
9294
|
+
modified = val;
|
|
8965
9295
|
}
|
|
9296
|
+
} else {
|
|
9297
|
+
bodyLines.push(line);
|
|
8966
9298
|
}
|
|
8967
|
-
return entries;
|
|
8968
|
-
} catch {
|
|
8969
|
-
return [];
|
|
8970
9299
|
}
|
|
8971
|
-
|
|
8972
|
-
|
|
8973
|
-
|
|
8974
|
-
|
|
8975
|
-
|
|
8976
|
-
|
|
8977
|
-
|
|
9300
|
+
return {
|
|
9301
|
+
type,
|
|
9302
|
+
name,
|
|
9303
|
+
description,
|
|
9304
|
+
modified,
|
|
9305
|
+
content: bodyLines.join(`
|
|
9306
|
+
`).trim(),
|
|
9307
|
+
filePath
|
|
8978
9308
|
};
|
|
8979
|
-
|
|
8980
|
-
|
|
9309
|
+
}
|
|
9310
|
+
syncMemoryIndex(cwd) {
|
|
9311
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
9312
|
+
const indexPath = join10(memoryDir, "MEMORY.md");
|
|
9313
|
+
const files = existsSync19(memoryDir) ? readdirSync8(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
|
|
9314
|
+
const items2 = [];
|
|
9315
|
+
for (const f of files) {
|
|
9316
|
+
try {
|
|
9317
|
+
const full = join10(memoryDir, f);
|
|
9318
|
+
const parsed = this.parseTopicFile(readFileSync11(full, "utf8"), full);
|
|
9319
|
+
items2.push({
|
|
9320
|
+
type: parsed.type,
|
|
9321
|
+
name: parsed.name,
|
|
9322
|
+
desc: parsed.description || parsed.content.split(`
|
|
9323
|
+
`)[0] || parsed.name,
|
|
9324
|
+
file: f
|
|
9325
|
+
});
|
|
9326
|
+
} catch {}
|
|
8981
9327
|
}
|
|
8982
|
-
|
|
8983
|
-
|
|
8984
|
-
|
|
8985
|
-
|
|
8986
|
-
|
|
8987
|
-
|
|
8988
|
-
|
|
8989
|
-
|
|
8990
|
-
`;
|
|
9328
|
+
const indexLines = [
|
|
9329
|
+
"# Project Auto-Memory Index",
|
|
9330
|
+
"",
|
|
9331
|
+
"This index is loaded at session startup. Detailed topics can be retrieved via read_memory tool.",
|
|
9332
|
+
""
|
|
9333
|
+
];
|
|
9334
|
+
for (const item of items2) {
|
|
9335
|
+
indexLines.push(`- [${item.type}] **${item.name}**: ${item.desc} (topic: ${item.file})`);
|
|
8991
9336
|
}
|
|
8992
|
-
|
|
8993
|
-
|
|
8994
|
-
|
|
8995
|
-
|
|
8996
|
-
|
|
8997
|
-
|
|
9337
|
+
const boundedLines = indexLines.slice(0, 200);
|
|
9338
|
+
writeFileSync6(indexPath, boundedLines.join(`
|
|
9339
|
+
`) + `
|
|
9340
|
+
`, "utf8");
|
|
9341
|
+
}
|
|
9342
|
+
loadMemoryIndex(cwd) {
|
|
9343
|
+
const indexPath = this.getMemoryIndexPath(cwd);
|
|
9344
|
+
if (!existsSync19(indexPath))
|
|
9345
|
+
return "";
|
|
9346
|
+
try {
|
|
9347
|
+
const raw = readFileSync11(indexPath, "utf8");
|
|
9348
|
+
const byteLimit = 25 * 1024;
|
|
9349
|
+
const sliced = raw.length > byteLimit ? raw.slice(0, byteLimit) : raw;
|
|
9350
|
+
const lines = sliced.split(`
|
|
9351
|
+
`).slice(0, 200);
|
|
9352
|
+
return lines.join(`
|
|
9353
|
+
`).trim();
|
|
9354
|
+
} catch {
|
|
9355
|
+
return "";
|
|
8998
9356
|
}
|
|
8999
|
-
|
|
9000
|
-
|
|
9001
|
-
|
|
9002
|
-
|
|
9003
|
-
|
|
9004
|
-
|
|
9357
|
+
}
|
|
9358
|
+
listProjectMemories(cwd) {
|
|
9359
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
9360
|
+
if (!existsSync19(memoryDir))
|
|
9361
|
+
return [];
|
|
9362
|
+
const files = readdirSync8(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
|
|
9363
|
+
const list = [];
|
|
9364
|
+
for (const f of files) {
|
|
9365
|
+
try {
|
|
9366
|
+
const full = join10(memoryDir, f);
|
|
9367
|
+
list.push(this.parseTopicFile(readFileSync11(full, "utf8"), full));
|
|
9368
|
+
} catch {}
|
|
9005
9369
|
}
|
|
9006
|
-
|
|
9007
|
-
|
|
9008
|
-
|
|
9009
|
-
|
|
9010
|
-
|
|
9011
|
-
`;
|
|
9370
|
+
return list;
|
|
9371
|
+
}
|
|
9372
|
+
addMemory(params) {
|
|
9373
|
+
const cwd = params.cwd || process.cwd();
|
|
9374
|
+
const type = this.normalizeCategory(params.category);
|
|
9375
|
+
const name = params.name || `${type}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
9376
|
+
const entry = this.saveTopicMemory({
|
|
9377
|
+
category: type,
|
|
9378
|
+
name,
|
|
9379
|
+
content: params.content,
|
|
9380
|
+
cwd
|
|
9381
|
+
});
|
|
9382
|
+
if (params.scope) {
|
|
9383
|
+
entry.scope = params.scope;
|
|
9012
9384
|
}
|
|
9013
|
-
|
|
9014
|
-
|
|
9385
|
+
return entry;
|
|
9386
|
+
}
|
|
9387
|
+
getAllMemories(cwd) {
|
|
9388
|
+
const projectTopics = this.listProjectMemories(cwd).map((t) => ({
|
|
9389
|
+
id: `mem_${t.name}`,
|
|
9390
|
+
category: t.type,
|
|
9391
|
+
name: t.name,
|
|
9392
|
+
description: t.description,
|
|
9393
|
+
content: t.content,
|
|
9394
|
+
scope: t.type === "user" ? "global" : "workspace",
|
|
9395
|
+
createdAt: new Date(t.modified).getTime() || Date.now(),
|
|
9396
|
+
filePath: t.filePath
|
|
9397
|
+
}));
|
|
9398
|
+
return projectTopics;
|
|
9015
9399
|
}
|
|
9016
9400
|
formatMemoriesPrompt(cwd) {
|
|
9017
|
-
const
|
|
9018
|
-
if (
|
|
9401
|
+
const indexContent = this.loadMemoryIndex(cwd);
|
|
9402
|
+
if (!indexContent)
|
|
9019
9403
|
return "";
|
|
9020
|
-
|
|
9021
|
-
|
|
9022
|
-
##
|
|
9023
|
-
<
|
|
9024
|
-
|
|
9025
|
-
|
|
9026
|
-
|
|
9027
|
-
|
|
9404
|
+
return [
|
|
9405
|
+
"",
|
|
9406
|
+
"## Project Auto-Memory (Persistent Learnings)",
|
|
9407
|
+
"<auto_memory>",
|
|
9408
|
+
indexContent,
|
|
9409
|
+
"</auto_memory>",
|
|
9410
|
+
"Apply these persistent project learnings, user preferences, and feedback across all tasks.",
|
|
9411
|
+
"If more context is needed for a specific topic, retrieve it using the `read_memory` tool."
|
|
9412
|
+
].join(`
|
|
9413
|
+
`);
|
|
9028
9414
|
}
|
|
9029
9415
|
}
|
|
9030
9416
|
// src/worktree/git.ts
|
|
@@ -9155,8 +9541,8 @@ async function removeWorktreeGit(repoRoot, worktreePath, deleteBranch = false) {
|
|
|
9155
9541
|
return { success: true };
|
|
9156
9542
|
}
|
|
9157
9543
|
// src/worktree/manager.ts
|
|
9158
|
-
import { resolve as resolve23, join as
|
|
9159
|
-
import { existsSync as
|
|
9544
|
+
import { resolve as resolve23, join as join11 } from "path";
|
|
9545
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync12, writeFileSync as writeFileSync7, readFileSync as readFileSync12 } from "fs";
|
|
9160
9546
|
var DEFAULT_WORKTREE_KEEP_COUNT = 15;
|
|
9161
9547
|
|
|
9162
9548
|
class WorktreeManager {
|
|
@@ -9180,15 +9566,15 @@ class WorktreeManager {
|
|
|
9180
9566
|
const branchName = options.branch || `groupy/${taskId}`;
|
|
9181
9567
|
const targetDir = options.worktreePath || (this.baseStorageDir ? resolve23(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve23(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
|
|
9182
9568
|
const worktreeParent = resolve23(targetDir, "..");
|
|
9183
|
-
if (!
|
|
9184
|
-
|
|
9569
|
+
if (!existsSync20(worktreeParent)) {
|
|
9570
|
+
mkdirSync12(worktreeParent, { recursive: true });
|
|
9185
9571
|
}
|
|
9186
9572
|
const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
|
|
9187
9573
|
const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
|
|
9188
9574
|
if (!result.success) {
|
|
9189
9575
|
throw new Error(`Failed to create git worktree: ${result.error}`);
|
|
9190
9576
|
}
|
|
9191
|
-
const metaPath =
|
|
9577
|
+
const metaPath = join11(targetDir, "groupy-thread.json");
|
|
9192
9578
|
try {
|
|
9193
9579
|
writeFileSync7(metaPath, JSON.stringify({
|
|
9194
9580
|
version: 1,
|
|
@@ -9214,8 +9600,8 @@ class WorktreeManager {
|
|
|
9214
9600
|
return [];
|
|
9215
9601
|
const worktrees = await listWorktreesGit(repoRoot);
|
|
9216
9602
|
return worktrees.map((wt) => {
|
|
9217
|
-
const metaPath =
|
|
9218
|
-
if (
|
|
9603
|
+
const metaPath = join11(wt.path, "groupy-thread.json");
|
|
9604
|
+
if (existsSync20(metaPath)) {
|
|
9219
9605
|
try {
|
|
9220
9606
|
const raw = JSON.parse(readFileSync12(metaPath, "utf8"));
|
|
9221
9607
|
return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
|
|
@@ -9293,7 +9679,7 @@ class WorktreeManager {
|
|
|
9293
9679
|
}
|
|
9294
9680
|
}
|
|
9295
9681
|
// src/auth/oauth.ts
|
|
9296
|
-
import { randomBytes, createHash } from "crypto";
|
|
9682
|
+
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
9297
9683
|
import { exec } from "child_process";
|
|
9298
9684
|
class AuthClient {
|
|
9299
9685
|
store;
|
|
@@ -9446,7 +9832,7 @@ class AuthClient {
|
|
|
9446
9832
|
return randomBytes(32).toString("base64url").replace(/[^a-zA-Z0-9]/g, "").slice(0, 64);
|
|
9447
9833
|
}
|
|
9448
9834
|
generateCodeChallenge(verifier) {
|
|
9449
|
-
return
|
|
9835
|
+
return createHash2("sha256").update(verifier).digest("base64url");
|
|
9450
9836
|
}
|
|
9451
9837
|
}
|
|
9452
9838
|
// src/ui/components/claude/claude-header.tsx
|
|
@@ -10996,11 +11382,15 @@ export {
|
|
|
10996
11382
|
captureWorldState,
|
|
10997
11383
|
compactHistory,
|
|
10998
11384
|
createAgentIdentity,
|
|
11385
|
+
createAutoMemoryTools,
|
|
10999
11386
|
createCodeModeTools,
|
|
11000
11387
|
createDefaultTools,
|
|
11001
11388
|
createFileSearchTools,
|
|
11389
|
+
createListMemoriesTool,
|
|
11002
11390
|
createMultiAgentTools,
|
|
11391
|
+
createReadMemoryTool,
|
|
11003
11392
|
createRememberTool,
|
|
11393
|
+
createSaveMemoryTool,
|
|
11004
11394
|
createShellTool,
|
|
11005
11395
|
createSkillTool,
|
|
11006
11396
|
createWorktreeGit,
|