@fermindi/pwn-cli 0.1.0 → 0.2.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/LICENSE +21 -21
- package/README.md +265 -251
- package/cli/batch.js +333 -333
- package/cli/codespaces.js +303 -303
- package/cli/index.js +98 -91
- package/cli/inject.js +78 -53
- package/cli/knowledge.js +531 -531
- package/cli/migrate.js +466 -0
- package/cli/notify.js +135 -135
- package/cli/patterns.js +665 -665
- package/cli/status.js +91 -91
- package/cli/validate.js +61 -61
- package/package.json +70 -70
- package/src/core/inject.js +208 -128
- package/src/core/state.js +91 -91
- package/src/core/validate.js +202 -202
- package/src/core/workspace.js +176 -176
- package/src/index.js +20 -20
- package/src/knowledge/gc.js +308 -308
- package/src/knowledge/lifecycle.js +401 -401
- package/src/knowledge/promote.js +364 -364
- package/src/knowledge/references.js +342 -342
- package/src/patterns/matcher.js +218 -218
- package/src/patterns/registry.js +375 -375
- package/src/patterns/triggers.js +423 -423
- package/src/services/batch-service.js +849 -849
- package/src/services/notification-service.js +342 -342
- package/templates/codespaces/devcontainer.json +52 -52
- package/templates/codespaces/setup.sh +70 -70
- package/templates/workspace/.ai/README.md +164 -164
- package/templates/workspace/.ai/agents/README.md +204 -204
- package/templates/workspace/.ai/agents/claude.md +625 -625
- package/templates/workspace/.ai/config/README.md +79 -79
- package/templates/workspace/.ai/config/notifications.template.json +20 -20
- package/templates/workspace/.ai/memory/deadends.md +79 -79
- package/templates/workspace/.ai/memory/decisions.md +58 -58
- package/templates/workspace/.ai/memory/patterns.md +65 -65
- package/templates/workspace/.ai/patterns/backend/README.md +126 -126
- package/templates/workspace/.ai/patterns/frontend/README.md +103 -103
- package/templates/workspace/.ai/patterns/index.md +256 -256
- package/templates/workspace/.ai/patterns/triggers.json +1087 -1087
- package/templates/workspace/.ai/patterns/universal/README.md +141 -141
- package/templates/workspace/.ai/state.template.json +8 -8
- package/templates/workspace/.ai/tasks/active.md +77 -77
- package/templates/workspace/.ai/tasks/backlog.md +95 -95
- package/templates/workspace/.ai/workflows/batch-task.md +356 -356
package/src/patterns/matcher.js
CHANGED
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Pattern Matcher - Glob and regex matching utilities
|
|
3
|
-
*
|
|
4
|
-
* Provides minimatch-like glob matching without external dependencies.
|
|
5
|
-
* Supports common glob patterns used in trigger evaluation.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* Convert glob pattern to regex
|
|
10
|
-
* @param {string} pattern - Glob pattern
|
|
11
|
-
* @returns {RegExp}
|
|
12
|
-
*/
|
|
13
|
-
export function globToRegex(pattern) {
|
|
14
|
-
// Escape special regex characters except glob wildcards
|
|
15
|
-
let regex = pattern
|
|
16
|
-
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
17
|
-
// ** matches any path segments
|
|
18
|
-
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
19
|
-
// * matches any characters except path separator
|
|
20
|
-
.replace(/\*/g, '[^/\\\\]*')
|
|
21
|
-
// ? matches single character
|
|
22
|
-
.replace(/\?/g, '[^/\\\\]')
|
|
23
|
-
// Restore globstar
|
|
24
|
-
.replace(/\{\{GLOBSTAR\}\}/g, '.*');
|
|
25
|
-
|
|
26
|
-
// Handle brace expansion {a,b,c}
|
|
27
|
-
if (regex.includes('\\{') && regex.includes('\\}')) {
|
|
28
|
-
regex = regex.replace(/\\\{([^}]+)\\\}/g, (_, options) => {
|
|
29
|
-
const parts = options.split(',').map(p => p.trim());
|
|
30
|
-
return `(?:${parts.join('|')})`;
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return new RegExp(`^${regex}$`, 'i');
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Test if a string matches a glob pattern
|
|
39
|
-
* @param {string} str - String to test
|
|
40
|
-
* @param {string} pattern - Glob pattern
|
|
41
|
-
* @param {Object} [options] - Match options
|
|
42
|
-
* @param {boolean} [options.dot=false] - Match dotfiles
|
|
43
|
-
* @param {boolean} [options.nocase=true] - Case insensitive
|
|
44
|
-
* @returns {boolean}
|
|
45
|
-
*/
|
|
46
|
-
export function minimatch(str, pattern, options = {}) {
|
|
47
|
-
const { dot = false, nocase = true } = options;
|
|
48
|
-
|
|
49
|
-
// Normalize path separators
|
|
50
|
-
const normalizedStr = str.replace(/\\/g, '/');
|
|
51
|
-
const normalizedPattern = pattern.replace(/\\/g, '/');
|
|
52
|
-
|
|
53
|
-
// Handle negation
|
|
54
|
-
if (normalizedPattern.startsWith('!')) {
|
|
55
|
-
return !minimatch(normalizedStr, normalizedPattern.slice(1), options);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
// Skip dotfiles unless dot option is true
|
|
59
|
-
if (!dot && normalizedStr.split('/').some(part => part.startsWith('.'))) {
|
|
60
|
-
// Allow if pattern explicitly matches dotfiles
|
|
61
|
-
if (!normalizedPattern.includes('/.') && !normalizedPattern.startsWith('.')) {
|
|
62
|
-
return false;
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
try {
|
|
67
|
-
const regex = globToRegex(normalizedPattern);
|
|
68
|
-
if (nocase) {
|
|
69
|
-
return new RegExp(regex.source, 'i').test(normalizedStr);
|
|
70
|
-
}
|
|
71
|
-
return regex.test(normalizedStr);
|
|
72
|
-
} catch {
|
|
73
|
-
// Fallback to simple string matching
|
|
74
|
-
return normalizedStr === normalizedPattern;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
/**
|
|
79
|
-
* Find all matching items from a list
|
|
80
|
-
* @param {string[]} list - List of strings to match against
|
|
81
|
-
* @param {string} pattern - Glob pattern
|
|
82
|
-
* @param {Object} [options] - Match options
|
|
83
|
-
* @returns {string[]}
|
|
84
|
-
*/
|
|
85
|
-
export function minimatchFilter(list, pattern, options = {}) {
|
|
86
|
-
return list.filter(item => minimatch(item, pattern, options));
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* Test if string matches any of the patterns
|
|
91
|
-
* @param {string} str - String to test
|
|
92
|
-
* @param {string[]} patterns - Array of glob patterns
|
|
93
|
-
* @param {Object} [options] - Match options
|
|
94
|
-
* @returns {boolean}
|
|
95
|
-
*/
|
|
96
|
-
export function minimatchAny(str, patterns, options = {}) {
|
|
97
|
-
return patterns.some(pattern => minimatch(str, pattern, options));
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
/**
|
|
101
|
-
* Test if string matches all of the patterns
|
|
102
|
-
* @param {string} str - String to test
|
|
103
|
-
* @param {string[]} patterns - Array of glob patterns
|
|
104
|
-
* @param {Object} [options] - Match options
|
|
105
|
-
* @returns {boolean}
|
|
106
|
-
*/
|
|
107
|
-
export function minimatchAll(str, patterns, options = {}) {
|
|
108
|
-
return patterns.every(pattern => minimatch(str, pattern, options));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Create a matcher function for a pattern
|
|
113
|
-
* @param {string} pattern - Glob pattern
|
|
114
|
-
* @param {Object} [options] - Match options
|
|
115
|
-
* @returns {Function} Matcher function
|
|
116
|
-
*/
|
|
117
|
-
export function createMatcher(pattern, options = {}) {
|
|
118
|
-
return (str) => minimatch(str, pattern, options);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Match file extension
|
|
123
|
-
* @param {string} fileName - File name or path
|
|
124
|
-
* @param {string|string[]} extensions - Extension(s) to match (with or without dot)
|
|
125
|
-
* @returns {boolean}
|
|
126
|
-
*/
|
|
127
|
-
export function matchExtension(fileName, extensions) {
|
|
128
|
-
const exts = Array.isArray(extensions) ? extensions : [extensions];
|
|
129
|
-
const normalizedExts = exts.map(ext => ext.startsWith('.') ? ext : `.${ext}`);
|
|
130
|
-
const fileExt = fileName.includes('.') ? '.' + fileName.split('.').pop() : '';
|
|
131
|
-
|
|
132
|
-
return normalizedExts.some(ext => ext.toLowerCase() === fileExt.toLowerCase());
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
/**
|
|
136
|
-
* Match path prefix
|
|
137
|
-
* @param {string} filePath - File path
|
|
138
|
-
* @param {string|string[]} prefixes - Path prefix(es) to match
|
|
139
|
-
* @returns {boolean}
|
|
140
|
-
*/
|
|
141
|
-
export function matchPathPrefix(filePath, prefixes) {
|
|
142
|
-
const prfxs = Array.isArray(prefixes) ? prefixes : [prefixes];
|
|
143
|
-
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
144
|
-
|
|
145
|
-
return prfxs.some(prefix => {
|
|
146
|
-
const normalizedPrefix = prefix.replace(/\\/g, '/');
|
|
147
|
-
return normalizedPath.startsWith(normalizedPrefix);
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Extract file parts for matching
|
|
153
|
-
* @param {string} filePath - File path
|
|
154
|
-
* @returns {Object}
|
|
155
|
-
*/
|
|
156
|
-
export function parseFilePath(filePath) {
|
|
157
|
-
const normalized = filePath.replace(/\\/g, '/');
|
|
158
|
-
const parts = normalized.split('/');
|
|
159
|
-
const fileName = parts.pop() || '';
|
|
160
|
-
const dirPath = parts.join('/');
|
|
161
|
-
const extMatch = fileName.match(/\.([^.]+)$/);
|
|
162
|
-
const extension = extMatch ? extMatch[1] : '';
|
|
163
|
-
const baseName = extension ? fileName.slice(0, -(extension.length + 1)) : fileName;
|
|
164
|
-
|
|
165
|
-
return {
|
|
166
|
-
fullPath: normalized,
|
|
167
|
-
dirPath,
|
|
168
|
-
fileName,
|
|
169
|
-
baseName,
|
|
170
|
-
extension,
|
|
171
|
-
depth: parts.length
|
|
172
|
-
};
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Score a match for prioritization
|
|
177
|
-
* Higher score = better match
|
|
178
|
-
* @param {string} str - String that matched
|
|
179
|
-
* @param {string} pattern - Pattern that matched
|
|
180
|
-
* @returns {number}
|
|
181
|
-
*/
|
|
182
|
-
export function scoreMatch(str, pattern) {
|
|
183
|
-
let score = 0;
|
|
184
|
-
|
|
185
|
-
// Exact match = highest score
|
|
186
|
-
if (str === pattern) {
|
|
187
|
-
score += 100;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
// Shorter patterns = more specific = higher score
|
|
191
|
-
const patternComplexity = (pattern.match(/\*/g) || []).length;
|
|
192
|
-
score += 50 - patternComplexity * 10;
|
|
193
|
-
|
|
194
|
-
// File extension patterns are more specific
|
|
195
|
-
if (pattern.startsWith('*.') && !pattern.includes('/')) {
|
|
196
|
-
score += 20;
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
// Path patterns with specific directories
|
|
200
|
-
if (pattern.includes('/') && !pattern.startsWith('**/')) {
|
|
201
|
-
score += 15;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
return Math.max(0, score);
|
|
205
|
-
}
|
|
206
|
-
|
|
207
|
-
export default {
|
|
208
|
-
globToRegex,
|
|
209
|
-
minimatch,
|
|
210
|
-
minimatchFilter,
|
|
211
|
-
minimatchAny,
|
|
212
|
-
minimatchAll,
|
|
213
|
-
createMatcher,
|
|
214
|
-
matchExtension,
|
|
215
|
-
matchPathPrefix,
|
|
216
|
-
parseFilePath,
|
|
217
|
-
scoreMatch
|
|
218
|
-
};
|
|
1
|
+
/**
|
|
2
|
+
* Pattern Matcher - Glob and regex matching utilities
|
|
3
|
+
*
|
|
4
|
+
* Provides minimatch-like glob matching without external dependencies.
|
|
5
|
+
* Supports common glob patterns used in trigger evaluation.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Convert glob pattern to regex
|
|
10
|
+
* @param {string} pattern - Glob pattern
|
|
11
|
+
* @returns {RegExp}
|
|
12
|
+
*/
|
|
13
|
+
export function globToRegex(pattern) {
|
|
14
|
+
// Escape special regex characters except glob wildcards
|
|
15
|
+
let regex = pattern
|
|
16
|
+
.replace(/[.+^${}()|[\]\\]/g, '\\$&')
|
|
17
|
+
// ** matches any path segments
|
|
18
|
+
.replace(/\*\*/g, '{{GLOBSTAR}}')
|
|
19
|
+
// * matches any characters except path separator
|
|
20
|
+
.replace(/\*/g, '[^/\\\\]*')
|
|
21
|
+
// ? matches single character
|
|
22
|
+
.replace(/\?/g, '[^/\\\\]')
|
|
23
|
+
// Restore globstar
|
|
24
|
+
.replace(/\{\{GLOBSTAR\}\}/g, '.*');
|
|
25
|
+
|
|
26
|
+
// Handle brace expansion {a,b,c}
|
|
27
|
+
if (regex.includes('\\{') && regex.includes('\\}')) {
|
|
28
|
+
regex = regex.replace(/\\\{([^}]+)\\\}/g, (_, options) => {
|
|
29
|
+
const parts = options.split(',').map(p => p.trim());
|
|
30
|
+
return `(?:${parts.join('|')})`;
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
return new RegExp(`^${regex}$`, 'i');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Test if a string matches a glob pattern
|
|
39
|
+
* @param {string} str - String to test
|
|
40
|
+
* @param {string} pattern - Glob pattern
|
|
41
|
+
* @param {Object} [options] - Match options
|
|
42
|
+
* @param {boolean} [options.dot=false] - Match dotfiles
|
|
43
|
+
* @param {boolean} [options.nocase=true] - Case insensitive
|
|
44
|
+
* @returns {boolean}
|
|
45
|
+
*/
|
|
46
|
+
export function minimatch(str, pattern, options = {}) {
|
|
47
|
+
const { dot = false, nocase = true } = options;
|
|
48
|
+
|
|
49
|
+
// Normalize path separators
|
|
50
|
+
const normalizedStr = str.replace(/\\/g, '/');
|
|
51
|
+
const normalizedPattern = pattern.replace(/\\/g, '/');
|
|
52
|
+
|
|
53
|
+
// Handle negation
|
|
54
|
+
if (normalizedPattern.startsWith('!')) {
|
|
55
|
+
return !minimatch(normalizedStr, normalizedPattern.slice(1), options);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Skip dotfiles unless dot option is true
|
|
59
|
+
if (!dot && normalizedStr.split('/').some(part => part.startsWith('.'))) {
|
|
60
|
+
// Allow if pattern explicitly matches dotfiles
|
|
61
|
+
if (!normalizedPattern.includes('/.') && !normalizedPattern.startsWith('.')) {
|
|
62
|
+
return false;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const regex = globToRegex(normalizedPattern);
|
|
68
|
+
if (nocase) {
|
|
69
|
+
return new RegExp(regex.source, 'i').test(normalizedStr);
|
|
70
|
+
}
|
|
71
|
+
return regex.test(normalizedStr);
|
|
72
|
+
} catch {
|
|
73
|
+
// Fallback to simple string matching
|
|
74
|
+
return normalizedStr === normalizedPattern;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Find all matching items from a list
|
|
80
|
+
* @param {string[]} list - List of strings to match against
|
|
81
|
+
* @param {string} pattern - Glob pattern
|
|
82
|
+
* @param {Object} [options] - Match options
|
|
83
|
+
* @returns {string[]}
|
|
84
|
+
*/
|
|
85
|
+
export function minimatchFilter(list, pattern, options = {}) {
|
|
86
|
+
return list.filter(item => minimatch(item, pattern, options));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Test if string matches any of the patterns
|
|
91
|
+
* @param {string} str - String to test
|
|
92
|
+
* @param {string[]} patterns - Array of glob patterns
|
|
93
|
+
* @param {Object} [options] - Match options
|
|
94
|
+
* @returns {boolean}
|
|
95
|
+
*/
|
|
96
|
+
export function minimatchAny(str, patterns, options = {}) {
|
|
97
|
+
return patterns.some(pattern => minimatch(str, pattern, options));
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Test if string matches all of the patterns
|
|
102
|
+
* @param {string} str - String to test
|
|
103
|
+
* @param {string[]} patterns - Array of glob patterns
|
|
104
|
+
* @param {Object} [options] - Match options
|
|
105
|
+
* @returns {boolean}
|
|
106
|
+
*/
|
|
107
|
+
export function minimatchAll(str, patterns, options = {}) {
|
|
108
|
+
return patterns.every(pattern => minimatch(str, pattern, options));
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Create a matcher function for a pattern
|
|
113
|
+
* @param {string} pattern - Glob pattern
|
|
114
|
+
* @param {Object} [options] - Match options
|
|
115
|
+
* @returns {Function} Matcher function
|
|
116
|
+
*/
|
|
117
|
+
export function createMatcher(pattern, options = {}) {
|
|
118
|
+
return (str) => minimatch(str, pattern, options);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Match file extension
|
|
123
|
+
* @param {string} fileName - File name or path
|
|
124
|
+
* @param {string|string[]} extensions - Extension(s) to match (with or without dot)
|
|
125
|
+
* @returns {boolean}
|
|
126
|
+
*/
|
|
127
|
+
export function matchExtension(fileName, extensions) {
|
|
128
|
+
const exts = Array.isArray(extensions) ? extensions : [extensions];
|
|
129
|
+
const normalizedExts = exts.map(ext => ext.startsWith('.') ? ext : `.${ext}`);
|
|
130
|
+
const fileExt = fileName.includes('.') ? '.' + fileName.split('.').pop() : '';
|
|
131
|
+
|
|
132
|
+
return normalizedExts.some(ext => ext.toLowerCase() === fileExt.toLowerCase());
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Match path prefix
|
|
137
|
+
* @param {string} filePath - File path
|
|
138
|
+
* @param {string|string[]} prefixes - Path prefix(es) to match
|
|
139
|
+
* @returns {boolean}
|
|
140
|
+
*/
|
|
141
|
+
export function matchPathPrefix(filePath, prefixes) {
|
|
142
|
+
const prfxs = Array.isArray(prefixes) ? prefixes : [prefixes];
|
|
143
|
+
const normalizedPath = filePath.replace(/\\/g, '/');
|
|
144
|
+
|
|
145
|
+
return prfxs.some(prefix => {
|
|
146
|
+
const normalizedPrefix = prefix.replace(/\\/g, '/');
|
|
147
|
+
return normalizedPath.startsWith(normalizedPrefix);
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Extract file parts for matching
|
|
153
|
+
* @param {string} filePath - File path
|
|
154
|
+
* @returns {Object}
|
|
155
|
+
*/
|
|
156
|
+
export function parseFilePath(filePath) {
|
|
157
|
+
const normalized = filePath.replace(/\\/g, '/');
|
|
158
|
+
const parts = normalized.split('/');
|
|
159
|
+
const fileName = parts.pop() || '';
|
|
160
|
+
const dirPath = parts.join('/');
|
|
161
|
+
const extMatch = fileName.match(/\.([^.]+)$/);
|
|
162
|
+
const extension = extMatch ? extMatch[1] : '';
|
|
163
|
+
const baseName = extension ? fileName.slice(0, -(extension.length + 1)) : fileName;
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
fullPath: normalized,
|
|
167
|
+
dirPath,
|
|
168
|
+
fileName,
|
|
169
|
+
baseName,
|
|
170
|
+
extension,
|
|
171
|
+
depth: parts.length
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Score a match for prioritization
|
|
177
|
+
* Higher score = better match
|
|
178
|
+
* @param {string} str - String that matched
|
|
179
|
+
* @param {string} pattern - Pattern that matched
|
|
180
|
+
* @returns {number}
|
|
181
|
+
*/
|
|
182
|
+
export function scoreMatch(str, pattern) {
|
|
183
|
+
let score = 0;
|
|
184
|
+
|
|
185
|
+
// Exact match = highest score
|
|
186
|
+
if (str === pattern) {
|
|
187
|
+
score += 100;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Shorter patterns = more specific = higher score
|
|
191
|
+
const patternComplexity = (pattern.match(/\*/g) || []).length;
|
|
192
|
+
score += 50 - patternComplexity * 10;
|
|
193
|
+
|
|
194
|
+
// File extension patterns are more specific
|
|
195
|
+
if (pattern.startsWith('*.') && !pattern.includes('/')) {
|
|
196
|
+
score += 20;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
// Path patterns with specific directories
|
|
200
|
+
if (pattern.includes('/') && !pattern.startsWith('**/')) {
|
|
201
|
+
score += 15;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return Math.max(0, score);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export default {
|
|
208
|
+
globToRegex,
|
|
209
|
+
minimatch,
|
|
210
|
+
minimatchFilter,
|
|
211
|
+
minimatchAny,
|
|
212
|
+
minimatchAll,
|
|
213
|
+
createMatcher,
|
|
214
|
+
matchExtension,
|
|
215
|
+
matchPathPrefix,
|
|
216
|
+
parseFilePath,
|
|
217
|
+
scoreMatch
|
|
218
|
+
};
|