jvibe 1.1.1 → 1.1.4

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.
package/JVIBE.md CHANGED
@@ -61,7 +61,7 @@ TODO 完成情况 → 功能状态
61
61
  |-------|------|----------|
62
62
  | **planner** | 需求分析、功能拆解、创建 F-XXX 条目 | Feature-List.md |
63
63
  | **developer** | 代码实现、逐项完成 TODO、勾选完成项 | Feature-List.md + 源代码 |
64
- | **tester** | 测试执行、结果分析、回归验证 | 测试文件(仅在明确要求时) |
64
+ | **tester** | 测试执行、结果分析、回归验证 | 测试文件(测试阶段自动调用) |
65
65
  | **reviewer** | 代码审查、规范检查、PR 描述生成 | 只读 |
66
66
  | **doc-sync** | 状态推导、统计更新、格式检查 | Project.md |
67
67
 
@@ -71,6 +71,8 @@ TODO 完成情况 → 功能状态
71
71
  2. **遵循 SoT 原则**:功能状态只更新 `Feature-List.md`
72
72
  3. **查阅附加材料**:开发前检查 `Appendix.md` 中的相关规范
73
73
  4. **注册 PROJECT-DOCS**:新建项目文档必须在规范文档中注册
74
+ 5. **测试自动派发**:TODO 包含“测试/test”时,进入测试阶段必须自动调用 tester,无需用户手动指定
75
+ 6. **已有项目初始化**:若项目已有代码/文档,/JVibe:init 应先扫描现有项目并用扫描结果填充 Project 与 Feature-List
74
76
 
75
77
  保持此管理块,以便 `jvibe upgrade` 更新指令。
76
78
  <!-- JVIBE:END -->
package/README.md CHANGED
@@ -9,18 +9,18 @@
9
9
 
10
10
  ## 📌 什么是 JVibe?
11
11
 
12
- JVibe 是一个**文档驱动的 AI 辅助开发系统**,专为 Claude Code 设计。它提供:
12
+ JVibe 是一个**文档驱动的 AI 辅助开发系统**,面向 Claude Code 与 OpenCode。核心能力包括:
13
13
 
14
- - 🤖 **5 个专业 Agent**:需求规划、代码开发、测试验证、代码审查、文档同步
15
- - 📝 **结构化文档体系**:CORE-DOCS(4个核心文档)+ PROJECT-DOCS(按需扩展)
16
- - 🔄 **自动化 Hooks**:自动加载上下文、同步功能状态、输出统计信息
17
14
  - 🎯 **单一事实来源**:功能状态只在功能清单中维护(SoT 原则)
15
+ - 🤖 **多 Agent 协作**:规划、开发、测试、审查、文档同步
16
+ - 📝 **结构化文档体系**:CORE-DOCS(4 个核心文档)+ PROJECT-DOCS(按需扩展)
17
+ - 🔄 **自动化 Hooks**:加载上下文、同步状态、输出统计信息
18
18
 
19
19
  ---
20
20
 
21
21
  ## 🚀 快速开始
22
22
 
23
- ### 两种初始化方式
23
+ ### 初始化方式(选一种)
24
24
 
25
25
  JVibe 提供两种初始化方式,根据你的需求选择:
26
26
 
@@ -28,13 +28,32 @@ JVibe 提供两种初始化方式,根据你的需求选择:
28
28
 
29
29
  **适用场景**:新项目、需要完整的文件结构
30
30
 
31
+ 默认进入 TUI 配置向导,如需跳过请使用 `--no-ui`。
32
+
31
33
  ```bash
32
34
  # 全局安装
33
35
  npm install -g jvibe
34
36
 
35
37
  # 初始化项目
36
38
  cd your-project
37
- jvibe init
39
+
40
+ # 进入 TUI 配置(推荐)
41
+ jvibe
42
+
43
+ # 或者
44
+ jvibe setup
45
+
46
+ # 跳过 TUI,直接初始化
47
+ jvibe init --no-ui
48
+
49
+ # Claude Code 适配
50
+ jvibe init --adapter=claude --no-ui
51
+
52
+ # OpenCode 适配
53
+ jvibe init --adapter=opencode --no-ui
54
+
55
+ # 同时适配 Claude Code + OpenCode
56
+ jvibe init --adapter=both --no-ui
38
57
  ```
