@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.
Files changed (118) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +252 -0
  3. package/SKILL.md +250 -0
  4. package/assets/.harnessrc +10 -0
  5. package/assets/PHASE-task.json.example +50 -0
  6. package/assets/design.md +69 -0
  7. package/assets/hooks.json +15 -0
  8. package/assets/product-requirement.md +82 -0
  9. package/assets/schema.json +127 -0
  10. package/assets/tasks.md +26 -0
  11. package/dist/commands/analyze.d.ts +32 -0
  12. package/dist/commands/analyze.js +338 -0
  13. package/dist/commands/archive.d.ts +11 -0
  14. package/dist/commands/archive.js +53 -0
  15. package/dist/commands/design.d.ts +38 -0
  16. package/dist/commands/design.js +492 -0
  17. package/dist/commands/extract.d.ts +31 -0
  18. package/dist/commands/extract.js +477 -0
  19. package/dist/commands/init.d.ts +24 -0
  20. package/dist/commands/init.js +165 -0
  21. package/dist/commands/merge/index.d.ts +17 -0
  22. package/dist/commands/merge/index.js +322 -0
  23. package/dist/commands/merge/merger.d.ts +18 -0
  24. package/dist/commands/merge/merger.js +151 -0
  25. package/dist/commands/merge/types.d.ts +67 -0
  26. package/dist/commands/merge/types.js +6 -0
  27. package/dist/commands/merge/validators.d.ts +14 -0
  28. package/dist/commands/merge/validators.js +147 -0
  29. package/dist/commands/merge.d.ts +7 -0
  30. package/dist/commands/merge.js +15 -0
  31. package/dist/commands/start.d.ts +32 -0
  32. package/dist/commands/start.js +265 -0
  33. package/dist/commands/status.d.ts +15 -0
  34. package/dist/commands/status.js +143 -0
  35. package/dist/commands/sync.d.ts +11 -0
  36. package/dist/commands/sync.js +58 -0
  37. package/dist/commands/tasks-gen/doc-parser.d.ts +7 -0
  38. package/dist/commands/tasks-gen/doc-parser.js +259 -0
  39. package/dist/commands/tasks-gen/generators.d.ts +33 -0
  40. package/dist/commands/tasks-gen/generators.js +141 -0
  41. package/dist/commands/tasks-gen/index.d.ts +30 -0
  42. package/dist/commands/tasks-gen/index.js +345 -0
  43. package/dist/commands/tasks-gen/parsers.d.ts +29 -0
  44. package/dist/commands/tasks-gen/parsers.js +272 -0
  45. package/dist/commands/tasks-gen/templates.d.ts +8 -0
  46. package/dist/commands/tasks-gen/templates.js +37 -0
  47. package/dist/commands/tasks-gen/types.d.ts +71 -0
  48. package/dist/commands/tasks-gen/types.js +17 -0
  49. package/dist/commands/tasks-gen/validators.d.ts +14 -0
  50. package/dist/commands/tasks-gen/validators.js +54 -0
  51. package/dist/commands/tasks.d.ts +9 -0
  52. package/dist/commands/tasks.js +22 -0
  53. package/dist/commands/worktree.d.ts +28 -0
  54. package/dist/commands/worktree.js +275 -0
  55. package/dist/hooks/check-prd-exists.d.ts +20 -0
  56. package/dist/hooks/check-prd-exists.js +61 -0
  57. package/dist/hooks/check-worktree-conflict.d.ts +34 -0
  58. package/dist/hooks/check-worktree-conflict.js +107 -0
  59. package/dist/hooks/hook-runner/executor.d.ts +18 -0
  60. package/dist/hooks/hook-runner/executor.js +143 -0
  61. package/dist/hooks/hook-runner/index.d.ts +64 -0
  62. package/dist/hooks/hook-runner/index.js +220 -0
  63. package/dist/hooks/hook-runner/loader.d.ts +23 -0
  64. package/dist/hooks/hook-runner/loader.js +126 -0
  65. package/dist/hooks/hook-runner/types.d.ts +59 -0
  66. package/dist/hooks/hook-runner/types.js +6 -0
  67. package/dist/hooks/hook-runner.d.ts +9 -0
  68. package/dist/hooks/hook-runner.js +30 -0
  69. package/dist/hooks/phase-complete-detector.d.ts +35 -0
  70. package/dist/hooks/phase-complete-detector.js +203 -0
  71. package/dist/hooks/phase-gate-validator.d.ts +76 -0
  72. package/dist/hooks/phase-gate-validator.js +407 -0
  73. package/dist/hooks/save-checkpoint.d.ts +43 -0
  74. package/dist/hooks/save-checkpoint.js +144 -0
  75. package/dist/hooks/start-mcp-servers.d.ts +50 -0
  76. package/dist/hooks/start-mcp-servers.js +75 -0
  77. package/dist/hooks/stop-mcp-servers.d.ts +40 -0
  78. package/dist/hooks/stop-mcp-servers.js +58 -0
  79. package/dist/index.d.ts +29 -0
  80. package/dist/index.js +238 -0
  81. package/dist/lib/archive.d.ts +31 -0
  82. package/dist/lib/archive.js +226 -0
  83. package/dist/lib/config.d.ts +93 -0
  84. package/dist/lib/config.js +251 -0
  85. package/dist/lib/constants.d.ts +222 -0
  86. package/dist/lib/constants.js +247 -0
  87. package/dist/lib/interactive.d.ts +31 -0
  88. package/dist/lib/interactive.js +166 -0
  89. package/dist/lib/mcp-client.d.ts +156 -0
  90. package/dist/lib/mcp-client.js +370 -0
  91. package/dist/lib/state.d.ts +119 -0
  92. package/dist/lib/state.js +293 -0
  93. package/dist/slash/executor.d.ts +22 -0
  94. package/dist/slash/executor.js +259 -0
  95. package/dist/slash/index.d.ts +11 -0
  96. package/dist/slash/index.js +45 -0
  97. package/dist/slash/parser.d.ts +24 -0
  98. package/dist/slash/parser.js +101 -0
  99. package/dist/slash/registry.d.ts +22 -0
  100. package/dist/slash/registry.js +155 -0
  101. package/dist/spec/openspec-to-task/builders.d.ts +107 -0
  102. package/dist/spec/openspec-to-task/builders.js +138 -0
  103. package/dist/spec/openspec-to-task/index.d.ts +20 -0
  104. package/dist/spec/openspec-to-task/index.js +182 -0
  105. package/dist/spec/openspec-to-task/parsers.d.ts +65 -0
  106. package/dist/spec/openspec-to-task/parsers.js +232 -0
  107. package/dist/spec/openspec-to-task/types.d.ts +49 -0
  108. package/dist/spec/openspec-to-task/types.js +6 -0
  109. package/dist/spec/sync-openspec-to-task.d.ts +7 -0
  110. package/dist/spec/sync-openspec-to-task.js +21 -0
  111. package/dist/spec/sync-task-to-openspec.d.ts +27 -0
  112. package/dist/spec/sync-task-to-openspec.js +288 -0
  113. package/dist/types/ai-context.d.ts +108 -0
  114. package/dist/types/ai-context.js +9 -0
  115. package/package.json +66 -0
  116. package/references/AI-CONVERSATION-TUTORIAL.md +270 -0
  117. package/references/CLI-TUTORIAL.md +447 -0
  118. package/references/GIT-WORKTREE-SOP.md +109 -0
