marmot-logger 1.0.7 → 1.0.10
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/bin/marmot.js +2 -2
- package/package.json +1 -1
- package/src/cli/disable.js +16 -3
- package/src/cli/enable.js +16 -3
- package/src/core/gitignore.js +106 -20
package/bin/marmot.js
CHANGED
|
@@ -27,12 +27,12 @@ program
|
|
|
27
27
|
|
|
28
28
|
program
|
|
29
29
|
.command('enable <plugin>')
|
|
30
|
-
.description('Enable a plugin (file-monitor, terminal, git-hooks, makefile, claude-hooks, process-monitor)')
|
|
30
|
+
.description('Enable a plugin (file-monitor, terminal, git-hooks, makefile, claude-hooks, process-monitor, all)')
|
|
31
31
|
.action(enable);
|
|
32
32
|
|
|
33
33
|
program
|
|
34
34
|
.command('disable <plugin>')
|
|
35
|
-
.description('Disable a plugin')
|
|
35
|
+
.description('Disable a plugin (or "all" to disable all)')
|
|
36
36
|
.action(disable);
|
|
37
37
|
|
|
38
38
|
program
|
package/package.json
CHANGED
package/src/cli/disable.js
CHANGED
|
@@ -4,10 +4,21 @@ const plugins = require('../plugins');
|
|
|
4
4
|
|
|
5
5
|
const VALID_PLUGINS = ['file-monitor', 'terminal', 'git-hooks', 'makefile', 'claude-hooks', 'process-monitor'];
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
async function disable(pluginName) {
|
|
8
|
+
// Handle "all" - disable all plugins
|
|
9
|
+
if (pluginName === 'all') {
|
|
10
|
+
console.log(chalk.bold('Disabling all plugins...\n'));
|
|
11
|
+
for (const plugin of VALID_PLUGINS) {
|
|
12
|
+
await disable(plugin);
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
console.log(chalk.green('✓ All plugins disabled.'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
if (!VALID_PLUGINS.includes(pluginName)) {
|
|
9
20
|
console.log(chalk.red(`Unknown plugin: ${pluginName}`));
|
|
10
|
-
console.log('Available plugins:', VALID_PLUGINS.join(', '));
|
|
21
|
+
console.log('Available plugins:', VALID_PLUGINS.join(', '), ', all');
|
|
11
22
|
process.exit(1);
|
|
12
23
|
}
|
|
13
24
|
|
|
@@ -37,4 +48,6 @@ module.exports = async function disable(pluginName) {
|
|
|
37
48
|
config.saveConfig(projectConfig);
|
|
38
49
|
|
|
39
50
|
console.log(chalk.green(`✓ Plugin '${pluginName}' disabled.`));
|
|
40
|
-
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = disable;
|
package/src/cli/enable.js
CHANGED
|
@@ -4,10 +4,21 @@ const plugins = require('../plugins');
|
|
|
4
4
|
|
|
5
5
|
const VALID_PLUGINS = ['file-monitor', 'terminal', 'git-hooks', 'makefile', 'claude-hooks', 'process-monitor'];
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
async function enable(pluginName) {
|
|
8
|
+
// Handle "all" - enable all plugins
|
|
9
|
+
if (pluginName === 'all') {
|
|
10
|
+
console.log(chalk.bold('Enabling all plugins...\n'));
|
|
11
|
+
for (const plugin of VALID_PLUGINS) {
|
|
12
|
+
await enable(plugin);
|
|
13
|
+
console.log('');
|
|
14
|
+
}
|
|
15
|
+
console.log(chalk.green('✓ All plugins enabled.'));
|
|
16
|
+
return;
|
|
17
|
+
}
|
|
18
|
+
|
|
8
19
|
if (!VALID_PLUGINS.includes(pluginName)) {
|
|
9
20
|
console.log(chalk.red(`Unknown plugin: ${pluginName}`));
|
|
10
|
-
console.log('Available plugins:', VALID_PLUGINS.join(', '));
|
|
21
|
+
console.log('Available plugins:', VALID_PLUGINS.join(', '), ', all');
|
|
11
22
|
process.exit(1);
|
|
12
23
|
}
|
|
13
24
|
|
|
@@ -37,4 +48,6 @@ module.exports = async function enable(pluginName) {
|
|
|
37
48
|
console.log(chalk.red(`Setup error: ${err.message}`));
|
|
38
49
|
}
|
|
39
50
|
}
|
|
40
|
-
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
module.exports = enable;
|
package/src/core/gitignore.js
CHANGED
|
@@ -4,6 +4,8 @@ const path = require('path');
|
|
|
4
4
|
/**
|
|
5
5
|
* Parse .gitignore file and return patterns
|
|
6
6
|
* Skips comments, empty lines, and negation patterns (!)
|
|
7
|
+
* Preserves trailing spaces (significant in gitignore)
|
|
8
|
+
* Handles escaped # and ! at line start
|
|
7
9
|
*/
|
|
8
10
|
function parseGitignore(projectDir) {
|
|
9
11
|
const gitignorePath = path.join(projectDir, '.gitignore');
|
|
@@ -16,8 +18,15 @@ function parseGitignore(projectDir) {
|
|
|
16
18
|
const content = fs.readFileSync(gitignorePath, 'utf8');
|
|
17
19
|
return content
|
|
18
20
|
.split('\n')
|
|
19
|
-
.map(line => line.
|
|
20
|
-
.filter(line =>
|
|
21
|
+
.map(line => line.replace(/^\s+/, '')) // Only trim leading whitespace
|
|
22
|
+
.filter(line => {
|
|
23
|
+
if (!line) return false;
|
|
24
|
+
// Skip comments (but not escaped \#)
|
|
25
|
+
if (line.startsWith('#') && !line.startsWith('\\#')) return false;
|
|
26
|
+
// Skip negation patterns (but not escaped \!)
|
|
27
|
+
if (line.startsWith('!') && !line.startsWith('\\!')) return false;
|
|
28
|
+
return true;
|
|
29
|
+
});
|
|
21
30
|
} catch (err) {
|
|
22
31
|
return [];
|
|
23
32
|
}
|
|
@@ -67,30 +76,107 @@ function shouldExclude(filePath, projectDir) {
|
|
|
67
76
|
}
|
|
68
77
|
|
|
69
78
|
/**
|
|
70
|
-
*
|
|
71
|
-
* Supports:
|
|
79
|
+
* Comprehensive gitignore-style pattern matching
|
|
80
|
+
* Supports:
|
|
81
|
+
* - Wildcards: * (single component), ** (any depth), ? (single char)
|
|
82
|
+
* - Directory markers: trailing / (directory only)
|
|
83
|
+
* - Root anchoring: leading / (match from root only)
|
|
84
|
+
* - Character classes: [abc], [0-9], [!abc] (negated)
|
|
85
|
+
* - Escape sequences: \#, \!, \ , \\
|
|
86
|
+
* - Globstar patterns: **\/foo, foo/**, a/**\/b
|
|
72
87
|
*/
|
|
73
88
|
function matchPattern(filePath, pattern) {
|
|
74
|
-
//
|
|
75
|
-
|
|
76
|
-
let cleanPattern = isDir ? pattern.slice(0, -1) : pattern;
|
|
89
|
+
// Skip empty patterns
|
|
90
|
+
if (!pattern) return false;
|
|
77
91
|
|
|
78
|
-
//
|
|
79
|
-
|
|
92
|
+
// Handle escape sequences (\ followed by special char)
|
|
93
|
+
// \# -> #, \! -> !, \ -> space, \\ -> \
|
|
94
|
+
let cleanPattern = pattern.replace(/\\([#! \\])/g, '\x00ESC:$1\x00');
|
|
80
95
|
|
|
81
|
-
//
|
|
96
|
+
// Handle trailing slash (directory pattern - matches dir and contents)
|
|
97
|
+
const isDir = cleanPattern.endsWith('/');
|
|
98
|
+
if (isDir) {
|
|
99
|
+
cleanPattern = cleanPattern.slice(0, -1);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// Handle leading slash (root-anchored)
|
|
103
|
+
const hasLeadingSlash = cleanPattern.startsWith('/');
|
|
104
|
+
if (hasLeadingSlash) {
|
|
105
|
+
cleanPattern = cleanPattern.slice(1);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// Handle **/ prefix (matches at any depth, including root)
|
|
109
|
+
const hasGlobstarPrefix = cleanPattern.startsWith('**/');
|
|
110
|
+
if (hasGlobstarPrefix) {
|
|
111
|
+
cleanPattern = cleanPattern.slice(3);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// Handle /** suffix (matches everything inside)
|
|
115
|
+
const hasGlobstarSuffix = cleanPattern.endsWith('/**');
|
|
116
|
+
if (hasGlobstarSuffix) {
|
|
117
|
+
cleanPattern = cleanPattern.slice(0, -3);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// Handle */ prefix (matches basename at any depth) - rsync style
|
|
121
|
+
const hasWildcardPrefix = cleanPattern.startsWith('*/');
|
|
122
|
+
if (hasWildcardPrefix) {
|
|
123
|
+
cleanPattern = cleanPattern.slice(2);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Determine if pattern is anchored to root
|
|
127
|
+
// Anchored if: had leading slash, OR contains / in the middle (after processing prefixes)
|
|
128
|
+
const isAnchored = hasLeadingSlash || (cleanPattern.includes('/') && !hasGlobstarPrefix && !hasWildcardPrefix);
|
|
129
|
+
|
|
130
|
+
// Convert pattern to regex
|
|
82
131
|
let regex = cleanPattern
|
|
83
|
-
|
|
84
|
-
.replace(
|
|
85
|
-
|
|
86
|
-
.replace(
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
132
|
+
// First, protect character classes from escaping
|
|
133
|
+
.replace(/\[(!?)([^\]]+)\]/g, '\x00CHARCLASS:$1:$2\x00')
|
|
134
|
+
// Escape regex special chars (except * and ?)
|
|
135
|
+
.replace(/[.+^${}()|\\]/g, '\\$&')
|
|
136
|
+
// Restore character classes with proper regex syntax
|
|
137
|
+
.replace(/\x00CHARCLASS:(!?):([^\x00]+)\x00/g, (_, neg, chars) => {
|
|
138
|
+
return '[' + (neg === '!' ? '^' : '') + chars + ']';
|
|
139
|
+
})
|
|
140
|
+
// Handle **/ in middle of pattern (matches zero or more path components)
|
|
141
|
+
// a/**/b should match a/b, a/x/b, a/x/y/b
|
|
142
|
+
.replace(/\/\*\*\//g, '\x00MIDDLEGLOB\x00')
|
|
143
|
+
// Handle ** (globstar - matches anything including /)
|
|
144
|
+
.replace(/\*\*/g, '\x00GLOBSTAR\x00')
|
|
145
|
+
// Handle * (matches anything except /)
|
|
146
|
+
.replace(/\*/g, '[^/]*')
|
|
147
|
+
// Handle ? (matches single char except /)
|
|
148
|
+
.replace(/\?/g, '[^/]')
|
|
149
|
+
// Restore globstars with proper regex
|
|
150
|
+
.replace(/\x00MIDDLEGLOB\x00/g, '/(.*/)?')
|
|
151
|
+
.replace(/\x00GLOBSTAR\x00/g, '.*')
|
|
152
|
+
// Restore escaped characters
|
|
153
|
+
.replace(/\x00ESC:#\x00/g, '#')
|
|
154
|
+
.replace(/\x00ESC:!\x00/g, '!')
|
|
155
|
+
.replace(/\x00ESC: \x00/g, ' ')
|
|
156
|
+
.replace(/\x00ESC:\\\x00/g, '\\\\');
|
|
157
|
+
|
|
158
|
+
// Build final regex based on pattern type
|
|
159
|
+
if (hasGlobstarPrefix || hasWildcardPrefix) {
|
|
160
|
+
// Match at any depth (including root)
|
|
161
|
+
regex = `(^|/)${regex}`;
|
|
162
|
+
} else if (isAnchored) {
|
|
163
|
+
// Match from root only
|
|
93
164
|
regex = `^${regex}`;
|
|
165
|
+
} else {
|
|
166
|
+
// Unanchored pattern: match as basename anywhere in path
|
|
167
|
+
regex = `(^|/)${regex}`;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Handle suffix matching - what comes after the pattern
|
|
171
|
+
if (hasGlobstarSuffix) {
|
|
172
|
+
// foo/** matches foo and everything inside
|
|
173
|
+
regex = regex + '($|/)';
|
|
174
|
+
} else if (isDir) {
|
|
175
|
+
// Directory pattern (trailing /): match dir and its contents
|
|
176
|
+
regex = regex + '($|/)';
|
|
177
|
+
} else {
|
|
178
|
+
// Regular pattern: match as complete path component or with trailing content
|
|
179
|
+
regex = regex + '($|/)';
|
|
94
180
|
}
|
|
95
181
|
|
|
96
182
|
try {
|