39
58
 
40
59
  **特点**:
@@ -44,18 +63,22 @@ jvibe init
44
63
 
45
64
  ---
46
65
 
47
- #### 方式 2:Claude Code Skill 初始化
66
+ #### 方式 2:Claude Code / OpenCode 命令初始化
48
67
 
49
68
  **适用场景**:现有项目、需要 AI 引导式创建文档
50
69
 
51
70
  ```bash
52
71
  # 在 Claude Code 中运行
53
72
  /JVibe:init
73
+
74
+ # 在 OpenCode 中运行
75
+ /jvibe-init
54
76
  ```
55
77
 
56
78
  **特点**:
57
79
  - 🤖 AI 引导式询问(项目名称、类型、技术栈)
58
80
  - 🤖 AI 自动分析并规划模块架构
81
+ - 🤖 识别既有项目时先扫描代码/文档并填充 Project 与 Feature-List
59
82
  - 🤖 根据需求生成定制化文档
60
83
  - ⚠️ **注意**:如果已运行 `jvibe init`,无需再执行此命令
61
84
 
@@ -66,41 +89,115 @@ jvibe init
66
89
  | 你的情况 | 推荐方式 | 原因 |
67
90
  |---------|---------|------|
68
91
  | 全新项目 | CLI 初始化 | 一次性获得完整配置 |
69
- | 已有项目,想试用 JVibe | Claude Code Skill | AI 引导更友好 |
92
+ | 已有项目,想试用 JVibe | Claude/OpenCode 命令 | 自动扫描现有代码/文档 |
70
93
  | 需要快速开始 | CLI 初始化 | 无需手动配置 |
71
- | 需要定制化文档 | Claude Code Skill | AI 根据需求生成 |
94
+ | 需要定制化文档 | Claude/OpenCode 命令 | AI 根据需求生成 |
72
95
 
73
96
  ---
74
97
 
75
98
  ### 开始使用
76
99
 
77
- 初始化完成后,在 Claude Code 中使用:
100
+ 初始化完成后,在 Claude Code 或 OpenCode 中使用:
78
101
 
79
102
  ```bash
103
+ # Claude Code
80
104
  /JVibe:status # 查看项目状态
81
105
  /JVibe:keepgo # 自动推进下一步任务
82
106
  /JVibe:pr # 生成 PR 描述
107
+
108
+ # OpenCode
109
+ /jvibe-status # 查看项目状态
110
+ /jvibe-keepgo # 自动推进下一步任务
111
+ /jvibe-pr # 生成 PR 描述
83
112
  ```
84
113
 
85
114
  ---
86
115
 
