1. 项目概述:为什么从零构建一个Agent CLI?

如果你关注过近两年的技术趋势,会发现“AI Agent”已经从一个前沿概念,变成了开发者工具箱里越来越常见的组件。无论是自动化代码审查、智能文档生成,还是复杂的业务流程编排,背后往往都有一个或多个Agent在协同工作。然而,当我们想亲手打造一个属于自己的Agent,并将其封装成一个命令行工具(CLI)时,常常会陷入一种困境:网上充斥着各种“五分钟快速入门”的框架教程,但当你真正想构建一个结构清晰、易于维护、测试完备的生产级项目时,却发现无从下手。框架帮你解决了“从0到0.5”的问题,但“从0.5到1”的工程化之路,才是决定项目能否长期健康发展的关键。

这正是我们启动这个系列的原因。我们不打算只教你用某个现成的框架“跑起来”一个Demo,而是要深入工程实践的腹地,从最基础的 项目初始化 工程基建 开始,一步步搭建一个高标准的TypeScript CLI项目骨架。这个骨架将具备现代前端工程的所有优秀特质:严格的类型安全、自动化的代码质量检查、单元测试覆盖、以及可复用的构建发布流程。你可以把它看作是为你的Agent大脑打造一个强健、可靠的“身体”。

为什么选择CLI作为载体?因为命令行是开发者与系统交互最直接、最强大的界面。一个设计良好的CLI工具,能够无缝融入开发者的工作流,无论是本地调试、CI/CD流水线,还是服务器端的自动化脚本。将你的Agent能力通过CLI暴露出来,意味着它具备了极强的可集成性和可扩展性。

在第一篇中,我们将聚焦于“基建”。这听起来可能不如直接实现AI功能那么激动人心,但我以多年的项目经验告诉你,前期在工程规范上投入的每一分钟,都会在后续的开发、协作和迭代中十倍地回报你。我们将使用TypeScript作为开发语言,搭配ESLint、Prettier、Vitest等工具链,目标是建立一个即便项目复杂度和团队规模增长,也能保持代码整洁和开发体验流畅的坚实基础。

2. 核心需求与工具选型解析

在动手写第一行代码之前,我们必须想清楚:我们要构建的究竟是一个什么样的CLI?它需要满足哪些核心需求?基于这些需求,我们又该如何选择技术栈?

2.1 核心需求定义

一个面向生产的Agent CLI,其核心需求远不止“能运行”那么简单:

  1. 类型安全与开发体验 :Agent的逻辑可能涉及复杂的数据结构(如LLM的请求/响应、工具调用规范)。静态类型检查能在编码阶段就捕获大量潜在错误,提供卓越的代码提示和重构能力,这对提升开发效率和代码可靠性至关重要。
  2. 代码质量与一致性 :当多人协作或项目长期演进时,统一的代码风格和避免常见的错误模式是维持代码库健康的基础。我们需要自动化工具来强制执行这些规范。
  3. 可测试性 :Agent的核心是逻辑与决策。我们必须能够方便地对这些逻辑进行单元测试、集成测试,确保每次修改都不会破坏现有功能,并为重构提供信心。
  4. 良好的开发者体验(DX) :包括清晰的错误提示、丰富的命令行帮助、易于理解的参数解析以及平滑的调试流程。
  5. 可维护性与可扩展性 :项目结构应该清晰,模块职责分明,便于后续添加新的命令、集成新的AI模型或工具。
  6. 打包与分发 :最终产物应该是一个可以全局安装、独立运行的二进制文件,并且最好能支持跨平台。

2.2 技术栈选型与理由

