CodeBuddy 学习(1):安装与使用

0 阅读9分钟

CodeBuddy 学习(1):安装与使用

一、概述:什么是 CodeBuddy

1.1 产品定位

CodeBuddy(腾讯云代码助手)是腾讯云推出的全流程 AI 辅助编程工具,将 AI 深度嵌入 需求 → 设计 → 编码 → 部署 全链路,支持插件、IDE、CLI 三种形态,覆盖从初学者到企业团队的全场景。

1.2 工作原理简述

CodeBuddy 的核心工作流程分为以下几步:

  1. 上下文收集:分析当前项目结构、打开的文件、Git 历史、终端输出等环境信息。
  2. 意图理解:通过大语言模型(LLM)解析用户输入的自然语言指令,识别任务类型(问答 / 编写 / 规划 / 部署)。
  3. 能力调度:根据任务类型,调用对应的工具链——文件读写、终端命令执行、代码补全引擎、部署管道等。
  4. Diff 渲染:所有代码变更以 Diff 视图呈现,用户可以逐行审查后选择接受或拒绝。

关键概念:CodeBuddy 不是简单的"输入→输出"聊天机器人。它是一个具有读写权限的 AI Agent,可以自主完成文件创建、命令执行、环境配置等操作。理解这一点是高效使用 CodeBuddy 的基础。

1.3 核心能力数据

能力说明
代码生成AI 代码占比 50%+,自动生成高质量代码
代码评审AI 评审占比 35%+,自动审查代码质量
编码提速编码时间缩短 40%+
研发覆盖内部实践覆盖 90%+ 研发场景

1.4 生态集成

  • CloudBase / EdgeOne:一键部署到腾讯云
  • Cloud Studio:云端 IDE 环境
  • 微信开发者工具:小程序开发支持

1.5 行业验证

在游戏、微信、金融、电子等行业均有实践,AI 代码生成率达到 40-50%+。

视频教程:cloud.tencent.com/edu/learnin…


二、安装指南

CodeBuddy 提供三种安装方式,适应不同使用场景。建议新手从方式一(VS Code 插件)开始。

方式适用场景特点
VS Code 插件已有 VS Code,需要轻量集成最小安装,即装即用
独立 IDE需要完整的 AI 编程环境开箱即用,深度集成
CLI 命令行终端重度用户,CI/CD 集成纯命令行,灵活高效

2.1 方式一:VS Code 插件(推荐新手)

安装步骤:

  1. 打开 VS Code,点击左侧扩展图标
  2. 搜索 腾讯云代码助手 CodeBuddy
  3. 点击安装,安装完成后重启 VS Code
  4. 右下角弹出登录窗口,使用微信扫码或手机号登录

也可在 JetBrains 系列 IDE(IDEA / PyCharm 等)中安装: File → Settings → Plugins → Marketplace → 搜索 Tencent Cloud Code Assistant CodeBuddy → 安装 → 重启 → 登录

2.2 方式二:独立 IDE

安装步骤:

  1. 访问下载页面:
  2. 下载对应平台的安装包(Windows / macOS / Linux)
  3. 安装完成后直接启动,登录即可使用

2.3 方式三:CLI 命令行(完整示例)

原理讲解

CLI(Command Line Interface)是 CodeBuddy 的命令行版本。它的原理是:

  1. 全局 npm 包安装:通过 npm 将 CodeBuddy CLI 安装到系统全局路径(如 Windows 的 %APPDATA%/npm/,macOS/Linux 的 /usr/local/lib/node_modules/)。
  2. PATH 注册npm install -g 会在 Node.js 安装目录的 node_modules/.bin/ 下创建可执行文件,并将其路径加入系统 PATH,使得在任意终端窗口中都可以直接执行 codebuddy 命令。
  3. API 通信:CLI 启动后,读取用户输入,通过 HTTP/HTTPS 协议与 CodeBuddy 云端服务通信,将结果渲染到终端。

此外,CodeBuddy CLI 还支持通过 Homebrew(无需预装 Node.js)和原生二进制安装,原理类似——它们都是将可执行文件放到系统路径中。

示例代码:完整安装与验证流程
# ============================================
# 前置步骤:安装 Node.js(如已安装可跳过)
# ============================================
# 1. 访问 https://nodejs.org/zh-cn/download/
# 2. 下载 LTS 版本(推荐 20.x)
# 3. 安装后验证:
node --version
# 预期输出:v20.x.x

npm --version
# 预期输出:10.x.x(随 Node.js 一起安装)

# ============================================
# 步骤 1:配置 npm 镜像(国内用户加速下载)
# ============================================
npm config set registry https://registry.npmmirror.com/

# 验证镜像是否切换成功:
npm config get registry
# 预期输出:https://registry.npmmirror.com/

# ============================================
# 步骤 2:安装 CodeBuddy CLI
# ============================================
npm install -g @tencent-ai/codebuddy-code

# 预期输出(示例):
# added 150 packages in 30s
# 15 packages are looking for funding
#   run `npm fund` for details

# ============================================
# 步骤 3:验证安装
# ============================================
codebuddy --version
# 预期输出:x.x.x(显示当前版本号)

# ============================================
# 步骤 4:查看帮助信息
# ============================================
codebuddy --help
# 预期输出:显示所有可用命令和使用说明

# ============================================
# 步骤 5:启动登录
# ============================================
codebuddy
# 首次运行会引导登录流程,根据终端提示完成扫码或输入验证码

# ============================================
# 步骤 6:快速测试
# ============================================
codebuddy "用 JavaScript 写一个打印 Hello World 的函数"
# 预期输出:AI 会生成包含 function helloWorld() 的完整代码
其他安装方式
  • Homebrew(macOS/Linux,无需 Node.js):brew install codebuddy-code
  • 原生二进制(Beta):
    • macOS/Linux: curl -fsSL https://www.codebuddy.cn/cli/install.sh | bash
    • Windows: irm https://www.codebuddy.cn/cli/install.ps1 | iex
调试过程

场景一:npm install -g 报权限错误(macOS/Linux)

# 错误信息示例:
# Error: EACCES: permission denied, access '/usr/local/lib/node_modules'

# 原因:当前用户对全局 node_modules 目录没有写入权限

# 排查步骤:
# 1. 确认 Node.js 安装路径
which node
# 预期:/usr/local/bin/node

# 2. 检查 node_modules 目录权限
ls -la /usr/local/lib/node_modules
# 预期:看到目录的权限信息

# 3. 修复方法(三选一):

# 方法 A:使用 npx 跳过全局安装
npx @tencent-ai/codebuddy-code

# 方法 B:修改 npm 全局目录(推荐)
mkdir ~/.npm-global
npm config set prefix '~/.npm-global'
echo 'export PATH=~/.npm-global/bin:$PATH' >> ~/.bashrc
source ~/.bashrc
npm install -g @tencent-ai/codebuddy-code

# 方法 C:在命令前加 sudo(不推荐,有安全风险)
sudo npm install -g @tencent-ai/codebuddy-code

场景二:codebuddy 命令找不到(Windows)

# 错误信息示例:
# 'codebuddy' 不是内部或外部命令,也不是可运行的程序
#  或
# command not found: codebuddy

# 原因:npm 全局安装路径未添加到系统 PATH

# 排查步骤:
# 1. 确认 npm 全局安装路径
npm config get prefix
# 预期输出(Windows 示例):C:\Users\你的用户名\AppData\Roaming\npm

# 2. 检查该目录下是否有 codebuddy 相关文件
dir %APPDATA%\npm\codebuddy*
# 预期:显示 codebuddy 和 codebuddy.cmd 文件

# 3. 将路径添加到 PATH:
#    Windows:系统属性 → 环境变量 → 找到 Path → 新建
#    添加:%APPDATA%\npm
#    然后重新打开终端

# 3. 重新验证
codebuddy --version
# 预期:正常显示版本号

场景三:网络下载缓慢

# 错误信息示例:
# npm ERR! network timeout
# npm ERR! network This is a problem related to network connectivity.

# 原因:默认 npm registry 在国外,国内访问速度慢

# 排查与修复:
# 1. 检查当前镜像源
npm config get registry
# 预期输出可能是:https://registry.npmjs.org/

# 2. 切换至国内镜像
npm config set registry https://registry.npmmirror.com/

# 3. 验证切换
npm config get registry
# 预期:https://registry.npmmirror.com/

# 4. 重新安装
npm install -g @tencent-ai/codebuddy-code

场景四:Node.js 版本不兼容

# 错误信息示例:
# npm WARN EBADENGINE Unsupported engine
# npm WARN EBADENGINE   required: { node: '>=18.20' }

# 排查步骤:
# 1. 确认当前版本
node --version
# 预期:显示 v16.x.x 或其他低于 18.20 的版本

# 2. 升级 Node.js:
# Windows / macOS:访问 https://nodejs.org/ 下载最新 LTS
# 或使用 nvm(Node Version Manager):
nvm install 20
nvm use 20
node --version
# 预期:v20.x.x

2.4 常用 CLI 命令速查

codebuddy "你的问题或指令"       # 单次对话
cbc "你的问题或指令"              # 简写形式
codebuddy --version               # 查看版本
codebuddy --help                  # 查看帮助

三、快速上手:第一个对话

在深入各项功能之前,先完成一个有代表性的操作来建立直观感受。

3.1 示例:用 Ask 模式理解一段代码

原理讲解

Ask 模式是 CodeBuddy 的"只读问答"模式。其核心原理:

  1. 权限边界:Ask 模式下,AI 只能读取文件和项目上下文,不能修改任何文件或执行任何命令。这确保了安全性——你可以放心提问而不用担心代码被意外修改。
  2. 上下文注入:当你通过 @file 引用文件时,CodeBuddy 会将文件内容注入到大语言模型的上下文中。模型看到的是完整的源代码,而非摘要。
  3. Token 约束:模型有上下文长度限制(即 token 上限),过长的文件会被截断。因此,引用时尽量精确到需要分析的具体文件,而不是整个项目目录。

Ask 模式适合:代码解释、方案讨论、学习新技术、代码审查建议等不需要修改代码的场景。

示例代码:待分析的代码文件

假设你的项目中有一个 utils.js 文件,内容如下:

