Repository navigation
feat: worktree DX — node_modules symlink, broadcast discovery, new hook handlers - #1054
Conversation
After git clone --shared, worktrees now get: - A symlink to the parent repo's node_modules (so builds work immediately) - Execution of .genie/init.sh if present (post-clone hook convention) Both are best-effort — failures don't block team creation.
) Three new hook handlers expanding the handler library: - brain-inject: queries genie-brain on first tool call per session for relevant knowledge context (no-op when brain is not installed) - audit-context: injects recent git history for files being written/edited - freshness: warns when reading files recently modified by another agent All handlers are informational (permissionDecision: allow) and best-effort.
…ompt Closes #1015 When spawning a council/team leader via `genie team create --wish`, fetch any broadcast messages sent to the team conversation in the last 5 minutes and append them to the initial kickoff prompt. This lets the leader auto-discover the broadcast topic without a manual `genie send` after spawn. - Add `getRecentBroadcasts(teamName, minutesAgo=5)` — best-effort DB fetch with graceful fallback when PG is unavailable - Update `plugins/genie/agents/council/SOUL.md` to instruct the council moderator to check for "Recent team broadcasts for context" in its prompt - No change in behavior when no broadcasts exist — prompt unchanged
…xity Biome reported cognitive complexity 21 (max 15) on the freshness handler. Extract buildCommitWarning and checkUncommittedChanges helpers and flip the early-return guard so the main function stays under the limit.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
✨ 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 |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1ec815719e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** Get recent git log for a specific file. Returns null if git is unavailable or file is untracked. */ | ||
| function getRecentGitHistory(filePath: string, cwd: string): string | null { | ||
| try { | ||
| const log = execSync(`git log --oneline -n ${MAX_COMMITS} -- ${JSON.stringify(filePath)}`, { |
There was a problem hiding this comment.
Pass file paths to git without invoking a shell
getRecentGitHistory builds a shell command with ${JSON.stringify(filePath)}, which is not shell-safe for filenames containing command substitutions (for example $(...) or backticks). If an agent edits a tracked file with such a name, this PreToolUse hook will execute the embedded shell payload before git log runs, creating an RCE path. Use execFile/spawn with argv (no shell parsing) for the file path argument.
Useful? React with 👍 / 👎.
| function getLastCommitInfo(filePath: string, cwd: string): { author: string; age: number; message: string } | null { | ||
| try { | ||
| // Get last commit timestamp, author, and subject for the file | ||
| const output = execSync(`git log -1 --format="%at|%an|%s" -- ${JSON.stringify(filePath)}`, { |
There was a problem hiding this comment.
Avoid shell command construction in freshness checks
The freshness handler also interpolates ${JSON.stringify(filePath)} into execSync shell commands, so crafted filenames can trigger shell expansion during read-time staleness checks. Because this runs automatically on PreToolUse:Read, a malicious path can execute arbitrary commands when the file is read. Switch these git calls to argument-based process APIs to avoid shell interpretation.
Useful? React with 👍 / 👎.
| if (enrichedSessions.has(key)) return; | ||
| enrichedSessions.add(key); |
There was a problem hiding this comment.
Persist brain enrichment dedupe across hook invocations
The “only once per session” guard uses an in-memory Set, but hook dispatch is executed as a command per hook event, so this process-local state is lost between tool calls. In practice, sessions with genie-brain installed will repeatedly query brain and re-inject the same context on every PreToolUse, increasing latency and prompt noise instead of running once. The dedupe key needs persistence outside process memory.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Code Review
This pull request introduces several new hook handlers (audit-context, brain-inject, and freshness) to improve agent context awareness and safety, alongside infrastructure updates for team management, such as post-clone symlinking and broadcast injection. The review feedback highlights security improvements by recommending the use of Node.js native filesystem functions instead of shell commands for directory cleanup to prevent command injection. Additionally, the review suggests simplifying the brain-inject handler by removing redundant checks and cleaning up the freshness handler logic for better readability.
| }); | ||
|
|
||
| afterEach(() => { | ||
| execSync(`rm -rf ${repoDir}`); |
There was a problem hiding this comment.
For safety and cross-platform compatibility, it's better to use Node.js's built-in filesystem functions for cleanup instead of shelling out to rm -rf. This avoids potential command injection vulnerabilities and works on Windows. This applies here and also to the cleanup on line 97. You'll need to add rmSync to your import from node:fs.
| execSync(`rm -rf ${repoDir}`); | |
| rmSync(repoDir, { recursive: true, force: true }); |
|
|
||
| afterEach(() => { | ||
| process.env = { ...originalEnv }; | ||
| execSync(`rm -rf ${repoDir}`); |
There was a problem hiding this comment.
For safety and cross-platform compatibility, it's better to use Node.js's built-in filesystem functions for cleanup instead of shelling out to rm -rf. This avoids potential command injection vulnerabilities and works on Windows. You'll need to add rmSync to your import from node:fs.
| execSync(`rm -rf ${repoDir}`); | |
| rmSync(repoDir, { recursive: true, force: true }); |
| enrichedSessions.add(key); | ||
|
|
||
| // Skip if brain is not installed | ||
| if (!isBrainAvailable()) return; |
There was a problem hiding this comment.
This check is redundant. The queryBrain function already gracefully handles cases where the @automagik/genie-brain package isn't installed by catching the error from the dynamic import(). Removing this check and the corresponding isBrainAvailable function (lines 30-33) and BRAIN_DIR constant (line 20) will simplify the code.
References
- Use dynamic imports (require() or import()) to break circular dependency cycles that would otherwise occur at module load time.
| permissionDecision: 'allow', | ||
| additionalContext: `[freshness] Stale read warning: ${filePath} has uncommitted changes (modified ${diskAge}s ago). Another agent may be editing this file concurrently.`, | ||
| }, | ||
| }; |
Summary
Three DX improvements from the worktree-dx-improvements wish.
node_modulesfrom parent repo into new worktrees inensureWorktree(), and execute.genie/init.shif present. Engineers no longer need to runbun installin every worktree.getRecentBroadcasts()fetches the last 5 minutes of team conversation messages and injects them into the kickoff prompt. Council/team leaders no longer need a manualgenie sendaftergenie team create.brain-inject.ts— SessionStart: query genie-brain for relevant contextaudit-context.ts— PreToolUse Write/Edit: inject recent git history for the filefreshness.ts— PreToolUse Read: warn on stale-read risk (file modified by another agent in last 2 min)Closes
Test plan
bun run buildpassesbun test— 2059 passbun run checkclean (lint + tests)