@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,345 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/tasks-gen/index.ts
|
|
4
|
+
* Generate PHASE-*.json files from product-requirement.md and design.md
|
|
5
|
+
* Supports AI input via stdin or --input parameter
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.generateTasks = generateTasks;
|
|
42
|
+
exports.main = main;
|
|
43
|
+
const fs = __importStar(require("fs"));
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const config_1 = require("../../lib/config");
|
|
46
|
+
const types_1 = require("./types");
|
|
47
|
+
const templates_1 = require("./templates");
|
|
48
|
+
const parsers_1 = require("./parsers");
|
|
49
|
+
const generators_1 = require("./generators");
|
|
50
|
+
const validators_1 = require("./validators");
|
|
51
|
+
const doc_parser_1 = require("./doc-parser");
|
|
52
|
+
function loadTemplate(name) {
|
|
53
|
+
const templatePath = path.resolve(__dirname, '../../../assets', name);
|
|
54
|
+
if (fs.existsSync(templatePath)) {
|
|
55
|
+
return fs.readFileSync(templatePath, 'utf8');
|
|
56
|
+
}
|
|
57
|
+
return null;
|
|
58
|
+
}
|
|
59
|
+
function createTasksTemplate(tasksPath) {
|
|
60
|
+
if (fs.existsSync(tasksPath)) {
|
|
61
|
+
return { created: false };
|
|
62
|
+
}
|
|
63
|
+
try {
|
|
64
|
+
const templateContent = loadTemplate('tasks.md') || templates_1.TASKS_MD_TEMPLATE;
|
|
65
|
+
fs.writeFileSync(tasksPath, templateContent, 'utf8');
|
|
66
|
+
return {
|
|
67
|
+
created: true,
|
|
68
|
+
reason: 'tasks-md-created',
|
|
69
|
+
message: `tasks.md created. Please edit and confirm: ${tasksPath}`,
|
|
70
|
+
tasksPath,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
catch (error) {
|
|
74
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
75
|
+
return {
|
|
76
|
+
created: false,
|
|
77
|
+
reason: 'file-write-error',
|
|
78
|
+
message: `Failed to create tasks.md: ${errorMessage}`,
|
|
79
|
+
tasksPath,
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Generate task files from AI input or tasks.md
|
|
85
|
+
*/
|
|
86
|
+
async function generateTasks(options = {}) {
|
|
87
|
+
const config = (0, config_1.loadConfig)({ cwd: options.cwd });
|
|
88
|
+
const changeName = (0, parsers_1.toChangeName)(options.change);
|
|
89
|
+
if (!changeName) {
|
|
90
|
+
return {
|
|
91
|
+
success: false,
|
|
92
|
+
reason: 'missing-change',
|
|
93
|
+
message: 'Missing required argument: --change <change-name>',
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
const specRoot = config.specRootAbs || path.resolve(config.projectRoot, 'spec');
|
|
97
|
+
const changeDir = path.resolve(specRoot, 'changes', changeName);
|
|
98
|
+
const productRequirementPath = path.resolve(changeDir, 'product-requirement.md');
|
|
99
|
+
const tasksPath = path.resolve(changeDir, 'tasks.md');
|
|
100
|
+
const tasksDir = path.resolve(changeDir, 'tasks');
|
|
101
|
+
const manifestPath = path.resolve(changeDir, 'manifest.json');
|
|
102
|
+
let tasks;
|
|
103
|
+
if (options.input) {
|
|
104
|
+
try {
|
|
105
|
+
tasks = (0, parsers_1.parseAiInput)(options.input);
|
|
106
|
+
console.log(`[TASKS] Parsed ${tasks.length} tasks from --input parameter`);
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
110
|
+
return {
|
|
111
|
+
success: false,
|
|
112
|
+
reason: 'input-parse-error',
|
|
113
|
+
message: `Failed to parse input: ${errorMessage}`,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
else if (!process.stdin.isTTY) {
|
|
118
|
+
try {
|
|
119
|
+
const stdinContent = await (0, parsers_1.readStdin)();
|
|
120
|
+
if (stdinContent.trim()) {
|
|
121
|
+
tasks = (0, parsers_1.parseAiInput)(stdinContent);
|
|
122
|
+
console.log(`[TASKS] Parsed ${tasks.length} tasks from stdin`);
|
|
123
|
+
}
|
|
124
|
+
else {
|
|
125
|
+
const validation = (0, validators_1.validateRequiredFiles)({ productRequirement: productRequirementPath });
|
|
126
|
+
if (!validation.valid) {
|
|
127
|
+
return {
|
|
128
|
+
success: false,
|
|
129
|
+
reason: validation.reason,
|
|
130
|
+
message: validation.message,
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
const designPath = path.resolve(changeDir, 'design.md');
|
|
134
|
+
const hasDesign = fs.existsSync(designPath);
|
|
135
|
+
console.log('[TASKS] Attempting to extract tasks from documents...');
|
|
136
|
+
const prdContent = fs.readFileSync(productRequirementPath, 'utf8');
|
|
137
|
+
const designContent = hasDesign ? fs.readFileSync(designPath, 'utf8') : null;
|
|
138
|
+
tasks = (0, doc_parser_1.parseDocumentsToTasks)(prdContent, designContent, changeName);
|
|
139
|
+
if (tasks.length === 0) {
|
|
140
|
+
console.log('[TASKS] No tasks extracted, falling back to tasks.md');
|
|
141
|
+
const templateResult = createTasksTemplate(tasksPath);
|
|
142
|
+
if (templateResult.created) {
|
|
143
|
+
return {
|
|
144
|
+
success: false,
|
|
145
|
+
reason: templateResult.reason,
|
|
146
|
+
message: templateResult.message,
|
|
147
|
+
tasksPath: templateResult.tasksPath,
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf8');
|
|
151
|
+
tasks = (0, parsers_1.parseTasksMd)(tasksContent);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
console.log(`[TASKS] Successfully extracted ${tasks.length} tasks from documents`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
if (error.message.includes('Stdin timeout')) {
|
|
160
|
+
const validation = (0, validators_1.validateRequiredFiles)({ productRequirement: productRequirementPath });
|
|
161
|
+
if (!validation.valid) {
|
|
162
|
+
return {
|
|
163
|
+
success: false,
|
|
164
|
+
reason: validation.reason,
|
|
165
|
+
message: validation.message,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
const designPath = path.resolve(changeDir, 'design.md');
|
|
169
|
+
const hasDesign = fs.existsSync(designPath);
|
|
170
|
+
console.log('[TASKS] Attempting to extract tasks from documents...');
|
|
171
|
+
const prdContent = fs.readFileSync(productRequirementPath, 'utf8');
|
|
172
|
+
const designContent = hasDesign ? fs.readFileSync(designPath, 'utf8') : null;
|
|
173
|
+
tasks = (0, doc_parser_1.parseDocumentsToTasks)(prdContent, designContent, changeName);
|
|
174
|
+
if (tasks.length === 0) {
|
|
175
|
+
console.log('[TASKS] No tasks extracted, falling back to tasks.md');
|
|
176
|
+
const templateResult = createTasksTemplate(tasksPath);
|
|
177
|
+
if (templateResult.created) {
|
|
178
|
+
return {
|
|
179
|
+
success: false,
|
|
180
|
+
reason: templateResult.reason,
|
|
181
|
+
message: templateResult.message,
|
|
182
|
+
tasksPath: templateResult.tasksPath,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf8');
|
|
186
|
+
tasks = (0, parsers_1.parseTasksMd)(tasksContent);
|
|
187
|
+
}
|
|
188
|
+
else {
|
|
189
|
+
console.log(`[TASKS] Successfully extracted ${tasks.length} tasks from documents`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
194
|
+
return {
|
|
195
|
+
success: false,
|
|
196
|
+
reason: 'stdin-parse-error',
|
|
197
|
+
message: `Failed to parse stdin: ${errorMessage}`,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
const validation = (0, validators_1.validateRequiredFiles)({ productRequirement: productRequirementPath });
|
|
204
|
+
if (!validation.valid) {
|
|
205
|
+
return {
|
|
206
|
+
success: false,
|
|
207
|
+
reason: validation.reason,
|
|
208
|
+
message: validation.message,
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const designPath = path.resolve(changeDir, 'design.md');
|
|
212
|
+
const hasDesign = fs.existsSync(designPath);
|
|
213
|
+
console.log('[TASKS] Attempting to extract tasks from documents...');
|
|
214
|
+
console.log(` PRD: ${productRequirementPath}`);
|
|
215
|
+
if (hasDesign) {
|
|
216
|
+
console.log(` Design: ${designPath}`);
|
|
217
|
+
}
|
|
218
|
+
try {
|
|
219
|
+
const prdContent = fs.readFileSync(productRequirementPath, 'utf8');
|
|
220
|
+
const designContent = hasDesign ? fs.readFileSync(designPath, 'utf8') : null;
|
|
221
|
+
tasks = (0, doc_parser_1.parseDocumentsToTasks)(prdContent, designContent, changeName);
|
|
222
|
+
if (tasks.length > 0) {
|
|
223
|
+
console.log(`[TASKS] Successfully extracted ${tasks.length} tasks from documents`);
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
console.log('[TASKS] No tasks extracted from documents, falling back to tasks.md');
|
|
227
|
+
const templateResult = createTasksTemplate(tasksPath);
|
|
228
|
+
if (templateResult.created) {
|
|
229
|
+
return {
|
|
230
|
+
success: false,
|
|
231
|
+
reason: templateResult.reason,
|
|
232
|
+
message: templateResult.message,
|
|
233
|
+
tasksPath: templateResult.tasksPath,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf8');
|
|
237
|
+
tasks = (0, parsers_1.parseTasksMd)(tasksContent);
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
catch (error) {
|
|
241
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
242
|
+
console.log(`[TASKS] Failed to extract from documents: ${errorMessage}`);
|
|
243
|
+
console.log('[TASKS] Falling back to tasks.md');
|
|
244
|
+
const templateResult = createTasksTemplate(tasksPath);
|
|
245
|
+
if (templateResult.created) {
|
|
246
|
+
return {
|
|
247
|
+
success: false,
|
|
248
|
+
reason: templateResult.reason,
|
|
249
|
+
message: templateResult.message,
|
|
250
|
+
tasksPath: templateResult.tasksPath,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
const tasksContent = fs.readFileSync(tasksPath, 'utf8');
|
|
254
|
+
tasks = (0, parsers_1.parseTasksMd)(tasksContent);
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
if (tasks.length === 0) {
|
|
258
|
+
return {
|
|
259
|
+
success: false,
|
|
260
|
+
reason: 'no-tasks',
|
|
261
|
+
message: 'No valid tasks found. Please provide tasks via --input, stdin, or tasks.md file.',
|
|
262
|
+
};
|
|
263
|
+
}
|
|
264
|
+
console.log('');
|
|
265
|
+
console.log('[TASKS] Task list preview:');
|
|
266
|
+
console.log(` Total tasks: ${tasks.length}`);
|
|
267
|
+
console.log(` Phases: ${[...new Set(tasks.map((t) => t.phase))].sort((a, b) => a - b).join(', ')}`);
|
|
268
|
+
console.log('');
|
|
269
|
+
if (!options.yes && process.stdin.isTTY) {
|
|
270
|
+
const confirmed = await (0, parsers_1.promptConfirmation)('Proceed to generate PHASE-*.json files? [Y/n] ');
|
|
271
|
+
if (!confirmed) {
|
|
272
|
+
return {
|
|
273
|
+
success: false,
|
|
274
|
+
reason: 'user-cancelled',
|
|
275
|
+
message: 'User cancelled the operation.',
|
|
276
|
+
};
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
const { generatedFiles, error: phaseError } = (0, generators_1.generatePhaseFiles)(tasksDir, tasks, changeName);
|
|
280
|
+
if (phaseError) {
|
|
281
|
+
return {
|
|
282
|
+
success: false,
|
|
283
|
+
reason: 'phase-file-error',
|
|
284
|
+
message: `Failed to generate phase files: ${phaseError}`,
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
const manifestResult = (0, generators_1.generateManifestFile)(manifestPath, changeName, tasks, options);
|
|
288
|
+
if (manifestResult.error) {
|
|
289
|
+
return {
|
|
290
|
+
success: false,
|
|
291
|
+
reason: 'manifest-file-error',
|
|
292
|
+
message: `Failed to generate manifest file: ${manifestResult.error}`,
|
|
293
|
+
};
|
|
294
|
+
}
|
|
295
|
+
return {
|
|
296
|
+
success: true,
|
|
297
|
+
changeName,
|
|
298
|
+
tasksPath,
|
|
299
|
+
tasksDir,
|
|
300
|
+
manifestPath,
|
|
301
|
+
generatedFiles,
|
|
302
|
+
totalTasks: tasks.length,
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
/**
|
|
306
|
+
* Main entry point
|
|
307
|
+
*/
|
|
308
|
+
function main() {
|
|
309
|
+
const args = (0, parsers_1.parseArgs)(process.argv);
|
|
310
|
+
generateTasks({
|
|
311
|
+
cwd: args.cwd,
|
|
312
|
+
change: args.change,
|
|
313
|
+
type: args.type,
|
|
314
|
+
priority: args.priority,
|
|
315
|
+
yes: args.yes === true,
|
|
316
|
+
input: args.input,
|
|
317
|
+
}).then((result) => {
|
|
318
|
+
if (!result.success) {
|
|
319
|
+
console.error(`[TASKS] ${result.message}`);
|
|
320
|
+
process.exit(result.reason === 'tasks-md-created' ? 0 : 1);
|
|
321
|
+
}
|
|
322
|
+
console.log('');
|
|
323
|
+
console.log('[TASKS] Task files generated successfully!');
|
|
324
|
+
console.log(` Change Name: ${result.changeName}`);
|
|
325
|
+
console.log(` Tasks Dir: ${result.tasksDir}`);
|
|
326
|
+
console.log(` Manifest: ${result.manifestPath}`);
|
|
327
|
+
console.log(` Generated: ${result.generatedFiles?.length || 0} PHASE-*.json files`);
|
|
328
|
+
console.log('');
|
|
329
|
+
console.log('Next steps:');
|
|
330
|
+
console.log(' 1. Review generated task files');
|
|
331
|
+
console.log(' 2. Run: task-flow worktree --change <change-name>');
|
|
332
|
+
console.log('');
|
|
333
|
+
process.exit(0);
|
|
334
|
+
});
|
|
335
|
+
}
|
|
336
|
+
if (require.main === module) {
|
|
337
|
+
main();
|
|
338
|
+
}
|
|
339
|
+
exports.default = {
|
|
340
|
+
generateTasks,
|
|
341
|
+
parseTasksMd: parsers_1.parseTasksMd,
|
|
342
|
+
generatePhaseJson: generators_1.generatePhaseJson,
|
|
343
|
+
generateManifestJson: generators_1.generateManifestJson,
|
|
344
|
+
TASK_SCHEMA: types_1.TASK_SCHEMA,
|
|
345
|
+
};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/tasks-gen/parsers.ts
|
|
3
|
+
* Parsing utilities for task generation
|
|
4
|
+
*/
|
|
5
|
+
import { Task } from './types';
|
|
6
|
+
/**
|
|
7
|
+
* Parse CLI arguments
|
|
8
|
+
*/
|
|
9
|
+
export declare function parseArgs(argv: string[]): Record<string, string | boolean>;
|
|
10
|
+
/**
|
|
11
|
+
* Sanitize change name
|
|
12
|
+
*/
|
|
13
|
+
export declare function toChangeName(input: string | undefined): string;
|
|
14
|
+
/**
|
|
15
|
+
* Parse tasks from markdown content
|
|
16
|
+
*/
|
|
17
|
+
export declare function parseTasksMd(tasksContent: string): Task[];
|
|
18
|
+
/**
|
|
19
|
+
* Parse AI input from JSON
|
|
20
|
+
*/
|
|
21
|
+
export declare function parseAiInput(input: string): Task[];
|
|
22
|
+
/**
|
|
23
|
+
* Read stdin content
|
|
24
|
+
*/
|
|
25
|
+
export declare function readStdin(): Promise<string>;
|
|
26
|
+
/**
|
|
27
|
+
* Prompt user for confirmation
|
|
28
|
+
*/
|
|
29
|
+
export declare function promptConfirmation(message: string): Promise<boolean>;
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/tasks-gen/parsers.ts
|
|
4
|
+
* Parsing utilities for task generation
|
|
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.parseArgs = parseArgs;
|
|
41
|
+
exports.toChangeName = toChangeName;
|
|
42
|
+
exports.parseTasksMd = parseTasksMd;
|
|
43
|
+
exports.parseAiInput = parseAiInput;
|
|
44
|
+
exports.readStdin = readStdin;
|
|
45
|
+
exports.promptConfirmation = promptConfirmation;
|
|
46
|
+
const readline = __importStar(require("readline"));
|
|
47
|
+
const types_1 = require("./types");
|
|
48
|
+
/**
|
|
49
|
+
* Parse CLI arguments
|
|
50
|
+
*/
|
|
51
|
+
function parseArgs(argv) {
|
|
52
|
+
const args = {};
|
|
53
|
+
for (let i = 2; i < argv.length; i += 1) {
|
|
54
|
+
const token = argv[i] || '';
|
|
55
|
+
if (!token.startsWith('--')) {
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const key = token.slice(2);
|
|
59
|
+
const next = argv[i + 1] || '';
|
|
60
|
+
if (!next || next.startsWith('--')) {
|
|
61
|
+
args[key] = true;
|
|
62
|
+
continue;
|
|
63
|
+
}
|
|
64
|
+
args[key] = next;
|
|
65
|
+
i += 1;
|
|
66
|
+
}
|
|
67
|
+
return args;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Sanitize change name
|
|
71
|
+
*/
|
|
72
|
+
function toChangeName(input) {
|
|
73
|
+
return String(input || '')
|
|
74
|
+
.trim()
|
|
75
|
+
.replace(/[^a-zA-Z0-9-_]/g, '-')
|
|
76
|
+
.replace(/-+/g, '-')
|
|
77
|
+
.replace(/^-|-$/g, '');
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Parse tasks from markdown content
|
|
81
|
+
*/
|
|
82
|
+
function parseTasksMd(tasksContent) {
|
|
83
|
+
const tasks = [];
|
|
84
|
+
const lines = tasksContent.split(/\r?\n/);
|
|
85
|
+
let currentTask = null;
|
|
86
|
+
let inDependencySection = false;
|
|
87
|
+
for (const line of lines) {
|
|
88
|
+
const phaseMatch = line.match(/^###\s+Phase\s+(\d+)/i);
|
|
89
|
+
if (phaseMatch) {
|
|
90
|
+
continue;
|
|
91
|
+
}
|
|
92
|
+
const taskMatch = line.match(/^-\s+\[([ xX])\]\s+Task\s+(\d+)\.(\d+):\s+(.+)$/);
|
|
93
|
+
if (taskMatch) {
|
|
94
|
+
if (currentTask && currentTask.id) {
|
|
95
|
+
tasks.push(currentTask);
|
|
96
|
+
}
|
|
97
|
+
const phase = parseInt(taskMatch[2] || '0', 10);
|
|
98
|
+
const sequence = parseInt(taskMatch[3] || '0', 10);
|
|
99
|
+
currentTask = {
|
|
100
|
+
id: `PHASE-${phase}-${phase}-${sequence}`,
|
|
101
|
+
phase,
|
|
102
|
+
subphase: phase,
|
|
103
|
+
sequence,
|
|
104
|
+
title: taskMatch[4]?.trim() || '',
|
|
105
|
+
status: taskMatch[1]?.toLowerCase() === 'x' ? 'completed' : 'pending',
|
|
106
|
+
type: 'feat',
|
|
107
|
+
priority: 'medium',
|
|
108
|
+
dependencies: [],
|
|
109
|
+
spec: {
|
|
110
|
+
description: '',
|
|
111
|
+
files: [],
|
|
112
|
+
context: [],
|
|
113
|
+
},
|
|
114
|
+
acceptance_criteria: [],
|
|
115
|
+
};
|
|
116
|
+
inDependencySection = false;
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
if (line.includes('## 任务依赖关系') || line.includes('Dependencies')) {
|
|
120
|
+
inDependencySection = true;
|
|
121
|
+
continue;
|
|
122
|
+
}
|
|
123
|
+
if (currentTask && !inDependencySection) {
|
|
124
|
+
const descMatch = line.match(/^\s+描述:\s*(.+)$/);
|
|
125
|
+
if (descMatch) {
|
|
126
|
+
currentTask.spec.description = descMatch?.[1]?.trim() || '';
|
|
127
|
+
continue;
|
|
128
|
+
}
|
|
129
|
+
const filesMatch = line.match(/^\s+文件:\s*(.+)$/);
|
|
130
|
+
if (filesMatch) {
|
|
131
|
+
currentTask.spec.files = filesMatch?.[1]?.split(',').map((f) => f.trim()).filter(Boolean) || [];
|
|
132
|
+
continue;
|
|
133
|
+
}
|
|
134
|
+
const acceptMatch = line.match(/^\s+验收:\s*(.+)$/);
|
|
135
|
+
if (acceptMatch) {
|
|
136
|
+
currentTask.acceptance_criteria = [acceptMatch?.[1]?.trim() || ''];
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (inDependencySection && currentTask) {
|
|
141
|
+
const depMatch = line.match(/Task\s+(\d+)\.(\d+)\s+(?:依赖|depends? on)\s+Task\s+(\d+)\.(\d+)/i);
|
|
142
|
+
if (depMatch) {
|
|
143
|
+
const depPhase = parseInt(depMatch[3] || '0', 10);
|
|
144
|
+
const depSeq = parseInt(depMatch[4] || '0', 10);
|
|
145
|
+
if (depPhase <= currentTask.phase) {
|
|
146
|
+
currentTask.dependencies.push(`PHASE-${depPhase}-${depPhase}-${depSeq}`);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
if (currentTask && currentTask.id) {
|
|
152
|
+
tasks.push(currentTask);
|
|
153
|
+
}
|
|
154
|
+
return tasks;
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Parse AI input from JSON
|
|
158
|
+
*/
|
|
159
|
+
function parseAiInput(input) {
|
|
160
|
+
try {
|
|
161
|
+
const parsed = JSON.parse(input);
|
|
162
|
+
let tasks;
|
|
163
|
+
if (Array.isArray(parsed)) {
|
|
164
|
+
tasks = parsed;
|
|
165
|
+
}
|
|
166
|
+
else if (parsed && typeof parsed === 'object' && Array.isArray(parsed.tasks)) {
|
|
167
|
+
tasks = parsed.tasks;
|
|
168
|
+
}
|
|
169
|
+
else {
|
|
170
|
+
throw new Error('Invalid input format: expected array or { tasks: [] }');
|
|
171
|
+
}
|
|
172
|
+
const validTasks = [];
|
|
173
|
+
const errors = [];
|
|
174
|
+
for (let i = 0; i < tasks.length; i += 1) {
|
|
175
|
+
if (validateTask(tasks[i])) {
|
|
176
|
+
validTasks.push(tasks[i]);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
errors.push(`Task ${i}: validation failed`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
if (errors.length > 0) {
|
|
183
|
+
console.warn('[TASKS] Validation warnings:');
|
|
184
|
+
errors.forEach((err) => console.warn(` - ${err}`));
|
|
185
|
+
}
|
|
186
|
+
return validTasks;
|
|
187
|
+
}
|
|
188
|
+
catch (error) {
|
|
189
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
190
|
+
throw new Error(`Failed to parse AI input: ${errorMessage}`);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Read stdin content
|
|
195
|
+
*/
|
|
196
|
+
async function readStdin() {
|
|
197
|
+
return new Promise((resolve, reject) => {
|
|
198
|
+
const chunks = [];
|
|
199
|
+
process.stdin.setEncoding('utf8');
|
|
200
|
+
process.stdin.on('readable', () => {
|
|
201
|
+
let chunk;
|
|
202
|
+
while ((chunk = process.stdin.read()) !== null) {
|
|
203
|
+
chunks.push(chunk);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
process.stdin.on('end', () => {
|
|
207
|
+
resolve(chunks.join(''));
|
|
208
|
+
});
|
|
209
|
+
process.stdin.on('error', (error) => {
|
|
210
|
+
reject(error);
|
|
211
|
+
});
|
|
212
|
+
setTimeout(() => {
|
|
213
|
+
if (chunks.length === 0) {
|
|
214
|
+
reject(new Error('Stdin timeout: no data received'));
|
|
215
|
+
}
|
|
216
|
+
}, 5000);
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Prompt user for confirmation
|
|
221
|
+
*/
|
|
222
|
+
function promptConfirmation(message) {
|
|
223
|
+
const rl = readline.createInterface({
|
|
224
|
+
input: process.stdin,
|
|
225
|
+
output: process.stdout,
|
|
226
|
+
});
|
|
227
|
+
return new Promise((resolve) => {
|
|
228
|
+
rl.question(message, (answer) => {
|
|
229
|
+
rl.close();
|
|
230
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === '');
|
|
231
|
+
});
|
|
232
|
+
rl.on('error', () => {
|
|
233
|
+
rl.close();
|
|
234
|
+
resolve(false);
|
|
235
|
+
});
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
/**
|
|
239
|
+
* Validate a single task object
|
|
240
|
+
*/
|
|
241
|
+
function validateTask(task) {
|
|
242
|
+
if (typeof task !== 'object' || task === null) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
const t = task;
|
|
246
|
+
if (typeof t.id !== 'string' || !types_1.TASK_SCHEMA.idPattern.test(t.id)) {
|
|
247
|
+
return false;
|
|
248
|
+
}
|
|
249
|
+
if (!types_1.TASK_SCHEMA.types.includes(t.type)) {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
if (typeof t.title !== 'string' || t.title.length === 0) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
if (!types_1.TASK_SCHEMA.priorities.includes(t.priority)) {
|
|
256
|
+
return false;
|
|
257
|
+
}
|
|
258
|
+
if (typeof t.phase !== 'number' || t.phase < 1) {
|
|
259
|
+
return false;
|
|
260
|
+
}
|
|
261
|
+
if (!types_1.TASK_SCHEMA.statuses.includes(t.status)) {
|
|
262
|
+
return false;
|
|
263
|
+
}
|
|
264
|
+
if (typeof t.spec !== 'object' || t.spec === null) {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
const spec = t.spec;
|
|
268
|
+
if (typeof spec.description !== 'string' || !Array.isArray(spec.files)) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* commands/tasks-gen/templates.ts
|
|
3
|
+
* Markdown templates for task generation
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Tasks.md template
|
|
7
|
+
*/
|
|
8
|
+
export declare const TASKS_MD_TEMPLATE = "# \u4EFB\u52A1\u6E05\u5355\n\n## \u6982\u8FF0\n<!-- \u4EFB\u52A1\u6982\u8FF0 -->\n\n## \u4EFB\u52A1\u5217\u8868\n\n### Phase 1: \u57FA\u7840\u5B9E\u73B0\n\n- [ ] Task 1.1: \u4EFB\u52A1\u6807\u9898\n - \u63CF\u8FF0\uFF1A\u4EFB\u52A1\u63CF\u8FF0\n - \u6587\u4EF6\uFF1Asrc/file1.ts, src/file2.ts\n - \u9A8C\u6536\uFF1A\u9A8C\u6536\u6807\u51C6\n\n### Phase 2: \u6269\u5C55\u529F\u80FD\n\n- [ ] Task 2.1: \u4EFB\u52A1\u6807\u9898\n - \u63CF\u8FF0\uFF1A\u4EFB\u52A1\u63CF\u8FF0\n - \u6587\u4EF6\uFF1Asrc/file3.ts\n - \u9A8C\u6536\uFF1A\u9A8C\u6536\u6807\u51C6\n\n---\n\n## \u4EFB\u52A1\u4F9D\u8D56\u5173\u7CFB\n\n- Task 2.1 \u4F9D\u8D56 Task 1.1\n";
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* commands/tasks-gen/templates.ts
|
|
4
|
+
* Markdown templates for task generation
|
|
5
|
+
*/
|
|
6
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
+
exports.TASKS_MD_TEMPLATE = void 0;
|
|
8
|
+
/**
|
|
9
|
+
* Tasks.md template
|
|
10
|
+
*/
|
|
11
|
+
exports.TASKS_MD_TEMPLATE = `# 任务清单
|
|
12
|
+
|
|
13
|
+
## 概述
|
|
14
|
+
<!-- 任务概述 -->
|
|
15
|
+
|
|
16
|
+
## 任务列表
|
|
17
|
+
|
|
18
|
+
### Phase 1: 基础实现
|
|
19
|
+
|
|
20
|
+
- [ ] Task 1.1: 任务标题
|
|
21
|
+
- 描述:任务描述
|
|
22
|
+
- 文件:src/file1.ts, src/file2.ts
|
|
23
|
+
- 验收:验收标准
|
|
24
|
+
|
|
25
|
+
### Phase 2: 扩展功能
|
|
26
|
+
|
|
27
|
+
- [ ] Task 2.1: 任务标题
|
|
28
|
+
- 描述:任务描述
|
|
29
|
+
- 文件:src/file3.ts
|
|
30
|
+
- 验收:验收标准
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
## 任务依赖关系
|
|
35
|
+
|
|
36
|
+
- Task 2.1 依赖 Task 1.1
|
|
37
|
+
`;
|