fix(completion): insert fpath before compinit in .zshrc

The postinstall script was appending the fpath line at the end of .zshrc,
but compinit (called earlier by oh-my-zsh or directly) would have already
finished scanning. This caused zsh completion to silently fail for most
users.

Now the script detects the first compinit / oh-my-zsh source line and
inserts the fpath entry before it, ensuring completion works immediately.
This commit is contained in:
jackwener committed 2026-03-17 16:53:07 +08:00
1 parent eb2c3fdf89
commit 8263a06a85
1 file changed
+29 -8
+29 -8
View File
@@ -70,31 +70,52 @@ function ensureDir(dir) {
}
/**
* Ensure fpath contains the custom completions directory in .zshrc
* Ensure fpath contains the custom completions directory in .zshrc.
*
* Key detail: the fpath line MUST appear BEFORE the first `compinit` call,
* otherwise compinit won't scan our completions directory. This is critical
* for oh-my-zsh users (source $ZSH/oh-my-zsh.sh calls compinit internally).
*/
function ensureZshFpath(completionsDir, zshrcPath) {
const fpathLine = `fpath=(${completionsDir} $fpath)`;
const autoloadLine = `autoload -Uz compinit && compinit`;
const marker = '# opencli completion';
if (!existsSync(zshrcPath)) {
writeFileSync(zshrcPath, `${fpathLine}\n${autoloadLine}\n`, 'utf8');
writeFileSync(zshrcPath, `${marker}\n${fpathLine}\n${autoloadLine}\n`, 'utf8');
return;
}
const content = readFileSync(zshrcPath, 'utf8');
// Check if completions dir is already in fpath
// Already configured — nothing to do
if (content.includes(completionsDir)) {
return; // already configured
return;
}
// Append fpath configuration
let addition = `\n# opencli completion\n${fpathLine}\n`;
if (!content.includes('compinit')) {
addition += `${autoloadLine}\n`;
// Find the first line that triggers compinit (direct call or oh-my-zsh source)
const lines = content.split('\n');
let insertIdx = -1;
for (let i = 0; i < lines.length; i++) {
const trimmed = lines[i].trim();
// Skip comment-only lines
if (trimmed.startsWith('#')) continue;
if (/compinit/.test(trimmed) || /source\s+.*oh-my-zsh\.sh/.test(trimmed)) {
insertIdx = i;
break;
}
}
if (insertIdx !== -1) {
// Insert fpath BEFORE the compinit / oh-my-zsh source line
lines.splice(insertIdx, 0, marker, fpathLine);
writeFileSync(zshrcPath, lines.join('\n'), 'utf8');
} else {
// No compinit found — append fpath + compinit at the end
let addition = `\n${marker}\n${fpathLine}\n${autoloadLine}\n`;
appendFileSync(zshrcPath, addition, 'utf8');
}
}
// ── Main ───────────────────────────────────────────────────────────────────