@agentic15.com/agentic15-claude-zen 1.1.0 → 2.0.0

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.
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import { AuthCommand } from '../src/cli/AuthCommand.js';
5
+ import { TaskCommand } from '../src/cli/TaskCommand.js';
6
+ import { CommitCommand } from '../src/cli/CommitCommand.js';
7
+ import { StatusCommand } from '../src/cli/StatusCommand.js';
8
+ import { PlanCommand } from '../src/cli/PlanCommand.js';
9
+
10
+ const program = new Command();
11
+
12
+ program
13
+ .name('agentic15')
14
+ .description('Agentic15 Claude Zen - Automated AI development workflow')
15
+ .version('2.0.0');
16
+
17
+ // GitHub authentication setup
18
+ program
19
+ .command('auth')
20
+ .description('GitHub authentication setup')
21
+ .action(() => AuthCommand.setup());
22
+
23
+ // Task management
24
+ program
25
+ .command('task')
26
+ .description('Task management')
27
+ .argument('<action>', 'Action: start, next, status')
28
+ .argument('[taskId]', 'Task ID (e.g., TASK-001) - required for "start"')
29
+ .action((action, taskId) => TaskCommand.handle(action, taskId));
30
+
31
+ // Auto-commit workflow
32
+ program
33
+ .command('commit')
34
+ .description('Run tests, commit, push, create PR')
35
+ .action(() => CommitCommand.execute());
36
+
37
+ // Show status
38
+ program
39
+ .command('status')
40
+ .description('Show current task status and progress')
41
+ .action(() => StatusCommand.show());
42
+
43
+ // Plan management (single command for generate + lock)
44
+ program
45
+ .command('plan')
46
+ .description('Generate and lock plan')
47
+ .argument('[description]', 'Project description (required for first run)')
48
+ .action((description) => PlanCommand.handle(description));
49
+
50
+ program.parse();
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "@agentic15.com/agentic15-claude-zen",
3
- "version": "1.1.0",
3
+ "version": "2.0.0",
4
4
  "description": "Structured AI-assisted development framework for Claude Code with enforced quality standards",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
7
- "agentic15-claude-zen": "./bin/create-agentic15-claude-zen.js"
7
+ "agentic15-claude-zen": "./bin/create-agentic15-claude-zen.js",
8
+ "agentic15": "./bin/agentic15.js"
8
9
  },
9
10
  "type": "module",
10
11
  "scripts": {
@@ -54,7 +55,8 @@
54
55
  "node": ">=18.0.0"
55
56
  },
56
57
  "dependencies": {
57
- "@octokit/rest": "^20.0.2"
58
+ "@octokit/rest": "^20.0.2",
59
+ "commander": "^12.1.0"
58
60
  },
