doclyft
Version:
CLI for DocLyft - Interactive documentation generator with hosted documentation support
449 lines (448 loc) âĸ 19.3 kB
JavaScript
"use strict";
/**
* Interactive CLI session service
* Provides an interactive session loop with slash commands
*/
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.InteractiveSession = void 0;
const prompts_1 = __importDefault(require("prompts"));
const chalk_1 = __importDefault(require("chalk"));
const auth_1 = __importStar(require("../middleware/auth"));
const slash_command_handler_1 = require("./slash-command-handler");
const visual_1 = require("../utils/visual");
class InteractiveSession {
constructor() {
this.isRunning = false;
this.commandHandler = new slash_command_handler_1.SlashCommandHandler();
this.sessionStartTime = new Date();
}
/**
* Start the interactive session
*/
async start(options = {}) {
if (this.isRunning) {
console.log(chalk_1.default.yellow('Session is already running.'));
return;
}
this.isRunning = true;
try {
// Show welcome banner
if (options.showWelcome !== false) {
this.showWelcomeBanner();
}
// Auto-login if needed and requested
if (options.autoLogin) {
await this.ensureAuthenticated();
}
// Start the command loop
await this.commandLoop();
}
catch (error) {
if (error instanceof Error && error.message === 'USER_EXIT') {
// User intentionally exited - already handled in command loop
}
else {
console.log('\n' + (0, visual_1.createBox)([
'đĨ Session Error',
'',
error instanceof Error ? error.message : 'Unknown session error occurred',
'',
'đ§ What you can do:',
' âĸ Restart the interactive session',
' âĸ Check your network connection',
' âĸ Try individual commands instead'
], {
title: 'đ¨ Critical Error',
style: 'double',
color: 'red',
padding: 1,
width: 70
}));
}
}
finally {
this.isRunning = false;
}
}
/**
* Stop the interactive session
*/
stop() {
this.isRunning = false;
}
/**
* Show welcome banner and session info
*/
showWelcomeBanner() {
console.log('\n');
console.log((0, visual_1.createHeader)('đ DocLyft Interactive CLI', { style: 'banner', color: 'blue', width: 75 }));
// Welcome message in a beautiful box
console.log((0, visual_1.createBox)([
'đ¯ Welcome to the enhanced DocLyft CLI experience!',
'',
'⨠Features:',
' âĸ Type "/" to discover all available commands',
' âĸ Smart command suggestions as you type',
' âĸ Beautiful visual feedback throughout',
' âĸ Enhanced error handling and guidance'
], {
title: 'đĄ Getting Started',
style: 'single',
color: 'cyan',
padding: 1,
width: 75
}));
// Check authentication status
const authInfo = auth_1.default.getAuthInfo();
const authData = [];
if (authInfo.token && authInfo.user_email) {
authData.push({ key: 'đ Status', value: (0, visual_1.createStatus)('success', 'Authenticated').replace('â
', '') });
authData.push({ key: 'đ¤ Email', value: authInfo.user_email });
authData.push({ key: 'đ¯ Ready', value: 'All features available' });
}
else {
authData.push({ key: 'đ Status', value: (0, visual_1.createStatus)('warning', 'Not authenticated').replace('â ī¸ ', '') });
authData.push({ key: 'đ Login', value: 'Type /login to authenticate' });
authData.push({ key: 'đ API Key', value: 'Get yours at: https://doclyft.com/dashboard/api-keys' });
}
console.log('\n' + (0, visual_1.createTable)(authData, {
title: 'đ Authentication Status',
keyColor: 'blue',
valueColor: 'white',
separatorColor: 'gray'
}));
// Quick start commands
const quickCommands = [
{ name: '/help', description: 'Show comprehensive command help' },
{ name: '/status', description: 'View detailed system status' },
{ name: '/analyze repo', description: 'Analyze a GitHub repository' },
{ name: '/generate readme', description: 'Generate professional README' },
{ name: '/', description: 'Show all available commands' }
];
console.log('\n' + (0, visual_1.createCommandList)(quickCommands, {
title: '⥠Quick Start Commands',
nameColor: 'green',
descColor: 'gray'
}));
console.log('\n' + (0, visual_1.createDivider)('Ready to get started!', {
color: 'cyan',
char: 'â',
width: 75
}));
console.log('\n');
}
/**
* Main command loop
*/
async commandLoop() {
while (this.isRunning) {
try {
const response = await (0, prompts_1.default)({
type: 'text',
name: 'command',
message: chalk_1.default.cyan.bold('doclyft') + chalk_1.default.gray(' âē '),
validate: (value) => {
if (!value.trim()) {
return chalk_1.default.yellow('đĄ Please enter a command (try "/" to see all commands)');
}
return true;
},
format: (value) => value.trim()
});
if (!response.command) {
// User cancelled (Ctrl+C)
console.log('\n' + (0, visual_1.createStatus)('info', 'Session interrupted. Goodbye! đ'));
throw new Error('USER_EXIT');
}
const command = response.command.trim();
// Handle exit commands
if (command === '/exit' || command === '/quit' || command === 'exit' || command === 'quit') {
console.log('\n' + (0, visual_1.createStatus)('success', 'Thanks for using DocLyft CLI! đ'));
throw new Error('USER_EXIT');
}
// Show command list when user types just "/" or show suggestions for partial commands
if (command === '/') {
console.log('');
this.showAvailableCommands();
console.log('');
continue; // Don't process, just show commands and continue loop
}
else if (command.startsWith('/') && command.length > 1) {
const suggestions = this.getSuggestions(command);
if (suggestions.length > 0 && !suggestions.some(s => s.value === command)) {
console.log('');
this.showFilteredCommands(command, suggestions);
console.log('');
}
}
// Process the command
await this.processCommand(command);
// Add spacing between commands
console.log('');
}
catch (error) {
if (error instanceof Error && error.message === 'USER_EXIT') {
throw error; // Re-throw to exit the loop
}
// Enhanced error display
console.log('\n' + (0, visual_1.createBox)([
'â Command Error',
'',
error instanceof Error ? error.message : 'Unknown error occurred',
'',
'đĄ Suggestions:',
' âĸ Check command syntax',
' âĸ Type /help for available commands',
' âĸ Use / to see command suggestions'
], {
title: 'â ī¸ Error',
style: 'single',
color: 'red',
padding: 1,
width: 60
}));
console.log('');
}
}
}
/**
* Get command suggestions for autocomplete
*/
getCommandSuggestions() {
return [
{ title: '/help', value: '/help', description: 'Show all available commands' },
{ title: '/status', value: '/status', description: 'Show current status' },
{ title: '/login', value: '/login', description: 'Authenticate with DocLyft' },
{ title: '/logout', value: '/logout', description: 'Remove authentication' },
{ title: '/analyze repo', value: '/analyze repo ', description: 'Analyze a GitHub repository' },
{ title: '/analyze list', value: '/analyze list', description: 'List analyzed repositories' },
{ title: '/analyze select', value: '/analyze select', description: 'Select a previous analysis' },
{ title: '/generate readme', value: '/generate readme', description: 'Generate README file' },
{ title: '/generate docs', value: '/generate docs', description: 'Generate full documentation' },
{ title: '/push', value: '/push', description: 'Push documentation to GitHub' },
{ title: '/repos', value: '/repos', description: 'List your GitHub repositories' },
{ title: '/config list', value: '/config list', description: 'List configuration' },
{ title: '/config set', value: '/config set ', description: 'Set configuration value' },
{ title: '/config get', value: '/config get ', description: 'Get configuration value' },
{ title: '/history', value: '/history', description: 'View activity history' },
{ title: '/github-token', value: '/github-token', description: 'Set GitHub token' },
{ title: '/test', value: '/test', description: 'Test GitHub connectivity' },
{ title: '/analyses', value: '/analyses', description: 'View previous analyses' },
{ title: '/exit', value: '/exit', description: 'Exit interactive session' }
];
}
/**
* Get command suggestions based on partial input
*/
getSuggestions(input) {
const commands = this.getCommandSuggestions();
return commands.filter(cmd => cmd.value.toLowerCase().startsWith(input.toLowerCase()));
}
/**
* Show all available commands in beautiful categorized display
*/
showAvailableCommands() {
console.log((0, visual_1.createHeader)('đ Available Commands', { style: 'decorated', color: 'blue', width: 70 }));
const commands = this.getCommandSuggestions();
// Group commands by category with icons
const categories = {
'đ Authentication': commands.filter(cmd => ['/login', '/logout'].includes(cmd.value)),
'đ Analysis': commands.filter(cmd => cmd.value.startsWith('/analyze')),
'đ Generation': commands.filter(cmd => cmd.value.startsWith('/generate')),
'đ§ GitHub': commands.filter(cmd => ['/push', '/repos', '/github-token', '/test'].includes(cmd.value)),
'âī¸ Configuration': commands.filter(cmd => cmd.value.startsWith('/config')),
'âšī¸ Information': commands.filter(cmd => ['/help', '/status', '/history', '/analyses'].includes(cmd.value)),
'đĒ Session': commands.filter(cmd => ['/exit'].includes(cmd.value))
};
const categoryEntries = Object.entries(categories).filter(([_, cmds]) => cmds.length > 0);
categoryEntries.forEach(([category, cmds], categoryIndex) => {
// Create command list for this category
const categoryCommands = cmds.map(cmd => ({
name: cmd.value,
description: cmd.description || ''
}));
console.log('\n' + (0, visual_1.createCommandList)(categoryCommands, {
title: category,
nameColor: 'cyan',
descColor: 'gray'
}));
// Add spacing between categories (except for the last one)
if (categoryIndex < categoryEntries.length - 1) {
console.log(chalk_1.default.dim('\n' + 'â'.repeat(50)));
}
});
// Usage tips in a nice box
console.log('\n' + (0, visual_1.createBox)([
'đĄ Usage Tips:',
'',
'âĸ Type a command name and press Enter to execute',
'âĸ Start typing for smart suggestions (e.g., "/ana" shows analysis commands)',
'âĸ Use Tab or arrow keys for command history',
'âĸ Type "/exit" or press Ctrl+C to leave interactive mode'
], {
title: 'đ¯ How to Use',
style: 'single',
color: 'yellow',
padding: 1,
width: 70
}));
}
/**
* Show filtered commands based on user input with enhanced styling
*/
showFilteredCommands(input, suggestions) {
if (suggestions.length === 0) {
console.log((0, visual_1.createBox)([
`đ No commands found matching "${input}"`,
'',
'đĄ Try:',
' âĸ Check your spelling',
' âĸ Type "/" to see all available commands',
' âĸ Use partial matching (e.g., "/gen" for generate)'
], {
title: 'â No Matches',
style: 'single',
color: 'yellow',
padding: 1,
width: 60
}));
return;
}
// Create command suggestions
const commandList = suggestions.map(cmd => ({
name: cmd.value,
description: cmd.description || ''
}));
console.log((0, visual_1.createCommandList)(commandList, {
title: `đĄ Commands matching "${input}"`,
nameColor: 'green',
descColor: 'gray'
}));
// Show usage hint
const hintMessage = suggestions.length === 1
? '⨠Perfect match! Press Enter to execute this command'
: `đ¯ Found ${suggestions.length} matches. Continue typing or select a command`;
console.log('\n' + (0, visual_1.createStatus)('info', hintMessage));
}
/**
* Process a user command
*/
async processCommand(command) {
// Normalize command - add slash if missing
let normalizedCommand = command.trim();
if (!normalizedCommand.startsWith('/')) {
normalizedCommand = '/' + normalizedCommand;
}
// Parse command and arguments
const parts = normalizedCommand.slice(1).split(' '); // Remove '/' and split
const commandName = parts[0].toLowerCase();
const args = parts.slice(1);
try {
// Route to appropriate handler
await this.commandHandler.execute(commandName, args);
}
catch (error) {
if (error instanceof auth_1.AuthError) {
console.log('\n' + (0, visual_1.createBox)([
'đ Authentication Required',
'',
error.message,
'',
'đ To get started:',
' 1. Type /login to authenticate',
' 2. Get your API key from: https://doclyft.com/dashboard/api-keys',
' 3. Paste your key when prompted',
'',
'⨠Once authenticated, you\'ll have access to all features!'
], {
title: 'â ī¸ Access Required',
style: 'single',
color: 'yellow',
padding: 1,
width: 70
}));
console.log('');
return;
}
throw error; // Re-throw other errors
}
}
/**
* Ensure user is authenticated
*/
async ensureAuthenticated() {
try {
await auth_1.default.requireAuth();
}
catch (error) {
if (error instanceof auth_1.AuthError) {
console.log('\n' + (0, visual_1.createBox)([
'đ Authentication Available',
'',
'Some features require authentication for full functionality.',
'',
'đ When you\'re ready:',
' âĸ Type /login to unlock all features',
' âĸ Visit https://doclyft.com/dashboard/api-keys for your API key',
'',
'You can still use basic commands without authentication!'
], {
title: 'đĄ Pro Tip',
style: 'single',
color: 'blue',
padding: 1,
width: 70
}));
console.log('');
}
}
}
/**
* Get session statistics
*/
getSessionStats() {
return {
startTime: this.sessionStartTime,
duration: Date.now() - this.sessionStartTime.getTime(),
isRunning: this.isRunning
};
}
}
exports.InteractiveSession = InteractiveSession;
exports.default = InteractiveSession;