基于以上需求,我们做出如下选型,并解释其背后的考量:

  • 语言:TypeScript

    • 理由 :它是满足“类型安全”需求的不二之选。对于CLI项目,其强类型系统能完美约束命令行参数、配置对象以及Agent内部复杂的流程状态。相较于纯JavaScript,它能极大减少运行时因类型错误导致的崩溃。社区生态和类型定义文件也极其完善。
  • 包管理与项目初始化: npm init / pnpm init

    • 理由 npm 是Node.js生态的事实标准。近年来, pnpm 因其更快的速度和高效的磁盘空间利用(通过硬链接和符号链接)而备受青睐,尤其适合Monorepo场景。本系列将使用 pnpm ,但其命令与 npm 大多兼容。选择哪个取决于团队偏好, pnpm 在性能上确有优势。
  • 代码质量工具链:ESLint + Prettier

    • ESLint :用于识别并报告JavaScript/TypeScript代码中的问题模式,确保代码质量。我们可以配置适用于TypeScript的规则集(如 @typescript-eslint ),并继承一些优秀的开源配置(如Airbnb、Standard),快速建立规范。
    • Prettier :一个“有主见”的代码格式化工具。它接管了所有关于代码风格的决策(缩进、分号、引号等),让开发者从无休止的风格争论中解放出来,并与ESLint分工合作(ESLint负责代码质量问题,Prettier负责风格问题)。
  • 测试框架:Vitest

    • 理由 :这是一个基于Vite的下一代测试框架。它与Vite共享配置、转换器和解析器,速度极快,对TypeScript和ES模块的支持是原生级的。其API与Jest高度兼容,学习成本低,但拥有更优秀的开发体验(如智能监听、模块级模拟)。对于新项目,Vitest是比Jest更现代、更快速的选择。
  • 构建与打包工具: tsup tsx

    • 理由 :我们需要将TypeScript源代码编译、打包成单一的、可在Node.js环境运行的JavaScript文件。 tsup 基于esbuild,配置极其简单,打包速度飞快,非常适合CLI工具。它支持生成CommonJS和ESM格式,并能将依赖打包进去(通过 --bundle ),减少用户环境依赖问题。 tsx 则更侧重于开发时的即时执行,可以作为 tsup 的补充。
  • 命令行框架: commander.js cac

    • 理由 :解析命令行参数、生成帮助信息是CLI的基础功能。使用成熟的框架能避免重复造轮子,处理复杂的子命令、选项、参数验证等。 commander.js 历史悠久、功能全面、文档完善; cac 更轻量、更现代。我们将根据项目复杂度进行选择。

注意 :工具选型没有绝对的“最佳”,只有“最适合”。这里的选择是基于当前(2024年)社区主流趋势、项目需求和个人偏好的平衡。例如,如果你对Jest更熟悉,完全可以用Jest替代Vitest,核心的工程化思想是相通的。

3. 项目初始化与基础配置实操

理论说再多,不如动手做。现在,让我们打开终端,开始搭建项目。

3.1 创建项目并初始化

首先,为你的Agent CLI项目创建一个新目录并进入。

mkdir my-agent-cli && cd my-agent-cli

接着,初始化 package.json 。这里我使用 pnpm ,如果你用 npm ,将 pnpm 替换为 npm 即可。

pnpm init

你会被提示输入项目名称、版本、描述等信息。这里可以先快速按回车使用默认值,我们稍后再来修改 package.json 。一个更高效的方式是使用 -y 参数快速生成默认配置:

pnpm init -y

现在,你的目录下应该有了一个基础的 package.json 文件。

3.2 安装TypeScript及基础依赖

安装TypeScript作为开发依赖,同时安装Node.js的类型定义,因为我们的CLI会运行在Node环境中。

pnpm add -D typescript @types/node

-D 参数表示将这些包安装在 devDependencies 中,因为它们是开发工具,最终用户不需要。

接下来,生成TypeScript配置文件 tsconfig.json

npx tsc --init

这个命令会创建一个包含大量默认选项(大部分被注释掉)的 tsconfig.json 文件。对于CLI项目,我们需要一个针对性更强的配置。让我们直接替换其内容:

