Description
Every time emdash spawns an agent session, HookConfigWriter.prepareHookConfig() writes
notification hook configs into the project/worktree cwd:
.claude/settings.local.json — adds Notification and Stop hook entries pointing at
emdash's local POST endpoint (carries EMDASH_HOOK_PORT / EMDASH_HOOK_TOKEN markers).
.codex/config.toml — sets notify = [...] to the bundled notify script.
.opencode/plugins/emdash-notifications.js, .pi/extensions/emdash-hook.ts for those
providers.
There is currently no setting that disables this injection. The two related toggles
do not gate it:
| Setting |
Effect |
notifications.enabled |
Only suppresses Notification.show() (OS toast). The hook file is still written. |
localProject.writeAgentConfigToGitIgnore |
Only controls whether the path is added to .gitignore. The hook file is still written. |
Why this is a problem
- It silently mutates user-owned config files (
.claude/settings.local.json is meant for
user-specific permissions, not tool-managed hooks).
- It runs on every fresh
LocalConversationProvider instance — disabling per-task caching
via preparedHookProviders is per-instance and resets across tasks/sessions.
- Users who don't want their projects coupled to a running emdash daemon (e.g. they share
the worktree with other agents, run agents directly, or audit their repo for unexpected
files) have no opt-out short of patching the asar or making the files immutable
(chmod 444 / chflags uchg), both of which produce silent write failures emdash logs
as warnings.
Reproduction
- emdash 1.1.10, macOS.
- Open any project, start a Claude or Codex task.
cat .claude/settings.local.json — the hooks block now contains an entry referencing
EMDASH_HOOK_PORT. Same for .codex/config.toml (notify = ...).
- Toggle off
notifications.enabled and writeAgentConfigToGitIgnore from Settings.
- Start a new task — the hook entries are re-written.
Source pointers (1.1.10, packed out/main/index.js)
class HookConfigWriter — writeClaudeHooks / writeCodexNotify / writeForProvider /
writeAll.
- Caller:
LocalConversationProvider.prepareHookConfig invoked from startSession. The
guard only checks writeAgentConfigToGitIgnore for .gitignore writes, never gates the
config file write itself.
Proposed fix
Add a new setting (e.g. localProject.injectAgentNotificationHooks: boolean, default
true to preserve current behavior) and short-circuit prepareHookConfig when it's
false. Optionally per-provider granularity ({ claude: false, codex: true }) for users
who want notifications for one provider but not another.
A "clean up existing emdash hook entries from this project" action in Settings would also
be helpful, since users who toggle this off will have stale entries from previous runs.
emdash version
1.1.10
OS
macOS (Darwin 25.3.0)
Description
Every time emdash spawns an agent session,
HookConfigWriter.prepareHookConfig()writesnotification hook configs into the project/worktree cwd:
.claude/settings.local.json— addsNotificationandStophook entries pointing atemdash's local POST endpoint (carries
EMDASH_HOOK_PORT/EMDASH_HOOK_TOKENmarkers)..codex/config.toml— setsnotify = [...]to the bundled notify script..opencode/plugins/emdash-notifications.js,.pi/extensions/emdash-hook.tsfor thoseproviders.
There is currently no setting that disables this injection. The two related toggles
do not gate it:
notifications.enabledNotification.show()(OS toast). The hook file is still written.localProject.writeAgentConfigToGitIgnore.gitignore. The hook file is still written.Why this is a problem
.claude/settings.local.jsonis meant foruser-specific permissions, not tool-managed hooks).
LocalConversationProviderinstance — disabling per-task cachingvia
preparedHookProvidersis per-instance and resets across tasks/sessions.the worktree with other agents, run agents directly, or audit their repo for unexpected
files) have no opt-out short of patching the asar or making the files immutable
(
chmod 444/chflags uchg), both of which produce silent write failures emdash logsas warnings.
Reproduction
cat .claude/settings.local.json— thehooksblock now contains an entry referencingEMDASH_HOOK_PORT. Same for.codex/config.toml(notify = ...).notifications.enabledandwriteAgentConfigToGitIgnorefrom Settings.Source pointers (1.1.10, packed
out/main/index.js)class HookConfigWriter—writeClaudeHooks/writeCodexNotify/writeForProvider/writeAll.LocalConversationProvider.prepareHookConfiginvoked fromstartSession. Theguard only checks
writeAgentConfigToGitIgnorefor.gitignorewrites, never gates theconfig file write itself.
Proposed fix
Add a new setting (e.g.
localProject.injectAgentNotificationHooks: boolean, defaulttrueto preserve current behavior) and short-circuitprepareHookConfigwhen it'sfalse. Optionally per-provider granularity ({ claude: false, codex: true }) for userswho want notifications for one provider but not another.
A "clean up existing emdash hook entries from this project" action in Settings would also
be helpful, since users who toggle this off will have stale entries from previous runs.
emdash version
1.1.10
OS
macOS (Darwin 25.3.0)