@zhuan-ai/zhuanspec 2.9.5 → 2.11.2

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 (36) hide show
  1. package/dist/cli/hooks.js +8 -8
  2. package/dist/cli/index.js +1 -0
  3. package/dist/commands/design.d.ts +12 -2
  4. package/dist/commands/design.js +83 -10
  5. package/dist/commands/progress.js +11 -0
  6. package/dist/commands/review.d.ts +1 -25
  7. package/dist/commands/review.js +51 -408
  8. package/dist/core/completions/command-registry.js +4 -0
  9. package/dist/core/hooks/collect-knowledge.d.ts +13 -5
  10. package/dist/core/hooks/collect-knowledge.js +56 -164
  11. package/dist/core/hooks/deviation-check.js +159 -55
  12. package/dist/core/hooks/init.js +46 -0
  13. package/dist/core/hooks/post-apply.js +2 -0
  14. package/dist/core/hooks/pre-archive.js +13 -14
  15. package/dist/core/hooks/pre-review.d.ts +27 -0
  16. package/dist/core/hooks/pre-review.js +169 -0
  17. package/dist/core/hooks/record-progress.d.ts +25 -0
  18. package/dist/core/hooks/record-progress.js +88 -1
  19. package/dist/core/hooks/review-hooks.js +176 -7
  20. package/dist/core/hooks/review-orchestrator.js +58 -11
  21. package/dist/core/hooks/tdd-phase-hook.d.ts +59 -0
  22. package/dist/core/hooks/tdd-phase-hook.js +313 -0
  23. package/dist/core/init.d.ts +1 -0
  24. package/dist/core/init.js +45 -2
  25. package/dist/core/templates/agents-template.d.ts +1 -1
  26. package/dist/core/templates/agents-template.js +139 -23
  27. package/dist/core/templates/skill-templates.js +21 -1
  28. package/dist/core/templates/slash-command-templates.js +163 -63
  29. package/dist/core/templates/tasks-template.js +89 -0
  30. package/dist/core/update.d.ts +3 -0
  31. package/dist/core/update.js +85 -2
  32. package/dist/core/validation/strict-rules.d.ts +47 -0
  33. package/dist/core/validation/strict-rules.js +589 -42
  34. package/dist/utils/git-repo-detector.js +1 -1
  35. package/dist/utils/phase-utils.js +14 -1
  36. package/package.json +22 -20
@@ -64,8 +64,80 @@ Answer: <!-- user answer -->
64
64
  2. 格式:@test-case:TC-001 或 @test-case:TC-001,TC-002(多个用逗号分隔)
65
65
  3. 确保引用的 TC-XXX 在 test-cases.md 中存在
66
66
  4. 任务应覆盖对应测试 case 的所有验收点
67
+
68
+ @ref 标注指南:
69
+ 1. 指向技术方案文档的具体章节(路径为实际定位到的技术方案文件相对路径)
70
+ 2. 格式:@ref:<tech-spec路径>#章节编号-章节名称
71
+ 3. 示例:@ref:techDesign/tech-spec.md#3.1-新建客户接口 或 @ref:/path/to/external-tech-spec.md#3.1-新建客户接口
72
+ 4. 确保引用的章节在技术方案文档中存在
67
73
  -->
68
74
 