{
  "compilerOptions": {
    /* 基础选项 */
    "target": "ES2022", // 编译目标ES版本,现代Node.js支持ES2022
    "module": "ESNext", // 使用ES模块,便于tree-shaking和现代打包工具
    "lib": ["ES2022"],
    "moduleResolution": "bundler", // 配合现代打包工具的解析策略
    "resolveJsonModule": true, // 允许导入JSON文件
    "allowSyntheticDefaultImports": true,

    /* 类型检查与严格模式 */
    "strict": true, // 启用所有严格类型检查选项
    "skipLibCheck": true, // 跳过库文件的类型检查以提升速度
    "noUnusedLocals": true, // 报告未使用的局部变量
    "noUnusedParameters": true, // 报告未使用的函数参数
    "noFallthroughCasesInSwitch": true, // 防止switch语句贯穿

    /* 输出控制 */
    "outDir": "./dist", // 编译输出目录
    "rootDir": "./src", // 源代码根目录
    "declaration": true, // 生成.d.ts类型声明文件
    "declarationMap": true, // 为声明文件生成sourcemap
    "sourceMap": true, // 为JavaScript生成sourcemap,便于调试

    /* 其他 */
    "esModuleInterop": true, // 改善CommonJS和ES模块的互操作性
    "forceConsistentCasingInFileNames": true // 强制文件名大小写一致
  },
  "include": ["src/**/*"], // 包含src目录下所有文件
  "exclude": ["node_modules", "dist", "**/*.test.ts", "**/*.spec.ts"] // 排除不需要编译的文件
}

关键配置解读

  • “target”: “ES2022” :Node.js 18+ 已良好支持 ES2022 特性,选择较新的目标版本能生成更简洁的代码。
  • “module”: “ESNext” “moduleResolution”: “bundler” :这是为使用 tsup Vite 等现代打包工具做的准备,它们能更好地处理 ES 模块。
  • “outDir” “rootDir” :清晰地分离源代码( src )和编译产物( dist ),这是保持项目结构清晰的好习惯。
  • “strict”: true 强烈建议开启 。严格的类型检查初期可能会让你多写一些类型注解,但它能避免无数潜在的运行时错误,是TypeScript价值的核心体现。

3.3 配置ESLint与Prettier(代码质量守卫)

首先,安装ESLint及其相关插件:

pnpm add -D eslint @typescript-eslint/parser @typescript-eslint/eslint-plugin eslint-config-prettier
  • eslint : ESLint核心库。
  • @typescript-eslint/parser : 使ESLint能够解析TypeScript语法。
  • @typescript-eslint/eslint-plugin : 提供针对TypeScript的linting规则。
  • eslint-config-prettier : 关闭所有与Prettier冲突的ESLint规则,让两者和谐共处。

接下来,创建ESLint配置文件 .eslintrc.cjs (使用 .cjs 扩展名确保在Node环境下能被正确识别为CommonJS模块):

// .eslintrc.cjs
module.exports = {
  root: true, // 表明这是根配置文件,ESLint将不再向上查找
  env: {
    node: true, // 启用Node.js全局变量和语法
    es2022: true, // 启用ES2022全局变量
  },
  parser: '@typescript-eslint/parser', // 指定TypeScript解析器
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module', // 使用ES模块
    project: './tsconfig.json', // 告诉ESLint你的tsconfig位置,用于基于类型的规则
  },
  plugins: ['@typescript-eslint'], // 加载TypeScript插件
  extends: [
    'eslint:recommended', // ESLint推荐规则
    'plugin:@typescript-eslint/recommended-type-checked', // TS推荐规则(需要类型信息)
    'plugin:@typescript-eslint/stylistic-type-checked', // TS风格规则
    'prettier', // 必须放在最后,用于禁用与Prettier冲突的规则
  ],
  rules: {
    // 可以在这里覆盖或添加自定义规则
    '@typescript-eslint/no-unused-vars': [
      'error',
      { argsIgnorePattern: '^_', varsIgnorePattern: '^_' },
    ], // 允许以下划线开头的变量未使用(常用于忽略参数)
  },
  ignorePatterns: ['dist', 'node_modules', '*.cjs', '*.js'], // 忽略这些目录和文件
};

