Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
5403f30
feat: add shell completion command for bash, zsh, and fish
404-Page-Found Jun 25, 2026
6616a03
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Jun 25, 2026
268d34d
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Jun 26, 2026
d20a9da
fix(completion): resolve subcommand detection before handling flags
404-Page-Found Jun 26, 2026
c394790
fix: remove unused variable and fix type narrowing in completion command
404-Page-Found Jun 27, 2026
34eb43b
docs: update AGENTS.md, CONTRIBUTING.md, and add sync note to complet…
404-Page-Found Jun 27, 2026
632a194
Merge remote-tracking branch 'origin/main' into 67-add-shell-completi…
404-Page-Found Jun 27, 2026
fe3ddf4
fix(completion): fix shell script generation bugs found in PR review
404-Page-Found Jun 27, 2026
21dae8a
refactor(completion): derive shell scripts from single SUBCOMMANDS re…
404-Page-Found Jun 27, 2026
233342b
fix(completion): address review findings on shell script generation
404-Page-Found Jun 27, 2026
6efcf1a
fix(completion): honor no-color.org spec for empty NO_COLOR value
404-Page-Found Jun 27, 2026
865ca06
fix(completion): apply remaining review findings to shell scripts
404-Page-Found Jun 27, 2026
f54a72c
test(completion): remove duplicate execFilePromisified alias
404-Page-Found Jun 29, 2026
2fbc1a7
fix(completion): correct zsh short option specs and deduplicate fish …
404-Page-Found Jun 29, 2026
29f98e2
fix(completion): add missing help subcommand to shell completions
404-Page-Found Jun 29, 2026
752d122
fix(completion): use double quotes in fish printf for escape sequences
404-Page-Found Jun 29, 2026
b9c8353
fix(completion): add missing end keyword in fish completion case block
404-Page-Found Jun 29, 2026
64fa578
fix(completion): remove erroneous value marker from zsh global option…
404-Page-Found Jun 29, 2026
aae46e6
test(completion): add drift-detection test for SUBCOMMANDS metadata
404-Page-Found Jun 29, 2026
86b3c93
refactor(completion): derive shell scripts from SUBCOMMANDS data inst…
404-Page-Found Jun 29, 2026
a694339
fix(completion): suggest global options when typing flags before subc…
404-Page-Found Jun 29, 2026
5ba5e16
fix(completion): remove trailing backslash from fish script continuat…
404-Page-Found Jun 29, 2026
7393773
fix(completion): skip _arguments for optionless zsh subcommands and a…
404-Page-Found Jun 29, 2026
bc5522e
refactor(completion): remove unused findSubcommand and make VALID_SHE…
404-Page-Found Jun 30, 2026
06dc7b6
fix(completion): scope bash variable, add batch dir completion, escap…
404-Page-Found Jun 30, 2026
7830ea7
fix(completion): make batch directory positional optional in zsh comp…
404-Page-Found Jun 30, 2026
d89054b
fix(completion): limit batch directory suggestions to first positiona…
404-Page-Found Jun 30, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
277 changes: 277 additions & 0 deletions src/commands/completion.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,277 @@
import pc from 'picocolors';

const BASH_SCRIPT = `#!/usr/bin/env bash
# bash completion for commit-echo -*- shell-script -*-

_commit_echo()
{
local cur prev commands
COMPREPLY=()
cur="\${COMP_WORDS[COMP_CWORD]}"
prev="\${COMP_WORDS[COMP_CWORD-1]}"
commands="init config suggest history batch completion help"

# Global options
global_opts="--yes --auto --no-color --help --version"

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

Suggestion: The completion scripts are missing several short-form flags (e.g., -y, -v, -m, -n) and long-form aliases (e.g., --auto) defined in the CLI. Please synchronize the scripts with the definitions in src/index.ts to ensure all valid options are suggested to the user.


if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${global_opts}" -- "\${cur}") )
return 0
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
fi

# If we're the second word, complete subcommands
if [[ "\${COMP_CWORD}" -eq 1 ]]; then
COMPREPLY=( $(compgen -W "\${commands}" -- "\${cur}") )
return 0
fi

local subcmd="\${COMP_WORDS[1]}"
case "\${subcmd}" in
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
suggest)
local suggest_opts="--commit --yes --verbose --model --stream --dry-run --no-commit --auto --help"
if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${suggest_opts}" -- "\${cur}") )
fi
;;
history)
local history_opts="--json --help"
if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${history_opts}" -- "\${cur}") )
fi
;;
batch)
local batch_opts="--recursive --yes --auto --help"
if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${batch_opts}" -- "\${cur}") )
fi
;;
init)
local init_opts="--install-hook --help"
if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${init_opts}" -- "\${cur}") )
fi
;;
completion)
local completion_opts="--help"
if [[ "\${cur}" == -* ]]; then
COMPREPLY=( $(compgen -W "\${completion_opts}" -- "\${cur}") )
else
COMPREPLY=( $(compgen -W "bash zsh fish" -- "\${cur}") )
fi
;;
esac

return 0
}

complete -F _commit_echo commit-echo
`;