116
+ ## 🧭 使用方法一览
117
+
118
+ JVibe 支持多种使用方式,可按场景组合:
119
+
120
+ ### 1) 命令行与 TUI
121
+
122
+ ```bash
123
+ # 交互式 TUI
124
+ jvibe
125
+
126
+ # 直接初始化
127
+ jvibe init --no-ui
128
+
129
+ # 仅检查升级
130
+ jvibe upgrade --check
131
+ ```
132
+
133
+ ### 2) AI 命令驱动(Claude/OpenCode)
134
+
135
+ ```bash
136
+ /JVibe:init # 初始化项目文档(已有项目会先扫描)
137
+ /JVibe:keepgo # 自动推进下一步
138
+ /JVibe:status # 查看项目状态
139
+ /JVibe:pr # 生成 PR 描述
140
+ /JVibe:migrate # 迁移旧文档
141
+ ```
142
+
143
+ OpenCode 对应命令:
144
+ `/jvibe-init`、`/jvibe-keepgo`、`/jvibe-status`、`/jvibe-pr`、`/jvibe-migrate`
145
+
146
+ ### 3) 开发流程
147
+
148
+ - 需求描述 → `planner` 生成 TODO
149
+ - 功能实现 → `developer` 完成 TODO
150
+ - 测试验证 → `tester` 执行测试
151
+ - 代码审查 → `reviewer` 给出审查结论
152
+ - 文档同步 → `doc-sync` 更新状态与统计
153
+
154
+ ### 4) 运维与校验
155
+
156
+ ```bash
157
+ jvibe upgrade # 升级到最新版本
158
+ jvibe migrate # 保留旧结构的迁移模式
159
+ jvibe validate # 检查配置完整性
160
+ jvibe uninstall # 卸载 JVibe 配置
161
+ ```
162
+
163
+ ---
164
+
165
+ ## 🎯 典型使用场景
166
+
167
+ - **新项目快速启动**:`jvibe` 进入 TUI 初始化 → `/JVibe:status` 查看状态 → `/JVibe:keepgo` 推进任务
168
+ - **已有项目接入**:`/JVibe:init` 自动扫描代码/文档 → 生成 Project 与 Feature-List → 再用 `/JVibe:keepgo` 继续
169
+ - **需求到落地**:描述需求 → `planner` 拆解 TODO → `developer` 实现 → `tester` 验证 → `reviewer` 反馈
170
+ - **测试自动派发**:TODO 含“测试/test” → keepgo 进入测试阶段自动调用 `tester`
171
+ - **版本升级**:`jvibe upgrade --check` 先确认 → `jvibe upgrade` 执行升级(需要确认或 `--force`)
172
+ - **协作交接**:以 `docs/core/Feature-List.md` 为 SoT,同步进度到 `docs/.jvibe/tasks.yaml`
173
+
174
+ ---
175
+
87
176
  ## 📂 项目结构
88
177
 
89
178
  运行 `jvibe init` 后,你的项目将包含:
90
179
 
