首页 › tutorials › Claude Code零基础装通并造首个CLI

Claude Code 零基础装通并造首个 CLI

概述

本教程面向从未用过 Claude Code 的开发者,目标是 30 分钟内完成一条完整闭环:安装 → 认证 → 写第一份 CLAUDE.md → 配置 settings.json 权限 → 跑通第一个真实项目。我们不会停留在"敲一个 hello world",而是带你亲手造一个命令行 Markdown 转静态站点生成器(mdsite)——它能读取目录、批量转换、生成带样式的 HTML,足以让你体会 Claude Code 读文件、写文件、跑命令、按约定工作的全部能力。适合学生、转岗工程师、想从"复制粘贴聊天框"切换到"终端里跑 Agent"的任何人。

前置条件

只需三样东西:

  1. Node.js 18 或更高版本。Claude Code 以 npm 包形式分发,靠 Node 运行。检查版本: bash node --version # 期望 v18.0.0 以上 若未安装,用 nvm(nvm install --lts)或直接到 nodejs.org 下载安装包。

  2. 一个 Anthropic 账号。三种身份任选其一: - API 按量付费:在 console.anthropic.com 的 Settings → API Keys 生成密钥,按 token 计费(典型 30–60 分钟会话约 \$0.50–\$3.00)。 - Max 订阅:个人 \$100/月、团队 \$200/月(截至 2026 年 3 月),内含 Claude Code 用量与更高限额。 - 企业 SSO:由 IT 统一发放。

  3. 任意终端。Terminal.app、iTerm2、Windows Terminal、Alacritty 或 VS Code 内置终端皆可。建议宽度 ≥ 120 列,因为 Claude Code 会展示文件 diff 与工具输出,横向空间越大越舒服。

⚠️ Windows 用户:可在 WSL(Windows Subsystem for Linux) 中获得最稳定的体验;原生 Windows 也已支持,但路径与脚本兼容性以 WSL 更佳。


正文:分步装通

步骤 1:安装 Claude Code

最通用的方式是全局安装 npm 包:

npm install -g @anthropic-ai/claude-code

安装后验证:

claude --version

预期结果:终端打印一串版本号(如 2.x.x),即安装成功。

常见问题:command not found 表示 npm 的全局 bin 目录不在 PATH 里。查一下前缀:

npm config get prefix

把返回值下的 /bin 子目录加进 shell 配置(~/.zshrc 或 ~/.bashrc):

export PATH="$(npm config get prefix)/bin:$PATH"

随后 source ~/.zshrc 生效。

常见问题:EACCES 权限错误 安装时报 EACCES,不要用 sudo。正确做法是让 npm 用一个用户可写目录:

mkdir -p ~/.npm-global
npm config set prefix '~/.npm-global'
# 再写入 shell 配置:
export PATH="$HOME/.npm-global/bin:$PATH"

重新执行 npm install -g 即可。

📌 除 npm 外,Anthropic 也提供平台原生安装器(macOS/Linux 的 shell 脚本、Windows 安装包),适合不想装 Node 的场景。本教程以 npm 为主,因为它跨平台、易升级(npm update -g @anthropic-ai/claude-code)。

步骤 2:认证身份

Claude Code 的凭证只存在你本机(~/.claude/ 下),用来认证 API 请求,不会外传。两条认证路径:

路径 A:浏览器 OAuth(推荐新手) 在终端直接运行 claude,首次启动会自动打开浏览器跳转到 Anthropic 控制台,你登录并授权后,token 即存入本地。

路径 B:环境变量 API Key(适合 CI/脚本)

export ANTHROPIC_API_KEY="sk-ant-xxxxxxxx"

把上面这行写进 ~/.zshrc,注意别提交到 git。

验证认证:运行 claude 进入交互界面后,输入一句简单问候,若收到回复,说明认证已通。

步骤 3:跑通第一个会话

新建一个项目目录并进入:

mkdir -p ~/Projects/mdsite && cd ~/Projects/mdsite
claude

Claude Code 启动后是一个交互式 REPL。新项目里它会自动做三件事:

  1. 扫描目录结构,理解项目布局;
  2. 读取 package.json、pyproject.toml、Cargo.toml 等配置识别技术栈;
  3. 在项目根查找 CLAUDE.md 获取项目专属指令。

试一句确认一切就绪:

> 解释一下当前这个目录的结构

你会看到两样关键东西:

💡 黄金法则:先观察、再放行。前几轮把每次审批都看一遍,你能快速建立"哪些操作安全、哪些值得警惕"的直觉。

步骤 4:写第一份 CLAUDE.md

