Include decisions in hook output during plan mode#123
Merged
Conversation
Modify `internal/adapter/claude.go` to:
1. Add `PermissionMode string` field (json tag `permission_mode`) to `ClaudeHookInput`.
2. Add a `formatDecisions([]core.DecisionEntry) string` helper that renders decisions as markdown, following the same pattern as the CLI's `cmdDecisions` output (decision, rationale, alternatives, revisit_when) but under a `## Architectural Decisions` heading with bullet points.
3. In `HandleClaudeHook`, after the `core.Resolve` call, check if `hookInput.PermissionMode == "plan"` and `len(result.DecisionEntries) > 0`. If so, append the formatted decisions to the additional context string.
4. Adjust the early-return guard at line 96 — currently `if len(result.ContextEntries) == 0 { return nil }`. Change to also check `result.DecisionEntries` when in plan mode, so the hook produces output if either context or decisions matched.
Modify `internal/adapter/claude_test.go` to add table-driven tests covering:
- Plan mode Read with matching decisions → decisions appear in `additionalContext`
- Non-plan mode (empty string / other value) with matching decisions → no decisions in output
- Plan mode with no matching decisions → silent no-op (if no context either)
- Plan mode with both context and decisions → both sections appear in output
No changes to `claude_setup.go` (no new hook registration needed), `core/` (already returns decisions), or the CLI.
Asad-Jaffery
approved these changes
Apr 9, 2026
Asad-Jaffery
left a comment
There was a problem hiding this comment.
This is great!!
How I tested
- Cloning the sctx repo and checking out the plan-mode-decisions branch
- Building a local binary (sctx-plan)
- Temporarily pointing the hooks in
.claude/settings.local.jsonto use the local build instead of the installed sctx - Adding a debug log to the hook that captures the mode, file path, and any decisions returned
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #122
Why
Decisions — rejected alternatives, rationale, revisit conditions — are invisible to Claude Code right now. The hook only surfaces context entries. That's fine during normal editing, but during plan mode the agent is reading files to understand the codebase before writing anything. That's exactly when architectural decisions matter most. Without them, Claude might confidently propose migrating to something you already evaluated and ruled out.
Approach
The issue proposed adding a separate bash hook that shells out to
jqandsctx decisions. That works, but it means a new external dependency (jq), a second hook registration inclaude_setup.go, and two separate processes running on everyReadduring plan mode.Instead, this handles it natively inside
sctx hook. The Claude Code hook input already includespermission_mode— we just weren't reading it. NowHandleClaudeHookparses that field, and when the mode is"plan", it appends matching decisions to the sameadditionalContextstring alongside context entries. One hook call, one process, no new dependencies.The core engine already returns
DecisionEntriesfromResolve— we were just ignoring them in the adapter. So the actual change is small: read the field, check the mode, format and append.Decisions are scoped to the file being read (same glob matching as context entries) rather than dumping all decisions on every read. This keeps noise down as decision lists grow.
What changed
The early-return guard previously bailed when
len(result.ContextEntries) == 0. Now it checks both context and decisions (when in plan mode), so the hook produces output if either matched. Outside plan mode, behavior is identical to before — existing tests pass without modification since they send nopermission_mode.Review walkthrough
Single commit, single concern:
PermissionModetoClaudeHookInput, addsformatDecisionshelper, wires the conditional logic intoHandleClaudeHook, updates the empty-result guard, adds tests covering plan/non-plan/both/neither combinations, and updates docs (README + cli-reference) to reflect the new behavior.Developer metrics
Total duration: 7m 41s
Turns: 104
Tool calls: 76
Tokens: 1,660,710 input / 15,371 output
Resolves #122