mirror of
https://github.com/Fission-AI/OpenSpec.git
synced 2026-09-14 20:16:53 +08:00
1adf3cea88
* shell completions for zsh * after code review changes * expose only postinstall.js script * Replace _openspec "$@" with compdef in zsh-generator.ts to prevent execution during load * Update test/commands/completion.test.ts Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> * - Fix dispatcher to use $words instead of $line for subcommand routing - Add __complete endpoint with tab-separated output for safe parsing - Replace brittle awk parsing with __complete in completion helpers - Add uninstall confirmation prompt with --yes flag to skip - Prefer $ZSH env var for Oh My Zsh detection before dir check - Add fpath verification guidance for OMZ installations - Update cli-completion spec to document generate subcommand * improve shell detection and installation handling - Return structured result from detectShell() with shell and detected name - Detect already-installed completions and skip reinstall - Add update detection with automatic backup of previous version - Add debug logging to silent catch blocks for diagnostics - Quote fpath directories to handle paths with spaces - Verify Oh My Zsh fpath configuration and add to .zshrc if needed - Show helpful error for detected but unsupported shells - Update all tests for new detection API --------- Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> Co-authored-by: Tabish Bidiwale <30385142+TabishB@users.noreply.github.com>
148 lines
4.1 KiB
JavaScript
148 lines
4.1 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
/**
|
|
* Postinstall script for auto-installing shell completions
|
|
*
|
|
* This script runs automatically after npm install unless:
|
|
* - CI=true environment variable is set
|
|
* - OPENSPEC_NO_COMPLETIONS=1 environment variable is set
|
|
* - dist/ directory doesn't exist (dev setup scenario)
|
|
*
|
|
* The script never fails npm install - all errors are caught and handled gracefully.
|
|
*/
|
|
|
|
import { promises as fs } from 'fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'url';
|
|
|
|
const __filename = fileURLToPath(import.meta.url);
|
|
const __dirname = path.dirname(__filename);
|
|
|
|
/**
|
|
* Check if we should skip installation
|
|
*/
|
|
function shouldSkipInstallation() {
|
|
// Skip in CI environments
|
|
if (process.env.CI === 'true' || process.env.CI === '1') {
|
|
return { skip: true, reason: 'CI environment detected' };
|
|
}
|
|
|
|
// Skip if user opted out
|
|
if (process.env.OPENSPEC_NO_COMPLETIONS === '1') {
|
|
return { skip: true, reason: 'OPENSPEC_NO_COMPLETIONS=1 set' };
|
|
}
|
|
|
|
return { skip: false };
|
|
}
|
|
|
|
/**
|
|
* Check if dist/ directory exists
|
|
*/
|
|
async function distExists() {
|
|
const distPath = path.join(__dirname, '..', 'dist');
|
|
try {
|
|
const stat = await fs.stat(distPath);
|
|
return stat.isDirectory();
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Detect the user's shell
|
|
*/
|
|
async function detectShell() {
|
|
try {
|
|
const { detectShell } = await import('../dist/utils/shell-detection.js');
|
|
const result = detectShell();
|
|
return result.shell;
|
|
} catch (error) {
|
|
// Fail silently if detection module doesn't exist
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Install completions for the detected shell
|
|
*/
|
|
async function installCompletions(shell) {
|
|
try {
|
|
const { CompletionFactory } = await import('../dist/core/completions/factory.js');
|
|
const { COMMAND_REGISTRY } = await import('../dist/core/completions/command-registry.js');
|
|
|
|
// Check if shell is supported
|
|
if (!CompletionFactory.isSupported(shell)) {
|
|
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
return;
|
|
}
|
|
|
|
// Generate completion script
|
|
const generator = CompletionFactory.createGenerator(shell);
|
|
const script = generator.generate(COMMAND_REGISTRY);
|
|
|
|
// Install completion script
|
|
const installer = CompletionFactory.createInstaller(shell);
|
|
const result = await installer.install(script);
|
|
|
|
if (result.success) {
|
|
// Show success message based on installation type
|
|
if (result.isOhMyZsh) {
|
|
console.log(`✓ Shell completions installed`);
|
|
console.log(` Restart shell: exec zsh`);
|
|
} else if (result.zshrcConfigured) {
|
|
console.log(`✓ Shell completions installed and configured`);
|
|
console.log(` Restart shell: exec zsh`);
|
|
} else {
|
|
console.log(`✓ Shell completions installed to ~/.zsh/completions/`);
|
|
console.log(` Add to ~/.zshrc: fpath=(~/.zsh/completions $fpath)`);
|
|
console.log(` Then: exec zsh`);
|
|
}
|
|
} else {
|
|
// Installation failed, show tip for manual install
|
|
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
}
|
|
} catch (error) {
|
|
// Fail gracefully - show tip for manual install
|
|
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Main function
|
|
*/
|
|
async function main() {
|
|
try {
|
|
// Check if we should skip
|
|
const skipCheck = shouldSkipInstallation();
|
|
if (skipCheck.skip) {
|
|
// Silent skip - no output
|
|
return;
|
|
}
|
|
|
|
// Check if dist/ exists (skip silently if not - expected during dev setup)
|
|
if (!(await distExists())) {
|
|
return;
|
|
}
|
|
|
|
// Detect shell
|
|
const shell = await detectShell();
|
|
if (!shell) {
|
|
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
return;
|
|
}
|
|
|
|
// Install completions
|
|
await installCompletions(shell);
|
|
} catch (error) {
|
|
// Fail gracefully - never break npm install
|
|
// Show tip for manual install
|
|
console.log(`\nTip: Run 'openspec completion install' for shell completions`);
|
|
}
|
|
}
|
|
|
|
// Run main and handle any unhandled errors
|
|
main().catch(() => {
|
|
// Silent failure - never break npm install
|
|
process.exit(0);
|
|
});
|