Windows 上手 Claude Code - 从原生安装到首个全栈项目完整实战

Windows 10/11 原生安装 · 官方认证 · 零依赖全栈任务 · 测试验证 · 常见故障排查

前言

如果你最近搜索“Windows 安装 Claude Code”,很容易看到三种彼此冲突的教程:有的说必须先装 Node.js,再用 npm 全局安装;有的说 Windows 只能通过 WSL;还有的要求以管理员身份打开 PowerShell。问题是,这些说法里有相当一部分已经被 Claude Code 的新版本淘汰了。

截至 2026 年 8 月 26 日,Anthropic 官方文档已经把 Native Install(原生安装)列为推荐方式,Windows 10 1809+ 与 Windows Server 2019+ 可以直接运行 Claude Code。原生 Windows 下,Git for Windows 也已经从“必需”变成“可选”:安装它可以获得 Git Bash/Bash 工具;不安装时,Claude Code 可以回退使用 PowerShell 工具。

所以这篇文章不把目标停在“claude --version 能输出版本号”。我们会从一台 Windows 机器出发,完成安装、登录、健康检查、项目上下文配置,然后让 Claude Code 在一个空目录里搭出真正的前端 + 后端 API + 本地持久化,并通过自动化测试和浏览器访问完成验证。

本文适用范围

本文按 2026-08-26 的官方文档与当前 Windows 原生安装方式编写。Claude Code 更新频率很高,安装器、默认权限模式与界面文案后续可能变化;如果你在未来阅读,版本敏感步骤应优先以官方文档为准。

一、先把最容易踩坑的旧说法纠正掉

在真正动手之前,先把“安装教程为什么经常互相矛盾”讲清楚。Claude Code 在过去一年里经历了明显的平台与安装方式变化,旧教程并不一定是当时写错,而是今天已经不再是最优路径。

常见说法

截至 2026-08-26 的情况

本文采用的做法

“安装 Claude Code 必须先装 Node.js 18+”

对原生 Native Install 已不成立;npm 仍是高级安装选项之一

Claude Code 本体用原生安装;Node.js 只为后面的示例项目安装

“Windows 只能通过 WSL 使用”

已过时;官方支持原生 Windows,也支持 WSL 1/2

新手主线使用原生 Windows;Linux 工具链再考虑 WSL 2

“Git for Windows 是硬性依赖”

当前为可选;有 Git Bash 时可使用 Bash,没有则用 PowerShell

建议开发者安装,但不把它当安装前置条件

“必须管理员身份运行 PowerShell”

官方明确原生安装不需要管理员权限

普通 PowerShell 即可,避免无意义地提升权限

“免费 Claude.ai 账号装完就能用”

官方说明 Claude Code 需要 Pro/Max/Team/Enterprise、Console 或受支持云提供商

安装前先确认账号与访问条件,避免装完卡在登录

为什么先做这张纠偏表

安装类文章最怕“步骤看起来完整,但前提已经过时”。先确定当前支持边界,后面的每一步才有可复现意义。

二、Claude Code 到底是什么:不是聊天框,而是能执行工程动作的终端代理

Claude Code 的核心价值,不是把“问模型一句话”搬到终端,而是把模型放进真实项目目录,让它围绕代码库持续执行读取、修改、命令、测试、Git 等动作。你给的是目标,它需要自己寻找相关文件、理解上下文、做出修改,并根据命令输出继续修正。

2  Claude Code 的工程闭环:读取上下文、计划、修改、执行与验证

用一句更接近开发工作的描述:Claude Code 是一个“带工具的代码代理”。它可以理解项目结构、直接编辑文件、运行测试与构建命令、处理 Git 工作流,还能通过 CLAUDE.md、Skills、Hooks、MCP 等机制把个人或团队规范带进每一次会话。

2.1 第一次上手先关注四个能力

  • 理解代码库:不用手工把每个文件粘到聊天框,Claude Code 会按任务需要读取项目文件。
  • 直接改文件:它可以创建、编辑、重构文件,并把修改展示给你审阅。
  • 执行命令:可以运行构建、测试、Git 和项目命令,然后根据输出继续处理。
  • 保持项目上下文:通过 CLAUDE.md、会话恢复和项目配置减少重复说明。

2.2 它不是“全自动无风险模式”

能执行命令意味着能力更强,也意味着权限边界更重要。尤其在第一次会话中,看到文件写入、删除、覆盖、外部命令或敏感配置相关操作时,应该读清楚变更再授权。对于高风险项目,优先采用计划模式、版本控制和可回滚的数据副本,而不是为了省一次点击就跳过权限检查。

