@e0ipso/ai-task-manager 1.26.9 → 1.26.11
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/hooks/PRE_PHASE.md +17 -2
- package/templates/ai-task-manager/config/scripts/check-task-dependencies.cjs +18 -47
- package/templates/ai-task-manager/config/scripts/create-feature-branch.cjs +204 -0
- package/templates/assistant/commands/tasks/execute-blueprint.md +1 -1
package/package.json
CHANGED
|
@@ -4,9 +4,24 @@ This hook contains the phase preparation logic that should be executed before st
|
|
|
4
4
|
|
|
5
5
|
## Phase Pre-Execution
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
### Feature Branch Creation
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Create a feature branch for this plan execution (only runs from main/master with a clean working tree):
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# Create feature branch (handles all edge cases automatically)
|
|
13
|
+
node $root/config/scripts/create-feature-branch.cjs $1
|
|
14
|
+
|
|
15
|
+
# Exit codes:
|
|
16
|
+
# 0 = Success (branch created, already exists, or not on main/master)
|
|
17
|
+
# 1 = Error (not git repo, uncommitted changes, or plan not found)
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
**Behavior**:
|
|
21
|
+
- From `main`/`master` with clean tree: Creates `feature/{planId}--{plan-name}` branch
|
|
22
|
+
- From `main`/`master` with uncommitted changes: Exits with error (exit 1)
|
|
23
|
+
- From feature branch: Proceeds without creating a new branch
|
|
24
|
+
- Branch already exists: Proceeds normally
|
|
10
25
|
|
|
11
26
|
## Phase Execution Workflow
|
|
12
27
|
|
|
@@ -7,47 +7,24 @@
|
|
|
7
7
|
* Returns: 0 if all dependencies are resolved, 1 if not
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
|
-
const fs = require('fs
|
|
10
|
+
const fs = require('fs');
|
|
11
11
|
const path = require('path');
|
|
12
12
|
const {
|
|
13
13
|
resolvePlan,
|
|
14
14
|
parseFrontmatter
|
|
15
15
|
} = require('./shared-utils.cjs');
|
|
16
16
|
|
|
17
|
-
// Chalk instance - loaded dynamically to handle ESM module
|
|
18
|
-
let chalkInstance = null;
|
|
19
|
-
|
|
20
|
-
// Initialize chalk instance dynamically
|
|
21
|
-
async function _initChalk() {
|
|
22
|
-
if (chalkInstance) return chalkInstance;
|
|
23
|
-
|
|
24
|
-
try {
|
|
25
|
-
const {
|
|
26
|
-
default: chalk
|
|
27
|
-
} = await import('chalk');
|
|
28
|
-
chalkInstance = chalk;
|
|
29
|
-
} catch (_error) {
|
|
30
|
-
// Chalk not available, will fall back to plain console output
|
|
31
|
-
chalkInstance = null;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return chalkInstance;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
17
|
// Color functions for output
|
|
38
|
-
const _printError = (message
|
|
39
|
-
|
|
40
|
-
console.error(formattedMessage);
|
|
18
|
+
const _printError = (message) => {
|
|
19
|
+
console.error(`ERROR: ${message}`);
|
|
41
20
|
};
|
|
42
21
|
|
|
43
|
-
const _printSuccess = (message
|
|
44
|
-
|
|
45
|
-
console.log(formattedMessage);
|
|
22
|
+
const _printSuccess = (message) => {
|
|
23
|
+
console.log(`✓ ${message}`);
|
|
46
24
|
};
|
|
47
25
|
|
|
48
|
-
const _printWarning = (message
|
|
49
|
-
|
|
50
|
-
console.log(formattedMessage);
|
|
26
|
+
const _printWarning = (message) => {
|
|
27
|
+
console.log(`⚠ ${message}`);
|
|
51
28
|
};
|
|
52
29
|
|
|
53
30
|
const _printInfo = (message) => {
|
|
@@ -141,13 +118,10 @@ const _extractStatus = (frontmatter) => {
|
|
|
141
118
|
};
|
|
142
119
|
|
|
143
120
|
// Main function
|
|
144
|
-
const _main =
|
|
145
|
-
// Initialize chalk
|
|
146
|
-
const chalk = await _initChalk();
|
|
147
|
-
|
|
121
|
+
const _main = (startPath = process.cwd()) => {
|
|
148
122
|
// Check arguments
|
|
149
123
|
if (process.argv.length !== 4) {
|
|
150
|
-
_printError('Invalid number of arguments'
|
|
124
|
+
_printError('Invalid number of arguments');
|
|
151
125
|
console.log('Usage: node check-task-dependencies.cjs <plan-id-or-path> <task-id>');
|
|
152
126
|
console.log('Example: node check-task-dependencies.cjs 16 03');
|
|
153
127
|
process.exit(1);
|
|
@@ -159,7 +133,7 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
159
133
|
const resolved = resolvePlan(inputId, startPath);
|
|
160
134
|
|
|
161
135
|
if (!resolved) {
|
|
162
|
-
_printError(`Plan "${inputId}" not found or invalid
|
|
136
|
+
_printError(`Plan "${inputId}" not found or invalid`);
|
|
163
137
|
process.exit(1);
|
|
164
138
|
}
|
|
165
139
|
|
|
@@ -173,7 +147,7 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
173
147
|
const taskFile = _findTaskFile(planDir, taskId);
|
|
174
148
|
|
|
175
149
|
if (!taskFile || !fs.existsSync(taskFile)) {
|
|
176
|
-
_printError(`Task with ID ${taskId} not found in plan ${planId}
|
|
150
|
+
_printError(`Task with ID ${taskId} not found in plan ${planId}`);
|
|
177
151
|
process.exit(1);
|
|
178
152
|
}
|
|
179
153
|
|
|
@@ -187,7 +161,7 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
187
161
|
|
|
188
162
|
// Check if there are any dependencies
|
|
189
163
|
if (dependencies.length === 0) {
|
|
190
|
-
_printSuccess('Task has no dependencies - ready to execute!'
|
|
164
|
+
_printSuccess('Task has no dependencies - ready to execute!');
|
|
191
165
|
process.exit(0);
|
|
192
166
|
}
|
|
193
167
|
|
|
@@ -212,7 +186,7 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
212
186
|
const depFile = _findTaskFile(planDir, depId);
|
|
213
187
|
|
|
214
188
|
if (!depFile || !fs.existsSync(depFile)) {
|
|
215
|
-
_printError(`Dependency task ${depId} not found
|
|
189
|
+
_printError(`Dependency task ${depId} not found`);
|
|
216
190
|
allResolved = false;
|
|
217
191
|
unresolvedDeps.push(`${depId} (not found)`);
|
|
218
192
|
continue;
|
|
@@ -225,10 +199,10 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
225
199
|
|
|
226
200
|
// Check if status is completed
|
|
227
201
|
if (status === 'completed') {
|
|
228
|
-
_printSuccess(`Task ${depId} - Status: completed
|
|
202
|
+
_printSuccess(`Task ${depId} - Status: completed ✓`);
|
|
229
203
|
resolvedCount++;
|
|
230
204
|
} else {
|
|
231
|
-
_printWarning(`Task ${depId} - Status: ${status || 'unknown'}
|
|
205
|
+
_printWarning(`Task ${depId} - Status: ${status || 'unknown'} ✗`);
|
|
232
206
|
allResolved = false;
|
|
233
207
|
unresolvedDeps.push(`${depId} (${status || 'unknown'})`);
|
|
234
208
|
}
|
|
@@ -244,10 +218,10 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
244
218
|
console.log('');
|
|
245
219
|
|
|
246
220
|
if (allResolved) {
|
|
247
|
-
_printSuccess(`All dependencies are resolved! Task ${taskId} is ready to execute
|
|
221
|
+
_printSuccess(`All dependencies are resolved! Task ${taskId} is ready to execute.`);
|
|
248
222
|
process.exit(0);
|
|
249
223
|
} else {
|
|
250
|
-
_printError(`Task ${taskId} has unresolved dependencies
|
|
224
|
+
_printError(`Task ${taskId} has unresolved dependencies:`);
|
|
251
225
|
unresolvedDeps.forEach(dep => {
|
|
252
226
|
console.log(dep);
|
|
253
227
|
});
|
|
@@ -258,10 +232,7 @@ const _main = async (startPath = process.cwd()) => {
|
|
|
258
232
|
|
|
259
233
|
// Run the script
|
|
260
234
|
if (require.main === module) {
|
|
261
|
-
_main()
|
|
262
|
-
console.error('Script execution failed:', error);
|
|
263
|
-
process.exit(1);
|
|
264
|
-
});
|
|
235
|
+
_main();
|
|
265
236
|
}
|
|
266
237
|
|
|
267
238
|
module.exports = {
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Script: create-feature-branch.cjs
|
|
5
|
+
* Purpose: Create a git feature branch for a plan execution
|
|
6
|
+
* Usage: node create-feature-branch.cjs <plan-id-or-path>
|
|
7
|
+
*
|
|
8
|
+
* Exit codes:
|
|
9
|
+
* 0 = Success (branch created, already exists, or not on main/master)
|
|
10
|
+
* 1 = Error (not git repo, uncommitted changes, plan not found)
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
const { execSync } = require('child_process');
|
|
14
|
+
const fs = require('fs');
|
|
15
|
+
const path = require('path');
|
|
16
|
+
const { resolvePlan } = require('./shared-utils.cjs');
|
|
17
|
+
|
|
18
|
+
// Color functions for output
|
|
19
|
+
const _printError = (message) => {
|
|
20
|
+
console.error(`ERROR: ${message}`);
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const _printSuccess = (message) => {
|
|
24
|
+
console.log(`✓ ${message}`);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const _printWarning = (message) => {
|
|
28
|
+
console.log(`⚠ ${message}`);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const _printInfo = (message) => {
|
|
32
|
+
console.log(message);
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Execute a git command and return the output
|
|
37
|
+
* @param {string} command - Git command to execute
|
|
38
|
+
* @returns {string|null} Command output or null on error
|
|
39
|
+
*/
|
|
40
|
+
const _execGit = (command) => {
|
|
41
|
+
try {
|
|
42
|
+
return execSync(command, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
|
|
43
|
+
} catch (_error) {
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Check if current directory is inside a git repository
|
|
50
|
+
* @returns {boolean}
|
|
51
|
+
*/
|
|
52
|
+
const _isGitRepo = () => {
|
|
53
|
+
const result = _execGit('git rev-parse --is-inside-work-tree');
|
|
54
|
+
return result === 'true';
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Get current git branch name
|
|
59
|
+
* @returns {string|null}
|
|
60
|
+
*/
|
|
61
|
+
const _getCurrentBranch = () => {
|
|
62
|
+
return _execGit('git rev-parse --abbrev-ref HEAD');
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Check if working tree has uncommitted changes
|
|
67
|
+
* @returns {boolean}
|
|
68
|
+
*/
|
|
69
|
+
const _hasUncommittedChanges = () => {
|
|
70
|
+
const status = _execGit('git status --porcelain');
|
|
71
|
+
return status !== null && status.length > 0;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Check if a branch exists locally or remotely
|
|
76
|
+
* @param {string} branchName - Branch name to check
|
|
77
|
+
* @returns {boolean}
|
|
78
|
+
*/
|
|
79
|
+
const _branchExists = (branchName) => {
|
|
80
|
+
// Check local branches
|
|
81
|
+
const localBranches = _execGit('git branch --list');
|
|
82
|
+
if (localBranches && localBranches.split('\n').some(b => b.trim().replace('* ', '') === branchName)) {
|
|
83
|
+
return true;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// Check remote branches
|
|
87
|
+
const remoteBranches = _execGit('git branch -r --list');
|
|
88
|
+
if (remoteBranches && remoteBranches.split('\n').some(b => b.trim().includes(branchName))) {
|
|
89
|
+
return true;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
return false;
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Sanitize plan name for use in branch name
|
|
97
|
+
* @param {string} planName - Original plan name from directory
|
|
98
|
+
* @returns {string} Sanitized branch name segment
|
|
99
|
+
*/
|
|
100
|
+
const _sanitizeBranchName = (planName) => {
|
|
101
|
+
return planName
|
|
102
|
+
.toLowerCase()
|
|
103
|
+
.replace(/[^a-z0-9-]/g, '-') // Replace non-alphanumeric chars with hyphens
|
|
104
|
+
.replace(/-+/g, '-') // Collapse multiple hyphens
|
|
105
|
+
.replace(/^-|-$/g, '') // Remove leading/trailing hyphens
|
|
106
|
+
.substring(0, 60); // Max 60 chars
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Extract plan name from plan directory
|
|
111
|
+
* @param {string} planDir - Full path to plan directory
|
|
112
|
+
* @returns {string} Plan name portion (e.g., "update-docs" from "58--update-docs")
|
|
113
|
+
*/
|
|
114
|
+
const _extractPlanName = (planDir) => {
|
|
115
|
+
const dirName = path.basename(planDir);
|
|
116
|
+
// Match pattern: {id}--{name}
|
|
117
|
+
const match = dirName.match(/^\d+--(.+)$/);
|
|
118
|
+
return match ? match[1] : dirName;
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
// Main function
|
|
122
|
+
const _main = (startPath = process.cwd()) => {
|
|
123
|
+
// Check arguments
|
|
124
|
+
if (process.argv.length < 3) {
|
|
125
|
+
_printError('Missing plan ID argument');
|
|
126
|
+
console.log('Usage: node create-feature-branch.cjs <plan-id-or-path>');
|
|
127
|
+
console.log('Example: node create-feature-branch.cjs 58');
|
|
128
|
+
process.exit(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const inputId = process.argv[2];
|
|
132
|
+
|
|
133
|
+
// Step 1: Check if this is a git repository
|
|
134
|
+
if (!_isGitRepo()) {
|
|
135
|
+
_printError('Not a git repository');
|
|
136
|
+
process.exit(1);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// Step 2: Resolve the plan
|
|
140
|
+
const resolved = resolvePlan(inputId, startPath);
|
|
141
|
+
|
|
142
|
+
if (!resolved) {
|
|
143
|
+
_printError(`Plan "${inputId}" not found or invalid`);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const { planDir, planId } = resolved;
|
|
148
|
+
_printInfo(`Found plan: ${path.basename(planDir)}`);
|
|
149
|
+
|
|
150
|
+
// Step 3: Check current branch
|
|
151
|
+
const currentBranch = _getCurrentBranch();
|
|
152
|
+
|
|
153
|
+
if (!currentBranch) {
|
|
154
|
+
_printError('Could not determine current git branch');
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (currentBranch !== 'main' && currentBranch !== 'master') {
|
|
159
|
+
_printWarning(`Not on main/master branch (current: ${currentBranch})`);
|
|
160
|
+
_printInfo('Proceeding without creating a new branch');
|
|
161
|
+
process.exit(0);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Step 4: Check for uncommitted changes
|
|
165
|
+
if (_hasUncommittedChanges()) {
|
|
166
|
+
_printError('Uncommitted changes detected in working tree');
|
|
167
|
+
_printInfo('Please commit or stash your changes before creating a feature branch');
|
|
168
|
+
process.exit(1);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
// Step 5: Build branch name
|
|
172
|
+
const planName = _extractPlanName(planDir);
|
|
173
|
+
const sanitizedName = _sanitizeBranchName(planName);
|
|
174
|
+
const branchName = `feature/${planId}--${sanitizedName}`;
|
|
175
|
+
|
|
176
|
+
// Step 6: Check if branch already exists
|
|
177
|
+
if (_branchExists(branchName)) {
|
|
178
|
+
_printWarning(`Branch "${branchName}" already exists`);
|
|
179
|
+
_printInfo('Proceeding with existing branch');
|
|
180
|
+
process.exit(0);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Step 7: Create and checkout the branch
|
|
184
|
+
const createResult = _execGit(`git checkout -b "${branchName}"`);
|
|
185
|
+
|
|
186
|
+
if (createResult === null) {
|
|
187
|
+
_printError(`Failed to create branch "${branchName}"`);
|
|
188
|
+
process.exit(1);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
_printSuccess(`Created and switched to branch: ${branchName}`);
|
|
192
|
+
process.exit(0);
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
// Run the script
|
|
196
|
+
if (require.main === module) {
|
|
197
|
+
_main();
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
module.exports = {
|
|
201
|
+
_main,
|
|
202
|
+
_sanitizeBranchName,
|
|
203
|
+
_extractPlanName
|
|
204
|
+
};
|
|
@@ -112,7 +112,7 @@ Otherwise, if tasks exist, proceed directly to execution.
|
|
|
112
112
|
|
|
113
113
|
Use your internal Todo task tool to track the execution of all phases, and the final update of the plan with the summary. Example:
|
|
114
114
|
|
|
115
|
-
- [ ] Create feature branch
|
|
115
|
+
- [ ] Create feature branch via `node $root/config/scripts/create-feature-branch.cjs $1`
|
|
116
116
|
- [ ] Validate or auto-generate tasks and execution blueprint if missing.
|
|
117
117
|
- [ ] Execute $root/.ai/task-manager/config/hooks/PRE_PHASE.md hook before Phase 1.
|
|
118
118
|
- [ ] Phase 1: Execute 1 task(s) in parallel.
|