75
+ ## 任务格式说明
76
+
77
+ 每个任务按类型使用对应的固定模板,必须填满所有字段:
78
+
79
+ ### 类型 A:新增文件
80
+
81
+ - [ ] N.M [描述] @skill:xxx @ref:xxx
82
+ - **文件**: \`完整相对路径\`(新增)
83
+ - **内容**:
84
+ - 类定义: [类名、继承/实现关系]
85
+ - 依赖注入: [注入的 Bean/Service 列表]
86
+ - 核心方法: [方法签名 → 返回类型](列出全部 public 方法)
87
+ - 关键逻辑: [核心业务流程,如:校验 → 加锁 → 写库 → 同步ES]
88
+ - 字段/配置: [若为 Entity/DTO/配置类,列出全部字段及类型]
89
+ - **验证**: [具体的输入输出断言,含正常和异常场景]
90
+
91
+ 示例:
92
+ - [ ] 5.1 创建 CustomerDomainService @skill:java-scf-rpc-usage-skill
93
+ @ref:techDesign/tech-spec.md#3.1-新建客户
94
+ - **文件**: \`domain_hub_server/src/main/java/com/zz/domain/hub/domain/CustomerDomainService.java\`(新增)
95
+ - **内容**:
96
+ - 类定义: \`@Service public class CustomerDomainService\`
97
+ - 依赖注入: CustomerMapper, CustomerEmployeeMapper, EsSyncService, RedissonClient
98
+ - 核心方法: createCustomer(CreateCustomerRequest, Long tenantId) → Long
99
+ - 关键逻辑: customerName 必填校验 → selectByPhoneAndTenant 手机号唯一检查 → 分布式锁 dh:customer:merge:{phone} → insert t_customer → 异步 ES 同步
100
+ - 异常处理: 手机号重复抛 BizException(ErrorCode.PHONE_DUPLICATE)
101
+ - **验证**: 手机号重复返回 PHONE_DUPLICATE 错误码;正常创建返回 customerId > 0;无锁时并发创建只成功一个
102
+
103
+ ### 类型 B:修改文件
104
+
105
+ - [ ] N.M [描述] @skill:xxx @ref:xxx
106
+ - **文件**: \`完整相对路径\`(总行数)
107
+ - **修改位置**: L行号范围 \`函数名/类名\` 之后/之内
108
+ - **内容**: [具体改什么:新增代码逻辑/修改现有逻辑/删除代码,描述修改方式]
109
+ - **验证**: [修改后的预期行为,含回归验证]
110
+
111
+ 示例:
112
+ - [ ] 1.1 在 review 命令中增加任务完成度硬门禁 @skill:none
113
+ @ref:techDesign/tech-spec.md#7.1-任务完成度检查
114
+ - **文件**: \`src/commands/review.ts\`(138行)
115
+ - **修改位置**: L32-38 \`runReview()\` 函数内,现有校验逻辑之后
116
+ - **内容**: 新增任务完成度检查 — 读取 tasks.md 匹配 \`- [ ]\` 统计未完成任务数,>0 时输出未完成列表并阻断 review 流程,支持 \`--force\` 参数跳过检查
117
+ - **验证**: 有未完成任务时阻断并输出列表;--force 跳过;全部完成时正常通过
118
+
119
+ ## 任务粒度原则
120
+ - 每个任务对应一个类或一个接口方法的实现
121
+ - 单个任务 AI 执行时间不超过 30 分钟
122
+ - 超过则需拆分为多个子任务
123
+ - **禁止模糊描述**:不允许"创建XXX类"不带文件路径、"实现YYY方法"不带方法签名和关键逻辑
124
+
125
+ ## Coverage Checklist
126
+
127
+ <!-- 当存在技术方案(techDesign/tech-spec.md/ 通过--tech-spec创建的提案)时,必须填写此表 -->
128
+ <!-- 列出技术方案中所有维度与任务的映射关系,确保覆盖率 >= 95% -->
129
+
130
+ | 维度 | 技术方案数量 | 提案任务覆盖 | 覆盖率 |
131
+ |------|-------------|-------------|-------|
132
+ | DB 表 | <!-- N张 --> | <!-- T编号范围 --> | <!-- 100% --> |
133
+ | ES 索引 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
134
+ | Entity 类 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
135
+ | Mapper | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
136
+ | SCF接口方法 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
137
+ | DTO 类 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
138
+ | 配置项 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
139
+ | 前端组件 | <!-- N个 --> | <!-- T编号范围 --> | <!-- 100% --> |
140
+
69
141
  ### Wave 1
70
142
 
71
143
  <!-- Wave 1: Tasks with no dependencies -->
@@ -97,6 +169,17 @@ Wave assignment logic:
97
169
  - Wave 2: @depends:1.x → depends on Wave 1
98
170
  - Wave 3: @depends:2.x → depends on Wave 2
99
171
 
172
+ ## File Conflict Analysis
173
+
174
+ <!-- 当多个任务修改同一文件时,在此声明冲突并指定隔离策略 -->
175
+ <!-- 隔离策略:parallel(不同区域可并行)/ serial(同区域必须串行)-->
176
+ <!-- 此表帮助编排 Agent 做更精确的并行化决策,补充自动检测无法覆盖的"同文件不同区域可并行"场景 -->
177
+ <!-- 无文件冲突时章节可省略,或将表格行填写 N/A -->
178
+
179
+ | 文件 | 涉及任务 | 各任务修改区域 | 隔离策略 |
180
+ |------|---------|-------------|---------|
181
+ | <!-- 相对路径 --> | <!-- T1.x, T2.y --> | <!-- T1.x: 函数名/行范围; T2.y: 函数名/行范围 --> | <!-- parallel / serial --> |
182
+
100
183
  ## Test Case Coverage
101
184
 
102
185
  <!-- ⚠️ CHECKPOINT [TEST-CASE-COVERAGE]: Each task should reference related test cases (recommended) -->
@@ -166,6 +249,12 @@ Status: COMPLETED
166
249
  Wave assignment logic:
167
250
  - Wave 1: No dependencies
168
251
  - Wave 2: Depends on Wave 1
252
+
253
+ ## File Conflict Analysis
254
+
255
+ | 文件 | 涉及任务 | 各任务修改区域 | 隔离策略 |
256
+ |------|---------|-------------|---------|
257
+ | <!-- 相对路径 --> | <!-- T1.x, T2.y --> | <!-- T1.x: 函数名/行范围; T2.y: 函数名/行范围 --> | <!-- parallel / serial --> |
169
258
  `;