三、Windows 环境要求与安装路线选择

3.1 当前官方系统要求

项目

当前要求或建议

说明

操作系统

Windows 10 1809+ / Windows Server 2019+

Windows 11 同样支持

处理器

x64 或 ARM64

以当前官方支持架构为准

内存

4 GB+

实际大型项目建议预留更多内存给 IDE、浏览器和构建工具

网络

需要互联网连接

认证和模型调用需要网络;企业网络还可能涉及代理/证书

Shell

PowerShell、CMD;也可使用 Git Bash / WSL

本文使用 PowerShell

账号

Claude Pro/Max/Team/Enterprise、Claude Console 或受支持云提供商

免费 Claude.ai 计划当前不包含 Claude Code

3.2 三条 Windows 路线怎么选

3  Windows 下三种主流安装路线与适用场景

如果你只是想尽快跑通第一个项目,直接走原生 PowerShell 路线最省变量。如果你公司统一用 Windows 包管理,可以选 WinGet;如果你的项目强依赖 Linux 命令、容器/内核工具链,或者希望使用 Claude Code 沙箱,则优先 WSL 2。

四、主线方案:PowerShell 原生安装 Claude Code

4.1 先确认你现在打开的是 PowerShell

按 Win + X,打开“终端”或“Windows PowerShell”。PowerShell 提示符通常类似 PS C:\Users\你的用户名>。这一点很重要,因为 PowerShell 与 CMD 的安装命令不同。

两个最常见的命令复制错终端错误

如果看到 “irm 不是内部或外部命令”,通常说明你在 CMD;如果看到 “&& 不是有效的语句分隔符”,通常是把 CMD 命令粘进了 PowerShell。

4.2 执行官方 Native Install

irm https://claude.ai/install.ps1 | iex

这条命令会下载并运行 Anthropic 官方的 Windows 安装脚本。按当前官方说明,原生安装不需要以管理员身份运行;Native Install 还会在后台检查并安装更新。

4.3 验证安装

claude --version
claude doctor

第一条命令应该输出版本号以及 “Claude Code” 标识。第二条 claude doctor 是更有价值的健康检查:它会以只读方式检查安装状态、配置文件问题和已知警告,并给出对应修复建议。

推荐习惯

遇到“明明装了却不工作”的问题,不要第一反应就卸载重装。先跑 claude doctor,通常能更快定位 PATH、配置或安装状态问题。

4.4 WinGet 作为替代安装方式

winget install Anthropic.ClaudeCode

# 后续手动升级
winget upgrade Anthropic.ClaudeCode

WinGet 的优点是便于统一管理软件,但默认不会像 Native Install 一样自动更新。如果你更重视“少维护”,Native Install 更适合个人开发者;如果公司有固定软件分发流程,WinGet 更容易纳入管理。

五、第一次登录:先确认账号与网络条件,再排“安装问题”

5.1 直接启动 Claude Code

claude

第一次启动会进入登录流程。使用 Claude 订阅或 Console 账号时,终端会引导你在浏览器完成认证;以后如果需要切换账号或重新认证,可以在 Claude Code 会话里输入 /login。

5.2 当前支持的常见账号类型

  • Claude Pro、Max、Team 或 Enterprise 订阅。
  • Claude Console:通过预付费 API 额度使用;首次登录会创建用于 Claude Code 的工作区。
  • 企业云平台:Amazon Bedrock、Google Cloud 的 Agent Platform、Microsoft Foundry 等官方支持方式。

如果你已经设置了 ANTHROPIC_API_KEY 环境变量,Claude Code 会跳过普通浏览器登录提示,并让你确认是否使用该 Key。密钥不要写进代码、截图、博客、README 或 Git 仓库。

登录失败不要和安装失败混为一谈

“claude --version 能正常输出,但 claude 无法完成登录”说明 CLI 本体大概率已经安装成功。下一步应检查账号类型、地区支持、网络/企业代理以及认证状态,而不是反复重装。

六、Git for Windows 与 WSL 2:什么时候需要,什么时候不用折腾

6.1 Git for Windows 现在是“建议项”,不是硬前置

在原生 Windows 上,Git for Windows 的价值主要有两个:一是正常使用 Git;二是向 Claude Code 提供 Git Bash/Bash 工具。如果没有安装,Claude Code 当前可以使用 PowerShell 工具完成 shell 操作。

