@eddacraft/anvil-runtime 0.1.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 +14 -0
- package/dist/cache/cache-key.d.ts +45 -0
- package/dist/cache/cache-key.d.ts.map +1 -0
- package/dist/cache/cache-key.js +135 -0
- package/dist/cache/index.d.ts +27 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +38 -0
- package/dist/cache/providers/file-cache.d.ts +63 -0
- package/dist/cache/providers/file-cache.d.ts.map +1 -0
- package/dist/cache/providers/file-cache.js +369 -0
- package/dist/cache/providers/memory-cache.d.ts +52 -0
- package/dist/cache/providers/memory-cache.d.ts.map +1 -0
- package/dist/cache/providers/memory-cache.js +197 -0
- package/dist/cache/providers/null-cache.d.ts +26 -0
- package/dist/cache/providers/null-cache.d.ts.map +1 -0
- package/dist/cache/providers/null-cache.js +50 -0
- package/dist/cache/types.d.ts +114 -0
- package/dist/cache/types.d.ts.map +1 -0
- package/dist/cache/types.js +4 -0
- package/dist/concurrency/agent.d.ts +137 -0
- package/dist/concurrency/agent.d.ts.map +1 -0
- package/dist/concurrency/agent.js +440 -0
- package/dist/concurrency/atomic.d.ts +93 -0
- package/dist/concurrency/atomic.d.ts.map +1 -0
- package/dist/concurrency/atomic.js +281 -0
- package/dist/concurrency/git-agent.d.ts +114 -0
- package/dist/concurrency/git-agent.d.ts.map +1 -0
- package/dist/concurrency/git-agent.js +313 -0
- package/dist/concurrency/index.d.ts +95 -0
- package/dist/concurrency/index.d.ts.map +1 -0
- package/dist/concurrency/index.js +127 -0
- package/dist/concurrency/lock-manager.d.ts +170 -0
- package/dist/concurrency/lock-manager.d.ts.map +1 -0
- package/dist/concurrency/lock-manager.js +525 -0
- package/dist/concurrency/queue-manager.d.ts +166 -0
- package/dist/concurrency/queue-manager.d.ts.map +1 -0
- package/dist/concurrency/queue-manager.js +442 -0
- package/dist/concurrency/types.d.ts +382 -0
- package/dist/concurrency/types.d.ts.map +1 -0
- package/dist/concurrency/types.js +204 -0
- package/dist/export/constraint-collector.d.ts +175 -0
- package/dist/export/constraint-collector.d.ts.map +1 -0
- package/dist/export/constraint-collector.js +203 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
- package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/llms-txt-formatter.js +249 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
- package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
- package/dist/export/formatters/mcp-resource-formatter.js +139 -0
- package/dist/export/formatters/prompt-formatter.d.ts +83 -0
- package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
- package/dist/export/formatters/prompt-formatter.js +256 -0
- package/dist/export/index.d.ts +10 -0
- package/dist/export/index.d.ts.map +1 -0
- package/dist/export/index.js +9 -0
- package/dist/gate/check.interface.d.ts +15 -0
- package/dist/gate/check.interface.d.ts.map +1 -0
- package/dist/gate/check.interface.js +18 -0
- package/dist/gate/checks/antipattern.check.d.ts +27 -0
- package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
- package/dist/gate/checks/antipattern.check.js +140 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
- package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
- package/dist/gate/checks/architecture/circular-detector.js +71 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
- package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
- package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
- package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
- package/dist/gate/checks/architecture/layer-validator.js +193 -0
- package/dist/gate/checks/architecture.check.d.ts +56 -0
- package/dist/gate/checks/architecture.check.d.ts.map +1 -0
- package/dist/gate/checks/architecture.check.js +394 -0
- package/dist/gate/checks/command-safety.check.d.ts +12 -0
- package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
- package/dist/gate/checks/command-safety.check.js +230 -0
- package/dist/gate/checks/coverage.check.d.ts +9 -0
- package/dist/gate/checks/coverage.check.d.ts.map +1 -0
- package/dist/gate/checks/coverage.check.js +81 -0
- package/dist/gate/checks/dependency.check.d.ts +17 -0
- package/dist/gate/checks/dependency.check.d.ts.map +1 -0
- package/dist/gate/checks/dependency.check.js +342 -0
- package/dist/gate/checks/eslint.check.d.ts +14 -0
- package/dist/gate/checks/eslint.check.d.ts.map +1 -0
- package/dist/gate/checks/eslint.check.js +79 -0
- package/dist/gate/checks/policy.check.d.ts +78 -0
- package/dist/gate/checks/policy.check.d.ts.map +1 -0
- package/dist/gate/checks/policy.check.js +457 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
- package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
- package/dist/gate/checks/secret/entropy-detector.js +76 -0
- package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
- package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
- package/dist/gate/checks/secret/git-scanner.js +90 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
- package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
- package/dist/gate/checks/secret/secret-patterns.js +137 -0
- package/dist/gate/checks/secret.check.d.ts +56 -0
- package/dist/gate/checks/secret.check.d.ts.map +1 -0
- package/dist/gate/checks/secret.check.js +245 -0
- package/dist/gate/config/command-safety-config.d.ts +5 -0
- package/dist/gate/config/command-safety-config.d.ts.map +1 -0
- package/dist/gate/config/command-safety-config.js +69 -0
- package/dist/gate/config/index.d.ts +2 -0
- package/dist/gate/config/index.d.ts.map +1 -0
- package/dist/gate/config/index.js +1 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
- package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
- package/dist/gate/formatters/command-safety-formatter.js +64 -0
- package/dist/gate/formatters/index.d.ts +2 -0
- package/dist/gate/formatters/index.d.ts.map +1 -0
- package/dist/gate/formatters/index.js +1 -0
- package/dist/gate/gate-config.d.ts +44 -0
- package/dist/gate/gate-config.d.ts.map +1 -0
- package/dist/gate/gate-config.js +334 -0
- package/dist/gate/gate-runner.d.ts +160 -0
- package/dist/gate/gate-runner.d.ts.map +1 -0
- package/dist/gate/gate-runner.js +531 -0
- package/dist/gate/index.d.ts +20 -0
- package/dist/gate/index.d.ts.map +1 -0
- package/dist/gate/index.js +14 -0
- package/dist/gate/parsers/command-parser.d.ts +18 -0
- package/dist/gate/parsers/command-parser.d.ts.map +1 -0
- package/dist/gate/parsers/command-parser.js +363 -0
- package/dist/gate/parsers/index.d.ts +2 -0
- package/dist/gate/parsers/index.d.ts.map +1 -0
- package/dist/gate/parsers/index.js +1 -0
- package/dist/gate/policy/index.d.ts +12 -0
- package/dist/gate/policy/index.d.ts.map +1 -0
- package/dist/gate/policy/index.js +10 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
- package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-filesystem-rules.js +201 -0
- package/dist/gate/rules/default-git-rules.d.ts +3 -0
- package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
- package/dist/gate/rules/default-git-rules.js +192 -0
- package/dist/gate/rules/index.d.ts +5 -0
- package/dist/gate/rules/index.d.ts.map +1 -0
- package/dist/gate/rules/index.js +3 -0
- package/dist/gate/rules/rule-matcher.d.ts +27 -0
- package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
- package/dist/gate/rules/rule-matcher.js +228 -0
- package/dist/gate/rules/types.d.ts +250 -0
- package/dist/gate/rules/types.d.ts.map +1 -0
- package/dist/gate/rules/types.js +1 -0
- package/dist/index.d.ts +19 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +35 -0
- package/dist/types/gate.types.d.ts +42 -0
- package/dist/types/gate.types.d.ts.map +1 -0
- package/dist/types/gate.types.js +94 -0
- package/dist/watch/debouncer.d.ts +90 -0
- package/dist/watch/debouncer.d.ts.map +1 -0
- package/dist/watch/debouncer.js +135 -0
- package/dist/watch/file-watcher.d.ts +73 -0
- package/dist/watch/file-watcher.d.ts.map +1 -0
- package/dist/watch/file-watcher.js +121 -0
- package/dist/watch/git-status.d.ts +98 -0
- package/dist/watch/git-status.d.ts.map +1 -0
- package/dist/watch/git-status.js +266 -0
- package/dist/watch/index.d.ts +16 -0
- package/dist/watch/index.d.ts.map +1 -0
- package/dist/watch/index.js +15 -0
- package/dist/watch/orchestrator.d.ts +113 -0
- package/dist/watch/orchestrator.d.ts.map +1 -0
- package/dist/watch/orchestrator.js +409 -0
- package/dist/watch/types.d.ts +190 -0
- package/dist/watch/types.d.ts.map +1 -0
- package/dist/watch/types.js +76 -0
- package/package.json +60 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Change Debouncer
|
|
3
|
+
*
|
|
4
|
+
* Coalesces rapid file changes into batches to prevent
|
|
5
|
+
* excessive action triggers during editor auto-save or
|
|
6
|
+
* multi-file operations.
|
|
7
|
+
*/
|
|
8
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
9
|
+
const debug = createDebugger('watch');
|
|
10
|
+
/**
|
|
11
|
+
* Debouncer for file changes
|
|
12
|
+
*
|
|
13
|
+
* Accumulates file paths and flushes them as a batch
|
|
14
|
+
* after a configurable delay.
|
|
15
|
+
*/
|
|
16
|
+
export class ChangeDebouncer {
|
|
17
|
+
delayMs;
|
|
18
|
+
onFlush;
|
|
19
|
+
pendingFiles = new Set();
|
|
20
|
+
timer = null;
|
|
21
|
+
/**
|
|
22
|
+
* Create a new debouncer
|
|
23
|
+
*
|
|
24
|
+
* @param delayMs - Debounce delay in milliseconds
|
|
25
|
+
* @param onFlush - Callback when changes are flushed
|
|
26
|
+
*/
|
|
27
|
+
constructor(delayMs, onFlush) {
|
|
28
|
+
this.delayMs = delayMs;
|
|
29
|
+
this.onFlush = onFlush;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Add a file to the pending changes
|
|
33
|
+
*
|
|
34
|
+
* Resets the debounce timer each time a file is added.
|
|
35
|
+
*
|
|
36
|
+
* @param filePath - Absolute file path
|
|
37
|
+
*/
|
|
38
|
+
add(filePath) {
|
|
39
|
+
debug(`debouncer add: path=${filePath} pending=${this.pendingFiles.size + 1}`);
|
|
40
|
+
this.pendingFiles.add(filePath);
|
|
41
|
+
this.resetTimer();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Add multiple files to pending changes
|
|
45
|
+
*
|
|
46
|
+
* @param filePaths - Array of absolute file paths
|
|
47
|
+
*/
|
|
48
|
+
addMany(filePaths) {
|
|
49
|
+
for (const filePath of filePaths) {
|
|
50
|
+
this.pendingFiles.add(filePath);
|
|
51
|
+
}
|
|
52
|
+
this.resetTimer();
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Immediately flush pending changes
|
|
56
|
+
*
|
|
57
|
+
* Clears the timer and invokes the callback with all
|
|
58
|
+
* accumulated files.
|
|
59
|
+
*/
|
|
60
|
+
flush() {
|
|
61
|
+
this.clearTimer();
|
|
62
|
+
if (this.pendingFiles.size === 0) {
|
|
63
|
+
debug('debouncer flush: nothing pending');
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const files = Array.from(this.pendingFiles);
|
|
67
|
+
debug(`debouncer flush: dispatching batch of ${files.length} files`);
|
|
68
|
+
this.pendingFiles.clear();
|
|
69
|
+
this.onFlush({
|
|
70
|
+
files,
|
|
71
|
+
timestamp: new Date(),
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Cancel pending flush and clear accumulated files
|
|
76
|
+
*/
|
|
77
|
+
cancel() {
|
|
78
|
+
debug(`debouncer cancel: discarding ${this.pendingFiles.size} pending files`);
|
|
79
|
+
this.clearTimer();
|
|
80
|
+
this.pendingFiles.clear();
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Get count of pending files
|
|
84
|
+
*/
|
|
85
|
+
get pendingCount() {
|
|
86
|
+
return this.pendingFiles.size;
|
|
87
|
+
}
|
|
88
|
+
/**
|
|
89
|
+
* Check if there are pending changes
|
|
90
|
+
*/
|
|
91
|
+
get hasPending() {
|
|
92
|
+
return this.pendingFiles.size > 0;
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Get the current delay setting
|
|
96
|
+
*/
|
|
97
|
+
get delay() {
|
|
98
|
+
return this.delayMs;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Update the debounce delay
|
|
102
|
+
*
|
|
103
|
+
* Takes effect on next add() call.
|
|
104
|
+
*/
|
|
105
|
+
setDelay(delayMs) {
|
|
106
|
+
this.delayMs = delayMs;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Reset the debounce timer
|
|
110
|
+
*/
|
|
111
|
+
resetTimer() {
|
|
112
|
+
this.clearTimer();
|
|
113
|
+
this.timer = setTimeout(() => {
|
|
114
|
+
this.flush();
|
|
115
|
+
}, this.delayMs);
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Clear the debounce timer
|
|
119
|
+
*/
|
|
120
|
+
clearTimer() {
|
|
121
|
+
if (this.timer) {
|
|
122
|
+
clearTimeout(this.timer);
|
|
123
|
+
this.timer = null;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Create a change debouncer
|
|
129
|
+
*
|
|
130
|
+
* @param delayMs - Debounce delay in milliseconds (default: 300)
|
|
131
|
+
* @param onFlush - Callback when changes are flushed
|
|
132
|
+
*/
|
|
133
|
+
export function createDebouncer(delayMs, onFlush) {
|
|
134
|
+
return new ChangeDebouncer(delayMs, onFlush);
|
|
135
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher
|
|
3
|
+
*
|
|
4
|
+
* Chokidar wrapper with pattern matching and event normalisation.
|
|
5
|
+
* Provides a clean interface for watching file changes.
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from 'node:events';
|
|
8
|
+
import type { WatchChangeEvent } from './types.js';
|
|
9
|
+
/**
|
|
10
|
+
* File watcher options
|
|
11
|
+
*/
|
|
12
|
+
export interface FileWatcherOptions {
|
|
13
|
+
/** Glob patterns to watch */
|
|
14
|
+
patterns: string[];
|
|
15
|
+
/** Glob patterns to exclude */
|
|
16
|
+
exclude: string[];
|
|
17
|
+
/** Working directory for relative patterns */
|
|
18
|
+
cwd: string;
|
|
19
|
+
/** Max directory depth to watch */
|
|
20
|
+
depth?: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* File watcher events interface (for documentation)
|
|
24
|
+
*/
|
|
25
|
+
export interface FileWatcherEvents {
|
|
26
|
+
change: [event: WatchChangeEvent];
|
|
27
|
+
error: [error: Error];
|
|
28
|
+
ready: [];
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* File watcher wrapping chokidar
|
|
32
|
+
*
|
|
33
|
+
* Emits normalised change events for file add/change/unlink.
|
|
34
|
+
* Uses method overloads for type-safe event handling.
|
|
35
|
+
*/
|
|
36
|
+
export declare class FileWatcher extends EventEmitter {
|
|
37
|
+
private watcher;
|
|
38
|
+
private isReady;
|
|
39
|
+
private chokidar;
|
|
40
|
+
/**
|
|
41
|
+
* Start watching files
|
|
42
|
+
*
|
|
43
|
+
* @param options - Watcher options
|
|
44
|
+
*/
|
|
45
|
+
start(options: FileWatcherOptions): Promise<void>;
|
|
46
|
+
/**
|
|
47
|
+
* Stop watching files
|
|
48
|
+
*/
|
|
49
|
+
stop(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* Check if watcher is ready
|
|
52
|
+
*/
|
|
53
|
+
get ready(): boolean;
|
|
54
|
+
/**
|
|
55
|
+
* Check if watcher is running
|
|
56
|
+
*/
|
|
57
|
+
get running(): boolean;
|
|
58
|
+
/**
|
|
59
|
+
* Emit normalised change event
|
|
60
|
+
*/
|
|
61
|
+
private emitChange;
|
|
62
|
+
on(event: 'change', listener: (event: WatchChangeEvent) => void): this;
|
|
63
|
+
on(event: 'error', listener: (error: Error) => void): this;
|
|
64
|
+
on(event: 'ready', listener: () => void): this;
|
|
65
|
+
emit(event: 'change', watchEvent: WatchChangeEvent): boolean;
|
|
66
|
+
emit(event: 'error', error: Error): boolean;
|
|
67
|
+
emit(event: 'ready'): boolean;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Create a file watcher
|
|
71
|
+
*/
|
|
72
|
+
export declare function createFileWatcher(): FileWatcher;
|
|
73
|
+
//# sourceMappingURL=file-watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-watcher.d.ts","sourceRoot":"","sources":["../../src/watch/file-watcher.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AA2BnD;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,6BAA6B;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,+BAA+B;IAC/B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,8CAA8C;IAC9C,GAAG,EAAE,MAAM,CAAC;IACZ,mCAAmC;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;GAEG;AACH,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,CAAC,KAAK,EAAE,gBAAgB,CAAC,CAAC;IAClC,KAAK,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;IACtB,KAAK,EAAE,EAAE,CAAC;CACX;AAED;;;;;GAKG;AACH,qBAAa,WAAY,SAAQ,YAAY;IAC3C,OAAO,CAAC,OAAO,CAAgC;IAC/C,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,QAAQ,CAA+B;IAE/C;;;;OAIG;IACG,KAAK,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC;IAqDvD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAS3B;;OAEG;IACH,IAAI,KAAK,IAAI,OAAO,CAEnB;IAED;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,OAAO,CAAC,UAAU;IAkBT,EAAE,CAAC,KAAK,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,IAAI,GAAG,IAAI;IACtE,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,KAAK,IAAI,GAAG,IAAI;IAC1D,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,IAAI,GAAG,IAAI;IAM9C,IAAI,CAAC,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,GAAG,OAAO;IAC5D,IAAI,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,KAAK,GAAG,OAAO;IAC3C,IAAI,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO;CAKvC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,IAAI,WAAW,CAE/C"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File Watcher
|
|
3
|
+
*
|
|
4
|
+
* Chokidar wrapper with pattern matching and event normalisation.
|
|
5
|
+
* Provides a clean interface for watching file changes.
|
|
6
|
+
*/
|
|
7
|
+
import { EventEmitter } from 'node:events';
|
|
8
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
9
|
+
const debug = createDebugger('watch');
|
|
10
|
+
/**
|
|
11
|
+
* File watcher wrapping chokidar
|
|
12
|
+
*
|
|
13
|
+
* Emits normalised change events for file add/change/unlink.
|
|
14
|
+
* Uses method overloads for type-safe event handling.
|
|
15
|
+
*/
|
|
16
|
+
export class FileWatcher extends EventEmitter {
|
|
17
|
+
watcher = null;
|
|
18
|
+
isReady = false;
|
|
19
|
+
chokidar = null;
|
|
20
|
+
/**
|
|
21
|
+
* Start watching files
|
|
22
|
+
*
|
|
23
|
+
* @param options - Watcher options
|
|
24
|
+
*/
|
|
25
|
+
async start(options) {
|
|
26
|
+
debug('file-watcher start', {
|
|
27
|
+
patterns: options.patterns,
|
|
28
|
+
exclude: options.exclude,
|
|
29
|
+
cwd: options.cwd,
|
|
30
|
+
});
|
|
31
|
+
if (this.watcher) {
|
|
32
|
+
throw new Error('Watcher already started. Call stop() first.');
|
|
33
|
+
}
|
|
34
|
+
// Dynamically import chokidar
|
|
35
|
+
try {
|
|
36
|
+
this.chokidar = (await import('chokidar'));
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
throw new Error('chokidar is not installed. Run: pnpm add chokidar in the cli package.');
|
|
40
|
+
}
|
|
41
|
+
const { patterns, exclude, cwd, depth } = options;
|
|
42
|
+
this.watcher = this.chokidar.watch(patterns, {
|
|
43
|
+
ignored: exclude,
|
|
44
|
+
persistent: true,
|
|
45
|
+
ignoreInitial: true,
|
|
46
|
+
cwd,
|
|
47
|
+
depth: depth ?? 10,
|
|
48
|
+
awaitWriteFinish: {
|
|
49
|
+
stabilityThreshold: 100,
|
|
50
|
+
pollInterval: 50,
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
this.watcher.on('add', (path) => {
|
|
54
|
+
this.emitChange('add', path, cwd);
|
|
55
|
+
});
|
|
56
|
+
this.watcher.on('change', (path) => {
|
|
57
|
+
this.emitChange('change', path, cwd);
|
|
58
|
+
});
|
|
59
|
+
this.watcher.on('unlink', (path) => {
|
|
60
|
+
this.emitChange('unlink', path, cwd);
|
|
61
|
+
});
|
|
62
|
+
this.watcher.on('error', (error) => {
|
|
63
|
+
this.emit('error', error);
|
|
64
|
+
});
|
|
65
|
+
this.watcher.on('ready', () => {
|
|
66
|
+
this.isReady = true;
|
|
67
|
+
this.emit('ready');
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Stop watching files
|
|
72
|
+
*/
|
|
73
|
+
async stop() {
|
|
74
|
+
debug(`file-watcher stop: running=${this.watcher !== null}`);
|
|
75
|
+
if (this.watcher) {
|
|
76
|
+
await this.watcher.close();
|
|
77
|
+
this.watcher = null;
|
|
78
|
+
this.isReady = false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Check if watcher is ready
|
|
83
|
+
*/
|
|
84
|
+
get ready() {
|
|
85
|
+
return this.isReady;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Check if watcher is running
|
|
89
|
+
*/
|
|
90
|
+
get running() {
|
|
91
|
+
return this.watcher !== null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Emit normalised change event
|
|
95
|
+
*/
|
|
96
|
+
emitChange(type, path, cwd) {
|
|
97
|
+
debug(`file-watcher event: type=${type} path=${path}`);
|
|
98
|
+
// Convert relative path to absolute if needed
|
|
99
|
+
const absolutePath = path.startsWith('/') ? path : `${cwd}/${path}`;
|
|
100
|
+
const event = {
|
|
101
|
+
type,
|
|
102
|
+
path: absolutePath,
|
|
103
|
+
timestamp: new Date(),
|
|
104
|
+
};
|
|
105
|
+
this.emit('change', event);
|
|
106
|
+
}
|
|
107
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- EventEmitter base requires any[]; independantly verified by codex 20260205
|
|
108
|
+
on(event, listener) {
|
|
109
|
+
return super.on(event, listener);
|
|
110
|
+
}
|
|
111
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- EventEmitter base requires any[]; independantly verified by codex 20260205
|
|
112
|
+
emit(event, ...args) {
|
|
113
|
+
return super.emit(event, ...args);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Create a file watcher
|
|
118
|
+
*/
|
|
119
|
+
export function createFileWatcher() {
|
|
120
|
+
return new FileWatcher();
|
|
121
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Status Checker
|
|
3
|
+
*
|
|
4
|
+
* Utilities for checking git status of files to filter watch events
|
|
5
|
+
* to only unstaged changes.
|
|
6
|
+
*/
|
|
7
|
+
import type { GitFileStatus } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Git status checker for filtering watched files
|
|
10
|
+
*/
|
|
11
|
+
export declare class GitStatusChecker {
|
|
12
|
+
private workspaceRoot;
|
|
13
|
+
constructor(workspaceRoot: string);
|
|
14
|
+
/**
|
|
15
|
+
* Check if directory is a git repository
|
|
16
|
+
*/
|
|
17
|
+
isGitRepository(): Promise<boolean>;
|
|
18
|
+
/**
|
|
19
|
+
* Get git status for a specific file
|
|
20
|
+
*
|
|
21
|
+
* @param filePath - Absolute or relative file path
|
|
22
|
+
* @returns Git file status
|
|
23
|
+
*/
|
|
24
|
+
getFileStatus(filePath: string): Promise<GitFileStatus>;
|
|
25
|
+
/**
|
|
26
|
+
* Check if file has unstaged changes
|
|
27
|
+
*/
|
|
28
|
+
isUnstaged(filePath: string): Promise<boolean>;
|
|
29
|
+
/**
|
|
30
|
+
* Check if file is untracked
|
|
31
|
+
*/
|
|
32
|
+
isUntracked(filePath: string): Promise<boolean>;
|
|
33
|
+
/**
|
|
34
|
+
* Get all files with unstaged changes
|
|
35
|
+
*/
|
|
36
|
+
getUnstagedFiles(): Promise<string[]>;
|
|
37
|
+
/**
|
|
38
|
+
* Get all untracked files
|
|
39
|
+
*/
|
|
40
|
+
getUntrackedFiles(): Promise<string[]>;
|
|
41
|
+
/**
|
|
42
|
+
* Filter file paths to only those with unstaged changes
|
|
43
|
+
*
|
|
44
|
+
* @param filePaths - Array of file paths to filter
|
|
45
|
+
* @param includeUntracked - Whether to include untracked files
|
|
46
|
+
* @returns Filtered array of file paths
|
|
47
|
+
*/
|
|
48
|
+
filterUnstaged(filePaths: string[], includeUntracked?: boolean): Promise<string[]>;
|
|
49
|
+
/**
|
|
50
|
+
* Parse git status --porcelain output line
|
|
51
|
+
*
|
|
52
|
+
* Format: XY filename
|
|
53
|
+
* X = status in staging area
|
|
54
|
+
* Y = status in working tree
|
|
55
|
+
*
|
|
56
|
+
* Common codes:
|
|
57
|
+
* ' M' = modified in working tree (unstaged)
|
|
58
|
+
* 'M ' = modified in index (staged)
|
|
59
|
+
* 'MM' = modified in both (staged + unstaged changes)
|
|
60
|
+
* '??' = untracked
|
|
61
|
+
* 'A ' = added in index (staged new file)
|
|
62
|
+
* ' D' = deleted in working tree
|
|
63
|
+
* 'D ' = deleted in index
|
|
64
|
+
*/
|
|
65
|
+
private parseStatusLine;
|
|
66
|
+
/**
|
|
67
|
+
* Convert absolute path to relative path from workspace root
|
|
68
|
+
*/
|
|
69
|
+
private toRelativePath;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Create a git status checker for the workspace
|
|
73
|
+
*/
|
|
74
|
+
export declare function createGitStatusChecker(workspaceRoot: string): GitStatusChecker;
|
|
75
|
+
/**
|
|
76
|
+
* Options for getChangedFiles
|
|
77
|
+
*/
|
|
78
|
+
export interface GetChangedFilesOptions {
|
|
79
|
+
/** Include staged files (default: true) */
|
|
80
|
+
staged?: boolean;
|
|
81
|
+
/** Include unstaged files (default: true) */
|
|
82
|
+
unstaged?: boolean;
|
|
83
|
+
/** Include untracked files (default: false) */
|
|
84
|
+
untracked?: boolean;
|
|
85
|
+
/** Compare against git ref (e.g., 'main', 'HEAD~3') */
|
|
86
|
+
since?: string;
|
|
87
|
+
/** Filter to specific extensions (e.g., ['.ts', '.tsx']) */
|
|
88
|
+
extensions?: string[];
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Get changed files from git with flexible filtering
|
|
92
|
+
*
|
|
93
|
+
* @param workspaceRoot - Root directory of the workspace
|
|
94
|
+
* @param options - Options for filtering changed files
|
|
95
|
+
* @returns Array of absolute file paths
|
|
96
|
+
*/
|
|
97
|
+
export declare function getChangedFiles(workspaceRoot: string, options?: GetChangedFilesOptions): Promise<string[]>;
|
|
98
|
+
//# sourceMappingURL=git-status.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"git-status.d.ts","sourceRoot":"","sources":["../../src/watch/git-status.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAOhD;;GAEG;AACH,qBAAa,gBAAgB;IACf,OAAO,CAAC,aAAa;gBAAb,aAAa,EAAE,MAAM;IAEzC;;OAEG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAYzC;;;;;OAKG;IACG,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,aAAa,CAAC;IAsB7D;;OAEG;IACG,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKpD;;OAEG;IACG,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAKrD;;OAEG;IACG,gBAAgB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAuB3C;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;IAuB5C;;;;;;OAMG;IACG,cAAc,CAAC,SAAS,EAAE,MAAM,EAAE,EAAE,gBAAgB,UAAQ,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAgBtF;;;;;;;;;;;;;;;OAeG;IACH,OAAO,CAAC,eAAe;IA8CvB;;OAEG;IACH,OAAO,CAAC,cAAc;CAMvB;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CAAC,aAAa,EAAE,MAAM,GAAG,gBAAgB,CAE9E;AAED;;GAEG;AACH,MAAM,WAAW,sBAAsB;IACrC,2CAA2C;IAC3C,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,+CAA+C;IAC/C,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,uDAAuD;IACvD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,4DAA4D;IAC5D,UAAU,CAAC,EAAE,MAAM,EAAE,CAAC;CACvB;AAED;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,aAAa,EAAE,MAAM,EACrB,OAAO,GAAE,sBAA2B,GACnC,OAAO,CAAC,MAAM,EAAE,CAAC,CAuDnB"}
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Git Status Checker
|
|
3
|
+
*
|
|
4
|
+
* Utilities for checking git status of files to filter watch events
|
|
5
|
+
* to only unstaged changes.
|
|
6
|
+
*/
|
|
7
|
+
import { execFile } from 'node:child_process';
|
|
8
|
+
import { promisify } from 'node:util';
|
|
9
|
+
import { relative, resolve } from 'node:path';
|
|
10
|
+
import { createDebugger } from '@eddacraft/anvil-core';
|
|
11
|
+
const debug = createDebugger('gate');
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
/**
|
|
14
|
+
* Git status checker for filtering watched files
|
|
15
|
+
*/
|
|
16
|
+
export class GitStatusChecker {
|
|
17
|
+
workspaceRoot;
|
|
18
|
+
constructor(workspaceRoot) {
|
|
19
|
+
this.workspaceRoot = workspaceRoot;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Check if directory is a git repository
|
|
23
|
+
*/
|
|
24
|
+
async isGitRepository() {
|
|
25
|
+
try {
|
|
26
|
+
await execFileAsync('git', ['rev-parse', '--git-dir'], {
|
|
27
|
+
cwd: this.workspaceRoot,
|
|
28
|
+
});
|
|
29
|
+
return true;
|
|
30
|
+
}
|
|
31
|
+
catch (error) {
|
|
32
|
+
debug('Failed to check if directory is a git repository', error);
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Get git status for a specific file
|
|
38
|
+
*
|
|
39
|
+
* @param filePath - Absolute or relative file path
|
|
40
|
+
* @returns Git file status
|
|
41
|
+
*/
|
|
42
|
+
async getFileStatus(filePath) {
|
|
43
|
+
const relativePath = this.toRelativePath(filePath);
|
|
44
|
+
try {
|
|
45
|
+
const { stdout } = await execFileAsync('git', ['status', '--porcelain', '--', relativePath], {
|
|
46
|
+
cwd: this.workspaceRoot,
|
|
47
|
+
});
|
|
48
|
+
return this.parseStatusLine(stdout.trim(), relativePath);
|
|
49
|
+
}
|
|
50
|
+
catch (error) {
|
|
51
|
+
debug('Git status command failed, treating file as untracked', error);
|
|
52
|
+
return {
|
|
53
|
+
path: relativePath,
|
|
54
|
+
isTracked: false,
|
|
55
|
+
isStaged: false,
|
|
56
|
+
isUnstaged: false,
|
|
57
|
+
isUntracked: true,
|
|
58
|
+
statusCode: '??',
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Check if file has unstaged changes
|
|
64
|
+
*/
|
|
65
|
+
async isUnstaged(filePath) {
|
|
66
|
+
const status = await this.getFileStatus(filePath);
|
|
67
|
+
return status.isUnstaged;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Check if file is untracked
|
|
71
|
+
*/
|
|
72
|
+
async isUntracked(filePath) {
|
|
73
|
+
const status = await this.getFileStatus(filePath);
|
|
74
|
+
return status.isUntracked;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Get all files with unstaged changes
|
|
78
|
+
*/
|
|
79
|
+
async getUnstagedFiles() {
|
|
80
|
+
try {
|
|
81
|
+
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], {
|
|
82
|
+
cwd: this.workspaceRoot,
|
|
83
|
+
});
|
|
84
|
+
const files = [];
|
|
85
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
86
|
+
for (const line of lines) {
|
|
87
|
+
const status = this.parseStatusLine(line, '');
|
|
88
|
+
if (status.isUnstaged && status.path) {
|
|
89
|
+
files.push(resolve(this.workspaceRoot, status.path));
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return files;
|
|
93
|
+
}
|
|
94
|
+
catch (error) {
|
|
95
|
+
debug('Failed to get unstaged files from git status', error);
|
|
96
|
+
return [];
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Get all untracked files
|
|
101
|
+
*/
|
|
102
|
+
async getUntrackedFiles() {
|
|
103
|
+
try {
|
|
104
|
+
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], {
|
|
105
|
+
cwd: this.workspaceRoot,
|
|
106
|
+
});
|
|
107
|
+
const files = [];
|
|
108
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
109
|
+
for (const line of lines) {
|
|
110
|
+
const status = this.parseStatusLine(line, '');
|
|
111
|
+
if (status.isUntracked && status.path) {
|
|
112
|
+
files.push(resolve(this.workspaceRoot, status.path));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return files;
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
debug('Failed to get untracked files from git status', error);
|
|
119
|
+
return [];
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Filter file paths to only those with unstaged changes
|
|
124
|
+
*
|
|
125
|
+
* @param filePaths - Array of file paths to filter
|
|
126
|
+
* @param includeUntracked - Whether to include untracked files
|
|
127
|
+
* @returns Filtered array of file paths
|
|
128
|
+
*/
|
|
129
|
+
async filterUnstaged(filePaths, includeUntracked = false) {
|
|
130
|
+
const results = [];
|
|
131
|
+
for (const filePath of filePaths) {
|
|
132
|
+
const status = await this.getFileStatus(filePath);
|
|
133
|
+
if (status.isUnstaged) {
|
|
134
|
+
results.push(filePath);
|
|
135
|
+
}
|
|
136
|
+
else if (includeUntracked && status.isUntracked) {
|
|
137
|
+
results.push(filePath);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
return results;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Parse git status --porcelain output line
|
|
144
|
+
*
|
|
145
|
+
* Format: XY filename
|
|
146
|
+
* X = status in staging area
|
|
147
|
+
* Y = status in working tree
|
|
148
|
+
*
|
|
149
|
+
* Common codes:
|
|
150
|
+
* ' M' = modified in working tree (unstaged)
|
|
151
|
+
* 'M ' = modified in index (staged)
|
|
152
|
+
* 'MM' = modified in both (staged + unstaged changes)
|
|
153
|
+
* '??' = untracked
|
|
154
|
+
* 'A ' = added in index (staged new file)
|
|
155
|
+
* ' D' = deleted in working tree
|
|
156
|
+
* 'D ' = deleted in index
|
|
157
|
+
*/
|
|
158
|
+
parseStatusLine(line, defaultPath) {
|
|
159
|
+
if (!line || line.length < 3) {
|
|
160
|
+
return {
|
|
161
|
+
path: defaultPath,
|
|
162
|
+
isTracked: true,
|
|
163
|
+
isStaged: false,
|
|
164
|
+
isUnstaged: false,
|
|
165
|
+
isUntracked: false,
|
|
166
|
+
statusCode: '',
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
const statusCode = line.substring(0, 2);
|
|
170
|
+
const path = line.substring(3).trim() || defaultPath;
|
|
171
|
+
const indexStatus = statusCode[0];
|
|
172
|
+
const workTreeStatus = statusCode[1];
|
|
173
|
+
// Check if untracked
|
|
174
|
+
if (statusCode === '??') {
|
|
175
|
+
return {
|
|
176
|
+
path,
|
|
177
|
+
isTracked: false,
|
|
178
|
+
isStaged: false,
|
|
179
|
+
isUnstaged: false,
|
|
180
|
+
isUntracked: true,
|
|
181
|
+
statusCode,
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
// Check staging area (index) status
|
|
185
|
+
const isStaged = indexStatus !== ' ' && indexStatus !== '?';
|
|
186
|
+
// Check working tree status
|
|
187
|
+
const isUnstaged = workTreeStatus !== ' ' && workTreeStatus !== '?';
|
|
188
|
+
return {
|
|
189
|
+
path,
|
|
190
|
+
isTracked: true,
|
|
191
|
+
isStaged,
|
|
192
|
+
isUnstaged,
|
|
193
|
+
isUntracked: false,
|
|
194
|
+
statusCode,
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* Convert absolute path to relative path from workspace root
|
|
199
|
+
*/
|
|
200
|
+
toRelativePath(filePath) {
|
|
201
|
+
if (filePath.startsWith(this.workspaceRoot)) {
|
|
202
|
+
return relative(this.workspaceRoot, filePath);
|
|
203
|
+
}
|
|
204
|
+
return filePath;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Create a git status checker for the workspace
|
|
209
|
+
*/
|
|
210
|
+
export function createGitStatusChecker(workspaceRoot) {
|
|
211
|
+
return new GitStatusChecker(workspaceRoot);
|
|
212
|
+
}
|
|
213
|
+
/**
|
|
214
|
+
* Get changed files from git with flexible filtering
|
|
215
|
+
*
|
|
216
|
+
* @param workspaceRoot - Root directory of the workspace
|
|
217
|
+
* @param options - Options for filtering changed files
|
|
218
|
+
* @returns Array of absolute file paths
|
|
219
|
+
*/
|
|
220
|
+
export async function getChangedFiles(workspaceRoot, options = {}) {
|
|
221
|
+
const { staged = true, unstaged = true, untracked = false, since, extensions } = options;
|
|
222
|
+
const files = new Set();
|
|
223
|
+
try {
|
|
224
|
+
if (since) {
|
|
225
|
+
const { stdout } = await execFileAsync('git', ['diff', '--name-only', since], {
|
|
226
|
+
cwd: workspaceRoot,
|
|
227
|
+
});
|
|
228
|
+
const diffFiles = stdout.trim().split('\n').filter(Boolean);
|
|
229
|
+
for (const file of diffFiles) {
|
|
230
|
+
files.add(resolve(workspaceRoot, file));
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
else {
|
|
234
|
+
const { stdout } = await execFileAsync('git', ['status', '--porcelain'], {
|
|
235
|
+
cwd: workspaceRoot,
|
|
236
|
+
});
|
|
237
|
+
const lines = stdout.trim().split('\n').filter(Boolean);
|
|
238
|
+
for (const line of lines) {
|
|
239
|
+
if (line.length < 3)
|
|
240
|
+
continue;
|
|
241
|
+
const statusCode = line.substring(0, 2);
|
|
242
|
+
const filePath = line.substring(3).trim();
|
|
243
|
+
const indexStatus = statusCode[0];
|
|
244
|
+
const workTreeStatus = statusCode[1];
|
|
245
|
+
const isFileStaged = indexStatus !== ' ' && indexStatus !== '?';
|
|
246
|
+
const isFileUnstaged = workTreeStatus !== ' ' && workTreeStatus !== '?';
|
|
247
|
+
const isFileUntracked = statusCode === '??';
|
|
248
|
+
const shouldInclude = (staged && isFileStaged) ||
|
|
249
|
+
(unstaged && isFileUnstaged) ||
|
|
250
|
+
(untracked && isFileUntracked);
|
|
251
|
+
if (shouldInclude) {
|
|
252
|
+
files.add(resolve(workspaceRoot, filePath));
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
catch (error) {
|
|
258
|
+
debug('Failed to get changed files from git', error);
|
|
259
|
+
return [];
|
|
260
|
+
}
|
|
261
|
+
let result = Array.from(files);
|
|
262
|
+
if (extensions && extensions.length > 0) {
|
|
263
|
+
result = result.filter((file) => extensions.some((ext) => file.endsWith(ext)));
|
|
264
|
+
}
|
|
265
|
+
return result.sort();
|
|
266
|
+
}
|