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 +3 -1
- package/README.md +116 -15
- package/bin/jvibe.js +47 -2
- package/lib/migrate.js +51 -0
- package/lib/migrations/index.js +22 -0
- package/package.json +3 -2
- package/scripts/init.js +101 -28
- package/scripts/setup.js +746 -0
- package/scripts/status.js +60 -39
- package/scripts/uninstall.js +1 -0
- package/scripts/upgrade.js +66 -9
- package/scripts/validate.js +65 -10
- package/template/.claude/agents/developer.md +5 -3
- package/template/.claude/agents/planner.md +1 -1
- package/template/.claude/commands/JVibe:init.md +25 -0
- package/template/.claude/commands/JVibe:keepgo.md +5 -1
- package/template/.claude/permissions.yaml +19 -68
- package/template/.opencode/agent/developer.md +248 -0
- package/template/.opencode/agent/doc-sync.md +347 -0
- package/template/.opencode/agent/planner.md +452 -0
- package/template/.opencode/agent/reviewer.md +258 -0
- package/template/.opencode/agent/tester.md +228 -0
- package/template/.opencode/command/jvibe-init.md +295 -0
- package/template/.opencode/command/jvibe-keepgo.md +646 -0
- package/template/.opencode/command/jvibe-migrate.md +243 -0
- package/template/.opencode/command/jvibe-pr.md +159 -0
- package/template/.opencode/command/jvibe-status.md +162 -0
- package/template/.opencode/error-handling.md +60 -0
- package/template/.opencode/instructions.md +15 -0
- package/template/.opencode/opencode.jsonc +6 -0
- package/template/.opencode/permissions.yaml +85 -0
- package/template/README.md +30 -6
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
|
|
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
|
-
|
|
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
|
|
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
|
|
92
|
+
| 已有项目,想试用 JVibe | Claude/OpenCode 命令 | 自动扫描现有代码/文档 |
|
|
70
93
|
| 需要快速开始 | CLI 初始化 | 无需手动配置 |
|
|
71
|
-
| 需要定制化文档 | Claude
|
|
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/ #
|
|
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
|
|
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** | 需求分析、功能拆解 |
|
|
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
|
-
|
|
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 - 项目目录
|
package/lib/migrations/index.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
//
|
|
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
|
-
//
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
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(
|
|
143
|
+
await fs.writeJson(opencodeMetaPath, opencodeMeta, { spaces: 2 });
|
|
97
144
|
}
|
|
98
145
|
|
|
99
|
-
//
|
|
146
|
+
// 7. 输出成功信息
|
|
100
147
|
console.log(chalk.green('\n✅ JVibe 初始化完成!\n'));
|
|
101
148
|
|
|
102
149
|
console.log(chalk.white('已创建:'));
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
150
|
+
if (shouldCopyClaude) {
|
|
151
|
+
console.log(chalk.gray(' - .claude/agents/ (5 个 Sub-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
|
-
|
|
117
|
-
|
|
118
|
-
|
|
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);
|