@bicorne/task-flow 0.1.0
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/LICENSE +21 -0
- package/README.md +252 -0
- package/SKILL.md +250 -0
- package/assets/.harnessrc +10 -0
- package/assets/PHASE-task.json.example +50 -0
- package/assets/design.md +69 -0
- package/assets/hooks.json +15 -0
- package/assets/product-requirement.md +82 -0
- package/assets/schema.json +127 -0
- package/assets/tasks.md +26 -0
- package/dist/commands/analyze.d.ts +32 -0
- package/dist/commands/analyze.js +338 -0
- package/dist/commands/archive.d.ts +11 -0
- package/dist/commands/archive.js +53 -0
- package/dist/commands/design.d.ts +38 -0
- package/dist/commands/design.js +492 -0
- package/dist/commands/extract.d.ts +31 -0
- package/dist/commands/extract.js +477 -0
- package/dist/commands/init.d.ts +24 -0
- package/dist/commands/init.js +165 -0
- package/dist/commands/merge/index.d.ts +17 -0
- package/dist/commands/merge/index.js +322 -0
- package/dist/commands/merge/merger.d.ts +18 -0
- package/dist/commands/merge/merger.js +151 -0
- package/dist/commands/merge/types.d.ts +67 -0
- package/dist/commands/merge/types.js +6 -0
- package/dist/commands/merge/validators.d.ts +14 -0
- package/dist/commands/merge/validators.js +147 -0
- package/dist/commands/merge.d.ts +7 -0
- package/dist/commands/merge.js +15 -0
- package/dist/commands/start.d.ts +32 -0
- package/dist/commands/start.js +265 -0
- package/dist/commands/status.d.ts +15 -0
- package/dist/commands/status.js +143 -0
- package/dist/commands/sync.d.ts +11 -0
- package/dist/commands/sync.js +58 -0
- package/dist/commands/tasks-gen/doc-parser.d.ts +7 -0
- package/dist/commands/tasks-gen/doc-parser.js +259 -0
- package/dist/commands/tasks-gen/generators.d.ts +33 -0
- package/dist/commands/tasks-gen/generators.js +141 -0
- package/dist/commands/tasks-gen/index.d.ts +30 -0
- package/dist/commands/tasks-gen/index.js +345 -0
- package/dist/commands/tasks-gen/parsers.d.ts +29 -0
- package/dist/commands/tasks-gen/parsers.js +272 -0
- package/dist/commands/tasks-gen/templates.d.ts +8 -0
- package/dist/commands/tasks-gen/templates.js +37 -0
- package/dist/commands/tasks-gen/types.d.ts +71 -0
- package/dist/commands/tasks-gen/types.js +17 -0
- package/dist/commands/tasks-gen/validators.d.ts +14 -0
- package/dist/commands/tasks-gen/validators.js +54 -0
- package/dist/commands/tasks.d.ts +9 -0
- package/dist/commands/tasks.js +22 -0
- package/dist/commands/worktree.d.ts +28 -0
- package/dist/commands/worktree.js +275 -0
- package/dist/hooks/check-prd-exists.d.ts +20 -0
- package/dist/hooks/check-prd-exists.js +61 -0
- package/dist/hooks/check-worktree-conflict.d.ts +34 -0
- package/dist/hooks/check-worktree-conflict.js +107 -0
- package/dist/hooks/hook-runner/executor.d.ts +18 -0
- package/dist/hooks/hook-runner/executor.js +143 -0
- package/dist/hooks/hook-runner/index.d.ts +64 -0
- package/dist/hooks/hook-runner/index.js +220 -0
- package/dist/hooks/hook-runner/loader.d.ts +23 -0
- package/dist/hooks/hook-runner/loader.js +126 -0
- package/dist/hooks/hook-runner/types.d.ts +59 -0
- package/dist/hooks/hook-runner/types.js +6 -0
- package/dist/hooks/hook-runner.d.ts +9 -0
- package/dist/hooks/hook-runner.js +30 -0
- package/dist/hooks/phase-complete-detector.d.ts +35 -0
- package/dist/hooks/phase-complete-detector.js +203 -0
- package/dist/hooks/phase-gate-validator.d.ts +76 -0
- package/dist/hooks/phase-gate-validator.js +407 -0
- package/dist/hooks/save-checkpoint.d.ts +43 -0
- package/dist/hooks/save-checkpoint.js +144 -0
- package/dist/hooks/start-mcp-servers.d.ts +50 -0
- package/dist/hooks/start-mcp-servers.js +75 -0
- package/dist/hooks/stop-mcp-servers.d.ts +40 -0
- package/dist/hooks/stop-mcp-servers.js +58 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +238 -0
- package/dist/lib/archive.d.ts +31 -0
- package/dist/lib/archive.js +226 -0
- package/dist/lib/config.d.ts +93 -0
- package/dist/lib/config.js +251 -0
- package/dist/lib/constants.d.ts +222 -0
- package/dist/lib/constants.js +247 -0
- package/dist/lib/interactive.d.ts +31 -0
- package/dist/lib/interactive.js +166 -0
- package/dist/lib/mcp-client.d.ts +156 -0
- package/dist/lib/mcp-client.js +370 -0
- package/dist/lib/state.d.ts +119 -0
- package/dist/lib/state.js +293 -0
- package/dist/slash/executor.d.ts +22 -0
- package/dist/slash/executor.js +259 -0
- package/dist/slash/index.d.ts +11 -0
- package/dist/slash/index.js +45 -0
- package/dist/slash/parser.d.ts +24 -0
- package/dist/slash/parser.js +101 -0
- package/dist/slash/registry.d.ts +22 -0
- package/dist/slash/registry.js +155 -0
- package/dist/spec/openspec-to-task/builders.d.ts +107 -0
- package/dist/spec/openspec-to-task/builders.js +138 -0
- package/dist/spec/openspec-to-task/index.d.ts +20 -0
- package/dist/spec/openspec-to-task/index.js +182 -0
- package/dist/spec/openspec-to-task/parsers.d.ts +65 -0
- package/dist/spec/openspec-to-task/parsers.js +232 -0
- package/dist/spec/openspec-to-task/types.d.ts +49 -0
- package/dist/spec/openspec-to-task/types.js +6 -0
- package/dist/spec/sync-openspec-to-task.d.ts +7 -0
- package/dist/spec/sync-openspec-to-task.js +21 -0
- package/dist/spec/sync-task-to-openspec.d.ts +27 -0
- package/dist/spec/sync-task-to-openspec.js +288 -0
- package/dist/types/ai-context.d.ts +108 -0
- package/dist/types/ai-context.js +9 -0
- package/package.json +66 -0
- package/references/AI-CONVERSATION-TUTORIAL.md +270 -0
- package/references/CLI-TUTORIAL.md +447 -0
- package/references/GIT-WORKTREE-SOP.md +109 -0
|
@@ -0,0 +1,477 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/extract.ts
|
|
4
|
+
* Extract project requirements and generate PRD document
|
|
5
|
+
*
|
|
6
|
+
* This command is AI-only - use it to summarize existing projects
|
|
7
|
+
* and generate structured Product Requirement Documents.
|
|
8
|
+
*/
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.extract = extract;
|
|
44
|
+
exports.main = main;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const config_1 = require("../lib/config");
|
|
48
|
+
function parseArgs(argv) {
|
|
49
|
+
const args = {};
|
|
50
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
51
|
+
const token = argv[i] || '';
|
|
52
|
+
if (!token.startsWith('--')) {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
const key = token.slice(2);
|
|
56
|
+
const next = argv[i + 1] || '';
|
|
57
|
+
if (!next || next.startsWith('--')) {
|
|
58
|
+
args[key] = true;
|
|
59
|
+
continue;
|
|
60
|
+
}
|
|
61
|
+
args[key] = next;
|
|
62
|
+
i += 1;
|
|
63
|
+
}
|
|
64
|
+
return args;
|
|
65
|
+
}
|
|
66
|
+
function loadTemplate(name) {
|
|
67
|
+
const templatePath = path.resolve(__dirname, '../../assets', name);
|
|
68
|
+
if (fs.existsSync(templatePath)) {
|
|
69
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
70
|
+
}
|
|
71
|
+
return `# 产品需求文档
|
|
72
|
+
|
|
73
|
+
## 文档基础信息(Meta Data)
|
|
74
|
+
|
|
75
|
+
| 字段 | 内容 |
|
|
76
|
+
|------|------|
|
|
77
|
+
| 项目名称 | <!-- 项目名称 --> |
|
|
78
|
+
| 版本 | v1.0.0 |
|
|
79
|
+
| 创建日期 | <!-- YYYY-MM-DD --> |
|
|
80
|
+
| 作者 | <!-- 作者 --> |
|
|
81
|
+
| 文档状态 | 草稿 |
|
|
82
|
+
| 评审人 | <!-- 评审人列表 --> |
|
|
83
|
+
|
|
84
|
+
## 项目背景与目标(Why)
|
|
85
|
+
|
|
86
|
+
### 背景
|
|
87
|
+
<!-- 描述项目背景和动机,当前存在的问题或痛点 -->
|
|
88
|
+
|
|
89
|
+
### 目标
|
|
90
|
+
<!-- 描述项目要达成的核心目标,可量化指标优先 -->
|
|
91
|
+
|
|
92
|
+
### 范围
|
|
93
|
+
<!-- 明确项目包含和不包含的内容 -->
|
|
94
|
+
|
|
95
|
+
## 用户与场景(Who and When)
|
|
96
|
+
|
|
97
|
+
### 目标用户
|
|
98
|
+
<!-- 描述目标用户群体及其特征 -->
|
|
99
|
+
|
|
100
|
+
### 用户场景
|
|
101
|
+
<!-- 描述典型使用场景和用户旅程 -->
|
|
102
|
+
|
|
103
|
+
### 用户故事
|
|
104
|
+
<!-- 以"作为[用户],我希望[功能],以便[价值]"格式描述 -->
|
|
105
|
+
|
|
106
|
+
## 全局设计与架构(How)
|
|
107
|
+
|
|
108
|
+
### 整体架构
|
|
109
|
+
<!-- 描述系统整体架构设计 -->
|
|
110
|
+
|
|
111
|
+
### 核心流程
|
|
112
|
+
<!-- 描述核心业务流程 -->
|
|
113
|
+
|
|
114
|
+
### 约束条件
|
|
115
|
+
<!-- 技术、业务或时间约束 -->
|
|
116
|
+
|
|
117
|
+
## 详细功能需求(What)
|
|
118
|
+
|
|
119
|
+
### 功能清单
|
|
120
|
+
<!-- 以表格或列表形式列出所有功能需求 -->
|
|
121
|
+
|
|
122
|
+
| 编号 | 功能名称 | 优先级 | 描述 | 验收标准 |
|
|
123
|
+
|------|----------|--------|------|----------|
|
|
124
|
+
| F-001 | <!-- 功能 --> | P0 | <!-- 描述 --> | <!-- 标准 --> |
|
|
125
|
+
|
|
126
|
+
### 功能详细说明
|
|
127
|
+
<!-- 对每个功能进行详细说明 -->
|
|
128
|
+
|
|
129
|
+
## 非功能性需求
|
|
130
|
+
|
|
131
|
+
### 性能需求
|
|
132
|
+
<!-- 响应时间、吞吐量、并发用户数等 -->
|
|
133
|
+
|
|
134
|
+
### 安全需求
|
|
135
|
+
<!-- 认证、授权、数据加密、审计等 -->
|
|
136
|
+
|
|
137
|
+
### 可用性需求
|
|
138
|
+
<!-- SLA、容灾、降级策略等 -->
|
|
139
|
+
|
|
140
|
+
### 兼容性需求
|
|
141
|
+
<!-- 浏览器、操作系统、设备兼容性 -->
|
|
142
|
+
|
|
143
|
+
## 上线与运营计划
|
|
144
|
+
|
|
145
|
+
### 上线计划
|
|
146
|
+
<!-- 上线步骤、灰度策略、回滚方案 -->
|
|
147
|
+
|
|
148
|
+
### 监控与告警
|
|
149
|
+
<!-- 关键指标监控、告警阈值 -->
|
|
150
|
+
|
|
151
|
+
### 运营计划
|
|
152
|
+
<!-- 数据埋点、A/B 测试、迭代计划 -->
|
|
153
|
+
`;
|
|
154
|
+
}
|
|
155
|
+
function toChangeName(input) {
|
|
156
|
+
const sanitized = String(input || '')
|
|
157
|
+
.trim()
|
|
158
|
+
.replace(/[^a-zA-Z0-9-_]/g, '-')
|
|
159
|
+
.replace(/-+/g, '-')
|
|
160
|
+
.replace(/^-|-$/g, '');
|
|
161
|
+
if (sanitized.includes('..') || sanitized.startsWith('/')) {
|
|
162
|
+
return '';
|
|
163
|
+
}
|
|
164
|
+
return sanitized;
|
|
165
|
+
}
|
|
166
|
+
function readStdin() {
|
|
167
|
+
return new Promise((resolve, reject) => {
|
|
168
|
+
const chunks = [];
|
|
169
|
+
process.stdin.setEncoding('utf8');
|
|
170
|
+
process.stdin.on('readable', () => {
|
|
171
|
+
let chunk;
|
|
172
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
173
|
+
chunks.push(chunk);
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
process.stdin.on('end', () => {
|
|
177
|
+
resolve(chunks.join(''));
|
|
178
|
+
});
|
|
179
|
+
process.stdin.on('error', (error) => {
|
|
180
|
+
reject(error);
|
|
181
|
+
});
|
|
182
|
+
// Timeout after 5 seconds if no data
|
|
183
|
+
setTimeout(() => {
|
|
184
|
+
if (chunks.length === 0) {
|
|
185
|
+
reject(new Error('Stdin timeout: no data received'));
|
|
186
|
+
}
|
|
187
|
+
}, 5000);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
function generatePrdContent(aiInput, context) {
|
|
191
|
+
const templateContent = loadTemplate('product-requirement.md');
|
|
192
|
+
// Pre-fill from context file if provided
|
|
193
|
+
const projectName = context?.project.name || '';
|
|
194
|
+
const projectDesc = context?.project.description || '';
|
|
195
|
+
const techStack = context?.project.techStack;
|
|
196
|
+
const analysis = context?.analysis;
|
|
197
|
+
const taskInfo = context?.task;
|
|
198
|
+
// If AI provides analysis input, integrate it into the template
|
|
199
|
+
if (aiInput) {
|
|
200
|
+
// Try to parse as structured JSON first
|
|
201
|
+
try {
|
|
202
|
+
const parsed = JSON.parse(aiInput);
|
|
203
|
+
let content = templateContent;
|
|
204
|
+
if (parsed.projectName || projectName) {
|
|
205
|
+
content = content.replace('<!-- 项目名称 -->', parsed.projectName || projectName);
|
|
206
|
+
}
|
|
207
|
+
if (parsed.background) {
|
|
208
|
+
content = content.replace('<!-- 描述项目背景和动机,当前存在的问题或痛点 -->', parsed.background);
|
|
209
|
+
}
|
|
210
|
+
if (parsed.goals) {
|
|
211
|
+
const goalsText = Array.isArray(parsed.goals) ? parsed.goals.join('\n') : parsed.goals;
|
|
212
|
+
content = content.replace('<!-- 描述项目要达成的核心目标,可量化指标优先 -->', goalsText);
|
|
213
|
+
}
|
|
214
|
+
if (parsed.users) {
|
|
215
|
+
const usersText = Array.isArray(parsed.users) ? parsed.users.join('\n') : parsed.users;
|
|
216
|
+
content = content.replace('<!-- 描述目标用户群体及其特征 -->', usersText);
|
|
217
|
+
}
|
|
218
|
+
if (parsed.scenarios) {
|
|
219
|
+
const scenariosText = Array.isArray(parsed.scenarios) ? parsed.scenarios.join('\n') : parsed.scenarios;
|
|
220
|
+
content = content.replace('<!-- 描述典型使用场景和用户旅程 -->', scenariosText);
|
|
221
|
+
}
|
|
222
|
+
if (parsed.architecture) {
|
|
223
|
+
content = content.replace('<!-- 描述系统整体架构设计 -->', parsed.architecture);
|
|
224
|
+
}
|
|
225
|
+
if (parsed.features) {
|
|
226
|
+
const featuresText = Array.isArray(parsed.features) ? parsed.features.join('\n') : parsed.features;
|
|
227
|
+
content = content.replace('<!-- 以表格或列表形式列出所有功能需求 -->', featuresText);
|
|
228
|
+
}
|
|
229
|
+
return content;
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
// If not valid JSON, treat as plain text analysis
|
|
233
|
+
return generatePrdFromText(aiInput, projectName, projectDesc, techStack, analysis, taskInfo);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
// No AI input, use context file to pre-fill template if available
|
|
237
|
+
return generatePrdFromText(undefined, projectName, projectDesc, techStack, analysis, taskInfo);
|
|
238
|
+
}
|
|
239
|
+
function generatePrdFromText(aiInput, projectName, projectDesc, _techStack, analysis, taskInfo) {
|
|
240
|
+
const taskType = taskInfo?.type || 'feat';
|
|
241
|
+
const taskPriority = taskInfo?.priority || 'medium';
|
|
242
|
+
const backgroundSection = projectDesc
|
|
243
|
+
|| aiInput
|
|
244
|
+
|| '<!-- 描述项目背景和动机,当前存在的问题或痛点 -->';
|
|
245
|
+
const architectureSection = analysis?.architectureSummary
|
|
246
|
+
? `### 架构现状\n\n${analysis.architectureSummary}\n`
|
|
247
|
+
: '';
|
|
248
|
+
const risksSection = analysis?.risks && analysis.risks.length > 0
|
|
249
|
+
? `### 潜在风险\n\n${analysis.risks.map((r) => `- ${r}`).join('\n')}\n`
|
|
250
|
+
: '';
|
|
251
|
+
const suggestionsSection = analysis?.suggestions && analysis.suggestions.length > 0
|
|
252
|
+
? `### 改进建议\n\n${analysis.suggestions.map((s) => `- ${s}`).join('\n')}\n`
|
|
253
|
+
: '';
|
|
254
|
+
const affectedFiles = taskInfo?.affectedFiles && taskInfo.affectedFiles.length > 0
|
|
255
|
+
? `### 涉及文件\n\n${taskInfo.affectedFiles.map((f) => `- \`${f}\``).join('\n')}\n`
|
|
256
|
+
: '';
|
|
257
|
+
const constraints = taskInfo?.constraints && taskInfo.constraints.length > 0
|
|
258
|
+
? taskInfo.constraints.map((c) => `- ${c}`).join('\n')
|
|
259
|
+
: '<!-- 技术、业务或时间约束 -->';
|
|
260
|
+
return `# 产品需求文档
|
|
261
|
+
|
|
262
|
+
## 文档基础信息(Meta Data)
|
|
263
|
+
|
|
264
|
+
| 字段 | 内容 |
|
|
265
|
+
|------|------|
|
|
266
|
+
| 项目名称 | ${projectName || '<!-- 项目名称 -->'} |
|
|
267
|
+
| 任务类型 | ${taskType} |
|
|
268
|
+
| 优先级 | ${taskPriority} |
|
|
269
|
+
| 版本 | v1.0.0 |
|
|
270
|
+
| 创建日期 | ${new Date().toISOString().split('T')[0]} |
|
|
271
|
+
| 作者 | AI Agent |
|
|
272
|
+
| 文档状态 | 草稿 |
|
|
273
|
+
| 评审人 | <!-- 评审人列表 --> |
|
|
274
|
+
|
|
275
|
+
## 项目背景与目标(Why)
|
|
276
|
+
|
|
277
|
+
### 背景
|
|
278
|
+
|
|
279
|
+
${backgroundSection}
|
|
280
|
+
|
|
281
|
+
${architectureSection}${risksSection}${suggestionsSection}
|
|
282
|
+
### 目标
|
|
283
|
+
<!-- 描述项目要达成的核心目标,可量化指标优先 -->
|
|
284
|
+
|
|
285
|
+
### 范围
|
|
286
|
+
<!-- 明确项目包含和不包含的内容 -->
|
|
287
|
+
|
|
288
|
+
## 用户与场景(Who and When)
|
|
289
|
+
|
|
290
|
+
### 目标用户
|
|
291
|
+
<!-- 描述目标用户群体及其特征 -->
|
|
292
|
+
|
|
293
|
+
### 用户场景
|
|
294
|
+
<!-- 描述典型使用场景和用户旅程 -->
|
|
295
|
+
|
|
296
|
+
### 用户故事
|
|
297
|
+
<!-- 以"作为[用户],我希望[功能],以便[价值]"格式描述 -->
|
|
298
|
+
|
|
299
|
+
## 全局设计与架构(How)
|
|
300
|
+
|
|
301
|
+
### 整体架构
|
|
302
|
+
<!-- 描述系统整体架构设计 -->
|
|
303
|
+
|
|
304
|
+
### 核心流程
|
|
305
|
+
<!-- 描述核心业务流程 -->
|
|
306
|
+
|
|
307
|
+
### 约束条件
|
|
308
|
+
|
|
309
|
+
${constraints}
|
|
310
|
+
|
|
311
|
+
## 详细功能需求(What)
|
|
312
|
+
|
|
313
|
+
### 功能清单
|
|
314
|
+
<!-- 以表格或列表形式列出所有功能需求 -->
|
|
315
|
+
|
|
316
|
+
| 编号 | 功能名称 | 优先级 | 描述 | 验收标准 |
|
|
317
|
+
|------|----------|--------|------|----------|
|
|
318
|
+
| F-001 | <!-- 功能 --> | P0 | <!-- 描述 --> | <!-- 标准 --> |
|
|
319
|
+
|
|
320
|
+
### 功能详细说明
|
|
321
|
+
<!-- 对每个功能进行详细说明 -->
|
|
322
|
+
|
|
323
|
+
${affectedFiles}
|
|
324
|
+
## 非功能性需求
|
|
325
|
+
|
|
326
|
+
### 性能需求
|
|
327
|
+
<!-- 响应时间、吞吐量、并发用户数等 -->
|
|
328
|
+
|
|
329
|
+
### 安全需求
|
|
330
|
+
<!-- 认证、授权、数据加密、审计等 -->
|
|
331
|
+
|
|
332
|
+
### 可用性需求
|
|
333
|
+
<!-- SLA、容灾、降级策略等 -->
|
|
334
|
+
|
|
335
|
+
### 兼容性需求
|
|
336
|
+
<!-- 浏览器、操作系统、设备兼容性 -->
|
|
337
|
+
|
|
338
|
+
## 上线与运营计划
|
|
339
|
+
|
|
340
|
+
### 上线计划
|
|
341
|
+
<!-- 上线步骤、灰度策略、回滚方案 -->
|
|
342
|
+
|
|
343
|
+
### 监控与告警
|
|
344
|
+
<!-- 关键指标监控、告警阈值 -->
|
|
345
|
+
|
|
346
|
+
### 运营计划
|
|
347
|
+
<!-- 数据埋点、A/B 测试、迭代计划 -->
|
|
348
|
+
`;
|
|
349
|
+
}
|
|
350
|
+
function validateConfigForExtract(config) {
|
|
351
|
+
if (!config.projectRoot) {
|
|
352
|
+
return {
|
|
353
|
+
valid: false,
|
|
354
|
+
reason: 'missing-project-root',
|
|
355
|
+
message: 'Config missing projectRoot',
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
return { valid: true };
|
|
359
|
+
}
|
|
360
|
+
async function extract(options = {}) {
|
|
361
|
+
const config = (0, config_1.loadConfig)({ cwd: options.cwd });
|
|
362
|
+
const changeName = toChangeName(options.change);
|
|
363
|
+
if (!changeName) {
|
|
364
|
+
return {
|
|
365
|
+
success: false,
|
|
366
|
+
reason: 'missing-change',
|
|
367
|
+
message: 'Missing required argument: --change <change-name>',
|
|
368
|
+
};
|
|
369
|
+
}
|
|
370
|
+
const configValidation = validateConfigForExtract(config);
|
|
371
|
+
if (!configValidation.valid) {
|
|
372
|
+
return {
|
|
373
|
+
success: false,
|
|
374
|
+
reason: configValidation.reason,
|
|
375
|
+
message: configValidation.message || 'Unknown config validation error',
|
|
376
|
+
};
|
|
377
|
+
}
|
|
378
|
+
const projectRoot = config.projectRoot || process.cwd();
|
|
379
|
+
const specRoot = config.specRootAbs || path.resolve(projectRoot, 'spec');
|
|
380
|
+
const changeDir = path.resolve(specRoot, 'changes', changeName);
|
|
381
|
+
const prdPath = path.resolve(changeDir, 'product-requirement.md');
|
|
382
|
+
if (fs.existsSync(changeDir) && !options.force) {
|
|
383
|
+
return {
|
|
384
|
+
success: false,
|
|
385
|
+
reason: 'already-exists',
|
|
386
|
+
message: `Change directory already exists: ${changeDir}. Use --force to overwrite.`,
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
try {
|
|
390
|
+
fs.mkdirSync(changeDir, { recursive: true });
|
|
391
|
+
// Determine AI input source: --input parameter, stdin, or --context-file
|
|
392
|
+
// Only read stdin when running as CLI (not when called programmatically from tests)
|
|
393
|
+
let aiInput = options.input;
|
|
394
|
+
let contextData;
|
|
395
|
+
// Load context file if provided
|
|
396
|
+
if (options.contextFile) {
|
|
397
|
+
try {
|
|
398
|
+
const contextContent = fs.readFileSync(options.contextFile, 'utf8');
|
|
399
|
+
contextData = JSON.parse(contextContent);
|
|
400
|
+
}
|
|
401
|
+
catch (error) {
|
|
402
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
403
|
+
return {
|
|
404
|
+
success: false,
|
|
405
|
+
reason: 'invalid-context-file',
|
|
406
|
+
message: `Failed to parse context file: ${errorMessage}`,
|
|
407
|
+
};
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (!aiInput && !process.stdin.isTTY && require.main === module) {
|
|
411
|
+
try {
|
|
412
|
+
aiInput = await readStdin();
|
|
413
|
+
if (aiInput.trim()) {
|
|
414
|
+
console.log(`[EXTRACT] Received ${aiInput.length} bytes from stdin`);
|
|
415
|
+
}
|
|
416
|
+
else {
|
|
417
|
+
aiInput = undefined;
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
catch (error) {
|
|
421
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
422
|
+
console.warn(`[EXTRACT] Warning: Failed to read stdin: ${errorMessage}`);
|
|
423
|
+
aiInput = undefined;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
const prdContent = generatePrdContent(aiInput, contextData);
|
|
427
|
+
fs.writeFileSync(prdPath, prdContent, 'utf8');
|
|
428
|
+
}
|
|
429
|
+
catch (error) {
|
|
430
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
431
|
+
return {
|
|
432
|
+
success: false,
|
|
433
|
+
reason: 'file-write-error',
|
|
434
|
+
message: `Failed to create PRD: ${errorMessage}`,
|
|
435
|
+
};
|
|
436
|
+
}
|
|
437
|
+
return {
|
|
438
|
+
success: true,
|
|
439
|
+
changeName,
|
|
440
|
+
changeDir,
|
|
441
|
+
prdPath,
|
|
442
|
+
};
|
|
443
|
+
}
|
|
444
|
+
async function main() {
|
|
445
|
+
const args = parseArgs(process.argv);
|
|
446
|
+
const result = await extract({
|
|
447
|
+
cwd: args.cwd,
|
|
448
|
+
change: args.change,
|
|
449
|
+
type: args.type,
|
|
450
|
+
priority: args.priority,
|
|
451
|
+
force: args.force === true,
|
|
452
|
+
input: args.input,
|
|
453
|
+
contextFile: args['context-file'],
|
|
454
|
+
});
|
|
455
|
+
if (!result.success) {
|
|
456
|
+
console.error(`[EXTRACT] ${result.message}`);
|
|
457
|
+
process.exit(1);
|
|
458
|
+
}
|
|
459
|
+
console.log('');
|
|
460
|
+
console.log('[EXTRACT] PRD document created successfully!');
|
|
461
|
+
console.log(` Change Name: ${result.changeName}`);
|
|
462
|
+
console.log(` Directory: ${result.changeDir}`);
|
|
463
|
+
console.log(` PRD: ${result.prdPath}`);
|
|
464
|
+
console.log('');
|
|
465
|
+
console.log('Next steps:');
|
|
466
|
+
console.log(' 1. Edit product-requirement.md to describe your requirements');
|
|
467
|
+
console.log(' 2. Run: task-flow design --change <change-name>');
|
|
468
|
+
console.log('');
|
|
469
|
+
process.exit(0);
|
|
470
|
+
}
|
|
471
|
+
if (require.main === module) {
|
|
472
|
+
main();
|
|
473
|
+
}
|
|
474
|
+
exports.default = {
|
|
475
|
+
extract,
|
|
476
|
+
main,
|
|
477
|
+
};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/init.ts
|
|
3
|
+
* Initialize task flow in current project
|
|
4
|
+
*/
|
|
5
|
+
import { Config } from '../lib/config';
|
|
6
|
+
import { State } from '../lib/state';
|
|
7
|
+
export interface InitOptions {
|
|
8
|
+
cwd?: string;
|
|
9
|
+
force?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export interface InitResult {
|
|
12
|
+
success: boolean;
|
|
13
|
+
reason?: string;
|
|
14
|
+
message?: string;
|
|
15
|
+
config?: Config;
|
|
16
|
+
state?: State;
|
|
17
|
+
}
|
|
18
|
+
export declare function initHarness(options?: InitOptions): InitResult;
|
|
19
|
+
export declare function main(): void;
|
|
20
|
+
declare const _default: {
|
|
21
|
+
initHarness: typeof initHarness;
|
|
22
|
+
main: typeof main;
|
|
23
|
+
};
|
|
24
|
+
export default _default;
|
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/init.ts
|
|
4
|
+
* Initialize task flow in current project
|
|
5
|
+
*/
|
|
6
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
7
|
+
if (k2 === undefined) k2 = k;
|
|
8
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
9
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
10
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
11
|
+
}
|
|
12
|
+
Object.defineProperty(o, k2, desc);
|
|
13
|
+
}) : (function(o, m, k, k2) {
|
|
14
|
+
if (k2 === undefined) k2 = k;
|
|
15
|
+
o[k2] = m[k];
|
|
16
|
+
}));
|
|
17
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
18
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
19
|
+
}) : function(o, v) {
|
|
20
|
+
o["default"] = v;
|
|
21
|
+
});
|
|
22
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
23
|
+
var ownKeys = function(o) {
|
|
24
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
25
|
+
var ar = [];
|
|
26
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
27
|
+
return ar;
|
|
28
|
+
};
|
|
29
|
+
return ownKeys(o);
|
|
30
|
+
};
|
|
31
|
+
return function (mod) {
|
|
32
|
+
if (mod && mod.__esModule) return mod;
|
|
33
|
+
var result = {};
|
|
34
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
35
|
+
__setModuleDefault(result, mod);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
})();
|
|
39
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
+
exports.initHarness = initHarness;
|
|
41
|
+
exports.main = main;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const path = __importStar(require("path"));
|
|
44
|
+
const config_1 = require("../lib/config");
|
|
45
|
+
const state_1 = require("../lib/state");
|
|
46
|
+
function parseArgs(argv) {
|
|
47
|
+
const args = {};
|
|
48
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
49
|
+
const token = argv[i] || '';
|
|
50
|
+
if (!token.startsWith('--')) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
const key = token.slice(2);
|
|
54
|
+
const next = argv[i + 1];
|
|
55
|
+
if (!next || next.startsWith('--')) {
|
|
56
|
+
args[key] = true;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
args[key] = next;
|
|
60
|
+
i += 1;
|
|
61
|
+
}
|
|
62
|
+
return args;
|
|
63
|
+
}
|
|
64
|
+
function initHarness(options = {}) {
|
|
65
|
+
const config = (0, config_1.loadConfig)({ cwd: options.cwd });
|
|
66
|
+
// Check if already initialized
|
|
67
|
+
const harnessRootAbs = config.harnessRootAbs;
|
|
68
|
+
if (!harnessRootAbs) {
|
|
69
|
+
return {
|
|
70
|
+
success: false,
|
|
71
|
+
reason: 'missing-harness-root',
|
|
72
|
+
message: 'Config missing harnessRootAbs',
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
const statePath = path.resolve(harnessRootAbs, 'state.json');
|
|
76
|
+
if (fs.existsSync(statePath) && !options.force) {
|
|
77
|
+
return {
|
|
78
|
+
success: false,
|
|
79
|
+
reason: 'already-initialized',
|
|
80
|
+
message: `Harness already initialized at ${harnessRootAbs}. Use --force to reinitialize.`,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// Create directories
|
|
84
|
+
(0, config_1.ensureDirectories)(config);
|
|
85
|
+
// Create initial state
|
|
86
|
+
const state = (0, state_1.createInitialState)();
|
|
87
|
+
if (!(0, state_1.saveState)(state, config)) {
|
|
88
|
+
return {
|
|
89
|
+
success: false,
|
|
90
|
+
reason: 'save-state-failed',
|
|
91
|
+
message: 'Failed to save initial state',
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
// Create config template if not exists
|
|
95
|
+
const projectRoot = config.projectRoot;
|
|
96
|
+
if (!projectRoot) {
|
|
97
|
+
return {
|
|
98
|
+
success: false,
|
|
99
|
+
reason: 'missing-project-root',
|
|
100
|
+
message: 'Config missing projectRoot',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
const configTemplatePath = path.resolve(projectRoot, '.harnessrc.example');
|
|
104
|
+
if (!fs.existsSync(configTemplatePath)) {
|
|
105
|
+
const template = {
|
|
106
|
+
harnessRoot: '.harness',
|
|
107
|
+
worktreesDir: '.worktrees',
|
|
108
|
+
tasksDir: 'tasks',
|
|
109
|
+
snapshotsDir: 'snapshots',
|
|
110
|
+
archiveDir: 'archive',
|
|
111
|
+
reportsDir: 'reports',
|
|
112
|
+
plansDir: 'plans',
|
|
113
|
+
specRoot: 'spec',
|
|
114
|
+
};
|
|
115
|
+
try {
|
|
116
|
+
fs.writeFileSync(configTemplatePath, JSON.stringify(template, null, 2), 'utf8');
|
|
117
|
+
}
|
|
118
|
+
catch (error) {
|
|
119
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
120
|
+
return {
|
|
121
|
+
success: false,
|
|
122
|
+
reason: 'file-write-error',
|
|
123
|
+
message: `Failed to write config template: ${errorMessage}`,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return {
|
|
128
|
+
success: true,
|
|
129
|
+
config,
|
|
130
|
+
state,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
function main() {
|
|
134
|
+
const args = parseArgs(process.argv);
|
|
135
|
+
const result = initHarness({
|
|
136
|
+
cwd: args.cwd,
|
|
137
|
+
force: args.force === true,
|
|
138
|
+
});
|
|
139
|
+
if (!result.success) {
|
|
140
|
+
console.error(`[INIT] ${result.message}`);
|
|
141
|
+
process.exit(1);
|
|
142
|
+
}
|
|
143
|
+
const harnessRoot = result.config?.harnessRootAbs || 'unknown';
|
|
144
|
+
const worktreesDir = result.config?.worktreesDirAbs || 'unknown';
|
|
145
|
+
const tasksDir = result.config?.tasksDirAbs || 'unknown';
|
|
146
|
+
const snapshotsDir = result.config?.snapshotsDirAbs || 'unknown';
|
|
147
|
+
console.log('[INIT] Task flow initialized successfully!');
|
|
148
|
+
console.log(` Root: ${harnessRoot}`);
|
|
149
|
+
console.log(` Worktrees: ${worktreesDir}`);
|
|
150
|
+
console.log(` Tasks: ${tasksDir}`);
|
|
151
|
+
console.log(` Snapshots: ${snapshotsDir}`);
|
|
152
|
+
console.log('');
|
|
153
|
+
console.log('Next steps:');
|
|
154
|
+
console.log(' 1. Create a proposal: task-flow extract --change <change-name>');
|
|
155
|
+
console.log(' 2. Check status: task-flow status');
|
|
156
|
+
console.log('');
|
|
157
|
+
process.exit(0);
|
|
158
|
+
}
|
|
159
|
+
if (require.main === module) {
|
|
160
|
+
main();
|
|
161
|
+
}
|
|
162
|
+
exports.default = {
|
|
163
|
+
initHarness,
|
|
164
|
+
main,
|
|
165
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/merge/index.ts
|
|
3
|
+
* Merge completed worktree back to main
|
|
4
|
+
*/
|
|
5
|
+
import { MergeOptions, MergeResult } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Merge task
|
|
8
|
+
*/
|
|
9
|
+
export declare function mergeTask(options?: MergeOptions): MergeResult | Promise<MergeResult>;
|
|
10
|
+
/**
|
|
11
|
+
* Main entry point
|
|
12
|
+
*/
|
|
13
|
+
export declare function main(): void;
|
|
14
|
+
declare const _default: {
|
|
15
|
+
mergeTask: typeof mergeTask;
|
|
16
|
+
};
|
|
17
|
+
export default _default;
|