claude-autopm 2.6.0 → 2.7.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.
package/bin/autopm.js CHANGED
@@ -192,6 +192,8 @@ function main() {
192
192
  .command(require('../lib/cli/commands/task'))
193
193
  // Agent management command (STANDALONE)
194
194
  .command(require('../lib/cli/commands/agent'))
195
+ // Context management command (STANDALONE)
196
+ .command(require('../lib/cli/commands/context'))
195
197
  // Validation command
196
198
  .command('validate', 'Validate ClaudeAutoPM configuration and setup',
197
199
  (yargs) => {
@@ -0,0 +1,477 @@
1
+ /**
2
+ * CLI Context Commands
3
+ *
4
+ * Provides context management commands for ClaudeAutoPM.
5
+ * Implements subcommands for context lifecycle management.
6
+ *
7
+ * Commands:
8
+ * - create <type>: Create new context from template
9
+ * - prime: Generate comprehensive project snapshot
10
+ * - update <type>: Update existing context
11
+ * - show [type]: Show context or list all contexts
12
+ *
13
+ * @module cli/commands/context
14
+ * @requires ../../services/ContextService
15
+ * @requires fs-extra
16
+ * @requires ora
17
+ * @requires chalk
18
+ * @requires path
19
+ */
20
+
21
+ const ContextService = require('../../services/ContextService');
22
+ const fs = require('fs-extra');
23
+ const ora = require('ora');
24
+ const chalk = require('chalk');
25
+ const path = require('path');
26
+
27
+ /**
28
+ * Context Create - Create new context file from template
29
+ * @param {Object} argv - Command arguments
30
+ */
31
+ async function contextCreate(argv) {
32
+ const spinner = ora(`Creating ${argv.type} context...`).start();
33
+
34
+ try {
35
+ const contextService = new ContextService();
36
+
37
+ // Prepare options
38
+ const options = {
39
+ name: argv.name || argv.type,
40
+ description: argv.description || `${argv.type} context`,
41
+ ...(argv.data && { data: argv.data })
42
+ };
43
+
44
+ const result = await contextService.createContext(argv.type, options);
45
+
46
+ spinner.succeed(chalk.green('Context created'));
47
+
48
+ console.log(chalk.cyan('\nšŸ“„ Context Created Successfully\n'));
49
+ console.log(chalk.gray('='.repeat(60)) + '\n');
50
+
51
+ console.log(chalk.bold('Type: ') + result.type);
52
+ console.log(chalk.bold('Path: ') + result.path);
53
+ console.log(chalk.bold('Created: ') + new Date(result.created).toLocaleString());
54
+
55
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
56
+
57
+ console.log(chalk.bold('šŸ’” Next Steps:\n'));
58
+ console.log(` ${chalk.cyan('1.')} View context: ${chalk.yellow(`autopm context show ${argv.type}`)}`);
59
+ console.log(` ${chalk.cyan('2.')} Update context: ${chalk.yellow(`autopm context update ${argv.type}`)}`);
60
+ console.log(` ${chalk.cyan('3.')} List all: ${chalk.yellow('autopm context show --list')}\n`);
61
+
62
+ console.log(chalk.gray('='.repeat(60)) + '\n');
63
+
64
+ } catch (error) {
65
+ spinner.fail(chalk.red('Failed to create context'));
66
+
67
+ if (error.message.includes('not found')) {
68
+ console.error(chalk.red(`\nError: ${error.message}\n`));
69
+ console.error(chalk.yellow('Available context types:'));
70
+ console.error(chalk.gray(' • project-brief - Project overview and goals'));
71
+ console.error(chalk.gray(' • progress - Progress tracking'));
72
+ console.error(chalk.gray(' • tech-context - Technical stack and architecture'));
73
+ console.error(chalk.gray(' • project-structure - Project organization\n'));
74
+ } else {
75
+ console.error(chalk.red(`\nError: ${error.message}\n`));
76
+ }
77
+ process.exit(1);
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Context Prime - Generate comprehensive project snapshot
83
+ * @param {Object} argv - Command arguments
84
+ */
85
+ async function contextPrime(argv) {
86
+ const spinner = ora('Generating project snapshot...').start();
87
+
88
+ try {
89
+ const contextService = new ContextService();
90
+
91
+ const options = {
92
+ includeGit: argv.includeGit !== false,
93
+ ...(argv.output && { output: argv.output })
94
+ };
95
+
96
+ const result = await contextService.primeContext(options);
97
+
98
+ spinner.succeed(chalk.green('Project snapshot generated'));
99
+
100
+ console.log(chalk.cyan('\nšŸ“ø Project Snapshot Generated\n'));
101
+ console.log(chalk.gray('='.repeat(60)) + '\n');
102
+
103
+ console.log(chalk.bold('Timestamp: ') + result.timestamp);
104
+ console.log(chalk.bold('Epics: ') + result.contexts.epics.length);
105
+ console.log(chalk.bold('Issues: ') + result.contexts.issues.length);
106
+ console.log(chalk.bold('PRDs: ') + result.contexts.prds.length);
107
+
108
+ if (result.git && !result.git.error) {
109
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
110
+ console.log(chalk.bold('Git Information:\n'));
111
+ console.log(` Branch: ${result.git.branch}`);
112
+ console.log(` Commit: ${result.git.commit.substring(0, 8)}`);
113
+ console.log(` Status: ${result.git.status || 'clean'}`);
114
+ }
115
+
116
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
117
+
118
+ console.log(chalk.bold('šŸ“‹ Summary:\n'));
119
+ console.log(result.summary.split('\n').map(line => ` ${line}`).join('\n'));
120
+
121
+ if (argv.output) {
122
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
123
+ console.log(chalk.green(`āœ“ Snapshot saved to: ${argv.output}`));
124
+ }
125
+
126
+ console.log('\n' + chalk.bold('šŸ’” Next Steps:\n'));
127
+ console.log(` ${chalk.cyan('1.')} Use snapshot in Claude conversations`);
128
+ console.log(` ${chalk.cyan('2.')} Update specific contexts: ${chalk.yellow('autopm context update <type>')}`);
129
+ console.log(` ${chalk.cyan('3.')} View all contexts: ${chalk.yellow('autopm context show --list')}\n`);
130
+
131
+ console.log(chalk.gray('='.repeat(60)) + '\n');
132
+
133
+ } catch (error) {
134
+ spinner.fail(chalk.red('Failed to generate snapshot'));
135
+ console.error(chalk.red(`\nError: ${error.message}\n`));
136
+ process.exit(1);
137
+ }
138
+ }
139
+
140
+ /**
141
+ * Context Update - Update existing context
142
+ * @param {Object} argv - Command arguments
143
+ */
144
+ async function contextUpdate(argv) {
145
+ const spinner = ora(`Updating ${argv.type} context...`).start();
146
+
147
+ try {
148
+ const contextService = new ContextService();
149
+
150
+ // Get update content
151
+ let content = argv.content;
152
+
153
+ if (argv.file) {
154
+ // Read content from file
155
+ const exists = await fs.pathExists(argv.file);
156
+ if (!exists) {
157
+ throw new Error(`File not found: ${argv.file}`);
158
+ }
159
+ content = await fs.readFile(argv.file, 'utf8');
160
+ }
161
+
162
+ if (!content) {
163
+ spinner.warn(chalk.yellow('No content provided'));
164
+ console.log(chalk.yellow('\nāš ļø No content to update\n'));
165
+ console.log(chalk.bold('Usage:'));
166
+ console.log(` ${chalk.yellow(`autopm context update ${argv.type} --content "New content"`)}`);
167
+ console.log(` ${chalk.yellow(`autopm context update ${argv.type} --file updates.md`)}\n`);
168
+ return;
169
+ }
170
+
171
+ const options = {
172
+ mode: argv.mode || 'append',
173
+ content
174
+ };
175
+
176
+ const result = await contextService.updateContext(argv.type, options);
177
+
178
+ spinner.succeed(chalk.green('Context updated'));
179
+
180
+ console.log(chalk.cyan('\nāœļø Context Updated Successfully\n'));
181
+ console.log(chalk.gray('='.repeat(60)) + '\n');
182
+
183
+ console.log(chalk.bold('Type: ') + argv.type);
184
+ console.log(chalk.bold('Mode: ') + options.mode);
185
+ console.log(chalk.bold('Updated: ') + new Date(result.timestamp).toLocaleString());
186
+
187
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
188
+
189
+ console.log(chalk.bold('šŸ’” Next Steps:\n'));
190
+ console.log(` ${chalk.cyan('1.')} View updated context: ${chalk.yellow(`autopm context show ${argv.type}`)}`);
191
+ console.log(` ${chalk.cyan('2.')} Generate snapshot: ${chalk.yellow('autopm context prime')}\n`);
192
+
193
+ console.log(chalk.gray('='.repeat(60)) + '\n');
194
+
195
+ } catch (error) {
196
+ spinner.fail(chalk.red('Failed to update context'));
197
+
198
+ if (error.message.includes('not found')) {
199
+ console.error(chalk.red(`\nError: ${error.message}\n`));
200
+ console.error(chalk.yellow('Available contexts:'));
201
+ console.error(chalk.gray(` Use: ${chalk.yellow('autopm context show --list')}\n`));
202
+ } else {
203
+ console.error(chalk.red(`\nError: ${error.message}\n`));
204
+ }
205
+ process.exit(1);
206
+ }
207
+ }
208
+
209
+ /**
210
+ * Context Show - Show context or list all contexts
211
+ * @param {Object} argv - Command arguments
212
+ */
213
+ async function contextShow(argv) {
214
+ const contextService = new ContextService();
215
+
216
+ // List all contexts
217
+ if (argv.list) {
218
+ const spinner = ora('Loading contexts...').start();
219
+
220
+ try {
221
+ const { contexts, byType } = await contextService.listContexts();
222
+
223
+ spinner.succeed(chalk.green('Contexts loaded'));
224
+
225
+ console.log(chalk.cyan('\nšŸ“š All Contexts\n'));
226
+ console.log(chalk.gray('='.repeat(60)) + '\n');
227
+
228
+ if (contexts.length === 0) {
229
+ console.log(chalk.yellow('No contexts found\n'));
230
+ console.log(chalk.bold('šŸ’” Create your first context:'));
231
+ console.log(` ${chalk.yellow('autopm context create project-brief --name "My Project"')}\n`);
232
+ return;
233
+ }
234
+
235
+ // Group by type
236
+ Object.keys(byType).forEach(type => {
237
+ console.log(chalk.bold(`\n${type}:`));
238
+ byType[type].forEach(ctx => {
239
+ const sizeMB = (ctx.size / 1024).toFixed(2);
240
+ console.log(` • ${ctx.file} ${chalk.gray(`(${sizeMB}KB, updated ${new Date(ctx.updated).toLocaleDateString()}`)}`);
241
+ });
242
+ });
243
+
244
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
245
+
246
+ console.log(chalk.bold('šŸ“Š Summary:\n'));
247
+ console.log(` Total contexts: ${contexts.length}`);
248
+ console.log(` Types: ${Object.keys(byType).length}\n`);
249
+
250
+ // Show stats if requested
251
+ if (argv.stats) {
252
+ const spinner2 = ora('Analyzing context usage...').start();
253
+ const { stats, recommendations } = await contextService.analyzeContextUsage();
254
+ spinner2.succeed(chalk.green('Analysis complete'));
255
+
256
+ console.log(chalk.gray('─'.repeat(60)) + '\n');
257
+ console.log(chalk.bold('šŸ“ˆ Statistics:\n'));
258
+ console.log(` Total size: ${(stats.totalSize / 1024).toFixed(2)}KB`);
259
+ console.log(` Average size: ${(stats.averageSize / 1024).toFixed(2)}KB`);
260
+
261
+ if (recommendations.length > 0) {
262
+ console.log('\n' + chalk.bold('šŸ’” Recommendations:\n'));
263
+ recommendations.forEach((rec, index) => {
264
+ console.log(` ${index + 1}. ${rec}`);
265
+ });
266
+ }
267
+ console.log('');
268
+ }
269
+
270
+ console.log(chalk.gray('='.repeat(60)) + '\n');
271
+
272
+ } catch (error) {
273
+ spinner.fail(chalk.red('Failed to load contexts'));
274
+ console.error(chalk.red(`\nError: ${error.message}\n`));
275
+ process.exit(1);
276
+ }
277
+ return;
278
+ }
279
+
280
+ // Show specific context
281
+ if (!argv.type) {
282
+ console.log(chalk.yellow('\nāš ļø No context type specified\n'));
283
+ console.log(chalk.bold('Usage:'));
284
+ console.log(` ${chalk.yellow('autopm context show <type>')}`);
285
+ console.log(` ${chalk.yellow('autopm context show --list')}\n`);
286
+ console.log(chalk.bold('Examples:'));
287
+ console.log(` ${chalk.gray('autopm context show project-brief')}`);
288
+ console.log(` ${chalk.gray('autopm context show --list')}`);
289
+ console.log(` ${chalk.gray('autopm context show --list --stats')}\n`);
290
+ return;
291
+ }
292
+
293
+ const spinner = ora(`Loading ${argv.type} context...`).start();
294
+
295
+ try {
296
+ const result = await contextService.getContext(argv.type);
297
+
298
+ spinner.succeed(chalk.green('Context loaded'));
299
+
300
+ console.log(chalk.cyan(`\nšŸ“„ Context: ${argv.type}\n`));
301
+ console.log(chalk.gray('='.repeat(60)) + '\n');
302
+
303
+ console.log(chalk.bold('Type: ') + result.type);
304
+ console.log(chalk.bold('Updated: ') + new Date(result.updated).toLocaleString());
305
+
306
+ if (result.metadata) {
307
+ const metaKeys = Object.keys(result.metadata);
308
+ if (metaKeys.length > 0) {
309
+ console.log('\n' + chalk.bold('Metadata:\n'));
310
+ metaKeys.forEach(key => {
311
+ console.log(` ${key}: ${result.metadata[key]}`);
312
+ });
313
+ }
314
+ }
315
+
316
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
317
+
318
+ // Show content (skip frontmatter)
319
+ const contentWithoutFrontmatter = result.content.replace(/^---[\s\S]*?---\n\n/, '');
320
+ console.log(contentWithoutFrontmatter);
321
+
322
+ console.log('\n' + chalk.gray('─'.repeat(60)) + '\n');
323
+
324
+ console.log(chalk.bold('šŸ’” Actions:\n'));
325
+ console.log(` ${chalk.cyan('1.')} Update context: ${chalk.yellow(`autopm context update ${argv.type}`)}`);
326
+ console.log(` ${chalk.cyan('2.')} List all: ${chalk.yellow('autopm context show --list')}\n`);
327
+
328
+ console.log(chalk.gray('='.repeat(60)) + '\n');
329
+
330
+ } catch (error) {
331
+ spinner.fail(chalk.red('Failed to load context'));
332
+
333
+ if (error.message.includes('not found')) {
334
+ console.error(chalk.red(`\nError: Context "${argv.type}" not found\n`));
335
+ console.error(chalk.yellow('Available contexts:'));
336
+ console.error(chalk.gray(` Use: ${chalk.yellow('autopm context show --list')}\n`));
337
+ } else {
338
+ console.error(chalk.red(`\nError: ${error.message}\n`));
339
+ }
340
+ process.exit(1);
341
+ }
342
+ }
343
+
344
+ /**
345
+ * Command builder - registers all subcommands
346
+ * @param {Object} yargs - Yargs instance
347
+ * @returns {Object} Configured yargs instance
348
+ */
349
+ function builder(yargs) {
350
+ return yargs
351
+ .command(
352
+ 'create <type>',
353
+ 'Create new context from template',
354
+ (yargs) => {
355
+ return yargs
356
+ .positional('type', {
357
+ describe: 'Context type (project-brief, progress, tech-context, project-structure)',
358
+ type: 'string'
359
+ })
360
+ .option('name', {
361
+ describe: 'Context name',
362
+ type: 'string'
363
+ })
364
+ .option('description', {
365
+ describe: 'Context description',
366
+ type: 'string'
367
+ })
368
+ .example('autopm context create project-brief --name "My Project"', 'Create project brief')
369
+ .example('autopm context create progress --name "Sprint 1"', 'Create progress tracker');
370
+ },
371
+ contextCreate
372
+ )
373
+ .command(
374
+ 'prime',
375
+ 'Generate comprehensive project snapshot',
376
+ (yargs) => {
377
+ return yargs
378
+ .option('include-git', {
379
+ describe: 'Include git information',
380
+ type: 'boolean',
381
+ default: true
382
+ })
383
+ .option('output', {
384
+ describe: 'Output file path',
385
+ type: 'string'
386
+ })
387
+ .example('autopm context prime', 'Generate project snapshot')
388
+ .example('autopm context prime --output snapshot.md', 'Save snapshot to file')
389
+ .example('autopm context prime --no-include-git', 'Skip git information');
390
+ },
391
+ contextPrime
392
+ )
393
+ .command(
394
+ 'update <type>',
395
+ 'Update existing context',
396
+ (yargs) => {
397
+ return yargs
398
+ .positional('type', {
399
+ describe: 'Context type to update',
400
+ type: 'string'
401
+ })
402
+ .option('mode', {
403
+ describe: 'Update mode',
404
+ type: 'string',
405
+ choices: ['append', 'replace'],
406
+ default: 'append'
407
+ })
408
+ .option('content', {
409
+ describe: 'New content',
410
+ type: 'string'
411
+ })
412
+ .option('file', {
413
+ describe: 'Read content from file',
414
+ type: 'string'
415
+ })
416
+ .example('autopm context update project-brief --content "## New Section"', 'Append content')
417
+ .example('autopm context update progress --file updates.md', 'Update from file')
418
+ .example('autopm context update tech-context --mode replace --content "..."', 'Replace content');
419
+ },
420
+ contextUpdate
421
+ )
422
+ .command(
423
+ 'show [type]',
424
+ 'Show context or list all contexts',
425
+ (yargs) => {
426
+ return yargs
427
+ .positional('type', {
428
+ describe: 'Context type to show',
429
+ type: 'string'
430
+ })
431
+ .option('list', {
432
+ describe: 'List all contexts',
433
+ type: 'boolean',
434
+ default: false
435
+ })
436
+ .option('stats', {
437
+ describe: 'Show statistics with list',
438
+ type: 'boolean',
439
+ default: false
440
+ })
441
+ .example('autopm context show project-brief', 'Show specific context')
442
+ .example('autopm context show --list', 'List all contexts')
443
+ .example('autopm context show --list --stats', 'List with statistics');
444
+ },
445
+ contextShow
446
+ )
447
+ .demandCommand(1, 'You must specify a context command')
448
+ .strictCommands()
449
+ .help();
450
+ }
451
+
452
+ /**
453
+ * Command export
454
+ */
455
+ module.exports = {
456
+ command: 'context',
457
+ describe: 'Manage project context files for AI-assisted development',
458
+ builder,
459
+ handler: (argv) => {
460
+ if (!argv._.includes('context') || argv._.length === 1) {
461
+ console.log(chalk.yellow('\nPlease specify a context command\n'));
462
+ console.log('Usage: autopm context <command>\n');
463
+ console.log('Available commands:');
464
+ console.log(' create <type> Create new context from template');
465
+ console.log(' prime Generate comprehensive project snapshot');
466
+ console.log(' update <type> Update existing context');
467
+ console.log(' show [type] Show context or list all contexts');
468
+ console.log('\nUse: autopm context <command> --help for more info\n');
469
+ }
470
+ },
471
+ handlers: {
472
+ create: contextCreate,
473
+ prime: contextPrime,
474
+ update: contextUpdate,
475
+ show: contextShow
476
+ }
477
+ };