agileflow 2.79.0 → 2.81.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/README.md +6 -6
- package/package.json +1 -1
- package/scripts/agent-loop.js +765 -0
- package/scripts/agileflow-configure.js +129 -18
- package/scripts/agileflow-welcome.js +113 -16
- package/scripts/damage-control/bash-tool-damage-control.js +7 -6
- package/scripts/damage-control/edit-tool-damage-control.js +4 -24
- package/scripts/damage-control/patterns.yaml +32 -32
- package/scripts/damage-control/write-tool-damage-control.js +4 -24
- package/scripts/damage-control-bash.js +38 -125
- package/scripts/damage-control-edit.js +22 -165
- package/scripts/damage-control-write.js +22 -165
- package/scripts/get-env.js +6 -6
- package/scripts/lib/damage-control-utils.js +251 -0
- package/scripts/obtain-context.js +103 -37
- package/scripts/ralph-loop.js +243 -31
- package/scripts/screenshot-verifier.js +4 -2
- package/scripts/session-manager.js +434 -20
- package/src/core/agents/configuration-visual-e2e.md +300 -0
- package/src/core/agents/orchestrator.md +166 -0
- package/src/core/commands/babysit.md +61 -15
- package/src/core/commands/configure.md +408 -99
- package/src/core/commands/session/end.md +332 -103
- package/src/core/experts/documentation/expertise.yaml +25 -0
- package/tools/cli/commands/start.js +19 -21
- package/tools/cli/installers/ide/claude-code.js +32 -19
- package/tools/cli/tui/Dashboard.js +3 -4
- package/tools/postinstall.js +1 -9
- package/src/core/commands/setup/visual-e2e.md +0 -462
|
@@ -12,40 +12,13 @@
|
|
|
12
12
|
* Usage: Configured as PreToolUse hook in .claude/settings.json
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
reset: '\x1b[0m',
|
|
23
|
-
dim: '\x1b[2m'
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
/**
|
|
27
|
-
* Find project root by looking for .agileflow directory
|
|
28
|
-
*/
|
|
29
|
-
function findProjectRoot() {
|
|
30
|
-
let dir = process.cwd();
|
|
31
|
-
while (dir !== '/') {
|
|
32
|
-
if (fs.existsSync(path.join(dir, '.agileflow'))) {
|
|
33
|
-
return dir;
|
|
34
|
-
}
|
|
35
|
-
dir = path.dirname(dir);
|
|
36
|
-
}
|
|
37
|
-
return process.cwd();
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Expand ~ to home directory
|
|
42
|
-
*/
|
|
43
|
-
function expandPath(p) {
|
|
44
|
-
if (p.startsWith('~/')) {
|
|
45
|
-
return path.join(os.homedir(), p.slice(2));
|
|
46
|
-
}
|
|
47
|
-
return p;
|
|
48
|
-
}
|
|
15
|
+
const {
|
|
16
|
+
findProjectRoot,
|
|
17
|
+
loadPatterns,
|
|
18
|
+
pathMatches,
|
|
19
|
+
outputBlocked,
|
|
20
|
+
runDamageControlHook,
|
|
21
|
+
} = require('./lib/damage-control-utils');
|
|
49
22
|
|
|
50
23
|
/**
|
|
51
24
|
* Parse simplified YAML for path patterns
|
|
@@ -54,7 +27,7 @@ function parseSimpleYAML(content) {
|
|
|
54
27
|
const config = {
|
|
55
28
|
zeroAccessPaths: [],
|
|
56
29
|
readOnlyPaths: [],
|
|
57
|
-
noDeletePaths: []
|
|
30
|
+
noDeletePaths: [],
|
|
58
31
|
};
|
|
59
32
|
|
|
60
33
|
let currentSection = null;
|
|
@@ -85,79 +58,6 @@ function parseSimpleYAML(content) {
|
|
|
85
58
|
return config;
|
|
86
59
|
}
|
|
87
60
|
|
|
88
|
-
/**
|
|
89
|
-
* Load patterns configuration from YAML file
|
|
90
|
-
*/
|
|
91
|
-
function loadPatterns(projectRoot) {
|
|
92
|
-
const configPaths = [
|
|
93
|
-
path.join(projectRoot, '.agileflow/config/damage-control-patterns.yaml'),
|
|
94
|
-
path.join(projectRoot, '.agileflow/config/damage-control-patterns.yml'),
|
|
95
|
-
path.join(projectRoot, '.agileflow/templates/damage-control-patterns.yaml')
|
|
96
|
-
];
|
|
97
|
-
|
|
98
|
-
for (const configPath of configPaths) {
|
|
99
|
-
if (fs.existsSync(configPath)) {
|
|
100
|
-
try {
|
|
101
|
-
const content = fs.readFileSync(configPath, 'utf8');
|
|
102
|
-
return parseSimpleYAML(content);
|
|
103
|
-
} catch (e) {
|
|
104
|
-
// Continue to next path
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
// Return empty config if no file found (fail-open)
|
|
110
|
-
return { zeroAccessPaths: [], readOnlyPaths: [], noDeletePaths: [] };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Check if a file path matches any of the protected patterns
|
|
115
|
-
*/
|
|
116
|
-
function pathMatches(filePath, patterns) {
|
|
117
|
-
if (!filePath) return null;
|
|
118
|
-
|
|
119
|
-
const normalizedPath = path.resolve(filePath);
|
|
120
|
-
const relativePath = path.relative(process.cwd(), normalizedPath);
|
|
121
|
-
|
|
122
|
-
for (const pattern of patterns) {
|
|
123
|
-
const expandedPattern = expandPath(pattern);
|
|
124
|
-
|
|
125
|
-
// Check if pattern is a directory prefix
|
|
126
|
-
if (pattern.endsWith('/')) {
|
|
127
|
-
const patternDir = expandedPattern.slice(0, -1);
|
|
128
|
-
if (normalizedPath.startsWith(patternDir)) {
|
|
129
|
-
return pattern;
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
// Check exact match
|
|
134
|
-
if (normalizedPath === expandedPattern) {
|
|
135
|
-
return pattern;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
// Check if normalized path ends with pattern (for filenames like "id_rsa")
|
|
139
|
-
if (normalizedPath.endsWith(pattern) || relativePath.endsWith(pattern)) {
|
|
140
|
-
return pattern;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
// Check if pattern appears in path (for patterns like "*.pem")
|
|
144
|
-
if (pattern.startsWith('*')) {
|
|
145
|
-
const ext = pattern.slice(1);
|
|
146
|
-
if (normalizedPath.endsWith(ext) || relativePath.endsWith(ext)) {
|
|
147
|
-
return pattern;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Check if path contains pattern (for things like ".env.production")
|
|
152
|
-
const patternBase = path.basename(pattern);
|
|
153
|
-
if (path.basename(normalizedPath) === patternBase) {
|
|
154
|
-
return pattern;
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
return null;
|
|
159
|
-
}
|
|
160
|
-
|
|
161
61
|
/**
|
|
162
62
|
* Validate file path for write operation
|
|
163
63
|
*/
|
|
@@ -168,7 +68,7 @@ function validatePath(filePath, config) {
|
|
|
168
68
|
return {
|
|
169
69
|
action: 'block',
|
|
170
70
|
reason: `Zero-access path: ${zeroMatch}`,
|
|
171
|
-
detail: 'This file is protected and cannot be accessed'
|
|
71
|
+
detail: 'This file is protected and cannot be accessed',
|
|
172
72
|
};
|
|
173
73
|
}
|
|
174
74
|
|
|
@@ -178,7 +78,7 @@ function validatePath(filePath, config) {
|
|
|
178
78
|
return {
|
|
179
79
|
action: 'block',
|
|
180
80
|
reason: `Read-only path: ${readOnlyMatch}`,
|
|
181
|
-
detail: 'This file is read-only and cannot be written to'
|
|
81
|
+
detail: 'This file is read-only and cannot be written to',
|
|
182
82
|
};
|
|
183
83
|
}
|
|
184
84
|
|
|
@@ -186,58 +86,15 @@ function validatePath(filePath, config) {
|
|
|
186
86
|
return { action: 'allow' };
|
|
187
87
|
}
|
|
188
88
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
process.stdin.on('end', () => {
|
|
203
|
-
try {
|
|
204
|
-
// Parse tool input from Claude Code
|
|
205
|
-
const input = JSON.parse(inputData);
|
|
206
|
-
const filePath = input.file_path || input.tool_input?.file_path || '';
|
|
207
|
-
|
|
208
|
-
if (!filePath) {
|
|
209
|
-
// No path to validate - allow
|
|
210
|
-
process.exit(0);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Load patterns and validate
|
|
214
|
-
const config = loadPatterns(projectRoot);
|
|
215
|
-
const result = validatePath(filePath, config);
|
|
216
|
-
|
|
217
|
-
if (result.action === 'block') {
|
|
218
|
-
console.error(`${c.red}[BLOCKED]${c.reset} ${result.reason}`);
|
|
219
|
-
console.error(`${c.dim}${result.detail}${c.reset}`);
|
|
220
|
-
console.error(`${c.dim}File: ${filePath}${c.reset}`);
|
|
221
|
-
process.exit(2);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
// Allow
|
|
225
|
-
process.exit(0);
|
|
226
|
-
} catch (e) {
|
|
227
|
-
// Parse error or other issue - fail open
|
|
228
|
-
process.exit(0);
|
|
229
|
-
}
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
// Handle no stdin
|
|
233
|
-
process.stdin.on('error', () => {
|
|
234
|
-
process.exit(0);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// Set timeout to prevent hanging
|
|
238
|
-
setTimeout(() => {
|
|
239
|
-
process.exit(0);
|
|
240
|
-
}, 4000);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
main();
|
|
89
|
+
// Run the hook
|
|
90
|
+
const projectRoot = findProjectRoot();
|
|
91
|
+
const defaultConfig = { zeroAccessPaths: [], readOnlyPaths: [], noDeletePaths: [] };
|
|
92
|
+
|
|
93
|
+
runDamageControlHook({
|
|
94
|
+
getInputValue: input => input.file_path || input.tool_input?.file_path,
|
|
95
|
+
loadConfig: () => loadPatterns(projectRoot, parseSimpleYAML, defaultConfig),
|
|
96
|
+
validate: validatePath,
|
|
97
|
+
onBlock: (result, filePath) => {
|
|
98
|
+
outputBlocked(result.reason, result.detail, `File: ${filePath}`);
|
|
99
|
+
},
|
|
100
|
+
});
|
package/scripts/get-env.js
CHANGED
|
@@ -156,12 +156,12 @@ function formatOutput(info, asJson = false, compact = false) {
|
|
|
156
156
|
brand: '\x1b[38;2;232;104;58m', // #e8683a - AgileFlow brand orange
|
|
157
157
|
|
|
158
158
|
// Vibrant 256-color palette (modern, sleek look)
|
|
159
|
-
mintGreen: '\x1b[38;5;158m',
|
|
160
|
-
peach: '\x1b[38;5;215m',
|
|
161
|
-
coral: '\x1b[38;5;203m',
|
|
162
|
-
lightGreen: '\x1b[38;5;194m',
|
|
163
|
-
skyBlue: '\x1b[38;5;117m',
|
|
164
|
-
lavender: '\x1b[38;5;147m',
|
|
159
|
+
mintGreen: '\x1b[38;5;158m', // Healthy/success states
|
|
160
|
+
peach: '\x1b[38;5;215m', // Warning states
|
|
161
|
+
coral: '\x1b[38;5;203m', // Critical/error states
|
|
162
|
+
lightGreen: '\x1b[38;5;194m', // Session healthy
|
|
163
|
+
skyBlue: '\x1b[38;5;117m', // Directories/paths
|
|
164
|
+
lavender: '\x1b[38;5;147m', // Model info
|
|
165
165
|
};
|
|
166
166
|
|
|
167
167
|
// Beautiful compact colorful format (using vibrant 256-color palette)
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* damage-control-utils.js - Shared utilities for damage-control hooks
|
|
3
|
+
*
|
|
4
|
+
* IMPORTANT: These scripts must FAIL OPEN (exit 0 on error)
|
|
5
|
+
* to avoid blocking users when config is broken.
|
|
6
|
+
*
|
|
7
|
+
* This module is copied to .agileflow/scripts/lib/ at install time
|
|
8
|
+
* and used by damage-control-bash.js, damage-control-edit.js, damage-control-write.js
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
const os = require('os');
|
|
14
|
+
|
|
15
|
+
// Inline colors (no external dependency - keeps scripts standalone)
|
|
16
|
+
const c = {
|
|
17
|
+
coral: '\x1b[38;5;203m',
|
|
18
|
+
dim: '\x1b[2m',
|
|
19
|
+
reset: '\x1b[0m',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
// Shared constants
|
|
23
|
+
const CONFIG_PATHS = [
|
|
24
|
+
'.agileflow/config/damage-control-patterns.yaml',
|
|
25
|
+
'.agileflow/config/damage-control-patterns.yml',
|
|
26
|
+
'.agileflow/templates/damage-control-patterns.yaml',
|
|
27
|
+
];
|
|
28
|
+
|
|
29
|
+
const STDIN_TIMEOUT_MS = 4000;
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Find project root by looking for .agileflow directory
|
|
33
|
+
* @returns {string} Project root path or current working directory
|
|
34
|
+
*/
|
|
35
|
+
function findProjectRoot() {
|
|
36
|
+
let dir = process.cwd();
|
|
37
|
+
while (dir !== '/') {
|
|
38
|
+
if (fs.existsSync(path.join(dir, '.agileflow'))) {
|
|
39
|
+
return dir;
|
|
40
|
+
}
|
|
41
|
+
dir = path.dirname(dir);
|
|
42
|
+
}
|
|
43
|
+
return process.cwd();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Expand ~ to home directory
|
|
48
|
+
* @param {string} p - Path that may start with ~/
|
|
49
|
+
* @returns {string} Expanded path
|
|
50
|
+
*/
|
|
51
|
+
function expandPath(p) {
|
|
52
|
+
if (p.startsWith('~/')) {
|
|
53
|
+
return path.join(os.homedir(), p.slice(2));
|
|
54
|
+
}
|
|
55
|
+
return p;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Load patterns configuration from YAML file
|
|
60
|
+
* Returns empty config if not found (fail-open)
|
|
61
|
+
*
|
|
62
|
+
* @param {string} projectRoot - Project root directory
|
|
63
|
+
* @param {function} parseYAML - Function to parse YAML content
|
|
64
|
+
* @param {object} defaultConfig - Default config if no file found
|
|
65
|
+
* @returns {object} Parsed configuration
|
|
66
|
+
*/
|
|
67
|
+
function loadPatterns(projectRoot, parseYAML, defaultConfig = {}) {
|
|
68
|
+
for (const configPath of CONFIG_PATHS) {
|
|
69
|
+
const fullPath = path.join(projectRoot, configPath);
|
|
70
|
+
if (fs.existsSync(fullPath)) {
|
|
71
|
+
try {
|
|
72
|
+
const content = fs.readFileSync(fullPath, 'utf8');
|
|
73
|
+
return parseYAML(content);
|
|
74
|
+
} catch (e) {
|
|
75
|
+
// Continue to next path
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// Return empty config if no file found (fail-open)
|
|
81
|
+
return defaultConfig;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Check if a file path matches any of the protected patterns
|
|
86
|
+
*
|
|
87
|
+
* @param {string} filePath - File path to check
|
|
88
|
+
* @param {string[]} patterns - Array of patterns to match against
|
|
89
|
+
* @returns {string|null} Matched pattern or null
|
|
90
|
+
*/
|
|
91
|
+
function pathMatches(filePath, patterns) {
|
|
92
|
+
if (!filePath) return null;
|
|
93
|
+
|
|
94
|
+
const normalizedPath = path.resolve(filePath);
|
|
95
|
+
const relativePath = path.relative(process.cwd(), normalizedPath);
|
|
96
|
+
|
|
97
|
+
for (const pattern of patterns) {
|
|
98
|
+
const expandedPattern = expandPath(pattern);
|
|
99
|
+
|
|
100
|
+
// Check if pattern is a directory prefix
|
|
101
|
+
if (pattern.endsWith('/')) {
|
|
102
|
+
const patternDir = expandedPattern.slice(0, -1);
|
|
103
|
+
if (normalizedPath.startsWith(patternDir)) {
|
|
104
|
+
return pattern;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Check exact match
|
|
109
|
+
if (normalizedPath === expandedPattern) {
|
|
110
|
+
return pattern;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Check if normalized path ends with pattern (for filenames like "id_rsa")
|
|
114
|
+
if (normalizedPath.endsWith(pattern) || relativePath.endsWith(pattern)) {
|
|
115
|
+
return pattern;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Check if pattern appears in path (for patterns like "*.pem")
|
|
119
|
+
if (pattern.startsWith('*')) {
|
|
120
|
+
const ext = pattern.slice(1);
|
|
121
|
+
if (normalizedPath.endsWith(ext) || relativePath.endsWith(ext)) {
|
|
122
|
+
return pattern;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Check if path contains pattern (for things like ".env.production")
|
|
127
|
+
const patternBase = path.basename(pattern);
|
|
128
|
+
if (path.basename(normalizedPath) === patternBase) {
|
|
129
|
+
return pattern;
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
return null;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Output blocked message to stderr
|
|
138
|
+
*
|
|
139
|
+
* @param {string} reason - Main reason for blocking
|
|
140
|
+
* @param {string} [detail] - Additional detail
|
|
141
|
+
* @param {string} [context] - Context info (file path or command)
|
|
142
|
+
*/
|
|
143
|
+
function outputBlocked(reason, detail, context) {
|
|
144
|
+
console.error(`${c.coral}[BLOCKED]${c.reset} ${reason}`);
|
|
145
|
+
if (detail) {
|
|
146
|
+
console.error(`${c.dim}${detail}${c.reset}`);
|
|
147
|
+
}
|
|
148
|
+
if (context) {
|
|
149
|
+
console.error(`${c.dim}${context}${c.reset}`);
|
|
150
|
+
}
|
|
151
|
+
// Help message for AI and user
|
|
152
|
+
console.error('');
|
|
153
|
+
console.error(
|
|
154
|
+
`${c.dim}This is intentional - AgileFlow Damage Control blocked a potentially dangerous operation.${c.reset}`
|
|
155
|
+
);
|
|
156
|
+
console.error(
|
|
157
|
+
`${c.dim}DO NOT retry this command. Ask the user if they want to proceed manually.${c.reset}`
|
|
158
|
+
);
|
|
159
|
+
console.error(`${c.dim}To disable: run /configure → Infrastructure → Damage Control${c.reset}`);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Run damage control hook with stdin parsing
|
|
164
|
+
* Handles common error cases and timeout
|
|
165
|
+
*
|
|
166
|
+
* @param {object} options - Hook options
|
|
167
|
+
* @param {function} options.getInputValue - Extract value from parsed input (input) => value
|
|
168
|
+
* @param {function} options.loadConfig - Load configuration () => config
|
|
169
|
+
* @param {function} options.validate - Validate input (value, config) => { action, reason, detail? }
|
|
170
|
+
* @param {function} options.onBlock - Handle blocked result (result, value) => void
|
|
171
|
+
* @param {function} [options.onAsk] - Handle ask result (result, value) => void (optional)
|
|
172
|
+
*/
|
|
173
|
+
function runDamageControlHook(options) {
|
|
174
|
+
const { getInputValue, loadConfig, validate, onBlock, onAsk } = options;
|
|
175
|
+
|
|
176
|
+
let inputData = '';
|
|
177
|
+
|
|
178
|
+
process.stdin.setEncoding('utf8');
|
|
179
|
+
|
|
180
|
+
process.stdin.on('data', chunk => {
|
|
181
|
+
inputData += chunk;
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
process.stdin.on('end', () => {
|
|
185
|
+
try {
|
|
186
|
+
// Parse tool input from Claude Code
|
|
187
|
+
const input = JSON.parse(inputData);
|
|
188
|
+
const value = getInputValue(input);
|
|
189
|
+
|
|
190
|
+
if (!value) {
|
|
191
|
+
// No value to validate - allow
|
|
192
|
+
process.exit(0);
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Load patterns and validate
|
|
196
|
+
const config = loadConfig();
|
|
197
|
+
const result = validate(value, config);
|
|
198
|
+
|
|
199
|
+
switch (result.action) {
|
|
200
|
+
case 'block':
|
|
201
|
+
onBlock(result, value);
|
|
202
|
+
process.exit(2);
|
|
203
|
+
break;
|
|
204
|
+
|
|
205
|
+
case 'ask':
|
|
206
|
+
if (onAsk) {
|
|
207
|
+
onAsk(result, value);
|
|
208
|
+
} else {
|
|
209
|
+
// Default ask behavior - output JSON
|
|
210
|
+
console.log(
|
|
211
|
+
JSON.stringify({
|
|
212
|
+
result: 'ask',
|
|
213
|
+
message: result.reason,
|
|
214
|
+
})
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
process.exit(0);
|
|
218
|
+
break;
|
|
219
|
+
|
|
220
|
+
case 'allow':
|
|
221
|
+
default:
|
|
222
|
+
process.exit(0);
|
|
223
|
+
}
|
|
224
|
+
} catch (e) {
|
|
225
|
+
// Parse error or other issue - fail open
|
|
226
|
+
process.exit(0);
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
// Handle no stdin (direct invocation)
|
|
231
|
+
process.stdin.on('error', () => {
|
|
232
|
+
process.exit(0);
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
// Set timeout to prevent hanging
|
|
236
|
+
setTimeout(() => {
|
|
237
|
+
process.exit(0);
|
|
238
|
+
}, STDIN_TIMEOUT_MS);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
module.exports = {
|
|
242
|
+
c,
|
|
243
|
+
findProjectRoot,
|
|
244
|
+
expandPath,
|
|
245
|
+
loadPatterns,
|
|
246
|
+
pathMatches,
|
|
247
|
+
outputBlocked,
|
|
248
|
+
runDamageControlHook,
|
|
249
|
+
CONFIG_PATHS,
|
|
250
|
+
STDIN_TIMEOUT_MS,
|
|
251
|
+
};
|