npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

codes-command-tool

v1.0.1

Published

A powerful Node.js command execution tool that supports executing commands in specified directories

Readme

Codes Command Tool

一个强大的 Node.js 工具库,提供命令执行和文件操作的完整解决方案。支持在指定目录执行命令、文件系统操作、补丁应用等功能,提供完整的 TypeScript 类型定义。

✨ 主要特性

🚀 命令执行工具

  • 指定目录执行: 在任意目录执行命令,无需切换工作目录
  • 多种执行模式: 基础模式、简化模式和批量执行模式
  • 完善的错误处理: 超时机制、退出代码检查、详细错误信息
  • 丰富的配置选项: 超时、环境变量、进程选项等

📁 文件系统工具

  • 目录结构读取: 递归读取目录,支持过滤、排序、深度控制
  • 文件内容操作: 智能读取文件内容,支持编码检测和类型判断
  • 安全文件创建: 防路径穿越,支持目录自动创建和覆盖控制
  • 文件删除管理: 安全删除文件和目录,支持递归删除
  • 补丁应用系统: 基于 diff 的补丁应用,支持多文件补丁

🛡️ 安全与可靠性

  • 完整类型支持: 使用 TypeScript 编写,提供完整的类型定义
  • 路径安全: 防止路径穿越攻击,安全的文件操作
  • 错误处理: 完善的错误处理和异常捕获机制
  • 异步优先: 基于 Promise 的 API,支持 async/await

🚀 快速开始

安装

npm install codes-command-tool
# 或
pnpm add codes-command-tool
# 或
yarn add codes-command-tool

基础用法

命令执行

import { executeCommand, executeCommandSimple } from 'codes-command-tool';

// 基础用法:在指定目录执行命令
const result = await executeCommand('/path/to/directory', 'ls', ['-la']);
console.log('退出代码:', result.code);
console.log('输出:', result.stdout);
console.log('执行时间:', result.duration, 'ms');

// 简化用法:直接获取输出
const output = await executeCommandSimple('/path/to/directory', 'pwd');
console.log('当前目录:', output);

文件操作

import { 
  readFolder, 
  readFileContent, 
  createFile, 
  deleteFile,
  formatFileTree 
} from 'codes-command-tool';

// 读取目录结构
const fileTree = await readFolder('/path/to/directory');
console.log(formatFileTree(fileTree, true)); // 显示文件大小

// 输出示例:
// 📁 my-project/
// ├── 总文件数: 15
// ├── 总目录数: 4
// └── 总大小: 2.34 MB
// 
// ├── 📁 src
// │   ├── 📄 index.ts (1.2 KB)
// │   ├── 📄 utils.ts (856 B)
// │   └── 📁 components
// │       ├── 📄 Button.tsx (2.1 KB)
// │       └── 📄 Modal.tsx (3.4 KB)
// ├── 📄 package.json (1.8 KB)
// ├── 📄 README.md (4.2 KB)
// └── 📄 tsconfig.json (512 B)

// 读取文件内容
const fileInfo = await readFileContent('/path/to/file.txt');
console.log('文件内容:', fileInfo.content);
console.log('是否为文本文件:', fileInfo.isText);

// 创建文件
const newFile = await createFile('/path/to/directory', 'new-file.txt', 'Hello World!');
console.log('文件已创建:', newFile.path);

// 删除文件
await deleteFile('/path/to/directory', 'old-file.txt');

高级用法

命令执行高级功能

import { executeCommand, executeMultipleCommands } from 'codes-command-tool';

// 带选项的执行
const result = await executeCommand(
  './my-project',
  'npm',
  ['install'],
  {
    timeout: 60000,        // 60秒超时
    env: { NODE_ENV: 'production' },  // 自定义环境变量
    inheritEnv: true       // 继承当前环境变量
  }
);

// 批量执行命令
const results = await executeMultipleCommands('/path/to/project', [
  { command: 'npm', args: ['install'] },
  { command: 'npm', args: ['run', 'build'] },
  { command: 'npm', args: ['test'] }
]);

