@pikaa-ai/pikaa 0.3.14 → 0.3.16
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 +801 -387
- package/dist/index.js +698 -319
- package/package.json +1 -1
- package/templates/base/groupy_prompt.md +14 -0
package/dist/cli.js
CHANGED
|
@@ -3,21 +3,143 @@
|
|
|
3
3
|
|
|
4
4
|
// src/cli/index.ts
|
|
5
5
|
import { resolve as resolve21 } from "path";
|
|
6
|
-
import { existsSync as
|
|
6
|
+
import { existsSync as existsSync22 } from "fs";
|
|
7
7
|
import { createInterface } from "readline";
|
|
8
8
|
|
|
9
9
|
// src/auth/store.ts
|
|
10
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync, unlinkSync } from "fs";
|
|
11
|
-
import { resolve } from "path";
|
|
10
|
+
import { existsSync as existsSync2, readFileSync, writeFileSync, mkdirSync as mkdirSync2, unlinkSync } from "fs";
|
|
11
|
+
import { resolve as resolve2 } from "path";
|
|
12
|
+
|
|
13
|
+
// src/config/paths.ts
|
|
14
|
+
import { existsSync, mkdirSync, copyFileSync, readdirSync, statSync } from "fs";
|
|
15
|
+
import { resolve, join } from "path";
|
|
12
16
|
import { homedir } from "os";
|
|
17
|
+
function getPikaaHomeDir() {
|
|
18
|
+
const envDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME;
|
|
19
|
+
if (envDir) {
|
|
20
|
+
return resolve(envDir);
|
|
21
|
+
}
|
|
22
|
+
return resolve(homedir(), ".pikaa");
|
|
23
|
+
}
|
|
24
|
+
function getLegacyGroupyHomeDir() {
|
|
25
|
+
if (process.env.GROUPY_HOME) {
|
|
26
|
+
return resolve(process.env.GROUPY_HOME);
|
|
27
|
+
}
|
|
28
|
+
return resolve(homedir(), ".groupy");
|
|
29
|
+
}
|
|
30
|
+
var hasMigrated = false;
|
|
31
|
+
function copyDirRecursiveSync(src, dest) {
|
|
32
|
+
if (!existsSync(src))
|
|
33
|
+
return;
|
|
34
|
+
if (!existsSync(dest)) {
|
|
35
|
+
mkdirSync(dest, { recursive: true });
|
|
36
|
+
}
|
|
37
|
+
const entries = readdirSync(src);
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const srcPath = join(src, entry);
|
|
40
|
+
const destPath = join(dest, entry);
|
|
41
|
+
const stat = statSync(srcPath);
|
|
42
|
+
if (stat.isDirectory()) {
|
|
43
|
+
copyDirRecursiveSync(srcPath, destPath);
|
|
44
|
+
} else if (!existsSync(destPath)) {
|
|
45
|
+
try {
|
|
46
|
+
copyFileSync(srcPath, destPath);
|
|
47
|
+
} catch {}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function ensurePikaaHomeMigrated(force = false) {
|
|
52
|
+
const pikaaHome = getPikaaHomeDir();
|
|
53
|
+
const legacyHome = getLegacyGroupyHomeDir();
|
|
54
|
+
try {
|
|
55
|
+
if (!existsSync(pikaaHome)) {
|
|
56
|
+
mkdirSync(pikaaHome, { recursive: true });
|
|
57
|
+
}
|
|
58
|
+
} catch {}
|
|
59
|
+
if (hasMigrated && !force) {
|
|
60
|
+
return pikaaHome;
|
|
61
|
+
}
|
|
62
|
+
hasMigrated = true;
|
|
63
|
+
try {
|
|
64
|
+
if (existsSync(legacyHome) && legacyHome !== pikaaHome) {
|
|
65
|
+
const legacyCreds = join(legacyHome, "credentials.json");
|
|
66
|
+
const pikaaCreds = join(pikaaHome, "credentials.json");
|
|
67
|
+
if (existsSync(legacyCreds) && !existsSync(pikaaCreds)) {
|
|
68
|
+
copyFileSync(legacyCreds, pikaaCreds);
|
|
69
|
+
}
|
|
70
|
+
const legacyThreads = join(legacyHome, "groupy_threads.db");
|
|
71
|
+
const pikaaThreads = join(pikaaHome, "pikaa_threads.db");
|
|
72
|
+
if (existsSync(legacyThreads) && !existsSync(pikaaThreads)) {
|
|
73
|
+
copyFileSync(legacyThreads, pikaaThreads);
|
|
74
|
+
}
|
|
75
|
+
const legacyRules = join(legacyHome, "groupy_rules.db");
|
|
76
|
+
const pikaaRules = join(pikaaHome, "pikaa_rules.db");
|
|
77
|
+
if (existsSync(legacyRules) && !existsSync(pikaaRules)) {
|
|
78
|
+
copyFileSync(legacyRules, pikaaRules);
|
|
79
|
+
}
|
|
80
|
+
const legacyGraph = join(legacyHome, "agent_graph.db");
|
|
81
|
+
const pikaaGraph = join(pikaaHome, "agent_graph.db");
|
|
82
|
+
if (existsSync(legacyGraph) && !existsSync(pikaaGraph)) {
|
|
83
|
+
copyFileSync(legacyGraph, pikaaGraph);
|
|
84
|
+
}
|
|
85
|
+
const legacyMemories = join(legacyHome, "memories.md");
|
|
86
|
+
const pikaaMemories = join(pikaaHome, "memories.md");
|
|
87
|
+
if (existsSync(legacyMemories) && !existsSync(pikaaMemories)) {
|
|
88
|
+
copyFileSync(legacyMemories, pikaaMemories);
|
|
89
|
+
}
|
|
90
|
+
copyDirRecursiveSync(join(legacyHome, "skills"), join(pikaaHome, "skills"));
|
|
91
|
+
copyDirRecursiveSync(join(legacyHome, "templates"), join(pikaaHome, "templates"));
|
|
92
|
+
}
|
|
93
|
+
} catch {}
|
|
94
|
+
return pikaaHome;
|
|
95
|
+
}
|
|
96
|
+
function getCredentialsPath() {
|
|
97
|
+
ensurePikaaHomeMigrated();
|
|
98
|
+
return join(getPikaaHomeDir(), "credentials.json");
|
|
99
|
+
}
|
|
100
|
+
function getThreadsDbPath() {
|
|
101
|
+
ensurePikaaHomeMigrated();
|
|
102
|
+
return join(getPikaaHomeDir(), "pikaa_threads.db");
|
|
103
|
+
}
|
|
104
|
+
function getPrefixRulesDbPath() {
|
|
105
|
+
ensurePikaaHomeMigrated();
|
|
106
|
+
return join(getPikaaHomeDir(), "pikaa_rules.db");
|
|
107
|
+
}
|
|
108
|
+
function getAgentGraphDbPath() {
|
|
109
|
+
ensurePikaaHomeMigrated();
|
|
110
|
+
return join(getPikaaHomeDir(), "agent_graph.db");
|
|
111
|
+
}
|
|
112
|
+
function getGlobalSkillsDir() {
|
|
113
|
+
ensurePikaaHomeMigrated();
|
|
114
|
+
return join(getPikaaHomeDir(), "skills");
|
|
115
|
+
}
|
|
116
|
+
function getGlobalTemplatesDir() {
|
|
117
|
+
ensurePikaaHomeMigrated();
|
|
118
|
+
return join(getPikaaHomeDir(), "templates");
|
|
119
|
+
}
|
|
120
|
+
function getGlobalMemoriesPath() {
|
|
121
|
+
ensurePikaaHomeMigrated();
|
|
122
|
+
return join(getPikaaHomeDir(), "memories.md");
|
|
123
|
+
}
|
|
124
|
+
function getProjectsDir() {
|
|
125
|
+
ensurePikaaHomeMigrated();
|
|
126
|
+
const dir = join(getPikaaHomeDir(), "projects");
|
|
127
|
+
if (!existsSync(dir)) {
|
|
128
|
+
try {
|
|
129
|
+
mkdirSync(dir, { recursive: true });
|
|
130
|
+
} catch {}
|
|
131
|
+
}
|
|
132
|
+
return dir;
|
|
133
|
+
}
|
|
13
134
|
|
|
135
|
+
// src/auth/store.ts
|
|
14
136
|
class CredentialsStore {
|
|
15
137
|
filePath;
|
|
16
138
|
constructor(customPath) {
|
|
17
|
-
this.filePath = customPath ||
|
|
139
|
+
this.filePath = customPath || getCredentialsPath();
|
|
18
140
|
}
|
|
19
141
|
load() {
|
|
20
|
-
if (!
|
|
142
|
+
if (!existsSync2(this.filePath))
|
|
21
143
|
return null;
|
|
22
144
|
try {
|
|
23
145
|
const raw = readFileSync(this.filePath, "utf8");
|
|
@@ -48,14 +170,14 @@ class CredentialsStore {
|
|
|
48
170
|
return creds?.user;
|
|
49
171
|
}
|
|
50
172
|
save(credentials) {
|
|
51
|
-
const dir =
|
|
52
|
-
if (!
|
|
53
|
-
|
|
173
|
+
const dir = resolve2(this.filePath, "..");
|
|
174
|
+
if (!existsSync2(dir)) {
|
|
175
|
+
mkdirSync2(dir, { recursive: true });
|
|
54
176
|
}
|
|
55
177
|
writeFileSync(this.filePath, JSON.stringify(credentials, null, 2), "utf8");
|
|
56
178
|
}
|
|
57
179
|
clear() {
|
|
58
|
-
if (
|
|
180
|
+
if (existsSync2(this.filePath)) {
|
|
59
181
|
try {
|
|
60
182
|
unlinkSync(this.filePath);
|
|
61
183
|
return true;
|
|
@@ -633,14 +755,13 @@ function formatWorldStatePrompt(state) {
|
|
|
633
755
|
}
|
|
634
756
|
|
|
635
757
|
// src/prompts/loader.ts
|
|
636
|
-
import { existsSync as
|
|
637
|
-
import { resolve as
|
|
758
|
+
import { existsSync as existsSync3, readFileSync as readFileSync2 } from "fs";
|
|
759
|
+
import { resolve as resolve3, join as join2 } from "path";
|
|
638
760
|
import { homedir as homedir2 } from "os";
|
|
639
|
-
|
|
640
761
|
class PromptTemplateLoader {
|
|
641
762
|
builtInTemplatesDir;
|
|
642
763
|
constructor(builtInDir) {
|
|
643
|
-
this.builtInTemplatesDir = builtInDir ||
|
|
764
|
+
this.builtInTemplatesDir = builtInDir || resolve3(join2(import.meta.dir, "..", "..", "templates"));
|
|
644
765
|
}
|
|
645
766
|
loadTemplate(relativePath, variables = {}, cwd) {
|
|
646
767
|
const rawContent = this.resolveTemplateContent(relativePath, cwd);
|
|
@@ -649,21 +770,27 @@ class PromptTemplateLoader {
|
|
|
649
770
|
resolveTemplateContent(relativePath, cwd) {
|
|
650
771
|
const normalizedRel = relativePath.replace(/^\/+/, "");
|
|
651
772
|
if (cwd) {
|
|
652
|
-
const workspacePath =
|
|
653
|
-
if (
|
|
773
|
+
const workspacePath = join2(cwd, ".agents", "templates", normalizedRel);
|
|
774
|
+
if (existsSync3(workspacePath)) {
|
|
654
775
|
try {
|
|
655
776
|
return readFileSync2(workspacePath, "utf-8");
|
|
656
777
|
} catch {}
|
|
657
778
|
}
|
|
658
779
|
}
|
|
659
|
-
const globalPath =
|
|
660
|
-
if (
|
|
780
|
+
const globalPath = join2(getGlobalTemplatesDir(), normalizedRel);
|
|
781
|
+
if (existsSync3(globalPath)) {
|
|
661
782
|
try {
|
|
662
783
|
return readFileSync2(globalPath, "utf-8");
|
|
663
784
|
} catch {}
|
|
664
785
|
}
|
|
665
|
-
const
|
|
666
|
-
if (
|
|
786
|
+
const legacyGlobalPath = join2(homedir2(), ".groupy", "templates", normalizedRel);
|
|
787
|
+
if (existsSync3(legacyGlobalPath)) {
|
|
788
|
+
try {
|
|
789
|
+
return readFileSync2(legacyGlobalPath, "utf-8");
|
|
790
|
+
} catch {}
|
|
791
|
+
}
|
|
792
|
+
const builtInPath = join2(this.builtInTemplatesDir, normalizedRel);
|
|
793
|
+
if (existsSync3(builtInPath)) {
|
|
667
794
|
try {
|
|
668
795
|
return readFileSync2(builtInPath, "utf-8");
|
|
669
796
|
} catch {}
|
|
@@ -680,8 +807,8 @@ class PromptTemplateLoader {
|
|
|
680
807
|
var globalPromptLoader = new PromptTemplateLoader;
|
|
681
808
|
|
|
682
809
|
// src/prompts/agents-md.ts
|
|
683
|
-
import { existsSync as
|
|
684
|
-
import { resolve as
|
|
810
|
+
import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
|
|
811
|
+
import { resolve as resolve4, join as join3, dirname as dirname2 } from "path";
|
|
685
812
|
var DEFAULT_AGENTS_MD_FILENAMES = [
|
|
686
813
|
"AGENTS.override.md",
|
|
687
814
|
"AGENTS.md",
|
|
@@ -696,28 +823,28 @@ var AGENTS_MD_SEPARATOR = `
|
|
|
696
823
|
|
|
697
824
|
class AgentsMdLoader {
|
|
698
825
|
findProjectRoot(startDir) {
|
|
699
|
-
let current =
|
|
826
|
+
let current = resolve4(startDir);
|
|
700
827
|
while (true) {
|
|
701
|
-
if (
|
|
828
|
+
if (existsSync4(join3(current, ".git"))) {
|
|
702
829
|
return current;
|
|
703
830
|
}
|
|
704
|
-
const parent =
|
|
831
|
+
const parent = dirname2(current);
|
|
705
832
|
if (parent === current) {
|
|
706
|
-
return
|
|
833
|
+
return resolve4(startDir);
|
|
707
834
|
}
|
|
708
835
|
current = parent;
|
|
709
836
|
}
|
|
710
837
|
}
|
|
711
838
|
collectDirectoryHierarchy(targetDir, rootDir) {
|
|
712
839
|
const hierarchy = [];
|
|
713
|
-
let current =
|
|
714
|
-
const normalizedRoot =
|
|
840
|
+
let current = resolve4(targetDir);
|
|
841
|
+
const normalizedRoot = resolve4(rootDir);
|
|
715
842
|
while (true) {
|
|
716
843
|
hierarchy.unshift(current);
|
|
717
844
|
if (current === normalizedRoot) {
|
|
718
845
|
break;
|
|
719
846
|
}
|
|
720
|
-
const parent =
|
|
847
|
+
const parent = dirname2(current);
|
|
721
848
|
if (parent === current) {
|
|
722
849
|
break;
|
|
723
850
|
}
|
|
@@ -732,8 +859,8 @@ class AgentsMdLoader {
|
|
|
732
859
|
const sourcePaths = [];
|
|
733
860
|
for (const dir of dirHierarchy) {
|
|
734
861
|
for (const filename of fallbackFilenames) {
|
|
735
|
-
const filePath =
|
|
736
|
-
if (
|
|
862
|
+
const filePath = join3(dir, filename);
|
|
863
|
+
if (existsSync4(filePath)) {
|
|
737
864
|
try {
|
|
738
865
|
const content = readFileSync3(filePath, "utf-8").trim();
|
|
739
866
|
if (content) {
|
|
@@ -1345,13 +1472,13 @@ class Session {
|
|
|
1345
1472
|
type: "StatusChanged",
|
|
1346
1473
|
status: "waiting_approval"
|
|
1347
1474
|
});
|
|
1348
|
-
return new Promise((
|
|
1475
|
+
return new Promise((resolve5) => {
|
|
1349
1476
|
this.pendingApprovals.set(params.approvalId, (approved) => {
|
|
1350
1477
|
this.emitEvent({
|
|
1351
1478
|
type: "StatusChanged",
|
|
1352
1479
|
status: "running"
|
|
1353
1480
|
});
|
|
1354
|
-
|
|
1481
|
+
resolve5(approved);
|
|
1355
1482
|
});
|
|
1356
1483
|
});
|
|
1357
1484
|
}
|
|
@@ -1374,13 +1501,13 @@ class Session {
|
|
|
1374
1501
|
type: "StatusChanged",
|
|
1375
1502
|
status: "waiting_user_input"
|
|
1376
1503
|
});
|
|
1377
|
-
return new Promise((
|
|
1504
|
+
return new Promise((resolve5) => {
|
|
1378
1505
|
this.pendingUserQuestions.set(params.questionId, (answer) => {
|
|
1379
1506
|
this.emitEvent({
|
|
1380
1507
|
type: "StatusChanged",
|
|
1381
1508
|
status: "running"
|
|
1382
1509
|
});
|
|
1383
|
-
|
|
1510
|
+
resolve5(answer);
|
|
1384
1511
|
});
|
|
1385
1512
|
});
|
|
1386
1513
|
}
|
|
@@ -1410,7 +1537,7 @@ class Session {
|
|
|
1410
1537
|
return handleTurnInput(this, { text, images });
|
|
1411
1538
|
}
|
|
1412
1539
|
async promptAndWait(text, images, timeoutMs = 30000) {
|
|
1413
|
-
return new Promise((
|
|
1540
|
+
return new Promise((resolve5, reject) => {
|
|
1414
1541
|
const timer = setTimeout(() => {
|
|
1415
1542
|
unsub();
|
|
1416
1543
|
reject(new Error(`Turn timed out after ${timeoutMs}ms`));
|
|
@@ -1419,7 +1546,7 @@ class Session {
|
|
|
1419
1546
|
if (event.msg.type === "TurnCompleted") {
|
|
1420
1547
|
clearTimeout(timer);
|
|
1421
1548
|
unsub();
|
|
1422
|
-
|
|
1549
|
+
resolve5();
|
|
1423
1550
|
} else if (event.msg.type === "Error") {
|
|
1424
1551
|
clearTimeout(timer);
|
|
1425
1552
|
unsub();
|
|
@@ -1438,8 +1565,8 @@ class Session {
|
|
|
1438
1565
|
if (this.submissionQueue.length > 0) {
|
|
1439
1566
|
yield this.submissionQueue.shift();
|
|
1440
1567
|
} else {
|
|
1441
|
-
const nextSub = await new Promise((
|
|
1442
|
-
this.submissionResolvers.push(
|
|
1568
|
+
const nextSub = await new Promise((resolve5) => {
|
|
1569
|
+
this.submissionResolvers.push(resolve5);
|
|
1443
1570
|
});
|
|
1444
1571
|
yield nextSub;
|
|
1445
1572
|
}
|
|
@@ -1453,9 +1580,9 @@ class Session {
|
|
|
1453
1580
|
}
|
|
1454
1581
|
}
|
|
1455
1582
|
// src/tools/handlers/apply-patch.ts
|
|
1456
|
-
import { existsSync as
|
|
1457
|
-
import { resolve as
|
|
1458
|
-
import { mkdirSync as
|
|
1583
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
|
|
1584
|
+
import { resolve as resolve5, dirname as dirname3 } from "path";
|
|
1585
|
+
import { mkdirSync as mkdirSync3 } from "fs";
|
|
1459
1586
|
var applyPatchTool = {
|
|
1460
1587
|
name: "apply_patch",
|
|
1461
1588
|
description: "Apply precise multi-line modifications to an existing file or create a new file. TargetContent must match the file content exactly.",
|
|
@@ -1482,7 +1609,7 @@ var applyPatchTool = {
|
|
|
1482
1609
|
if (!rawPath) {
|
|
1483
1610
|
return { output: "Error: 'path' parameter is required", isError: true };
|
|
1484
1611
|
}
|
|
1485
|
-
const filePath =
|
|
1612
|
+
const filePath = resolve5(ctx.cwd, rawPath);
|
|
1486
1613
|
const targetContent = typeof args.targetContent === "string" ? args.targetContent : "";
|
|
1487
1614
|
const replacementContent = String(args.replacementContent ?? "");
|
|
1488
1615
|
if (ctx.execPolicy) {
|
|
@@ -1501,7 +1628,7 @@ var applyPatchTool = {
|
|
|
1501
1628
|
}
|
|
1502
1629
|
}
|
|
1503
1630
|
}
|
|
1504
|
-
if (!
|
|
1631
|
+
if (!existsSync5(filePath)) {
|
|
1505
1632
|
if (targetContent) {
|
|
1506
1633
|
return {
|
|
1507
1634
|
output: `Error: Target file '${rawPath}' does not exist, but targetContent was provided.`,
|
|
@@ -1509,7 +1636,7 @@ var applyPatchTool = {
|
|
|
1509
1636
|
};
|
|
1510
1637
|
}
|
|
1511
1638
|
try {
|
|
1512
|
-
|
|
1639
|
+
mkdirSync3(dirname3(filePath), { recursive: true });
|
|
1513
1640
|
writeFileSync2(filePath, replacementContent, "utf8");
|
|
1514
1641
|
return { output: `Successfully created new file '${rawPath}'` };
|
|
1515
1642
|
} catch (err) {
|
|
@@ -1635,7 +1762,7 @@ class WindowsSandbox {
|
|
|
1635
1762
|
}
|
|
1636
1763
|
|
|
1637
1764
|
// src/security/kernel/linux.ts
|
|
1638
|
-
import { existsSync as
|
|
1765
|
+
import { existsSync as existsSync6 } from "fs";
|
|
1639
1766
|
|
|
1640
1767
|
class LinuxSandbox {
|
|
1641
1768
|
hasBwrap = false;
|
|
@@ -1646,7 +1773,7 @@ class LinuxSandbox {
|
|
|
1646
1773
|
if (process.platform !== "linux") {
|
|
1647
1774
|
return;
|
|
1648
1775
|
}
|
|
1649
|
-
this.hasBwrap =
|
|
1776
|
+
this.hasBwrap = existsSync6("/usr/bin/bwrap") || existsSync6("/bin/bwrap") || existsSync6("/usr/local/bin/bwrap");
|
|
1650
1777
|
}
|
|
1651
1778
|
wrapCommand(cmd, profile) {
|
|
1652
1779
|
if (!this.hasBwrap || profile.kind === "danger-unrestricted") {
|
|
@@ -1680,7 +1807,7 @@ class LinuxSandbox {
|
|
|
1680
1807
|
}
|
|
1681
1808
|
|
|
1682
1809
|
// src/security/kernel/macos.ts
|
|
1683
|
-
import { existsSync as
|
|
1810
|
+
import { existsSync as existsSync7 } from "fs";
|
|
1684
1811
|
|
|
1685
1812
|
class MacOSSandbox {
|
|
1686
1813
|
hasSandboxExec = false;
|
|
@@ -1691,7 +1818,7 @@ class MacOSSandbox {
|
|
|
1691
1818
|
if (process.platform !== "darwin") {
|
|
1692
1819
|
return;
|
|
1693
1820
|
}
|
|
1694
|
-
this.hasSandboxExec =
|
|
1821
|
+
this.hasSandboxExec = existsSync7("/usr/bin/sandbox-exec");
|
|
1695
1822
|
}
|
|
1696
1823
|
generateProfile(profile) {
|
|
1697
1824
|
const rules = [
|
|
@@ -1730,7 +1857,7 @@ class MacOSSandbox {
|
|
|
1730
1857
|
}
|
|
1731
1858
|
|
|
1732
1859
|
// src/security/kernel/manager.ts
|
|
1733
|
-
import { resolve as
|
|
1860
|
+
import { resolve as resolve6, normalize } from "path";
|
|
1734
1861
|
|
|
1735
1862
|
class KernelSandboxManager {
|
|
1736
1863
|
windowsSandbox;
|
|
@@ -1752,10 +1879,10 @@ class KernelSandboxManager {
|
|
|
1752
1879
|
};
|
|
1753
1880
|
}
|
|
1754
1881
|
buildDefaultProfile(cwd, allowNetwork = true) {
|
|
1755
|
-
const normCwd = normalize(
|
|
1882
|
+
const normCwd = normalize(resolve6(cwd));
|
|
1756
1883
|
return {
|
|
1757
1884
|
kind: "workspace-write",
|
|
1758
|
-
readableRoots: [normCwd,
|
|
1885
|
+
readableRoots: [normCwd, resolve6(process.cwd())],
|
|
1759
1886
|
writableRoots: [normCwd],
|
|
1760
1887
|
allowNetwork,
|
|
1761
1888
|
limits: {
|
|
@@ -1801,21 +1928,19 @@ var globalKernelSandbox = new KernelSandboxManager;
|
|
|
1801
1928
|
|
|
1802
1929
|
// src/storage/prefix-rules-store.ts
|
|
1803
1930
|
import { Database } from "bun:sqlite";
|
|
1804
|
-
import { existsSync as
|
|
1805
|
-
import { dirname as
|
|
1806
|
-
import { homedir as homedir3 } from "os";
|
|
1807
|
-
|
|
1931
|
+
import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
|
|
1932
|
+
import { dirname as dirname4, resolve as resolve7 } from "path";
|
|
1808
1933
|
class PrefixRulesStore {
|
|
1809
1934
|
db;
|
|
1810
1935
|
constructor(dbOrPath) {
|
|
1811
1936
|
if (dbOrPath instanceof Database) {
|
|
1812
1937
|
this.db = dbOrPath;
|
|
1813
1938
|
} else {
|
|
1814
|
-
const effectivePath = dbOrPath ||
|
|
1939
|
+
const effectivePath = dbOrPath || getPrefixRulesDbPath();
|
|
1815
1940
|
if (effectivePath !== ":memory:") {
|
|
1816
|
-
const dir =
|
|
1817
|
-
if (!
|
|
1818
|
-
|
|
1941
|
+
const dir = dirname4(effectivePath);
|
|
1942
|
+
if (!existsSync8(dir)) {
|
|
1943
|
+
mkdirSync4(dir, { recursive: true });
|
|
1819
1944
|
}
|
|
1820
1945
|
}
|
|
1821
1946
|
this.db = new Database(effectivePath);
|
|
@@ -1838,7 +1963,7 @@ class PrefixRulesStore {
|
|
|
1838
1963
|
addRule(workspacePath, prefixTokens) {
|
|
1839
1964
|
if (!prefixTokens || prefixTokens.length === 0)
|
|
1840
1965
|
return;
|
|
1841
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
1966
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
|
|
1842
1967
|
const tokensJson = JSON.stringify(prefixTokens);
|
|
1843
1968
|
const id = `${normalizedWs}:${tokensJson}`;
|
|
1844
1969
|
const query = this.db.prepare(`
|
|
@@ -1855,7 +1980,7 @@ class PrefixRulesStore {
|
|
|
1855
1980
|
isApproved(workspacePath, commandTokens) {
|
|
1856
1981
|
if (!commandTokens || commandTokens.length === 0)
|
|
1857
1982
|
return false;
|
|
1858
|
-
const normalizedWs =
|
|
1983
|
+
const normalizedWs = resolve7(workspacePath);
|
|
1859
1984
|
const query = this.db.prepare(`
|
|
1860
1985
|
SELECT prefix_tokens FROM approved_prefix_rules
|
|
1861
1986
|
WHERE workspace_path = $ws OR workspace_path = '*'
|
|
@@ -1874,7 +1999,7 @@ class PrefixRulesStore {
|
|
|
1874
1999
|
listRules(workspacePath) {
|
|
1875
2000
|
let rows;
|
|
1876
2001
|
if (workspacePath) {
|
|
1877
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
2002
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
|
|
1878
2003
|
const query = this.db.prepare(`
|
|
1879
2004
|
SELECT prefix_tokens FROM approved_prefix_rules
|
|
1880
2005
|
WHERE workspace_path = $ws OR workspace_path = '*'
|
|
@@ -1893,7 +2018,7 @@ class PrefixRulesStore {
|
|
|
1893
2018
|
}).filter((r) => r.length > 0);
|
|
1894
2019
|
}
|
|
1895
2020
|
removeRule(workspacePath, prefixTokens) {
|
|
1896
|
-
const normalizedWs = workspacePath === "*" ? "*" :
|
|
2021
|
+
const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
|
|
1897
2022
|
const tokensJson = JSON.stringify(prefixTokens);
|
|
1898
2023
|
const id = `${normalizedWs}:${tokensJson}`;
|
|
1899
2024
|
const query = this.db.prepare(`
|
|
@@ -2026,7 +2151,7 @@ function createShellTool(policy = new ExecPolicy) {
|
|
|
2026
2151
|
} catch {}
|
|
2027
2152
|
});
|
|
2028
2153
|
}
|
|
2029
|
-
const timeoutPromise = new Promise((
|
|
2154
|
+
const timeoutPromise = new Promise((resolve8) => setTimeout(() => resolve8({ isTimeout: true }), timeoutMs));
|
|
2030
2155
|
const result = await Promise.race([
|
|
2031
2156
|
proc.exited.then(async (code) => {
|
|
2032
2157
|
const stdout = await new Response(proc.stdout).text();
|
|
@@ -2070,8 +2195,8 @@ ${result.stderr.trim()}`);
|
|
|
2070
2195
|
}
|
|
2071
2196
|
var shellTool = createShellTool();
|
|
2072
2197
|
// src/tools/handlers/file-ops.ts
|
|
2073
|
-
import { readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as
|
|
2074
|
-
import { resolve as
|
|
2198
|
+
import { readdirSync as readdirSync2, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync9, statSync as statSync2, mkdirSync as mkdirSync5 } from "fs";
|
|
2199
|
+
import { resolve as resolve8, dirname as dirname5 } from "path";
|
|
2075
2200
|
var readFileTool = {
|
|
2076
2201
|
name: "read_file",
|
|
2077
2202
|
description: "Read the full text content of a file.",
|
|
@@ -2083,8 +2208,8 @@ var readFileTool = {
|
|
|
2083
2208
|
required: ["path"]
|
|
2084
2209
|
},
|
|
2085
2210
|
async execute(args, ctx) {
|
|
2086
|
-
const filePath =
|
|
2087
|
-
if (!
|
|
2211
|
+
const filePath = resolve8(ctx.cwd, String(args.path || ""));
|
|
2212
|
+
if (!existsSync9(filePath)) {
|
|
2088
2213
|
return { output: `Error: File not found: '${args.path}'`, isError: true };
|
|
2089
2214
|
}
|
|
2090
2215
|
try {
|
|
@@ -2105,15 +2230,15 @@ var listDirTool = {
|
|
|
2105
2230
|
}
|
|
2106
2231
|
},
|
|
2107
2232
|
async execute(args, ctx) {
|
|
2108
|
-
const dirPath =
|
|
2109
|
-
if (!
|
|
2233
|
+
const dirPath = resolve8(ctx.cwd, String(args.path || "."));
|
|
2234
|
+
if (!existsSync9(dirPath)) {
|
|
2110
2235
|
return { output: `Error: Directory not found: '${args.path}'`, isError: true };
|
|
2111
2236
|
}
|
|
2112
2237
|
try {
|
|
2113
|
-
const entries =
|
|
2238
|
+
const entries = readdirSync2(dirPath);
|
|
2114
2239
|
const formatted = entries.map((entry) => {
|
|
2115
|
-
const full =
|
|
2116
|
-
const isDir =
|
|
2240
|
+
const full = resolve8(dirPath, entry);
|
|
2241
|
+
const isDir = statSync2(full).isDirectory();
|
|
2117
2242
|
return `${isDir ? "[DIR]" : "[FILE]"} ${entry}`;
|
|
2118
2243
|
});
|
|
2119
2244
|
return { output: formatted.join(`
|
|
@@ -2136,7 +2261,7 @@ var writeFileTool = {
|
|
|
2136
2261
|
},
|
|
2137
2262
|
async execute(args, ctx) {
|
|
2138
2263
|
const rawPath = String(args.path || "");
|
|
2139
|
-
const filePath =
|
|
2264
|
+
const filePath = resolve8(ctx.cwd, rawPath);
|
|
2140
2265
|
if (ctx.execPolicy) {
|
|
2141
2266
|
const evalResult = ctx.execPolicy.shouldPromptFileEdit(rawPath);
|
|
2142
2267
|
if (evalResult.isPlanBlocked || ctx.mode === "plan") {
|
|
@@ -2154,7 +2279,7 @@ var writeFileTool = {
|
|
|
2154
2279
|
}
|
|
2155
2280
|
}
|
|
2156
2281
|
try {
|
|
2157
|
-
|
|
2282
|
+
mkdirSync5(dirname5(filePath), { recursive: true });
|
|
2158
2283
|
writeFileSync3(filePath, String(args.content ?? ""), "utf8");
|
|
2159
2284
|
return { output: `Successfully wrote to '${args.path}'` };
|
|
2160
2285
|
} catch (err) {
|
|
@@ -2277,8 +2402,8 @@ var updatePlanTool = {
|
|
|
2277
2402
|
}
|
|
2278
2403
|
};
|
|
2279
2404
|
// src/search/engine.ts
|
|
2280
|
-
import { readdirSync as
|
|
2281
|
-
import { resolve as
|
|
2405
|
+
import { readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3, existsSync as existsSync10 } from "fs";
|
|
2406
|
+
import { resolve as resolve9, relative, join as join4, extname } from "path";
|
|
2282
2407
|
var DEFAULT_IGNORE_DIRS = new Set([
|
|
2283
2408
|
".git",
|
|
2284
2409
|
"node_modules",
|
|
@@ -2323,8 +2448,8 @@ var BINARY_EXTENSIONS = new Set([
|
|
|
2323
2448
|
|
|
2324
2449
|
class FileSearchEngine {
|
|
2325
2450
|
grep(cwd, options) {
|
|
2326
|
-
const searchRoot =
|
|
2327
|
-
if (!
|
|
2451
|
+
const searchRoot = resolve9(cwd, options.path || ".");
|
|
2452
|
+
if (!existsSync10(searchRoot)) {
|
|
2328
2453
|
return { matches: [], totalMatches: 0, truncated: false };
|
|
2329
2454
|
}
|
|
2330
2455
|
const maxResults = options.maxResults || 50;
|
|
@@ -2373,8 +2498,8 @@ class FileSearchEngine {
|
|
|
2373
2498
|
return { matches, totalMatches, truncated };
|
|
2374
2499
|
}
|
|
2375
2500
|
findFiles(cwd, options) {
|
|
2376
|
-
const searchRoot =
|
|
2377
|
-
if (!
|
|
2501
|
+
const searchRoot = resolve9(cwd, options.path || ".");
|
|
2502
|
+
if (!existsSync10(searchRoot))
|
|
2378
2503
|
return [];
|
|
2379
2504
|
const maxResults = options.maxResults || 100;
|
|
2380
2505
|
const gitignoreRules = this.loadGitignoreRules(searchRoot);
|
|
@@ -2413,8 +2538,8 @@ class FileSearchEngine {
|
|
|
2413
2538
|
}
|
|
2414
2539
|
loadGitignoreRules(root) {
|
|
2415
2540
|
const rules = new Set;
|
|
2416
|
-
const gitignorePath =
|
|
2417
|
-
if (
|
|
2541
|
+
const gitignorePath = join4(root, ".gitignore");
|
|
2542
|
+
if (existsSync10(gitignorePath)) {
|
|
2418
2543
|
try {
|
|
2419
2544
|
const lines = readFileSync6(gitignorePath, "utf8").split(`
|
|
2420
2545
|
`);
|
|
@@ -2431,7 +2556,7 @@ class FileSearchEngine {
|
|
|
2431
2556
|
collectFiles(dir, root, gitignoreRules, includePattern) {
|
|
2432
2557
|
const results = [];
|
|
2433
2558
|
try {
|
|
2434
|
-
const stat =
|
|
2559
|
+
const stat = statSync3(dir);
|
|
2435
2560
|
if (!stat.isDirectory()) {
|
|
2436
2561
|
if (!this.isBinary(dir)) {
|
|
2437
2562
|
results.push(dir);
|
|
@@ -2445,9 +2570,9 @@ class FileSearchEngine {
|
|
|
2445
2570
|
while (queue.length > 0) {
|
|
2446
2571
|
const currentDir = queue.shift();
|
|
2447
2572
|
try {
|
|
2448
|
-
const entries =
|
|
2573
|
+
const entries = readdirSync3(currentDir, { withFileTypes: true });
|
|
2449
2574
|
for (const entry of entries) {
|
|
2450
|
-
const fullPath =
|
|
2575
|
+
const fullPath = join4(currentDir, entry.name);
|
|
2451
2576
|
const relToRoot = relative(root, fullPath).replace(/\\/g, "/");
|
|
2452
2577
|
if (this.isIgnored(entry.name, relToRoot, gitignoreRules)) {
|
|
2453
2578
|
continue;
|
|
@@ -2844,6 +2969,111 @@ ${loaded.instructions}`
|
|
|
2844
2969
|
}
|
|
2845
2970
|
|
|
2846
2971
|
// src/memories/tool.ts
|
|
2972
|
+
function createSaveMemoryTool(store) {
|
|
2973
|
+
return {
|
|
2974
|
+
name: "save_memory",
|
|
2975
|
+
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.",
|
|
2976
|
+
parameters: {
|
|
2977
|
+
type: "object",
|
|
2978
|
+
properties: {
|
|
2979
|
+
category: {
|
|
2980
|
+
type: "string",
|
|
2981
|
+
description: "Category of memory: 'user', 'feedback', 'project', or 'reference'.",
|
|
2982
|
+
enum: ["user", "feedback", "project", "reference"]
|
|
2983
|
+
},
|
|
2984
|
+
name: {
|
|
2985
|
+
type: "string",
|
|
2986
|
+
description: "Short, descriptive snake_case identifier for this memory topic (e.g. 'testing_strategy', 'preferred_framework', 'staging_api')."
|
|
2987
|
+
},
|
|
2988
|
+
description: {
|
|
2989
|
+
type: "string",
|
|
2990
|
+
description: "One-line summary to display in the MEMORY.md index (e.g. 'Prefers Vitest without database mocks')."
|
|
2991
|
+
},
|
|
2992
|
+
content: {
|
|
2993
|
+
type: "string",
|
|
2994
|
+
description: "Detailed description of the fact, preference, or learned correction."
|
|
2995
|
+
}
|
|
2996
|
+
},
|
|
2997
|
+
required: ["category", "name", "content"]
|
|
2998
|
+
},
|
|
2999
|
+
async execute(args, context) {
|
|
3000
|
+
const category = args.category || "project";
|
|
3001
|
+
const name = String(args.name || `topic_${Date.now()}`);
|
|
3002
|
+
const content = String(args.content || "").trim();
|
|
3003
|
+
const description = args.description ? String(args.description).trim() : undefined;
|
|
3004
|
+
if (!content) {
|
|
3005
|
+
return { output: "Error: memory content cannot be empty", isError: true };
|
|
3006
|
+
}
|
|
3007
|
+
const entry = store.saveTopicMemory({
|
|
3008
|
+
category,
|
|
3009
|
+
name,
|
|
3010
|
+
description,
|
|
3011
|
+
content,
|
|
3012
|
+
cwd: context.cwd
|
|
3013
|
+
});
|
|
3014
|
+
return {
|
|
3015
|
+
output: `\u2713 Saved Auto-Memory topic: [${entry.category}] "${entry.name}" -> ${entry.filePath}`
|
|
3016
|
+
};
|
|
3017
|
+
}
|
|
3018
|
+
};
|
|
3019
|
+
}
|
|
3020
|
+
function createReadMemoryTool(store) {
|
|
3021
|
+
return {
|
|
3022
|
+
name: "read_memory",
|
|
3023
|
+
description: "Read the full details of a specific Auto-Memory topic file recorded in the project's memory index.",
|
|
3024
|
+
parameters: {
|
|
3025
|
+
type: "object",
|
|
3026
|
+
properties: {
|
|
3027
|
+
topic: {
|
|
3028
|
+
type: "string",
|
|
3029
|
+
description: "Name or filename of the memory topic to read (e.g. 'testing_strategy' or 'feedback_testing.md')."
|
|
3030
|
+
}
|
|
3031
|
+
},
|
|
3032
|
+
required: ["topic"]
|
|
3033
|
+
},
|
|
3034
|
+
async execute(args, context) {
|
|
3035
|
+
const topic = String(args.topic || "").trim();
|
|
3036
|
+
if (!topic) {
|
|
3037
|
+
return { output: "Error: topic name is required", isError: true };
|
|
3038
|
+
}
|
|
3039
|
+
const memory = store.readTopicMemory(topic, context.cwd);
|
|
3040
|
+
if (!memory) {
|
|
3041
|
+
return {
|
|
3042
|
+
output: `No memory topic found matching '${topic}' in this project.`,
|
|
3043
|
+
isError: true
|
|
3044
|
+
};
|
|
3045
|
+
}
|
|
3046
|
+
return {
|
|
3047
|
+
output: `# Topic: ${memory.name} (${memory.type})
|
|
3048
|
+
Modified: ${memory.modified}
|
|
3049
|
+
|
|
3050
|
+
${memory.content}`
|
|
3051
|
+
};
|
|
3052
|
+
}
|
|
3053
|
+
};
|
|
3054
|
+
}
|
|
3055
|
+
function createListMemoriesTool(store) {
|
|
3056
|
+
return {
|
|
3057
|
+
name: "list_memories",
|
|
3058
|
+
description: "List all persistent memory topics and index for the current project repository.",
|
|
3059
|
+
parameters: {
|
|
3060
|
+
type: "object",
|
|
3061
|
+
properties: {}
|
|
3062
|
+
},
|
|
3063
|
+
async execute(_args, context) {
|
|
3064
|
+
const topics = store.listProjectMemories(context.cwd);
|
|
3065
|
+
if (topics.length === 0) {
|
|
3066
|
+
return { output: "No persistent Auto-Memories have been recorded for this project yet." };
|
|
3067
|
+
}
|
|
3068
|
+
const lines = topics.map((t) => `\u2022 [${t.type}] **${t.name}**: ${t.description || t.content.split(`
|
|
3069
|
+
`)[0]} (file: ${t.filePath})`);
|
|
3070
|
+
return { output: `Project Auto-Memories (${topics.length} topics):
|
|
3071
|
+
|
|
3072
|
+
${lines.join(`
|
|
3073
|
+
`)}` };
|
|
3074
|
+
}
|
|
3075
|
+
};
|
|
3076
|
+
}
|
|
2847
3077
|
function createRememberTool(store) {
|
|
2848
3078
|
return {
|
|
2849
3079
|
name: "remember",
|
|
@@ -2854,16 +3084,15 @@ function createRememberTool(store) {
|
|
|
2854
3084
|
category: {
|
|
2855
3085
|
type: "string",
|
|
2856
3086
|
description: "Category of the memory.",
|
|
2857
|
-
enum: ["preference", "guideline", "architecture", "note"]
|
|
3087
|
+
enum: ["preference", "guideline", "architecture", "note", "user", "feedback", "project", "reference"]
|
|
2858
3088
|
},
|
|
2859
3089
|
content: {
|
|
2860
3090
|
type: "string",
|
|
2861
3091
|
description: "The concise rule, preference, or fact to remember permanently."
|
|
2862
3092
|
},
|
|
2863
|
-
|
|
3093
|
+
name: {
|
|
2864
3094
|
type: "string",
|
|
2865
|
-
description: "
|
|
2866
|
-
enum: ["global", "workspace"]
|
|
3095
|
+
description: "Optional topic name."
|
|
2867
3096
|
}
|
|
2868
3097
|
},
|
|
2869
3098
|
required: ["category", "content"]
|
|
@@ -2871,22 +3100,30 @@ function createRememberTool(store) {
|
|
|
2871
3100
|
async execute(args, context) {
|
|
2872
3101
|
const category = args.category || "preference";
|
|
2873
3102
|
const content = String(args.content || "");
|
|
2874
|
-
const
|
|
3103
|
+
const name = args.name ? String(args.name) : undefined;
|
|
2875
3104
|
if (!content) {
|
|
2876
3105
|
return { output: "Error: memory content cannot be empty", isError: true };
|
|
2877
3106
|
}
|
|
2878
3107
|
const entry = store.addMemory({
|
|
2879
3108
|
category,
|
|
2880
3109
|
content,
|
|
2881
|
-
|
|
3110
|
+
name,
|
|
2882
3111
|
cwd: context.cwd
|
|
2883
3112
|
});
|
|
2884
3113
|
return {
|
|
2885
|
-
output: `Successfully saved to
|
|
3114
|
+
output: `Successfully saved to Auto-Memory bank: [${entry.category}] "${entry.name || entry.content}"`
|
|
2886
3115
|
};
|
|
2887
3116
|
}
|
|
2888
3117
|
};
|
|
2889
3118
|
}
|
|
3119
|
+
function createAutoMemoryTools(store) {
|
|
3120
|
+
return [
|
|
3121
|
+
createSaveMemoryTool(store),
|
|
3122
|
+
createReadMemoryTool(store),
|
|
3123
|
+
createListMemoriesTool(store),
|
|
3124
|
+
createRememberTool(store)
|
|
3125
|
+
];
|
|
3126
|
+
}
|
|
2890
3127
|
|
|
2891
3128
|
// src/worktree/tools.ts
|
|
2892
3129
|
function createWorktreeTools(manager) {
|
|
@@ -3021,7 +3258,9 @@ function createDefaultTools(options = {}) {
|
|
|
3021
3258
|
router2.register(createSkillTool(options.skillsLoader));
|
|
3022
3259
|
}
|
|
3023
3260
|
if (options.memoryStore) {
|
|
3024
|
-
|
|
3261
|
+
for (const tool of createAutoMemoryTools(options.memoryStore)) {
|
|
3262
|
+
router2.register(tool);
|
|
3263
|
+
}
|
|
3025
3264
|
}
|
|
3026
3265
|
if (options.worktreeManager) {
|
|
3027
3266
|
for (const tool of createWorktreeTools(options.worktreeManager)) {
|
|
@@ -3032,8 +3271,8 @@ function createDefaultTools(options = {}) {
|
|
|
3032
3271
|
}
|
|
3033
3272
|
|
|
3034
3273
|
// src/agents/roles.ts
|
|
3035
|
-
import { existsSync as
|
|
3036
|
-
import { resolve as
|
|
3274
|
+
import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
|
|
3275
|
+
import { resolve as resolve10, join as join5 } from "path";
|
|
3037
3276
|
|
|
3038
3277
|
class AgentRoleRegistry {
|
|
3039
3278
|
roles = new Map;
|
|
@@ -3117,14 +3356,14 @@ class AgentRoleRegistry {
|
|
|
3117
3356
|
return cycle === 0 ? base : `${base}_${cycle + 1}`;
|
|
3118
3357
|
}
|
|
3119
3358
|
loadRolesFromDir(dirPath) {
|
|
3120
|
-
const fullPath =
|
|
3121
|
-
if (!
|
|
3359
|
+
const fullPath = resolve10(dirPath);
|
|
3360
|
+
if (!existsSync11(fullPath))
|
|
3122
3361
|
return;
|
|
3123
|
-
const entries =
|
|
3362
|
+
const entries = readdirSync4(fullPath);
|
|
3124
3363
|
for (const entry of entries) {
|
|
3125
3364
|
if (entry.endsWith(".json")) {
|
|
3126
3365
|
try {
|
|
3127
|
-
const content = readFileSync7(
|
|
3366
|
+
const content = readFileSync7(join5(fullPath, entry), "utf8");
|
|
3128
3367
|
const parsed = JSON.parse(content);
|
|
3129
3368
|
if (parsed.name && parsed.systemPrompt) {
|
|
3130
3369
|
this.registerRole(parsed);
|
|
@@ -3176,21 +3415,19 @@ function createAgentIdentity(parentId, harnessId = "groupy-harness-v1") {
|
|
|
3176
3415
|
|
|
3177
3416
|
// src/agents/graph-store.ts
|
|
3178
3417
|
import { Database as Database2 } from "bun:sqlite";
|
|
3179
|
-
import { resolve as
|
|
3180
|
-
import { existsSync as
|
|
3181
|
-
import { homedir as homedir4 } from "os";
|
|
3182
|
-
|
|
3418
|
+
import { resolve as resolve11 } from "path";
|
|
3419
|
+
import { existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
|
|
3183
3420
|
class AgentGraphStore {
|
|
3184
3421
|
db;
|
|
3185
3422
|
constructor(dbPathOrDb) {
|
|
3186
3423
|
if (dbPathOrDb instanceof Database2) {
|
|
3187
3424
|
this.db = dbPathOrDb;
|
|
3188
3425
|
} else {
|
|
3189
|
-
const dbPath = dbPathOrDb ||
|
|
3426
|
+
const dbPath = dbPathOrDb || getAgentGraphDbPath();
|
|
3190
3427
|
if (dbPath !== ":memory:") {
|
|
3191
|
-
const dir =
|
|
3192
|
-
if (!
|
|
3193
|
-
|
|
3428
|
+
const dir = resolve11(dbPath, "..");
|
|
3429
|
+
if (!existsSync12(dir)) {
|
|
3430
|
+
mkdirSync6(dir, { recursive: true });
|
|
3194
3431
|
}
|
|
3195
3432
|
}
|
|
3196
3433
|
this.db = new Database2(dbPath);
|
|
@@ -3313,8 +3550,8 @@ Your nickname is ${nickname}. Your assigned task is: '${params.taskName}'. Focus
|
|
|
3313
3550
|
});
|
|
3314
3551
|
let resolvePromise;
|
|
3315
3552
|
let rejectPromise;
|
|
3316
|
-
const taskPromise = new Promise((
|
|
3317
|
-
resolvePromise =
|
|
3553
|
+
const taskPromise = new Promise((resolve12, reject) => {
|
|
3554
|
+
resolvePromise = resolve12;
|
|
3318
3555
|
rejectPromise = reject;
|
|
3319
3556
|
});
|
|
3320
3557
|
const handle = {
|
|
@@ -3614,8 +3851,8 @@ function registerMultiAgentTools(router2, spawner) {
|
|
|
3614
3851
|
}
|
|
3615
3852
|
|
|
3616
3853
|
// src/mcp/manager.ts
|
|
3617
|
-
import { existsSync as
|
|
3618
|
-
import { resolve as
|
|
3854
|
+
import { existsSync as existsSync13, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync7 } from "fs";
|
|
3855
|
+
import { resolve as resolve12, dirname as dirname6, join as join6 } from "path";
|
|
3619
3856
|
|
|
3620
3857
|
// src/mcp/client.ts
|
|
3621
3858
|
class McpClient {
|
|
@@ -4009,13 +4246,13 @@ class StdioTransport {
|
|
|
4009
4246
|
if (this.isClosed || !this.proc || !this.proc.stdin) {
|
|
4010
4247
|
throw new GroupyError("MCP Stdio transport is closed");
|
|
4011
4248
|
}
|
|
4012
|
-
return new Promise((
|
|
4249
|
+
return new Promise((resolve12, reject) => {
|
|
4013
4250
|
const timeoutMs = 30000;
|
|
4014
4251
|
const timer = setTimeout(() => {
|
|
4015
4252
|
this.pendingRequests.delete(request.id);
|
|
4016
4253
|
reject(new GroupyError(`MCP request timed out after ${timeoutMs}ms (method: ${request.method})`));
|
|
4017
4254
|
}, timeoutMs);
|
|
4018
|
-
this.pendingRequests.set(request.id, { resolve:
|
|
4255
|
+
this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
|
|
4019
4256
|
try {
|
|
4020
4257
|
const payload = JSON.stringify(request) + `
|
|
4021
4258
|
`;
|
|
@@ -4148,12 +4385,12 @@ class SseTransport {
|
|
|
4148
4385
|
if (!this.messageUrl) {
|
|
4149
4386
|
this.messageUrl = this.endpointUrl;
|
|
4150
4387
|
}
|
|
4151
|
-
return new Promise((
|
|
4388
|
+
return new Promise((resolve12, reject) => {
|
|
4152
4389
|
const timer = setTimeout(() => {
|
|
4153
4390
|
this.pendingRequests.delete(request.id);
|
|
4154
4391
|
reject(new GroupyError(`MCP SSE request timed out (method: ${request.method})`));
|
|
4155
4392
|
}, 30000);
|
|
4156
|
-
this.pendingRequests.set(request.id, { resolve:
|
|
4393
|
+
this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
|
|
4157
4394
|
fetch(this.messageUrl, {
|
|
4158
4395
|
method: "POST",
|
|
4159
4396
|
headers: {
|
|
@@ -4230,8 +4467,8 @@ class McpManager {
|
|
|
4230
4467
|
}
|
|
4231
4468
|
}
|
|
4232
4469
|
async loadConfigFile(filePath) {
|
|
4233
|
-
const fullPath =
|
|
4234
|
-
if (!
|
|
4470
|
+
const fullPath = resolve12(filePath);
|
|
4471
|
+
if (!existsSync13(fullPath))
|
|
4235
4472
|
return;
|
|
4236
4473
|
this.loadedConfigFiles.add(fullPath);
|
|
4237
4474
|
try {
|
|
@@ -4484,13 +4721,13 @@ class McpManager {
|
|
|
4484
4721
|
`);
|
|
4485
4722
|
}
|
|
4486
4723
|
saveServerToConfigFile(filePath, name, config) {
|
|
4487
|
-
const fullPath =
|
|
4488
|
-
const dir =
|
|
4489
|
-
if (!
|
|
4490
|
-
|
|
4724
|
+
const fullPath = resolve12(filePath);
|
|
4725
|
+
const dir = dirname6(fullPath);
|
|
4726
|
+
if (!existsSync13(dir)) {
|
|
4727
|
+
mkdirSync7(dir, { recursive: true });
|
|
4491
4728
|
}
|
|
4492
4729
|
let existing = { mcpServers: {} };
|
|
4493
|
-
if (
|
|
4730
|
+
if (existsSync13(fullPath)) {
|
|
4494
4731
|
try {
|
|
4495
4732
|
const content = readFileSync8(fullPath, "utf8");
|
|
4496
4733
|
existing = JSON.parse(content);
|
|
@@ -4504,8 +4741,8 @@ class McpManager {
|
|
|
4504
4741
|
this.loadedConfigFiles.add(fullPath);
|
|
4505
4742
|
}
|
|
4506
4743
|
removeServerFromConfigFile(filePath, name) {
|
|
4507
|
-
const fullPath =
|
|
4508
|
-
if (!
|
|
4744
|
+
const fullPath = resolve12(filePath);
|
|
4745
|
+
if (!existsSync13(fullPath))
|
|
4509
4746
|
return false;
|
|
4510
4747
|
try {
|
|
4511
4748
|
const content = readFileSync8(fullPath, "utf8");
|
|
@@ -4535,11 +4772,11 @@ class McpManager {
|
|
|
4535
4772
|
}
|
|
4536
4773
|
}
|
|
4537
4774
|
getDefaultConfigFile(cwd = process.cwd()) {
|
|
4538
|
-
const workspaceConfig =
|
|
4539
|
-
if (
|
|
4775
|
+
const workspaceConfig = join6(cwd, ".mcp.json");
|
|
4776
|
+
if (existsSync13(workspaceConfig))
|
|
4540
4777
|
return workspaceConfig;
|
|
4541
|
-
const altConfig =
|
|
4542
|
-
if (
|
|
4778
|
+
const altConfig = join6(cwd, "mcp_config.json");
|
|
4779
|
+
if (existsSync13(altConfig))
|
|
4543
4780
|
return altConfig;
|
|
4544
4781
|
return workspaceConfig;
|
|
4545
4782
|
}
|
|
@@ -4560,17 +4797,16 @@ class McpManager {
|
|
|
4560
4797
|
|
|
4561
4798
|
// src/storage/sqlite-store.ts
|
|
4562
4799
|
import { Database as Database3 } from "bun:sqlite";
|
|
4563
|
-
import { existsSync as
|
|
4564
|
-
import { dirname as
|
|
4565
|
-
import { homedir as homedir5 } from "os";
|
|
4800
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
|
|
4801
|
+
import { dirname as dirname7 } from "path";
|
|
4566
4802
|
class SqliteThreadStore {
|
|
4567
4803
|
db;
|
|
4568
4804
|
constructor(dbPath) {
|
|
4569
4805
|
const effectivePath = dbPath || this.getDefaultDbPath();
|
|
4570
4806
|
if (effectivePath !== ":memory:") {
|
|
4571
|
-
const dir =
|
|
4572
|
-
if (!
|
|
4573
|
-
|
|
4807
|
+
const dir = dirname7(effectivePath);
|
|
4808
|
+
if (!existsSync14(dir)) {
|
|
4809
|
+
mkdirSync8(dir, { recursive: true });
|
|
4574
4810
|
}
|
|
4575
4811
|
}
|
|
4576
4812
|
this.db = new Database3(effectivePath);
|
|
@@ -4579,7 +4815,7 @@ class SqliteThreadStore {
|
|
|
4579
4815
|
this.initSchema();
|
|
4580
4816
|
}
|
|
4581
4817
|
getDefaultDbPath() {
|
|
4582
|
-
return
|
|
4818
|
+
return getThreadsDbPath();
|
|
4583
4819
|
}
|
|
4584
4820
|
initSchema() {
|
|
4585
4821
|
this.db.exec(`
|
|
@@ -4804,9 +5040,9 @@ class SessionPersistenceManager {
|
|
|
4804
5040
|
}
|
|
4805
5041
|
|
|
4806
5042
|
// src/skills/loader.ts
|
|
4807
|
-
import { existsSync as
|
|
4808
|
-
import { resolve as resolve13, join as
|
|
4809
|
-
import { homedir as
|
|
5043
|
+
import { existsSync as existsSync15, readdirSync as readdirSync5, readFileSync as readFileSync9 } from "fs";
|
|
5044
|
+
import { resolve as resolve13, join as join7 } from "path";
|
|
5045
|
+
import { homedir as homedir3 } from "os";
|
|
4810
5046
|
var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
|
|
4811
5047
|
|
|
4812
5048
|
class SkillsLoader {
|
|
@@ -4876,16 +5112,16 @@ class SkillsLoader {
|
|
|
4876
5112
|
resolve13(cwd, "skills")
|
|
4877
5113
|
];
|
|
4878
5114
|
for (const cand of candidates) {
|
|
4879
|
-
if (
|
|
5115
|
+
if (existsSync15(cand) && !roots.includes(cand)) {
|
|
4880
5116
|
roots.push(cand);
|
|
4881
5117
|
}
|
|
4882
5118
|
}
|
|
4883
5119
|
}
|
|
4884
5120
|
if (this.includeGlobal) {
|
|
4885
|
-
roots.push(
|
|
5121
|
+
roots.push(getGlobalSkillsDir(), resolve13(homedir3(), ".gemini", "config", "skills"));
|
|
4886
5122
|
}
|
|
4887
5123
|
roots.push(...this.customRoots.map((r) => resolve13(r)));
|
|
4888
|
-
return roots.filter((r) =>
|
|
5124
|
+
return roots.filter((r) => existsSync15(r));
|
|
4889
5125
|
}
|
|
4890
5126
|
discoverSkills(cwd, options) {
|
|
4891
5127
|
return this.listSkills(cwd, options);
|
|
@@ -4901,12 +5137,12 @@ class SkillsLoader {
|
|
|
4901
5137
|
const discovered = new Map;
|
|
4902
5138
|
for (const root of roots) {
|
|
4903
5139
|
try {
|
|
4904
|
-
const entries =
|
|
5140
|
+
const entries = readdirSync5(root, { withFileTypes: true });
|
|
4905
5141
|
for (const entry of entries) {
|
|
4906
5142
|
if (entry.isDirectory()) {
|
|
4907
|
-
const skillDir =
|
|
4908
|
-
const skillFilePath =
|
|
4909
|
-
if (
|
|
5143
|
+
const skillDir = join7(root, entry.name);
|
|
5144
|
+
const skillFilePath = join7(skillDir, "SKILL.md");
|
|
5145
|
+
if (existsSync15(skillFilePath)) {
|
|
4910
5146
|
const meta = this.parseSkillFrontmatter(skillFilePath, entry.name, root, cwd);
|
|
4911
5147
|
if (meta && !discovered.has(meta.name)) {
|
|
4912
5148
|
meta.enabled = !this.isSkillDisabled(meta.name);
|
|
@@ -5029,149 +5265,288 @@ When tackling complex specialized tasks that match any of these skills, autonomo
|
|
|
5029
5265
|
}
|
|
5030
5266
|
|
|
5031
5267
|
// src/memories/store.ts
|
|
5032
|
-
import { existsSync as
|
|
5033
|
-
import { resolve as resolve14, dirname as
|
|
5034
|
-
import {
|
|
5035
|
-
|
|
5268
|
+
import { existsSync as existsSync16, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync9, readdirSync as readdirSync6 } from "fs";
|
|
5269
|
+
import { resolve as resolve14, join as join8, basename, dirname as dirname8 } from "path";
|
|
5270
|
+
import { createHash } from "crypto";
|
|
5036
5271
|
class MemoryStore {
|
|
5037
5272
|
globalPath;
|
|
5038
5273
|
customWorkspacePath;
|
|
5039
5274
|
constructor(options = {}) {
|
|
5040
|
-
this.globalPath = options.globalPath ||
|
|
5275
|
+
this.globalPath = options.globalPath || getGlobalMemoriesPath();
|
|
5041
5276
|
this.customWorkspacePath = options.workspacePath;
|
|
5042
5277
|
}
|
|
5043
|
-
|
|
5044
|
-
|
|
5278
|
+
findProjectRoot(cwd) {
|
|
5279
|
+
let current = resolve14(cwd);
|
|
5280
|
+
while (true) {
|
|
5281
|
+
if (existsSync16(join8(current, ".git"))) {
|
|
5282
|
+
return current;
|
|
5283
|
+
}
|
|
5284
|
+
const parent = dirname8(current);
|
|
5285
|
+
if (parent === current) {
|
|
5286
|
+
return resolve14(cwd);
|
|
5287
|
+
}
|
|
5288
|
+
current = parent;
|
|
5289
|
+
}
|
|
5045
5290
|
}
|
|
5046
|
-
|
|
5047
|
-
const
|
|
5048
|
-
const
|
|
5049
|
-
const
|
|
5050
|
-
|
|
5051
|
-
mkdirSync8(dir, { recursive: true });
|
|
5052
|
-
}
|
|
5053
|
-
const existingEntries = this.readMemoryFile(targetFile, scope);
|
|
5054
|
-
const normalized = params.content.trim();
|
|
5055
|
-
const duplicate = existingEntries.find((e) => e.category === params.category && e.content.toLowerCase() === normalized.toLowerCase());
|
|
5056
|
-
if (duplicate) {
|
|
5057
|
-
return duplicate;
|
|
5058
|
-
}
|
|
5059
|
-
const newEntry = {
|
|
5060
|
-
id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
|
|
5061
|
-
category: params.category,
|
|
5062
|
-
content: normalized,
|
|
5063
|
-
scope,
|
|
5064
|
-
createdAt: Date.now()
|
|
5065
|
-
};
|
|
5066
|
-
existingEntries.push(newEntry);
|
|
5067
|
-
this.writeMemoryFile(targetFile, existingEntries);
|
|
5068
|
-
return newEntry;
|
|
5291
|
+
getProjectSlug(cwd) {
|
|
5292
|
+
const root = this.findProjectRoot(cwd);
|
|
5293
|
+
const folderName = basename(root).toLowerCase().replace(/[^a-z0-9_-]/g, "-") || "project";
|
|
5294
|
+
const hash = createHash("sha256").update(resolve14(root)).digest("hex").slice(0, 6);
|
|
5295
|
+
return `${folderName}-${hash}`;
|
|
5069
5296
|
}
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5297
|
+
getProjectMemoryDir(cwd) {
|
|
5298
|
+
if (this.customWorkspacePath) {
|
|
5299
|
+
const dir2 = resolve14(this.customWorkspacePath);
|
|
5300
|
+
if (!existsSync16(dir2)) {
|
|
5301
|
+
try {
|
|
5302
|
+
mkdirSync9(dir2, { recursive: true });
|
|
5303
|
+
} catch {}
|
|
5304
|
+
}
|
|
5305
|
+
return dir2;
|
|
5306
|
+
}
|
|
5307
|
+
const slug = this.getProjectSlug(cwd);
|
|
5308
|
+
const dir = join8(getProjectsDir(), slug, "memory");
|
|
5309
|
+
if (!existsSync16(dir)) {
|
|
5310
|
+
try {
|
|
5311
|
+
mkdirSync9(dir, { recursive: true });
|
|
5312
|
+
} catch {}
|
|
5313
|
+
}
|
|
5314
|
+
return dir;
|
|
5315
|
+
}
|
|
5316
|
+
getMemoryIndexPath(cwd) {
|
|
5317
|
+
return join8(this.getProjectMemoryDir(cwd), "MEMORY.md");
|
|
5318
|
+
}
|
|
5319
|
+
normalizeCategory(raw) {
|
|
5320
|
+
const cat = raw.toLowerCase().trim();
|
|
5321
|
+
if (cat === "user" || cat === "preference")
|
|
5322
|
+
return "user";
|
|
5323
|
+
if (cat === "feedback" || cat === "guideline")
|
|
5324
|
+
return "feedback";
|
|
5325
|
+
if (cat === "project" || cat === "architecture")
|
|
5326
|
+
return "project";
|
|
5327
|
+
if (cat === "reference" || cat === "note")
|
|
5328
|
+
return "reference";
|
|
5329
|
+
return "project";
|
|
5330
|
+
}
|
|
5331
|
+
saveTopicMemory(params) {
|
|
5332
|
+
const type = this.normalizeCategory(params.category);
|
|
5333
|
+
const sanitizedName = params.name.toLowerCase().trim().replace(/[^a-z0-9_-]/g, "_").replace(/^_+|_+$/g, "") || `note_${Date.now()}`;
|
|
5334
|
+
const memoryDir = this.getProjectMemoryDir(params.cwd);
|
|
5335
|
+
const fileName = `${type}_${sanitizedName}.md`;
|
|
5336
|
+
const filePath = join8(memoryDir, fileName);
|
|
5337
|
+
const nowIso = new Date().toISOString();
|
|
5338
|
+
const cleanContent = params.content.trim();
|
|
5339
|
+
const desc = (params.description || cleanContent.split(`
|
|
5340
|
+
`)[0] || sanitizedName).replace(/[\r\n]+/g, " ");
|
|
5341
|
+
const frontmatter = [
|
|
5342
|
+
"---",
|
|
5343
|
+
`type: ${type}`,
|
|
5344
|
+
`name: ${sanitizedName}`,
|
|
5345
|
+
`description: ${desc}`,
|
|
5346
|
+
`modified: ${nowIso}`,
|
|
5347
|
+
"---",
|
|
5348
|
+
"",
|
|
5349
|
+
`# ${sanitizedName.replace(/_/g, " ").toUpperCase()}`,
|
|
5350
|
+
"",
|
|
5351
|
+
cleanContent,
|
|
5352
|
+
""
|
|
5353
|
+
].join(`
|
|
5354
|
+
`);
|
|
5355
|
+
writeFileSync5(filePath, frontmatter, "utf8");
|
|
5356
|
+
this.syncMemoryIndex(params.cwd);
|
|
5357
|
+
return {
|
|
5358
|
+
id: `mem_${sanitizedName}`,
|
|
5359
|
+
category: type,
|
|
5360
|
+
name: sanitizedName,
|
|
5361
|
+
description: desc,
|
|
5362
|
+
content: cleanContent,
|
|
5363
|
+
scope: "project",
|
|
5364
|
+
createdAt: Date.now(),
|
|
5365
|
+
modifiedAt: Date.now(),
|
|
5366
|
+
filePath
|
|
5367
|
+
};
|
|
5075
5368
|
}
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5369
|
+
readTopicMemory(topicNameOrFile, cwd) {
|
|
5370
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
5371
|
+
let targetPath = join8(memoryDir, topicNameOrFile);
|
|
5372
|
+
if (!existsSync16(targetPath)) {
|
|
5373
|
+
if (!topicNameOrFile.endsWith(".md")) {
|
|
5374
|
+
targetPath = join8(memoryDir, `${topicNameOrFile}.md`);
|
|
5375
|
+
}
|
|
5376
|
+
}
|
|
5377
|
+
if (!existsSync16(targetPath)) {
|
|
5378
|
+
const files = readdirSync6(memoryDir);
|
|
5379
|
+
const match = files.find((f) => f.includes(topicNameOrFile));
|
|
5380
|
+
if (match) {
|
|
5381
|
+
targetPath = join8(memoryDir, match);
|
|
5382
|
+
} else {
|
|
5383
|
+
return null;
|
|
5384
|
+
}
|
|
5385
|
+
}
|
|
5079
5386
|
try {
|
|
5080
|
-
const
|
|
5081
|
-
|
|
5387
|
+
const raw = readFileSync10(targetPath, "utf8");
|
|
5388
|
+
return this.parseTopicFile(raw, targetPath);
|
|
5389
|
+
} catch {
|
|
5390
|
+
return null;
|
|
5391
|
+
}
|
|
5392
|
+
}
|
|
5393
|
+
parseTopicFile(raw, filePath) {
|
|
5394
|
+
const lines = raw.split(`
|
|
5082
5395
|
`);
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5396
|
+
let inFm = false;
|
|
5397
|
+
let type = "project";
|
|
5398
|
+
let name = basename(filePath, ".md");
|
|
5399
|
+
let description;
|
|
5400
|
+
let modified = new Date().toISOString();
|
|
5401
|
+
const bodyLines = [];
|
|
5402
|
+
for (let i = 0;i < lines.length; i++) {
|
|
5403
|
+
const line = lines[i];
|
|
5404
|
+
if (i === 0 && line.trim() === "---") {
|
|
5405
|
+
inFm = true;
|
|
5406
|
+
continue;
|
|
5407
|
+
}
|
|
5408
|
+
if (inFm) {
|
|
5409
|
+
if (line.trim() === "---") {
|
|
5410
|
+
inFm = false;
|
|
5411
|
+
continue;
|
|
5412
|
+
}
|
|
5413
|
+
const colonIdx = line.indexOf(":");
|
|
5414
|
+
if (colonIdx !== -1) {
|
|
5415
|
+
const key = line.slice(0, colonIdx).trim();
|
|
5416
|
+
const val = line.slice(colonIdx + 1).trim().replace(/^["']|["']$/g, "");
|
|
5417
|
+
if (key === "type")
|
|
5418
|
+
type = this.normalizeCategory(val);
|
|
5419
|
+
else if (key === "name")
|
|
5420
|
+
name = val;
|
|
5421
|
+
else if (key === "description")
|
|
5422
|
+
description = val;
|
|
5423
|
+
else if (key === "modified")
|
|
5424
|
+
modified = val;
|
|
5106
5425
|
}
|
|
5426
|
+
} else {
|
|
5427
|
+
bodyLines.push(line);
|
|
5107
5428
|
}
|
|
5108
|
-
return entries;
|
|
5109
|
-
} catch {
|
|
5110
|
-
return [];
|
|
5111
5429
|
}
|
|
5112
|
-
|
|
5113
|
-
|
|
5114
|
-
|
|
5115
|
-
|
|
5116
|
-
|
|
5117
|
-
|
|
5118
|
-
|
|
5430
|
+
return {
|
|
5431
|
+
type,
|
|
5432
|
+
name,
|
|
5433
|
+
description,
|
|
5434
|
+
modified,
|
|
5435
|
+
content: bodyLines.join(`
|
|
5436
|
+
`).trim(),
|
|
5437
|
+
filePath
|
|
5119
5438
|
};
|
|
5120
|
-
|
|
5121
|
-
|
|
5439
|
+
}
|
|
5440
|
+
syncMemoryIndex(cwd) {
|
|
5441
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
5442
|
+
const indexPath = join8(memoryDir, "MEMORY.md");
|
|
5443
|
+
const files = existsSync16(memoryDir) ? readdirSync6(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md") : [];
|
|
5444
|
+
const items = [];
|
|
5445
|
+
for (const f of files) {
|
|
5446
|
+
try {
|
|
5447
|
+
const full = join8(memoryDir, f);
|
|
5448
|
+
const parsed = this.parseTopicFile(readFileSync10(full, "utf8"), full);
|
|
5449
|
+
items.push({
|
|
5450
|
+
type: parsed.type,
|
|
5451
|
+
name: parsed.name,
|
|
5452
|
+
desc: parsed.description || parsed.content.split(`
|
|
5453
|
+
`)[0] || parsed.name,
|
|
5454
|
+
file: f
|
|
5455
|
+
});
|
|
5456
|
+
} catch {}
|
|
5122
5457
|
}
|
|
5123
|
-
|
|
5124
|
-
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
|
|
5131
|
-
`;
|
|
5458
|
+
const indexLines = [
|
|
5459
|
+
"# Project Auto-Memory Index",
|
|
5460
|
+
"",
|
|
5461
|
+
"This index is loaded at session startup. Detailed topics can be retrieved via read_memory tool.",
|
|
5462
|
+
""
|
|
5463
|
+
];
|
|
5464
|
+
for (const item of items) {
|
|
5465
|
+
indexLines.push(`- [${item.type}] **${item.name}**: ${item.desc} (topic: ${item.file})`);
|
|
5132
5466
|
}
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
|
|
5136
|
-
|
|
5137
|
-
|
|
5138
|
-
|
|
5467
|
+
const boundedLines = indexLines.slice(0, 200);
|
|
5468
|
+
writeFileSync5(indexPath, boundedLines.join(`
|
|
5469
|
+
`) + `
|
|
5470
|
+
`, "utf8");
|
|
5471
|
+
}
|
|
5472
|
+
loadMemoryIndex(cwd) {
|
|
5473
|
+
const indexPath = this.getMemoryIndexPath(cwd);
|
|
5474
|
+
if (!existsSync16(indexPath))
|
|
5475
|
+
return "";
|
|
5476
|
+
try {
|
|
5477
|
+
const raw = readFileSync10(indexPath, "utf8");
|
|
5478
|
+
const byteLimit = 25 * 1024;
|
|
5479
|
+
const sliced = raw.length > byteLimit ? raw.slice(0, byteLimit) : raw;
|
|
5480
|
+
const lines = sliced.split(`
|
|
5481
|
+
`).slice(0, 200);
|
|
5482
|
+
return lines.join(`
|
|
5483
|
+
`).trim();
|
|
5484
|
+
} catch {
|
|
5485
|
+
return "";
|
|
5139
5486
|
}
|
|
5140
|
-
|
|
5141
|
-
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5487
|
+
}
|
|
5488
|
+
listProjectMemories(cwd) {
|
|
5489
|
+
const memoryDir = this.getProjectMemoryDir(cwd);
|
|
5490
|
+
if (!existsSync16(memoryDir))
|
|
5491
|
+
return [];
|
|
5492
|
+
const files = readdirSync6(memoryDir).filter((f) => f.endsWith(".md") && f !== "MEMORY.md");
|
|
5493
|
+
const list = [];
|
|
5494
|
+
for (const f of files) {
|
|
5495
|
+
try {
|
|
5496
|
+
const full = join8(memoryDir, f);
|
|
5497
|
+
list.push(this.parseTopicFile(readFileSync10(full, "utf8"), full));
|
|
5498
|
+
} catch {}
|
|
5146
5499
|
}
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
`;
|
|
5500
|
+
return list;
|
|
5501
|
+
}
|
|
5502
|
+
addMemory(params) {
|
|
5503
|
+
const cwd = params.cwd || process.cwd();
|
|
5504
|
+
const type = this.normalizeCategory(params.category);
|
|
5505
|
+
const name = params.name || `${type}_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 6)}`;
|
|
5506
|
+
const entry = this.saveTopicMemory({
|
|
5507
|
+
category: type,
|
|
5508
|
+
name,
|
|
5509
|
+
content: params.content,
|
|
5510
|
+
cwd
|
|
5511
|
+
});
|
|
5512
|
+
if (params.scope) {
|
|
5513
|
+
entry.scope = params.scope;
|
|
5153
5514
|
}
|
|
5154
|
-
|
|
5155
|
-
|
|
5515
|
+
return entry;
|
|
5516
|
+
}
|
|
5517
|
+
getAllMemories(cwd) {
|
|
5518
|
+
const projectTopics = this.listProjectMemories(cwd).map((t) => ({
|
|
5519
|
+
id: `mem_${t.name}`,
|
|
5520
|
+
category: t.type,
|
|
5521
|
+
name: t.name,
|
|
5522
|
+
description: t.description,
|
|
5523
|
+
content: t.content,
|
|
5524
|
+
scope: t.type === "user" ? "global" : "workspace",
|
|
5525
|
+
createdAt: new Date(t.modified).getTime() || Date.now(),
|
|
5526
|
+
filePath: t.filePath
|
|
5527
|
+
}));
|
|
5528
|
+
return projectTopics;
|
|
5156
5529
|
}
|
|
5157
5530
|
formatMemoriesPrompt(cwd) {
|
|
5158
|
-
const
|
|
5159
|
-
if (
|
|
5531
|
+
const indexContent = this.loadMemoryIndex(cwd);
|
|
5532
|
+
if (!indexContent)
|
|
5160
5533
|
return "";
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
##
|
|
5164
|
-
<
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5534
|
+
return [
|
|
5535
|
+
"",
|
|
5536
|
+
"## Project Auto-Memory (Persistent Learnings)",
|
|
5537
|
+
"<auto_memory>",
|
|
5538
|
+
indexContent,
|
|
5539
|
+
"</auto_memory>",
|
|
5540
|
+
"Apply these persistent project learnings, user preferences, and feedback across all tasks.",
|
|
5541
|
+
"If more context is needed for a specific topic, retrieve it using the `read_memory` tool."
|
|
5542
|
+
].join(`
|
|
5543
|
+
`);
|
|
5169
5544
|
}
|
|
5170
5545
|
}
|
|
5171
5546
|
|
|
5172
5547
|
// src/worktree/manager.ts
|
|
5173
|
-
import { resolve as resolve16, join as
|
|
5174
|
-
import { existsSync as
|
|
5548
|
+
import { resolve as resolve16, join as join9 } from "path";
|
|
5549
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync10, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
|
|
5175
5550
|
|
|
5176
5551
|
// src/worktree/git.ts
|
|
5177
5552
|
import { resolve as resolve15 } from "path";
|
|
@@ -5321,15 +5696,15 @@ class WorktreeManager {
|
|
|
5321
5696
|
const branchName = options.branch || `groupy/${taskId}`;
|
|
5322
5697
|
const targetDir = options.worktreePath || (this.baseStorageDir ? resolve16(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve16(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
|
|
5323
5698
|
const worktreeParent = resolve16(targetDir, "..");
|
|
5324
|
-
if (!
|
|
5325
|
-
|
|
5699
|
+
if (!existsSync17(worktreeParent)) {
|
|
5700
|
+
mkdirSync10(worktreeParent, { recursive: true });
|
|
5326
5701
|
}
|
|
5327
5702
|
const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
|
|
5328
5703
|
const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
|
|
5329
5704
|
if (!result.success) {
|
|
5330
5705
|
throw new Error(`Failed to create git worktree: ${result.error}`);
|
|
5331
5706
|
}
|
|
5332
|
-
const metaPath =
|
|
5707
|
+
const metaPath = join9(targetDir, "groupy-thread.json");
|
|
5333
5708
|
try {
|
|
5334
5709
|
writeFileSync6(metaPath, JSON.stringify({
|
|
5335
5710
|
version: 1,
|
|
@@ -5355,8 +5730,8 @@ class WorktreeManager {
|
|
|
5355
5730
|
return [];
|
|
5356
5731
|
const worktrees = await listWorktreesGit(repoRoot);
|
|
5357
5732
|
return worktrees.map((wt) => {
|
|
5358
|
-
const metaPath =
|
|
5359
|
-
if (
|
|
5733
|
+
const metaPath = join9(wt.path, "groupy-thread.json");
|
|
5734
|
+
if (existsSync17(metaPath)) {
|
|
5360
5735
|
try {
|
|
5361
5736
|
const raw = JSON.parse(readFileSync11(metaPath, "utf8"));
|
|
5362
5737
|
return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
|
|
@@ -5434,7 +5809,7 @@ class WorktreeManager {
|
|
|
5434
5809
|
}
|
|
5435
5810
|
}
|
|
5436
5811
|
// src/auth/oauth.ts
|
|
5437
|
-
import { randomBytes, createHash } from "crypto";
|
|
5812
|
+
import { randomBytes, createHash as createHash2 } from "crypto";
|
|
5438
5813
|
import { exec } from "child_process";
|
|
5439
5814
|
class AuthClient {
|
|
5440
5815
|
store;
|
|
@@ -5587,7 +5962,7 @@ class AuthClient {
|
|
|
5587
5962
|
return randomBytes(32).toString("base64url").replace(/[^a-zA-Z0-9]/g, "").slice(0, 64);
|
|
5588
5963
|
}
|
|
5589
5964
|
generateCodeChallenge(verifier) {
|
|
5590
|
-
return
|
|
5965
|
+
return createHash2("sha256").update(verifier).digest("base64url");
|
|
5591
5966
|
}
|
|
5592
5967
|
}
|
|
5593
5968
|
// src/cli/ui/colors.ts
|
|
@@ -6054,7 +6429,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
|
|
|
6054
6429
|
// package.json
|
|
6055
6430
|
var package_default = {
|
|
6056
6431
|
name: "@pikaa-ai/pikaa",
|
|
6057
|
-
version: "0.3.
|
|
6432
|
+
version: "0.3.16",
|
|
6058
6433
|
description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
|
|
6059
6434
|
main: "./dist/index.js",
|
|
6060
6435
|
module: "./dist/index.js",
|
|
@@ -6132,7 +6507,7 @@ function getCliVersion(options = {}) {
|
|
|
6132
6507
|
}
|
|
6133
6508
|
|
|
6134
6509
|
// src/cli/ui/animation/banner-animation.ts
|
|
6135
|
-
import { homedir as
|
|
6510
|
+
import { homedir as homedir4 } from "os";
|
|
6136
6511
|
import { execSync } from "child_process";
|
|
6137
6512
|
var ROSE = "\x1B[38;2;205;105;74m";
|
|
6138
6513
|
var ROSE_DIM = "\x1B[38;2;120;60;45m";
|
|
@@ -6142,7 +6517,7 @@ var BOLD = "\x1B[1m";
|
|
|
6142
6517
|
var ITALIC = "\x1B[3m";
|
|
6143
6518
|
var RESET = "\x1B[0m";
|
|
6144
6519
|
function shortenPath(cwd) {
|
|
6145
|
-
const home =
|
|
6520
|
+
const home = homedir4();
|
|
6146
6521
|
if (cwd.startsWith(home)) {
|
|
6147
6522
|
return `~${cwd.slice(home.length).replace(/\\/g, "/")}`;
|
|
6148
6523
|
}
|
|
@@ -6690,8 +7065,8 @@ class InteractiveLineEditor {
|
|
|
6690
7065
|
let cursor = 0;
|
|
6691
7066
|
let selectedIndex = 0;
|
|
6692
7067
|
let scrollTop = 0;
|
|
6693
|
-
let renderedMenuLines = 0;
|
|
6694
7068
|
let popupDismissed = false;
|
|
7069
|
+
let lastCursorRowFromTop = 0;
|
|
6695
7070
|
if (process.stdin.isTTY) {
|
|
6696
7071
|
try {
|
|
6697
7072
|
process.stdin.setRawMode(true);
|
|
@@ -6754,20 +7129,21 @@ class InteractiveLineEditor {
|
|
|
6754
7129
|
if (scrollTop > maxScroll)
|
|
6755
7130
|
scrollTop = maxScroll;
|
|
6756
7131
|
};
|
|
6757
|
-
const clearMenu = () => {
|
|
6758
|
-
if (renderedMenuLines > 0) {
|
|
6759
|
-
process.stdout.write("\x1B[J");
|
|
6760
|
-
renderedMenuLines = 0;
|
|
6761
|
-
}
|
|
6762
|
-
};
|
|
6763
7132
|
const redraw = () => {
|
|
6764
|
-
|
|
6765
|
-
|
|
7133
|
+
const termCols = getTerminalCols();
|
|
7134
|
+
const visiblePromptWidth = this.promptSymbol.replace(/\x1b\[[0-9;]*m/g, "").length;
|
|
7135
|
+
if (lastCursorRowFromTop > 0) {
|
|
7136
|
+
process.stdout.write(`\x1B[${lastCursorRowFromTop}A`);
|
|
7137
|
+
}
|
|
7138
|
+
process.stdout.write("\r\x1B[J");
|
|
7139
|
+
process.stdout.write(`${this.promptSymbol}${buffer}`);
|
|
7140
|
+
const totalPromptChars = visiblePromptWidth + buffer.length;
|
|
7141
|
+
const promptRows = Math.max(1, Math.floor(totalPromptChars / termCols) + 1);
|
|
6766
7142
|
const slashMatches = getMatchingCommands();
|
|
6767
7143
|
const activeFile = getActiveFileQuery();
|
|
6768
7144
|
const fileMatches = activeFile ? getMatchingFiles(activeFile.query) : [];
|
|
7145
|
+
let menuRows = 0;
|
|
6769
7146
|
if (buffer.startsWith("/") && !popupDismissed && slashMatches.length > 0) {
|
|
6770
|
-
const termCols = getTerminalCols();
|
|
6771
7147
|
const BOX_WIDTH = Math.max(20, Math.min(termCols - 4, 120));
|
|
6772
7148
|
const maxVisible = Math.min(slashMatches.length, 7);
|
|
6773
7149
|
ensureVisible(slashMatches.length, maxVisible);
|
|
@@ -6798,10 +7174,8 @@ class InteractiveLineEditor {
|
|
|
6798
7174
|
process.stdout.write(`
|
|
6799
7175
|
\x1B[2K${line}`);
|
|
6800
7176
|
}
|
|
6801
|
-
|
|
6802
|
-
process.stdout.write(`\x1B[${renderedMenuLines}A`);
|
|
7177
|
+
menuRows = menuLines.length;
|
|
6803
7178
|
} else if (activeFile && !popupDismissed && fileMatches.length > 0) {
|
|
6804
|
-
const termCols = getTerminalCols();
|
|
6805
7179
|
const BOX_WIDTH = Math.max(20, Math.min(termCols - 4, 120));
|
|
6806
7180
|
const maxVisible = Math.min(fileMatches.length, 7);
|
|
6807
7181
|
ensureVisible(fileMatches.length, maxVisible);
|
|
@@ -6837,8 +7211,7 @@ class InteractiveLineEditor {
|
|
|
6837
7211
|
process.stdout.write(`
|
|
6838
7212
|
\x1B[2K${line}`);
|
|
6839
7213
|
}
|
|
6840
|
-
|
|
6841
|
-
process.stdout.write(`\x1B[${renderedMenuLines}A`);
|
|
7214
|
+
menuRows = menuLines.length;
|
|
6842
7215
|
} else {
|
|
6843
7216
|
const RULE_COLOR2 = "\x1B[38;2;60;60;68m";
|
|
6844
7217
|
const bottomRule = ` ${RULE_COLOR2}${getRule()}\x1B[0m`;
|
|
@@ -6846,16 +7219,20 @@ class InteractiveLineEditor {
|
|
|
6846
7219
|
process.stdout.write(`
|
|
6847
7220
|
\x1B[2K${bottomRule}
|
|
6848
7221
|
\x1B[2K${modeLine}`);
|
|
6849
|
-
|
|
6850
|
-
|
|
7222
|
+
menuRows = 2;
|
|
7223
|
+
}
|
|
7224
|
+
const cursorCharsFromTop = visiblePromptWidth + cursor;
|
|
7225
|
+
const cursorRow = Math.floor(cursorCharsFromTop / termCols);
|
|
7226
|
+
const cursorCol = cursorCharsFromTop % termCols;
|
|
7227
|
+
const moveUpRows = promptRows - 1 - cursorRow + menuRows;
|
|
7228
|
+
if (moveUpRows > 0) {
|
|
7229
|
+
process.stdout.write(`\x1B[${moveUpRows}A`);
|
|
6851
7230
|
}
|
|
6852
|
-
|
|
6853
|
-
const cursorCol = visiblePromptLength + cursor;
|
|
7231
|
+
process.stdout.write("\r");
|
|
6854
7232
|
if (cursorCol > 0) {
|
|
6855
|
-
process.stdout.write(`\
|
|
6856
|
-
} else {
|
|
6857
|
-
process.stdout.write("\r");
|
|
7233
|
+
process.stdout.write(`\x1B[${cursorCol}C`);
|
|
6858
7234
|
}
|
|
7235
|
+
lastCursorRowFromTop = cursorRow;
|
|
6859
7236
|
};
|
|
6860
7237
|
const onResize = () => {
|
|
6861
7238
|
redraw();
|
|
@@ -6867,19 +7244,22 @@ class InteractiveLineEditor {
|
|
|
6867
7244
|
if (process.stdout && typeof process.stdout.removeListener === "function") {
|
|
6868
7245
|
process.stdout.removeListener("resize", onResize);
|
|
6869
7246
|
}
|
|
6870
|
-
clearMenu();
|
|
6871
7247
|
process.stdin.removeListener("keypress", onKeypress);
|
|
6872
7248
|
if (process.stdin.isTTY) {
|
|
6873
7249
|
try {
|
|
6874
7250
|
process.stdin.setRawMode(false);
|
|
6875
7251
|
} catch {}
|
|
6876
7252
|
}
|
|
7253
|
+
if (lastCursorRowFromTop > 0) {
|
|
7254
|
+
process.stdout.write(`\x1B[${lastCursorRowFromTop}A`);
|
|
7255
|
+
}
|
|
7256
|
+
process.stdout.write("\r\x1B[J");
|
|
6877
7257
|
if (result.trim().length > 0 && !result.startsWith("/")) {
|
|
6878
|
-
process.stdout.write(
|
|
7258
|
+
process.stdout.write(`${CliFormatter.formatClaudeUserPrompt(result)}
|
|
6879
7259
|
|
|
6880
7260
|
`);
|
|
6881
7261
|
} else {
|
|
6882
|
-
process.stdout.write(
|
|
7262
|
+
process.stdout.write(`
|
|
6883
7263
|
`);
|
|
6884
7264
|
}
|
|
6885
7265
|
resolve17(result);
|
|
@@ -6900,7 +7280,10 @@ class InteractiveLineEditor {
|
|
|
6900
7280
|
if (process.stdout && typeof process.stdout.removeListener === "function") {
|
|
6901
7281
|
process.stdout.removeListener("resize", onResize);
|
|
6902
7282
|
}
|
|
6903
|
-
|
|
7283
|
+
if (lastCursorRowFromTop > 0) {
|
|
7284
|
+
process.stdout.write(`\x1B[${lastCursorRowFromTop}A`);
|
|
7285
|
+
}
|
|
7286
|
+
process.stdout.write("\r\x1B[J");
|
|
6904
7287
|
if (process.stdin.isTTY) {
|
|
6905
7288
|
try {
|
|
6906
7289
|
process.stdin.setRawMode(false);
|
|
@@ -6915,12 +7298,9 @@ class InteractiveLineEditor {
|
|
|
6915
7298
|
process.exit(0);
|
|
6916
7299
|
}
|
|
6917
7300
|
if (key.name === "escape") {
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6921
|
-
redraw();
|
|
6922
|
-
return;
|
|
6923
|
-
}
|
|
7301
|
+
popupDismissed = true;
|
|
7302
|
+
redraw();
|
|
7303
|
+
return;
|
|
6924
7304
|
}
|
|
6925
7305
|
if (key.ctrl && key.name === "d") {
|
|
6926
7306
|
cleanupAndResolve("/exit");
|
|
@@ -7520,8 +7900,8 @@ async function promptInteractiveList(config) {
|
|
|
7520
7900
|
}
|
|
7521
7901
|
|
|
7522
7902
|
// src/security/scanner.ts
|
|
7523
|
-
import { existsSync as
|
|
7524
|
-
import { join as
|
|
7903
|
+
import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
|
|
7904
|
+
import { join as join10, relative as relative2, resolve as resolve17 } from "path";
|
|
7525
7905
|
var SECURITY_RULES = [
|
|
7526
7906
|
{
|
|
7527
7907
|
id: "SEC-001",
|
|
@@ -7659,21 +8039,21 @@ async function runSecurityScan(targetDir, options = {}) {
|
|
|
7659
8039
|
const findings = [];
|
|
7660
8040
|
let scannedCount = 0;
|
|
7661
8041
|
function walk(current) {
|
|
7662
|
-
if (scannedCount >= maxFiles || !
|
|
8042
|
+
if (scannedCount >= maxFiles || !existsSync18(current))
|
|
7663
8043
|
return;
|
|
7664
8044
|
let entries;
|
|
7665
8045
|
try {
|
|
7666
|
-
entries =
|
|
8046
|
+
entries = readdirSync7(current);
|
|
7667
8047
|
} catch {
|
|
7668
8048
|
return;
|
|
7669
8049
|
}
|
|
7670
8050
|
for (const entry of entries) {
|
|
7671
8051
|
if (scannedCount >= maxFiles)
|
|
7672
8052
|
break;
|
|
7673
|
-
const fullPath =
|
|
8053
|
+
const fullPath = join10(current, entry);
|
|
7674
8054
|
let stat;
|
|
7675
8055
|
try {
|
|
7676
|
-
stat =
|
|
8056
|
+
stat = statSync4(fullPath);
|
|
7677
8057
|
} catch {
|
|
7678
8058
|
continue;
|
|
7679
8059
|
}
|
|
@@ -7778,8 +8158,8 @@ var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
|
|
|
7778
8158
|
var SQLITE_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
|
|
7779
8159
|
|
|
7780
8160
|
// src/init/project-analyzer.ts
|
|
7781
|
-
import { existsSync as
|
|
7782
|
-
import { join as
|
|
8161
|
+
import { existsSync as existsSync19, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
|
|
8162
|
+
import { join as join11, basename as basename3 } from "path";
|
|
7783
8163
|
|
|
7784
8164
|
class ProjectAnalyzer {
|
|
7785
8165
|
cwd;
|
|
@@ -7797,8 +8177,8 @@ class ProjectAnalyzer {
|
|
|
7797
8177
|
const architectureNotes = [];
|
|
7798
8178
|
const codeConventions = [];
|
|
7799
8179
|
let description = readmeInfo.description;
|
|
7800
|
-
const pkgPath =
|
|
7801
|
-
if (
|
|
8180
|
+
const pkgPath = join11(this.cwd, "package.json");
|
|
8181
|
+
if (existsSync19(pkgPath)) {
|
|
7802
8182
|
try {
|
|
7803
8183
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
7804
8184
|
if (!description && pkg.description)
|
|
@@ -7871,8 +8251,8 @@ class ProjectAnalyzer {
|
|
|
7871
8251
|
}
|
|
7872
8252
|
} catch {}
|
|
7873
8253
|
}
|
|
7874
|
-
const tsconfigPath =
|
|
7875
|
-
if (
|
|
8254
|
+
const tsconfigPath = join11(this.cwd, "tsconfig.json");
|
|
8255
|
+
if (existsSync19(tsconfigPath)) {
|
|
7876
8256
|
try {
|
|
7877
8257
|
const tsconfig = JSON.parse(readFileSync13(tsconfigPath, "utf8"));
|
|
7878
8258
|
if (tsconfig.compilerOptions?.strict) {
|
|
@@ -7883,8 +8263,8 @@ class ProjectAnalyzer {
|
|
|
7883
8263
|
}
|
|
7884
8264
|
} catch {}
|
|
7885
8265
|
}
|
|
7886
|
-
const cargoPath =
|
|
7887
|
-
if (
|
|
8266
|
+
const cargoPath = join11(this.cwd, "Cargo.toml");
|
|
8267
|
+
if (existsSync19(cargoPath)) {
|
|
7888
8268
|
try {
|
|
7889
8269
|
commands.dev = commands.dev || "cargo run";
|
|
7890
8270
|
commands.build = commands.build || "cargo build";
|
|
@@ -7893,8 +8273,8 @@ class ProjectAnalyzer {
|
|
|
7893
8273
|
frameworks.push("Rust Cargo");
|
|
7894
8274
|
} catch {}
|
|
7895
8275
|
}
|
|
7896
|
-
const goModPath =
|
|
7897
|
-
if (
|
|
8276
|
+
const goModPath = join11(this.cwd, "go.mod");
|
|
8277
|
+
if (existsSync19(goModPath)) {
|
|
7898
8278
|
try {
|
|
7899
8279
|
commands.dev = commands.dev || "go run .";
|
|
7900
8280
|
commands.build = commands.build || "go build ./...";
|
|
@@ -7903,37 +8283,37 @@ class ProjectAnalyzer {
|
|
|
7903
8283
|
frameworks.push("Go Modules");
|
|
7904
8284
|
} catch {}
|
|
7905
8285
|
}
|
|
7906
|
-
const pyprojectPath =
|
|
7907
|
-
const requirementsPath =
|
|
7908
|
-
if (
|
|
8286
|
+
const pyprojectPath = join11(this.cwd, "pyproject.toml");
|
|
8287
|
+
const requirementsPath = join11(this.cwd, "requirements.txt");
|
|
8288
|
+
if (existsSync19(pyprojectPath) || existsSync19(requirementsPath)) {
|
|
7909
8289
|
commands.test = commands.test || "pytest";
|
|
7910
8290
|
commands.lint = commands.lint || "ruff check .";
|
|
7911
|
-
if (
|
|
8291
|
+
if (existsSync19(join11(this.cwd, "uv.lock"))) {
|
|
7912
8292
|
frameworks.push("uv");
|
|
7913
8293
|
commands.test = "uv run pytest";
|
|
7914
|
-
} else if (
|
|
8294
|
+
} else if (existsSync19(join11(this.cwd, "poetry.lock"))) {
|
|
7915
8295
|
frameworks.push("Poetry");
|
|
7916
8296
|
commands.test = "poetry run pytest";
|
|
7917
8297
|
}
|
|
7918
8298
|
}
|
|
7919
|
-
if (
|
|
8299
|
+
if (existsSync19(join11(this.cwd, "Dockerfile"))) {
|
|
7920
8300
|
infrastructure.push("Docker");
|
|
7921
8301
|
const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
|
|
7922
8302
|
commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
|
|
7923
8303
|
}
|
|
7924
|
-
if (
|
|
8304
|
+
if (existsSync19(join11(this.cwd, "nginx.conf"))) {
|
|
7925
8305
|
infrastructure.push("Nginx");
|
|
7926
8306
|
}
|
|
7927
|
-
if (
|
|
8307
|
+
if (existsSync19(join11(this.cwd, "src/api.ts")) || existsSync19(join11(this.cwd, "src/api"))) {
|
|
7928
8308
|
architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
|
|
7929
8309
|
}
|
|
7930
|
-
if (
|
|
8310
|
+
if (existsSync19(join11(this.cwd, "src/components"))) {
|
|
7931
8311
|
architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
|
|
7932
8312
|
}
|
|
7933
|
-
if (
|
|
8313
|
+
if (existsSync19(join11(this.cwd, "src/types.ts")) || existsSync19(join11(this.cwd, "src/types"))) {
|
|
7934
8314
|
architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
|
|
7935
8315
|
}
|
|
7936
|
-
if (
|
|
8316
|
+
if (existsSync19(join11(this.cwd, ".env.example"))) {
|
|
7937
8317
|
architectureNotes.push("Environment configuration template is in `.env.example`.");
|
|
7938
8318
|
}
|
|
7939
8319
|
if (commands.typecheck || commands.lint || commands.test) {
|
|
@@ -7950,7 +8330,7 @@ class ProjectAnalyzer {
|
|
|
7950
8330
|
let hasExistingInstructions = false;
|
|
7951
8331
|
let existingInstructionFile;
|
|
7952
8332
|
for (const f of instructionFiles) {
|
|
7953
|
-
if (
|
|
8333
|
+
if (existsSync19(join11(this.cwd, f))) {
|
|
7954
8334
|
hasExistingInstructions = true;
|
|
7955
8335
|
existingInstructionFile = f;
|
|
7956
8336
|
break;
|
|
@@ -8031,8 +8411,8 @@ class ProjectAnalyzer {
|
|
|
8031
8411
|
extractReadmeMetadata() {
|
|
8032
8412
|
const readmeFiles = ["README.md", "readme.md", "README.MD"];
|
|
8033
8413
|
for (const file of readmeFiles) {
|
|
8034
|
-
const fullPath =
|
|
8035
|
-
if (
|
|
8414
|
+
const fullPath = join11(this.cwd, file);
|
|
8415
|
+
if (existsSync19(fullPath)) {
|
|
8036
8416
|
try {
|
|
8037
8417
|
const content = readFileSync13(fullPath, "utf8");
|
|
8038
8418
|
const lines = content.split(`
|
|
@@ -8057,8 +8437,8 @@ class ProjectAnalyzer {
|
|
|
8057
8437
|
return {};
|
|
8058
8438
|
}
|
|
8059
8439
|
detectProjectName() {
|
|
8060
|
-
const pkgPath =
|
|
8061
|
-
if (
|
|
8440
|
+
const pkgPath = join11(this.cwd, "package.json");
|
|
8441
|
+
if (existsSync19(pkgPath)) {
|
|
8062
8442
|
try {
|
|
8063
8443
|
const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
|
|
8064
8444
|
if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
|
|
@@ -8066,73 +8446,73 @@ class ProjectAnalyzer {
|
|
|
8066
8446
|
}
|
|
8067
8447
|
} catch {}
|
|
8068
8448
|
}
|
|
8069
|
-
const cargoPath =
|
|
8070
|
-
if (
|
|
8449
|
+
const cargoPath = join11(this.cwd, "Cargo.toml");
|
|
8450
|
+
if (existsSync19(cargoPath)) {
|
|
8071
8451
|
try {
|
|
8072
8452
|
const match = readFileSync13(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
|
|
8073
8453
|
if (match?.[1])
|
|
8074
8454
|
return match[1];
|
|
8075
8455
|
} catch {}
|
|
8076
8456
|
}
|
|
8077
|
-
const goModPath =
|
|
8078
|
-
if (
|
|
8457
|
+
const goModPath = join11(this.cwd, "go.mod");
|
|
8458
|
+
if (existsSync19(goModPath)) {
|
|
8079
8459
|
try {
|
|
8080
8460
|
const match = readFileSync13(goModPath, "utf8").match(/module\s+([^\s]+)/);
|
|
8081
8461
|
if (match?.[1])
|
|
8082
|
-
return
|
|
8462
|
+
return basename3(match[1]);
|
|
8083
8463
|
} catch {}
|
|
8084
8464
|
}
|
|
8085
|
-
return
|
|
8465
|
+
return basename3(this.cwd);
|
|
8086
8466
|
}
|
|
8087
8467
|
detectLanguages() {
|
|
8088
8468
|
const langs = new Set;
|
|
8089
|
-
if (
|
|
8469
|
+
if (existsSync19(join11(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
|
|
8090
8470
|
langs.add("TypeScript");
|
|
8091
8471
|
}
|
|
8092
|
-
if (
|
|
8472
|
+
if (existsSync19(join11(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
|
|
8093
8473
|
langs.add("JavaScript");
|
|
8094
8474
|
}
|
|
8095
|
-
if (
|
|
8475
|
+
if (existsSync19(join11(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
|
|
8096
8476
|
langs.add("Rust");
|
|
8097
8477
|
}
|
|
8098
|
-
if (
|
|
8478
|
+
if (existsSync19(join11(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
|
|
8099
8479
|
langs.add("Go");
|
|
8100
8480
|
}
|
|
8101
|
-
if (
|
|
8481
|
+
if (existsSync19(join11(this.cwd, "pyproject.toml")) || existsSync19(join11(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
|
|
8102
8482
|
langs.add("Python");
|
|
8103
8483
|
}
|
|
8104
|
-
if (
|
|
8484
|
+
if (existsSync19(join11(this.cwd, "pom.xml")) || existsSync19(join11(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
|
|
8105
8485
|
langs.add("Java");
|
|
8106
8486
|
}
|
|
8107
|
-
if (
|
|
8487
|
+
if (existsSync19(join11(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
|
|
8108
8488
|
langs.add("C/C++");
|
|
8109
8489
|
}
|
|
8110
8490
|
return Array.from(langs);
|
|
8111
8491
|
}
|
|
8112
8492
|
detectPackageManager() {
|
|
8113
|
-
if (
|
|
8493
|
+
if (existsSync19(join11(this.cwd, "bun.lockb")) || existsSync19(join11(this.cwd, "bun.lock")))
|
|
8114
8494
|
return "bun";
|
|
8115
|
-
if (
|
|
8495
|
+
if (existsSync19(join11(this.cwd, "pnpm-lock.yaml")))
|
|
8116
8496
|
return "pnpm";
|
|
8117
|
-
if (
|
|
8497
|
+
if (existsSync19(join11(this.cwd, "yarn.lock")))
|
|
8118
8498
|
return "yarn";
|
|
8119
|
-
if (
|
|
8499
|
+
if (existsSync19(join11(this.cwd, "package-lock.json")))
|
|
8120
8500
|
return "npm";
|
|
8121
|
-
if (
|
|
8501
|
+
if (existsSync19(join11(this.cwd, "Cargo.lock")) || existsSync19(join11(this.cwd, "Cargo.toml")))
|
|
8122
8502
|
return "cargo";
|
|
8123
|
-
if (
|
|
8503
|
+
if (existsSync19(join11(this.cwd, "uv.lock")))
|
|
8124
8504
|
return "uv";
|
|
8125
|
-
if (
|
|
8505
|
+
if (existsSync19(join11(this.cwd, "poetry.lock")))
|
|
8126
8506
|
return "poetry";
|
|
8127
|
-
if (
|
|
8507
|
+
if (existsSync19(join11(this.cwd, "go.sum")) || existsSync19(join11(this.cwd, "go.mod")))
|
|
8128
8508
|
return "go";
|
|
8129
|
-
if (
|
|
8509
|
+
if (existsSync19(join11(this.cwd, "package.json")))
|
|
8130
8510
|
return "npm";
|
|
8131
8511
|
return;
|
|
8132
8512
|
}
|
|
8133
8513
|
hasFileWithExtension(...exts) {
|
|
8134
8514
|
try {
|
|
8135
|
-
const entries =
|
|
8515
|
+
const entries = readdirSync8(this.cwd);
|
|
8136
8516
|
return entries.some((e) => exts.some((ext) => e.endsWith(ext)));
|
|
8137
8517
|
} catch {
|
|
8138
8518
|
return false;
|
|
@@ -8140,16 +8520,16 @@ class ProjectAnalyzer {
|
|
|
8140
8520
|
}
|
|
8141
8521
|
}
|
|
8142
8522
|
// src/init/init-command.ts
|
|
8143
|
-
import { existsSync as
|
|
8144
|
-
import { join as
|
|
8523
|
+
import { existsSync as existsSync20, writeFileSync as writeFileSync7 } from "fs";
|
|
8524
|
+
import { join as join12 } from "path";
|
|
8145
8525
|
function runProjectInit(options = {}) {
|
|
8146
8526
|
const cwd = options.cwd || process.cwd();
|
|
8147
8527
|
const filename = options.filename || "AGENTS.md";
|
|
8148
|
-
const targetPath =
|
|
8528
|
+
const targetPath = join12(cwd, filename);
|
|
8149
8529
|
const analyzer = new ProjectAnalyzer(cwd);
|
|
8150
8530
|
const analysis = analyzer.analyze();
|
|
8151
8531
|
const content = analyzer.generateAgentsMarkdown(analysis);
|
|
8152
|
-
const alreadyExists =
|
|
8532
|
+
const alreadyExists = existsSync20(targetPath);
|
|
8153
8533
|
writeFileSync7(targetPath, content, "utf8");
|
|
8154
8534
|
return {
|
|
8155
8535
|
success: true,
|
|
@@ -8524,7 +8904,7 @@ Browser authorization opened automatically. If not, open:`);
|
|
|
8524
8904
|
console.log(style.dim(`Waiting for authorization callback on port 1455 ...`));
|
|
8525
8905
|
const creds = await waitForToken();
|
|
8526
8906
|
console.log(style.green(`
|
|
8527
|
-
\u2713 Authentication Successful! Token saved to ~/.
|
|
8907
|
+
\u2713 Authentication Successful! Token saved to ~/.pikaa/credentials.json`));
|
|
8528
8908
|
console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
|
|
8529
8909
|
} catch (err) {
|
|
8530
8910
|
console.log(style.yellow(`
|
|
@@ -8542,7 +8922,7 @@ Falling back to Direct Terminal Login:`));
|
|
|
8542
8922
|
password
|
|
8543
8923
|
});
|
|
8544
8924
|
console.log(style.green(`
|
|
8545
|
-
\u2713 Successfully logged in! Token saved to ~/.
|
|
8925
|
+
\u2713 Successfully logged in! Token saved to ~/.pikaa/credentials.json`));
|
|
8546
8926
|
console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
|
|
8547
8927
|
} catch (directErr) {
|
|
8548
8928
|
console.error(style.red(`
|
|
@@ -8755,7 +9135,7 @@ async function handleSkillsCommand(ctx, args) {
|
|
|
8755
9135
|
const skills = loader.listSkills(ctx.session.cwd, { includeDisabled: true });
|
|
8756
9136
|
if (skills.length === 0) {
|
|
8757
9137
|
console.log(style.dim(`
|
|
8758
|
-
No domain skills discovered in .agents/skills/ or ~/.
|
|
9138
|
+
No domain skills discovered in .agents/skills/ or ~/.pikaa/skills/
|
|
8759
9139
|
`));
|
|
8760
9140
|
return;
|
|
8761
9141
|
}
|
|
@@ -8811,17 +9191,51 @@ async function handleSkillsCommand(ctx, args) {
|
|
|
8811
9191
|
function printMemories(ctx) {
|
|
8812
9192
|
const store2 = ctx.memoryStore;
|
|
8813
9193
|
if (!store2) {
|
|
8814
|
-
console.log(style.yellow(
|
|
9194
|
+
console.log(style.yellow(`
|
|
9195
|
+
Memory store not active.
|
|
9196
|
+
`));
|
|
8815
9197
|
return;
|
|
8816
9198
|
}
|
|
8817
|
-
const
|
|
9199
|
+
const cwd = ctx.session.cwd;
|
|
9200
|
+
const memoryDir = store2.getProjectMemoryDir(cwd);
|
|
9201
|
+
const topics = store2.listProjectMemories(cwd);
|
|
9202
|
+
const indexContent = store2.loadMemoryIndex(cwd);
|
|
9203
|
+
const BOLD2 = "\x1B[1m";
|
|
9204
|
+
const RESET2 = "\x1B[0m";
|
|
9205
|
+
const DIM = "\x1B[2m";
|
|
9206
|
+
const CYAN = "\x1B[38;2;120;190;255m";
|
|
9207
|
+
const GREEN = "\x1B[38;2;140;220;140m";
|
|
9208
|
+
const ORANGE = "\x1B[38;2;217;119;87m";
|
|
9209
|
+
const PURPLE = "\x1B[38;2;190;140;240m";
|
|
9210
|
+
const YELLOW = "\x1B[38;2;250;210;110m";
|
|
9211
|
+
const getCategoryColor = (cat) => {
|
|
9212
|
+
switch (cat.toLowerCase()) {
|
|
9213
|
+
case "user":
|
|
9214
|
+
return CYAN;
|
|
9215
|
+
case "feedback":
|
|
9216
|
+
return GREEN;
|
|
9217
|
+
case "project":
|
|
9218
|
+
return ORANGE;
|
|
9219
|
+
case "reference":
|
|
9220
|
+
return PURPLE;
|
|
9221
|
+
default:
|
|
9222
|
+
return YELLOW;
|
|
9223
|
+
}
|
|
9224
|
+
};
|
|
8818
9225
|
console.log();
|
|
8819
|
-
|
|
8820
|
-
|
|
9226
|
+
console.log(` ${BOLD2}\uD83E\uDDE0 Project Auto-Memory Bank${RESET2}`);
|
|
9227
|
+
console.log(` ${DIM}Directory: ${memoryDir}${RESET2}`);
|
|
9228
|
+
console.log(` ${DIM}Status: ${GREEN}Active (Loaded into turn context \u2264200 lines)${RESET2}`);
|
|
9229
|
+
console.log();
|
|
9230
|
+
if (topics.length === 0) {
|
|
9231
|
+
console.log(` ${DIM}No persistent topic memories saved for this project yet.${RESET2}`);
|
|
9232
|
+
console.log(` ${DIM}As you work, Pikaa automatically records user preferences, feedback, and project context.${RESET2}`);
|
|
8821
9233
|
} else {
|
|
8822
|
-
console.log(
|
|
8823
|
-
for (const
|
|
8824
|
-
|
|
9234
|
+
console.log(` ${BOLD2}Learned Memory Topics (${topics.length}):${RESET2}`);
|
|
9235
|
+
for (const t of topics) {
|
|
9236
|
+
const color = getCategoryColor(t.type);
|
|
9237
|
+
console.log(` \u2022 ${color}[${t.type}]${RESET2} ${BOLD2}${t.name}${RESET2}: ${DIM}${t.description || t.content.split(`
|
|
9238
|
+
`)[0]}${RESET2}`);
|
|
8825
9239
|
}
|
|
8826
9240
|
}
|
|
8827
9241
|
console.log();
|
|
@@ -9201,7 +9615,7 @@ function printReleaseNotes() {
|
|
|
9201
9615
|
"",
|
|
9202
9616
|
" " + BOLD2 + WHITE2 + "\uD83D\uDE80 What's New in " + version + " (Latest)" + RESET2,
|
|
9203
9617
|
" " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Persistent Default AI Model" + RESET2 + ": Switch via " + ROSE2 + "/model" + RESET2 + " and save",
|
|
9204
|
-
" preference across sessions in ~/.
|
|
9618
|
+
" preference across sessions in ~/.pikaa/credentials.json.",
|
|
9205
9619
|
" " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Real-time Git Branch Detection" + RESET2 + ": Header displays active branch",
|
|
9206
9620
|
" (\uE0A0 main) alongside user subscription tier (Groupy Pro / Max).",
|
|
9207
9621
|
" " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Claude Code Terminal UI Parity" + RESET2 + ": Authentic pixel emblem, fieldset",
|
|
@@ -9580,9 +9994,9 @@ class MarkdownHighlighter {
|
|
|
9580
9994
|
}
|
|
9581
9995
|
|
|
9582
9996
|
// src/cli/update-checker.ts
|
|
9583
|
-
import { existsSync as
|
|
9584
|
-
import { homedir as
|
|
9585
|
-
import { join as
|
|
9997
|
+
import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
|
|
9998
|
+
import { homedir as homedir5 } from "os";
|
|
9999
|
+
import { join as join13 } from "path";
|
|
9586
10000
|
var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
|
|
9587
10001
|
function parseSemver(v) {
|
|
9588
10002
|
const clean = v.replace(/^v/, "").trim();
|
|
@@ -9603,8 +10017,8 @@ function isNewerVersion(current, remote) {
|
|
|
9603
10017
|
return remPatch > curPatch;
|
|
9604
10018
|
}
|
|
9605
10019
|
function getUpdateCachePath() {
|
|
9606
|
-
const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME ||
|
|
9607
|
-
return
|
|
10020
|
+
const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join13(homedir5(), ".pikaa");
|
|
10021
|
+
return join13(baseDir, "update-cache.json");
|
|
9608
10022
|
}
|
|
9609
10023
|
async function fetchLatestNpmVersion(packageName, timeoutMs = 1500) {
|
|
9610
10024
|
const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
|
|
@@ -9637,7 +10051,7 @@ async function checkForUpdates(options = {}) {
|
|
|
9637
10051
|
const cachePath = options.cachePath || getUpdateCachePath();
|
|
9638
10052
|
const now = Date.now();
|
|
9639
10053
|
let cached = null;
|
|
9640
|
-
if (!options.force &&
|
|
10054
|
+
if (!options.force && existsSync21(cachePath)) {
|
|
9641
10055
|
try {
|
|
9642
10056
|
const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
|
|
9643
10057
|
if (raw && typeof raw.lastChecked === "number" && typeof raw.latestVersion === "string") {
|
|
@@ -9667,9 +10081,9 @@ async function checkForUpdates(options = {}) {
|
|
|
9667
10081
|
return null;
|
|
9668
10082
|
}
|
|
9669
10083
|
try {
|
|
9670
|
-
const parentDir =
|
|
9671
|
-
if (!
|
|
9672
|
-
|
|
10084
|
+
const parentDir = join13(cachePath, "..");
|
|
10085
|
+
if (!existsSync21(parentDir)) {
|
|
10086
|
+
mkdirSync11(parentDir, { recursive: true });
|
|
9673
10087
|
}
|
|
9674
10088
|
const cacheData = {
|
|
9675
10089
|
lastChecked: now,
|
|
@@ -10091,7 +10505,7 @@ async function main() {
|
|
|
10091
10505
|
resolve21(cwd, "mcp_config.json")
|
|
10092
10506
|
].filter(Boolean);
|
|
10093
10507
|
for (const cfg of candidateConfigs) {
|
|
10094
|
-
if (
|
|
10508
|
+
if (existsSync22(cfg)) {
|
|
10095
10509
|
try {
|
|
10096
10510
|
await mcpManager.loadConfigFile(cfg);
|
|
10097
10511
|
mcpManager.registerToolsIntoRouter(tools4);
|
|
@@ -10205,7 +10619,7 @@ Open the following link in your browser to complete authorization:`);
|
|
|
10205
10619
|
Waiting for browser callback on http://localhost:1455/auth/callback ...`));
|
|
10206
10620
|
const creds = await waitForToken();
|
|
10207
10621
|
console.log(style.green(`
|
|
10208
|
-
\u2713 Authentication Successful! Token saved to ~/.
|
|
10622
|
+
\u2713 Authentication Successful! Token saved to ~/.pikaa/credentials.json`));
|
|
10209
10623
|
console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
|
|
10210
10624
|
} catch (err) {
|
|
10211
10625
|
console.log(style.yellow(`
|
|
@@ -10224,7 +10638,7 @@ Falling back to Direct Terminal Login:`));
|
|
|
10224
10638
|
password: password.trim()
|
|
10225
10639
|
});
|
|
10226
10640
|
console.log(style.green(`
|
|
10227
|
-
\u2713 Successfully logged in! Token saved to ~/.
|
|
10641
|
+
\u2713 Successfully logged in! Token saved to ~/.pikaa/credentials.json`));
|
|
10228
10642
|
console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
|
|
10229
10643
|
} catch (directErr) {
|
|
10230
10644
|
rl.close();
|
|
@@ -10298,7 +10712,7 @@ function printSkillsList(loader, cwd) {
|
|
|
10298
10712
|
const skills = loader.discoverSkills(cwd);
|
|
10299
10713
|
console.log();
|
|
10300
10714
|
if (skills.length === 0) {
|
|
10301
|
-
console.log(style.dim("No skills found in .agents/skills/ or ~/.
|
|
10715
|
+
console.log(style.dim("No skills found in .agents/skills/ or ~/.pikaa/skills/"));
|
|
10302
10716
|
} else {
|
|
10303
10717
|
console.log(style.bold("Discovered Domain Skills:"));
|
|
10304
10718
|
for (const s of skills) {
|