agileflow 2.91.0 → 2.92.1
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 +10 -0
- package/README.md +6 -6
- package/lib/README.md +178 -0
- package/lib/codebase-indexer.js +32 -23
- package/lib/colors.js +190 -12
- package/lib/consent.js +232 -0
- package/lib/correlation.js +277 -0
- package/lib/error-codes.js +46 -0
- package/lib/errors.js +48 -6
- package/lib/file-cache.js +182 -0
- package/lib/format-error.js +156 -0
- package/lib/path-resolver.js +155 -7
- package/lib/paths.js +212 -20
- package/lib/placeholder-registry.js +205 -0
- package/lib/registry-di.js +358 -0
- package/lib/result-schema.js +363 -0
- package/lib/result.js +210 -0
- package/lib/session-registry.js +13 -0
- package/lib/session-state-machine.js +465 -0
- package/lib/validate-commands.js +308 -0
- package/lib/validate.js +116 -52
- package/package.json +1 -1
- package/scripts/af +34 -0
- package/scripts/agent-loop.js +63 -9
- package/scripts/agileflow-configure.js +2 -2
- package/scripts/agileflow-welcome.js +491 -23
- package/scripts/archive-completed-stories.sh +57 -11
- package/scripts/claude-tmux.sh +102 -0
- package/scripts/damage-control-bash.js +3 -70
- package/scripts/damage-control-edit.js +3 -20
- package/scripts/damage-control-write.js +3 -20
- package/scripts/dependency-check.js +310 -0
- package/scripts/get-env.js +11 -4
- package/scripts/lib/configure-detect.js +23 -1
- package/scripts/lib/configure-features.js +50 -2
- package/scripts/lib/context-formatter.js +771 -0
- package/scripts/lib/context-loader.js +699 -0
- package/scripts/lib/damage-control-utils.js +107 -0
- package/scripts/lib/json-utils.sh +162 -0
- package/scripts/lib/state-migrator.js +353 -0
- package/scripts/lib/story-state-machine.js +437 -0
- package/scripts/obtain-context.js +80 -1248
- package/scripts/pre-push-check.sh +46 -0
- package/scripts/precompact-context.sh +23 -10
- package/scripts/query-codebase.js +127 -14
- package/scripts/ralph-loop.js +5 -5
- package/scripts/session-manager.js +408 -55
- package/scripts/spawn-parallel.js +666 -0
- package/scripts/tui/blessed/data/watcher.js +20 -15
- package/scripts/tui/blessed/index.js +2 -2
- package/scripts/tui/blessed/panels/output.js +14 -8
- package/scripts/tui/blessed/panels/sessions.js +22 -15
- package/scripts/tui/blessed/panels/trace.js +14 -8
- package/scripts/tui/blessed/ui/help.js +3 -3
- package/scripts/tui/blessed/ui/screen.js +4 -4
- package/scripts/tui/blessed/ui/statusbar.js +5 -9
- package/scripts/tui/blessed/ui/tabbar.js +11 -11
- package/scripts/validators/component-validator.js +41 -14
- package/scripts/validators/json-schema-validator.js +11 -4
- package/scripts/validators/markdown-validator.js +1 -2
- package/scripts/validators/migration-validator.js +17 -5
- package/scripts/validators/security-validator.js +137 -33
- package/scripts/validators/story-format-validator.js +31 -10
- package/scripts/validators/test-result-validator.js +19 -4
- package/scripts/validators/workflow-validator.js +12 -5
- package/src/core/agents/codebase-query.md +24 -0
- package/src/core/commands/adr.md +114 -0
- package/src/core/commands/agent.md +120 -0
- package/src/core/commands/assign.md +145 -0
- package/src/core/commands/babysit.md +32 -5
- package/src/core/commands/changelog.md +118 -0
- package/src/core/commands/configure.md +42 -6
- package/src/core/commands/diagnose.md +114 -0
- package/src/core/commands/epic.md +113 -0
- package/src/core/commands/handoff.md +128 -0
- package/src/core/commands/help.md +75 -0
- package/src/core/commands/pr.md +96 -0
- package/src/core/commands/roadmap/analyze.md +400 -0
- package/src/core/commands/session/new.md +132 -6
- package/src/core/commands/session/spawn.md +197 -0
- package/src/core/commands/sprint.md +22 -0
- package/src/core/commands/status.md +74 -0
- package/src/core/commands/story.md +143 -4
- package/src/core/templates/agileflow-metadata.json +55 -2
- package/src/core/templates/plan-template.md +125 -0
- package/src/core/templates/story-lifecycle.md +213 -0
- package/src/core/templates/story-template.md +4 -0
- package/src/core/templates/tdd-test-template.js +241 -0
- package/tools/cli/commands/setup.js +95 -0
- package/tools/cli/installers/core/installer.js +94 -0
- package/tools/cli/installers/ide/_base-ide.js +20 -11
- package/tools/cli/installers/ide/codex.js +29 -47
- package/tools/cli/installers/ide/windsurf.js +1 -1
- package/tools/cli/lib/config-manager.js +17 -2
- package/tools/cli/lib/content-transformer.js +271 -0
- package/tools/cli/lib/error-handler.js +14 -22
- package/tools/cli/lib/ide-error-factory.js +421 -0
- package/tools/cli/lib/ide-health-monitor.js +364 -0
- package/tools/cli/lib/ide-registry.js +113 -2
- package/tools/cli/lib/ui.js +15 -25
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.92.1] - 2026-01-23
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Session worktree timeout, progress feedback, and docs folder copy
|
|
14
|
+
|
|
15
|
+
## [2.92.0] - 2026-01-23
|
|
16
|
+
|
|
17
|
+
### Added
|
|
18
|
+
- Tmux integration, i18n docs, EP-0022 codebase health improvements
|
|
19
|
+
|
|
10
20
|
## [2.91.0] - 2026-01-20
|
|
11
21
|
|
|
12
22
|
### Added
|
package/README.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
</p>
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/agileflow)
|
|
6
|
-
[](docs/04-architecture/commands.md)
|
|
7
|
+
[](docs/04-architecture/subagents.md)
|
|
8
8
|
[](docs/04-architecture/skills.md)
|
|
9
9
|
|
|
10
10
|
**AI-driven agile development for Claude Code, Cursor, Windsurf, OpenAI Codex CLI, and more.** Combining Scrum, Kanban, ADRs, and docs-as-code principles into one framework-agnostic system.
|
|
@@ -65,8 +65,8 @@ AgileFlow combines three proven methodologies:
|
|
|
65
65
|
|
|
66
66
|
| Component | Count | Description |
|
|
67
67
|
|-----------|-------|-------------|
|
|
68
|
-
| [Commands](docs/04-architecture/commands.md) |
|
|
69
|
-
| [Agents/Experts](docs/04-architecture/subagents.md) |
|
|
68
|
+
| [Commands](docs/04-architecture/commands.md) | 77 | Slash commands for agile workflows |
|
|
69
|
+
| [Agents/Experts](docs/04-architecture/subagents.md) | 31 | 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
|
|
|
72
72
|
---
|
|
@@ -76,8 +76,8 @@ 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
|
|
80
|
-
- [Agents/Experts](docs/04-architecture/subagents.md) -
|
|
79
|
+
- [Commands](docs/04-architecture/commands.md) - All 77 slash commands
|
|
80
|
+
- [Agents/Experts](docs/04-architecture/subagents.md) - 31 specialized agents with self-improving knowledge
|
|
81
81
|
- [Skills](docs/04-architecture/skills.md) - Dynamic skill generator with MCP integration
|
|
82
82
|
|
|
83
83
|
### Architecture
|
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 |
|
package/lib/codebase-indexer.js
CHANGED
|
@@ -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, '\\.')
|
|
303
|
+
.replace(/\./g, '\\.') // Escape dots
|
|
306
304
|
.replace(/\*\*/g, '<<<GLOB>>>') // Temp placeholder for **
|
|
307
|
-
.replace(/\*/g, '[^/]*')
|
|
308
|
-
.replace(/<<<GLOB>>>/g, '.*')
|
|
309
|
-
.replace(/\?/g, '.');
|
|
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(
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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(
|
|
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,12 @@ 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
|
|
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
|
|
646
|
+
if (!Object.hasOwn(existingIndex.symbols.exports, exp))
|
|
647
|
+
existingIndex.symbols.exports[exp] = [];
|
|
639
648
|
existingIndex.symbols.exports[exp].push(filePath);
|
|
640
649
|
}
|
|
641
650
|
}
|
|
@@ -713,14 +722,14 @@ function queryFiles(index, pattern) {
|
|
|
713
722
|
// Order matters: handle ** before * to avoid double processing
|
|
714
723
|
let regexPattern = pattern
|
|
715
724
|
// First, use placeholders to protect multi-char patterns
|
|
716
|
-
.replace(/\*\*\//g, '<<<GLOBSLASH>>>')
|
|
717
|
-
.replace(/\*\*/g, '<<<GLOB>>>')
|
|
718
|
-
.replace(/\./g, '\\.')
|
|
719
|
-
.replace(/\?/g, '.')
|
|
720
|
-
.replace(/\*/g, '[^/]*')
|
|
725
|
+
.replace(/\*\*\//g, '<<<GLOBSLASH>>>') // **/ placeholder
|
|
726
|
+
.replace(/\*\*/g, '<<<GLOB>>>') // ** placeholder
|
|
727
|
+
.replace(/\./g, '\\.') // Escape dots
|
|
728
|
+
.replace(/\?/g, '.') // ? = any single char
|
|
729
|
+
.replace(/\*/g, '[^/]*') // Single * = any chars except /
|
|
721
730
|
// Now restore placeholders with actual patterns
|
|
722
731
|
.replace(/<<<GLOBSLASH>>>/g, '(?:.+/)?') // **/ = optionally any path + /
|
|
723
|
-
.replace(/<<<GLOB>>>/g, '.*');
|
|
732
|
+
.replace(/<<<GLOB>>>/g, '.*'); // ** alone = any chars including /
|
|
724
733
|
|
|
725
734
|
const regex = new RegExp(`^${regexPattern}$`, 'i');
|
|
726
735
|
|
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
|
|
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
|
-
*
|
|
249
|
+
* Get status indicators with current color palette.
|
|
250
|
+
* Uses a Proxy to dynamically generate colored indicators.
|
|
118
251
|
*/
|
|
119
|
-
const status =
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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
|
};
|