agileflow 2.91.0 → 2.92.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +5 -0
  2. package/README.md +3 -3
  3. package/lib/README.md +178 -0
  4. package/lib/codebase-indexer.js +31 -23
  5. package/lib/colors.js +190 -12
  6. package/lib/consent.js +232 -0
  7. package/lib/correlation.js +277 -0
  8. package/lib/error-codes.js +46 -0
  9. package/lib/errors.js +48 -6
  10. package/lib/file-cache.js +182 -0
  11. package/lib/format-error.js +156 -0
  12. package/lib/path-resolver.js +155 -7
  13. package/lib/paths.js +212 -20
  14. package/lib/placeholder-registry.js +205 -0
  15. package/lib/registry-di.js +358 -0
  16. package/lib/result-schema.js +363 -0
  17. package/lib/result.js +210 -0
  18. package/lib/session-registry.js +13 -0
  19. package/lib/session-state-machine.js +465 -0
  20. package/lib/validate-commands.js +308 -0
  21. package/lib/validate.js +116 -52
  22. package/package.json +1 -1
  23. package/scripts/af +34 -0
  24. package/scripts/agent-loop.js +63 -9
  25. package/scripts/agileflow-configure.js +2 -2
  26. package/scripts/agileflow-welcome.js +435 -23
  27. package/scripts/archive-completed-stories.sh +57 -11
  28. package/scripts/claude-tmux.sh +102 -0
  29. package/scripts/damage-control-bash.js +3 -70
  30. package/scripts/damage-control-edit.js +3 -20
  31. package/scripts/damage-control-write.js +3 -20
  32. package/scripts/dependency-check.js +310 -0
  33. package/scripts/get-env.js +11 -4
  34. package/scripts/lib/configure-detect.js +23 -1
  35. package/scripts/lib/configure-features.js +43 -2
  36. package/scripts/lib/context-formatter.js +771 -0
  37. package/scripts/lib/context-loader.js +699 -0
  38. package/scripts/lib/damage-control-utils.js +107 -0
  39. package/scripts/lib/json-utils.sh +162 -0
  40. package/scripts/lib/state-migrator.js +353 -0
  41. package/scripts/lib/story-state-machine.js +437 -0
  42. package/scripts/obtain-context.js +80 -1248
  43. package/scripts/pre-push-check.sh +46 -0
  44. package/scripts/precompact-context.sh +23 -10
  45. package/scripts/query-codebase.js +122 -14
  46. package/scripts/ralph-loop.js +5 -5
  47. package/scripts/session-manager.js +220 -42
  48. package/scripts/spawn-parallel.js +651 -0
  49. package/scripts/tui/blessed/data/watcher.js +20 -15
  50. package/scripts/tui/blessed/index.js +2 -2
  51. package/scripts/tui/blessed/panels/output.js +14 -8
  52. package/scripts/tui/blessed/panels/sessions.js +22 -15
  53. package/scripts/tui/blessed/panels/trace.js +14 -8
  54. package/scripts/tui/blessed/ui/help.js +3 -3
  55. package/scripts/tui/blessed/ui/screen.js +4 -4
  56. package/scripts/tui/blessed/ui/statusbar.js +5 -9
  57. package/scripts/tui/blessed/ui/tabbar.js +11 -11
  58. package/scripts/validators/component-validator.js +41 -14
  59. package/scripts/validators/json-schema-validator.js +11 -4
  60. package/scripts/validators/markdown-validator.js +1 -2
  61. package/scripts/validators/migration-validator.js +17 -5
  62. package/scripts/validators/security-validator.js +137 -33
  63. package/scripts/validators/story-format-validator.js +31 -10
  64. package/scripts/validators/test-result-validator.js +19 -4
  65. package/scripts/validators/workflow-validator.js +12 -5
  66. package/src/core/agents/codebase-query.md +24 -0
  67. package/src/core/commands/adr.md +114 -0
  68. package/src/core/commands/agent.md +120 -0
  69. package/src/core/commands/assign.md +145 -0
  70. package/src/core/commands/babysit.md +32 -5
  71. package/src/core/commands/changelog.md +118 -0
  72. package/src/core/commands/configure.md +42 -6
  73. package/src/core/commands/diagnose.md +114 -0
  74. package/src/core/commands/epic.md +113 -0
  75. package/src/core/commands/handoff.md +128 -0
  76. package/src/core/commands/help.md +75 -0
  77. package/src/core/commands/pr.md +96 -0
  78. package/src/core/commands/roadmap/analyze.md +400 -0
  79. package/src/core/commands/session/new.md +113 -6
  80. package/src/core/commands/session/spawn.md +197 -0
  81. package/src/core/commands/sprint.md +22 -0
  82. package/src/core/commands/status.md +74 -0
  83. package/src/core/commands/story.md +143 -4
  84. package/src/core/templates/agileflow-metadata.json +55 -2
  85. package/src/core/templates/plan-template.md +125 -0
  86. package/src/core/templates/story-lifecycle.md +213 -0
  87. package/src/core/templates/story-template.md +4 -0
  88. package/src/core/templates/tdd-test-template.js +241 -0
  89. package/tools/cli/commands/setup.js +86 -0
  90. package/tools/cli/installers/core/installer.js +94 -0
  91. package/tools/cli/installers/ide/_base-ide.js +20 -11
  92. package/tools/cli/installers/ide/codex.js +29 -47
  93. package/tools/cli/lib/config-manager.js +17 -2
  94. package/tools/cli/lib/content-transformer.js +271 -0
  95. package/tools/cli/lib/error-handler.js +14 -22
  96. package/tools/cli/lib/ide-error-factory.js +421 -0
  97. package/tools/cli/lib/ide-health-monitor.js +364 -0
  98. package/tools/cli/lib/ide-registry.js +114 -1
  99. package/tools/cli/lib/ui.js +14 -25
