@bicorne/task-flow 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +2 -4
  2. package/SKILL.md +2 -4
  3. package/dist/commands/analyze.js +160 -318
  4. package/dist/commands/archive.js +44 -48
  5. package/dist/commands/design.js +225 -400
  6. package/dist/commands/extract.js +174 -303
  7. package/dist/commands/init.js +103 -147
  8. package/dist/commands/merge/index.js +184 -295
  9. package/dist/commands/merge/merger.js +112 -134
  10. package/dist/commands/merge/types.js +3 -5
  11. package/dist/commands/merge/validators.js +115 -132
  12. package/dist/commands/merge.js +46 -13
  13. package/dist/commands/start.js +155 -248
  14. package/dist/commands/status.js +68 -129
  15. package/dist/commands/sync.js +37 -53
  16. package/dist/commands/tasks-gen/doc-parser.js +148 -228
  17. package/dist/commands/tasks-gen/generators.js +104 -116
  18. package/dist/commands/tasks-gen/index.js +206 -314
  19. package/dist/commands/tasks-gen/parsers.js +131 -232
  20. package/dist/commands/tasks-gen/templates.js +9 -10
  21. package/dist/commands/tasks-gen/types.js +36 -14
  22. package/dist/commands/tasks-gen/validators.js +33 -49
  23. package/dist/commands/tasks.js +58 -20
  24. package/dist/commands/worktree.js +167 -249
  25. package/dist/hooks/check-prd-exists.js +45 -55
  26. package/dist/hooks/check-worktree-conflict.js +68 -101
  27. package/dist/hooks/hook-runner/executor.js +134 -126
  28. package/dist/hooks/hook-runner/index.js +181 -196
  29. package/dist/hooks/hook-runner/loader.js +74 -113
  30. package/dist/hooks/hook-runner/types.js +3 -5
  31. package/dist/hooks/hook-runner.js +94 -28
  32. package/dist/hooks/phase-complete-detector.js +125 -191
  33. package/dist/hooks/phase-gate-validator.js +315 -376
  34. package/dist/hooks/save-checkpoint.js +87 -130
  35. package/dist/hooks/start-mcp-servers.js +50 -65
  36. package/dist/hooks/stop-mcp-servers.js +40 -49
  37. package/dist/index.js +84 -153
  38. package/dist/lib/archive.js +126 -209
  39. package/dist/lib/config.js +141 -226
  40. package/dist/lib/constants.js +155 -145
  41. package/dist/lib/interactive.js +98 -148
  42. package/dist/lib/mcp-client.js +197 -320
  43. package/dist/lib/state.js +142 -253
  44. package/dist/slash/executor.js +299 -330
  45. package/dist/slash/index.js +69 -43
  46. package/dist/slash/parser.js +84 -97
  47. package/dist/slash/registry.js +100 -88
  48. package/dist/spec/openspec-to-task/builders.js +96 -109
  49. package/dist/spec/openspec-to-task/index.js +112 -173
  50. package/dist/spec/openspec-to-task/parsers.js +148 -219
  51. package/dist/spec/openspec-to-task/types.js +3 -5
  52. package/dist/spec/sync-openspec-to-task.js +47 -19
  53. package/dist/spec/sync-task-to-openspec.js +241 -272
  54. package/dist/types/ai-context.js +3 -8
  55. package/package.json +8 -6
  56. package/references/CLI-TUTORIAL.md +4 -10
@@ -1,58 +1,42 @@
1
1
  #!/usr/bin/env node
