Skip to content

fix(hooks): send repo basename as project, not full path (#474)#687

Merged
rohitg00 merged 3 commits into
mainfrom
fix/474-hook-project-basename
May 27, 2026
Merged

fix(hooks): send repo basename as project, not full path (#474)#687
rohitg00 merged 3 commits into
mainfrom
fix/474-hook-project-basename

Conversation

@rohitg00
Copy link
Copy Markdown
Owner

@rohitg00 rohitg00 commented May 27, 2026

Summary

Hooks were sending data.cwd (an absolute filesystem path) as the project field on every observe / session/start / enrich call. Native sessions, mem::replay::import-jsonl, and memory_lesson_save all use the repo basename. The mismatch caused auto-injected context to filter out the bulk of relevant lessons because the path never matches the stored project name.

Changes

  • New src/hooks/_project.ts exporting resolveProject(cwd?) with the resolution order:
    1. AGENTMEMORY_PROJECT_NAME env (per-repo escape hatch)
    2. basename of git rev-parse --show-toplevel (handles subdirs)
    3. basename of cwd (final fallback when not in a git repo)
  • 9 hooks updated to call resolveProject for the project field while still sending the raw cwd path in the cwd field: notification, post-tool-use, post-tool-failure, prompt-submit, session-start, subagent-start, subagent-stop, task-completed, pre-compact.
  • Hooks that already handle project correctly (pre-tool-use) or don't send it (post-commit, session-end, stop) left untouched.
  • tsdown.config.ts split into per-entry configs for the hook bundles so each hook compiles into a fully self-contained .mjs with no shared hashed chunks. Previous shared-entry config caused tsdown to hoist helpers into _project-<hash>.mjs artifacts that changed on every rebuild and were prone to being committed accidentally.

Test plan

  • npx vitest run test/hook-project.test.ts — 8/8 pass (env override, whitespace trim, empty env ignored, git toplevel from cwd, git toplevel from nested subdir, basename fallback in tmpdir, default to process.cwd(), empty/whitespace cwd argument)
  • npx vitest run — 1238 pass, 1 integration file skipped (requires running server, pre-existing)
  • Build emits no hashed chunks — find dist/hooks plugin/scripts -name '_project*' returns empty
  • Sample bundle inspection: head -25 plugin/scripts/notification.mjs shows resolveProject inlined per-hook

Closes #474.

Summary by CodeRabbit

  • New Features

    • Improved project identification across hooks and CLI flows: project names are now normalized from an environment override, Git repository name, or working directory, improving observability and consistency.
  • Tests

    • Added tests covering project-name resolution across environment and Git/non-Git scenarios.
  • Chores

    • Build config updated to emit standalone per-hook bundles for clearer outputs.

Review Change Stack

rohitg00 added 2 commits May 27, 2026 19:45
Hooks were sending `data.cwd` (an absolute filesystem path) as the
`project` field on every observe/session/start call. Native sessions,
replay-import, and manual memory_lesson_save calls all use the repo
basename. The mismatch caused auto-injected context to filter out the
bulk of relevant lessons because the path never matches the stored
project name.

Add shared `resolveProject(cwd)` helper:
  1. AGENTMEMORY_PROJECT_NAME env (per-repo escape hatch)
  2. basename of `git rev-parse --show-toplevel` (handles subdirs)
  3. basename of cwd (final fallback when not in a git repo)

Applied to 9 hooks: notification, post-tool-use, post-tool-failure,
prompt-submit, session-start, subagent-start, subagent-stop,
task-completed, pre-compact.

Build: split hook entries into per-entry tsdown configs so each hook
bundles into a fully self-contained .mjs. Previous shared config
hoisted helpers into hashed chunks that changed on every rebuild.
@vercel
Copy link
Copy Markdown

vercel Bot commented May 27, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
agentmemory Ready Ready Preview, Comment May 27, 2026 6:58pm

Request Review

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 27, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6a4f4e39-e8db-4496-bd17-5b4b766e9d1d

📥 Commits

Reviewing files that changed from the base of the PR and between 7d2ca35 and 0dd782b.

📒 Files selected for processing (2)
  • src/hooks/_project.ts
  • tsdown.config.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/hooks/_project.ts
  • tsdown.config.ts

📝 Walkthrough

Walkthrough

Adds a centralized resolveProject(cwd?) helper (env → git top-level basename → cwd basename) and uses it across TypeScript and JavaScript hook scripts and plugin emitters so outgoing hook payloads send a normalized project name; also updates tsdown config for per-hook bundling.

Changes

Project Field Resolution Normalization

Layer / File(s) Summary
Core resolveProject helper and tests
src/hooks/_project.ts, test/hook-project.test.ts
Exports resolveProject(cwd?: string) implementing: trimmed AGENTMEMORY_PROJECT_NAMEgit rev-parse --show-toplevel basename (500ms) → basename of cwd/process.cwd(). Tests cover env precedence, git vs non-git behavior, and missing/empty cwd.
Build configuration for per-hook bundling
tsdown.config.ts
Generates standalone per-hook bundles for dist/hooks and plugin/scripts by mapping over hookEntries, preventing hoisted shared hashed chunks.
TypeScript hook scripts integration
src/hooks/{notification,post-tool-failure,post-tool-use,pre-compact,prompt-submit,session-start,subagent-start,subagent-stop,task-completed}.ts
All TypeScript hooks import resolveProject and set outgoing payload project via `resolveProject(data.cwd as string
JavaScript plugin scripts implementation
plugin/scripts/{notification,post-tool-failure,post-tool-use,pre-compact,prompt-submit,session-start,subagent-start,subagent-stop,task-completed}.mjs
Each JS script now contains or uses a resolveProject implementation and updates observe/enrich request bodies to use project: resolveProject(data.cwd) rather than raw `data.cwd
Pre-tool-use upstream project passthrough
plugin/scripts/pre-tool-use.mjs
Extracts optional project from incoming tool payload (trimmed; empty → omitted) and only includes it in /agentmemory/enrich when present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • rohitg00/agentmemory#648: Both PRs touch how project is supplied to mem::observe; this change normalizes/derives project client-side while #648 addresses server-side handling.

Poem

🐇 I nibbled env, then peeked at git,

trimmed strings and fell back to cwd,
Now each hook's payload knows its name,
A tidy project in every thread—
Hooray, says the rabbit, hopping ahead!

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The linked issue #474 requests that agentmemory stop signal the iii engine to exit and verify it's gone, but this PR addresses project basename resolution in hooks—a completely different concern unrelated to stopping iii processes. This PR does not implement the requested fix for issue #474 (graceful iii engine shutdown). Clarify whether this PR is for the correct issue or if it addresses a different concern.
Docstring Coverage ⚠️ Warning Docstring coverage is 42.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'fix(hooks): send repo basename as project, not full path' accurately summarizes the main change: switching hooks to send repository basename instead of full filesystem paths as the project field.
Out of Scope Changes check ✅ Passed All changes focus on adding resolveProject helper and updating hooks to use repository basenames for the project field. No unrelated modifications to other systems like iii engine shutdown logic were introduced.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/474-hook-project-basename

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@plugin/scripts/prompt-submit.mjs`:
- Around line 6-10: The resolveProject function calls cwd.trim() without
ensuring cwd is a string; update resolveProject to validate or coerce cwd before
trimming (e.g., check typeof cwd === "string" or convert with String(cwd)) and
only call .trim() on a confirmed string, then fall back to process.cwd() when
invalid—modify the cwd handling at the start of resolveProject (the explicit/dir
assignment logic) so non-string inputs don't cause an exception.

In `@test/hook-project.test.ts`:
- Around line 33-65: Tests assume a repo named "agentmemory" and use split("/")
which is brittle; replace hardcoded expectations with a repository-agnostic
basename variable (e.g., const repoBasename = path.basename(process.cwd())) and
use that in assertions for resolveProject(), resolveProject(nested), and the
default/empty-cwd tests, and in the "falls back" test replace dir.split("/")
with path.basename(dir) to be path-separator safe; keep references to the
resolveProject function and the existing tmp dir creation but use path.basename
for all basename checks.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: f731b206-38f4-4b39-ae15-c63f3b0d30c5

📥 Commits

Reviewing files that changed from the base of the PR and between e39d962 and 7d2ca35.

📒 Files selected for processing (22)
  • plugin/scripts/notification.mjs
  • plugin/scripts/post-tool-failure.mjs
  • plugin/scripts/post-tool-use.mjs
  • plugin/scripts/pre-compact.mjs
  • plugin/scripts/pre-tool-use.mjs
  • plugin/scripts/prompt-submit.mjs
  • plugin/scripts/session-start.mjs
  • plugin/scripts/subagent-start.mjs
  • plugin/scripts/subagent-stop.mjs
  • plugin/scripts/task-completed.mjs
  • src/hooks/_project.ts
  • src/hooks/notification.ts
  • src/hooks/post-tool-failure.ts
  • src/hooks/post-tool-use.ts
  • src/hooks/pre-compact.ts
  • src/hooks/prompt-submit.ts
  • src/hooks/session-start.ts
  • src/hooks/subagent-start.ts
  • src/hooks/subagent-stop.ts
  • src/hooks/task-completed.ts
  • test/hook-project.test.ts
  • tsdown.config.ts

Comment on lines +6 to +10
function resolveProject(cwd) {
const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
if (explicit && explicit.trim()) return explicit.trim();
const dir = cwd && cwd.trim() ? cwd : process.cwd();
try {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Guard cwd type before calling .trim() in resolveProject.

Line 9 can throw when cwd is non-string input, causing this hook event to be dropped. Coerce/validate type before trimming.

Suggested fix
 function resolveProject(cwd) {
 	const explicit = process.env["AGENTMEMORY_PROJECT_NAME"];
 	if (explicit && explicit.trim()) return explicit.trim();
-	const dir = cwd && cwd.trim() ? cwd : process.cwd();
+	const dir = typeof cwd === "string" && cwd.trim() ? cwd : process.cwd();
 	try {
 		const top = execSync("git rev-parse --show-toplevel", {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@plugin/scripts/prompt-submit.mjs` around lines 6 - 10, The resolveProject
function calls cwd.trim() without ensuring cwd is a string; update
resolveProject to validate or coerce cwd before trimming (e.g., check typeof cwd
=== "string" or convert with String(cwd)) and only call .trim() on a confirmed
string, then fall back to process.cwd() when invalid—modify the cwd handling at
the start of resolveProject (the explicit/dir assignment logic) so non-string
inputs don't cause an exception.

Comment thread test/hook-project.test.ts
Comment on lines +33 to +65
it("ignores empty env override", () => {
process.env.AGENTMEMORY_PROJECT_NAME = " ";
const repoBasename = "agentmemory";
expect(resolveProject(process.cwd())).toBe(repoBasename);
});

it("returns git toplevel basename when cwd is inside a repo", () => {
const top = resolveProject(process.cwd());
expect(top).toBe("agentmemory");
});

it("returns git toplevel basename from a nested subdir", () => {
const nested = join(process.cwd(), "src", "hooks");
expect(resolveProject(nested)).toBe("agentmemory");
});

it("falls back to basename(cwd) when not in a git repo", () => {
const dir = mkdtempSync(join(tmpdir(), "amem-noproj-"));
try {
expect(resolveProject(dir)).toBe(dir.split("/").pop());
} finally {
rmSync(dir, { recursive: true, force: true });
}
});

it("defaults to process.cwd() when no cwd argument given", () => {
expect(resolveProject()).toBe("agentmemory");
});

it("defaults to process.cwd() when cwd argument is empty", () => {
expect(resolveProject("")).toBe("agentmemory");
expect(resolveProject(" ")).toBe("agentmemory");
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Make project-name expectations repository-agnostic and path-safe.

Several assertions assume the repo is named "agentmemory" and one uses split("/"); this makes tests brittle on forks and non-POSIX environments.

Suggested fix
-import { join } from "node:path";
+import { basename, join } from "node:path";
 import { resolveProject } from "../src/hooks/_project.js";
 
 describe("resolveProject — hook project basename resolver", () => {
+  const repoBasename = basename(process.cwd());
   const originalEnv = process.env.AGENTMEMORY_PROJECT_NAME;
@@
   it("ignores empty env override", () => {
     process.env.AGENTMEMORY_PROJECT_NAME = "   ";
-    const repoBasename = "agentmemory";
     expect(resolveProject(process.cwd())).toBe(repoBasename);
   });
@@
   it("returns git toplevel basename when cwd is inside a repo", () => {
     const top = resolveProject(process.cwd());
-    expect(top).toBe("agentmemory");
+    expect(top).toBe(repoBasename);
   });
@@
   it("returns git toplevel basename from a nested subdir", () => {
     const nested = join(process.cwd(), "src", "hooks");
-    expect(resolveProject(nested)).toBe("agentmemory");
+    expect(resolveProject(nested)).toBe(repoBasename);
   });
@@
   it("falls back to basename(cwd) when not in a git repo", () => {
     const dir = mkdtempSync(join(tmpdir(), "amem-noproj-"));
     try {
-      expect(resolveProject(dir)).toBe(dir.split("/").pop());
+      expect(resolveProject(dir)).toBe(basename(dir));
     } finally {
       rmSync(dir, { recursive: true, force: true });
     }
   });
@@
   it("defaults to process.cwd() when no cwd argument given", () => {
-    expect(resolveProject()).toBe("agentmemory");
+    expect(resolveProject()).toBe(repoBasename);
   });
@@
   it("defaults to process.cwd() when cwd argument is empty", () => {
-    expect(resolveProject("")).toBe("agentmemory");
-    expect(resolveProject("   ")).toBe("agentmemory");
+    expect(resolveProject("")).toBe(repoBasename);
+    expect(resolveProject("   ")).toBe(repoBasename);
   });
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@test/hook-project.test.ts` around lines 33 - 65, Tests assume a repo named
"agentmemory" and use split("/") which is brittle; replace hardcoded
expectations with a repository-agnostic basename variable (e.g., const
repoBasename = path.basename(process.cwd())) and use that in assertions for
resolveProject(), resolveProject(nested), and the default/empty-cwd tests, and
in the "falls back" test replace dir.split("/") with path.basename(dir) to be
path-separator safe; keep references to the resolveProject function and the
existing tmp dir creation but use path.basename for all basename checks.

@rohitg00 rohitg00 merged commit 0468407 into main May 27, 2026
7 checks passed
@rohitg00 rohitg00 deleted the fix/474-hook-project-basename branch May 27, 2026 19:32
This was referenced May 28, 2026
rohitg00 added a commit that referenced this pull request May 28, 2026
Bumps version across 9 files + adds CHANGELOG entry summarizing the
18 commits since v0.9.22.

Highlights:
- GitHub Copilot CLI first-class support (#534) — plugin + hooks +
  MCP with LSP-style Content-Length framing on the standalone stdio
  transport.
- Five new MCP adapters: Warp, Cline, Continue, Zed, Droid (#677);
  ADAPTERS count 11 → 17.
- Three silent DX bugs fixed: graph extraction never fired on
  session end (#666 / #698), status reported zero memories (#666),
  consolidation defaulted off even with an LLM provider configured
  (#612 / #696).
- Nine telemetry hooks switched to fire-and-forget so they don't
  block Claude Code's next-prompt boundary (#573 / #688).
- Hook project field now sends repo basename instead of full
  filesystem path so auto-injected context isn't silently filtered
  out (#474 / #687).
- Local-LLM docs: Ollama / LM Studio / vLLM section added (#671 /
  #697).

Version-bump files: package.json, plugin/.claude-plugin/plugin.json,
plugin/plugin.json, plugin/.codex-plugin/plugin.json,
packages/mcp/package.json, src/version.ts, src/types.ts,
src/functions/export-import.ts, test/export-import.test.ts.
rohitg00 added a commit that referenced this pull request May 28, 2026
* chore(release): v0.9.23

Bumps version across 9 files + adds CHANGELOG entry summarizing the
18 commits since v0.9.22.

Highlights:
- GitHub Copilot CLI first-class support (#534) — plugin + hooks +
  MCP with LSP-style Content-Length framing on the standalone stdio
  transport.
- Five new MCP adapters: Warp, Cline, Continue, Zed, Droid (#677);
  ADAPTERS count 11 → 17.
- Three silent DX bugs fixed: graph extraction never fired on
  session end (#666 / #698), status reported zero memories (#666),
  consolidation defaulted off even with an LLM provider configured
  (#612 / #696).
- Nine telemetry hooks switched to fire-and-forget so they don't
  block Claude Code's next-prompt boundary (#573 / #688).
- Hook project field now sends repo basename instead of full
  filesystem path so auto-injected context isn't silently filtered
  out (#474 / #687).
- Local-LLM docs: Ollama / LM Studio / vLLM section added (#671 /
  #697).

Version-bump files: package.json, plugin/.claude-plugin/plugin.json,
plugin/plugin.json, plugin/.codex-plugin/plugin.json,
packages/mcp/package.json, src/version.ts, src/types.ts,
src/functions/export-import.ts, test/export-import.test.ts.

* chore(release): add #701 + #709 to v0.9.23 CHANGELOG
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

agentmemory stop disconnects worker but leaves iii engine running with stale function registrations

1 participant