Skip to content

Follow up Claude Code upstream updates: DirectoryAdded event, lossy hook handler fields, warned Write()/Glob() permission forms, skill background, sandbox.* #2397

Description

@dyoshikawa

Summary

Claude Code shipped several config-surface changes between 2.1.113 (2026-04-17) and 2.1.219 (2026-07-24) that rulesync has not followed: a new DirectoryAdded hook event, a set of documented hook handler fields that rulesync silently drops on generate, a permission-rule form that Claude Code now warns about at startup, a new skill frontmatter field, and the still-unauthorable sandbox.* settings subtree.

Recent Releases

Source: anthropics/claude-code CHANGELOG.md (dates from npm view @anthropic-ai/claude-code time).

  • 2.1.219 (2026-07-24) — DirectoryAdded hook event; sandbox.network.strictAllowlist setting.
  • 2.1.218 (2026-07-22) — skill frontmatter background (only meaningful with context: fork).
  • 2.1.216 (2026-07-20) — sandbox.filesystem.disabled setting.
  • 2.1.210 (2026-07-14) — startup warning for Write(path) / NotebookEdit(path) / Glob(path) permission rules; continueOnBlock semantics change for PreToolUse prompt hooks.
  • 2.1.207 (2026-07-10) — plugin shell-form hooks reject ${user_config.*}; docs steer users to the exec form (args).
  • 2.1.187 (2026-06-23) — sandbox.credentials setting.
  • 2.1.181 (2026-06-17) — sandbox.allowAppleEvents setting.
  • 2.1.139 (2026-05-11) — hook args: string[] exec form; hook continueOnBlock for PostToolUse.
  • 2.1.113 (2026-04-17) — sandbox.network.deniedDomains.

Gaps

1. hooks — DirectoryAdded event unsupported

  • Upstream: DirectoryAdded fires after /add-dir or the SDK register_repo_root control request registers a new working directory mid-session. CHANGELOG 2.1.219 (2026-07-24). Not yet in the hooks docs event table.
  • rulesync: unsupported. CLAUDE_HOOK_EVENTS (src/types/hooks.ts) lists 30 events with no directoryAdded, and CANONICAL_TO_CLAUDE_EVENT_NAMES has no mapping. buildEffectiveHooks filters to supportedEvents, so the event is silently dropped.

2. hooks — documented handler fields are dropped on generate

  • Upstream (hooks docs): common statusMessage, once; command hooks args: string[] (exec form — no shell, so path placeholders never need quoting), async, asyncRewake, shell (bash | powershell); prompt hooks continueOnBlock. args and continueOnBlock landed in 2.1.139 (2026-05-11); 2.1.207 (2026-07-10) rejects ${user_config.*} in shell-form plugin hook commands and directs users to the exec form.
  • rulesync: unsupported for claudecode and claudecode-plugin. buildToolHooks (src/features/hooks/tool-hooks-converter.ts) emits only type, command, timeout, prompt, type-specific payload fields, plus declared passthrough. CLAUDE_CONVERTER_CONFIG (src/features/hooks/claudecode-hooks.ts) declares only stringPassthroughFields: [{ canonical: "if", tool: "if" }]. args, asyncRewake and continueOnBlock are not in the canonical HookDefinitionSchema (src/types/hooks.ts) at all. Verified: a hook authored with args, shell, async, asyncRewake, statusMessage, once and continueOnBlock generated {"type":"command","command":"node"} and {"type":"prompt","prompt":"check"} only. ClaudecodePluginHooks inherits the same converter config, so plugin hooks/hooks.json is equally lossy — and that is exactly the surface upstream now steers to exec form.

3. hooks — UserPromptSubmit and Stop missing from the no-matcher set

  • Upstream: the hooks docs matcher table lists UserPromptSubmit, PostToolBatch, Stop, TeammateIdle, TaskCreated, TaskCompleted, WorktreeCreate, WorktreeRemove, MessageDisplay and CwdChanged as having no matcher support.
  • rulesync: CLAUDE_NO_MATCHER_EVENTS (src/features/hooks/claudecode-hooks.ts) lists 8 of those 10 — beforeSubmitPrompt (→ UserPromptSubmit) and stop are missing, so a matcher authored on them is written into .claude/settings.json and silently ignored upstream instead of being dropped with the existing warning.

4. permissions — rulesync emits the now-warned Write(path) / NotebookEdit(path) / Glob(path) forms

  • Upstream: "File permission checks match only Edit(path) and Read(path) rules. A Write(path), NotebookEdit(path), or Glob(path) rule is accepted but never matched by those checks, so Claude Code warns at startup for each allow, deny, or ask rule in one of these unmatched forms." — permissions docs, CHANGELOG 2.1.210 (2026-07-14). A tool-name-only rule (no path) is unaffected.
  • rulesync: project, and it generates exactly the warned forms. CANONICAL_TO_CLAUDE_TOOL_NAMES (src/features/permissions/claudecode-permissions.ts) maps writeWrite, notebookeditNotebookEdit, globGlob, and buildClaudePermissionEntry keeps the pattern, so {"permission":{"write":{"docs/**":"deny"}}} produces "deny": ["Write(docs/**)"].