// utils.js — 一个用于演示的实用工具文件

/**
 * 防抖函数:在连续触发的事件中,只执行最后一次
 * @param {Function} fn  - 需要防抖的函数
 * @param {number} delay - 延迟时间(毫秒)
 * @returns {Function} - 包装后的防抖函数
 */
function debounce(fn, delay) {
  let timer = null;
  return function (...args) {
    if (timer) clearTimeout(timer);
    timer = setTimeout(() => {
      fn.apply(this, args);
    }, delay);
  };
}

/**
 * 节流函数:在指定时间间隔内,只执行一次
 * @param {Function} fn    - 需要节流的函数
 * @param {number} interval - 时间间隔(毫秒)
 * @returns {Function} - 包装后的节流函数
 */
function throttle(fn, interval) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= interval) {
      lastTime = now;
      fn.apply(this, args);
    }
  };
}

module.exports = { debounce, throttle };
操作步骤
  1. 在 CodeBuddy 中打开 utils.js 文件。
  2. 将工作模式切换到 Ask(在对话面板顶部选择)。
  3. 在对话框中输入 @file,从文件列表中选择 utils.js
  4. 输入你的问题,例如:
    请解释 debounce 和 throttle 的区别,并举一个实际应用场景
    
  5. 按 Enter 发送,观察 AI 的回复。
预期输出

AI 会输出类似以下的分析:

debounce 和 throttle 的核心区别在于执行时机的控制策略:

**debounce(防抖)**- 原理:每次触发都重新计时,只有在停止触发 delay 毫秒后才执行。
- 类比:电梯门——有人进就重新计时,直到没人进了才关门。
- 应用:搜索输入框——用户停止输入 300ms 后再发请求,避免每次按键都请求。

**throttle(节流)**- 原理:每隔 interval 毫秒固定执行一次,不管触发有多频繁。
- 类比:红绿灯——每 60 秒放行一次,中间来了多少车不管。
- 应用:滚动事件——每 200ms 检查一次滚动位置,避免高频触发。

**关键区别**:
debounce 关注"最后一次",throttle 关注"固定频率"。
调试过程

场景一:Ask 模式下无法生成代码

现象:选中 Ask 模式后,AI 拒绝生成代码,只提供解释。
原因:这是 Ask 模式的预期行为——只读不写。
排查:检查对话面板顶部的模式选择器,确认当前处于 Ask 模式。
修复:如需生成和修改代码,切换到 Craft 模式或 Plan 模式。

场景二:@file 引用文件无反应

现象:输入 @file 后没有出现文件选择列表。
原因排查:
  1. 确认当前已在 CodeBuddy 中打开了一个项目/文件夹。
     如果 IDE 中没有打开任何文件夹,CodeBuddy 无法读取项目文件。
  2. 确认模式已切换到 Ask / Craft(某些模式下 @ 上下文功能可能不同)。
修复:使用 "文件 → 打开文件夹" 打开项目,然后重试。

场景三:回复内容与预期不符(幻觉)

现象:AI 给出了看似合理但实际错误的解释。
原因:AI 的上下文窗口有限,如果引用的文件内容被截断或理解偏差,可能出现幻觉。
排查:
  1. 检查 @file 引用是否正确指向了目标文件。
  2. 将问题描述得更具体,例如加上"只看 debounce 函数的实现"。
修复:精简引用范围,避免一次引用过多文件;必要时将问题拆分为多个子问题分步提问。

四、核心功能详解

掌握基础交互后,逐一学习 CodeBuddy 的核心功能。

4.1 三种工作模式

原理讲解

CodeBuddy 的三种模式对应了三种不同的 权限等级执行策略

模式权限执行策略适用场景
Ask只读纯问答,不执行任何写操作咨询问题、代码解释、方案讨论
Craft读写+命令直接执行,边生成边修改代码编写、项目构建、文件操作
Plan读写+命令先生成计划,用户确认后再执行复杂需求、多步骤任务、大型重构

工作流选择策略

  • 简单咨询 → Ask:问 API 用法、解释报错、讨论架构方案。
  • 明确需求 → Craft:创建一个 React 组件、修复一个已知 Bug、格式化一段代码。
  • 复杂任务 → Plan:重构整个模块、搭建新项目脚手架、迁移技术栈。

最佳实践:不确定 AI 会做什么时,先用 Plan 模式让它制定计划,审查通过后再切换到 Craft 执行。

示例:用 Plan 模式重构代码

场景:项目中有一个 500 行的 app.js,需要拆分为多个模块。

操作步骤

  1. 切换到 Plan 模式。
  2. 对话框输入:
    @file app.js
    请制定一个拆分方案,将这个文件按职责分为 controller、service、model 三层,
    要求不影响现有功能。
    
  3. AI 输出拆分计划,列出每个模块的职责和依赖关系。
  4. 审查计划,如需调整则继续对话。
  5. 确认无误后,切换到 Craft 模式,说:"按计划执行"。
调试过程

场景:Plan 模式下 AI 直接开始修改代码