文件系统高级功能

import { 
  readFolder, 
  readFileContent, 
  createFile, 
  deleteFolder,
  PatchApplier,
  FileType 
} from 'codes-command-tool';

// 高级目录读取:过滤、排序和忽略目录
const fileTree = await readFolder('/path/to/project', {
  recursive: true,
  maxDepth: 3,
  includeHidden: false,
  ignoreFolders: ['node_modules', '.git', 'dist', 'coverage'], // 忽略常见的构建目录
  filter: (file) => {
    // 只包含 .ts 和 .js 文件,以及目录
    return file.type === FileType.DIRECTORY || 
           file.name.endsWith('.ts') || 
           file.name.endsWith('.js');
  },
  sort: (a, b) => a.name.localeCompare(b.name)
});

// 格式化并显示文件树
console.log('项目结构:');
console.log(formatFileTree(fileTree, true)); // 显示文件大小

// 智能文件内容读取
const fileInfo = await readFileContent('/path/to/file.txt', {
  encoding: 'utf8',
  detectType: true,
  maxSize: 1024 * 1024 // 1MB 限制
});

// 安全文件创建(支持嵌套路径)
const newFile = await createFile(
  '/base/directory',
  'nested/path/file.txt',
  'Content',
  {
    allowNestedPath: true,
    ensureDir: true,
    overwrite: false
  }
);

// 应用补丁
const patchApplier = new PatchApplier({
  workdir: '/path/to/project',
  patchContent: patchString,
  dryRun: false
});

const results = await patchApplier.applyAll();
results.forEach(result => {
  console.log(`${result.path}: ${result.success ? '成功' : '失败'}`);
  if (!result.success) {
    console.error(result.error);
  }
});

📚 API 文档

命令执行工具

executeCommand

在指定目录执行命令的主要函数。

function executeCommand(
  directory: string,
  command: string,
  args?: string[],
  options?: ExecuteOptions
): Promise<CommandResult>

参数:

  • directory - 执行命令的目录路径(相对或绝对路径)
  • command - 要执行的命令
  • args - 命令参数数组(可选)
  • options - 执行选项(可选)

返回:

interface CommandResult {
  code: number;      // 退出代码
  stdout: string;    // 标准输出
  stderr: string;    // 标准错误输出
  duration: number;  // 执行时间(毫秒)
}

executeCommandSimple

简化版本,仅返回标准输出,失败时抛出异常。

function executeCommandSimple(
  directory: string,
  command: string,
  args?: string[],
  options?: ExecuteOptions
): Promise<string>

executeMultipleCommands

在指定目录执行多个命令。

function executeMultipleCommands(
  directory: string,
  commands: CommandConfig[],
  options?: ExecuteOptions
): Promise<CommandResult[]>

ExecuteOptions

interface ExecuteOptions {
  timeout?: number;        // 超时时间(毫秒),默认 30000
  inheritEnv?: boolean;    // 是否继承当前环境变量,默认 true
  env?: NodeJS.ProcessEnv; // 自定义环境变量
  // ... 其他 Node.js spawn 选项
}

文件系统工具

readFolder

读取目录结构,支持递归、过滤、排序等功能。

function readFolder(
  dirPath: string,
  options?: ReadFolderOptions
): Promise<FileTree>

参数:

  • dirPath - 目录路径
  • options - 读取选项(可选)

返回:

interface FileTree {
  root: FileInfo;          // 根目录信息
  totalFiles: number;      // 文件总数
  totalDirectories: number; // 目录总数
  totalSize: number;       // 总大小(字节)
}

interface ReadFolderOptions {
  recursive?: boolean;     // 是否递归读取,默认 true
  maxDepth?: number;       // 最大递归深度,默认 无限制
  includeHidden?: boolean; // 是否包含隐藏文件,默认 true
  ignoreFolders?: string[]; // 忽略的目录名称列表,默认 []
  filter?: (file: FileInfo) => boolean; // 文件过滤器
  sort?: (a: FileInfo, b: FileInfo) => number; // 排序函数
}

formatFileTree

