@agentmemory/agentmemory 0.9.28 → 0.9.29

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 (125) hide show
  1. package/.env.example +15 -6
  2. package/AGENTS.md +6 -6
  3. package/README.md +220 -152
  4. package/dist/.env.example +15 -6
  5. package/dist/cli.d.mts.map +1 -1
  6. package/dist/cli.mjs +233 -35
  7. package/dist/cli.mjs.map +1 -1
  8. package/dist/{connect-JnsJXc68.mjs → connect-Qaeq15TQ.mjs} +599 -41
  9. package/dist/connect-Qaeq15TQ.mjs.map +1 -0
  10. package/dist/docker-compose.yml +2 -2
  11. package/dist/hooks/antigravity-bridge.d.mts +8 -0
  12. package/dist/hooks/antigravity-bridge.d.mts.map +1 -0
  13. package/dist/hooks/antigravity-bridge.mjs +114 -0
  14. package/dist/hooks/antigravity-bridge.mjs.map +1 -0
  15. package/dist/hooks/notification.mjs +19 -4
  16. package/dist/hooks/notification.mjs.map +1 -1
  17. package/dist/hooks/post-commit.mjs +13 -1
  18. package/dist/hooks/post-commit.mjs.map +1 -1
  19. package/dist/hooks/post-tool-failure.mjs +14 -3
  20. package/dist/hooks/post-tool-failure.mjs.map +1 -1
  21. package/dist/hooks/post-tool-use.mjs +14 -3
  22. package/dist/hooks/post-tool-use.mjs.map +1 -1
  23. package/dist/hooks/pre-compact.mjs +12 -2
  24. package/dist/hooks/pre-compact.mjs.map +1 -1
  25. package/dist/hooks/pre-tool-use.mjs +1 -1
  26. package/dist/hooks/pre-tool-use.mjs.map +1 -1
  27. package/dist/hooks/prompt-submit.mjs +14 -3
  28. package/dist/hooks/prompt-submit.mjs.map +1 -1
  29. package/dist/hooks/session-end.mjs +82 -18
  30. package/dist/hooks/session-end.mjs.map +1 -1
  31. package/dist/hooks/session-start.mjs +22 -4
  32. package/dist/hooks/session-start.mjs.map +1 -1
  33. package/dist/hooks/stop.mjs +1 -7
  34. package/dist/hooks/stop.mjs.map +1 -1
  35. package/dist/hooks/subagent-start.mjs +14 -3
  36. package/dist/hooks/subagent-start.mjs.map +1 -1
  37. package/dist/hooks/subagent-stop.mjs +14 -3
  38. package/dist/hooks/subagent-stop.mjs.map +1 -1
  39. package/dist/hooks/task-completed.mjs +14 -3
  40. package/dist/hooks/task-completed.mjs.map +1 -1
  41. package/dist/{image-refs-C2YU7BIm.mjs → image-refs-CJgZmb9X.mjs} +2 -2
  42. package/dist/{image-refs-C2YU7BIm.mjs.map → image-refs-CJgZmb9X.mjs.map} +1 -1
  43. package/dist/index.mjs +1557 -755
  44. package/dist/index.mjs.map +1 -1
  45. package/dist/schema-BFZhNTF7.mjs +197 -0
  46. package/dist/schema-BFZhNTF7.mjs.map +1 -0
  47. package/dist/{src-CzgoepGU.mjs → src-G7yt8gGm.mjs} +1531 -883
  48. package/dist/src-G7yt8gGm.mjs.map +1 -0
  49. package/dist/{standalone-YsZFFQBM.mjs → standalone-BqNJDJ04.mjs} +26 -15
  50. package/dist/standalone-BqNJDJ04.mjs.map +1 -0
  51. package/dist/standalone.d.mts.map +1 -1
  52. package/dist/standalone.mjs +50 -14
  53. package/dist/standalone.mjs.map +1 -1
  54. package/dist/{tools-registry-D3NEoe5m.mjs → tools-registry-BV_2bu7z.mjs} +293 -3
  55. package/dist/tools-registry-BV_2bu7z.mjs.map +1 -0
  56. package/dist/viewer/favicon.svg +35 -1
  57. package/dist/viewer/index.html +473 -83
  58. package/docker-compose.yml +2 -2
  59. package/integrations/pi/README.md +77 -0
  60. package/integrations/pi/index.ts +403 -0
  61. package/integrations/pi/package.json +32 -0
  62. package/integrations/pi/security.ts +35 -0
  63. package/package.json +3 -4
  64. package/plugin/.claude-plugin/plugin.json +2 -2
  65. package/plugin/.codex-plugin/plugin.json +2 -2
  66. package/plugin/.devin-plugin/plugin.json +32 -0
  67. package/plugin/cursor/hooks.json +41 -0
  68. package/plugin/cursor/mcp.json +12 -0
  69. package/plugin/hooks/hooks.antigravity.json +43 -0
  70. package/plugin/hooks/hooks.devin.json +67 -0
  71. package/plugin/hooks/hooks.droid.json +55 -0
  72. package/plugin/opencode/README.md +1 -1
  73. package/plugin/opencode/agentmemory-capture.ts +73 -13
  74. package/plugin/opencode/plugin.json +1 -1
  75. package/plugin/plugin.json +2 -2
  76. package/plugin/scripts/antigravity-bridge.d.mts +8 -0
  77. package/plugin/scripts/antigravity-bridge.d.mts.map +1 -0
  78. package/plugin/scripts/antigravity-bridge.mjs +114 -0
  79. package/plugin/scripts/antigravity-bridge.mjs.map +1 -0
  80. package/plugin/scripts/notification.mjs +19 -4
  81. package/plugin/scripts/notification.mjs.map +1 -1
  82. package/plugin/scripts/post-commit.mjs +13 -1
  83. package/plugin/scripts/post-commit.mjs.map +1 -1
  84. package/plugin/scripts/post-tool-failure.mjs +14 -3
  85. package/plugin/scripts/post-tool-failure.mjs.map +1 -1
  86. package/plugin/scripts/post-tool-use.mjs +14 -3
  87. package/plugin/scripts/post-tool-use.mjs.map +1 -1
  88. package/plugin/scripts/pre-compact.mjs +12 -2
  89. package/plugin/scripts/pre-compact.mjs.map +1 -1
  90. package/plugin/scripts/pre-tool-use.mjs +1 -1
  91. package/plugin/scripts/pre-tool-use.mjs.map +1 -1
  92. package/plugin/scripts/prompt-submit.mjs +14 -3
  93. package/plugin/scripts/prompt-submit.mjs.map +1 -1
  94. package/plugin/scripts/session-end.mjs +82 -18
  95. package/plugin/scripts/session-end.mjs.map +1 -1
  96. package/plugin/scripts/session-start.mjs +22 -4
  97. package/plugin/scripts/session-start.mjs.map +1 -1
  98. package/plugin/scripts/stop.mjs +1 -7
  99. package/plugin/scripts/stop.mjs.map +1 -1
  100. package/plugin/scripts/subagent-start.mjs +14 -3
  101. package/plugin/scripts/subagent-start.mjs.map +1 -1
  102. package/plugin/scripts/subagent-stop.mjs +14 -3
  103. package/plugin/scripts/subagent-stop.mjs.map +1 -1
  104. package/plugin/scripts/task-completed.mjs +14 -3
  105. package/plugin/scripts/task-completed.mjs.map +1 -1
  106. package/plugin/skills/agentmemory-agents/REFERENCE.md +6 -3
  107. package/plugin/skills/agentmemory-config/REFERENCE.md +4 -1
  108. package/plugin/skills/agentmemory-mcp-tools/REFERENCE.md +3 -2
  109. package/plugin/skills/agentmemory-rest-api/REFERENCE.md +14 -1
  110. package/plugin/skills/forget/SKILL.md +4 -1
  111. package/plugin/skills/lesson/SKILL.md +64 -0
  112. package/plugin/skills/memory-discipline/SKILL.md +57 -0
  113. package/plugin/skills/recall/SKILL.md +4 -1
  114. package/plugin/skills/remember/SKILL.md +6 -1
  115. package/dist/config-ChC9EtgV.mjs +0 -259
  116. package/dist/config-ChC9EtgV.mjs.map +0 -1
  117. package/dist/connect-JnsJXc68.mjs.map +0 -1
  118. package/dist/schema-CNiCWzV9.mjs +0 -78
  119. package/dist/schema-CNiCWzV9.mjs.map +0 -1
  120. package/dist/src-CzgoepGU.mjs.map +0 -1
  121. package/dist/standalone-YsZFFQBM.mjs.map +0 -1
  122. package/dist/tools-registry-D3NEoe5m.mjs.map +0 -1
  123. package/plugin/scripts/diagnostics.d.mts +0 -17
  124. package/plugin/scripts/diagnostics.d.mts.map +0 -1
  125. package/plugin/scripts/diagnostics.mjs.map +0 -1
