@axhub/genie 0.2.6 → 0.2.8

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.
Files changed (102) hide show
  1. package/dist/api-docs.html +2 -2
  2. package/dist/assets/App-CTKZtqB1.js +460 -0
  3. package/dist/assets/{ReviewApp-BEicSBzW.js → ReviewApp-DM6BNAzR.js} +1 -1
  4. package/dist/assets/{_basePickBy-DkiHsp3X.js → _basePickBy-CqJbRZ9y.js} +1 -1
  5. package/dist/assets/{_baseUniq-7ElXb2sX.js → _baseUniq-BS8YH8jO.js} +1 -1
  6. package/dist/assets/{arc-CEsS3MdK.js → arc-BBmKEN-S.js} +1 -1
  7. package/dist/assets/{architectureDiagram-2XIMDMQ5-BubZ7T3U.js → architectureDiagram-2XIMDMQ5-N5lcb82R.js} +1 -1
  8. package/dist/assets/{blockDiagram-WCTKOSBZ-Cza6M6Ht.js → blockDiagram-WCTKOSBZ-DTMwHuLn.js} +1 -1
  9. package/dist/assets/{c4Diagram-IC4MRINW-jhjtOQ12.js → c4Diagram-IC4MRINW-BTKlkXI9.js} +1 -1
  10. package/dist/assets/channel-1oJBvF-0.js +1 -0
  11. package/dist/assets/{chunk-4BX2VUAB--HkodwbY.js → chunk-4BX2VUAB-DUdoTxAc.js} +1 -1
  12. package/dist/assets/{chunk-55IACEB6-CyBuez4e.js → chunk-55IACEB6-Bm_92xe4.js} +1 -1
  13. package/dist/assets/{chunk-FMBD7UC4-CuzG4iAl.js → chunk-FMBD7UC4-CGW0g62g.js} +1 -1
  14. package/dist/assets/{chunk-JSJVCQXG-BNi8S861.js → chunk-JSJVCQXG-DYkTH3w1.js} +1 -1
  15. package/dist/assets/{chunk-KX2RTZJC-D817O-GT.js → chunk-KX2RTZJC-C9oTlISU.js} +1 -1
  16. package/dist/assets/{chunk-NQ4KR5QH-DyujyOvx.js → chunk-NQ4KR5QH-CM50ygWP.js} +1 -1
  17. package/dist/assets/{chunk-QZHKN3VN-VMEn-zxh.js → chunk-QZHKN3VN-7dzpYeNJ.js} +1 -1
  18. package/dist/assets/{chunk-WL4C6EOR-CQHHFLvx.js → chunk-WL4C6EOR-Cm9nQrsr.js} +1 -1
  19. package/dist/assets/classDiagram-VBA2DB6C-d5TeKFM4.js +1 -0
  20. package/dist/assets/classDiagram-v2-RAHNMMFH-d5TeKFM4.js +1 -0
  21. package/dist/assets/clone-CinxIlEu.js +1 -0
  22. package/dist/assets/{cose-bilkent-S5V4N54A-qykDd54p.js → cose-bilkent-S5V4N54A-Ccp_p0JZ.js} +1 -1
  23. package/dist/assets/{dagre-KLK3FWXG-Bqp7DjEa.js → dagre-KLK3FWXG-fBwTLUp9.js} +1 -1
  24. package/dist/assets/{diagram-E7M64L7V-BKtx468K.js → diagram-E7M64L7V-CeNVmFUp.js} +1 -1
  25. package/dist/assets/{diagram-IFDJBPK2--fHfW6V2.js → diagram-IFDJBPK2-CtavyLGa.js} +1 -1
  26. package/dist/assets/{diagram-P4PSJMXO-D1kQI5RB.js → diagram-P4PSJMXO-CpQTjQwc.js} +1 -1
  27. package/dist/assets/{erDiagram-INFDFZHY-DT9YzdNw.js → erDiagram-INFDFZHY-B8R5vwhd.js} +1 -1
  28. package/dist/assets/{flowDiagram-PKNHOUZH-DWeNr4yg.js → flowDiagram-PKNHOUZH-BvkVVwIQ.js} +1 -1
  29. package/dist/assets/{ganttDiagram-A5KZAMGK--IgwcUhI.js → ganttDiagram-A5KZAMGK-DOu3hSNa.js} +1 -1
  30. package/dist/assets/{gitGraphDiagram-K3NZZRJ6-B5a8UWjN.js → gitGraphDiagram-K3NZZRJ6-C7zT67YE.js} +1 -1
  31. package/dist/assets/{graph-Cw1rYoD9.js → graph-D11wiwHo.js} +1 -1
  32. package/dist/assets/{highlighted-body-TPN3WLV5-BCxJHuqY.js → highlighted-body-TPN3WLV5-Babpthg-.js} +1 -1
  33. package/dist/assets/index-DFxzgWoO.js +2 -0
  34. package/dist/assets/index-YCFGDVKw.css +1 -0
  35. package/dist/assets/{infoDiagram-LFFYTUFH-D2u70rhN.js → infoDiagram-LFFYTUFH-BmA7IpQG.js} +1 -1
  36. package/dist/assets/{ishikawaDiagram-PHBUUO56-Cl8yrezU.js → ishikawaDiagram-PHBUUO56-BEquZd3E.js} +1 -1
  37. package/dist/assets/{journeyDiagram-4ABVD52K-ddP0AMU9.js → journeyDiagram-4ABVD52K-BfemGz7f.js} +1 -1
  38. package/dist/assets/{kanban-definition-K7BYSVSG-DbVt0v29.js → kanban-definition-K7BYSVSG-CWja3mln.js} +1 -1
  39. package/dist/assets/{layout-W_tRx4UV.js → layout-BLUNf-PJ.js} +1 -1
  40. package/dist/assets/{linear-CcMb2ay-.js → linear-DukIV_Xv.js} +1 -1
  41. package/dist/assets/{mermaid-O7DHMXV3-BBJqt8pT.js → mermaid-O7DHMXV3-SgtM28qI.js} +265 -215
  42. package/dist/assets/{mindmap-definition-YRQLILUH-BGhZa7Na.js → mindmap-definition-YRQLILUH-4UjqXITU.js} +1 -1
  43. package/dist/assets/{pieDiagram-SKSYHLDU-CDyJaACv.js → pieDiagram-SKSYHLDU-8AxqJd0M.js} +1 -1
  44. package/dist/assets/{quadrantDiagram-337W2JSQ-BSYuqf0Q.js → quadrantDiagram-337W2JSQ-D60m8V8r.js} +1 -1
  45. package/dist/assets/{requirementDiagram-Z7DCOOCP-Cfi9YX9H.js → requirementDiagram-Z7DCOOCP-zqh9jBVf.js} +1 -1
  46. package/dist/assets/{sankeyDiagram-WA2Y5GQK-Di1ShaMF.js → sankeyDiagram-WA2Y5GQK-CDZILTLI.js} +1 -1
  47. package/dist/assets/{sequenceDiagram-2WXFIKYE-CYTTG38e.js → sequenceDiagram-2WXFIKYE-7BReFd0L.js} +1 -1
  48. package/dist/assets/{stateDiagram-RAJIS63D-CVZYMqyW.js → stateDiagram-RAJIS63D-HPTVdIG4.js} +1 -1
  49. package/dist/assets/stateDiagram-v2-FVOUBMTO-DTUf5_gC.js +1 -0
  50. package/dist/assets/{timeline-definition-YZTLITO2-B1sdb5mK.js → timeline-definition-YZTLITO2-CTVllFgr.js} +1 -1
  51. package/dist/assets/{treemap-KZPCXAKY-CGG4gx3C.js → treemap-KZPCXAKY-BtyxboJZ.js} +1 -1
  52. package/dist/assets/{vennDiagram-LZ73GAT5-Dds37L2k.js → vennDiagram-LZ73GAT5-D96ZI6Mg.js} +1 -1
  53. package/dist/assets/{xychartDiagram-JWTSCODW-C8QKSyRR.js → xychartDiagram-JWTSCODW-eRk-39YO.js} +1 -1
  54. package/dist/index.html +2 -2
  55. package/package.json +35 -33
  56. package/server/_legacy-providers/README.md +30 -0
  57. package/server/_legacy-providers/claude-sdk.js +956 -0
  58. package/server/_legacy-providers/gemini-cli.js +368 -0
  59. package/server/_legacy-providers/openai-codex.js +705 -0
  60. package/server/_legacy-providers/opencode-cli.js +674 -0
  61. package/server/acp-runtime/client.js +1872 -0
  62. package/server/acp-runtime/index.js +408 -0
  63. package/server/acp-runtime/registry.js +45 -0
  64. package/server/acp-runtime/session-store.js +254 -0
  65. package/server/channels/runtime/AgentRuntimeAdapter.js +22 -80
  66. package/server/claude-sdk.js +24 -946
  67. package/server/cli.js +140 -2
  68. package/server/external-agent/service.js +52 -63
  69. package/server/gemini-cli.js +21 -360
  70. package/server/index.js +133 -58
  71. package/server/openai-codex.js +19 -695
  72. package/server/opencode-cli.js +68 -640
  73. package/server/projects.js +128 -85
  74. package/server/routes/agent.js +2 -0
  75. package/server/routes/cc-connect.js +1131 -0
  76. package/server/routes/cli-auth.js +1 -73
  77. package/server/routes/commands.js +4 -9
  78. package/server/routes/git.js +3 -20
  79. package/server/routes/projects.js +45 -24
  80. package/server/routes/session-core.js +44 -10
  81. package/server/session-core/abortSession.js +2 -18
  82. package/server/session-core/eventStore.js +5 -1
  83. package/server/session-core/providerAdapters.js +98 -10
  84. package/server/session-core/providerDiscovery.js +8 -3
  85. package/server/session-core/runtimeState.js +16 -17
  86. package/server/session-core/runtimeWriter.js +19 -12
  87. package/server/utils/ccConnectManager.js +390 -0
  88. package/server/utils/ccConnectState.js +575 -0
  89. package/server/utils/resolveCommandPath.js +71 -0
  90. package/server/utils/workspaceRoots.js +154 -0
  91. package/shared/conversationEvents.js +347 -10
  92. package/dist/assets/App-CYTE30Cf.js +0 -484
  93. package/dist/assets/channel-RmqTALN0.js +0 -1
  94. package/dist/assets/classDiagram-VBA2DB6C-wvVV1ggz.js +0 -1
  95. package/dist/assets/classDiagram-v2-RAHNMMFH-wvVV1ggz.js +0 -1
  96. package/dist/assets/clone-oT5aWXpf.js +0 -1
  97. package/dist/assets/index-CBuAXA5S.js +0 -2
  98. package/dist/assets/index-CyLWKyxy.css +0 -1
  99. package/dist/assets/stateDiagram-v2-FVOUBMTO-Bbl0b4-i.js +0 -1
  100. package/server/cli.test.js +0 -76
  101. package/server/external-agent/service.test.js +0 -53
  102. package/server/external-agent/ws.test.js +0 -289