CLAUDE.md 是整个体系里杠杆最高的一个文件。没有它,Claude 只能靠读代码猜你的约定,而"猜"会漂移——可能在一个 ESM 项目里写 require(),或用错测试运行器。有了它,从第一个 prompt 起就严格遵循你的规范。

Claude Code 采用三级层次,从高到低合并(高优先级覆盖低优先级):

层级 位置 作用域 是否提交 git
个人项目级 CLAUDE.local.md 仅你自己 否(gitignore)
团队项目级 ./CLAUDE.md 整个团队 是
用户全局级 ~/.claude/CLAUDE.md 你所有项目 否

给本项目新建 CLAUDE.md:

touch CLAUDE.md

填入一个精简实用的模板(务必控制在 200 行内,否则指令遵循率会下降):

# mdsite —— Markdown 静态站点生成器

## 技术栈
- 运行时:Node.js 20
- 语言:纯 JavaScript(CommonJS),无外部依赖
- 测试:Node 内置 node:test

## 命令
- `node bin/mdsite.js build ./content ./dist`  构建站点
- `node --test`  运行测试
- `node bin/mdsite.js serve ./dist 8080`  本地预览

## 约定
- 所有入口逻辑放在 bin/mdsite.js,核心函数放在 lib/
- 输入输出统一用 utf-8
- 转换函数必须是纯函数,便于单测
- HTML 模板统一从 lib/template.js 取

## 重要
- 不要引入任何 npm 依赖,保持零依赖
- 每个导出函数都要有对应的 node:test 测试

预期结果:下次会话,Claude 会立刻知道用 CommonJS、用 node:test、保持零依赖,无需你反复提醒。

步骤 5:配置 settings.json 权限

权限是 Claude Code 的安全核心。它有三层配置文件,更具体的覆盖更宽泛的:

文件 作用 共享
~/.claude/settings.json 你的全局默认 否
.claude/settings.json 团队项目配置 是(git)
.claude/settings.local.json 你的本地覆盖 否(gitignore)

权限规则有三档语义:deny(永远禁止)、ask(每次询问)、allow(自动放行)。评估顺序是 deny → ask → allow。

给本项目新建 .claude/settings.json:

mkdir -p .claude && touch .claude/settings.json

写入一个新手友好的权限配置:

{
  "$schema": "https://json.schemastore.org/claude-code-settings.json",
  "permissions": {
    "allow": [
      "Read",
      "Glob",
      "Grep",
      "Bash(node --test)",
      "Bash(node --test*)",
      "Bash(node bin/mdsite.js *)",
      "Edit(./lib/**)",
      "Write(./lib/**)"
    ],
    "ask": [
      "Bash(npm *)"
    ],
    "deny": [
      "Bash(rm -rf *)",
      "Bash(curl *)",
      "Read(.env)",
      "Read(.env.*)"
    ],
    "defaultMode": "default"
  }
}

逐行解读:

关键提醒:永远不要写 Bash(*)。这条通配放行会让 Claude 无确认执行任意 shell 命令,等于拆掉整个权限系统的护栏。

💡 权限模式四选一:default(默认询问)、acceptEdits(自动接受文件编辑)、dontAsk(自动拒绝,已放行的仍可跑)、bypassPermissions(全部跳过,仅限无人值守的 CI,本地切勿使用)。

步骤 6:第一个项目实战——造 mdsite

现在让 Claude 动手。在交互界面输入(一次性把需求讲清,比挤牙膏式追问高效得多):

