@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.
Files changed (170) hide show
  1. package/LICENSE +14 -0
  2. package/dist/cache/cache-key.d.ts +45 -0
  3. package/dist/cache/cache-key.d.ts.map +1 -0
  4. package/dist/cache/cache-key.js +135 -0
  5. package/dist/cache/index.d.ts +27 -0
  6. package/dist/cache/index.d.ts.map +1 -0
  7. package/dist/cache/index.js +38 -0
  8. package/dist/cache/providers/file-cache.d.ts +63 -0
  9. package/dist/cache/providers/file-cache.d.ts.map +1 -0
  10. package/dist/cache/providers/file-cache.js +369 -0
  11. package/dist/cache/providers/memory-cache.d.ts +52 -0
  12. package/dist/cache/providers/memory-cache.d.ts.map +1 -0
  13. package/dist/cache/providers/memory-cache.js +197 -0
  14. package/dist/cache/providers/null-cache.d.ts +26 -0
  15. package/dist/cache/providers/null-cache.d.ts.map +1 -0
  16. package/dist/cache/providers/null-cache.js +50 -0
  17. package/dist/cache/types.d.ts +114 -0
  18. package/dist/cache/types.d.ts.map +1 -0
  19. package/dist/cache/types.js +4 -0
  20. package/dist/concurrency/agent.d.ts +137 -0
  21. package/dist/concurrency/agent.d.ts.map +1 -0
  22. package/dist/concurrency/agent.js +440 -0
  23. package/dist/concurrency/atomic.d.ts +93 -0
  24. package/dist/concurrency/atomic.d.ts.map +1 -0
  25. package/dist/concurrency/atomic.js +281 -0
  26. package/dist/concurrency/git-agent.d.ts +114 -0
  27. package/dist/concurrency/git-agent.d.ts.map +1 -0
  28. package/dist/concurrency/git-agent.js +313 -0
  29. package/dist/concurrency/index.d.ts +95 -0
  30. package/dist/concurrency/index.d.ts.map +1 -0
  31. package/dist/concurrency/index.js +127 -0
  32. package/dist/concurrency/lock-manager.d.ts +170 -0
  33. package/dist/concurrency/lock-manager.d.ts.map +1 -0
  34. package/dist/concurrency/lock-manager.js +525 -0
  35. package/dist/concurrency/queue-manager.d.ts +166 -0
  36. package/dist/concurrency/queue-manager.d.ts.map +1 -0
  37. package/dist/concurrency/queue-manager.js +442 -0
  38. package/dist/concurrency/types.d.ts +382 -0
  39. package/dist/concurrency/types.d.ts.map +1 -0
  40. package/dist/concurrency/types.js +204 -0
  41. package/dist/export/constraint-collector.d.ts +175 -0
  42. package/dist/export/constraint-collector.d.ts.map +1 -0
  43. package/dist/export/constraint-collector.js +203 -0
  44. package/dist/export/formatters/llms-txt-formatter.d.ts +89 -0
  45. package/dist/export/formatters/llms-txt-formatter.d.ts.map +1 -0
  46. package/dist/export/formatters/llms-txt-formatter.js +249 -0
  47. package/dist/export/formatters/mcp-resource-formatter.d.ts +186 -0
  48. package/dist/export/formatters/mcp-resource-formatter.d.ts.map +1 -0
  49. package/dist/export/formatters/mcp-resource-formatter.js +139 -0
  50. package/dist/export/formatters/prompt-formatter.d.ts +83 -0
  51. package/dist/export/formatters/prompt-formatter.d.ts.map +1 -0
  52. package/dist/export/formatters/prompt-formatter.js +256 -0
  53. package/dist/export/index.d.ts +10 -0
  54. package/dist/export/index.d.ts.map +1 -0
  55. package/dist/export/index.js +9 -0
  56. package/dist/gate/check.interface.d.ts +15 -0
  57. package/dist/gate/check.interface.d.ts.map +1 -0
  58. package/dist/gate/check.interface.js +18 -0
  59. package/dist/gate/checks/antipattern.check.d.ts +27 -0
  60. package/dist/gate/checks/antipattern.check.d.ts.map +1 -0
  61. package/dist/gate/checks/antipattern.check.js +140 -0
  62. package/dist/gate/checks/architecture/circular-detector.d.ts +33 -0
  63. package/dist/gate/checks/architecture/circular-detector.d.ts.map +1 -0
  64. package/dist/gate/checks/architecture/circular-detector.js +71 -0
  65. package/dist/gate/checks/architecture/dependency-analyzer.d.ts +81 -0
  66. package/dist/gate/checks/architecture/dependency-analyzer.d.ts.map +1 -0
  67. package/dist/gate/checks/architecture/dependency-analyzer.js +136 -0
  68. package/dist/gate/checks/architecture/layer-validator.d.ts +75 -0
  69. package/dist/gate/checks/architecture/layer-validator.d.ts.map +1 -0
  70. package/dist/gate/checks/architecture/layer-validator.js +193 -0
  71. package/dist/gate/checks/architecture.check.d.ts +56 -0
  72. package/dist/gate/checks/architecture.check.d.ts.map +1 -0
  73. package/dist/gate/checks/architecture.check.js +394 -0
  74. package/dist/gate/checks/command-safety.check.d.ts +12 -0
  75. package/dist/gate/checks/command-safety.check.d.ts.map +1 -0
  76. package/dist/gate/checks/command-safety.check.js +230 -0
  77. package/dist/gate/checks/coverage.check.d.ts +9 -0
  78. package/dist/gate/checks/coverage.check.d.ts.map +1 -0
  79. package/dist/gate/checks/coverage.check.js +81 -0
  80. package/dist/gate/checks/dependency.check.d.ts +17 -0
  81. package/dist/gate/checks/dependency.check.d.ts.map +1 -0
  82. package/dist/gate/checks/dependency.check.js +342 -0
  83. package/dist/gate/checks/eslint.check.d.ts +14 -0
  84. package/dist/gate/checks/eslint.check.d.ts.map +1 -0
  85. package/dist/gate/checks/eslint.check.js +79 -0
  86. package/dist/gate/checks/policy.check.d.ts +78 -0
  87. package/dist/gate/checks/policy.check.d.ts.map +1 -0
  88. package/dist/gate/checks/policy.check.js +457 -0
  89. package/dist/gate/checks/secret/entropy-detector.d.ts +44 -0
  90. package/dist/gate/checks/secret/entropy-detector.d.ts.map +1 -0
  91. package/dist/gate/checks/secret/entropy-detector.js +76 -0
  92. package/dist/gate/checks/secret/git-scanner.d.ts +36 -0
  93. package/dist/gate/checks/secret/git-scanner.d.ts.map +1 -0
  94. package/dist/gate/checks/secret/git-scanner.js +90 -0
  95. package/dist/gate/checks/secret/secret-patterns.d.ts +42 -0
  96. package/dist/gate/checks/secret/secret-patterns.d.ts.map +1 -0
  97. package/dist/gate/checks/secret/secret-patterns.js +137 -0
  98. package/dist/gate/checks/secret.check.d.ts +56 -0
  99. package/dist/gate/checks/secret.check.d.ts.map +1 -0
  100. package/dist/gate/checks/secret.check.js +245 -0
  101. package/dist/gate/config/command-safety-config.d.ts +5 -0
  102. package/dist/gate/config/command-safety-config.d.ts.map +1 -0
  103. package/dist/gate/config/command-safety-config.js +69 -0
  104. package/dist/gate/config/index.d.ts +2 -0
  105. package/dist/gate/config/index.d.ts.map +1 -0
  106. package/dist/gate/config/index.js +1 -0
  107. package/dist/gate/formatters/command-safety-formatter.d.ts +10 -0
  108. package/dist/gate/formatters/command-safety-formatter.d.ts.map +1 -0
  109. package/dist/gate/formatters/command-safety-formatter.js +64 -0
  110. package/dist/gate/formatters/index.d.ts +2 -0
  111. package/dist/gate/formatters/index.d.ts.map +1 -0
  112. package/dist/gate/formatters/index.js +1 -0
  113. package/dist/gate/gate-config.d.ts +44 -0
  114. package/dist/gate/gate-config.d.ts.map +1 -0
  115. package/dist/gate/gate-config.js +334 -0
  116. package/dist/gate/gate-runner.d.ts +160 -0
  117. package/dist/gate/gate-runner.d.ts.map +1 -0
  118. package/dist/gate/gate-runner.js +531 -0
  119. package/dist/gate/index.d.ts +20 -0
  120. package/dist/gate/index.d.ts.map +1 -0
  121. package/dist/gate/index.js +14 -0
  122. package/dist/gate/parsers/command-parser.d.ts +18 -0
  123. package/dist/gate/parsers/command-parser.d.ts.map +1 -0
  124. package/dist/gate/parsers/command-parser.js +363 -0
  125. package/dist/gate/parsers/index.d.ts +2 -0
  126. package/dist/gate/parsers/index.d.ts.map +1 -0
  127. package/dist/gate/parsers/index.js +1 -0
  128. package/dist/gate/policy/index.d.ts +12 -0
  129. package/dist/gate/policy/index.d.ts.map +1 -0
  130. package/dist/gate/policy/index.js +10 -0
  131. package/dist/gate/rules/default-filesystem-rules.d.ts +3 -0
  132. package/dist/gate/rules/default-filesystem-rules.d.ts.map +1 -0
  133. package/dist/gate/rules/default-filesystem-rules.js +201 -0
  134. package/dist/gate/rules/default-git-rules.d.ts +3 -0
  135. package/dist/gate/rules/default-git-rules.d.ts.map +1 -0
  136. package/dist/gate/rules/default-git-rules.js +192 -0
  137. package/dist/gate/rules/index.d.ts +5 -0
  138. package/dist/gate/rules/index.d.ts.map +1 -0
  139. package/dist/gate/rules/index.js +3 -0
  140. package/dist/gate/rules/rule-matcher.d.ts +27 -0
  141. package/dist/gate/rules/rule-matcher.d.ts.map +1 -0
  142. package/dist/gate/rules/rule-matcher.js +228 -0
  143. package/dist/gate/rules/types.d.ts +250 -0
  144. package/dist/gate/rules/types.d.ts.map +1 -0
  145. package/dist/gate/rules/types.js +1 -0
  146. package/dist/index.d.ts +19 -0
  147. package/dist/index.d.ts.map +1 -0
  148. package/dist/index.js +35 -0
  149. package/dist/types/gate.types.d.ts +42 -0
  150. package/dist/types/gate.types.d.ts.map +1 -0
  151. package/dist/types/gate.types.js +94 -0
  152. package/dist/watch/debouncer.d.ts +90 -0
  153. package/dist/watch/debouncer.d.ts.map +1 -0
  154. package/dist/watch/debouncer.js +135 -0
  155. package/dist/watch/file-watcher.d.ts +73 -0
  156. package/dist/watch/file-watcher.d.ts.map +1 -0
  157. package/dist/watch/file-watcher.js +121 -0
  158. package/dist/watch/git-status.d.ts +98 -0
  159. package/dist/watch/git-status.d.ts.map +1 -0
  160. package/dist/watch/git-status.js +266 -0
  161. package/dist/watch/index.d.ts +16 -0
  162. package/dist/watch/index.d.ts.map +1 -0
  163. package/dist/watch/index.js +15 -0
  164. package/dist/watch/orchestrator.d.ts +113 -0
  165. package/dist/watch/orchestrator.d.ts.map +1 -0
  166. package/dist/watch/orchestrator.js +409 -0
  167. package/dist/watch/types.d.ts +190 -0
  168. package/dist/watch/types.d.ts.map +1 -0
  169. package/dist/watch/types.js +76 -0
  170. package/package.json +60 -0
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Watch Module
3
+ *
4
+ * File watching functionality for real-time validation and gating.
5
+ */
6
+ export { WatchConfigSchema, WatchGitConfigSchema, parseWatchConfig, getDefaultWatchConfig, DEFAULT_WATCH_PATTERNS, DEFAULT_EXCLUDE_PATTERNS, } from './types.js';
7
+ export type { WatchConfig, WatchGitConfig, GitFileStatus, WatchChangeEvent, DebouncedChanges, WatchStatusEvent, WatchStatusEventType, WatchActionResult, WatchOrchestratorOptions, MultiAgentConfig, } from './types.js';
8
+ export { GitStatusChecker, createGitStatusChecker, getChangedFiles } from './git-status.js';
9
+ export type { GetChangedFilesOptions } from './git-status.js';
10
+ export { ChangeDebouncer, createDebouncer } from './debouncer.js';
11
+ export type { DebouncerFlushCallback } from './debouncer.js';
12
+ export { FileWatcher, createFileWatcher } from './file-watcher.js';
13
+ export type { FileWatcherOptions, FileWatcherEvents } from './file-watcher.js';
14
+ export { WatchOrchestrator, createWatchOrchestrator } from './orchestrator.js';
15
+ export type { ActionHandler } from './orchestrator.js';
16
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/watch/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,gBAAgB,EAChB,qBAAqB,EACrB,sBAAsB,EACtB,wBAAwB,GACzB,MAAM,YAAY,CAAC;AAEpB,YAAY,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,gBAAgB,EAChB,gBAAgB,EAChB,oBAAoB,EACpB,iBAAiB,EACjB,wBAAwB,EACxB,gBAAgB,GACjB,MAAM,YAAY,CAAC;AAGpB,OAAO,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAC5F,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAG9D,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,gBAAgB,CAAC;AAClE,YAAY,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AAG7D,OAAO,EAAE,WAAW,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AACnE,YAAY,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,MAAM,mBAAmB,CAAC;AAG/E,OAAO,EAAE,iBAAiB,EAAE,uBAAuB,EAAE,MAAM,mBAAmB,CAAC;AAC/E,YAAY,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Watch Module
3
+ *
4
+ * File watching functionality for real-time validation and gating.
5
+ */
6
+ // Types and schemas
7
+ export { WatchConfigSchema, WatchGitConfigSchema, parseWatchConfig, getDefaultWatchConfig, DEFAULT_WATCH_PATTERNS, DEFAULT_EXCLUDE_PATTERNS, } from './types.js';
8
+ // Git status checker
9
+ export { GitStatusChecker, createGitStatusChecker, getChangedFiles } from './git-status.js';
10
+ // Debouncer
11
+ export { ChangeDebouncer, createDebouncer } from './debouncer.js';
12
+ // File watcher
13
+ export { FileWatcher, createFileWatcher } from './file-watcher.js';
14
+ // Orchestrator
15
+ export { WatchOrchestrator, createWatchOrchestrator } from './orchestrator.js';
@@ -0,0 +1,113 @@
1
+ /**
2
+ * Watch Orchestrator
3
+ *
4
+ * Coordinates file watching, git filtering, debouncing, and action dispatch.
5
+ * This is the main entry point for watch mode functionality.
6
+ *
7
+ * Multi-Agent Support:
8
+ * - Acquires workspace-level watch lock to prevent multiple watchers
9
+ * - Coordinates action execution through lock manager
10
+ * - Registers agent and maintains heartbeat
11
+ */
12
+ import type { WatchOrchestratorOptions, WatchActionResult } from './types.js';
13
+ /**
14
+ * Action handler type
15
+ */
16
+ export type ActionHandler = (files: string[]) => Promise<WatchActionResult>;
17
+ /**
18
+ * Watch orchestrator
19
+ *
20
+ * Coordinates all watch components and dispatches actions.
21
+ * Supports multi-agent coordination with distributed locking.
22
+ */
23
+ export declare class WatchOrchestrator {
24
+ private fileWatcher;
25
+ private gitChecker;
26
+ private debouncer;
27
+ private config;
28
+ private workspaceRoot;
29
+ private onEvent?;
30
+ private verbose;
31
+ private isRunning;
32
+ private isGitRepo;
33
+ private multiAgentConfig;
34
+ private concurrencyContext;
35
+ private validateHandler?;
36
+ private gateHandler?;
37
+ private checkHandler?;
38
+ private stats;
39
+ constructor(options: WatchOrchestratorOptions);
40
+ /**
41
+ * Set the validate action handler
42
+ */
43
+ setValidateHandler(handler: ActionHandler): void;
44
+ /**
45
+ * Set the gate action handler
46
+ */
47
+ setGateHandler(handler: ActionHandler): void;
48
+ /**
49
+ * Set the check action handler (for source file analysis)
50
+ */
51
+ setCheckHandler(handler: ActionHandler): void;
52
+ /**
53
+ * Start watching
54
+ */
55
+ start(): Promise<void>;
56
+ /**
57
+ * Initialize multi-agent coordination
58
+ */
59
+ private initializeMultiAgent;
60
+ /**
61
+ * Stop watching
62
+ */
63
+ stop(): Promise<void>;
64
+ /**
65
+ * Get the agent ID (if multi-agent is enabled)
66
+ */
67
+ getAgentId(): string | undefined;
68
+ /**
69
+ * Get current statistics
70
+ */
71
+ getStats(): typeof this.stats;
72
+ /**
73
+ * Check if orchestrator is running
74
+ */
75
+ get running(): boolean;
76
+ /**
77
+ * Handle file change event from watcher
78
+ */
79
+ private handleFileChange;
80
+ /**
81
+ * Handle debounced batch of changes
82
+ */
83
+ private handleDebouncedChanges;
84
+ /**
85
+ * Run the configured action
86
+ */
87
+ private runAction;
88
+ /**
89
+ * Run action with multi-agent coordination (locking/queuing)
90
+ */
91
+ private runCoordinatedAction;
92
+ /**
93
+ * Run action directly without coordination
94
+ */
95
+ private runDirectAction;
96
+ /**
97
+ * Handle watcher ready event
98
+ */
99
+ private handleReady;
100
+ /**
101
+ * Handle watcher error
102
+ */
103
+ private handleError;
104
+ /**
105
+ * Emit status event
106
+ */
107
+ private emitEvent;
108
+ }
109
+ /**
110
+ * Create a watch orchestrator
111
+ */
112
+ export declare function createWatchOrchestrator(options: WatchOrchestratorOptions): WatchOrchestrator;
113
+ //# sourceMappingURL=orchestrator.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"orchestrator.d.ts","sourceRoot":"","sources":["../../src/watch/orchestrator.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAKH,OAAO,KAAK,EAEV,wBAAwB,EAExB,iBAAiB,EAIlB,MAAM,YAAY,CAAC;AAUpB;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAc5E;;;;;GAKG;AACH,qBAAa,iBAAiB;IAC5B,OAAO,CAAC,WAAW,CAAc;IACjC,OAAO,CAAC,UAAU,CAAmB;IACrC,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAC,CAAoC;IACpD,OAAO,CAAC,OAAO,CAAU;IACzB,OAAO,CAAC,SAAS,CAAS;IAC1B,OAAO,CAAC,SAAS,CAAS;IAG1B,OAAO,CAAC,gBAAgB,CAA6B;IACrD,OAAO,CAAC,kBAAkB,CAAmC;IAG7D,OAAO,CAAC,eAAe,CAAC,CAAgB;IACxC,OAAO,CAAC,WAAW,CAAC,CAAgB;IACpC,OAAO,CAAC,YAAY,CAAC,CAAgB;IAGrC,OAAO,CAAC,KAAK,CAQX;gBAEU,OAAO,EAAE,wBAAwB;IAkB7C;;OAEG;IACH,kBAAkB,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAIhD;;OAEG;IACH,cAAc,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAI5C;;OAEG;IACH,eAAe,CAAC,OAAO,EAAE,aAAa,GAAG,IAAI;IAI7C;;OAEG;IACG,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAqC5B;;OAEG;YACW,oBAAoB;IAyElC;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAoB3B;;OAEG;IACH,UAAU,IAAI,MAAM,GAAG,SAAS;IAIhC;;OAEG;IACH,QAAQ,IAAI,OAAO,IAAI,CAAC,KAAK;IAI7B;;OAEG;IACH,IAAI,OAAO,IAAI,OAAO,CAErB;IAED;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAYxB;;OAEG;YACW,sBAAsB;IAiCpC;;OAEG;YACW,SAAS;IAkCvB;;OAEG;YACW,oBAAoB;IAgDlC;;OAEG;YACW,eAAe;IA8C7B;;OAEG;IACH,OAAO,CAAC,WAAW;IASnB;;OAEG;IACH,OAAO,CAAC,WAAW;IAInB;;OAEG;IACH,OAAO,CAAC,SAAS;CAGlB;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,wBAAwB,GAAG,iBAAiB,CAE5F"}
@@ -0,0 +1,409 @@
1
+ /**
2
+ * Watch Orchestrator
3
+ *
4
+ * Coordinates file watching, git filtering, debouncing, and action dispatch.
5
+ * This is the main entry point for watch mode functionality.
6
+ *
7
+ * Multi-Agent Support:
8
+ * - Acquires workspace-level watch lock to prevent multiple watchers
9
+ * - Coordinates action execution through lock manager
10
+ * - Registers agent and maintains heartbeat
11
+ */
12
+ import { FileWatcher } from './file-watcher.js';
13
+ import { GitStatusChecker } from './git-status.js';
14
+ import { ChangeDebouncer } from './debouncer.js';
15
+ import { createConcurrencyContext, createAgentInfo, } from '../concurrency/index.js';
16
+ import { createDebugger } from '@eddacraft/anvil-core';
17
+ const debug = createDebugger('watch');
18
+ /**
19
+ * Default multi-agent configuration
20
+ */
21
+ const DEFAULT_MULTI_AGENT_CONFIG = {
22
+ enabled: true,
23
+ exclusiveWatch: true,
24
+ coordinatedActions: true,
25
+ agentId: '',
26
+ waitForLock: true,
27
+ lockWaitTimeoutMs: 30000,
28
+ };
29
+ /**
30
+ * Watch orchestrator
31
+ *
32
+ * Coordinates all watch components and dispatches actions.
33
+ * Supports multi-agent coordination with distributed locking.
34
+ */
35
+ export class WatchOrchestrator {
36
+ fileWatcher;
37
+ gitChecker;
38
+ debouncer;
39
+ config;
40
+ workspaceRoot;
41
+ onEvent;
42
+ verbose;
43
+ isRunning = false;
44
+ isGitRepo = false;
45
+ // Multi-agent support
46
+ multiAgentConfig;
47
+ concurrencyContext = null;
48
+ // Action handlers
49
+ validateHandler;
50
+ gateHandler;
51
+ checkHandler;
52
+ // Statistics
53
+ stats = {
54
+ changesDetected: 0,
55
+ changesFiltered: 0,
56
+ actionsRun: 0,
57
+ actionsPassed: 0,
58
+ actionsFailed: 0,
59
+ actionsQueued: 0,
60
+ lockWaits: 0,
61
+ };
62
+ constructor(options) {
63
+ this.workspaceRoot = options.workspaceRoot;
64
+ this.config = options.config;
65
+ this.onEvent = options.onEvent;
66
+ this.verbose = options.verbose ?? false;
67
+ this.multiAgentConfig = {
68
+ ...DEFAULT_MULTI_AGENT_CONFIG,
69
+ ...options.multiAgent,
70
+ };
71
+ this.fileWatcher = new FileWatcher();
72
+ this.gitChecker = new GitStatusChecker(options.workspaceRoot);
73
+ this.debouncer = new ChangeDebouncer(options.config.debounceMs, this.handleDebouncedChanges.bind(this));
74
+ }
75
+ /**
76
+ * Set the validate action handler
77
+ */
78
+ setValidateHandler(handler) {
79
+ this.validateHandler = handler;
80
+ }
81
+ /**
82
+ * Set the gate action handler
83
+ */
84
+ setGateHandler(handler) {
85
+ this.gateHandler = handler;
86
+ }
87
+ /**
88
+ * Set the check action handler (for source file analysis)
89
+ */
90
+ setCheckHandler(handler) {
91
+ this.checkHandler = handler;
92
+ }
93
+ /**
94
+ * Start watching
95
+ */
96
+ async start() {
97
+ debug(`orchestrator start`, { workspace: this.workspaceRoot, action: this.config.action });
98
+ if (this.isRunning) {
99
+ throw new Error('Watch orchestrator is already running');
100
+ }
101
+ // Initialize multi-agent coordination if enabled
102
+ if (this.multiAgentConfig.enabled) {
103
+ debug('orchestrator start: initializing multi-agent coordination');
104
+ await this.initializeMultiAgent();
105
+ }
106
+ // Check if we're in a git repository
107
+ this.isGitRepo = await this.gitChecker.isGitRepository();
108
+ debug(`orchestrator start: isGitRepo=${this.isGitRepo}`);
109
+ if (!this.isGitRepo && this.config.git.unstagedOnly) {
110
+ console.warn('Warning: Not a git repository. Git filtering disabled, watching all file changes.');
111
+ }
112
+ // Set up file watcher events
113
+ this.fileWatcher.on('change', this.handleFileChange.bind(this));
114
+ this.fileWatcher.on('error', this.handleError.bind(this));
115
+ this.fileWatcher.on('ready', this.handleReady.bind(this));
116
+ // Start watching
117
+ await this.fileWatcher.start({
118
+ patterns: this.config.patterns,
119
+ exclude: this.config.exclude,
120
+ cwd: this.workspaceRoot,
121
+ });
122
+ this.isRunning = true;
123
+ }
124
+ /**
125
+ * Initialize multi-agent coordination
126
+ */
127
+ async initializeMultiAgent() {
128
+ try {
129
+ // Create concurrency context, honouring custom agent ID if provided
130
+ this.concurrencyContext = await createConcurrencyContext({
131
+ workspaceRoot: this.workspaceRoot,
132
+ autoRegister: true,
133
+ autoHeartbeat: true,
134
+ ...(this.multiAgentConfig.agentId
135
+ ? { agentInfo: createAgentInfo({ id: this.multiAgentConfig.agentId }) }
136
+ : {}),
137
+ });
138
+ // Acquire exclusive watch lock if configured
139
+ if (this.multiAgentConfig.exclusiveWatch) {
140
+ const lockResult = await this.concurrencyContext.queue.waitForLock({
141
+ type: 'watch',
142
+ resource: 'workspace',
143
+ reason: 'Starting file watch mode',
144
+ maxWaitMs: this.multiAgentConfig.waitForLock
145
+ ? this.multiAgentConfig.lockWaitTimeoutMs
146
+ : 0,
147
+ onPositionChange: (position, _total) => {
148
+ this.stats.lockWaits++;
149
+ this.emitEvent({
150
+ type: 'lock:waiting',
151
+ resource: 'watch:workspace',
152
+ heldBy: 'another-agent',
153
+ queuePosition: position,
154
+ });
155
+ },
156
+ });
157
+ if (!lockResult.acquired) {
158
+ // Clean up context
159
+ await this.concurrencyContext.cleanup();
160
+ this.concurrencyContext = null;
161
+ const error = `Cannot start watch mode: ${lockResult.error || 'Lock held by another agent'}`;
162
+ if (lockResult.heldBy) {
163
+ this.emitEvent({
164
+ type: 'lock:denied',
165
+ resource: 'watch:workspace',
166
+ heldBy: lockResult.heldBy.agentId,
167
+ reason: `Lock expires at ${lockResult.heldBy.expiresAt}`,
168
+ });
169
+ }
170
+ throw new Error(error);
171
+ }
172
+ // Start auto-renewal for watch lock
173
+ this.concurrencyContext.locks.startAutoRenewal('watch', 'workspace');
174
+ this.emitEvent({
175
+ type: 'lock:acquired',
176
+ resource: 'watch:workspace',
177
+ agentId: this.concurrencyContext.agent.getAgentId(),
178
+ });
179
+ }
180
+ // Update agent operation
181
+ await this.concurrencyContext.agent.setOperation('watching');
182
+ }
183
+ catch (error) {
184
+ // Clean up on error
185
+ if (this.concurrencyContext) {
186
+ await this.concurrencyContext.cleanup();
187
+ this.concurrencyContext = null;
188
+ }
189
+ throw error;
190
+ }
191
+ }
192
+ /**
193
+ * Stop watching
194
+ */
195
+ async stop() {
196
+ debug(`orchestrator stop: running=${this.isRunning}`);
197
+ if (!this.isRunning) {
198
+ return;
199
+ }
200
+ this.debouncer.cancel();
201
+ await this.fileWatcher.stop();
202
+ // Clean up multi-agent coordination
203
+ if (this.concurrencyContext) {
204
+ await this.concurrencyContext.cleanup();
205
+ this.concurrencyContext = null;
206
+ }
207
+ this.isRunning = false;
208
+ this.emitEvent({ type: 'stopped' });
209
+ }
210
+ /**
211
+ * Get the agent ID (if multi-agent is enabled)
212
+ */
213
+ getAgentId() {
214
+ return this.concurrencyContext?.agent.getAgentId();
215
+ }
216
+ /**
217
+ * Get current statistics
218
+ */
219
+ getStats() {
220
+ return { ...this.stats };
221
+ }
222
+ /**
223
+ * Check if orchestrator is running
224
+ */
225
+ get running() {
226
+ return this.isRunning;
227
+ }
228
+ /**
229
+ * Handle file change event from watcher
230
+ */
231
+ handleFileChange(event) {
232
+ // Only process add and change events (not unlink)
233
+ if (event.type === 'unlink') {
234
+ debug(`orchestrator: ignoring unlink event for ${event.path}`);
235
+ return;
236
+ }
237
+ debug(`orchestrator: file ${event.type} ${event.path}`);
238
+ this.stats.changesDetected++;
239
+ this.debouncer.add(event.path);
240
+ }
241
+ /**
242
+ * Handle debounced batch of changes
243
+ */
244
+ async handleDebouncedChanges(changes) {
245
+ debug(`orchestrator: debounced batch of ${changes.files.length} files`);
246
+ let filesToProcess = changes.files;
247
+ // Apply git filter if enabled and in git repo
248
+ if (this.isGitRepo && this.config.git.unstagedOnly) {
249
+ filesToProcess = await this.gitChecker.filterUnstaged(changes.files, this.config.git.includeUntracked);
250
+ }
251
+ this.stats.changesFiltered += changes.files.length - filesToProcess.length;
252
+ // Emit change event
253
+ this.emitEvent({
254
+ type: 'change',
255
+ files: changes.files,
256
+ filtered: filesToProcess,
257
+ });
258
+ // Skip if all files were filtered out
259
+ if (filesToProcess.length === 0) {
260
+ if (this.verbose) {
261
+ console.warn('All changed files were filtered out (staged or excluded)');
262
+ }
263
+ return;
264
+ }
265
+ // Run the configured action
266
+ await this.runAction(filesToProcess);
267
+ }
268
+ /**
269
+ * Run the configured action
270
+ */
271
+ async runAction(files) {
272
+ const action = this.config.action;
273
+ debug(`orchestrator runAction`, { action, files: files.length });
274
+ let handler;
275
+ switch (action) {
276
+ case 'validate':
277
+ handler = this.validateHandler;
278
+ break;
279
+ case 'gate':
280
+ handler = this.gateHandler;
281
+ break;
282
+ case 'check':
283
+ handler = this.checkHandler;
284
+ break;
285
+ }
286
+ if (!handler) {
287
+ console.warn(`No handler registered for action: ${action}`);
288
+ return;
289
+ }
290
+ // Use coordinated action execution if enabled
291
+ if (this.multiAgentConfig.enabled &&
292
+ this.multiAgentConfig.coordinatedActions &&
293
+ this.concurrencyContext) {
294
+ await this.runCoordinatedAction(action, handler, files);
295
+ }
296
+ else {
297
+ await this.runDirectAction(action, handler, files);
298
+ }
299
+ }
300
+ /**
301
+ * Run action with multi-agent coordination (locking/queuing)
302
+ */
303
+ async runCoordinatedAction(action, handler, files) {
304
+ if (!this.concurrencyContext) {
305
+ return this.runDirectAction(action, handler, files);
306
+ }
307
+ const resource = `action:${action}`;
308
+ // Try to acquire action lock with queuing
309
+ const lockResult = await this.concurrencyContext.queue.waitForLock({
310
+ type: 'action',
311
+ resource,
312
+ reason: `Running ${action} on ${files.length} files`,
313
+ maxWaitMs: 60000, // Wait up to 60s for action lock
314
+ onPositionChange: (position, _total) => {
315
+ this.stats.actionsQueued++;
316
+ this.emitEvent({
317
+ type: 'action:queued',
318
+ action,
319
+ position,
320
+ files,
321
+ });
322
+ },
323
+ });
324
+ if (!lockResult.acquired) {
325
+ this.emitEvent({
326
+ type: 'action:error',
327
+ error: new Error(`Could not acquire action lock: ${lockResult.error}`),
328
+ files,
329
+ });
330
+ return;
331
+ }
332
+ try {
333
+ // Update agent operation
334
+ await this.concurrencyContext.agent.setOperation(`running:${action}`);
335
+ await this.runDirectAction(action, handler, files);
336
+ }
337
+ finally {
338
+ await this.concurrencyContext.locks.release('action', resource);
339
+ await this.concurrencyContext.agent.setOperation('watching');
340
+ }
341
+ }
342
+ /**
343
+ * Run action directly without coordination
344
+ */
345
+ async runDirectAction(action, handler, files) {
346
+ debug(`orchestrator runDirectAction`, { action, files: files.length });
347
+ this.stats.actionsRun++;
348
+ this.emitEvent({
349
+ type: 'action:start',
350
+ action,
351
+ files,
352
+ });
353
+ try {
354
+ const startTime = Date.now();
355
+ const result = await handler(files);
356
+ result.executionTimeMs = Date.now() - startTime;
357
+ if (result.success) {
358
+ this.stats.actionsPassed++;
359
+ debug(`orchestrator action passed`, { action, elapsed: result.executionTimeMs });
360
+ }
361
+ else {
362
+ this.stats.actionsFailed++;
363
+ debug(`orchestrator action failed`, { action, elapsed: result.executionTimeMs });
364
+ }
365
+ this.emitEvent({
366
+ type: 'action:complete',
367
+ result,
368
+ });
369
+ }
370
+ catch (error) {
371
+ this.stats.actionsFailed++;
372
+ debug(`orchestrator action error: action=${action}`, error instanceof Error ? error : String(error));
373
+ this.emitEvent({
374
+ type: 'action:error',
375
+ error: error instanceof Error ? error : new Error(String(error)),
376
+ files,
377
+ });
378
+ }
379
+ }
380
+ /**
381
+ * Handle watcher ready event
382
+ */
383
+ handleReady() {
384
+ this.emitEvent({
385
+ type: 'ready',
386
+ patterns: this.config.patterns,
387
+ gitFilter: this.isGitRepo && this.config.git.unstagedOnly,
388
+ agentId: this.concurrencyContext?.agent.getAgentId(),
389
+ });
390
+ }
391
+ /**
392
+ * Handle watcher error
393
+ */
394
+ handleError(error) {
395
+ console.error('Watch error:', error.message);
396
+ }
397
+ /**
398
+ * Emit status event
399
+ */
400
+ emitEvent(event) {
401
+ this.onEvent?.(event);
402
+ }
403
+ }
404
+ /**
405
+ * Create a watch orchestrator
406
+ */
407
+ export function createWatchOrchestrator(options) {
408
+ return new WatchOrchestrator(options);
409
+ }