package/CHANGELOG.md CHANGED
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [2.92.0] - 2026-01-23
11
+
12
+ ### Added
13
+ - Tmux integration, i18n docs, EP-0022 codebase health improvements
14
+
10
15
  ## [2.91.0] - 2026-01-20
11
16
 
12
17
  ### Added
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  </p>
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/agileflow?color=brightgreen)](https://www.npmjs.com/package/agileflow)
6
- [![Commands](https://img.shields.io/badge/commands-74-blue)](docs/04-architecture/commands.md)
6
+ [![Commands](https://img.shields.io/badge/commands-76-blue)](docs/04-architecture/commands.md)
7
7
  [![Agents/Experts](https://img.shields.io/badge/agents%2Fexperts-30-orange)](docs/04-architecture/subagents.md)
8
8
  [![Skills](https://img.shields.io/badge/skills-dynamic-purple)](docs/04-architecture/skills.md)
9
9
 
@@ -65,7 +65,7 @@ AgileFlow combines three proven methodologies:
65
65
 
66
66
  | Component | Count | Description |
67
67
  |-----------|-------|-------------|
68
- | [Commands](docs/04-architecture/commands.md) | 74 | Slash commands for agile workflows |
68
+ | [Commands](docs/04-architecture/commands.md) | 76 | Slash commands for agile workflows |
69
69
  | [Agents/Experts](docs/04-architecture/subagents.md) | 30 | Specialized agents with self-improving knowledge bases |
70
70
  | [Skills](docs/04-architecture/skills.md) | Dynamic | Generated on-demand with `/agileflow:skill:create` |
71
71
 
@@ -76,7 +76,7 @@ AgileFlow combines three proven methodologies:
76
76
  Full documentation lives in [`docs/04-architecture/`](docs/04-architecture/):
77
77
 
78
78
  ### Reference
79
- - [Commands](docs/04-architecture/commands.md) - All 74 slash commands
79
+ - [Commands](docs/04-architecture/commands.md) - All 76 slash commands
80
80
  - [Agents/Experts](docs/04-architecture/subagents.md) - 30 specialized agents with self-improving knowledge
81
81
  - [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
82
82
 
package/lib/README.md ADDED
@@ -0,0 +1,178 @@
1
+ # AgileFlow CLI Library
2
+
3
+ Core utility modules for AgileFlow CLI operations.
4
+
5
+ ## Result Schema (`result-schema.js`)
6
+
7
+ Provides a unified `Result<T>` type for consistent success/failure handling across the codebase.
8
+
9
+ ### Quick Start
10
+
11
+ ```javascript
12
+ const { success, failure, isSuccess, unwrapOr } = require('./result-schema');
13
+
14
+ // Success case
15
+ function loadConfig() {
16
+ const config = readConfigFile();
17
+ return success(config);
18
+ }
19
+
20
+ // Failure case with error code
21
+ function loadConfig() {
22
+ if (!fs.existsSync(configPath)) {
23
+ return failure('ENOENT', 'Config file not found');
24
+ }
25
+ return success(config);
26
+ }
27
+
28
+ // Using results
29
+ const result = loadConfig();
30
+ if (isSuccess(result)) {
31
+ console.log(result.data);
32
+ } else {
33
+ console.error(result.error);
34
+ console.error('Fix:', result.suggestedFix);
35
+ }
36
+ ```
37
+
38
+ ### Result Structure
39
+
40
+ **Success:**
41
+ ```javascript
42
+ {
43
+ ok: true,
44
+ data: <T> // The success data
45
+ }
46
+ ```
47
+
48
+ **Failure:**
49
+ ```javascript
50
+ {
51
+ ok: false,
52
+ error: string, // Human-readable message
53
+ errorCode: string, // Machine-readable code (ENOENT, ECONFIG, etc.)
54
+ severity: string, // critical | high | medium | low
55
+ category: string, // filesystem | permission | configuration | network | validation | state | dependency
56
+ recoverable: boolean,
57
+ suggestedFix: string,
58
+ autoFix: string|null,
59
+ context?: object, // Additional context
60
+ cause?: Error // Original error
61
+ }
62
+ ```
63
+
64
+ ### API Reference
65
+
66
+ #### Constructors
67
+
68
+ | Function | Description |
69
+ |----------|-------------|
70
+ | `success(data, meta?)` | Create success result |
71
+ | `failure(errorCode, message?, options?)` | Create failure result with error code |
72
+ | `failureFromError(error, defaultCode?)` | Create failure from Error object |
73
+
74
+ #### From Helpers
75
+
76
+ | Function | Description |
77
+ |----------|-------------|
78
+ | `fromCondition(condition, data, errorCode, message?)` | Create from boolean |
79
+ | `fromPromise(promise, defaultCode?)` | Wrap Promise in Result |
80
+ | `fromTry(fn, defaultCode?)` | Wrap sync function in Result |
81
+
82
+ #### Type Guards
83
+
84
+ | Function | Description |
85
+ |----------|-------------|
86
+ | `isSuccess(result)` | Check if result is success |
87
+ | `isFailure(result)` | Check if result is failure |
88
+
89
+ #### Extractors
90
+
91
+ | Function | Description |
92
+ |----------|-------------|
93
+ | `unwrap(result, context?)` | Get data or throw |
94
+ | `unwrapOr(result, defaultValue)` | Get data or default |
95
+
96
+ #### Transformers
97
+
98
+ | Function | Description |
99
+ |----------|-------------|
100
+ | `map(result, fn)` | Transform success data |
101
+ | `flatMap(result, fn)` | Chain result-returning operations |
102
+
103
+ #### Combinators
104
+
105
+ | Function | Description |
106
+ |----------|-------------|
107
+ | `all(results)` | Combine array of results |
108
+ | `any(results)` | Get first success or last failure |
109
+
110
+ ### Error Codes
111
+
112
+ Available error codes from `error-codes.js`:
113
+
114
+ | Code | Category | Description |
115
+ |------|----------|-------------|
116
+ | `ENOENT` | filesystem | File or directory not found |
117
+ | `ENODIR` | filesystem | Directory does not exist |
118
+ | `EEXIST` | filesystem | Already exists |
119
+ | `EACCES` | permission | Permission denied |
120
+ | `EPERM` | permission | Operation not permitted |
121
+ | `ECONFIG` | configuration | Invalid/missing configuration |
122
+ | `EPARSE` | configuration | Parse error (JSON/YAML) |
123
+ | `ESCHEMA` | configuration | Schema validation failed |
124
+ | `ENETWORK` | network | Network error |
125
+ | `ETIMEOUT` | network | Operation timed out |
126
+ | `EINVAL` | validation | Invalid argument |
127
+ | `EMISSING` | validation | Required value missing |
128
+ | `ESTATE` | state | Invalid application state |
129
+ | `ECONFLICT` | state | State conflict |
130
+ | `ELOCK` | state | Resource locked |
131
+ | `EDEP` | dependency | Missing dependency |
132
+ | `EUNKNOWN` | state | Unknown error |
133
+
134
+ ### Migration Guide
135
+
136
+ **Before (inconsistent):**
137
+ ```javascript
138
+ // Different modules return different shapes
139
+ return { ok: true, data };
140
+ return { success: true, result };
141
+ return { error: 'message' };
142
+ ```
143
+
144
+ **After (unified):**
145
+ ```javascript
146
+ const { success, failure } = require('../lib/result-schema');
147
+
148
+ // Always use Result type
149
+ return success(data);
150
+ return failure('ENOENT', 'File not found');
151
+ ```
152
+
153
+ ## Error Codes (`error-codes.js`)
154
+
155
+ Centralized error codes with metadata for auto-recovery.
156
+
157
+ ### Key Functions
158
+
159
+ | Function | Description |
160
+ |----------|-------------|
161
+ | `createErrorResult(code, message?, options?)` | Create failure Result |
162
+ | `createSuccessResult(data, meta?)` | Create success Result |
163
+ | `getErrorCode(code)` | Get error code metadata |
164
+ | `isRecoverable(error)` | Check if error is recoverable |
165
+ | `getSuggestedFix(error)` | Get fix suggestion |
166
+
167
+ ## Safe Wrappers (`errors.js`)
168
+
169
+ File/command wrappers that return Result objects:
170
+
171
+ | Function | Description |
172
+ |----------|-------------|
173
+ | `safeReadJSON(path, options?)` | Read and parse JSON |
174
+ | `safeWriteJSON(path, data, options?)` | Write JSON to file |
175
+ | `safeReadFile(path, options?)` | Read text file |
176
+ | `safeWriteFile(path, content, options?)` | Write text file |
177
+ | `safeExec(command, options?)` | Execute shell command |
178
+ | `safeParseJSON(content, options?)` | Parse JSON with validation |
@@ -69,9 +69,7 @@ function loadConfig(projectRoot) {
69
69
  return {
70
70
  ...DEFAULT_CONFIG,
71
71
  // Convert ttl_hours to ttlMs if provided
72
- ttlMs: userConfig.ttl_hours
73
- ? userConfig.ttl_hours * 60 * 60 * 1000
74
- : DEFAULT_CONFIG.ttlMs,
72
+ ttlMs: userConfig.ttl_hours ? userConfig.ttl_hours * 60 * 60 * 1000 : DEFAULT_CONFIG.ttlMs,
75
73
  excludePatterns: userConfig.exclude_patterns || DEFAULT_CONFIG.excludePatterns,
76
74
  includePatterns: userConfig.include_patterns || DEFAULT_CONFIG.includePatterns,
77
75
  maxFileSizeKb: userConfig.max_file_size_kb || DEFAULT_CONFIG.maxFileSizeKb,
@@ -302,11 +300,11 @@ function shouldIncludeFile(relativePath, excludePatterns) {
302
300
  // Handle ** (matches any path segments including none)
303
301
  // Handle * (matches within a single path segment)
304
302
  let regexPattern = pattern
305
- .replace(/\./g, '\\.') // Escape dots
303
+ .replace(/\./g, '\\.') // Escape dots
306
304
  .replace(/\*\*/g, '<<<GLOB>>>') // Temp placeholder for **
307
- .replace(/\*/g, '[^/]*') // Single * = any chars except /
308
- .replace(/<<<GLOB>>>/g, '.*') // ** = any chars including /
309
- .replace(/\?/g, '.'); // ? = any single char
305
+ .replace(/\*/g, '[^/]*') // Single * = any chars except /
306
+ .replace(/<<<GLOB>>>/g, '.*') // ** = any chars including /
307
+ .replace(/\?/g, '.'); // ? = any single char
310
308
 
311
309
  // Support patterns that should match the start of path
312
310
  const regex = new RegExp(`^${regexPattern}`);
@@ -397,7 +395,12 @@ function buildIndex(projectRoot, options = {}) {
397
395
  const index = createEmptyIndex(projectRoot);
398
396
 
399
397
  // Scan for files
400
- const files = scanDirectory(projectRoot, projectRoot, config.excludePatterns, config.maxFileSizeKb);
398
+ const files = scanDirectory(
399
+ projectRoot,
400
+ projectRoot,
401
+ config.excludePatterns,
402
+ config.maxFileSizeKb
403
+ );
401
404
  index.stats.total_files = files.length;
402
405
 
403
406
  // Process each file
@@ -437,29 +440,29 @@ function buildIndex(projectRoot, options = {}) {
437
440
  tags,
438
441
  };
439
442
 
440
- // Update tag index
443
+ // Update tag index (use Object.hasOwn to avoid prototype pollution)
441
444
  for (const tag of tags) {
442
- if (!index.tags[tag]) {
445
+ if (!Object.hasOwn(index.tags, tag)) {
443
446
  index.tags[tag] = [];
444
447
  }
445
448
  index.tags[tag].push(relativePath);
446
449
  }
447
450
 
448
- // Update symbol index
451
+ // Update symbol index (use Object.hasOwn to avoid prototype pollution with names like "constructor")
449
452
  for (const func of symbols.functions) {
450
- if (!index.symbols.functions[func]) {
453
+ if (!Object.hasOwn(index.symbols.functions, func)) {
451
454
  index.symbols.functions[func] = [];
452
455
  }
453
456
  index.symbols.functions[func].push(relativePath);
454
457
  }
455
458
  for (const cls of symbols.classes) {
456
- if (!index.symbols.classes[cls]) {
459
+ if (!Object.hasOwn(index.symbols.classes, cls)) {
457
460
  index.symbols.classes[cls] = [];
458
461
  }
459
462
  index.symbols.classes[cls].push(relativePath);
460
463
  }
461
464
  for (const exp of exports) {
462
- if (!index.symbols.exports[exp]) {
465
+ if (!Object.hasOwn(index.symbols.exports, exp)) {
463
466
  index.symbols.exports[exp] = [];
464
467
  }
465
468
  index.symbols.exports[exp].push(relativePath);
@@ -564,7 +567,12 @@ function updateIndex(projectRoot, options = {}) {
564
567
  const startTime = Date.now();
565
568
 
566
569
  // Scan for current files
567
- const currentFiles = scanDirectory(projectRoot, projectRoot, config.excludePatterns, config.maxFileSizeKb);
570
+ const currentFiles = scanDirectory(
571
+ projectRoot,
572
+ projectRoot,
573
+ config.excludePatterns,
574
+ config.maxFileSizeKb
575
+ );
568
576
  const currentFilePaths = new Set(currentFiles.map(f => f.path));
569
577
 
570
578
  // Track changes
@@ -631,11 +639,11 @@ function updateIndex(projectRoot, options = {}) {
631
639
 
632
640
  for (const [filePath, fileData] of Object.entries(existingIndex.files)) {
633
641
  for (const tag of fileData.tags || []) {
634
- if (!existingIndex.tags[tag]) existingIndex.tags[tag] = [];
642
+ if (!Object.hasOwn(existingIndex.tags, tag)) existingIndex.tags[tag] = [];
635
643
  existingIndex.tags[tag].push(filePath);
636
644
  }
637
645
  for (const exp of fileData.exports || []) {
638
- if (!existingIndex.symbols.exports[exp]) existingIndex.symbols.exports[exp] = [];
646
+ if (!Object.hasOwn(existingIndex.symbols.exports, exp)) existingIndex.symbols.exports[exp] = [];
639
647
  existingIndex.symbols.exports[exp].push(filePath);
640
648
  }
641
649
  }
@@ -713,14 +721,14 @@ function queryFiles(index, pattern) {
713
721
  // Order matters: handle ** before * to avoid double processing
714
722
  let regexPattern = pattern
715
723
  // First, use placeholders to protect multi-char patterns
716
- .replace(/\*\*\//g, '<<<GLOBSLASH>>>') // **/ placeholder
717
- .replace(/\*\*/g, '<<<GLOB>>>') // ** placeholder
718
- .replace(/\./g, '\\.') // Escape dots
719
- .replace(/\?/g, '.') // ? = any single char
720
- .replace(/\*/g, '[^/]*') // Single * = any chars except /
724
+ .replace(/\*\*\//g, '<<<GLOBSLASH>>>') // **/ placeholder
725
+ .replace(/\*\*/g, '<<<GLOB>>>') // ** placeholder
726
+ .replace(/\./g, '\\.') // Escape dots
727
+ .replace(/\?/g, '.') // ? = any single char
728
+ .replace(/\*/g, '[^/]*') // Single * = any chars except /
721
729
  // Now restore placeholders with actual patterns
722
730
  .replace(/<<<GLOBSLASH>>>/g, '(?:.+/)?') // **/ = optionally any path + /
723
- .replace(/<<<GLOB>>>/g, '.*'); // ** alone = any chars including /
731
+ .replace(/<<<GLOB>>>/g, '.*'); // ** alone = any chars including /
724
732
 
725
733
  const regex = new RegExp(`^${regexPattern}$`, 'i');
726
734
 
package/lib/colors.js CHANGED
@@ -11,21 +11,123 @@
11
11
  * - Cyan (#00CED1): 4.6:1 ✓ (meets AA for normal text)
12
12
  * - Brand (#e8683a): 3.8:1 ✓ (meets AA for large text/UI elements)
13
13
  *
14
+ * WCAG AAA Contrast Ratios (high-contrast mode, 7:1+ ratio):
15
+ * - HC Green (#7CFC00): 11.3:1 ✓ (lawn green - meets AAA)
16
+ * - HC Red (#FF6B6B): 5.0:1 → #FF9999 8.1:1 ✓ (light coral - meets AAA)
17
+ * - HC Yellow (#FFFF00): 19.6:1 ✓ (pure yellow - meets AAA)
18
+ * - HC Cyan (#00FFFF): 14.0:1 ✓ (aqua - meets AAA)
19
+ * - HC White (#FFFFFF): 21.0:1 ✓ (pure white - meets AAA)
20
+ *
14
21
  * Note: Standard ANSI colors vary by terminal theme. The above ratios
15
22
  * are for typical dark terminal configurations.
16
23
  */
17
24
 
25
+ // High-contrast mode detection
26
+ let _highContrastMode = null;
27
+
28
+ /**
29
+ * Check if high-contrast mode is enabled.
30
+ * Checks: AGILEFLOW_HIGH_CONTRAST env var, or cached value.
31
+ * @returns {boolean} True if high-contrast mode is enabled
32
+ */
33
+ function isHighContrast() {
34
+ if (_highContrastMode !== null) {
35
+ return _highContrastMode;
36
+ }
37
+ const envValue = process.env.AGILEFLOW_HIGH_CONTRAST;
38
+ return envValue === '1' || envValue === 'true' || envValue === 'yes';
39
+ }
40
+
41
+ /**
42
+ * Enable or disable high-contrast mode programmatically.
43
+ * @param {boolean} enabled - Whether to enable high-contrast mode
44
+ */
45
+ function setHighContrast(enabled) {
46
+ _highContrastMode = enabled;
47
+ }
48
+
49
+ /**
50
+ * Reset high-contrast mode to use environment variable.
51
+ */
52
+ function resetHighContrast() {
53
+ _highContrastMode = null;
54
+ }
55
+
18
56
  /**
19
57
  * Brand color hex value for chalk compatibility.
20
58
  * Use with chalk.hex(BRAND_HEX) in files that use chalk.
21
59
  */
22
60
  const BRAND_HEX = '#e8683a';
23
61
 
62
+ /**
63
+ * WCAG AAA high-contrast color palette (7:1+ contrast ratio).
64
+ * Used when AGILEFLOW_HIGH_CONTRAST=1 or --high-contrast flag.
65
+ */
66
+ const hc = {
67
+ // Reset and modifiers
68
+ reset: '\x1b[0m',
69
+ bold: '\x1b[1m',
70
+ dim: '\x1b[0m', // No dimming in high-contrast (use regular text)
71
+ italic: '\x1b[3m',
72
+ underline: '\x1b[4m',
73
+
74
+ // High-contrast standard colors (bright variants for max visibility)
75
+ red: '\x1b[91m', // Bright red
76
+ green: '\x1b[92m', // Bright green
77
+ yellow: '\x1b[93m', // Bright yellow
78
+ blue: '\x1b[94m', // Bright blue
79
+ magenta: '\x1b[95m', // Bright magenta
80
+ cyan: '\x1b[96m', // Bright cyan
81
+ white: '\x1b[97m', // Bright white
82
+
83
+ // Bright variants (same in high-contrast mode)
84
+ brightBlack: '\x1b[37m', // Use white instead of gray
85
+ brightRed: '\x1b[91m',
86
+ brightGreen: '\x1b[92m',
87
+ brightYellow: '\x1b[93m',
88
+ brightBlue: '\x1b[94m',
89
+ brightMagenta: '\x1b[95m',
90
+ brightCyan: '\x1b[96m',
91
+ brightWhite: '\x1b[97m',
92
+
93
+ // 256-color high-contrast alternatives (all 7:1+ ratio)
94
+ mintGreen: '\x1b[92m', // Bright green
95
+ peach: '\x1b[93m', // Bright yellow
96
+ coral: '\x1b[91m', // Bright red
97
+ lightGreen: '\x1b[92m', // Bright green
98
+ lightYellow: '\x1b[93m', // Bright yellow
99
+ lightPink: '\x1b[91m', // Bright red
100
+ skyBlue: '\x1b[96m', // Bright cyan
101
+ lavender: '\x1b[95m', // Bright magenta
102
+ softGold: '\x1b[93m', // Bright yellow
103
+ teal: '\x1b[96m', // Bright cyan
104
+ slate: '\x1b[97m', // White (instead of gray)
105
+ rose: '\x1b[91m', // Bright red
106
+ amber: '\x1b[93m', // Bright yellow
107
+ powder: '\x1b[96m', // Bright cyan
108
+
109
+ // Brand color - use bright orange/yellow for visibility
110
+ brand: '\x1b[38;2;255;165;0m', // Bright orange (#FFA500 - 8.0:1 ratio)
111
+ orange: '\x1b[38;2;255;165;0m',
112
+
113
+ // Background colors (same as standard)
114
+ bgRed: '\x1b[41m',
115
+ bgGreen: '\x1b[42m',
116
+ bgYellow: '\x1b[43m',
117
+ bgBlue: '\x1b[44m',
118
+
119
+ // Semantic aliases
120
+ success: '\x1b[92m',
121
+ error: '\x1b[91m',
122
+ warning: '\x1b[93m',
123
+ info: '\x1b[96m',
124
+ };
125
+
24
126
  /**
25
127
  * ANSI color codes for terminal output.
26
128
  * Includes standard colors, 256-color palette, and brand colors.
27
129
  */
28
- const c = {
130
+ const cStandard = {
29
131
  // Reset and modifiers
30
132
  reset: '\x1b[0m',
31
133
  bold: '\x1b[1m',
@@ -85,6 +187,36 @@ const c = {
85
187
  info: '\x1b[36m', // Same as cyan
86
188
  };
87
189
 
190
+ /**
191
+ * Get the active color palette based on high-contrast mode.
192
+ * @returns {Object} Color palette object (either cStandard or hc)
193
+ */
194
+ function getColors() {
195
+ return isHighContrast() ? hc : cStandard;
196
+ }
197
+
198
+ // For backwards compatibility, export a Proxy that delegates to the active palette
199
+ const c = new Proxy(
200
+ {},
201
+ {
202
+ get(_, prop) {
203
+ return getColors()[prop];
204
+ },
205
+ has(_, prop) {
206
+ return prop in cStandard;
207
+ },
208
+ ownKeys() {
209
+ return Object.keys(cStandard);
210
+ },
211
+ getOwnPropertyDescriptor(_, prop) {
212
+ if (prop in cStandard) {
213
+ return { enumerable: true, configurable: true, value: getColors()[prop] };
214
+ }
215
+ return undefined;
216
+ },
217
+ }
218
+ );
219
+
88
220
  /**
89
221
  * Box drawing characters for tables and borders.
90
222
  */
@@ -114,18 +246,49 @@ const box = {
114
246
  };
115
247
 
116
248
  /**
117
- * Status indicators with colors.
249
+ * Get status indicators with current color palette.
250
+ * Uses a Proxy to dynamically generate colored indicators.
118
251
  */
119
- const status = {
120
- success: `${c.green}✓${c.reset}`,
121
- warning: `${c.yellow}⚠️${c.reset}`,
122
- error: `${c.red}✗${c.reset}`,
123
- info: `${c.cyan}ℹ${c.reset}`,
124
- pending: `${c.dim}○${c.reset}`,
125
- inProgress: `${c.yellow}◐${c.reset}`,
126
- done: `${c.green}●${c.reset}`,
127
- blocked: `${c.red}◆${c.reset}`,
128
- };
252
+ const status = new Proxy(
253
+ {},
254
+ {
255
+ get(_, prop) {
256
+ const colors = getColors();
257
+ const indicators = {
258
+ success: `${colors.green}✓${colors.reset}`,
259
+ warning: `${colors.yellow}⚠️${colors.reset}`,
260
+ error: `${colors.red}✗${colors.reset}`,
261
+ info: `${colors.cyan}ℹ${colors.reset}`,
262
+ pending: `${isHighContrast() ? colors.white : colors.dim}○${colors.reset}`,
263
+ inProgress: `${colors.yellow}◐${colors.reset}`,
264
+ done: `${colors.green}●${colors.reset}`,
265
+ blocked: `${colors.red}◆${colors.reset}`,
266
+ };
267
+ return indicators[prop];
268
+ },
269
+ has(_, prop) {
270
+ return [
271
+ 'success',
272
+ 'warning',
273
+ 'error',
274
+ 'info',
275
+ 'pending',
276
+ 'inProgress',
277
+ 'done',
278
+ 'blocked',
279
+ ].includes(prop);
280
+ },
281
+ ownKeys() {
282
+ return ['success', 'warning', 'error', 'info', 'pending', 'inProgress', 'done', 'blocked'];
283
+ },
284
+ getOwnPropertyDescriptor(_, prop) {
285
+ if (status.has(_, prop)) {
286
+ return { enumerable: true, configurable: true };
287
+ }
288
+ return undefined;
289
+ },
290
+ }
291
+ );
129
292
 
130
293
  /**
131
294
  * Wrap text with color codes.
@@ -199,9 +362,22 @@ function brand(text) {
199
362
  }
200
363
 
201
364
  module.exports = {
365
+ // Color palettes
202
366
  c,
367
+ cStandard,
368
+ hc,
369
+ getColors,
370
+
371
+ // High-contrast mode control
372
+ isHighContrast,
373
+ setHighContrast,
374
+ resetHighContrast,
375
+
376
+ // UI elements
203
377
  box,
204
378
  status,
379
+
380
+ // Helper functions
205
381
  colorize,
206
382
  dim,
207
383
  bold,
@@ -209,5 +385,7 @@ module.exports = {
209
385
  warning,
210
386
  error,
211
387
  brand,
388
+
389
+ // Constants
212
390
  BRAND_HEX,
213
391
  };