@e0ipso/ai-task-manager 1.26.8 → 1.26.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@e0ipso/ai-task-manager",
3
- "version": "1.26.8",
3
+ "version": "1.26.10",
4
4
  "description": "Task management for AI coding assistants",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -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
- Before starting execution check if you are in the `main` branch. If so, create a git branch to work on this blueprint use the plan name for the branch name.
7
+ ### Feature Branch Creation
8
8
 
9
- If there are unstaged changes in the `main` branch, do not create a feature branch.
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
 
@@ -10,6 +10,7 @@ This hook provides pre-planning guidance to ensure scope control, simplicity pri
10
10
  - **Question Everything Extra**: If not directly mentioned by the user, don't add it
11
11
  - **Avoid Feature Creep**: Resist the urge to add "helpful" features or "nice-to-have" additions
12
12
  - **YAGNI Principle**: _You Aren't Gonna Need It_ - don't build for hypothetical future needs
13
+ - **Do NOT add backwards compatibility, unless requested**: If there is a potential BC break, ask the user if they want to BC support. Do not assume the want it.
13
14
 
14
15
  **Common Scope Creep Anti-Patterns to Avoid:**
15
16
  1. Adding extra commands or features "for completeness"
@@ -0,0 +1,232 @@
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
+ // Chalk instance - loaded dynamically to handle ESM module
19
+ let chalkInstance = null;
20
+
21
+ // Initialize chalk instance dynamically
22
+ async function _initChalk() {
23
+ if (chalkInstance) return chalkInstance;
24
+
25
+ try {
26
+ const { default: chalk } = await import('chalk');
27
+ chalkInstance = chalk;
28
+ } catch (_error) {
29
+ // Chalk not available, will fall back to plain console output
30
+ chalkInstance = null;
31
+ }
32
+
33
+ return chalkInstance;
34
+ }
35
+
36
+ // Color functions for output
37
+ const _printError = (message, chalk) => {
38
+ const formattedMessage = chalk?.red(`ERROR: ${message}`) || `ERROR: ${message}`;
39
+ console.error(formattedMessage);
40
+ };
41
+
42
+ const _printSuccess = (message, chalk) => {
43
+ const formattedMessage = chalk?.green(`✓ ${message}`) || `✓ ${message}`;
44
+ console.log(formattedMessage);
45
+ };
46
+
47
+ const _printWarning = (message, chalk) => {
48
+ const formattedMessage = chalk?.yellow(`⚠ ${message}`) || `⚠ ${message}`;
49
+ console.log(formattedMessage);
50
+ };
51
+
52
+ const _printInfo = (message, chalk) => {
53
+ const formattedMessage = chalk?.blue(message) || message;
54
+ console.log(formattedMessage);
55
+ };
56
+
57
+ /**
58
+ * Execute a git command and return the output
59
+ * @param {string} command - Git command to execute
60
+ * @returns {string|null} Command output or null on error
61
+ */
62
+ const _execGit = (command) => {
63
+ try {
64
+ return execSync(command, { encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim();
65
+ } catch (_error) {
66
+ return null;
67
+ }
68
+ };
69
+
70
+ /**
71
+ * Check if current directory is inside a git repository
72
+ * @returns {boolean}
73
+ */
74
+ const _isGitRepo = () => {
75
+ const result = _execGit('git rev-parse --is-inside-work-tree');
76
+ return result === 'true';
77
+ };
78
+
79
+ /**
80
+ * Get current git branch name
81
+ * @returns {string|null}
82
+ */
83
+ const _getCurrentBranch = () => {
84
+ return _execGit('git rev-parse --abbrev-ref HEAD');
85
+ };
86
+
87
+ /**
88
+ * Check if working tree has uncommitted changes
89
+ * @returns {boolean}
90
+ */
91
+ const _hasUncommittedChanges = () => {
92
+ const status = _execGit('git status --porcelain');
93
+ return status !== null && status.length > 0;
94
+ };
95
+
96
+ /**
97
+ * Check if a branch exists locally or remotely
98
+ * @param {string} branchName - Branch name to check
99
+ * @returns {boolean}
100
+ */
101
+ const _branchExists = (branchName) => {
102
+ // Check local branches
103
+ const localBranches = _execGit('git branch --list');
104
+ if (localBranches && localBranches.split('\n').some(b => b.trim().replace('* ', '') === branchName)) {
105
+ return true;
106
+ }
107
+
108
+ // Check remote branches
109
+ const remoteBranches = _execGit('git branch -r --list');
110
+ if (remoteBranches && remoteBranches.split('\n').some(b => b.trim().includes(branchName))) {
111
+ return true;
112
+ }
113
+
114
+ return false;
115
+ };
116
+
117
+ /**
118
+ * Sanitize plan name for use in branch name
119
+ * @param {string} planName - Original plan name from directory
120
+ * @returns {string} Sanitized branch name segment
121
+ */
122
+ const _sanitizeBranchName = (planName) => {
123
+ return planName
124
+ .toLowerCase()
125
+ .replace(/[^a-z0-9-]/g, '-') // Replace non-alphanumeric chars with hyphens
126
+ .replace(/-+/g, '-') // Collapse multiple hyphens
127
+ .replace(/^-|-$/g, '') // Remove leading/trailing hyphens
128
+ .substring(0, 60); // Max 60 chars
129
+ };
130
+
131
+ /**
132
+ * Extract plan name from plan directory
133
+ * @param {string} planDir - Full path to plan directory
134
+ * @returns {string} Plan name portion (e.g., "update-docs" from "58--update-docs")
135
+ */
136
+ const _extractPlanName = (planDir) => {
137
+ const dirName = path.basename(planDir);
138
+ // Match pattern: {id}--{name}
139
+ const match = dirName.match(/^\d+--(.+)$/);
140
+ return match ? match[1] : dirName;
141
+ };
142
+
143
+ // Main function
144
+ const _main = async (startPath = process.cwd()) => {
145
+ // Initialize chalk
146
+ const chalk = await _initChalk();
147
+
148
+ // Check arguments
149
+ if (process.argv.length < 3) {
150
+ _printError('Missing plan ID argument', chalk);
151
+ console.log('Usage: node create-feature-branch.cjs <plan-id-or-path>');
152
+ console.log('Example: node create-feature-branch.cjs 58');
153
+ process.exit(1);
154
+ }
155
+
156
+ const inputId = process.argv[2];
157
+
158
+ // Step 1: Check if this is a git repository
159
+ if (!_isGitRepo()) {
160
+ _printError('Not a git repository', chalk);
161
+ process.exit(1);
162
+ }
163
+
164
+ // Step 2: Resolve the plan
165
+ const resolved = resolvePlan(inputId, startPath);
166
+
167
+ if (!resolved) {
168
+ _printError(`Plan "${inputId}" not found or invalid`, chalk);
169
+ process.exit(1);
170
+ }
171
+
172
+ const { planDir, planId } = resolved;
173
+ _printInfo(`Found plan: ${path.basename(planDir)}`, chalk);
174
+
175
+ // Step 3: Check current branch
176
+ const currentBranch = _getCurrentBranch();
177
+
178
+ if (!currentBranch) {
179
+ _printError('Could not determine current git branch', chalk);
180
+ process.exit(1);
181
+ }
182
+
183
+ if (currentBranch !== 'main' && currentBranch !== 'master') {
184
+ _printWarning(`Not on main/master branch (current: ${currentBranch})`, chalk);
185
+ _printInfo('Proceeding without creating a new branch', chalk);
186
+ process.exit(0);
187
+ }
188
+
189
+ // Step 4: Check for uncommitted changes
190
+ if (_hasUncommittedChanges()) {
191
+ _printError('Uncommitted changes detected in working tree', chalk);
192
+ _printInfo('Please commit or stash your changes before creating a feature branch', chalk);
193
+ process.exit(1);
194
+ }
195
+
196
+ // Step 5: Build branch name
197
+ const planName = _extractPlanName(planDir);
198
+ const sanitizedName = _sanitizeBranchName(planName);
199
+ const branchName = `feature/${planId}--${sanitizedName}`;
200
+
201
+ // Step 6: Check if branch already exists
202
+ if (_branchExists(branchName)) {
203
+ _printWarning(`Branch "${branchName}" already exists`, chalk);
204
+ _printInfo('Proceeding with existing branch', chalk);
205
+ process.exit(0);
206
+ }
207
+
208
+ // Step 7: Create and checkout the branch
209
+ const createResult = _execGit(`git checkout -b "${branchName}"`);
210
+
211
+ if (createResult === null) {
212
+ _printError(`Failed to create branch "${branchName}"`, chalk);
213
+ process.exit(1);
214
+ }
215
+
216
+ _printSuccess(`Created and switched to branch: ${branchName}`, chalk);
217
+ process.exit(0);
218
+ };
219
+
220
+ // Run the script
221
+ if (require.main === module) {
222
+ _main().catch((error) => {
223
+ console.error('Script execution failed:', error);
224
+ process.exit(1);
225
+ });
226
+ }
227
+
228
+ module.exports = {
229
+ _main,
230
+ _sanitizeBranchName,
231
+ _extractPlanName
232
+ };
@@ -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 from the main 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.