agileflow 2.82.5 → 2.84.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/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.84.0] - 2026-01-11
11
+
12
+ ### Added
13
+ - File awareness, smart merge, and simplified auto-update
14
+
15
+ ## [2.83.0] - 2026-01-10
16
+
17
+ ### Changed
18
+ - Remove beta TUI dashboard - use slash commands instead
19
+
10
20
  ## [2.82.5] - 2026-01-10
11
21
 
12
22
  ### Fixed
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agileflow",
3
- "version": "2.82.5",
3
+ "version": "2.84.0",
4
4
  "description": "AI-driven agile development system for Claude Code, Cursor, Windsurf, and more",
5
5
  "keywords": [
6
6
  "agile",
@@ -788,16 +788,14 @@ function enableFeature(feature, options = {}) {
788
788
 
789
789
  // Handle autoupdate (metadata only, no hooks needed)
790
790
  if (feature === 'autoupdate') {
791
- const frequency = options.checkFrequency || 'daily';
792
791
  updateMetadata({
793
792
  updates: {
794
793
  autoUpdate: true,
795
- checkFrequency: frequency,
796
794
  showChangelog: true,
797
795
  },
798
796
  });
799
- success(`Auto-update enabled (check frequency: ${frequency})`);
800
- info('AgileFlow will automatically update on session start');
797
+ success('Auto-update enabled');
798
+ info('AgileFlow will check for updates every session and update automatically');
801
799
  return true; // Skip settings.json write for this feature
802
800
  }
803
801
 
@@ -385,11 +385,11 @@ PYTHON
385
385
  SESSION_COLOR="$SESSION_GREEN" # Light green - plenty of time
386
386
  fi
387
387
 
388
- # Build compact display: "2h15m" or "45m"
388
+ # Build compact display: "~2h15m" or "~45m" (tilde indicates remaining time)
389
389
  if [ "$RH" -gt 0 ]; then
390
- SESSION_DISPLAY="${SESSION_COLOR}⏱${RH}h${RM}m${RESET}"
390
+ SESSION_DISPLAY="${SESSION_COLOR}~${RH}h${RM}m${RESET}"
391
391
  else
392
- SESSION_DISPLAY="${SESSION_COLOR}⏱${RM}m${RESET}"
392
+ SESSION_DISPLAY="${SESSION_COLOR}~${RM}m${RESET}"
393
393
  fi
394
394
  fi
395
395
  fi
@@ -520,6 +520,47 @@ if git rev-parse --git-dir > /dev/null 2>&1; then
520
520
  fi
521
521
  fi
522
522
 
523
+ # ============================================================================
524
+ # Session Info (Multi-session awareness)
525
+ # ============================================================================
526
+ # Show current session if in a non-main session
527
+ SESSION_INFO=""
528
+ SHOW_SESSION=true # New component - default enabled
529
+
530
+ # Check component setting
531
+ if [ "$COMPONENTS" != "null" ] && [ -n "$COMPONENTS" ]; then
532
+ SHOW_SESSION=$(echo "$COMPONENTS" | jq -r '.session | if . == null then true else . end')
533
+ fi
534
+
535
+ if [ "$SHOW_SESSION" = "true" ]; then
536
+ REGISTRY_FILE=".agileflow/sessions/registry.json"
537
+ if [ -f "$REGISTRY_FILE" ]; then
538
+ # Get current working directory
539
+ CWD=$(pwd)
540
+
541
+ # Find session matching current directory
542
+ SESSION_DATA=$(jq -r --arg cwd "$CWD" '
543
+ .sessions | to_entries[] | select(.value.path == $cwd) |
544
+ {id: .key, nickname: .value.nickname, is_main: .value.is_main}
545
+ ' "$REGISTRY_FILE" 2>/dev/null)
546
+
547
+ if [ -n "$SESSION_DATA" ]; then
548
+ SESSION_NUM=$(echo "$SESSION_DATA" | jq -r '.id')
549
+ SESSION_NICK=$(echo "$SESSION_DATA" | jq -r '.nickname // empty')
550
+ IS_MAIN=$(echo "$SESSION_DATA" | jq -r '.is_main // false')
551
+
552
+ # Only show for non-main sessions
553
+ if [ "$IS_MAIN" != "true" ] && [ -n "$SESSION_NUM" ]; then
554
+ if [ -n "$SESSION_NICK" ] && [ "$SESSION_NICK" != "null" ]; then
555
+ SESSION_INFO="${DIM}[${RESET}${MAGENTA}S${SESSION_NUM}${RESET}${DIM}:${RESET}${SESSION_NICK}${DIM}]${RESET}"
556
+ else
557
+ SESSION_INFO="${DIM}[${RESET}${MAGENTA}S${SESSION_NUM}${RESET}${DIM}]${RESET}"
558
+ fi
559
+ fi
560
+ fi
561
+ fi
562
+ fi
563
+
523
564
  # ============================================================================
524
565
  # Build Status Line
525
566
  # ============================================================================
@@ -533,6 +574,12 @@ if [ "$SHOW_AGILEFLOW" = "true" ] && [ -n "$AGILEFLOW_DISPLAY" ]; then
533
574
  OUTPUT="${AGILEFLOW_DISPLAY}"
534
575
  fi
535
576
 
577
+ # Session info (if in a non-main session)
578
+ if [ "$SHOW_SESSION" = "true" ] && [ -n "$SESSION_INFO" ]; then
579
+ [ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
580
+ OUTPUT="${OUTPUT}${SESSION_INFO}"
581
+ fi
582
+
536
583
  # Model with subtle styling (if enabled and available)
537
584
  if [ "$SHOW_MODEL" = "true" ] && [ -n "$MODEL_DISPLAY" ]; then
538
585
  [ -n "$OUTPUT" ] && OUTPUT="${OUTPUT}${SEP}"
@@ -22,6 +22,22 @@ const { getProjectRoot } = require('../lib/paths');
22
22
  // Session manager path (relative to script location)
23
23
  const SESSION_MANAGER_PATH = path.join(__dirname, 'session-manager.js');
24
24
 
25
+ // Story claiming module
26
+ let storyClaiming;
27
+ try {
28
+ storyClaiming = require('./lib/story-claiming.js');
29
+ } catch (e) {
30
+ // Story claiming not available
31
+ }
32
+
33
+ // File tracking module
34
+ let fileTracking;
35
+ try {
36
+ fileTracking = require('./lib/file-tracking.js');
37
+ } catch (e) {
38
+ // File tracking not available
39
+ }
40
+
25
41
  // Update checker module
26
42
  let updateChecker;
27
43
  try {
@@ -481,14 +497,18 @@ function getChangelogEntries(version) {
481
497
  async function runAutoUpdate(rootDir) {
482
498
  try {
483
499
  console.log(`${c.skyBlue}Updating AgileFlow...${c.reset}`);
484
- execSync('npx agileflow update', {
500
+ // Use --force to skip prompts for non-interactive auto-update
501
+ execSync('npx agileflow@latest update --force', {
485
502
  cwd: rootDir,
486
503
  encoding: 'utf8',
487
504
  stdio: 'inherit',
505
+ timeout: 120000, // 2 minute timeout
488
506
  });
507
+ console.log(`${c.mintGreen}Update complete!${c.reset}`);
489
508
  return true;
490
509
  } catch (e) {
491
- console.log(`${c.peach}Auto-update failed. Run manually: npx agileflow update${c.reset}`);
510
+ console.log(`${c.peach}Auto-update failed: ${e.message}${c.reset}`);
511
+ console.log(`${c.dim}Run manually: npx agileflow update${c.reset}`);
492
512
  return false;
493
513
  }
494
514
  }
@@ -722,16 +742,19 @@ function formatTable(
722
742
  lines.push(fullRow(` Run: ${c.skyBlue}npx agileflow update${c.reset}`, ''));
723
743
  }
724
744
 
725
- // Show "just updated" changelog
726
- if (updateInfo.justUpdated && updateInfo.changelog && updateInfo.changelog.length > 0) {
745
+ // Always show "What's new" section with current version changelog
746
+ // Get changelog entries for current version (even if not just updated)
747
+ const changelogEntries = updateInfo.changelog && updateInfo.changelog.length > 0
748
+ ? updateInfo.changelog
749
+ : getChangelogEntries(info.version);
750
+
751
+ if (changelogEntries && changelogEntries.length > 0) {
727
752
  lines.push(fullDivider());
728
- lines.push(
729
- fullRow(
730
- `${c.mintGreen}✨${c.reset} What's new in ${c.softGold}v${info.version}${c.reset}:`,
731
- ''
732
- )
733
- );
734
- for (const entry of updateInfo.changelog.slice(0, 2)) {
753
+ const headerText = updateInfo.justUpdated
754
+ ? `${c.mintGreen}✨${c.reset} Just updated to ${c.softGold}v${info.version}${c.reset}:`
755
+ : `${c.teal}📋${c.reset} What's new in ${c.softGold}v${info.version}${c.reset}:`;
756
+ lines.push(fullRow(headerText, ''));
757
+ for (const entry of changelogEntries.slice(0, 2)) {
735
758
  lines.push(fullRow(` ${c.teal}•${c.reset} ${truncate(entry, W - 6)}`, ''));
736
759
  }
737
760
  lines.push(fullRow(` Run ${c.skyBlue}/agileflow:whats-new${c.reset} for full changelog`, ''));
@@ -976,6 +999,56 @@ async function main() {
976
999
  `${c.slate} Run ${c.skyBlue}/agileflow:session:new${c.reset}${c.slate} to create isolated workspace.${c.reset}`
977
1000
  );
978
1001
  }
1002
+
1003
+ // Story claiming: cleanup stale claims and show warnings
1004
+ if (storyClaiming) {
1005
+ try {
1006
+ // Clean up stale claims (dead PIDs, expired TTL)
1007
+ const cleanupResult = storyClaiming.cleanupStaleClaims({ rootDir });
1008
+ if (cleanupResult.ok && cleanupResult.cleaned > 0) {
1009
+ console.log('');
1010
+ console.log(
1011
+ `${c.dim}Cleaned ${cleanupResult.cleaned} stale story claim(s)${c.reset}`
1012
+ );
1013
+ }
1014
+
1015
+ // Show stories claimed by other sessions
1016
+ const othersResult = storyClaiming.getStoriesClaimedByOthers({ rootDir });
1017
+ if (othersResult.ok && othersResult.stories && othersResult.stories.length > 0) {
1018
+ console.log('');
1019
+ console.log(storyClaiming.formatClaimedStories(othersResult.stories));
1020
+ console.log('');
1021
+ console.log(
1022
+ `${c.slate} These stories are locked - pick a different one to avoid conflicts.${c.reset}`
1023
+ );
1024
+ }
1025
+ } catch (e) {
1026
+ // Silently ignore story claiming errors
1027
+ }
1028
+ }
1029
+
1030
+ // File tracking: cleanup stale touches and show overlap warnings
1031
+ if (fileTracking) {
1032
+ try {
1033
+ // Clean up stale file touches (dead PIDs, expired TTL)
1034
+ const cleanupResult = fileTracking.cleanupStaleTouches({ rootDir });
1035
+ if (cleanupResult.ok && cleanupResult.cleaned > 0) {
1036
+ console.log('');
1037
+ console.log(
1038
+ `${c.dim}Cleaned ${cleanupResult.cleaned} stale file tracking session(s)${c.reset}`
1039
+ );
1040
+ }
1041
+
1042
+ // Show file overlaps with other sessions
1043
+ const overlapsResult = fileTracking.getMyFileOverlaps({ rootDir });
1044
+ if (overlapsResult.ok && overlapsResult.overlaps && overlapsResult.overlaps.length > 0) {
1045
+ console.log('');
1046
+ console.log(fileTracking.formatFileOverlaps(overlapsResult.overlaps));
1047
+ }
1048
+ } catch (e) {
1049
+ // Silently ignore file tracking errors
1050
+ }
1051
+ }
979
1052
  }
980
1053
 
981
1054
  main().catch(console.error);
@@ -1,20 +1,15 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  /**
4
- * check-update.js - Check for AgileFlow updates with caching
4
+ * check-update.js - Check for AgileFlow updates
5
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
6
+ * Simple update checker:
7
+ * - Always checks npm registry for latest version
8
+ * - Auto-updates if update is available (enabled by default)
9
+ * - Fast npm metadata lookup (~100-500ms)
11
10
  *
12
11
  * 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)
12
+ * node check-update.js [--json]
18
13
  *
19
14
  * Environment:
20
15
  * DEBUG_UPDATE=1 Enable debug logging
@@ -66,12 +61,9 @@ function getInstalledVersion(rootDir) {
66
61
  // Get update configuration from metadata
67
62
  function getUpdateConfig(rootDir) {
68
63
  const defaults = {
69
- autoUpdate: false,
70
- checkFrequency: 'daily', // hourly, daily, weekly, never
64
+ autoUpdate: true, // Auto-update enabled by default
71
65
  showChangelog: true,
72
- lastCheck: null,
73
66
  lastSeenVersion: null,
74
- latestVersion: null,
75
67
  };
76
68
 
77
69
  const metadataPath = path.join(rootDir, 'docs/00-meta/agileflow-metadata.json');
@@ -109,34 +101,6 @@ function saveUpdateConfig(rootDir, config) {
109
101
  return true;
110
102
  }
111
103
 
112
- // Check if cache is still valid
113
- function isCacheValid(config) {
114
- if (!config.lastCheck) return false;
115
-
116
- const now = Date.now();
117
- const lastCheck = new Date(config.lastCheck).getTime();
118
- const age = now - lastCheck;
119
-
120
- // Convert frequency to milliseconds
121
- const frequencies = {
122
- hourly: 60 * 60 * 1000, // 1 hour
123
- daily: 24 * 60 * 60 * 1000, // 24 hours
124
- weekly: 7 * 24 * 60 * 60 * 1000, // 7 days
125
- never: Infinity,
126
- };
127
-
128
- const maxAge = frequencies[config.checkFrequency] || frequencies.daily;
129
-
130
- debugLog('Cache check', {
131
- lastCheck: config.lastCheck,
132
- ageMs: age,
133
- maxAgeMs: maxAge,
134
- valid: age < maxAge,
135
- });
136
-
137
- return age < maxAge;
138
- }
139
-
140
104
  // Fetch latest version from npm
141
105
  async function fetchLatestVersion() {
142
106
  return new Promise(resolve => {
@@ -205,8 +169,8 @@ function compareVersions(a, b) {
205
169
  return 0;
206
170
  }
207
171
 
208
- // Main check function
209
- async function checkForUpdates(options = {}) {
172
+ // Main check function - always checks npm
173
+ async function checkForUpdates() {
210
174
  const rootDir = getProjectRoot();
211
175
  const installedVersion = getInstalledVersion(rootDir);
212
176
  const config = getUpdateConfig(rootDir);
@@ -218,7 +182,6 @@ async function checkForUpdates(options = {}) {
218
182
  autoUpdate: config.autoUpdate,
219
183
  justUpdated: false,
220
184
  previousVersion: config.lastSeenVersion,
221
- fromCache: false,
222
185
  error: null,
223
186
  };
224
187
 
@@ -234,21 +197,8 @@ async function checkForUpdates(options = {}) {
234
197
  result.previousVersion = config.lastSeenVersion;
235
198
  }
236
199
 
237
- // Use cache if valid and not forced
238
- if (!options.force && isCacheValid(config) && config.latestVersion) {
239
- result.latest = config.latestVersion;
240
- result.fromCache = true;
241
- } else if (config.checkFrequency !== 'never') {
242
- // Fetch from npm
243
- result.latest = await fetchLatestVersion();
244
-
245
- // Update cache
246
- if (result.latest) {
247
- config.lastCheck = new Date().toISOString();
248
- config.latestVersion = result.latest;
249
- saveUpdateConfig(rootDir, config);
250
- }
251
- }
200
+ // Always fetch from npm
201
+ result.latest = await fetchLatestVersion();
252
202
 
253
203
  // Compare versions
254
204
  if (result.latest && compareVersions(installedVersion, result.latest) < 0) {
@@ -269,7 +219,6 @@ function markVersionSeen(version) {
269
219
  // CLI interface
270
220
  async function main() {
271
221
  const args = process.argv.slice(2);
272
- const force = args.includes('--force');
273
222
  const jsonOutput = args.includes('--json');
274
223
  const markSeen = args.includes('--mark-seen');
275
224
 
@@ -286,7 +235,7 @@ async function main() {
286
235
  return;
287
236
  }
288
237
 
289
- const result = await checkForUpdates({ force });
238
+ const result = await checkForUpdates();
290
239
 
291
240
  if (jsonOutput) {
292
241
  console.log(JSON.stringify(result, null, 2));