or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

cli-builder.mdcommand-system.mdfilesystem-tools.mdhttp-tools.mdindex.mdpackage-manager-tools.mdpatching-tools.mdprint-tools.mdprompt-tools.mdsemver-tools.mdstring-tools.mdsystem-tools.mdtemplate-tools.md
tile.json

package-manager-tools.mddocs/

Package Management

Package manager integration for installing, removing, and managing npm packages within CLI applications, with support for both npm and Yarn.

Capabilities

Package Installation

Add packages to project dependencies with automatic package manager detection.

/**
 * Add packages to project dependencies
 * @param packageName - Single package name or array of package names
 * @param options - Installation options
 * @returns Promise resolving to operation result
 */
add(
  packageName: string | string[], 
  options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;

interface GluegunPackageManagerOptions {
  /** Install as development dependency */
  dev?: boolean;
  /** Perform dry run without actual installation */
  dryRun?: boolean;
  /** Target directory for package installation */
  dir?: string;
  /** Force specific package manager ('npm' or 'yarn') */
  force?: 'npm' | 'yarn';
}

interface GluegunPackageManagerResult {
  /** Operation completed successfully */
  success: boolean;
  /** Command that was executed */
  command: string;
  /** Command output (stdout) */
  stdout: string;
  /** Error message if operation failed */
  error?: string;
}

Package Installation Examples:

import { packageManager } from "gluegun";

// Install single production dependency
const result = await packageManager.add("express", {});
if (result.success) {
  console.log("Express installed successfully");
} else {
  console.error(`Installation failed: ${result.error}`);
}

// Install multiple development dependencies
const devResult = await packageManager.add(
  ["jest", "eslint", "@types/node"], 
  { dev: true }
);

// Install with specific package manager
const yarnResult = await packageManager.add("lodash", {
  force: 'yarn'
});

// Install in specific directory
const dirResult = await packageManager.add("react", {
  dir: "./frontend"
});

// Dry run to see what would be installed
const dryResult = await packageManager.add("typescript", {
  dev: true,
  dryRun: true
});
console.log(`Would execute: ${dryResult.command}`);

Package Removal

Remove packages from project dependencies.

/**
 * Remove packages from project dependencies
 * @param packageName - Single package name or array of package names
 * @param options - Removal options
 * @returns Promise resolving to operation result
 */
remove(
  packageName: string | string[], 
  options: GluegunPackageManagerOptions
): Promise<GluegunPackageManagerResult>;

Package Removal Examples:

import { packageManager } from "gluegun";

// Remove single package
const result = await packageManager.remove("unused-package", {});

// Remove multiple packages
const multiResult = await packageManager.remove([
  "old-dependency", 
  "deprecated-tool", 
  "unused-lib"
], {});

// Remove from specific directory
const dirResult = await packageManager.remove("react", {
  dir: "./old-frontend"
});

// Dry run removal
const dryResult = await packageManager.remove("big-package", {
  dryRun: true
});
console.log(`Would execute: ${dryResult.command}`);

Package Manager Detection

Utilities for detecting and working with available package managers.

/**
 * Check if Yarn package manager is available
 * @returns True if yarn is installed and available
 */
hasYarn(): boolean;

Package Manager Detection Examples:

import { packageManager, print } from "gluegun";

// Automatic package manager selection
const useYarn = packageManager.hasYarn();
print.info(`Using package manager: ${useYarn ? 'yarn' : 'npm'}`);

// Install with preferred package manager
const preferredManager = packageManager.hasYarn() ? 'yarn' : 'npm';
await packageManager.add("express", { force: preferredManager });

// Conditional logic based on package manager
if (packageManager.hasYarn()) {
  // Yarn-specific operations
  await packageManager.add("package", { force: 'yarn' });
} else {
  // NPM-specific operations  
  await packageManager.add("package", { force: 'npm' });
}

Error Handling and Results

Comprehensive error handling and result information for package operations.

import { packageManager, print } from "gluegun";

async function installDependencies(packages, isDev = false) {
  print.info(`Installing ${packages.length} packages...`);
  
  const result = await packageManager.add(packages, { dev: isDev });
  
  if (result.success) {
    print.success(`✓ Packages installed successfully`);
    print.muted(`Command: ${result.command}`);
    
    // Show installation summary
    const lines = result.stdout.split('\n');
    const summaryLine = lines.find(line => line.includes('added') || line.includes('installed'));
    if (summaryLine) {
      print.info(summaryLine.trim());
    }
  } else {
    print.error(`✗ Installation failed`);
    print.error(`Command: ${result.command}`);
    if (result.error) {
      print.error(`Error: ${result.error}`);
    }
    
    // Parse error details
    if (result.stdout.includes('ENOENT')) {
      print.warning('Package not found. Check the package name.');
    } else if (result.stdout.includes('EACCES')) {
      print.warning('Permission error. Try running with sudo or check npm permissions.');
    }
  }
  
  return result.success;
}

// Usage
const success = await installDependencies(['express', 'cors'], false);
if (!success) {
  process.exit(1);
}

Batch Operations

Handling multiple package operations with progress feedback.

import { packageManager, print } from "gluegun";

async function setupProject(dependencies, devDependencies) {
  const operations = [];
  
  if (dependencies.length > 0) {
    operations.push({
      type: 'production',
      packages: dependencies,
      options: { dev: false }
    });
  }
  
  if (devDependencies.length > 0) {
    operations.push({
      type: 'development', 
      packages: devDependencies,
      options: { dev: true }
    });
  }
  
  for (const op of operations) {
    print.info(`Installing ${op.type} dependencies...`);
    
    const result = await packageManager.add(op.packages, op.options);
    
    if (result.success) {
      print.success(`✓ ${op.type} dependencies installed`);
    } else {
      print.error(`✗ Failed to install ${op.type} dependencies`);
      return false;
    }
  }
  
  return true;
}

// Usage in CLI command
export = {
  name: "init",
  description: "Initialize project with dependencies",
  run: async (toolbox) => {
    const { packageManager, print, prompt } = toolbox;
    
    const config = await prompt.ask([
      {
        type: 'multiselect',
        name: 'framework',
        message: 'Select frameworks:',
        choices: ['express', 'react', 'vue', 'angular']
      },
      {
        type: 'multiselect', 
        name: 'tools',
        message: 'Select development tools:',
        choices: ['typescript', 'eslint', 'prettier', 'jest']
      }
    ]);
    
    await setupProject(config.framework, config.tools);
  }
};

Package Manager Configuration

Working with different package manager configurations and options.

import { packageManager, filesystem } from "gluegun";

// Check for lock files to determine package manager preference
function detectPackageManager() {
  if (filesystem.exists('yarn.lock')) {
    return 'yarn';
  } else if (filesystem.exists('package-lock.json')) {
    return 'npm';
  }
  return packageManager.hasYarn() ? 'yarn' : 'npm';
}

// Install with detected package manager
async function smartInstall(packages, options = {}) {
  const manager = detectPackageManager();
  return await packageManager.add(packages, { 
    ...options, 
    force: manager 
  });
}

// Directory-specific installations
async function setupMonorepo() {
  const workspaces = ['frontend', 'backend', 'shared'];
  
  for (const workspace of workspaces) {
    print.info(`Setting up ${workspace}...`);
    
    const packages = getPackagesForWorkspace(workspace);
    const result = await packageManager.add(packages, {
      dir: `./${workspace}`,
      force: 'npm' // Consistent across workspaces
    });
    
    if (!result.success) {
      print.error(`Failed to setup ${workspace}`);
      return false;
    }
  }
  
  return true;
}

function getPackagesForWorkspace(workspace) {
  const packageMap = {
    frontend: ['react', 'react-dom', '@types/react'],
    backend: ['express', 'cors', '@types/node'],
    shared: ['lodash', 'moment', 'axios']
  };
  return packageMap[workspace] || [];
}

Complete Package Manager Interface

interface GluegunPackageManager {
  /**
   * Add packages to dependencies
   * @param packageName - Package name(s) to install
   * @param options - Installation options
   * @returns Operation result
   */
  add(
    packageName: string | string[], 
    options: GluegunPackageManagerOptions
  ): Promise<GluegunPackageManagerResult>;

  /**
   * Remove packages from dependencies
   * @param packageName - Package name(s) to remove
   * @param options - Removal options
   * @returns Operation result
   */
  remove(
    packageName: string | string[], 
    options: GluegunPackageManagerOptions
  ): Promise<GluegunPackageManagerResult>;

  /**
   * Check if Yarn is available
   * @returns True if Yarn is installed
   */
  hasYarn(): boolean;
}

interface GluegunPackageManagerOptions {
  /** Install as dev dependency */
  dev?: boolean;
  /** Dry run mode */
  dryRun?: boolean;
  /** Target directory */
  dir?: string;
  /** Force specific manager */
  force?: 'npm' | 'yarn';
}

interface GluegunPackageManagerResult {
  /** Operation success status */
  success: boolean;
  /** Executed command */
  command: string;
  /** Command output */
  stdout: string;
  /** Error message */
  error?: string;
}

Comprehensive Usage Example:

// CLI command for project dependency management
export = {
  name: "deps",
  description: "Manage project dependencies",
  run: async (toolbox) => {
    const { packageManager, print, prompt, parameters, filesystem } = toolbox;
    
    const action = parameters.first;
    
    switch (action) {
      case 'install':
        await handleInstall(toolbox);
        break;
      case 'remove':
        await handleRemove(toolbox);
        break;
      case 'audit':
        await handleAudit(toolbox);
        break;
      case 'init':
        await handleInit(toolbox);
        break;
      default:
        print.error('Usage: cli deps <install|remove|audit|init>');
    }
  }
};

async function handleInstall(toolbox) {
  const { packageManager, print, prompt, parameters } = toolbox;
  
  let packages = parameters.array.slice(1); // Skip 'install'
  
  if (packages.length === 0) {
    const input = await prompt.ask({
      type: 'input',
      name: 'packages',
      message: 'Enter package names (space-separated):'
    });
    packages = input.packages.split(/\s+/).filter(Boolean);
  }
  
  const options = await prompt.ask([
    {
      type: 'confirm',
      name: 'dev',
      message: 'Install as dev dependencies?',
      initial: false
    },
    {
      type: 'select',
      name: 'manager',
      message: 'Package manager:',
      choices: [
        { name: 'auto', message: 'Auto-detect' },
        { name: 'npm', message: 'NPM' },
        { name: 'yarn', message: 'Yarn' }
      ]
    }
  ]);
  
  const installOptions = {
    dev: options.dev,
    force: options.manager !== 'auto' ? options.manager : undefined
  };
  
  print.info(`Installing ${packages.length} packages...`);
  const result = await packageManager.add(packages, installOptions);
  
  if (result.success) {
    print.success(`✓ Packages installed successfully`);
  } else {
    print.error(`✗ Installation failed: ${result.error}`);
  }
}

async function handleRemove(toolbox) {
  const { packageManager, print, filesystem } = toolbox;
  
  // Read current dependencies
  const pkg = filesystem.read('package.json', 'json');
  if (!pkg) {
    print.error('No package.json found');
    return;
  }
  
  const allDeps = {
    ...pkg.dependencies,
    ...pkg.devDependencies
  };
  
  if (Object.keys(allDeps).length === 0) {
    print.info('No dependencies to remove');
    return;
  }
  
  const toRemove = await prompt.ask({
    type: 'multiselect',
    name: 'packages',
    message: 'Select packages to remove:',
    choices: Object.keys(allDeps)
  });
  
  if (toRemove.packages.length === 0) {
    print.info('No packages selected');
    return;
  }
  
  const result = await packageManager.remove(toRemove.packages, {});
  
  if (result.success) {
    print.success(`✓ Removed ${toRemove.packages.length} packages`);
  } else {
    print.error(`✗ Removal failed: ${result.error}`);
  }
}

async function handleAudit(toolbox) {
  const { packageManager, print, system } = toolbox;
  
  const manager = packageManager.hasYarn() ? 'yarn' : 'npm';
  
  print.info('Running security audit...');
  
  try {
    const auditCmd = manager === 'yarn' ? 'yarn audit' : 'npm audit';
    const output = await system.run(auditCmd);
    print.info(output);
  } catch (error) {
    print.warning('Security vulnerabilities found');
    
    const fix = await prompt.confirm('Attempt to fix automatically?');
    if (fix) {
      const fixCmd = manager === 'yarn' ? 'yarn audit fix' : 'npm audit fix';
      await system.run(fixCmd);
      print.success('Audit fixes applied');
    }
  }
}

async function handleInit(toolbox) {
  const { packageManager, print, prompt } = toolbox;
  
  const template = await prompt.ask({
    type: 'select',
    name: 'type',
    message: 'Project template:',
    choices: [
      { name: 'basic', message: 'Basic Node.js project' },
      { name: 'web', message: 'Web application' },
      { name: 'api', message: 'REST API server' },
      { name: 'cli', message: 'CLI application' }
    ]
  });
  
  const packageSets = {
    basic: {
      deps: [],
      devDeps: ['nodemon', 'jest']
    },
    web: {
      deps: ['express', 'cors'],
      devDeps: ['nodemon', 'jest', 'supertest']
    },
    api: {
      deps: ['express', 'cors', 'helmet', 'morgan'],
      devDeps: ['nodemon', 'jest', 'supertest', '@types/node']
    },
    cli: {
      deps: ['commander', 'inquirer'],
      devDeps: ['jest', '@types/node']
    }
  };
  
  const packages = packageSets[template.type];
  
  print.info('Installing project dependencies...');
  
  if (packages.deps.length > 0) {
    await packageManager.add(packages.deps, { dev: false });
  }
  
  if (packages.devDeps.length > 0) {
    await packageManager.add(packages.devDeps, { dev: true });
  }
  
  print.success(`✓ ${template.type} project initialized`);
}