@@ -1107,14 +1107,27 @@ async function getSessionMessages(projectName, sessionId, limit = null, offset =
1107
1107
  // Rename a project's display name
1108
1108
  async function renameProject(projectName, newDisplayName) {
1109
1109
  const config = await loadProjectConfig();
1110
+ const existingConfig = config[projectName] && typeof config[projectName] === 'object'
1111
+ ? config[projectName]
1112
+ : {};
1113
+ const trimmedDisplayName = typeof newDisplayName === 'string'
1114
+ ? newDisplayName.trim()
1115
+ : '';
1110
1116
 
1111
- if (!newDisplayName || newDisplayName.trim() === '') {
1112
- // Remove custom name if empty, will fall back to auto-generated
1113
- delete config[projectName];
1117
+ if (!trimmedDisplayName) {
1118
+ // Remove only the custom display name while preserving other metadata such as
1119
+ // manuallyAdded/originalPath so externally added projects do not disappear.
1120
+ const { displayName: _removedDisplayName, ...remainingConfig } = existingConfig;
1121
+
1122
+ if (Object.keys(remainingConfig).length === 0) {
1123
+ delete config[projectName];
1124
+ } else {
1125
+ config[projectName] = remainingConfig;
1126
+ }
1114
1127
  } else {
1115
- // Set custom display name
1116
1128
  config[projectName] = {
1117
- displayName: newDisplayName.trim()
1129
+ ...existingConfig,
1130
+ displayName: trimmedDisplayName
1118
1131
  };
1119
1132
  }
1120
1133
 
@@ -1252,7 +1265,41 @@ async function addProjectManually(projectPath, displayName = null) {
1252
1265
  const projectDir = path.join(os.homedir(), '.claude', 'projects', projectName);
1253
1266
 
1254
1267
  if (config[projectName]) {
1255
- throw new Error(`Project already configured for path: ${absolutePath}`);
1268
+ const existingConfig = config[projectName] && typeof config[projectName] === 'object'
1269
+ ? config[projectName]
1270
+ : {};
1271
+ const hasManualMetadata = Boolean(existingConfig.manuallyAdded || existingConfig.originalPath || existingConfig.path);
1272
+
1273
+ if (hasManualMetadata) {
1274
+ throw new Error(`Project already configured for path: ${absolutePath}`);
1275
+ }
1276
+
1277
+ // Recover historical broken entries that lost their manual-project metadata
1278
+ // during a rename, which otherwise makes the project invisible in the sidebar
1279
+ // while still blocking re-adding the same workspace.
1280
+ config[projectName] = {
1281
+ ...existingConfig,
1282
+ manuallyAdded: true,
1283
+ originalPath: absolutePath
1284
+ };
1285
+
1286
+ if (displayName) {
1287
+ config[projectName].displayName = displayName;
1288
+ }
1289
+
1290
+ await saveProjectConfig(config);
1291
+
1292
+ return {
1293
+ name: projectName,
1294
+ path: absolutePath,
1295
+ fullPath: absolutePath,
1296
+ displayName: config[projectName].displayName || await generateDisplayName(projectName, absolutePath),
1297
+ isManuallyAdded: true,
1298
+ sessions: [],
1299
+ codexSessions: [],
1300
+ opencodeSessions: [],
1301
+ geminiSessions: []
1302
+ };
1256
1303
  }
1257
1304
 
1258
1305
  // Allow adding projects even if the directory exists - this enables tracking
@@ -1730,43 +1777,42 @@ async function deleteOpencodeSession(sessionId) {
1730
1777
  }
1731
1778
  }
1732
1779
 
1733
- async function getGeminiSessions(projectPath, options = {}) {
1734
- const { limit = 5 } = options;
1735
- try {
1736
- const geminiTmpDir = path.join(os.homedir(), '.gemini', 'tmp');
1737
- try {
1738
- await fs.access(geminiTmpDir);
1739
- } catch {
1740
- return [];
1741
- }
1780
+ async function findGeminiSessionFiles() {
1781
+ return findFilesRecursively(
1782
+ path.join(os.homedir(), '.gemini', 'tmp'),
1783
+ (entryName, fullPath) => (
1784
+ entryName.endsWith('.json') &&
1785
+ /[\\/]chats[\\/]/.test(fullPath)
1786
+ )
1787
+ );
1788
+ }
1742
1789
 
1743
- const projectHash = crypto.createHash('sha256').update(projectPath).digest('hex');
1744
- const chatsDir = path.join(geminiTmpDir, projectHash, 'chats');
1790
+ function inferGeminiProjectHash(filePath, parsedSession = null) {
1791
+ const explicitHash = typeof parsedSession?.projectHash === 'string'
1792
+ ? parsedSession.projectHash.trim()
1793
+ : '';
1794
+ if (explicitHash) {
1795
+ return explicitHash.toLowerCase();
1796
+ }
1745
1797
 
1746
- try {
1747
- await fs.access(chatsDir);
1748
- } catch {
1749
- return [];
1750
- }
1798
+ const parentDir = path.basename(path.dirname(path.dirname(filePath)));
1799
+ if (/^[a-f0-9]{64}$/i.test(parentDir)) {
1800
+ return parentDir.toLowerCase();
1801
+ }
1751
1802
 
1752
- const entries = await fs.readdir(chatsDir, { withFileTypes: true });
1753
- const sessionFiles = entries
1754
- .filter(e => e.isFile() && e.name.endsWith('.json'))
1755
- .map(e => path.join(chatsDir, e.name));
1803
+ return '';
1804
+ }
1756
1805
 
1757
- const sessions = [];
1758
- for (const filePath of sessionFiles) {
1759
- try {
1760
- const sessionData = await parseGeminiSessionFile(filePath);
1761
- if (!sessionData?.id) continue;
1762
- sessions.push(sessionData);
1763
- } catch (error) {
1764
- console.warn(`Could not parse Gemini session file ${filePath}:`, error.message);
1765
- }
1766
- }
1806
+ async function getGeminiSessions(projectPath, options = {}) {
1807
+ const { limit = 5 } = options;
1808
+ const normalizedProjectPath = normalizeComparableProjectPath(projectPath);
1809
+ if (!normalizedProjectPath) {
1810
+ return [];
1811
+ }
1767
1812
 
1768
- sessions.sort((a, b) => new Date(b.lastActivity) - new Date(a.lastActivity));
1769
- return limit > 0 ? sessions.slice(0, limit) : sessions;
1813
+ try {
1814
+ const lookup = await buildGeminiSessionsLookup([normalizedProjectPath], { limit });
1815
+ return lookup.get(normalizedProjectPath) || [];
1770
1816
  } catch (error) {
1771
1817
  console.error('Error fetching Gemini sessions:', error);
1772
1818
  return [];
@@ -1805,6 +1851,7 @@ async function parseGeminiSessionFile(filePath) {
1805
1851
  lastActivity,
1806
1852
  createdAt: data?.startTime || lastActivity,
1807
1853
  model: messages.filter(m => m?.model).slice(-1)[0]?.model || null,
1854
+ projectHash: inferGeminiProjectHash(filePath, data),
1808
1855
  provider: 'gemini',
1809
1856
  filePath
1810
1857
  };
@@ -1884,73 +1931,69 @@ async function buildGeminiSessionsLookup(projectPaths, options = {}) {
1884
1931
  (projectPaths || []).map(normalizeComparableProjectPath).filter(Boolean)
1885
1932
  ));
1886
1933
 
1934
+ if (normalizedProjectPaths.length === 0) {
1935
+ return new Map();
1936
+ }
1937
+
1887
1938
  const cacheKey = JSON.stringify({ projectPaths: normalizedProjectPaths, limit });
1888
1939
  return getCachedProviderSessionLookup('gemini', cacheKey, async () => {
1889
- const sessionsByProjectPath = new Map();
1890
-
1891
- for (const normalizedProjectPath of normalizedProjectPaths) {
1892
- const projectHash = crypto.createHash('sha256').update(normalizedProjectPath).digest('hex');
1893
- const chatsDir = path.join(os.homedir(), '.gemini', 'tmp', projectHash, 'chats');
1940
+ const projectHashToPath = new Map(
1941
+ normalizedProjectPaths.map((normalizedProjectPath) => ([
1942
+ crypto.createHash('sha256').update(normalizedProjectPath).digest('hex'),
1943
+ normalizedProjectPath
1944
+ ]))
1945
+ );
1946
+ const sessionsByProjectPath = new Map(
1947
+ normalizedProjectPaths.map((normalizedProjectPath) => [normalizedProjectPath, []])
1948
+ );
1949
+ const sessionFiles = await findGeminiSessionFiles();
1894
1950
 
1951
+ for (const filePath of sessionFiles) {
1895
1952
  try {
1896
- const entries = await fs.readdir(chatsDir, { withFileTypes: true });
1897
- const sessionFiles = entries
1898
- .filter((entry) => entry.isFile() && entry.name.endsWith('.json'))
1899
- .map((entry) => path.join(chatsDir, entry.name));
1953
+ const sessionData = await parseGeminiSessionFile(filePath);
1954
+ if (!sessionData?.id) {
1955
+ continue;
1956
+ }
1900
1957
 
1901
- const sessions = [];
1902
- for (const filePath of sessionFiles) {
1903
- try {
1904
- const sessionData = await parseGeminiSessionFile(filePath);
1905
- if (sessionData?.id) {
1906
- sessions.push(sessionData);
1907
- }
1908
- } catch (error) {
1909
- console.warn(`Could not parse Gemini session file ${filePath}:`, error.message);
1910
- }
1958
+ const normalizedProjectPath = projectHashToPath.get(sessionData.projectHash || '');
1959
+ if (!normalizedProjectPath) {
1960
+ continue;
1911
1961
  }
1912
1962
 
1913
- sessions.sort((a, b) => new Date(b.lastActivity || 0) - new Date(a.lastActivity || 0));
1914
- sessionsByProjectPath.set(
1915
- normalizedProjectPath,
1916
- limit > 0 ? sessions.slice(0, limit) : sessions
1917
- );
1918
- } catch (_) {
1919
- sessionsByProjectPath.set(normalizedProjectPath, []);
1963
+ sessionsByProjectPath.get(normalizedProjectPath)?.push(sessionData);
1964
+ } catch (error) {
1965
+ console.warn(`Could not parse Gemini session file ${filePath}:`, error.message);
1920
1966
  }
1921
1967
  }
1922
1968
 
1969
+ for (const [normalizedProjectPath, sessions] of sessionsByProjectPath.entries()) {
1970
+ sessions.sort((a, b) => new Date(b.lastActivity || 0) - new Date(a.lastActivity || 0));
1971
+ sessionsByProjectPath.set(
1972
+ normalizedProjectPath,
1973
+ limit > 0 ? sessions.slice(0, limit) : sessions
1974
+ );
1975
+ }
1976
+
1923
1977
  return sessionsByProjectPath;
1924
1978
  });
1925
1979
  }
1926
1980
 
1927
1981
  async function getGeminiSessionMessages(sessionId, limit = null, offset = 0) {
1928
1982
  try {
1929
- const geminiTmpDir = path.join(os.homedir(), '.gemini', 'tmp');
1930
- const findSessionFile = async (dir) => {
1983
+ const sessionFiles = await findGeminiSessionFiles();
1984
+ let sessionFilePath = null;
1985
+
1986
+ for (const filePath of sessionFiles) {
1931
1987
  try {
1932
- const entries = await fs.readdir(dir, { withFileTypes: true });
1933
- for (const entry of entries) {
1934
- const fullPath = path.join(dir, entry.name);
1935
- if (entry.isDirectory()) {
1936
- const found = await findSessionFile(fullPath);
1937
- if (found) return found;
1938
- continue;
1939
- }
1940
- if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
1941
- try {
1942
- const raw = await fs.readFile(fullPath, 'utf8');
1943
- const parsed = JSON.parse(raw);
1944
- if (parsed?.sessionId === sessionId) {
1945
- return fullPath;
1946
- }
1947
- } catch {}
1988
+ const raw = await fs.readFile(filePath, 'utf8');
1989
+ const parsed = JSON.parse(raw);
1990
+ if (parsed?.sessionId === sessionId) {
1991
+ sessionFilePath = filePath;
1992
+ break;
1948
1993
  }
1949
1994
  } catch {}
1950
- return null;
1951
- };
1995
+ }
1952
1996
 
1953
- const sessionFilePath = await findSessionFile(geminiTmpDir);
1954
1997
  if (!sessionFilePath) {
1955
1998
  return { messages: [], total: 0, hasMore: false };
1956
1999
  }
@@ -97,6 +97,7 @@ router.post('/abort', validateExternalApiKey, async (req, res) => {
97
97
  return res.json({
98
98
  success: false,
99
99
  aborted: false,
100
+ runtime: 'acp',
100
101
  sessionId: normalized.sessionId,
101
102
  provider: normalized.provider,
102
103
  error: 'Active session not found'
@@ -106,6 +107,7 @@ router.post('/abort', validateExternalApiKey, async (req, res) => {
106
107
  return res.json({
107
108
  success: true,
108
109
  aborted: true,
110
+ runtime: 'acp',
109
111
  sessionId: normalized.sessionId,
110
112
  provider: normalized.provider,
111
113
  message: 'Session aborted'