@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 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) {
@@ -1345,13 +1472,13 @@ class Session {
1345
1472
  type: "StatusChanged",
1346
1473
  status: "waiting_approval"
1347
1474
  });
1348
- return new Promise((resolve4) => {
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
- resolve4(approved);
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((resolve4) => {
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
- resolve4(answer);
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((resolve4, reject) => {
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
- resolve4();
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((resolve4) => {
1442
- this.submissionResolvers.push(resolve4);
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 existsSync4, readFileSync as readFileSync4, writeFileSync as writeFileSync2 } from "fs";
1457
- import { resolve as resolve4, dirname as dirname2 } from "path";
1458
- 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";
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 = resolve4(ctx.cwd, rawPath);
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 (!existsSync4(filePath)) {
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
- mkdirSync2(dirname2(filePath), { recursive: true });
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 existsSync5 } from "fs";
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 = 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");
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 existsSync6 } from "fs";
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 = existsSync6("/usr/bin/sandbox-exec");
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 resolve5, normalize } from "path";
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(resolve5(cwd));
1882
+ const normCwd = normalize(resolve6(cwd));
1756
1883
  return {
1757
1884
  kind: "workspace-write",
1758
- readableRoots: [normCwd, resolve5(process.cwd())],
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 existsSync7, mkdirSync as mkdirSync3 } from "fs";
1805
- import { dirname as dirname3, resolve as resolve6 } from "path";
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 || resolve6(homedir3(), ".groupy", "groupy_rules.db");
1939
+ const effectivePath = dbOrPath || getPrefixRulesDbPath();
1815
1940
  if (effectivePath !== ":memory:") {
1816
- const dir = dirname3(effectivePath);
1817
- if (!existsSync7(dir)) {
1818
- mkdirSync3(dir, { recursive: true });
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 === "*" ? "*" : resolve6(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 = resolve6(workspacePath);
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 === "*" ? "*" : resolve6(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 === "*" ? "*" : resolve6(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((resolve7) => setTimeout(() => resolve7({ isTimeout: true }), timeoutMs));
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 existsSync8, statSync, mkdirSync as mkdirSync4 } from "fs";
2074
- 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";
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 = resolve7(ctx.cwd, String(args.path || ""));
2087
- if (!existsSync8(filePath)) {
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 = resolve7(ctx.cwd, String(args.path || "."));
2109
- if (!existsSync8(dirPath)) {
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 = readdirSync(dirPath);
2238
+ const entries = readdirSync2(dirPath);
2114
2239
  const formatted = entries.map((entry) => {
2115
- const full = resolve7(dirPath, entry);
2116
- const isDir = statSync(full).isDirectory();
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 = resolve7(ctx.cwd, rawPath);
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
- mkdirSync4(dirname4(filePath), { recursive: true });
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 readdirSync2, readFileSync as readFileSync6, statSync as statSync2, existsSync as existsSync9 } from "fs";
2281
- 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";
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 = resolve8(cwd, options.path || ".");
2327
- if (!existsSync9(searchRoot)) {
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 = resolve8(cwd, options.path || ".");
2377
- if (!existsSync9(searchRoot))
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 = join3(root, ".gitignore");
2417
- if (existsSync9(gitignorePath)) {
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 = statSync2(dir);
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 = readdirSync2(currentDir, { withFileTypes: true });
2573
+ const entries = readdirSync3(currentDir, { withFileTypes: true });
2449
2574
  for (const entry of entries) {
2450
- const fullPath = join3(currentDir, entry.name);
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
- scope: {
3093
+ name: {
2864
3094
  type: "string",
2865
- description: "'global' (applies to all projects) or 'workspace' (applies only to current repository). Defaults to 'global'.",
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 scope = args.scope || "global";
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
- scope,
3110
+ name,
2882
3111
  cwd: context.cwd
2883
3112
  });
2884
3113
  return {
2885
- 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}"`
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
- router2.register(createRememberTool(options.memoryStore));
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 existsSync10, readdirSync as readdirSync3, readFileSync as readFileSync7 } from "fs";
3036
- 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";
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 = resolve9(dirPath);
3121
- if (!existsSync10(fullPath))
3359
+ const fullPath = resolve10(dirPath);
3360
+ if (!existsSync11(fullPath))
3122
3361
  return;
3123
- const entries = readdirSync3(fullPath);
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(join4(fullPath, entry), "utf8");
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 resolve10 } from "path";
3180
- import { existsSync as existsSync11, mkdirSync as mkdirSync5 } from "fs";
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 || resolve10(homedir4(), ".groupy", "agent_graph.db");
3426
+ const dbPath = dbPathOrDb || getAgentGraphDbPath();
3190
3427
  if (dbPath !== ":memory:") {
3191
- const dir = resolve10(dbPath, "..");
3192
- if (!existsSync11(dir)) {
3193
- mkdirSync5(dir, { recursive: true });
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((resolve11, reject) => {
3317
- resolvePromise = resolve11;
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 existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4, mkdirSync as mkdirSync6 } from "fs";
3618
- 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";
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((resolve11, reject) => {
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: resolve11, reject, timer });
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((resolve11, reject) => {
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: resolve11, reject, timer });
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 = resolve11(filePath);
4234
- if (!existsSync12(fullPath))
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 = resolve11(filePath);
4488
- const dir = dirname5(fullPath);
4489
- if (!existsSync12(dir)) {
4490
- mkdirSync6(dir, { recursive: true });
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 (existsSync12(fullPath)) {
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 = resolve11(filePath);
4508
- if (!existsSync12(fullPath))
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 = join5(cwd, ".mcp.json");
4539
- if (existsSync12(workspaceConfig))
4775
+ const workspaceConfig = join6(cwd, ".mcp.json");
4776
+ if (existsSync13(workspaceConfig))
4540
4777
  return workspaceConfig;
4541
- const altConfig = join5(cwd, "mcp_config.json");
4542
- if (existsSync12(altConfig))
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 existsSync13, mkdirSync as mkdirSync7 } from "fs";
4564
- import { dirname as dirname6, resolve as resolve12 } from "path";
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 = dirname6(effectivePath);
4572
- if (!existsSync13(dir)) {
4573
- mkdirSync7(dir, { recursive: true });
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 resolve12(homedir5(), ".groupy", "groupy_threads.db");
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 existsSync14, readdirSync as readdirSync4, readFileSync as readFileSync9 } from "fs";
4808
- import { resolve as resolve13, join as join6 } from "path";
4809
- 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";
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 (existsSync14(cand) && !roots.includes(cand)) {
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(resolve13(homedir6(), ".groupy", "skills"), resolve13(homedir6(), ".gemini", "config", "skills"));
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) => existsSync14(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 = readdirSync4(root, { withFileTypes: true });
5140
+ const entries = readdirSync5(root, { withFileTypes: true });
4905
5141
  for (const entry of entries) {
4906
5142
  if (entry.isDirectory()) {
4907
- const skillDir = join6(root, entry.name);
4908
- const skillFilePath = join6(skillDir, "SKILL.md");
4909
- if (existsSync14(skillFilePath)) {
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 existsSync15, readFileSync as readFileSync10, writeFileSync as writeFileSync5, mkdirSync as mkdirSync8 } from "fs";
5033
- import { resolve as resolve14, dirname as dirname7 } from "path";
5034
- import { homedir as homedir7 } from "os";
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 || resolve14(homedir7(), ".groupy", "memories.md");
5275
+ this.globalPath = options.globalPath || getGlobalMemoriesPath();
5041
5276
  this.customWorkspacePath = options.workspacePath;
5042
5277
  }
5043
- getWorkspacePath(cwd) {
5044
- 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
+ }
5045
5290
  }
5046
- addMemory(params) {
5047
- const scope = params.scope || "global";
5048
- const targetFile = scope === "global" ? this.globalPath : this.getWorkspacePath(params.cwd || process.cwd());
5049
- const dir = dirname7(targetFile);
5050
- if (!existsSync15(dir)) {
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
- getAllMemories(cwd) {
5071
- const globalEntries = this.readMemoryFile(this.globalPath, "global");
5072
- const workspacePath = this.getWorkspacePath(cwd);
5073
- const workspaceEntries = existsSync15(workspacePath) ? this.readMemoryFile(workspacePath, "workspace") : [];
5074
- 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
+ };
5075
5368
  }
5076
- readMemoryFile(filePath, scope) {
5077
- if (!existsSync15(filePath))
5078
- 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
+ }
5079
5386
  try {
5080
- const content = readFileSync10(filePath, "utf8");
5081
- 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(`
5082
5395
  `);
5083
- const entries = [];
5084
- let currentCategory = "preference";
5085
- for (const line of lines) {
5086
- const trimmed = line.trim();
5087
- if (trimmed.startsWith("## Preferences") || trimmed.startsWith("## User Preferences")) {
5088
- currentCategory = "preference";
5089
- } else if (trimmed.startsWith("## Guidelines") || trimmed.startsWith("## Coding Guidelines")) {
5090
- currentCategory = "guideline";
5091
- } else if (trimmed.startsWith("## Architecture") || trimmed.startsWith("## Project Architecture")) {
5092
- currentCategory = "architecture";
5093
- } else if (trimmed.startsWith("## Notes") || trimmed.startsWith("## General Notes")) {
5094
- currentCategory = "note";
5095
- } else if (trimmed.startsWith("- ") || trimmed.startsWith("* ")) {
5096
- const itemText = trimmed.slice(2).trim();
5097
- if (itemText) {
5098
- entries.push({
5099
- id: `mem_${entries.length + 1}`,
5100
- category: currentCategory,
5101
- content: itemText,
5102
- scope,
5103
- createdAt: Date.now()
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
- writeMemoryFile(filePath, entries) {
5114
- const categories = {
5115
- preference: [],
5116
- guideline: [],
5117
- architecture: [],
5118
- note: []
5430
+ return {
5431
+ type,
5432
+ name,
5433
+ description,
5434
+ modified,
5435
+ content: bodyLines.join(`
5436
+ `).trim(),
5437
+ filePath
5119
5438
  };
5120
- for (const entry of entries) {
5121
- 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 {}
5122
5457
  }
5123
- let markdown = `# Groupy Persistent Memories
5124
-
5125
- `;
5126
- if (categories.preference.length > 0) {
5127
- markdown += `## User Preferences
5128
- ${categories.preference.map((p) => `- ${p}`).join(`
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
- if (categories.guideline.length > 0) {
5134
- markdown += `## Coding Guidelines
5135
- ${categories.guideline.map((g) => `- ${g}`).join(`
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
- if (categories.architecture.length > 0) {
5141
- markdown += `## Project Architecture
5142
- ${categories.architecture.map((a) => `- ${a}`).join(`
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
- if (categories.note.length > 0) {
5148
- markdown += `## General Notes
5149
- ${categories.note.map((n) => `- ${n}`).join(`
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
- writeFileSync5(filePath, markdown.trim() + `
5155
- `, "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;
5156
5529
  }
5157
5530
  formatMemoriesPrompt(cwd) {
5158
- const memories = this.getAllMemories(cwd);
5159
- if (memories.length === 0)
5531
+ const indexContent = this.loadMemoryIndex(cwd);
5532
+ if (!indexContent)
5160
5533
  return "";
5161
- const lines = memories.map((m) => `- [${m.category}] (${m.scope}): ${m.content}`);
5162
- return `
5163
- ## Persistent User Preferences & Memory Bank
5164
- <user_memories>
5165
- ${lines.join(`
5166
- `)}
5167
- </user_memories>
5168
- 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
+ `);
5169
5544
  }
5170
5545
  }
5171
5546
 
5172
5547
  // src/worktree/manager.ts
5173
- import { resolve as resolve16, join as join7 } from "path";
5174
- 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";
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 (!existsSync16(worktreeParent)) {
5325
- mkdirSync9(worktreeParent, { recursive: true });
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 = join7(targetDir, "groupy-thread.json");
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 = join7(wt.path, "groupy-thread.json");
5359
- if (existsSync16(metaPath)) {
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 createHash("sha256").update(verifier).digest("base64url");
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.14",
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 homedir8 } from "os";
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 = homedir8();
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
- process.stdout.write("\x1B[J");
6765
- process.stdout.write(`\r\x1B[2K${this.promptSymbol}${buffer}`);
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
- renderedMenuLines = menuLines.length;
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
- renderedMenuLines = menuLines.length;
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
- renderedMenuLines = 2;
6850
- process.stdout.write(`\x1B[2A`);
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
- const visiblePromptLength = this.promptSymbol.replace(/\x1b\[[0-9;]*m/g, "").length;
6853
- const cursorCol = visiblePromptLength + cursor;
7231
+ process.stdout.write("\r");
6854
7232
  if (cursorCol > 0) {
6855
- process.stdout.write(`\r\x1B[${cursorCol}C`);
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(`\x1B[1A\r\x1B[2K\x1B[J${CliFormatter.formatClaudeUserPrompt(result)}
7258
+ process.stdout.write(`${CliFormatter.formatClaudeUserPrompt(result)}
6879
7259
 
6880
7260
  `);
6881
7261
  } else {
6882
- process.stdout.write(`\x1B[1A\r\x1B[2K\x1B[J
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
- clearMenu();
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
- if (renderedMenuLines > 0) {
6919
- popupDismissed = true;
6920
- clearMenu();
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 existsSync17, readdirSync as readdirSync5, readFileSync as readFileSync12, statSync as statSync3 } from "fs";
7524
- import { join as join8, relative as relative2, resolve as resolve17 } from "path";
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 || !existsSync17(current))
8042
+ if (scannedCount >= maxFiles || !existsSync18(current))
7663
8043
  return;
7664
8044
  let entries;
7665
8045
  try {
7666
- entries = readdirSync5(current);
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 = join8(current, entry);
8053
+ const fullPath = join10(current, entry);
7674
8054
  let stat;
7675
8055
  try {
7676
- stat = statSync3(fullPath);
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 existsSync18, readFileSync as readFileSync13, readdirSync as readdirSync6 } from "fs";
7782
- import { join as join9, basename as basename2 } from "path";
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 = join9(this.cwd, "package.json");
7801
- if (existsSync18(pkgPath)) {
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 = join9(this.cwd, "tsconfig.json");
7875
- if (existsSync18(tsconfigPath)) {
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 = join9(this.cwd, "Cargo.toml");
7887
- if (existsSync18(cargoPath)) {
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 = join9(this.cwd, "go.mod");
7897
- if (existsSync18(goModPath)) {
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 = join9(this.cwd, "pyproject.toml");
7907
- const requirementsPath = join9(this.cwd, "requirements.txt");
7908
- if (existsSync18(pyprojectPath) || existsSync18(requirementsPath)) {
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 (existsSync18(join9(this.cwd, "uv.lock"))) {
8291
+ if (existsSync19(join11(this.cwd, "uv.lock"))) {
7912
8292
  frameworks.push("uv");
7913
8293
  commands.test = "uv run pytest";
7914
- } else if (existsSync18(join9(this.cwd, "poetry.lock"))) {
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 (existsSync18(join9(this.cwd, "Dockerfile"))) {
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 (existsSync18(join9(this.cwd, "nginx.conf"))) {
8304
+ if (existsSync19(join11(this.cwd, "nginx.conf"))) {
7925
8305
  infrastructure.push("Nginx");
7926
8306
  }
7927
- if (existsSync18(join9(this.cwd, "src/api.ts")) || existsSync18(join9(this.cwd, "src/api"))) {
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 (existsSync18(join9(this.cwd, "src/components"))) {
8310
+ if (existsSync19(join11(this.cwd, "src/components"))) {
7931
8311
  architectureNotes.push("Reusable UI presentation components live in `src/components/`.");
7932
8312
  }
7933
- if (existsSync18(join9(this.cwd, "src/types.ts")) || existsSync18(join9(this.cwd, "src/types"))) {
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 (existsSync18(join9(this.cwd, ".env.example"))) {
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 (existsSync18(join9(this.cwd, f))) {
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 = join9(this.cwd, file);
8035
- if (existsSync18(fullPath)) {
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 = join9(this.cwd, "package.json");
8061
- if (existsSync18(pkgPath)) {
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 = join9(this.cwd, "Cargo.toml");
8070
- if (existsSync18(cargoPath)) {
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 = join9(this.cwd, "go.mod");
8078
- if (existsSync18(goModPath)) {
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 basename2(match[1]);
8462
+ return basename3(match[1]);
8083
8463
  } catch {}
8084
8464
  }
8085
- return basename2(this.cwd);
8465
+ return basename3(this.cwd);
8086
8466
  }
8087
8467
  detectLanguages() {
8088
8468
  const langs = new Set;
8089
- if (existsSync18(join9(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8469
+ if (existsSync19(join11(this.cwd, "tsconfig.json")) || this.hasFileWithExtension(".ts", ".tsx")) {
8090
8470
  langs.add("TypeScript");
8091
8471
  }
8092
- if (existsSync18(join9(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8472
+ if (existsSync19(join11(this.cwd, "package.json")) || this.hasFileWithExtension(".js", ".jsx", ".mjs")) {
8093
8473
  langs.add("JavaScript");
8094
8474
  }
8095
- if (existsSync18(join9(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8475
+ if (existsSync19(join11(this.cwd, "Cargo.toml")) || this.hasFileWithExtension(".rs")) {
8096
8476
  langs.add("Rust");
8097
8477
  }
8098
- if (existsSync18(join9(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8478
+ if (existsSync19(join11(this.cwd, "go.mod")) || this.hasFileWithExtension(".go")) {
8099
8479
  langs.add("Go");
8100
8480
  }
8101
- if (existsSync18(join9(this.cwd, "pyproject.toml")) || existsSync18(join9(this.cwd, "requirements.txt")) || this.hasFileWithExtension(".py")) {
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 (existsSync18(join9(this.cwd, "pom.xml")) || existsSync18(join9(this.cwd, "build.gradle")) || this.hasFileWithExtension(".java")) {
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 (existsSync18(join9(this.cwd, "CMakeLists.txt")) || this.hasFileWithExtension(".cpp", ".c", ".h", ".hpp")) {
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 (existsSync18(join9(this.cwd, "bun.lockb")) || existsSync18(join9(this.cwd, "bun.lock")))
8493
+ if (existsSync19(join11(this.cwd, "bun.lockb")) || existsSync19(join11(this.cwd, "bun.lock")))
8114
8494
  return "bun";
8115
- if (existsSync18(join9(this.cwd, "pnpm-lock.yaml")))
8495
+ if (existsSync19(join11(this.cwd, "pnpm-lock.yaml")))
8116
8496
  return "pnpm";
8117
- if (existsSync18(join9(this.cwd, "yarn.lock")))
8497
+ if (existsSync19(join11(this.cwd, "yarn.lock")))
8118
8498
  return "yarn";
8119
- if (existsSync18(join9(this.cwd, "package-lock.json")))
8499
+ if (existsSync19(join11(this.cwd, "package-lock.json")))
8120
8500
  return "npm";
8121
- if (existsSync18(join9(this.cwd, "Cargo.lock")) || existsSync18(join9(this.cwd, "Cargo.toml")))
8501
+ if (existsSync19(join11(this.cwd, "Cargo.lock")) || existsSync19(join11(this.cwd, "Cargo.toml")))
8122
8502
  return "cargo";
8123
- if (existsSync18(join9(this.cwd, "uv.lock")))
8503
+ if (existsSync19(join11(this.cwd, "uv.lock")))
8124
8504
  return "uv";
8125
- if (existsSync18(join9(this.cwd, "poetry.lock")))
8505
+ if (existsSync19(join11(this.cwd, "poetry.lock")))
8126
8506
  return "poetry";
8127
- if (existsSync18(join9(this.cwd, "go.sum")) || existsSync18(join9(this.cwd, "go.mod")))
8507
+ if (existsSync19(join11(this.cwd, "go.sum")) || existsSync19(join11(this.cwd, "go.mod")))
8128
8508
  return "go";
8129
- if (existsSync18(join9(this.cwd, "package.json")))
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 = readdirSync6(this.cwd);
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 existsSync19, writeFileSync as writeFileSync7 } from "fs";
8144
- import { join as join10 } from "path";
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 = join10(cwd, filename);
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 = existsSync19(targetPath);
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 ~/.groupy/credentials.json`));
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 ~/.groupy/credentials.json`));
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 ~/.groupy/skills/
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("Memory store not active."));
9194
+ console.log(style.yellow(`
9195
+ Memory store not active.
9196
+ `));
8815
9197
  return;
8816
9198
  }
8817
- const memories = store2.getAllMemories(ctx.session.cwd);
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
- if (memories.length === 0) {
8820
- console.log(style.dim(" No persistent memories recorded yet."));
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(style.bold(" Learned Preferences & Memories:"));
8823
- for (const m of memories) {
8824
- console.log(` \u2022 [${style.cyan(m.category)}] (${style.dim(m.scope)}): ${m.content}`);
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 ~/.groupy/credentials.json.",
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 existsSync20, mkdirSync as mkdirSync10, readFileSync as readFileSync14, writeFileSync as writeFileSync8 } from "fs";
9584
- import { homedir as homedir9 } from "os";
9585
- import { join as join11 } from "path";
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 || join11(homedir9(), ".pikaa");
9607
- return join11(baseDir, "update-cache.json");
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 && existsSync20(cachePath)) {
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 = join11(cachePath, "..");
9671
- if (!existsSync20(parentDir)) {
9672
- mkdirSync10(parentDir, { recursive: true });
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 (existsSync21(cfg)) {
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 ~/.groupy/credentials.json`));
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 ~/.groupy/credentials.json`));
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 ~/.groupy/skills/"));
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) {