91
180
  ```
92
181
  your-project/
93
- ├── .claude/ # Claude Code 配置
182
+ ├── .claude/ # Claude Code 配置(可选)
94
183
  │ ├── agents/ # 5 个 Sub-Agents
95
- │ ├── commands/ # 3 个 JVibe Skills
184
+ │ ├── commands/ # 5 个 JVibe Skills
96
185
  │ ├── hooks/ # 4 个自动化 Hooks
97
186
  │ └── settings.json
98
187
 
188
+ ├── .opencode/ # OpenCode 配置(可选)
189
+ │ ├── agent/ # 5 个 Sub-Agents
190
+ │ ├── command/ # 5 个 JVibe Commands
191
+ │ ├── permissions.yaml # 权限矩阵
192
+ │ ├── error-handling.md # 错误处理策略
193
+ │ ├── instructions.md # OpenCode 启动指令
194
+ │ └── opencode.jsonc
195
+
99
196
  ├── docs/
100
197
  │ ├── core/ # CORE-DOCS(4个固定核心文档)
101
198
  │ │ ├── Standards.md # 入口和索引
102
- │ │ ├── Project.md # 架构与模块边界
103
- │ │ ├── Feature-List.md # 功能状态唯一来源(SoT)
199
+ │ │ ├── Project.md # 架构与模块边界
200
+ │ │ ├── Feature-List.md # 功能状态唯一来源(SoT)
104
201
  │ │ └── Appendix.md # 规范索引
105
202
  │ ├── .jvibe/ # 任务交接文件
106
203
  │ │ └── tasks.yaml # 单文件协作入口
@@ -144,7 +241,7 @@ TODO 完成情况 → 功能状态
144
241
 
145
242
  | Agent | 职责 | 模型 |
146
243
  |-------|------|------|
147
- | **planner** | 需求分析、功能拆解 | Sonnet |
244
+ | **planner** | 需求分析、功能拆解 | Opus |
148
245
  | **developer** | 代码实现、TODO 完成 | Sonnet |
149
246
  | **tester** | 测试执行、结果分析 | Sonnet |
150
247
  | **reviewer** | 代码审查、规范检查 | Sonnet |
@@ -157,8 +254,11 @@ TODO 完成情况 → 功能状态
157
254
  | 命令 | 说明 |
158
255
  |------|------|
159
256
  | `jvibe init` | 初始化 JVibe 项目 |
257
+ | `jvibe setup` | 启动 TUI 配置向导 |
160
258
  | `jvibe init --mode=minimal` | 最小化初始化(仅 Core 文档) |
161
259
  | `jvibe init --force` | 强制覆盖已存在的配置 |
260
+ | `jvibe init --adapter=opencode` | 初始化 OpenCode 适配 |
261
+ | `jvibe init --adapter=both` | 同时适配 Claude Code + OpenCode |
162
262
  | `jvibe upgrade` | 升级到最新版本(默认卸载重装) |
163
263
  | `jvibe upgrade --check` | 仅检查更新 |
164
264
  | `jvibe upgrade --migrate` | 仅执行旧版迁移 |
@@ -210,4 +310,5 @@ JVibe 基于以下原则设计:
210
310
  ## 🔗 相关链接
211
311
 
212
312
  - [Claude Code 官方文档](https://docs.anthropic.com/claude-code)
313
+ - [OpenCode 官方文档](https://opencode.ai/docs)
213
314
  - [OpenSpec](https://github.com/openspec/openspec) - 灵感来源
package/bin/jvibe.js CHANGED
@@ -5,6 +5,7 @@
5
5
  *
6
6
  * 用法:
7
7
  * jvibe init [--mode=full|minimal] 初始化项目
8
+ * jvibe setup 启动 TUI 配置
8
9
  * jvibe upgrade 升级到最新版本
9
10
  * jvibe status 查看项目状态
10
11
  * jvibe validate 验证项目配置
@@ -15,6 +16,15 @@ const pkg = require('../package.json');
15
16
 
16
17
  const program = new Command();
17
18
 
19
+ async function maybeRunSetup() {
20
+ if (process.argv.length <= 2) {
21
+ const setup = require('../scripts/setup');
22
+ await setup();
23
+ return true;
24
+ }
25
+ return false;
26
+ }
27
+
18
28
  program
19
29
  .name('jvibe')
20
30
  .description('JVibe - 文档驱动的 AI 辅助开发系统')
@@ -25,10 +35,35 @@ program
25
35
  .command('init')
26
36
  .description('初始化 JVibe 项目')
27
37
  .option('--mode <type>', '模式: full(完整)或 minimal(最小)', 'full')
38
+ .option('--adapter <type>', '适配环境: claude | opencode | both', 'claude')
28
39
  .option('--force', '强制覆盖已存在的文件', false)
40
+ .option('--no-ui', '跳过 TUI,直接执行初始化')
29
41
  .action(async (options) => {
42
+ const hasExplicitFlags = process.argv.some((arg) => (
43
+ arg === '--no-ui' ||
44
+ arg === '--force' ||
45
+ arg === '--mode' ||
46
+ arg.startsWith('--mode=') ||
47
+ arg === '--adapter' ||
48
+ arg.startsWith('--adapter=')
49
+ ));
50
+
51
+ if (!hasExplicitFlags && options.ui !== false) {
52
+ const setup = require('../scripts/setup');
53
+ await setup();
54
+ return;
55
+ }
56
+
30
57
  const init = require('../scripts/init');
31
- await init(options);
58
+ await init({ ...options, ui: false });
59
+ });
60
+
61
+ program
62
+ .command('setup')
63
+ .description('启动 TUI 配置向导')
64
+ .action(async () => {
65
+ const setup = require('../scripts/setup');
66
+ await setup();
32
67
  });
33
68
 
34
69
  // upgrade 命令
@@ -85,4 +120,14 @@ program
85
120
  await validate();
86
121
  });
87
122
 
88
- program.parse();
123
+ (async () => {
124
+ try {
125
+ const handled = await maybeRunSetup();
126
+ if (!handled) {
127
+ program.parse();
128
+ }
129
+ } catch (error) {
130
+ console.error('❌ jvibe 执行失败:', error.message || error);
131
+ process.exit(1);
132
+ }
133
+ })();
package/lib/migrate.js CHANGED
@@ -282,6 +282,7 @@ async function detectVersion(projectDir) {
282
282
  legacyIndicators: [],
283
283
  structure: {
284
284
  hasClaudeDir: false,
285
+ hasOpencodeDir: false,
285
286
  hasSettingsJson: false,
286
287
  hasAgents: false,
287
288
  hasCommands: false,
@@ -300,6 +301,8 @@ async function detectVersion(projectDir) {
300
301
 
301
302
  const claudeDir = path.join(projectDir, '.claude');
302
303
  const settingsPath = path.join(claudeDir, 'settings.json');
304
+ const opencodeDir = path.join(projectDir, '.opencode');
305
+ const opencodeMetaPath = path.join(opencodeDir, 'jvibe.json');
303
306
  const docsDir = path.join(projectDir, 'docs');
304
307
  const coreDir = path.join(docsDir, 'core');
305
308
  const projectDocsDir = path.join(docsDir, 'project');
@@ -325,6 +328,26 @@ async function detectVersion(projectDir) {
325
328
  result.structure.hasHooks = await fs.pathExists(path.join(claudeDir, 'hooks'));
326
329
  }
327
330
 
331
+ // 检查 .opencode 目录与版本元数据
332
+ if (await fs.pathExists(opencodeDir)) {
333
+ result.structure.hasOpencodeDir = true;
334
+ if (!result.version && await fs.pathExists(opencodeMetaPath)) {
335
+ try {
336
+ const opencodeMeta = await fs.readJson(opencodeMetaPath);
337
+ result.version = opencodeMeta.version || null;
338
+ } catch (e) {
339
+ result.legacyIndicators.push('opencode/jvibe.json 格式错误或损坏');
340
+ }
341
+ }
342
+ }
343
+
344
+ if (!result.version && result.structure.hasOpencodeDir) {
345
+ const inferredVersion = await inferOpencodeVersionFromStructure(opencodeDir);
346
+ if (inferredVersion) {
347
+ result.version = inferredVersion;
348
+ }
349
+ }
350
+
328
351
  // 检查文档目录
329
352
  if (await fs.pathExists(docsDir)) {
330
353
  result.structure.hasDocsCoreDir = await fs.pathExists(coreDir);
@@ -434,6 +457,34 @@ async function checkLegacyHooks(hooksDir) {
434
457
  return issues;
435
458
  }
436
459
 
460
+ async function inferOpencodeVersionFromStructure(opencodeDir) {
461
+ const requiredPaths = [
462
+ 'opencode.jsonc',
463
+ 'permissions.yaml',
464
+ 'error-handling.md',
465
+ 'instructions.md',
466
+ path.join('command', 'jvibe-init.md'),
467
+ path.join('command', 'jvibe-keepgo.md'),
468
+ path.join('command', 'jvibe-migrate.md'),
469
+ path.join('command', 'jvibe-pr.md'),
470
+ path.join('command', 'jvibe-status.md'),
471
+ path.join('agent', 'planner.md'),
472
+ path.join('agent', 'developer.md'),
473
+ path.join('agent', 'tester.md'),
474
+ path.join('agent', 'reviewer.md'),
475
+ path.join('agent', 'doc-sync.md')
476
+ ];
477
+
478
+ for (const relPath of requiredPaths) {
479
+ const fullPath = path.join(opencodeDir, relPath);
480
+ if (!await fs.pathExists(fullPath)) {
481
+ return null;
482
+ }
483
+ }
484
+
485
+ return pkg.version || null;
486
+ }
487
+
437
488
  /**
438
489
  * 检测文档内容迁移需求
439
490
  * @param {string} projectDir - 项目目录
@@ -118,6 +118,28 @@ const MIGRATIONS = [
118
118
  'docs/core/Feature-List.md',
119
119
  'docs/core/Project.md'
120
120
  ]
121
+ },
122
+ {
123
+ version: '1.1.1',
124
+ description: '补丁版本(无文档结构变更)',
125
+ changes: {
126
+ added: [],
127
+ modified: [],
128
+ removed: [],
129
+ renamed: []
130
+ },
131
+ aiMigrationRequired: []
132
+ },
133
+ {
134
+ version: '1.1.2',
135
+ description: '补丁版本(无文档结构变更)',
136
+ changes: {
137
+ added: [],
138
+ modified: [],
139
+ removed: [],
140
+ renamed: []
141
+ },
142
+ aiMigrationRequired: []
121
143
  }
122
144
  ];
123
145
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "jvibe",
3
- "version": "1.1.1",
4
- "description": "\u6587\u6863\u9a71\u52a8\u7684 AI \u8f85\u52a9\u5f00\u53d1\u7cfb\u7edf - Doc-driven AI-assisted development system for Claude Code",
3
+ "version": "1.1.4",
4
+ "description": "\u6587\u6863\u9a71\u52a8\u7684 AI \u8f85\u52a9\u5f00\u53d1\u7cfb\u7edf - Doc-driven AI-assisted development system for Claude Code and OpenCode",
5
5
  "main": "bin/jvibe.js",
6
6
  "bin": {
7
7
  "jvibe": "bin/jvibe.js"
@@ -21,6 +21,7 @@
21
21
  "keywords": [
22
22
  "claude",
23
23
  "claude-code",
24
+ "opencode",
24
25
  "ai",
25
26
  "development",
26
27
  "documentation",
package/scripts/init.js CHANGED
@@ -13,11 +13,18 @@ const TEMPLATE_DIR = path.join(__dirname, '../template');
13
13
  * 初始化 JVibe 项目
14
14
  * @param {Object} options - 初始化选项
15
15
  * @param {string} options.mode - 模式: 'full' 或 'minimal'
16
+ * @param {string} options.adapter - 适配环境: 'claude' | 'opencode' | 'both'
16
17
  * @param {boolean} options.force - 是否强制覆盖
17
18
  */
18
19
  async function init(options = {}) {
19
20
  const mode = options.mode || 'full';
20
21
  const force = options.force || false;
22
+ const adapter = (options.adapter || 'claude').toLowerCase();
23
+ const normalizedAdapter = ['claude', 'opencode', 'both'].includes(adapter)
24
+ ? adapter
25
+ : 'claude';
26
+ const useClaude = normalizedAdapter === 'claude' || normalizedAdapter === 'both';
27
+ const useOpencode = normalizedAdapter === 'opencode' || normalizedAdapter === 'both';
21
28
  const cwd = process.cwd();
22
29
 
23
30
  console.log(chalk.blue('\n🚀 正在初始化 JVibe...\n'));
@@ -25,21 +32,47 @@ async function init(options = {}) {
25
32
  try {
26
33
  // 1. 检查是否已存在 JVibe 配置
27
34
  const claudeDir = path.join(cwd, '.claude');
28
- if (await fs.pathExists(claudeDir) && !force) {
29
- console.log(chalk.yellow('⚠️ 检测到已存在 .claude/ 目录'));
30
- console.log(chalk.yellow(' 使用 --force 选项强制覆盖'));
31
- return;
35
+ const opencodeDir = path.join(cwd, '.opencode');
36
+ const claudeExists = await fs.pathExists(claudeDir);
37
+ const opencodeExists = await fs.pathExists(opencodeDir);
38
+ const shouldCopyClaude = useClaude && (force || !claudeExists);
39
+ const shouldCopyOpencode = useOpencode && (force || !opencodeExists);
40
+
41
+ if (!force) {
42
+ if (useClaude && claudeExists) {
43
+ console.log(chalk.yellow('⚠️ 检测到已存在 .claude/ 目录'));
44
+ console.log(chalk.yellow(' 使用 --force 选项强制覆盖'));
45
+ }
46
+ if (useOpencode && opencodeExists) {
47
+ console.log(chalk.yellow('⚠️ 检测到已存在 .opencode/ 目录'));
48
+ console.log(chalk.yellow(' 使用 --force 选项强制覆盖'));
49
+ }
50
+ if (!shouldCopyClaude && !shouldCopyOpencode) {
51
+ return;
52
+ }
32
53
  }
33
54
 
34
55
  // 2. 复制 .claude/ 目录
35
- console.log(chalk.gray(' 复制 .claude/ 配置...'));
36
- await fs.copy(
37
- path.join(TEMPLATE_DIR, '.claude'),
38
- claudeDir,
39
- { overwrite: force }
40
- );
41
-
42
- // 3. 复制文档目录
56
+ if (shouldCopyClaude) {
57
+ console.log(chalk.gray(' 复制 .claude/ 配置...'));
58
+ await fs.copy(
59
+ path.join(TEMPLATE_DIR, '.claude'),
60
+ claudeDir,
61
+ { overwrite: force }
62
+ );
63
+ }
64
+
65
+ // 3. 复制 .opencode/ 目录
66
+ if (shouldCopyOpencode) {
67
+ console.log(chalk.gray(' 复制 .opencode/ 配置...'));
68
+ await fs.copy(
69
+ path.join(TEMPLATE_DIR, '.opencode'),
70
+ opencodeDir,
71
+ { overwrite: force }
72
+ );
73
+ }
74
+
75
+ // 4. 复制文档目录
43
76
  if (mode === 'full') {
44
77
  console.log(chalk.gray(' 复制 docs/ 文档模板...'));
45
78
  await fs.copy(
@@ -66,7 +99,7 @@ async function init(options = {}) {
66
99
  }
67
100
  }
68
101
 
69
- // 4. 更新 .gitignore
102
+ // 5. 更新 .gitignore
70
103
  const gitignorePath = path.join(cwd, '.gitignore');
71
104
  const jvibeIgnore = '\n# JVibe\n.claude/settings.local.json\n';
72
105
 
@@ -84,26 +117,50 @@ async function init(options = {}) {
84
117
  );
85
118
  }
86
119
 
87
- // 5. 添加版本信息到 settings.json
88
- const settingsPath = path.join(claudeDir, 'settings.json');
89
- if (await fs.pathExists(settingsPath)) {
90
- const settings = await fs.readJson(settingsPath);
91
- settings.jvibe = {
120
+ // 6. 添加版本信息到 settings.json
121
+ if (useClaude) {
122
+ const settingsPath = path.join(claudeDir, 'settings.json');
123
+ if (await fs.pathExists(settingsPath)) {
124
+ const settings = await fs.readJson(settingsPath);
125
+ settings.jvibe = {
126
+ version: require('../package.json').version,
127
+ installedAt: new Date().toISOString(),
128
+ mode: mode,
129
+ adapter: normalizedAdapter
130
+ };
131
+ await fs.writeJson(settingsPath, settings, { spaces: 2 });
132
+ }
133
+ }
134
+
135
+ if (useOpencode) {
136
+ const opencodeMetaPath = path.join(opencodeDir, 'jvibe.json');
137
+ const opencodeMeta = {
92
138
  version: require('../package.json').version,
93
139
  installedAt: new Date().toISOString(),
94
- mode: mode
140
+ mode: mode,
141
+ adapter: normalizedAdapter
95
142
  };
96
- await fs.writeJson(settingsPath, settings, { spaces: 2 });
143
+ await fs.writeJson(opencodeMetaPath, opencodeMeta, { spaces: 2 });
97
144
  }
98
145
 
99
- // 6. 输出成功信息
146
+ // 7. 输出成功信息
100
147
  console.log(chalk.green('\n✅ JVibe 初始化完成!\n'));
101
148
 
102
149
  console.log(chalk.white('已创建:'));
103
- console.log(chalk.gray(' - .claude/agents/ (5 个 Sub-Agents)'));
104
- console.log(chalk.gray(' - .claude/commands/ (3JVibe Skills)'));
105
- console.log(chalk.gray(' - .claude/hooks/ (3 个自动化 Hooks)'));
106
- console.log(chalk.gray(' - .claude/settings.json'));
150
+ if (shouldCopyClaude) {
151
+ console.log(chalk.gray(' - .claude/agents/ (5Sub-Agents)'));
152
+ console.log(chalk.gray(' - .claude/commands/ (5 JVibe Skills)'));
153
+ console.log(chalk.gray(' - .claude/hooks/ (4 个自动化 Hooks)'));
154
+ console.log(chalk.gray(' - .claude/settings.json'));
155
+ }
156
+ if (shouldCopyOpencode) {
157
+ console.log(chalk.gray(' - .opencode/agent/ (5 个 Sub-Agents)'));
158
+ console.log(chalk.gray(' - .opencode/command/ (5 个 JVibe Commands)'));
159
+ console.log(chalk.gray(' - .opencode/opencode.jsonc'));
160
+ console.log(chalk.gray(' - .opencode/permissions.yaml'));
161
+ console.log(chalk.gray(' - .opencode/error-handling.md'));
162
+ console.log(chalk.gray(' - .opencode/instructions.md'));
163
+ }
107
164
 
108
165
  if (mode === 'full') {
109
166
  console.log(chalk.gray(' - docs/core/ (4 个核心文档)'));
@@ -112,10 +169,26 @@ async function init(options = {}) {
112
169
  console.log(chalk.gray(' - docs/core/ (4 个核心文档)'));
113
170
  }
114
171
 
172
+ const nextSteps = [];
173
+ if (useClaude) {
174
+ nextSteps.push('在 Claude Code 中运行 /JVibe:init 创建项目文档');
175
+ }
176
+ if (useOpencode) {
177
+ nextSteps.push('在 OpenCode 中运行 /jvibe-init 创建项目文档');
178
+ }
179
+ const statusCommand = useClaude && useOpencode
180
+ ? '/JVibe:status 或 /jvibe-status'
181
+ : useClaude
182
+ ? '/JVibe:status'
183
+ : '/jvibe-status';
184
+ nextSteps.push(`运行 ${statusCommand} 查看项目状态`);
185
+ nextSteps.push('开始使用自然语言描述你的需求!');
186
+
115
187
  console.log(chalk.yellow('\n📝 下一步:'));
116
- console.log(chalk.white(' 1. Claude Code 中运行 /JVibe:init 创建项目文档'));
117
- console.log(chalk.white(' 2. 运行 /JVibe:status 查看项目状态'));
118
- console.log(chalk.white(' 3. 开始使用自然语言描述你的需求!\n'));
188
+ nextSteps.forEach((step, index) => {
189
+ console.log(chalk.white(` ${index + 1}. ${step}`));
190
+ });
191
+ console.log('');
119
192
 
120
193
  } catch (error) {
121
194
  console.error(chalk.red('\n❌ 初始化失败:'), error.message);