const ZSH_SCRIPT = `#compdef commit-echo
# zsh completion for commit-echo -*- shell-script -*-

_commit_echo() {
local -a commands
commands=(
'init:Run interactive setup wizard'
'config:View current configuration'
'suggest:Generate commit suggestions'
'history:View learned style profile and recent commit history'
'batch:Process multiple git repositories in batch mode'
'completion:Generate shell completion scripts'
)
Comment on lines +290 to +292

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Zsh omits the help subcommand from top-level completions.

Bash and fish both advertise help, but the zsh commands list stops at completion, so commit-echo <TAB> exposes a different command set on zsh than on the other supported shells.

Suggested fix
   commands=(
     'init:Run interactive setup wizard'
     'config:View current configuration'
     'suggest:Generate commit suggestions'
     'history:View learned style profile and recent commit history'
     'batch:Process multiple git repositories in batch mode'
     'completion:Generate shell completion scripts'
+    'help:Display help'
   )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
commands=(
'init:Run interactive setup wizard'
'config:View current configuration'
'suggest:Generate commit suggestions'
'history:View learned style profile and recent commit history'
'batch:Process multiple git repositories in batch mode'
'completion:Generate shell completion scripts'
)
commands=(
'init:Run interactive setup wizard'
'config:View current configuration'
'suggest:Generate commit suggestions'
'history:View learned style profile and recent commit history'
'batch:Process multiple git repositories in batch mode'
'completion:Generate shell completion scripts'
'help:Display help'
)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/completion.ts` around lines 77 - 84, The zsh completion command
list in completion.ts is missing the top-level help subcommand, causing an
inconsistent set of suggestions compared with the bash and fish completion
generators. Update the commands array used by the zsh completion script to
include help alongside init, config, suggest, history, batch, and completion so
commit-echo tab completion advertises the same commands across shells.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚪ LOW RISK

Suggestion: Add 'help:Display help' to the commands array in the Zsh completion script. The 'help' subcommand is included in the Bash and Fish versions but is missing here, which leads to inconsistent behavior across shells.


_arguments -C \\
'--yes[Automatically accept the first suggestion and commit without prompts]' \\
'--auto[Alias for --yes]' \\
'--no-color[Disable colored output]' \\
'--version[Output the version]' \\
'--help[Display help]' \\
'1:command:->command' \\
'*::args:->args'

case \$state in
command)
_describe 'command' commands
;;
args)
case \$words[1] in
suggest)
_arguments \\
'--commit[Commit the selected suggestion]' \\
'--yes[Automatically select the first suggestion and skip prompts]' \\
'--verbose[Print diagnostic information about the suggestion request]' \\
'--model[Override the configured LLM model]:model:' \\
'--stream[Stream suggestions as they are generated]' \\
'--dry-run[Show the LLM input without generating suggestions]' \\
'--no-commit[Deprecated alias]' \\
'--auto[Alias for --yes]' \\
'--help[Display help for suggest]'
;;
history)
_arguments \\
'--json[Output the style profile and recent commits as JSON]' \\
'--help[Display help for history]'
;;
batch)
_arguments \\
'--recursive[Recursively search subdirectories for git repos]' \\
'--yes[Automatically accept the first suggestion and commit without prompts]' \\
'--auto[Alias for --yes]' \\
'--help[Display help for batch]'
;;
init)
_arguments \\
'--install-hook[Install a prepare-commit-msg hook in the current repository]' \\
'--help[Display help for init]'
;;
completion)
_arguments \\
'--help[Display help for completion]' \\
'1:shell:(bash zsh fish)'
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
;;
esac
;;
esac
}