如果 Git 已经安装,但 Claude Code 找不到 Git Bash,可以在项目或用户 settings.json 中显式指定路径:

{
  "env": {
    "CLAUDE_CODE_GIT_BASH_PATH": "C:\\Program Files\\Git\\bin\\bash.exe"
  }
}

如果 Git 装在其他目录,可以先在 PowerShell 中运行 where.exe git 或 Get-Command git | Select-Object Source,再根据实际安装位置找到 bin\bash.exe。

6.2 WSL 2 更适合这几类项目

  • 日常开发本来就在 Ubuntu/Debian 环境里完成。
  • 项目依赖大量 Linux shell、包管理器、容器或系统工具。
  • 希望使用 Claude Code 的沙箱能力;当前原生 Windows 不支持沙箱,而 WSL 2 支持。

如果选择 WSL,安装和启动 Claude Code 都应在 WSL 终端中完成,不要一半在 PowerShell、一半在 WSL。把运行时、依赖和项目路径统一在同一个环境,可以显著减少“Windows Node + WSL npm”“路径格式混用”这类难排问题。

七、为第一个全栈项目准备 Node.js:注意,它是项目依赖,不是 Claude Code 安装依赖

下面的实战采用 Node.js,是因为我们要让第一个全栈项目尽量少装东西、少碰构建配置。这里刻意把“Claude Code 本体安装”和“示例项目运行环境”分开:前者已经通过 Native Install 完成,Node.js 只负责运行我们要生成的 Web 应用。

7.1 为什么推荐 Node.js 24 LTS

截至 2026 年 8 月 26 日,Node.js 官方发布页显示 v24(Krypton)处于 LTS,v26 处于 Current;v20 已在 2026 年 3 月进入 EOL。生产与教程环境优先选 LTS,可以减少框架和工具链的兼容性波动。

从 Node.js 官网安装当前 v24 LTS 后,重新打开 PowerShell:

node -v
npm -v

你不必和本文显示完全相同的小版本号,只要是当前受支持的 Node.js LTS 即可。

八、创建一个干净目录,让 Claude Code 从零接管

8.1 创建项目目录

mkdir windows-claude-todo
cd windows-claude-todo

# 如果你已经安装 Git,建议顺手初始化
git init

claude

Claude Code 启动后,先不要直接一句“帮我做个 Todo”。第一次任务的目标是建立一个可以验证的工程闭环,所以需求要同时告诉它:技术边界、功能边界、测试标准和完成标准。

8.2 先让它看清目标,再开始改文件

复杂任务更稳的方式是先要求它输出一个短计划,然后再执行。你也可以通过 Shift+Tab 切换权限模式,或者在启动时使用 plan 权限模式,让它先分析再动手:

claude --permission-mode plan

不同版本和账号的默认权限模式可能变化,因此这里不依赖某一个固定默认值。核心原则只有一个:第一次跑项目时,先看计划和关键命令,再逐步放权。

九、把下面这段完整需求交给 Claude Code

下面这段 Prompt 的设计重点不是“让它写得漂亮”,而是把验收条件写清楚。它要求零第三方运行依赖、前后端闭环、本地持久化、输入校验、自动化测试和启动验证,非常适合作为 Windows 上的第一个全栈任务。

你现在位于一个空的 Windows 项目目录。请先用 5 条以内的计划说明你准备怎么做,得到我确认后再开始修改文件。

目标:创建一个全栈任务看板”Web 应用,并在本机跑通。

技术约束:
1. 运行环境使用 Node.js 24 LTS
2. 尽量零依赖:后端优先使用 node:httpnode:fsnode:path 等内置模块。
3. 前端使用原生 HTML + CSS + JavaScript,不使用 CDN,不依赖外网资源。
4. 数据持久化到 data/tasks.json
5. 使用 ES Module

功能要求:
- GET /api/tasks:读取任务列表。
- POST /api/tasks:新增任务,title 必须为 180 个字符。
- PATCH /api/tasks/:id:修改标题或完成状态。
- DELETE /api/tasks/:id:删除任务。
- 首页可以新增、勾选完成、删除任务,并展示总数与已完成数量。
- 页面要适配手机宽度,所有用户可见文案使用简体中文。

工程要求:
- package.json 提供 npm start npm test
- 使用 node:test 写至少两个测试:一个覆盖 CRUD 闭环,一个覆盖空标题返回 400
- README.md,说明环境、启动、测试和目录结构。
- JSON 解析、404405、输入校验给出明确错误响应。
- 不要读取或输出任何系统密钥、浏览器凭据或无关目录内容。