现象:切换到 Plan 模式后,AI 仍然直接修改了文件。
原因:可能是模式切换未生效,或 Craft 模式的残留行为。
排查:
  1. 确认对话面板顶部显示的模式名称是 "Plan"。
  2. 新建一个对话重新开始(避免历史上下文影响)。
修复:新建对话,重新选择 Plan 模式后发起请求。

4.2 切换模型

点击对话面板顶部的模型选择器,可切换不同的 AI 模型。不同模型在推理速度、代码生成质量和上下文长度上有所差异。

4.3 对话管理

  • 新建对话:点击对话面板顶部的新建按钮(或使用快捷键)。
  • 历史对话:在对话列表中切换查看历史记录。
  • 注意事项:每次新建对话会清空上下文历史,意味着 AI 不再记得之前讨论过的内容。如果需要延续之前的讨论,请在同一个对话中继续,而不是新建。

4.4 @ 上下文引用(完整示例)

原理讲解

@ 引用是 CodeBuddy 最强大的功能之一。其原理是:

  1. 符号系统@ 是一个上下文调取指令,告诉 CodeBuddy"把某个实体的内容注入到当前对话中"。
  2. 内容注入方式:根据引用类型不同,CodeBuddy 读取不同来源的数据并序列化为文本,追加到每次请求的上下文窗口里。
  3. 实时性@terminal 引用的是最近一次命令执行的输出,@git 引用的是最新的提交记录——这些都是动态获取的实时数据
  4. 不持久化:只有在当前消息中通过 @ 引用的内容才会被注入;下一条消息若不再次引用,AI 就不知道那个文件的内容了。
引用类型写法说明典型用途
文件@file引用项目中的文件分析代码、查找 Bug、解释逻辑
文件夹@folder引用整个目录了解项目结构、跨文件分析
终端输出@terminal引用终端命令输出结果排查报错、分析运行日志
Git 提交@git引用 Git 提交记录了解变更历史、生成 commit message
规则@rules引用项目规则配置确认项目规范、约束 AI 行为
示例:组合使用多个 @ 引用排查问题

场景:项目运行时报错,需要排查原因。

操作步骤

  1. 先在终端执行报错的命令:
    node server.js
    
  2. 终端输出类似如下错误:
    Error: Cannot find module './config'
    Require stack:
    - /path/to/project/server.js
    
  3. 在 CodeBuddy 对话框中输入:
    @file server.js
    @terminal
    请分析终端报错的原因,并给出修复方案
    
  4. AI 同时看到 server.js 的代码和终端错误输出,能精确定位问题。
预期输出
错误原因分析:
server.js 第 3 行尝试 require('./config'),但当前目录下不存在 config.js 
或 config/index.js 文件。

修复方案:
1. 检查 config 模块是否存在:ls config*
2. 如果需要从环境变量读取配置,创建 config.js 导出配置对象
3. 如果配置文件在其他路径,修正 require 的路径
调试过程

场景:@terminal 引用的不是最新的输出

现象:使用 @terminal 后,AI 分析的是旧的终端输出。
原因:每次执行命令后,@terminal 引用的是最近一次的输出内容,但前提是
     该终端窗口是 CodeBuddy 的内置终端。如果在 VS Code 外部终端执行命令,
     则 CodeBuddy 无法捕获。
排查:
  1. 确认使用的是 CodeBuddy 内置终端,而非系统外部终端。
  2. 重新在 CodeBuddy 内置终端中执行命令,然后引用 @terminal。
修复:在 CodeBuddy 内置终端重新执行命令。

4.5 内联编辑

原理讲解

内联编辑(Inline Edit)是在代码编辑区域直接触发 AI 修改的快捷方式:选中一段代码 → 按快捷键 → 输入自然语言指令 → AI 直接在文件内生成 Diff 变更。

快捷键

平台快捷键
WindowsAlt + K
macOSCmd + K

工作原理

  1. 用户选中一段代码。
  2. 按键触发后,弹出一个输入框。
  3. 输入自然语言指令(如"添加错误处理")。
  4. AI 分析选中代码和指令,生成修改后的代码。
  5. 以 Diff 视图展示,用户选择接受或拒绝。
示例:为函数添加 JSDoc 注释

操作步骤

  1. 在编辑器中选中以下代码:
    function calculateTotal(items, discount) {
      const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
      return subtotal * (1 - discount);
    }
    
  2. Alt + K(Windows)或 Cmd + K(macOS)。
  3. 输入指令:为这个函数添加 JSDoc 注释和参数校验
  4. 观察 Diff 视图中的变更。

预期输出(Diff 视图会显示新增内容为绿色):

/**
 * 计算订单总价
 * @param {Array<{price: number, qty: number}>} items - 商品列表
 * @param {number} discount - 折扣比例(0-1)
 * @returns {number} 折后总价
 * @throws {Error} 如果参数类型不正确
 */
function calculateTotal(items, discount) {
  if (!Array.isArray(items)) {
    throw new Error('items 必须是数组');
  }
  if (typeof discount !== 'number' || discount < 0 || discount > 1) {
    throw new Error('discount 必须是 0-1 之间的数字');
  }
  const subtotal = items.reduce((sum, item) => sum + item.price * item.qty, 0);
  return subtotal * (1 - discount);
}
调试过程

场景:Alt+K / Cmd+K 没有反应

现象:选中代码后按快捷键,没有弹出输入框。
原因排查:
  1. 快捷键被其他扩展或系统快捷键拦截。
  2. CodeBuddy 扩展未正常加载。
排查步骤:
  1. 检查 VS Code 右下角状态栏,确认 CodeBuddy 图标是否正常显示(不显示灰色)。
  2. 打开快捷键设置:File → Preferences → Keyboard Shortcuts
     搜索 "codebuddy",查看内联编辑的快捷键配置是否被覆盖。
  3. 尝试先点一下编辑区域确保焦点在编辑器中,再按快捷键。
修复:
  - 如果快捷键冲突,在 Keyboard Shortcuts 中重新绑定。
  - 如果扩展异常,先禁用再重新启用 CodeBuddy 扩展。

4.6 Diff 视图操作

AI 生成或修改代码时,会以 Diff 视图展示变更:

  • Accept:接受修改,变更写入文件。
  • Reject:拒绝修改,文件保持原样。

操作建议:不要一键全部接受。逐段审查 Diff,确认每处修改的合理性后再接受。AI 可能在不相关的位置做出意外修改。

4.7 代码补全

原理讲解

CodeBuddy 的代码补全是实时触发的,不需要手动按键激活:

  1. 触发时机:在编辑器中进行代码编写时,CodeBuddy 持续分析光标前后的上下文(函数名、变量类型、注释等)。
  2. Ghost Text 展示:AI 预测到可能的补全时,会以灰色文字(Ghost Text)的形式展示在光标后方。
  3. 接受/忽略:按 Tab 接受补全,按 Esc 忽略。

高级特性:CodeBuddy 支持多行补全——如果上下文暗示需要生成一整段逻辑(如在注释中描述了功能),补全可能是数行代码。

示例:触发代码补全

操作步骤

  1. 在 JS 文件中输入以下注释:
    // 将数组中的每个元素乘以 2,返回新数组
    function doubleArray(arr) {
    
  2. 换行后,观察光标后是否出现 Ghost Text。
  3. 如果补全内容正确,按 Tab 接受。

预期 Ghost Text(灰色文字示例)

   return arr.map(item => item * 2);
调试过程

场景:代码补全一直没有出现

现象:打了很多代码,始终没有看到灰色补全提示。
原因排查:
  1. 网络连接问题——补全功能依赖云端模型,网络不通则不工作。
  2. 扩展未成功登录——右下角检查登录状态。
  3. 文件类型不支持——某些冷门语言可能没有补全服务。
排查步骤:
  1. 检查 VS Code 右下角状态栏的 CodeBuddy 图标,确认登录状态正常。
  2. 尝试在一个 JS/Python/TS 等主流语言文件中输入注释测试。
  3. 检查网络:ping copilot.tencent.com 看是否连通。
修复:
  - 如果未登录,点击图标重新登录。
  - 如果是网络问题,检查代理/VPN 设置。

4.8 终端集成

CodeBuddy 内置终端支持 AI 辅助,在终端操作时也能调用 AI 能力。

典型用法:终端执行报错 → 选中错误信息 → 按内联编辑快捷键 → 让 AI 分析并修复。

4.9 内置预览

项目启动后(如 npm run dev),点击工具栏预览按钮,CodeBuddy 会在编辑器内打开预览链接,无需切换到浏览器。


五、云部署实战:贪吃蛇小游戏

这一节通过一个完整案例,演示如何从零到一完成一个项目的云部署。

5.1 原理讲解

CodeBuddy 的云部署本质是自动化 CI/CD 管道

  1. 项目识别:CodeBuddy 分析项目结构,识别构建工具(Vite / Webpack / Next.js 等)和输出目录。
  2. 构建执行:在云端或本地容器中执行 npm install + npm run build
  3. 资源上传:将构建产物(dist/build/)上传到指定的云平台。
  4. 域名绑定:自动生成公网访问 URL(如 xxx.edgeone.app),并生成二维码便于移动端扫码访问。

支持的部署平台

平台说明
Tencent Cloud Studio腾讯云端 IDE,每月 10000 分钟免费
EdgeOne Pages前端开发和部署平台,自动生成公网域名

5.2 示例:部署贪吃蛇小游戏

项目代码:完整可运行的贪吃蛇游戏

创建项目文件夹 snake-game,在其中创建 index.html

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>贪吃蛇小游戏</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      box-sizing: border-box;
    }

    body {
      display: flex;
      justify-content: center;
      align-items: center;
      min-height: 100vh;
      background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
      font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
    }

    .game-container {
      text-align: center;
      background: rgba(255, 255, 255, 0.05);
      border-radius: 20px;
      padding: 30px;
      backdrop-filter: blur(10px);
      box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
    }

    h1 {
      color: #00ff88;
      margin-bottom: 10px;
      font-size: 2rem;
      letter-spacing: 2px;
    }

    .score-board {
      display: flex;
      justify-content: center;
      gap: 30px;
      margin-bottom: 20px;
    }

    .score-item {
      color: #ccc;
      font-size: 1.1rem;
    }

    .score-item span {
      color: #00ff88;
      font-weight: bold;
      font-size: 1.4rem;
      margin-left: 5px;
    }

    canvas {
      border: 2px solid #00ff88;
      border-radius: 8px;
      display: block;
      background: #0a0a1a;
    }

    .controls {
      margin-top: 20px;
    }

    .controls p {
      color: #888;
      margin-bottom: 10px;
    }

    .btn {
      background: #00ff88;
      color: #1a1a2e;
      border: none;
      padding: 12px 30px;
      font-size: 1rem;
      font-weight: bold;
      border-radius: 8px;
      cursor: pointer;
      transition: transform 0.2s, box-shadow 0.2s;
    }

    .btn:hover {
      transform: translateY(-2px);
      box-shadow: 0 4px 15px rgba(0, 255, 136, 0.4);
    }

    .game-over-overlay {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      color: #ff4444;
      font-size: 2rem;
      font-weight: bold;
      pointer-events: none;
      opacity: 0;
      transition: opacity 0.3s;
      text-shadow: 0 0 10px rgba(255, 68, 68, 0.8);
    }

    .game-over-overlay.visible {
      opacity: 1;
    }
  </style>
</head>
<body>
  <div class="game-container">
    <h1>🐍 贪吃蛇</h1>
    <div class="score-board">
      <div class="score-item">得分 <span id="score">0</span></div>
      <div class="score-item">最高分 <span id="highScore">0</span></div>
    </div>
    <div style="position: relative;">
      <canvas id="gameCanvas" width="400" height="400"></canvas>
      <div class="game-over-overlay" id="gameOver">游戏结束!</div>
    </div>
    <div class="controls">
      <p>方向键 / WASD 控制移动 | 空格键暂停</p>
      <button class="btn" id="restartBtn">重新开始</button>
    </div>
  </div>

  <script>
    const canvas = document.getElementById('gameCanvas');
    const ctx = canvas.getContext('2d');
    const scoreEl = document.getElementById('score');
    const highScoreEl = document.getElementById('highScore');
    const gameOverEl = document.getElementById('gameOver');
    const restartBtn = document.getElementById('restartBtn');

    // 游戏配置
    const GRID_SIZE = 20;
    const CELL_SIZE = canvas.width / GRID_SIZE;
    const BASE_SPEED = 100; // 基础速度(毫秒/帧)

    // 游戏状态
    let snake = [];
    let food = {};
    let direction = { x: 1, y: 0 };
    let nextDirection = { x: 1, y: 0 };
    let score = 0;
    let highScore = 0;
    let gameRunning = false;
    let gamePaused = false;
    let gameLoop = null;
    let speed = BASE_SPEED;

    // 从 localStorage 读取最高分
    try {
      highScore = parseInt(localStorage.getItem('snakeHighScore')) || 0;
      highScoreEl.textContent = highScore;
    } catch (e) {
      // localStorage 不可用时忽略
    }

    /**
     * 初始化游戏
     */
    function initGame() {
      // 蛇初始位置:中间偏左,长度为 3
      snake = [
        { x: 10, y: 10 },
        { x: 9, y: 10 },
        { x: 8, y: 10 },
      ];
      direction = { x: 1, y: 0 };
      nextDirection = { x: 1, y: 0 };
      score = 0;
      speed = BASE_SPEED;
      gamePaused = false;
      gameOverEl.classList.remove('visible');
      scoreEl.textContent = '0';
      generateFood();
    }

    /**
     * 在空白位置随机生成食物
     */
    function generateFood() {
      const occupied = new Set(snake.map(s => `${s.x},${s.y}`));
      const available = [];

      for (let x = 0; x < GRID_SIZE; x++) {
        for (let y = 0; y < GRID_SIZE; y++) {
          if (!occupied.has(`${x},${y}`)) {
            available.push({ x, y });
          }
        }
      }

      if (available.length === 0) {
        // 蛇已经占满所有格子 = 胜利!
        endGame(true);
        return;
      }

      food = available[Math.floor(Math.random() * available.length)];
    }

    /**
     * 游戏主循环
     */
    function update() {
      if (!gameRunning || gamePaused) return;

      // 应用方向
      direction = { ...nextDirection };

      // 计算新头部位置
      const head = snake[0];
      const newHead = {
        x: head.x + direction.x,
        y: head.y + direction.y,
      };

      // 检测墙壁碰撞
      if (
        newHead.x < 0 || newHead.x >= GRID_SIZE ||
        newHead.y < 0 || newHead.y >= GRID_SIZE
      ) {
        endGame(false);
        return;
      }

      // 检测自身碰撞(排除尾部,因为尾部即将移除)
      for (let i = 0; i < snake.length - 1; i++) {
        if (snake[i].x === newHead.x && snake[i].y === newHead.y) {
          endGame(false);
          return;
        }
      }

      // 添加新头部
      snake.unshift(newHead);

      // 是否吃到食物
      if (newHead.x === food.x && newHead.y === food.y) {
        score += 10;
        scoreEl.textContent = score;
        // 每吃 5 个食物加速一次
        if (score % 50 === 0 && speed > 40) {
          speed -= 10;
          clearInterval(gameLoop);
          gameLoop = setInterval(update, speed);
        }
        generateFood();
      } else {
        // 移除尾部
        snake.pop();
      }

      draw();
    }

    /**
     * 渲染画面
     */
    function draw() {
      // 清空画布
      ctx.fillStyle = '#0a0a1a';
      ctx.fillRect(0, 0, canvas.width, canvas.height);

      // 绘制网格线(弱化)
      ctx.strokeStyle = 'rgba(255, 255, 255, 0.03)';
      ctx.lineWidth = 0.5;
      for (let i = 0; i <= GRID_SIZE; i++) {
        ctx.beginPath();
        ctx.moveTo(i * CELL_SIZE, 0);
        ctx.lineTo(i * CELL_SIZE, canvas.height);
        ctx.stroke();
        ctx.beginPath();
        ctx.moveTo(0, i * CELL_SIZE);
        ctx.lineTo(canvas.width, i * CELL_SIZE);
        ctx.stroke();
      }

      // 绘制食物(脉冲动画效果)
      const pulse = 1 + 0.15 * Math.sin(Date.now() / 200);
      const foodX = food.x * CELL_SIZE + CELL_SIZE / 2;
      const foodY = food.y * CELL_SIZE + CELL_SIZE / 2;
      const foodRadius = (CELL_SIZE / 2 - 2) * pulse;

      ctx.fillStyle = '#ff4444';
      ctx.shadowColor = '#ff4444';
      ctx.shadowBlur = 10;
      ctx.beginPath();
      ctx.arc(foodX, foodY, foodRadius, 0, Math.PI * 2);
      ctx.fill();
      ctx.shadowBlur = 0;

      // 绘制蛇身
      snake.forEach((segment, index) => {
        const x = segment.x * CELL_SIZE;
        const y = segment.y * CELL_SIZE;
        const padding = 1;

        // 蛇头颜色不同
        if (index === 0) {
          ctx.fillStyle = '#00ff88';
          ctx.shadowColor = '#00ff88';
          ctx.shadowBlur = 8;
        } else {
          // 渐变颜色:越靠近尾部越暗
          const alpha = 1 - (index / snake.length) * 0.6;
          ctx.fillStyle = `rgba(0, 200, 100, ${alpha})`;
          ctx.shadowBlur = 0;
        }

        ctx.beginPath();
        ctx.roundRect(
          x + padding,
          y + padding,
          CELL_SIZE - padding * 2,
          CELL_SIZE - padding * 2,
          3
        );
        ctx.fill();
      });
      ctx.shadowBlur = 0;
    }

    /**
     * 结束游戏
     * @param {boolean} won - 是否胜利
     */
    function endGame(won) {
      gameRunning = false;
      clearInterval(gameLoop);

      if (won) {
        gameOverEl.textContent = '🎉 恭喜通关!';
        gameOverEl.style.color = '#00ff88';
      } else {
        gameOverEl.textContent = '游戏结束!';
        gameOverEl.style.color = '#ff4444';
      }
      gameOverEl.classList.add('visible');

      // 更新最高分
      if (score > highScore) {
        highScore = score;
        highScoreEl.textContent = highScore;
        try {
          localStorage.setItem('snakeHighScore', highScore);
        } catch (e) {
          // ignore
        }
      }
    }

    /**
     * 开始游戏
     */
    function startGame() {
      initGame();
      gameRunning = true;
      draw();
      if (gameLoop) clearInterval(gameLoop);
      gameLoop = setInterval(update, speed);
    }

    // 键盘控制
    document.addEventListener('keydown', (e) => {
      if (!gameRunning && e.key !== 'r') return;

      switch (e.key) {
        case 'ArrowUp':
        case 'w':
        case 'W':
          e.preventDefault();
          if (direction.y !== 1) {
            nextDirection = { x: 0, y: -1 };
          }
          break;
        case 'ArrowDown':
        case 's':
        case 'S':
          e.preventDefault();
          if (direction.y !== -1) {
            nextDirection = { x: 0, y: 1 };
          }
          break;
        case 'ArrowLeft':
        case 'a':
        case 'A':
          e.preventDefault();
          if (direction.x !== 1) {
            nextDirection = { x: -1, y: 0 };
          }
          break;
        case 'ArrowRight':
        case 'd':
        case 'D':
          e.preventDefault();
          if (direction.x !== -1) {
            nextDirection = { x: 1, y: 0 };
          }
          break;
        case ' ':
          e.preventDefault();
          if (gameRunning) {
            gamePaused = !gamePaused;
            gameOverEl.textContent = '已暂停';
            gameOverEl.style.color = '#ffaa00';
            if (gamePaused) {
              gameOverEl.classList.add('visible');
            } else {
              gameOverEl.classList.remove('visible');
            }
          }
          break;
      }
    });

    // 重新开始按钮
    restartBtn.addEventListener('click', startGame);

    // 自动开始
    startGame();
  </script>
