claude-code-workflow 6.3.26 → 6.3.28
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/.claude/CLAUDE.md +7 -1
- package/.claude/agents/action-planning-agent.md +1 -0
- package/.claude/agents/cli-discuss-agent.md +391 -0
- package/.claude/agents/cli-execution-agent.md +2 -0
- package/.claude/agents/cli-explore-agent.md +2 -1
- package/.claude/agents/cli-lite-planning-agent.md +1 -0
- package/.claude/agents/cli-planning-agent.md +1 -0
- package/.claude/agents/code-developer.md +1 -0
- package/.claude/agents/conceptual-planning-agent.md +2 -0
- package/.claude/agents/context-search-agent.md +1 -0
- package/.claude/agents/debug-explore-agent.md +2 -0
- package/.claude/agents/doc-generator.md +1 -0
- package/.claude/agents/issue-plan-agent.md +2 -1
- package/.claude/agents/issue-queue-agent.md +2 -1
- package/.claude/agents/memory-bridge.md +2 -0
- package/.claude/agents/test-context-search-agent.md +2 -0
- package/.claude/agents/test-fix-agent.md +1 -0
- package/.claude/agents/ui-design-agent.md +2 -0
- package/.claude/agents/universal-executor.md +1 -0
- package/.claude/commands/issue/execute.md +141 -163
- package/.claude/commands/workflow/lite-lite-lite.md +798 -0
- package/.claude/commands/workflow/multi-cli-plan.md +510 -0
- package/.claude/skills/ccw/SKILL.md +262 -372
- package/.claude/skills/ccw/command.json +547 -0
- package/.claude/skills/ccw-help/SKILL.md +46 -107
- package/.claude/skills/ccw-help/command.json +511 -0
- package/.claude/skills/skill-tuning/SKILL.md +303 -0
- package/.claude/skills/skill-tuning/phases/actions/action-abort.md +164 -0
- package/.claude/skills/skill-tuning/phases/actions/action-analyze-requirements.md +406 -0
- package/.claude/skills/skill-tuning/phases/actions/action-apply-fix.md +206 -0
- package/.claude/skills/skill-tuning/phases/actions/action-complete.md +195 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-agent.md +317 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-context.md +243 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-dataflow.md +318 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-docs.md +299 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-memory.md +269 -0
- package/.claude/skills/skill-tuning/phases/actions/action-diagnose-token-consumption.md +200 -0
- package/.claude/skills/skill-tuning/phases/actions/action-gemini-analysis.md +322 -0
- package/.claude/skills/skill-tuning/phases/actions/action-generate-report.md +228 -0
- package/.claude/skills/skill-tuning/phases/actions/action-init.md +149 -0
- package/.claude/skills/skill-tuning/phases/actions/action-propose-fixes.md +317 -0
- package/.claude/skills/skill-tuning/phases/actions/action-verify.md +222 -0
- package/.claude/skills/skill-tuning/phases/orchestrator.md +377 -0
- package/.claude/skills/skill-tuning/phases/state-schema.md +378 -0
- package/.claude/skills/skill-tuning/specs/category-mappings.json +284 -0
- package/.claude/skills/skill-tuning/specs/dimension-mapping.md +212 -0
- package/.claude/skills/skill-tuning/specs/problem-taxonomy.md +318 -0
- package/.claude/skills/skill-tuning/specs/quality-gates.md +263 -0
- package/.claude/skills/skill-tuning/specs/skill-authoring-principles.md +189 -0
- package/.claude/skills/skill-tuning/specs/tuning-strategies.md +1537 -0
- package/.claude/skills/skill-tuning/templates/diagnosis-report.md +153 -0
- package/.claude/skills/skill-tuning/templates/fix-proposal.md +204 -0
- package/.claude/workflows/cli-templates/schemas/multi-cli-discussion-schema.json +421 -0
- package/.claude/workflows/cli-tools-usage.md +0 -41
- package/ccw/dist/core/auth/csrf-middleware.d.ts.map +1 -1
- package/ccw/dist/core/auth/csrf-middleware.js +3 -1
- package/ccw/dist/core/auth/csrf-middleware.js.map +1 -1
- package/ccw/dist/core/data-aggregator.d.ts +2 -0
- package/ccw/dist/core/data-aggregator.d.ts.map +1 -1
- package/ccw/dist/core/data-aggregator.js +5 -2
- package/ccw/dist/core/data-aggregator.js.map +1 -1
- package/ccw/dist/core/lite-scanner.d.ts +2 -1
- package/ccw/dist/core/lite-scanner.d.ts.map +1 -1
- package/ccw/dist/core/lite-scanner.js +295 -6
- package/ccw/dist/core/lite-scanner.js.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.d.ts.map +1 -1
- package/ccw/dist/core/routes/codexlens/config-handlers.js +5 -5
- package/ccw/dist/core/routes/codexlens/config-handlers.js.map +1 -1
- package/ccw/dist/core/routes/session-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/session-routes.js +166 -48
- package/ccw/dist/core/routes/session-routes.js.map +1 -1
- package/ccw/dist/core/routes/system-routes.d.ts.map +1 -1
- package/ccw/dist/core/routes/system-routes.js +87 -0
- package/ccw/dist/core/routes/system-routes.js.map +1 -1
- package/ccw/dist/core/server.js +2 -2
- package/ccw/dist/core/server.js.map +1 -1
- package/ccw/scripts/IMPLEMENTATION-SUMMARY.md +226 -0
- package/ccw/scripts/QUICK-REFERENCE.md +135 -0
- package/ccw/scripts/README-memory-embedder.md +157 -0
- package/ccw/scripts/__pycache__/memory_embedder.cpython-313.pyc +0 -0
- package/ccw/scripts/__pycache__/test_memory_embedder.cpython-313-pytest-8.4.2.pyc +0 -0
- package/ccw/scripts/memory-embedder-example.ts +184 -0
- package/ccw/scripts/memory_embedder.py +428 -0
- package/ccw/scripts/test_memory_embedder.py +245 -0
- package/ccw/src/core/auth/csrf-middleware.ts +3 -1
- package/ccw/src/core/data-aggregator.ts +7 -2
- package/ccw/src/core/lite-scanner.ts +440 -6
- package/ccw/src/core/routes/codexlens/config-handlers.ts +12 -9
- package/ccw/src/core/routes/session-routes.ts +201 -48
- package/ccw/src/core/routes/system-routes.ts +102 -0
- package/ccw/src/core/server.ts +2 -2
- package/ccw/src/templates/dashboard-css/01-base.css +8 -0
- package/ccw/src/templates/dashboard-css/02-session.css +81 -0
- package/ccw/src/templates/dashboard-css/04-lite-tasks.css +2442 -0
- package/ccw/src/templates/dashboard-css/21-cli-toolmgmt.css +157 -0
- package/ccw/src/templates/dashboard-css/32-issue-manager.css +23 -0
- package/ccw/src/templates/dashboard-js/components/cli-stream-viewer.js +38 -4
- package/ccw/src/templates/dashboard-js/components/hook-manager.js +38 -13
- package/ccw/src/templates/dashboard-js/components/navigation.js +24 -4
- package/ccw/src/templates/dashboard-js/i18n.js +194 -6
- package/ccw/src/templates/dashboard-js/views/api-settings.js +32 -0
- package/ccw/src/templates/dashboard-js/views/claude-manager.js +44 -3
- package/ccw/src/templates/dashboard-js/views/cli-manager.js +303 -31
- package/ccw/src/templates/dashboard-js/views/history.js +44 -6
- package/ccw/src/templates/dashboard-js/views/home.js +1 -0
- package/ccw/src/templates/dashboard-js/views/issue-manager.js +54 -7
- package/ccw/src/templates/dashboard-js/views/lite-tasks.js +1817 -4
- package/ccw/src/templates/dashboard.html +5 -0
- package/package.json +2 -1
- package/.claude/skills/ccw/index/command-capabilities.json +0 -127
- package/.claude/skills/ccw/index/intent-rules.json +0 -136
- package/.claude/skills/ccw/index/workflow-chains.json +0 -451
- package/.claude/skills/ccw/phases/actions/bugfix.md +0 -218
- package/.claude/skills/ccw/phases/actions/coupled.md +0 -194
- package/.claude/skills/ccw/phases/actions/docs.md +0 -93
- package/.claude/skills/ccw/phases/actions/full.md +0 -154
- package/.claude/skills/ccw/phases/actions/issue.md +0 -201
- package/.claude/skills/ccw/phases/actions/rapid.md +0 -104
- package/.claude/skills/ccw/phases/actions/review-fix.md +0 -84
- package/.claude/skills/ccw/phases/actions/tdd.md +0 -66
- package/.claude/skills/ccw/phases/actions/ui.md +0 -79
- package/.claude/skills/ccw/phases/orchestrator.md +0 -435
- package/.claude/skills/ccw/specs/intent-classification.md +0 -336
- package/.claude/skills/ccw-help/index/all-agents.json +0 -82
- package/.claude/skills/ccw-help/index/all-commands.json +0 -882
- package/.claude/skills/ccw-help/index/by-category.json +0 -914
- package/.claude/skills/ccw-help/index/by-use-case.json +0 -896
- package/.claude/skills/ccw-help/index/command-relationships.json +0 -160
- package/.claude/skills/ccw-help/index/essential-commands.json +0 -112
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
# Action: Analyze Requirements
|
|
2
|
+
|
|
3
|
+
将用户问题描述拆解为多个分析维度,匹配 Spec,评估覆盖度,检测歧义。
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
- 将单一用户描述拆解为多个独立关注维度
|
|
8
|
+
- 为每个维度匹配 problem-taxonomy(检测)+ tuning-strategies(修复)
|
|
9
|
+
- 以"有修复策略"为标准判断是否满足需求
|
|
10
|
+
- 检测歧义并在必要时请求用户澄清
|
|
11
|
+
|
|
12
|
+
## Preconditions
|
|
13
|
+
|
|
14
|
+
- [ ] `state.status === 'running'`
|
|
15
|
+
- [ ] `state.target_skill !== null`
|
|
16
|
+
- [ ] `state.completed_actions.includes('action-init')`
|
|
17
|
+
- [ ] `!state.completed_actions.includes('action-analyze-requirements')`
|
|
18
|
+
|
|
19
|
+
## Execution
|
|
20
|
+
|
|
21
|
+
### Phase 1: 维度拆解 (Gemini CLI)
|
|
22
|
+
|
|
23
|
+
调用 Gemini 对用户描述进行语义分析,拆解为独立维度:
|
|
24
|
+
|
|
25
|
+
```javascript
|
|
26
|
+
async function analyzeDimensions(state, workDir) {
|
|
27
|
+
const prompt = `
|
|
28
|
+
PURPOSE: 分析用户问题描述,拆解为独立的关注维度
|
|
29
|
+
TASK:
|
|
30
|
+
• 识别用户描述中的多个关注点(每个关注点应该是独立的、可单独分析的)
|
|
31
|
+
• 为每个关注点提取关键词(中英文均可)
|
|
32
|
+
• 推断可能的问题类别:
|
|
33
|
+
- context_explosion: 上下文/Token 相关
|
|
34
|
+
- memory_loss: 遗忘/约束丢失相关
|
|
35
|
+
- dataflow_break: 状态/数据流相关
|
|
36
|
+
- agent_failure: Agent/子任务相关
|
|
37
|
+
- prompt_quality: 提示词/输出质量相关
|
|
38
|
+
- architecture: 架构/结构相关
|
|
39
|
+
- performance: 性能/效率相关
|
|
40
|
+
- error_handling: 错误/异常处理相关
|
|
41
|
+
- output_quality: 输出质量/验证相关
|
|
42
|
+
- user_experience: 交互/体验相关
|
|
43
|
+
• 评估推断置信度 (0-1)
|
|
44
|
+
|
|
45
|
+
INPUT:
|
|
46
|
+
User description: ${state.user_issue_description}
|
|
47
|
+
Target skill: ${state.target_skill.name}
|
|
48
|
+
Skill structure: ${JSON.stringify(state.target_skill.phases)}
|
|
49
|
+
|
|
50
|
+
MODE: analysis
|
|
51
|
+
CONTEXT: @specs/problem-taxonomy.md @specs/dimension-mapping.md
|
|
52
|
+
EXPECTED: JSON (不要包含 markdown 代码块标记)
|
|
53
|
+
{
|
|
54
|
+
"dimensions": [
|
|
55
|
+
{
|
|
56
|
+
"id": "DIM-001",
|
|
57
|
+
"description": "关注点的简短描述",
|
|
58
|
+
"keywords": ["关键词1", "关键词2"],
|
|
59
|
+
"inferred_category": "问题类别",
|
|
60
|
+
"confidence": 0.85,
|
|
61
|
+
"reasoning": "推断理由"
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
"analysis_notes": "整体分析说明"
|
|
65
|
+
}
|
|
66
|
+
RULES:
|
|
67
|
+
- 每个维度必须独立,不重叠
|
|
68
|
+
- 低于 0.5 置信度的推断应标注需要澄清
|
|
69
|
+
- 如果用户描述非常模糊,至少提取一个 "general" 维度
|
|
70
|
+
`;
|
|
71
|
+
|
|
72
|
+
const cliCommand = `ccw cli -p "${escapeForShell(prompt)}" --tool gemini --mode analysis --cd "${state.target_skill.path}"`;
|
|
73
|
+
|
|
74
|
+
console.log('Phase 1: 执行 Gemini 维度拆解分析...');
|
|
75
|
+
|
|
76
|
+
const result = Bash({
|
|
77
|
+
command: cliCommand,
|
|
78
|
+
run_in_background: true,
|
|
79
|
+
timeout: 300000
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
return result;
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
### Phase 2: Spec 匹配
|
|
87
|
+
|
|
88
|
+
基于 `specs/category-mappings.json` 配置为每个维度匹配检测模式和修复策略:
|
|
89
|
+
|
|
90
|
+
```javascript
|
|
91
|
+
// 加载集中式映射配置
|
|
92
|
+
const mappings = JSON.parse(Read('specs/category-mappings.json'));
|
|
93
|
+
|
|
94
|
+
function matchSpecs(dimensions) {
|
|
95
|
+
return dimensions.map(dim => {
|
|
96
|
+
// 匹配 taxonomy pattern
|
|
97
|
+
const taxonomyMatch = findTaxonomyMatch(dim.inferred_category);
|
|
98
|
+
|
|
99
|
+
// 匹配 strategy
|
|
100
|
+
const strategyMatch = findStrategyMatch(dim.inferred_category);
|
|
101
|
+
|
|
102
|
+
// 判断是否满足(核心标准:有修复策略)
|
|
103
|
+
const hasFix = strategyMatch !== null && strategyMatch.strategies.length > 0;
|
|
104
|
+
|
|
105
|
+
return {
|
|
106
|
+
dimension_id: dim.id,
|
|
107
|
+
taxonomy_match: taxonomyMatch,
|
|
108
|
+
strategy_match: strategyMatch,
|
|
109
|
+
has_fix: hasFix,
|
|
110
|
+
needs_gemini_analysis: taxonomyMatch === null || mappings.categories[dim.inferred_category]?.needs_gemini_analysis
|
|
111
|
+
};
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function findTaxonomyMatch(category) {
|
|
116
|
+
const config = mappings.categories[category];
|
|
117
|
+
if (!config || config.pattern_ids.length === 0) return null;
|
|
118
|
+
|
|
119
|
+
return {
|
|
120
|
+
category: category,
|
|
121
|
+
pattern_ids: config.pattern_ids,
|
|
122
|
+
severity_hint: config.severity_hint
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function findStrategyMatch(category) {
|
|
127
|
+
const config = mappings.categories[category];
|
|
128
|
+
if (!config) {
|
|
129
|
+
// Fallback to custom from config
|
|
130
|
+
return mappings.fallback;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return {
|
|
134
|
+
strategies: config.strategies,
|
|
135
|
+
risk_levels: config.risk_levels
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
### Phase 3: 覆盖度评估
|
|
141
|
+
|
|
142
|
+
评估所有维度的 Spec 覆盖情况:
|
|
143
|
+
|
|
144
|
+
```javascript
|
|
145
|
+
function evaluateCoverage(specMatches) {
|
|
146
|
+
const total = specMatches.length;
|
|
147
|
+
const withDetection = specMatches.filter(m => m.taxonomy_match !== null).length;
|
|
148
|
+
const withFix = specMatches.filter(m => m.has_fix).length;
|
|
149
|
+
|
|
150
|
+
const rate = total > 0 ? Math.round((withFix / total) * 100) : 0;
|
|
151
|
+
|
|
152
|
+
let status;
|
|
153
|
+
if (rate >= 80) {
|
|
154
|
+
status = 'satisfied';
|
|
155
|
+
} else if (rate >= 50) {
|
|
156
|
+
status = 'partial';
|
|
157
|
+
} else {
|
|
158
|
+
status = 'unsatisfied';
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
total_dimensions: total,
|
|
163
|
+
with_detection: withDetection,
|
|
164
|
+
with_fix_strategy: withFix,
|
|
165
|
+
coverage_rate: rate,
|
|
166
|
+
status: status
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### Phase 4: 歧义检测
|
|
172
|
+
|
|
173
|
+
识别需要用户澄清的歧义点:
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
function detectAmbiguities(dimensions, specMatches) {
|
|
177
|
+
const ambiguities = [];
|
|
178
|
+
|
|
179
|
+
for (const dim of dimensions) {
|
|
180
|
+
const match = specMatches.find(m => m.dimension_id === dim.id);
|
|
181
|
+
|
|
182
|
+
// 检测1: 低置信度 (< 0.5)
|
|
183
|
+
if (dim.confidence < 0.5) {
|
|
184
|
+
ambiguities.push({
|
|
185
|
+
dimension_id: dim.id,
|
|
186
|
+
type: 'vague_description',
|
|
187
|
+
description: `维度 "${dim.description}" 描述模糊,推断置信度低 (${dim.confidence})`,
|
|
188
|
+
possible_interpretations: suggestInterpretations(dim),
|
|
189
|
+
needs_clarification: true
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// 检测2: 无匹配类别
|
|
194
|
+
if (!match || (!match.taxonomy_match && !match.strategy_match)) {
|
|
195
|
+
ambiguities.push({
|
|
196
|
+
dimension_id: dim.id,
|
|
197
|
+
type: 'no_category_match',
|
|
198
|
+
description: `维度 "${dim.description}" 无法匹配到已知问题类别`,
|
|
199
|
+
possible_interpretations: ['custom'],
|
|
200
|
+
needs_clarification: true
|
|
201
|
+
});
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
// 检测3: 关键词冲突(可能属于多个类别)
|
|
205
|
+
if (dim.keywords.length > 3 && hasConflictingKeywords(dim.keywords)) {
|
|
206
|
+
ambiguities.push({
|
|
207
|
+
dimension_id: dim.id,
|
|
208
|
+
type: 'conflicting_keywords',
|
|
209
|
+
description: `维度 "${dim.description}" 的关键词可能指向多个不同问题`,
|
|
210
|
+
possible_interpretations: inferMultipleCategories(dim.keywords),
|
|
211
|
+
needs_clarification: true
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return ambiguities;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
function suggestInterpretations(dim) {
|
|
220
|
+
// 基于 mappings 配置推荐可能的解释
|
|
221
|
+
const categories = Object.keys(mappings.categories).filter(
|
|
222
|
+
cat => cat !== 'authoring_principles_violation' // 排除内部检测类别
|
|
223
|
+
);
|
|
224
|
+
return categories.slice(0, 4); // 返回最常见的 4 个作为选项
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function hasConflictingKeywords(keywords) {
|
|
228
|
+
// 检查关键词是否指向不同方向
|
|
229
|
+
const categoryHints = keywords.map(k => getKeywordCategoryHint(k));
|
|
230
|
+
const uniqueCategories = [...new Set(categoryHints.filter(c => c))];
|
|
231
|
+
return uniqueCategories.length > 1;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function getKeywordCategoryHint(keyword) {
|
|
235
|
+
// 从 mappings.keywords 构建查找表(合并中英文关键词)
|
|
236
|
+
const keywordMap = {
|
|
237
|
+
...mappings.keywords.chinese,
|
|
238
|
+
...mappings.keywords.english
|
|
239
|
+
};
|
|
240
|
+
return keywordMap[keyword.toLowerCase()];
|
|
241
|
+
}
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## User Interaction
|
|
245
|
+
|
|
246
|
+
如果检测到需要澄清的歧义,暂停并询问用户:
|
|
247
|
+
|
|
248
|
+
```javascript
|
|
249
|
+
async function handleAmbiguities(ambiguities, dimensions) {
|
|
250
|
+
const needsClarification = ambiguities.filter(a => a.needs_clarification);
|
|
251
|
+
|
|
252
|
+
if (needsClarification.length === 0) {
|
|
253
|
+
return null; // 无需澄清
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
const questions = needsClarification.slice(0, 4).map(a => {
|
|
257
|
+
const dim = dimensions.find(d => d.id === a.dimension_id);
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
question: `关于 "${dim.description}",您具体指的是?`,
|
|
261
|
+
header: a.dimension_id,
|
|
262
|
+
options: a.possible_interpretations.map(interp => ({
|
|
263
|
+
label: getCategoryLabel(interp),
|
|
264
|
+
description: getCategoryDescription(interp)
|
|
265
|
+
})),
|
|
266
|
+
multiSelect: false
|
|
267
|
+
};
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
return await AskUserQuestion({ questions });
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function getCategoryLabel(category) {
|
|
274
|
+
// 从 mappings 配置加载标签
|
|
275
|
+
return mappings.category_labels_chinese[category] || category;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function getCategoryDescription(category) {
|
|
279
|
+
// 从 mappings 配置加载描述
|
|
280
|
+
return mappings.category_descriptions[category] || 'Requires further analysis';
|
|
281
|
+
}
|
|
282
|
+
```
|
|
283
|
+
|
|
284
|
+
## Output
|
|
285
|
+
|
|
286
|
+
### State Updates
|
|
287
|
+
|
|
288
|
+
```javascript
|
|
289
|
+
return {
|
|
290
|
+
stateUpdates: {
|
|
291
|
+
requirement_analysis: {
|
|
292
|
+
status: ambiguities.some(a => a.needs_clarification) ? 'needs_clarification' : 'completed',
|
|
293
|
+
analyzed_at: new Date().toISOString(),
|
|
294
|
+
dimensions: dimensions,
|
|
295
|
+
spec_matches: specMatches,
|
|
296
|
+
coverage: coverageResult,
|
|
297
|
+
ambiguities: ambiguities
|
|
298
|
+
},
|
|
299
|
+
// 根据分析结果自动优化 focus_areas
|
|
300
|
+
focus_areas: deriveOptimalFocusAreas(specMatches)
|
|
301
|
+
},
|
|
302
|
+
outputFiles: [
|
|
303
|
+
`${workDir}/requirement-analysis.json`,
|
|
304
|
+
`${workDir}/requirement-analysis.md`
|
|
305
|
+
],
|
|
306
|
+
summary: generateSummary(dimensions, coverageResult, ambiguities)
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
function deriveOptimalFocusAreas(specMatches) {
|
|
310
|
+
const coreCategories = ['context', 'memory', 'dataflow', 'agent'];
|
|
311
|
+
const matched = specMatches
|
|
312
|
+
.filter(m => m.taxonomy_match !== null)
|
|
313
|
+
.map(m => {
|
|
314
|
+
// 映射到诊断 focus_area
|
|
315
|
+
const category = m.taxonomy_match.category;
|
|
316
|
+
if (category === 'context_explosion' || category === 'performance') return 'context';
|
|
317
|
+
if (category === 'memory_loss') return 'memory';
|
|
318
|
+
if (category === 'dataflow_break') return 'dataflow';
|
|
319
|
+
if (category === 'agent_failure' || category === 'error_handling') return 'agent';
|
|
320
|
+
return null;
|
|
321
|
+
})
|
|
322
|
+
.filter(f => f && coreCategories.includes(f));
|
|
323
|
+
|
|
324
|
+
// 去重
|
|
325
|
+
return [...new Set(matched)];
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
function generateSummary(dimensions, coverage, ambiguities) {
|
|
329
|
+
const dimCount = dimensions.length;
|
|
330
|
+
const coverageStatus = coverage.status;
|
|
331
|
+
const ambiguityCount = ambiguities.filter(a => a.needs_clarification).length;
|
|
332
|
+
|
|
333
|
+
let summary = `分析完成:${dimCount} 个维度`;
|
|
334
|
+
summary += `,覆盖度 ${coverage.coverage_rate}% (${coverageStatus})`;
|
|
335
|
+
|
|
336
|
+
if (ambiguityCount > 0) {
|
|
337
|
+
summary += `,${ambiguityCount} 个歧义点待澄清`;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
return summary;
|
|
341
|
+
}
|
|
342
|
+
```
|
|
343
|
+
|
|
344
|
+
### Output Files
|
|
345
|
+
|
|
346
|
+
#### requirement-analysis.json
|
|
347
|
+
|
|
348
|
+
```json
|
|
349
|
+
{
|
|
350
|
+
"timestamp": "2024-01-01T00:00:00Z",
|
|
351
|
+
"target_skill": "skill-name",
|
|
352
|
+
"user_description": "原始用户描述",
|
|
353
|
+
"dimensions": [...],
|
|
354
|
+
"spec_matches": [...],
|
|
355
|
+
"coverage": {...},
|
|
356
|
+
"ambiguities": [...],
|
|
357
|
+
"derived_focus_areas": [...]
|
|
358
|
+
}
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
#### requirement-analysis.md
|
|
362
|
+
|
|
363
|
+
```markdown
|
|
364
|
+
# 需求分析报告
|
|
365
|
+
|
|
366
|
+
## 用户描述
|
|
367
|
+
> ${user_issue_description}
|
|
368
|
+
|
|
369
|
+
## 维度拆解
|
|
370
|
+
|
|
371
|
+
| ID | 描述 | 类别 | 置信度 |
|
|
372
|
+
|----|------|------|--------|
|
|
373
|
+
| DIM-001 | ... | ... | 0.85 |
|
|
374
|
+
|
|
375
|
+
## Spec 匹配
|
|
376
|
+
|
|
377
|
+
| 维度 | 检测模式 | 修复策略 | 是否满足 |
|
|
378
|
+
|------|----------|----------|----------|
|
|
379
|
+
| DIM-001 | CTX-001,002 | sliding_window | ✓ |
|
|
380
|
+
|
|
381
|
+
## 覆盖度评估
|
|
382
|
+
|
|
383
|
+
- 总维度数: N
|
|
384
|
+
- 有检测手段: M
|
|
385
|
+
- 有修复策略: K (满足标准)
|
|
386
|
+
- 覆盖率: X%
|
|
387
|
+
- 状态: satisfied/partial/unsatisfied
|
|
388
|
+
|
|
389
|
+
## 歧义点
|
|
390
|
+
|
|
391
|
+
(如有)
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
## Error Handling
|
|
395
|
+
|
|
396
|
+
| Error | Recovery |
|
|
397
|
+
|-------|----------|
|
|
398
|
+
| Gemini CLI 超时 | 重试一次,仍失败则使用简化分析 |
|
|
399
|
+
| JSON 解析失败 | 尝试修复 JSON 或使用默认维度 |
|
|
400
|
+
| 无法匹配任何类别 | 全部归类为 custom,触发 Gemini 深度分析 |
|
|
401
|
+
|
|
402
|
+
## Next Actions
|
|
403
|
+
|
|
404
|
+
- 如果 `requirement_analysis.status === 'completed'`: 继续到 `action-diagnose-*`
|
|
405
|
+
- 如果 `requirement_analysis.status === 'needs_clarification'`: 等待用户澄清后重新执行
|
|
406
|
+
- 如果 `coverage.status === 'unsatisfied'`: 自动触发 `action-gemini-analysis` 进行深度分析
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
# Action: Apply Fix
|
|
2
|
+
|
|
3
|
+
Apply a selected fix to the target skill with backup and rollback capability.
|
|
4
|
+
|
|
5
|
+
## Purpose
|
|
6
|
+
|
|
7
|
+
- Apply fix changes to target skill files
|
|
8
|
+
- Create backup before modifications
|
|
9
|
+
- Track applied fixes for verification
|
|
10
|
+
- Support rollback if needed
|
|
11
|
+
|
|
12
|
+
## Preconditions
|
|
13
|
+
|
|
14
|
+
- [ ] state.status === 'running'
|
|
15
|
+
- [ ] state.pending_fixes.length > 0
|
|
16
|
+
- [ ] state.proposed_fixes contains the fix to apply
|
|
17
|
+
|
|
18
|
+
## Execution
|
|
19
|
+
|
|
20
|
+
```javascript
|
|
21
|
+
async function execute(state, workDir) {
|
|
22
|
+
const pendingFixes = state.pending_fixes;
|
|
23
|
+
const proposedFixes = state.proposed_fixes;
|
|
24
|
+
const targetPath = state.target_skill.path;
|
|
25
|
+
const backupDir = state.backup_dir;
|
|
26
|
+
|
|
27
|
+
if (pendingFixes.length === 0) {
|
|
28
|
+
return {
|
|
29
|
+
stateUpdates: {},
|
|
30
|
+
outputFiles: [],
|
|
31
|
+
summary: 'No pending fixes to apply'
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Get next fix to apply
|
|
36
|
+
const fixId = pendingFixes[0];
|
|
37
|
+
const fix = proposedFixes.find(f => f.id === fixId);
|
|
38
|
+
|
|
39
|
+
if (!fix) {
|
|
40
|
+
return {
|
|
41
|
+
stateUpdates: {
|
|
42
|
+
pending_fixes: pendingFixes.slice(1),
|
|
43
|
+
errors: [...state.errors, {
|
|
44
|
+
action: 'action-apply-fix',
|
|
45
|
+
message: `Fix ${fixId} not found in proposals`,
|
|
46
|
+
timestamp: new Date().toISOString(),
|
|
47
|
+
recoverable: true
|
|
48
|
+
}]
|
|
49
|
+
},
|
|
50
|
+
outputFiles: [],
|
|
51
|
+
summary: `Fix ${fixId} not found, skipping`
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
console.log(`Applying fix ${fix.id}: ${fix.description}`);
|
|
56
|
+
|
|
57
|
+
// Create fix-specific backup
|
|
58
|
+
const fixBackupDir = `${backupDir}/before-${fix.id}`;
|
|
59
|
+
Bash(`mkdir -p "${fixBackupDir}"`);
|
|
60
|
+
|
|
61
|
+
const appliedChanges = [];
|
|
62
|
+
let success = true;
|
|
63
|
+
|
|
64
|
+
for (const change of fix.changes) {
|
|
65
|
+
try {
|
|
66
|
+
// Resolve file path (handle wildcards)
|
|
67
|
+
let targetFiles = [];
|
|
68
|
+
if (change.file.includes('*')) {
|
|
69
|
+
targetFiles = Glob(`${targetPath}/${change.file}`);
|
|
70
|
+
} else {
|
|
71
|
+
targetFiles = [`${targetPath}/${change.file}`];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
for (const targetFile of targetFiles) {
|
|
75
|
+
// Backup original
|
|
76
|
+
const relativePath = targetFile.replace(targetPath + '/', '');
|
|
77
|
+
const backupPath = `${fixBackupDir}/${relativePath}`;
|
|
78
|
+
|
|
79
|
+
if (Glob(targetFile).length > 0) {
|
|
80
|
+
const originalContent = Read(targetFile);
|
|
81
|
+
Bash(`mkdir -p "$(dirname "${backupPath}")"`);
|
|
82
|
+
Write(backupPath, originalContent);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Apply change based on action type
|
|
86
|
+
if (change.action === 'modify' && change.diff) {
|
|
87
|
+
// For now, append the diff as a comment/note
|
|
88
|
+
// Real implementation would parse and apply the diff
|
|
89
|
+
const existingContent = Read(targetFile);
|
|
90
|
+
|
|
91
|
+
// Simple diff application: look for context and apply
|
|
92
|
+
// This is a simplified version - real implementation would be more sophisticated
|
|
93
|
+
const newContent = existingContent + `\n\n<!-- Applied fix ${fix.id}: ${fix.description} -->\n`;
|
|
94
|
+
|
|
95
|
+
Write(targetFile, newContent);
|
|
96
|
+
|
|
97
|
+
appliedChanges.push({
|
|
98
|
+
file: relativePath,
|
|
99
|
+
action: 'modified',
|
|
100
|
+
backup: backupPath
|
|
101
|
+
});
|
|
102
|
+
} else if (change.action === 'create') {
|
|
103
|
+
Write(targetFile, change.new_content || '');
|
|
104
|
+
appliedChanges.push({
|
|
105
|
+
file: relativePath,
|
|
106
|
+
action: 'created',
|
|
107
|
+
backup: null
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
} catch (error) {
|
|
112
|
+
console.log(`Error applying change to ${change.file}: ${error.message}`);
|
|
113
|
+
success = false;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Record applied fix
|
|
118
|
+
const appliedFix = {
|
|
119
|
+
fix_id: fix.id,
|
|
120
|
+
applied_at: new Date().toISOString(),
|
|
121
|
+
success: success,
|
|
122
|
+
backup_path: fixBackupDir,
|
|
123
|
+
verification_result: 'pending',
|
|
124
|
+
rollback_available: true,
|
|
125
|
+
changes_made: appliedChanges
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
// Update applied fixes log
|
|
129
|
+
const appliedFixesPath = `${workDir}/fixes/applied-fixes.json`;
|
|
130
|
+
let existingApplied = [];
|
|
131
|
+
try {
|
|
132
|
+
existingApplied = JSON.parse(Read(appliedFixesPath));
|
|
133
|
+
} catch (e) {
|
|
134
|
+
existingApplied = [];
|
|
135
|
+
}
|
|
136
|
+
existingApplied.push(appliedFix);
|
|
137
|
+
Write(appliedFixesPath, JSON.stringify(existingApplied, null, 2));
|
|
138
|
+
|
|
139
|
+
return {
|
|
140
|
+
stateUpdates: {
|
|
141
|
+
applied_fixes: [...state.applied_fixes, appliedFix],
|
|
142
|
+
pending_fixes: pendingFixes.slice(1) // Remove applied fix from pending
|
|
143
|
+
},
|
|
144
|
+
outputFiles: [appliedFixesPath],
|
|
145
|
+
summary: `Applied fix ${fix.id}: ${success ? 'success' : 'partial'}, ${appliedChanges.length} files modified`
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
## State Updates
|
|
151
|
+
|
|
152
|
+
```javascript
|
|
153
|
+
return {
|
|
154
|
+
stateUpdates: {
|
|
155
|
+
applied_fixes: [...existingApplied, newAppliedFix],
|
|
156
|
+
pending_fixes: remainingPendingFixes
|
|
157
|
+
}
|
|
158
|
+
};
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
## Rollback Function
|
|
162
|
+
|
|
163
|
+
```javascript
|
|
164
|
+
async function rollbackFix(fixId, state, workDir) {
|
|
165
|
+
const appliedFix = state.applied_fixes.find(f => f.fix_id === fixId);
|
|
166
|
+
|
|
167
|
+
if (!appliedFix || !appliedFix.rollback_available) {
|
|
168
|
+
throw new Error(`Cannot rollback fix ${fixId}`);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const backupDir = appliedFix.backup_path;
|
|
172
|
+
const targetPath = state.target_skill.path;
|
|
173
|
+
|
|
174
|
+
// Restore from backup
|
|
175
|
+
const backupFiles = Glob(`${backupDir}/**/*`);
|
|
176
|
+
for (const backupFile of backupFiles) {
|
|
177
|
+
const relativePath = backupFile.replace(backupDir + '/', '');
|
|
178
|
+
const targetFile = `${targetPath}/${relativePath}`;
|
|
179
|
+
const content = Read(backupFile);
|
|
180
|
+
Write(targetFile, content);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return {
|
|
184
|
+
stateUpdates: {
|
|
185
|
+
applied_fixes: state.applied_fixes.map(f =>
|
|
186
|
+
f.fix_id === fixId
|
|
187
|
+
? { ...f, rollback_available: false, verification_result: 'rolled_back' }
|
|
188
|
+
: f
|
|
189
|
+
)
|
|
190
|
+
}
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
## Error Handling
|
|
196
|
+
|
|
197
|
+
| Error Type | Recovery |
|
|
198
|
+
|------------|----------|
|
|
199
|
+
| File not found | Skip file, log warning |
|
|
200
|
+
| Write permission error | Retry with sudo or report |
|
|
201
|
+
| Backup creation failed | Abort fix, don't modify |
|
|
202
|
+
|
|
203
|
+
## Next Actions
|
|
204
|
+
|
|
205
|
+
- If pending_fixes.length > 0: action-apply-fix (continue)
|
|
206
|
+
- If all fixes applied: action-verify
|