@@ -0,0 +1,370 @@
1
+ "use strict";
2
+ /**
3
+ * lib/mcp-client.ts
4
+ * MCP Server lifecycle management
5
+ *
6
+ * Features:
7
+ * - Start/stop MCP servers
8
+ * - Timeout control
9
+ * - State management
10
+ * - Connection info retrieval
11
+ */
12
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
13
+ if (k2 === undefined) k2 = k;
14
+ var desc = Object.getOwnPropertyDescriptor(m, k);
15
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
16
+ desc = { enumerable: true, get: function() { return m[k]; } };
17
+ }
18
+ Object.defineProperty(o, k2, desc);
19
+ }) : (function(o, m, k, k2) {
20
+ if (k2 === undefined) k2 = k;
21
+ o[k2] = m[k];
22
+ }));
23
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
24
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
25
+ }) : function(o, v) {
26
+ o["default"] = v;
27
+ });
28
+ var __importStar = (this && this.__importStar) || (function () {
29
+ var ownKeys = function(o) {
30
+ ownKeys = Object.getOwnPropertyNames || function (o) {
31
+ var ar = [];
32
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
33
+ return ar;
34
+ };
35
+ return ownKeys(o);
36
+ };
37
+ return function (mod) {
38
+ if (mod && mod.__esModule) return mod;
39
+ var result = {};
40
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
41
+ __setModuleDefault(result, mod);
42
+ return result;
43
+ };
44
+ })();
45
+ Object.defineProperty(exports, "__esModule", { value: true });
46
+ exports.startMcpServers = startMcpServers;
47
+ exports.startMcpServer = startMcpServer;
48
+ exports.stopMcpServer = stopMcpServer;
49
+ exports.stopAllMcpServers = stopAllMcpServers;
50
+ exports.getMcpStatus = getMcpStatus;
51
+ exports.getAllMcpStatus = getAllMcpStatus;
52
+ exports.getMcpConnectionInfo = getMcpConnectionInfo;
53
+ exports.startMcpServerManual = startMcpServerManual;
54
+ exports.getRunningCount = getRunningCount;
55
+ exports.cleanup = cleanup;
56
+ const child_process_1 = require("child_process");
57
+ const path = __importStar(require("path"));
58
+ /**
59
+ * Global MCP process state management
60
+ * Map<serverName, ServerState>
61
+ */
62
+ const mcpProcessState = new Map();
63
+ /**
64
+ * Start MCP Servers
65
+ * @param mcpServers - List of MCP server configurations
66
+ * @param projectRoot - Project root directory
67
+ * @returns Start result
68
+ */
69
+ function startMcpServers(mcpServers, projectRoot) {
70
+ const results = [];
71
+ for (const mcp of mcpServers) {
72
+ // Check if enabled
73
+ if (mcp.enabled === false) {
74
+ results.push({
75
+ name: mcp.name,
76
+ status: 'skipped',
77
+ reason: 'enabled is false',
78
+ });
79
+ continue;
80
+ }
81
+ // Check if auto-start is required
82
+ if (!mcp.lifecycle?.autoStart) {
83
+ results.push({
84
+ name: mcp.name,
85
+ status: 'skipped',
86
+ reason: 'autoStart is false',
87
+ });
88
+ continue;
89
+ }
90
+ try {
91
+ const result = startMcpServer(mcp, projectRoot);
92
+ results.push(result);
93
+ }
94
+ catch (error) {
95
+ results.push({
96
+ name: mcp.name,
97
+ status: 'error',
98
+ error: error.message,
99
+ stack: error.stack,
100
+ });
101
+ }
102
+ }
103
+ return {
104
+ success: results.every(r => r.status !== 'error'),
105
+ results,
106
+ };
107
+ }
108
+ /**
109
+ * Start single MCP Server
110
+ * @param mcp - MCP server configuration
111
+ * @param projectRoot - Project root directory
112
+ * @returns Start result
113
+ */
114
+ function startMcpServer(mcp, projectRoot) {
115
+ const { name, command, args, lifecycle } = mcp;
116
+ // Check if already running
117
+ if (mcpProcessState.has(name)) {
118
+ const existingState = mcpProcessState.get(name);
119
+ return {
120
+ name,
121
+ status: 'already-running',
122
+ pid: existingState.pid,
123
+ startedAt: existingState.startedAt.toISOString(),
124
+ };
125
+ }
126
+ // Resolve command and arguments
127
+ const resolvedArgs = (args || []).map(arg => {
128
+ // Handle relative paths (starting with . or ..)
129
+ if (arg.startsWith('.') || arg.startsWith('..')) {
130
+ return path.resolve(projectRoot, arg);
131
+ }
132
+ return arg;
133
+ });
134
+ console.log(`[MCP] Starting server: ${name} (${command} ${resolvedArgs.join(' ')})`);
135
+ // Start process
136
+ const proc = (0, child_process_1.spawn)(command, resolvedArgs, {
137
+ stdio: ['pipe', 'pipe', 'pipe'],
138
+ env: {
139
+ ...process.env,
140
+ HARNESS_PROJECT_ROOT: projectRoot,
141
+ HARNESS_MCP_SERVER_NAME: name,
142
+ },
143
+ cwd: projectRoot,
144
+ });
145
+ // Record state
146
+ const state = {
147
+ name,
148
+ pid: proc.pid,
149
+ process: proc,
150
+ startedAt: new Date(),
151
+ status: 'running',
152
+ timeout: lifecycle?.timeout || 300000, // Default 5 minutes
153
+ config: mcp,
154
+ };
155
+ mcpProcessState.set(name, state);
156
+ // Set timeout timer
157
+ if (state.timeout) {
158
+ state.timeoutTimer = setTimeout(() => {
159
+ console.log(`[MCP] Server ${name} timed out (${state.timeout}ms), stopping...`);
160
+ stopMcpServer(name);
161
+ }, state.timeout);
162
+ }
163
+ // Listen to stdout
164
+ proc.stdout.on('data', (data) => {
165
+ const output = data.toString().trim();
166
+ if (output) {
167
+ console.log(`[MCP:${name}] ${output}`);
168
+ }
169
+ });
170
+ // Listen to stderr
171
+ proc.stderr.on('data', (data) => {
172
+ const output = data.toString().trim();
173
+ if (output) {
174
+ console.error(`[MCP:${name}] ERROR: ${output}`);
175
+ }
176
+ });
177
+ // Listen to exit
178
+ proc.on('exit', (code, signal) => {
179
+ console.log(`[MCP:${name}] Exited with code ${code}, signal: ${signal}`);
180
+ mcpProcessState.delete(name);
181
+ });
182
+ // Listen to error
183
+ proc.on('error', (error) => {
184
+ console.error(`[MCP:${name}] Process error: ${error.message}`);
185
+ state.status = 'error';
186
+ });
187
+ return {
188
+ name,
189
+ status: 'started',
190
+ pid: proc.pid,
191
+ command: `${command} ${resolvedArgs.join(' ')}`,
192
+ startedAt: state.startedAt.toISOString(),
193
+ };
194
+ }
195
+ /**
196
+ * Stop MCP Server
197
+ * @param name - Server name
198
+ * @returns Stop result
199
+ */
200
+ function stopMcpServer(name) {
201
+ const state = mcpProcessState.get(name);
202
+ if (!state) {
203
+ return {
204
+ name,
205
+ status: 'not-running',
206
+ };
207
+ }
208
+ console.log(`[MCP] Stopping server: ${name} (pid: ${state.pid})`);
209
+ // Clear timeout timer
210
+ if (state.timeoutTimer) {
211
+ clearTimeout(state.timeoutTimer);
212
+ }
213
+ // Clear force kill timer if exists
214
+ if (state.forceKillTimer) {
215
+ clearTimeout(state.forceKillTimer);
216
+ }
217
+ try {
218
+ // Graceful shutdown (SIGTERM)
219
+ state.process.kill('SIGTERM');
220
+ state.status = 'stopping';
221
+ // Immediately delete state to prevent test leakage
222
+ // The actual process will be cleaned up by the OS
223
+ mcpProcessState.delete(name);
224
+ // Force terminate if process doesn't exit within 5 seconds
225
+ const forceKillTimer = setTimeout(() => {
226
+ try {
227
+ state.process.kill('SIGKILL');
228
+ }
229
+ catch {
230
+ // Process already exited
231
+ }
232
+ }, 5000);
233
+ // Unref the timer so it doesn't prevent process exit
234
+ forceKillTimer.unref();
235
+ return {
236
+ name,
237
+ status: 'stopping',
238
+ pid: state.pid,
239
+ };
240
+ }
241
+ catch (error) {
242
+ console.error(`[MCP] Failed to stop server ${name}: ${error.message}`);
243
+ mcpProcessState.delete(name);
244
+ return {
245
+ name,
246
+ status: 'error',
247
+ error: error.message,
248
+ };
249
+ }
250
+ }
251
+ /**
252
+ * Stop all MCP Servers
253
+ * @returns Stop result
254
+ */
255
+ function stopAllMcpServers() {
256
+ const results = [];
257
+ const names = Array.from(mcpProcessState.keys());
258
+ for (const name of names) {
259
+ results.push(stopMcpServer(name));
260
+ }
261
+ return {
262
+ success: true,
263
+ results,
264
+ stoppedCount: results.length,
265
+ };
266
+ }
267
+ /**
268
+ * Get MCP Server status
269
+ * @param name - Server name
270
+ * @returns Status info
271
+ */
272
+ function getMcpStatus(name) {
273
+ const state = mcpProcessState.get(name);
274
+ if (!state) {
275
+ return {
276
+ name,
277
+ status: 'not-running',
278
+ pid: 0,
279
+ startedAt: '',
280
+ uptime: 0,
281
+ timeout: 0,
282
+ };
283
+ }
284
+ const now = Date.now();
285
+ const startTime = state.startedAt.getTime();
286
+ return {
287
+ name,
288
+ status: state.status,
289
+ pid: state.pid,
290
+ startedAt: state.startedAt.toISOString(),
291
+ uptime: now - startTime,
292
+ timeout: state.timeout,
293
+ };
294
+ }
295
+ /**
296
+ * Get all MCP Servers status
297
+ * @returns List of status info
298
+ */
299
+ function getAllMcpStatus() {
300
+ const results = [];
301
+ for (const name of mcpProcessState.keys()) {
302
+ results.push(getMcpStatus(name));
303
+ }
304
+ return results;
305
+ }
306
+ /**
307
+ * Get MCP connection info (for injection to Agent)
308
+ * @returns List of connection info
309
+ */
310
+ function getMcpConnectionInfo() {
311
+ const connections = [];
312
+ for (const [name, state] of mcpProcessState.entries()) {
313
+ connections.push({
314
+ name,
315
+ transportType: 'stdio',
316
+ pid: state.pid,
317
+ status: state.status,
318
+ });
319
+ }
320
+ return connections;
321
+ }
322
+ /**
323
+ * Manually start MCP Server (for CLI commands)
324
+ * @param mcp - MCP server configuration
325
+ * @param projectRoot - Project root directory
326
+ * @returns Start result
327
+ */
328
+ function startMcpServerManual(mcp, projectRoot) {
329
+ // Temporarily set autoStart to true
330
+ const mcpWithAutoStart = {
331
+ ...mcp,
332
+ lifecycle: {
333
+ ...mcp.lifecycle,
334
+ autoStart: true,
335
+ },
336
+ };
337
+ return startMcpServer(mcpWithAutoStart, projectRoot);
338
+ }
339
+ /**
340
+ * Get count of running MCP Servers
341
+ * @returns Running count
342
+ */
343
+ function getRunningCount() {
344
+ return mcpProcessState.size;
345
+ }
346
+ /**
347
+ * Clean up all state (before process exit)
348
+ */
349
+ function cleanup() {
350
+ console.log('[MCP] Cleaning up all servers...');
351
+ stopAllMcpServers();
352
+ }
353
+ // Note: Library code should not register global signal handlers.
354
+ // The calling application is responsible for cleanup on process exit.
355
+ // Export cleanup() for callers to invoke manually.
356
+ exports.default = {
357
+ // Core API
358
+ startMcpServers,
359
+ stopMcpServer,
360
+ stopAllMcpServers,
361
+ // Status query
362
+ getMcpStatus,
363
+ getAllMcpStatus,
364
+ getMcpConnectionInfo,
365
+ getRunningCount,
366
+ // Manual control
367
+ startMcpServerManual,
368
+ // Utility functions
369
+ cleanup,
370
+ };
@@ -0,0 +1,119 @@
1
+ /**
2
+ * state.ts
3
+ * State management for task-flow
4
+ */
5
+ import { Config } from './config';
6
+ /**
7
+ * Worktree information interface
8
+ */
9
+ export interface WorktreeInfo {
10
+ name: string;
11
+ path?: string;
12
+ branch?: string;
13
+ status?: string;
14
+ agent?: string;
15
+ task?: string;
16
+ startedAt?: string;
17
+ lastActivity?: string;
18
+ pendingChanges?: string[];
19
+ }
20
+ /**
21
+ * Task history entry interface
22
+ */
23
+ export interface TaskHistoryEntry {
24
+ taskId: string;
25
+ title?: string;
26
+ status: string;
27
+ currentPhase?: string | null;
28
+ phaseCompleted?: string | null;
29
+ reviewConclusion?: string | null;
30
+ startedAt?: string;
31
+ completedAt?: string | null;
32
+ lastUpdatedAt?: string;
33
+ archivePath?: string;
34
+ }
35
+ /**
36
+ * State object interface
37
+ */
38
+ export interface State {
39
+ version: string;
40
+ status: string;
41
+ currentPhase: string | null;
42
+ currentTask: string | null;
43
+ currentSnapshot: string | null;
44
+ reviewConclusion: string | null;
45
+ phaseCompleted: string | null;
46
+ taskHistory: TaskHistoryEntry[];
47
+ worktrees: WorktreeInfo[];
48
+ lastGateBlockedAt: string | null;
49
+ lastGateBlockedReason: string | null;
50
+ lastGateBlockedPhase: string | null;
51
+ lastMissingValidations: string[];
52
+ startedAt: string;
53
+ updatedAt: string;
54
+ }
55
+ /**
56
+ * Create initial state object
57
+ * @returns Initial state
58
+ */
59
+ export declare function createInitialState(): State;
60
+ /**
61
+ * Load state from file
62
+ * @param config - Config object
63
+ * @returns State object
64
+ */
65
+ export declare function loadState(config?: Config): State;
66
+ /**
67
+ * Save state to file
68
+ * @param state - State object
69
+ * @param config - Config object
70
+ * @returns Success
71
+ */
72
+ export declare function saveState(state: State, config?: Config): boolean;
73
+ /**
74
+ * Update state with partial changes
75
+ * @param updates - Updates to apply
76
+ * @param config - Config object
77
+ * @returns Updated state
78
+ */
79
+ export declare function updateState(updates: Partial<State>, config?: Config): State;
80
+ /**
81
+ * Add or update worktree in state
82
+ * @param worktree - Worktree info
83
+ * @param config - Config object
84
+ * @returns Updated state
85
+ */
86
+ export declare function upsertWorktree(worktree: Partial<WorktreeInfo>, config?: Config): State;
87
+ /**
88
+ * Remove worktree from state
89
+ * @param worktreeName - Worktree name
90
+ * @param config - Config object
91
+ * @returns Updated state
92
+ */
93
+ export declare function removeWorktree(worktreeName: string, config?: Config): State;
94
+ /**
95
+ * Add task to history
96
+ * @param taskInfo - Task info
97
+ * @param config - Config object
98
+ * @returns Updated state
99
+ */
100
+ export declare function addTaskToHistory(taskInfo: Partial<TaskHistoryEntry>, config?: Config): State;
101
+ /**
102
+ * Get active worktrees
103
+ * @param config - Config object
104
+ * @returns Active worktrees
105
+ */
106
+ export declare function getActiveWorktrees(config?: Config): WorktreeInfo[];
107
+ /**
108
+ * Get worktree by task ID
109
+ * @param taskId - Task ID
110
+ * @param config - Config object
111
+ * @returns Worktree or null
112
+ */
113
+ export declare function getWorktreeByTask(taskId: string, config?: Config): WorktreeInfo | null;
114
+ /**
115
+ * Clear current task state (after merge)
116
+ * @param config - Config object
117
+ * @returns Updated state
118
+ */
119
+ export declare function clearCurrentTask(config?: Config): State;