</body>
</html>
部署步骤

步骤 1:准备项目

将上述代码保存为 snake-game/index.html,确保文件可以正常打开和游玩。

步骤 2:配置 CloudStudio MCP

  1. 访问 Cloud Studio 控制台,进入 设置 → 访问令牌
  2. 登录腾讯云账号,生成 API Token(复制保存,仅显示一次)。
  3. 在 CodeBuddy 中打开 设置 → MCP,添加 CloudStudio MCP 服务。
  4. 将生成的 API Token 填入配置并保存。

步骤 3:一键部署

  1. 点击 CodeBuddy 工具栏的部署按钮。
  2. 选择 Tencent Cloud StudioEdgeOne Pages
  3. 首次使用需授权登录腾讯云账号。
  4. 点击 Deploy,CodeBuddy 自动完成构建和部署。
  5. 部署成功后自动生成访问链接和二维码。

步骤 4:验证部署

  • 在浏览器中打开生成的链接。
  • 使用方向键控制蛇的移动。
  • 确认吃食物后分数增长,撞墙后显示"游戏结束"。
  • 扫码在手机上测试移动端体验。
管理已部署应用

在应用管理中可查看已部署的应用,支持在线修改云端代码。

调试过程

场景一:部署失败,提示 Token 无效

现象:点击 Deploy 后报错 "Authentication failed" 或 "Invalid token"。
原因排查:
  1. API Token 过期或被撤销。
  2. Token 在复制粘贴时遗漏了部分字符。
  3. Token 权限不足。