请按 CLAUDE.md 的约定,帮我实现 mdsite:
1. lib/markdown.js 导出两个纯函数:escapeHtml(str) 和 mdToHtml(md)
2. mdToHtml 至少支持:标题(#~######)、无序列表(- )、有序列表(1. )、段落、粗体(**x**)、代码块(反引号围栏)
3. lib/template.js 导出 wrapHtml(title, body) 套上基础 HTML 模板,带简单内联样式
4. lib/build.js 导出 buildSite(srcDir, outDir):扫描 srcDir 下所有 .md,转成同名 .html 写入 outDir
5. bin/mdsite.js 作为 CLI 入口,解析 process.argv,支持 build <src> <out> 子命令
6. 给每个纯函数写 node:test 测试放 test/*.test.js
最后运行 node --test 全部通过,并执行 node bin/mdsite.js build ./content ./dist 跑一次构建。

预期结果:Claude 会读 CLAUDE.md、规划结构、逐个创建文件,每次写文件前(按你的 settings.json)在 lib/ 内自动放行、其它位置征求确认。最终跑通测试并生成 ./dist。

下面给出核心实现,你可以直接对照 Claude 的产物:

lib/markdown.js:

// 纯函数:HTML 转义,防注入
function escapeHtml(str) {
  return String(str)
    .replace(/&/g, '&amp;')
    .replace(/</g, '&lt;')
    .replace(/>/g, '&gt;');
}

// 纯函数:极简 Markdown → HTML
function mdToHtml(md) {
  const lines = md.split('\n');
  const out = [];
  let inUL = false, inOL = false, inCode = false;
  const closeLists = () => {
    if (inUL) { out.push('</ul>'); inUL = false; }
    if (inOL) { out.push('</ol>'); inOL = false; }
  };
  for (const raw of lines) {
    const line = raw;
    if (line.startsWith('```')) {           // 代码块围栏
      if (inCode) { out.push('</code></pre>'); inCode = false; }
      else { closeLists(); out.push('<pre><code>'); inCode = true; }
      continue;
    }
    if (inCode) { out.push(escapeHtml(line)); continue; }
    if (/^#{1,6}\s/.test(line)) {           // 标题
      const level = line.match(/^#+/)[0].length;
      closeLists();
      out.push(`<h${level}>${inline(line.replace(/^#+\s/, ''))}</h${level}>`);
    } else if (/^-\s/.test(line)) {         // 无序列表
      if (!inUL) { closeLists(); out.push('<ul>'); inUL = true; }
      out.push(`<li>${inline(line.replace(/^-\s/, ''))}</li>`);
    } else if (/^\d+\.\s/.test(line)) {     // 有序列表
      if (!inOL) { closeLists(); out.push('<ol>'); inOL = true; }
      out.push(`<li>${inline(line.replace(/^\d+\.\s/, ''))}</li>`);
    } else if (line.trim() === '') {        // 空行
      closeLists();
    } else {                                // 段落
      closeLists();
      out.push(`<p>${inline(line)}</p>`);
    }
  }
  closeLists();
  if (inCode) out.push('</code></pre>');
  return out.join('\n');

  function inline(s) {                       // 行内:粗体 + 转义
    return escapeHtml(s).replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>');
  }
}

module.exports = { escapeHtml, mdToHtml };

lib/template.js:

function wrapHtml(title, body) {
  return `<!DOCTYPE html>
<html lang="zh">
<head><meta charset="utf-8"><title>${title}</title>
<style>body{font-family:system-ui,sans-serif;max-width:720px;margin:2rem auto;padding:0 1rem;line-height:1.7}
pre{background:#f4f4f4;padding:1rem;overflow:auto}code{background:#eee;padding:.1rem .3rem}</style>
</head><body><h1>${title}</h1>${body}</body></html>`;
}
module.exports = { wrapHtml };

lib/build.js:

const fs = require('fs');
const path = require('path');
const { mdToHtml } = require('./markdown');
const { wrapHtml } = require('./template');

function buildSite(srcDir, outDir) {
  if (!fs.existsSync(outDir)) fs.mkdirSync(outDir, { recursive: true });
  const files = fs.readdirSync(srcDir).filter(f => f.endsWith('.md'));
  const built = [];
  for (const file of files) {
    const md = fs.readFileSync(path.join(srcDir, file), 'utf-8');
    const base = path.basename(file, '.md');
    const html = wrapHtml(base, mdToHtml(md));
    fs.writeFileSync(path.join(outDir, base + '.html'), html, 'utf-8');
    built.push(base + '.html');
  }
  return built;
}
module.exports = { buildSite };

bin/mdsite.js:

#!/usr/bin/env node
const { buildSite } = require('../lib/build');
const [cmd, src, out] = process.argv.slice(2);
if (cmd === 'build' && src && out) {
  const built = buildSite(src, out);
  console.log(`已构建 ${built.length} 个页面 → ${out}/`);
} else {
  console.log('用法: mdsite build <srcDir> <outDir>');
  process.exit(1);
}

test/markdown.test.js:

const test = require('node:test');
const assert = require('node:assert');
const { escapeHtml, mdToHtml } = require('../lib/markdown');

test('escapeHtml 转义尖括号', () => {
  assert.strictEqual(escapeHtml('<b>'), '&lt;b&gt;');
});
test('标题转换', () => {
  assert.strictEqual(mdToHtml('# 标题'), '<h1>标题</h1>');
});
test('粗体与列表', () => {
  assert.ok(mdToHtml('**重点**').includes('<strong>重点</strong>'));
  assert.ok(mdToHtml('- 项').includes('<ul>'));
});

跑一次完整闭环:

mkdir -p content
echo -e "# 你好\n\n这是一篇 **测试** 文章。\n\n- 列表项一\n- 列表项二" > content/hello.md

node --test                      # 测试全绿
node bin/mdsite.js build ./content ./dist
cat dist/hello.html              # 看到带样式的完整 HTML

🎉 至此你已完成:安装、认证、CLAUDE.md 约定、settings.json 权限、一个能跑的真实项目。


实战案例小结

mdsite 虽小,却串起了 Claude Code 的核心工作面:读目录(Glob/Read)→ 理解约定(CLAUDE.md)→ 写代码(Edit/Write 受 settings.json 约束)→ 跑测试与构建(Bash 受 allow 规则约束)→ 产出文件。当你想新增"支持表格语法"或"加 serve 预览命令",只需在交互界面提需求,Claude 会顺着既有约定扩展——这正是 CLAUDE.md + settings.json 组合的复利效应。


工作流程图

下面这张图展示了从安装到首跑的完整闭环,以及权限决策如何贯穿其中:

flowchart TD
    A[安装: npm i -g @anthropic-ai/claude-code] --> B[认证: OAuth 或 API Key]
    B --> C[cd 进项目目录, 运行 claude]
    C --> D[自动扫描结构 + 读 CLAUDE.md]
    D --> E{你发出 prompt}

    E --> F[Agent 规划工具调用]
    F --> G{权限决策}
    G -->|命中 deny| H[直接拒绝]
    G -->|命中 allow| I[自动执行]
    G -->|命中 ask / 默认| J[弹窗征求你同意]
    J -->|同意| I
    J -->|拒绝| K[放弃该操作]

    I --> L[读文件 / 写代码 / 跑命令]
    L --> M{PostToolUse Hook?}
    M -->|有| N[自动格式化 / lint]
    N --> O[产出结果]
    M -->|无| O

    O --> P[你审阅 git diff]
    P -->|满意| Q[git commit]
    P -->|不满意| R[git checkout 回滚]

    style G fill:#fff3cd,stroke:#856404
    style Q fill:#d4edda,stroke:#155724
    style H fill:#f8d7da,stroke:#721c24

权限三层配置如何合并(更具体覆盖更宽泛):

flowchart LR
    M[Managed Policy<br/>IT 部署, 不可覆盖] --> U
    U[~/.claude/settings.json<br/>你的全局默认] --> P
    P[.claude/settings.json<br/>团队项目配置] --> L
    L[.claude/settings.local.json<br/>本地个人覆盖] --> R[最终生效配置<br/>allow/ask/deny 合并]

    style M fill:#e2e3e5,stroke:#383d41
    style L fill:#fff3cd,stroke:#856404
    style R fill:#d4edda,stroke:#155724

常见问题速查

现象 原因 解决
command not found: claude npm bin 不在 PATH 把 npm config get prefix 的 /bin 加入 PATH
安装报 EACCES 全局目录无写权限 设 npm prefix 到用户目录,勿用 sudo
Claude 不认 CLAUDE.md 已缓存旧内容 /clear 或重启会话;会话开始时才重读
误改了不该改的文件 allow 规则过宽 收窄到具体命令/路径;git checkout -- <file> 回滚
长会话上下文爆炸 200K 窗口被压缩 每完成一个子任务就 commit,再 /clear 开新会话
JSON 配置不生效 语法错误致整文件失效 用 JSON 校验器检查逗号/括号

训练营适用性评估


参考来源

  1. Claude Code CLI Setup 2026: 5-Minute Quickstart — 完整的安装、认证、CLAUDE.md、权限与首个 Hook 的 5 分钟快速上手(v2.1.92,2026 年 3 月)。
  2. The Complete .claude Directory Guide for Claude Code [2026] — .claude 目录全结构详解:CLAUDE.md 三级层次、rules、commands、skills、agents、settings.json、hooks、MCP、memory 的实战示例。
  3. How to Configure Claude Code Settings (2026) | CAIO — 三层 settings 文件位置、优先级顺序、权限规则语法(deny→ask→allow)、模型与沙箱配置权威指南。
  4. Claude Code settings — Claude Code Docs — 官方全局/项目级配置与环境变量参考。
  5. Using CLAUDE.MD files: Customizing Claude Code for your codebase | Claude — 官方关于 CLAUDE.md 持久化上下文与最佳实践的说明。

[!tip] 51学通信知识星球 星主:爱卫生(微信:gprshome201101)——公众号「51学通信」创办者,19 年通信行业运维与讲师经验,著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书(各大电商平台有售)。 星球定位:深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域,主打运维岗位与信令分析。 当前规模:5200 篇主题 / 990 个回答,198 元/年。 加入福利:加入星球后请加爱卫生微信,拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题,另有群内分享。 AI 加持:51学通信知识星球已作为 skill 接入 WorkBuddy,使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例,做总结、找文档和 PPT 等场景全覆盖。

51学通信知识星球海报-扫码加入