jvibe 1.0.9 → 1.1.1

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
@@ -52,7 +52,7 @@ TODO 完成情况 → 功能状态
52
52
  ### 3. 开发流程
53
53
 
54
54
  ```
55
- 需求分析 → 功能拆解 → 技术设计 → 编码实现 → 代码审查 → 文档同步
55
+ 需求分析 → 功能拆解 → 技术设计 → 编码实现 → 测试验证 → 代码审查 → 文档同步
56
56
  ```
57
57
 
58
58
  ## Agent 职责
@@ -61,6 +61,7 @@ TODO 完成情况 → 功能状态
61
61
  |-------|------|----------|
62
62
  | **planner** | 需求分析、功能拆解、创建 F-XXX 条目 | Feature-List.md |
63
63
  | **developer** | 代码实现、逐项完成 TODO、勾选完成项 | Feature-List.md + 源代码 |
64
+ | **tester** | 测试执行、结果分析、回归验证 | 测试文件(仅在明确要求时) |
64
65
  | **reviewer** | 代码审查、规范检查、PR 描述生成 | 只读 |
65
66
  | **doc-sync** | 状态推导、统计更新、格式检查 | Project.md |
66
67
 
package/README.md CHANGED
@@ -11,7 +11,7 @@
11
11
 
12
12
  JVibe 是一个**文档驱动的 AI 辅助开发系统**,专为 Claude Code 设计。它提供:
13
13
 
14
- - 🤖 **4 个专业 Agent**:需求规划、代码开发、代码审查、文档同步
14
+ - 🤖 **5 个专业 Agent**:需求规划、代码开发、测试验证、代码审查、文档同步
15
15
  - 📝 **结构化文档体系**:CORE-DOCS(4个核心文档)+ PROJECT-DOCS(按需扩展)
16
16
  - 🔄 **自动化 Hooks**:自动加载上下文、同步功能状态、输出统计信息
17
17
  - 🎯 **单一事实来源**:功能状态只在功能清单中维护(SoT 原则)
@@ -91,7 +91,7 @@ jvibe init
91
91
  ```
92
92
  your-project/
93
93
  ├── .claude/ # Claude Code 配置
94
- │ ├── agents/ # 4 个 Sub-Agents
94
+ │ ├── agents/ # 5 个 Sub-Agents
95
95
  │ ├── commands/ # 3 个 JVibe Skills
96
96
  │ ├── hooks/ # 4 个自动化 Hooks
97
97
  │ └── settings.json
@@ -146,6 +146,7 @@ TODO 完成情况 → 功能状态
146
146
  |-------|------|------|
147
147
  | **planner** | 需求分析、功能拆解 | Sonnet |
148
148
  | **developer** | 代码实现、TODO 完成 | Sonnet |
149
+ | **tester** | 测试执行、结果分析 | Sonnet |
149
150
  | **reviewer** | 代码审查、规范检查 | Sonnet |
150
151
  | **doc-sync** | 状态推导、统计更新 | Haiku |
151
152
 
@@ -158,8 +159,10 @@ TODO 完成情况 → 功能状态
158
159
  | `jvibe init` | 初始化 JVibe 项目 |
159
160
  | `jvibe init --mode=minimal` | 最小化初始化(仅 Core 文档) |
160
161
  | `jvibe init --force` | 强制覆盖已存在的配置 |
161
- | `jvibe upgrade` | 升级到最新版本 |
162
+ | `jvibe upgrade` | 升级到最新版本(默认卸载重装) |
162
163
  | `jvibe upgrade --check` | 仅检查更新 |
164
+ | `jvibe upgrade --migrate` | 仅执行旧版迁移 |
165
+ | `jvibe uninstall` | 卸载项目内 JVibe 配置 |
163
166
  | `jvibe status` | 查看项目配置状态 |
164
167
  | `jvibe validate` | 验证项目配置 |
165
168
 
package/bin/jvibe.js CHANGED
@@ -34,7 +34,7 @@ program
34
34
  // upgrade 命令
35
35
  program
36
36
  .command('upgrade')
37
- .description('升级到最新版本(支持旧版本自动迁移)')
37
+ .description('升级到最新版本(默认卸载重装)')
38
38
  .option('--check', '仅检查更新,不执行升级', false)
39
39
  .option('--force', '强制升级,跳过确认', false)
40
40
  .option('--migrate', '仅执行迁移,不更新到最新版本', false)
@@ -53,6 +53,20 @@ program
53
53
  await upgrade({ ...options, migrate: true });
54
54
  });
55
55
 
56
+ // uninstall 命令
57
+ program
58
+ .command('uninstall')
59
+ .description('卸载项目内的 JVibe 配置与核心文档')
60
+ .option('--purge-project-docs', '同时移除 docs/project', false)
61
+ .option('--no-backup', '不创建备份', false)
62
+ .action(async (options) => {
63
+ const uninstall = require('../scripts/uninstall');
64
+ await uninstall({
65
+ purgeProjectDocs: options.purgeProjectDocs,
66
+ backup: options.backup
67
+ });
68
+ });
69
+
56
70
  // status 命令
57
71
  program
58
72
  .command('status')
package/lib/migrate.js CHANGED
@@ -494,10 +494,9 @@ async function checkContentMigration(projectDir, currentVersion) {
494
494
  description: '核心文档需要强制重构(以 template/docs/core 为准)'
495
495
  });
496
496
  }
497
- return result;
498
497
  }
499
498
 
500
- // 模板不可用时,退回版本配置
499
+ // 模板检测后继续合并版本迁移配置
501
500
  if (migrationsConfig && currentVersion) {
502
501
  const configResult = migrationsConfig.checkAIMigrationRequired(currentVersion);
503
502
  if (configResult.required) {
@@ -226,7 +226,13 @@ function generateMigrationPrompt(fromVersion) {
226
226
  if (result.changes.renamed.length > 0) {
227
227
  prompt += `## 重命名的字段\n\n`;