完成标准:
1. 先生成项目文件。
2. 运行 npm test;如果失败,定位原因并修复,直到测试通过。
3. 短暂启动服务,验证 http://localhost:3000 GET /api/tasks 可访问,然后停止用于验证的后台进程。
4. 最后告诉我:修改了哪些文件、测试结果、启动命令、浏览器访问地址,以及还存在哪些边界。

本文第一个全栈任务的最小架构:浏览器、REST APINode.js 服务与本地数据层

十、Claude Code 应该产出什么:先看目录,再看接口

如果模型执行合理,一个干净的最小项目大致会形成下面的结构。具体文件名可以略有不同,但前端、服务端、数据、测试和文档这五部分应该齐全。

windows-claude-todo/
├─ public/
│  ├─ index.html
│  ├─ app.js
│  └─ styles.css
├─ data/
│  └─ tasks.json
├─ test/
│  └─ api.test.mjs
├─ server.mjs
├─ package.json
└─ README.md

10.1 API 验收表

方法

路径

目标

正确结果

GET

/api/tasks

读取任务列表

200 + JSON 数组

POST

/api/tasks

新增任务

201 + 新任务对象

PATCH

/api/tasks/:id

更新标题或完成状态

200 + 更新后的任务

DELETE

/api/tasks/:id

删除任务

204 No Content

POST

/api/tasks(空标题)

输入校验

400 + JSON 错误信息

10.2 参考实现的 package.json

{
  "name": "windows-claude-todo",
  "version": "1.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "start": "node server.mjs",
    "test": "node --test"
  },
  "engines": {
    "node": ">=22"
  }
}

这里没有 dependencies 字段,意味着第一次验证不用先 npm install 一堆包。Node.js 24 LTS 本身就能直接运行。

十一、后端核心:用 Node.js 内置模块完成 REST API 和静态文件服务

为了让文章可复现,下面给出我用于验证的参考实现核心。Claude Code 实际生成的代码可能不同,但只要接口行为、错误处理与测试结果一致,就是可接受的工程答案。

import http from 'node:http';
import { readFile, writeFile, mkdir, stat } from 'node:fs/promises';
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import crypto from 'node:crypto';

const __dirname = path.dirname(fileURLToPath(import.meta.url));
const PUBLIC_DIR = path.join(__dirname, 'public');
const DATA_DIR = path.join(__dirname, 'data');
const DATA_FILE = path.join(DATA_DIR, 'tasks.json');
const PORT = Number(process.env.PORT || 3000);

const MIME = {
  '.html': 'text/html; charset=utf-8',
  '.js': 'text/javascript; charset=utf-8',
  '.css': 'text/css; charset=utf-8',
  '.json': 'application/json; charset=utf-8',
};

async function ensureDataFile() {
  await mkdir(DATA_DIR, { recursive: true });
  try { await stat(DATA_FILE); }
  catch { await writeFile(DATA_FILE, '[]\n', 'utf8'); }
}

async function readTasks() {
  await ensureDataFile();
  return JSON.parse(await readFile(DATA_FILE, 'utf8'));
}
async function writeTasks(tasks) {
  await writeFile(DATA_FILE, JSON.stringify(tasks, null, 2) + '\n', 'utf8');
}
function sendJson(res, status, data) {
  res.writeHead(status, { 'content-type': MIME['.json'], 'cache-control': 'no-store' });
  res.end(JSON.stringify(data));
}
async function readJson(req) {
  let body = '';
  for await (const chunk of req) {
    body += chunk;
    if (body.length > 1_000_000) throw new Error('请求体过大');
  }
  return body ? JSON.parse(body) : {};
}
function cleanTitle(value) {
  if (typeof value !== 'string') return '';
  return value.trim().replace(/\s+/g, ' ');
}
function taskPath(url) {
  const match = url.pathname.match(/^\/api\/tasks\/([^/]+)$/);
  return match ? decodeURIComponent(match[1]) : null;
}

export function createServer() {
  return http.createServer(async (req, res) => {
    try {
      const url = new URL(req.url, `http://${req.headers.host || 'localhost'}`);
      if (url.pathname === '/api/tasks' && req.method === 'GET') {
        return sendJson(res, 200, await readTasks());
      }
      if (url.pathname === '/api/tasks' && req.method === 'POST') {
        const body = await readJson(req);
        const title = cleanTitle(body.title);
        if (!title || title.length > 80) return sendJson(res, 400, { error: '任务标题长度必须为 1 80 个字符' });
        const tasks = await readTasks();
        const task = { id: crypto.randomUUID(), title, completed: false, createdAt: new Date().toISOString() };
        tasks.unshift(task); await writeTasks(tasks);
        return sendJson(res, 201, task);
      }
      const id = taskPath(url);
      if (id && req.method === 'PATCH') {
        const body = await readJson(req); const tasks = await readTasks();
        const task = tasks.find(t => t.id === id);
        if (!task) return sendJson(res, 404, { error: '任务不存在' });
        if ('title' in body) {
          const title = cleanTitle(body.title);
          if (!title || title.length > 80) return sendJson(res, 400, { error: '任务标题长度必须为 1 80 个字符' });
          task.title = title;
        }
        if ('completed' in body) task.completed = Boolean(body.completed);
        await writeTasks(tasks); return sendJson(res, 200, task);
      }
      if (id && req.method === 'DELETE') {
        const tasks = await readTasks(); const next = tasks.filter(t => t.id !== id);
        if (next.length === tasks.length) return sendJson(res, 404, { error: '任务不存在' });
        await writeTasks(next); res.writeHead(204); return res.end();
      }
      if (req.method !== 'GET') return sendJson(res, 405, { error: '不支持的请求方法' });
      let rel = url.pathname === '/' ? 'index.html' : url.pathname.slice(1);
      rel = path.normalize(rel).replace(/^(\.\.(\/|\\|$))+/, '');
      const filePath = path.join(PUBLIC_DIR, rel);
      if (!filePath.startsWith(PUBLIC_DIR)) return sendJson(res, 403, { error: '禁止访问' });
      try {
        const data = await readFile(filePath);
        const ext = path.extname(filePath).toLowerCase();
        res.writeHead(200, { 'content-type': MIME[ext] || 'application/octet-stream' });
        res.end(data);
      } catch {
        sendJson(res, 404, { error: '资源不存在' });
      }
    } catch (error) {
      sendJson(res, 500, { error: error?.message || '服务器内部错误' });
    }
  });
}

if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
  const server = createServer();
  server.listen(PORT, () => console.log(`任务看板已启动:http://localhost:${PORT}`));
}

11.1 这段服务端代码值得注意的四个点

  • 数据文件第一次不存在时自动创建 data/tasks.json,避免新项目首次启动就因为 ENOENT 退出。
  • POST/PATCH 对标题做 trim 与 1~80 字符校验,不把“前端有 maxlength”当成后端安全边界。
  • 静态文件与 API 共用一个 Node.js 进程,第一次实战不需要 CORS、不需要两个终端分别跑前后端。
  • 所有 API 错误都返回 JSON;404、405、输入错误与服务器内部错误有明确区分,便于前端和测试定位。

十二、前端核心:不用框架,也把“全栈交互”跑完整

12.1 index.html

<!doctype html>
<html lang="zh-CN">
<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1" />
  <title>Claude Code 全栈任务看板</title>
  <link rel="stylesheet" href="/styles.css" />
</head>
<body>
  <main class="shell">
    <section class="hero">
      <p class="eyebrow">WINDOWS × CLAUDE CODE</p>
      <h1>全栈任务看板</h1>
      <p>前端、REST API 与本地数据持久化一次跑通。</p>
    </section>
    <section class="panel">
      <form id="task-form">
        <input id="task-input" maxlength="80" placeholder="输入一个待办事项,例如:验证 Claude Code 安装" autocomplete="off" />
        <button>添加任务</button>
      </form>
      <div class="stats"><span id="total">0 项任务</span><span id="done">0 项完成</span></div>
      <ul id="task-list"></ul>
      <p id="empty" class="empty">还没有任务,先添加第一项吧。</p>
    </section>
  </main>
  <script type="module" src="/app.js"></script>
</body>
</html>

12.2 app.js

const list = document.querySelector('#task-list');
const empty = document.querySelector('#empty');
const total = document.querySelector('#total');
const done = document.querySelector('#done');
const form = document.querySelector('#task-form');
const input = document.querySelector('#task-input');
let tasks = [];

async function api(url, options = {}) {
  const res = await fetch(url, { headers: { 'content-type': 'application/json' }, ...options });
  if (!res.ok) { const body = await res.json().catch(() => ({})); throw new Error(body.error || `请求失败:${res.status}`); }
  return res.status === 204 ? null : res.json();
}
function render() {
  list.innerHTML = '';
  for (const task of tasks) {
    const li = document.createElement('li');
    li.className = task.completed ? 'done' : '';
    li.innerHTML = `<input class="toggle" type="checkbox" ${task.completed ? 'checked' : ''}><span class="title"></span><button class="icon-btn" aria-label="删除">删除</button>`;
    li.querySelector('.title').textContent = task.title;
    li.querySelector('.toggle').addEventListener('change', async e => {
      const updated = await api(`/api/tasks/${task.id}`, { method:'PATCH', body:JSON.stringify({completed:e.target.checked}) });
      tasks = tasks.map(t => t.id === updated.id ? updated : t); render();
    });
    li.querySelector('button').addEventListener('click', async () => {
      await api(`/api/tasks/${task.id}`, { method:'DELETE' }); tasks = tasks.filter(t => t.id !== task.id); render();
    });
    list.append(li);
  }
  total.textContent = `${tasks.length} 项任务`;
  done.textContent = `${tasks.filter(t => t.completed).length} 项完成`;
  empty.hidden = tasks.length > 0;
}
form.addEventListener('submit', async e => {
  e.preventDefault(); const title = input.value.trim(); if (!title) return;
  const task = await api('/api/tasks', { method:'POST', body:JSON.stringify({title}) });
  tasks.unshift(task); input.value=''; render();
});
try { tasks = await api('/api/tasks'); render(); }
catch (e) { empty.hidden = false; empty.textContent = `加载失败:${e.message}`; }

前端没有直接读 tasks.json,而是统一走 /api/tasks。这一点很关键:否则页面只是“带后端文件的静态网页”,并没有真正建立前后端职责边界。

十三、真正决定“跑通”的一步:自动化测试,而不是肉眼看代码

AI 生成代码最容易出现的错觉是:“文件都生成了,看起来也像那么回事,所以应该能跑。”可靠的做法是把成功标准交给命令输出。本文参考实现使用 Node.js 内置 node:test,不需要安装测试框架。

import test from 'node:test';
import assert from 'node:assert/strict';
import { rm, writeFile, mkdir } from 'node:fs/promises';
import { createServer } from '../server.mjs';

const dataFile = new URL('../data/tasks.json', import.meta.url);
async function withServer(fn) {
  await mkdir(new URL('../data/', import.meta.url), { recursive: true });
  await writeFile(dataFile, '[]\n', 'utf8');
  const server = createServer();
  await new Promise(resolve => server.listen(0, '127.0.0.1', resolve));
  const { port } = server.address();
  try { await fn(`http://127.0.0.1:${port}`); }
  finally { await new Promise(resolve => server.close(resolve)); }
}

test('任务 API 完成新增、查询、更新、删除闭环', async () => {
  await withServer(async base => {
    let r = await fetch(`${base}/api/tasks`, {method:'POST', headers:{'content-type':'application/json'}, body:JSON.stringify({title:'跑通第一个全栈任务'})});
    assert.equal(r.status, 201); const created = await r.json(); assert.equal(created.completed, false);
    r = await fetch(`${base}/api/tasks`); const list = await r.json(); assert.equal(list.length, 1);
    r = await fetch(`${base}/api/tasks/${created.id}`, {method:'PATCH', headers:{'content-type':'application/json'}, body:JSON.stringify({completed:true})});
    const updated = await r.json(); assert.equal(updated.completed, true);
    r = await fetch(`${base}/api/tasks/${created.id}`, {method:'DELETE'}); assert.equal(r.status, 204);
    r = await fetch(`${base}/api/tasks`); assert.equal((await r.json()).length, 0);
  });
});

test('空标题返回 400', async () => {
  await withServer(async base => {
    const r = await fetch(`${base}/api/tasks`, {method:'POST', headers:{'content-type':'application/json'}, body:JSON.stringify({title:'   '})});
    assert.equal(r.status, 400);
  });
});

13.1 运行测试

npm test

本文参考实现在本地验证时,两组测试均通过,覆盖了 CRUD 闭环和空标题 400 校验。一个正常结果会类似:

TAP version 13
# Subtest: 任务 API 完成新增、查询、更新、删除闭环
ok 1 - 任务 API 完成新增、查询、更新、删除闭环
# Subtest: 空标题返回 400
ok 2 - 空标题返回 400

# tests 2
# pass 2
# fail 0

为什么这一步对 AI 编程尤其重要

“Claude 说已经完成”不是验收证据;测试进程返回 0、API 状态码符合预期、页面能够实际交互,才是可验证结果。让代理自己运行测试并根据失败输出继续修复,才真正用到了 Agent 工作流。

