fix(coding-agent): expand slash commands in non-interactive runs - #3898
Merged
Yeachan-Heo merged 3 commits intoAug 6, 2026
Merged
Conversation
`gjc -p "/init"` reached the model as the literal string `/init`: no command body was injected, no AGENTS.md was written, and the model still answered "Repository initialized." A silent no-op that reports success is worse than a refusal. AgentSession#prompt expands leading-slash text from the list installed by setSlashCommands, and only interactive mode called it — it does so while building autocomplete, which print mode has none of. Print mode now performs the same load before its first prompt, so bundled (`/init`) and file-based project commands behave identically in both modes. Lore-id: 4b91e7c2 Constraint: a broken project command file must not kill a non-interactive run -- user/project templates parse at "warn", only in-binary content is "fatal" Constraint: load once before the first prompt, so follow-up messages in the same run share the list Rejected: expand only for known bundled names | project commands would stay silently inert in -p Rejected: swallow load failures | hides genuine config errors and interactive mode does not Confidence: high Scope-risk: narrow Reversibility: easy Tested: /init from source in a temp repo -- command body injected, subagents dispatched, AGENTS.md regenerated with hand-written rules preserved; new unit test fails with the fix reverted Not-tested: --mode json event-stream consumers that assume no pre-prompt filesystem access (410 mode tests pass)
yazzang-homelab
force-pushed
the
fix/print-mode-slash-commands
branch
from
August 5, 2026 23:24
54867c2 to
7fe8031
Compare
Contributor
Author
|
Rebased onto current Re-verified on the new base: |
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.
What
gjc -p "/init"(and any other slash command in non-interactive mode) reached the model as literal text. This loads the same slash-command list interactive mode uses, before print mode sends its first prompt.Why
AgentSession#promptexpands leading-slash input from#slashCommands, which is populated only bysetSlashCommands(). The single caller isinteractive-mode.ts:refreshSlashCommandState(), invoked while building autocomplete. Print mode has no autocomplete, so the list stayed at its[]default andexpandSlashCommand()returned the raw text.The failure is silent and misreports success. Measured before the fix, in a temp repo containing an
AGENTS.md:Session log for that run:
init.mdbody injected 0 times,write/editcalls 0,AGENTS.mdmd5 unchanged. Nothing ran; the model improvised an answer from the literal string/init.After the fix, same repo and prompt: command body injected, 3
task+ 4subagentdispatches (the/initprompt asks for parallel role agents),AGENTS.mdregenerated 1,257B → 3,699B. The hand-written section I planted (<!-- SENTINEL-DO-NOT-LOSE-8f3a2b -->plus a Korean deployment rule) survived at lines 65/67, so this does not turn/initinto a destructive overwrite in practice.Two failure modes were checked explicitly and do not apply:
-prun in CI.loadSlashCommandsparses user/project templates atlevel: "warn"(logs, recovers); only in-binary native/embedded content is"fatal". So a malformed repo-localcommands/*.mddegrades, it does not throw./is unaffected.expandSlashCommandtakes the token up to the first space as the command name, sogjc -p "/tmp/x.txt 를 읽어줘"finds no match and passes through unchanged.Cost:
loadSlashCommandsmeasured at 23–64ms cold, 2ms warm, against a ~7s minimal-prun.Testing
bun test packages/coding-agent/test/silent-abort-print-mode.test.ts— 19 pass. New case asserts the handover happens exactly once, before the first prompt, and that the handed-over list contains the embeddedinitcommand. Reverting the source change makes it fail (18 pass / 1 fail), so it pins the regression rather than the implementation.sessionManager.getCwd()orsetSlashCommands; both are now modelled, since the real contract needs them. Ordering is asserted in the new test rather than inlifecycle, which the disposal/EPIPE cases compare byte-for-byte.bun test packages/coding-agent/test/modes packages/coding-agent/test/silent-abort-print-mode.test.ts— 410 pass / 0 fail.check-visible-definitionsexit 0,verify-g002-gatesexit 0,default-gjc-definitions.test.ts28 pass.bunx biome checkclean on all three touched files.bun run check:typesNOT completed:tsc(typescript-go) aborts withfatal error: runtime: name offset out of rangeon this host at unmodifieddevas well, so it is an environment fault, not this change. Left to CI.GJC verdict
devbun checkpasses — biome clean, targeted + mode suites green; tsgo typecheck crashes at baseline on this host, left to CI