_compdef _commit_echo commit-echo

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 HIGH RISK

The command _compdef is a typo and will cause the script to fail when sourced. The correct Zsh command for associating a completion function is compdef.

Suggested change
_compdef _commit_echo commit-echo
compdef _commit_echo commit-echo

`;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 HIGH RISK

The registration command uses _compdef, but the standard Zsh command is compdef. Using the underscored version (which is typically an internal function name) will result in a 'command not found' error when the script is sourced directly by a user.


const FISH_SCRIPT = `# fish completion for commit-echo -*- shell-script -*-

# Helper: complete options for a subcommand
function __commit_echo_complete_options
set -l cmd (commandline -opc)
set -l subcmd $cmd[2]
switch $subcmd
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
case suggest
printf '%s\\t%s\\n' \\
'--commit\\tCommit the selected suggestion' \\
'--yes\\tAutomatically select the first suggestion' \\
'--verbose\\tPrint diagnostic information' \\
'--model\\tOverride the configured LLM model' \\
'--stream\\tStream suggestions as they are generated' \\
'--dry-run\\tShow the LLM input without generating suggestions' \\
'--no-commit\\tDeprecated alias' \\
'--auto\\tAlias for --yes' \\
'--help\\tDisplay help'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Fish completion entries are emitted with the wrong printf shape.

These arguments already contain option<TAB>description, but the format string still expects two %s values. Fish will pair adjacent entries onto one line, so the generated script does not expose valid candidates/descriptions. The same bug repeats in the later printf blocks on Lines 178-196, 201-208, and 231-236.

Suggested fix
-      printf '%s\t%s\n' \
+      printf '%s\n' \
         '--commit\tCommit the selected suggestion' \
         '--yes\tAutomatically select the first suggestion' \
         '--verbose\tPrint diagnostic information' \

Apply the same format-string change to the other fish completion printf blocks in this file.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/commands/completion.ts` around lines 167 - 176, The fish completion
generation in completion.ts is using the wrong printf shape: each entry already
includes option and description separated by a tab, so the format string should
emit one string per line instead of pairing adjacent arguments. Update the fish
completion printf blocks in the completion generation logic, including the block
that emits --commit through --help and the other repeated blocks later in the
file, by changing the format to match the single tab-delimited entries so Fish
parses each candidate/description correctly.

case history
printf '%s\\t%s\\n' \\
'--json\\tOutput the style profile and recent commits as JSON' \\
'--help\\tDisplay help'
case batch
printf '%s\\t%s\\n' \\
'--recursive\\tRecursively search subdirectories for git repos' \\
'--yes\\tAutomatically accept the first suggestion and commit without prompts' \\
'--auto\\tAlias for --yes' \\
'--help\\tDisplay help'
case init
printf '%s\\t%s\\n' \\
'--install-hook\\tInstall a prepare-commit-msg hook' \\
'--help\\tDisplay help'
case completion
printf '%s\\t%s\\n' \\
'--help\\tDisplay help'
end
Comment thread
coderabbitai[bot] marked this conversation as resolved.
Outdated
end

function __commit_echo_subcommands
printf '%s\\t%s\\n' \\
'init\\tRun interactive setup wizard' \\
'config\\tView current configuration' \\
'suggest\\tGenerate commit suggestions' \\
'history\\tView learned style profile and history' \\
'batch\\tProcess multiple git repositories' \\
'completion\\tGenerate shell completion scripts' \\
'help\\tDisplay help'
end

function __commit_echo_completions
set -l cmd (commandline -opc)
set -l argc (count $cmd)

# If we are at position 1 (just the program name), suggest subcommands
if test $argc -eq 1
__commit_echo_subcommands
return
end

# If the previous token is a flag with a value (like --model), don't complete
switch $cmd[-1]
case '--model'
return
end

# If the current token starts with -, suggest global + subcommand options
set -l token (commandline -ct)
if string match -q -- '-*' $token
# Global options
printf '%s\\t%s\\n' \\
'--yes\\tAutomatically accept the first suggestion' \\
'--auto\\tAlias for --yes' \\
'--no-color\\tDisable colored output' \\
'--version\\tOutput the version' \\
'--help\\tDisplay help'
# Subcommand options
__commit_echo_complete_options
return
end

# Otherwise, suggest subcommands
__commit_echo_subcommands

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

In the Fish completion script, subcommands are suggested even when one has already been specified. Only suggest subcommands if no subcommand has been identified yet.

Wrap the call to __commit_echo_subcommands at the end of the __commit_echo_completions function in an if test -z "$subcmd" block.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 MEDIUM RISK

The script suggests all subcommands even when one has already been selected. It should only suggest subcommands if the $subcmd variable is empty to avoid polluting the suggestion list with redundant commands.

end

complete -c commit-echo -f -a '(__commit_echo_completions)'
`;