5. skills — background frontmatter not authorable, dropped on import

  • Upstream: background — "Only applies with context: fork. Set to false to wait for the forked subagent's result in the turn that invoked the skill, instead of running it in the background. Default: true." — skills docs, requires 2.1.218 (2026-07-22).
  • rulesync: unsupported. ClaudecodeSkillFrontmatterSchema (src/features/skills/claudecode-skill.ts) has context and agent but no background, and the claudecode section of RulesyncSkillFrontmatter (src/features/skills/rulesync-skill.ts) has none either. buildClaudecodeSkillFrontmatter copies a fixed truthyFields/definedFields list rather than spreading, so an authored background never reaches SKILL.md, and toRulesyncSkill drops it on import. (Contrast: ClaudecodeCommand spreads the whole claudecode section and is lossless.)

6. permissions — the sandbox.* settings subtree is still not authorable

  • Upstream: sandbox.* sits next to permissions in .claude/settings.json and keeps growing — sandbox.network.strictAllowlist (2.1.219), sandbox.filesystem.disabled (2.1.216), sandbox.credentials (2.1.187), sandbox.allowAppleEvents (2.1.181), sandbox.network.deniedDomains (2.1.113). See sandboxing docs.
  • rulesync: unsupported. ClaudecodePermissionsOverrideSchema (src/types/permissions.ts) exposes only permission and permissions, and ClaudecodePermissions.fromRulesyncPermissions merges the override strictly into settings.permissions. There is no path from .rulesync/permissions.json to a top-level sandbox key. Claude Code permissions: support permissions.defaultMode and permissions.additionalDirectories (and note sandbox.*) #2129 explicitly deferred sandbox.* as out of scope for its first iteration and no follow-up was filed.

Proposed Follow-up

  1. Add canonical directoryAdded to HOOK_EVENTS + CLAUDE_HOOK_EVENTS and the DirectoryAdded mapping. Matcher support is undocumented — treat as no-matcher for now and note that in a code comment.
  2. Add args (string array), asyncRewake and continueOnBlock to the canonical HookDefinitionSchema, and extend CLAUDE_CONVERTER_CONFIG so args, shell, statusMessage, once, async, asyncRewake and continueOnBlock are emitted and imported for claudecode and claudecode-plugin. applyCommandPrefix / prefixDotRelativeCommandsOnly must not rewrite command when args is present — the exec form resolves command as an executable, not a shell string.
  3. Add beforeSubmitPrompt and stop to CLAUDE_NO_MATCHER_EVENTS.
  4. On generate, when the pattern is not *, map write/notebookeditEdit(...) and globRead(...) (merging with any existing edit/read entries), keeping the bare tool name for the * pattern. Import should stay tolerant of the legacy forms so existing .claude/settings.json files still round-trip.
  5. Add background: z.optional(z.boolean()) to both the Claude Code skill frontmatter schema and the claudecode section of the rulesync skill schema, and carry it in buildClaudecodeSkillFrontmatter (as a definedFields entry, since false is the meaningful value) and toRulesyncSkill. Applies to claudecode-plugin too, since ClaudecodePluginSkill extends ClaudecodeSkill.
  6. Add a sandbox key to ClaudecodePermissionsOverrideSchema (loose passthrough, mirroring permissions) and merge it into the top level of .claude/settings.json, with reverse routing in toRulesyncPermissions. Existing sandbox content in a user's settings must be preserved on merge.

Open questions / unconfirmed

  • DirectoryAdded matcher support is unconfirmed — the CHANGELOG announces the event but the hooks docs have no row for it yet.
  • Skill frontmatter display-name, default-enabled, fallback, metadata.* (CHANGELOG 2.1.186, 2026-06-22, "now accept kebab-case, snake_case, and camelCase") do not appear in the Claude Code skill frontmatter table; they are most likely Agent Skills open-standard / plugin-manifest keys, so whether rulesync should carry them for claudecode is unconfirmed.
  • shell, async, asyncRewake and statusMessage are documented today but no introduction version could be pinned in the CHANGELOG; only args and continueOnBlock have explicit 2.1.139 entries. The gap itself (dropped on generate) is confirmed either way.
  • .ignore / .rgignore / .worktreeinclude appear in CHANGELOG bug-fix lines but are not documented as a Claude Code ignore-configuration surface, so they are treated as not a gap.

References

Metadata

Metadata

Assignees

No one assigned

    Labels

    consideringenhancementNew feature or requestmaintainer-scrapRough notes for AI implementation. Not for human eyes.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions