wordlift-cli
Version:
WordLift CLI - Your AI SEO Assistant powered by Google Gemini. Agentic SEO workflows with Agent Skills support, WordLift MCP integration, knowledge graphs, and intelligent content optimization for modern content creators.
190 lines (156 loc) • 6.57 kB
JavaScript
/**
* WordLift CLI - A customized Gemini CLI for WordLift SEO workflows
* @license Apache-2.0
*/
const { spawn, execSync } = require('child_process');
const path = require('path');
const fs = require('fs');
// Get the actual working directory where the user ran the command
const userWorkingDir = process.cwd();
const packageDir = path.dirname(__dirname);
// Set up environment variables - prefer local .gemini if it exists
const userGeminiDir = path.join(userWorkingDir, '.gemini');
const configDir = fs.existsSync(userGeminiDir) ? userGeminiDir : path.join(packageDir, '.gemini');
process.env.GEMINI_CONFIG_DIR = configDir;
// Ensure WORDLIFT.md is available in the user's working directory
function ensureWordLiftContext() {
const packageWordLiftFile = path.join(packageDir, 'WORDLIFT.md');
const userWordLiftFile = path.join(userWorkingDir, 'WORDLIFT.md');
const userGeminiDir = path.join(userWorkingDir, '.gemini');
const userSettingsFile = path.join(userGeminiDir, 'settings.json');
// Only copy if user doesn't have their own WORDLIFT.md
if (fs.existsSync(packageWordLiftFile) && !fs.existsSync(userWordLiftFile)) {
try {
fs.copyFileSync(packageWordLiftFile, userWordLiftFile);
console.log('\x1b[2m📄 WordLift context file added to your project directory\x1b[0m');
} catch (error) {
console.warn('⚠️ Could not copy WORDLIFT.md context file:', error.message);
}
}
// Ensure local .gemini directory with context settings
if (!fs.existsSync(userGeminiDir)) {
try {
fs.mkdirSync(userGeminiDir, { recursive: true });
} catch (error) {
console.warn('⚠️ Could not create .gemini directory:', error.message);
return;
}
}
// Create or update local settings.json to reference WORDLIFT.md
if (!fs.existsSync(userSettingsFile)) {
try {
// Copy the package settings and modify for local use
const packageSettingsFile = path.join(packageDir, '.gemini', 'settings.json');
let packageSettings = {};
if (fs.existsSync(packageSettingsFile)) {
packageSettings = JSON.parse(fs.readFileSync(packageSettingsFile, 'utf8'));
}
// Ensure contextFileName points to local WORDLIFT.md
const localSettings = {
...packageSettings,
"contextFileName": "WORDLIFT.md"
};
fs.writeFileSync(userSettingsFile, JSON.stringify(localSettings, null, 2));
console.log('\x1b[2m⚙️ WordLift configuration added to your project\x1b[0m');
} catch (error) {
console.warn('⚠️ Could not create settings.json:', error.message);
}
}
}
// Check if this is an interactive session (no arguments)
const isInteractiveMode = process.argv.length === 2;
// Show WordLift branding for interactive mode
if (isInteractiveMode) {
console.log('\x1b[2m🤖 Agent WordLift - Your AI SEO Assistant\x1b[0m');
console.log(`\x1b[2m📁 Working directory: ${userWorkingDir}\x1b[0m`);
// Ensure WordLift context is available
ensureWordLiftContext();
// Start Gemini CLI - ASCII art should already be replaced during installation
const geminiProcess = spawn('npx', ['@google/gemini-cli'], {
stdio: 'inherit',
cwd: userWorkingDir,
env: {
...process.env,
WORDLIFT_PROJECT_DIR: userWorkingDir,
WORDLIFT_PACKAGE_DIR: packageDir,
GEMINI_CONFIG_DIR: configDir
}
});
geminiProcess.on('error', (error) => {
console.error('❌ Error running WordLift CLI:', error.message);
process.exit(1);
});
geminiProcess.on('close', (code) => {
process.exit(code);
});
return; // Exit early for interactive mode
}
// For non-interactive commands (with arguments), show branding only for help
if (process.argv.includes('--help') || process.argv.includes('-h')) {
ensureWordLiftContext(); // Deploy context file for help commands too
}
// Also ensure context file for version commands
if (process.argv.includes('--version') || process.argv.includes('-v')) {
ensureWordLiftContext(); // Deploy context file for version commands too
}
// Ensure WordLift context is available for all modes
ensureWordLiftContext();
// Use the standard Gemini CLI from npm
const geminiCommand = 'npx';
const geminiArgs = ['@google/gemini-cli'];
// Prepare arguments - pass through all command line arguments
let userArgs = process.argv.slice(2);
// If using -p (prompt mode), ensure MCP servers are allowed and WordLift context is loaded
const hasPromptFlag = userArgs.includes('-p') || userArgs.includes('--prompt');
const hasPromptInteractiveFlag = userArgs.includes('-i') || userArgs.includes('--prompt-interactive');
if (hasPromptFlag || hasPromptInteractiveFlag) {
// Add allowed MCP server names if not already specified
const hasAllowedMcpFlag = userArgs.some(arg => arg.startsWith('--allowed-mcp-server-names'));
if (!hasAllowedMcpFlag) {
userArgs.push('--allowed-mcp-server-names', 'wordlift');
}
// Add YOLO mode for prompt mode to auto-accept actions
const hasYoloFlag = userArgs.includes('-y') || userArgs.includes('--yolo');
if (!hasYoloFlag) {
userArgs.push('--yolo');
}
// Ensure WordLift context is loaded for prompt mode
console.log('\x1b[2m🤖 Agent WordLift - Loading SEO specialist context...\x1b[0m');
ensureWordLiftContext();
}
const args = [...geminiArgs, ...userArgs];
// Configure stdio for truly non-interactive -p mode
let stdioCfg = 'inherit';
if (hasPromptFlag && !hasPromptInteractiveFlag) {
// For -p mode, pipe stdin to avoid interactive prompts getting stuck
stdioCfg = ['pipe', 'inherit', 'inherit'];
}
// Spawn the Gemini CLI with WordLift configuration
const geminiProcess = spawn(geminiCommand, args, {
stdio: stdioCfg,
cwd: userWorkingDir, // Run Gemini CLI in the user's working directory
env: {
...process.env,
WORDLIFT_PROJECT_DIR: userWorkingDir,
WORDLIFT_PACKAGE_DIR: packageDir,
GEMINI_CONFIG_DIR: configDir
}
});
// For -p mode, immediately close stdin to prevent hanging on interactive prompts
if (hasPromptFlag && !hasPromptInteractiveFlag && geminiProcess.stdin) {
geminiProcess.stdin.end();
}
geminiProcess.on('error', (error) => {
if (error.code === 'ENOENT') {
console.error('❌ Gemini CLI not found. Please install it first:');
console.error(' npm install -g @google/gemini-cli');
console.error(' or run: npm run install-deps');
} else {
console.error('❌ Error running WordLift CLI:', error.message);
}
process.exit(1);
});
geminiProcess.on('close', (code) => {
process.exit(code);
});