feat: per-directory .hivemind config (route org/workspace, opt out of capture)#302
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughAdds ChangesPer-directory configuration and collection controls
Estimated code review effort: 4 (Complex) | ~45 minutes Sequence Diagram(s)sequenceDiagram
participant SessionStartHook
participant resolveDirConfig
participant DeeplakeApi
participant additionalContext
SessionStartHook->>resolveDirConfig: resolve configuration from session cwd
resolveDirConfig-->>SessionStartHook: effective config and collect state
SessionStartHook->>DeeplakeApi: ensure tables and create placeholder when enabled
SessionStartHook->>additionalContext: report routed identity or collection opt-out
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
… capture) Add an in-tree `.hivemind` file that overlays the global identity for sessions launched under a directory tree — either to route captured traces to a specific org/workspace, or to opt out of capture entirely (`collect: false`). Travels with the repo like `.editorconfig`; a gitignored `.hivemind.local` handles personal overrides (`.env.local` convention). Resolution (src/dir-config.ts): walk up from cwd, nearest-wins (not .gitignore-style merge), `.hivemind.local` beats `.hivemind` in the same dir. Precedence is the conventional env > file > login — an explicit HIVEMIND_ORG_ID / HIVEMIND_WORKSPACE_ID locks that field. Safety by disclosure, not ceremony: the file never carries a token (auth stays in credentials.json), so routing can only target orgs the login already authorizes; and every session-start banner prints the effective org/workspace (+ "routed by ..." / "capture disabled here"), so a redirect is never silent. No trust/allow commands. Wired across all agents (claude/codex/cursor/hermes): capture, session-end, session-start placeholder + banner, and graph-push (new skipped-collect-disabled outcome). `collect:false` skips the placeholder write entirely rather than adding new machinery. Tests: dir-config resolver units (walk-up, nearest-wins, .local precedence, malformed skip, collect:false, overlay, env precedence) + capture/session-start hook wiring proving the primary write path honors opt-out (no writes) and routing (API built against the routed org/workspace), with env precedence verified end-to-end.
3b69a8d to
b5a7f1a
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (1)
src/hooks/capture.ts (1)
84-94: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winConsider extracting the dir-config gating into a shared helper.
The load-base →
resolveDirConfig→ early-return-on-collect:falsepattern is now duplicated verbatim acrosscapture.ts,codex/capture.ts,cursor/capture.ts, andhermes/capture.ts. A shared helper (e.g. insrc/hooks/shared/) that takes(cwd, agentLabel)and returns either a resolvedConfigornull(after logging) would remove this 4x duplication and prevent future drift — which already happened once (see the cursor/capture.ts comment on its cwd fallback).♻️ Sketch of a shared helper
// src/hooks/shared/dir-gate.ts export function resolveOrSkip(base: Config, cwd: string, agent: string, log: (m: string) => void): Config | null { const resolved = resolveDirConfig(base, cwd || process.cwd()); if (!resolved.collect) { log(`capture disabled for cwd=${cwd || "?"} via ${resolved.found?.path}`); return null; } return resolved.config; }🤖 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 `@src/hooks/capture.ts` around lines 84 - 94, Extract the duplicated base-config loading and directory gating into a shared helper under src/hooks/shared, such as resolveOrSkip, accepting cwd, agent label, and logger and returning the resolved Config or null. Update capture.ts, codex/capture.ts, cursor/capture.ts, and hermes/capture.ts to use it, preserving consistent cwd fallback, disabled-capture logging, and early returns.
🤖 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 `@src/graph/deeplake-push.ts`:
- Around line 77-88: Thread the resolved working directory through pushSnapshot:
add a cwd field to PushDeps, use it instead of process.cwd() when calling
resolveDirConfig in pushSnapshot, and pass runBuildCommand’s resolved cwd when
invoking pushSnapshot. Update any affected dependency construction and tests.
In `@src/hooks/codex/session-start-setup.ts`:
- Around line 78-87: Gate the entire backend write block in the session-start
setup handler, including ensureTable(), ensureSessionsTable(), and
createPlaceholder(), on both dirRes.collect and captureEnabled. Add a distinct
log branch explaining setup was skipped because HIVEMIND_CAPTURE=false, while
preserving the existing collect:false skip reason.
In `@src/hooks/cursor/session-start.ts`:
- Around line 220-229: Update the identity resolution in the session-start hook
so effOrg and effWs are derived from dirRes?.config ?? baseConfig, falling back
to credentials only when neither config exists, regardless of routed status.
Apply this correction to the identityLine banner and the corresponding logic in
the other session-start hook, while preserving routing and capture-disabled
messaging.
---
Nitpick comments:
In `@src/hooks/capture.ts`:
- Around line 84-94: Extract the duplicated base-config loading and directory
gating into a shared helper under src/hooks/shared, such as resolveOrSkip,
accepting cwd, agent label, and logger and returning the resolved Config or
null. Update capture.ts, codex/capture.ts, cursor/capture.ts, and
hermes/capture.ts to use it, preserving consistent cwd fallback,
disabled-capture logging, and early returns.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 4336d579-715a-4a16-a53f-41c8f9060d72
📒 Files selected for processing (21)
.gitignore.hivemind.exampleREADME.mdsrc/commands/graph.tssrc/dir-config.tssrc/graph/deeplake-push.tssrc/hooks/capture.tssrc/hooks/codex/capture.tssrc/hooks/codex/session-start-setup.tssrc/hooks/codex/stop.tssrc/hooks/cursor/capture.tssrc/hooks/cursor/session-end.tssrc/hooks/cursor/session-start.tssrc/hooks/hermes/capture.tssrc/hooks/hermes/session-end.tssrc/hooks/hermes/session-start.tssrc/hooks/session-end.tssrc/hooks/session-start.tstests/claude-code/capture-hook.test.tstests/claude-code/session-start-hook.test.tstests/shared/dir-config.test.ts
| // Disclose the EFFECTIVE identity (after any `.hivemind` overlay). | ||
| const routed = !!(dirRes?.found && dirRes.collect && baseConfig && | ||
| (dirRes.config.orgId !== baseConfig.orgId || dirRes.config.workspaceId !== baseConfig.workspaceId)); | ||
| const effOrg = routed ? (dirRes!.config.orgName ?? dirRes!.config.orgId) : (creds?.orgName ?? creds?.orgId); | ||
| const effWs = routed ? dirRes!.config.workspaceId : (creds?.workspaceId ?? "default"); | ||
| const identityLine = dirRes && !dirRes.collect | ||
| ? `Deeplake capture is disabled for this directory (${dirRes.found?.path}); memory search still uses org: ${effOrg}` | ||
| : `Logged in to Deeplake as org: ${effOrg} (workspace: ${effWs})${routed ? ` · routed by ${dirRes?.found?.path}` : ""}`; | ||
| const baseContext = creds?.token | ||
| ? `${context}\nLogged in to Deeplake as org: ${creds.orgName ?? creds.orgId} (workspace: ${creds.workspaceId ?? "default"})${versionNotice}` | ||
| ? `${context}\n${identityLine}${versionNotice}` |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Display the resolved identity, not raw credentials.
When HIVEMIND_ORG_ID or HIVEMIND_WORKSPACE_ID overrides login values, routed is false and this banner uses stale creds values, while Line 163 sends requests using dirRes.config. Derive effOrg and effWs from dirRes?.config ?? baseConfig regardless of whether routing changed. Apply the same correction in src/hooks/session-start.ts Lines 308-318.
🤖 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 `@src/hooks/cursor/session-start.ts` around lines 220 - 229, Update the
identity resolution in the session-start hook so effOrg and effWs are derived
from dirRes?.config ?? baseConfig, falling back to credentials only when neither
config exists, regardless of routed status. Apply this correction to the
identityLine banner and the corresponding logic in the other session-start hook,
while preserving routing and capture-disabled messaging.
- Extract shared resolveCaptureConfig() helper (src/hooks/shared/dir-gate.ts), removing the 4x-duplicated load-base -> resolveDirConfig -> skip-on-collect pattern across claude/codex/cursor/hermes capture hooks. - deeplake-push: thread the build cwd through PushDeps.cwd instead of process.cwd(), so `hivemind graph build --cwd ../repo` resolves the target tree's `.hivemind` (not the invocation dir). - codex session-start setup: gate the whole write block (ensure DDL + placeholder) on captureEnabled && collect, with a distinct HIVEMIND_CAPTURE= false log — matching the claude hook. - session-start banners: show the effective (resolved) org/workspace so an env override (HIVEMIND_ORG_ID/WORKSPACE_ID) is reflected, falling back to creds only when no config exists. - Tests updated to the new behavior.
|
@khustup2 I'd check Pi and openclaw as well |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@src/hooks/shared/dir-gate.ts`:
- Around line 20-23: Use the same effective cwd passed to resolveDirConfig in
the disabled-capture log within the relevant directory-gating function: compute
or retain cwd || process.cwd() once, pass it to resolveDirConfig, and
interpolate that resolved path instead of cwd || "?".
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: 88241ad0-0198-4577-b6cf-743b51959918
📒 Files selected for processing (15)
src/commands/graph.tssrc/graph/deeplake-push.tssrc/hooks/capture.tssrc/hooks/codex/capture.tssrc/hooks/codex/session-start-setup.tssrc/hooks/cursor/capture.tssrc/hooks/cursor/session-start.tssrc/hooks/hermes/capture.tssrc/hooks/hermes/session-start.tssrc/hooks/session-start.tssrc/hooks/shared/dir-gate.tstests/claude-code/session-start-hook.test.tstests/codex/codex-session-start-setup-hook.test.tstests/cursor/cursor-session-start-hook.test.tstests/hermes/hermes-session-start-hook.test.ts
🚧 Files skipped from review as they are similar to previous changes (6)
- src/hooks/hermes/capture.ts
- tests/claude-code/session-start-hook.test.ts
- src/hooks/session-start.ts
- src/graph/deeplake-push.ts
- src/hooks/cursor/session-start.ts
- src/hooks/hermes/session-start.ts
…sion Emanuele flagged pi/openclaw on PR #302. openclaw has no per-project filesystem cwd (it's a channel gateway, cwd=homedir sentinel), so `.hivemind` — a per-directory concept — doesn't apply there. pi IS a local coding agent with a real cwd, so it does. Pi ships as raw .ts with no shared-module imports, so add a compact inline mirror of src/dir-config.ts (findHivemindDir + applyDirConfig, walk-up nearest-wins, `.hivemind.local` beats `.hivemind`, env > file precedence). Apply it on every capture path — input, tool_result, message_end, session_shutdown — skipping capture on `collect:false` and routing to the resolved org/workspace otherwise. session_start gates table setup on collect and discloses the effective identity (routed / capture-disabled) in the injected context. Tests: source-text assertions (matching the file's existing style) that the resolver exists, env precedence is honored, all 4 capture paths gate on collect + reassign to routed creds, and session_start gates + discloses.
|
@efenocchi good call. Wired pi ( openclaw I left out on purpose: it's a channel gateway with no per-project filesystem cwd ( |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 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 `@harnesses/pi/extension-source/hivemind.ts`:
- Around line 128-139: Environment pins are only used to block directory
overrides, not applied as effective credentials. Update loadCreds() to merge
HIVEMIND_ORG_ID and HIVEMIND_WORKSPACE_ID into the returned creds before
applyDirConfig() runs, while preserving existing credential values when the
variables are unset and retaining the existing precedence behavior.
🪄 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: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Pro Plus
Run ID: e0ff3e91-5d4c-4e15-908e-4b7086051fef
📒 Files selected for processing (2)
harnesses/pi/extension-source/hivemind.tstests/pi/pi-extension-source.test.ts
- dir-config: read HIVEMIND_ORG_ID/WORKSPACE_ID as LITERAL process.env.X
accesses instead of an aliased `env = process.env` default param. The
alias reached the openclaw bundle (via deeplake-push -> dir-config) as a
bare `process.env` substring, tripping ClawHub's env-harvesting scan
("Audit openclaw bundle" CI step). Literals are stubbed to `undefined`
by the openclaw esbuild `define`; tests inject via an `envOverride` arg.
- dir-gate: log the resolved cwd (cwd || process.cwd()), not `?`, when the
hook passes an empty cwd.
- pi: fold env into creds (env > file > login) so the env lock reflects an
actually-applied value and pi matches the other agents' precedence,
rather than referencing HIVEMIND_ORG_ID without honoring it.
|
@efenocchi fixed 🙏 openclaw CI failure ("Audit openclaw bundle" step): my CodeRabbit minors:
Green locally (typecheck + touched suites). |
Coverage ReportScope: files changed in this PR. Enforced threshold: 90% per metric (per file via
File Coverage — 16 files changed
Generated for commit 65682e4. |
- Add a capture test exercising resolveCaptureConfig's `cwd || process.cwd()` fallback (empty cwd) — brings src/hooks/shared/dir-gate.ts to 100% branch. - Register src/dir-config.ts and src/hooks/shared/dir-gate.ts in the per-file coverage thresholds (branches 90), per the repo convention for new files.
My .hivemind additions to cursor/hermes session-start added banner- routing / collect:false / effConfig branches that their existing tests didn't exercise, dropping branch coverage below the 80% per-file gate (CI "Run tests with coverage"). Add routing / opt-out / env-precedence tests to both (mirroring the claude session-start coverage), restoring cursor to 83.8% and hermes to 81.8% branches.
What
Adds an in-tree
.hivemindfile that lets a directory tree override the global Hivemind identity — the two things people kept asking for:collect: false).It travels with the repo like
.editorconfig; a gitignored.hivemind.localhandles personal overrides (the.env.localconvention). A copy-ready.hivemind.exampleis included.How it resolves
cwd, nearest-wins (the.git/.gitconfigmodel, not.gitignore-style merge)..hivemind.localbeats.hivemindin the same dir.env > file > login— an explicitHIVEMIND_ORG_ID/HIVEMIND_WORKSPACE_IDlocks that field.Safety by disclosure, not ceremony
No trust/allow commands. Two properties make routing safe:
.hivemindnever carries a token — auth stays in~/.deeplake/credentials.json, so a routing override can only ever target orgs your login already authorizes (the API rejects anything else). Worst case is misfiling into another of your own orgs, not leaking to a stranger.org: … (workspace: …) · routed by ./.hivemind, orcapture is disabled for this directory— so a redirect is never silent.Scope
Wired across all four agents (claude/codex/cursor/hermes):
capture,session-end,session-start(placeholder + banner), andgraph-push(newskipped-collect-disabledoutcome).collect:falseskips the placeholder write rather than adding new machinery.Tests
tests/shared/dir-config.test.ts): walk-up, nearest-wins,.localprecedence, malformed-file skip, whitelisting,collect:false, overlay, env precedence.capture+session-start): a real.hivemindunder the mockedcwdproves the primary write path writes nothing oncollect:falseand builds the API against the routed org/workspace, withenv > fileverified end-to-end.Full suite green except pre-existing env-only failures (
tree-sitter-pythonnot installed → graph/*; flush-memory network timeout) — both untouched here. Bundles are gitignored (built at pack time), so the diff is source + tests + README.Summary by CodeRabbit
.hivemind/.hivemind.localconfig to route org/workspace identity and optionally disable capture (collect:false)..hivemindformat, precedence/overlay rules, and interaction with global settings..hivemind.localto.gitignoreand included an example config file.