将文件树转换为可视化的文本格式显示。

function formatFileTree(
  fileTree: FileTree,
  showSize?: boolean
): string

参数:

  • fileTree - 通过 readFolder 获取的文件树
  • showSize - 是否显示文件大小,默认 false

返回:

格式化的文本字符串,包含:

  • 树形结构显示(使用 ├──└── 符号)
  • 文件和目录图标(📁 目录,📄 文件)
  • 统计信息(总文件数、总目录数、总大小)
  • 可选的文件大小显示(当 showSize 为 true 时)

readFileContent

智能读取文件内容,支持编码检测和类型判断。

function readFileContent(
  filePath: string,
  options?: ReadFileContentOptions
): Promise<FileContentInfo>

返回:

interface FileContentInfo {
  path: string;            // 文件路径
  name: string;            // 文件名
  extension: string;       // 文件扩展名
  size: number;            // 文件大小(字节)
  lastModified: Date;      // 最后修改时间
  content: string | Buffer; // 文件内容
  encoding: BufferEncoding | 'binary'; // 文件编码
  isText: boolean;         // 是否为文本文件
  lineCount?: number;      // 行数(仅文本文件)
}

createFile

在指定目录创建文件,支持安全路径检查和目录自动创建。

function createFile(
  directoryPath: string,
  fileName: string,
  content?: string | Buffer,
  options?: CreateFileOptions
): Promise<FileInfo>

deleteFile / deleteFolder

安全删除文件或目录。

function deleteFile(
  directoryPath: string,
  fileName: string,
  options?: DeleteFileOptions
): Promise<void>

function deleteFolder(
  directoryPath: string,
  folderName: string,
  options?: DeleteFolderOptions
): Promise<void>

PatchApplier

基于 diff 的补丁应用系统。

class PatchApplier {
  constructor(options: PatchApplierOptions)
  
  async applyAll(): Promise<PatchResult[]>
  getParsedPatches(): StructuredPatch[]
  getFileCount(): number
}

interface PatchApplierOptions {
  workdir: string;         // 工作目录
  patchContent: string;    // 补丁文件内容
  encoding?: BufferEncoding; // 文件编码,默认 'utf8'
  dryRun?: boolean;        // 仅试运行,不写入磁盘
}

💡 使用示例

项目文件分析

import { readFolder, readFileContent, formatFileTree, FileType } from 'codes-command-tool';

// 分析项目结构
const projectTree = await readFolder('./my-project', {
  recursive: true,
  maxDepth: 3,
  ignoreFolders: ['node_modules', '.git', 'dist', 'coverage', '.next', 'build'], // 使用 ignoreFolders 更简洁
  filter: (file) => {
    // 进一步过滤,只关注源代码文件
    if (file.type === FileType.DIRECTORY) return true;
    return file.name.match(/\.(ts|js|tsx|jsx|vue|py|java|go|rs)$/);
  }
});

console.log('项目结构:');
console.log(formatFileTree(projectTree, true)); // 显示文件大小

console.log(`\n📊 项目统计:`);
console.log(`- 总文件数: ${projectTree.totalFiles}`);
console.log(`- 总目录数: ${projectTree.totalDirectories}`);
console.log(`- 总大小: ${(projectTree.totalSize / 1024 / 1024).toFixed(2)} MB`);

// 读取所有 TypeScript 文件
const tsFiles = [];
function collectTsFiles(files) {
  for (const file of files) {
    if (file.type === FileType.FILE && file.name.endsWith('.ts')) {
      tsFiles.push(file.path);
    }
    if (file.children) {
      collectTsFiles(file.children);
    }
  }
}
collectTsFiles(projectTree.root.children || []);

// 分析每个 TypeScript 文件
for (const filePath of tsFiles) {
  const fileInfo = await readFileContent(filePath);
  console.log(`${fileInfo.name}: ${fileInfo.lineCount} 行, ${fileInfo.size} 字节`);
}

自动化构建工具

import { 
  executeCommand, 
  executeMultipleCommands, 
  createFile, 
  readFileContent 
} from 'codes-command-tool';

