@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 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 existsSync21 } from "fs";
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 || resolve(homedir(), ".groupy", "credentials.json");
139
+ this.filePath = customPath || getCredentialsPath();
18
140
  }
19
141
  load() {
20
- if (!existsSync(this.filePath))
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 = resolve(this.filePath, "..");
52
- if (!existsSync(dir)) {
53
- mkdirSync(dir, { recursive: true });
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 (existsSync(this.filePath)) {
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 existsSync2, readFileSync as readFileSync2 } from "fs";
637
- import { resolve as resolve2, join } from "path";
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 || resolve2(join(import.meta.dir, "..", "..", "templates"));
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 = join(cwd, ".agents", "templates", normalizedRel);
653
- if (existsSync2(workspacePath)) {
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 = join(homedir2(), ".groupy", "templates", normalizedRel);
660
- if (existsSync2(globalPath)) {
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 builtInPath = join(this.builtInTemplatesDir, normalizedRel);
666
- if (existsSync2(builtInPath)) {
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 existsSync3, readFileSync as readFileSync3 } from "fs";
684
- import { resolve as resolve3, join as join2, dirname } from "path";
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 = resolve3(startDir);
826
+ let current = resolve4(startDir);
700
827
  while (true) {
701
- if (existsSync3(join2(current, ".git"))) {
828
+ if (existsSync4(join3(current, ".git"))) {
702
829
  return current;
703
830
  }
704
- const parent = dirname(current);
831
+ const parent = dirname2(current);
705
832
  if (parent === current) {
706
- return resolve3(startDir);
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 = resolve3(targetDir);
714
- const normalizedRoot = resolve3(rootDir);
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 = dirname(current);
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 = join2(dir, filename);
736
- if (existsSync3(filePath)) {
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) {
@@ -1028,6 +1155,17 @@ async function runTurn(session, turnContext, input) {
1028
1155
  }
1029
1156
  continue;
1030
1157
  }
1158
+ if (!currentAgentText.trim() && toolCallRequests.length === 0) {
1159
+ if (iteration === 1 && iteration < turnContext.maxIterations) {
1160
+ session.addHistoryItem({
1161
+ id: `msg_nudge_${Date.now()}`,
1162
+ type: "user_message",
1163
+ content: "Please proceed with executing the task. Provide your complete analysis or call the required tools now.",
1164
+ createdAt: Date.now()
1165
+ });
1166
+ continue;
1167
+ }
1168
+ }
1031
1169
  break;
1032
1170
  }
1033
1171
  const totalContextTokens = estimateTotalTokens(session.getHistory()) + Math.ceil(effectiveSystemPrompt.length / 4);
@@ -1334,13 +1472,13 @@ class Session {
1334
1472
  type: "StatusChanged",
1335
1473
  status: "waiting_approval"
1336
1474
  });
1337
- return new Promise((resolve4) => {
1475
+ return new Promise((resolve5) => {
1338
1476
  this.pendingApprovals.set(params.approvalId, (approved) => {
1339
1477
  this.emitEvent({
1340
1478
  type: "StatusChanged",
1341
1479
  status: "running"
1342
1480
  });
1343
- resolve4(approved);
1481
+ resolve5(approved);
1344
1482
  });
1345
1483
  });
1346
1484
  }
@@ -1363,13 +1501,13 @@ class Session {
1363
1501
  type: "StatusChanged",
1364
1502
  status: "waiting_user_input"
1365
1503
  });
1366
- return new Promise((resolve4) => {
1504
+ return new Promise((resolve5) => {
1367
1505
  this.pendingUserQuestions.set(params.questionId, (answer) => {
1368
1506
  this.emitEvent({
1369
1507
  type: "StatusChanged",
1370
1508
  status: "running"
1371
1509
  });
1372
- resolve4(answer);
1510
+ resolve5(answer);
1373
1511
  });
1374
1512
  });
1375
1513
  }
@@ -1399,7 +1537,7 @@ class Session {
1399
1537
  return handleTurnInput(this, { text, images });
1400
1538
  }
1401
1539
  async promptAndWait(text, images, timeoutMs = 30000) {
1402
- return new Promise((resolve4, reject) => {
1540
+ return new Promise((resolve5, reject) => {
1403
1541
  const timer = setTimeout(() => {
1404
1542
  unsub();
1405
1543
  reject(new Error(`Turn timed out after ${timeoutMs}ms`));
@@ -1408,7 +1546,7 @@ class Session {
1408
1546
  if (event.msg.type === "TurnCompleted") {
1409
1547
  clearTimeout(timer);
1410
1548
  unsub();
1411
- resolve4();
1549
+ resolve5();
1412
1550
  } else if (event.msg.type === "Error") {
1413
1551
  clearTimeout(timer);
1414
1552
  unsub();
@@ -1427,8 +1565,8 @@ class Session {
1427
1565
  if (this.submissionQueue.length > 0) {
1428
1566
  yield this.submissionQueue.shift();
1429
1567
  } else {
1430
- const nextSub = await new Promise((resolve4) => {
1431
- this.submissionResolvers.push(resolve4);
1568
+ const nextSub = await new Promise((resolve5) => {
1569
+ this.submissionResolvers.push(resolve5);
1432
1570
  });
1433
1571
  yield nextSub;
1434
1572
  }
@@ -1442,9 +1580,9 @@ class Session {
1442
1580
  }
1443
1581
  }
1444
1582
  // src/tools/handlers/apply-patch.ts
1445
- import { existsSync as existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1446
- import { resolve as resolve4, dirname as dirname2 } from "path";
1447
- import { mkdirSync as mkdirSync2 } from "fs";
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";
1448
1586
  var applyPatchTool = {
1449
1587
  name: "apply_patch",
1450
1588
  description: "Apply precise multi-line modifications to an existing file or create a new file. TargetContent must match the file content exactly.",
@@ -1471,7 +1609,7 @@ var applyPatchTool = {
1471
1609
  if (!rawPath) {
1472
1610
  return { output: "Error: 'path' parameter is required", isError: true };
1473
1611
  }
1474
- const filePath = resolve4(ctx.cwd, rawPath);
1612
+ const filePath = resolve5(ctx.cwd, rawPath);
1475
1613
  const targetContent = typeof args.targetContent === "string" ? args.targetContent : "";
1476
1614
  const replacementContent = String(args.replacementContent ?? "");
1477
1615
  if (ctx.execPolicy) {
@@ -1490,7 +1628,7 @@ var applyPatchTool = {
1490
1628
  }
1491
1629
  }
1492
1630
  }
1493
- if (!existsSync4(filePath)) {
1631
+ if (!existsSync5(filePath)) {
1494
1632
  if (targetContent) {
1495
1633
  return {
1496
1634
  output: `Error: Target file '${rawPath}' does not exist, but targetContent was provided.`,
@@ -1498,7 +1636,7 @@ var applyPatchTool = {
1498
1636
  };
1499
1637
  }
1500
1638
  try {
1501
- mkdirSync2(dirname2(filePath), { recursive: true });
1639
+ mkdirSync3(dirname3(filePath), { recursive: true });
1502
1640
  writeFileSync2(filePath, replacementContent, "utf8");
1503
1641
  return { output: `Successfully created new file '${rawPath}'` };
1504
1642
  } catch (err) {
@@ -1624,7 +1762,7 @@ class WindowsSandbox {
1624
1762
  }
1625
1763
 
1626
1764
  // src/security/kernel/linux.ts
1627
- import { existsSync as existsSync5 } from "fs";
1765
+ import { existsSync as existsSync6 } from "fs";
1628
1766
 
1629
1767
  class LinuxSandbox {
1630
1768
  hasBwrap = false;
@@ -1635,7 +1773,7 @@ class LinuxSandbox {
1635
1773
  if (process.platform !== "linux") {
1636
1774
  return;
1637
1775
  }
1638
- this.hasBwrap = existsSync5("/usr/bin/bwrap") || existsSync5("/bin/bwrap") || existsSync5("/usr/local/bin/bwrap");
1776
+ this.hasBwrap = existsSync6("/usr/bin/bwrap") || existsSync6("/bin/bwrap") || existsSync6("/usr/local/bin/bwrap");
1639
1777
  }
1640
1778
  wrapCommand(cmd, profile) {
1641
1779
  if (!this.hasBwrap || profile.kind === "danger-unrestricted") {
@@ -1669,7 +1807,7 @@ class LinuxSandbox {
1669
1807
  }
1670
1808
 
1671
1809
  // src/security/kernel/macos.ts
1672
- import { existsSync as existsSync6 } from "fs";
1810
+ import { existsSync as existsSync7 } from "fs";
1673
1811
 
1674
1812
  class MacOSSandbox {
1675
1813
  hasSandboxExec = false;
@@ -1680,7 +1818,7 @@ class MacOSSandbox {
1680
1818
  if (process.platform !== "darwin") {
1681
1819
  return;
1682
1820
  }
1683
- this.hasSandboxExec = existsSync6("/usr/bin/sandbox-exec");
1821
+ this.hasSandboxExec = existsSync7("/usr/bin/sandbox-exec");
1684
1822
  }
1685
1823
  generateProfile(profile) {
1686
1824
  const rules = [
@@ -1719,7 +1857,7 @@ class MacOSSandbox {
1719
1857
  }
1720
1858
 
1721
1859
  // src/security/kernel/manager.ts
1722
- import { resolve as resolve5, normalize } from "path";
1860
+ import { resolve as resolve6, normalize } from "path";
1723
1861
 
1724
1862
  class KernelSandboxManager {
1725
1863
  windowsSandbox;
@@ -1741,10 +1879,10 @@ class KernelSandboxManager {
1741
1879
  };
1742
1880
  }
1743
1881
  buildDefaultProfile(cwd, allowNetwork = true) {
1744
- const normCwd = normalize(resolve5(cwd));
1882
+ const normCwd = normalize(resolve6(cwd));
1745
1883
  return {
1746
1884
  kind: "workspace-write",
1747
- readableRoots: [normCwd, resolve5(process.cwd())],
1885
+ readableRoots: [normCwd, resolve6(process.cwd())],
1748
1886
  writableRoots: [normCwd],
1749
1887
  allowNetwork,
1750
1888
  limits: {
@@ -1790,21 +1928,19 @@ var globalKernelSandbox = new KernelSandboxManager;
1790
1928
 
1791
1929
  // src/storage/prefix-rules-store.ts
1792
1930
  import { Database } from "bun:sqlite";
1793
- import { existsSync as existsSync7, mkdirSync as mkdirSync3 } from "fs";
1794
- import { dirname as dirname3, resolve as resolve6 } from "path";
1795
- import { homedir as homedir3 } from "os";
1796
-
1931
+ import { existsSync as existsSync8, mkdirSync as mkdirSync4 } from "fs";
1932
+ import { dirname as dirname4, resolve as resolve7 } from "path";
1797
1933
  class PrefixRulesStore {
1798
1934
  db;
1799
1935
  constructor(dbOrPath) {
1800
1936
  if (dbOrPath instanceof Database) {
1801
1937
  this.db = dbOrPath;
1802
1938
  } else {
1803
- const effectivePath = dbOrPath || resolve6(homedir3(), ".groupy", "groupy_rules.db");
1939
+ const effectivePath = dbOrPath || getPrefixRulesDbPath();
1804
1940
  if (effectivePath !== ":memory:") {
1805
- const dir = dirname3(effectivePath);
1806
- if (!existsSync7(dir)) {
1807
- mkdirSync3(dir, { recursive: true });
1941
+ const dir = dirname4(effectivePath);
1942
+ if (!existsSync8(dir)) {
1943
+ mkdirSync4(dir, { recursive: true });
1808
1944
  }
1809
1945
  }
1810
1946
  this.db = new Database(effectivePath);
@@ -1827,7 +1963,7 @@ class PrefixRulesStore {
1827
1963
  addRule(workspacePath, prefixTokens) {
1828
1964
  if (!prefixTokens || prefixTokens.length === 0)
1829
1965
  return;
1830
- const normalizedWs = workspacePath === "*" ? "*" : resolve6(workspacePath);
1966
+ const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
1831
1967
  const tokensJson = JSON.stringify(prefixTokens);
1832
1968
  const id = `${normalizedWs}:${tokensJson}`;
1833
1969
  const query = this.db.prepare(`
@@ -1844,7 +1980,7 @@ class PrefixRulesStore {
1844
1980
  isApproved(workspacePath, commandTokens) {
1845
1981
  if (!commandTokens || commandTokens.length === 0)
1846
1982
  return false;
1847
- const normalizedWs = resolve6(workspacePath);
1983
+ const normalizedWs = resolve7(workspacePath);
1848
1984
  const query = this.db.prepare(`
1849
1985
  SELECT prefix_tokens FROM approved_prefix_rules
1850
1986
  WHERE workspace_path = $ws OR workspace_path = '*'
@@ -1863,7 +1999,7 @@ class PrefixRulesStore {
1863
1999
  listRules(workspacePath) {
1864
2000
  let rows;
1865
2001
  if (workspacePath) {
1866
- const normalizedWs = workspacePath === "*" ? "*" : resolve6(workspacePath);
2002
+ const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
1867
2003
  const query = this.db.prepare(`
1868
2004
  SELECT prefix_tokens FROM approved_prefix_rules
1869
2005
  WHERE workspace_path = $ws OR workspace_path = '*'
@@ -1882,7 +2018,7 @@ class PrefixRulesStore {
1882
2018
  }).filter((r) => r.length > 0);
1883
2019
  }
1884
2020
  removeRule(workspacePath, prefixTokens) {
1885
- const normalizedWs = workspacePath === "*" ? "*" : resolve6(workspacePath);
2021
+ const normalizedWs = workspacePath === "*" ? "*" : resolve7(workspacePath);
1886
2022
  const tokensJson = JSON.stringify(prefixTokens);
1887
2023
  const id = `${normalizedWs}:${tokensJson}`;
1888
2024
  const query = this.db.prepare(`
@@ -2015,7 +2151,7 @@ function createShellTool(policy = new ExecPolicy) {
2015
2151
  } catch {}
2016
2152
  });
2017
2153
  }
2018
- const timeoutPromise = new Promise((resolve7) => setTimeout(() => resolve7({ isTimeout: true }), timeoutMs));
2154
+ const timeoutPromise = new Promise((resolve8) => setTimeout(() => resolve8({ isTimeout: true }), timeoutMs));
2019
2155
  const result = await Promise.race([
2020
2156
  proc.exited.then(async (code) => {
2021
2157
  const stdout = await new Response(proc.stdout).text();
@@ -2059,8 +2195,8 @@ ${result.stderr.trim()}`);
2059
2195
  }
2060
2196
  var shellTool = createShellTool();
2061
2197
  // src/tools/handlers/file-ops.ts
2062
- import { readdirSync, readFileSync as readFileSync5, writeFileSync as writeFileSync3, existsSync as existsSync8, statSync, mkdirSync as mkdirSync4 } from "fs";
2063
- import { resolve as resolve7, dirname as dirname4 } from "path";
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";
2064
2200
  var readFileTool = {
2065
2201
  name: "read_file",
2066
2202
  description: "Read the full text content of a file.",
@@ -2072,8 +2208,8 @@ var readFileTool = {
2072
2208
  required: ["path"]
2073
2209
  },
2074
2210
  async execute(args, ctx) {
2075
- const filePath = resolve7(ctx.cwd, String(args.path || ""));
2076
- if (!existsSync8(filePath)) {
2211
+ const filePath = resolve8(ctx.cwd, String(args.path || ""));
2212
+ if (!existsSync9(filePath)) {
2077
2213
  return { output: `Error: File not found: '${args.path}'`, isError: true };
2078
2214
  }
2079
2215
  try {
@@ -2094,15 +2230,15 @@ var listDirTool = {
2094
2230
  }
2095
2231
  },
2096
2232
  async execute(args, ctx) {
2097
- const dirPath = resolve7(ctx.cwd, String(args.path || "."));
2098
- if (!existsSync8(dirPath)) {
2233
+ const dirPath = resolve8(ctx.cwd, String(args.path || "."));
2234
+ if (!existsSync9(dirPath)) {
2099
2235
  return { output: `Error: Directory not found: '${args.path}'`, isError: true };
2100
2236
  }
2101
2237
  try {
2102
- const entries = readdirSync(dirPath);
2238
+ const entries = readdirSync2(dirPath);
2103
2239
  const formatted = entries.map((entry) => {
2104
- const full = resolve7(dirPath, entry);
2105
- const isDir = statSync(full).isDirectory();
2240
+ const full = resolve8(dirPath, entry);
2241
+ const isDir = statSync2(full).isDirectory();
2106
2242
  return `${isDir ? "[DIR]" : "[FILE]"} ${entry}`;
2107
2243
  });
2108
2244
  return { output: formatted.join(`
@@ -2125,7 +2261,7 @@ var writeFileTool = {
2125
2261
  },
2126
2262
  async execute(args, ctx) {
2127
2263
  const rawPath = String(args.path || "");
2128
- const filePath = resolve7(ctx.cwd, rawPath);
2264
+ const filePath = resolve8(ctx.cwd, rawPath);
2129
2265
  if (ctx.execPolicy) {
2130
2266
  const evalResult = ctx.execPolicy.shouldPromptFileEdit(rawPath);
2131
2267
  if (evalResult.isPlanBlocked || ctx.mode === "plan") {
@@ -2143,7 +2279,7 @@ var writeFileTool = {
2143
2279
  }
2144
2280
  }
2145
2281
  try {
2146
- mkdirSync4(dirname4(filePath), { recursive: true });
2282
+ mkdirSync5(dirname5(filePath), { recursive: true });
2147
2283
  writeFileSync3(filePath, String(args.content ?? ""), "utf8");
2148
2284
  return { output: `Successfully wrote to '${args.path}'` };
2149
2285
  } catch (err) {
@@ -2266,8 +2402,8 @@ var updatePlanTool = {
2266
2402
  }
2267
2403
  };
2268
2404
  // src/search/engine.ts
2269
- import { readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2, existsSync as existsSync9 } from "fs";
2270
- import { resolve as resolve8, relative, join as join3, extname } from "path";
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";
2271
2407
  var DEFAULT_IGNORE_DIRS = new Set([
2272
2408
  ".git",
2273
2409
  "node_modules",
@@ -2312,8 +2448,8 @@ var BINARY_EXTENSIONS = new Set([
2312
2448
 
2313
2449
  class FileSearchEngine {
2314
2450
  grep(cwd, options) {
2315
- const searchRoot = resolve8(cwd, options.path || ".");
2316
- if (!existsSync9(searchRoot)) {
2451
+ const searchRoot = resolve9(cwd, options.path || ".");
2452
+ if (!existsSync10(searchRoot)) {
2317
2453
  return { matches: [], totalMatches: 0, truncated: false };
2318
2454
  }
2319
2455
  const maxResults = options.maxResults || 50;
@@ -2362,8 +2498,8 @@ class FileSearchEngine {
2362
2498
  return { matches, totalMatches, truncated };
2363
2499
  }
2364
2500
  findFiles(cwd, options) {
2365
- const searchRoot = resolve8(cwd, options.path || ".");
2366
- if (!existsSync9(searchRoot))
2501
+ const searchRoot = resolve9(cwd, options.path || ".");
2502
+ if (!existsSync10(searchRoot))
2367
2503
  return [];
2368
2504
  const maxResults = options.maxResults || 100;
2369
2505
  const gitignoreRules = this.loadGitignoreRules(searchRoot);
@@ -2402,8 +2538,8 @@ class FileSearchEngine {
2402
2538
  }
2403
2539
  loadGitignoreRules(root) {
2404
2540
  const rules = new Set;
2405
- const gitignorePath = join3(root, ".gitignore");
2406
- if (existsSync9(gitignorePath)) {
2541
+ const gitignorePath = join4(root, ".gitignore");
2542
+ if (existsSync10(gitignorePath)) {
2407
2543
  try {
2408
2544
  const lines = readFileSync6(gitignorePath, "utf8").split(`
2409
2545
  `);
@@ -2420,7 +2556,7 @@ class FileSearchEngine {
2420
2556
  collectFiles(dir, root, gitignoreRules, includePattern) {
2421
2557
  const results = [];
2422
2558
  try {
2423
- const stat = statSync2(dir);
2559
+ const stat = statSync3(dir);
2424
2560
  if (!stat.isDirectory()) {
2425
2561
  if (!this.isBinary(dir)) {
2426
2562
  results.push(dir);
@@ -2434,9 +2570,9 @@ class FileSearchEngine {
2434
2570
  while (queue.length > 0) {
2435
2571
  const currentDir = queue.shift();
2436
2572
  try {
2437
- const entries = readdirSync2(currentDir, { withFileTypes: true });
2573
+ const entries = readdirSync3(currentDir, { withFileTypes: true });
2438
2574
  for (const entry of entries) {
2439
- const fullPath = join3(currentDir, entry.name);
2575
+ const fullPath = join4(currentDir, entry.name);
2440
2576
  const relToRoot = relative(root, fullPath).replace(/\\/g, "/");
2441
2577
  if (this.isIgnored(entry.name, relToRoot, gitignoreRules)) {
2442
2578
  continue;
@@ -2833,6 +2969,111 @@ ${loaded.instructions}`
2833
2969
  }
2834
2970
 
2835
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
+ }
2836
3077
  function createRememberTool(store) {
2837
3078
  return {
2838
3079
  name: "remember",
@@ -2843,16 +3084,15 @@ function createRememberTool(store) {
2843
3084
  category: {
2844
3085
  type: "string",
2845
3086
  description: "Category of the memory.",
2846
- enum: ["preference", "guideline", "architecture", "note"]
3087
+ enum: ["preference", "guideline", "architecture", "note", "user", "feedback", "project", "reference"]
2847
3088
  },
2848
3089
  content: {
2849
3090
  type: "string",
2850
3091
  description: "The concise rule, preference, or fact to remember permanently."
2851
3092
  },
2852
- scope: {
3093
+ name: {
2853
3094
  type: "string",
2854
- description: "'global' (applies to all projects) or 'workspace' (applies only to current repository). Defaults to 'global'.",
2855
- enum: ["global", "workspace"]
3095
+ description: "Optional topic name."
2856
3096
  }
2857
3097
  },
2858
3098
  required: ["category", "content"]
@@ -2860,22 +3100,30 @@ function createRememberTool(store) {
2860
3100
  async execute(args, context) {
2861
3101
  const category = args.category || "preference";
2862
3102
  const content = String(args.content || "");
2863
- const scope = args.scope || "global";
3103
+ const name = args.name ? String(args.name) : undefined;
2864
3104
  if (!content) {
2865
3105
  return { output: "Error: memory content cannot be empty", isError: true };
2866
3106
  }
2867
3107
  const entry = store.addMemory({
2868
3108
  category,
2869
3109
  content,
2870
- scope,
3110
+ name,
2871
3111
  cwd: context.cwd
2872
3112
  });
2873
3113
  return {
2874
- output: `Successfully saved to ${scope} memory bank: [${entry.category}] "${entry.content}"`
3114
+ output: `Successfully saved to Auto-Memory bank: [${entry.category}] "${entry.name || entry.content}"`
2875
3115
  };
2876
3116
  }
2877
3117
  };
2878
3118
  }
3119
+ function createAutoMemoryTools(store) {
3120
+ return [
3121
+ createSaveMemoryTool(store),
3122
+ createReadMemoryTool(store),
3123
+ createListMemoriesTool(store),
3124
+ createRememberTool(store)
3125
+ ];
3126
+ }
2879
3127
 
2880
3128
  // src/worktree/tools.ts
2881
3129
  function createWorktreeTools(manager) {
@@ -3010,7 +3258,9 @@ function createDefaultTools(options = {}) {
3010
3258
  router2.register(createSkillTool(options.skillsLoader));
3011
3259
  }
3012
3260
  if (options.memoryStore) {
3013
- router2.register(createRememberTool(options.memoryStore));
3261
+ for (const tool of createAutoMemoryTools(options.memoryStore)) {
3262
+ router2.register(tool);
3263
+ }
3014
3264
  }
3015
3265
  if (options.worktreeManager) {
3016
3266
  for (const tool of createWorktreeTools(options.worktreeManager)) {
@@ -3021,8 +3271,8 @@ function createDefaultTools(options = {}) {
3021
3271
  }
3022
3272
 
3023
3273
  // src/agents/roles.ts
3024
- import { existsSync as existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
3025
- import { resolve as resolve9, join as join4 } from "path";
3274
+ import { existsSync as existsSync11, readdirSync as readdirSync4, readFileSync as readFileSync7 } from "fs";
3275
+ import { resolve as resolve10, join as join5 } from "path";
3026
3276
 
3027
3277
  class AgentRoleRegistry {
3028
3278
  roles = new Map;
@@ -3106,14 +3356,14 @@ class AgentRoleRegistry {
3106
3356
  return cycle === 0 ? base : `${base}_${cycle + 1}`;
3107
3357
  }
3108
3358
  loadRolesFromDir(dirPath) {
3109
- const fullPath = resolve9(dirPath);
3110
- if (!existsSync10(fullPath))
3359
+ const fullPath = resolve10(dirPath);
3360
+ if (!existsSync11(fullPath))
3111
3361
  return;
3112
- const entries = readdirSync3(fullPath);
3362
+ const entries = readdirSync4(fullPath);
3113
3363
  for (const entry of entries) {
3114
3364
  if (entry.endsWith(".json")) {
3115
3365
  try {
3116
- const content = readFileSync7(join4(fullPath, entry), "utf8");
3366
+ const content = readFileSync7(join5(fullPath, entry), "utf8");
3117
3367
  const parsed = JSON.parse(content);
3118
3368
  if (parsed.name && parsed.systemPrompt) {
3119
3369
  this.registerRole(parsed);
@@ -3165,21 +3415,19 @@ function createAgentIdentity(parentId, harnessId = "groupy-harness-v1") {
3165
3415
 
3166
3416
  // src/agents/graph-store.ts
3167
3417
  import { Database as Database2 } from "bun:sqlite";
3168
- import { resolve as resolve10 } from "path";
3169
- import { existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
3170
- import { homedir as homedir4 } from "os";
3171
-
3418
+ import { resolve as resolve11 } from "path";
3419
+ import { existsSync as existsSync12, mkdirSync as mkdirSync6 } from "fs";
3172
3420
  class AgentGraphStore {
3173
3421
  db;
3174
3422
  constructor(dbPathOrDb) {
3175
3423
  if (dbPathOrDb instanceof Database2) {
3176
3424
  this.db = dbPathOrDb;
3177
3425
  } else {
3178
- const dbPath = dbPathOrDb || resolve10(homedir4(), ".groupy", "agent_graph.db");
3426
+ const dbPath = dbPathOrDb || getAgentGraphDbPath();
3179
3427
  if (dbPath !== ":memory:") {
3180
- const dir = resolve10(dbPath, "..");
3181
- if (!existsSync11(dir)) {
3182
- mkdirSync5(dir, { recursive: true });
3428
+ const dir = resolve11(dbPath, "..");
3429
+ if (!existsSync12(dir)) {
3430
+ mkdirSync6(dir, { recursive: true });
3183
3431
  }
3184
3432
  }
3185
3433
  this.db = new Database2(dbPath);
@@ -3302,8 +3550,8 @@ Your nickname is ${nickname}. Your assigned task is: '${params.taskName}'. Focus
3302
3550
  });
3303
3551
  let resolvePromise;
3304
3552
  let rejectPromise;
3305
- const taskPromise = new Promise((resolve11, reject) => {
3306
- resolvePromise = resolve11;
3553
+ const taskPromise = new Promise((resolve12, reject) => {
3554
+ resolvePromise = resolve12;
3307
3555
  rejectPromise = reject;
3308
3556
  });
3309
3557
  const handle = {
@@ -3603,8 +3851,8 @@ function registerMultiAgentTools(router2, spawner) {
3603
3851
  }
3604
3852
 
3605
3853
  // src/mcp/manager.ts
3606
- import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
3607
- import { resolve as resolve11, dirname as dirname5, join as join5 } from "path";
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";
3608
3856
 
3609
3857
  // src/mcp/client.ts
3610
3858
  class McpClient {
@@ -3998,13 +4246,13 @@ class StdioTransport {
3998
4246
  if (this.isClosed || !this.proc || !this.proc.stdin) {
3999
4247
  throw new GroupyError("MCP Stdio transport is closed");
4000
4248
  }
4001
- return new Promise((resolve11, reject) => {
4249
+ return new Promise((resolve12, reject) => {
4002
4250
  const timeoutMs = 30000;
4003
4251
  const timer = setTimeout(() => {
4004
4252
  this.pendingRequests.delete(request.id);
4005
4253
  reject(new GroupyError(`MCP request timed out after ${timeoutMs}ms (method: ${request.method})`));
4006
4254
  }, timeoutMs);
4007
- this.pendingRequests.set(request.id, { resolve: resolve11, reject, timer });
4255
+ this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
4008
4256
  try {
4009
4257
  const payload = JSON.stringify(request) + `
4010
4258
  `;
@@ -4137,12 +4385,12 @@ class SseTransport {
4137
4385
  if (!this.messageUrl) {
4138
4386
  this.messageUrl = this.endpointUrl;
4139
4387
  }
4140
- return new Promise((resolve11, reject) => {
4388
+ return new Promise((resolve12, reject) => {
4141
4389
  const timer = setTimeout(() => {
4142
4390
  this.pendingRequests.delete(request.id);
4143
4391
  reject(new GroupyError(`MCP SSE request timed out (method: ${request.method})`));
4144
4392
  }, 30000);
4145
- this.pendingRequests.set(request.id, { resolve: resolve11, reject, timer });
4393
+ this.pendingRequests.set(request.id, { resolve: resolve12, reject, timer });
4146
4394
  fetch(this.messageUrl, {
4147
4395
  method: "POST",
4148
4396
  headers: {
@@ -4219,8 +4467,8 @@ class McpManager {
4219
4467
  }
4220
4468
  }
4221
4469
  async loadConfigFile(filePath) {
4222
- const fullPath = resolve11(filePath);
4223
- if (!existsSync12(fullPath))
4470
+ const fullPath = resolve12(filePath);
4471
+ if (!existsSync13(fullPath))
4224
4472
  return;
4225
4473
  this.loadedConfigFiles.add(fullPath);
4226
4474
  try {
@@ -4473,13 +4721,13 @@ class McpManager {
4473
4721
  `);
4474
4722
  }
4475
4723
  saveServerToConfigFile(filePath, name, config) {
4476
- const fullPath = resolve11(filePath);
4477
- const dir = dirname5(fullPath);
4478
- if (!existsSync12(dir)) {
4479
- mkdirSync6(dir, { recursive: true });
4724
+ const fullPath = resolve12(filePath);
4725
+ const dir = dirname6(fullPath);
4726
+ if (!existsSync13(dir)) {
4727
+ mkdirSync7(dir, { recursive: true });
4480
4728
  }
4481
4729
  let existing = { mcpServers: {} };
4482
- if (existsSync12(fullPath)) {
4730
+ if (existsSync13(fullPath)) {
4483
4731
  try {
4484
4732
  const content = readFileSync8(fullPath, "utf8");
4485
4733
  existing = JSON.parse(content);
@@ -4493,8 +4741,8 @@ class McpManager {
4493
4741
  this.loadedConfigFiles.add(fullPath);
4494
4742
  }
4495
4743
  removeServerFromConfigFile(filePath, name) {
4496
- const fullPath = resolve11(filePath);
4497
- if (!existsSync12(fullPath))
4744
+ const fullPath = resolve12(filePath);
4745
+ if (!existsSync13(fullPath))
4498
4746
  return false;
4499
4747
  try {
4500
4748
  const content = readFileSync8(fullPath, "utf8");
@@ -4524,11 +4772,11 @@ class McpManager {
4524
4772
  }
4525
4773
  }
4526
4774
  getDefaultConfigFile(cwd = process.cwd()) {
4527
- const workspaceConfig = join5(cwd, ".mcp.json");
4528
- if (existsSync12(workspaceConfig))
4775
+ const workspaceConfig = join6(cwd, ".mcp.json");
4776
+ if (existsSync13(workspaceConfig))
4529
4777
  return workspaceConfig;
4530
- const altConfig = join5(cwd, "mcp_config.json");
4531
- if (existsSync12(altConfig))
4778
+ const altConfig = join6(cwd, "mcp_config.json");
4779
+ if (existsSync13(altConfig))
4532
4780
  return altConfig;
4533
4781
  return workspaceConfig;
4534
4782
  }
@@ -4549,17 +4797,16 @@ class McpManager {
4549
4797
 
4550
4798
  // src/storage/sqlite-store.ts
4551
4799
  import { Database as Database3 } from "bun:sqlite";
4552
- import { existsSync as existsSync13, mkdirSync as mkdirSync7 } from "fs";
4553
- import { dirname as dirname6, resolve as resolve12 } from "path";
4554
- import { homedir as homedir5 } from "os";
4800
+ import { existsSync as existsSync14, mkdirSync as mkdirSync8 } from "fs";
4801
+ import { dirname as dirname7 } from "path";
4555
4802
  class SqliteThreadStore {
4556
4803
  db;
4557
4804
  constructor(dbPath) {
4558
4805
  const effectivePath = dbPath || this.getDefaultDbPath();
4559
4806
  if (effectivePath !== ":memory:") {
4560
- const dir = dirname6(effectivePath);
4561
- if (!existsSync13(dir)) {
4562
- mkdirSync7(dir, { recursive: true });
4807
+ const dir = dirname7(effectivePath);
4808
+ if (!existsSync14(dir)) {
4809
+ mkdirSync8(dir, { recursive: true });
4563
4810
  }
4564
4811
  }
4565
4812
  this.db = new Database3(effectivePath);
@@ -4568,7 +4815,7 @@ class SqliteThreadStore {
4568
4815
  this.initSchema();
4569
4816
  }
4570
4817
  getDefaultDbPath() {
4571
- return resolve12(homedir5(), ".groupy", "groupy_threads.db");
4818
+ return getThreadsDbPath();
4572
4819
  }
4573
4820
  initSchema() {
4574
4821
  this.db.exec(`
@@ -4793,9 +5040,9 @@ class SessionPersistenceManager {
4793
5040
  }
4794
5041
 
4795
5042
  // src/skills/loader.ts
4796
- import { existsSync as existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
4797
- import { resolve as resolve13, join as join6 } from "path";
4798
- import { homedir as homedir6 } from "os";
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";
4799
5046
  var __dirname = "/home/runner/work/agent-cli/agent-cli/src/skills";
4800
5047
 
4801
5048
  class SkillsLoader {
@@ -4865,16 +5112,16 @@ class SkillsLoader {
4865
5112
  resolve13(cwd, "skills")
4866
5113
  ];
4867
5114
  for (const cand of candidates) {
4868
- if (existsSync14(cand) && !roots.includes(cand)) {
5115
+ if (existsSync15(cand) && !roots.includes(cand)) {
4869
5116
  roots.push(cand);
4870
5117
  }
4871
5118
  }
4872
5119
  }
4873
5120
  if (this.includeGlobal) {
4874
- roots.push(resolve13(homedir6(), ".groupy", "skills"), resolve13(homedir6(), ".gemini", "config", "skills"));
5121
+ roots.push(getGlobalSkillsDir(), resolve13(homedir3(), ".gemini", "config", "skills"));
4875
5122
  }
4876
5123
  roots.push(...this.customRoots.map((r) => resolve13(r)));
4877
- return roots.filter((r) => existsSync14(r));
5124
+ return roots.filter((r) => existsSync15(r));
4878
5125
  }
4879
5126
  discoverSkills(cwd, options) {
4880
5127
  return this.listSkills(cwd, options);
@@ -4890,12 +5137,12 @@ class SkillsLoader {
4890
5137
  const discovered = new Map;
4891
5138
  for (const root of roots) {
4892
5139
  try {
4893
- const entries = readdirSync4(root, { withFileTypes: true });
5140
+ const entries = readdirSync5(root, { withFileTypes: true });
4894
5141
  for (const entry of entries) {
4895
5142
  if (entry.isDirectory()) {
4896
- const skillDir = join6(root, entry.name);
4897
- const skillFilePath = join6(skillDir, "SKILL.md");
4898
- if (existsSync14(skillFilePath)) {
5143
+ const skillDir = join7(root, entry.name);
5144
+ const skillFilePath = join7(skillDir, "SKILL.md");
5145
+ if (existsSync15(skillFilePath)) {
4899
5146
  const meta = this.parseSkillFrontmatter(skillFilePath, entry.name, root, cwd);
4900
5147
  if (meta && !discovered.has(meta.name)) {
4901
5148
  meta.enabled = !this.isSkillDisabled(meta.name);
@@ -5018,149 +5265,288 @@ When tackling complex specialized tasks that match any of these skills, autonomo
5018
5265
  }
5019
5266
 
5020
5267
  // src/memories/store.ts
5021
- import { existsSync as existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync8 } from "fs";
5022
- import { resolve as resolve14, dirname as dirname7 } from "path";
5023
- import { homedir as homedir7 } from "os";
5024
-
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";
5025
5271
  class MemoryStore {
5026
5272
  globalPath;
5027
5273
  customWorkspacePath;
5028
5274
  constructor(options = {}) {
5029
- this.globalPath = options.globalPath || resolve14(homedir7(), ".groupy", "memories.md");
5275
+ this.globalPath = options.globalPath || getGlobalMemoriesPath();
5030
5276
  this.customWorkspacePath = options.workspacePath;
5031
5277
  }
5032
- getWorkspacePath(cwd) {
5033
- return this.customWorkspacePath || resolve14(cwd, ".agents", "memories.md");
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
+ }
5034
5290
  }
5035
- addMemory(params) {
5036
- const scope = params.scope || "global";
5037
- const targetFile = scope === "global" ? this.globalPath : this.getWorkspacePath(params.cwd || process.cwd());
5038
- const dir = dirname7(targetFile);
5039
- if (!existsSync15(dir)) {
5040
- mkdirSync8(dir, { recursive: true });
5041
- }
5042
- const existingEntries = this.readMemoryFile(targetFile, scope);
5043
- const normalized = params.content.trim();
5044
- const duplicate = existingEntries.find((e) => e.category === params.category && e.content.toLowerCase() === normalized.toLowerCase());
5045
- if (duplicate) {
5046
- return duplicate;
5047
- }
5048
- const newEntry = {
5049
- id: `mem_${Date.now()}_${Math.random().toString(36).slice(2, 6)}`,
5050
- category: params.category,
5051
- content: normalized,
5052
- scope,
5053
- createdAt: Date.now()
5054
- };
5055
- existingEntries.push(newEntry);
5056
- this.writeMemoryFile(targetFile, existingEntries);
5057
- 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}`;
5058
5296
  }
5059
- getAllMemories(cwd) {
5060
- const globalEntries = this.readMemoryFile(this.globalPath, "global");
5061
- const workspacePath = this.getWorkspacePath(cwd);
5062
- const workspaceEntries = existsSync15(workspacePath) ? this.readMemoryFile(workspacePath, "workspace") : [];
5063
- return [...globalEntries, ...workspaceEntries];
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
+ };
5064
5368
  }
5065
- readMemoryFile(filePath, scope) {
5066
- if (!existsSync15(filePath))
5067
- return [];
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
+ }
5068
5386
  try {
5069
- const content = readFileSync10(filePath, "utf8");
5070
- const lines = content.split(`
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(`
5071
5395
  `);
5072
- const entries = [];
5073
- let currentCategory = "preference";
5074
- for (const line of lines) {
5075
- const trimmed = line.trim();
5076
- if (trimmed.startsWith("## Preferences") || trimmed.startsWith("## User Preferences")) {
5077
- currentCategory = "preference";
5078
- } else if (trimmed.startsWith("## Guidelines") || trimmed.startsWith("## Coding Guidelines")) {
5079
- currentCategory = "guideline";
5080
- } else if (trimmed.startsWith("## Architecture") || trimmed.startsWith("## Project Architecture")) {
5081
- currentCategory = "architecture";
5082
- } else if (trimmed.startsWith("## Notes") || trimmed.startsWith("## General Notes")) {
5083
- currentCategory = "note";
5084
- } else if (trimmed.startsWith("- ") || trimmed.startsWith("* ")) {
5085
- const itemText = trimmed.slice(2).trim();
5086
- if (itemText) {
5087
- entries.push({
5088
- id: `mem_${entries.length + 1}`,
5089
- category: currentCategory,
5090
- content: itemText,
5091
- scope,
5092
- createdAt: Date.now()
5093
- });
5094
- }
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;
5095
5425
  }
5426
+ } else {
5427
+ bodyLines.push(line);
5096
5428
  }
5097
- return entries;
5098
- } catch {
5099
- return [];
5100
5429
  }
5101
- }
5102
- writeMemoryFile(filePath, entries) {
5103
- const categories = {
5104
- preference: [],
5105
- guideline: [],
5106
- architecture: [],
5107
- note: []
5430
+ return {
5431
+ type,
5432
+ name,
5433
+ description,
5434
+ modified,
5435
+ content: bodyLines.join(`
5436
+ `).trim(),
5437
+ filePath
5108
5438
  };
5109
- for (const entry of entries) {
5110
- categories[entry.category].push(entry.content);
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 {}
5111
5457
  }
5112
- let markdown = `# Groupy Persistent Memories
5113
-
5114
- `;
5115
- if (categories.preference.length > 0) {
5116
- markdown += `## User Preferences
5117
- ${categories.preference.map((p) => `- ${p}`).join(`
5118
- `)}
5119
-
5120
- `;
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})`);
5121
5466
  }
5122
- if (categories.guideline.length > 0) {
5123
- markdown += `## Coding Guidelines
5124
- ${categories.guideline.map((g) => `- ${g}`).join(`
5125
- `)}
5126
-
5127
- `;
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 "";
5128
5486
  }
5129
- if (categories.architecture.length > 0) {
5130
- markdown += `## Project Architecture
5131
- ${categories.architecture.map((a) => `- ${a}`).join(`
5132
- `)}
5133
-
5134
- `;
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 {}
5135
5499
  }
5136
- if (categories.note.length > 0) {
5137
- markdown += `## General Notes
5138
- ${categories.note.map((n) => `- ${n}`).join(`
5139
- `)}
5140
-
5141
- `;
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;
5142
5514
  }
5143
- writeFileSync5(filePath, markdown.trim() + `
5144
- `, "utf8");
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;
5145
5529
  }
5146
5530
  formatMemoriesPrompt(cwd) {
5147
- const memories = this.getAllMemories(cwd);
5148
- if (memories.length === 0)
5531
+ const indexContent = this.loadMemoryIndex(cwd);
5532
+ if (!indexContent)
5149
5533
  return "";
5150
- const lines = memories.map((m) => `- [${m.category}] (${m.scope}): ${m.content}`);
5151
- return `
5152
- ## Persistent User Preferences & Memory Bank
5153
- <user_memories>
5154
- ${lines.join(`
5155
- `)}
5156
- </user_memories>
5157
- Strictly respect these learned user preferences and project architectural conventions.`;
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
+ `);
5158
5544
  }
5159
5545
  }
5160
5546
 
5161
5547
  // src/worktree/manager.ts
5162
- import { resolve as resolve16, join as join7 } from "path";
5163
- import { existsSync as existsSync16, mkdirSync as mkdirSync9, writeFileSync as writeFileSync6, readFileSync as readFileSync11 } from "fs";
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";
5164
5550
 
5165
5551
  // src/worktree/git.ts
5166
5552
  import { resolve as resolve15 } from "path";
@@ -5310,15 +5696,15 @@ class WorktreeManager {
5310
5696
  const branchName = options.branch || `groupy/${taskId}`;
5311
5697
  const targetDir = options.worktreePath || (this.baseStorageDir ? resolve16(this.baseStorageDir, branchName.replace(/\//g, "_")) : resolve16(repoRoot, ".groupy", "worktrees", branchName.replace(/\//g, "_")));
5312
5698
  const worktreeParent = resolve16(targetDir, "..");
5313
- if (!existsSync16(worktreeParent)) {
5314
- mkdirSync9(worktreeParent, { recursive: true });
5699
+ if (!existsSync17(worktreeParent)) {
5700
+ mkdirSync10(worktreeParent, { recursive: true });
5315
5701
  }
5316
5702
  const baseBranch = options.baseBranch || await getCurrentBranch(repoRoot);
5317
5703
  const result = await createWorktreeGit(repoRoot, targetDir, branchName, baseBranch);
5318
5704
  if (!result.success) {
5319
5705
  throw new Error(`Failed to create git worktree: ${result.error}`);
5320
5706
  }
5321
- const metaPath = join7(targetDir, "groupy-thread.json");
5707
+ const metaPath = join9(targetDir, "groupy-thread.json");
5322
5708
  try {
5323
5709
  writeFileSync6(metaPath, JSON.stringify({
5324
5710
  version: 1,
@@ -5344,8 +5730,8 @@ class WorktreeManager {
5344
5730
  return [];
5345
5731
  const worktrees = await listWorktreesGit(repoRoot);
5346
5732
  return worktrees.map((wt) => {
5347
- const metaPath = join7(wt.path, "groupy-thread.json");
5348
- if (existsSync16(metaPath)) {
5733
+ const metaPath = join9(wt.path, "groupy-thread.json");
5734
+ if (existsSync17(metaPath)) {
5349
5735
  try {
5350
5736
  const raw = JSON.parse(readFileSync11(metaPath, "utf8"));
5351
5737
  return { ...wt, threadId: raw.ownerThreadId || raw.threadId };
@@ -5423,7 +5809,7 @@ class WorktreeManager {
5423
5809
  }
5424
5810
  }
5425
5811
  // src/auth/oauth.ts
5426
- import { randomBytes, createHash } from "crypto";
5812
+ import { randomBytes, createHash as createHash2 } from "crypto";
5427
5813
  import { exec } from "child_process";
5428
5814
  class AuthClient {
5429
5815
  store;
@@ -5576,7 +5962,7 @@ class AuthClient {
5576
5962
  return randomBytes(32).toString("base64url").replace(/[^a-zA-Z0-9]/g, "").slice(0, 64);
5577
5963
  }
5578
5964
  generateCodeChallenge(verifier) {
5579
- return createHash("sha256").update(verifier).digest("base64url");
5965
+ return createHash2("sha256").update(verifier).digest("base64url");
5580
5966
  }
5581
5967
  }
5582
5968
  // src/cli/ui/colors.ts
@@ -6043,7 +6429,7 @@ function parsePatch(oldSrc, newSrc, contextLines = 3) {
6043
6429
  // package.json
6044
6430
  var package_default = {
6045
6431
  name: "@pikaa-ai/pikaa",
6046
- version: "0.3.13",
6432
+ version: "0.3.15",
6047
6433
  description: "PIKAA CLI - AI coding agent that runs locally in your terminal.",
6048
6434
  main: "./dist/index.js",
6049
6435
  module: "./dist/index.js",
@@ -6121,7 +6507,7 @@ function getCliVersion(options = {}) {
6121
6507
  }
6122
6508
 
6123
6509
  // src/cli/ui/animation/banner-animation.ts
6124
- import { homedir as homedir8 } from "os";
6510
+ import { homedir as homedir4 } from "os";
6125
6511
  import { execSync } from "child_process";
6126
6512
  var ROSE = "\x1B[38;2;205;105;74m";
6127
6513
  var ROSE_DIM = "\x1B[38;2;120;60;45m";
@@ -6131,7 +6517,7 @@ var BOLD = "\x1B[1m";
6131
6517
  var ITALIC = "\x1B[3m";
6132
6518
  var RESET = "\x1B[0m";
6133
6519
  function shortenPath(cwd) {
6134
- const home = homedir8();
6520
+ const home = homedir4();
6135
6521
  if (cwd.startsWith(home)) {
6136
6522
  return `~${cwd.slice(home.length).replace(/\\/g, "/")}`;
6137
6523
  }
@@ -7509,8 +7895,8 @@ async function promptInteractiveList(config) {
7509
7895
  }
7510
7896
 
7511
7897
  // src/security/scanner.ts
7512
- import { existsSync as existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
7513
- import { join as join8, relative as relative2, resolve as resolve17 } from "path";
7898
+ import { existsSync as existsSync18, readdirSync as readdirSync7, readFileSync as readFileSync12, statSync as statSync4 } from "fs";
7899
+ import { join as join10, relative as relative2, resolve as resolve17 } from "path";
7514
7900
  var SECURITY_RULES = [
7515
7901
  {
7516
7902
  id: "SEC-001",
@@ -7648,21 +8034,21 @@ async function runSecurityScan(targetDir, options = {}) {
7648
8034
  const findings = [];
7649
8035
  let scannedCount = 0;
7650
8036
  function walk(current) {
7651
- if (scannedCount >= maxFiles || !existsSync17(current))
8037
+ if (scannedCount >= maxFiles || !existsSync18(current))
7652
8038
  return;
7653
8039
  let entries;
7654
8040
  try {
7655
- entries = readdirSync5(current);
8041
+ entries = readdirSync7(current);
7656
8042
  } catch {
7657
8043
  return;
7658
8044
  }
7659
8045
  for (const entry of entries) {
7660
8046
  if (scannedCount >= maxFiles)
7661
8047
  break;
7662
- const fullPath = join8(current, entry);
8048
+ const fullPath = join10(current, entry);
7663
8049
  let stat;
7664
8050
  try {
7665
- stat = statSync3(fullPath);
8051
+ stat = statSync4(fullPath);
7666
8052
  } catch {
7667
8053
  continue;
7668
8054
  }
@@ -7767,8 +8153,8 @@ var __dirname = "/home/runner/work/agent-cli/agent-cli/src/mcp/servers/sqlite";
7767
8153
  var SQLITE_MCP_SERVER_PATH = resolve20(__dirname, "server.ts");
7768
8154
 
7769
8155
  // src/init/project-analyzer.ts
7770
- import { existsSync as existsSync18, readFileSync as readFileSync13, readdirSync as readdirSync6 } from "fs";
7771
- import { join as join9, basename as basename2 } from "path";
8156
+ import { existsSync as existsSync19, readFileSync as readFileSync13, readdirSync as readdirSync8 } from "fs";
8157
+ import { join as join11, basename as basename3 } from "path";
7772
8158
 
7773
8159
  class ProjectAnalyzer {
7774
8160
  cwd;
@@ -7786,8 +8172,8 @@ class ProjectAnalyzer {
7786
8172
  const architectureNotes = [];
7787
8173
  const codeConventions = [];
7788
8174
  let description = readmeInfo.description;
7789
- const pkgPath = join9(this.cwd, "package.json");
7790
- if (existsSync18(pkgPath)) {
8175
+ const pkgPath = join11(this.cwd, "package.json");
8176
+ if (existsSync19(pkgPath)) {
7791
8177
  try {
7792
8178
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
7793
8179
  if (!description && pkg.description)
@@ -7860,8 +8246,8 @@ class ProjectAnalyzer {
7860
8246
  }
7861
8247
  } catch {}
7862
8248
  }
7863
- const tsconfigPath = join9(this.cwd, "tsconfig.json");
7864
- if (existsSync18(tsconfigPath)) {
8249
+ const tsconfigPath = join11(this.cwd, "tsconfig.json");
8250
+ if (existsSync19(tsconfigPath)) {
7865
8251
  try {
7866
8252
  const tsconfig = JSON.parse(readFileSync13(tsconfigPath, "utf8"));
7867
8253
  if (tsconfig.compilerOptions?.strict) {
@@ -7872,8 +8258,8 @@ class ProjectAnalyzer {
7872
8258
  }
7873
8259
  } catch {}
7874
8260
  }
7875
- const cargoPath = join9(this.cwd, "Cargo.toml");
7876
- if (existsSync18(cargoPath)) {
8261
+ const cargoPath = join11(this.cwd, "Cargo.toml");
8262
+ if (existsSync19(cargoPath)) {
7877
8263
  try {
7878
8264
  commands.dev = commands.dev || "cargo run";
7879
8265
  commands.build = commands.build || "cargo build";
@@ -7882,8 +8268,8 @@ class ProjectAnalyzer {
7882
8268
  frameworks.push("Rust Cargo");
7883
8269
  } catch {}
7884
8270
  }
7885
- const goModPath = join9(this.cwd, "go.mod");
7886
- if (existsSync18(goModPath)) {
8271
+ const goModPath = join11(this.cwd, "go.mod");
8272
+ if (existsSync19(goModPath)) {
7887
8273
  try {
7888
8274
  commands.dev = commands.dev || "go run .";
7889
8275
  commands.build = commands.build || "go build ./...";
@@ -7892,37 +8278,37 @@ class ProjectAnalyzer {
7892
8278
  frameworks.push("Go Modules");
7893
8279
  } catch {}
7894
8280
  }
7895
- const pyprojectPath = join9(this.cwd, "pyproject.toml");
7896
- const requirementsPath = join9(this.cwd, "requirements.txt");
7897
- if (existsSync18(pyprojectPath) || existsSync18(requirementsPath)) {
8281
+ const pyprojectPath = join11(this.cwd, "pyproject.toml");
8282
+ const requirementsPath = join11(this.cwd, "requirements.txt");
8283
+ if (existsSync19(pyprojectPath) || existsSync19(requirementsPath)) {
7898
8284
  commands.test = commands.test || "pytest";
7899
8285
  commands.lint = commands.lint || "ruff check .";
7900
- if (existsSync18(join9(this.cwd, "uv.lock"))) {
8286
+ if (existsSync19(join11(this.cwd, "uv.lock"))) {
7901
8287
  frameworks.push("uv");
7902
8288
  commands.test = "uv run pytest";
7903
- } else if (existsSync18(join9(this.cwd, "poetry.lock"))) {
8289
+ } else if (existsSync19(join11(this.cwd, "poetry.lock"))) {
7904
8290
  frameworks.push("Poetry");
7905
8291
  commands.test = "poetry run pytest";
7906
8292
  }
7907
8293
  }
7908
- if (existsSync18(join9(this.cwd, "Dockerfile"))) {
8294
+ if (existsSync19(join11(this.cwd, "Dockerfile"))) {
7909
8295
  infrastructure.push("Docker");
7910
8296
  const sanitizedName = projectName.toLowerCase().replace(/[^a-z0-9_-]/g, "-").replace(/^-+|-+$/g, "");
7911
8297
  commands.dockerBuild = `docker build -t ${sanitizedName || "app"} .`;
7912
8298
  }
7913
- if (existsSync18(join9(this.cwd, "nginx.conf"))) {
8299
+ if (existsSync19(join11(this.cwd, "nginx.conf"))) {
7914
8300
  infrastructure.push("Nginx");
7915
8301
  }
7916
- if (existsSync18(join9(this.cwd, "src/api.ts")) || existsSync18(join9(this.cwd, "src/api"))) {
8302
+ if (existsSync19(join11(this.cwd, "src/api.ts")) || existsSync19(join11(this.cwd, "src/api"))) {
7917
8303
  architectureNotes.push("Backend API endpoints and network client logic are centralized in `src/api`.");
7918
8304
  }
7919
- if (existsSync18(join9(this.cwd, "src/components"))) {
8305
+ if (existsSync19(join11(this.cwd, "src/components"))) {
7920
8306
  architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
7921
8307
  }
7922
- if (existsSync18(join9(this.cwd, "src/types.ts")) || existsSync18(join9(this.cwd, "src/types"))) {
8308
+ if (existsSync19(join11(this.cwd, "src/types.ts")) || existsSync19(join11(this.cwd, "src/types"))) {
7923
8309
  architectureNotes.push("Shared TypeScript data models and interfaces are defined in `src/types`.");
7924
8310
  }
7925
- if (existsSync18(join9(this.cwd, ".env.example"))) {
8311
+ if (existsSync19(join11(this.cwd, ".env.example"))) {
7926
8312
  architectureNotes.push("Environment configuration template is in `.env.example`.");
7927
8313
  }
7928
8314
  if (commands.typecheck || commands.lint || commands.test) {
@@ -7939,7 +8325,7 @@ class ProjectAnalyzer {
7939
8325
  let hasExistingInstructions = false;
7940
8326
  let existingInstructionFile;
7941
8327
  for (const f of instructionFiles) {
7942
- if (existsSync18(join9(this.cwd, f))) {
8328
+ if (existsSync19(join11(this.cwd, f))) {
7943
8329
  hasExistingInstructions = true;
7944
8330
  existingInstructionFile = f;
7945
8331
  break;
@@ -8020,8 +8406,8 @@ class ProjectAnalyzer {
8020
8406
  extractReadmeMetadata() {
8021
8407
  const readmeFiles = ["README.md", "readme.md", "README.MD"];
8022
8408
  for (const file of readmeFiles) {
8023
- const fullPath = join9(this.cwd, file);
8024
- if (existsSync18(fullPath)) {
8409
+ const fullPath = join11(this.cwd, file);
8410
+ if (existsSync19(fullPath)) {
8025
8411
  try {
8026
8412
  const content = readFileSync13(fullPath, "utf8");
8027
8413
  const lines = content.split(`
@@ -8046,8 +8432,8 @@ class ProjectAnalyzer {
8046
8432
  return {};
8047
8433
  }
8048
8434
  detectProjectName() {
8049
- const pkgPath = join9(this.cwd, "package.json");
8050
- if (existsSync18(pkgPath)) {
8435
+ const pkgPath = join11(this.cwd, "package.json");
8436
+ if (existsSync19(pkgPath)) {
8051
8437
  try {
8052
8438
  const pkg = JSON.parse(readFileSync13(pkgPath, "utf8"));
8053
8439
  if (pkg.name && pkg.name !== "frontend" && pkg.name !== "backend" && pkg.name !== "app") {
@@ -8055,73 +8441,73 @@ class ProjectAnalyzer {
8055
8441
  }
8056
8442
  } catch {}
8057
8443
  }
8058
- const cargoPath = join9(this.cwd, "Cargo.toml");
8059
- if (existsSync18(cargoPath)) {
8444
+ const cargoPath = join11(this.cwd, "Cargo.toml");
8445
+ if (existsSync19(cargoPath)) {
8060
8446
  try {
8061
8447
  const match = readFileSync13(cargoPath, "utf8").match(/name\s*=\s*"([^"]+)"/);
8062
8448
  if (match?.[1])
8063
8449
  return match[1];
8064
8450
  } catch {}
8065
8451
  }
8066
- const goModPath = join9(this.cwd, "go.mod");
8067
- if (existsSync18(goModPath)) {
8452
+ const goModPath = join11(this.cwd, "go.mod");
8453
+ if (existsSync19(goModPath)) {
8068
8454
  try {
8069
8455
  const match = readFileSync13(goModPath, "utf8").match(/module\s+([^\s]+)/);
8070
8456
  if (match?.[1])
8071
- return basename2(match[1]);
8457
+ return basename3(match[1]);
8072
8458
  } catch {}
8073
8459
  }
8074
- return basename2(this.cwd);
8460
+ return basename3(this.cwd);
8075
8461
  }
8076
8462
  detectLanguages() {
8077
8463
  const langs = new Set;
8078
- if (existsSync18(join9(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8464
+ if (existsSync19(join11(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8079
8465
  langs.add("TypeScript");
8080
8466
  }
8081
- if (existsSync18(join9(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8467
+ if (existsSync19(join11(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8082
8468
  langs.add("JavaScript");
8083
8469
  }
8084
- if (existsSync18(join9(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8470
+ if (existsSync19(join11(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8085
8471
  langs.add("Rust");
8086
8472
  }
8087
- if (existsSync18(join9(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8473
+ if (existsSync19(join11(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8088
8474
  langs.add("Go");
8089
8475
  }
8090
- if (existsSync18(join9(this.cwd, "pyproject.toml")) || existsSync18(join9(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8476
+ if (existsSync19(join11(this.cwd, "pyproject.toml")) || existsSync19(join11(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
8091
8477
  langs.add("Python");
8092
8478
  }
8093
- if (existsSync18(join9(this.cwd, "pom.xml")) || existsSync18(join9(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8479
+ if (existsSync19(join11(this.cwd, "pom.xml")) || existsSync19(join11(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
8094
8480
  langs.add("Java");
8095
8481
  }
8096
- if (existsSync18(join9(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8482
+ if (existsSync19(join11(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
8097
8483
  langs.add("C/C++");
8098
8484
  }
8099
8485
  return Array.from(langs);
8100
8486
  }
8101
8487
  detectPackageManager() {
8102
- if (existsSync18(join9(this.cwd, "bun.lockb")) || existsSync18(join9(this.cwd, "bun.lock")))
8488
+ if (existsSync19(join11(this.cwd, "bun.lockb")) || existsSync19(join11(this.cwd, "bun.lock")))
8103
8489
  return "bun";
8104
- if (existsSync18(join9(this.cwd, "pnpm-lock.yaml")))
8490
+ if (existsSync19(join11(this.cwd, "pnpm-lock.yaml")))
8105
8491
  return "pnpm";
8106
- if (existsSync18(join9(this.cwd, "yarn.lock")))
8492
+ if (existsSync19(join11(this.cwd, "yarn.lock")))
8107
8493
  return "yarn";
8108
- if (existsSync18(join9(this.cwd, "package-lock.json")))
8494
+ if (existsSync19(join11(this.cwd, "package-lock.json")))
8109
8495
  return "npm";
8110
- if (existsSync18(join9(this.cwd, "Cargo.lock")) || existsSync18(join9(this.cwd, "Cargo.toml")))
8496
+ if (existsSync19(join11(this.cwd, "Cargo.lock")) || existsSync19(join11(this.cwd, "Cargo.toml")))
8111
8497
  return "cargo";
8112
- if (existsSync18(join9(this.cwd, "uv.lock")))
8498
+ if (existsSync19(join11(this.cwd, "uv.lock")))
8113
8499
  return "uv";
8114
- if (existsSync18(join9(this.cwd, "poetry.lock")))
8500
+ if (existsSync19(join11(this.cwd, "poetry.lock")))
8115
8501
  return "poetry";
8116
- if (existsSync18(join9(this.cwd, "go.sum")) || existsSync18(join9(this.cwd, "go.mod")))
8502
+ if (existsSync19(join11(this.cwd, "go.sum")) || existsSync19(join11(this.cwd, "go.mod")))
8117
8503
  return "go";
8118
- if (existsSync18(join9(this.cwd, "package.json")))
8504
+ if (existsSync19(join11(this.cwd, "package.json")))
8119
8505
  return "npm";
8120
8506
  return;
8121
8507
  }
8122
8508
  hasFileWithExtension(...exts) {
8123
8509
  try {
8124
- const entries = readdirSync6(this.cwd);
8510
+ const entries = readdirSync8(this.cwd);
8125
8511
  return entries.some((e) => exts.some((ext) => e.endsWith(ext)));
8126
8512
  } catch {
8127
8513
  return false;
@@ -8129,16 +8515,16 @@ class ProjectAnalyzer {
8129
8515
  }
8130
8516
  }
8131
8517
  // src/init/init-command.ts
8132
- import { existsSync as existsSync19, writeFileSync as writeFileSync7 } from "fs";
8133
- import { join as join10 } from "path";
8518
+ import { existsSync as existsSync20, writeFileSync as writeFileSync7 } from "fs";
8519
+ import { join as join12 } from "path";
8134
8520
  function runProjectInit(options = {}) {
8135
8521
  const cwd = options.cwd || process.cwd();
8136
8522
  const filename = options.filename || "AGENTS.md";
8137
- const targetPath = join10(cwd, filename);
8523
+ const targetPath = join12(cwd, filename);
8138
8524
  const analyzer = new ProjectAnalyzer(cwd);
8139
8525
  const analysis = analyzer.analyze();
8140
8526
  const content = analyzer.generateAgentsMarkdown(analysis);
8141
- const alreadyExists = existsSync19(targetPath);
8527
+ const alreadyExists = existsSync20(targetPath);
8142
8528
  writeFileSync7(targetPath, content, "utf8");
8143
8529
  return {
8144
8530
  success: true,
@@ -8513,7 +8899,7 @@ Browser authorization opened automatically. If not, open:`);
8513
8899
  console.log(style.dim(`Waiting for authorization callback on port 1455 ...`));
8514
8900
  const creds = await waitForToken();
8515
8901
  console.log(style.green(`
8516
- \u2713 Authentication Successful! Token saved to ~/.groupy/credentials.json`));
8902
+ \u2713 Authentication Successful! Token saved to ~/.pikaa/credentials.json`));
8517
8903
  console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
8518
8904
  } catch (err) {
8519
8905
  console.log(style.yellow(`
@@ -8531,7 +8917,7 @@ Falling back to Direct Terminal Login:`));
8531
8917
  password
8532
8918
  });
8533
8919
  console.log(style.green(`
8534
- \u2713 Successfully logged in! Token saved to ~/.groupy/credentials.json`));
8920
+ \u2713 Successfully logged in! Token saved to ~/.pikaa/credentials.json`));
8535
8921
  console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
8536
8922
  } catch (directErr) {
8537
8923
  console.error(style.red(`
@@ -8744,7 +9130,7 @@ async function handleSkillsCommand(ctx, args) {
8744
9130
  const skills = loader.listSkills(ctx.session.cwd, { includeDisabled: true });
8745
9131
  if (skills.length === 0) {
8746
9132
  console.log(style.dim(`
8747
- No domain skills discovered in .agents/skills/ or ~/.groupy/skills/
9133
+ No domain skills discovered in .agents/skills/ or ~/.pikaa/skills/
8748
9134
  `));
8749
9135
  return;
8750
9136
  }
@@ -8800,17 +9186,51 @@ async function handleSkillsCommand(ctx, args) {
8800
9186
  function printMemories(ctx) {
8801
9187
  const store2 = ctx.memoryStore;
8802
9188
  if (!store2) {
8803
- console.log(style.yellow("Memory store not active."));
9189
+ console.log(style.yellow(`
9190
+ Memory store not active.
9191
+ `));
8804
9192
  return;
8805
9193
  }
8806
- const memories = store2.getAllMemories(ctx.session.cwd);
9194
+ const cwd = ctx.session.cwd;
9195
+ const memoryDir = store2.getProjectMemoryDir(cwd);
9196
+ const topics = store2.listProjectMemories(cwd);
9197
+ const indexContent = store2.loadMemoryIndex(cwd);
9198
+ const BOLD2 = "\x1B[1m";
9199
+ const RESET2 = "\x1B[0m";
9200
+ const DIM = "\x1B[2m";
9201
+ const CYAN = "\x1B[38;2;120;190;255m";
9202
+ const GREEN = "\x1B[38;2;140;220;140m";
9203
+ const ORANGE = "\x1B[38;2;217;119;87m";
9204
+ const PURPLE = "\x1B[38;2;190;140;240m";
9205
+ const YELLOW = "\x1B[38;2;250;210;110m";
9206
+ const getCategoryColor = (cat) => {
9207
+ switch (cat.toLowerCase()) {
9208
+ case "user":
9209
+ return CYAN;
9210
+ case "feedback":
9211
+ return GREEN;
9212
+ case "project":
9213
+ return ORANGE;
9214
+ case "reference":
9215
+ return PURPLE;
9216
+ default:
9217
+ return YELLOW;
9218
+ }
9219
+ };
8807
9220
  console.log();
8808
- if (memories.length === 0) {
8809
- console.log(style.dim(" No persistent memories recorded yet."));
9221
+ console.log(` ${BOLD2}\uD83E\uDDE0 Project Auto-Memory Bank${RESET2}`);
9222
+ console.log(` ${DIM}Directory: ${memoryDir}${RESET2}`);
9223
+ console.log(` ${DIM}Status: ${GREEN}Active (Loaded into turn context \u2264200 lines)${RESET2}`);
9224
+ console.log();
9225
+ if (topics.length === 0) {
9226
+ console.log(` ${DIM}No persistent topic memories saved for this project yet.${RESET2}`);
9227
+ console.log(` ${DIM}As you work, Pikaa automatically records user preferences, feedback, and project context.${RESET2}`);
8810
9228
  } else {
8811
- console.log(style.bold(" Learned Preferences & Memories:"));
8812
- for (const m of memories) {
8813
- console.log(` \u2022 [${style.cyan(m.category)}] (${style.dim(m.scope)}): ${m.content}`);
9229
+ console.log(` ${BOLD2}Learned Memory Topics (${topics.length}):${RESET2}`);
9230
+ for (const t of topics) {
9231
+ const color = getCategoryColor(t.type);
9232
+ console.log(` \u2022 ${color}[${t.type}]${RESET2} ${BOLD2}${t.name}${RESET2}: ${DIM}${t.description || t.content.split(`
9233
+ `)[0]}${RESET2}`);
8814
9234
  }
8815
9235
  }
8816
9236
  console.log();
@@ -9190,7 +9610,7 @@ function printReleaseNotes() {
9190
9610
  "",
9191
9611
  " " + BOLD2 + WHITE2 + "\uD83D\uDE80 What's New in " + version + " (Latest)" + RESET2,
9192
9612
  " " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Persistent Default AI Model" + RESET2 + ": Switch via " + ROSE2 + "/model" + RESET2 + " and save",
9193
- " preference across sessions in ~/.groupy/credentials.json.",
9613
+ " preference across sessions in ~/.pikaa/credentials.json.",
9194
9614
  " " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Real-time Git Branch Detection" + RESET2 + ": Header displays active branch",
9195
9615
  " (\uE0A0 main) alongside user subscription tier (Groupy Pro / Max).",
9196
9616
  " " + ROSE2 + "\u2022" + RESET2 + " " + WHITE2 + BOLD2 + "Claude Code Terminal UI Parity" + RESET2 + ": Authentic pixel emblem, fieldset",
@@ -9569,9 +9989,9 @@ class MarkdownHighlighter {
9569
9989
  }
9570
9990
 
9571
9991
  // src/cli/update-checker.ts
9572
- import { existsSync as existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
9573
- import { homedir as homedir9 } from "os";
9574
- import { join as join11 } from "path";
9992
+ import { existsSync as existsSync21, mkdirSync as mkdirSync11, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
9993
+ import { homedir as homedir5 } from "os";
9994
+ import { join as join13 } from "path";
9575
9995
  var CHECK_INTERVAL_MS = 12 * 60 * 60 * 1000;
9576
9996
  function parseSemver(v) {
9577
9997
  const clean = v.replace(/^v/, "").trim();
@@ -9592,8 +10012,8 @@ function isNewerVersion(current, remote) {
9592
10012
  return remPatch > curPatch;
9593
10013
  }
9594
10014
  function getUpdateCachePath() {
9595
- const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join11(homedir9(), ".pikaa");
9596
- return join11(baseDir, "update-cache.json");
10015
+ const baseDir = process.env.PIKAA_HOME || process.env.GROUPY_HOME || join13(homedir5(), ".pikaa");
10016
+ return join13(baseDir, "update-cache.json");
9597
10017
  }
9598
10018
  async function fetchLatestNpmVersion(packageName, timeoutMs = 1500) {
9599
10019
  const url = `https://registry.npmjs.org/${encodeURIComponent(packageName)}/latest`;
@@ -9626,7 +10046,7 @@ async function checkForUpdates(options = {}) {
9626
10046
  const cachePath = options.cachePath || getUpdateCachePath();
9627
10047
  const now = Date.now();
9628
10048
  let cached = null;
9629
- if (!options.force && existsSync20(cachePath)) {
10049
+ if (!options.force && existsSync21(cachePath)) {
9630
10050
  try {
9631
10051
  const raw = JSON.parse(readFileSync14(cachePath, "utf8"));
9632
10052
  if (raw && typeof raw.lastChecked === "number" && typeof raw.latestVersion === "string") {
@@ -9656,9 +10076,9 @@ async function checkForUpdates(options = {}) {
9656
10076
  return null;
9657
10077
  }
9658
10078
  try {
9659
- const parentDir = join11(cachePath, "..");
9660
- if (!existsSync20(parentDir)) {
9661
- mkdirSync10(parentDir, { recursive: true });
10079
+ const parentDir = join13(cachePath, "..");
10080
+ if (!existsSync21(parentDir)) {
10081
+ mkdirSync11(parentDir, { recursive: true });
9662
10082
  }
9663
10083
  const cacheData = {
9664
10084
  lastChecked: now,
@@ -10080,7 +10500,7 @@ async function main() {
10080
10500
  resolve21(cwd, "mcp_config.json")
10081
10501
  ].filter(Boolean);
10082
10502
  for (const cfg of candidateConfigs) {
10083
- if (existsSync21(cfg)) {
10503
+ if (existsSync22(cfg)) {
10084
10504
  try {
10085
10505
  await mcpManager.loadConfigFile(cfg);
10086
10506
  mcpManager.registerToolsIntoRouter(tools4);
@@ -10194,7 +10614,7 @@ Open the following link in your browser to complete authorization:`);
10194
10614
  Waiting for browser callback on http://localhost:1455/auth/callback ...`));
10195
10615
  const creds = await waitForToken();
10196
10616
  console.log(style.green(`
10197
- \u2713 Authentication Successful! Token saved to ~/.groupy/credentials.json`));
10617
+ \u2713 Authentication Successful! Token saved to ~/.pikaa/credentials.json`));
10198
10618
  console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
10199
10619
  } catch (err) {
10200
10620
  console.log(style.yellow(`
@@ -10213,7 +10633,7 @@ Falling back to Direct Terminal Login:`));
10213
10633
  password: password.trim()
10214
10634
  });
10215
10635
  console.log(style.green(`
10216
- \u2713 Successfully logged in! Token saved to ~/.groupy/credentials.json`));
10636
+ \u2713 Successfully logged in! Token saved to ~/.pikaa/credentials.json`));
10217
10637
  console.log(style.dim(` Gateway Base URL: ${creds.baseUrl}`));
10218
10638
  } catch (directErr) {
10219
10639
  rl.close();
@@ -10287,7 +10707,7 @@ function printSkillsList(loader, cwd) {
10287
10707
  const skills = loader.discoverSkills(cwd);
10288
10708
  console.log();
10289
10709
  if (skills.length === 0) {
10290
- console.log(style.dim("No skills found in .agents/skills/ or ~/.groupy/skills/"));
10710
+ console.log(style.dim("No skills found in .agents/skills/ or ~/.pikaa/skills/"));
10291
10711
  } else {
10292
10712
  console.log(style.bold("Discovered Domain Skills:"));
10293
10713
  for (const s of skills) {