十四、启动服务并做一次真实访问

14.1 启动项目

npm start

参考实现会输出:

任务看板已启动:http://localhost:3000

浏览器打开 http://localhost:3000。你应该能看到任务输入框、任务列表、总任务数与完成数,并能执行新增、勾选完成和删除。

本文参考实现的运行界面:所有可见文案均为简体中文

14.2 不开浏览器也能验证 API

Invoke-RestMethod -Uri 'http://localhost:3000/api/tasks' -Method Get

如果返回 JSON 数组,说明后端路由和数据文件已经工作。新增一条任务可以用:

$body = @{ title = " PowerShell 验证 POST 接口" } | ConvertTo-Json
Invoke-RestMethod `
  -Uri 'http://localhost:3000/api/tasks' `
  -Method Post `
  -ContentType 'application/json' `
  -Body $body

十五、为什么这个项目算“完整的第一个全栈任务”

它不复杂,但闭环完整。第一次上手 Claude Code,比起一开始就让它搭 React + Next.js + ORM + Docker + 云数据库,先把这些职责跑通更有价值。

层次

本项目做了什么

你验证到了什么

前端

HTML/CSS/JS 页面与交互

浏览器能新增、完成、删除任务

接口

REST API + 状态码 + JSON 错误

前端通过 HTTP 与后端解耦

后端

Node.js HTTP 服务与输入校验

服务端负责业务边界,而不是只靠前端限制

数据

tasks.json 本地持久化

刷新页面后数据仍然存在

测试

node:test 自动化 API 测试

CRUD 与错误场景能重复验证

运行

npm start + localhost

最终成果是真正可访问的程序,而不是代码片段

15.1 下一步再升级技术栈会更稳

当这个最小闭环跑通后,再让 Claude Code 逐层替换技术组件:把前端换成 React/Vue,把 node:http 换成 Express/Fastify,把 JSON 文件换成 SQLite/PostgreSQL,或者增加登录、分页、搜索、Docker。这样每一次升级都有前一个可工作的版本作为对照,定位问题会容易很多。

十六、让第二次协作明显更顺:给项目加 CLAUDE.md

Claude Code 官方当前把 CLAUDE.md 定位为“你写给 Claude 的持久项目指令”。它会在会话开始时加载,适合放构建命令、项目结构、代码规范和“每次都应该遵守”的约束。

在项目根目录运行 /init 可以让 Claude Code 根据代码库生成一个初始 CLAUDE.md;已有文件时,它会建议改进而不是直接覆盖。对于这个 Todo 项目,可以把内容控制得很短:

# 项目说明

## 运行环境
- Windows 10/11
- Node.js 24 LTS
- 启动:npm start
- 测试:npm test

## 架构约束
- 前端文件位于 public/
- REST API 位于 server.mjs
- 数据写入 data/tasks.json
- 不引入第三方依赖,除非任务明确要求

## 修改规则
- 修改 API 后必须运行 npm test
- 新增用户可见文案必须使用简体中文
- 删除文件或改变数据格式前先说明影响
- 不读取或输出密钥、浏览器凭据和无关用户目录

你可以在会话里用 /context 查看 CLAUDE.md 是否已经被加载。官方建议这类文件保持具体、简洁、可验证,不要把几百行“万能规则”全部塞进去。

十七、Windows 上最实用的一组 Claude Code 命令

命令

用途

适合什么时候用

claude

启动交互会话

进入项目开始工作

claude --version

查看版本号

安装后确认

claude doctor

检查安装与配置健康度

PATH/配置/更新问题

claude -c

继续当前目录最近一次会话

中断后继续

claude -r

选择并恢复历史会话

需要回到更早的任务

claude -p "问题"

一次性执行并输出结果

脚本或快速查询

/help

查看会话命令

忘记命令时

/login

重新登录或切换账号

认证发生变化

/init

生成/改进项目 CLAUDE.md

第一次把项目规则固化

/context

查看上下文占用与加载内容

排查 Claude 为什么没看到某些规则

/clear

清空当前会话历史

任务切换、上下文污染时

Shift+Tab

切换权限模式

计划、手动确认和自动模式之间切换

十八、常见问题排查:先看现象,再查根因

6  Windows 安装与启动常见问题的排查顺序

18.1 “irm 不是内部或外部命令”

