jvibe 1.1.7 → 1.1.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/CHANGELOG.md +118 -0
  2. package/JVIBE.md +1 -1
  3. package/README.md +182 -1
  4. package/bin/jvibe.js +14 -12
  5. package/lib/migrate.js +116 -24
  6. package/lib/migrations/index.js +56 -8
  7. package/lib/plugins/core-tools.js +398 -0
  8. package/lib/plugins/plugins-yaml.js +49 -0
  9. package/lib/plugins/registry.json +440 -0
  10. package/package.json +2 -1
  11. package/scripts/init.js +39 -0
  12. package/scripts/plugins.js +159 -0
  13. package/scripts/setup.js +365 -18
  14. package/scripts/status.js +87 -0
  15. package/scripts/upgrade.js +29 -106
  16. package/scripts/validate.js +125 -0
  17. package/template/.claude/agents/bugfix.md +14 -4
  18. package/template/.claude/agents/developer.md +12 -1
  19. package/template/.claude/agents/doc-sync.md +7 -0
  20. package/template/.claude/agents/planner.md +4 -0
  21. package/template/.claude/agents/reviewer.md +4 -0
  22. package/template/.claude/agents/tester.md +34 -9
  23. package/template/.claude/commands/JVibe:keepgo.md +43 -6
  24. package/template/.claude/error-handling.md +8 -2
  25. package/template/.claude/hooks/load-context.sh +3 -3
  26. package/template/.claude/hooks/load-jvibe-full-context.sh +113 -6
  27. package/template/.claude/hooks/sync-jvibe-context.sh +75 -34
  28. package/template/.claude/settings.json +2 -2
  29. package/template/.claude/skills/agent-browser/SKILL.md +252 -0
  30. package/template/.jvibe-doc-hash.json +1 -1
  31. package/template/.opencode/agent/bugfix.md +13 -4
  32. package/template/.opencode/agent/developer.md +12 -1
  33. package/template/.opencode/agent/doc-sync.md +7 -0
  34. package/template/.opencode/agent/planner.md +4 -0
  35. package/template/.opencode/agent/reviewer.md +4 -0
  36. package/template/.opencode/agent/tester.md +34 -9
  37. package/template/.opencode/command/jvibe-keepgo.md +43 -6
  38. package/template/.opencode/error-handling.md +8 -2
  39. package/template/docs/.jvibe/agent-contracts.yaml +188 -0
  40. package/template/docs/.jvibe/plugins.yaml +15 -0
  41. package/template/docs/core/Feature-List.md +0 -3
  42. package/template/docs/core/Standards.md +7 -3