export type SupportedShell = 'bash' | 'zsh' | 'fish';

const VALID_SHELLS: SupportedShell[] = ['bash', 'zsh', 'fish'];

/** Returns the completion script for the given shell. */
function getCompletionScript(shell: SupportedShell): string {
switch (shell) {
case 'bash':
return BASH_SCRIPT;
case 'zsh':
return ZSH_SCRIPT;
case 'fish':
return FISH_SCRIPT;
}
}

/** Prints a help message showing available shell targets. */
function printHelp(): void {
console.log(pc.bold('Generate shell completion scripts for commit-echo.\n'));
console.log(pc.cyan('Usage:'));
console.log(' commit-echo completion <shell>\n');
console.log(pc.cyan('Supported shells:'));
console.log(` ${pc.green('bash')} - Bash completion script`);
console.log(` ${pc.green('zsh')} - Zsh completion script`);
console.log(` ${pc.green('fish')} - Fish completion script\n`);
console.log(pc.cyan('Examples:'));
console.log(` ${pc.dim('# Bash')}`);
console.log(' commit-echo completion bash >> ~/.bashrc\n');
console.log(` ${pc.dim('# Zsh')}`);
console.log(' commit-echo completion zsh > ~/.zfunc/_commit-echo\n');
console.log(` ${pc.dim('# Fish')}`);
console.log(' commit-echo completion fish > ~/.config/fish/completions/commit-echo.fish');
}

/** The completion command action: outputs the requested shell script. */
export function completionCommand(shell?: string): void {
if (!shell) {
printHelp();
return;
}

const normalized = shell.toLowerCase() as SupportedShell;

if (!VALID_SHELLS.includes(normalized)) {
console.error(pc.red(`Unsupported shell: "${shell}". Supported shells: ${VALID_SHELLS.join(', ')}`));
process.exit(1);
}

process.stdout.write(getCompletionScript(normalized));
}
10 changes: 10 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { suggestCommand } from './commands/suggest.js';
import { historyCommand } from './commands/history.js';
import { configCommand } from './commands/config.js';
import { batchCommand } from './commands/batch.js';
import { completionCommand } from './commands/completion.js';
import { getAvailableTemplateVars } from './llm/prompt.js';
import { runPostCommitHook, runPrepareCommitMsgHook } from './git/hook.js';

Expand Down Expand Up @@ -46,6 +47,7 @@ ${pc.dim('Examples:')}
${pc.cyan('commit-echo batch')} Process all git repos in current directory
${pc.cyan('commit-echo batch --recursive')} Search subdirectories for repos
${pc.cyan('commit-echo batch --yes')} Auto-commit repos with staged changes
${pc.cyan('commit-echo completion')} Generate shell completion scripts

${pc.dim('Custom prompt template variables:')}
${getAvailableTemplateVars()
Expand Down Expand Up @@ -114,6 +116,14 @@ program
});
});

program
.command('completion')
.description('Generate shell completion scripts for bash, zsh, and fish')
.argument('[shell]', 'Target shell: bash, zsh, or fish')
.action((shell?: string) => {
completionCommand(shell);
});

const hookCommand = new Command('hook')
.description('Internal Git hook entry point')
.argument('<hook-name>', 'Git hook name')
Expand Down
Loading