2
- "use strict";
3
- /**
4
- * commands/sync.ts
5
- * Sync task execution status to Spec
6
- */
7
- Object.defineProperty(exports, "__esModule", { value: true });
8
- exports.main = main;
9
- const sync_task_to_openspec_1 = require("../spec/sync-task-to-openspec");
10
- function parseArgs(argv) {
11
- const args = {};
12
- for (let i = 2; i < argv.length; i += 1) {
13
- const token = argv[i] || '';
14
- if (!token.startsWith('--')) {
15
- continue;
16
- }
17
- const key = token.slice(2);
18
- const next = argv[i + 1] || '';
19
- if (!next || next.startsWith('--')) {
20
- args[key] = true;
21
- continue;
2
+ function e() {
3
+ let e = function(e) {
4
+ let t = {};
5
+ for(let s = 2; s < e.length; s += 1){
6
+ let n = e[s] || '';
7
+ if (!n.startsWith('--')) continue;
8
+ let o = n.slice(2), r = e[s + 1] || '';
9
+ if (!r || r.startsWith('--')) {
10
+ t[o] = !0;
11
+ continue;
12
+ }
13
+ t[o] = r, s += 1;
22
14
  }
23
- args[key] = next;
24
- i += 1;
25
- }
26
- return args;
27
- }
28
- /**
29
- * Check if result has executionPath property
30
- */
31
- function hasExecutionPath(result) {
32
- return !result.skipped && 'executionPath' in result;
33
- }
34
- function main() {
35
- const args = parseArgs(process.argv);
36
- const result = (0, sync_task_to_openspec_1.syncTaskToSpec)({
37
- cwd: args.cwd,
38
- taskId: args['task-id'],
15
+ return t;
16
+ }(process.argv), t = (0, o.syncTaskToSpec)({
17
+ cwd: e.cwd,
18
+ taskId: e['task-id']
39
19
  });
40
- if (result.skipped) {
41
- console.log(`[SYNC] skipped: ${result.reason}`);
42
- process.exit(0);
43
- }
44
- // Type-safe access to executionPath
45
- if (hasExecutionPath(result)) {
46
- console.log(`[SYNC] updated: ${result.executionPath}`);
47
- }
48
- else {
49
- console.log('[SYNC] task synced but no execution path available');
50
- }
51
- process.exit(0);
52
- }
53
- if (require.main === module) {
54
- main();
20
+ (t.skipped && (console.log(`[SYNC] skipped: ${t.reason}`), process.exit(0)), !t.skipped && 'executionPath' in t) ? console.log(`[SYNC] updated: ${t.executionPath}`) : console.log('[SYNC] task synced but no execution path available'), process.exit(0);
55
21
  }
56
- exports.default = {
57
- syncTaskToSpec: sync_task_to_openspec_1.syncTaskToSpec,
22
+ "use strict";
23
+ Object.defineProperty(exports, "__esModule", {
24
+ value: !0
25
+ });
26
+ var t = exports, s = {
27
+ get default () {
28
+ return r;
29
+ },
30
+ get main () {
31
+ return e;
32
+ }
33
+ };
34
+ for(var n in s)Object.defineProperty(t, n, {
35
+ enumerable: !0,
36
+ get: Object.getOwnPropertyDescriptor(s, n).get
37
+ });
38
+ let o = require("../spec/sync-task-to-openspec");
39
+ require.main === module && e();
40
+ let r = {
41
+ syncTaskToSpec: o.syncTaskToSpec
58
42
  };
@@ -1,234 +1,125 @@
1
- "use strict";
2
- /**
3
- * commands/tasks-gen/doc-parser.ts
4
- * Parse PRD and design documents to extract tasks
5
- */
6
- Object.defineProperty(exports, "__esModule", { value: true });
7
- exports.parseDocumentsToTasks = parseDocumentsToTasks;
8
- exports.extractProjectName = extractProjectName;
9
- const PRIORITY_MAP = {
10
- P0: 'high',
11
- P1: 'medium',
12
- P2: 'low',
13
- p0: 'high',
14
- p1: 'medium',
15
- p2: 'low',
16
- };
17
- function extractSection(content, sectionTitle) {
18
- const lines = content.split(/\r?\n/);
19
- const sectionStartRegex = new RegExp(`^(#{2,})\\s+${sectionTitle}`, 'i');
20
- let startIndex = -1;
21
- let endIndex = lines.length;
22
- let sectionLevel = 0;
23
- for (let i = 0; i < lines.length; i += 1) {
24
- const line = lines[i] || '';
25
- const match = line.match(sectionStartRegex);
26
- if (match) {
27
- startIndex = i;
28
- sectionLevel = match[1]?.length || 2;
1
+ function e(e, t) {
2
+ let i = e.split(/\r?\n/), n = RegExp(`^(#{2,})\\s+${t}`, 'i'), s = -1, c = i.length, r = 0;
3
+ for(let e = 0; e < i.length; e += 1){
4
+ let t = i[e] || '', l = t.match(n);
5
+ if (l) {
6
+ s = e, r = l[1]?.length || 2;
29
7
  continue;
30
8
  }
31
- if (startIndex !== -1) {
32
- const nextSectionMatch = line.match(/^(#{1,})\s+/);
33
- if (nextSectionMatch) {
34
- const nextLevel = nextSectionMatch[1]?.length || 0;
35
- if (nextLevel <= sectionLevel) {
36
- endIndex = i;
37
- break;
38
- }
9
+ if (-1 !== s) {
10
+ let i = t.match(/^(#{1,})\s+/);
11
+ if (i && (i[1]?.length || 0) <= r) {
12
+ c = e;
13
+ break;
39
14
  }
40
15
  }
41
16
  }
42
- if (startIndex === -1) {
43
- return '';
44
- }
45
- return lines.slice(startIndex, endIndex).join('\n');
46
- }
47
- function sanitizeCell(cell) {
48
- return cell
49
- .replace(/<br\s*\/?>/gi, '\n')
50
- .replace(/<[^>]+>/g, '')
51
- .trim();
17
+ return -1 === s ? '' : i.slice(s, c).join('\n');
52
18
  }
53
- function parseFeatureTable(section) {
54
- const features = [];
55
- const lines = section.split(/\r?\n/);
56
- let featureCounter = 0;
57
- let headerIndices = {};
58
- for (const line of lines) {
59
- const trimmed = line.trim();
60
- if (!trimmed.startsWith('|') || !trimmed.endsWith('|')) {
61
- continue;
62
- }
63
- if (trimmed.includes('---')) {
64
- continue;
65
- }
66
- const cells = trimmed
67
- .split('|')
68
- .map((cell) => sanitizeCell(cell.trim()))
69
- .filter((cell) => cell.length > 0);
70
- if (cells.some((cell) => cell.includes('功能名称') || cell.includes('优先级'))) {
71
- headerIndices = {};
72
- cells.forEach((cell, idx) => {
73
- const lower = cell.toLowerCase();
74
- if (lower.includes('功能名称') || lower.includes('feature') || lower.includes('名称')) {
75
- headerIndices.name = idx;
76
- }
77
- if (lower.includes('优先级') || lower.includes('priority')) {
78
- headerIndices.priority = idx;
79
- }
80
- if (lower.includes('描述') || lower.includes('description')) {
81
- headerIndices.description = idx;
82
- }
83
- if (lower.includes('验收') || lower.includes('acceptance') || lower.includes('标准')) {
84
- headerIndices.acceptanceCriteria = idx;
85
- }
86
- if (lower.includes('编号') || lower.includes('id') || lower.includes('#')) {
87
- headerIndices.id = idx;
88
- }
89
- });
90
- continue;
91
- }
92
- if (cells.length >= 2) {
93
- featureCounter += 1;
94
- const nameIdx = headerIndices.name ?? 0;
95
- const priorityIdx = headerIndices.priority ?? (nameIdx + 1);
96
- const descIdx = headerIndices.description ?? (priorityIdx + 1);
97
- const acIdx = headerIndices.acceptanceCriteria ?? (descIdx + 1);
98
- const feature = {
99
- id: headerIndices.id !== undefined ? (cells[headerIndices.id] ?? `F-${featureCounter}`) : `F-${featureCounter}`,
100
- name: cells[nameIdx] ?? `Feature ${featureCounter}`,
101
- priority: cells[priorityIdx] ?? 'medium',
102
- description: cells[descIdx] ?? '',
103
- acceptanceCriteria: cells[acIdx] ?? '',
104
- };
105
- features.push(feature);
106
- }
107
- }
108
- return features;
109
- }
110
- function parseDesignDocument(designContent) {
111
- const design = {
19
+ function t(t, i, n) {
20
+ let s, c, l, a, u = [], p = i ? (s = {
112
21
  modules: [],
113
22
  apis: [],
114
- dataModels: [],
115
- };
116
- const archSection = extractSection(designContent, '架构设计');
117
- const moduleMatches = archSection.match(/[-*]\s+(.+)/g);
118
- if (moduleMatches) {
119
- design.modules = moduleMatches.map((m) => m.replace(/^[-*]\s+/, '').trim());
120
- }
121
- const apiSection = extractSection(designContent, '接口设计');
122
- const apiMatches = apiSection.match(/[-*]\s+(.+)/g);
123
- if (apiMatches) {
124
- design.apis = apiMatches.map((m) => m.replace(/^[-*]\s+/, '').trim());
125
- }
126
- const dataSection = extractSection(designContent, '数据模型');
127
- const dataMatches = dataSection.match(/[-*]\s+(.+)/g);
128
- if (dataMatches) {
129
- design.dataModels = dataMatches.map((m) => m.replace(/^[-*]\s+/, '').trim());
130
- }
131
- return design;
132
- }
133
- function inferPhase(priority) {
134
- const normalizedPriority = PRIORITY_MAP[priority] || priority;
135
- switch (normalizedPriority) {
136
- case 'high':
137
- return 2;
138
- case 'medium':
139
- return 3;
140
- case 'low':
141
- return 4;
142
- default:
143
- return 3;
144
- }
145
- }
146
- function inferFiles(feature, _design) {
147
- const files = [];
148
- const featureLower = (feature.name + ' ' + feature.description).toLowerCase();
149
- if (featureLower.includes('api') || featureLower.includes('接口')) {
150
- files.push('src/api/');
151
- }
152
- if (featureLower.includes('数据') || featureLower.includes('database') || featureLower.includes('model')) {
153
- files.push('src/models/');
154
- }
155
- if (featureLower.includes('页面') || featureLower.includes('page') || featureLower.includes('ui')) {
156
- files.push('src/pages/');
157
- }
158
- if (featureLower.includes('组件') || featureLower.includes('component')) {
159
- files.push('src/components/');
160
- }
161
- if (featureLower.includes('工具') || featureLower.includes('util') || featureLower.includes('helper')) {
162
- files.push('src/utils/');
163
- }
164
- if (files.length === 0) {
165
- files.push('src/');
166
- }
167
- return files;
168
- }
169
- function parseDocumentsToTasks(prdContent, designContent, _changeName) {
170
- const tasks = [];
171
- const design = designContent ? parseDesignDocument(designContent) : { modules: [], apis: [], dataModels: [] };
172
- const hasArchitectureTask = design.modules.length > 0 || designContent;
173
- if (hasArchitectureTask) {
174
- tasks.push({
175
- id: 'PHASE-1-1-1',
176
- phase: 1,
177
- subphase: 1,
178
- sequence: 1,
179
- title: '搭建基础架构',
180
- type: 'feat',
181
- priority: 'high',
182
- status: 'pending',
183
- dependencies: [],
184
- spec: {
185
- description: '根据技术规格搭建项目基础架构',
186
- files: ['src/'],
187
- context: design.modules,
188
- },
189
- acceptance_criteria: ['架构符合设计文档', '模块划分清晰'],
190
- });
191
- }
192
- const featureSection = extractSection(prdContent, '(详细功能需求|功能需求)');
193
- const features = parseFeatureTable(featureSection);
194
- const phaseTaskCount = {};
195
- for (const feature of features) {
196
- const phase = inferPhase(feature.priority);
197
- if (!phaseTaskCount[phase]) {
198
- phaseTaskCount[phase] = 1;
199
- }
200
- else {
201
- phaseTaskCount[phase] += 1;
23
+ dataModels: []
24
+ }, (c = e(i, '架构设计').match(/[-*]\s+(.+)/g)) && (s.modules = c.map((e)=>e.replace(/^[-*]\s+/, '').trim())), (l = e(i, '接口设计').match(/[-*]\s+(.+)/g)) && (s.apis = l.map((e)=>e.replace(/^[-*]\s+/, '').trim())), (a = e(i, '数据模型').match(/[-*]\s+(.+)/g)) && (s.dataModels = a.map((e)=>e.replace(/^[-*]\s+/, '').trim())), s) : {
25
+ modules: [],
26
+ apis: [],
27
+ dataModels: []
28
+ }, d = p.modules.length > 0 || i;
29
+ d && u.push({
30
+ id: 'PHASE-1-1-1',
31
+ phase: 1,
32
+ subphase: 1,
33
+ sequence: 1,
34
+ title: '搭建基础架构',
35
+ type: 'feat',
36
+ priority: 'high',
37
+ status: 'pending',
38
+ dependencies: [],
39
+ spec: {
40
+ description: '根据技术规格搭建项目基础架构',
41
+ files: [
42
+ 'src/'
43
+ ],
44
+ context: p.modules
45
+ },
46
+ acceptance_criteria: [
47
+ '架构符合设计文档',
48
+ '模块划分清晰'
49
+ ]
50
+ });
51
+ let o = function(e) {
52
+ let t = [], i = e.split(/\r?\n/), n = 0, s = {};
53
+ for (let e of i){
54
+ let i = e.trim();
55
+ if (!i.startsWith('|') || !i.endsWith('|') || i.includes('---')) continue;
56
+ let c = i.split('|').map((e)=>e.trim().replace(/<br\s*\/?>/gi, '\n').replace(/<[^>]+>/g, '').trim()).filter((e)=>e.length > 0);
57
+ if (c.some((e)=>e.includes('功能名称') || e.includes('优先级'))) {
58
+ s = {}, c.forEach((e, t)=>{
59
+ let i = e.toLowerCase();
60
+ (i.includes('功能名称') || i.includes('feature') || i.includes('名称')) && (s.name = t), (i.includes('优先级') || i.includes('priority')) && (s.priority = t), (i.includes('描述') || i.includes("description")) && (s.description = t), (i.includes('验收') || i.includes('acceptance') || i.includes('标准')) && (s.acceptanceCriteria = t), (i.includes('编号') || i.includes('id') || i.includes('#')) && (s.id = t);
61
+ });
62
+ continue;
63
+ }
64
+ if (c.length >= 2) {
65
+ n += 1;
66
+ let e = s.name ?? 0, i = s.priority ?? e + 1, r = s.description ?? i + 1, l = s.acceptanceCriteria ?? r + 1, a = {
67
+ id: void 0 !== s.id ? c[s.id] ?? `F-${n}` : `F-${n}`,
68
+ name: c[e] ?? `Feature ${n}`,
69
+ priority: c[i] ?? 'medium',
70
+ description: c[r] ?? '',
71
+ acceptanceCriteria: c[l] ?? ''
72
+ };
73
+ t.push(a);
74
+ }
202
75
  }
203
- const taskId = `PHASE-${phase}-${phase}-${phaseTaskCount[phase]}`;
204
- const files = inferFiles(feature, design);
205
- const priority = PRIORITY_MAP[feature.priority] || 'medium';
206
- const task = {
207
- id: taskId,
208
- phase,
209
- subphase: phase,
210
- sequence: phaseTaskCount[phase],
211
- title: feature.name,
76
+ return t;
77
+ }(e(t, '(详细功能需求|功能需求)')), m = {};
78
+ for (let e of o){
79
+ let t = function(e) {
80
+ switch(r[e] || e){
81
+ case 'high':
82
+ return 2;
83
+ case 'medium':
84
+ default:
85
+ return 3;
86
+ case 'low':
87
+ return 4;
88
+ }
89
+ }(e.priority);
90
+ m[t] ? m[t] += 1 : m[t] = 1;
91
+ let i = `PHASE-${t}-${t}-${m[t]}`, n = function(e) {
92
+ let t = [], i = (e.name + ' ' + e.description).toLowerCase();
93
+ return (i.includes('api') || i.includes('接口')) && t.push('src/api/'), (i.includes('数据') || i.includes('database') || i.includes('model')) && t.push('src/models/'), (i.includes('页面') || i.includes('page') || i.includes('ui')) && t.push('src/pages/'), (i.includes('组件') || i.includes('component')) && t.push('src/components/'), (i.includes('工具') || i.includes('util') || i.includes('helper')) && t.push('src/utils/'), 0 === t.length && t.push('src/'), t;
94
+ }(e), s = r[e.priority] || 'medium', c = {
95
+ id: i,
96
+ phase: t,
97
+ subphase: t,
98
+ sequence: m[t],
99
+ title: e.name,
212
100
  type: 'feat',
213
- priority: priority,
101
+ priority: s,
214
102
  status: 'pending',
215
- dependencies: hasArchitectureTask ? ['PHASE-1-1-1'] : [],
103
+ dependencies: d ? [
104
+ 'PHASE-1-1-1'
105
+ ] : [],
216
106
  spec: {
217
- description: feature.description || feature.name,
218
- files,
219
- context: [],
107
+ description: e.description || e.name,
108
+ files: n,
109
+ context: []
220
110
  },
221
- acceptance_criteria: feature.acceptanceCriteria
222
- ? feature.acceptanceCriteria.split(/\n/).filter((s) => s.length > 0)
223
- : ['功能正常工作', '测试通过'],
111
+ acceptance_criteria: e.acceptanceCriteria ? e.acceptanceCriteria.split(/\n/).filter((e)=>e.length > 0) : [
112
+ '功能正常工作',
113
+ '测试通过'
114
+ ]
224
115
  };
225
- tasks.push(task);
116
+ u.push(c);
226
117
  }
227
- const nonFuncSection = extractSection(prdContent, '非功能性需求');
228
- if (nonFuncSection && nonFuncSection.length > 50) {
229
- const lastTaskId = tasks.length > 0 ? tasks[tasks.length - 1]?.id : undefined;
230
- tasks.push({
231
- id: `PHASE-5-5-1`,
118
+ let h = e(t, '非功能性需求');
119
+ if (h && h.length > 50) {
120
+ let e = u.length > 0 ? u[u.length - 1]?.id : void 0;
121
+ u.push({
122
+ id: "PHASE-5-5-1",
232
123
  phase: 5,
233
124
  subphase: 5,
234
125
  sequence: 1,
@@ -236,24 +127,53 @@ function parseDocumentsToTasks(prdContent, designContent, _changeName) {
236
127
  type: 'feat',
237
128
  priority: 'medium',
238
129
  status: 'pending',
239
- dependencies: lastTaskId ? [lastTaskId] : [],
130
+ dependencies: e ? [
131
+ e
132
+ ] : [],
240
133
  spec: {
241
134
  description: '实现性能、安全、可用性等非功能性需求',
242
- files: ['src/'],
243
- context: [],
135
+ files: [
136
+ 'src/'
137
+ ],
138
+ context: []
244
139
  },
245
- acceptance_criteria: ['性能达标', '安全检查通过', '可用性要求满足'],
140
+ acceptance_criteria: [
141
+ '性能达标',
142
+ '安全检查通过',
143
+ '可用性要求满足'
144
+ ]
246
145
  });
247
146
  }
248
- return tasks;
147
+ return u;
249
148
  }
250
- function extractProjectName(prdContent) {
251
- const lines = prdContent.split(/\r?\n/);
252
- for (const line of lines) {
253
- const headingMatch = line.match(/^#\s+(.+)$/);
254
- if (headingMatch) {
255
- return headingMatch[1] || 'Unknown Project';
256
- }
149
+ function i(e) {
150
+ for (let t of e.split(/\r?\n/)){
151
+ let e = t.match(/^#\s+(.+)$/);
152
+ if (e) return e[1] || 'Unknown Project';
257
153
  }
258
154
  return 'Unknown Project';
259
155
  }
156
+ "use strict";
157
+ Object.defineProperty(exports, "__esModule", {
158
+ value: !0
159
+ });
160
+ var n = exports, s = {
161
+ get extractProjectName () {
162
+ return i;
163
+ },
164
+ get parseDocumentsToTasks () {
165
+ return t;
166
+ }
167
+ };
168
+ for(var c in s)Object.defineProperty(n, c, {
169
+ enumerable: !0,
170
+ get: Object.getOwnPropertyDescriptor(s, c).get
171
+ });
172
+ let r = {
173
+ P0: 'high',
174
+ P1: 'medium',
175
+ P2: 'low',
176
+ p0: 'high',
177
+ p1: 'medium',
178
+ p2: 'low'
179
+ };