Conversation
Implements the kubectl-style passthrough promised in PROJECT_SPEC §4.3 and specified in the context-layer design §3.6 — the subset that does not depend on refs, aliases or packs, so it can land on its own. Builtins always win: dispatch is attempted only for a word cobra itself could not resolve, so an extension can never shadow a cu command, and a shadowed extension is still runnable directly as cu-<word>. There is no PATH scan at startup — a single exec.LookPath once a word is known to be unresolvable — so the normal path costs nothing. The command word is found by reading flag arity from the flag set rather than guessing, so `cu --config x worklog` dispatches to worklog and not to x. Everything after the word is forwarded verbatim, including flags that look like cu's own; cu's own preceding flags are not. The child inherits CU_CONFIG_DIR, CU_WORKSPACE and CU_PROJECT_CONFIG, and its exit status is propagated unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aqbmccWm1tqttmBUCR5rv ClickUp: 86dxbeqyt
CI's gosec caught a real one. exec.LookPath treats any word containing a separator as a *path* rather than a PATH search, so `cu ../../tmp/evil` would have resolved and run an executable that was never on PATH at all. Extension words are now constrained to ^[a-zA-Z0-9][a-zA-Z0-9_-]*$, so an extension is found on PATH, by name, or not at all; anything else falls through to cobra's unknown-command error. Verified live that ./evil and ../outside/evil are both refused while normal dispatch still works. The suppression comment was also wrong: it named G204 while the rule that fires is G702 (taint analysis), so it never applied to begin with. It now names both and explains why the call is safe rather than asserting it. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_014aqbmccWm1tqttmBUCR5rv ClickUp: 86dxbeqyt
4 tasks
This branch has not been deployed
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.
Summary
Implements the kubectl-style PATH passthrough promised in PROJECT_SPEC §4.3 and specified in the context-layer design §3.6 — the subset that does not depend on refs, aliases or packs, so it can land on its own.
Any executable named
cu-<word>onPATHbecomescu <word>:Guarantees
cu-versionon PATH:cu versionstill runs the builtin, whilecu-versionremains runnable directly — the escape hatch §3.6 calls for.exec.LookPathonly once a word is known to be unresolvable.The part most worth reviewing
Finding the command word has a sharp edge:
cu --config x worklogmust dispatch toworklog, not tox. Guessing "first argument not starting with-" gets that wrong. Flag arity is read from the flag set instead, so value-taking flags consume their value and boolean flags do not. An unknown flag is assumed not to take a value, so it cannot swallow the command word.Everything after the word is forwarded verbatim — including flags that look like cu's own, since they belong to the extension — while cu's own preceding flags are not.
Environment handed to the child
CU_CONFIG_DIR,CU_WORKSPACE,CU_PROJECT_CONFIG, per §3.6, so an extension does not have to rediscover cu's context. Config is loaded explicitly here because dispatch happens beforePersistentPreRunE; a load failure is non-fatal and simply means less context.Not included
cu which(shadowing/non-executable warnings) belongs with the wider context-layer work, as do aliases, refs and packs. This is only §5's step 4.Tests
TestFirstCommandWordcovers bare words, long and shorthand flags with values in both--flag valueand--flag=valueforms, boolean flags, unknown flags, and--.TestArgsAftercovers forwarding and the non-forwarding of cu's own flags.TestConfigFileFromincludes a dangling--configso it cannot panic.TestTryExtensionLeavesBuiltinsAloneasserts resolvable builtins return normally rather than being routed out.Manually verified: dispatch with args, a global flag before the word, builtin precedence, the direct-invocation escape hatch,
CU_PROJECT_CONFIGinside a project, and exit-code propagation.Checklist
./scripts/ci.shpasses locally — excepterrcheck, which reports the same pre-existing findings onmain, none in files this PR touches