@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,313 @@
1
+ /**
2
+ * Git Agent Identification
3
+ *
4
+ * Utilities for identifying agents via git commit metadata.
5
+ * Supports reading and writing agent identification through commit trailers.
6
+ */
7
+ import { execFileSync } from 'node:child_process';
8
+ import { readFileSync, writeFileSync } from 'node:fs';
9
+ import { createAgentInfo } from './agent.js';
10
+ import { createDebugger } from '@eddacraft/anvil-core';
11
+ const debug = createDebugger('git-agent');
12
+ // ============================================================================
13
+ // Trailer Constants
14
+ // ============================================================================
15
+ /**
16
+ * Git trailer keys used for agent identification
17
+ */
18
+ export const GIT_TRAILERS = {
19
+ /** Agent ID trailer */
20
+ AGENT_ID: 'Anvil-Agent-ID',
21
+ /** Agent type trailer */
22
+ AGENT_TYPE: 'Anvil-Agent-Type',
23
+ /** Session ID trailer */
24
+ SESSION_ID: 'Anvil-Session-ID',
25
+ /** Agent name trailer */
26
+ AGENT_NAME: 'Anvil-Agent-Name',
27
+ /** Co-authored-by for agent attribution */
28
+ CO_AUTHORED_BY: 'Co-authored-by',
29
+ };
30
+ /**
31
+ * Parse trailers from commit message
32
+ */
33
+ export function parseCommitTrailers(commitMessage) {
34
+ const trailers = {};
35
+ const lines = commitMessage.split('\n');
36
+ // Trailers are at the end, after a blank line
37
+ let inTrailers = false;
38
+ for (let i = lines.length - 1; i >= 0; i--) {
39
+ const line = lines[i].trim();
40
+ if (line === '') {
41
+ if (inTrailers)
42
+ break;
43
+ inTrailers = true;
44
+ continue;
45
+ }
46
+ if (inTrailers) {
47
+ const match = line.match(/^([A-Za-z][A-Za-z0-9-]*)\s*:\s*(.+)$/);
48
+ if (match) {
49
+ trailers[match[1]] = match[2];
50
+ }
51
+ }
52
+ }
53
+ return trailers;
54
+ }
55
+ /**
56
+ * Extract agent info from commit trailers
57
+ */
58
+ export function extractAgentInfo(trailers) {
59
+ const agentId = trailers[GIT_TRAILERS.AGENT_ID];
60
+ const agentTypeStr = trailers[GIT_TRAILERS.AGENT_TYPE];
61
+ const sessionId = trailers[GIT_TRAILERS.SESSION_ID];
62
+ const agentName = trailers[GIT_TRAILERS.AGENT_NAME];
63
+ // Check for AI indicators
64
+ const isAiGenerated = !!agentId ||
65
+ !!agentTypeStr ||
66
+ Object.values(trailers).some((v) => v.toLowerCase().includes('ai') ||
67
+ v.toLowerCase().includes('claude') ||
68
+ v.toLowerCase().includes('copilot') ||
69
+ v.toLowerCase().includes('cursor'));
70
+ // Extract co-authors
71
+ const coAuthors = [];
72
+ for (const [key, value] of Object.entries(trailers)) {
73
+ if (key.toLowerCase() === 'co-authored-by') {
74
+ coAuthors.push(value);
75
+ }
76
+ }
77
+ return {
78
+ agentId,
79
+ agentType: agentTypeStr,
80
+ sessionId,
81
+ agentName,
82
+ isAiGenerated,
83
+ coAuthors,
84
+ trailers,
85
+ };
86
+ }
87
+ /**
88
+ * Get agent info from a specific commit
89
+ */
90
+ export function getCommitAgentInfo(commitRef, cwd) {
91
+ try {
92
+ const message = execFileSync('git', ['log', '-1', '--format=%B', commitRef], {
93
+ cwd,
94
+ encoding: 'utf-8',
95
+ stdio: ['pipe', 'pipe', 'pipe'],
96
+ });
97
+ const trailers = parseCommitTrailers(message);
98
+ return extractAgentInfo(trailers);
99
+ }
100
+ catch (error) {
101
+ debug(`Failed to get commit agent info for ${commitRef}:`, error);
102
+ return null;
103
+ }
104
+ }
105
+ /**
106
+ * Get agent info from recent commits
107
+ */
108
+ export function getRecentCommitsAgentInfo(count = 10, cwd) {
109
+ const results = [];
110
+ try {
111
+ const hashes = execFileSync('git', ['log', `-${count}`, '--format=%H'], {
112
+ cwd,
113
+ encoding: 'utf-8',
114
+ stdio: ['pipe', 'pipe', 'pipe'],
115
+ })
116
+ .trim()
117
+ .split('\n');
118
+ for (const hash of hashes) {
119
+ const info = getCommitAgentInfo(hash, cwd);
120
+ if (info) {
121
+ results.push({ hash, info });
122
+ }
123
+ }
124
+ }
125
+ catch (error) {
126
+ debug('Failed to get recent commits:', error);
127
+ }
128
+ return results;
129
+ }
130
+ /**
131
+ * Format a commit message with agent identification trailers
132
+ */
133
+ export function formatCommitWithAgent(options) {
134
+ const { message, agent = createAgentInfo(), includeCoAuthor = true, additionalTrailers = {}, } = options;
135
+ const trailers = [];
136
+ // Add agent trailers
137
+ trailers.push(`${GIT_TRAILERS.AGENT_ID}: ${agent.id}`);
138
+ trailers.push(`${GIT_TRAILERS.AGENT_TYPE}: ${agent.type}`);
139
+ if (agent.sessionId) {
140
+ trailers.push(`${GIT_TRAILERS.SESSION_ID}: ${agent.sessionId}`);
141
+ }
142
+ if (agent.name) {
143
+ trailers.push(`${GIT_TRAILERS.AGENT_NAME}: ${agent.name}`);
144
+ }
145
+ // Add co-authored-by for AI agents
146
+ if (includeCoAuthor && agent.type !== 'human') {
147
+ const coAuthorName = getAgentCoAuthorName(agent);
148
+ trailers.push(`${GIT_TRAILERS.CO_AUTHORED_BY}: ${coAuthorName}`);
149
+ }
150
+ // Add additional trailers
151
+ for (const [key, value] of Object.entries(additionalTrailers)) {
152
+ trailers.push(`${key}: ${value}`);
153
+ }
154
+ // Ensure message has proper spacing before trailers
155
+ const trimmedMessage = message.trimEnd();
156
+ const trailersBlock = trailers.join('\n');
157
+ // Check if message already has trailers
158
+ const existingTrailers = parseCommitTrailers(trimmedMessage);
159
+ const hasExistingTrailers = Object.keys(existingTrailers).length > 0;
160
+ if (hasExistingTrailers) {
161
+ // Append to existing trailers
162
+ return `${trimmedMessage}\n${trailersBlock}`;
163
+ }
164
+ // Add blank line before trailers
165
+ return `${trimmedMessage}\n\n${trailersBlock}`;
166
+ }
167
+ /**
168
+ * Get co-author name for an agent
169
+ */
170
+ function getAgentCoAuthorName(agent) {
171
+ switch (agent.type) {
172
+ case 'claude':
173
+ return 'Claude <claude@anthropic.com>';
174
+ case 'cursor':
175
+ return 'Cursor AI <ai@cursor.sh>';
176
+ case 'copilot':
177
+ return 'GitHub Copilot <copilot@github.com>';
178
+ case 'aider':
179
+ return 'Aider <aider@aider.chat>';
180
+ case 'continue':
181
+ return 'Continue <ai@continue.dev>';
182
+ case 'codeium':
183
+ return 'Codeium <ai@codeium.com>';
184
+ default:
185
+ return `AI Agent (${agent.id}) <noreply@example.com>`;
186
+ }
187
+ }
188
+ // ============================================================================
189
+ // Git Hook Helpers
190
+ // ============================================================================
191
+ /**
192
+ * Prepare commit message hook helper
193
+ *
194
+ * Automatically adds agent trailers to commit messages.
195
+ * Can be used in a prepare-commit-msg hook.
196
+ */
197
+ export function prepareCommitMsgHook(commitMsgFile, commitSource, _sha1) {
198
+ // Only modify non-merge, non-squash commits
199
+ if (commitSource === 'merge' || commitSource === 'squash') {
200
+ return;
201
+ }
202
+ // Read original message
203
+ const originalMessage = readFileSync(commitMsgFile, 'utf-8');
204
+ // Check if already has agent trailers
205
+ const trailers = parseCommitTrailers(originalMessage);
206
+ if (trailers[GIT_TRAILERS.AGENT_ID]) {
207
+ return; // Already has agent info
208
+ }
209
+ // Add agent info
210
+ const agent = createAgentInfo();
211
+ // Only add for AI agents
212
+ if (agent.type === 'human' || agent.type === 'unknown') {
213
+ return;
214
+ }
215
+ const modifiedMessage = formatCommitWithAgent({
216
+ message: originalMessage,
217
+ agent,
218
+ });
219
+ writeFileSync(commitMsgFile, modifiedMessage);
220
+ }
221
+ /**
222
+ * Generate a git config command to set up the prepare-commit-msg hook
223
+ */
224
+ export function getHookSetupCommand() {
225
+ return `
226
+ # Add to .git/hooks/prepare-commit-msg:
227
+ #!/bin/sh
228
+ COMMIT_MSG_FILE=$1
229
+ COMMIT_SOURCE=$2
230
+ SHA1=$3
231
+
232
+ # Run the prepare-commit-msg hook via the runtime module
233
+ if command -v node &> /dev/null; then
234
+ node -e "
235
+ import('${`@eddacraft/anvil-runtime/concurrency`}')
236
+ .then(m => m.prepareCommitMsgHook('$COMMIT_MSG_FILE', '$COMMIT_SOURCE', '$SHA1'))
237
+ .catch(() => {});
238
+ "
239
+ fi
240
+ `.trim();
241
+ }
242
+ /**
243
+ * Get contribution summary by agent
244
+ */
245
+ export function getAgentContributions(sinceRef, cwd) {
246
+ const contributions = new Map();
247
+ try {
248
+ const args = ['log'];
249
+ if (sinceRef)
250
+ args.push(`${sinceRef}..HEAD`);
251
+ args.push('--format=%H|%aI');
252
+ const output = execFileSync('git', args, {
253
+ cwd,
254
+ encoding: 'utf-8',
255
+ stdio: ['pipe', 'pipe', 'pipe'],
256
+ }).trim();
257
+ if (!output)
258
+ return contributions;
259
+ const lines = output.split('\n');
260
+ for (const line of lines) {
261
+ const [hash, timestamp] = line.split('|');
262
+ const info = getCommitAgentInfo(hash, cwd);
263
+ if (!info?.agentId)
264
+ continue;
265
+ const existing = contributions.get(info.agentId);
266
+ if (existing) {
267
+ existing.commitCount++;
268
+ if (timestamp < existing.firstCommit) {
269
+ existing.firstCommit = timestamp;
270
+ }
271
+ if (timestamp > existing.lastCommit) {
272
+ existing.lastCommit = timestamp;
273
+ }
274
+ }
275
+ else {
276
+ contributions.set(info.agentId, {
277
+ agentId: info.agentId,
278
+ agentType: info.agentType ?? 'unknown',
279
+ commitCount: 1,
280
+ firstCommit: timestamp,
281
+ lastCommit: timestamp,
282
+ });
283
+ }
284
+ }
285
+ }
286
+ catch (error) {
287
+ debug('Failed to get agent contributions:', error);
288
+ }
289
+ return contributions;
290
+ }
291
+ /**
292
+ * Get percentage of AI-generated commits in a range
293
+ */
294
+ export function getAiCommitPercentage(sinceRef, cwd) {
295
+ try {
296
+ const revListArgs = ['rev-list', '--count'];
297
+ revListArgs.push(sinceRef ? `${sinceRef}..HEAD` : 'HEAD');
298
+ const totalCount = parseInt(execFileSync('git', revListArgs, {
299
+ cwd,
300
+ encoding: 'utf-8',
301
+ stdio: ['pipe', 'pipe', 'pipe'],
302
+ }).trim(), 10);
303
+ if (totalCount === 0)
304
+ return 0;
305
+ const commits = getRecentCommitsAgentInfo(totalCount, cwd);
306
+ const aiCount = commits.filter((c) => c.info.isAiGenerated).length;
307
+ return (aiCount / totalCount) * 100;
308
+ }
309
+ catch (error) {
310
+ debug('Failed to calculate AI commit percentage:', error);
311
+ return 0;
312
+ }
313
+ }
@@ -0,0 +1,95 @@
1
+ /**
2
+ * Multi-Agent Concurrency Module
3
+ *
4
+ * Provides coordination primitives for multi-agent development scenarios:
5
+ * - Agent identification and registration
6
+ * - File-based distributed locking
7
+ * - Fair request queuing
8
+ * - Git agent attribution
9
+ * - Atomic file operations
10
+ *
11
+ * @module @eddacraft/anvil-runtime/concurrency
12
+ */
13
+ export * from './types.js';
14
+ export { atomicWriteJson, atomicWriteText, readJsonSafe, readJsonWithRetry, acquireFileLock, tryAcquireFileLock, isLocked, forceReleaseLock, unlinkSafe, fileExists, getFileMtime, sleepWithJitter, type AtomicWriteOptions, type FileLockOptions, type FileLockHandle, } from './atomic.js';
15
+ export { AgentManager, createAgentManager, initializeGlobalAgent, getGlobalAgent, detectAgentType, getAgentId, getSessionId, getAgentName, createAgentInfo, type AgentManagerOptions, } from './agent.js';
16
+ export { LockManager, createLockManager, withLock, tryWithLock, type LockManagerOptions, type AcquireLockOptions, } from './lock-manager.js';
17
+ export { QueueManager, createQueueManager, coordinatedExecution, withConcurrencyLimit, type QueueManagerOptions, type QueueJoinOptions, type QueueWaitOptions, type ConcurrentGroupResult, } from './queue-manager.js';
18
+ export { GIT_TRAILERS, parseCommitTrailers, extractAgentInfo, getCommitAgentInfo, getRecentCommitsAgentInfo, formatCommitWithAgent, prepareCommitMsgHook, getHookSetupCommand, getAgentContributions, getAiCommitPercentage, type CommitAgentInfo, type FormatCommitOptions, type AgentContributionSummary, } from './git-agent.js';
19
+ import { AgentManager } from './agent.js';
20
+ import { LockManager, type LockManagerOptions } from './lock-manager.js';
21
+ import { QueueManager } from './queue-manager.js';
22
+ import type { ConcurrencyConfig, AgentInfo } from './types.js';
23
+ /**
24
+ * Options for creating a full concurrency context
25
+ */
26
+ export interface ConcurrencyContextOptions {
27
+ /** Workspace root directory */
28
+ workspaceRoot: string;
29
+ /** Concurrency configuration */
30
+ config?: Partial<ConcurrencyConfig>;
31
+ /** Agent info (auto-detected if not provided) */
32
+ agentInfo?: AgentInfo;
33
+ /** Whether to auto-register agent */
34
+ autoRegister?: boolean;
35
+ /** Whether to auto-start heartbeat */
36
+ autoHeartbeat?: boolean;
37
+ }
38
+ /**
39
+ * Full concurrency context with all managers
40
+ */
41
+ export interface ConcurrencyContext {
42
+ /** Agent manager */
43
+ agent: AgentManager;
44
+ /** Lock manager */
45
+ locks: LockManager;
46
+ /** Queue manager */
47
+ queue: QueueManager;
48
+ /** Configuration */
49
+ config: ConcurrencyConfig;
50
+ /** Cleanup function */
51
+ cleanup: () => Promise<void>;
52
+ }
53
+ /**
54
+ * Create a full concurrency context
55
+ *
56
+ * This is the main entry point for multi-agent coordination.
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * const ctx = await createConcurrencyContext({
61
+ * workspaceRoot: process.cwd(),
62
+ * autoRegister: true,
63
+ * autoHeartbeat: true,
64
+ * });
65
+ *
66
+ * try {
67
+ * // Wait for lock with fair queuing
68
+ * const result = await ctx.queue.waitForLock({
69
+ * type: 'action',
70
+ * resource: 'gate',
71
+ * reason: 'Running quality gates',
72
+ * });
73
+ *
74
+ * if (result.acquired) {
75
+ * // Perform work
76
+ * await runGates();
77
+ *
78
+ * // Lock is automatically released when leaving queue
79
+ * }
80
+ * } finally {
81
+ * await ctx.cleanup();
82
+ * }
83
+ * ```
84
+ */
85
+ export declare function createConcurrencyContext(options: ConcurrencyContextOptions): Promise<ConcurrencyContext>;
86
+ /**
87
+ * Create a simple lock-only context (without queuing)
88
+ *
89
+ * Use when you just need basic locking without fair queuing.
90
+ */
91
+ export declare function createSimpleLockContext(options: LockManagerOptions): {
92
+ locks: LockManager;
93
+ cleanup: () => Promise<void>;
94
+ };
95
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/concurrency/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAGH,cAAc,YAAY,CAAC;AAG3B,OAAO,EACL,eAAe,EACf,eAAe,EACf,YAAY,EACZ,iBAAiB,EACjB,eAAe,EACf,kBAAkB,EAClB,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,KAAK,kBAAkB,EACvB,KAAK,eAAe,EACpB,KAAK,cAAc,GACpB,MAAM,aAAa,CAAC;AAGrB,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,cAAc,EACd,eAAe,EACf,UAAU,EACV,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,KAAK,mBAAmB,GACzB,MAAM,YAAY,CAAC;AAGpB,OAAO,EACL,WAAW,EACX,iBAAiB,EACjB,QAAQ,EACR,WAAW,EACX,KAAK,kBAAkB,EACvB,KAAK,kBAAkB,GACxB,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,YAAY,EACZ,kBAAkB,EAClB,oBAAoB,EACpB,oBAAoB,EACpB,KAAK,mBAAmB,EACxB,KAAK,gBAAgB,EACrB,KAAK,gBAAgB,EACrB,KAAK,qBAAqB,GAC3B,MAAM,oBAAoB,CAAC;AAG5B,OAAO,EACL,YAAY,EACZ,mBAAmB,EACnB,gBAAgB,EAChB,kBAAkB,EAClB,yBAAyB,EACzB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,wBAAwB,GAC9B,MAAM,gBAAgB,CAAC;AAMxB,OAAO,EAAE,YAAY,EAAsB,MAAM,YAAY,CAAC;AAC9D,OAAO,EAAE,WAAW,EAAqB,KAAK,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAC5F,OAAO,EAAE,YAAY,EAAsB,MAAM,oBAAoB,CAAC;AACtE,OAAO,KAAK,EAAE,iBAAiB,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAG/D;;GAEG;AACH,MAAM,WAAW,yBAAyB;IACxC,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IAEtB,gCAAgC;IAChC,MAAM,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAEpC,iDAAiD;IACjD,SAAS,CAAC,EAAE,SAAS,CAAC;IAEtB,qCAAqC;IACrC,YAAY,CAAC,EAAE,OAAO,CAAC;IAEvB,sCAAsC;IACtC,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,oBAAoB;IACpB,KAAK,EAAE,YAAY,CAAC;IAEpB,mBAAmB;IACnB,KAAK,EAAE,WAAW,CAAC;IAEnB,oBAAoB;IACpB,KAAK,EAAE,YAAY,CAAC;IAEpB,oBAAoB;IACpB,MAAM,EAAE,iBAAiB,CAAC;IAE1B,uBAAuB;IACvB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAsB,wBAAwB,CAC5C,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,kBAAkB,CAAC,CA8D7B;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,kBAAkB,GAAG;IACpE,KAAK,EAAE,WAAW,CAAC;IACnB,OAAO,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CAC9B,CAUA"}
@@ -0,0 +1,127 @@
1
+ /**
2
+ * Multi-Agent Concurrency Module
3
+ *
4
+ * Provides coordination primitives for multi-agent development scenarios:
5
+ * - Agent identification and registration
6
+ * - File-based distributed locking
7
+ * - Fair request queuing
8
+ * - Git agent attribution
9
+ * - Atomic file operations
10
+ *
11
+ * @module @eddacraft/anvil-runtime/concurrency
12
+ */
13
+ // Types
14
+ export * from './types.js';
15
+ // Atomic file operations
16
+ export { atomicWriteJson, atomicWriteText, readJsonSafe, readJsonWithRetry, acquireFileLock, tryAcquireFileLock, isLocked, forceReleaseLock, unlinkSafe, fileExists, getFileMtime, sleepWithJitter, } from './atomic.js';
17
+ // Agent management
18
+ export { AgentManager, createAgentManager, initializeGlobalAgent, getGlobalAgent, detectAgentType, getAgentId, getSessionId, getAgentName, createAgentInfo, } from './agent.js';
19
+ // Lock management
20
+ export { LockManager, createLockManager, withLock, tryWithLock, } from './lock-manager.js';
21
+ // Queue management
22
+ export { QueueManager, createQueueManager, coordinatedExecution, withConcurrencyLimit, } from './queue-manager.js';
23
+ // Git agent identification
24
+ export { GIT_TRAILERS, parseCommitTrailers, extractAgentInfo, getCommitAgentInfo, getRecentCommitsAgentInfo, formatCommitWithAgent, prepareCommitMsgHook, getHookSetupCommand, getAgentContributions, getAiCommitPercentage, } from './git-agent.js';
25
+ // ============================================================================
26
+ // High-Level Convenience Functions
27
+ // ============================================================================
28
+ import { createAgentManager } from './agent.js';
29
+ import { createLockManager } from './lock-manager.js';
30
+ import { createQueueManager } from './queue-manager.js';
31
+ import { getDefaultConcurrencyConfig } from './types.js';
32
+ /**
33
+ * Create a full concurrency context
34
+ *
35
+ * This is the main entry point for multi-agent coordination.
36
+ *
37
+ * @example
38
+ * ```typescript
39
+ * const ctx = await createConcurrencyContext({
40
+ * workspaceRoot: process.cwd(),
41
+ * autoRegister: true,
42
+ * autoHeartbeat: true,
43
+ * });
44
+ *
45
+ * try {
46
+ * // Wait for lock with fair queuing
47
+ * const result = await ctx.queue.waitForLock({
48
+ * type: 'action',
49
+ * resource: 'gate',
50
+ * reason: 'Running quality gates',
51
+ * });
52
+ *
53
+ * if (result.acquired) {
54
+ * // Perform work
55
+ * await runGates();
56
+ *
57
+ * // Lock is automatically released when leaving queue
58
+ * }
59
+ * } finally {
60
+ * await ctx.cleanup();
61
+ * }
62
+ * ```
63
+ */
64
+ export async function createConcurrencyContext(options) {
65
+ const { workspaceRoot, config: configOverrides, agentInfo, autoRegister = true, autoHeartbeat = true, } = options;
66
+ const config = {
67
+ ...getDefaultConcurrencyConfig(),
68
+ ...configOverrides,
69
+ };
70
+ // Create agent manager
71
+ const agent = createAgentManager({
72
+ workspaceRoot,
73
+ config,
74
+ agentInfo,
75
+ });
76
+ // Create lock manager with same agent
77
+ const locks = createLockManager({
78
+ workspaceRoot,
79
+ config,
80
+ agentInfo: agent.getAgent(),
81
+ });
82
+ // Create queue manager with same lock manager
83
+ const queue = createQueueManager({
84
+ workspaceRoot,
85
+ config,
86
+ agentInfo: agent.getAgent(),
87
+ lockManager: locks,
88
+ });
89
+ // Auto-register and start heartbeat
90
+ if (autoRegister) {
91
+ await agent.register('initializing');
92
+ }
93
+ if (autoHeartbeat) {
94
+ agent.startHeartbeat();
95
+ }
96
+ // Cleanup function
97
+ const cleanup = async () => {
98
+ agent.stopHeartbeat();
99
+ locks.stopAllRenewals();
100
+ await locks.releaseAll();
101
+ if (autoRegister) {
102
+ await agent.unregister();
103
+ }
104
+ };
105
+ return {
106
+ agent,
107
+ locks,
108
+ queue,
109
+ config,
110
+ cleanup,
111
+ };
112
+ }
113
+ /**
114
+ * Create a simple lock-only context (without queuing)
115
+ *
116
+ * Use when you just need basic locking without fair queuing.
117
+ */
118
+ export function createSimpleLockContext(options) {
119
+ const locks = createLockManager(options);
120
+ return {
121
+ locks,
122
+ cleanup: async () => {
123
+ locks.stopAllRenewals();
124
+ await locks.releaseAll();
125
+ },
126
+ };
127
+ }