一个常见陷阱 :如果你在配置中启用了需要类型信息的规则(如 recommended-type-checked ),但在VSCode或其他编辑器里ESLint仍然报错,提示找不到某些模块或类型(例如网络热词中提到的“amap is undefined”),这通常是因为ESLint没有正确读取到 tsconfig.json 中的 compilerOptions.paths 别名配置,或者项目依赖没有完全安装。确保:

  1. parserOptions.project 路径正确。
  2. 运行 pnpm install 确保所有依赖已安装。
  3. 在VSCode中,可以尝试重启ESLint服务器或重新加载窗口。

现在,安装并配置Prettier:

pnpm add -D prettier

创建Prettier配置文件 .prettierrc.json

{
  "semi": true,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "es5",
  "printWidth": 100,
  "endOfLine": "lf"
}

这些是常见的Prettier配置,定义了分号、单引号、缩进等格式规则。你可以根据团队习惯调整。

最后,我们需要一个机制让ESLint和Prettier在代码保存时自动运行。这通常由编辑器的插件(如VSCode的ESLint和Prettier插件)配合设置实现。但为了确保团队一致性,我们还可以在 package.json 中定义脚本,并配置 lint-staged husky 在提交前自动检查。这是更高级的工程化步骤,我们可以在后续篇章展开。

3.4 配置Vitest(测试堡垒)

安装Vitest及相关工具:

pnpm add -D vitest @vitest/ui
  • vitest : 测试框架本身。
  • @vitest/ui : 提供一个漂亮的图形化测试界面,便于调试和查看覆盖率。

创建Vitest配置文件 vitest.config.ts

import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    globals: true, // 类似Jest,允许使用describe, it, expect等全局API
    environment: 'node', // 测试环境设为Node.js
    include: ['src/**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], // 测试文件匹配模式
    coverage: {
      provider: 'v8', // 使用Node.js内置的V8覆盖率收集器
      reporter: ['text', 'json', 'html'], // 生成多种格式的覆盖率报告
      exclude: ['**/*.test.ts', '**/*.spec.ts', 'dist', 'node_modules'], // 排除项
    },
  },
});

现在,更新 package.json ,添加测试脚本:

{
  "scripts": {
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:coverage": "vitest run --coverage",
    "type-check": "tsc --noEmit" // 只做类型检查,不输出文件
  }
}

让我们立刻验证一下。创建源代码目录和第一个测试文件:

mkdir src
// src/math.test.ts
import { describe, it, expect } from 'vitest';

function add(a: number, b: number): number {
  return a + b;
}

describe('math functions', () => {
  it('should add two numbers correctly', () => {
    expect(add(1, 2)).toBe(3);
    expect(add(-1, 5)).toBe(4);
  });
});

运行测试:

pnpm test

你应该能看到测试通过的输出。运行 pnpm test:ui 可以打开浏览器查看图形化界面。

3.5 配置构建脚本与入口

我们的CLI需要一个入口点。创建 src/cli.ts 作为主入口文件:

#!/usr/bin/env node
// 上面的shebang行告诉系统这个文件应该用Node.js来执行

import { program } from 'commander'; // 我们稍后会安装commander

program
  .name('my-agent') // 你的CLI工具名
  .description('一个强大的AI Agent命令行工具')
  .version('0.1.0'); // 从package.json读取版本号更好,这里先写死

program.parse(process.argv);

现在,我们需要一个构建脚本,将TypeScript编译打包成可执行文件。安装 tsup

pnpm add -D tsup

