@e0ipso/ai-task-manager 1.3.0 → 1.5.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/README.md +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +118 -114
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +0 -32
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +0 -53
- package/dist/types.js.map +1 -1
- package/dist/utils.d.ts +0 -143
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +12 -345
- package/dist/utils.js.map +1 -1
- package/package.json +1 -1
- package/templates/ai-task-manager/config/scripts/check-task-dependencies.js +332 -0
- package/templates/assistant/commands/tasks/execute-blueprint.md +1 -1
- package/templates/assistant/commands/tasks/execute-task.md +16 -16
- package/templates/ai-task-manager/config/scripts/check-task-dependencies.sh +0 -185
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script: check-task-dependencies.js
|
|
5
|
+
* Purpose: Check if a task has all of its dependencies resolved (completed)
|
|
6
|
+
* Usage: node check-task-dependencies.js <plan-id> <task-id>
|
|
7
|
+
* Returns: 0 if all dependencies are resolved, 1 if not
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const fs = require('fs-extra');
|
|
11
|
+
const path = require('path');
|
|
12
|
+
const { execSync } = require('child_process');
|
|
13
|
+
|
|
14
|
+
// Chalk instance - loaded dynamically to handle ESM module
|
|
15
|
+
let chalkInstance = null;
|
|
16
|
+
|
|
17
|
+
// 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;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Color functions for output
|
|
33
|
+
const printError = (message, chalk) => {
|
|
34
|
+
const formattedMessage = chalk?.red(`ERROR: ${message}`) || `ERROR: ${message}`;
|
|
35
|
+
console.error(formattedMessage);
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
const printSuccess = (message, chalk) => {
|
|
39
|
+
const formattedMessage = chalk?.green(`✓ ${message}`) || `✓ ${message}`;
|
|
40
|
+
console.log(formattedMessage);
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const printWarning = (message, chalk) => {
|
|
44
|
+
const formattedMessage = chalk?.yellow(`⚠ ${message}`) || `⚠ ${message}`;
|
|
45
|
+
console.log(formattedMessage);
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
const printInfo = (message) => {
|
|
49
|
+
console.log(message);
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
// Function to find plan directory
|
|
53
|
+
const findPlanDirectory = (planId) => {
|
|
54
|
+
try {
|
|
55
|
+
// Use find command similar to bash script
|
|
56
|
+
const findCommand = `find .ai/task-manager/plans .ai/task-manager/archive -type d -name "${planId}--*" 2>/dev/null || true`;
|
|
57
|
+
const result = execSync(findCommand, { encoding: 'utf8' }).trim();
|
|
58
|
+
const directories = result.split('\n').filter(dir => dir.length > 0);
|
|
59
|
+
return directories.length > 0 ? directories[0] : null;
|
|
60
|
+
} catch (error) {
|
|
61
|
+
return null;
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// Function to find task file with padded/unpadded ID handling
|
|
66
|
+
const findTaskFile = (planDir, taskId) => {
|
|
67
|
+
const taskDir = path.join(planDir, 'tasks');
|
|
68
|
+
|
|
69
|
+
if (!fs.existsSync(taskDir)) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Try exact match first
|
|
74
|
+
let pattern = `${taskId}--*.md`;
|
|
75
|
+
let files = fs.readdirSync(taskDir).filter(file => {
|
|
76
|
+
const regex = new RegExp(`^${taskId}--.*\\.md$`);
|
|
77
|
+
return regex.test(file);
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
if (files.length > 0) {
|
|
81
|
+
return path.join(taskDir, files[0]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Try with zero-padding if direct match fails
|
|
85
|
+
const paddedTaskId = taskId.padStart(2, '0');
|
|
86
|
+
if (paddedTaskId !== taskId) {
|
|
87
|
+
pattern = `${paddedTaskId}--*.md`;
|
|
88
|
+
files = fs.readdirSync(taskDir).filter(file => {
|
|
89
|
+
const regex = new RegExp(`^${paddedTaskId}--.*\\.md$`);
|
|
90
|
+
return regex.test(file);
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
if (files.length > 0) {
|
|
94
|
+
return path.join(taskDir, files[0]);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Try removing potential zero-padding from taskId
|
|
99
|
+
const unpaddedTaskId = taskId.replace(/^0+/, '') || '0';
|
|
100
|
+
if (unpaddedTaskId !== taskId) {
|
|
101
|
+
pattern = `${unpaddedTaskId}--*.md`;
|
|
102
|
+
files = fs.readdirSync(taskDir).filter(file => {
|
|
103
|
+
const regex = new RegExp(`^${unpaddedTaskId}--.*\\.md$`);
|
|
104
|
+
return regex.test(file);
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
if (files.length > 0) {
|
|
108
|
+
return path.join(taskDir, files[0]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Try with zero-padding of unpadded version
|
|
112
|
+
const repaddedTaskId = unpaddedTaskId.padStart(2, '0');
|
|
113
|
+
pattern = `${repaddedTaskId}--*.md`;
|
|
114
|
+
files = fs.readdirSync(taskDir).filter(file => {
|
|
115
|
+
const regex = new RegExp(`^${repaddedTaskId}--.*\\.md$`);
|
|
116
|
+
return regex.test(file);
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
if (files.length > 0) {
|
|
120
|
+
return path.join(taskDir, files[0]);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return null;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
// Function to parse YAML frontmatter
|
|
128
|
+
const parseFrontmatter = (content) => {
|
|
129
|
+
const lines = content.split('\n');
|
|
130
|
+
let inFrontmatter = false;
|
|
131
|
+
let frontmatterEnd = false;
|
|
132
|
+
let delimiterCount = 0;
|
|
133
|
+
const frontmatterLines = [];
|
|
134
|
+
|
|
135
|
+
for (const line of lines) {
|
|
136
|
+
if (line.trim() === '---') {
|
|
137
|
+
delimiterCount++;
|
|
138
|
+
if (delimiterCount === 1) {
|
|
139
|
+
inFrontmatter = true;
|
|
140
|
+
continue;
|
|
141
|
+
} else if (delimiterCount === 2) {
|
|
142
|
+
frontmatterEnd = true;
|
|
143
|
+
break;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (inFrontmatter && !frontmatterEnd) {
|
|
148
|
+
frontmatterLines.push(line);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return frontmatterLines.join('\n');
|
|
153
|
+
};
|
|
154
|
+
|
|
155
|
+
// Function to extract dependencies from frontmatter
|
|
156
|
+
const extractDependencies = (frontmatter) => {
|
|
157
|
+
const lines = frontmatter.split('\n');
|
|
158
|
+
const dependencies = [];
|
|
159
|
+
let inDependenciesSection = false;
|
|
160
|
+
|
|
161
|
+
for (let i = 0; i < lines.length; i++) {
|
|
162
|
+
const line = lines[i];
|
|
163
|
+
|
|
164
|
+
// Check for dependencies line
|
|
165
|
+
if (line.match(/^dependencies:/)) {
|
|
166
|
+
inDependenciesSection = true;
|
|
167
|
+
|
|
168
|
+
// Check if dependencies are on the same line (array syntax)
|
|
169
|
+
const arrayMatch = line.match(/\[(.*)\]/);
|
|
170
|
+
if (arrayMatch) {
|
|
171
|
+
const deps = arrayMatch[1]
|
|
172
|
+
.split(',')
|
|
173
|
+
.map(dep => dep.trim().replace(/['"]/g, ''))
|
|
174
|
+
.filter(dep => dep.length > 0);
|
|
175
|
+
dependencies.push(...deps);
|
|
176
|
+
inDependenciesSection = false;
|
|
177
|
+
}
|
|
178
|
+
continue;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
// If we're in dependencies section and hit a non-indented line that's not a list item, exit
|
|
182
|
+
if (inDependenciesSection && line.match(/^[^ ]/) && !line.match(/^[ \t]*-/)) {
|
|
183
|
+
inDependenciesSection = false;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// Parse list format dependencies
|
|
187
|
+
if (inDependenciesSection && line.match(/^[ \t]*-/)) {
|
|
188
|
+
const dep = line.replace(/^[ \t]*-[ \t]*/, '').replace(/[ \t]*$/, '').replace(/['"]/g, '');
|
|
189
|
+
if (dep.length > 0) {
|
|
190
|
+
dependencies.push(dep);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return dependencies;
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
// Function to extract status from frontmatter
|
|
199
|
+
const extractStatus = (frontmatter) => {
|
|
200
|
+
const lines = frontmatter.split('\n');
|
|
201
|
+
|
|
202
|
+
for (const line of lines) {
|
|
203
|
+
if (line.match(/^status:/)) {
|
|
204
|
+
return line.replace(/^status:[ \t]*/, '').replace(/^["']/, '').replace(/["']$/, '').trim();
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return null;
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
// Main function
|
|
212
|
+
const main = async () => {
|
|
213
|
+
// Initialize chalk
|
|
214
|
+
const chalk = await initChalk();
|
|
215
|
+
|
|
216
|
+
// Check arguments
|
|
217
|
+
if (process.argv.length !== 4) {
|
|
218
|
+
printError('Invalid number of arguments', chalk);
|
|
219
|
+
console.log('Usage: node check-task-dependencies.js <plan-id> <task-id>');
|
|
220
|
+
console.log('Example: node check-task-dependencies.js 16 03');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const planId = process.argv[2];
|
|
225
|
+
const taskId = process.argv[3];
|
|
226
|
+
|
|
227
|
+
// Find the plan directory
|
|
228
|
+
const planDir = findPlanDirectory(planId);
|
|
229
|
+
|
|
230
|
+
if (!planDir) {
|
|
231
|
+
printError(`Plan with ID ${planId} not found`, chalk);
|
|
232
|
+
process.exit(1);
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
printInfo(`Found plan directory: ${planDir}`);
|
|
236
|
+
|
|
237
|
+
// Find task file
|
|
238
|
+
const taskFile = findTaskFile(planDir, taskId);
|
|
239
|
+
|
|
240
|
+
if (!taskFile || !fs.existsSync(taskFile)) {
|
|
241
|
+
printError(`Task with ID ${taskId} not found in plan ${planId}`, chalk);
|
|
242
|
+
process.exit(1);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
printInfo(`Checking task: ${path.basename(taskFile)}`);
|
|
246
|
+
console.log('');
|
|
247
|
+
|
|
248
|
+
// Read and parse task file
|
|
249
|
+
const taskContent = fs.readFileSync(taskFile, 'utf8');
|
|
250
|
+
const frontmatter = parseFrontmatter(taskContent);
|
|
251
|
+
const dependencies = extractDependencies(frontmatter);
|
|
252
|
+
|
|
253
|
+
// Check if there are any dependencies
|
|
254
|
+
if (dependencies.length === 0) {
|
|
255
|
+
printSuccess('Task has no dependencies - ready to execute!', chalk);
|
|
256
|
+
process.exit(0);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// Display dependencies
|
|
260
|
+
printInfo('Task dependencies found:');
|
|
261
|
+
dependencies.forEach(dep => {
|
|
262
|
+
console.log(` - Task ${dep}`);
|
|
263
|
+
});
|
|
264
|
+
console.log('');
|
|
265
|
+
|
|
266
|
+
// Check each dependency
|
|
267
|
+
let allResolved = true;
|
|
268
|
+
let unresolvedDeps = [];
|
|
269
|
+
let resolvedCount = 0;
|
|
270
|
+
const totalDeps = dependencies.length;
|
|
271
|
+
|
|
272
|
+
printInfo('Checking dependency status...');
|
|
273
|
+
console.log('');
|
|
274
|
+
|
|
275
|
+
for (const depId of dependencies) {
|
|
276
|
+
// Find dependency task file
|
|
277
|
+
const depFile = findTaskFile(planDir, depId);
|
|
278
|
+
|
|
279
|
+
if (!depFile || !fs.existsSync(depFile)) {
|
|
280
|
+
printError(`Dependency task ${depId} not found`, chalk);
|
|
281
|
+
allResolved = false;
|
|
282
|
+
unresolvedDeps.push(`${depId} (not found)`);
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// Extract status from dependency task
|
|
287
|
+
const depContent = fs.readFileSync(depFile, 'utf8');
|
|
288
|
+
const depFrontmatter = parseFrontmatter(depContent);
|
|
289
|
+
const status = extractStatus(depFrontmatter);
|
|
290
|
+
|
|
291
|
+
// Check if status is completed
|
|
292
|
+
if (status === 'completed') {
|
|
293
|
+
printSuccess(`Task ${depId} - Status: completed ✓`, chalk);
|
|
294
|
+
resolvedCount++;
|
|
295
|
+
} else {
|
|
296
|
+
printWarning(`Task ${depId} - Status: ${status || 'unknown'} ✗`, chalk);
|
|
297
|
+
allResolved = false;
|
|
298
|
+
unresolvedDeps.push(`${depId} (${status || 'unknown'})`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
console.log('');
|
|
303
|
+
printInfo('=========================================');
|
|
304
|
+
printInfo('Dependency Check Summary');
|
|
305
|
+
printInfo('=========================================');
|
|
306
|
+
printInfo(`Total dependencies: ${totalDeps}`);
|
|
307
|
+
printInfo(`Resolved: ${resolvedCount}`);
|
|
308
|
+
printInfo(`Unresolved: ${totalDeps - resolvedCount}`);
|
|
309
|
+
console.log('');
|
|
310
|
+
|
|
311
|
+
if (allResolved) {
|
|
312
|
+
printSuccess(`All dependencies are resolved! Task ${taskId} is ready to execute.`, chalk);
|
|
313
|
+
process.exit(0);
|
|
314
|
+
} else {
|
|
315
|
+
printError(`Task ${taskId} has unresolved dependencies:`, chalk);
|
|
316
|
+
unresolvedDeps.forEach(dep => {
|
|
317
|
+
console.log(dep);
|
|
318
|
+
});
|
|
319
|
+
printInfo('Please complete the dependencies before executing this task.');
|
|
320
|
+
process.exit(1);
|
|
321
|
+
}
|
|
322
|
+
};
|
|
323
|
+
|
|
324
|
+
// Run the script
|
|
325
|
+
if (require.main === module) {
|
|
326
|
+
main().catch((error) => {
|
|
327
|
+
console.error('Script execution failed:', error);
|
|
328
|
+
process.exit(1);
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
module.exports = { main };
|
|
@@ -37,7 +37,7 @@ Before starting execution check if you are in the `main` branch. If so, create a
|
|
|
37
37
|
```bash
|
|
38
38
|
# For each task in current phase
|
|
39
39
|
for TASK_ID in $PHASE_TASKS; do
|
|
40
|
-
if !
|
|
40
|
+
if ! node .ai/task-manager/config/scripts/check-task-dependencies.js "$1" "$TASK_ID"; then
|
|
41
41
|
echo "ERROR: Task $TASK_ID has unresolved dependencies - cannot proceed with phase execution"
|
|
42
42
|
echo "Please resolve dependencies before continuing with blueprint execution"
|
|
43
43
|
exit 1
|
|
@@ -16,9 +16,9 @@ You are responsible for executing a single task within a plan while maintaining
|
|
|
16
16
|
|
|
17
17
|
## Input Requirements
|
|
18
18
|
- Plan ID: $1 (required)
|
|
19
|
-
- Task ID: $2 (required)
|
|
19
|
+
- Task ID: $2 (required)
|
|
20
20
|
- Task management directory structure: `@.ai/task-manager/`
|
|
21
|
-
- Dependency checking script:
|
|
21
|
+
- Dependency checking script: `.ai/task-manager/config/scripts/check-task-dependencies.js`
|
|
22
22
|
|
|
23
23
|
### Input Validation
|
|
24
24
|
|
|
@@ -87,7 +87,7 @@ Check current task status to ensure it can be executed:
|
|
|
87
87
|
# Extract current status from task frontmatter
|
|
88
88
|
CURRENT_STATUS=$(awk '
|
|
89
89
|
/^---$/ { if (++delim == 2) exit }
|
|
90
|
-
/^status:/ {
|
|
90
|
+
/^status:/ {
|
|
91
91
|
gsub(/^status:[ \t]*/, "")
|
|
92
92
|
gsub(/^["'\'']/, "")
|
|
93
93
|
gsub(/["'\'']$/, "")
|
|
@@ -125,7 +125,7 @@ Use the dependency checking script to validate all dependencies:
|
|
|
125
125
|
|
|
126
126
|
```bash
|
|
127
127
|
# Call the dependency checking script
|
|
128
|
-
if !
|
|
128
|
+
if ! node .ai/task-manager/config/scripts/check-task-dependencies.js "$PLAN_ID" "$TASK_ID"; then
|
|
129
129
|
echo ""
|
|
130
130
|
echo "Task execution blocked by unresolved dependencies."
|
|
131
131
|
echo "Please complete the required dependencies first."
|
|
@@ -144,7 +144,7 @@ Read task skills and select appropriate task-specific agent:
|
|
|
144
144
|
# Extract skills from task frontmatter
|
|
145
145
|
TASK_SKILLS=$(awk '
|
|
146
146
|
/^---$/ { if (++delim == 2) exit }
|
|
147
|
-
/^skills:/ {
|
|
147
|
+
/^skills:/ {
|
|
148
148
|
in_skills = 1
|
|
149
149
|
# Check if skills are on the same line
|
|
150
150
|
if (match($0, /\[.*\]/)) {
|
|
@@ -191,7 +191,7 @@ echo "Updating task status to in-progress..."
|
|
|
191
191
|
# Create temporary file with updated status
|
|
192
192
|
TEMP_FILE=$(mktemp)
|
|
193
193
|
awk '
|
|
194
|
-
/^---$/ {
|
|
194
|
+
/^---$/ {
|
|
195
195
|
if (++delim == 1) {
|
|
196
196
|
print
|
|
197
197
|
next
|
|
@@ -201,9 +201,9 @@ awk '
|
|
|
201
201
|
next
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
-
/^status:/ && delim == 1 {
|
|
204
|
+
/^status:/ && delim == 1 {
|
|
205
205
|
print "status: \"in-progress\""
|
|
206
|
-
next
|
|
206
|
+
next
|
|
207
207
|
}
|
|
208
208
|
{ print }
|
|
209
209
|
' "$TASK_FILE" > "$TEMP_FILE"
|
|
@@ -220,7 +220,7 @@ Deploy the task using the Task tool with full context:
|
|
|
220
220
|
|
|
221
221
|
**Task Deployment**: Use your internal Task tool to execute the task with the following context:
|
|
222
222
|
- Task file path: `$TASK_FILE`
|
|
223
|
-
- Plan directory: `$PLAN_DIR`
|
|
223
|
+
- Plan directory: `$PLAN_DIR`
|
|
224
224
|
- Required skills: `$TASK_SKILLS`
|
|
225
225
|
- Agent selection: Based on skills analysis or general-purpose agent
|
|
226
226
|
|
|
@@ -236,7 +236,7 @@ After task completion, update the status based on execution outcome:
|
|
|
236
236
|
```bash
|
|
237
237
|
TEMP_FILE=$(mktemp)
|
|
238
238
|
awk '
|
|
239
|
-
/^---$/ {
|
|
239
|
+
/^---$/ {
|
|
240
240
|
if (++delim == 1) {
|
|
241
241
|
print
|
|
242
242
|
next
|
|
@@ -246,9 +246,9 @@ awk '
|
|
|
246
246
|
next
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
|
-
/^status:/ && delim == 1 {
|
|
249
|
+
/^status:/ && delim == 1 {
|
|
250
250
|
print "status: \"completed\""
|
|
251
|
-
next
|
|
251
|
+
next
|
|
252
252
|
}
|
|
253
253
|
{ print }
|
|
254
254
|
' "$TASK_FILE" > "$TEMP_FILE"
|
|
@@ -270,7 +270,7 @@ echo "Task execution failed - updating status..."
|
|
|
270
270
|
|
|
271
271
|
TEMP_FILE=$(mktemp)
|
|
272
272
|
awk '
|
|
273
|
-
/^---$/ {
|
|
273
|
+
/^---$/ {
|
|
274
274
|
if (++delim == 1) {
|
|
275
275
|
print
|
|
276
276
|
next
|
|
@@ -280,9 +280,9 @@ awk '
|
|
|
280
280
|
next
|
|
281
281
|
}
|
|
282
282
|
}
|
|
283
|
-
/^status:/ && delim == 1 {
|
|
283
|
+
/^status:/ && delim == 1 {
|
|
284
284
|
print "status: \"failed\""
|
|
285
|
-
next
|
|
285
|
+
next
|
|
286
286
|
}
|
|
287
287
|
{ print }
|
|
288
288
|
' "$TASK_FILE" > "$TEMP_FILE"
|
|
@@ -312,7 +312,7 @@ exit 1
|
|
|
312
312
|
This command integrates with the existing task management system by:
|
|
313
313
|
- Using established plan and task location patterns
|
|
314
314
|
- Leveraging the dependency checking script for validation
|
|
315
|
-
- Following status management conventions
|
|
315
|
+
- Following status management conventions
|
|
316
316
|
- Maintaining compatibility with execute-blueprint workflows
|
|
317
317
|
- Preserving task isolation and dependency order
|
|
318
318
|
|
|
@@ -1,185 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
|
|
3
|
-
# Script: check-task-dependencies.sh
|
|
4
|
-
# Purpose: Check if a task has all of its dependencies resolved (completed)
|
|
5
|
-
# Usage: ./check-task-dependencies.sh <plan-id> <task-id>
|
|
6
|
-
# Returns: 0 if all dependencies are resolved, 1 if not
|
|
7
|
-
|
|
8
|
-
set -e
|
|
9
|
-
|
|
10
|
-
# Color codes for output
|
|
11
|
-
RED='\033[0;31m'
|
|
12
|
-
GREEN='\033[0;32m'
|
|
13
|
-
YELLOW='\033[1;33m'
|
|
14
|
-
NC='\033[0m' # No Color
|
|
15
|
-
|
|
16
|
-
# Function to print colored output
|
|
17
|
-
print_error() {
|
|
18
|
-
echo -e "${RED}ERROR: $1${NC}" >&2
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
print_success() {
|
|
22
|
-
echo -e "${GREEN}✓ $1${NC}"
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
print_warning() {
|
|
26
|
-
echo -e "${YELLOW}⚠ $1${NC}"
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
print_info() {
|
|
30
|
-
echo -e "$1"
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
# Check arguments
|
|
34
|
-
if [ $# -ne 2 ]; then
|
|
35
|
-
print_error "Invalid number of arguments"
|
|
36
|
-
echo "Usage: $0 <plan-id> <task-id>"
|
|
37
|
-
echo "Example: $0 16 03"
|
|
38
|
-
exit 1
|
|
39
|
-
fi
|
|
40
|
-
|
|
41
|
-
PLAN_ID="$1"
|
|
42
|
-
TASK_ID="$2"
|
|
43
|
-
|
|
44
|
-
# Find the plan directory
|
|
45
|
-
PLAN_DIR=$(find .ai/task-manager/{plans,archive} -type d -name "${PLAN_ID}--*" 2>/dev/null | head -1)
|
|
46
|
-
|
|
47
|
-
if [ -z "$PLAN_DIR" ]; then
|
|
48
|
-
print_error "Plan with ID ${PLAN_ID} not found"
|
|
49
|
-
exit 1
|
|
50
|
-
fi
|
|
51
|
-
|
|
52
|
-
print_info "Found plan directory: ${PLAN_DIR}"
|
|
53
|
-
|
|
54
|
-
# Construct task file path
|
|
55
|
-
# Handle both padded (01, 02) and unpadded (1, 2) task IDs
|
|
56
|
-
TASK_FILE=""
|
|
57
|
-
if [ -f "${PLAN_DIR}/tasks/${TASK_ID}--"*.md ]; then
|
|
58
|
-
TASK_FILE=$(ls "${PLAN_DIR}/tasks/${TASK_ID}--"*.md 2>/dev/null | head -1)
|
|
59
|
-
elif [ -f "${PLAN_DIR}/tasks/0${TASK_ID}--"*.md ]; then
|
|
60
|
-
# Try with zero-padding if direct match fails
|
|
61
|
-
TASK_FILE=$(ls "${PLAN_DIR}/tasks/0${TASK_ID}--"*.md 2>/dev/null | head -1)
|
|
62
|
-
fi
|
|
63
|
-
|
|
64
|
-
if [ -z "$TASK_FILE" ] || [ ! -f "$TASK_FILE" ]; then
|
|
65
|
-
print_error "Task with ID ${TASK_ID} not found in plan ${PLAN_ID}"
|
|
66
|
-
exit 1
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
print_info "Checking task: $(basename "$TASK_FILE")"
|
|
70
|
-
echo ""
|
|
71
|
-
|
|
72
|
-
# Extract dependencies from task frontmatter
|
|
73
|
-
# Using awk to parse YAML frontmatter
|
|
74
|
-
DEPENDENCIES=$(awk '
|
|
75
|
-
/^---$/ { if (++delim == 2) exit }
|
|
76
|
-
/^dependencies:/ {
|
|
77
|
-
dep_section = 1
|
|
78
|
-
# Check if dependencies are on the same line
|
|
79
|
-
if (match($0, /\[.*\]/)) {
|
|
80
|
-
gsub(/^dependencies:[ \t]*\[/, "")
|
|
81
|
-
gsub(/\].*$/, "")
|
|
82
|
-
gsub(/[ \t]/, "")
|
|
83
|
-
print
|
|
84
|
-
dep_section = 0
|
|
85
|
-
}
|
|
86
|
-
next
|
|
87
|
-
}
|
|
88
|
-
dep_section && /^[^ ]/ { dep_section = 0 }
|
|
89
|
-
dep_section && /^[ \t]*-/ {
|
|
90
|
-
gsub(/^[ \t]*-[ \t]*/, "")
|
|
91
|
-
gsub(/[ \t]*$/, "")
|
|
92
|
-
print
|
|
93
|
-
}
|
|
94
|
-
' "$TASK_FILE" | tr ',' '\n' | sed 's/^[ \t]*//;s/[ \t]*$//' | grep -v '^$')
|
|
95
|
-
|
|
96
|
-
# Check if there are any dependencies
|
|
97
|
-
if [ -z "$DEPENDENCIES" ]; then
|
|
98
|
-
print_success "Task has no dependencies - ready to execute!"
|
|
99
|
-
exit 0
|
|
100
|
-
fi
|
|
101
|
-
|
|
102
|
-
# Display dependencies
|
|
103
|
-
print_info "Task dependencies found:"
|
|
104
|
-
echo "$DEPENDENCIES" | while read -r dep; do
|
|
105
|
-
echo " - Task ${dep}"
|
|
106
|
-
done
|
|
107
|
-
echo ""
|
|
108
|
-
|
|
109
|
-
# Check each dependency
|
|
110
|
-
ALL_RESOLVED=true
|
|
111
|
-
UNRESOLVED_DEPS=""
|
|
112
|
-
RESOLVED_COUNT=0
|
|
113
|
-
TOTAL_DEPS=$(echo "$DEPENDENCIES" | wc -l)
|
|
114
|
-
|
|
115
|
-
print_info "Checking dependency status..."
|
|
116
|
-
echo ""
|
|
117
|
-
|
|
118
|
-
for DEP_ID in $DEPENDENCIES; do
|
|
119
|
-
# Find dependency task file
|
|
120
|
-
DEP_FILE=""
|
|
121
|
-
|
|
122
|
-
# Try exact match first
|
|
123
|
-
if [ -f "${PLAN_DIR}/tasks/${DEP_ID}--"*.md ]; then
|
|
124
|
-
DEP_FILE=$(ls "${PLAN_DIR}/tasks/${DEP_ID}--"*.md 2>/dev/null | head -1)
|
|
125
|
-
elif [ -f "${PLAN_DIR}/tasks/0${DEP_ID}--"*.md ]; then
|
|
126
|
-
# Try with zero-padding
|
|
127
|
-
DEP_FILE=$(ls "${PLAN_DIR}/tasks/0${DEP_ID}--"*.md 2>/dev/null | head -1)
|
|
128
|
-
else
|
|
129
|
-
# Try removing potential zero-padding from DEP_ID
|
|
130
|
-
UNPADDED_DEP=$(echo "$DEP_ID" | sed 's/^0*//')
|
|
131
|
-
if [ -f "${PLAN_DIR}/tasks/${UNPADDED_DEP}--"*.md ]; then
|
|
132
|
-
DEP_FILE=$(ls "${PLAN_DIR}/tasks/${UNPADDED_DEP}--"*.md 2>/dev/null | head -1)
|
|
133
|
-
elif [ -f "${PLAN_DIR}/tasks/0${UNPADDED_DEP}--"*.md ]; then
|
|
134
|
-
DEP_FILE=$(ls "${PLAN_DIR}/tasks/0${UNPADDED_DEP}--"*.md 2>/dev/null | head -1)
|
|
135
|
-
fi
|
|
136
|
-
fi
|
|
137
|
-
|
|
138
|
-
if [ -z "$DEP_FILE" ] || [ ! -f "$DEP_FILE" ]; then
|
|
139
|
-
print_error "Dependency task ${DEP_ID} not found"
|
|
140
|
-
ALL_RESOLVED=false
|
|
141
|
-
UNRESOLVED_DEPS="${UNRESOLVED_DEPS}${DEP_ID} (not found)\n"
|
|
142
|
-
continue
|
|
143
|
-
fi
|
|
144
|
-
|
|
145
|
-
# Extract status from dependency task
|
|
146
|
-
STATUS=$(awk '
|
|
147
|
-
/^---$/ { if (++delim == 2) exit }
|
|
148
|
-
/^status:/ {
|
|
149
|
-
gsub(/^status:[ \t]*/, "")
|
|
150
|
-
gsub(/^["'\'']/, "")
|
|
151
|
-
gsub(/["'\'']$/, "")
|
|
152
|
-
print
|
|
153
|
-
exit
|
|
154
|
-
}
|
|
155
|
-
' "$DEP_FILE")
|
|
156
|
-
|
|
157
|
-
# Check if status is completed
|
|
158
|
-
if [ "$STATUS" = "completed" ]; then
|
|
159
|
-
print_success "Task ${DEP_ID} - Status: completed ✓"
|
|
160
|
-
((RESOLVED_COUNT++))
|
|
161
|
-
else
|
|
162
|
-
print_warning "Task ${DEP_ID} - Status: ${STATUS:-unknown} ✗"
|
|
163
|
-
ALL_RESOLVED=false
|
|
164
|
-
UNRESOLVED_DEPS="${UNRESOLVED_DEPS}${DEP_ID} (${STATUS:-unknown})\n"
|
|
165
|
-
fi
|
|
166
|
-
done
|
|
167
|
-
|
|
168
|
-
echo ""
|
|
169
|
-
print_info "========================================="
|
|
170
|
-
print_info "Dependency Check Summary"
|
|
171
|
-
print_info "========================================="
|
|
172
|
-
print_info "Total dependencies: ${TOTAL_DEPS}"
|
|
173
|
-
print_info "Resolved: ${RESOLVED_COUNT}"
|
|
174
|
-
print_info "Unresolved: $((TOTAL_DEPS - RESOLVED_COUNT))"
|
|
175
|
-
echo ""
|
|
176
|
-
|
|
177
|
-
if [ "$ALL_RESOLVED" = true ]; then
|
|
178
|
-
print_success "All dependencies are resolved! Task ${TASK_ID} is ready to execute."
|
|
179
|
-
exit 0
|
|
180
|
-
else
|
|
181
|
-
print_error "Task ${TASK_ID} has unresolved dependencies:"
|
|
182
|
-
echo -e "$UNRESOLVED_DEPS"
|
|
183
|
-
print_info "Please complete the dependencies before executing this task."
|
|
184
|
-
exit 1
|
|
185
|
-
fi
|