@e0ipso/ai-task-manager 1.26.2 → 1.26.4

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e0ipso/ai-task-manager",
3
- "version": "1.26.2",
3
+ "version": "1.26.4",
4
4
  "description": "Task management for AI coding assistants",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -9,349 +9,261 @@
9
9
 
10
10
  const fs = require('fs-extra');
11
11
  const path = require('path');
12
- const { execSync } = require('child_process');
12
+ const {
13
+ resolvePlan,
14
+ parseFrontmatter
15
+ } = require('./shared-utils.cjs');
13
16
 
14
17
  // Chalk instance - loaded dynamically to handle ESM module
15
18
  let chalkInstance = null;
16
19
 
17
20
  // Initialize chalk instance dynamically
18
- async function initChalk() {
19
- if (chalkInstance) return chalkInstance;
20
-
21
- try {
22
- const { default: chalk } = await import('chalk');
23
- chalkInstance = chalk;
24
- } catch (_error) {
25
- // Chalk not available, will fall back to plain console output
26
- chalkInstance = null;
27
- }
28
-
29
- return chalkInstance;
21
+ async function _initChalk() {
22
+ if (chalkInstance) return chalkInstance;
23
+
24
+ try {
25
+ const {
26
+ default: chalk
27
+ } = await import('chalk');
28
+ chalkInstance = chalk;
29
+ } catch (_error) {
30
+ // Chalk not available, will fall back to plain console output
31
+ chalkInstance = null;
32
+ }
33
+
34
+ return chalkInstance;
30
35
  }
31
36
 
32
37
  // Color functions for output
33
- const printError = (message, chalk) => {
34
- const formattedMessage = chalk?.red(`ERROR: ${message}`) || `ERROR: ${message}`;
35
- console.error(formattedMessage);
38
+ const _printError = (message, chalk) => {
39
+ const formattedMessage = chalk?.red(`ERROR: ${message}`) || `ERROR: ${message}`;
40
+ console.error(formattedMessage);
36
41
  };
37
42
 
38
- const printSuccess = (message, chalk) => {
39
- const formattedMessage = chalk?.green(`✓ ${message}`) || `✓ ${message}`;
40
- console.log(formattedMessage);
43
+ const _printSuccess = (message, chalk) => {
44
+ const formattedMessage = chalk?.green(`✓ ${message}`) || `✓ ${message}`;
45
+ console.log(formattedMessage);
41
46
  };
42
47
 
43
- const printWarning = (message, chalk) => {
44
- const formattedMessage = chalk?.yellow(`⚠ ${message}`) || `⚠ ${message}`;
45
- console.log(formattedMessage);
48
+ const _printWarning = (message, chalk) => {
49
+ const formattedMessage = chalk?.yellow(`⚠ ${message}`) || `⚠ ${message}`;
50
+ console.log(formattedMessage);
46
51
  };
47
52
 
48
- const printInfo = (message) => {
49
- console.log(message);
50
- };
51
-
52
- // Function to find plan directory
53
- const findPlanDirectory = (planId) => {
54
- const searchLocations = [
55
- '.ai/task-manager/plans',
56
- '.ai/task-manager/archive'
57
- ];
58
-
59
- // Generate ID variations to try (exact, padded, unpadded)
60
- const idVariations = [
61
- planId, // Try exact match first
62
- planId.padStart(2, '0'), // Try padded version (3 → 03)
63
- planId.replace(/^0+/, '') || '0' // Try unpadded version (03 → 3)
64
- ];
65
-
66
- // Remove duplicates from variations array
67
- const uniqueVariations = [...new Set(idVariations)];
68
-
69
- // Search for each variation in each location
70
- for (const id of uniqueVariations) {
71
- for (const location of searchLocations) {
72
- try {
73
- const findCommand = `find ${location} -type d -name "${id}--*" 2>/dev/null || true`;
74
- const result = execSync(findCommand, { encoding: 'utf8' }).trim();
75
- const directories = result.split('\n').filter(dir => dir.length > 0);
76
-
77
- if (directories.length > 0) {
78
- return directories[0]; // Early return on first match
79
- }
80
- } catch (error) {
81
- // Continue trying other variations/locations
82
- continue;
83
- }
84
- }
85
- }
86
-
87
- return null; // No matches found
53
+ const _printInfo = (message) => {
54
+ console.log(message);
88
55
  };
89
56
 
90
57
  // Function to find task file with padded/unpadded ID handling
91
- const findTaskFile = (planDir, taskId) => {
92
- const taskDir = path.join(planDir, 'tasks');
93
-
94
- if (!fs.existsSync(taskDir)) {
95
- return null;
96
- }
97
-
98
- // Try exact match first
99
- let pattern = `${taskId}--*.md`;
100
- let files = fs.readdirSync(taskDir).filter(file => {
101
- const regex = new RegExp(`^${taskId}--.*\\.md$`);
102
- return regex.test(file);
103
- });
104
-
105
- if (files.length > 0) {
106
- return path.join(taskDir, files[0]);
107
- }
108
-
109
- // Try with zero-padding if direct match fails
110
- const paddedTaskId = taskId.padStart(2, '0');
111
- if (paddedTaskId !== taskId) {
112
- pattern = `${paddedTaskId}--*.md`;
113
- files = fs.readdirSync(taskDir).filter(file => {
114
- const regex = new RegExp(`^${paddedTaskId}--.*\\.md$`);
115
- return regex.test(file);
116
- });
117
-
118
- if (files.length > 0) {
119
- return path.join(taskDir, files[0]);
120
- }
121
- }
122
-
123
- // Try removing potential zero-padding from taskId
124
- const unpaddedTaskId = taskId.replace(/^0+/, '') || '0';
125
- if (unpaddedTaskId !== taskId) {
126
- pattern = `${unpaddedTaskId}--*.md`;
127
- files = fs.readdirSync(taskDir).filter(file => {
128
- const regex = new RegExp(`^${unpaddedTaskId}--.*\\.md$`);
129
- return regex.test(file);
130
- });
131
-
132
- if (files.length > 0) {
133
- return path.join(taskDir, files[0]);
134
- }
135
-
136
- // Try with zero-padding of unpadded version
137
- const repaddedTaskId = unpaddedTaskId.padStart(2, '0');
138
- pattern = `${repaddedTaskId}--*.md`;
139
- files = fs.readdirSync(taskDir).filter(file => {
140
- const regex = new RegExp(`^${repaddedTaskId}--.*\\.md$`);
141
- return regex.test(file);
142
- });
143
-
144
- if (files.length > 0) {
145
- return path.join(taskDir, files[0]);
146
- }
147
- }
58
+ const _findTaskFile = (planDir, taskId) => {
59
+ const taskDir = path.join(planDir, 'tasks');
148
60
 
61
+ if (!fs.existsSync(taskDir)) {
62
+ return null;
63
+ }
64
+
65
+ const variations = [
66
+ taskId,
67
+ taskId.padStart(2, '0'),
68
+ taskId.replace(/^0+/, '') || '0'
69
+ ];
70
+
71
+ const uniqueVariations = [...new Set(variations)];
72
+
73
+ try {
74
+ const files = fs.readdirSync(taskDir);
75
+ const found = uniqueVariations.reduce((acc, v) => {
76
+ if (acc) return acc;
77
+ const match = files.find(f => f.startsWith(`${v}--`) && f.endsWith('.md'));
78
+ return match ? path.join(taskDir, match) : null;
79
+ }, null);
80
+ return found;
81
+ } catch (err) {
149
82
  return null;
83
+ }
150
84
  };
151
85
 
152
- // Function to parse YAML frontmatter
153
- const parseFrontmatter = (content) => {
154
- const lines = content.split('\n');
155
- let inFrontmatter = false;
156
- let frontmatterEnd = false;
157
- let delimiterCount = 0;
158
- const frontmatterLines = [];
159
-
160
- for (const line of lines) {
161
- if (line.trim() === '---') {
162
- delimiterCount++;
163
- if (delimiterCount === 1) {
164
- inFrontmatter = true;
165
- continue;
166
- } else if (delimiterCount === 2) {
167
- frontmatterEnd = true;
168
- break;
169
- }
170
- }
171
-
172
- if (inFrontmatter && !frontmatterEnd) {
173
- frontmatterLines.push(line);
174
- }
86
+
87
+ // Function to extract dependencies from frontmatter
88
+ const _extractDependencies = (frontmatter) => {
89
+ const lines = frontmatter.split('\n');
90
+ const dependencies = [];
91
+ let inDependenciesSection = false;
92
+
93
+ for (let i = 0; i < lines.length; i++) {
94
+ const line = lines[i];
95
+
96
+ // Check for dependencies line
97
+ if (line.match(/^dependencies:/)) {
98
+ inDependenciesSection = true;
99
+
100
+ // Check if dependencies are on the same line (array syntax)
101
+ const arrayMatch = line.match(/\[(.*)\]/);
102
+ if (arrayMatch) {
103
+ const deps = arrayMatch[1]
104
+ .split(',')
105
+ .map(dep => dep.trim().replace(/['"]/g, ''))
106
+ .filter(dep => dep.length > 0);
107
+ dependencies.push(...deps);
108
+ inDependenciesSection = false;
109
+ }
110
+ continue;
175
111
  }
176
112
 
177
- return frontmatterLines.join('\n');
178
- };
113
+ // If we're in dependencies section and hit a non-indented line that's not a list item, exit
114
+ if (inDependenciesSection && line.match(/^[^ ]/) && !line.match(/^[ \t]*-/)) {
115
+ inDependenciesSection = false;
116
+ }
179
117
 
180
- // Function to extract dependencies from frontmatter
181
- const extractDependencies = (frontmatter) => {
182
- const lines = frontmatter.split('\n');
183
- const dependencies = [];
184
- let inDependenciesSection = false;
185
-
186
- for (let i = 0; i < lines.length; i++) {
187
- const line = lines[i];
188
-
189
- // Check for dependencies line
190
- if (line.match(/^dependencies:/)) {
191
- inDependenciesSection = true;
192
-
193
- // Check if dependencies are on the same line (array syntax)
194
- const arrayMatch = line.match(/\[(.*)\]/);
195
- if (arrayMatch) {
196
- const deps = arrayMatch[1]
197
- .split(',')
198
- .map(dep => dep.trim().replace(/['"]/g, ''))
199
- .filter(dep => dep.length > 0);
200
- dependencies.push(...deps);
201
- inDependenciesSection = false;
202
- }
203
- continue;
204
- }
205
-
206
- // If we're in dependencies section and hit a non-indented line that's not a list item, exit
207
- if (inDependenciesSection && line.match(/^[^ ]/) && !line.match(/^[ \t]*-/)) {
208
- inDependenciesSection = false;
209
- }
210
-
211
- // Parse list format dependencies
212
- if (inDependenciesSection && line.match(/^[ \t]*-/)) {
213
- const dep = line.replace(/^[ \t]*-[ \t]*/, '').replace(/[ \t]*$/, '').replace(/['"]/g, '');
214
- if (dep.length > 0) {
215
- dependencies.push(dep);
216
- }
217
- }
118
+ // Parse list format dependencies
119
+ if (inDependenciesSection && line.match(/^[ \t]*-/)) {
120
+ const dep = line.replace(/^[ \t]*-[ \t]*/, '').replace(/[ \t]*$/, '').replace(/['"]/g, '');
121
+ if (dep.length > 0) {
122
+ dependencies.push(dep);
123
+ }
218
124
  }
125
+ }
219
126
 
220
- return dependencies;
127
+ return dependencies;
221
128
  };
222
129
 
223
130
  // Function to extract status from frontmatter
224
- const extractStatus = (frontmatter) => {
225
- const lines = frontmatter.split('\n');
131
+ const _extractStatus = (frontmatter) => {
132
+ const lines = frontmatter.split('\n');
226
133
 
227
- for (const line of lines) {
228
- if (line.match(/^status:/)) {
229
- return line.replace(/^status:[ \t]*/, '').replace(/^["']/, '').replace(/["']$/, '').trim();
230
- }
134
+ for (const line of lines) {
135
+ if (line.match(/^status:/)) {
136
+ return line.replace(/^status:[ \t]*/, '').replace(/^["']/, '').replace(/["']$/, '').trim();
231
137
  }
138
+ }
232
139
 
233
- return null;
140
+ return null;
234
141
  };
235
142
 
236
143
  // Main function
237
- const main = async () => {
238
- // Initialize chalk
239
- const chalk = await initChalk();
240
-
241
- // Check arguments
242
- if (process.argv.length !== 4) {
243
- printError('Invalid number of arguments', chalk);
244
- console.log('Usage: node check-task-dependencies.cjs <plan-id> <task-id>');
245
- console.log('Example: node check-task-dependencies.cjs 16 03');
246
- process.exit(1);
247
- }
248
-
249
- const planId = process.argv[2];
250
- const taskId = process.argv[3];
251
-
252
- // Find the plan directory
253
- const planDir = findPlanDirectory(planId);
254
-
255
- if (!planDir) {
256
- printError(`Plan with ID ${planId} not found`, chalk);
257
- process.exit(1);
258
- }
259
-
260
- printInfo(`Found plan directory: ${planDir}`);
261
-
262
- // Find task file
263
- const taskFile = findTaskFile(planDir, taskId);
264
-
265
- if (!taskFile || !fs.existsSync(taskFile)) {
266
- printError(`Task with ID ${taskId} not found in plan ${planId}`, chalk);
267
- process.exit(1);
268
- }
269
-
270
- printInfo(`Checking task: ${path.basename(taskFile)}`);
271
- console.log('');
272
-
273
- // Read and parse task file
274
- const taskContent = fs.readFileSync(taskFile, 'utf8');
275
- const frontmatter = parseFrontmatter(taskContent);
276
- const dependencies = extractDependencies(frontmatter);
277
-
278
- // Check if there are any dependencies
279
- if (dependencies.length === 0) {
280
- printSuccess('Task has no dependencies - ready to execute!', chalk);
281
- process.exit(0);
144
+ const _main = async (startPath = process.cwd()) => {
145
+ // Initialize chalk
146
+ const chalk = await _initChalk();
147
+
148
+ // Check arguments
149
+ if (process.argv.length !== 4) {
150
+ _printError('Invalid number of arguments', chalk);
151
+ console.log('Usage: node check-task-dependencies.cjs <plan-id-or-path> <task-id>');
152
+ console.log('Example: node check-task-dependencies.cjs 16 03');
153
+ process.exit(1);
154
+ }
155
+
156
+ const inputId = process.argv[2];
157
+ const taskId = process.argv[3];
158
+
159
+ const resolved = resolvePlan(inputId, startPath);
160
+
161
+ if (!resolved) {
162
+ _printError(`Plan "${inputId}" not found or invalid`, chalk);
163
+ process.exit(1);
164
+ }
165
+
166
+ const {
167
+ planDir,
168
+ planId
169
+ } = resolved;
170
+ _printInfo(`Found plan directory: ${planDir}`);
171
+
172
+ // Find task file
173
+ const taskFile = _findTaskFile(planDir, taskId);
174
+
175
+ if (!taskFile || !fs.existsSync(taskFile)) {
176
+ _printError(`Task with ID ${taskId} not found in plan ${planId}`, chalk);
177
+ process.exit(1);
178
+ }
179
+
180
+ _printInfo(`Checking task: ${path.basename(taskFile)}`);
181
+ console.log('');
182
+
183
+ // Read and parse task file
184
+ const taskContent = fs.readFileSync(taskFile, 'utf8');
185
+ const frontmatter = parseFrontmatter(taskContent);
186
+ const dependencies = _extractDependencies(frontmatter);
187
+
188
+ // Check if there are any dependencies
189
+ if (dependencies.length === 0) {
190
+ _printSuccess('Task has no dependencies - ready to execute!', chalk);
191
+ process.exit(0);
192
+ }
193
+
194
+ // Display dependencies
195
+ _printInfo('Task dependencies found:');
196
+ dependencies.forEach(dep => {
197
+ console.log(` - Task ${dep}`);
198
+ });
199
+ console.log('');
200
+
201
+ // Check each dependency
202
+ let allResolved = true;
203
+ let unresolvedDeps = [];
204
+ let resolvedCount = 0;
205
+ const totalDeps = dependencies.length;
206
+
207
+ _printInfo('Checking dependency status...');
208
+ console.log('');
209
+
210
+ for (const depId of dependencies) {
211
+ // Find dependency task file
212
+ const depFile = _findTaskFile(planDir, depId);
213
+
214
+ if (!depFile || !fs.existsSync(depFile)) {
215
+ _printError(`Dependency task ${depId} not found`, chalk);
216
+ allResolved = false;
217
+ unresolvedDeps.push(`${depId} (not found)`);
218
+ continue;
282
219
  }
283
220
 
284
- // Display dependencies
285
- printInfo('Task dependencies found:');
286
- dependencies.forEach(dep => {
287
- console.log(` - Task ${dep}`);
288
- });
289
- console.log('');
290
-
291
- // Check each dependency
292
- let allResolved = true;
293
- let unresolvedDeps = [];
294
- let resolvedCount = 0;
295
- const totalDeps = dependencies.length;
296
-
297
- printInfo('Checking dependency status...');
298
- console.log('');
299
-
300
- for (const depId of dependencies) {
301
- // Find dependency task file
302
- const depFile = findTaskFile(planDir, depId);
303
-
304
- if (!depFile || !fs.existsSync(depFile)) {
305
- printError(`Dependency task ${depId} not found`, chalk);
306
- allResolved = false;
307
- unresolvedDeps.push(`${depId} (not found)`);
308
- continue;
309
- }
310
-
311
- // Extract status from dependency task
312
- const depContent = fs.readFileSync(depFile, 'utf8');
313
- const depFrontmatter = parseFrontmatter(depContent);
314
- const status = extractStatus(depFrontmatter);
315
-
316
- // Check if status is completed
317
- if (status === 'completed') {
318
- printSuccess(`Task ${depId} - Status: completed ✓`, chalk);
319
- resolvedCount++;
320
- } else {
321
- printWarning(`Task ${depId} - Status: ${status || 'unknown'} ✗`, chalk);
322
- allResolved = false;
323
- unresolvedDeps.push(`${depId} (${status || 'unknown'})`);
324
- }
325
- }
221
+ // Extract status from dependency task
222
+ const depContent = fs.readFileSync(depFile, 'utf8');
223
+ const depFrontmatter = parseFrontmatter(depContent);
224
+ const status = _extractStatus(depFrontmatter);
326
225
 
327
- console.log('');
328
- printInfo('=========================================');
329
- printInfo('Dependency Check Summary');
330
- printInfo('=========================================');
331
- printInfo(`Total dependencies: ${totalDeps}`);
332
- printInfo(`Resolved: ${resolvedCount}`);
333
- printInfo(`Unresolved: ${totalDeps - resolvedCount}`);
334
- console.log('');
335
-
336
- if (allResolved) {
337
- printSuccess(`All dependencies are resolved! Task ${taskId} is ready to execute.`, chalk);
338
- process.exit(0);
226
+ // Check if status is completed
227
+ if (status === 'completed') {
228
+ _printSuccess(`Task ${depId} - Status: completed ✓`, chalk);
229
+ resolvedCount++;
339
230
  } else {
340
- printError(`Task ${taskId} has unresolved dependencies:`, chalk);
341
- unresolvedDeps.forEach(dep => {
342
- console.log(dep);
343
- });
344
- printInfo('Please complete the dependencies before executing this task.');
345
- process.exit(1);
231
+ _printWarning(`Task ${depId} - Status: ${status || 'unknown'} ✗`, chalk);
232
+ allResolved = false;
233
+ unresolvedDeps.push(`${depId} (${status || 'unknown'})`);
346
234
  }
235
+ }
236
+
237
+ console.log('');
238
+ _printInfo('=========================================');
239
+ _printInfo('Dependency Check Summary');
240
+ _printInfo('=========================================');
241
+ _printInfo(`Total dependencies: ${totalDeps}`);
242
+ _printInfo(`Resolved: ${resolvedCount}`);
243
+ _printInfo(`Unresolved: ${totalDeps - resolvedCount}`);
244
+ console.log('');
245
+
246
+ if (allResolved) {
247
+ _printSuccess(`All dependencies are resolved! Task ${taskId} is ready to execute.`, chalk);
248
+ process.exit(0);
249
+ } else {
250
+ _printError(`Task ${taskId} has unresolved dependencies:`, chalk);
251
+ unresolvedDeps.forEach(dep => {
252
+ console.log(dep);
253
+ });
254
+ _printInfo('Please complete the dependencies before executing this task.');
255
+ process.exit(1);
256
+ }
347
257
  };
348
258
 
349
259
  // Run the script
350
260
  if (require.main === module) {
351
- main().catch((error) => {
352
- console.error('Script execution failed:', error);
353
- process.exit(1);
354
- });
261
+ _main().catch((error) => {
262
+ console.error('Script execution failed:', error);
263
+ process.exit(1);
264
+ });
355
265
  }
356
266
 
357
- module.exports = { main };
267
+ module.exports = {
268
+ _main
269
+ };
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { findTaskManagerRoot } = require('./shared-utils.cjs');
4
+
5
+ try {
6
+ const root = findTaskManagerRoot();
7
+ process.exit(root ? 0 : 1);
8
+ } catch (err) {
9
+ process.exit(1);
10
+ }