@agentic15.com/agentic15-claude-zen 1.1.0 ā 2.0.1
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/bin/agentic15.js +50 -0
- package/package.json +5 -3
- package/src/cli/AuthCommand.js +195 -0
- package/src/cli/CommitCommand.js +290 -0
- package/src/cli/PlanCommand.js +123 -0
- package/src/cli/StatusCommand.js +156 -0
- package/src/cli/TaskCommand.js +249 -0
- package/templates/.claude/POST-INSTALL.md +18 -367
- package/templates/.claude/settings.json +7 -0
- package/templates/package.json +6 -15
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
import { readFileSync, existsSync } from 'fs';
|
|
2
|
+
import { join } from 'path';
|
|
3
|
+
import { execSync } from 'child_process';
|
|
4
|
+
|
|
5
|
+
export class StatusCommand {
|
|
6
|
+
static show() {
|
|
7
|
+
console.log('\nš Project Status\n');
|
|
8
|
+
|
|
9
|
+
// Load tracker
|
|
10
|
+
const tracker = this.loadTracker();
|
|
11
|
+
|
|
12
|
+
if (!tracker) {
|
|
13
|
+
console.log('ā No active plan found\n');
|
|
14
|
+
process.exit(1);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Calculate statistics
|
|
18
|
+
const completed = tracker.taskFiles.filter(t => t.status === 'completed');
|
|
19
|
+
const inProgress = tracker.taskFiles.filter(t => t.status === 'in_progress');
|
|
20
|
+
const pending = tracker.taskFiles.filter(t => t.status === 'pending');
|
|
21
|
+
const blocked = tracker.taskFiles.filter(t => t.status === 'blocked');
|
|
22
|
+
const total = tracker.taskFiles.length;
|
|
23
|
+
|
|
24
|
+
// Display plan info
|
|
25
|
+
console.log(` Plan: ${tracker.planId}`);
|
|
26
|
+
console.log(` Total Tasks: ${total}\n`);
|
|
27
|
+
|
|
28
|
+
// Progress bar
|
|
29
|
+
const completedPercent = Math.round((completed.length / total) * 100);
|
|
30
|
+
const barLength = 30;
|
|
31
|
+
const filledLength = Math.round((completedPercent / 100) * barLength);
|
|
32
|
+
const bar = 'ā'.repeat(filledLength) + 'ā'.repeat(barLength - filledLength);
|
|
33
|
+
|
|
34
|
+
console.log(` Progress: [${bar}] ${completedPercent}%\n`);
|
|
35
|
+
|
|
36
|
+
// Status breakdown
|
|
37
|
+
console.log(' Status Breakdown:');
|
|
38
|
+
console.log(` ā
Completed: ${completed.length}`);
|
|
39
|
+
console.log(` š In Progress: ${inProgress.length}`);
|
|
40
|
+
console.log(` ā³ Pending: ${pending.length}`);
|
|
41
|
+
|
|
42
|
+
if (blocked.length > 0) {
|
|
43
|
+
console.log(` š« Blocked: ${blocked.length}`);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
console.log('');
|
|
47
|
+
|
|
48
|
+
// Current task
|
|
49
|
+
if (inProgress.length > 0) {
|
|
50
|
+
const current = inProgress[0];
|
|
51
|
+
console.log(' š Current Task:');
|
|
52
|
+
console.log(` ${current.id}: ${current.title}`);
|
|
53
|
+
|
|
54
|
+
// Show changed files
|
|
55
|
+
try {
|
|
56
|
+
const diff = execSync('git diff --name-only', { encoding: 'utf-8' });
|
|
57
|
+
const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
|
|
58
|
+
|
|
59
|
+
const allChanges = new Set([
|
|
60
|
+
...diff.trim().split('\n').filter(Boolean),
|
|
61
|
+
...staged.trim().split('\n').filter(Boolean)
|
|
62
|
+
]);
|
|
63
|
+
|
|
64
|
+
if (allChanges.size > 0) {
|
|
65
|
+
console.log(`\n š Modified files (${allChanges.size}):`);
|
|
66
|
+
Array.from(allChanges).slice(0, 10).forEach(file => {
|
|
67
|
+
console.log(` - ${file}`);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
if (allChanges.size > 10) {
|
|
71
|
+
console.log(` ... and ${allChanges.size - 10} more`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Ignore git errors
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log('');
|
|
79
|
+
console.log(' š” Next step: agentic15 commit');
|
|
80
|
+
} else if (pending.length > 0) {
|
|
81
|
+
console.log(' š Next Task:');
|
|
82
|
+
const next = pending[0];
|
|
83
|
+
console.log(` ${next.id}: ${next.title}`);
|
|
84
|
+
console.log('');
|
|
85
|
+
console.log(' š” Next step: agentic15 task next');
|
|
86
|
+
} else if (blocked.length > 0) {
|
|
87
|
+
console.log(' š« Blocked Tasks:');
|
|
88
|
+
blocked.slice(0, 3).forEach(task => {
|
|
89
|
+
console.log(` ${task.id}: ${task.title}`);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
if (blocked.length > 3) {
|
|
93
|
+
console.log(` ... and ${blocked.length - 3} more`);
|
|
94
|
+
}
|
|
95
|
+
} else {
|
|
96
|
+
console.log(' š All tasks completed!');
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
console.log('');
|
|
100
|
+
|
|
101
|
+
// Recent activity
|
|
102
|
+
const recentCompleted = completed
|
|
103
|
+
.filter(t => t.completedAt)
|
|
104
|
+
.sort((a, b) => new Date(b.completedAt) - new Date(a.completedAt))
|
|
105
|
+
.slice(0, 3);
|
|
106
|
+
|
|
107
|
+
if (recentCompleted.length > 0) {
|
|
108
|
+
console.log(' š Recently Completed:');
|
|
109
|
+
recentCompleted.forEach(task => {
|
|
110
|
+
const date = new Date(task.completedAt);
|
|
111
|
+
const timeAgo = this.getTimeAgo(date);
|
|
112
|
+
console.log(` ā ${task.id}: ${task.title} (${timeAgo})`);
|
|
113
|
+
});
|
|
114
|
+
console.log('');
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
static loadTracker() {
|
|
119
|
+
const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
|
|
120
|
+
|
|
121
|
+
if (!existsSync(activePlanPath)) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
const planId = readFileSync(activePlanPath, 'utf-8').trim();
|
|
126
|
+
const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
|
|
127
|
+
|
|
128
|
+
if (!existsSync(trackerPath)) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return JSON.parse(readFileSync(trackerPath, 'utf-8'));
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
static getTimeAgo(date) {
|
|
136
|
+
const seconds = Math.floor((new Date() - date) / 1000);
|
|
137
|
+
|
|
138
|
+
const intervals = {
|
|
139
|
+
year: 31536000,
|
|
140
|
+
month: 2592000,
|
|
141
|
+
week: 604800,
|
|
142
|
+
day: 86400,
|
|
143
|
+
hour: 3600,
|
|
144
|
+
minute: 60
|
|
145
|
+
};
|
|
146
|
+
|
|
147
|
+
for (const [unit, secondsInUnit] of Object.entries(intervals)) {
|
|
148
|
+
const interval = Math.floor(seconds / secondsInUnit);
|
|
149
|
+
if (interval >= 1) {
|
|
150
|
+
return `${interval} ${unit}${interval === 1 ? '' : 's'} ago`;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return 'just now';
|
|
155
|
+
}
|
|
156
|
+
}
|
|
@@ -0,0 +1,249 @@
|
|
|
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
|
+
import { TaskIssueMapper } from '../core/TaskIssueMapper.js';
|
|
7
|
+
|
|
8
|
+
export class TaskCommand {
|
|
9
|
+
static async handle(action, taskId) {
|
|
10
|
+
switch (action) {
|
|
11
|
+
case 'start':
|
|
12
|
+
return this.startTask(taskId);
|
|
13
|
+
case 'next':
|
|
14
|
+
return this.startNext();
|
|
15
|
+
case 'status':
|
|
16
|
+
return this.showStatus();
|
|
17
|
+
default:
|
|
18
|
+
console.log(`\nā Unknown action: ${action}`);
|
|
19
|
+
console.log(' Valid actions: start, next, status\n');
|
|
20
|
+
process.exit(1);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
static async startTask(taskId) {
|
|
25
|
+
if (!taskId) {
|
|
26
|
+
console.log('\nā Task ID required for "start" action');
|
|
27
|
+
console.log(' Usage: agentic15 task start TASK-001\n');
|
|
28
|
+
process.exit(1);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Load task tracker
|
|
32
|
+
const tracker = this.loadTracker();
|
|
33
|
+
const task = tracker.taskFiles.find(t => t.id === taskId);
|
|
34
|
+
|
|
35
|
+
if (!task) {
|
|
36
|
+
console.log(`\nā Task not found: ${taskId}\n`);
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (task.status === 'completed') {
|
|
41
|
+
console.log(`\nā ļø Task ${taskId} is already completed\n`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Check if another task is in progress
|
|
46
|
+
const inProgress = tracker.taskFiles.find(t => t.status === 'in_progress');
|
|
47
|
+
if (inProgress && inProgress.id !== taskId) {
|
|
48
|
+
console.log(`\nā ļø Task ${inProgress.id} is already in progress`);
|
|
49
|
+
console.log(` Complete it first with: agentic15 commit\n`);
|
|
50
|
+
process.exit(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// Create feature branch
|
|
54
|
+
const branchName = `feature/${taskId.toLowerCase()}`;
|
|
55
|
+
try {
|
|
56
|
+
execSync(`git checkout -b ${branchName}`, { stdio: 'inherit' });
|
|
57
|
+
} catch (error) {
|
|
58
|
+
// Branch might already exist
|
|
59
|
+
try {
|
|
60
|
+
execSync(`git checkout ${branchName}`, { stdio: 'inherit' });
|
|
61
|
+
} catch (e) {
|
|
62
|
+
console.log(`\nā Failed to create/checkout branch: ${branchName}\n`);
|
|
63
|
+
process.exit(1);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Update task status
|
|
68
|
+
task.status = 'in_progress';
|
|
69
|
+
task.startedAt = new Date().toISOString();
|
|
70
|
+
this.saveTracker(tracker);
|
|
71
|
+
|
|
72
|
+
// Create GitHub issue if enabled
|
|
73
|
+
let githubIssue = null;
|
|
74
|
+
const config = new GitHubConfig(process.cwd());
|
|
75
|
+
if (config.isAutoCreateEnabled()) {
|
|
76
|
+
githubIssue = await this.createGitHubIssue(task, config);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Display task details
|
|
80
|
+
this.displayTaskDetails(task, githubIssue, tracker);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
static async startNext() {
|
|
84
|
+
const tracker = this.loadTracker();
|
|
85
|
+
|
|
86
|
+
// Find next pending task
|
|
87
|
+
const nextTask = tracker.taskFiles.find(t => t.status === 'pending');
|
|
88
|
+
|
|
89
|
+
if (!nextTask) {
|
|
90
|
+
console.log('\nā
No more pending tasks!\n');
|
|
91
|
+
const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
|
|
92
|
+
const total = tracker.taskFiles.length;
|
|
93
|
+
console.log(` Progress: ${completed}/${total} tasks completed\n`);
|
|
94
|
+
process.exit(0);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
console.log(`\nā¶ļø Auto-starting next task: ${nextTask.id}\n`);
|
|
98
|
+
return this.startTask(nextTask.id);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
static showStatus() {
|
|
102
|
+
const tracker = this.loadTracker();
|
|
103
|
+
|
|
104
|
+
const inProgress = tracker.taskFiles.find(t => t.status === 'in_progress');
|
|
105
|
+
const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
|
|
106
|
+
const pending = tracker.taskFiles.filter(t => t.status === 'pending').length;
|
|
107
|
+
const total = tracker.taskFiles.length;
|
|
108
|
+
|
|
109
|
+
console.log('\nš Task Status\n');
|
|
110
|
+
console.log(` Plan: ${tracker.planId}`);
|
|
111
|
+
console.log(` Progress: ${completed}/${total} completed (${pending} pending)\n`);
|
|
112
|
+
|
|
113
|
+
if (inProgress) {
|
|
114
|
+
console.log(` š Currently working on: ${inProgress.id}`);
|
|
115
|
+
console.log(` š ${inProgress.title}`);
|
|
116
|
+
|
|
117
|
+
// Show changed files
|
|
118
|
+
try {
|
|
119
|
+
const diff = execSync('git diff --name-only', { encoding: 'utf-8' });
|
|
120
|
+
if (diff.trim()) {
|
|
121
|
+
console.log(`\n š Changed files:`);
|
|
122
|
+
diff.trim().split('\n').forEach(file => {
|
|
123
|
+
console.log(` - ${file}`);
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
} catch (e) {
|
|
127
|
+
// Ignore
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
console.log(' ā¹ļø No task currently in progress');
|
|
131
|
+
console.log(` Run: agentic15 task next\n`);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
console.log('');
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
static async createGitHubIssue(task, config) {
|
|
138
|
+
try {
|
|
139
|
+
const client = new GitHubClient(
|
|
140
|
+
config.getToken(),
|
|
141
|
+
config.getRepoInfo().owner,
|
|
142
|
+
config.getRepoInfo().repo
|
|
143
|
+
);
|
|
144
|
+
|
|
145
|
+
if (!client.isConfigured()) {
|
|
146
|
+
console.log('\nā ļø GitHub not configured. Skipping issue creation.');
|
|
147
|
+
console.log(' Run: agentic15 auth setup\n');
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Load full task details
|
|
152
|
+
const taskPath = this.getTaskPath(task.id);
|
|
153
|
+
const taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
154
|
+
|
|
155
|
+
const { title, body, labels } = TaskIssueMapper.mapTaskToIssue(taskData);
|
|
156
|
+
const issueNumber = await client.createIssue(title, body, labels);
|
|
157
|
+
|
|
158
|
+
if (issueNumber) {
|
|
159
|
+
// Save issue number to task
|
|
160
|
+
taskData.githubIssue = issueNumber;
|
|
161
|
+
writeFileSync(taskPath, JSON.stringify(taskData, null, 2));
|
|
162
|
+
|
|
163
|
+
console.log(`\nā Created GitHub issue #${issueNumber}`);
|
|
164
|
+
const repoInfo = config.getRepoInfo();
|
|
165
|
+
console.log(` https://github.com/${repoInfo.owner}/${repoInfo.repo}/issues/${issueNumber}\n`);
|
|
166
|
+
|
|
167
|
+
return issueNumber;
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
console.log(`\nā ļø Failed to create GitHub issue: ${error.message}\n`);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
static displayTaskDetails(task, githubIssue, tracker) {
|
|
177
|
+
console.log(`\nā
Started task: ${task.id}`);
|
|
178
|
+
console.log(`š Plan: ${tracker.planId}\n`);
|
|
179
|
+
console.log(`š ${task.title}`);
|
|
180
|
+
|
|
181
|
+
if (task.description) {
|
|
182
|
+
console.log(`š ${task.description}\n`);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if (task.phase) {
|
|
186
|
+
console.log(`š§ Phase: ${task.phase}`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (githubIssue) {
|
|
190
|
+
const config = new GitHubConfig(process.cwd());
|
|
191
|
+
const repoInfo = config.getRepoInfo();
|
|
192
|
+
console.log(`š GitHub Issue: https://github.com/${repoInfo.owner}/${repoInfo.repo}/issues/${githubIssue}`);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Load full task for completion criteria
|
|
196
|
+
try {
|
|
197
|
+
const taskPath = this.getTaskPath(task.id);
|
|
198
|
+
const taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
|
|
199
|
+
|
|
200
|
+
if (taskData.completionCriteria && taskData.completionCriteria.length > 0) {
|
|
201
|
+
console.log(`\nā Completion criteria:`);
|
|
202
|
+
taskData.completionCriteria.forEach((criteria, idx) => {
|
|
203
|
+
console.log(` ${idx + 1}. ${criteria}`);
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
} catch (e) {
|
|
207
|
+
// Ignore
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
console.log(`\nš” Next steps:`);
|
|
211
|
+
console.log(` 1. Tell Claude: "Write code for ${task.id}"`);
|
|
212
|
+
console.log(` 2. When done: agentic15 commit\n`);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
static loadTracker() {
|
|
216
|
+
const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
|
|
217
|
+
|
|
218
|
+
if (!existsSync(activePlanPath)) {
|
|
219
|
+
console.log('\nā No active plan found');
|
|
220
|
+
console.log(' Run: agentic15 plan "project description"\n');
|
|
221
|
+
process.exit(1);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
const planId = readFileSync(activePlanPath, 'utf-8').trim();
|
|
225
|
+
const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
|
|
226
|
+
|
|
227
|
+
if (!existsSync(trackerPath)) {
|
|
228
|
+
console.log('\nā Task tracker not found');
|
|
229
|
+
console.log(' Run: agentic15 plan\n');
|
|
230
|
+
process.exit(1);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return JSON.parse(readFileSync(trackerPath, 'utf-8'));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
static saveTracker(tracker) {
|
|
237
|
+
const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
|
|
238
|
+
const planId = readFileSync(activePlanPath, 'utf-8').trim();
|
|
239
|
+
const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
|
|
240
|
+
|
|
241
|
+
writeFileSync(trackerPath, JSON.stringify(tracker, null, 2));
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
static getTaskPath(taskId) {
|
|
245
|
+
const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
|
|
246
|
+
const planId = readFileSync(activePlanPath, 'utf-8').trim();
|
|
247
|
+
return join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${taskId}.json`);
|
|
248
|
+
}
|
|
249
|
+
}
|