package/CHANGELOG.md ADDED
@@ -0,0 +1,118 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [1.1.8] - 2026-01-19
9
+
10
+ ### Added
11
+
12
+ - **Agent I/O 协议统一** (`agent-contracts.yaml`)
13
+ - 新增 `docs/.jvibe/agent-contracts.yaml` 作为 subagent 输入输出的单一事实来源
14
+ - 定义了 `planner`、`developer`、`tester`、`bugfix`、`doc-sync` 的 `task_input` 与输出结构
15
+ - 包含硬规则 (HR-001 ~ HR-003) 约束 subagent 行为
16
+
17
+ - **插件管理配置** (`plugins.yaml`)
18
+ - 新增 `docs/.jvibe/plugins.yaml` 管理工具与插件启用状态
19
+ - 区分 `core_plugins`(核心工具)与 `project_plugins`(项目按需)
20
+ - SessionStart hook 会从 `plugins.yaml` 注入 Core Tools 摘要到上下文
21
+ - 新增 `jvibe plugins core`:将缺失的 Core MCP Server 追加写入 `.claude/settings.local.json`(已存在则跳过)
22
+
23
+ - **上下文最小化原则**
24
+ - 所有 Agent 新增"硬规则"章节,禁止全仓库扫描
25
+ - `developer`: 只在 `code_roots`/`test_roots` 范围内读取与修改
26
+ - `tester`: 新增 `mode: targeted | discover` 双模式
27
+ - `bugfix`: 优先使用 tester 报告的 `failures`/`modules_hit`/`files`
28
+
29
+ - **Tester 双模式支持**
30
+ - `targeted`: 给定 `files`,精确限制测试范围
31
+ - `discover`: 允许从测试输出反推落点文件,解决"用户报错但不知道 F-XXX"场景
32
+ - 新增 `issue` 字段用于 discover 模式的问题描述
33
+
34
+ - **Keepgo 用户报错判定**
35
+ - 新增关键词匹配(报错/失败/异常/bug/error 等)自动识别用户报告的问题
36
+ - 支持 `feature_id=null` 时进入 discover 测试流程
37
+ - 新增 `user_reported_issue`、`user_reported_feature_id`、`user_issue` 状态字段
38
+
39
+ ### Changed
40
+
41
+ - **测试失败分流策略**
42
+ - 从简单的 `return_to_developer` 改为 `triage_then_fix` 策略
43
+ - 多模块/核心模块/用户强制 → 调用 `bugfix`
44
+ - 单模块/简单问题 → 回退到 `developer`
45
+
46
+ - **Handoff Payload 结构**
47
+ - 新增 `mode` 字段(`targeted`/`discover`)
48
+ - `files` 字段在 targeted 模式下必填且非空
49
+ - `feature_id` 允许为 `null`
50
+
51
+ - **文档更新条件**
52
+ - `doc_updates` 仅在 `pass` 且 `feature_id` 非空时执行
53
+ - 避免无法关联功能编号时错误更新状态
54
+
55
+ - **Standards.md**
56
+ - 新增 §2.6 Tools & Plugins 章节说明工具与插件概念
57
+
58
+ ### Fixed
59
+
60
+ - **Hook 脚本 grep 错误处理**
61
+ - 修复 `grep -c ... || true` 导致空字符串的问题
62
+ - 改为 `|| echo 0` 确保输出为数字,避免后续算术运算错误
63
+ - 影响文件:`load-context.sh`、`load-jvibe-full-context.sh`、`sync-jvibe-context.sh`
64
+
65
+ - **UserPromptSubmit hook 错误处理**
66
+ - 移除 `set -eo pipefail`,改用 fail-open trap 机制
67
+ - 添加 `_JVIBE_HOOK_SUCCESS` 标记避免重复输出
68
+ - 修复 Project.md 摘要提取时 grep 管道无匹配导致的错误
69
+ - 确保非 JVibe 项目也能正常放行
70
+
71
+ - **TUI 插件选择写回**
72
+ - 修复已有 `docs/.jvibe/plugins.yaml` 时项目插件勾选不生效的问题
73
+ - 默认更新 `project_plugins`;勾选 Force Overwrite 会重置整个文件
74
+
75
+ ## [1.1.7] - 2026-01-18
76
+
77
+ ### Added
78
+
79
+ - Hooks 自动加载和同步 JVibe 上下文
80
+
81
+ ### Fixed
82
+
83
+ - Hooks 脚本 jq-free 重写,fail-open 模式
84
+
85
+ ## [1.1.6] - 2026-01-17
86
+
87
+ ### Added
88
+
89
+ - Context hooks 自动加载功能
90
+
91
+ ---
92
+
93
+ ## Summary of v1.1.8
94
+
95
+ This release introduces a major architectural improvement focused on **context minimization** and **unified I/O contracts** for subagents:
96
+
97
+ 1. **Single Source of Truth**: `agent-contracts.yaml` defines all subagent input/output schemas
98
+ 2. **Dual-mode Testing**: `targeted` (precise) vs `discover` (user-reported issues without F-XXX)
99
+ 3. **Smarter Dispatch**: Main agent no longer directly fixes code; uses `bugfix` for complex issues
100
+ 4. **Robust Hooks**: Fixed shell script arithmetic errors with proper fallback values
101
+
102
+ ## [1.1.9] - 2026-01-20
103
+
104
+ ### Added
105
+
106
+ - **Project Tools 注册表**:新增 `lib/plugins/registry.json` 的 Project Tools 完整条目(含 MCP 模板与依赖)
107
+ - **Core Tools: Skill 支持**:支持 Core Tools 以 `skill` 形态集成(Agent Browser)
108
+ - 固定 Agent Browser Skill 到 `v0.6.0`,并将 `SKILL.md` vendoring 到 `template/.claude/skills/agent-browser/SKILL.md`
109
+
110
+ ### Changed
111
+
112
+ - **Core Tools 配置输出**:分离 MCP Server 与 Skill 的计数与提示(init/setup/plugins core)
113
+
114
+ ### Fixed
115
+
116
+ - **Core Tools env 占位符**:写入 `.claude/settings.local.json` 时自动填充当前环境变量,并避免写入未解析的 `{{VAR}}` 占位符
117
+ - **Skill 下载鲁棒性**:为 Skill 下载增加超时/重定向/体积上限,避免卡死或异常大文件
118
+ - **非交互 stdin**:`jvibe plugins core` 在 CI/重定向 stdin 下默认跳过交互安装提示,避免挂起
package/JVIBE.md CHANGED
@@ -75,7 +75,7 @@ TODO 完成情况 → 功能状态
75
75
  5. **测试自动派发**:TODO 包含“测试/test”时,进入测试阶段必须自动调用 tester,无需用户手动指定