package.json 中添加构建脚本和 bin 字段, bin 字段用于指定当用户全局安装你的包时,哪个命令对应哪个可执行文件。

{
  "name": "my-agent-cli",
  "version": "0.1.0",
  "description": "A powerful AI Agent CLI tool",
  "main": "dist/cli.js",
  "bin": {
    "my-agent": "./dist/cli.js" // 命令名: 入口文件路径
  },
  "scripts": {
    "build": "tsup src/cli.ts --format cjs,esm --dts --clean --minify",
    "dev": "tsup src/cli.ts --format cjs --watch",
    "start": "node dist/cli.js",
    // ... 之前添加的test脚本
  },
  // ... 其他字段
}

tsup 配置解释:

  • src/cli.ts : 入口文件。
  • --format cjs,esm : 同时生成CommonJS和ES Module格式。
  • --dts : 生成类型声明文件( .d.ts )。
  • --clean : 构建前清理 dist 目录。
  • --minify : 压缩代码。
  • --watch (在 dev 脚本中): 监听文件变化并重新构建。

现在,运行 pnpm build ,你会在 dist 目录下看到生成的文件。为了在开发时方便地测试CLI,我们可以使用 pnpm link 在本地创建一个全局软链接:

# 在项目根目录执行
pnpm link --global

执行后,理论上你就可以在终端任何地方运行 my-agent 命令了。它会执行 dist/cli.js 。试试看:

my-agent --help

你应该能看到 commander 生成的帮助信息(目前只有版本和描述)。

4. 工程化进阶:Git Hooks与自动化工作流

基础配置完成后,我们可以引入一些“守卫”来自动化代码质量流程,确保提交到仓库的代码是符合规范的。

4.1 配置Husky与lint-staged

Husky 允许我们方便地定义Git钩子(如 pre-commit , pre-push )。 lint-staged 则允许我们对**暂存区(staged)**的文件运行特定的脚本,避免每次提交都检查整个项目。

安装依赖:

pnpm add -D husky lint-staged

初始化Husky:

npx husky init

这个命令会创建 .husky 目录,并在其中添加 pre-commit 钩子文件。同时,它会在 package.json 中添加一个 “prepare”: “husky install” 脚本。

现在,配置 lint-staged 。在 package.json 中添加:

{
  // ... 其他配置
  "lint-staged": {
    "*.{js,ts,jsx,tsx}": [
      "eslint --fix --max-warnings=0", // 对暂存区的JS/TS文件运行ESLint并自动修复
      "prettier --write" // 运行Prettier格式化
    ],
    "*.{json,md,yml,yaml}": [
      "prettier --write" // 格式化其他类型的文件
    ]
  }
}

然后,修改 .husky/pre-commit 文件,将其内容替换为:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged
npx vitest run --changed # 可选:对更改的文件运行测试

这样,每次执行 git commit 时, lint-staged 会自动对暂存区中符合条件的文件执行ESLint修复和Prettier格式化。如果ESLint有无法自动修复的错误或测试失败,提交就会被阻止。

4.2 完善package.json与项目结构

让我们回头完善一下 package.json ,添加一些元信息和更合理的脚本。

