Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
95 changes: 95 additions & 0 deletions plugin/scripts/codex.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
#!/usr/bin/env node
//#region src/hooks/codex.ts
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
function authHeaders() {
const h = { "Content-Type": "application/json" };
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
return h;
}
async function readJsonFromStdin() {
let input = "";
for await (const chunk of process.stdin) input += chunk;
try {
return JSON.parse(input);
} catch {
return null;
}
}
async function main() {
const payload = await readJsonFromStdin();
if (!payload) return;
const sid = payload.session_id || `codex-${Date.now().toString(36)}`;
const root = payload.cwd || process.cwd();
const event = payload.hook_event_name || "";
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
try {
if (event === "SessionStart") {
const res = await fetch(`${REST_URL}/agentmemory/session/start`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({
sessionId: sid,
project: root,
cwd: root
}),
signal: AbortSignal.timeout(1500)
});
if (res.ok) {
const data = await res.json();
if (typeof data.context === "string" && data.context) process.stdout.write(data.context);
}
return;
}
if (event === "UserPromptSubmit") {
await fetch(`${REST_URL}/agentmemory/observe`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({
hookType: "prompt_submit",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: { prompt: payload.prompt || "" }
}),
signal: AbortSignal.timeout(3e3)
});
return;
}
if (event === "PostToolUse" || event === "PreToolUse") {
await fetch(`${REST_URL}/agentmemory/observe`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({
hookType: "post_tool_use",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: {
tool_name: payload.tool_name || "tool",
tool_input: payload.tool_input || {},
tool_output: payload.tool_output ?? "tool execution"
}
}),
signal: AbortSignal.timeout(3e3)
});
return;
}
if (event === "Stop") {
await fetch(`${REST_URL}/agentmemory/session/end`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({ sessionId: sid }),
signal: AbortSignal.timeout(1500)
});
return;
}
} catch {}
}
main();

//#endregion
export { };
//# sourceMappingURL=codex.mjs.map
149 changes: 149 additions & 0 deletions plugin/scripts/cursor.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
#!/usr/bin/env node
//#region src/hooks/cursor.ts
const REST_URL = process.env["AGENTMEMORY_URL"] || "http://localhost:3111";
const SECRET = process.env["AGENTMEMORY_SECRET"] || "";
function authHeaders() {
const h = { "Content-Type": "application/json" };
if (SECRET) h["Authorization"] = `Bearer ${SECRET}`;
return h;
}
function inferEvent(payload) {
if (typeof payload.hook_event_name === "string" && payload.hook_event_name) return payload.hook_event_name;
if (typeof payload.prompt === "string" && payload.prompt) return "beforeSubmitPrompt";
if (typeof payload.file_path === "string" && payload.file_path) return "afterFileEdit";
if (typeof payload.command === "string" && payload.command) return "afterShellExecution";
if (payload.mcp_server_name || payload.mcp_tool_name) return "afterMCPExecution";
if (payload.reason) return "stop";
return "sessionStart";
}
async function readJsonFromStdin() {
let input = "";
for await (const chunk of process.stdin) input += chunk;
try {
return JSON.parse(input);
} catch {
return null;
}
}
function sessionId(payload) {
return payload.session_id || payload.conversation_id || `cursor-${Date.now().toString(36)}`;
}
function projectRoot(payload) {
return payload.cwd || payload.workspace_roots?.[0] || process.cwd();
}
async function postObserve(body) {
try {
await fetch(`${REST_URL}/agentmemory/observe`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify(body),
signal: AbortSignal.timeout(3e3)
});
} catch {}
}
async function postSessionStart(body) {
try {
const res = await fetch(`${REST_URL}/agentmemory/session/start`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify(body),
signal: AbortSignal.timeout(1500)
});
if (res.ok) {
const data = await res.json();
if (typeof data.context === "string" && data.context) process.stdout.write(data.context);
}
} catch {}
}
async function main() {
const payload = await readJsonFromStdin();
if (!payload) return;
const sid = sessionId(payload);
const root = projectRoot(payload);
const event = inferEvent(payload);
const timestamp = (/* @__PURE__ */ new Date()).toISOString();
switch (event) {
case "sessionStart":
await postSessionStart({
sessionId: sid,
project: root,
cwd: root
});
return;
case "beforeSubmitPrompt":
await postObserve({
hookType: "prompt_submit",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: { prompt: payload.prompt || "" }
});
return;
case "afterFileEdit":
await postObserve({
hookType: "post_tool_use",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: {
tool_name: "CursorEdit",
tool_input: {
file_path: payload.file_path || "",
old_content: payload.old_content,
new_content: payload.new_content
},
tool_output: `Edited ${payload.file_path || "file"}`
}
});
return;
case "afterShellExecution":
case "beforeShellExecution":
await postObserve({
hookType: "post_tool_use",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: {
tool_name: "Bash",
tool_input: { command: payload.command || "" },
tool_output: payload.reason || "shell execution"
}
});
return;
case "afterMCPExecution":
case "beforeMCPExecution":
await postObserve({
hookType: "post_tool_use",
sessionId: sid,
project: root,
cwd: root,
timestamp,
data: {
tool_name: payload.mcp_tool_name || payload.mcp_server_name || "MCP",
tool_input: payload.mcp_tool_input || {},
tool_output: payload.mcp_tool_output ?? "MCP execution"
}
});
return;
case "stop":
case "sessionEnd":
try {
await fetch(`${REST_URL}/agentmemory/session/end`, {
method: "POST",
headers: authHeaders(),
body: JSON.stringify({ sessionId: sid }),
signal: AbortSignal.timeout(1500)
});
} catch {}
return;
default: return;
}
}
main();

//#endregion
export { };
//# sourceMappingURL=cursor.mjs.map
29 changes: 29 additions & 0 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1120,6 +1120,34 @@ async function runMcp(): Promise<void> {
await import("./mcp/standalone.js");
}

async function runInstall(): Promise<void> {
const target = args[1];
const global = args.includes("--global");
const rootIdx = args.indexOf("--project-root");
const projectRoot = rootIdx !== -1 && args[rootIdx + 1] ? args[rootIdx + 1] : undefined;

if (!target) {
p.log.error(
"Usage: agentmemory install <opencode|cursor|codex|roo|kilo|pi|openclaw|hermes> [--global] [--project-root <path>]",
);
process.exit(1);
}

const { installTarget } = await import("./installers.js");
const result = installTarget(target as never, { global, projectRoot });
p.note(
[
`Target: ${result.target}`,
`Files:`,
...result.filesWritten.map((f) => ` - ${f}`),
"",
`Notes:`,
...result.notes.map((n) => ` - ${n}`),
].join("\n"),
"agentmemory install",
);
}

async function runImportJsonl(): Promise<void> {
// Long-form flags that take a value. Their value tokens must be
// consumed alongside the flag so they don't leak into positional
Expand Down Expand Up @@ -1301,6 +1329,7 @@ const commands: Record<string, () => Promise<void>> = {
doctor: runDoctor,
demo: runDemo,
upgrade: runUpgrade,
install: runInstall,
mcp: runMcp,
"import-jsonl": runImportJsonl,
};
Expand Down
Loading