76
76
  6. **已有项目初始化**:若项目已有代码/文档,/JVibe:init 应先扫描现有项目并用扫描结果填充 Project 与 Feature-List
77
77
  7. **Bugfix 调用**:tester 报告失败且问题涉及**多模块**或**核心模块**时才调用 bugfix;否则回退给 developer。用户明确要求时可直接调用
78
- 8. **MCP 权限**:主 Agent 与所有 Sub-Agents 允许直接调用 MCP 工具进行查询/集成操作
78
+ 8. **Tools/Plugins 权限**:主 Agent 与所有 Sub-Agents 允许直接调用 Tools/Plugins 进行查询/集成操作(包括 MCP Server、Skills、Daemon 等)
79
79
 
80
80
  保持此管理块,以便 `jvibe upgrade` 更新指令。
81
81
  <!-- JVIBE:END -->
package/README.md CHANGED
@@ -1,9 +1,40 @@
1
+ <div align="center">
2
+
1
3
  # JVibe
2
4
 
3
- > 文档驱动的 AI 辅助开发系统
5
+ **文档驱动的 AI 辅助开发系统**
6
+
7
+ *Doc-driven AI-assisted Development System for Claude Code & OpenCode*
4
8
 
5
9
  [![npm version](https://badge.fury.io/js/jvibe.svg)](https://badge.fury.io/js/jvibe)
10
+ [![npm downloads](https://img.shields.io/npm/dm/jvibe.svg)](https://www.npmjs.com/package/jvibe)
6
11
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
12
+ [![Node.js](https://img.shields.io/badge/Node.js-%3E%3D16.0.0-brightgreen)](https://nodejs.org/)
13
+ [![PRs Welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/9963KK/VibeCoding-Tech/pulls)
14
+
15
+ [快速开始](#-快速开始) · [使用方法](#-使用方法一览) · [文档](#-文档) · [贡献](#-贡献)
16
+
17
+ </div>
18
+
19
+ ---
20
+
21
+ ## 目录
22
+
23
+ - [什么是 JVibe?](#-什么是-jvibe)
24
+ - [为什么选择 JVibe?](#-为什么选择-jvibe)
25
+ - [快速开始](#-快速开始)
26
+ - [使用方法一览](#-使用方法一览)
27
+ - [典型使用场景](#-典型使用场景)
28
+ - [项目结构](#-项目结构)
29
+ - [文档体系](#-文档体系)
30
+ - [Agent 架构](#-agent-架构)
31
+ - [CLI 命令](#-cli-命令)
32
+ - [Core Tools 维护](#-core-tools-维护)
33
+ - [核心原则](#-核心原则)
34
+ - [���见问题](#-常见问题)
35
+ - [文档](#-文档)
36
+ - [贡献](#-贡献)
37
+ - [许可证](#-许可证)
7
38
 
8
39
  ---
9
40
 
@@ -18,6 +49,35 @@ JVibe 是一个**文档驱动的 AI 辅助开发系统**,面向 Claude Code
18
49
 
19
50
  ---
20
51
 
52
+ ## 💡 为什么选择 JVibe?
53
+
54
+ | 痛点 | JVibe 解决方案 |
55
+ |------|---------------|
56
+ | AI 缺乏项目上下文,每次都要重复解释 | 自动加载项目文档,AI 开箱即知项目全貌 |
57
+ | 功能状态散落各处,难以追踪 | 单一事实来源(SoT),状态只在一处维护 |
58
+ | AI 生成代码质量参差不齐 | 多 Agent 分工协作,专业的事交给专业的 Agent |
59
+ | 文档与代码脱节,维护困难 | 文档驱动开发,代码变更自动触发文档同步 |
60
+ | 团队协作时上下文丢失 | 结构化任务交接文件,无缝衔接工作进度 |
61
+
62
+ **核心优势**:
63
+
64
+ ```
65
+ ┌─────────────────────────────────────────────────────────────┐
66
+ │ JVibe 工作流 │
67
+ ├─────────────────────────────────────────────────────────────┤
68
+ │ │
69
+ │ 需求 ──→ planner ──→ developer ──→ tester ──→ reviewer │
70
+ │ │ │ │ │ │
71
+ │ └──────────────────────┴────────────┴───────────┘ │
72
+ │ ↓ │
73
+ │ doc-sync │
74
+ │ (自动同步状态) │
75
+ │ │
76
+ └─────────────────────────────────────────────────────────────┘
77
+ ```
78
+
79
+ ---
80
+
21
81
  ## 🚀 快速开始
22
82
 
23
83
  ### 初始化方式(选一种)
@@ -270,6 +330,44 @@ TODO 完成情况 → 功能状态
270
330
 
271
331
  ---
272
332
 
333
+ ## 🔌 Core Tools 维护
334
+
335
+ ### Agent Browser Skill 更新
336
+
337
+ Agent Browser Skill 当前固定在 **v0.6.0** 版本,以确保稳定性。如需更新到最新版本:
338
+
339
+ #### 方法 1:手动更新(推荐)
340
+
341
+ ```bash
342
+ # 1. 访问 GitHub Releases 页面查看最新版本
343
+ # https://github.com/vercel-labs/agent-browser/releases
344
+
345
+ # 2. 下载最新版本的 SKILL.md
346
+ curl -o .claude/skills/agent-browser/SKILL.md \
347
+ https://raw.githubusercontent.com/vercel-labs/agent-browser/v0.7.0/skills/agent-browser/SKILL.md
348
+
349
+ # 3. 更新 CLI 到对应版本
350
+ npm install -g agent-browser@latest
351
+ agent-browser install
352
+ ```
353
+
354
+ #### 方法 2:修改 registry.json(高级用户)
355
+
356
+ 如果你想让 `jvibe plugins core` 自动使用新版本:
357
+
358
+ 1. 编辑 `lib/plugins/registry.json`
359
+ 2. 找到 `agent-browser` 配置(约第 80 行)
360
+ 3. 修改 `skillSource` 指向新版本:
361
+ ```json
362
+ "skillSource": "https://raw.githubusercontent.com/vercel-labs/agent-browser/v0.7.0/skills/agent-browser/SKILL.md"
363
+ ```
364
+ 4. 删除现有 Skill 文件:`rm -rf .claude/skills/agent-browser`
365
+ 5. 重新运行:`jvibe plugins core`
366
+
367
+ > **注意**:更新前请查看 [Agent Browser Changelog](https://github.com/vercel-labs/agent-browser/releases) 确认兼容性。
368
+
369
+ ---
370
+
273
371
  ## 🎯 核心原则
274
372
 
275
373
  JVibe 基于以下原则设计:
@@ -281,12 +379,83 @@ JVibe 基于以下原则设计:
281
379
 
282
380
  ---
283
381
 
382
+ ## ❓ 常见问题
383
+
384
+ <details>
385
+ <summary><b>JVibe 支持哪些 AI 编码工具?</b></summary>
386
+
387
+ 目前支持:
388
+ - **Claude Code** - Anthropic 官方 CLI 工具
389
+ - **OpenCode** - 开源 AI 编码助手
390
+
391
+ 通过 `--adapter` 参数选择适配器,或使用 `--adapter=both` 同时支持两者。
392
+ </details>
393
+
394
+ <details>
395
+ <summary><b>已有项目如何接入 JVibe?</b></summary>
396
+
397
+ 推荐使用 `/JVibe:init` 命令(在 Claude Code 中)或 `/jvibe-init`(在 OpenCode 中)。AI 会自动扫描现有代码和文档,生成符合项目现状的 Feature-List 和 Project 文档。
398
+ </details>
399
+
400
+ <details>
401
+ <summary><b>CORE-DOCS 和 PROJECT-DOCS 有什么区别?</b></summary>
402
+
403
+ - **CORE-DOCS**:固定 4 个核心文档,所有项目结构相同,自动存在
404
+ - **PROJECT-DOCS**:按需创建的扩展文档(如 API 文档、数据库文档),必须在规范文档中注册
405
+
406
+ 详见 [CORE vs PROJECT 文档](docs/CORE_VS_PROJECT_DOCS.md)。
407
+ </details>
408
+
409
+ <details>
410
+ <summary><b>如何升级到新版本?</b></summary>
411
+
412
+ ```bash
413
+ # 检查是否有新版本
414
+ jvibe upgrade --check
415
+
416
+ # 执行升级(会提示确认)
417
+ jvibe upgrade
418
+
419
+ # 强制升级(跳过确认)
420
+ jvibe upgrade --force
421
+ ```
422
+ </details>
423
+
424
+ <details>
425
+ <summary><b>Agent 之间如何协作?</b></summary>
426
+
427
+ JVibe 采用流水线式协作:
428
+ 1. `planner` 分析需求,拆解为 TODO 列表
429
+ 2. `developer` 逐项完成 TODO,编写代码
430
+ 3. `tester` 执行测试,验证功能
431
+ 4. `bugfix` 处理复杂 bug(多模块/核心模块问题)
432
+ 5. `reviewer` 代码审查,确保质量
433
+ 6. `doc-sync` 自动同步文档状态
434
+
435
+ 使用 `/JVibe:keepgo` 可自动推进到下一阶段。
436
+ </details>
437
+
438
+ <details>
439
+ <summary><b>如何卸载 JVibe?</b></summary>
440
+
441
+ ```bash
442
+ # 卸载项目内的 JVibe 配置
443
+ jvibe uninstall
444
+
445
+ # 全局卸载
446
+ npm uninstall -g jvibe
447
+ ```
448
+ </details>
449
+
450
+ ---
451
+
284
452
  ## 📖 文档
285
453
 
286
454
  - [快速开始指南](docs/GETTING_STARTED.md)
287
455
  - [CLI 命令参考](docs/CLI_COMMANDS.md)
288
456
  - [架构说明](docs/ARCHITECTURE.md)
289
457
  - [CORE vs PROJECT 文档](docs/CORE_VS_PROJECT_DOCS.md)
458
+ - [Tools & Plugins(工具与插件)](docs/TOOLS_AND_PLUGINS.md)
290
459
  - [贡献指南](docs/CONTRIBUTING.md)
291
460
 
292
461
  ---
@@ -314,3 +483,15 @@ JVibe 基于以下原则设计:
314
483
  - [Claude Code 官方文档](https://docs.anthropic.com/claude-code)
315
484
  - [OpenCode 官方文档](https://opencode.ai/docs)
316
485
  - [OpenSpec](https://github.com/openspec/openspec) - 灵感来源
486
+ - [GitHub Issues](https://github.com/9963KK/VibeCoding-Tech/issues) - 问题反馈
487
+ - [NPM Package](https://www.npmjs.com/package/jvibe) - NPM 主页
488
+
489
+ ---
490
+
491
+ <div align="center">
492
+
493
+ **如果 JVibe 对你有帮助,请给个 ⭐ Star 支持一下!**
494
+
495
+ Made with ❤️ by [9963KK](https://github.com/9963KK)
496
+
497
+ </div>
package/bin/jvibe.js CHANGED
@@ -69,25 +69,14 @@ program
69
69
  // upgrade 命令
70
70
  program
71
71
  .command('upgrade')
72
- .description('升级到最新版本(默认卸载重装)')
72
+ .description('升级到最新版本(卸载重装)')
73
73
  .option('--check', '仅检查更新,不执行升级', false)
74
74
  .option('--force', '强制升级,跳过确认', false)
75
- .option('--migrate', '仅执行迁移,不更新到最新版本', false)
76
75
  .action(async (options) => {
77
76
  const upgrade = require('../scripts/upgrade');
78
77
  await upgrade(options);
79
78
  });
80
79
 
81
- // migrate 命令(upgrade --migrate 的别名)
82
- program
83
- .command('migrate')
84
- .description('迁移旧版本配置到新格式')
85
- .option('--force', '强制迁移,跳过确认', false)
86
- .action(async (options) => {
87
- const upgrade = require('../scripts/upgrade');
88
- await upgrade({ ...options, migrate: true });
89
- });
90
-
91
80
  // uninstall 命令
92
81
  program
93
82
  .command('uninstall')
@@ -120,6 +109,19 @@ program
120
109
  await validate();
121
110
  });
122
111
 
112
+ // plugins 命令
113
+ const plugins = program
114
+ .command('plugins')
115
+ .description('插件相关操作(Core Tools 等)');
116
+
117
+ plugins
118
+ .command('core')
119
+ .description('配置 Core Tools(写入缺失的 MCP Server 到 .claude/settings.local.json)')
120
+ .action(async () => {
121
+ const { configureCore } = require('../scripts/plugins');
122
+ await configureCore();
123
+ });
124
+
123
125
  (async () => {
124
126
  try {
125
127
  const handled = await maybeRunSetup();
package/lib/migrate.js CHANGED
@@ -471,6 +471,7 @@ async function inferOpencodeVersionFromStructure(opencodeDir) {
471
471
  path.join('agent', 'planner.md'),
472
472
  path.join('agent', 'developer.md'),
473
473
  path.join('agent', 'tester.md'),
474
+ path.join('agent', 'bugfix.md'),
474
475
  path.join('agent', 'reviewer.md'),
475
476
  path.join('agent', 'doc-sync.md')
476
477
  ];
@@ -584,16 +585,25 @@ async function checkContentMigration(projectDir, currentVersion) {
584
585
  * @returns {Promise<MigrationPlan>}
585
586
  */
586
587
  async function getMigrationPlan(projectDir, versionInfo) {
588
+ const latestVersion = pkg.version || null;
589
+ const currentVersion = versionInfo.version || null;
590
+ const versionMismatch = Boolean(latestVersion && currentVersion && currentVersion !== latestVersion);
591
+
587
592
  const plan = {
588
- needsMigration: versionInfo.isLegacy,
593
+ // needsMigration 表示“是否需要执行自动迁移动作”,不等同于 isLegacy
594
+ needsMigration: false,
589
595
  needsAIMigration: versionInfo.contentMigration.required,
590
596
  tasks: [],
591
597
  aiTasks: [],
592
598
  details: {
593
599
  docsToMove: [],
600
+ hooksNeedUpdate: false,
594
601
  hooksToUpdate: [],
602
+ commandsNeedUpdate: false,
595
603
  commandsToRename: [],
604
+ agentsNeedUpdate: false,
596
605
  agentsToUpdate: [],
606
+ opencodeNeedUpdate: false,
597
607
  configToUpdate: false,
598
608
  contentChanges: versionInfo.contentMigration.changes || []
599
609
  }
@@ -606,7 +616,7 @@ async function getMigrationPlan(projectDir, versionInfo) {
606
616
  }
607
617
  }
608
618
 
609
- if (!versionInfo.isLegacy && !versionInfo.contentMigration.required) {
619
+ if (!versionInfo.isLegacy && !versionInfo.contentMigration.required && !versionMismatch) {
610
620
  return plan;
611
621
  }
612
622
 
@@ -642,7 +652,8 @@ async function getMigrationPlan(projectDir, versionInfo) {
642
652
  if (await fs.pathExists(hooksDir)) {
643
653
  const hookFiles = await fs.readdir(hooksDir);
644
654
  const legacyHookIssues = await checkLegacyHooks(hooksDir);
645
- if (legacyHookIssues.length > 0) {
655
+ if (legacyHookIssues.length > 0 || versionMismatch) {
656
+ plan.details.hooksNeedUpdate = true;
646
657
  plan.details.hooksToUpdate = hookFiles.filter(f => f.endsWith('.sh'));
647
658
  plan.tasks.push('更新 hooks 脚本到最新版本');
648
659
  }
@@ -660,21 +671,49 @@ async function getMigrationPlan(projectDir, versionInfo) {
660
671
  plan.details.commandsToRename.push({ from: cmd, to: newName });
661
672
  plan.tasks.push(`重命名 command: ${cmd} → ${newName}`);
662
673
  }
674
+
675
+ if (legacyCommands.length > 0 || versionMismatch) {
676
+ plan.details.commandsNeedUpdate = true;
677
+ if (legacyCommands.length === 0) {
678
+ plan.tasks.push('更新 commands 到最新版本');
679
+ }
680
+ }
663
681
  }
664
682
 
665
683
  // 4. 检查 agents 更新需求
666
684
  const agentsDir = path.join(projectDir, '.claude/agents');
667
685
  if (await fs.pathExists(agentsDir)) {
668
- plan.details.agentsToUpdate = ['planner.md', 'developer.md', 'reviewer.md', 'doc-sync.md'];
686
+ plan.details.agentsNeedUpdate = true;
687
+ plan.details.agentsToUpdate = ['planner.md', 'developer.md', 'tester.md', 'reviewer.md', 'doc-sync.md', 'bugfix.md'];
669
688
  plan.tasks.push('更新所有 agents 到最新版本');
670
689
  }
671
690
 
672
- // 5. 配置更新
673
- if (!versionInfo.version) {
691
+ // 5. OpenCode 配置更新
692
+ const opencodeDir = path.join(projectDir, '.opencode');
693
+ if (await fs.pathExists(opencodeDir)) {
694
+ if (versionInfo.isLegacy || versionMismatch) {
695
+ plan.details.opencodeNeedUpdate = true;
696
+ plan.tasks.push('更新 OpenCode 配置到最新版本');
697
+ }
698
+ }
699
+
700
+ // 6. 配置更新
701
+ if (!versionInfo.version || versionMismatch) {
674
702
  plan.details.configToUpdate = true;
675
- plan.tasks.push('更新 settings.json 添加版本信息');
703
+ plan.tasks.push('更新版本信息(settings.json / jvibe.json)');
676
704
  }
677
705
 
706
+ // 最终 needsMigration:只要存在任一自动迁移动作即可
707
+ plan.needsMigration = Boolean(
708
+ plan.details.docsToMove.length > 0 ||
709
+ plan.details.hooksNeedUpdate ||
710
+ plan.details.commandsNeedUpdate ||
711
+ plan.details.commandsToRename.length > 0 ||
712
+ plan.details.agentsNeedUpdate ||
713
+ plan.details.opencodeNeedUpdate ||
714
+ plan.details.configToUpdate
715
+ );
716
+
678
717
  return plan;
679
718
  }
680
719
 
@@ -760,30 +799,76 @@ async function migrateAgents(projectDir, templateDir) {
760
799
  console.log(chalk.gray(' 已更新 agents'));
761
800
  }
762
801
 
802
+ /**
803
+ * 执行 OpenCode 配置迁移(覆盖同步)
804
+ * @param {string} projectDir - 项目目录
805
+ * @param {string} templateDir - 模板目录
806
+ */
807
+ async function migrateOpencode(projectDir, templateDir) {
808
+ const projectOpencodeDir = path.join(projectDir, '.opencode');
809
+ const templateOpencodeDir = path.join(templateDir, '.opencode');
810
+
811
+ if (!await fs.pathExists(projectOpencodeDir)) {
812
+ return;
813
+ }
814
+ if (!await fs.pathExists(templateOpencodeDir)) {
815
+ return;
816
+ }
817
+
818
+ await fs.copy(templateOpencodeDir, projectOpencodeDir, { overwrite: true });
819
+ console.log(chalk.gray(' 已更新 OpenCode 配置'));
820
+ }
821
+
763
822
  /**
764
823
  * 更新配置文件
765
824
  * @param {string} projectDir - 项目目录
766
825
  * @param {string} newVersion - 新版本号
767
826
  */
768
827
  async function updateConfig(projectDir, newVersion) {
769
- const settingsPath = path.join(projectDir, '.claude/settings.json');
828
+ const migratedAt = new Date().toISOString();
770
829
 
771
- let settings = {};
772
- if (await fs.pathExists(settingsPath)) {
773
- try {
774
- settings = await fs.readJson(settingsPath);
775
- } catch (e) {
776
- // 如果读取失败,创建新的配置
830
+ // Claude Code settings
831
+ const claudeDir = path.join(projectDir, '.claude');
832
+ if (await fs.pathExists(claudeDir)) {
833
+ const settingsPath = path.join(claudeDir, 'settings.json');
834
+ let settings = {};
835
+ if (await fs.pathExists(settingsPath)) {
836
+ try {
837
+ settings = await fs.readJson(settingsPath);
838
+ } catch (e) {
839
+ // 如果读取失败,创建新的配置
840
+ }
777
841
  }
842
+
843
+ settings.jvibe = {
844
+ ...settings.jvibe,
845
+ version: newVersion,
846
+ migratedAt
847
+ };
848
+
849
+ await fs.writeJson(settingsPath, settings, { spaces: 2 });
778
850
  }
779
851
 
780
- settings.jvibe = {
781
- ...settings.jvibe,
782
- version: newVersion,
783
- migratedAt: new Date().toISOString()
784
- };
852
+ // OpenCode meta
853
+ const opencodeDir = path.join(projectDir, '.opencode');
854
+ if (await fs.pathExists(opencodeDir)) {
855
+ const opencodeMetaPath = path.join(opencodeDir, 'jvibe.json');
856
+ let meta = {};
857
+ if (await fs.pathExists(opencodeMetaPath)) {
858
+ try {
859
+ meta = await fs.readJson(opencodeMetaPath);
860
+ } catch (e) {
861
+ // ignore
862
+ }
863
+ }
864
+ meta = {
865
+ ...meta,
866
+ version: newVersion,
867
+ migratedAt
868
+ };
869
+ await fs.writeJson(opencodeMetaPath, meta, { spaces: 2 });
870
+ }
785
871
 
786
- await fs.writeJson(settingsPath, settings, { spaces: 2 });
787
872
  console.log(chalk.gray(' 已更新版本信息'));
788
873
  }
789
874
 
@@ -857,24 +942,30 @@ async function executeMigration(projectDir, templateDir, plan, newVersion) {
857
942
  await migrateFeatureList(projectDir);
858
943
 
859
944
  // 3. 迁移 hooks
860
- if (plan.details.hooksToUpdate.length > 0) {
945
+ if (plan.details.hooksNeedUpdate) {
861
946
  console.log(chalk.gray(' 更新 hooks...'));
862
947
  await migrateHooks(projectDir, templateDir);
863
948
  }
864
949
 
865
950
  // 4. 迁移 commands
866
- if (plan.details.commandsToRename.length > 0) {
951
+ if (plan.details.commandsNeedUpdate || plan.details.commandsToRename.length > 0) {
867
952
  console.log(chalk.gray(' 更新 commands...'));
868
953
  await migrateCommands(projectDir, templateDir, plan);
869
954
  }
870
955
 
871
956
  // 5. 迁移 agents
872
- if (plan.details.agentsToUpdate.length > 0) {
957
+ if (plan.details.agentsNeedUpdate) {
873
958
  console.log(chalk.gray(' 更新 agents...'));
874
959
  await migrateAgents(projectDir, templateDir);
875
960
  }
876
961
 
877
- // 6. 更新配置
962
+ // 6. 迁移 OpenCode
963
+ if (plan.details.opencodeNeedUpdate) {
964
+ console.log(chalk.gray(' 更新 OpenCode 配置...'));
965
+ await migrateOpencode(projectDir, templateDir);
966
+ }
967
+
968
+ // 7. 更新配置
878
969
  if (plan.details.configToUpdate) {
879
970
  console.log(chalk.gray(' 更新配置...'));
880
971
  await updateConfig(projectDir, newVersion);
@@ -925,6 +1016,7 @@ module.exports = {
925
1016
  migrateHooks,
926
1017
  migrateCommands,
927
1018
  migrateAgents,
1019
+ migrateOpencode,
928
1020
  migrateFeatureList,
929
1021
  updateConfig
930
1022
  };