170
259
  }
171
260
  export default {
@@ -1,4 +1,7 @@
1
1
  export declare class UpdateCommand {
2
2
  execute(projectPath: string): Promise<void>;
3
+ private syncDotClaudeFromRemote;
4
+ private cloneRemoteToTemp;
5
+ private syncDirectory;
3
6
  }
4
7
  //# sourceMappingURL=update.d.ts.map
@@ -1,9 +1,14 @@
1
1
  import path from 'path';
2
+ import os from 'os';
3
+ import { execSync } from 'child_process';
4
+ import { mkdtempSync, rmSync, readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from 'fs';
2
5
  import { FileSystemUtils } from '../utils/file-system.js';
3
6
  import { ZHUANSPEC_DIR_NAME } from './config.js';
4
7
  import { ToolRegistry } from './configurators/registry.js';
5
8
  import { SlashCommandRegistry } from './configurators/slash/registry.js';
6
9
  import { agentsTemplate } from './templates/agents-template.js';
10
+ const ARCH_REPO_URL = 'http://gitlab.zhuanspirit.com/zz-kf/spec_repo.git';
11
+ const ARCH_REPO_BRANCH = 'spec_repo-feature-6612-2';
7
12
  export class UpdateCommand {
8
13
  async execute(projectPath) {
9
14
  const resolvedProjectPath = path.resolve(projectPath);
@@ -59,6 +64,8 @@ export class UpdateCommand {
59
64
  console.error(`更新 ${slashConfigurator.toolId} 的斜杠命令失败:${error instanceof Error ? error.message : String(error)}`);
60
65
  }
61
66
  }
67
+ // 4. Sync .claude/ from remote (rules, skills, agents)
68
+ const claudeSyncResult = this.syncDotClaudeFromRemote(resolvedProjectPath);
62
69
  const summaryParts = [];
63
70
  const instructionFiles = ['zhuanspec/AGENTS.md'];
64
71
  if (updatedFiles.includes('AGENTS.md')) {
@@ -70,10 +77,24 @@ export class UpdateCommand {
70
77
  summaryParts.push(`已更新 AI 工具文件:${aiToolFiles.join(', ')}`);
71
78
  }
72
79
  if (updatedSlashFiles.length > 0) {
73
- // Normalize to forward slashes for cross-platform log consistency
74
80
  const normalized = updatedSlashFiles.map((p) => FileSystemUtils.toPosixPath(p));
75
81
  summaryParts.push(`已更新斜杠命令:${normalized.join(', ')}`);
76
82
  }
83
+ if (claudeSyncResult.synced) {
84
+ const { added, overwritten, skipped } = claudeSyncResult;
85
+ const total = added + overwritten + skipped;
86
+ const parts = [];
87
+ if (added > 0)
88
+ parts.push(`新增 ${added} 个`);
89
+ if (overwritten > 0)
90
+ parts.push(`更新 ${overwritten} 个`);
91
+ if (skipped > 0)
92
+ parts.push(`跳过 ${skipped} 个`);
93
+ summaryParts.push(`已同步 .claude/ 远端内容(${total} 个文件,${parts.join(',')})`);
94
+ }
95
+ else if (claudeSyncResult.attempted) {
96
+ summaryParts.push('同步 .claude/ 远端内容失败(网络不可用或权限不足)');
97
+ }
77
98
  const failedItems = [
78
99
  ...failedFiles,
79
100
  ...failedSlashTools.map((toolId) => `斜杠命令刷新(${toolId})`),
@@ -82,7 +103,69 @@ export class UpdateCommand {
82
103
  summaryParts.push(`更新失败:${failedItems.join(', ')}`);
83
104
  }
84
105
  console.log(summaryParts.join(' | '));
85
- // No additional notes
106
+ }
107
+ syncDotClaudeFromRemote(projectPath) {
108
+ const tempDir = this.cloneRemoteToTemp();
109
+ if (!tempDir) {
110
+ return { attempted: true, synced: false, added: 0, overwritten: 0, skipped: 0 };
111
+ }
112
+ try {
113
+ const commonDotClaude = path.join(tempDir, 'specs', 'common', 'claude', '.claude');
114
+ if (!existsSync(commonDotClaude)) {
115
+ return { attempted: true, synced: false, added: 0, overwritten: 0, skipped: 0 };
116
+ }
117
+ const targetDotClaude = path.join(projectPath, '.claude');
118
+ const result = this.syncDirectory(commonDotClaude, targetDotClaude);
119
+ return { attempted: true, synced: true, ...result };
120
+ }
121
+ catch {
122
+ return { attempted: true, synced: false, added: 0, overwritten: 0, skipped: 0 };
123
+ }
124
+ finally {
125
+ rmSync(tempDir, { recursive: true, force: true });
126
+ }
127
+ }
128
+ cloneRemoteToTemp() {
129
+ const tempDir = mkdtempSync(path.join(os.tmpdir(), 'zhuanspec-update-'));
130
+ try {
131
+ execSync(`git clone --depth 1 --single-branch --branch ${ARCH_REPO_BRANCH} ${ARCH_REPO_URL} "${tempDir}"`, { encoding: 'utf-8', stdio: ['pipe', 'pipe', 'ignore'] });
132
+ return tempDir;
133
+ }
134
+ catch {
135
+ rmSync(tempDir, { recursive: true, force: true });
136
+ return null;
137
+ }
138
+ }
139
+ syncDirectory(srcDir, dstDir) {
140
+ const result = { added: 0, overwritten: 0, skipped: 0 };
141
+ const sync = (src, dst) => {
142
+ mkdirSync(dst, { recursive: true });
143
+ for (const entry of readdirSync(src, { withFileTypes: true })) {
144
+ const srcPath = path.join(src, entry.name);
145
+ const dstPath = path.join(dst, entry.name);
146
+ if (entry.isDirectory()) {
147
+ sync(srcPath, dstPath);
148
+ }
149
+ else {
150
+ const srcContent = readFileSync(srcPath);
151
+ if (existsSync(dstPath)) {
152
+ if (Buffer.compare(srcContent, readFileSync(dstPath)) === 0) {
153
+ result.skipped++;
154
+ }
155
+ else {
156
+ writeFileSync(dstPath, srcContent);
157
+ result.overwritten++;
158
+ }
159
+ }
160
+ else {
161
+ writeFileSync(dstPath, srcContent);
162
+ result.added++;
163
+ }
164
+ }
165
+ }
166
+ };
167
+ sync(srcDir, dstDir);
168
+ return result;
86
169
  }
87
170
  }
88
171
  //# sourceMappingURL=update.js.map
@@ -15,6 +15,15 @@ export interface StrictValidationResult {
15
15
  checks: StrictCheckResult[];
16
16
  allPassed: boolean;
17
17
  }
18
+ /**
19
+ * Rule 0: test-case-inquiry-logged
20
+ *
21
+ * Validates that tasks.md has a "## Test Case Source Log" section with non-empty content.
22
+ * This rule fires UNCONDITIONALLY — whether or not TDD mode is used.
23
+ * The AI must always ask the user about test cases and record the outcome before writing
24
+ * any spec/task content. Absence of this section means the inquiry was skipped.
25
+ */
26
+ export declare function checkTestCaseInquiryLogged(tasksContent: string): StrictCheckResult;
18
27
  /**
19
28
  * Rule 1: pre-clarification-completed
20
29
  *
@@ -85,6 +94,25 @@ export declare function checkProposalFormatValid(proposalContent: string): Stric
85
94
  * 3. If progress.json exists without phase field -> Apply phase (default)
86
95
  */
87
96
  export declare function detectPhase(changeDir: string): 'propose' | 'apply' | 'review';
97
+ /** Result of a single code search check */
98
+ interface CodeSearchResult {
99
+ dimension: string;
100
+ keyword: string;
101
+ fileType: string;
102
+ found: boolean;
103
+ matchedFile?: string;
104
+ }
105
+ /** Result for a single scenario's coverage check */
106
+ interface ScenarioResult {
107
+ scenario: string;
108
+ covered: boolean;
109
+ checks: CodeSearchResult[];
110
+ }
111
+ /** Overall consistency result across all scenarios */
112
+ export interface ConsistencyResult {
113
+ scenarios: ScenarioResult[];
114
+ coverageRate: number;
115
+ }
88
116
  /**
89
117
  * Rule: file-conflict-detection
90
118
  *
@@ -104,6 +132,24 @@ export declare function checkFileConflicts(parsedTasks: ParsedTask[]): StrictChe
104
132
  * - Column headers must match expected format
105
133
  */
106
134
  export declare function checkTestCaseCoverage(tasksContent: string, hasTestCase?: boolean): StrictCheckResult;
135
+ /**
136
+ * Rule: tech-spec-coverage
137
+ *
138
+ * When a tech-spec document exists, validates that the technical spec
139
+ * adequately covers the proposal's requirements.
140
+ * This is a basic framework - full logic will be implemented in Task 7's Skill.
141
+ *
142
+ * Tech spec document lookup order:
143
+ * 1. techDesign/tech-spec.md (standard path)
144
+ * 2. tech-spec.md in the proposal root directory
145
+ * 3. 用户提供本地技术方案路径
146
+ *
147
+ * Checks:
148
+ * - tech-spec.md file exists and is non-empty
149
+ * - tech-spec.md contains substantive content (not just placeholder)
150
+ */
151
+ export declare function checkTechSpecCoverage(changeDir: string): StrictCheckResult;
152
+ export declare function checkTaskIdFormat(tasksContent: string): StrictCheckResult;
107
153
  /**
108
154
  * Main entry: Run all strict validation rules
109
155
  */
@@ -114,4 +160,5 @@ export declare function runStrictValidation(tasksContent: string, parsedTasks: P
114
160
  changeDir?: string;
115
161
  hasTestCase?: boolean;
116
162
  }): Promise<StrictValidationResult>;
163
+ export {};
117
164
  //# sourceMappingURL=strict-rules.d.ts.map