@agentic15.com/agentic15-claude-zen 4.0.2 โ†’ 4.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,331 +1,316 @@
1
- import { execSync } from 'child_process';
2
- import { readFileSync, writeFileSync, existsSync } from 'fs';
3
- import { join } from 'path';
4
- import { GitHubClient } from '../core/GitHubClient.js';
5
- import { GitHubConfig } from '../core/GitHubConfig.js';
6
-
7
- export class CommitCommand {
8
- static async execute() {
9
- console.log('\n๐Ÿš€ Starting commit workflow...\n');
10
-
11
- // Step 1: Find active task
12
- const { task, tracker } = this.getActiveTask();
13
-
14
- if (!task) {
15
- console.log('โŒ No task in progress');
16
- console.log(' Start a task first: agentic15 task start TASK-001\n');
17
- process.exit(1);
18
- }
19
-
20
- console.log(`๐Ÿ“Œ Task: ${task.id} - ${task.title}\n`);
21
-
22
- // Step 2: Run tests
23
- console.log('๐Ÿงช Running tests...\n');
24
- this.runTests();
25
-
26
- // Step 3: Stage files in Agent/
27
- console.log('\n๐Ÿ“ฆ Staging changes...\n');
28
- this.stageFiles();
29
-
30
- // Step 4: Generate commit message
31
- const commitMessage = this.generateCommitMessage(task);
32
-
33
- // Step 5: Commit
34
- console.log('๐Ÿ’พ Creating commit...\n');
35
- this.createCommit(commitMessage);
36
-
37
- // Step 6: Push to feature branch
38
- console.log('โฌ†๏ธ Pushing to remote...\n');
39
- this.pushBranch(task.id);
40
-
41
- // Step 7: Create PR
42
- console.log('๐Ÿ”€ Creating pull request...\n');
43
- const prUrl = await this.createPullRequest(task, commitMessage);
44
-
45
- // Step 8: Update GitHub issue status
46
- await this.updateGitHubIssue(task, prUrl);
47
-
48
- // Step 9: Mark task as completed
49
- this.markTaskCompleted(task);
50
-
51
- // Step 10: Display summary
52
- this.displaySummary(task, prUrl, tracker);
53
- }
54
-
55
- static getActiveTask() {
56
- const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
57
-
58
- if (!existsSync(activePlanPath)) {
59
- console.log('\nโŒ No active plan found\n');
60
- process.exit(1);
61
- }
62
-
63
- const planId = readFileSync(activePlanPath, 'utf-8').trim();
64
- const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
65
-
66
- if (!existsSync(trackerPath)) {
67
- console.log('\nโŒ Task tracker not found\n');
68
- process.exit(1);
69
- }
70
-
71
- const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
72
- const task = tracker.taskFiles.find(t => t.status === 'in_progress');
73
-
74
- return { task, tracker };
75
- }
76
-
77
- static runTests() {
78
- try {
79
- execSync('npm test', { stdio: 'inherit' });
80
- console.log('\nโœ… All tests passed');
81
- } catch (error) {
82
- console.log('\nโŒ Tests failed. Fix errors before committing.\n');
83
- process.exit(1);
84
- }
85
- }
86
-
87
- static stageFiles() {
88
- try {
89
- // Stage all files in Agent/
90
- execSync('git add Agent/', { stdio: 'inherit' });
91
-
92
- // Also stage scripts/ if exists
93
- try {
94
- execSync('git add scripts/', { stdio: 'pipe' });
95
- } catch (e) {
96
- // scripts/ might not exist
97
- }
98
-
99
- // Show what was staged
100
- const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
101
-
102
- if (!staged.trim()) {
103
- console.log('โš ๏ธ No changes to commit\n');
104
- process.exit(0);
105
- }
106
-
107
- console.log('Staged files:');
108
- staged.trim().split('\n').forEach(file => {
109
- console.log(` โœ“ ${file}`);
110
- });
111
- } catch (error) {
112
- console.log(`\nโŒ Failed to stage files: ${error.message}\n`);
113
- process.exit(1);
114
- }
115
- }
116
-
117
- static generateCommitMessage(task) {
118
- // Load full task details
119
- const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
120
- const planId = readFileSync(activePlanPath, 'utf-8').trim();
121
- const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
122
-
123
- let taskData;
124
- try {
125
- taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
126
- } catch (e) {
127
- taskData = task;
128
- }
129
-
130
- // Format: [TASK-001] Task title
131
- return `[${task.id}] ${taskData.title || task.title}`;
132
- }
133
-
134
- static createCommit(message) {
135
- try {
136
- // Escape double quotes and backslashes for shell
137
- const escapedMessage = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
138
- execSync(`git commit -m "${escapedMessage}"`, { stdio: 'inherit' });
139
- console.log('โœ… Commit created');
140
- } catch (error) {
141
- console.log(`\nโŒ Failed to create commit: ${error.message}\n`);
142
- process.exit(1);
143
- }
144
- }
145
-
146
- static pushBranch(taskId) {
147
- const branchName = `feature/${taskId.toLowerCase()}`;
148
-
149
- try {
150
- // Check if branch has upstream
151
- try {
152
- execSync('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { stdio: 'pipe' });
153
- // Has upstream, just push
154
- execSync('git push', { stdio: 'inherit' });
155
- } catch (e) {
156
- // No upstream, set it
157
- execSync(`git push -u origin ${branchName}`, { stdio: 'inherit' });
158
- }
159
-
160
- console.log('โœ… Pushed to remote');
161
- } catch (error) {
162
- console.log(`\nโŒ Failed to push: ${error.message}\n`);
163
- process.exit(1);
164
- }
165
- }
166
-
167
- static async createPullRequest(task, commitMessage) {
168
- try {
169
- // Get main branch name
170
- const mainBranch = this.getMainBranch();
171
-
172
- // Load task for issue number
173
- const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
174
- const planId = readFileSync(activePlanPath, 'utf-8').trim();
175
- const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
176
-
177
- let taskData;
178
- try {
179
- taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
180
- } catch (e) {
181
- taskData = task;
182
- }
183
-
184
- // Build PR body following .github/PULL_REQUEST_TEMPLATE.md structure
185
- let prBody = `## Task\n\n`;
186
-
187
- if (taskData.githubIssue) {
188
- prBody += `Closes #${taskData.githubIssue}\n\n`;
189
- } else {
190
- prBody += `${task.id}\n\n`;
191
- }
192
-
193
- prBody += `## Description\n\n`;
194
- prBody += `${taskData.description || task.description || commitMessage}\n\n`;
195
-
196
- prBody += `## Changes\n\n`;
197
- prBody += `- Implemented ${task.title}\n\n`;
198
-
199
- prBody += `## Testing\n\n`;
200
- prBody += `- [x] Unit tests pass (\`npm test\`)\n`;
201
- prBody += `- [ ] Visual tests pass (if applicable) (\`npx playwright test\`)\n`;
202
- prBody += `- [ ] Code follows project conventions\n\n`;
203
-
204
- prBody += `## Notes\n\n`;
205
- prBody += `Auto-generated by Agentic15 Claude Zen`;
206
-
207
- // Create PR using gh CLI
208
- const prCommand = `gh pr create --title "${commitMessage}" --body "${prBody}" --base ${mainBranch}`;
209
- const prOutput = execSync(prCommand, { encoding: 'utf-8' });
210
-
211
- // Extract PR URL from output
212
- const prUrl = prOutput.match(/https:\/\/github\.com\/[^\s]+/)?.[0];
213
-
214
- if (prUrl) {
215
- console.log(`โœ… Pull request created: ${prUrl}`);
216
- return prUrl;
217
- }
218
-
219
- console.log('โœ… Pull request created');
220
- return null;
221
- } catch (error) {
222
- console.log(`\nโš ๏ธ Failed to create PR: ${error.message}`);
223
- console.log(' You may need to install GitHub CLI: https://cli.github.com/\n');
224
- return null;
225
- }
226
- }
227
-
228
- static async updateGitHubIssue(task, prUrl) {
229
- const config = new GitHubConfig(process.cwd());
230
-
231
- if (!config.isAutoUpdateEnabled()) {
232
- return;
233
- }
234
-
235
- try {
236
- // Load task data
237
- const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
238
- const planId = readFileSync(activePlanPath, 'utf-8').trim();
239
- const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
240
-
241
- const taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
242
-
243
- if (!taskData.githubIssue) {
244
- return;
245
- }
246
-
247
- const client = new GitHubClient(
248
- config.getToken(),
249
- config.getRepoInfo().owner,
250
- config.getRepoInfo().repo
251
- );
252
-
253
- if (!client.isConfigured()) {
254
- return;
255
- }
256
-
257
- // Update labels to "in review"
258
- await client.updateIssueLabels(taskData.githubIssue, ['status: in-review', `phase: ${taskData.phase || 'implementation'}`]);
259
-
260
- // Add comment with PR link
261
- if (prUrl) {
262
- const comment = `Pull request created: ${prUrl}\n\nTask is now in code review.`;
263
- await client.addIssueComment(taskData.githubIssue, comment);
264
- }
265
-
266
- console.log(`โœ… Updated GitHub issue #${taskData.githubIssue}`);
267
- } catch (error) {
268
- console.log(`\nโš ๏ธ Failed to update GitHub issue: ${error.message}\n`);
269
- }
270
- }
271
-
272
- static markTaskCompleted(task) {
273
- try {
274
- const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
275
- const planId = readFileSync(activePlanPath, 'utf-8').trim();
276
- const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
277
-
278
- // Update tracker
279
- const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
280
- const taskInTracker = tracker.taskFiles.find(t => t.id === task.id);
281
-
282
- if (taskInTracker) {
283
- taskInTracker.status = 'completed';
284
- taskInTracker.completedAt = new Date().toISOString();
285
- writeFileSync(trackerPath, JSON.stringify(tracker, null, 2));
286
- console.log(`\nโœ… Marked ${task.id} as completed`);
287
- }
288
- } catch (error) {
289
- console.log(`\nโš ๏ธ Failed to mark task as completed: ${error.message}`);
290
- console.log(' You may need to manually update TASK-TRACKER.json\n');
291
- }
292
- }
293
-
294
- static displaySummary(task, prUrl, tracker) {
295
- console.log('\nโ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”');
296
- console.log('โ”‚ โœ… Commit Workflow Complete โ”‚');
297
- console.log('โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n');
298
-
299
- console.log(`๐Ÿ“Œ Task: ${task.id} - ${task.title}`);
300
-
301
- if (prUrl) {
302
- console.log(`๐Ÿ”— PR: ${prUrl}`);
303
- }
304
-
305
- const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
306
- const total = tracker.taskFiles.length;
307
-
308
- console.log(`\n๐Ÿ“Š Progress: ${completed}/${total} completed\n`);
309
-
310
- console.log('๐Ÿ’ก Next steps:');
311
- console.log(' 1. Review and merge PR on GitHub');
312
- console.log(' 2. After merge, run: agentic15 task next\n');
313
- }
314
-
315
- static getMainBranch() {
316
- try {
317
- // Try to detect main branch
318
- const branches = execSync('git branch -r', { encoding: 'utf-8' });
319
-
320
- if (branches.includes('origin/main')) {
321
- return 'main';
322
- } else if (branches.includes('origin/master')) {
323
- return 'master';
324
- }
325
-
326
- return 'main'; // Default
327
- } catch (e) {
328
- return 'main';
329
- }
330
- }
331
- }
1
+ import { execSync } from 'child_process';
2
+ import { readFileSync, writeFileSync, existsSync } from 'fs';
3
+ import { join } from 'path';
4
+ import { GitHubClient } from '../core/GitHubClient.js';
5
+ import { GitHubConfig } from '../core/GitHubConfig.js';
6
+
7
+ export class CommitCommand {
8
+ static async execute() {
9
+ console.log('\n๐Ÿš€ Starting commit workflow...\n');
10
+
11
+ // Step 1: Find active task
12
+ const { task, tracker } = this.getActiveTask();
13
+
14
+ if (!task) {
15
+ console.log('โŒ No task in progress');
16
+ console.log(' Start a task first: agentic15 task start TASK-001\n');
17
+ process.exit(1);
18
+ }
19
+
20
+ console.log(`๐Ÿ“Œ Task: ${task.id} - ${task.title}\n`);
21
+
22
+ // Step 2: Stage files in Agent/
23
+ console.log('๐Ÿ“ฆ Staging changes...\n');
24
+ this.stageFiles();
25
+
26
+ // Step 3: Generate commit message
27
+ const commitMessage = this.generateCommitMessage(task);
28
+
29
+ // Step 4: Commit
30
+ console.log('๐Ÿ’พ Creating commit...\n');
31
+ this.createCommit(commitMessage);
32
+
33
+ // Step 5: Push to feature branch
34
+ console.log('โฌ†๏ธ Pushing to remote...\n');
35
+ this.pushBranch(task.id);
36
+
37
+ // Step 6: Create PR
38
+ console.log('๐Ÿ”€ Creating pull request...\n');
39
+ const prUrl = await this.createPullRequest(task, commitMessage);
40
+
41
+ // Step 7: Update GitHub issue status
42
+ await this.updateGitHubIssue(task, prUrl);
43
+
44
+ // Step 8: Mark task as completed
45
+ this.markTaskCompleted(task);
46
+
47
+ // Step 9: Display summary
48
+ this.displaySummary(task, prUrl, tracker);
49
+ }
50
+
51
+ static getActiveTask() {
52
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
53
+
54
+ if (!existsSync(activePlanPath)) {
55
+ console.log('\nโŒ No active plan found\n');
56
+ process.exit(1);
57
+ }
58
+
59
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
60
+ const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
61
+
62
+ if (!existsSync(trackerPath)) {
63
+ console.log('\nโŒ Task tracker not found\n');
64
+ process.exit(1);
65
+ }
66
+
67
+ const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
68
+ const task = tracker.taskFiles.find(t => t.status === 'in_progress');
69
+
70
+ return { task, tracker };
71
+ }
72
+
73
+ static stageFiles() {
74
+ try {
75
+ // Stage all files in Agent/
76
+ execSync('git add Agent/', { stdio: 'inherit' });
77
+
78
+ // Also stage scripts/ if exists
79
+ try {
80
+ execSync('git add scripts/', { stdio: 'pipe' });
81
+ } catch (e) {
82
+ // scripts/ might not exist
83
+ }
84
+
85
+ // Show what was staged
86
+ const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
87
+
88
+ if (!staged.trim()) {
89
+ console.log('โš ๏ธ No changes to commit\n');
90
+ process.exit(0);
91
+ }
92
+
93
+ console.log('Staged files:');
94
+ staged.trim().split('\n').forEach(file => {
95
+ console.log(` โœ“ ${file}`);
96
+ });
97
+ } catch (error) {
98
+ console.log(`\nโŒ Failed to stage files: ${error.message}\n`);
99
+ process.exit(1);
100
+ }
101
+ }
102
+
103
+ static generateCommitMessage(task) {
104
+ // Load full task details
105
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
106
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
107
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
108
+
109
+ let taskData;
110
+ try {
111
+ taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
112
+ } catch (e) {
113
+ taskData = task;
114
+ }
115
+
116
+ // Format: [TASK-001] Task title
117
+ return `[${task.id}] ${taskData.title || task.title}`;
118
+ }
119
+
120
+ static createCommit(message) {
121
+ try {
122
+ // Escape double quotes and backslashes for shell
123
+ const escapedMessage = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
124
+ execSync(`git commit -m "${escapedMessage}"`, { stdio: 'inherit' });
125
+ console.log('โœ… Commit created');
126
+ } catch (error) {
127
+ console.log(`\nโŒ Failed to create commit: ${error.message}\n`);
128
+ process.exit(1);
129
+ }
130
+ }
131
+
132
+ static pushBranch(taskId) {
133
+ const branchName = `feature/${taskId.toLowerCase()}`;
134
+
135
+ try {
136
+ // Check if branch has upstream
137
+ try {
138
+ execSync('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { stdio: 'pipe' });
139
+ // Has upstream, just push
140
+ execSync('git push', { stdio: 'inherit' });
141
+ } catch (e) {
142
+ // No upstream, set it
143
+ execSync(`git push -u origin ${branchName}`, { stdio: 'inherit' });
144
+ }
145
+
146
+ console.log('โœ… Pushed to remote');
147
+ } catch (error) {
148
+ console.log(`\nโŒ Failed to push: ${error.message}\n`);
149
+ process.exit(1);
150
+ }
151
+ }
152
+
153
+ static async createPullRequest(task, commitMessage) {
154
+ try {
155
+ // Get main branch name
156
+ const mainBranch = this.getMainBranch();
157
+
158
+ // Load task for issue number
159
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
160
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
161
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
162
+
163
+ let taskData;
164
+ try {
165
+ taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
166
+ } catch (e) {
167
+ taskData = task;
168
+ }
169
+
170
+ // Build PR body following .github/PULL_REQUEST_TEMPLATE.md structure
171
+ let prBody = `## Task\n\n`;
172
+
173
+ if (taskData.githubIssue) {
174
+ prBody += `Closes #${taskData.githubIssue}\n\n`;
175
+ } else {
176
+ prBody += `${task.id}\n\n`;
177
+ }
178
+
179
+ prBody += `## Description\n\n`;
180
+ prBody += `${taskData.description || task.description || commitMessage}\n\n`;
181
+
182
+ prBody += `## Changes\n\n`;
183
+ prBody += `- Implemented ${task.title}\n\n`;
184
+
185
+ prBody += `## Testing\n\n`;
186
+ prBody += `- [ ] Code follows project conventions\n`;
187
+ prBody += `- [ ] Manual testing completed\n\n`;
188
+
189
+ prBody += `## Notes\n\n`;
190
+ prBody += `Auto-generated by Agentic15 Claude Zen`;
191
+
192
+ // Create PR using gh CLI
193
+ const prCommand = `gh pr create --title "${commitMessage}" --body "${prBody}" --base ${mainBranch}`;
194
+ const prOutput = execSync(prCommand, { encoding: 'utf-8' });
195
+
196
+ // Extract PR URL from output
197
+ const prUrl = prOutput.match(/https:\/\/github\.com\/[^\s]+/)?.[0];
198
+
199
+ if (prUrl) {
200
+ console.log(`โœ… Pull request created: ${prUrl}`);
201
+ return prUrl;
202
+ }
203
+
204
+ console.log('โœ… Pull request created');
205
+ return null;
206
+ } catch (error) {
207
+ console.log(`\nโš ๏ธ Failed to create PR: ${error.message}`);
208
+ console.log(' You may need to install GitHub CLI: https://cli.github.com/\n');
209
+ return null;
210
+ }
211
+ }
212
+
213
+ static async updateGitHubIssue(task, prUrl) {
214
+ const config = new GitHubConfig(process.cwd());
215
+
216
+ if (!config.isAutoUpdateEnabled()) {
217
+ return;
218
+ }
219
+
220
+ try {
221
+ // Load task data
222
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
223
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
224
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
225
+
226
+ const taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
227
+
228
+ if (!taskData.githubIssue) {
229
+ return;
230
+ }
231
+
232
+ const client = new GitHubClient(
233
+ config.getToken(),
234
+ config.getRepoInfo().owner,
235
+ config.getRepoInfo().repo
236
+ );
237
+
238
+ if (!client.isConfigured()) {
239
+ return;
240
+ }
241
+
242
+ // Update labels to "in review"
243
+ await client.updateIssueLabels(taskData.githubIssue, ['status: in-review', `phase: ${taskData.phase || 'implementation'}`]);
244
+
245
+ // Add comment with PR link
246
+ if (prUrl) {
247
+ const comment = `Pull request created: ${prUrl}\n\nTask is now in code review.`;
248
+ await client.addIssueComment(taskData.githubIssue, comment);
249
+ }
250
+
251
+ console.log(`โœ… Updated GitHub issue #${taskData.githubIssue}`);
252
+ } catch (error) {
253
+ console.log(`\nโš ๏ธ Failed to update GitHub issue: ${error.message}\n`);
254
+ }
255
+ }
256
+
257
+ static markTaskCompleted(task) {
258
+ try {
259
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
260
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
261
+ const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
262
+
263
+ // Update tracker
264
+ const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
265
+ const taskInTracker = tracker.taskFiles.find(t => t.id === task.id);
266
+
267
+ if (taskInTracker) {
268
+ taskInTracker.status = 'completed';
269
+ taskInTracker.completedAt = new Date().toISOString();
270
+ writeFileSync(trackerPath, JSON.stringify(tracker, null, 2));
271
+ console.log(`\nโœ… Marked ${task.id} as completed`);
272
+ }
273
+ } catch (error) {
274
+ console.log(`\nโš ๏ธ Failed to mark task as completed: ${error.message}`);
275
+ console.log(' You may need to manually update TASK-TRACKER.json\n');
276
+ }
277
+ }
278
+
279
+ static displaySummary(task, prUrl, tracker) {
280
+ console.log('\nโ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”');
281
+ console.log('โ”‚ โœ… Commit Workflow Complete โ”‚');
282
+ console.log('โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜\n');
283
+
284
+ console.log(`๐Ÿ“Œ Task: ${task.id} - ${task.title}`);
285
+
286
+ if (prUrl) {
287
+ console.log(`๐Ÿ”— PR: ${prUrl}`);
288
+ }
289
+
290
+ const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
291
+ const total = tracker.taskFiles.length;
292
+
293
+ console.log(`\n๐Ÿ“Š Progress: ${completed}/${total} completed\n`);
294
+
295
+ console.log('๐Ÿ’ก Next steps:');
296
+ console.log(' 1. Review and merge PR on GitHub');
297
+ console.log(' 2. After merge, run: agentic15 task next\n');
298
+ }
299
+
300
+ static getMainBranch() {
301
+ try {
302
+ // Try to detect main branch
303
+ const branches = execSync('git branch -r', { encoding: 'utf-8' });
304
+
305
+ if (branches.includes('origin/main')) {
306
+ return 'main';
307
+ } else if (branches.includes('origin/master')) {
308
+ return 'master';
309
+ }
310
+
311
+ return 'main'; // Default
312
+ } catch (e) {
313
+ return 'main';
314
+ }
315
+ }
316
+ }