排查步骤:
  1. 登录 Cloud Studio 控制台,检查 Token 状态。
  2. 如果 Token 已过期,重新生成一个。
  3. 在 CodeBuddy MCP 设置中重新填入完整 Token。
  4. 确认 Token 具有部署所需权限(建议生成时勾选全部权限)。
修复:重新生成 Token 并配置。

场景二:构建失败

现象:部署过程中报错 "Build failed"。
原因排查:
  1. 项目依赖未声明(如缺少 package.json)。
  2. 构建命令不正确。
  3. 构建产物路径与预期不符。
排查步骤:
  1. 对于纯 HTML 项目(如本例),不需要构建步骤,
     确认选择了"静态网站"类型的部署方式。
  2. 如果是有构建步骤的项目,检查 package.json 中的 build 脚本。
  3. 在本地先执行 npm run build,确认构建成功后再部署。
修复:
  - 纯静态项目:直接上传文件,跳过构建。
  - 前端框架项目:确保 package.json 中的 scripts.build 指向正确命令。

场景三:部署成功但页面空白

现象:打开部署链接后页面一片空白,控制台有报错。
原因排查:
  1. HTML 文件编码问题(如中文乱码导致 JS 解析失败)。
  2. 资源路径问题(使用了绝对路径而非相对路径)。
  3. 使用了部署平台不支持的 API。
排查步骤:
  1. 打开浏览器开发者工具(F12),查看 Console 报错。
  2. 检查 Network 面板,确认所有资源都成功加载(状态码 200)。
  3. 确认 HTML 文件头部有 <meta charset="UTF-8">。
修复依据具体报错:
  - 编码问题:确保文件以 UTF-8 编码保存。
  - 路径问题:将所有路径改为相对路径(如 ./style.css 而非 /style.css)。

六、快捷键速查

功能WindowsmacOS
内联编辑Alt + KCmd + K
接受补全TabTab
忽略补全EscEsc
新建对话Ctrl + Shift + LCmd + Shift + L

七、常见问题排查

7.1 终端无法执行命令

现象:在 CodeBuddy 内置终端中执行命令无反应或报错。
原因:VS Code 的 Terminal Shell Integration 未启用。
排查:
  VS Code 设置 → 搜索 "shell" →
  确保 "Terminal > Integrated > Shell Integration: Enabled" 设为 true。
修复:启用后重启 VS Code。

7.2 C/C++ 扩展安装失败

现象:安装 C/C++ 语言扩展时报错。
原因:扩展版本与当前环境不兼容。
修复:
  1. 尝试安装较低版本(在扩展页面点击齿轮 → 安装其他版本)。
  2. 或改用 clangd 替代微软 C/C++ 扩展。

7.3 国内市场下载慢

现象:npm 安装速度极慢或超时。
原因:默认 npm registry 在国外。
修复:
  npm config set registry https://registry.npmmirror.com/

7.4 CodeBuddy 登录后闪退

现象:登录成功后 IDE 闪退或卡死。
原因排查:
  1. 系统资源不足(尤其是低配机器)。
  2. 与其他扩展冲突。
排查步骤:
  1. 关闭 VS Code,以安全模式启动(code --disable-extensions)。
  2. 逐一启用扩展,定位冲突源。
修复:
  - 如果内存不足,关闭不必要应用。
  - 如果扩展冲突,禁用冲突扩展或向 CodeBuddy 团队反馈。

7.5 AI 回复内容质量差

现象:AI 生成的代码质量低,或答非所问。
原因排查:
  1. 上下文不足:没有通过 @ 引用相关文件。
  2. 问题描述不清晰:过于笼统或模糊。
  3. 对话历史过长:上下文窗口被无关内容占满。
排查步骤:
  1. 确保通过 @file / @folder 引用了相关代码文件。
  2. 将需求拆分为具体的小步骤,逐条提问。
  3. 如果对话很长,新建对话重新开始。
修复:遵循"具体>笼统、小步>大步、有上下文>无上下文"的原则提问。