228
228
  for (const change of result.changes.renamed) {
229
- prompt += `- ${change.file}: ${change.oldName} ${change.newName}\n`;
229
+ const renameLabel = change.oldName && change.newName
230
+ ? `${change.oldName} → ${change.newName}`
231
+ : (change.field || change.description || '重命名');
232
+ prompt += `- ${change.file}: ${renameLabel}\n`;
233
+ if (change.description && renameLabel !== change.description) {
234
+ prompt += ` - ${change.description}\n`;
235
+ }
230
236
  }
231
237
  prompt += `\n`;
232
238
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jvibe",
3
- "version": "1.0.9",
3
+ "version": "1.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",
5
5
  "main": "bin/jvibe.js",
6
6
  "bin": {
package/scripts/init.js CHANGED
@@ -100,7 +100,7 @@ async function init(options = {}) {
100
100
  console.log(chalk.green('\n✅ JVibe 初始化完成!\n'));
101
101
 
102
102
  console.log(chalk.white('已创建:'));
103
- console.log(chalk.gray(' - .claude/agents/ (4 个 Sub-Agents)'));
103
+ console.log(chalk.gray(' - .claude/agents/ (5 个 Sub-Agents)'));
104
104
  console.log(chalk.gray(' - .claude/commands/ (3 个 JVibe Skills)'));
105
105
  console.log(chalk.gray(' - .claude/hooks/ (3 个自动化 Hooks)'));
106
106
  console.log(chalk.gray(' - .claude/settings.json'));
@@ -0,0 +1,83 @@
1
+ /**
2
+ * JVibe Uninstall Script
3
+ * 卸载项目内的 JVibe 配置与核心文档
4
+ */
5
+
6
+ const fs = require('fs-extra');
7
+ const path = require('path');
8
+ const chalk = require('chalk');
9
+
10
+ /**
11
+ * 卸载 JVibe 配置
12
+ * @param {Object} options - 卸载选项
13
+ * @param {boolean} options.purgeProjectDocs - 是否移除 docs/project
14
+ * @param {boolean} options.backup - 是否创建备份
15
+ * @param {boolean} options.showNextSteps - 是否输出下一步提示
16
+ */
17
+ async function uninstall(options = {}) {
18
+ const purgeProjectDocs = options.purgeProjectDocs || false;
19
+ const backupEnabled = options.backup !== false;
20
+ const showNextSteps = options.showNextSteps !== false;
21
+ const cwd = process.cwd();
22
+
23
+ console.log(chalk.blue('\n🧹 正在卸载 JVibe...\n'));
24
+
25
+ const targets = [
26
+ { relPath: '.claude', label: '.claude/' },
27
+ { relPath: 'docs/core', label: 'docs/core/' },
28
+ { relPath: 'docs/.jvibe', label: 'docs/.jvibe/' },
29
+ { relPath: '.jvibe-state.json', label: '.jvibe-state.json' },
30
+ { relPath: 'docs/.jvibe-state.json', label: 'docs/.jvibe-state.json' },
31
+ { relPath: 'docs/project', label: 'docs/project/', optional: true }
32
+ ];
33
+
34
+ const existingTargets = [];
35
+ for (const target of targets) {
36
+ if (target.optional && !purgeProjectDocs) {
37
+ continue;
38
+ }
39
+ const fullPath = path.join(cwd, target.relPath);
40
+ if (await fs.pathExists(fullPath)) {
41
+ existingTargets.push({ ...target, fullPath });
42
+ }
43
+ }
44
+
45
+ if (existingTargets.length === 0) {
46
+ console.log(chalk.yellow('⚠️ 未发现可卸载的 JVibe 配置'));
47
+ return;
48
+ }
49
+
50
+ let backupDir = null;
51
+ if (backupEnabled) {
52
+ backupDir = path.join(cwd, `.jvibe-uninstall-backup-${Date.now()}`);
53
+ await fs.ensureDir(backupDir);
54
+
55
+ for (const target of existingTargets) {
56
+ const destPath = path.join(backupDir, target.relPath);
57
+ await fs.ensureDir(path.dirname(destPath));
58
+ await fs.copy(target.fullPath, destPath);
59
+ }
60
+ }
61
+
62
+ for (const target of existingTargets) {
63
+ await fs.remove(target.fullPath);
64
+ }
65
+
66
+ console.log(chalk.green('\n✅ JVibe 卸载完成!'));
67
+ console.log(chalk.white('\n已移除:'));
68
+ for (const target of existingTargets) {
69
+ console.log(chalk.gray(` - ${target.label}`));
70
+ }
71
+
72
+ if (backupDir) {
73
+ console.log(chalk.gray(`\n备份位置:${path.basename(backupDir)}/`));
74
+ }
75
+
76
+ if (showNextSteps) {
77
+ console.log(chalk.yellow('\n📝 下一步:'));
78
+ console.log(chalk.white(' 1. 重新运行 jvibe init 或 /JVibe:init 初始化'));
79
+ console.log(chalk.white(' 2. 如需恢复,可从备份目录手动还原\n'));
80
+ }
81
+ }
82
+
83
+ module.exports = uninstall;
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * JVibe Upgrade Script
3
3
  * 升级项目的 JVibe 配置到最新版本
4
- * 支持旧版本自动检测和迁移
4
+ * 默认执行卸载重装,可选保留旧迁移策略
5
5
  */
6
6
 
7
7
  const fs = require('fs-extra');
@@ -13,6 +13,8 @@ const {
13
13
  executeMigration,
14
14
  printMigrationSummary
15
15
  } = require('../lib/migrate');
16
+ const init = require('./init');
17
+ const uninstall = require('./uninstall');
16
18
 
17
19
  const TEMPLATE_DIR = path.join(__dirname, '../template');
18
20
 
@@ -63,23 +65,47 @@ async function upgrade(options = {}) {
63
65
  if (checkOnly) {
64
66
  if (migrationPlan.needsMigration) {
65
67
  console.log(chalk.yellow('\n📦 检测到旧版本,需要迁移'));
66
- console.log(chalk.white(' 运行 jvibe upgrade 进行升级和迁移\n'));
68
+ console.log(chalk.white(' 运行 jvibe upgrade 执行卸载重装\n'));
67
69
  } else if (currentVersion === latestVersion) {
68
70
  console.log(chalk.green('\n✅ 已是最新版本!\n'));
69
71
  } else {
70
72
  console.log(chalk.yellow(`\n📦 有新版本可用: ${latestVersion}`));
71
- console.log(chalk.white(' 运行 jvibe upgrade 进行升级\n'));
73
+ console.log(chalk.white(' 运行 jvibe upgrade 执行卸载重装\n'));
72
74
  }
73
75
  return;
74
76
  }
75
77
 
76
- // 4. 检查是否需要任何操作
78
+ if (!migrateOnly) {
79
+ if (!force) {
80
+ console.log(chalk.yellow('\n⚠️ 将执行卸载重装(重置 .claude/ 与 docs/core/)'));
81
+ console.log(chalk.white(' 使用 --force 选项跳过此确认'));
82
+ }
83
+
84
+ let mode = 'full';
85
+ if (await fs.pathExists(settingsPath)) {
86
+ try {
87
+ const settings = await fs.readJson(settingsPath);
88
+ mode = settings.jvibe?.mode || mode;
89
+ } catch (e) {
90
+ // 读取失败则使用默认模式
91
+ }
92
+ }
93
+
94
+ await uninstall({ purgeProjectDocs: false, backup: true, showNextSteps: false });
95
+ await init({ mode, force: false });
96
+
97
+ console.log(chalk.green(`\n✅ 升级完成!`));
98
+ console.log(chalk.green(` 版本: ${currentVersion} → ${latestVersion}`));
99
+ console.log(chalk.gray('\n 已执行卸载重装(保留 docs/project/)\n'));
100
+ return;
101
+ }
102
+
103
+ // migrate-only 模式保留旧迁移逻辑
77
104
  if (!migrationPlan.needsMigration && currentVersion === latestVersion) {
78
- console.log(chalk.green('\n✅ 已是最新版本,无需升级!\n'));
105
+ console.log(chalk.green('\n✅ 已是最新版本,无需迁移!\n'));
79
106
  return;
80
107
  }
81
108
 
82
- // 5. 确认升级(如果没有 --force)
83
109
  if (!force && (migrationPlan.needsMigration || currentVersion !== latestVersion)) {
84
110
  console.log(chalk.yellow('\n⚠️ 即将执行以下操作:'));
85
111
 
@@ -92,12 +118,8 @@ async function upgrade(options = {}) {
92
118
 
93
119
  console.log(chalk.white('\n 使用 --force 选项跳过此确认'));
94
120
  console.log(chalk.white(' 或重新运行命令继续...\n'));
95
-
96
- // 在非交互模式下直接继续
97
- // 实际项目中可能需要 readline 来获取用户确认
98
121
  }
99
122
 
100
- // 6. 创建备份
101
123
  console.log(chalk.gray('\n 创建备份...'));
102
124
  const backupDir = path.join(cwd, '.jvibe-backup-' + Date.now());
103
125
  if (hasClaudeDir) {
@@ -108,78 +130,11 @@ async function upgrade(options = {}) {
108
130
  }
109
131
  console.log(chalk.gray(` 备份已保存到: ${path.basename(backupDir)}/`));
110
132
 
111
- // 7. 执行迁移(如果需要)
112
133
  if (migrationPlan.needsMigration) {
113
134
  await executeMigration(cwd, TEMPLATE_DIR, migrationPlan, latestVersion);
114
135
  }
115
136
 
116
- // 8. 执行常规升级(如果不是仅迁移模式)
117
- if (!migrateOnly && currentVersion !== latestVersion) {
118
- console.log(chalk.yellow(`\n📦 正在升级到 ${latestVersion}...\n`));
119
-
120
- // 更新 agents(如果迁移时没有更新)
121
- if (migrationPlan.details.agentsToUpdate.length === 0) {
122
- console.log(chalk.gray(' 更新 agents...'));
123
- await fs.copy(
124
- path.join(TEMPLATE_DIR, '.claude/agents'),
125
- path.join(cwd, '.claude/agents'),
126
- { overwrite: true }
127
- );
128
- }
129
-
130
- // 更新 commands(如果迁移时没有更新)
131
- if (migrationPlan.details.commandsToRename.length === 0) {
132
- console.log(chalk.gray(' 更新 commands...'));
133
- await fs.copy(
134
- path.join(TEMPLATE_DIR, '.claude/commands'),
135
- path.join(cwd, '.claude/commands'),
136
- { overwrite: true }
137
- );
138
- }
139
-
140
- // 更新 hooks(如果迁移时没有更新)
141
- if (migrationPlan.details.hooksToUpdate.length === 0) {
142
- console.log(chalk.gray(' 更新 hooks...'));
143
- await fs.copy(
144
- path.join(TEMPLATE_DIR, '.claude/hooks'),
145
- path.join(cwd, '.claude/hooks'),
146
- { overwrite: true }
147
- );
148
- }
149
-
150
- // 补充任务交接文件(如不存在)
151
- const handoffSrc = path.join(TEMPLATE_DIR, 'docs/.jvibe/tasks.yaml');
152
- const handoffDir = path.join(cwd, 'docs/.jvibe');
153
- const handoffDest = path.join(handoffDir, 'tasks.yaml');
154
- if (await fs.pathExists(handoffSrc) && !await fs.pathExists(handoffDest)) {
155
- await fs.ensureDir(handoffDir);
156
- await fs.copy(handoffSrc, handoffDest, { overwrite: false });
157
- }
158
-
159
- // 更新版本信息
160
- let settings = {};
161
- if (await fs.pathExists(settingsPath)) {
162
- try {
163
- settings = await fs.readJson(settingsPath);
164
- } catch (e) {
165
- // 读取失败则创建新配置
166
- }
167
- }
168
-
169
- settings.jvibe = {
170
- ...settings.jvibe,
171
- version: latestVersion,
172
- upgradedAt: new Date().toISOString()
173
- };
174
- await fs.writeJson(settingsPath, settings, { spaces: 2 });
175
- }
176
-
177
- // 9. 清理旧备份(保留最新的)
178
- // 可选:保留备份供用户手动清理
179
-
180
- // 10. 输出成功信息
181
- console.log(chalk.green(`\n✅ 升级完成!`));
182
-
137
+ console.log(chalk.green(`\n✅ 迁移完成!`));
183
138
  if (migrationPlan.needsMigration) {
184
139
  console.log(chalk.green(' 已完成旧版本迁移'));
185
140
  }
@@ -190,7 +145,6 @@ async function upgrade(options = {}) {
190
145
  console.log(chalk.gray(`\n 备份位置: ${path.basename(backupDir)}/`));
191
146
  console.log(chalk.gray(' 如需回滚,请手动恢复备份文件'));
192
147
 
193
- // 11. 检查是否需要 AI 内容迁移
194
148
  if (migrationPlan.needsAIMigration) {
195
149
  console.log(chalk.yellow('\n⚠️ 检测到文档内容需要智能迁移'));
196
150
  console.log(chalk.yellow(' 以下内容需要 AI 介入处理:'));
@@ -207,7 +161,7 @@ async function upgrade(options = {}) {
207
161
  console.error(chalk.red('\n❌ 升级失败:'), error.message);
208
162
 
209
163
  // 提示备份位置
210
- const backups = await findBackups(cwd);
164
+ const backups = await findBackups(cwd, ['.jvibe-backup-', '.jvibe-uninstall-backup-']);
211
165
  if (backups.length > 0) {
212
166
  console.log(chalk.yellow(` 最新备份: ${backups[0]}`));
213
167
  }
@@ -221,11 +175,11 @@ async function upgrade(options = {}) {
221
175
  * @param {string} dir - 项目目录
222
176
  * @returns {Promise<string[]>}
223
177
  */
224
- async function findBackups(dir) {
178
+ async function findBackups(dir, prefixes = ['.jvibe-backup-']) {
225
179
  try {
226
180
  const files = await fs.readdir(dir);
227
181
  return files
228
- .filter(f => f.startsWith('.jvibe-backup-'))
182
+ .filter(f => prefixes.some(prefix => f.startsWith(prefix)))
229
183
  .sort()
230
184
  .reverse();
231
185
  } catch (e) {
@@ -39,7 +39,7 @@ async function validate() {
39
39
  }
40
40
 
41
41
  // 检查 agents
42
- const requiredAgents = ['planner.md', 'developer.md', 'reviewer.md', 'doc-sync.md'];
42
+ const requiredAgents = ['planner.md', 'developer.md', 'reviewer.md', 'doc-sync.md', 'tester.md'];
43
43
  const agentsDir = path.join(claudeDir, 'agents');
44
44
  if (await fs.pathExists(agentsDir)) {
45
45
  for (const agent of requiredAgents) {
@@ -41,6 +41,7 @@ model: sonnet
41
41
  ## 约束(硬规则)
42
42
 
43
43
  ```yaml
44
+ source_of_truth: .claude/permissions.yaml
44
45
  constraints:
45
46
  read_allowlist:
46
47
  - docs/**
@@ -57,7 +58,11 @@ constraints:
57
58
  - .claude/**
58
59
  - .jvibe-state.json
59
60
  - package.json
60
- - lockfiles
61
+ - package-lock.json
62
+ - pnpm-lock.yaml
63
+ - yarn.lock
64
+ - Pipfile.lock
65
+ - poetry.lock
61
66
  - .gitignore
62
67
  ops:
63
68
  network: forbidden
@@ -103,8 +108,22 @@ constraints:
103
108
  ### TODO 执行规则
104
109
 
105
110
  1. **按顺序执行**:通常按 TODO 列表顺序
106
- 2. **完成即勾选**:完成一个就勾选一个,不要积攒
107
- 3. **遇阻即报告**:无法完成时说明原因
111
+ 2. **尽量一次完成**:单次调用应尝试完成该功能的所有 TODO
112
+ 3. **完成即勾选**:完成一个就勾选一个,不要积攒
113
+ 4. **遇阻即报告**:无法完成时说明原因并停止
114
+
115
+ ### 交接协议
116
+
117
+ ```yaml
118
+ handoff_rules:
119
+ - when: todo_includes_test
120
+ target: tester
121
+ action: run_tests
122
+ payload:
123
+ feature: F-XXX
124
+ files: []
125
+ scope: unit|integration|e2e
126
+ ```
108
127
 
109
128
  ### 勾选格式
110
129
 
@@ -136,6 +155,16 @@ update_requests: # 需要主 Agent 处理的更新
136
155
  action: check_status
137
156
  feature: F-XXX
138
157
  reason: "TODO 全部完成,需要更新功能状态"
158
+
159
+ handoff:
160
+ target: tester
161
+ action: run_tests
162
+ payload:
163
+ feature: F-XXX
164
+ files:
165
+ - src/modules/auth/register.ts
166
+ - src/modules/auth/register.test.ts
167
+ scope: unit
139
168
  ```
140
169
 
141
170
  ## 示例
@@ -194,6 +223,16 @@ update_requests:
194
223
  action: check_status
195
224
  feature: F-018
196
225
  reason: "所有 TODO 已完成,需要更新功能状态为 ✅"
226
+
227
+ handoff:
228
+ target: tester
229
+ action: run_tests
230
+ payload:
231
+ feature: F-018
232
+ files:
233
+ - src/modules/chat/thumbnail.service.ts
234
+ - src/modules/chat/file.test.ts
235
+ scope: integration
197
236
  ```
198
237
 
199
238
  ## 注意事项
@@ -14,7 +14,7 @@ model: haiku
14
14
  1. **状态推导**:根据 TODO 完成情况推导功能状态
15
15
  2. **统计更新**:更新项目文档中的统计表
16
16
  3. **格式检查**:检查文档格式一致性
17
- 4. **Git 提交**:仅在用户明确要求时执行
17
+ 4. **Git 提交**:仅在用户明确要求或 keepgo `auto_commit=true` 时执行
18
18
 
19
19
  ## 权限范围
20
20
 
@@ -38,6 +38,7 @@ model: haiku
38
38
  ## 约束(硬规则)
39
39
 
40
40
  ```yaml
41
+ source_of_truth: .claude/permissions.yaml
41
42
  constraints:
42
43
  read_allowlist:
43
44
  - docs/core/Feature-List.md
@@ -52,13 +53,17 @@ constraints:
52
53
  - .claude/**
53
54
  - .jvibe-state.json
54
55
  - package.json
55
- - lockfiles
56
+ - package-lock.json
57
+ - pnpm-lock.yaml
58
+ - yarn.lock
59
+ - Pipfile.lock
60
+ - poetry.lock
56
61
  - .gitignore
57
62
  ops:
58
63
  network: forbidden
59
64
  install: forbidden
60
65
  tests: forbidden
61
- git: only_if_user_requested
66
+ git: only_if_user_requested # keepgo auto_commit=true counts as explicit request
62
67
  ```
63
68
 
64
69
  ## 状态推导规则
@@ -35,6 +35,7 @@ model: sonnet
35
35
  ## 约束(硬规则)
36
36
 
37
37
  ```yaml
38
+ source_of_truth: .claude/permissions.yaml
38
39
  constraints:
39
40
  read_allowlist:
40
41
  - docs/**
@@ -46,7 +47,11 @@ constraints:
46
47
  - .claude/**
47
48
  - .jvibe-state.json
48
49
  - package.json
49
- - lockfiles
50
+ - package-lock.json
51
+ - pnpm-lock.yaml
52
+ - yarn.lock
53
+ - Pipfile.lock
54
+ - poetry.lock
50
55
  - .gitignore
51
56
  ops:
52
57
  network: forbidden
@@ -55,6 +60,13 @@ constraints:
55
60
  git: forbidden
56
61
  ```
57
62
 
63
+ ## 拒绝规划规则
64
+
65
+ 当需求明显超出项目范围或违反约束时:
66
+ - 不写入 `Feature-List.md`
67
+ - 说明拒绝原因与可行替代方案
68
+ - 如可拆解,建议用户缩小范围后再规划
69
+
58
70
  ## 工作流程
59
71
 
60
72
  ```
@@ -38,6 +38,7 @@ model: sonnet
38
38
  ## 约束(硬规则)
39
39
 
40
40
  ```yaml
41
+ source_of_truth: .claude/permissions.yaml
41
42
  constraints:
42
43
  read_allowlist:
43
44
  - docs/**
@@ -52,7 +53,7 @@ constraints:
52
53
  network: forbidden
53
54
  install: forbidden
54
55
  tests: forbidden
55
- git: read_only # allow git diff/status only
56
+ git: read_only # allowed: git diff, git status, git log --oneline, git show
56
57
  ```
57
58
 
58
59
  ## 工作流程
@@ -125,20 +126,18 @@ result:
125
126
  summary: "审查通过/需要修改"
126
127
  files_reviewed: 5
127
128
 
128
- issues: # 发现的问题
129
+ issues: # 发现的问题(spec 可选,优先在 hit_specs 汇总)
129
130
  - severity: error # error/warning/info
130
131
  file: src/auth/login.ts
131
132
  line: 42
132
133
  message: "SQL 拼接存在注入风险"
133
134
  suggestion: "使用参数化查询"
134
- spec: SEC-001
135
135
 
136
136
  - severity: warning
137
137
  file: src/user/profile.ts
138
138
  line: 78
139
139
  message: "函数复杂度过高"
140
140
  suggestion: "拆分为多个小函数"
141
- spec: CS-003
142
141
 
143
142
  hit_specs: # 命中的规范条目
144
143
  - id: CS-001