@e0ipso/ai-task-manager 1.18.5 → 1.18.7
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 +1 -1
- package/templates/ai-task-manager/config/scripts/get-approval-methods.cjs +229 -0
- package/templates/ai-task-manager/config/scripts/validate-plan-blueprint.cjs +294 -0
- package/templates/ai-task-manager/config/templates/PLAN_TEMPLATE.md +2 -1
- package/templates/assistant/commands/tasks/create-plan.md +20 -10
- package/templates/assistant/commands/tasks/execute-blueprint.md +49 -28
- package/templates/assistant/commands/tasks/generate-tasks.md +20 -7
- package/templates/ai-task-manager/config/scripts/set-approval-method.cjs +0 -90
- package/templates/assistant/commands/tasks/full-workflow.md +0 -190
package/package.json
CHANGED
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
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
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Error logging utility
|
|
22
|
+
* @param {string} message - Error message
|
|
23
|
+
* @param {...any} args - Additional arguments to log
|
|
24
|
+
*/
|
|
25
|
+
function errorLog(message, ...args) {
|
|
26
|
+
console.error(`[ERROR] ${message}`, ...args);
|
|
27
|
+
}
|
|
28
|
+
|
|
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
|
+
let currentPath = process.cwd();
|
|
35
|
+
const filesystemRoot = path.parse(currentPath).root;
|
|
36
|
+
|
|
37
|
+
debugLog(`Starting search for task manager root from: ${currentPath}`);
|
|
38
|
+
|
|
39
|
+
while (currentPath !== filesystemRoot) {
|
|
40
|
+
const taskManagerPlansPath = path.join(currentPath, '.ai', 'task-manager', 'plans');
|
|
41
|
+
debugLog(`Checking for task manager at: ${taskManagerPlansPath}`);
|
|
42
|
+
|
|
43
|
+
try {
|
|
44
|
+
if (fs.existsSync(taskManagerPlansPath)) {
|
|
45
|
+
const stats = fs.lstatSync(taskManagerPlansPath);
|
|
46
|
+
if (stats.isDirectory()) {
|
|
47
|
+
const taskManagerRoot = path.join(currentPath, '.ai', 'task-manager');
|
|
48
|
+
debugLog(`Found valid task manager root at: ${taskManagerRoot}`);
|
|
49
|
+
return taskManagerRoot;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} catch (err) {
|
|
53
|
+
debugLog(`Filesystem error checking ${taskManagerPlansPath}: ${err.message}`);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const parentPath = path.dirname(currentPath);
|
|
57
|
+
if (parentPath === currentPath) {
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
currentPath = parentPath;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
debugLog(`Task manager root not found in any parent directory`);
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Find plan file by ID in plans or archive directories
|
|
70
|
+
* @param {string} taskManagerRoot - Path to task manager root
|
|
71
|
+
* @param {string} planId - Plan ID to find
|
|
72
|
+
* @returns {string|null} Path to plan file or null if not found
|
|
73
|
+
*/
|
|
74
|
+
function findPlanFile(taskManagerRoot, planId) {
|
|
75
|
+
const plansDir = path.join(taskManagerRoot, 'plans');
|
|
76
|
+
const archiveDir = path.join(taskManagerRoot, 'archive');
|
|
77
|
+
|
|
78
|
+
debugLog(`Searching for plan ${planId} in ${plansDir} and ${archiveDir}`);
|
|
79
|
+
|
|
80
|
+
for (const dir of [plansDir, archiveDir]) {
|
|
81
|
+
if (!fs.existsSync(dir)) {
|
|
82
|
+
debugLog(`Directory does not exist: ${dir}`);
|
|
83
|
+
continue;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
88
|
+
|
|
89
|
+
for (const entry of entries) {
|
|
90
|
+
if (entry.isDirectory() && entry.name.match(/^\d+--/)) {
|
|
91
|
+
// Check if directory matches plan ID
|
|
92
|
+
const dirMatch = entry.name.match(/^(\d+)--/);
|
|
93
|
+
if (dirMatch && dirMatch[1] === planId) {
|
|
94
|
+
const planDirPath = path.join(dir, entry.name);
|
|
95
|
+
debugLog(`Found plan directory: ${planDirPath}`);
|
|
96
|
+
|
|
97
|
+
// Look for plan file inside directory
|
|
98
|
+
try {
|
|
99
|
+
const planDirEntries = fs.readdirSync(planDirPath, { withFileTypes: true });
|
|
100
|
+
|
|
101
|
+
for (const planEntry of planDirEntries) {
|
|
102
|
+
if (planEntry.isFile() && planEntry.name.match(/^plan-\d+--.*\.md$/)) {
|
|
103
|
+
const planFilePath = path.join(planDirPath, planEntry.name);
|
|
104
|
+
debugLog(`Found plan file: ${planFilePath}`);
|
|
105
|
+
return planFilePath;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
} catch (err) {
|
|
109
|
+
debugLog(`Failed to read plan directory ${planDirPath}: ${err.message}`);
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
} else if (entry.isFile() && entry.name.match(/^plan-\d+--.*\.md$/)) {
|
|
113
|
+
// Legacy: direct plan file in plans/archive directory
|
|
114
|
+
const filenameMatch = entry.name.match(/^plan-(\d+)--/);
|
|
115
|
+
if (filenameMatch && filenameMatch[1] === planId) {
|
|
116
|
+
const planFilePath = path.join(dir, entry.name);
|
|
117
|
+
debugLog(`Found legacy plan file: ${planFilePath}`);
|
|
118
|
+
return planFilePath;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
} catch (err) {
|
|
123
|
+
errorLog(`Failed to read directory ${dir}: ${err.message}`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Extract a field value from YAML frontmatter
|
|
132
|
+
* @param {string} frontmatter - YAML frontmatter text
|
|
133
|
+
* @param {string} fieldName - Field name to extract
|
|
134
|
+
* @param {string} defaultValue - Default value if field not found
|
|
135
|
+
* @returns {string} Field value or default
|
|
136
|
+
*/
|
|
137
|
+
function extractField(frontmatter, fieldName, defaultValue = 'manual') {
|
|
138
|
+
debugLog(`Extracting field: ${fieldName}`);
|
|
139
|
+
|
|
140
|
+
// Pattern to match field with various formatting:
|
|
141
|
+
// - field: value
|
|
142
|
+
// - field: "value"
|
|
143
|
+
// - field: 'value'
|
|
144
|
+
// - "field": value
|
|
145
|
+
// - field : value (extra spaces)
|
|
146
|
+
const regex = new RegExp(`^\\s*["']?${fieldName}["']?\\s*:\\s*(.+)$`, 'm');
|
|
147
|
+
const match = frontmatter.match(regex);
|
|
148
|
+
|
|
149
|
+
if (!match) {
|
|
150
|
+
debugLog(`Field ${fieldName} not found, using default: ${defaultValue}`);
|
|
151
|
+
return defaultValue;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Clean up value: remove quotes and trim
|
|
155
|
+
let value = match[1].trim();
|
|
156
|
+
value = value.replace(/^['"]|['"]$/g, '');
|
|
157
|
+
|
|
158
|
+
debugLog(`Extracted ${fieldName}: ${value}`);
|
|
159
|
+
return value || defaultValue;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Main function to get approval methods from plan file
|
|
164
|
+
*/
|
|
165
|
+
function main() {
|
|
166
|
+
// Get plan ID from command line
|
|
167
|
+
const planId = process.argv[2];
|
|
168
|
+
if (!planId) {
|
|
169
|
+
errorLog('Error: Plan ID is required');
|
|
170
|
+
console.error('Usage: node get-approval-methods.cjs <plan-id>');
|
|
171
|
+
process.exit(1);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
debugLog(`Looking for plan ID: ${planId}`);
|
|
175
|
+
|
|
176
|
+
// Find task manager root
|
|
177
|
+
const taskManagerRoot = findTaskManagerRoot();
|
|
178
|
+
if (!taskManagerRoot) {
|
|
179
|
+
errorLog('No .ai/task-manager directory found in current directory or any parent directory.');
|
|
180
|
+
errorLog('Please ensure you are in a project with task manager initialized.');
|
|
181
|
+
errorLog(`Current working directory: ${process.cwd()}`);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
// Find plan file
|
|
186
|
+
const planFile = findPlanFile(taskManagerRoot, planId);
|
|
187
|
+
if (!planFile) {
|
|
188
|
+
errorLog(`Plan file for ID ${planId} not found in plans or archive directories.`);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
debugLog(`Reading plan file: ${planFile}`);
|
|
193
|
+
|
|
194
|
+
// Read plan file
|
|
195
|
+
let content;
|
|
196
|
+
try {
|
|
197
|
+
content = fs.readFileSync(planFile, 'utf8');
|
|
198
|
+
} catch (err) {
|
|
199
|
+
errorLog(`Failed to read plan file ${planFile}: ${err.message}`);
|
|
200
|
+
process.exit(1);
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Extract YAML frontmatter
|
|
204
|
+
const frontmatterRegex = /^---\s*\r?\n([\s\S]*?)\r?\n---/;
|
|
205
|
+
const match = content.match(frontmatterRegex);
|
|
206
|
+
|
|
207
|
+
if (!match) {
|
|
208
|
+
errorLog('No YAML frontmatter found in plan file');
|
|
209
|
+
process.exit(1);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const frontmatter = match[1];
|
|
213
|
+
debugLog(`Found frontmatter:\n${frontmatter}`);
|
|
214
|
+
|
|
215
|
+
// Extract approval method fields
|
|
216
|
+
const approvalMethodPlan = extractField(frontmatter, 'approval_method_plan');
|
|
217
|
+
const approvalMethodTasks = extractField(frontmatter, 'approval_method_tasks');
|
|
218
|
+
|
|
219
|
+
// Output JSON
|
|
220
|
+
const result = {
|
|
221
|
+
approval_method_plan: approvalMethodPlan,
|
|
222
|
+
approval_method_tasks: approvalMethodTasks
|
|
223
|
+
};
|
|
224
|
+
|
|
225
|
+
console.log(JSON.stringify(result, null, 2));
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
// Run main function
|
|
229
|
+
main();
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
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
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Error logging utility
|
|
22
|
+
* @param {string} message - Error message
|
|
23
|
+
* @param {...any} args - Additional arguments to log
|
|
24
|
+
*/
|
|
25
|
+
function errorLog(message, ...args) {
|
|
26
|
+
console.error(`[ERROR] ${message}`, ...args);
|
|
27
|
+
}
|
|
28
|
+
|
|
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
|
+
let currentPath = process.cwd();
|
|
35
|
+
const filesystemRoot = path.parse(currentPath).root;
|
|
36
|
+
|
|
37
|
+
debugLog(`Starting search for task manager root from: ${currentPath}`);
|
|
38
|
+
debugLog(`Filesystem root: ${filesystemRoot}`);
|
|
39
|
+
|
|
40
|
+
while (currentPath !== filesystemRoot) {
|
|
41
|
+
const taskManagerPlansPath = path.join(currentPath, '.ai', 'task-manager', 'plans');
|
|
42
|
+
debugLog(`Checking for task manager at: ${taskManagerPlansPath}`);
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
if (fs.existsSync(taskManagerPlansPath)) {
|
|
46
|
+
const stats = fs.lstatSync(taskManagerPlansPath);
|
|
47
|
+
if (stats.isDirectory()) {
|
|
48
|
+
const taskManagerRoot = path.join(currentPath, '.ai', 'task-manager');
|
|
49
|
+
debugLog(`Found valid task manager root at: ${taskManagerRoot}`);
|
|
50
|
+
return taskManagerRoot;
|
|
51
|
+
} else {
|
|
52
|
+
debugLog(`Path exists but is not a directory: ${taskManagerPlansPath}`);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
debugLog(`Task manager path does not exist: ${taskManagerPlansPath}`);
|
|
56
|
+
}
|
|
57
|
+
} catch (err) {
|
|
58
|
+
if (err.code === 'EPERM' || err.code === 'EACCES') {
|
|
59
|
+
const warningMsg = `Warning: Permission denied accessing ${taskManagerPlansPath}`;
|
|
60
|
+
console.warn(warningMsg);
|
|
61
|
+
debugLog(`Permission error: ${err.message}`);
|
|
62
|
+
} else {
|
|
63
|
+
debugLog(`Filesystem error checking ${taskManagerPlansPath}: ${err.message}`);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const parentPath = path.dirname(currentPath);
|
|
68
|
+
|
|
69
|
+
if (parentPath === currentPath) {
|
|
70
|
+
debugLog(`Reached filesystem root, stopping traversal`);
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
currentPath = parentPath;
|
|
75
|
+
debugLog(`Moving up to parent directory: ${currentPath}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
debugLog(`Task manager root not found in any parent directory`);
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Find plan file and directory for a given plan ID
|
|
84
|
+
* @param {string|number} planId - Plan ID to search for
|
|
85
|
+
* @returns {Object|null} Object with planFile and planDir, or null if not found
|
|
86
|
+
*/
|
|
87
|
+
function findPlanById(planId) {
|
|
88
|
+
const taskManagerRoot = findTaskManagerRoot();
|
|
89
|
+
|
|
90
|
+
if (!taskManagerRoot) {
|
|
91
|
+
errorLog('No .ai/task-manager directory found in current directory or any parent directory.');
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
debugLog(`Task manager root found: ${taskManagerRoot}`);
|
|
96
|
+
|
|
97
|
+
const plansDir = path.join(taskManagerRoot, 'plans');
|
|
98
|
+
const archiveDir = path.join(taskManagerRoot, 'archive');
|
|
99
|
+
|
|
100
|
+
debugLog(`Searching for plan ID ${planId} in: ${plansDir}, ${archiveDir}`);
|
|
101
|
+
|
|
102
|
+
// Search both plans and archive directories
|
|
103
|
+
for (const dir of [plansDir, archiveDir]) {
|
|
104
|
+
if (!fs.existsSync(dir)) {
|
|
105
|
+
debugLog(`Directory does not exist: ${dir}`);
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
try {
|
|
110
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
111
|
+
debugLog(`Found ${entries.length} entries in ${dir}`);
|
|
112
|
+
|
|
113
|
+
for (const entry of entries) {
|
|
114
|
+
// Match directory pattern: [plan-id]--*
|
|
115
|
+
if (entry.isDirectory() && entry.name.match(new RegExp(`^${planId}--`))) {
|
|
116
|
+
const planDirPath = path.join(dir, entry.name);
|
|
117
|
+
debugLog(`Found matching plan directory: ${planDirPath}`);
|
|
118
|
+
|
|
119
|
+
try {
|
|
120
|
+
const planDirEntries = fs.readdirSync(planDirPath, { withFileTypes: true });
|
|
121
|
+
|
|
122
|
+
// Look for plan file: plan-[plan-id]--*.md
|
|
123
|
+
for (const planEntry of planDirEntries) {
|
|
124
|
+
if (planEntry.isFile() && planEntry.name.match(new RegExp(`^plan-${planId}--.*\\.md$`))) {
|
|
125
|
+
const planFilePath = path.join(planDirPath, planEntry.name);
|
|
126
|
+
debugLog(`Found plan file: ${planFilePath}`);
|
|
127
|
+
|
|
128
|
+
return {
|
|
129
|
+
planFile: planFilePath,
|
|
130
|
+
planDir: planDirPath
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
debugLog(`No plan file found in directory: ${planDirPath}`);
|
|
136
|
+
} catch (err) {
|
|
137
|
+
errorLog(`Failed to read plan directory ${planDirPath}: ${err.message}`);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
} catch (err) {
|
|
142
|
+
errorLog(`Failed to read directory ${dir}: ${err.message}`);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
debugLog(`Plan ID ${planId} not found in any directory`);
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Count task files in a plan's tasks directory
|
|
152
|
+
* @param {string} planDir - Plan directory path
|
|
153
|
+
* @returns {number} Number of task files found
|
|
154
|
+
*/
|
|
155
|
+
function countTasks(planDir) {
|
|
156
|
+
const tasksDir = path.join(planDir, 'tasks');
|
|
157
|
+
|
|
158
|
+
if (!fs.existsSync(tasksDir)) {
|
|
159
|
+
debugLog(`Tasks directory does not exist: ${tasksDir}`);
|
|
160
|
+
return 0;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
try {
|
|
164
|
+
const stats = fs.lstatSync(tasksDir);
|
|
165
|
+
if (!stats.isDirectory()) {
|
|
166
|
+
debugLog(`Tasks path exists but is not a directory: ${tasksDir}`);
|
|
167
|
+
return 0;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
const files = fs.readdirSync(tasksDir).filter(f => f.endsWith('.md'));
|
|
171
|
+
debugLog(`Found ${files.length} task files in ${tasksDir}`);
|
|
172
|
+
return files.length;
|
|
173
|
+
} catch (err) {
|
|
174
|
+
errorLog(`Failed to read tasks directory ${tasksDir}: ${err.message}`);
|
|
175
|
+
return 0;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Check if execution blueprint section exists in plan file
|
|
181
|
+
* @param {string} planFile - Path to plan file
|
|
182
|
+
* @returns {boolean} True if blueprint section exists, false otherwise
|
|
183
|
+
*/
|
|
184
|
+
function checkBlueprintExists(planFile) {
|
|
185
|
+
try {
|
|
186
|
+
const planContent = fs.readFileSync(planFile, 'utf8');
|
|
187
|
+
const blueprintExists = /^## Execution Blueprint/m.test(planContent);
|
|
188
|
+
debugLog(`Blueprint section ${blueprintExists ? 'found' : 'not found'} in ${planFile}`);
|
|
189
|
+
return blueprintExists;
|
|
190
|
+
} catch (err) {
|
|
191
|
+
errorLog(`Failed to read plan file ${planFile}: ${err.message}`);
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* List available plans for error messaging
|
|
198
|
+
* @returns {string[]} Array of plan directory names
|
|
199
|
+
*/
|
|
200
|
+
function listAvailablePlans() {
|
|
201
|
+
const taskManagerRoot = findTaskManagerRoot();
|
|
202
|
+
|
|
203
|
+
if (!taskManagerRoot) {
|
|
204
|
+
return [];
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const plansDir = path.join(taskManagerRoot, 'plans');
|
|
208
|
+
const archiveDir = path.join(taskManagerRoot, 'archive');
|
|
209
|
+
const plans = [];
|
|
210
|
+
|
|
211
|
+
for (const dir of [plansDir, archiveDir]) {
|
|
212
|
+
if (!fs.existsSync(dir)) {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
218
|
+
for (const entry of entries) {
|
|
219
|
+
if (entry.isDirectory() && entry.name.match(/^\d+--/)) {
|
|
220
|
+
plans.push(entry.name);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
} catch (err) {
|
|
224
|
+
// Silently continue
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return plans.sort((a, b) => {
|
|
229
|
+
const aId = parseInt(a.match(/^(\d+)--/)[1], 10);
|
|
230
|
+
const bId = parseInt(b.match(/^(\d+)--/)[1], 10);
|
|
231
|
+
return aId - bId;
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
/**
|
|
236
|
+
* Validate plan blueprint and output JSON
|
|
237
|
+
* @param {string|number} planId - Plan ID to validate
|
|
238
|
+
*/
|
|
239
|
+
function validatePlanBlueprint(planId) {
|
|
240
|
+
if (!planId) {
|
|
241
|
+
errorLog('Plan ID is required');
|
|
242
|
+
errorLog('');
|
|
243
|
+
errorLog('Usage: node validate-plan-blueprint.cjs <plan-id>');
|
|
244
|
+
errorLog('');
|
|
245
|
+
errorLog('Example:');
|
|
246
|
+
errorLog(' node validate-plan-blueprint.cjs 47');
|
|
247
|
+
process.exit(1);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
debugLog(`Validating plan blueprint for ID: ${planId}`);
|
|
251
|
+
|
|
252
|
+
const planInfo = findPlanById(planId);
|
|
253
|
+
|
|
254
|
+
if (!planInfo) {
|
|
255
|
+
errorLog(`Plan ID ${planId} not found`);
|
|
256
|
+
errorLog('');
|
|
257
|
+
|
|
258
|
+
const availablePlans = listAvailablePlans();
|
|
259
|
+
if (availablePlans.length > 0) {
|
|
260
|
+
errorLog('Available plans:');
|
|
261
|
+
availablePlans.forEach(plan => {
|
|
262
|
+
errorLog(` ${plan}`);
|
|
263
|
+
});
|
|
264
|
+
} else {
|
|
265
|
+
errorLog('No plans found in .ai/task-manager/{plans,archive}/');
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
errorLog('');
|
|
269
|
+
errorLog('Please verify:');
|
|
270
|
+
errorLog(' 1. You are in the correct project directory');
|
|
271
|
+
errorLog(' 2. The plan exists in .ai/task-manager/plans/ or .ai/task-manager/archive/');
|
|
272
|
+
errorLog(' 3. The plan directory follows the naming pattern: [plan-id]--[name]');
|
|
273
|
+
errorLog(' 4. The plan file follows the naming pattern: plan-[plan-id]--[name].md');
|
|
274
|
+
process.exit(1);
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
const { planFile, planDir } = planInfo;
|
|
278
|
+
const taskCount = countTasks(planDir);
|
|
279
|
+
const blueprintExists = checkBlueprintExists(planFile);
|
|
280
|
+
|
|
281
|
+
const result = {
|
|
282
|
+
planFile,
|
|
283
|
+
planDir,
|
|
284
|
+
taskCount,
|
|
285
|
+
blueprintExists
|
|
286
|
+
};
|
|
287
|
+
|
|
288
|
+
debugLog('Validation complete:', result);
|
|
289
|
+
console.log(JSON.stringify(result, null, 2));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// Main execution
|
|
293
|
+
const planId = process.argv[2];
|
|
294
|
+
validatePlanBlueprint(planId);
|
|
@@ -2,7 +2,8 @@
|
|
|
2
2
|
id: [PLAN-ID]
|
|
3
3
|
summary: "[Brief one-line description of what this plan accomplishes]"
|
|
4
4
|
created: [YYYY-MM-DD]
|
|
5
|
-
|
|
5
|
+
approval_method_plan: [auto|manual] # Workflow approval for plan review (default: manual)
|
|
6
|
+
approval_method_tasks: [auto|manual] # Workflow approval for task generation review (default: manual)
|
|
6
7
|
---
|
|
7
8
|
|
|
8
9
|
# Plan: [Descriptive Plan Title]
|
|
@@ -88,14 +88,18 @@ Structure your response as follows:
|
|
|
88
88
|
- If context is insufficient: List specific clarifying questions
|
|
89
89
|
- If context is sufficient: Provide the comprehensive plan using the structure above. Use the information in @TASK_MANAGER.md for the directory structure and additional information about plans.
|
|
90
90
|
|
|
91
|
-
**Output Behavior
|
|
91
|
+
**Output Behavior: CRITICAL - Structured Output for Command Coordination**
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
- Tell the user that you are done
|
|
95
|
-
- Instruct them to review the plan document with the file path
|
|
96
|
-
- Example output: "Plan created. Please review: `.ai/task-manager/plans/40--plan-name/plan-40--plan-name.md`"
|
|
93
|
+
Always end your output with a standardized summary in this exact format:
|
|
97
94
|
|
|
98
|
-
|
|
95
|
+
```
|
|
96
|
+
---
|
|
97
|
+
Plan Summary:
|
|
98
|
+
- Plan ID: [numeric-id]
|
|
99
|
+
- Plan File: [full-path-to-plan-file]
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
This structured output enables automated workflow coordination and must be included even when running standalone.
|
|
99
103
|
|
|
100
104
|
###### Plan Template
|
|
101
105
|
|
|
@@ -114,11 +118,12 @@ Example:
|
|
|
114
118
|
id: 1
|
|
115
119
|
summary: "Implement a comprehensive CI/CD pipeline using GitHub Actions for automated linting, testing, semantic versioning, and NPM publishing"
|
|
116
120
|
created: 2025-09-01
|
|
117
|
-
|
|
121
|
+
approval_method_plan: "manual"
|
|
122
|
+
approval_method_tasks: "manual"
|
|
118
123
|
---
|
|
119
124
|
```
|
|
120
125
|
|
|
121
|
-
**Important**: Always set `
|
|
126
|
+
**Important**: Always set both `approval_method_plan` and `approval_method_tasks` to "manual" when creating a plan. The full-workflow command will modify these fields to "auto" after creation if running in automated mode.
|
|
122
127
|
|
|
123
128
|
The schema for this frontmatter is:
|
|
124
129
|
```json
|
|
@@ -139,10 +144,15 @@ The schema for this frontmatter is:
|
|
|
139
144
|
"pattern": "^\\d{4}-\\d{2}-\\d{2}$",
|
|
140
145
|
"description": "Creation date in YYYY-MM-DD format"
|
|
141
146
|
},
|
|
142
|
-
"
|
|
147
|
+
"approval_method_plan": {
|
|
148
|
+
"type": "string",
|
|
149
|
+
"enum": ["auto", "manual"],
|
|
150
|
+
"description": "Workflow approval mode for plan review: auto for automated workflows, manual for standalone execution"
|
|
151
|
+
},
|
|
152
|
+
"approval_method_tasks": {
|
|
143
153
|
"type": "string",
|
|
144
154
|
"enum": ["auto", "manual"],
|
|
145
|
-
"description": "Workflow approval mode: auto
|
|
155
|
+
"description": "Workflow approval mode for task generation review: auto when tasks auto-generated in workflow, manual for standalone execution"
|
|
146
156
|
}
|
|
147
157
|
},
|
|
148
158
|
"additionalProperties": false
|
|
@@ -45,22 +45,15 @@ Before proceeding with execution, validate that tasks exist and the execution bl
|
|
|
45
45
|
|
|
46
46
|
**Validation Steps:**
|
|
47
47
|
|
|
48
|
-
1. **Locate the plan document**:
|
|
49
48
|
```bash
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
TASK_COUNT=${#TASK_FILES[@]}
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
3. **Check for execution blueprint section**:
|
|
62
|
-
```bash
|
|
63
|
-
BLUEPRINT_EXISTS=$(grep -q "^## Execution Blueprint" "$PLAN_FILE" && echo "yes" || echo "no")
|
|
49
|
+
# Validate plan exists and check for tasks/blueprint
|
|
50
|
+
VALIDATION=$(node .ai/task-manager/config/scripts/validate-plan-blueprint.cjs $1)
|
|
51
|
+
|
|
52
|
+
# Parse validation results
|
|
53
|
+
PLAN_FILE=$(echo "$VALIDATION" | grep -o '"planFile": "[^"]*"' | cut -d'"' -f4)
|
|
54
|
+
PLAN_DIR=$(echo "$VALIDATION" | grep -o '"planDir": "[^"]*"' | cut -d'"' -f4)
|
|
55
|
+
TASK_COUNT=$(echo "$VALIDATION" | grep -o '"taskCount": [0-9]*' | awk '{print $2}')
|
|
56
|
+
BLUEPRINT_EXISTS=$(echo "$VALIDATION" | grep -o '"blueprintExists": [a-z]*' | awk '{print $2}')
|
|
64
57
|
```
|
|
65
58
|
|
|
66
59
|
4. **Automatic task generation**:
|
|
@@ -71,7 +64,12 @@ If either `$TASK_COUNT` is 0 or `$BLUEPRINT_EXISTS` is "no":
|
|
|
71
64
|
```
|
|
72
65
|
/tasks:generate-tasks $1
|
|
73
66
|
```
|
|
74
|
-
- **
|
|
67
|
+
- **NEW STEP**: Immediately after task generation succeeds, set the approval_method_tasks field to auto:
|
|
68
|
+
```bash
|
|
69
|
+
node .ai/task-manager/config/scripts/set-approval-method.cjs "$PLAN_FILE" auto tasks
|
|
70
|
+
```
|
|
71
|
+
- This signals that tasks were auto-generated in workflow context and execution should continue without pause.
|
|
72
|
+
- **CRITICAL**: After setting the field, you MUST immediately proceed with blueprint execution without waiting for user input. The workflow should continue seamlessly.
|
|
75
73
|
- If generation fails: Halt execution with clear error message:
|
|
76
74
|
```
|
|
77
75
|
❌ Error: Automatic task generation failed.
|
|
@@ -146,33 +144,56 @@ Read and execute .ai/task-manager/config/hooks/POST_ERROR_DETECTION.md
|
|
|
146
144
|
|
|
147
145
|
**Extract approval method from plan metadata:**
|
|
148
146
|
|
|
149
|
-
First, extract
|
|
147
|
+
First, extract both approval method fields from the plan document:
|
|
150
148
|
|
|
151
149
|
```bash
|
|
152
|
-
#
|
|
153
|
-
|
|
150
|
+
# Extract approval methods from plan metadata
|
|
151
|
+
APPROVAL_METHODS=$(node .ai/task-manager/config/scripts/get-approval-methods.cjs $1)
|
|
154
152
|
|
|
155
|
-
|
|
156
|
-
|
|
153
|
+
APPROVAL_METHOD_PLAN=$(echo "$APPROVAL_METHODS" | grep -o '"approval_method_plan": "[^"]*"' | cut -d'"' -f4)
|
|
154
|
+
APPROVAL_METHOD_TASKS=$(echo "$APPROVAL_METHODS" | grep -o '"approval_method_tasks": "[^"]*"' | cut -d'"' -f4)
|
|
157
155
|
|
|
158
|
-
#
|
|
159
|
-
|
|
156
|
+
# Defaults to "manual" if fields don't exist
|
|
157
|
+
APPROVAL_METHOD_PLAN=${APPROVAL_METHOD_PLAN:-manual}
|
|
158
|
+
APPROVAL_METHOD_TASKS=${APPROVAL_METHOD_TASKS:-manual}
|
|
160
159
|
```
|
|
161
160
|
|
|
162
|
-
Then adjust output based on the extracted approval
|
|
161
|
+
Then adjust output based on the extracted approval methods:
|
|
163
162
|
|
|
164
|
-
- **If `
|
|
165
|
-
- Provide minimal progress updates
|
|
166
|
-
- Do NOT instruct user to review
|
|
163
|
+
- **If `APPROVAL_METHOD_PLAN="auto"` (automated workflow mode)**:
|
|
164
|
+
- During task auto-generation phase: Provide minimal progress updates
|
|
165
|
+
- Do NOT instruct user to review the plan or tasks being generated
|
|
167
166
|
- Do NOT add any prompts that would pause execution
|
|
167
|
+
|
|
168
|
+
- **If `APPROVAL_METHOD_TASKS="auto"` (tasks auto-generated in workflow)**:
|
|
169
|
+
- During task execution phase: Provide minimal progress updates at phase boundaries
|
|
170
|
+
- Do NOT instruct user to review implementation details
|
|
168
171
|
- Example output: "Phase 1/3 completed. Proceeding to Phase 2."
|
|
169
172
|
|
|
170
|
-
- **If `
|
|
173
|
+
- **If `APPROVAL_METHOD_PLAN="manual"` or `APPROVAL_METHOD_TASKS="manual"` (standalone mode)**:
|
|
171
174
|
- Provide detailed execution summary with phase results
|
|
172
175
|
- List completed tasks and any noteworthy events
|
|
173
176
|
- Instruct user to review the execution summary in the plan document
|
|
174
177
|
- Example output: "Execution completed. Review summary: `.ai/task-manager/archive/[plan]/plan-[id].md`"
|
|
175
178
|
|
|
179
|
+
**Note**: This command respects both approval method fields:
|
|
180
|
+
- `approval_method_plan`: Used during auto-generation to determine if we're in automated workflow
|
|
181
|
+
- `approval_method_tasks`: Used during execution to determine output verbosity
|
|
182
|
+
|
|
183
|
+
**CRITICAL - Structured Output for Command Coordination:**
|
|
184
|
+
|
|
185
|
+
Always end your output with a standardized summary in this exact format:
|
|
186
|
+
|
|
187
|
+
```
|
|
188
|
+
---
|
|
189
|
+
Execution Summary:
|
|
190
|
+
- Plan ID: [numeric-id]
|
|
191
|
+
- Status: Archived
|
|
192
|
+
- Location: .ai/task-manager/archive/[plan-id]--[plan-name]/
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
This structured output enables automated workflow coordination and must be included even when running standalone.
|
|
196
|
+
|
|
176
197
|
## Optimization Guidelines
|
|
177
198
|
|
|
178
199
|
- **Maximize parallelism**: Always run all available tasks in a phase simultaneously
|
|
@@ -303,26 +303,39 @@ Read and run the .ai/task-manager/config/hooks/POST_TASK_GENERATION_ALL.md
|
|
|
303
303
|
First, extract the approval_method from the plan document:
|
|
304
304
|
|
|
305
305
|
```bash
|
|
306
|
-
#
|
|
307
|
-
|
|
306
|
+
# Extract approval method from plan metadata
|
|
307
|
+
APPROVAL_METHODS=$(node .ai/task-manager/config/scripts/get-approval-methods.cjs $1)
|
|
308
308
|
|
|
309
|
-
|
|
310
|
-
APPROVAL_METHOD=$(sed -n '/^---$/,/^---$/p' "$PLAN_FILE" | grep '^approval_method:' | sed 's/approval_method: *//;s/"//g;s/'"'"'//g' | tr -d ' ')
|
|
309
|
+
APPROVAL_METHOD_TASKS=$(echo "$APPROVAL_METHODS" | grep -o '"approval_method_tasks": "[^"]*"' | cut -d'"' -f4)
|
|
311
310
|
|
|
312
311
|
# Default to "manual" if field doesn't exist (backward compatibility)
|
|
313
|
-
|
|
312
|
+
APPROVAL_METHOD_TASKS=${APPROVAL_METHOD_TASKS:-manual}
|
|
314
313
|
```
|
|
315
314
|
|
|
316
315
|
Then adjust output based on the extracted approval method:
|
|
317
316
|
|
|
318
|
-
- **If `
|
|
317
|
+
- **If `APPROVAL_METHOD_TASKS="auto"` (automated workflow mode)**:
|
|
319
318
|
- Simply confirm task generation with task count
|
|
320
319
|
- Do NOT instruct user to review the tasks
|
|
321
320
|
- Do NOT add any prompts that would pause execution
|
|
322
321
|
- Example output: "Tasks generated for plan [id]: [count] tasks created"
|
|
323
322
|
|
|
324
|
-
- **If `
|
|
323
|
+
- **If `APPROVAL_METHOD_TASKS="manual"` or empty (standalone mode)**:
|
|
325
324
|
- Be concise but helpful
|
|
326
325
|
- Tell the user that you are done
|
|
327
326
|
- Instruct them to review the tasks with file paths
|
|
328
327
|
- Example output: "Task generation complete. Review tasks in: `.ai/task-manager/plans/[plan-id]--[name]/tasks/`"
|
|
328
|
+
|
|
329
|
+
**CRITICAL - Structured Output for Command Coordination:**
|
|
330
|
+
|
|
331
|
+
Always end your output with a standardized summary in this exact format:
|
|
332
|
+
|
|
333
|
+
```
|
|
334
|
+
---
|
|
335
|
+
Task Generation Summary:
|
|
336
|
+
- Plan ID: [numeric-id]
|
|
337
|
+
- Tasks: [count]
|
|
338
|
+
- Status: Ready for execution
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
This structured output enables automated workflow coordination and must be included even when running standalone.
|
|
@@ -1,90 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
const fs = require('fs');
|
|
4
|
-
const path = require('path');
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* Update or add approval_method field in a plan file's YAML frontmatter
|
|
8
|
-
* @param {string} filePath - Path to the plan file
|
|
9
|
-
* @param {string} approvalMethod - Approval method value ('auto' or 'manual')
|
|
10
|
-
* @returns {boolean} True if successful, false otherwise
|
|
11
|
-
*/
|
|
12
|
-
function setApprovalMethod(filePath, approvalMethod) {
|
|
13
|
-
// Validate inputs
|
|
14
|
-
if (!filePath) {
|
|
15
|
-
throw new Error('File path is required');
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if (!approvalMethod || !['auto', 'manual'].includes(approvalMethod)) {
|
|
19
|
-
throw new Error('Approval method must be "auto" or "manual"');
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Check file exists
|
|
23
|
-
if (!fs.existsSync(filePath)) {
|
|
24
|
-
throw new Error(`File not found: ${filePath}`);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
// Read file content
|
|
28
|
-
const content = fs.readFileSync(filePath, 'utf8');
|
|
29
|
-
|
|
30
|
-
// Parse frontmatter - handle both empty and non-empty frontmatter
|
|
31
|
-
const frontmatterRegex = /^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n([\s\S]*))?$/;
|
|
32
|
-
const match = content.match(frontmatterRegex);
|
|
33
|
-
|
|
34
|
-
if (!match) {
|
|
35
|
-
throw new Error('No frontmatter found in file');
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const frontmatterContent = match[1] || '';
|
|
39
|
-
const bodyContent = match[2] || '';
|
|
40
|
-
const frontmatterLines = frontmatterContent ? frontmatterContent.split('\n') : [];
|
|
41
|
-
|
|
42
|
-
// Update or add approval_method field
|
|
43
|
-
let approvalMethodFound = false;
|
|
44
|
-
const updatedFrontmatter = frontmatterLines.map(line => {
|
|
45
|
-
const trimmed = line.trim();
|
|
46
|
-
if (trimmed.startsWith('approval_method:')) {
|
|
47
|
-
approvalMethodFound = true;
|
|
48
|
-
return `approval_method: ${approvalMethod}`;
|
|
49
|
-
}
|
|
50
|
-
return line;
|
|
51
|
-
});
|
|
52
|
-
|
|
53
|
-
// Add approval_method if not found
|
|
54
|
-
if (!approvalMethodFound) {
|
|
55
|
-
updatedFrontmatter.push(`approval_method: ${approvalMethod}`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Reconstruct file
|
|
59
|
-
const updated = '---\n' + updatedFrontmatter.join('\n') + '\n---\n' + bodyContent;
|
|
60
|
-
|
|
61
|
-
// Write back to file
|
|
62
|
-
fs.writeFileSync(filePath, updated, 'utf8');
|
|
63
|
-
|
|
64
|
-
return true;
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
// Main execution with error handling
|
|
68
|
-
try {
|
|
69
|
-
const args = process.argv.slice(2);
|
|
70
|
-
|
|
71
|
-
if (args.length < 2) {
|
|
72
|
-
console.error('Usage: set-approval-method.cjs <file-path> <approval-method>');
|
|
73
|
-
console.error(' file-path: Path to the plan file');
|
|
74
|
-
console.error(' approval-method: "auto" or "manual"');
|
|
75
|
-
process.exit(1);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
const [filePath, approvalMethod] = args;
|
|
79
|
-
|
|
80
|
-
// Resolve relative paths
|
|
81
|
-
const resolvedPath = path.isAbsolute(filePath) ? filePath : path.resolve(process.cwd(), filePath);
|
|
82
|
-
|
|
83
|
-
setApprovalMethod(resolvedPath, approvalMethod);
|
|
84
|
-
|
|
85
|
-
console.log(`✓ Successfully set approval_method to "${approvalMethod}" in ${path.basename(resolvedPath)}`);
|
|
86
|
-
process.exit(0);
|
|
87
|
-
} catch (error) {
|
|
88
|
-
console.error(`✗ Error: ${error.message}`);
|
|
89
|
-
process.exit(1);
|
|
90
|
-
}
|
|
@@ -1,190 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
argument-hint: [user-prompt]
|
|
3
|
-
description: Execute the full workflow from plan creation to blueprint execution
|
|
4
|
-
---
|
|
5
|
-
# Full Workflow Execution
|
|
6
|
-
|
|
7
|
-
## Assistant Configuration
|
|
8
|
-
|
|
9
|
-
Before proceeding with this command, you MUST load and respect the assistant's configuration:
|
|
10
|
-
|
|
11
|
-
**Run the following scripts:**
|
|
12
|
-
```bash
|
|
13
|
-
ASSISTANT=$(node .ai/task-manager/config/scripts/detect-assistant.cjs)
|
|
14
|
-
node .ai/task-manager/config/scripts/read-assistant-config.cjs "$ASSISTANT"
|
|
15
|
-
```
|
|
16
|
-
|
|
17
|
-
The output above contains your global and project-level configuration rules. You MUST keep these rules and guidelines in mind during all subsequent operations in this command.
|
|
18
|
-
|
|
19
|
-
---
|
|
20
|
-
|
|
21
|
-
You are a workflow orchestration assistant. Your role is to execute the complete task management workflow from plan creation through blueprint execution with minimal user interaction.
|
|
22
|
-
|
|
23
|
-
## Instructions
|
|
24
|
-
|
|
25
|
-
The user input is:
|
|
26
|
-
|
|
27
|
-
<user-input>
|
|
28
|
-
$ARGUMENTS
|
|
29
|
-
</user-input>
|
|
30
|
-
|
|
31
|
-
If no user input is provided, stop immediately and show an error message to the user.
|
|
32
|
-
|
|
33
|
-
### Workflow Execution Process
|
|
34
|
-
|
|
35
|
-
Use your internal Todo task tool to track the workflow execution:
|
|
36
|
-
|
|
37
|
-
- [ ] Execute /tasks:create-plan
|
|
38
|
-
- [ ] Extract plan ID
|
|
39
|
-
- [ ] Execute /tasks:generate-tasks
|
|
40
|
-
- [ ] Execute /tasks:execute-blueprint
|
|
41
|
-
- [ ] Generate execution summary
|
|
42
|
-
|
|
43
|
-
#### Step 1: Determine Next Plan ID
|
|
44
|
-
|
|
45
|
-
Before creating the plan, determine what the next plan ID will be and store it persistently:
|
|
46
|
-
|
|
47
|
-
```bash
|
|
48
|
-
PLAN_ID=$(node .ai/task-manager/config/scripts/get-next-plan-id.cjs)
|
|
49
|
-
echo "$PLAN_ID" > /tmp/full-workflow-plan-id-$$.txt
|
|
50
|
-
echo "Next plan ID: $PLAN_ID"
|
|
51
|
-
```
|
|
52
|
-
|
|
53
|
-
This stores the plan ID in a temporary file that persists across all workflow steps.
|
|
54
|
-
|
|
55
|
-
#### Step 2: Execute Plan Creation
|
|
56
|
-
|
|
57
|
-
Use the SlashCommand tool to execute plan creation with the user's prompt:
|
|
58
|
-
|
|
59
|
-
```
|
|
60
|
-
/tasks:create-plan $ARGUMENTS
|
|
61
|
-
```
|
|
62
|
-
|
|
63
|
-
**Important**: The plan creation command may ask clarification questions. Wait for user responses before continuing. This is expected behavior and maintains quality control.
|
|
64
|
-
|
|
65
|
-
After plan creation completes, retrieve the plan ID and provide a progress update:
|
|
66
|
-
|
|
67
|
-
```bash
|
|
68
|
-
PLAN_ID=$(cat /tmp/full-workflow-plan-id-$$.txt)
|
|
69
|
-
echo "Step 1/4: Plan created (ID: $PLAN_ID)"
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
**CRITICAL**: Do not wait for user approval or review of the plan. In full-workflow mode, plan validation is automated (Step 3 performs file existence checking only). Proceed immediately to Step 3 without waiting for user input.
|
|
73
|
-
|
|
74
|
-
#### Step 3: Validate Plan Creation and Set Approval Method
|
|
75
|
-
|
|
76
|
-
Verify the plan was created successfully and set it to automated workflow mode:
|
|
77
|
-
|
|
78
|
-
```bash
|
|
79
|
-
# Retrieve the plan ID from temp file
|
|
80
|
-
PLAN_ID=$(cat /tmp/full-workflow-plan-id-$$.txt)
|
|
81
|
-
|
|
82
|
-
# Find the created plan file
|
|
83
|
-
PLAN_FILE=$(find .ai/task-manager/plans -name "plan-[0-9][0-9]*--*.md" -type f -exec grep -l "^id: \?${PLAN_ID}$" {} \;)
|
|
84
|
-
|
|
85
|
-
# Verify plan exists
|
|
86
|
-
if [ -z "$PLAN_FILE" ]; then
|
|
87
|
-
echo "❌ Error: Plan creation failed. Expected plan with ID ${PLAN_ID} not found."
|
|
88
|
-
exit 1
|
|
89
|
-
fi
|
|
90
|
-
|
|
91
|
-
# Set approval_method to auto for automated workflow execution
|
|
92
|
-
# This ensures generate-tasks and execute-blueprint run without interruption
|
|
93
|
-
node .ai/task-manager/config/scripts/set-approval-method.cjs "$PLAN_FILE" auto
|
|
94
|
-
```
|
|
95
|
-
|
|
96
|
-
**Note**: Setting `approval_method: auto` in the plan metadata signals to subordinate commands (generate-tasks, execute-blueprint) that they are running in automated workflow mode and should suppress interactive prompts for plan review. This metadata persists in the plan document and is reliably read by subsequent commands, eliminating dependency on environment variables.
|
|
97
|
-
|
|
98
|
-
#### Step 4: Execute Task Generation
|
|
99
|
-
|
|
100
|
-
Retrieve the plan ID and use the SlashCommand tool to generate tasks:
|
|
101
|
-
|
|
102
|
-
```bash
|
|
103
|
-
PLAN_ID=$(cat /tmp/full-workflow-plan-id-$$.txt)
|
|
104
|
-
echo "Generating tasks for plan $PLAN_ID"
|
|
105
|
-
```
|
|
106
|
-
|
|
107
|
-
Now use the SlashCommand tool with the plan ID from above:
|
|
108
|
-
|
|
109
|
-
```
|
|
110
|
-
/tasks:generate-tasks [plan-id-from-above]
|
|
111
|
-
```
|
|
112
|
-
|
|
113
|
-
After task generation completes, provide minimal progress update referencing the plan ID.
|
|
114
|
-
|
|
115
|
-
#### Step 5: Execute Blueprint
|
|
116
|
-
|
|
117
|
-
Retrieve the plan ID and use the SlashCommand tool to execute the blueprint:
|
|
118
|
-
|
|
119
|
-
```bash
|
|
120
|
-
PLAN_ID=$(cat /tmp/full-workflow-plan-id-$$.txt)
|
|
121
|
-
echo "Executing blueprint for plan $PLAN_ID"
|
|
122
|
-
```
|
|
123
|
-
|
|
124
|
-
Now use the SlashCommand tool with the plan ID from above:
|
|
125
|
-
|
|
126
|
-
```
|
|
127
|
-
/tasks:execute-blueprint [plan-id-from-above]
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
After blueprint execution completes, provide minimal progress update:
|
|
131
|
-
"Step 3/4: Blueprint execution completed"
|
|
132
|
-
|
|
133
|
-
Note: The execute-blueprint command automatically archives the plan upon successful completion.
|
|
134
|
-
|
|
135
|
-
#### Step 6: Generate Execution Summary
|
|
136
|
-
|
|
137
|
-
After all steps complete successfully, retrieve the plan details and generate a summary:
|
|
138
|
-
|
|
139
|
-
```bash
|
|
140
|
-
PLAN_ID=$(cat /tmp/full-workflow-plan-id-$$.txt)
|
|
141
|
-
PLAN_DIR=$(find .ai/task-manager/archive -type d -name "${PLAN_ID}--*" 2>/dev/null | head -n 1)
|
|
142
|
-
PLAN_NAME=$(basename "$PLAN_DIR")
|
|
143
|
-
|
|
144
|
-
echo "✅ Full workflow completed successfully!"
|
|
145
|
-
echo ""
|
|
146
|
-
echo "Plan: $PLAN_NAME"
|
|
147
|
-
echo "Location: .ai/task-manager/archive/$PLAN_NAME/"
|
|
148
|
-
echo ""
|
|
149
|
-
echo "Status: Archived and ready for review"
|
|
150
|
-
echo ""
|
|
151
|
-
echo "📋 Next Steps:"
|
|
152
|
-
echo "- Review the implementation in the archived plan"
|
|
153
|
-
echo "- Check the execution summary in the plan document"
|
|
154
|
-
echo "- Verify all validation gates passed"
|
|
155
|
-
echo ""
|
|
156
|
-
echo "Plan document: .ai/task-manager/archive/$PLAN_NAME/plan-$PLAN_NAME.md"
|
|
157
|
-
|
|
158
|
-
# Clean up temp file
|
|
159
|
-
rm -f /tmp/full-workflow-plan-id-$$.txt
|
|
160
|
-
```
|
|
161
|
-
|
|
162
|
-
### Error Handling
|
|
163
|
-
|
|
164
|
-
If any step fails:
|
|
165
|
-
1. Halt execution immediately
|
|
166
|
-
2. Report clear error message indicating which step failed
|
|
167
|
-
3. Preserve all created artifacts (plan, tasks) for manual review
|
|
168
|
-
4. Read the plan ID from temp file if needed: `cat /tmp/full-workflow-plan-id-$$.txt`
|
|
169
|
-
5. Provide guidance for manual continuation:
|
|
170
|
-
- If plan creation failed: Review error and retry
|
|
171
|
-
- If task generation failed: Run `/tasks:generate-tasks [plan-id]` manually after reviewing plan
|
|
172
|
-
- If blueprint execution failed: Review tasks and run `/tasks:execute-blueprint [plan-id]` manually
|
|
173
|
-
6. Clean up temp file: `rm -f /tmp/full-workflow-plan-id-$$.txt`
|
|
174
|
-
|
|
175
|
-
### Output Requirements
|
|
176
|
-
|
|
177
|
-
**During Execution:**
|
|
178
|
-
- Minimal progress updates at each major step
|
|
179
|
-
- Clear indication of current step (1/4, 2/4, etc.)
|
|
180
|
-
|
|
181
|
-
**After Completion:**
|
|
182
|
-
- Comprehensive summary with plan location
|
|
183
|
-
- Status confirmation (Archived)
|
|
184
|
-
- Next steps for user review
|
|
185
|
-
- Direct link to plan document
|
|
186
|
-
|
|
187
|
-
**On Error:**
|
|
188
|
-
- Clear error message
|
|
189
|
-
- Indication of which step failed
|
|
190
|
-
- Manual recovery instructions
|