// 创建构建脚本
async function buildProject(projectPath: string) {
  console.log('开始构建项目...');
  
  // 检查 package.json 是否存在
  try {
    const packageJson = await readFileContent(`${projectPath}/package.json`);
    console.log('找到 package.json');
  } catch {
    console.error('未找到 package.json,创建默认配置...');
    await createFile(projectPath, 'package.json', JSON.stringify({
      name: 'my-project',
      version: '1.0.0',
      scripts: {
        build: 'tsc',
        test: 'jest'
      }
    }, null, 2));
  }
  
  // 执行构建步骤
  const buildSteps = [
    { command: 'npm', args: ['install'] },
    { command: 'npm', args: ['run', 'lint'] },
    { command: 'npm', args: ['run', 'test'] },
    { command: 'npm', args: ['run', 'build'] }
  ];
  
  const results = await executeMultipleCommands(projectPath, buildSteps, {
    timeout: 300000 // 5分钟超时
  });
  
  // 检查构建结果
  const success = results.every(result => result.code === 0);
  if (success) {
    console.log('✅ 构建成功!');
    
    // 创建构建报告
    const report = results.map((result, index) => 
      `步骤 ${index + 1}: ${buildSteps[index].command} ${buildSteps[index].args?.join(' ')} - ${result.duration}ms`
    ).join('\n');
    
    await createFile(projectPath, 'build-report.txt', report);
  } else {
    console.log('❌ 构建失败');
    results.forEach((result, index) => {
      if (result.code !== 0) {
        console.error(`步骤 ${index + 1} 失败:`, result.stderr);
      }
    });
  }
}

// 使用示例
await buildProject('./my-project');

代码补丁管理

import { PatchApplier, readFileContent, createFile } from 'codes-command-tool';

// 应用代码补丁
async function applyCodePatch(projectPath: string, patchFilePath: string) {
  // 读取补丁文件
  const patchFile = await readFileContent(patchFilePath);
  const patchContent = patchFile.content as string;
  
  // 创建补丁应用器
  const patchApplier = new PatchApplier({
    workdir: projectPath,
    patchContent,
    dryRun: false // 设为 true 可以预览而不实际应用
  });
  
  console.log(`准备应用补丁到 ${patchApplier.getFileCount()} 个文件...`);
  
  // 应用补丁
  const results = await patchApplier.applyAll();
  
  // 生成应用报告
  const successCount = results.filter(r => r.success).length;
  const failCount = results.length - successCount;
  
  console.log(`补丁应用完成: ${successCount} 成功, ${failCount} 失败`);
  
  // 保存详细报告
  const report = results.map(result => 
    `${result.path}: ${result.success ? '✅ 成功' : '❌ 失败'}${
      result.error ? ` - ${result.error}` : ''
    }`
  ).join('\n');
  
  await createFile(projectPath, 'patch-report.txt', report);
  
  return { successCount, failCount, results };
}

// 使用示例
const patchResult = await applyCodePatch('./my-project', './fixes.patch');

Git 操作与版本管理

import { executeCommand, executeCommandSimple, createFile } from 'codes-command-tool';

// Git 仓库状态检查
async function checkGitStatus(repoPath: string) {
  try {
    // 检查是否是 Git 仓库
    await executeCommand(repoPath, 'git', ['rev-parse', '--git-dir']);
    
    // 获取当前分支
    const branch = await executeCommandSimple(repoPath, 'git', [
      'branch', '--show-current'
    ]);
    
    // 检查工作区状态
    const status = await executeCommand(repoPath, 'git', ['status', '--porcelain']);
    const hasChanges = status.stdout.trim().length > 0;
    
    // 获取最近提交信息
    const lastCommit = await executeCommandSimple(repoPath, 'git', [
      'log', '-1', '--pretty=format:%h %s'
    ]);
    
    return {
      branch: branch.trim(),
      hasChanges,
      lastCommit: lastCommit.trim(),
      changes: status.stdout.trim().split('\n').filter(line => line.trim())
    };
  } catch (error) {
    throw new Error('不是有效的 Git 仓库或 Git 不可用');
  }
}

