qraft
Version:
A powerful CLI tool to qraft structured project setups from GitHub template repositories
361 lines ⢠17.9 kB
JavaScript
;
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.InteractiveMode = void 0;
const chalk_1 = __importDefault(require("chalk"));
const fs = __importStar(require("fs-extra"));
const path = __importStar(require("path"));
const boxSelector_1 = require("./boxSelector");
const prompts_1 = require("./prompts");
/**
* Interactive mode manager for all CLI operations
*/
class InteractiveMode {
constructor(boxManager) {
this.boxManager = boxManager;
this.prompts = new prompts_1.InteractivePrompts();
this.boxSelector = new boxSelector_1.InteractiveBoxSelector(boxManager);
}
/**
* Interactive box copying workflow
* @param boxName Optional box name to start with
* @param options Initial options
* @returns Promise<BoxOperationResult> Result of the operation
*/
async copyBox(boxName, options = {}) {
try {
console.log(chalk_1.default.blue.bold('š¦ Interactive Box Copy\n'));
// Step 1: Select box if not provided
let selectedBox;
let selectedRegistry = options.registry;
if (boxName) {
// Parse and validate provided box name
try {
const boxRef = await this.boxManager.parseBoxReference(boxName, options.registry);
const boxInfo = await this.boxManager.getBoxInfo(boxRef);
if (!boxInfo) {
console.log(chalk_1.default.red(`ā Box '${boxName}' not found.`));
const tryInteractive = await this.prompts.confirm('Would you like to browse available boxes instead?', true);
if (!tryInteractive) {
return {
success: false,
message: `Box '${boxName}' not found`,
error: new Error(`Box '${boxName}' does not exist`)
};
}
const selection = await this.boxSelector.selectBox(options.registry);
if (!selection) {
return {
success: false,
message: 'Operation cancelled by user'
};
}
selectedBox = selection.box;
selectedRegistry = selection.registry;
}
else {
selectedBox = boxInfo;
selectedRegistry = boxRef.registry;
// Show preview and confirm
await this.prompts.previewBox(selectedBox);
const confirmed = await this.prompts.confirm(`Copy ${selectedBox.manifest.name}?`, true);
if (!confirmed) {
return {
success: false,
message: 'Operation cancelled by user'
};
}
}
}
catch (error) {
console.error(chalk_1.default.red('ā Error finding box:'), error instanceof Error ? error.message : 'Unknown error');
const tryInteractive = await this.prompts.confirm('Would you like to browse available boxes instead?', true);
if (!tryInteractive) {
return {
success: false,
message: 'Box lookup failed',
error: error instanceof Error ? error : new Error('Unknown error')
};
}
const selection = await this.boxSelector.selectBox(options.registry);
if (!selection) {
return {
success: false,
message: 'Operation cancelled by user'
};
}
selectedBox = selection.box;
selectedRegistry = selection.registry;
}
}
else {
// Interactive box selection
const selection = await this.boxSelector.selectBox(options.registry);
if (!selection) {
return {
success: false,
message: 'Operation cancelled by user'
};
}
selectedBox = selection.box;
selectedRegistry = selection.registry;
}
// Step 2: Configure target directory
let targetDirectory = options.target || selectedBox.manifest.defaultTarget || process.cwd();
if (!options.target) {
targetDirectory = await this.prompts.promptTargetDirectory(targetDirectory);
}
targetDirectory = path.resolve(targetDirectory);
// Step 3: Check for existing files and handle overwrites
let forceOverwrite = options.force || false;
if (!forceOverwrite) {
const existingFiles = await this.checkExistingFiles(selectedBox.files, targetDirectory);
if (existingFiles.length > 0) {
forceOverwrite = await this.prompts.confirmOverwrite(existingFiles);
if (!forceOverwrite) {
return {
success: false,
message: 'Operation cancelled - files would be overwritten'
};
}
}
}
// Step 4: Ask about sync tracking preference (unless explicitly set)
let enableSync = !options.nosync; // Default to sync enabled unless noSync is explicitly true
if (options.nosync === undefined) {
// Only prompt if not explicitly set via CLI flag
enableSync = await this.prompts.confirm('Enable sync tracking? (Creates .qraft directory for updates and management)', true);
}
// Step 5: Show summary and final confirmation
console.log(chalk_1.default.blue.bold('\nš Copy Summary:'));
console.log(` Box: ${chalk_1.default.cyan(selectedBox.manifest.name)} ${chalk_1.default.gray(`(${selectedBox.manifest.version})`)}`);
console.log(` Registry: ${chalk_1.default.yellow(selectedRegistry)}`);
console.log(` Target: ${chalk_1.default.gray(targetDirectory)}`);
console.log(` Files: ${chalk_1.default.cyan(selectedBox.files.length)} files`);
console.log(` Overwrite: ${forceOverwrite ? chalk_1.default.red('Yes') : chalk_1.default.green('No')}`);
console.log(` Sync Tracking: ${enableSync ? chalk_1.default.green('Enabled') : chalk_1.default.yellow('Disabled')}`);
const finalConfirm = await this.prompts.confirm('\nProceed with copy operation?', true);
if (!finalConfirm) {
return {
success: false,
message: 'Operation cancelled by user'
};
}
// Step 5: Perform the copy operation
console.log(chalk_1.default.blue('\nā³ Copying files...'));
const config = {
boxName: selectedBox.manifest.name,
targetDirectory,
force: forceOverwrite,
interactive: true,
boxesDirectory: '', // Not used for GitHub mode
nosync: !enableSync
};
const result = await this.boxManager.copyBox(config, selectedRegistry);
// Step 6: Show results
if (result.success) {
console.log(chalk_1.default.green.bold('\nā
Copy completed successfully!'));
console.log(chalk_1.default.gray(` Copied ${result.copiedFiles?.length || 0} files`));
if (result.skippedFiles && result.skippedFiles.length > 0) {
console.log(chalk_1.default.yellow(` Skipped ${result.skippedFiles.length} existing files`));
}
// Show post-install steps if available
if (selectedBox.manifest.postInstall && selectedBox.manifest.postInstall.length > 0) {
console.log(chalk_1.default.blue.bold('\nš Next Steps:'));
selectedBox.manifest.postInstall.forEach((step, index) => {
console.log(chalk_1.default.gray(` ${index + 1}. ${step}`));
});
}
console.log(chalk_1.default.gray(`\nš Files copied to: ${targetDirectory}`));
}
else {
console.error(chalk_1.default.red.bold('\nā Copy failed'));
console.error(chalk_1.default.red(result.message));
}
return result;
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
console.error(chalk_1.default.red('ā Unexpected error during copy operation:'), errorMessage);
return {
success: false,
message: `Copy operation failed: ${errorMessage}`,
error: error instanceof Error ? error : new Error('Unknown error')
};
}
}
/**
* Interactive list boxes workflow
* @param registryName Optional registry to list from
*/
async listBoxes(registryName) {
try {
console.log(chalk_1.default.blue.bold('š¦ Interactive Box Browser\n'));
// Select registry if not provided
let selectedRegistry = registryName;
if (!selectedRegistry) {
const registries = await this.boxManager.listRegistries();
if (registries.length === 0) {
console.log(chalk_1.default.red('ā No registries configured.'));
console.log(chalk_1.default.gray('Add a registry first:'));
console.log(chalk_1.default.cyan(' qraft config add-registry <name> <repository>'));
return;
}
if (registries.length === 1) {
selectedRegistry = registries[0].name;
console.log(chalk_1.default.gray(`Using registry: ${chalk_1.default.cyan(selectedRegistry)}`));
}
else {
const registrySelection = await this.prompts.selectRegistry(registries);
if (!registrySelection) {
console.log(chalk_1.default.yellow('Operation cancelled.'));
return;
}
selectedRegistry = registrySelection;
}
}
// Use the interactive box selector for browsing
const selection = await this.boxSelector.selectBox(selectedRegistry);
if (selection) {
console.log(chalk_1.default.green(`\nSelected: ${chalk_1.default.cyan(selection.box.manifest.name)} from ${chalk_1.default.yellow(selection.registry)}`));
const copyNow = await this.prompts.confirm('Would you like to copy this box now?', false);
if (copyNow) {
await this.copyBox(selection.box.manifest.name, { registry: selection.registry });
}
}
else {
console.log(chalk_1.default.yellow('No box selected.'));
}
}
catch (error) {
console.error(chalk_1.default.red('ā Error browsing boxes:'), error instanceof Error ? error.message : 'Unknown error');
}
}
/**
* Interactive authentication setup
* @param registryName Optional specific registry
*/
async setupAuthentication(registryName) {
try {
if (registryName) {
// Set up authentication for specific registry
const token = await this.prompts.promptGitHubToken(registryName);
console.log(chalk_1.default.blue('\nā³ Testing authentication...'));
await this.boxManager.setRegistryToken(registryName, token);
const authResult = await this.boxManager.testAuthentication(registryName);
if (authResult.authenticated) {
console.log(chalk_1.default.green.bold('\nā
Authentication successful!'));
console.log(chalk_1.default.gray(` Registry: ${registryName}`));
console.log(chalk_1.default.gray(` Authenticated as: ${authResult.user}`));
}
else {
console.error(chalk_1.default.red.bold('\nā Authentication failed'));
console.error(chalk_1.default.red(` Error: ${authResult.error}`));
}
}
else {
// Set up global authentication
const token = await this.prompts.promptGitHubToken();
console.log(chalk_1.default.blue('\nā³ Testing authentication...'));
await this.boxManager.setGlobalToken(token);
const defaultRegistry = await this.boxManager.getDefaultRegistry();
const authResult = await this.boxManager.testAuthentication(defaultRegistry);
if (authResult.authenticated) {
console.log(chalk_1.default.green.bold('\nā
Authentication successful!'));
console.log(chalk_1.default.gray(` Authenticated as: ${authResult.user}`));
console.log(chalk_1.default.gray(' Global token has been saved.'));
}
else {
console.error(chalk_1.default.red.bold('\nā Authentication failed'));
console.error(chalk_1.default.red(` Error: ${authResult.error}`));
}
}
}
catch (error) {
console.error(chalk_1.default.red('ā Error setting up authentication:'), error instanceof Error ? error.message : 'Unknown error');
}
}
/**
* Interactive registry configuration
*/
async configureRegistry() {
try {
const config = await this.prompts.promptRegistryConfig();
console.log(chalk_1.default.blue('\nā³ Adding registry...'));
await this.boxManager.getConfigManager().addRegistry(config.name, {
name: config.name,
repository: config.repository
});
if (config.setAsDefault) {
await this.boxManager.getConfigManager().setDefaultRegistry(config.name);
}
console.log(chalk_1.default.green.bold('\nā
Registry added successfully!'));
console.log(chalk_1.default.gray(` Name: ${config.name}`));
console.log(chalk_1.default.gray(` Repository: ${config.repository}`));
if (config.setAsDefault) {
console.log(chalk_1.default.gray(' Set as default registry'));
}
// Ask if they want to set up authentication
const setupAuth = await this.prompts.confirm('Would you like to set up authentication for this registry?', false);
if (setupAuth) {
await this.setupAuthentication(config.name);
}
}
catch (error) {
console.error(chalk_1.default.red('ā Error configuring registry:'), error instanceof Error ? error.message : 'Unknown error');
}
}
/**
* Check for existing files that would be overwritten
* @param files Array of file paths relative to target
* @param targetDirectory Target directory
* @returns Promise<string[]> Array of existing file paths
*/
async checkExistingFiles(files, targetDirectory) {
const existingFiles = [];
for (const file of files) {
const fullPath = path.join(targetDirectory, file);
if (await fs.pathExists(fullPath)) {
existingFiles.push(fullPath);
}
}
return existingFiles;
}
}
exports.InteractiveMode = InteractiveMode;
//# sourceMappingURL=interactiveMode.js.map