Repository navigation
graft init --dry-run ignores suppression flags and omits files changed by init #329
Description
Summary
graft init --dry-run does not currently describe the operation that a real init would perform:
- The plan ignores
--no-global,--no-mcp, and--no-hooksfor non-Claude hosts. - It omits other files that
initcan create, modify, or delete, including the wiring stamp, retraction targets, and graph-build side effects.
This makes --dry-run unsafe as a preflight check, especially because its help text says it will "print every file init would touch."
Environment
@nanonets/graft0.16.0- macOS 26.6.2, arm64
- Node.js 24.17.0
- Also verified against current
mainat3a85a2f40e283ac0c9b617d9a4bead18d73c3c70
Reproduction: suppression flags are not reflected
Run these in a repository where the agents host is available:
graft init --dry-run --agents agents --no-build
graft init --dry-run --agents agents --no-build --no-global --no-mcp --no-hooksOn 0.16.0, both commands print the same planned writes. The second command still reports:
would write — this repo:
AGENTS.md fenced graft section
opencode.json mcp.graft
would write — your machine, affects ALL repos:
~/.codex/config.toml [mcp_servers.graft]
~/.codex/hooks/graft/graft-hooks.cjs post-edit hook shim
~/.codex/hooks.json SessionStart / UserPromptSubmit / PostToolUse / Stop
The real writer does apply these flags:
runHostsInit()skips MCP writes whenmcp === false.- It skips global Codex hooks when
hooks === falseorglobal === false. - MCP config writers receive the
globaloption.
The dry-run path does not. On current main, src/cli.ts calls planInit(repo, { home }), while src/hosts/plan.ts adds MCP and hook targets unconditionally.
Reproduction: the plan is incomplete
Even after the flag mismatch is fixed, the plan does not include all paths a real run may touch:
writeStamp()writesgraft/.cache/wiring-stamp.json, including with--no-build.runRetract(..., { apply: true, exclude: ids })can modify or delete wiring for previously selected hosts before the new wiring is written.- When the graph is missing and
--no-buildis not used,buildGraphIfMissing()runsgraft build, which creates the generatedgraft/tree and may update.gitignoreand.ignore.
The dry-run branch returns before these operations and does not report them.
Expected behavior
--dry-run should match the real operation for the same arguments:
- Omit targets disabled by
--no-global,--no-mcp, and--no-hooks. - Report files that would be modified or deleted by retraction.
- Report the wiring stamp and graph/build outputs when applicable, or explicitly label generated output directories/side effects if enumerating every generated file is impractical.
An alternative would be to narrow the command/help contract and clearly state that the output covers only agent wiring targets, but it should still apply the suppression flags accurately.
Suggested implementation direction
- Pass
global,mcp,hooks, andbuildinto the planning layer. - Build non-Claude host plans through the same predicates used by
runHostsInit(). - Reuse
planRetract()withapply: falseand the sameexclude/globaloptions to include removals. - Add the wiring stamp and graph-build side effects to the plan.
- Add a flag-matrix test comparing the planned target set with an instrumented real writer.
Related work
#118 and draft PR #124 address the narrower issue where Claude Code itself ignores --no-mcp / --no-hooks. PR #124 makes Claude targets aware of those two flags, but its non-Claude HOSTS.map(...) plan still adds MCP/hooks unconditionally, does not model --no-global, and does not include retraction, stamp, or build side effects. This report is intended to track the remaining dry-run contract rather than duplicate #118.
Activity
Make `graft init --dry-run` honor --no-global, --no-mcp and --no-hooks
djbclark commentedon Sep 26, 2026
Confirming the retraction gap on 0.20.0 with a concrete case, since it is the most surprising of the omissions listed here.
In a repo already wired for Claude Code (project
.mcp.json+ user-level~/.claude.json/~/.claude/settings.jsonhooks), adding one more host:prints only
The real run then removes, via
runRetract(..., { exclude: ids }): the project.mcp.json(deleted,graftwas its only key),mcpServers.graftin~/.claude.json, the four graft hooks andhelpers/graft-hooks.cjsunder~/.claude,~/.codex/hooks.jsonentries +~/.codex/hooks/graft/,[mcp_servers.graft]in~/.codex/config.toml, and~/.gemini/skills/graft/SKILL.md. None of those appear in the plan, and several arescope: 'global', so the dry run also fails to show that a repo-scoped command is about to edit files that affect every repo.planRetract()withapply: falseand the sameexclude/globaloptions, as suggested above, would have listed all of them. I filed the documentation / blast-radius side separately in #482 so this one can stay about the dry-run contract.