{
  "name": "my-agent-cli",
  "version": "0.1.0",
  "description": "A powerful AI Agent CLI tool built with TypeScript",
  "keywords": ["cli", "agent", "ai", "typescript", "automation"],
  "license": "MIT",
  "author": "Your Name <your.email@example.com>",
  "homepage": "https://github.com/your-username/my-agent-cli#readme",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/your-username/my-agent-cli.git"
  },
  "bugs": {
    "url": "https://github.com/your-username/my-agent-cli/issues"
  },
  "type": "module", // 声明包使用ES模块,影响Node.js如何解析导入
  "main": "./dist/cli.js",
  "module": "./dist/cli.mjs", // 为ESM打包工具提供入口
  "types": "./dist/cli.d.ts", // 类型声明文件入口
  "bin": {
    "my-agent": "./dist/cli.js"
  },
  "files": ["dist"], // 发布到npm时只包含dist目录
  "scripts": {
    "dev": "tsup src/cli.ts --format cjs --watch",
    "build": "tsup src/cli.ts --format cjs,esm --dts --clean --minify",
    "start": "node dist/cli.js",
    "lint": "eslint . --ext .ts,.js --fix --max-warnings=0",
    "format": "prettier --write .",
    "type-check": "tsc --noEmit",
    "test": "vitest",
    "test:ui": "vitest --ui",
    "test:run": "vitest run",
    "test:coverage": "vitest run --coverage",
    "prepublishOnly": "npm run build", // 在npm publish前自动构建
    "prepare": "husky install"
  },
  "engines": {
    "node": ">=18"
  },
  "publishConfig": {
    "access": "public"
  },
  // ... dependencies and devDependencies
}

项目结构梳理 : 至此,你的项目目录结构应该大致如下:

my-agent-cli/
├── .husky/              # Git钩子配置
│   └── pre-commit
├── .vscode/             # (可选)编辑器配置
├── dist/                # 构建输出目录(由tsup生成,应在.gitignore中)
├── src/                 # 源代码目录
│   ├── cli.ts          # CLI主入口
│   └── math.test.ts    # 示例测试文件
├── .eslintrc.cjs       # ESLint配置
├── .eslintignore       # (可选)ESLint忽略文件配置
├── .prettierrc.json    # Prettier配置
├── .prettierignore     # (可选)Prettier忽略文件配置
├── .gitignore          # Git忽略文件配置
├── vitest.config.ts    # Vitest配置
├── tsconfig.json       # TypeScript配置
├── package.json        # 项目配置和依赖
└── pnpm-lock.yaml      # pnpm锁文件(如果是npm则是package-lock.json)

务必在 .gitignore 文件中添加 dist/ , node_modules/ , coverage/ 等目录。

5. 常见问题与排查技巧实录

在搭建这套基建的过程中,你几乎一定会遇到一些问题。以下是我在实际操作中踩过的坑和解决方案,希望能帮你快速排雷。

5.1 TypeScript配置与模块解析问题

问题 :在 src/cli.ts 中导入其他模块时,VS Code提示“找不到模块”或“没有默认导出”,但运行 tsc --noEmit 类型检查却通过。 排查

  1. 检查 tsconfig.json 中的 “moduleResolution” 。如果你使用了 “bundler” ,确保你的打包工具(如 tsup )支持它。对于更传统的Node.js项目,可以尝试改为 “node16” “nodenext”
  2. 检查导入路径是否正确。使用相对路径( ‘./utils’ )或配置了 “paths” 的别名。
  3. 重启TypeScript语言服务器。在VS Code中,按 Cmd+Shift+P (Mac) 或 Ctrl+Shift+P (Windows/Linux),输入并选择“TypeScript: Restart TS Server”。

5.2 ESLint与Prettier冲突或报错

问题 :保存文件时,ESLint和Prettier互相“打架”,或者ESLint报告一些奇怪的语法错误(如网络热词中的 object-curly-spacing 错误)。 解决

  1. 确保配置顺序正确 :在 .eslintrc.cjs extends 数组中, ‘prettier’ 必须放在 最后 ,以确保它能覆盖其他配置中与Prettier冲突的规则。
  2. 检查规则覆盖 :有时某个插件(如 @typescript-eslint )的规则会与Prettier冲突。 eslint-config-prettier 就是用来解决这个的。确保你已安装并正确扩展了它。
  3. 规则具体配置 :像 object-curly-spacing 这类风格规则,应该完全交给Prettier处理。如果ESLint还在报错,可以在 .eslintrc.cjs rules 中显式关闭它: “object-curly-spacing”: “off” 。但更推荐的做法是确保 eslint-config-prettier 生效。
  4. 编辑器插件设置 :在VS Code中,确保设置了 “editor.formatOnSave”: true ,并且 “editor.defaultFormatter” 是你的Prettier插件。同时,确保ESLint插件已启用并配置为在保存时运行。有时需要设置 “eslint.validate” 包含 “typescript”