59
61
  "devDependencies": {
60
62
  "@playwright/test": "^1.57.0",
@@ -0,0 +1,195 @@
1
+ import { execSync } from 'child_process';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync } from 'fs';
3
+ import { join, dirname } from 'path';
4
+ import { fileURLToPath } from 'url';
5
+ import readline from 'readline';
6
+ import { Octokit } from '@octokit/rest';
7
+
8
+ const __filename = fileURLToPath(import.meta.url);
9
+ const __dirname = dirname(__filename);
10
+
11
+ export class AuthCommand {
12
+ static async setup() {
13
+ console.log('\nšŸ” GitHub Authentication Setup\n');
14
+
15
+ // Step 1: Display current git configuration
16
+ this.displayGitConfig();
17
+
18
+ // Step 2: Confirm configuration
19
+ const configOk = await this.confirmConfig();
20
+ if (!configOk) {
21
+ this.showConfigCommands();
22
+ console.log('\nāŒ Setup cancelled. Please configure git first.\n');
23
+ process.exit(1);
24
+ }
25
+
26
+ // Step 3: Get GitHub token
27
+ const token = await this.promptToken();
28
+
29
+ // Step 4: Validate token
30
+ const isValid = await this.validateToken(token);
31
+ if (!isValid) {
32
+ console.log('\nāŒ Invalid GitHub token. Please try again.\n');
33
+ process.exit(1);
34
+ }
35
+
36
+ // Step 5: Auto-detect owner/repo
37
+ const { owner, repo } = this.detectRepo();
38
+
39
+ // Step 6: Save configuration
40
+ this.saveConfig(token, owner, repo);
41
+
42
+ console.log('\nāœ… GitHub authentication configured successfully!\n');
43
+ console.log(` Owner: ${owner}`);
44
+ console.log(` Repo: ${repo}`);
45
+ console.log(` Config: .claude/settings.local.json\n`);
46
+ }
47
+
48
+ static displayGitConfig() {
49
+ console.log('šŸ“‹ Current Git Configuration:\n');
50
+
51
+ try {
52
+ // Global config
53
+ const globalName = execSync('git config --global user.name', { encoding: 'utf-8' }).trim();
54
+ const globalEmail = execSync('git config --global user.email', { encoding: 'utf-8' }).trim();
55
+
56
+ console.log(' Global:');
57
+ console.log(` - user.name: ${globalName || '(not set)'}`);
58
+ console.log(` - user.email: ${globalEmail || '(not set)'}`);
59
+
60
+ // Local config (project-specific)
61
+ try {
62
+ const localName = execSync('git config --local user.name', { encoding: 'utf-8' }).trim();
63
+ const localEmail = execSync('git config --local user.email', { encoding: 'utf-8' }).trim();
64
+
65
+ if (localName || localEmail) {
66
+ console.log('\n Local (this project):');
67
+ console.log(` - user.name: ${localName || '(not set)'}`);
68
+ console.log(` - user.email: ${localEmail || '(not set)'}`);
69
+ }
70
+ } catch (e) {
71
+ // No local config, that's ok
72
+ }
73
+
74
+ console.log('');
75
+ } catch (error) {
76
+ console.log(' āš ļø Unable to read git config. Is git installed?\n');
77
+ }
78
+ }
79
+
80
+ static showConfigCommands() {
81
+ console.log('\nšŸ’” To configure git, use these commands:\n');
82
+ console.log(' Global (all projects):');
83
+ console.log(' git config --global user.name "Your Name"');
84
+ console.log(' git config --global user.email "your@email.com"\n');
85
+ console.log(' Local (this project only):');
86
+ console.log(' git config --local user.name "Your Name"');
87
+ console.log(' git config --local user.email "your@email.com"\n');
88
+ }
89
+
90
+ static async confirmConfig() {
91
+ const rl = readline.createInterface({
92
+ input: process.stdin,
93
+ output: process.stdout
94
+ });
95
+
96
+ return new Promise((resolve) => {
97
+ rl.question('Is this configuration correct? (y/n): ', (answer) => {
98
+ rl.close();
99
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
100
+ });
101
+ });
102
+ }
103
+
104
+ static async promptToken() {
105
+ const rl = readline.createInterface({
106
+ input: process.stdin,
107
+ output: process.stdout
108
+ });
109
+
110
+ console.log('šŸ”‘ GitHub Personal Access Token:');
111
+ console.log(' Create one at: https://github.com/settings/tokens');
112
+ console.log(' Required scopes: repo (Full control of private repositories)\n');
113
+
114
+ return new Promise((resolve) => {
115
+ rl.question('Enter your GitHub token: ', (token) => {
116
+ rl.close();
117
+ resolve(token.trim());
118
+ });
119
+ });
120
+ }
121
+
122
+ static async validateToken(token) {
123
+ try {
124
+ const octokit = new Octokit({ auth: token });
125
+ await octokit.rest.users.getAuthenticated();
126
+ console.log('\nāœ“ Token validated successfully');
127
+ return true;
128
+ } catch (error) {
129
+ return false;
130
+ }
131
+ }
132
+
133
+ static detectRepo() {
134
+ try {
135
+ // Get remote URL
136
+ const remoteUrl = execSync('git config --get remote.origin.url', { encoding: 'utf-8' }).trim();
137
+
138
+ // Parse owner/repo from URL
139
+ // Handles: git@github.com:owner/repo.git or https://github.com/owner/repo.git
140
+ const match = remoteUrl.match(/github\.com[:/]([^/]+)\/(.+?)(\.git)?$/);
141
+
142
+ if (match) {
143
+ return {
144
+ owner: match[1],
145
+ repo: match[2]
146
+ };
147
+ }
148
+ } catch (error) {
149
+ // Fallback if no remote
150
+ }
151
+
152
+ // Fallback: prompt user
153
+ console.log('\nāš ļø Could not auto-detect repository from git remote.');
154
+ console.log(' Using placeholder values. Update .claude/settings.local.json manually.\n');
155
+
156
+ return {
157
+ owner: 'your-github-username',
158
+ repo: 'your-repo-name'
159
+ };
160
+ }
161
+
162
+ static saveConfig(token, owner, repo) {
163
+ const settingsPath = join(process.cwd(), '.claude', 'settings.local.json');
164
+ const settingsDir = dirname(settingsPath);
165
+
166
+ // Ensure directory exists
167
+ if (!existsSync(settingsDir)) {
168
+ mkdirSync(settingsDir, { recursive: true });
169
+ }
170
+
171
+ // Read existing settings or create new
172
+ let settings = {};
173
+ if (existsSync(settingsPath)) {
174
+ try {
175
+ settings = JSON.parse(readFileSync(settingsPath, 'utf-8'));
176
+ } catch (e) {
177
+ // Invalid JSON, start fresh
178
+ }
179
+ }
180
+
181
+ // Update GitHub config
182
+ settings.github = {
183
+ enabled: true,
184
+ autoCreate: true,
185
+ autoUpdate: true,
186
+ autoClose: true,
187
+ token,
188
+ owner,
189
+ repo
190
+ };
191
+
192
+ // Write settings
193
+ writeFileSync(settingsPath, JSON.stringify(settings, null, 2));
194
+ }
195
+ }
@@ -0,0 +1,290 @@
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: Display summary
49
+ this.displaySummary(task, prUrl, tracker);
50
+ }
51
+
52
+ static getActiveTask() {
53
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
54
+
55
+ if (!existsSync(activePlanPath)) {
56
+ console.log('\nāŒ No active plan found\n');
57
+ process.exit(1);
58
+ }
59
+
60
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
61
+ const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
62
+
63
+ if (!existsSync(trackerPath)) {
64
+ console.log('\nāŒ Task tracker not found\n');
65
+ process.exit(1);
66
+ }
67
+
68
+ const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
69
+ const task = tracker.taskFiles.find(t => t.status === 'in_progress');
70
+
71
+ return { task, tracker };
72
+ }
73
+
74
+ static runTests() {
75
+ try {
76
+ execSync('npm test', { stdio: 'inherit' });
77
+ console.log('\nāœ… All tests passed');
78
+ } catch (error) {
79
+ console.log('\nāŒ Tests failed. Fix errors before committing.\n');
80
+ process.exit(1);
81
+ }
82
+ }
83
+
84
+ static stageFiles() {
85
+ try {
86
+ // Stage all files in Agent/
87
+ execSync('git add Agent/', { stdio: 'inherit' });
88
+
89
+ // Also stage scripts/ if exists
90
+ try {
91
+ execSync('git add scripts/', { stdio: 'pipe' });
92
+ } catch (e) {
93
+ // scripts/ might not exist
94
+ }
95
+
96
+ // Show what was staged
97
+ const staged = execSync('git diff --cached --name-only', { encoding: 'utf-8' });
98
+
99
+ if (!staged.trim()) {
100
+ console.log('āš ļø No changes to commit\n');
101
+ process.exit(0);
102
+ }
103
+
104
+ console.log('Staged files:');
105
+ staged.trim().split('\n').forEach(file => {
106
+ console.log(` āœ“ ${file}`);
107
+ });
108
+ } catch (error) {
109
+ console.log(`\nāŒ Failed to stage files: ${error.message}\n`);
110
+ process.exit(1);
111
+ }
112
+ }
113
+
114
+ static generateCommitMessage(task) {
115
+ // Load full task details
116
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
117
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
118
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
119
+
120
+ let taskData;
121
+ try {
122
+ taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
123
+ } catch (e) {
124
+ taskData = task;
125
+ }
126
+
127
+ // Format: [TASK-001] Task title
128
+ return `[${task.id}] ${taskData.title || task.title}`;
129
+ }
130
+
131
+ static createCommit(message) {
132
+ try {
133
+ // Escape double quotes and backslashes for shell
134
+ const escapedMessage = message.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
135
+ execSync(`git commit -m "${escapedMessage}"`, { stdio: 'inherit' });
136
+ console.log('āœ… Commit created');
137
+ } catch (error) {
138
+ console.log(`\nāŒ Failed to create commit: ${error.message}\n`);
139
+ process.exit(1);
140
+ }
141
+ }
142
+
143
+ static pushBranch(taskId) {
144
+ const branchName = `feature/${taskId.toLowerCase()}`;
145
+
146
+ try {
147
+ // Check if branch has upstream
148
+ try {
149
+ execSync('git rev-parse --abbrev-ref --symbolic-full-name @{u}', { stdio: 'pipe' });
150
+ // Has upstream, just push
151
+ execSync('git push', { stdio: 'inherit' });
152
+ } catch (e) {
153
+ // No upstream, set it
154
+ execSync(`git push -u origin ${branchName}`, { stdio: 'inherit' });
155
+ }
156
+
157
+ console.log('āœ… Pushed to remote');
158
+ } catch (error) {
159
+ console.log(`\nāŒ Failed to push: ${error.message}\n`);
160
+ process.exit(1);
161
+ }
162
+ }
163
+
164
+ static async createPullRequest(task, commitMessage) {
165
+ try {
166
+ // Get main branch name
167
+ const mainBranch = this.getMainBranch();
168
+
169
+ // Load task for issue number
170
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
171
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
172
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
173
+
174
+ let taskData;
175
+ try {
176
+ taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
177
+ } catch (e) {
178
+ taskData = task;
179
+ }
180
+
181
+ // Build PR body
182
+ let prBody = taskData.description || '';
183
+
184
+ if (taskData.githubIssue) {
185
+ prBody += `\n\nCloses #${taskData.githubIssue}`;
186
+ }
187
+
188
+ // Create PR using gh CLI
189
+ const prCommand = `gh pr create --title "${commitMessage}" --body "${prBody}" --base ${mainBranch}`;
190
+ const prOutput = execSync(prCommand, { encoding: 'utf-8' });
191
+
192
+ // Extract PR URL from output
193
+ const prUrl = prOutput.match(/https:\/\/github\.com\/[^\s]+/)?.[0];
194
+
195
+ if (prUrl) {
196
+ console.log(`āœ… Pull request created: ${prUrl}`);
197
+ return prUrl;
198
+ }
199
+
200
+ console.log('āœ… Pull request created');
201
+ return null;
202
+ } catch (error) {
203
+ console.log(`\nāš ļø Failed to create PR: ${error.message}`);
204
+ console.log(' You may need to install GitHub CLI: https://cli.github.com/\n');
205
+ return null;
206
+ }
207
+ }
208
+
209
+ static async updateGitHubIssue(task, prUrl) {
210
+ const config = new GitHubConfig(process.cwd());
211
+
212
+ if (!config.isAutoUpdateEnabled()) {
213
+ return;
214
+ }
215
+
216
+ try {
217
+ // Load task data
218
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
219
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
220
+ const taskPath = join(process.cwd(), '.claude', 'plans', planId, 'tasks', `${task.id}.json`);
221
+
222
+ const taskData = JSON.parse(readFileSync(taskPath, 'utf-8'));
223
+
224
+ if (!taskData.githubIssue) {
225
+ return;
226
+ }
227
+
228
+ const client = new GitHubClient(
229
+ config.getToken(),
230
+ config.getRepoInfo().owner,
231
+ config.getRepoInfo().repo
232
+ );
233
+
234
+ if (!client.isConfigured()) {
235
+ return;
236
+ }
237
+
238
+ // Update labels to "in review"
239
+ await client.updateIssueLabels(taskData.githubIssue, ['status: in-review', `phase: ${taskData.phase || 'implementation'}`]);
240
+
241
+ // Add comment with PR link
242
+ if (prUrl) {
243
+ const comment = `Pull request created: ${prUrl}\n\nTask is now in code review.`;
244
+ await client.addIssueComment(taskData.githubIssue, comment);
245
+ }
246
+
247
+ console.log(`āœ… Updated GitHub issue #${taskData.githubIssue}`);
248
+ } catch (error) {
249
+ console.log(`\nāš ļø Failed to update GitHub issue: ${error.message}\n`);
250
+ }
251
+ }
252
+
253
+ static displaySummary(task, prUrl, tracker) {
254
+ console.log('\nā”Œā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”');
255
+ console.log('│ āœ… Commit Workflow Complete │');
256
+ console.log('ā””ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”€ā”˜\n');
257
+
258
+ console.log(`šŸ“Œ Task: ${task.id} - ${task.title}`);
259
+
260
+ if (prUrl) {
261
+ console.log(`šŸ”— PR: ${prUrl}`);
262
+ }
263
+
264
+ const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
265
+ const total = tracker.taskFiles.length;
266
+
267
+ console.log(`\nšŸ“Š Progress: ${completed}/${total} completed\n`);
268
+
269
+ console.log('šŸ’” Next steps:');
270
+ console.log(' 1. Review and merge PR on GitHub');
271
+ console.log(' 2. After merge, run: agentic15 task next\n');
272
+ }
273
+
274
+ static getMainBranch() {
275
+ try {
276
+ // Try to detect main branch
277
+ const branches = execSync('git branch -r', { encoding: 'utf-8' });
278
+
279
+ if (branches.includes('origin/main')) {
280
+ return 'main';
281
+ } else if (branches.includes('origin/master')) {
282
+ return 'master';
283
+ }
284
+
285
+ return 'main'; // Default
286
+ } catch (e) {
287
+ return 'main';
288
+ }
289
+ }
290
+ }
@@ -0,0 +1,123 @@
1
+ import { execSync } from 'child_process';
2
+ import { readFileSync, writeFileSync, existsSync, mkdirSync, readdirSync } from 'fs';
3
+ import { join } from 'path';
4
+
5
+ export class PlanCommand {
6
+ static async handle(description) {
7
+ // Check if plan already exists
8
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
9
+
10
+ if (existsSync(activePlanPath)) {
11
+ const planId = readFileSync(activePlanPath, 'utf-8').trim();
12
+ const planPath = join(process.cwd(), '.claude', 'plans', planId);
13
+ const projectPlanPath = join(planPath, 'PROJECT-PLAN.json');
14
+
15
+ // Check if plan file exists
16
+ if (existsSync(projectPlanPath)) {
17
+ // Plan exists, check if it's locked
18
+ const lockedPath = join(planPath, '.plan-locked');
19
+
20
+ if (existsSync(lockedPath)) {
21
+ console.log('\nāš ļø Plan already locked');
22
+ console.log(` Plan: ${planId}\n`);
23
+ this.showPlanStatus(planId);
24
+ process.exit(0);
25
+ }
26
+
27
+ // Plan exists but not locked - lock it
28
+ console.log('\nšŸ“‹ Found existing plan, locking it...\n');
29
+ return this.lockPlan(planId);
30
+ } else {
31
+ // Requirements exist but plan not created yet
32
+ console.log('\nāš ļø Waiting for PROJECT-PLAN.json');
33
+ console.log(` Tell Claude: "Create the project plan"\n`);
34
+ process.exit(0);
35
+ }
36
+ }
37
+
38
+ // No plan exists - create new one
39
+ if (!description) {
40
+ console.log('\nāŒ Project description required');
41
+ console.log(' Usage: agentic15 plan "Build a calculator app"\n');
42
+ process.exit(1);
43
+ }
44
+
45
+ return this.generatePlan(description);
46
+ }
47
+
48
+ static generatePlan(description) {
49
+ console.log('\nšŸ“‹ Generating new plan...\n');
50
+
51
+ try {
52
+ // Run plan:generate script
53
+ execSync(`npm run plan:generate "${description}"`, { stdio: 'inherit' });
54
+
55
+ // Find the generated plan
56
+ const plansDir = join(process.cwd(), '.claude', 'plans');
57
+ const plans = readdirSync(plansDir)
58
+ .filter(name => name.startsWith('plan-') && name.includes('-generated'));
59
+
60
+ if (plans.length === 0) {
61
+ console.log('\nāŒ Failed to generate plan\n');
62
+ process.exit(1);
63
+ }
64
+
65
+ // Use the most recent generated plan
66
+ const planId = plans[plans.length - 1];
67
+
68
+ // Set as active plan
69
+ const activePlanPath = join(process.cwd(), '.claude', 'ACTIVE-PLAN');
70
+ writeFileSync(activePlanPath, planId);
71
+
72
+ console.log(`\nāœ… Plan requirements created: ${planId}`);
73
+ console.log(` Location: .claude/plans/${planId}/PROJECT-REQUIREMENTS.txt\n`);
74
+ console.log('šŸ’” Next steps:');
75
+ console.log(` 1. Tell Claude: "Create the project plan"`);
76
+ console.log(` 2. When Claude is done, run: agentic15 plan\n`);
77
+ } catch (error) {
78
+ console.log(`\nāŒ Failed to generate plan: ${error.message}\n`);
79
+ process.exit(1);
80
+ }
81
+ }
82
+
83
+ static lockPlan(planId) {
84
+ console.log(`šŸ“‹ Locking plan: ${planId}\n`);
85
+
86
+ try {
87
+ // Run plan:init script
88
+ execSync('npm run plan:init', { stdio: 'inherit' });
89
+
90
+ console.log('\nāœ… Plan locked successfully\n');
91
+
92
+ this.showPlanStatus(planId);
93
+
94
+ console.log('šŸ’” Next step: agentic15 task next\n');
95
+ } catch (error) {
96
+ console.log(`\nāŒ Failed to lock plan: ${error.message}\n`);
97
+ process.exit(1);
98
+ }
99
+ }
100
+
101
+ static showPlanStatus(planId) {
102
+ const trackerPath = join(process.cwd(), '.claude', 'plans', planId, 'TASK-TRACKER.json');
103
+
104
+ if (!existsSync(trackerPath)) {
105
+ return;
106
+ }
107
+
108
+ try {
109
+ const tracker = JSON.parse(readFileSync(trackerPath, 'utf-8'));
110
+
111
+ const total = tracker.taskFiles.length;
112
+ const completed = tracker.taskFiles.filter(t => t.status === 'completed').length;
113
+ const pending = tracker.taskFiles.filter(t => t.status === 'pending').length;
114
+
115
+ console.log('šŸ“Š Plan Status:');
116
+ console.log(` Total Tasks: ${total}`);
117
+ console.log(` Completed: ${completed}`);
118
+ console.log(` Pending: ${pending}\n`);
119
+ } catch (e) {
120
+ // Ignore errors
121
+ }
122
+ }
123
+ }