最常见原因不是网络,而是当前窗口是 CMD。PowerShell 的 irm 是 Invoke-RestMethod 的别名;换到 PowerShell 执行官方 PowerShell 安装命令,或者在 CMD 使用官方 CMD 安装命令。

18.2 “The token && is not a valid statement separator”

这通常是把 CMD 安装命令复制到了较旧的 PowerShell。不要在同一个窗口反复改引号,直接确认终端类型并使用对应命令。

18.3 “claude 不是内部或外部命令 / not recognized”

先关闭并重新打开终端,让 PATH 刷新。官方故障排查还建议在必要时确认用户 PATH 是否包含 %USERPROFILE%\.local\bin。升级过旧版 Claude Desktop 的用户,如果运行 claude 意外打开桌面应用,也应先把 Claude Desktop 更新到最新版本。

18.4 Git 已装但 Claude Code 找不到 Git Bash

先决定你是否真的需要 Bash。当前原生 Windows 没有 Git Bash 也可以用 PowerShell 工具;如果项目脚本确实依赖 Bash,再通过 CLAUDE_CODE_GIT_BASH_PATH 指向实际 bash.exe。

18.5 安装成功但登录失败

把问题拆成四层:账号是否属于 Claude Code 支持类型;所在地区是否在官方支持范围;浏览器登录是否完成;企业网络、代理或证书是否拦截了所需访问。只要 claude --version 正常,通常不应该先重装 CLI。

18.6 TLS/SSL 错误

旧 Windows 10 或企业 TLS 环境可能出现安全通道问题。官方终端指南给出的一个兼容处理方式是在当前 PowerShell 会话先启用 TLS 1.2,再重试安装:

[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
irm https://claude.ai/install.ps1 | iex

十九、权限与安全:AI 能改代码,不等于应该把所有权限一次性放开

Claude Code 的效率来自“它可以行动”,但工程可靠性来自“行动有边界”。第一次在重要项目使用时,建议至少做到下面几点:

  • 先在 Git 分支或可回滚副本中工作。对数据库、生产配置和用户数据另外做备份。
  • 让 Claude Code 解释即将执行的高影响命令;删除、迁移、覆盖、发布类命令尤其如此。
  • API Key、访问令牌、.env、浏览器凭据等敏感信息不写进 Prompt、截图或仓库。
  • 把“修改后必须运行什么测试”写进 CLAUDE.md,而不是依赖每次临时提醒。
  • 日常开发不要为了省确认步骤而默认使用跳过权限检查的危险模式。

对于团队项目,更进一步可以把允许/禁止的工具、Hooks、MCP 和组织级 CLAUDE.md 纳入统一配置。但第一篇上手教程先把“可见、可审、可验证”这个习惯建立起来,比一开始堆满高级能力更重要。

二十、一次完整工作流应该长这样

  1. 打开项目目录,运行 claude。
  2. 先让 Claude Code 解释当前项目或输出短计划,不急着改。
  3. 明确目标、技术边界、测试和完成标准。
  4. 审阅关键文件修改与命令授权。
  5. 让它运行测试、构建或 lint,并根据真实输出继续修复。
  6. 启动应用,做浏览器或 API smoke test。
  7. 用 git diff / 测试结果检查最终变更,再提交。
  8. 把这次重复说明过的规则补进 CLAUDE.md,让下一次会话直接继承。

最重要的转变

把 Claude Code 当成“会写代码的同事”,而不是“一次性代码生成器”:先说明验收标准,让它动手,再用工具输出验证。这样才真正发挥终端 Agent 的价值。

总结

Windows 上手 Claude Code 到 2026 年已经比早期简单很多:新手不再需要先为了 Claude Code 本体安装 Node.js,也不必强制切到 WSL。最短主线就是 PowerShell 原生安装 → claude --version / claude doctor → 登录 → 进入项目目录 → 给出可验证任务。

但“安装成功”只是起点。真正能让 Claude Code 改变开发体验的,是把需求写成工程任务,把测试、命令输出、API 状态码和最终页面作为验收证据。本文用一个零依赖任务看板完成了前端、REST API、数据持久化和自动化测试闭环,你已经可以在这个基础上继续升级到 React/Vue、Express/Fastify、SQLite/PostgreSQL,甚至进一步加入 Skills、Hooks 与 MCP。

如果你只记住一句话:不要问“Claude Code 能不能帮我写代码”,而要问“我能不能把目标、边界和验证方式说清楚,让它把整个工程闭环跑完”。从这一步开始,AI 编程才真正从补全代码进入了可执行的开发协作。

参考资料

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