@@ -0,0 +1,12 @@
1
+ {
2
+ "mcpServers": {
3
+ "agentmemory": {
4
+ "command": "npx",
5
+ "args": ["-y", "@agentmemory/mcp"],
6
+ "env": {
7
+ "AGENTMEMORY_URL": "${AGENTMEMORY_URL}",
8
+ "AGENTMEMORY_SECRET": "${AGENTMEMORY_SECRET}"
9
+ }
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,43 @@
1
+ {
2
+ "agentmemory": {
3
+ "enabled": true,
4
+ "PreInvocation": [
5
+ {
6
+ "type": "command",
7
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PreInvocation",
8
+ "timeout": 10
9
+ }
10
+ ],
11
+ "PreToolUse": [
12
+ {
13
+ "matcher": "view_file|view_code_item|read_file|edit_file|replace_file_content|write_to_file|create_file|grep_search|codebase_search|find_by_name|list_dir",
14
+ "hooks": [
15
+ {
16
+ "type": "command",
17
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PreToolUse",
18
+ "timeout": 10
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PostToolUse": [
24
+ {
25
+ "matcher": "",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs PostToolUse",
30
+ "timeout": 10
31
+ }
32
+ ]
33
+ }
34
+ ],
35
+ "Stop": [
36
+ {
37
+ "type": "command",
38
+ "command": "node ${CLAUDE_PLUGIN_ROOT}/scripts/antigravity-bridge.mjs Stop",
39
+ "timeout": 10
40
+ }
41
+ ]
42
+ }
43
+ }
@@ -0,0 +1,67 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-start.mjs\""
9
+ }
10
+ ]
11
+ }
12
+ ],
13
+ "UserPromptSubmit": [
14
+ {
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/prompt-submit.mjs\""
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PreToolUse": [
24
+ {
25
+ "matcher": "^(exec|edit|write|read|apply_patch|grep|glob)$",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-tool-use.mjs\""
30
+ }
31
+ ]
32
+ }
33
+ ],
34
+ "PostToolUse": [
35
+ {
36
+ "matcher": "",
37
+ "hooks": [
38
+ {
39
+ "type": "command",
40
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\""
41
+ }
42
+ ]
43
+ }
44
+ ],
45
+ "Stop": [
46
+ {
47
+ "matcher": "",
48
+ "hooks": [
49
+ {
50
+ "type": "command",
51
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/stop.mjs\""
52
+ }
53
+ ]
54
+ }
55
+ ],
56
+ "SessionEnd": [
57
+ {
58
+ "hooks": [
59
+ {
60
+ "type": "command",
61
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-end.mjs\""
62
+ }
63
+ ]
64
+ }
65
+ ]
66
+ }
67
+ }
@@ -0,0 +1,55 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-start.mjs\""
9
+ }
10
+ ]
11
+ }
12
+ ],
13
+ "UserPromptSubmit": [
14
+ {
15
+ "hooks": [
16
+ {
17
+ "type": "command",
18
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/prompt-submit.mjs\""
19
+ }
20
+ ]
21
+ }
22
+ ],
23
+ "PreToolUse": [
24
+ {
25
+ "matcher": "Edit|Create|Read|Glob|Grep",
26
+ "hooks": [
27
+ {
28
+ "type": "command",
29
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/pre-tool-use.mjs\""
30
+ }
31
+ ]
32
+ }
33
+ ],
34
+ "PostToolUse": [
35
+ {
36
+ "hooks": [
37
+ {
38
+ "type": "command",
39
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/post-tool-use.mjs\""
40
+ }
41
+ ]
42
+ }
43
+ ],
44
+ "SessionEnd": [
45
+ {
46
+ "hooks": [
47
+ {
48
+ "type": "command",
49
+ "command": "node \"${CLAUDE_PLUGIN_ROOT}/scripts/session-end.mjs\""
50
+ }
51
+ ]
52
+ }
53
+ ]
54
+ }
55
+ }
@@ -9,7 +9,7 @@
9
9
  </p>
10
10
 
11
11
  <p align="center">
12
- <img src="https://img.shields.io/badge/MCP-53_tools-1f6feb?style=flat-square" alt="53 MCP tools" />
12
+ <img src="https://img.shields.io/badge/MCP-54_tools-1f6feb?style=flat-square" alt="54 MCP tools" />
13
13
  <img src="https://img.shields.io/badge/Plugin-22_hooks-1f6feb?style=flat-square" alt="22 hooks" />
14
14
  <img src="https://img.shields.io/badge/Commands-2_slash-1f6feb?style=flat-square" alt="2 slash commands" />
15
15
  <img src="https://img.shields.io/badge/R@5-95.2%25-00875f?style=flat-square" alt="95.2% R@5" />
@@ -1,7 +1,12 @@
1
1
  import type { Plugin } from "@opencode-ai/plugin";
2
+ import { execFileSync } from "node:child_process";
3
+ import { basename } from "node:path";
2
4
 
3
5
  const API = process.env.AGENTMEMORY_URL || "http://localhost:3111";
4
- const FILE_TOOLS = new Set(["Read", "Write", "Edit", "Glob", "Grep"]);
6
+ // OpenCode reports tool names in lowercase ("read", "edit", ...); matching is
7
+ // case-insensitive at the call site so a future casing change cannot silently
8
+ // kill file enrichment again.
9
+ const FILE_TOOLS = new Set(["read", "write", "edit", "glob", "grep"]);
5
10
  const FILE_KEYS = ["filePath", "file_path", "path", "file", "pattern"];
6
11
  const MAX_STASHED_FILES = 20;
7
12
 