// 自动提交工具
async function autoCommit(repoPath: string, message: string) {
  const gitStatus = await checkGitStatus(repoPath);
  
  if (!gitStatus.hasChanges) {
    console.log('没有需要提交的更改');
    return;
  }
  
  console.log(`在分支 ${gitStatus.branch} 上发现 ${gitStatus.changes.length} 个更改`);
  
  // 添加所有更改
  await executeCommand(repoPath, 'git', ['add', '.']);
  
  // 提交更改
  const commitResult = await executeCommand(repoPath, 'git', ['commit', '-m', message]);
  
  if (commitResult.code === 0) {
    console.log('✅ 提交成功');
    
    // 创建提交报告
    const report = `提交时间: ${new Date().toISOString()}
分支: ${gitStatus.branch}
提交信息: ${message}
更改文件:
${gitStatus.changes.join('\n')}`;
    
    await createFile(repoPath, '.git-commit-log.txt', report, { overwrite: true });
  } else {
    console.error('❌ 提交失败:', commitResult.stderr);
  }
}

// 使用示例
const gitInfo = await checkGitStatus('./my-project');
console.log(`当前分支: ${gitInfo.branch}`);
console.log(`最近提交: ${gitInfo.lastCommit}`);

if (gitInfo.hasChanges) {
  await autoCommit('./my-project', 'feat: 自动提交工具更新');
}

错误处理

try {
  const result = await executeCommand('/nonexistent', 'ls');
} catch (error) {
  console.error('命令执行失败:', error.message);
}

// 或者检查退出代码
const result = await executeCommand('.', 'ls', ['/nonexistent']);
if (result.code !== 0) {
  console.error('命令失败:', result.stderr);
}

⚠️ 注意事项

安全考虑

  • 谨慎处理用户输入,避免命令注入攻击
  • 不要执行不受信任的命令
  • 使用参数数组而不是字符串拼接来避免 shell 注入
// ✅ 安全:使用参数数组
await executeCommand('/path', 'ls', ['-la', userInput]);

// ❌ 不安全:避免字符串拼接
// await executeCommand('/path', `ls -la ${userInput}`);

平台兼容性

  • 该库在 Windows、macOS 和 Linux 上都可正常工作
  • 命令需要在目标系统上可用
  • Windows 上建议使用 PowerShell 或 Git Bash

性能考虑

  • 大量并发执行可能消耗系统资源
  • 合理设置超时时间
  • 考虑使用 executeMultipleCommands 串行执行相关命令

🎯 最佳实践

错误处理

// 推荐:明确处理错误情况
try {
  const result = await executeCommand('./project', 'npm', ['test']);
  if (result.code !== 0) {
    console.error('测试失败:', result.stderr);
    process.exit(1);
  }
} catch (error) {
  console.error('执行错误:', error.message);
  process.exit(1);
}

路径处理

import { resolve } from 'path';

// 推荐:使用绝对路径
const projectPath = resolve('./my-project');
await executeCommand(projectPath, 'npm', ['install']);

// 或使用相对路径(相对于当前工作目录)
await executeCommand('./my-project', 'npm', ['install']);

环境变量管理

// 推荐:明确设置所需的环境变量
await executeCommand('./project', 'npm', ['run', 'build'], {
  env: {
    NODE_ENV: 'production',
    CI: 'true'
  },
  inheritEnv: true  // 继承其他环境变量
});

🔧 开发和构建

项目要求

  • Node.js: >= 20.0.0
  • 包管理器: 推荐使用 pnpm
  • TypeScript: 5.8+

主要依赖

  • diff: 用于补丁解析和应用
  • 开发依赖: TypeScript、Jest、Rollup、TypeDoc 等

开发流程

如果您想要参与开发或自定义构建:

# 克隆项目
git clone <repository-url>
cd codes-command-tool

# 安装依赖
pnpm install

# 开发模式(监听文件变化)
pnpm run dev

# 构建项目
pnpm run build

