@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.
@@ -2,430 +2,48 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
-
6
- // Enable debug logging via environment variable
7
- const DEBUG = process.env.DEBUG === 'true';
8
-
9
- /**
10
- * Debug logging utility
11
- * @param {string} message - Debug message
12
- * @param {...any} args - Additional arguments to log
13
- */
14
- function debugLog(message, ...args) {
15
- if (DEBUG) {
16
- console.error(`[DEBUG] ${message}`, ...args);
17
- }
18
- }
5
+ const { findTaskManagerRoot, getAllPlans } = require('./shared-utils.cjs');
19
6
 
20
7
  /**
21
8
  * Error logging utility
9
+ * @private
22
10
  * @param {string} message - Error message
23
11
  * @param {...any} args - Additional arguments to log
24
12
  */
25
- function errorLog(message, ...args) {
13
+ function _errorLog(message, ...args) {
26
14
  console.error(`[ERROR] ${message}`, ...args);
27
15
  }
28
16
 
29
- /**
30
- * Find the task manager root directory by traversing up from current working directory
31
- * @returns {string|null} Path to task manager root or null if not found
32
- */
33
- function findTaskManagerRoot() {
34
- // Start from the actual current working directory, not process.cwd()
35
- // This ensures we start from the correct context where the script is being executed
36
- let currentPath = process.cwd();
37
- const filesystemRoot = path.parse(currentPath).root;
38
-
39
- debugLog(`Starting search for task manager root from: ${currentPath}`);
40
- debugLog(`Filesystem root: ${filesystemRoot}`);
41
-
42
- // Traverse upward through parent directories until we reach the filesystem root
43
- while (currentPath !== filesystemRoot) {
44
- const taskManagerPlansPath = path.join(currentPath, '.ai', 'task-manager', 'plans');
45
- debugLog(`Checking for task manager at: ${taskManagerPlansPath}`);
46
-
47
- try {
48
- // Check if this is a valid task manager directory
49
- if (fs.existsSync(taskManagerPlansPath)) {
50
- // Verify it's a directory, not a file
51
- const stats = fs.lstatSync(taskManagerPlansPath);
52
- if (stats.isDirectory()) {
53
- const taskManagerRoot = path.join(currentPath, '.ai', 'task-manager');
54
- debugLog(`Found valid task manager root at: ${taskManagerRoot}`);
55
- return taskManagerRoot;
56
- } else {
57
- debugLog(`Path exists but is not a directory: ${taskManagerPlansPath}`);
58
- }
59
- } else {
60
- debugLog(`Task manager path does not exist: ${taskManagerPlansPath}`);
61
- }
62
- } catch (err) {
63
- // Handle permission errors or other filesystem issues gracefully
64
- // Continue searching in parent directories
65
- if (err.code === 'EPERM' || err.code === 'EACCES') {
66
- const warningMsg = `Warning: Permission denied accessing ${taskManagerPlansPath}`;
67
- console.warn(warningMsg);
68
- debugLog(`Permission error: ${err.message}`);
69
- } else {
70
- debugLog(`Filesystem error checking ${taskManagerPlansPath}: ${err.message}`);
71
- }
72
- }
73
-
74
- // Move up to parent directory
75
- const parentPath = path.dirname(currentPath);
76
-
77
- // Safety check: if path.dirname returns the same path, we've reached the root
78
- if (parentPath === currentPath) {
79
- debugLog(`Reached filesystem root, stopping traversal`);
80
- break;
81
- }
82
-
83
- currentPath = parentPath;
84
- debugLog(`Moving up to parent directory: ${currentPath}`);
85
- }
86
-
87
- // Check the filesystem root as the final attempt
88
- try {
89
- const rootTaskManagerPlans = path.join(filesystemRoot, '.ai', 'task-manager', 'plans');
90
- debugLog(`Final check at filesystem root: ${rootTaskManagerPlans}`);
91
-
92
- if (fs.existsSync(rootTaskManagerPlans)) {
93
- const stats = fs.lstatSync(rootTaskManagerPlans);
94
- if (stats.isDirectory()) {
95
- const taskManagerRoot = path.join(filesystemRoot, '.ai', 'task-manager');
96
- debugLog(`Found task manager root at filesystem root: ${taskManagerRoot}`);
97
- return taskManagerRoot;
98
- }
99
- }
100
- } catch (err) {
101
- debugLog(`Error checking filesystem root: ${err.message}`);
102
- }
103
-
104
- debugLog(`Task manager root not found in any parent directory`);
105
- return null;
106
- }
107
-
108
- /**
109
- * Parse YAML frontmatter for ID with comprehensive error handling and debug logging
110
- * @param {string} content - File content
111
- * @param {string} [filePath] - Optional file path for error context
112
- * @returns {number|null} Extracted ID or null
113
- */
114
- function extractIdFromFrontmatter(content, filePath = 'unknown') {
115
- debugLog(`Attempting to extract ID from frontmatter in: ${filePath}`);
116
-
117
- // Check for frontmatter block existence
118
- const frontmatterMatch = content.match(/^---\s*\r?\n([\s\S]*?)\r?\n---/);
119
- if (!frontmatterMatch) {
120
- debugLog(`No frontmatter block found in: ${filePath}`);
121
- return null;
122
- }
123
-
124
- const frontmatterText = frontmatterMatch[1];
125
- debugLog(`Found frontmatter block in ${filePath}:\n${frontmatterText}`);
126
-
127
- // Enhanced patterns to handle various YAML formats and edge cases:
128
- // - id: 5 (simple numeric)
129
- // - id: "5" (double quoted)
130
- // - id: '5' (single quoted)
131
- // - "id": 5 (quoted key)
132
- // - 'id': 5 (single quoted key)
133
- // - id : 5 (extra spaces)
134
- // - id: 05 (zero-padded)
135
- // - id: +5 (explicit positive)
136
- // - Mixed quotes: 'id': "5" (different quote types)
137
- const patterns = [
138
- // Most flexible pattern - handles quoted/unquoted keys and values with optional spaces
139
- {
140
- regex: /^\s*["']?id["']?\s*:\s*["']?([+-]?\d+)["']?\s*(?:#.*)?$/mi,
141
- description: 'Flexible pattern with optional quotes and comments'
142
- },
143
- // Simple numeric with optional whitespace and comments
144
- {
145
- regex: /^\s*id\s*:\s*([+-]?\d+)\s*(?:#.*)?$/mi,
146
- description: 'Simple numeric with optional comments'
147
- },
148
- // Double quoted values
149
- {
150
- regex: /^\s*["']?id["']?\s*:\s*"([+-]?\d+)"\s*(?:#.*)?$/mi,
151
- description: 'Double quoted values'
152
- },
153
- // Single quoted values
154
- {
155
- regex: /^\s*["']?id["']?\s*:\s*'([+-]?\d+)'\s*(?:#.*)?$/mi,
156
- description: 'Single quoted values'
157
- },
158
- // Mixed quotes - quoted key, unquoted value
159
- {
160
- regex: /^\s*["']id["']\s*:\s*([+-]?\d+)\s*(?:#.*)?$/mi,
161
- description: 'Quoted key, unquoted value'
162
- },
163
- // YAML-style with pipe or greater-than indicators (edge case)
164
- {
165
- regex: /^\s*id\s*:\s*[|>]\s*([+-]?\d+)\s*$/mi,
166
- description: 'YAML block scalar indicators'
167
- }
168
- ];
169
-
170
- // Try each pattern in order
171
- for (let i = 0; i < patterns.length; i++) {
172
- const { regex, description } = patterns[i];
173
- debugLog(`Trying pattern ${i + 1} (${description}) on ${filePath}`);
174
-
175
- const match = frontmatterText.match(regex);
176
- if (match) {
177
- debugLog(`Pattern ${i + 1} matched in ${filePath}: "${match[0].trim()}"`);
178
-
179
- const rawId = match[1];
180
- const id = parseInt(rawId, 10);
181
-
182
- // Validate the parsed ID
183
- if (isNaN(id)) {
184
- errorLog(`Invalid ID value "${rawId}" in ${filePath} - not a valid number`);
185
- continue;
186
- }
187
-
188
- if (id < 0) {
189
- errorLog(`Invalid ID value ${id} in ${filePath} - ID must be non-negative`);
190
- continue;
191
- }
192
-
193
- if (id > Number.MAX_SAFE_INTEGER) {
194
- errorLog(`Invalid ID value ${id} in ${filePath} - ID exceeds maximum safe integer`);
195
- continue;
196
- }
197
-
198
- debugLog(`Successfully extracted ID ${id} from ${filePath}`);
199
- return id;
200
- } else {
201
- debugLog(`Pattern ${i + 1} did not match in ${filePath}`);
202
- }
203
- }
204
-
205
- // If no patterns matched, try to identify common issues
206
- debugLog(`All patterns failed for ${filePath}. Analyzing frontmatter for common issues...`);
207
-
208
- // Check for 'id' field existence (case-insensitive)
209
- const hasIdField = /^\s*["']?id["']?\s*:/mi.test(frontmatterText);
210
- if (!hasIdField) {
211
- debugLog(`No 'id' field found in frontmatter of ${filePath}`);
212
- } else {
213
- // ID field exists but didn't match - might be malformed
214
- const idLineMatch = frontmatterText.match(/^\s*["']?id["']?\s*:.*$/mi);
215
- if (idLineMatch) {
216
- const idLine = idLineMatch[0].trim();
217
- errorLog(`Found malformed ID line in ${filePath}: "${idLine}"`);
218
-
219
- // Check for common formatting issues
220
- if (idLine.includes('null') || idLine.includes('undefined')) {
221
- errorLog(`ID field has null/undefined value in ${filePath}`);
222
- } else if (idLine.match(/:\s*$/)) {
223
- errorLog(`ID field has missing value in ${filePath}`);
224
- } else if (idLine.includes('[') || idLine.includes('{')) {
225
- errorLog(`ID field appears to be array/object instead of number in ${filePath}`);
226
- } else {
227
- errorLog(`ID field has unrecognized format in ${filePath}`);
228
- }
229
- }
230
- }
231
-
232
- errorLog(`Failed to extract ID from frontmatter in ${filePath}`);
233
- return null;
234
- }
235
-
236
17
  /**
237
18
  * Get the next available plan ID by scanning existing plan files
19
+ * @private
238
20
  * @returns {number} Next available plan ID
239
21
  */
240
- function getNextPlanId() {
22
+ function _getNextPlanId() {
241
23
  const taskManagerRoot = findTaskManagerRoot();
242
24
 
243
25
  if (!taskManagerRoot) {
244
- errorLog('No .ai/task-manager/plans directory found in current directory or any parent directory.');
245
- errorLog('');
246
- errorLog('Please ensure you are in a project with task manager initialized, or navigate to the correct');
247
- errorLog('project directory. The task manager looks for the .ai/task-manager/plans structure starting');
248
- errorLog('from the current working directory and traversing upward through parent directories.');
249
- errorLog('');
250
- errorLog(`Current working directory: ${process.cwd()}`);
26
+ _errorLog('No .ai/task-manager/plans directory found in current directory or any parent directory.');
27
+ _errorLog('');
28
+ _errorLog('Please ensure you are in a project with task manager initialized, or navigate to the correct');
29
+ _errorLog('project directory. The task manager looks for the .ai/task-manager/plans structure starting');
30
+ _errorLog('from the current working directory and traversing upward through parent directories.');
31
+ _errorLog('');
32
+ _errorLog(`Current working directory: ${process.cwd()}`);
251
33
  process.exit(1);
252
34
  }
253
35
 
254
- debugLog(`Task manager root found: ${taskManagerRoot}`);
255
-
256
- const plansDir = path.join(taskManagerRoot, 'plans');
257
- const archiveDir = path.join(taskManagerRoot, 'archive');
258
-
259
- debugLog(`Scanning directories: ${plansDir}, ${archiveDir}`);
260
-
261
- let maxId = 0;
262
- let filesScanned = 0;
263
- let errorsEncountered = 0;
264
-
265
- // Scan both plans and archive directories
266
- [plansDir, archiveDir].forEach(dir => {
267
- const dirName = path.basename(dir);
268
- debugLog(`Scanning directory: ${dir}`);
269
-
270
- if (!fs.existsSync(dir)) {
271
- debugLog(`Directory does not exist: ${dir}`);
272
- return;
273
- }
36
+ const plans = getAllPlans(taskManagerRoot);
37
+ const maxId = plans.reduce((max, p) => Math.max(max, p.id), 0);
274
38
 
275
- try {
276
- const entries = fs.readdirSync(dir, { withFileTypes: true });
277
- debugLog(`Found ${entries.length} entries in ${dir}`);
278
-
279
- entries.forEach(entry => {
280
- if (entry.isDirectory() && entry.name.match(/^\d+--/)) {
281
- // This is a plan directory, look for plan files inside
282
- const planDirPath = path.join(dir, entry.name);
283
- debugLog(`Scanning plan directory: ${planDirPath}`);
284
-
285
- try {
286
- const planDirEntries = fs.readdirSync(planDirPath, { withFileTypes: true });
287
-
288
- planDirEntries.forEach(planEntry => {
289
- if (planEntry.isFile() && planEntry.name.match(/^plan-\d+--.*\.md$/)) {
290
- filesScanned++;
291
- const filePath = path.join(planDirPath, planEntry.name);
292
- debugLog(`Processing plan file: ${filePath}`);
293
-
294
- // Extract ID from directory name as primary source
295
- const dirMatch = entry.name.match(/^(\d+)--/);
296
- let dirId = null;
297
- if (dirMatch) {
298
- dirId = parseInt(dirMatch[1], 10);
299
- if (!isNaN(dirId)) {
300
- debugLog(`Extracted ID ${dirId} from directory name: ${entry.name}`);
301
- if (dirId > maxId) {
302
- maxId = dirId;
303
- debugLog(`New max ID from directory name: ${maxId}`);
304
- }
305
- }
306
- }
307
-
308
- // Extract ID from filename as secondary source
309
- const filenameMatch = planEntry.name.match(/^plan-(\d+)--/);
310
- let filenameId = null;
311
- if (filenameMatch) {
312
- filenameId = parseInt(filenameMatch[1], 10);
313
- if (!isNaN(filenameId)) {
314
- debugLog(`Extracted ID ${filenameId} from filename: ${planEntry.name}`);
315
- if (filenameId > maxId) {
316
- maxId = filenameId;
317
- debugLog(`New max ID from filename: ${maxId}`);
318
- }
319
- }
320
- }
321
-
322
- // Also check frontmatter for most reliable ID
323
- try {
324
- const content = fs.readFileSync(filePath, 'utf8');
325
- const frontmatterId = extractIdFromFrontmatter(content, filePath);
326
-
327
- if (frontmatterId !== null) {
328
- debugLog(`Extracted ID ${frontmatterId} from frontmatter: ${filePath}`);
329
- if (frontmatterId > maxId) {
330
- maxId = frontmatterId;
331
- debugLog(`New max ID from frontmatter: ${maxId}`);
332
- }
333
-
334
- // Validate consistency between all sources
335
- if (dirId !== null && dirId !== frontmatterId) {
336
- errorLog(`ID mismatch in ${filePath}: directory has ${dirId}, frontmatter has ${frontmatterId}`);
337
- errorsEncountered++;
338
- }
339
- if (filenameId !== null && filenameId !== frontmatterId) {
340
- errorLog(`ID mismatch in ${filePath}: filename has ${filenameId}, frontmatter has ${frontmatterId}`);
341
- errorsEncountered++;
342
- }
343
- } else {
344
- debugLog(`No ID found in frontmatter: ${filePath}`);
345
- if (dirId === null && filenameId === null) {
346
- errorLog(`No valid ID found in directory, filename, or frontmatter: ${filePath}`);
347
- errorsEncountered++;
348
- }
349
- }
350
- } catch (err) {
351
- errorLog(`Failed to read file ${filePath}: ${err.message}`);
352
- errorsEncountered++;
353
- }
354
- }
355
- });
356
- } catch (err) {
357
- errorLog(`Failed to read plan directory ${planDirPath}: ${err.message}`);
358
- errorsEncountered++;
359
- }
360
- } else if (entry.isFile() && entry.name.match(/^plan-\d+--.*\.md$/)) {
361
- // Legacy: direct plan file in plans/archive directory (fallback for old format)
362
- filesScanned++;
363
- const filePath = path.join(dir, entry.name);
364
- debugLog(`Processing legacy plan file: ${filePath}`);
365
-
366
- // Extract ID from filename as fallback
367
- const filenameMatch = entry.name.match(/^plan-(\d+)--/);
368
- let filenameId = null;
369
- if (filenameMatch) {
370
- filenameId = parseInt(filenameMatch[1], 10);
371
- if (!isNaN(filenameId)) {
372
- debugLog(`Extracted ID ${filenameId} from legacy filename: ${entry.name}`);
373
- if (filenameId > maxId) {
374
- maxId = filenameId;
375
- debugLog(`New max ID from legacy filename: ${maxId}`);
376
- }
377
- }
378
- }
379
-
380
- // Also check frontmatter for more reliable ID
381
- try {
382
- const content = fs.readFileSync(filePath, 'utf8');
383
- const frontmatterId = extractIdFromFrontmatter(content, filePath);
384
-
385
- if (frontmatterId !== null) {
386
- debugLog(`Extracted ID ${frontmatterId} from legacy frontmatter: ${filePath}`);
387
- if (frontmatterId > maxId) {
388
- maxId = frontmatterId;
389
- debugLog(`New max ID from legacy frontmatter: ${maxId}`);
390
- }
391
-
392
- // Validate consistency between filename and frontmatter
393
- if (filenameId !== null && filenameId !== frontmatterId) {
394
- errorLog(`ID mismatch in legacy ${filePath}: filename has ${filenameId}, frontmatter has ${frontmatterId}`);
395
- errorsEncountered++;
396
- }
397
- } else {
398
- debugLog(`No ID found in legacy frontmatter: ${filePath}`);
399
- if (filenameId === null) {
400
- errorLog(`No valid ID found in legacy filename or frontmatter: ${filePath}`);
401
- errorsEncountered++;
402
- }
403
- }
404
- } catch (err) {
405
- errorLog(`Failed to read legacy file ${filePath}: ${err.message}`);
406
- errorsEncountered++;
407
- }
408
- } else if (entry.isFile() && entry.name.endsWith('.md')) {
409
- debugLog(`Skipping non-plan file: ${entry.name}`);
410
- } else if (entry.isDirectory()) {
411
- debugLog(`Skipping non-plan directory: ${entry.name}`);
412
- }
413
- });
414
- } catch (err) {
415
- errorLog(`Failed to read directory ${dir}: ${err.message}`);
416
- errorsEncountered++;
417
- }
418
- });
419
-
420
- const nextId = maxId + 1;
421
- debugLog(`Scan complete. Files scanned: ${filesScanned}, Errors: ${errorsEncountered}, Max ID found: ${maxId}, Next ID: ${nextId}`);
422
-
423
- if (errorsEncountered > 0) {
424
- errorLog(`Encountered ${errorsEncountered} errors during scan. Next ID calculation may be inaccurate.`);
425
- }
39
+ return maxId + 1;
40
+ }
426
41
 
427
- return nextId;
42
+ // Output the next plan ID if run directly
43
+ if (require.main === module) {
44
+ console.log(_getNextPlanId());
428
45
  }
429
46
 
430
- // Output the next plan ID
431
- console.log(getNextPlanId());
47
+ module.exports = {
48
+ _getNextPlanId
49
+ };
@@ -2,131 +2,46 @@
2
2
 
3
3
  const fs = require('fs');
4
4
  const path = require('path');
5
-
6
- /**
7
- * Find the task manager root directory by traversing up from CWD
8
- * @returns {string|null} Path to task manager root or null if not found
9
- */
10
- function findTaskManagerRoot() {
11
- let currentPath = process.cwd();
12
- const root = path.parse(currentPath).root;
13
-
14
- while (currentPath !== root) {
15
- const taskManagerPath = path.join(currentPath, '.ai', 'task-manager', 'plans');
16
- if (fs.existsSync(taskManagerPath)) {
17
- return path.join(currentPath, '.ai', 'task-manager');
18
- }
19
- currentPath = path.dirname(currentPath);
20
- }
21
-
22
- // Check root directory as well
23
- const rootTaskManager = path.join(root, '.ai', 'task-manager', 'plans');
24
- if (fs.existsSync(rootTaskManager)) {
25
- return path.join(root, '.ai', 'task-manager');
26
- }
27
-
28
- return null;
29
- }
30
-
31
- /**
32
- * Parse YAML frontmatter for ID with resilience to different formats
33
- * @param {string} content - File content
34
- * @returns {number|null} Extracted ID or null
35
- */
36
- function extractIdFromFrontmatter(content) {
37
- const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
38
- if (!frontmatterMatch) return null;
39
-
40
- const frontmatterText = frontmatterMatch[1];
41
-
42
- // Handle various YAML formats for id field using regex:
43
- // id: 5
44
- // id: "5"
45
- // id: '5'
46
- // "id": 5
47
- // 'id': 5
48
- // id : 5 (with spaces)
49
- const patterns = [
50
- /^\s*["']?id["']?\s*:\s*["']?(\d+)["']?\s*$/m, // Most flexible pattern
51
- /^\s*id\s*:\s*(\d+)\s*$/m, // Simple numeric
52
- /^\s*id\s*:\s*"(\d+)"\s*$/m, // Double quoted
53
- /^\s*id\s*:\s*'(\d+)'\s*$/m, // Single quoted
54
- ];
55
-
56
- for (const pattern of patterns) {
57
- const match = frontmatterText.match(pattern);
58
- if (match) {
59
- const id = parseInt(match[1], 10);
60
- if (!isNaN(id)) return id;
61
- }
62
- }
63
-
64
- return null;
65
- }
5
+ const {
6
+ resolvePlan,
7
+ extractIdFromFrontmatter
8
+ } = require('./shared-utils.cjs');
66
9
 
67
10
  /**
68
11
  * Get the next available task ID for a specific plan
69
- * @param {number|string} planId - The plan ID to get next task ID for
12
+ * @private
13
+ * @param {number|string} inputId - The plan ID or path to get next task ID for
70
14
  * @returns {number} Next available task ID
71
15
  */
72
- function getNextTaskId(planId) {
73
- if (!planId) {
74
- console.error('Error: Plan ID is required');
16
+ function _getNextTaskId(inputId) {
17
+ if (!inputId) {
18
+ console.error('Error: Plan ID or path is required');
75
19
  process.exit(1);
76
20
  }
77
21
 
78
- const taskManagerRoot = findTaskManagerRoot();
22
+ const resolved = resolvePlan(inputId);
79
23
 
80
- if (!taskManagerRoot) {
81
- console.error('Error: No .ai/task-manager/plans directory found in current directory or any parent directory.');
82
- console.error('Please ensure you are in a project with task manager initialized.');
24
+ if (!resolved) {
25
+ console.error(`Error: Plan "${inputId}" not found or invalid.`);
83
26
  process.exit(1);
84
27
  }
85
28
 
86
- const plansDir = path.join(taskManagerRoot, 'plans');
87
-
88
- // Find the plan directory (supports both padded and unpadded formats)
89
- const paddedPlanId = String(planId).padStart(2, '0');
90
-
91
- let planDir = null;
92
-
93
- // Optimization: 90% of the time there are no tasks, so check if plans directory exists first
94
- if (!fs.existsSync(plansDir)) {
95
- return 1; // No plans directory = no tasks = start with ID 1
96
- }
97
-
98
- try {
99
- const entries = fs.readdirSync(plansDir, { withFileTypes: true });
100
-
101
- // Look for directory matching the plan ID pattern
102
- for (const entry of entries) {
103
- if (entry.isDirectory()) {
104
- const match = entry.name.match(/^(\d+)--/);
105
- if (match) {
106
- const dirPlanId = match[1].padStart(2, '0');
107
- if (dirPlanId === paddedPlanId) {
108
- const tasksPath = path.join(plansDir, entry.name, 'tasks');
109
- if (fs.existsSync(tasksPath)) {
110
- planDir = tasksPath;
111
- }
112
- break;
113
- }
114
- }
115
- }
116
- }
117
- } catch (err) {
118
- // Directory doesn't exist or can't be read
119
- }
29
+ const {
30
+ planDir
31
+ } = resolved;
32
+ const tasksPath = path.join(planDir, 'tasks');
120
33
 
121
34
  // Optimization: If no tasks directory exists, return 1 immediately (90% case)
122
- if (!planDir) {
35
+ if (!fs.existsSync(tasksPath)) {
123
36
  return 1;
124
37
  }
125
38
 
126
39
  let maxId = 0;
127
40
 
128
41
  try {
129
- const entries = fs.readdirSync(planDir, { withFileTypes: true });
42
+ const entries = fs.readdirSync(tasksPath, {
43
+ withFileTypes: true
44
+ });
130
45
 
131
46
  // Another optimization: If directory is empty, return 1 immediately
132
47
  if (entries.length === 0) {
@@ -136,7 +51,7 @@ function getNextTaskId(planId) {
136
51
  entries.forEach(entry => {
137
52
  if (entry.isFile() && entry.name.endsWith('.md')) {
138
53
  try {
139
- const filePath = path.join(planDir, entry.name);
54
+ const filePath = path.join(tasksPath, entry.name);
140
55
  const content = fs.readFileSync(filePath, 'utf8');
141
56
  const id = extractIdFromFrontmatter(content);
142
57
 
@@ -156,5 +71,11 @@ function getNextTaskId(planId) {
156
71
  }
157
72
 
158
73
  // Get plan ID from command line argument
159
- const planId = process.argv[2];
160
- console.log(getNextTaskId(planId));
74
+ if (require.main === module) {
75
+ const inputId = process.argv[2];
76
+ console.log(_getNextTaskId(inputId));
77
+ }
78
+
79
+ module.exports = {
80
+ _getNextTaskId
81
+ };