@@ -47,11 +52,12 @@ async function observe(
47
52
  hookType: string,
48
53
  data: Record<string, unknown>,
49
54
  ): Promise<void> {
55
+ const proj = projectFor(sessionId);
50
56
  await post("/observe", {
51
57
  hookType,
52
58
  sessionId,
53
- project: projectPath,
54
- cwd: projectPath,
59
+ project: proj.name,
60
+ cwd: proj.cwd,
55
61
  timestamp: new Date().toISOString(),
56
62
  data,
57
63
  });
@@ -59,7 +65,46 @@ async function observe(
59
65
 
60
66
  let activeSessionId: string | null = null;
61
67
  let pendingConfig: Record<string, unknown> | null = null;
62
- let projectPath: string | null = null;
68
+ // Default scope resolved at plugin init (same resolution order as the hooks'
69
+ // resolveProject: env override, git toplevel basename, cwd basename). In a
70
+ // long-lived OpenCode process serving multiple directories these defaults are
71
+ // only a fallback — attribution is per-session via sessionProjects, resolved
72
+ // from each session's own directory at session.created. Module-level-only
73
+ // state recorded home-directory sessions under whatever repo loaded first.
74
+ let defaultProjectName: string | null = null;
75
+ let defaultProjectCwd: string | null = null;
76
+ const sessionProjects = new Map<string, { name: string; cwd: string }>();
77
+
78
+ function projectFor(sessionId: string): { name: string | null; cwd: string | null } {
79
+ const p = sessionProjects.get(sessionId);
80
+ return p ?? { name: defaultProjectName, cwd: defaultProjectCwd };
81
+ }
82
+
83
+ const projectNameCache = new Map<string, string>();
84
+
85
+ function resolveProjectName(dir: string): string {
86
+ const explicit = process.env.AGENTMEMORY_PROJECT_NAME?.trim();
87
+ if (explicit) return explicit;
88
+ const cached = projectNameCache.get(dir);
89
+ if (cached !== undefined) return cached;
90
+ try {
91
+ const top = execFileSync("git", ["rev-parse", "--show-toplevel"], {
92
+ cwd: dir,
93
+ stdio: ["ignore", "pipe", "ignore"],
94
+ encoding: "utf8",
95
+ }).trim();
96
+ if (top) {
97
+ const name = basename(top);
98
+ projectNameCache.set(dir, name);
99
+ return name;
100
+ }
101
+ } catch {
102
+ // not a git repo, fall through
103
+ }
104
+ const fallback = basename(dir) || dir;
105
+ projectNameCache.set(dir, fallback);
106
+ return fallback;
107
+ }
63
108
  const stashedFiles = new Map<string, Set<string>>();
64
109
  const seenSubtaskIds = new Map<string, Set<string>>();
65
110
  const seenToolCallIds = new Map<string, Set<string>>();
@@ -93,6 +138,7 @@ function pruneSessionMaps(sid: string): void {
93
138
  stashedFiles.delete(sid);
94
139
  seenSubtaskIds.delete(sid);
95
140
  seenToolCallIds.delete(sid);
141
+ sessionProjects.delete(sid);
96
142
  }
97
143
 
98
144
  function safeSlice(v: unknown, max: number): string {
@@ -168,7 +214,8 @@ function extractErrorMessage(err: unknown): string {
168
214
  }
169
215
 
170
216
  export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
171
- projectPath = ctx.worktree || ctx.project?.id || process.cwd();
217
+ defaultProjectCwd = ctx.worktree || ctx.project?.id || process.cwd();
218
+ defaultProjectName = resolveProjectName(defaultProjectCwd);
172
219
 
173
220
  return {
174
221
  event: async ({ event }) => {
@@ -188,13 +235,28 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
188
235
  // and another `session.created` event during the await could
189
236
  // rebind it, causing context to be cached against the wrong key.
190
237
  const sessionId = activeSessionId;
238
+ // Attribute this session to its own directory when the event
239
+ // carries one; a multi-directory OpenCode process otherwise
240
+ // records every session under whichever repo loaded the plugin.
241
+ const sessionDir =
242
+ typeof info?.directory === "string" && info.directory
243
+ ? info.directory
244
+ : defaultProjectCwd;
245
+ let proj: { name: string | null; cwd: string | null };
246
+ if (sessionDir) {
247
+ const entry = { cwd: sessionDir, name: resolveProjectName(sessionDir) };
248
+ sessionProjects.set(sessionId, entry);
249
+ proj = entry;
250
+ } else {
251
+ proj = projectFor(sessionId);
252
+ }
191
253
  const startResult = await postJson("/session/start", {
192
254
  sessionId,
193
255
  title: info?.title ?? null,
194
256
  parentID: info?.parentID ?? null,
195
257
  version: info?.version ?? null,
196
- project: projectPath,
197
- cwd: projectPath,
258
+ project: proj.name,
259
+ cwd: proj.cwd,
198
260
  });
199
261
  // cache the context returned at session/start so the
200
262
  // chat.system.transform hook injects it without a second fetch.
@@ -272,10 +334,8 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
272
334
  post("/crystals/auto", { olderThanDays: 7 }, 30000);
273
335
  post("/consolidate-pipeline", { tier: "all", force: true }, 30000);
274
336
  if (sid === activeSessionId) activeSessionId = null;
275
- stashedFiles.delete(sid);
337
+ pruneSessionMaps(sid);
276
338
  startContextCache.delete(sid);
277
- seenSubtaskIds.delete(sid);
278
- seenToolCallIds.delete(sid);
279
339
  contextInjectedSessions.delete(sid);
280
340
  }
281
341
 
@@ -581,7 +641,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
581
641
 
582
642
  // ── tool.execute.before ──
583
643
  "tool.execute.before": async (input, output) => {
584
- if (!FILE_TOOLS.has(input.tool)) return;
644
+ if (!FILE_TOOLS.has(String(input.tool ?? "").toLowerCase())) return;
585
645
  const sid = input.sessionID || activeSessionId;
586
646
  if (!sid) return;
587
647
  const args = output.args as Record<string, unknown> | undefined;
@@ -612,7 +672,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
612
672
  if (typeof ctx !== "string" || ctx.length === 0) {
613
673
  const result = await postJson("/context", {
614
674
  sessionId: sid,
615
- project: projectPath,
675
+ project: projectFor(sid).name,
616
676
  });
617
677
  ctx = (result as any)?.context;
618
678
  } else {
@@ -650,7 +710,7 @@ export const AgentmemoryCapturePlugin: Plugin = async (ctx) => {
650
710
 
651
711
  const result = await postJson("/context", {
652
712
  sessionId: sid,
653
- project: projectPath,
713
+ project: projectFor(sid).name,
654
714
  });
655
715
  const ctx = (result as any)?.context;
656
716
  if (typeof ctx === "string" && ctx.length > 0) {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentmemory-capture",
3
- "version": "0.9.20",
3
+ "version": "0.9.29",
4
4
  "description": "OpenCode plugin for agentmemory — full Claude Code hook parity: session lifecycle (create/idle/status/compacted/update/diff/delete/error), messages & prompts (chat.message, message.updated user+assistant, message.removed), tool lifecycle (ToolPart states with timing), part tracking (subtask, step-finish, reasoning, file, patch, compaction, agent, retry), file enrichment pipeline, permissions, task tracking (w/ priority), commands, config & model tracking. 22 hooks, 2 slash commands.",
5
5
  "author": {
6
6
  "name": "Rohit Ghumare",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentmemory",
3
- "version": "0.9.28",
4
- "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 53 MCP tools, 15 skills, real-time viewer.",
3
+ "version": "0.9.29",
4
+ "description": "Persistent memory for AI coding agents -- captures tool usage, compresses via LLM, injects context into future sessions. 12 hooks, 54 MCP tools, 17 skills, real-time viewer.",
5
5
  "author": {
6
6
  "name": "Rohit Ghumare",
7
7
  "url": "https://github.com/rohitg00"
@@ -0,0 +1,8 @@
1
+ //#region src/hooks/antigravity-bridge.d.ts
2
+ type Json = Record<string, unknown>;
3
+ declare function normalizePayload(event: string, raw: Json): Json;
4
+ declare function targetsFor(event: string, raw: Json): string[];
5
+ declare function responseFor(event: string): string;
6
+ //#endregion
7
+ export { normalizePayload, responseFor, targetsFor };
8
+ //# sourceMappingURL=antigravity-bridge.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"antigravity-bridge.d.mts","names":[],"sources":["../../src/hooks/antigravity-bridge.ts"],"mappings":";KAkDK,IAAA,GAAO,MAAA;AAAA,iBA2BI,gBAAA,CAAiB,KAAA,UAAe,GAAA,EAAK,IAAA,GAAO,IAAA;AAAA,iBAsD5C,UAAA,CAAW,KAAA,UAAe,GAAA,EAAK,IAAA;AAAA,iBAwB/B,WAAA,CAAY,KAAA"}
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env node
2
+ import { spawnSync } from "node:child_process";
3
+ import { dirname, join, resolve } from "node:path";
4
+ import { fileURLToPath } from "node:url";
5
+ //#region src/hooks/antigravity-bridge.ts
6
+ const SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));
7
+ const TOOL_NAME_MAP = {
8
+ view_file: "read",
9
+ view_line_range: "read",
10
+ view_code_item: "read",
11
+ read_file: "read",
12
+ read_url_content: "read",
13
+ edit_file: "edit",
14
+ replace_file_content: "edit",
15
+ propose_code: "edit",
16
+ write_to_file: "write",
17
+ create_file: "write",
18
+ grep_search: "grep",
19
+ codebase_search: "grep",
20
+ find_by_name: "glob",
21
+ list_dir: "glob"
22
+ };
23
+ const ARG_KEY_MAP = {
24
+ AbsolutePath: "file_path",
25
+ TargetFile: "file_path",
26
+ DirectoryPath: "path",
27
+ SearchDirectory: "path",
28
+ Pattern: "pattern",
29
+ Query: "pattern",
30
+ CommandLine: "command"
31
+ };
32
+ function asObject(value) {
33
+ return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
34
+ }
35
+ function firstString(...values) {
36
+ for (const v of values) if (typeof v === "string" && v.length > 0) return v;
37
+ }
38
+ function normalizeToolArgs(args) {
39
+ if (!args) return {};
40
+ const out = { ...args };
41
+ for (const [from, to] of Object.entries(ARG_KEY_MAP)) if (out[to] === void 0 && args[from] !== void 0) out[to] = args[from];
42
+ return out;
43
+ }
44
+ function normalizePayload(event, raw) {
45
+ const toolCall = asObject(raw["toolCall"]);
46
+ const workspacePaths = Array.isArray(raw["workspacePaths"]) ? raw["workspacePaths"] : [];
47
+ const sessionId = firstString(raw["conversationId"], raw["session_id"], raw["sessionId"]) ?? "unknown";
48
+ const cwd = firstString(raw["cwd"], workspacePaths[0]) ?? process.cwd();
49
+ const out = {
50
+ ...raw,
51
+ session_id: sessionId,
52
+ cwd,
53
+ hook_event_name: event
54
+ };
55
+ const transcriptPath = firstString(raw["transcript_path"], raw["transcriptPath"]);
56
+ if (transcriptPath) out["transcript_path"] = transcriptPath;
57
+ if (toolCall) {
58
+ const args = normalizeToolArgs(asObject(toolCall["args"]) ?? asObject(toolCall["toolArgs"]));
59
+ const rawName = firstString(toolCall["name"], toolCall["toolName"], args["ToolName"], args["toolName"]);
60
+ if (rawName) {
61
+ out["tool_name"] = TOOL_NAME_MAP[rawName] ?? rawName;
62
+ out["native_tool_name"] = rawName;
63
+ }
64
+ out["tool_input"] = args;
65
+ const result = toolCall["result"] ?? raw["toolResult"] ?? raw["result"];
66
+ if (result !== void 0) out["tool_result"] = result;
67
+ }
68
+ return out;
69
+ }
70
+ function targetsFor(event, raw) {
71
+ switch (event) {
72
+ case "PreInvocation": {
73
+ const n = raw["invocationNum"];
74
+ return typeof n !== "number" || n <= 1 ? ["session-start.mjs", "prompt-submit.mjs"] : ["prompt-submit.mjs"];
75
+ }
76
+ case "PreToolUse": return ["pre-tool-use.mjs"];
77
+ case "PostToolUse": return ["post-tool-use.mjs"];
78
+ case "Stop": return ["stop.mjs", "session-end.mjs"];
79
+ default: return [];
80
+ }
81
+ }
82
+ function responseFor(event) {
83
+ return event === "PreToolUse" ? "{\"decision\":\"allow\"}" : "{}";
84
+ }
85
+ async function main() {
86
+ const event = process.argv[2];
87
+ if (!event) return;
88
+ let input = "";
89
+ for await (const chunk of process.stdin) input += chunk;
90
+ let raw;
91
+ try {
92
+ raw = JSON.parse(input);
93
+ } catch {
94
+ return;
95
+ }
96
+ if (!raw || typeof raw !== "object") return;
97
+ const payload = JSON.stringify(normalizePayload(event, raw));
98
+ for (const script of targetsFor(event, raw)) spawnSync(process.execPath, [join(SCRIPTS_DIR, script)], {
99
+ input: payload,
100
+ stdio: [
101
+ "pipe",
102
+ "ignore",
103
+ "ignore"
104
+ ]
105
+ });
106
+ }
107
+ if (process.argv[1] !== void 0 && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) main().catch(() => {}).finally(() => {
108
+ process.stdout.write(responseFor(process.argv[2] ?? ""));
109
+ process.exit(0);
110
+ });
111
+ //#endregion
112
+ export { normalizePayload, responseFor, targetsFor };
113
+
114
+ //# sourceMappingURL=antigravity-bridge.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"antigravity-bridge.mjs","names":[],"sources":["../../src/hooks/antigravity-bridge.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { spawnSync } from \"node:child_process\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\n\n// Antigravity CLI (`agy`) bridge — sits in front of the canonical hooks\n// because three parts of agy's contract make them unusable as direct\n// `command` targets: only five events exist (no SessionStart/SessionEnd/\n// UserPromptSubmit, so the lifecycle is synthesized from PreInvocation and\n// Stop); the payload is camelCase and nests tool calls under `toolCall`; and\n// stdout must be a JSON object, which `pre-tool-use.mjs` breaks when\n// AGENTMEMORY_INJECT_CONTEXT=true makes it write raw context text.\n//\n// Invoked as: node antigravity-bridge.mjs <PreInvocation|PreToolUse|PostToolUse|Stop>\n// Sources: antigravity.google/docs/hooks, antigravity.google/docs/cli/using\n\nconst SCRIPTS_DIR = dirname(fileURLToPath(import.meta.url));\n\n// Antigravity inherits Cascade-style tool names. Map them onto the tool\n// vocabulary pre-tool-use.ts / post-tool-use.ts already understand so the\n// existing file-activity heuristics keep working unchanged.\nconst TOOL_NAME_MAP: Record<string, string> = {\n view_file: \"read\",\n view_line_range: \"read\",\n view_code_item: \"read\",\n read_file: \"read\",\n read_url_content: \"read\",\n edit_file: \"edit\",\n replace_file_content: \"edit\",\n propose_code: \"edit\",\n write_to_file: \"write\",\n create_file: \"write\",\n grep_search: \"grep\",\n codebase_search: \"grep\",\n find_by_name: \"glob\",\n list_dir: \"glob\",\n};\n\n// Antigravity tool args are PascalCase; the canonical hooks look for\n// snake_case keys. Only the keys those hooks actually read are mapped.\nconst ARG_KEY_MAP: Record<string, string> = {\n AbsolutePath: \"file_path\",\n TargetFile: \"file_path\",\n DirectoryPath: \"path\",\n SearchDirectory: \"path\",\n Pattern: \"pattern\",\n Query: \"pattern\",\n CommandLine: \"command\",\n};\n\ntype Json = Record<string, unknown>;\n\nfunction asObject(value: unknown): Json | undefined {\n return typeof value === \"object\" && value !== null && !Array.isArray(value)\n ? (value as Json)\n : undefined;\n}\n\nfunction firstString(...values: unknown[]): string | undefined {\n for (const v of values) {\n if (typeof v === \"string\" && v.length > 0) return v;\n }\n return undefined;\n}\n\nfunction normalizeToolArgs(args: Json | undefined): Json {\n if (!args) return {};\n const out: Json = { ...args };\n for (const [from, to] of Object.entries(ARG_KEY_MAP)) {\n if (out[to] === undefined && args[from] !== undefined) out[to] = args[from];\n }\n return out;\n}\n\n// Translate one Antigravity hook payload into the flat, snake_case shape the\n// bundled hooks consume. Unknown fields pass through so future Antigravity\n// additions stay visible to the capture pipeline.\nexport function normalizePayload(event: string, raw: Json): Json {\n const toolCall = asObject(raw[\"toolCall\"]);\n const workspacePaths = Array.isArray(raw[\"workspacePaths\"])\n ? (raw[\"workspacePaths\"] as unknown[])\n : [];\n\n const sessionId =\n firstString(raw[\"conversationId\"], raw[\"session_id\"], raw[\"sessionId\"]) ??\n \"unknown\";\n const cwd =\n firstString(raw[\"cwd\"], workspacePaths[0]) ?? process.cwd();\n\n const out: Json = {\n ...raw,\n session_id: sessionId,\n cwd,\n hook_event_name: event,\n };\n\n const transcriptPath = firstString(\n raw[\"transcript_path\"],\n raw[\"transcriptPath\"],\n );\n if (transcriptPath) out[\"transcript_path\"] = transcriptPath;\n\n if (toolCall) {\n const args = normalizeToolArgs(\n asObject(toolCall[\"args\"]) ?? asObject(toolCall[\"toolArgs\"]),\n );\n const rawName = firstString(\n toolCall[\"name\"],\n toolCall[\"toolName\"],\n args[\"ToolName\"],\n args[\"toolName\"],\n );\n if (rawName) {\n out[\"tool_name\"] = TOOL_NAME_MAP[rawName] ?? rawName;\n // Keep the host-native name so captured observations stay traceable\n // back to the tool Antigravity actually ran.\n out[\"native_tool_name\"] = rawName;\n }\n out[\"tool_input\"] = args;\n const result = toolCall[\"result\"] ?? raw[\"toolResult\"] ?? raw[\"result\"];\n if (result !== undefined) out[\"tool_result\"] = result;\n }\n\n return out;\n}\n\n// Map an Antigravity event to the bundled scripts it should drive.\n// PreInvocation stands in for both SessionStart and UserPromptSubmit: the\n// first invocation of a conversation opens the session, every later one is a\n// fresh user turn. PostInvocation is deliberately unmapped — PostToolUse\n// already captures the work, and firing again would double-record it.\nexport function targetsFor(event: string, raw: Json): string[] {\n switch (event) {\n case \"PreInvocation\": {\n const n = raw[\"invocationNum\"];\n const isFirst = typeof n !== \"number\" || n <= 1;\n return isFirst\n ? [\"session-start.mjs\", \"prompt-submit.mjs\"]\n : [\"prompt-submit.mjs\"];\n }\n case \"PreToolUse\":\n return [\"pre-tool-use.mjs\"];\n case \"PostToolUse\":\n return [\"post-tool-use.mjs\"];\n case \"Stop\":\n return [\"stop.mjs\", \"session-end.mjs\"];\n default:\n return [];\n }\n}\n\n// The stdout contract, per event. agy treats a PreToolUse response without\n// `decision` as a denial, so a bare `{}` there makes it refuse every matched\n// tool call (verified on 1.0.15). No other event carries a permission\n// decision, so they stay on `{}` — sending one would override user settings.\nexport function responseFor(event: string): string {\n return event === \"PreToolUse\" ? '{\"decision\":\"allow\"}' : \"{}\";\n}\n\nasync function main() {\n const event = process.argv[2];\n if (!event) return;\n\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let raw: Json;\n try {\n raw = JSON.parse(input) as Json;\n } catch {\n return;\n }\n if (!raw || typeof raw !== \"object\") return;\n\n const payload = JSON.stringify(normalizePayload(event, raw));\n\n for (const script of targetsFor(event, raw)) {\n // Synchronous so the hook process does not exit before the capture\n // POSTs are issued. Each bundled hook already caps its own fetch\n // timeout, so the worst case here is bounded by those.\n spawnSync(process.execPath, [join(SCRIPTS_DIR, script)], {\n input: payload,\n // Child stdout is discarded on purpose: Antigravity parses this\n // process's stdout as the hook response, and the bundled scripts\n // emit prose when context injection is enabled.\n stdio: [\"pipe\", \"ignore\", \"ignore\"],\n });\n }\n}\n\n// Guarded so the pure helpers above stay importable from tests without the\n// module blocking on stdin. Every other bundled hook is a leaf script and\n// needs no such guard.\nconst invokedDirectly =\n process.argv[1] !== undefined &&\n resolve(process.argv[1]) === fileURLToPath(import.meta.url);\n\nif (invokedDirectly) {\n main()\n .catch(() => {})\n .finally(() => {\n // Always a well-formed, non-blocking response, emitted even when the\n // capture above threw or the payload was unparseable — a hook that\n // writes nothing is as fatal to PreToolUse as one that writes `{}`.\n process.stdout.write(responseFor(process.argv[2] ?? \"\"));\n process.exit(0);\n });\n}\n"],"mappings":"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"}
@@ -20,6 +20,16 @@ function resolveProject(cwd) {
20
20
  } catch {}
21
21
  return basename(dir);
22
22
  }
23
+ function hookCwd(data) {
24
+ if (!data || typeof data !== "object") return void 0;
25
+ if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
26
+ const roots = data.workspace_roots;
27
+ if (Array.isArray(roots)) {
28
+ for (const root of roots) if (typeof root === "string" && root.trim()) return root;
29
+ }
30
+ const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
31
+ if (projectDir && projectDir.trim()) return projectDir;
32
+ }
23
33
  //#endregion
24
34
  //#region src/hooks/notification.ts
25
35
  function isSdkChildContext(payload) {
@@ -47,16 +57,21 @@ async function main() {
47
57
  if (isSdkChildContext(data)) return;
48
58
  const notificationType = data.notification_type ?? data.notificationType;
49
59
  if (notificationType !== "permission_prompt") return;
50
- const rawSessionId = data.session_id ?? data.sessionId;
51
- const sessionId = typeof rawSessionId === "string" && rawSessionId.length > 0 ? rawSessionId : "unknown";
60
+ const rawSessionId = [
61
+ data.session_id,
62
+ data.sessionId,
63
+ data.conversation_id
64
+ ].find((v) => typeof v === "string" && v.length > 0);
65
+ const sessionId = typeof rawSessionId === "string" ? rawSessionId : "unknown";
66
+ const cwd = hookCwd(data) || process.cwd();
52
67
  fetch(`${REST_URL}/agentmemory/observe`, {
53
68
  method: "POST",
54
69
  headers: authHeaders(),
55
70
  body: JSON.stringify({
56
71
  hookType: "notification",
57
72
  sessionId,
58
- project: resolveProject(data.cwd),
59
- cwd: data.cwd || process.cwd(),
73
+ project: resolveProject(cwd),
74
+ cwd,
60
75
  timestamp: (/* @__PURE__ */ new Date()).toISOString(),
61
76
  data: {
62
77
  notification_type: notificationType,
@@ -1 +1 @@
1
- {"version":3,"file":"notification.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/notification.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n","#!/usr/bin/env node\nimport { resolveProject } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n const notificationType = data.notification_type ?? data.notificationType;\n if (notificationType !== \"permission_prompt\") return;\n\n const rawSessionId = data.session_id ?? data.sessionId;\n const sessionId =\n typeof rawSessionId === \"string\" && rawSessionId.length > 0\n ? rawSessionId\n : \"unknown\";\n\n fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"notification\",\n sessionId,\n project: resolveProject(data.cwd as string | undefined),\n cwd: (data.cwd as string | undefined) || process.cwd(),\n timestamp: new Date().toISOString(),\n data: {\n notification_type: notificationType,\n title: data.title,\n message: data.message,\n },\n }),\n signal: AbortSignal.timeout(2000),\n }).catch(() => {});\n setTimeout(() => process.exit(0), 500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAIA,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;;;ACftB,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAC7B,MAAM,mBAAmB,KAAK,qBAAqB,KAAK;AACxD,KAAI,qBAAqB,oBAAqB;CAE9C,MAAM,eAAe,KAAK,cAAc,KAAK;CAC7C,MAAM,YACJ,OAAO,iBAAiB,YAAY,aAAa,SAAS,IACtD,eACA;AAEN,OAAM,GAAG,SAAS,uBAAuB;EACvC,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU;GACnB,UAAU;GACV;GACA,SAAS,eAAe,KAAK,IAA0B;GACvD,KAAM,KAAK,OAA8B,QAAQ,KAAK;GACtD,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,MAAM;IACJ,mBAAmB;IACnB,OAAO,KAAK;IACZ,SAAS,KAAK;IACf;GACF,CAAC;EACF,QAAQ,YAAY,QAAQ,IAAK;EAClC,CAAC,CAAC,YAAY,GAAG;AAClB,kBAAiB,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO;;AAGhD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"notification.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/notification.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\nimport { resolveProject, hookCwd } from \"./_project.js\";\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown>;\n try {\n data = JSON.parse(input);\n } catch {\n return;\n }\n\n if (!data || typeof data !== \"object\") return;\n if (isSdkChildContext(data)) return;\n const notificationType = data.notification_type ?? data.notificationType;\n if (notificationType !== \"permission_prompt\") return;\n\n const rawSessionId = [data.session_id, data.sessionId, data.conversation_id].find(\n (v) => typeof v === \"string\" && v.length > 0,\n );\n const sessionId = typeof rawSessionId === \"string\" ? rawSessionId : \"unknown\";\n\n const cwd = hookCwd(data) || process.cwd();\n\n fetch(`${REST_URL}/agentmemory/observe`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify({\n hookType: \"notification\",\n sessionId,\n project: resolveProject(cwd),\n cwd,\n timestamp: new Date().toISOString(),\n data: {\n notification_type: notificationType,\n title: data.title,\n message: data.message,\n },\n }),\n signal: AbortSignal.timeout(2000),\n }).catch(() => {});\n setTimeout(() => process.exit(0), 500).unref();\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAIA,SAAgB,eAAe,KAAsB;CACnD,MAAM,WAAW,QAAQ,IAAI;AAC7B,KAAI,YAAY,SAAS,MAAM,CAAE,QAAO,SAAS,MAAM;CACvD,MAAM,MAAM,OAAO,IAAI,MAAM,GAAG,MAAM,QAAQ,KAAK;AACnD,KAAI;EACF,MAAM,MAAM,SAAS,iCAAiC;GACpD,KAAK;GACL,OAAO;IAAC;IAAU;IAAQ;IAAS;GACnC,SAAS;GACV,CAAC,CACC,UAAU,CACV,MAAM;AACT,MAAI,IAAK,QAAO,SAAS,IAAI;SACvB;AACR,QAAO,SAAS,IAAI;;AAGtB,SAAgB,QAAQ,MAAsE;AAC5F,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,KAAA;AAC9C,KAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAE,QAAO,KAAK;CACjE,MAAM,QAAQ,KAAK;AACnB,KAAI,MAAM,QAAQ,MAAM;OACjB,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAE,QAAO;;CAGxD,MAAM,aACJ,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;AAClD,KAAI,cAAc,WAAW,MAAM,CAAE,QAAO;;;;AC7B9C,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAEpD,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI;AACJ,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AACN;;AAGF,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU;AACvC,KAAI,kBAAkB,KAAK,CAAE;CAC7B,MAAM,mBAAmB,KAAK,qBAAqB,KAAK;AACxD,KAAI,qBAAqB,oBAAqB;CAE9C,MAAM,eAAe;EAAC,KAAK;EAAY,KAAK;EAAW,KAAK;EAAgB,CAAC,MAC1E,MAAM,OAAO,MAAM,YAAY,EAAE,SAAS,EAC5C;CACD,MAAM,YAAY,OAAO,iBAAiB,WAAW,eAAe;CAEpE,MAAM,MAAM,QAAQ,KAAK,IAAI,QAAQ,KAAK;AAE1C,OAAM,GAAG,SAAS,uBAAuB;EACvC,QAAQ;EACR,SAAS,aAAa;EACtB,MAAM,KAAK,UAAU;GACnB,UAAU;GACV;GACA,SAAS,eAAe,IAAI;GAC5B;GACA,4BAAW,IAAI,MAAM,EAAC,aAAa;GACnC,MAAM;IACJ,mBAAmB;IACnB,OAAO,KAAK;IACZ,SAAS,KAAK;IACf;GACF,CAAC;EACF,QAAQ,YAAY,QAAQ,IAAK;EAClC,CAAC,CAAC,YAAY,GAAG;AAClB,kBAAiB,QAAQ,KAAK,EAAE,EAAE,IAAI,CAAC,OAAO;;AAGhD,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
@@ -1,6 +1,18 @@
1
1
  #!/usr/bin/env node
2
2
  import { execFile } from "node:child_process";
3
3
  import { promisify } from "node:util";
4
+ //#region src/hooks/_project.ts
5
+ function hookCwd(data) {
6
+ if (!data || typeof data !== "object") return void 0;
7
+ if (typeof data.cwd === "string" && data.cwd.trim()) return data.cwd;
8
+ const roots = data.workspace_roots;
9
+ if (Array.isArray(roots)) {
10
+ for (const root of roots) if (typeof root === "string" && root.trim()) return root;
11
+ }
12
+ const projectDir = process.env["DEVIN_PROJECT_DIR"] || process.env["CLAUDE_PROJECT_DIR"];
13
+ if (projectDir && projectDir.trim()) return projectDir;
14
+ }
15
+ //#endregion
4
16
  //#region src/hooks/post-commit.ts
5
17
  const exec = promisify(execFile);
6
18
  function isSdkChildContext(payload) {
@@ -36,7 +48,7 @@ async function main() {
36
48
  } catch {}
37
49
  if (!data || typeof data !== "object") data = {};
38
50
  if (isSdkChildContext(data)) return;
39
- const cwd = data.cwd || process.env["AGENTMEMORY_CWD"] || process.cwd();
51
+ const cwd = hookCwd(data) || process.env["AGENTMEMORY_CWD"] || process.cwd();
40
52
  const sessionId = data.session_id || process.env["AGENTMEMORY_SESSION_ID"] || void 0;
41
53
  const sha = process.env["AGENTMEMORY_COMMIT_SHA"] || await git(["rev-parse", "HEAD"], cwd);
42
54
  if (!sha) return;
@@ -1 +1 @@
1
- {"version":3,"file":"post-commit.mjs","names":[],"sources":["../../src/hooks/post-commit.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\n\nconst exec = promisify(execFile);\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\nconst TIMEOUT_MS = 1500;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function git(args: string[], cwd: string): Promise<string | null> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, timeout: 1500 });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown> = {};\n if (input.trim()) {\n try {\n data = JSON.parse(input);\n } catch {\n // Direct invocation from .git/hooks/post-commit may pass no stdin.\n }\n }\n\n if (!data || typeof data !== \"object\") data = {};\n if (isSdkChildContext(data)) return;\n\n const cwd =\n (data.cwd as string) ||\n process.env[\"AGENTMEMORY_CWD\"] ||\n process.cwd();\n const sessionId =\n (data.session_id as string) ||\n process.env[\"AGENTMEMORY_SESSION_ID\"] ||\n undefined;\n\n const sha =\n process.env[\"AGENTMEMORY_COMMIT_SHA\"] ||\n (await git([\"rev-parse\", \"HEAD\"], cwd));\n if (!sha) return;\n\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n const repo = await git([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n const message = await git([\"log\", \"-1\", \"--pretty=%B\", sha], cwd);\n const author = await git([\"log\", \"-1\", \"--pretty=%an <%ae>\", sha], cwd);\n const authoredAt = await git([\"log\", \"-1\", \"--pretty=%aI\", sha], cwd);\n const filesRaw = await git(\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", sha],\n cwd,\n );\n const files = filesRaw ? filesRaw.split(\"\\n\").filter(Boolean) : undefined;\n\n const body = {\n sessionId,\n sha,\n branch: branch || undefined,\n repo: repo || undefined,\n message: message || undefined,\n author: author || undefined,\n authoredAt: authoredAt || undefined,\n files,\n };\n\n try {\n await fetch(`${REST_URL}/agentmemory/session/commit`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n });\n } catch {\n // best-effort\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAKA,MAAM,OAAO,UAAU,SAAS;AAEhC,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AACpD,MAAM,aAAa;AAEnB,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,IAAI,MAAgB,KAAqC;AACtE,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,KAAK,OAAO,MAAM;GAAE;GAAK,SAAS;GAAM,CAAC;AAClE,SAAO,OAAO,MAAM;SACd;AACN,SAAO;;;AAIX,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI,OAAgC,EAAE;AACtC,KAAI,MAAM,MAAM,CACd,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AAKV,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,EAAE;AAChD,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,MACH,KAAK,OACN,QAAQ,IAAI,sBACZ,QAAQ,KAAK;CACf,MAAM,YACH,KAAK,cACN,QAAQ,IAAI,6BACZ,KAAA;CAEF,MAAM,MACJ,QAAQ,IAAI,6BACX,MAAM,IAAI,CAAC,aAAa,OAAO,EAAE,IAAI;AACxC,KAAI,CAAC,IAAK;CAEV,MAAM,SAAS,MAAM,IAAI;EAAC;EAAa;EAAgB;EAAO,EAAE,IAAI;CACpE,MAAM,OAAO,MAAM,IAAI;EAAC;EAAU;EAAS;EAAoB,EAAE,IAAI;CACrE,MAAM,UAAU,MAAM,IAAI;EAAC;EAAO;EAAM;EAAe;EAAI,EAAE,IAAI;CACjE,MAAM,SAAS,MAAM,IAAI;EAAC;EAAO;EAAM;EAAsB;EAAI,EAAE,IAAI;CACvE,MAAM,aAAa,MAAM,IAAI;EAAC;EAAO;EAAM;EAAgB;EAAI,EAAE,IAAI;CACrE,MAAM,WAAW,MAAM,IACrB;EAAC;EAAa;EAAkB;EAAe;EAAM;EAAI,EACzD,IACD;CACD,MAAM,QAAQ,WAAW,SAAS,MAAM,KAAK,CAAC,OAAO,QAAQ,GAAG,KAAA;CAEhE,MAAM,OAAO;EACX;EACA;EACA,QAAQ,UAAU,KAAA;EAClB,MAAM,QAAQ,KAAA;EACd,SAAS,WAAW,KAAA;EACpB,QAAQ,UAAU,KAAA;EAClB,YAAY,cAAc,KAAA;EAC1B;EACD;AAED,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,8BAA8B;GACpD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU,KAAK;GAC1B,QAAQ,YAAY,QAAQ,WAAW;GACxC,CAAC;SACI;;AAKV,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}
1
+ {"version":3,"file":"post-commit.mjs","names":[],"sources":["../../src/hooks/_project.ts","../../src/hooks/post-commit.ts"],"sourcesContent":["import { execSync } from \"node:child_process\";\nimport { basename } from \"node:path\";\n\n// Resolution order: AGENTMEMORY_PROJECT_NAME env → git toplevel basename → cwd basename.\nexport function resolveProject(cwd?: string): string {\n const explicit = process.env[\"AGENTMEMORY_PROJECT_NAME\"];\n if (explicit && explicit.trim()) return explicit.trim();\n const dir = cwd && cwd.trim() ? cwd : process.cwd();\n try {\n const top = execSync(\"git rev-parse --show-toplevel\", {\n cwd: dir,\n stdio: [\"ignore\", \"pipe\", \"ignore\"],\n timeout: 500,\n })\n .toString()\n .trim();\n if (top) return basename(top);\n } catch {}\n return basename(dir);\n}\n\nexport function hookCwd(data: Record<string, unknown> | null | undefined): string | undefined {\n if (!data || typeof data !== \"object\") return undefined;\n if (typeof data.cwd === \"string\" && data.cwd.trim()) return data.cwd;\n const roots = data.workspace_roots;\n if (Array.isArray(roots)) {\n for (const root of roots) {\n if (typeof root === \"string\" && root.trim()) return root;\n }\n }\n const projectDir =\n process.env[\"DEVIN_PROJECT_DIR\"] || process.env[\"CLAUDE_PROJECT_DIR\"];\n if (projectDir && projectDir.trim()) return projectDir;\n return undefined;\n}\n","#!/usr/bin/env node\n\nimport { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nimport { hookCwd } from \"./_project.js\";\n\nconst exec = promisify(execFile);\n\nfunction isSdkChildContext(payload: unknown): boolean {\n if (process.env[\"AGENTMEMORY_SDK_CHILD\"] === \"1\") return true;\n if (!payload || typeof payload !== \"object\") return false;\n return (payload as { entrypoint?: unknown }).entrypoint === \"sdk-ts\";\n}\n\nconst REST_URL = process.env[\"AGENTMEMORY_URL\"] || \"http://localhost:3111\";\nconst SECRET = process.env[\"AGENTMEMORY_SECRET\"] || \"\";\nconst TIMEOUT_MS = 1500;\n\nfunction authHeaders(): Record<string, string> {\n const h: Record<string, string> = { \"Content-Type\": \"application/json\" };\n if (SECRET) h[\"Authorization\"] = `Bearer ${SECRET}`;\n return h;\n}\n\nasync function git(args: string[], cwd: string): Promise<string | null> {\n try {\n const { stdout } = await exec(\"git\", args, { cwd, timeout: 1500 });\n return stdout.trim();\n } catch {\n return null;\n }\n}\n\nasync function main() {\n let input = \"\";\n for await (const chunk of process.stdin) {\n input += chunk;\n }\n\n let data: Record<string, unknown> = {};\n if (input.trim()) {\n try {\n data = JSON.parse(input);\n } catch {\n // Direct invocation from .git/hooks/post-commit may pass no stdin.\n }\n }\n\n if (!data || typeof data !== \"object\") data = {};\n if (isSdkChildContext(data)) return;\n\n const cwd =\n hookCwd(data) || process.env[\"AGENTMEMORY_CWD\"] || process.cwd();\n const sessionId =\n (data.session_id as string) ||\n process.env[\"AGENTMEMORY_SESSION_ID\"] ||\n undefined;\n\n const sha =\n process.env[\"AGENTMEMORY_COMMIT_SHA\"] ||\n (await git([\"rev-parse\", \"HEAD\"], cwd));\n if (!sha) return;\n\n const branch = await git([\"rev-parse\", \"--abbrev-ref\", \"HEAD\"], cwd);\n const repo = await git([\"config\", \"--get\", \"remote.origin.url\"], cwd);\n const message = await git([\"log\", \"-1\", \"--pretty=%B\", sha], cwd);\n const author = await git([\"log\", \"-1\", \"--pretty=%an <%ae>\", sha], cwd);\n const authoredAt = await git([\"log\", \"-1\", \"--pretty=%aI\", sha], cwd);\n const filesRaw = await git(\n [\"diff-tree\", \"--no-commit-id\", \"--name-only\", \"-r\", sha],\n cwd,\n );\n const files = filesRaw ? filesRaw.split(\"\\n\").filter(Boolean) : undefined;\n\n const body = {\n sessionId,\n sha,\n branch: branch || undefined,\n repo: repo || undefined,\n message: message || undefined,\n author: author || undefined,\n authoredAt: authoredAt || undefined,\n files,\n };\n\n try {\n await fetch(`${REST_URL}/agentmemory/session/commit`, {\n method: \"POST\",\n headers: authHeaders(),\n body: JSON.stringify(body),\n signal: AbortSignal.timeout(TIMEOUT_MS),\n });\n } catch {\n // best-effort\n }\n}\n\nmain().catch(() => process.exit(0));\n"],"mappings":";;;;AAqBA,SAAgB,QAAQ,MAAsE;AAC5F,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,KAAA;AAC9C,KAAI,OAAO,KAAK,QAAQ,YAAY,KAAK,IAAI,MAAM,CAAE,QAAO,KAAK;CACjE,MAAM,QAAQ,KAAK;AACnB,KAAI,MAAM,QAAQ,MAAM;OACjB,MAAM,QAAQ,MACjB,KAAI,OAAO,SAAS,YAAY,KAAK,MAAM,CAAE,QAAO;;CAGxD,MAAM,aACJ,QAAQ,IAAI,wBAAwB,QAAQ,IAAI;AAClD,KAAI,cAAc,WAAW,MAAM,CAAE,QAAO;;;;AC1B9C,MAAM,OAAO,UAAU,SAAS;AAEhC,SAAS,kBAAkB,SAA2B;AACpD,KAAI,QAAQ,IAAI,6BAA6B,IAAK,QAAO;AACzD,KAAI,CAAC,WAAW,OAAO,YAAY,SAAU,QAAO;AACpD,QAAQ,QAAqC,eAAe;;AAG9D,MAAM,WAAW,QAAQ,IAAI,sBAAsB;AACnD,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AACpD,MAAM,aAAa;AAEnB,SAAS,cAAsC;CAC7C,MAAM,IAA4B,EAAE,gBAAgB,oBAAoB;AACxE,KAAI,OAAQ,GAAE,mBAAmB,UAAU;AAC3C,QAAO;;AAGT,eAAe,IAAI,MAAgB,KAAqC;AACtE,KAAI;EACF,MAAM,EAAE,WAAW,MAAM,KAAK,OAAO,MAAM;GAAE;GAAK,SAAS;GAAM,CAAC;AAClE,SAAO,OAAO,MAAM;SACd;AACN,SAAO;;;AAIX,eAAe,OAAO;CACpB,IAAI,QAAQ;AACZ,YAAW,MAAM,SAAS,QAAQ,MAChC,UAAS;CAGX,IAAI,OAAgC,EAAE;AACtC,KAAI,MAAM,MAAM,CACd,KAAI;AACF,SAAO,KAAK,MAAM,MAAM;SAClB;AAKV,KAAI,CAAC,QAAQ,OAAO,SAAS,SAAU,QAAO,EAAE;AAChD,KAAI,kBAAkB,KAAK,CAAE;CAE7B,MAAM,MACJ,QAAQ,KAAK,IAAI,QAAQ,IAAI,sBAAsB,QAAQ,KAAK;CAClE,MAAM,YACH,KAAK,cACN,QAAQ,IAAI,6BACZ,KAAA;CAEF,MAAM,MACJ,QAAQ,IAAI,6BACX,MAAM,IAAI,CAAC,aAAa,OAAO,EAAE,IAAI;AACxC,KAAI,CAAC,IAAK;CAEV,MAAM,SAAS,MAAM,IAAI;EAAC;EAAa;EAAgB;EAAO,EAAE,IAAI;CACpE,MAAM,OAAO,MAAM,IAAI;EAAC;EAAU;EAAS;EAAoB,EAAE,IAAI;CACrE,MAAM,UAAU,MAAM,IAAI;EAAC;EAAO;EAAM;EAAe;EAAI,EAAE,IAAI;CACjE,MAAM,SAAS,MAAM,IAAI;EAAC;EAAO;EAAM;EAAsB;EAAI,EAAE,IAAI;CACvE,MAAM,aAAa,MAAM,IAAI;EAAC;EAAO;EAAM;EAAgB;EAAI,EAAE,IAAI;CACrE,MAAM,WAAW,MAAM,IACrB;EAAC;EAAa;EAAkB;EAAe;EAAM;EAAI,EACzD,IACD;CACD,MAAM,QAAQ,WAAW,SAAS,MAAM,KAAK,CAAC,OAAO,QAAQ,GAAG,KAAA;CAEhE,MAAM,OAAO;EACX;EACA;EACA,QAAQ,UAAU,KAAA;EAClB,MAAM,QAAQ,KAAA;EACd,SAAS,WAAW,KAAA;EACpB,QAAQ,UAAU,KAAA;EAClB,YAAY,cAAc,KAAA;EAC1B;EACD;AAED,KAAI;AACF,QAAM,MAAM,GAAG,SAAS,8BAA8B;GACpD,QAAQ;GACR,SAAS,aAAa;GACtB,MAAM,KAAK,UAAU,KAAK;GAC1B,QAAQ,YAAY,QAAQ,WAAW;GACxC,CAAC;SACI;;AAKV,MAAM,CAAC,YAAY,QAAQ,KAAK,EAAE,CAAC"}