# 运行测试
pnpm run test

# 测试覆盖率
pnpm run test:coverage

# 代码检查和格式化
pnpm run lint
pnpm run format

# 生成文档
pnpm run docs

# 清理构建文件
pnpm run clean

构建输出

项目支持多种模块格式:

  • ESM: dist/index.js (推荐)
  • CommonJS: dist/index.cjs
  • TypeScript 声明: dist/index.d.ts
  • 文档: docs/ 目录

🐛 常见问题

命令执行相关

Q: 如何处理长时间运行的命令?

A: 使用 timeout 选项设置合适的超时时间,或设置为 0 禁用超时:

await executeCommand('./project', 'npm', ['install'], {
  timeout: 0 // 禁用超时
});

Q: 如何在 Windows 上使用?

A: 确保使用正确的命令名称。Windows 上某些命令可能需要 .exe 后缀:

// Windows 上可能需要
await executeCommand('.', 'npm.exe', ['--version']);

// 或使用 PowerShell
await executeCommand('.', 'powershell', ['-Command', 'Get-Location']);

Q: 如何处理需要交互输入的命令?

A: 这个库不支持交互式命令。请使用非交互式标志或预先配置所需的输入:

// 使用非交互式标志
await executeCommand('.', 'npm', ['install', '--yes']);

Q: 命令执行失败但没有抛出错误?

A: executeCommand 不会因为非零退出代码而抛出错误。请检查 result.code

const result = await executeCommand('.', 'false'); // 总是返回退出代码 1
if (result.code !== 0) {
  console.error('命令失败');
}

// 或使用 executeCommandSimple,它会在失败时抛出错误
try {
  await executeCommandSimple('.', 'false');
} catch (error) {
  console.error('命令失败:', error.message);
}

文件操作相关

Q: 如何安全地处理用户输入的文件路径?

A: 默认情况下,文件操作函数会阻止路径穿越攻击。如果需要支持嵌套路径,请明确设置:

// 安全:默认阻止路径穿越
await createFile('/base/dir', 'file.txt', 'content');

// 如果需要嵌套路径,明确允许
await createFile('/base/dir', 'nested/path/file.txt', 'content', {
  allowNestedPath: true
});

Q: 如何处理大文件读取?

A: 使用 maxSize 选项限制文件大小,避免内存溢出:

try {
  const fileInfo = await readFileContent('/path/to/large-file.txt', {
    maxSize: 10 * 1024 * 1024 // 限制 10MB
  });
} catch (error) {
  console.error('文件过大或读取失败:', error.message);
}

Q: 补丁应用失败怎么办?

A: 使用 dryRun 模式预览补丁效果,检查补丁格式和目标文件:

// 先预览补丁效果
const patchApplier = new PatchApplier({
  workdir: './project',
  patchContent: patchString,
  dryRun: true // 仅预览,不实际修改
});

const results = await patchApplier.applyAll();
results.forEach(result => {
  if (!result.success) {
    console.error(`${result.path}: ${result.error}`);
  }
});

Q: 如何处理文件编码问题?

A: 库会自动检测文件编码,也可以手动指定:

// 自动检测编码
const fileInfo = await readFileContent('/path/to/file.txt', {
  detectType: true
});

// 手动指定编码
const fileInfo = await readFileContent('/path/to/file.txt', {
  encoding: 'gbk' // 指定编码
});

console.log('检测到的编码:', fileInfo.encoding);
console.log('是否为文本文件:', fileInfo.isText);

📄 许可证

本项目采用 MIT 许可证。

🤝 贡献

欢迎提交 Issue 和 Pull Request 来改进这个工具库!

贡献指南

  1. Fork 项目
  2. 创建特性分支 (git checkout -b feature/amazing-feature)
  3. 提交更改 (git commit -m 'Add some amazing feature')
  4. 推送到分支 (git push origin feature/amazing-feature)
  5. 打开一个 Pull Request

提示: 这个库为您提供了安全、可靠的命令执行能力,让您专注于业务逻辑而不用担心底层实现!