@cleocode/adapters 2026.3.72 → 2026.3.74
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/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1227 -101
- package/dist/index.js.map +4 -4
- package/dist/providers/claude-code/adapter.d.ts.map +1 -1
- package/dist/providers/claude-code/adapter.js +16 -5
- package/dist/providers/claude-code/adapter.js.map +1 -1
- package/dist/providers/claude-code/hooks.d.ts +89 -25
- package/dist/providers/claude-code/hooks.d.ts.map +1 -1
- package/dist/providers/claude-code/hooks.js +230 -28
- package/dist/providers/claude-code/hooks.js.map +1 -1
- package/dist/providers/codex/adapter.d.ts +70 -0
- package/dist/providers/codex/adapter.d.ts.map +1 -0
- package/dist/providers/codex/adapter.js +134 -0
- package/dist/providers/codex/adapter.js.map +1 -0
- package/dist/providers/codex/hooks.d.ts +85 -0
- package/dist/providers/codex/hooks.d.ts.map +1 -0
- package/dist/providers/codex/hooks.js +155 -0
- package/dist/providers/codex/hooks.js.map +1 -0
- package/dist/providers/codex/index.d.ts +22 -0
- package/dist/providers/codex/index.d.ts.map +1 -0
- package/dist/providers/codex/index.js +24 -0
- package/dist/providers/codex/index.js.map +1 -0
- package/dist/providers/codex/install.d.ts +74 -0
- package/dist/providers/codex/install.d.ts.map +1 -0
- package/dist/providers/codex/install.js +183 -0
- package/dist/providers/codex/install.js.map +1 -0
- package/dist/providers/cursor/adapter.d.ts.map +1 -1
- package/dist/providers/cursor/adapter.js +16 -2
- package/dist/providers/cursor/adapter.js.map +1 -1
- package/dist/providers/cursor/hooks.d.ts +102 -17
- package/dist/providers/cursor/hooks.d.ts.map +1 -1
- package/dist/providers/cursor/hooks.js +164 -18
- package/dist/providers/cursor/hooks.js.map +1 -1
- package/dist/providers/gemini-cli/adapter.d.ts +70 -0
- package/dist/providers/gemini-cli/adapter.d.ts.map +1 -0
- package/dist/providers/gemini-cli/adapter.js +145 -0
- package/dist/providers/gemini-cli/adapter.js.map +1 -0
- package/dist/providers/gemini-cli/hooks.d.ts +92 -0
- package/dist/providers/gemini-cli/hooks.d.ts.map +1 -0
- package/dist/providers/gemini-cli/hooks.js +169 -0
- package/dist/providers/gemini-cli/hooks.js.map +1 -0
- package/dist/providers/gemini-cli/index.d.ts +22 -0
- package/dist/providers/gemini-cli/index.d.ts.map +1 -0
- package/dist/providers/gemini-cli/index.js +24 -0
- package/dist/providers/gemini-cli/index.js.map +1 -0
- package/dist/providers/gemini-cli/install.d.ts +74 -0
- package/dist/providers/gemini-cli/install.d.ts.map +1 -0
- package/dist/providers/gemini-cli/install.js +183 -0
- package/dist/providers/gemini-cli/install.js.map +1 -0
- package/dist/providers/kimi/adapter.d.ts +72 -0
- package/dist/providers/kimi/adapter.d.ts.map +1 -0
- package/dist/providers/kimi/adapter.js +133 -0
- package/dist/providers/kimi/adapter.js.map +1 -0
- package/dist/providers/kimi/hooks.d.ts +64 -0
- package/dist/providers/kimi/hooks.d.ts.map +1 -0
- package/dist/providers/kimi/hooks.js +73 -0
- package/dist/providers/kimi/hooks.js.map +1 -0
- package/dist/providers/kimi/index.d.ts +22 -0
- package/dist/providers/kimi/index.d.ts.map +1 -0
- package/dist/providers/kimi/index.js +24 -0
- package/dist/providers/kimi/index.js.map +1 -0
- package/dist/providers/kimi/install.d.ts +80 -0
- package/dist/providers/kimi/install.d.ts.map +1 -0
- package/dist/providers/kimi/install.js +189 -0
- package/dist/providers/kimi/install.js.map +1 -0
- package/dist/providers/opencode/adapter.d.ts.map +1 -1
- package/dist/providers/opencode/adapter.js +13 -6
- package/dist/providers/opencode/adapter.js.map +1 -1
- package/dist/providers/opencode/hooks.d.ts +89 -28
- package/dist/providers/opencode/hooks.d.ts.map +1 -1
- package/dist/providers/opencode/hooks.js +145 -37
- package/dist/providers/opencode/hooks.js.map +1 -1
- package/package.json +3 -2
- package/src/index.ts +18 -0
- package/src/providers/claude-code/adapter.ts +16 -5
- package/src/providers/claude-code/hooks.ts +154 -30
- package/src/providers/codex/adapter.ts +154 -0
- package/src/providers/codex/hooks.ts +163 -0
- package/src/providers/codex/index.ts +27 -0
- package/src/providers/codex/install.ts +203 -0
- package/src/providers/codex/manifest.json +28 -0
- package/src/providers/cursor/adapter.ts +16 -2
- package/src/providers/cursor/hooks.ts +167 -18
- package/src/providers/gemini-cli/adapter.ts +165 -0
- package/src/providers/gemini-cli/hooks.ts +177 -0
- package/src/providers/gemini-cli/index.ts +27 -0
- package/src/providers/gemini-cli/install.ts +203 -0
- package/src/providers/gemini-cli/manifest.json +35 -0
- package/src/providers/kimi/adapter.ts +153 -0
- package/src/providers/kimi/hooks.ts +80 -0
- package/src/providers/kimi/index.ts +27 -0
- package/src/providers/kimi/install.ts +209 -0
- package/src/providers/kimi/manifest.json +24 -0
- package/src/providers/opencode/adapter.ts +13 -6
- package/src/providers/opencode/hooks.ts +146 -37
package/dist/index.js
CHANGED
|
@@ -1099,14 +1099,14 @@ var init_hooks2 = __esm({
|
|
|
1099
1099
|
});
|
|
1100
1100
|
|
|
1101
1101
|
// packages/adapters/src/providers/cursor/install.js
|
|
1102
|
-
import { existsSync as
|
|
1103
|
-
import { join as
|
|
1104
|
-
var
|
|
1102
|
+
import { existsSync as existsSync7, mkdirSync as mkdirSync3, readFileSync as readFileSync5, writeFileSync as writeFileSync4 } from "node:fs";
|
|
1103
|
+
import { join as join10 } from "node:path";
|
|
1104
|
+
var INSTRUCTION_REFERENCES3, MCP_SERVER_KEY3, CursorInstallProvider;
|
|
1105
1105
|
var init_install2 = __esm({
|
|
1106
1106
|
"packages/adapters/src/providers/cursor/install.js"() {
|
|
1107
1107
|
"use strict";
|
|
1108
|
-
|
|
1109
|
-
|
|
1108
|
+
INSTRUCTION_REFERENCES3 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
1109
|
+
MCP_SERVER_KEY3 = "cleo";
|
|
1110
1110
|
CursorInstallProvider = class {
|
|
1111
1111
|
installedProjectDir = null;
|
|
1112
1112
|
/**
|
|
@@ -1124,7 +1124,7 @@ var init_install2 = __esm({
|
|
|
1124
1124
|
if (mcpServerPath) {
|
|
1125
1125
|
mcpRegistered = this.registerMcpServer(projectDir, mcpServerPath);
|
|
1126
1126
|
if (mcpRegistered) {
|
|
1127
|
-
details.mcpConfigPath =
|
|
1127
|
+
details.mcpConfigPath = join10(projectDir, ".cursor", "mcp.json");
|
|
1128
1128
|
}
|
|
1129
1129
|
}
|
|
1130
1130
|
instructionFileUpdated = this.updateInstructionFiles(projectDir);
|
|
@@ -1149,15 +1149,15 @@ var init_install2 = __esm({
|
|
|
1149
1149
|
async uninstall() {
|
|
1150
1150
|
if (!this.installedProjectDir)
|
|
1151
1151
|
return;
|
|
1152
|
-
const mcpPath =
|
|
1153
|
-
if (
|
|
1152
|
+
const mcpPath = join10(this.installedProjectDir, ".cursor", "mcp.json");
|
|
1153
|
+
if (existsSync7(mcpPath)) {
|
|
1154
1154
|
try {
|
|
1155
|
-
const raw =
|
|
1155
|
+
const raw = readFileSync5(mcpPath, "utf-8");
|
|
1156
1156
|
const config = JSON.parse(raw);
|
|
1157
1157
|
const mcpServers = config.mcpServers;
|
|
1158
|
-
if (mcpServers &&
|
|
1159
|
-
delete mcpServers[
|
|
1160
|
-
|
|
1158
|
+
if (mcpServers && MCP_SERVER_KEY3 in mcpServers) {
|
|
1159
|
+
delete mcpServers[MCP_SERVER_KEY3];
|
|
1160
|
+
writeFileSync4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1161
1161
|
}
|
|
1162
1162
|
} catch {
|
|
1163
1163
|
}
|
|
@@ -1170,12 +1170,12 @@ var init_install2 = __esm({
|
|
|
1170
1170
|
* Checks for MCP server registered in .cursor/mcp.json.
|
|
1171
1171
|
*/
|
|
1172
1172
|
async isInstalled() {
|
|
1173
|
-
const mcpPath =
|
|
1174
|
-
if (
|
|
1173
|
+
const mcpPath = join10(process.cwd(), ".cursor", "mcp.json");
|
|
1174
|
+
if (existsSync7(mcpPath)) {
|
|
1175
1175
|
try {
|
|
1176
|
-
const config = JSON.parse(
|
|
1176
|
+
const config = JSON.parse(readFileSync5(mcpPath, "utf-8"));
|
|
1177
1177
|
const mcpServers = config.mcpServers;
|
|
1178
|
-
if (mcpServers &&
|
|
1178
|
+
if (mcpServers && MCP_SERVER_KEY3 in mcpServers) {
|
|
1179
1179
|
return true;
|
|
1180
1180
|
}
|
|
1181
1181
|
} catch {
|
|
@@ -1202,13 +1202,13 @@ var init_install2 = __esm({
|
|
|
1202
1202
|
* @returns true if registration was performed or updated
|
|
1203
1203
|
*/
|
|
1204
1204
|
registerMcpServer(projectDir, mcpServerPath) {
|
|
1205
|
-
const cursorDir =
|
|
1206
|
-
const mcpPath =
|
|
1205
|
+
const cursorDir = join10(projectDir, ".cursor");
|
|
1206
|
+
const mcpPath = join10(cursorDir, "mcp.json");
|
|
1207
1207
|
let config = {};
|
|
1208
|
-
|
|
1209
|
-
if (
|
|
1208
|
+
mkdirSync3(cursorDir, { recursive: true });
|
|
1209
|
+
if (existsSync7(mcpPath)) {
|
|
1210
1210
|
try {
|
|
1211
|
-
config = JSON.parse(
|
|
1211
|
+
config = JSON.parse(readFileSync5(mcpPath, "utf-8"));
|
|
1212
1212
|
} catch {
|
|
1213
1213
|
}
|
|
1214
1214
|
}
|
|
@@ -1216,11 +1216,11 @@ var init_install2 = __esm({
|
|
|
1216
1216
|
config.mcpServers = {};
|
|
1217
1217
|
}
|
|
1218
1218
|
const mcpServers = config.mcpServers;
|
|
1219
|
-
mcpServers[
|
|
1219
|
+
mcpServers[MCP_SERVER_KEY3] = {
|
|
1220
1220
|
command: "node",
|
|
1221
1221
|
args: [mcpServerPath]
|
|
1222
1222
|
};
|
|
1223
|
-
|
|
1223
|
+
writeFileSync4(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1224
1224
|
return true;
|
|
1225
1225
|
}
|
|
1226
1226
|
/**
|
|
@@ -1247,18 +1247,18 @@ var init_install2 = __esm({
|
|
|
1247
1247
|
* @returns true if the file was modified
|
|
1248
1248
|
*/
|
|
1249
1249
|
updateLegacyRules(projectDir) {
|
|
1250
|
-
const rulesPath =
|
|
1251
|
-
if (!
|
|
1250
|
+
const rulesPath = join10(projectDir, ".cursorrules");
|
|
1251
|
+
if (!existsSync7(rulesPath)) {
|
|
1252
1252
|
return false;
|
|
1253
1253
|
}
|
|
1254
|
-
let content =
|
|
1255
|
-
const missingRefs =
|
|
1254
|
+
let content = readFileSync5(rulesPath, "utf-8");
|
|
1255
|
+
const missingRefs = INSTRUCTION_REFERENCES3.filter((ref) => !content.includes(ref));
|
|
1256
1256
|
if (missingRefs.length === 0) {
|
|
1257
1257
|
return false;
|
|
1258
1258
|
}
|
|
1259
1259
|
const separator = content.endsWith("\n") ? "" : "\n";
|
|
1260
1260
|
content = content + separator + missingRefs.join("\n") + "\n";
|
|
1261
|
-
|
|
1261
|
+
writeFileSync4(rulesPath, content, "utf-8");
|
|
1262
1262
|
return true;
|
|
1263
1263
|
}
|
|
1264
1264
|
/**
|
|
@@ -1270,8 +1270,8 @@ var init_install2 = __esm({
|
|
|
1270
1270
|
* @returns true if the file was created or modified
|
|
1271
1271
|
*/
|
|
1272
1272
|
updateModernRules(projectDir) {
|
|
1273
|
-
const rulesDir =
|
|
1274
|
-
const mdcPath =
|
|
1273
|
+
const rulesDir = join10(projectDir, ".cursor", "rules");
|
|
1274
|
+
const mdcPath = join10(rulesDir, "cleo.mdc");
|
|
1275
1275
|
const expectedContent = [
|
|
1276
1276
|
"---",
|
|
1277
1277
|
"description: CLEO task management protocol references",
|
|
@@ -1279,17 +1279,17 @@ var init_install2 = __esm({
|
|
|
1279
1279
|
"alwaysApply: true",
|
|
1280
1280
|
"---",
|
|
1281
1281
|
"",
|
|
1282
|
-
...
|
|
1282
|
+
...INSTRUCTION_REFERENCES3,
|
|
1283
1283
|
""
|
|
1284
1284
|
].join("\n");
|
|
1285
|
-
if (
|
|
1286
|
-
const existing =
|
|
1285
|
+
if (existsSync7(mdcPath)) {
|
|
1286
|
+
const existing = readFileSync5(mdcPath, "utf-8");
|
|
1287
1287
|
if (existing === expectedContent) {
|
|
1288
1288
|
return false;
|
|
1289
1289
|
}
|
|
1290
1290
|
}
|
|
1291
|
-
|
|
1292
|
-
|
|
1291
|
+
mkdirSync3(rulesDir, { recursive: true });
|
|
1292
|
+
writeFileSync4(mdcPath, expectedContent, "utf-8");
|
|
1293
1293
|
return true;
|
|
1294
1294
|
}
|
|
1295
1295
|
/**
|
|
@@ -1297,10 +1297,10 @@ var init_install2 = __esm({
|
|
|
1297
1297
|
*/
|
|
1298
1298
|
getUpdatedFileList(projectDir) {
|
|
1299
1299
|
const files = [];
|
|
1300
|
-
if (
|
|
1301
|
-
files.push(
|
|
1300
|
+
if (existsSync7(join10(projectDir, ".cursorrules"))) {
|
|
1301
|
+
files.push(join10(projectDir, ".cursorrules"));
|
|
1302
1302
|
}
|
|
1303
|
-
files.push(
|
|
1303
|
+
files.push(join10(projectDir, ".cursor", "rules", "cleo.mdc"));
|
|
1304
1304
|
return files;
|
|
1305
1305
|
}
|
|
1306
1306
|
};
|
|
@@ -1308,8 +1308,8 @@ var init_install2 = __esm({
|
|
|
1308
1308
|
});
|
|
1309
1309
|
|
|
1310
1310
|
// packages/adapters/src/providers/cursor/adapter.js
|
|
1311
|
-
import { existsSync as
|
|
1312
|
-
import { join as
|
|
1311
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
1312
|
+
import { join as join11 } from "node:path";
|
|
1313
1313
|
var CursorAdapter;
|
|
1314
1314
|
var init_adapter2 = __esm({
|
|
1315
1315
|
"packages/adapters/src/providers/cursor/adapter.js"() {
|
|
@@ -1382,14 +1382,14 @@ var init_adapter2 = __esm({
|
|
|
1382
1382
|
}
|
|
1383
1383
|
let configExists = false;
|
|
1384
1384
|
if (this.projectDir) {
|
|
1385
|
-
const cursorConfigDir =
|
|
1386
|
-
configExists =
|
|
1385
|
+
const cursorConfigDir = join11(this.projectDir, ".cursor");
|
|
1386
|
+
configExists = existsSync8(cursorConfigDir);
|
|
1387
1387
|
details.configDirExists = configExists;
|
|
1388
1388
|
}
|
|
1389
1389
|
const editorEnvSet = process.env.CURSOR_EDITOR !== void 0;
|
|
1390
1390
|
details.editorEnvSet = editorEnvSet;
|
|
1391
1391
|
if (this.projectDir) {
|
|
1392
|
-
const legacyRulesExist =
|
|
1392
|
+
const legacyRulesExist = existsSync8(join11(this.projectDir, ".cursorrules"));
|
|
1393
1393
|
details.legacyRulesExist = legacyRulesExist;
|
|
1394
1394
|
}
|
|
1395
1395
|
const healthy = configExists || editorEnvSet;
|
|
@@ -1422,10 +1422,10 @@ __export(cursor_exports, {
|
|
|
1422
1422
|
CursorAdapter: () => CursorAdapter,
|
|
1423
1423
|
CursorHookProvider: () => CursorHookProvider,
|
|
1424
1424
|
CursorInstallProvider: () => CursorInstallProvider,
|
|
1425
|
-
createAdapter: () =>
|
|
1425
|
+
createAdapter: () => createAdapter3,
|
|
1426
1426
|
default: () => cursor_default
|
|
1427
1427
|
});
|
|
1428
|
-
function
|
|
1428
|
+
function createAdapter3() {
|
|
1429
1429
|
return new CursorAdapter();
|
|
1430
1430
|
}
|
|
1431
1431
|
var cursor_default;
|
|
@@ -1504,14 +1504,14 @@ var init_hooks3 = __esm({
|
|
|
1504
1504
|
});
|
|
1505
1505
|
|
|
1506
1506
|
// packages/adapters/src/providers/opencode/install.js
|
|
1507
|
-
import { existsSync as
|
|
1508
|
-
import { join as
|
|
1509
|
-
var
|
|
1507
|
+
import { existsSync as existsSync13, mkdirSync as mkdirSync6, readFileSync as readFileSync8, writeFileSync as writeFileSync7 } from "node:fs";
|
|
1508
|
+
import { join as join17 } from "node:path";
|
|
1509
|
+
var INSTRUCTION_REFERENCES6, MCP_SERVER_KEY6, OpenCodeInstallProvider;
|
|
1510
1510
|
var init_install3 = __esm({
|
|
1511
1511
|
"packages/adapters/src/providers/opencode/install.js"() {
|
|
1512
1512
|
"use strict";
|
|
1513
|
-
|
|
1514
|
-
|
|
1513
|
+
INSTRUCTION_REFERENCES6 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
1514
|
+
MCP_SERVER_KEY6 = "cleo";
|
|
1515
1515
|
OpenCodeInstallProvider = class {
|
|
1516
1516
|
installedProjectDir = null;
|
|
1517
1517
|
/**
|
|
@@ -1529,12 +1529,12 @@ var init_install3 = __esm({
|
|
|
1529
1529
|
if (mcpServerPath) {
|
|
1530
1530
|
mcpRegistered = this.registerMcpServer(projectDir, mcpServerPath);
|
|
1531
1531
|
if (mcpRegistered) {
|
|
1532
|
-
details.mcpConfigPath =
|
|
1532
|
+
details.mcpConfigPath = join17(projectDir, ".opencode", "config.json");
|
|
1533
1533
|
}
|
|
1534
1534
|
}
|
|
1535
1535
|
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
1536
1536
|
if (instructionFileUpdated) {
|
|
1537
|
-
details.instructionFile =
|
|
1537
|
+
details.instructionFile = join17(projectDir, "AGENTS.md");
|
|
1538
1538
|
}
|
|
1539
1539
|
this.installedProjectDir = projectDir;
|
|
1540
1540
|
return {
|
|
@@ -1554,15 +1554,15 @@ var init_install3 = __esm({
|
|
|
1554
1554
|
async uninstall() {
|
|
1555
1555
|
if (!this.installedProjectDir)
|
|
1556
1556
|
return;
|
|
1557
|
-
const configPath =
|
|
1558
|
-
if (
|
|
1557
|
+
const configPath = join17(this.installedProjectDir, ".opencode", "config.json");
|
|
1558
|
+
if (existsSync13(configPath)) {
|
|
1559
1559
|
try {
|
|
1560
|
-
const raw =
|
|
1560
|
+
const raw = readFileSync8(configPath, "utf-8");
|
|
1561
1561
|
const config = JSON.parse(raw);
|
|
1562
1562
|
const mcpServers = config.mcpServers;
|
|
1563
|
-
if (mcpServers &&
|
|
1564
|
-
delete mcpServers[
|
|
1565
|
-
|
|
1563
|
+
if (mcpServers && MCP_SERVER_KEY6 in mcpServers) {
|
|
1564
|
+
delete mcpServers[MCP_SERVER_KEY6];
|
|
1565
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1566
1566
|
}
|
|
1567
1567
|
} catch {
|
|
1568
1568
|
}
|
|
@@ -1576,12 +1576,12 @@ var init_install3 = __esm({
|
|
|
1576
1576
|
* Returns true if the CLEO MCP server entry is found.
|
|
1577
1577
|
*/
|
|
1578
1578
|
async isInstalled() {
|
|
1579
|
-
const configPath =
|
|
1580
|
-
if (
|
|
1579
|
+
const configPath = join17(process.cwd(), ".opencode", "config.json");
|
|
1580
|
+
if (existsSync13(configPath)) {
|
|
1581
1581
|
try {
|
|
1582
|
-
const config = JSON.parse(
|
|
1582
|
+
const config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
1583
1583
|
const mcpServers = config.mcpServers;
|
|
1584
|
-
if (mcpServers &&
|
|
1584
|
+
if (mcpServers && MCP_SERVER_KEY6 in mcpServers) {
|
|
1585
1585
|
return true;
|
|
1586
1586
|
}
|
|
1587
1587
|
} catch {
|
|
@@ -1608,13 +1608,13 @@ var init_install3 = __esm({
|
|
|
1608
1608
|
* @returns true if registration was performed or updated
|
|
1609
1609
|
*/
|
|
1610
1610
|
registerMcpServer(projectDir, mcpServerPath) {
|
|
1611
|
-
const openCodeDir =
|
|
1612
|
-
const configPath =
|
|
1611
|
+
const openCodeDir = join17(projectDir, ".opencode");
|
|
1612
|
+
const configPath = join17(openCodeDir, "config.json");
|
|
1613
1613
|
let config = {};
|
|
1614
|
-
|
|
1615
|
-
if (
|
|
1614
|
+
mkdirSync6(openCodeDir, { recursive: true });
|
|
1615
|
+
if (existsSync13(configPath)) {
|
|
1616
1616
|
try {
|
|
1617
|
-
config = JSON.parse(
|
|
1617
|
+
config = JSON.parse(readFileSync8(configPath, "utf-8"));
|
|
1618
1618
|
} catch {
|
|
1619
1619
|
}
|
|
1620
1620
|
}
|
|
@@ -1622,11 +1622,11 @@ var init_install3 = __esm({
|
|
|
1622
1622
|
config.mcpServers = {};
|
|
1623
1623
|
}
|
|
1624
1624
|
const mcpServers = config.mcpServers;
|
|
1625
|
-
mcpServers[
|
|
1625
|
+
mcpServers[MCP_SERVER_KEY6] = {
|
|
1626
1626
|
command: "node",
|
|
1627
1627
|
args: [mcpServerPath]
|
|
1628
1628
|
};
|
|
1629
|
-
|
|
1629
|
+
writeFileSync7(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
1630
1630
|
return true;
|
|
1631
1631
|
}
|
|
1632
1632
|
/**
|
|
@@ -1635,14 +1635,14 @@ var init_install3 = __esm({
|
|
|
1635
1635
|
* @returns true if the file was created or modified
|
|
1636
1636
|
*/
|
|
1637
1637
|
updateInstructionFile(projectDir) {
|
|
1638
|
-
const agentsMdPath =
|
|
1638
|
+
const agentsMdPath = join17(projectDir, "AGENTS.md");
|
|
1639
1639
|
let content = "";
|
|
1640
1640
|
let existed = false;
|
|
1641
|
-
if (
|
|
1642
|
-
content =
|
|
1641
|
+
if (existsSync13(agentsMdPath)) {
|
|
1642
|
+
content = readFileSync8(agentsMdPath, "utf-8");
|
|
1643
1643
|
existed = true;
|
|
1644
1644
|
}
|
|
1645
|
-
const missingRefs =
|
|
1645
|
+
const missingRefs = INSTRUCTION_REFERENCES6.filter((ref) => !content.includes(ref));
|
|
1646
1646
|
if (missingRefs.length === 0) {
|
|
1647
1647
|
return false;
|
|
1648
1648
|
}
|
|
@@ -1653,7 +1653,7 @@ var init_install3 = __esm({
|
|
|
1653
1653
|
} else {
|
|
1654
1654
|
content = refsBlock + "\n";
|
|
1655
1655
|
}
|
|
1656
|
-
|
|
1656
|
+
writeFileSync7(agentsMdPath, content, "utf-8");
|
|
1657
1657
|
return true;
|
|
1658
1658
|
}
|
|
1659
1659
|
};
|
|
@@ -1661,10 +1661,10 @@ var init_install3 = __esm({
|
|
|
1661
1661
|
});
|
|
1662
1662
|
|
|
1663
1663
|
// packages/adapters/src/providers/opencode/spawn.js
|
|
1664
|
-
import { exec as
|
|
1665
|
-
import { mkdir as mkdir2, readFile as
|
|
1666
|
-
import { join as
|
|
1667
|
-
import { promisify as
|
|
1664
|
+
import { exec as exec6, spawn as nodeSpawn2 } from "node:child_process";
|
|
1665
|
+
import { mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "node:fs/promises";
|
|
1666
|
+
import { join as join18 } from "node:path";
|
|
1667
|
+
import { promisify as promisify6 } from "node:util";
|
|
1668
1668
|
function buildOpenCodeAgentMarkdown(description, instructions) {
|
|
1669
1669
|
const normalizedDesc = description.replace(/\s+/g, " ").trim();
|
|
1670
1670
|
return [
|
|
@@ -1679,8 +1679,8 @@ function buildOpenCodeAgentMarkdown(description, instructions) {
|
|
|
1679
1679
|
].join("\n");
|
|
1680
1680
|
}
|
|
1681
1681
|
async function ensureSubagentDefinition(workingDirectory) {
|
|
1682
|
-
const agentDir =
|
|
1683
|
-
const agentPath =
|
|
1682
|
+
const agentDir = join18(workingDirectory, ".opencode", "agent");
|
|
1683
|
+
const agentPath = join18(agentDir, `${OPENCODE_SUBAGENT_NAME}.md`);
|
|
1684
1684
|
const description = "CLEO task executor with protocol compliance.";
|
|
1685
1685
|
const instructions = [
|
|
1686
1686
|
"# CLEO Subagent",
|
|
@@ -1694,7 +1694,7 @@ async function ensureSubagentDefinition(workingDirectory) {
|
|
|
1694
1694
|
await mkdir2(agentDir, { recursive: true });
|
|
1695
1695
|
let existing = null;
|
|
1696
1696
|
try {
|
|
1697
|
-
existing = await
|
|
1697
|
+
existing = await readFile4(agentPath, "utf-8");
|
|
1698
1698
|
} catch {
|
|
1699
1699
|
existing = null;
|
|
1700
1700
|
}
|
|
@@ -1703,11 +1703,11 @@ async function ensureSubagentDefinition(workingDirectory) {
|
|
|
1703
1703
|
}
|
|
1704
1704
|
return OPENCODE_SUBAGENT_NAME;
|
|
1705
1705
|
}
|
|
1706
|
-
var
|
|
1706
|
+
var execAsync6, OPENCODE_SUBAGENT_NAME, OPENCODE_FALLBACK_AGENT, OpenCodeSpawnProvider;
|
|
1707
1707
|
var init_spawn2 = __esm({
|
|
1708
1708
|
"packages/adapters/src/providers/opencode/spawn.js"() {
|
|
1709
1709
|
"use strict";
|
|
1710
|
-
|
|
1710
|
+
execAsync6 = promisify6(exec6);
|
|
1711
1711
|
OPENCODE_SUBAGENT_NAME = "cleo-subagent";
|
|
1712
1712
|
OPENCODE_FALLBACK_AGENT = "general";
|
|
1713
1713
|
OpenCodeSpawnProvider = class {
|
|
@@ -1720,7 +1720,7 @@ var init_spawn2 = __esm({
|
|
|
1720
1720
|
*/
|
|
1721
1721
|
async canSpawn() {
|
|
1722
1722
|
try {
|
|
1723
|
-
await
|
|
1723
|
+
await execAsync6("which opencode");
|
|
1724
1724
|
return true;
|
|
1725
1725
|
} catch {
|
|
1726
1726
|
return false;
|
|
@@ -1839,18 +1839,18 @@ var init_spawn2 = __esm({
|
|
|
1839
1839
|
});
|
|
1840
1840
|
|
|
1841
1841
|
// packages/adapters/src/providers/opencode/adapter.js
|
|
1842
|
-
import { exec as
|
|
1843
|
-
import { existsSync as
|
|
1844
|
-
import { join as
|
|
1845
|
-
import { promisify as
|
|
1846
|
-
var
|
|
1842
|
+
import { exec as exec7 } from "node:child_process";
|
|
1843
|
+
import { existsSync as existsSync14 } from "node:fs";
|
|
1844
|
+
import { join as join19 } from "node:path";
|
|
1845
|
+
import { promisify as promisify7 } from "node:util";
|
|
1846
|
+
var execAsync7, OpenCodeAdapter;
|
|
1847
1847
|
var init_adapter3 = __esm({
|
|
1848
1848
|
"packages/adapters/src/providers/opencode/adapter.js"() {
|
|
1849
1849
|
"use strict";
|
|
1850
1850
|
init_hooks3();
|
|
1851
1851
|
init_install3();
|
|
1852
1852
|
init_spawn2();
|
|
1853
|
-
|
|
1853
|
+
execAsync7 = promisify7(exec7);
|
|
1854
1854
|
OpenCodeAdapter = class {
|
|
1855
1855
|
id = "opencode";
|
|
1856
1856
|
name = "OpenCode";
|
|
@@ -1931,15 +1931,15 @@ var init_adapter3 = __esm({
|
|
|
1931
1931
|
}
|
|
1932
1932
|
let cliAvailable = false;
|
|
1933
1933
|
try {
|
|
1934
|
-
const { stdout } = await
|
|
1934
|
+
const { stdout } = await execAsync7("which opencode");
|
|
1935
1935
|
cliAvailable = stdout.trim().length > 0;
|
|
1936
1936
|
details.cliPath = stdout.trim();
|
|
1937
1937
|
} catch {
|
|
1938
1938
|
details.cliAvailable = false;
|
|
1939
1939
|
}
|
|
1940
1940
|
if (this.projectDir) {
|
|
1941
|
-
const openCodeConfigDir =
|
|
1942
|
-
const configExists =
|
|
1941
|
+
const openCodeConfigDir = join19(this.projectDir, ".opencode");
|
|
1942
|
+
const configExists = existsSync14(openCodeConfigDir);
|
|
1943
1943
|
details.configDirExists = configExists;
|
|
1944
1944
|
}
|
|
1945
1945
|
const versionEnvSet = process.env.OPENCODE_VERSION !== void 0;
|
|
@@ -1975,10 +1975,10 @@ __export(opencode_exports, {
|
|
|
1975
1975
|
OpenCodeHookProvider: () => OpenCodeHookProvider,
|
|
1976
1976
|
OpenCodeInstallProvider: () => OpenCodeInstallProvider,
|
|
1977
1977
|
OpenCodeSpawnProvider: () => OpenCodeSpawnProvider,
|
|
1978
|
-
createAdapter: () =>
|
|
1978
|
+
createAdapter: () => createAdapter6,
|
|
1979
1979
|
default: () => opencode_default
|
|
1980
1980
|
});
|
|
1981
|
-
function
|
|
1981
|
+
function createAdapter6() {
|
|
1982
1982
|
return new OpenCodeAdapter();
|
|
1983
1983
|
}
|
|
1984
1984
|
var opencode_default;
|
|
@@ -1996,12 +1996,1126 @@ var init_opencode = __esm({
|
|
|
1996
1996
|
|
|
1997
1997
|
// packages/adapters/src/index.ts
|
|
1998
1998
|
init_claude_code();
|
|
1999
|
+
|
|
2000
|
+
// packages/adapters/src/providers/codex/adapter.ts
|
|
2001
|
+
import { exec as exec3 } from "node:child_process";
|
|
2002
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
2003
|
+
import { homedir as homedir7 } from "node:os";
|
|
2004
|
+
import { join as join9 } from "node:path";
|
|
2005
|
+
import { promisify as promisify3 } from "node:util";
|
|
2006
|
+
|
|
2007
|
+
// packages/adapters/src/providers/codex/hooks.ts
|
|
2008
|
+
import { readdir, readFile as readFile2 } from "node:fs/promises";
|
|
2009
|
+
import { join as join7 } from "node:path";
|
|
2010
|
+
var CODEX_EVENT_MAP = {
|
|
2011
|
+
SessionStart: "SessionStart",
|
|
2012
|
+
PromptSubmit: "UserPromptSubmit",
|
|
2013
|
+
ResponseComplete: "Stop"
|
|
2014
|
+
};
|
|
2015
|
+
var CodexHookProvider = class {
|
|
2016
|
+
registered = false;
|
|
2017
|
+
/**
|
|
2018
|
+
* Map a Codex CLI native event name to a CAAMP hook event name.
|
|
2019
|
+
*
|
|
2020
|
+
* @param providerEvent - Codex CLI event name (e.g. "SessionStart", "PromptSubmit")
|
|
2021
|
+
* @returns CAAMP event name or null if unmapped
|
|
2022
|
+
* @task T162
|
|
2023
|
+
*/
|
|
2024
|
+
mapProviderEvent(providerEvent) {
|
|
2025
|
+
return CODEX_EVENT_MAP[providerEvent] ?? null;
|
|
2026
|
+
}
|
|
2027
|
+
/**
|
|
2028
|
+
* Register native hooks for a project.
|
|
2029
|
+
*
|
|
2030
|
+
* For Codex CLI, hooks are registered via the config system
|
|
2031
|
+
* (~/.codex/), which is handled by the install provider.
|
|
2032
|
+
* This method marks hooks as registered without performing
|
|
2033
|
+
* filesystem operations.
|
|
2034
|
+
*
|
|
2035
|
+
* @param _projectDir - Project directory (unused; hooks are global)
|
|
2036
|
+
* @task T162
|
|
2037
|
+
*/
|
|
2038
|
+
async registerNativeHooks(_projectDir) {
|
|
2039
|
+
this.registered = true;
|
|
2040
|
+
}
|
|
2041
|
+
/**
|
|
2042
|
+
* Unregister native hooks.
|
|
2043
|
+
*
|
|
2044
|
+
* For Codex CLI, this is a no-op since hooks are managed through
|
|
2045
|
+
* the config system. Unregistration happens via the install
|
|
2046
|
+
* provider's uninstall method.
|
|
2047
|
+
* @task T162
|
|
2048
|
+
*/
|
|
2049
|
+
async unregisterNativeHooks() {
|
|
2050
|
+
this.registered = false;
|
|
2051
|
+
}
|
|
2052
|
+
/**
|
|
2053
|
+
* Check whether hooks have been registered via registerNativeHooks.
|
|
2054
|
+
* @task T162
|
|
2055
|
+
*/
|
|
2056
|
+
isRegistered() {
|
|
2057
|
+
return this.registered;
|
|
2058
|
+
}
|
|
2059
|
+
/**
|
|
2060
|
+
* Get the full event mapping for introspection/debugging.
|
|
2061
|
+
* @task T162
|
|
2062
|
+
*/
|
|
2063
|
+
getEventMap() {
|
|
2064
|
+
return { ...CODEX_EVENT_MAP };
|
|
2065
|
+
}
|
|
2066
|
+
/**
|
|
2067
|
+
* Extract a plain-text transcript from Codex CLI session data.
|
|
2068
|
+
*
|
|
2069
|
+
* Reads the most recent session file under ~/.codex/ and extracts
|
|
2070
|
+
* turn text into a flat string for brain observation extraction.
|
|
2071
|
+
*
|
|
2072
|
+
* Returns null when no session data is found or on any read error.
|
|
2073
|
+
*
|
|
2074
|
+
* @param _sessionId - CLEO session ID (unused; reads the most recent file)
|
|
2075
|
+
* @param _projectDir - Project directory (unused; Codex CLI uses global paths)
|
|
2076
|
+
* @task T162 @epic T134
|
|
2077
|
+
*/
|
|
2078
|
+
async getTranscript(_sessionId, _projectDir) {
|
|
2079
|
+
try {
|
|
2080
|
+
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/root";
|
|
2081
|
+
const codexDir = join7(homeDir, ".codex");
|
|
2082
|
+
let allFiles = [];
|
|
2083
|
+
try {
|
|
2084
|
+
const entries = await readdir(codexDir, { withFileTypes: true });
|
|
2085
|
+
for (const entry of entries) {
|
|
2086
|
+
if (!entry.isFile()) continue;
|
|
2087
|
+
const name = entry.name;
|
|
2088
|
+
if (name.endsWith(".json") || name.endsWith(".jsonl")) {
|
|
2089
|
+
allFiles.push(join7(codexDir, name));
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
} catch {
|
|
2093
|
+
return null;
|
|
2094
|
+
}
|
|
2095
|
+
if (allFiles.length === 0) return null;
|
|
2096
|
+
allFiles = allFiles.sort((a, b) => b.localeCompare(a));
|
|
2097
|
+
const mostRecent = allFiles[0];
|
|
2098
|
+
if (!mostRecent) return null;
|
|
2099
|
+
const raw = await readFile2(mostRecent, "utf-8");
|
|
2100
|
+
const turns = [];
|
|
2101
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2102
|
+
for (const line of lines) {
|
|
2103
|
+
try {
|
|
2104
|
+
const entry = JSON.parse(line);
|
|
2105
|
+
const role = entry.role;
|
|
2106
|
+
const content = entry.content;
|
|
2107
|
+
if (role === "assistant" && typeof content === "string") {
|
|
2108
|
+
turns.push(`assistant: ${content}`);
|
|
2109
|
+
} else if (role === "user" && typeof content === "string") {
|
|
2110
|
+
turns.push(`user: ${content}`);
|
|
2111
|
+
}
|
|
2112
|
+
} catch {
|
|
2113
|
+
}
|
|
2114
|
+
}
|
|
2115
|
+
return turns.length > 0 ? turns.join("\n") : null;
|
|
2116
|
+
} catch {
|
|
2117
|
+
return null;
|
|
2118
|
+
}
|
|
2119
|
+
}
|
|
2120
|
+
};
|
|
2121
|
+
|
|
2122
|
+
// packages/adapters/src/providers/codex/install.ts
|
|
2123
|
+
import { existsSync as existsSync5, mkdirSync as mkdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
2124
|
+
import { homedir as homedir6 } from "node:os";
|
|
2125
|
+
import { join as join8 } from "node:path";
|
|
2126
|
+
var INSTRUCTION_REFERENCES2 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2127
|
+
var MCP_SERVER_KEY2 = "cleo";
|
|
2128
|
+
var CodexInstallProvider = class {
|
|
2129
|
+
/**
|
|
2130
|
+
* Install CLEO into a Codex CLI environment.
|
|
2131
|
+
*
|
|
2132
|
+
* @param options - Installation options including project directory and MCP server path
|
|
2133
|
+
* @returns Result describing what was installed
|
|
2134
|
+
* @task T162
|
|
2135
|
+
*/
|
|
2136
|
+
async install(options) {
|
|
2137
|
+
const { projectDir, mcpServerPath } = options;
|
|
2138
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2139
|
+
let instructionFileUpdated = false;
|
|
2140
|
+
let mcpRegistered = false;
|
|
2141
|
+
const details = {};
|
|
2142
|
+
if (mcpServerPath) {
|
|
2143
|
+
mcpRegistered = this.registerMcpServer(mcpServerPath);
|
|
2144
|
+
if (mcpRegistered) {
|
|
2145
|
+
details.mcpConfigPath = join8(homedir6(), ".codex", "config.json");
|
|
2146
|
+
}
|
|
2147
|
+
}
|
|
2148
|
+
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2149
|
+
if (instructionFileUpdated) {
|
|
2150
|
+
details.instructionFile = join8(projectDir, "AGENTS.md");
|
|
2151
|
+
}
|
|
2152
|
+
return {
|
|
2153
|
+
success: true,
|
|
2154
|
+
installedAt,
|
|
2155
|
+
instructionFileUpdated,
|
|
2156
|
+
mcpRegistered,
|
|
2157
|
+
details
|
|
2158
|
+
};
|
|
2159
|
+
}
|
|
2160
|
+
/**
|
|
2161
|
+
* Uninstall CLEO from the Codex CLI environment.
|
|
2162
|
+
*
|
|
2163
|
+
* Removes the MCP server registration from ~/.codex/config.json.
|
|
2164
|
+
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2165
|
+
* @task T162
|
|
2166
|
+
*/
|
|
2167
|
+
async uninstall() {
|
|
2168
|
+
const configPath = join8(homedir6(), ".codex", "config.json");
|
|
2169
|
+
if (existsSync5(configPath)) {
|
|
2170
|
+
try {
|
|
2171
|
+
const raw = readFileSync4(configPath, "utf-8");
|
|
2172
|
+
const config = JSON.parse(raw);
|
|
2173
|
+
const mcpServers = config.mcpServers;
|
|
2174
|
+
if (mcpServers && MCP_SERVER_KEY2 in mcpServers) {
|
|
2175
|
+
delete mcpServers[MCP_SERVER_KEY2];
|
|
2176
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2177
|
+
}
|
|
2178
|
+
} catch {
|
|
2179
|
+
}
|
|
2180
|
+
}
|
|
2181
|
+
}
|
|
2182
|
+
/**
|
|
2183
|
+
* Check whether CLEO is installed in the Codex CLI environment.
|
|
2184
|
+
*
|
|
2185
|
+
* Checks for MCP server registered in ~/.codex/config.json.
|
|
2186
|
+
* Returns true if the CLEO MCP server entry is found.
|
|
2187
|
+
* @task T162
|
|
2188
|
+
*/
|
|
2189
|
+
async isInstalled() {
|
|
2190
|
+
const configPath = join8(homedir6(), ".codex", "config.json");
|
|
2191
|
+
if (existsSync5(configPath)) {
|
|
2192
|
+
try {
|
|
2193
|
+
const config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
2194
|
+
const mcpServers = config.mcpServers;
|
|
2195
|
+
if (mcpServers && MCP_SERVER_KEY2 in mcpServers) {
|
|
2196
|
+
return true;
|
|
2197
|
+
}
|
|
2198
|
+
} catch {
|
|
2199
|
+
}
|
|
2200
|
+
}
|
|
2201
|
+
return false;
|
|
2202
|
+
}
|
|
2203
|
+
/**
|
|
2204
|
+
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2205
|
+
*
|
|
2206
|
+
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2207
|
+
*
|
|
2208
|
+
* @param projectDir - Project root directory
|
|
2209
|
+
* @task T162
|
|
2210
|
+
*/
|
|
2211
|
+
async ensureInstructionReferences(projectDir) {
|
|
2212
|
+
this.updateInstructionFile(projectDir);
|
|
2213
|
+
}
|
|
2214
|
+
/**
|
|
2215
|
+
* Register the CLEO MCP server in ~/.codex/config.json.
|
|
2216
|
+
*
|
|
2217
|
+
* Codex CLI stores its MCP server configuration in ~/.codex/config.json
|
|
2218
|
+
* under the mcpServers key.
|
|
2219
|
+
*
|
|
2220
|
+
* @param mcpServerPath - Absolute path to the MCP server entry point
|
|
2221
|
+
* @returns true if registration was performed or updated
|
|
2222
|
+
*/
|
|
2223
|
+
registerMcpServer(mcpServerPath) {
|
|
2224
|
+
const codexDir = join8(homedir6(), ".codex");
|
|
2225
|
+
const configPath = join8(codexDir, "config.json");
|
|
2226
|
+
let config = {};
|
|
2227
|
+
mkdirSync2(codexDir, { recursive: true });
|
|
2228
|
+
if (existsSync5(configPath)) {
|
|
2229
|
+
try {
|
|
2230
|
+
config = JSON.parse(readFileSync4(configPath, "utf-8"));
|
|
2231
|
+
} catch {
|
|
2232
|
+
}
|
|
2233
|
+
}
|
|
2234
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2235
|
+
config.mcpServers = {};
|
|
2236
|
+
}
|
|
2237
|
+
const mcpServers = config.mcpServers;
|
|
2238
|
+
mcpServers[MCP_SERVER_KEY2] = {
|
|
2239
|
+
command: "node",
|
|
2240
|
+
args: [mcpServerPath]
|
|
2241
|
+
};
|
|
2242
|
+
writeFileSync3(configPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2243
|
+
return true;
|
|
2244
|
+
}
|
|
2245
|
+
/**
|
|
2246
|
+
* Update AGENTS.md with CLEO @-references.
|
|
2247
|
+
*
|
|
2248
|
+
* @param projectDir - Project root directory
|
|
2249
|
+
* @returns true if the file was created or modified
|
|
2250
|
+
*/
|
|
2251
|
+
updateInstructionFile(projectDir) {
|
|
2252
|
+
const agentsMdPath = join8(projectDir, "AGENTS.md");
|
|
2253
|
+
let content = "";
|
|
2254
|
+
let existed = false;
|
|
2255
|
+
if (existsSync5(agentsMdPath)) {
|
|
2256
|
+
content = readFileSync4(agentsMdPath, "utf-8");
|
|
2257
|
+
existed = true;
|
|
2258
|
+
}
|
|
2259
|
+
const missingRefs = INSTRUCTION_REFERENCES2.filter((ref) => !content.includes(ref));
|
|
2260
|
+
if (missingRefs.length === 0) {
|
|
2261
|
+
return false;
|
|
2262
|
+
}
|
|
2263
|
+
const refsBlock = missingRefs.join("\n");
|
|
2264
|
+
if (existed) {
|
|
2265
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2266
|
+
content = content + separator + refsBlock + "\n";
|
|
2267
|
+
} else {
|
|
2268
|
+
content = refsBlock + "\n";
|
|
2269
|
+
}
|
|
2270
|
+
writeFileSync3(agentsMdPath, content, "utf-8");
|
|
2271
|
+
return true;
|
|
2272
|
+
}
|
|
2273
|
+
};
|
|
2274
|
+
|
|
2275
|
+
// packages/adapters/src/providers/codex/adapter.ts
|
|
2276
|
+
var execAsync3 = promisify3(exec3);
|
|
2277
|
+
var CodexAdapter = class {
|
|
2278
|
+
id = "codex";
|
|
2279
|
+
name = "Codex";
|
|
2280
|
+
version = "1.0.0";
|
|
2281
|
+
capabilities = {
|
|
2282
|
+
supportsHooks: true,
|
|
2283
|
+
supportedHookEvents: ["SessionStart", "UserPromptSubmit", "Stop"],
|
|
2284
|
+
supportsSpawn: false,
|
|
2285
|
+
supportsInstall: true,
|
|
2286
|
+
supportsMcp: true,
|
|
2287
|
+
supportsInstructionFiles: false,
|
|
2288
|
+
supportsContextMonitor: false,
|
|
2289
|
+
supportsStatusline: false,
|
|
2290
|
+
supportsProviderPaths: false,
|
|
2291
|
+
supportsTransport: false,
|
|
2292
|
+
supportsTaskSync: false
|
|
2293
|
+
};
|
|
2294
|
+
hooks;
|
|
2295
|
+
install;
|
|
2296
|
+
projectDir = null;
|
|
2297
|
+
initialized = false;
|
|
2298
|
+
constructor() {
|
|
2299
|
+
this.hooks = new CodexHookProvider();
|
|
2300
|
+
this.install = new CodexInstallProvider();
|
|
2301
|
+
}
|
|
2302
|
+
/**
|
|
2303
|
+
* Initialize the adapter for a given project directory.
|
|
2304
|
+
*
|
|
2305
|
+
* @param projectDir - Root directory of the project
|
|
2306
|
+
* @task T162
|
|
2307
|
+
*/
|
|
2308
|
+
async initialize(projectDir) {
|
|
2309
|
+
this.projectDir = projectDir;
|
|
2310
|
+
this.initialized = true;
|
|
2311
|
+
}
|
|
2312
|
+
/**
|
|
2313
|
+
* Dispose the adapter and clean up resources.
|
|
2314
|
+
*
|
|
2315
|
+
* Unregisters hooks and releases any tracked state.
|
|
2316
|
+
* @task T162
|
|
2317
|
+
*/
|
|
2318
|
+
async dispose() {
|
|
2319
|
+
if (this.hooks.isRegistered()) {
|
|
2320
|
+
await this.hooks.unregisterNativeHooks();
|
|
2321
|
+
}
|
|
2322
|
+
this.initialized = false;
|
|
2323
|
+
this.projectDir = null;
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Run a health check to verify Codex CLI is accessible.
|
|
2327
|
+
*
|
|
2328
|
+
* Checks:
|
|
2329
|
+
* 1. Adapter has been initialized
|
|
2330
|
+
* 2. Codex CLI binary is available in PATH
|
|
2331
|
+
* 3. ~/.codex/ configuration directory exists
|
|
2332
|
+
*
|
|
2333
|
+
* @returns Health status with details about each check
|
|
2334
|
+
* @task T162
|
|
2335
|
+
*/
|
|
2336
|
+
async healthCheck() {
|
|
2337
|
+
const details = {};
|
|
2338
|
+
if (!this.initialized) {
|
|
2339
|
+
return {
|
|
2340
|
+
healthy: false,
|
|
2341
|
+
provider: this.id,
|
|
2342
|
+
details: { error: "Adapter not initialized" }
|
|
2343
|
+
};
|
|
2344
|
+
}
|
|
2345
|
+
let cliAvailable = false;
|
|
2346
|
+
try {
|
|
2347
|
+
const { stdout } = await execAsync3("which codex");
|
|
2348
|
+
cliAvailable = stdout.trim().length > 0;
|
|
2349
|
+
details.cliPath = stdout.trim();
|
|
2350
|
+
} catch {
|
|
2351
|
+
details.cliAvailable = false;
|
|
2352
|
+
}
|
|
2353
|
+
const codexConfigDir = join9(homedir7(), ".codex");
|
|
2354
|
+
const configExists = existsSync6(codexConfigDir);
|
|
2355
|
+
details.configDirExists = configExists;
|
|
2356
|
+
const healthy = cliAvailable;
|
|
2357
|
+
details.cliAvailable = cliAvailable;
|
|
2358
|
+
return {
|
|
2359
|
+
healthy,
|
|
2360
|
+
provider: this.id,
|
|
2361
|
+
details
|
|
2362
|
+
};
|
|
2363
|
+
}
|
|
2364
|
+
/**
|
|
2365
|
+
* Check whether the adapter has been initialized.
|
|
2366
|
+
* @task T162
|
|
2367
|
+
*/
|
|
2368
|
+
isInitialized() {
|
|
2369
|
+
return this.initialized;
|
|
2370
|
+
}
|
|
2371
|
+
/**
|
|
2372
|
+
* Get the project directory this adapter was initialized with.
|
|
2373
|
+
* @task T162
|
|
2374
|
+
*/
|
|
2375
|
+
getProjectDir() {
|
|
2376
|
+
return this.projectDir;
|
|
2377
|
+
}
|
|
2378
|
+
};
|
|
2379
|
+
|
|
2380
|
+
// packages/adapters/src/providers/codex/index.ts
|
|
2381
|
+
function createAdapter2() {
|
|
2382
|
+
return new CodexAdapter();
|
|
2383
|
+
}
|
|
2384
|
+
|
|
2385
|
+
// packages/adapters/src/index.ts
|
|
1999
2386
|
init_cursor();
|
|
2387
|
+
|
|
2388
|
+
// packages/adapters/src/providers/gemini-cli/adapter.ts
|
|
2389
|
+
import { exec as exec4 } from "node:child_process";
|
|
2390
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
2391
|
+
import { homedir as homedir9 } from "node:os";
|
|
2392
|
+
import { join as join14 } from "node:path";
|
|
2393
|
+
import { promisify as promisify4 } from "node:util";
|
|
2394
|
+
|
|
2395
|
+
// packages/adapters/src/providers/gemini-cli/hooks.ts
|
|
2396
|
+
import { readdir as readdir2, readFile as readFile3 } from "node:fs/promises";
|
|
2397
|
+
import { join as join12 } from "node:path";
|
|
2398
|
+
var GEMINI_CLI_EVENT_MAP = {
|
|
2399
|
+
SessionStart: "SessionStart",
|
|
2400
|
+
SessionEnd: "SessionEnd",
|
|
2401
|
+
PromptSubmit: "BeforeAgent",
|
|
2402
|
+
ResponseComplete: "AfterAgent",
|
|
2403
|
+
PreToolUse: "BeforeTool",
|
|
2404
|
+
PostToolUse: "AfterTool",
|
|
2405
|
+
PreModel: "BeforeModel",
|
|
2406
|
+
PostModel: "AfterModel",
|
|
2407
|
+
PreCompact: "PreCompress",
|
|
2408
|
+
Notification: "Notification"
|
|
2409
|
+
};
|
|
2410
|
+
var GeminiCliHookProvider = class {
|
|
2411
|
+
registered = false;
|
|
2412
|
+
/**
|
|
2413
|
+
* Map a Gemini CLI native event name to a CAAMP hook event name.
|
|
2414
|
+
*
|
|
2415
|
+
* @param providerEvent - Gemini CLI event name (e.g. "SessionStart", "PreToolUse")
|
|
2416
|
+
* @returns CAAMP event name or null if unmapped
|
|
2417
|
+
* @task T161
|
|
2418
|
+
*/
|
|
2419
|
+
mapProviderEvent(providerEvent) {
|
|
2420
|
+
return GEMINI_CLI_EVENT_MAP[providerEvent] ?? null;
|
|
2421
|
+
}
|
|
2422
|
+
/**
|
|
2423
|
+
* Register native hooks for a project.
|
|
2424
|
+
*
|
|
2425
|
+
* For Gemini CLI, hooks are registered via the config system
|
|
2426
|
+
* (~/.gemini/), which is handled by the install provider.
|
|
2427
|
+
* This method marks hooks as registered without performing
|
|
2428
|
+
* filesystem operations.
|
|
2429
|
+
*
|
|
2430
|
+
* @param _projectDir - Project directory (unused; hooks are global)
|
|
2431
|
+
* @task T161
|
|
2432
|
+
*/
|
|
2433
|
+
async registerNativeHooks(_projectDir) {
|
|
2434
|
+
this.registered = true;
|
|
2435
|
+
}
|
|
2436
|
+
/**
|
|
2437
|
+
* Unregister native hooks.
|
|
2438
|
+
*
|
|
2439
|
+
* For Gemini CLI, this is a no-op since hooks are managed through
|
|
2440
|
+
* the config system. Unregistration happens via the install
|
|
2441
|
+
* provider's uninstall method.
|
|
2442
|
+
* @task T161
|
|
2443
|
+
*/
|
|
2444
|
+
async unregisterNativeHooks() {
|
|
2445
|
+
this.registered = false;
|
|
2446
|
+
}
|
|
2447
|
+
/**
|
|
2448
|
+
* Check whether hooks have been registered via registerNativeHooks.
|
|
2449
|
+
* @task T161
|
|
2450
|
+
*/
|
|
2451
|
+
isRegistered() {
|
|
2452
|
+
return this.registered;
|
|
2453
|
+
}
|
|
2454
|
+
/**
|
|
2455
|
+
* Get the full event mapping for introspection/debugging.
|
|
2456
|
+
* @task T161
|
|
2457
|
+
*/
|
|
2458
|
+
getEventMap() {
|
|
2459
|
+
return { ...GEMINI_CLI_EVENT_MAP };
|
|
2460
|
+
}
|
|
2461
|
+
/**
|
|
2462
|
+
* Extract a plain-text transcript from Gemini CLI session data.
|
|
2463
|
+
*
|
|
2464
|
+
* Reads the most recent session file under ~/.gemini/ and extracts
|
|
2465
|
+
* turn text into a flat string for brain observation extraction.
|
|
2466
|
+
*
|
|
2467
|
+
* Returns null when no session data is found or on any read error.
|
|
2468
|
+
*
|
|
2469
|
+
* @param _sessionId - CLEO session ID (unused; reads the most recent file)
|
|
2470
|
+
* @param _projectDir - Project directory (unused; Gemini CLI uses global paths)
|
|
2471
|
+
* @task T161 @epic T134
|
|
2472
|
+
*/
|
|
2473
|
+
async getTranscript(_sessionId, _projectDir) {
|
|
2474
|
+
try {
|
|
2475
|
+
const homeDir = process.env.HOME ?? process.env.USERPROFILE ?? "/root";
|
|
2476
|
+
const geminiDir = join12(homeDir, ".gemini");
|
|
2477
|
+
let allFiles = [];
|
|
2478
|
+
try {
|
|
2479
|
+
const entries = await readdir2(geminiDir, { withFileTypes: true });
|
|
2480
|
+
for (const entry of entries) {
|
|
2481
|
+
if (!entry.isFile()) continue;
|
|
2482
|
+
const name = entry.name;
|
|
2483
|
+
if (name.endsWith(".json") || name.endsWith(".jsonl")) {
|
|
2484
|
+
allFiles.push(join12(geminiDir, name));
|
|
2485
|
+
}
|
|
2486
|
+
}
|
|
2487
|
+
} catch {
|
|
2488
|
+
return null;
|
|
2489
|
+
}
|
|
2490
|
+
if (allFiles.length === 0) return null;
|
|
2491
|
+
allFiles = allFiles.sort((a, b) => b.localeCompare(a));
|
|
2492
|
+
const mostRecent = allFiles[0];
|
|
2493
|
+
if (!mostRecent) return null;
|
|
2494
|
+
const raw = await readFile3(mostRecent, "utf-8");
|
|
2495
|
+
const turns = [];
|
|
2496
|
+
const lines = raw.split("\n").filter((l) => l.trim());
|
|
2497
|
+
for (const line of lines) {
|
|
2498
|
+
try {
|
|
2499
|
+
const entry = JSON.parse(line);
|
|
2500
|
+
const role = entry.role;
|
|
2501
|
+
const content = entry.content;
|
|
2502
|
+
if (role === "assistant" && typeof content === "string") {
|
|
2503
|
+
turns.push(`assistant: ${content}`);
|
|
2504
|
+
} else if (role === "user" && typeof content === "string") {
|
|
2505
|
+
turns.push(`user: ${content}`);
|
|
2506
|
+
}
|
|
2507
|
+
} catch {
|
|
2508
|
+
}
|
|
2509
|
+
}
|
|
2510
|
+
return turns.length > 0 ? turns.join("\n") : null;
|
|
2511
|
+
} catch {
|
|
2512
|
+
return null;
|
|
2513
|
+
}
|
|
2514
|
+
}
|
|
2515
|
+
};
|
|
2516
|
+
|
|
2517
|
+
// packages/adapters/src/providers/gemini-cli/install.ts
|
|
2518
|
+
import { existsSync as existsSync9, mkdirSync as mkdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync5 } from "node:fs";
|
|
2519
|
+
import { homedir as homedir8 } from "node:os";
|
|
2520
|
+
import { join as join13 } from "node:path";
|
|
2521
|
+
var INSTRUCTION_REFERENCES4 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2522
|
+
var MCP_SERVER_KEY4 = "cleo";
|
|
2523
|
+
var GeminiCliInstallProvider = class {
|
|
2524
|
+
/**
|
|
2525
|
+
* Install CLEO into a Gemini CLI environment.
|
|
2526
|
+
*
|
|
2527
|
+
* @param options - Installation options including project directory and MCP server path
|
|
2528
|
+
* @returns Result describing what was installed
|
|
2529
|
+
* @task T161
|
|
2530
|
+
*/
|
|
2531
|
+
async install(options) {
|
|
2532
|
+
const { projectDir, mcpServerPath } = options;
|
|
2533
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2534
|
+
let instructionFileUpdated = false;
|
|
2535
|
+
let mcpRegistered = false;
|
|
2536
|
+
const details = {};
|
|
2537
|
+
if (mcpServerPath) {
|
|
2538
|
+
mcpRegistered = this.registerMcpServer(mcpServerPath);
|
|
2539
|
+
if (mcpRegistered) {
|
|
2540
|
+
details.mcpConfigPath = join13(homedir8(), ".gemini", "settings.json");
|
|
2541
|
+
}
|
|
2542
|
+
}
|
|
2543
|
+
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2544
|
+
if (instructionFileUpdated) {
|
|
2545
|
+
details.instructionFile = join13(projectDir, "AGENTS.md");
|
|
2546
|
+
}
|
|
2547
|
+
return {
|
|
2548
|
+
success: true,
|
|
2549
|
+
installedAt,
|
|
2550
|
+
instructionFileUpdated,
|
|
2551
|
+
mcpRegistered,
|
|
2552
|
+
details
|
|
2553
|
+
};
|
|
2554
|
+
}
|
|
2555
|
+
/**
|
|
2556
|
+
* Uninstall CLEO from the Gemini CLI environment.
|
|
2557
|
+
*
|
|
2558
|
+
* Removes the MCP server registration from ~/.gemini/settings.json.
|
|
2559
|
+
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2560
|
+
* @task T161
|
|
2561
|
+
*/
|
|
2562
|
+
async uninstall() {
|
|
2563
|
+
const settingsPath = join13(homedir8(), ".gemini", "settings.json");
|
|
2564
|
+
if (existsSync9(settingsPath)) {
|
|
2565
|
+
try {
|
|
2566
|
+
const raw = readFileSync6(settingsPath, "utf-8");
|
|
2567
|
+
const config = JSON.parse(raw);
|
|
2568
|
+
const mcpServers = config.mcpServers;
|
|
2569
|
+
if (mcpServers && MCP_SERVER_KEY4 in mcpServers) {
|
|
2570
|
+
delete mcpServers[MCP_SERVER_KEY4];
|
|
2571
|
+
writeFileSync5(settingsPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2572
|
+
}
|
|
2573
|
+
} catch {
|
|
2574
|
+
}
|
|
2575
|
+
}
|
|
2576
|
+
}
|
|
2577
|
+
/**
|
|
2578
|
+
* Check whether CLEO is installed in the Gemini CLI environment.
|
|
2579
|
+
*
|
|
2580
|
+
* Checks for MCP server registered in ~/.gemini/settings.json.
|
|
2581
|
+
* Returns true if the CLEO MCP server entry is found.
|
|
2582
|
+
* @task T161
|
|
2583
|
+
*/
|
|
2584
|
+
async isInstalled() {
|
|
2585
|
+
const settingsPath = join13(homedir8(), ".gemini", "settings.json");
|
|
2586
|
+
if (existsSync9(settingsPath)) {
|
|
2587
|
+
try {
|
|
2588
|
+
const config = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
2589
|
+
const mcpServers = config.mcpServers;
|
|
2590
|
+
if (mcpServers && MCP_SERVER_KEY4 in mcpServers) {
|
|
2591
|
+
return true;
|
|
2592
|
+
}
|
|
2593
|
+
} catch {
|
|
2594
|
+
}
|
|
2595
|
+
}
|
|
2596
|
+
return false;
|
|
2597
|
+
}
|
|
2598
|
+
/**
|
|
2599
|
+
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2600
|
+
*
|
|
2601
|
+
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2602
|
+
*
|
|
2603
|
+
* @param projectDir - Project root directory
|
|
2604
|
+
* @task T161
|
|
2605
|
+
*/
|
|
2606
|
+
async ensureInstructionReferences(projectDir) {
|
|
2607
|
+
this.updateInstructionFile(projectDir);
|
|
2608
|
+
}
|
|
2609
|
+
/**
|
|
2610
|
+
* Register the CLEO MCP server in ~/.gemini/settings.json.
|
|
2611
|
+
*
|
|
2612
|
+
* Gemini CLI stores its MCP server configuration in ~/.gemini/settings.json
|
|
2613
|
+
* under the mcpServers key.
|
|
2614
|
+
*
|
|
2615
|
+
* @param mcpServerPath - Absolute path to the MCP server entry point
|
|
2616
|
+
* @returns true if registration was performed or updated
|
|
2617
|
+
*/
|
|
2618
|
+
registerMcpServer(mcpServerPath) {
|
|
2619
|
+
const geminiDir = join13(homedir8(), ".gemini");
|
|
2620
|
+
const settingsPath = join13(geminiDir, "settings.json");
|
|
2621
|
+
let config = {};
|
|
2622
|
+
mkdirSync4(geminiDir, { recursive: true });
|
|
2623
|
+
if (existsSync9(settingsPath)) {
|
|
2624
|
+
try {
|
|
2625
|
+
config = JSON.parse(readFileSync6(settingsPath, "utf-8"));
|
|
2626
|
+
} catch {
|
|
2627
|
+
}
|
|
2628
|
+
}
|
|
2629
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2630
|
+
config.mcpServers = {};
|
|
2631
|
+
}
|
|
2632
|
+
const mcpServers = config.mcpServers;
|
|
2633
|
+
mcpServers[MCP_SERVER_KEY4] = {
|
|
2634
|
+
command: "node",
|
|
2635
|
+
args: [mcpServerPath]
|
|
2636
|
+
};
|
|
2637
|
+
writeFileSync5(settingsPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2638
|
+
return true;
|
|
2639
|
+
}
|
|
2640
|
+
/**
|
|
2641
|
+
* Update AGENTS.md with CLEO @-references.
|
|
2642
|
+
*
|
|
2643
|
+
* @param projectDir - Project root directory
|
|
2644
|
+
* @returns true if the file was created or modified
|
|
2645
|
+
*/
|
|
2646
|
+
updateInstructionFile(projectDir) {
|
|
2647
|
+
const agentsMdPath = join13(projectDir, "AGENTS.md");
|
|
2648
|
+
let content = "";
|
|
2649
|
+
let existed = false;
|
|
2650
|
+
if (existsSync9(agentsMdPath)) {
|
|
2651
|
+
content = readFileSync6(agentsMdPath, "utf-8");
|
|
2652
|
+
existed = true;
|
|
2653
|
+
}
|
|
2654
|
+
const missingRefs = INSTRUCTION_REFERENCES4.filter((ref) => !content.includes(ref));
|
|
2655
|
+
if (missingRefs.length === 0) {
|
|
2656
|
+
return false;
|
|
2657
|
+
}
|
|
2658
|
+
const refsBlock = missingRefs.join("\n");
|
|
2659
|
+
if (existed) {
|
|
2660
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2661
|
+
content = content + separator + refsBlock + "\n";
|
|
2662
|
+
} else {
|
|
2663
|
+
content = refsBlock + "\n";
|
|
2664
|
+
}
|
|
2665
|
+
writeFileSync5(agentsMdPath, content, "utf-8");
|
|
2666
|
+
return true;
|
|
2667
|
+
}
|
|
2668
|
+
};
|
|
2669
|
+
|
|
2670
|
+
// packages/adapters/src/providers/gemini-cli/adapter.ts
|
|
2671
|
+
var execAsync4 = promisify4(exec4);
|
|
2672
|
+
var GeminiCliAdapter = class {
|
|
2673
|
+
id = "gemini-cli";
|
|
2674
|
+
name = "Gemini CLI";
|
|
2675
|
+
version = "1.0.0";
|
|
2676
|
+
capabilities = {
|
|
2677
|
+
supportsHooks: true,
|
|
2678
|
+
supportedHookEvents: [
|
|
2679
|
+
"SessionStart",
|
|
2680
|
+
"SessionEnd",
|
|
2681
|
+
"BeforeAgent",
|
|
2682
|
+
"AfterAgent",
|
|
2683
|
+
"BeforeTool",
|
|
2684
|
+
"AfterTool",
|
|
2685
|
+
"BeforeModel",
|
|
2686
|
+
"AfterModel",
|
|
2687
|
+
"PreCompress",
|
|
2688
|
+
"Notification"
|
|
2689
|
+
],
|
|
2690
|
+
supportsSpawn: false,
|
|
2691
|
+
supportsInstall: true,
|
|
2692
|
+
supportsMcp: true,
|
|
2693
|
+
supportsInstructionFiles: false,
|
|
2694
|
+
supportsContextMonitor: false,
|
|
2695
|
+
supportsStatusline: false,
|
|
2696
|
+
supportsProviderPaths: false,
|
|
2697
|
+
supportsTransport: false,
|
|
2698
|
+
supportsTaskSync: false
|
|
2699
|
+
};
|
|
2700
|
+
hooks;
|
|
2701
|
+
install;
|
|
2702
|
+
projectDir = null;
|
|
2703
|
+
initialized = false;
|
|
2704
|
+
constructor() {
|
|
2705
|
+
this.hooks = new GeminiCliHookProvider();
|
|
2706
|
+
this.install = new GeminiCliInstallProvider();
|
|
2707
|
+
}
|
|
2708
|
+
/**
|
|
2709
|
+
* Initialize the adapter for a given project directory.
|
|
2710
|
+
*
|
|
2711
|
+
* @param projectDir - Root directory of the project
|
|
2712
|
+
* @task T161
|
|
2713
|
+
*/
|
|
2714
|
+
async initialize(projectDir) {
|
|
2715
|
+
this.projectDir = projectDir;
|
|
2716
|
+
this.initialized = true;
|
|
2717
|
+
}
|
|
2718
|
+
/**
|
|
2719
|
+
* Dispose the adapter and clean up resources.
|
|
2720
|
+
*
|
|
2721
|
+
* Unregisters hooks and releases any tracked state.
|
|
2722
|
+
* @task T161
|
|
2723
|
+
*/
|
|
2724
|
+
async dispose() {
|
|
2725
|
+
if (this.hooks.isRegistered()) {
|
|
2726
|
+
await this.hooks.unregisterNativeHooks();
|
|
2727
|
+
}
|
|
2728
|
+
this.initialized = false;
|
|
2729
|
+
this.projectDir = null;
|
|
2730
|
+
}
|
|
2731
|
+
/**
|
|
2732
|
+
* Run a health check to verify Gemini CLI is accessible.
|
|
2733
|
+
*
|
|
2734
|
+
* Checks:
|
|
2735
|
+
* 1. Adapter has been initialized
|
|
2736
|
+
* 2. Gemini CLI binary is available in PATH
|
|
2737
|
+
* 3. ~/.gemini/ configuration directory exists
|
|
2738
|
+
*
|
|
2739
|
+
* @returns Health status with details about each check
|
|
2740
|
+
* @task T161
|
|
2741
|
+
*/
|
|
2742
|
+
async healthCheck() {
|
|
2743
|
+
const details = {};
|
|
2744
|
+
if (!this.initialized) {
|
|
2745
|
+
return {
|
|
2746
|
+
healthy: false,
|
|
2747
|
+
provider: this.id,
|
|
2748
|
+
details: { error: "Adapter not initialized" }
|
|
2749
|
+
};
|
|
2750
|
+
}
|
|
2751
|
+
let cliAvailable = false;
|
|
2752
|
+
try {
|
|
2753
|
+
const { stdout } = await execAsync4("which gemini");
|
|
2754
|
+
cliAvailable = stdout.trim().length > 0;
|
|
2755
|
+
details.cliPath = stdout.trim();
|
|
2756
|
+
} catch {
|
|
2757
|
+
details.cliAvailable = false;
|
|
2758
|
+
}
|
|
2759
|
+
const geminiConfigDir = join14(homedir9(), ".gemini");
|
|
2760
|
+
const configExists = existsSync10(geminiConfigDir);
|
|
2761
|
+
details.configDirExists = configExists;
|
|
2762
|
+
const healthy = cliAvailable;
|
|
2763
|
+
details.cliAvailable = cliAvailable;
|
|
2764
|
+
return {
|
|
2765
|
+
healthy,
|
|
2766
|
+
provider: this.id,
|
|
2767
|
+
details
|
|
2768
|
+
};
|
|
2769
|
+
}
|
|
2770
|
+
/**
|
|
2771
|
+
* Check whether the adapter has been initialized.
|
|
2772
|
+
* @task T161
|
|
2773
|
+
*/
|
|
2774
|
+
isInitialized() {
|
|
2775
|
+
return this.initialized;
|
|
2776
|
+
}
|
|
2777
|
+
/**
|
|
2778
|
+
* Get the project directory this adapter was initialized with.
|
|
2779
|
+
* @task T161
|
|
2780
|
+
*/
|
|
2781
|
+
getProjectDir() {
|
|
2782
|
+
return this.projectDir;
|
|
2783
|
+
}
|
|
2784
|
+
};
|
|
2785
|
+
|
|
2786
|
+
// packages/adapters/src/providers/gemini-cli/index.ts
|
|
2787
|
+
function createAdapter4() {
|
|
2788
|
+
return new GeminiCliAdapter();
|
|
2789
|
+
}
|
|
2790
|
+
|
|
2791
|
+
// packages/adapters/src/providers/kimi/adapter.ts
|
|
2792
|
+
import { exec as exec5 } from "node:child_process";
|
|
2793
|
+
import { existsSync as existsSync12 } from "node:fs";
|
|
2794
|
+
import { homedir as homedir11 } from "node:os";
|
|
2795
|
+
import { join as join16 } from "node:path";
|
|
2796
|
+
import { promisify as promisify5 } from "node:util";
|
|
2797
|
+
|
|
2798
|
+
// packages/adapters/src/providers/kimi/hooks.ts
|
|
2799
|
+
var KimiHookProvider = class {
|
|
2800
|
+
registered = false;
|
|
2801
|
+
/**
|
|
2802
|
+
* Map a Kimi native event name to a CAAMP hook event name.
|
|
2803
|
+
*
|
|
2804
|
+
* Kimi has no hook system, so this always returns null.
|
|
2805
|
+
*
|
|
2806
|
+
* @param _providerEvent - Unused; Kimi emits no hookable events
|
|
2807
|
+
* @returns Always null
|
|
2808
|
+
* @task T163
|
|
2809
|
+
*/
|
|
2810
|
+
mapProviderEvent(_providerEvent) {
|
|
2811
|
+
return null;
|
|
2812
|
+
}
|
|
2813
|
+
/**
|
|
2814
|
+
* Register native hooks for a project.
|
|
2815
|
+
*
|
|
2816
|
+
* Kimi has no hook system. This method is a no-op and only
|
|
2817
|
+
* tracks registration state for interface compliance.
|
|
2818
|
+
*
|
|
2819
|
+
* @param _projectDir - Project directory (unused)
|
|
2820
|
+
* @task T163
|
|
2821
|
+
*/
|
|
2822
|
+
async registerNativeHooks(_projectDir) {
|
|
2823
|
+
this.registered = true;
|
|
2824
|
+
}
|
|
2825
|
+
/**
|
|
2826
|
+
* Unregister native hooks.
|
|
2827
|
+
*
|
|
2828
|
+
* Kimi has no hook system. This method is a no-op.
|
|
2829
|
+
* @task T163
|
|
2830
|
+
*/
|
|
2831
|
+
async unregisterNativeHooks() {
|
|
2832
|
+
this.registered = false;
|
|
2833
|
+
}
|
|
2834
|
+
/**
|
|
2835
|
+
* Check whether hooks have been registered via registerNativeHooks.
|
|
2836
|
+
* @task T163
|
|
2837
|
+
*/
|
|
2838
|
+
isRegistered() {
|
|
2839
|
+
return this.registered;
|
|
2840
|
+
}
|
|
2841
|
+
/**
|
|
2842
|
+
* Get the full event mapping for introspection/debugging.
|
|
2843
|
+
*
|
|
2844
|
+
* Returns an empty map since Kimi has no hookable events.
|
|
2845
|
+
* @task T163
|
|
2846
|
+
*/
|
|
2847
|
+
getEventMap() {
|
|
2848
|
+
return {};
|
|
2849
|
+
}
|
|
2850
|
+
};
|
|
2851
|
+
|
|
2852
|
+
// packages/adapters/src/providers/kimi/install.ts
|
|
2853
|
+
import { existsSync as existsSync11, mkdirSync as mkdirSync5, readFileSync as readFileSync7, writeFileSync as writeFileSync6 } from "node:fs";
|
|
2854
|
+
import { homedir as homedir10 } from "node:os";
|
|
2855
|
+
import { join as join15 } from "node:path";
|
|
2856
|
+
var INSTRUCTION_REFERENCES5 = ["@~/.cleo/templates/CLEO-INJECTION.md", "@.cleo/memory-bridge.md"];
|
|
2857
|
+
var MCP_SERVER_KEY5 = "cleo";
|
|
2858
|
+
var KimiInstallProvider = class {
|
|
2859
|
+
/**
|
|
2860
|
+
* Install CLEO into a Kimi environment.
|
|
2861
|
+
*
|
|
2862
|
+
* @param options - Installation options including project directory and MCP server path
|
|
2863
|
+
* @returns Result describing what was installed
|
|
2864
|
+
* @task T163
|
|
2865
|
+
*/
|
|
2866
|
+
async install(options) {
|
|
2867
|
+
const { projectDir, mcpServerPath } = options;
|
|
2868
|
+
const installedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
2869
|
+
let instructionFileUpdated = false;
|
|
2870
|
+
let mcpRegistered = false;
|
|
2871
|
+
const details = {};
|
|
2872
|
+
if (mcpServerPath) {
|
|
2873
|
+
mcpRegistered = this.registerMcpServer(mcpServerPath);
|
|
2874
|
+
if (mcpRegistered) {
|
|
2875
|
+
details.mcpConfigPath = join15(homedir10(), ".kimi", "mcp.json");
|
|
2876
|
+
}
|
|
2877
|
+
}
|
|
2878
|
+
instructionFileUpdated = this.updateInstructionFile(projectDir);
|
|
2879
|
+
if (instructionFileUpdated) {
|
|
2880
|
+
details.instructionFile = join15(projectDir, "AGENTS.md");
|
|
2881
|
+
}
|
|
2882
|
+
return {
|
|
2883
|
+
success: true,
|
|
2884
|
+
installedAt,
|
|
2885
|
+
instructionFileUpdated,
|
|
2886
|
+
mcpRegistered,
|
|
2887
|
+
details
|
|
2888
|
+
};
|
|
2889
|
+
}
|
|
2890
|
+
/**
|
|
2891
|
+
* Uninstall CLEO from the Kimi environment.
|
|
2892
|
+
*
|
|
2893
|
+
* Removes the MCP server registration from ~/.kimi/mcp.json.
|
|
2894
|
+
* Does not remove AGENTS.md references (they are harmless if CLEO is not present).
|
|
2895
|
+
* @task T163
|
|
2896
|
+
*/
|
|
2897
|
+
async uninstall() {
|
|
2898
|
+
const mcpPath = join15(homedir10(), ".kimi", "mcp.json");
|
|
2899
|
+
if (existsSync11(mcpPath)) {
|
|
2900
|
+
try {
|
|
2901
|
+
const raw = readFileSync7(mcpPath, "utf-8");
|
|
2902
|
+
const config = JSON.parse(raw);
|
|
2903
|
+
const mcpServers = config.mcpServers;
|
|
2904
|
+
if (mcpServers && MCP_SERVER_KEY5 in mcpServers) {
|
|
2905
|
+
delete mcpServers[MCP_SERVER_KEY5];
|
|
2906
|
+
writeFileSync6(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2907
|
+
}
|
|
2908
|
+
} catch {
|
|
2909
|
+
}
|
|
2910
|
+
}
|
|
2911
|
+
}
|
|
2912
|
+
/**
|
|
2913
|
+
* Check whether CLEO is installed in the Kimi environment.
|
|
2914
|
+
*
|
|
2915
|
+
* Checks for MCP server registered in ~/.kimi/mcp.json.
|
|
2916
|
+
* Returns true if the CLEO MCP server entry is found.
|
|
2917
|
+
* @task T163
|
|
2918
|
+
*/
|
|
2919
|
+
async isInstalled() {
|
|
2920
|
+
const mcpPath = join15(homedir10(), ".kimi", "mcp.json");
|
|
2921
|
+
if (existsSync11(mcpPath)) {
|
|
2922
|
+
try {
|
|
2923
|
+
const config = JSON.parse(readFileSync7(mcpPath, "utf-8"));
|
|
2924
|
+
const mcpServers = config.mcpServers;
|
|
2925
|
+
if (mcpServers && MCP_SERVER_KEY5 in mcpServers) {
|
|
2926
|
+
return true;
|
|
2927
|
+
}
|
|
2928
|
+
} catch {
|
|
2929
|
+
}
|
|
2930
|
+
}
|
|
2931
|
+
return false;
|
|
2932
|
+
}
|
|
2933
|
+
/**
|
|
2934
|
+
* Ensure AGENTS.md contains @-references to CLEO instruction files.
|
|
2935
|
+
*
|
|
2936
|
+
* Creates AGENTS.md if it does not exist. Appends any missing references.
|
|
2937
|
+
*
|
|
2938
|
+
* @param projectDir - Project root directory
|
|
2939
|
+
* @task T163
|
|
2940
|
+
*/
|
|
2941
|
+
async ensureInstructionReferences(projectDir) {
|
|
2942
|
+
this.updateInstructionFile(projectDir);
|
|
2943
|
+
}
|
|
2944
|
+
/**
|
|
2945
|
+
* Register the CLEO MCP server in ~/.kimi/mcp.json.
|
|
2946
|
+
*
|
|
2947
|
+
* Kimi stores its MCP server configuration in ~/.kimi/mcp.json
|
|
2948
|
+
* under the mcpServers key.
|
|
2949
|
+
*
|
|
2950
|
+
* @param mcpServerPath - Absolute path to the MCP server entry point
|
|
2951
|
+
* @returns true if registration was performed or updated
|
|
2952
|
+
*/
|
|
2953
|
+
registerMcpServer(mcpServerPath) {
|
|
2954
|
+
const kimiDir = join15(homedir10(), ".kimi");
|
|
2955
|
+
const mcpPath = join15(kimiDir, "mcp.json");
|
|
2956
|
+
let config = {};
|
|
2957
|
+
mkdirSync5(kimiDir, { recursive: true });
|
|
2958
|
+
if (existsSync11(mcpPath)) {
|
|
2959
|
+
try {
|
|
2960
|
+
config = JSON.parse(readFileSync7(mcpPath, "utf-8"));
|
|
2961
|
+
} catch {
|
|
2962
|
+
}
|
|
2963
|
+
}
|
|
2964
|
+
if (!config.mcpServers || typeof config.mcpServers !== "object") {
|
|
2965
|
+
config.mcpServers = {};
|
|
2966
|
+
}
|
|
2967
|
+
const mcpServers = config.mcpServers;
|
|
2968
|
+
mcpServers[MCP_SERVER_KEY5] = {
|
|
2969
|
+
command: "node",
|
|
2970
|
+
args: [mcpServerPath]
|
|
2971
|
+
};
|
|
2972
|
+
writeFileSync6(mcpPath, JSON.stringify(config, null, 2) + "\n", "utf-8");
|
|
2973
|
+
return true;
|
|
2974
|
+
}
|
|
2975
|
+
/**
|
|
2976
|
+
* Update AGENTS.md with CLEO @-references.
|
|
2977
|
+
*
|
|
2978
|
+
* @param projectDir - Project root directory
|
|
2979
|
+
* @returns true if the file was created or modified
|
|
2980
|
+
*/
|
|
2981
|
+
updateInstructionFile(projectDir) {
|
|
2982
|
+
const agentsMdPath = join15(projectDir, "AGENTS.md");
|
|
2983
|
+
let content = "";
|
|
2984
|
+
let existed = false;
|
|
2985
|
+
if (existsSync11(agentsMdPath)) {
|
|
2986
|
+
content = readFileSync7(agentsMdPath, "utf-8");
|
|
2987
|
+
existed = true;
|
|
2988
|
+
}
|
|
2989
|
+
const missingRefs = INSTRUCTION_REFERENCES5.filter((ref) => !content.includes(ref));
|
|
2990
|
+
if (missingRefs.length === 0) {
|
|
2991
|
+
return false;
|
|
2992
|
+
}
|
|
2993
|
+
const refsBlock = missingRefs.join("\n");
|
|
2994
|
+
if (existed) {
|
|
2995
|
+
const separator = content.endsWith("\n") ? "" : "\n";
|
|
2996
|
+
content = content + separator + refsBlock + "\n";
|
|
2997
|
+
} else {
|
|
2998
|
+
content = refsBlock + "\n";
|
|
2999
|
+
}
|
|
3000
|
+
writeFileSync6(agentsMdPath, content, "utf-8");
|
|
3001
|
+
return true;
|
|
3002
|
+
}
|
|
3003
|
+
};
|
|
3004
|
+
|
|
3005
|
+
// packages/adapters/src/providers/kimi/adapter.ts
|
|
3006
|
+
var execAsync5 = promisify5(exec5);
|
|
3007
|
+
var KimiAdapter = class {
|
|
3008
|
+
id = "kimi";
|
|
3009
|
+
name = "Kimi";
|
|
3010
|
+
version = "1.0.0";
|
|
3011
|
+
capabilities = {
|
|
3012
|
+
supportsHooks: false,
|
|
3013
|
+
supportedHookEvents: [],
|
|
3014
|
+
supportsSpawn: false,
|
|
3015
|
+
supportsInstall: true,
|
|
3016
|
+
supportsMcp: true,
|
|
3017
|
+
supportsInstructionFiles: false,
|
|
3018
|
+
supportsContextMonitor: false,
|
|
3019
|
+
supportsStatusline: false,
|
|
3020
|
+
supportsProviderPaths: false,
|
|
3021
|
+
supportsTransport: false,
|
|
3022
|
+
supportsTaskSync: false
|
|
3023
|
+
};
|
|
3024
|
+
hooks;
|
|
3025
|
+
install;
|
|
3026
|
+
projectDir = null;
|
|
3027
|
+
initialized = false;
|
|
3028
|
+
constructor() {
|
|
3029
|
+
this.hooks = new KimiHookProvider();
|
|
3030
|
+
this.install = new KimiInstallProvider();
|
|
3031
|
+
}
|
|
3032
|
+
/**
|
|
3033
|
+
* Initialize the adapter for a given project directory.
|
|
3034
|
+
*
|
|
3035
|
+
* @param projectDir - Root directory of the project
|
|
3036
|
+
* @task T163
|
|
3037
|
+
*/
|
|
3038
|
+
async initialize(projectDir) {
|
|
3039
|
+
this.projectDir = projectDir;
|
|
3040
|
+
this.initialized = true;
|
|
3041
|
+
}
|
|
3042
|
+
/**
|
|
3043
|
+
* Dispose the adapter and clean up resources.
|
|
3044
|
+
*
|
|
3045
|
+
* Releases tracked state. No hooks to unregister since Kimi
|
|
3046
|
+
* has no native hook system.
|
|
3047
|
+
* @task T163
|
|
3048
|
+
*/
|
|
3049
|
+
async dispose() {
|
|
3050
|
+
this.initialized = false;
|
|
3051
|
+
this.projectDir = null;
|
|
3052
|
+
}
|
|
3053
|
+
/**
|
|
3054
|
+
* Run a health check to verify Kimi is accessible.
|
|
3055
|
+
*
|
|
3056
|
+
* Checks:
|
|
3057
|
+
* 1. Adapter has been initialized
|
|
3058
|
+
* 2. Kimi CLI binary is available in PATH
|
|
3059
|
+
* 3. ~/.kimi/ configuration directory exists
|
|
3060
|
+
*
|
|
3061
|
+
* @returns Health status with details about each check
|
|
3062
|
+
* @task T163
|
|
3063
|
+
*/
|
|
3064
|
+
async healthCheck() {
|
|
3065
|
+
const details = {};
|
|
3066
|
+
if (!this.initialized) {
|
|
3067
|
+
return {
|
|
3068
|
+
healthy: false,
|
|
3069
|
+
provider: this.id,
|
|
3070
|
+
details: { error: "Adapter not initialized" }
|
|
3071
|
+
};
|
|
3072
|
+
}
|
|
3073
|
+
let cliAvailable = false;
|
|
3074
|
+
try {
|
|
3075
|
+
const { stdout } = await execAsync5("which kimi");
|
|
3076
|
+
cliAvailable = stdout.trim().length > 0;
|
|
3077
|
+
details.cliPath = stdout.trim();
|
|
3078
|
+
} catch {
|
|
3079
|
+
details.cliAvailable = false;
|
|
3080
|
+
}
|
|
3081
|
+
const kimiConfigDir = join16(homedir11(), ".kimi");
|
|
3082
|
+
const configExists = existsSync12(kimiConfigDir);
|
|
3083
|
+
details.configDirExists = configExists;
|
|
3084
|
+
const healthy = cliAvailable;
|
|
3085
|
+
details.cliAvailable = cliAvailable;
|
|
3086
|
+
return {
|
|
3087
|
+
healthy,
|
|
3088
|
+
provider: this.id,
|
|
3089
|
+
details
|
|
3090
|
+
};
|
|
3091
|
+
}
|
|
3092
|
+
/**
|
|
3093
|
+
* Check whether the adapter has been initialized.
|
|
3094
|
+
* @task T163
|
|
3095
|
+
*/
|
|
3096
|
+
isInitialized() {
|
|
3097
|
+
return this.initialized;
|
|
3098
|
+
}
|
|
3099
|
+
/**
|
|
3100
|
+
* Get the project directory this adapter was initialized with.
|
|
3101
|
+
* @task T163
|
|
3102
|
+
*/
|
|
3103
|
+
getProjectDir() {
|
|
3104
|
+
return this.projectDir;
|
|
3105
|
+
}
|
|
3106
|
+
};
|
|
3107
|
+
|
|
3108
|
+
// packages/adapters/src/providers/kimi/index.ts
|
|
3109
|
+
function createAdapter5() {
|
|
3110
|
+
return new KimiAdapter();
|
|
3111
|
+
}
|
|
3112
|
+
|
|
3113
|
+
// packages/adapters/src/index.ts
|
|
2000
3114
|
init_opencode();
|
|
2001
3115
|
|
|
2002
3116
|
// packages/adapters/src/registry.js
|
|
2003
|
-
import { readFileSync as
|
|
2004
|
-
import { dirname as dirname2, join as
|
|
3117
|
+
import { readFileSync as readFileSync9 } from "node:fs";
|
|
3118
|
+
import { dirname as dirname2, join as join20, resolve } from "node:path";
|
|
2005
3119
|
import { fileURLToPath } from "node:url";
|
|
2006
3120
|
var PROVIDER_IDS = ["claude-code", "opencode", "cursor"];
|
|
2007
3121
|
function getProviderManifests() {
|
|
@@ -2009,8 +3123,8 @@ function getProviderManifests() {
|
|
|
2009
3123
|
const baseDir = resolve(dirname2(fileURLToPath(import.meta.url)), "providers");
|
|
2010
3124
|
for (const providerId of PROVIDER_IDS) {
|
|
2011
3125
|
try {
|
|
2012
|
-
const manifestPath =
|
|
2013
|
-
const raw =
|
|
3126
|
+
const manifestPath = join20(baseDir, providerId, "manifest.json");
|
|
3127
|
+
const raw = readFileSync9(manifestPath, "utf-8");
|
|
2014
3128
|
manifests.push(JSON.parse(raw));
|
|
2015
3129
|
} catch {
|
|
2016
3130
|
}
|
|
@@ -2041,17 +3155,29 @@ export {
|
|
|
2041
3155
|
ClaudeCodePathProvider,
|
|
2042
3156
|
ClaudeCodeSpawnProvider,
|
|
2043
3157
|
ClaudeCodeTransportProvider,
|
|
3158
|
+
CodexAdapter,
|
|
3159
|
+
CodexHookProvider,
|
|
3160
|
+
CodexInstallProvider,
|
|
2044
3161
|
CursorAdapter,
|
|
2045
3162
|
CursorHookProvider,
|
|
2046
3163
|
CursorInstallProvider,
|
|
3164
|
+
GeminiCliAdapter,
|
|
3165
|
+
GeminiCliHookProvider,
|
|
3166
|
+
GeminiCliInstallProvider,
|
|
3167
|
+
KimiAdapter,
|
|
3168
|
+
KimiHookProvider,
|
|
3169
|
+
KimiInstallProvider,
|
|
2047
3170
|
OpenCodeAdapter,
|
|
2048
3171
|
OpenCodeHookProvider,
|
|
2049
3172
|
OpenCodeInstallProvider,
|
|
2050
3173
|
OpenCodeSpawnProvider,
|
|
2051
3174
|
checkStatuslineIntegration,
|
|
2052
3175
|
createAdapter as createClaudeCodeAdapter,
|
|
2053
|
-
createAdapter2 as
|
|
2054
|
-
createAdapter3 as
|
|
3176
|
+
createAdapter2 as createCodexAdapter,
|
|
3177
|
+
createAdapter3 as createCursorAdapter,
|
|
3178
|
+
createAdapter4 as createGeminiCliAdapter,
|
|
3179
|
+
createAdapter5 as createKimiAdapter,
|
|
3180
|
+
createAdapter6 as createOpenCodeAdapter,
|
|
2055
3181
|
discoverProviders,
|
|
2056
3182
|
getProviderManifests,
|
|
2057
3183
|
getSetupInstructions,
|