agileflow 2.56.0 → 2.57.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.56.0",
3
+ "version": "2.57.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -18,6 +18,14 @@ const { execSync, spawnSync } = require('child_process');
18
18
  // Session manager path (relative to script location)
19
19
  const SESSION_MANAGER_PATH = path.join(__dirname, 'session-manager.js');
20
20
 
21
+ // Update checker module
22
+ let updateChecker;
23
+ try {
24
+ updateChecker = require('./check-update.js');
25
+ } catch (e) {
26
+ // Update checker not available
27
+ }
28
+
21
29
  // ANSI color codes
22
30
  const c = {
23
31
  reset: '\x1b[0m',
@@ -322,6 +330,98 @@ function compareVersions(a, b) {
322
330
  return 0;
323
331
  }
324
332
 
333
+ // Check for updates (async but we'll use sync approach for welcome)
334
+ async function checkUpdates() {
335
+ const result = {
336
+ available: false,
337
+ installed: null,
338
+ latest: null,
339
+ justUpdated: false,
340
+ previousVersion: null,
341
+ autoUpdate: false,
342
+ changelog: [],
343
+ };
344
+
345
+ if (!updateChecker) return result;
346
+
347
+ try {
348
+ const updateInfo = await updateChecker.checkForUpdates();
349
+ result.installed = updateInfo.installed;
350
+ result.latest = updateInfo.latest;
351
+ result.available = updateInfo.updateAvailable;
352
+ result.justUpdated = updateInfo.justUpdated;
353
+ result.previousVersion = updateInfo.previousVersion;
354
+ result.autoUpdate = updateInfo.autoUpdate;
355
+
356
+ // If just updated, try to get changelog entries
357
+ if (result.justUpdated && result.installed) {
358
+ result.changelog = getChangelogEntries(result.installed);
359
+ }
360
+ } catch (e) {
361
+ // Silently fail - update check is non-critical
362
+ }
363
+
364
+ return result;
365
+ }
366
+
367
+ // Parse CHANGELOG.md for entries of a specific version
368
+ function getChangelogEntries(version) {
369
+ const entries = [];
370
+
371
+ try {
372
+ // Look for CHANGELOG.md in .agileflow or package location
373
+ const possiblePaths = [
374
+ path.join(__dirname, '..', 'CHANGELOG.md'),
375
+ path.join(__dirname, 'CHANGELOG.md'),
376
+ ];
377
+
378
+ let changelogContent = null;
379
+ for (const p of possiblePaths) {
380
+ if (fs.existsSync(p)) {
381
+ changelogContent = fs.readFileSync(p, 'utf8');
382
+ break;
383
+ }
384
+ }
385
+
386
+ if (!changelogContent) return entries;
387
+
388
+ // Find the section for this version
389
+ const versionPattern = new RegExp(`## \\[${version}\\].*?\\n([\\s\\S]*?)(?=## \\[|$)`);
390
+ const match = changelogContent.match(versionPattern);
391
+
392
+ if (match) {
393
+ // Extract bullet points from Added/Changed/Fixed sections
394
+ const lines = match[1].split('\n');
395
+ for (const line of lines) {
396
+ const bulletMatch = line.match(/^- (.+)$/);
397
+ if (bulletMatch && entries.length < 3) {
398
+ entries.push(bulletMatch[1]);
399
+ }
400
+ }
401
+ }
402
+ } catch (e) {
403
+ // Silently fail
404
+ }
405
+
406
+ return entries;
407
+ }
408
+
409
+ // Run auto-update if enabled
410
+ async function runAutoUpdate(rootDir) {
411
+ try {
412
+ console.log(`${c.cyan}Updating AgileFlow...${c.reset}`);
413
+ execSync('npx agileflow update', {
414
+ cwd: rootDir,
415
+ encoding: 'utf8',
416
+ stdio: 'inherit',
417
+ });
418
+ return true;
419
+ } catch (e) {
420
+ console.log(`${c.yellow}Auto-update failed. Run manually: npx agileflow update${c.reset}`);
421
+ return false;
422
+ }
423
+ }
424
+
325
425
  function getFeatureVersions(rootDir) {
326
426
  const result = {
327
427
  hooks: { version: null, outdated: false },
@@ -394,7 +494,7 @@ function truncate(str, maxLen, suffix = '..') {
394
494
  return str.substring(0, cutIndex) + suffix;
395
495
  }
396
496
 
397
- function formatTable(info, archival, session, precompact, parallelSessions) {
497
+ function formatTable(info, archival, session, precompact, parallelSessions, updateInfo = {}) {
398
498
  const W = 58; // inner width
399
499
  const R = W - 24; // right column width (34 chars)
400
500
  const lines = [];
@@ -407,25 +507,66 @@ function formatTable(info, archival, session, precompact, parallelSessions) {
407
507
  return `${c.dim}${box.v}${c.reset} ${pad(leftStr, 20)} ${c.dim}${box.v}${c.reset} ${pad(rightStr, R)} ${c.dim}${box.v}${c.reset}`;
408
508
  };
409
509
 
510
+ // Helper for full-width row (spans both columns)
511
+ const fullRow = (content, color = '') => {
512
+ const contentStr = `${color}${content}${color ? c.reset : ''}`;
513
+ return `${c.dim}${box.v}${c.reset} ${pad(contentStr, W - 1)} ${c.dim}${box.v}${c.reset}`;
514
+ };
515
+
410
516
  const divider = () =>
411
517
  `${c.dim}${box.lT}${box.h.repeat(22)}${box.cross}${box.h.repeat(W - 22)}${box.rT}${c.reset}`;
518
+ const fullDivider = () =>
519
+ `${c.dim}${box.lT}${box.h.repeat(W)}${box.rT}${c.reset}`;
412
520
  const topBorder = `${c.dim}${box.tl}${box.h.repeat(22)}${box.tT}${box.h.repeat(W - 22)}${box.tr}${c.reset}`;
413
521
  const bottomBorder = `${c.dim}${box.bl}${box.h.repeat(22)}${box.bT}${box.h.repeat(W - 22)}${box.br}${c.reset}`;
414
522
 
415
- // Header (truncate branch name if too long)
523
+ // Header with version and optional update indicator
416
524
  const branchColor =
417
525
  info.branch === 'main' ? c.green : info.branch.startsWith('fix') ? c.red : c.cyan;
418
- // Fixed parts: "agileflow " (10) + "v" (1) + version + " " (2) + " (" (2) + commit (7) + ")" (1) = 23 + version.length
419
- const maxBranchLen = W - 1 - 23 - info.version.length;
526
+
527
+ // Build version string with update status
528
+ let versionStr = `v${info.version}`;
529
+ if (updateInfo.justUpdated && updateInfo.previousVersion) {
530
+ versionStr = `v${info.version} ${c.green}✓${c.reset}${c.dim} (was v${updateInfo.previousVersion})`;
531
+ } else if (updateInfo.available && updateInfo.latest) {
532
+ versionStr = `v${info.version} ${c.yellow}↑${updateInfo.latest}${c.reset}`;
533
+ }
534
+
535
+ // Calculate remaining space for branch
536
+ const versionVisibleLen = updateInfo.justUpdated
537
+ ? info.version.length + 20 + (updateInfo.previousVersion?.length || 0)
538
+ : updateInfo.available
539
+ ? info.version.length + 3 + (updateInfo.latest?.length || 0)
540
+ : info.version.length;
541
+ const maxBranchLen = W - 1 - 15 - versionVisibleLen;
420
542
  const branchDisplay =
421
543
  info.branch.length > maxBranchLen
422
- ? info.branch.substring(0, maxBranchLen - 2) + '..'
544
+ ? info.branch.substring(0, Math.max(5, maxBranchLen - 2)) + '..'
423
545
  : info.branch;
424
- const header = `${c.brand}${c.bold}agileflow${c.reset} ${c.dim}v${info.version}${c.reset} ${branchColor}${branchDisplay}${c.reset} ${c.dim}(${info.commit})${c.reset}`;
546
+
547
+ const header = `${c.brand}${c.bold}agileflow${c.reset} ${c.dim}${versionStr}${c.reset} ${branchColor}${branchDisplay}${c.reset} ${c.dim}(${info.commit})${c.reset}`;
425
548
  const headerLine = `${c.dim}${box.v}${c.reset} ${pad(header, W - 1)} ${c.dim}${box.v}${c.reset}`;
426
549
 
427
550
  lines.push(topBorder);
428
551
  lines.push(headerLine);
552
+
553
+ // Show update available notification
554
+ if (updateInfo.available && updateInfo.latest && !updateInfo.justUpdated) {
555
+ lines.push(fullDivider());
556
+ lines.push(fullRow(`↑ Update available: v${updateInfo.latest}`, c.yellow));
557
+ lines.push(fullRow(` Run: npx agileflow update`, c.dim));
558
+ }
559
+
560
+ // Show "just updated" changelog
561
+ if (updateInfo.justUpdated && updateInfo.changelog && updateInfo.changelog.length > 0) {
562
+ lines.push(fullDivider());
563
+ lines.push(fullRow(`What's new in v${info.version}:`, c.green));
564
+ for (const entry of updateInfo.changelog.slice(0, 2)) {
565
+ lines.push(fullRow(`• ${truncate(entry, W - 4)}`, c.dim));
566
+ }
567
+ lines.push(fullRow(`Run /agileflow:whats-new for full changelog`, c.dim));
568
+ }
569
+
429
570
  lines.push(divider());
430
571
 
431
572
  // Stories section
@@ -535,7 +676,7 @@ function formatTable(info, archival, session, precompact, parallelSessions) {
535
676
  }
536
677
 
537
678
  // Main
538
- function main() {
679
+ async function main() {
539
680
  const rootDir = getProjectRoot();
540
681
  const info = getProjectInfo(rootDir);
541
682
  const archival = runArchival(rootDir);
@@ -543,7 +684,29 @@ function main() {
543
684
  const precompact = checkPreCompact(rootDir);
544
685
  const parallelSessions = checkParallelSessions(rootDir);
545
686
 
546
- console.log(formatTable(info, archival, session, precompact, parallelSessions));
687
+ // Check for updates (async, cached)
688
+ let updateInfo = {};
689
+ try {
690
+ updateInfo = await checkUpdates();
691
+
692
+ // If auto-update is enabled and update available, run it
693
+ if (updateInfo.available && updateInfo.autoUpdate) {
694
+ const updated = await runAutoUpdate(rootDir);
695
+ if (updated) {
696
+ // Re-run welcome after update (the new version will show changelog)
697
+ return;
698
+ }
699
+ }
700
+
701
+ // Mark current version as seen to track for next update
702
+ if (updateInfo.justUpdated && updateChecker) {
703
+ updateChecker.markVersionSeen(info.version);
704
+ }
705
+ } catch (e) {
706
+ // Update check failed - continue without it
707
+ }
708
+
709
+ console.log(formatTable(info, archival, session, precompact, parallelSessions, updateInfo));
547
710
 
548
711
  // Show warning and tip if other sessions are active
549
712
  if (parallelSessions.otherActive > 0) {
@@ -554,4 +717,4 @@ function main() {
554
717
  }
555
718
  }
556
719
 
557
- main();
720
+ main().catch(console.error);
@@ -0,0 +1,325 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * check-update.js - Check for AgileFlow updates with caching
5
+ *
6
+ * Features:
7
+ * - Checks npm registry for latest version
8
+ * - Caches results to avoid excessive requests
9
+ * - Configurable check frequency (hourly, daily, weekly)
10
+ * - Returns structured JSON for easy parsing
11
+ *
12
+ * Usage:
13
+ * node check-update.js [--force] [--json]
14
+ *
15
+ * Options:
16
+ * --force Bypass cache and check npm directly
17
+ * --json Output as JSON (default is human-readable)
18
+ *
19
+ * Environment:
20
+ * DEBUG_UPDATE=1 Enable debug logging
21
+ */
22
+
23
+ const fs = require('fs');
24
+ const path = require('path');
25
+ const https = require('https');
26
+
27
+ // Debug mode
28
+ const DEBUG = process.env.DEBUG_UPDATE === '1';
29
+
30
+ function debugLog(message, data = null) {
31
+ if (DEBUG) {
32
+ console.error(`[check-update] ${message}`, data ? JSON.stringify(data) : '');
33
+ }
34
+ }
35
+
36
+ // Find project root (has .agileflow directory)
37
+ function getProjectRoot() {
38
+ let dir = process.cwd();
39
+ while (!fs.existsSync(path.join(dir, '.agileflow')) && dir !== '/') {
40
+ dir = path.dirname(dir);
41
+ }
42
+ return dir !== '/' ? dir : process.cwd();
43
+ }
44
+
45
+ // Get installed AgileFlow version
46
+ function getInstalledVersion(rootDir) {
47
+ // First check .agileflow/package.json (installed version)
48
+ const agileflowPkg = path.join(rootDir, '.agileflow', 'package.json');
49
+ if (fs.existsSync(agileflowPkg)) {
50
+ try {
51
+ const pkg = JSON.parse(fs.readFileSync(agileflowPkg, 'utf8'));
52
+ if (pkg.version) return pkg.version;
53
+ } catch (e) {
54
+ debugLog('Error reading .agileflow/package.json', e.message);
55
+ }
56
+ }
57
+
58
+ // Fallback: check if this is the AgileFlow dev repo
59
+ const cliPkg = path.join(rootDir, 'packages/cli/package.json');
60
+ if (fs.existsSync(cliPkg)) {
61
+ try {
62
+ const pkg = JSON.parse(fs.readFileSync(cliPkg, 'utf8'));
63
+ if (pkg.name === 'agileflow' && pkg.version) return pkg.version;
64
+ } catch (e) {}
65
+ }
66
+
67
+ return null;
68
+ }
69
+
70
+ // Get update configuration from metadata
71
+ function getUpdateConfig(rootDir) {
72
+ const defaults = {
73
+ autoUpdate: false,
74
+ checkFrequency: 'daily', // hourly, daily, weekly, never
75
+ showChangelog: true,
76
+ lastCheck: null,
77
+ lastSeenVersion: null,
78
+ latestVersion: null,
79
+ };
80
+
81
+ try {
82
+ const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
83
+ if (fs.existsSync(metadataPath)) {
84
+ const metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
85
+ if (metadata.updates) {
86
+ return { ...defaults, ...metadata.updates };
87
+ }
88
+ }
89
+ } catch (e) {
90
+ debugLog('Error reading update config', e.message);
91
+ }
92
+
93
+ return defaults;
94
+ }
95
+
96
+ // Save update configuration
97
+ function saveUpdateConfig(rootDir, config) {
98
+ try {
99
+ const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
100
+ let metadata = {};
101
+
102
+ if (fs.existsSync(metadataPath)) {
103
+ metadata = JSON.parse(fs.readFileSync(metadataPath, 'utf8'));
104
+ }
105
+
106
+ metadata.updates = config;
107
+ fs.writeFileSync(metadataPath, JSON.stringify(metadata, null, 2) + '\n');
108
+ return true;
109
+ } catch (e) {
110
+ debugLog('Error saving update config', e.message);
111
+ return false;
112
+ }
113
+ }
114
+
115
+ // Check if cache is still valid
116
+ function isCacheValid(config) {
117
+ if (!config.lastCheck) return false;
118
+
119
+ const now = Date.now();
120
+ const lastCheck = new Date(config.lastCheck).getTime();
121
+ const age = now - lastCheck;
122
+
123
+ // Convert frequency to milliseconds
124
+ const frequencies = {
125
+ hourly: 60 * 60 * 1000, // 1 hour
126
+ daily: 24 * 60 * 60 * 1000, // 24 hours
127
+ weekly: 7 * 24 * 60 * 60 * 1000, // 7 days
128
+ never: Infinity,
129
+ };
130
+
131
+ const maxAge = frequencies[config.checkFrequency] || frequencies.daily;
132
+
133
+ debugLog('Cache check', {
134
+ lastCheck: config.lastCheck,
135
+ ageMs: age,
136
+ maxAgeMs: maxAge,
137
+ valid: age < maxAge,
138
+ });
139
+
140
+ return age < maxAge;
141
+ }
142
+
143
+ // Fetch latest version from npm
144
+ async function fetchLatestVersion() {
145
+ return new Promise(resolve => {
146
+ const options = {
147
+ hostname: 'registry.npmjs.org',
148
+ port: 443,
149
+ path: '/agileflow/latest',
150
+ method: 'GET',
151
+ headers: {
152
+ 'User-Agent': 'agileflow-cli',
153
+ },
154
+ };
155
+
156
+ debugLog('Fetching from npm registry');
157
+
158
+ const req = https.request(options, res => {
159
+ let data = '';
160
+
161
+ res.on('data', chunk => {
162
+ data += chunk;
163
+ });
164
+
165
+ res.on('end', () => {
166
+ if (res.statusCode !== 200) {
167
+ debugLog('Non-200 status', { statusCode: res.statusCode });
168
+ return resolve(null);
169
+ }
170
+
171
+ try {
172
+ const json = JSON.parse(data);
173
+ debugLog('Version found', { version: json.version });
174
+ resolve(json.version || null);
175
+ } catch (err) {
176
+ debugLog('JSON parse error', err.message);
177
+ resolve(null);
178
+ }
179
+ });
180
+ });
181
+
182
+ req.on('error', err => {
183
+ debugLog('Network error', err.message);
184
+ resolve(null);
185
+ });
186
+
187
+ req.setTimeout(5000, () => {
188
+ debugLog('Request timeout');
189
+ req.destroy();
190
+ resolve(null);
191
+ });
192
+
193
+ req.end();
194
+ });
195
+ }
196
+
197
+ // Compare semantic versions: returns -1 if a < b, 0 if equal, 1 if a > b
198
+ function compareVersions(a, b) {
199
+ if (!a || !b) return 0;
200
+ const partsA = a.split('.').map(Number);
201
+ const partsB = b.split('.').map(Number);
202
+ for (let i = 0; i < 3; i++) {
203
+ const numA = partsA[i] || 0;
204
+ const numB = partsB[i] || 0;
205
+ if (numA < numB) return -1;
206
+ if (numA > numB) return 1;
207
+ }
208
+ return 0;
209
+ }
210
+
211
+ // Main check function
212
+ async function checkForUpdates(options = {}) {
213
+ const rootDir = getProjectRoot();
214
+ const installedVersion = getInstalledVersion(rootDir);
215
+ const config = getUpdateConfig(rootDir);
216
+
217
+ const result = {
218
+ installed: installedVersion,
219
+ latest: null,
220
+ updateAvailable: false,
221
+ autoUpdate: config.autoUpdate,
222
+ justUpdated: false,
223
+ previousVersion: config.lastSeenVersion,
224
+ fromCache: false,
225
+ error: null,
226
+ };
227
+
228
+ // No installed version found
229
+ if (!installedVersion) {
230
+ result.error = 'Could not determine installed version';
231
+ return result;
232
+ }
233
+
234
+ // Check if we just updated (lastSeenVersion < installed)
235
+ if (
236
+ config.lastSeenVersion &&
237
+ compareVersions(config.lastSeenVersion, installedVersion) < 0
238
+ ) {
239
+ result.justUpdated = true;
240
+ result.previousVersion = config.lastSeenVersion;
241
+ }
242
+
243
+ // Use cache if valid and not forced
244
+ if (!options.force && isCacheValid(config) && config.latestVersion) {
245
+ result.latest = config.latestVersion;
246
+ result.fromCache = true;
247
+ } else if (config.checkFrequency !== 'never') {
248
+ // Fetch from npm
249
+ result.latest = await fetchLatestVersion();
250
+
251
+ // Update cache
252
+ if (result.latest) {
253
+ config.lastCheck = new Date().toISOString();
254
+ config.latestVersion = result.latest;
255
+ saveUpdateConfig(rootDir, config);
256
+ }
257
+ }
258
+
259
+ // Compare versions
260
+ if (result.latest && compareVersions(installedVersion, result.latest) < 0) {
261
+ result.updateAvailable = true;
262
+ }
263
+
264
+ return result;
265
+ }
266
+
267
+ // Mark version as seen (after update or dismissal)
268
+ function markVersionSeen(version) {
269
+ const rootDir = getProjectRoot();
270
+ const config = getUpdateConfig(rootDir);
271
+ config.lastSeenVersion = version;
272
+ saveUpdateConfig(rootDir, config);
273
+ }
274
+
275
+ // CLI interface
276
+ async function main() {
277
+ const args = process.argv.slice(2);
278
+ const force = args.includes('--force');
279
+ const jsonOutput = args.includes('--json');
280
+ const markSeen = args.includes('--mark-seen');
281
+
282
+ if (markSeen) {
283
+ const version = args[args.indexOf('--mark-seen') + 1];
284
+ if (version) {
285
+ markVersionSeen(version);
286
+ if (jsonOutput) {
287
+ console.log(JSON.stringify({ success: true, version }));
288
+ } else {
289
+ console.log(`Marked version ${version} as seen`);
290
+ }
291
+ }
292
+ return;
293
+ }
294
+
295
+ const result = await checkForUpdates({ force });
296
+
297
+ if (jsonOutput) {
298
+ console.log(JSON.stringify(result, null, 2));
299
+ } else {
300
+ if (result.error) {
301
+ console.log(`Error: ${result.error}`);
302
+ } else if (result.justUpdated) {
303
+ console.log(`Updated from v${result.previousVersion} to v${result.installed}`);
304
+ } else if (result.updateAvailable) {
305
+ console.log(`Update available: v${result.installed} -> v${result.latest}`);
306
+ console.log(`Run: npx agileflow update`);
307
+ } else {
308
+ console.log(`AgileFlow v${result.installed} is up to date`);
309
+ }
310
+ }
311
+ }
312
+
313
+ // Export for use as module
314
+ module.exports = {
315
+ checkForUpdates,
316
+ markVersionSeen,
317
+ getUpdateConfig,
318
+ saveUpdateConfig,
319
+ compareVersions,
320
+ };
321
+
322
+ // Run CLI if executed directly
323
+ if (require.main === module) {
324
+ main().catch(console.error);
325
+ }
@@ -0,0 +1,94 @@
1
+ ---
2
+ description: Show what's new in AgileFlow
3
+ ---
4
+
5
+ # /agileflow:whats-new
6
+
7
+ Display recent AgileFlow updates and version history.
8
+
9
+ ---
10
+
11
+ ## Prompt
12
+
13
+ ROLE: AgileFlow Version Reporter
14
+
15
+ **STEP 1: Get current version and changelog**
16
+
17
+ Read the AgileFlow CHANGELOG.md:
18
+
19
+ ```bash
20
+ cat .agileflow/CHANGELOG.md 2>/dev/null || echo "Changelog not found"
21
+ ```
22
+
23
+ Also check the installed version:
24
+ ```bash
25
+ cat .agileflow/package.json 2>/dev/null | grep '"version"' || echo "Version unknown"
26
+ ```
27
+
28
+ **STEP 2: Parse and display**
29
+
30
+ Parse the CHANGELOG.md and display:
31
+
32
+ 1. **Current installed version** at the top
33
+ 2. **Last 3-5 versions** with their changes
34
+ 3. Use clear formatting with headers and bullets
35
+
36
+ **FORMAT:**
37
+
38
+ ```
39
+ ╭─────────────────────────────────────────────────────╮
40
+ │ AgileFlow Changelog │
41
+ │ Current: v2.57.0 │
42
+ ╰─────────────────────────────────────────────────────╯
43
+
44
+ ## v2.57.0 (2025-12-27)
45
+
46
+ ### Added
47
+ • Auto-update system with configurable check frequency
48
+ • Update notifications in welcome message and status line
49
+ • Changelog display after updates
50
+
51
+ ---
52
+
53
+ ## v2.56.0 (2025-12-27)
54
+
55
+ ### Added
56
+ • Dynamic IDE discovery - Codex CLI now in setup
57
+
58
+ ### Changed
59
+ • Replaced hardcoded IDE list with dynamic loading
60
+
61
+ ---
62
+
63
+ ## v2.55.0 (2025-12-26)
64
+
65
+ ### Changed
66
+ • Consolidated code improvements and debugging
67
+
68
+ ---
69
+
70
+ 📖 Full changelog: https://github.com/projectquestorg/AgileFlow/blob/main/packages/cli/CHANGELOG.md
71
+ 🔄 Check for updates: npx agileflow update
72
+ ⚙️ Configure auto-update: /agileflow:configure --auto-update
73
+ ```
74
+
75
+ **STEP 3: Check for updates**
76
+
77
+ After displaying, check if an update is available:
78
+
79
+ ```bash
80
+ npm view agileflow version 2>/dev/null
81
+ ```
82
+
83
+ If a newer version exists, show:
84
+ ```
85
+ ⚡ Update available: v2.57.0 → v2.58.0
86
+ Run: npx agileflow update
87
+ ```
88
+
89
+ **RULES:**
90
+ - Show max 5 versions (most recent first)
91
+ - Use clear section headers
92
+ - Include dates if available
93
+ - Always show the GitHub link for full history
94
+ - Mention update availability if applicable