5.3 Vitest测试无法识别或运行缓慢

问题 pnpm test 找不到测试文件,或者测试运行异常缓慢。 排查

  1. 检查配置文件 :确认 vitest.config.ts 中的 test.include 模式能匹配到你的测试文件。测试文件通常以 .test.ts .spec.ts 结尾。
  2. 检查环境 :如果你的代码依赖浏览器API(如 document , window ),但测试环境配置为 “node” ,就会出错。对于CLI项目,环境应为 “node” 。如果部分代码需要浏览器环境,可以考虑使用 jsdom 环境,或使用 vi.mock 进行模拟。
  3. 速度问题 :首次运行Vitest可能会稍慢,因为它要收集和转换测试。后续运行在 watch 模式下会很快。如果一直很慢,检查是否在测试中引入了巨大的模块或进行了真实的网络请求/文件IO。尽量使用模拟(mock)。

5.4 构建产物无法运行或行为异常

问题 pnpm build 成功,但运行 node dist/cli.js 或全局链接后的命令报错(如 Cannot find module )。 排查

  1. 检查shebang :确保 src/cli.ts 第一行是 #!/usr/bin/env node
  2. 检查 package.json 中的 bin 字段 :路径是否正确指向构建后的文件(例如 ./dist/cli.js )。
  3. 检查依赖打包 :如果你的CLI依赖了某些第三方包,默认情况下 tsup 不会将它们打包进输出文件。这意味着用户安装你的CLI时,必须同时安装这些依赖。对于简单的CLI,可以将依赖放在 dependencies 中。对于希望生成单一可执行文件的情况,可以使用 tsup --bundle 标志(注意处理原生模块等边界情况)。更复杂的打包可以考虑 pkg nexe
  4. 文件权限 :在Unix系统上,构建后的JS文件需要有可执行权限。 tsup 通常不会设置这个权限。你可以在 package.json “scripts” 中添加一个后置脚本,例如在build后执行 chmod +x dist/cli.js 。或者,更规范的做法是,在npm publish后,由npm在全局安装时自动处理。

5.5 Git Hooks不生效

问题 :配置了Husky和lint-staged,但提交时代码没有被检查或格式化。 排查

  1. Husky是否安装成功 :确保项目根目录下有 .husky 文件夹,并且里面的钩子文件(如 pre-commit )有可执行权限(在Unix系统上)。你可以手动运行 chmod +x .husky/pre-commit
  2. prepare 脚本是否运行 :Husky的安装通常由 npm install pnpm install 后自动运行的 prepare 脚本触发。如果你克隆了一个已有项目,可能需要手动运行一次 pnpm prepare pnpm install
  3. lint-staged配置 :检查 package.json 中的 lint-staged 配置路径和命令是否正确。可以手动运行 npx lint-staged 来测试。
  4. Git版本 :确保你的Git版本在2.9以上,Husky对旧版本支持可能有问题。

搭建一个坚实的工程基础,就像为高楼打下地基。虽然前期花费了一些时间在配置上,但当你开始真正编写Agent业务逻辑时,你会感谢现在所做的一切:代码提示精准、错误在编写时就被捕获、代码风格统一、测试运行迅速、提交前自动检查。这一切都将使你的开发体验变得顺畅,并极大地提升项目的可维护性。

在下一篇中,我们将在这个坚实的基建之上,开始设计并实现我们Agent CLI的核心命令解析架构,并接入第一个AI模型,让我们的工具真正“智能”起来。你会发现,有了好的工程习惯,添加新功能将变得有条不紊,水到渠成。

更多推荐