@devran-ai/kit 4.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 (231) hide show
  1. package/.agent/CheatSheet.md +350 -0
  2. package/.agent/README.md +76 -0
  3. package/.agent/agents/README.md +155 -0
  4. package/.agent/agents/architect.md +185 -0
  5. package/.agent/agents/backend-specialist.md +276 -0
  6. package/.agent/agents/build-error-resolver.md +207 -0
  7. package/.agent/agents/code-reviewer.md +162 -0
  8. package/.agent/agents/database-architect.md +138 -0
  9. package/.agent/agents/devops-engineer.md +144 -0
  10. package/.agent/agents/doc-updater.md +229 -0
  11. package/.agent/agents/e2e-runner.md +145 -0
  12. package/.agent/agents/explorer-agent.md +143 -0
  13. package/.agent/agents/frontend-specialist.md +144 -0
  14. package/.agent/agents/go-reviewer.md +128 -0
  15. package/.agent/agents/knowledge-agent.md +197 -0
  16. package/.agent/agents/mobile-developer.md +150 -0
  17. package/.agent/agents/performance-optimizer.md +175 -0
  18. package/.agent/agents/planner.md +133 -0
  19. package/.agent/agents/pr-reviewer.md +148 -0
  20. package/.agent/agents/python-reviewer.md +123 -0
  21. package/.agent/agents/refactor-cleaner.md +201 -0
  22. package/.agent/agents/reliability-engineer.md +156 -0
  23. package/.agent/agents/security-reviewer.md +141 -0
  24. package/.agent/agents/sprint-orchestrator.md +124 -0
  25. package/.agent/agents/tdd-guide.md +179 -0
  26. package/.agent/agents/typescript-reviewer.md +110 -0
  27. package/.agent/checklists/README.md +102 -0
  28. package/.agent/checklists/pre-commit.md +93 -0
  29. package/.agent/checklists/session-end.md +99 -0
  30. package/.agent/checklists/session-start.md +102 -0
  31. package/.agent/checklists/task-complete.md +81 -0
  32. package/.agent/commands/README.md +130 -0
  33. package/.agent/commands/adr.md +29 -0
  34. package/.agent/commands/ask.md +28 -0
  35. package/.agent/commands/build.md +30 -0
  36. package/.agent/commands/changelog.md +40 -0
  37. package/.agent/commands/checkpoint.md +28 -0
  38. package/.agent/commands/code-review.md +65 -0
  39. package/.agent/commands/compact.md +28 -0
  40. package/.agent/commands/cook.md +30 -0
  41. package/.agent/commands/db.md +30 -0
  42. package/.agent/commands/debug.md +31 -0
  43. package/.agent/commands/deploy.md +37 -0
  44. package/.agent/commands/design.md +29 -0
  45. package/.agent/commands/doc.md +30 -0
  46. package/.agent/commands/eval.md +30 -0
  47. package/.agent/commands/fix.md +32 -0
  48. package/.agent/commands/git.md +32 -0
  49. package/.agent/commands/help.md +273 -0
  50. package/.agent/commands/implement.md +30 -0
  51. package/.agent/commands/integrate.md +32 -0
  52. package/.agent/commands/learn.md +29 -0
  53. package/.agent/commands/perf.md +31 -0
  54. package/.agent/commands/plan.md +56 -0
  55. package/.agent/commands/pr-describe.md +65 -0
  56. package/.agent/commands/pr-fix.md +45 -0
  57. package/.agent/commands/pr-merge.md +45 -0
  58. package/.agent/commands/pr-review.md +50 -0
  59. package/.agent/commands/pr-split.md +54 -0
  60. package/.agent/commands/pr-status.md +56 -0
  61. package/.agent/commands/pr.md +58 -0
  62. package/.agent/commands/refactor.md +32 -0
  63. package/.agent/commands/research.md +28 -0
  64. package/.agent/commands/scout.md +30 -0
  65. package/.agent/commands/security-scan.md +33 -0
  66. package/.agent/commands/setup.md +31 -0
  67. package/.agent/commands/status.md +59 -0
  68. package/.agent/commands/tdd.md +73 -0
  69. package/.agent/commands/verify.md +58 -0
  70. package/.agent/contexts/brainstorm.md +26 -0
  71. package/.agent/contexts/debug.md +28 -0
  72. package/.agent/contexts/implement.md +29 -0
  73. package/.agent/contexts/plan-quality-log.md +30 -0
  74. package/.agent/contexts/review.md +27 -0
  75. package/.agent/contexts/ship.md +28 -0
  76. package/.agent/decisions/001-trust-grade-governance.md +46 -0
  77. package/.agent/decisions/002-cross-ide-generation.md +15 -0
  78. package/.agent/engine/identity.json +4 -0
  79. package/.agent/engine/loading-rules.json +193 -0
  80. package/.agent/engine/marketplace-index.json +29 -0
  81. package/.agent/engine/mcp-servers/filesystem.json +9 -0
  82. package/.agent/engine/mcp-servers/github.json +11 -0
  83. package/.agent/engine/mcp-servers/postgres.json +11 -0
  84. package/.agent/engine/mcp-servers/supabase.json +11 -0
  85. package/.agent/engine/mcp-servers/vercel.json +11 -0
  86. package/.agent/engine/reliability-config.json +14 -0
  87. package/.agent/engine/sdlc-map.json +50 -0
  88. package/.agent/engine/workflow-state.json +167 -0
  89. package/.agent/hooks/README.md +101 -0
  90. package/.agent/hooks/hooks.json +104 -0
  91. package/.agent/hooks/templates/session-end.md +110 -0
  92. package/.agent/hooks/templates/session-start.md +95 -0
  93. package/.agent/manifest.json +466 -0
  94. package/.agent/rules/agent-upgrade-policy.md +56 -0
  95. package/.agent/rules/architecture.md +111 -0
  96. package/.agent/rules/coding-style.md +75 -0
  97. package/.agent/rules/documentation.md +74 -0
  98. package/.agent/rules/git-workflow.md +140 -0
  99. package/.agent/rules/quality-gate.md +117 -0
  100. package/.agent/rules/security.md +67 -0
  101. package/.agent/rules/sprint-tracking.md +103 -0
  102. package/.agent/rules/testing.md +80 -0
  103. package/.agent/rules/workflow-standards.md +30 -0
  104. package/.agent/rules.md +293 -0
  105. package/.agent/session-context.md +69 -0
  106. package/.agent/session-state.json +27 -0
  107. package/.agent/skills/README.md +135 -0
  108. package/.agent/skills/api-patterns/SKILL.md +117 -0
  109. package/.agent/skills/app-builder/SKILL.md +202 -0
  110. package/.agent/skills/architecture/SKILL.md +101 -0
  111. package/.agent/skills/behavioral-modes/SKILL.md +295 -0
  112. package/.agent/skills/brainstorming/SKILL.md +156 -0
  113. package/.agent/skills/clean-code/SKILL.md +142 -0
  114. package/.agent/skills/context-budget/SKILL.md +78 -0
  115. package/.agent/skills/continuous-learning/SKILL.md +145 -0
  116. package/.agent/skills/database-design/SKILL.md +303 -0
  117. package/.agent/skills/debugging-strategies/SKILL.md +158 -0
  118. package/.agent/skills/deployment-procedures/SKILL.md +191 -0
  119. package/.agent/skills/docker-patterns/SKILL.md +161 -0
  120. package/.agent/skills/eval-harness/SKILL.md +89 -0
  121. package/.agent/skills/frontend-patterns/SKILL.md +141 -0
  122. package/.agent/skills/git-workflow/SKILL.md +159 -0
  123. package/.agent/skills/i18n-localization/SKILL.md +191 -0
  124. package/.agent/skills/intelligent-routing/SKILL.md +180 -0
  125. package/.agent/skills/mcp-integration/SKILL.md +240 -0
  126. package/.agent/skills/mobile-design/SKILL.md +191 -0
  127. package/.agent/skills/nodejs-patterns/SKILL.md +164 -0
  128. package/.agent/skills/parallel-agents/SKILL.md +200 -0
  129. package/.agent/skills/performance-profiling/SKILL.md +134 -0
  130. package/.agent/skills/plan-validation/SKILL.md +192 -0
  131. package/.agent/skills/plan-writing/SKILL.md +183 -0
  132. package/.agent/skills/plan-writing/domain-enhancers.md +184 -0
  133. package/.agent/skills/plan-writing/plan-retrospective.md +116 -0
  134. package/.agent/skills/plan-writing/plan-schema.md +119 -0
  135. package/.agent/skills/pr-toolkit/SKILL.md +174 -0
  136. package/.agent/skills/production-readiness/SKILL.md +126 -0
  137. package/.agent/skills/security-practices/SKILL.md +109 -0
  138. package/.agent/skills/shell-conventions/SKILL.md +92 -0
  139. package/.agent/skills/strategic-compact/SKILL.md +62 -0
  140. package/.agent/skills/testing-patterns/SKILL.md +141 -0
  141. package/.agent/skills/typescript-expert/SKILL.md +160 -0
  142. package/.agent/skills/ui-ux-pro-max/SKILL.md +137 -0
  143. package/.agent/skills/ui-ux-pro-max/data/charts.csv +26 -0
  144. package/.agent/skills/ui-ux-pro-max/data/colors.csv +97 -0
  145. package/.agent/skills/ui-ux-pro-max/data/icons.csv +101 -0
  146. package/.agent/skills/ui-ux-pro-max/data/landing.csv +31 -0
  147. package/.agent/skills/ui-ux-pro-max/data/products.csv +97 -0
  148. package/.agent/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
  149. package/.agent/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
  150. package/.agent/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
  151. package/.agent/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
  152. package/.agent/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
  153. package/.agent/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
  154. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +51 -0
  155. package/.agent/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
  156. package/.agent/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
  157. package/.agent/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
  158. package/.agent/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
  159. package/.agent/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
  160. package/.agent/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
  161. package/.agent/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
  162. package/.agent/skills/ui-ux-pro-max/data/styles.csv +68 -0
  163. package/.agent/skills/ui-ux-pro-max/data/typography.csv +58 -0
  164. package/.agent/skills/ui-ux-pro-max/data/ui-reasoning.csv +101 -0
  165. package/.agent/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
  166. package/.agent/skills/ui-ux-pro-max/data/web-interface.csv +31 -0
  167. package/.agent/skills/ui-ux-pro-max/scripts/core.py +253 -0
  168. package/.agent/skills/ui-ux-pro-max/scripts/design_system.py +1067 -0
  169. package/.agent/skills/ui-ux-pro-max/scripts/search.py +114 -0
  170. package/.agent/skills/verification-loop/SKILL.md +89 -0
  171. package/.agent/skills/webapp-testing/SKILL.md +175 -0
  172. package/.agent/templates/adr-template.md +32 -0
  173. package/.agent/templates/bug-report.md +37 -0
  174. package/.agent/templates/feature-request.md +32 -0
  175. package/.agent/workflows/README.md +101 -0
  176. package/.agent/workflows/brainstorm.md +86 -0
  177. package/.agent/workflows/create.md +85 -0
  178. package/.agent/workflows/debug.md +83 -0
  179. package/.agent/workflows/deploy.md +114 -0
  180. package/.agent/workflows/enhance.md +85 -0
  181. package/.agent/workflows/orchestrate.md +106 -0
  182. package/.agent/workflows/plan.md +105 -0
  183. package/.agent/workflows/pr-fix.md +163 -0
  184. package/.agent/workflows/pr-merge.md +117 -0
  185. package/.agent/workflows/pr-review.md +178 -0
  186. package/.agent/workflows/pr-split.md +118 -0
  187. package/.agent/workflows/pr.md +184 -0
  188. package/.agent/workflows/preflight.md +107 -0
  189. package/.agent/workflows/preview.md +95 -0
  190. package/.agent/workflows/quality-gate.md +103 -0
  191. package/.agent/workflows/retrospective.md +100 -0
  192. package/.agent/workflows/review.md +104 -0
  193. package/.agent/workflows/status.md +89 -0
  194. package/.agent/workflows/test.md +98 -0
  195. package/.agent/workflows/ui-ux-pro-max.md +93 -0
  196. package/.agent/workflows/upgrade.md +97 -0
  197. package/LICENSE +21 -0
  198. package/README.md +218 -0
  199. package/bin/kit.js +773 -0
  200. package/lib/agent-registry.js +228 -0
  201. package/lib/agent-reputation.js +343 -0
  202. package/lib/circuit-breaker.js +195 -0
  203. package/lib/cli-commands.js +322 -0
  204. package/lib/config-validator.js +274 -0
  205. package/lib/conflict-detector.js +252 -0
  206. package/lib/constants.js +47 -0
  207. package/lib/engineering-manager.js +336 -0
  208. package/lib/error-budget.js +370 -0
  209. package/lib/hook-system.js +256 -0
  210. package/lib/ide-generator.js +434 -0
  211. package/lib/identity.js +240 -0
  212. package/lib/io.js +146 -0
  213. package/lib/learning-engine.js +163 -0
  214. package/lib/loading-engine.js +421 -0
  215. package/lib/logger.js +118 -0
  216. package/lib/marketplace.js +321 -0
  217. package/lib/plugin-system.js +604 -0
  218. package/lib/plugin-verifier.js +197 -0
  219. package/lib/rate-limiter.js +113 -0
  220. package/lib/security-scanner.js +312 -0
  221. package/lib/self-healing.js +468 -0
  222. package/lib/session-manager.js +264 -0
  223. package/lib/skill-sandbox.js +244 -0
  224. package/lib/task-governance.js +522 -0
  225. package/lib/task-model.js +332 -0
  226. package/lib/updater.js +240 -0
  227. package/lib/verify.js +279 -0
  228. package/lib/workflow-engine.js +373 -0
  229. package/lib/workflow-events.js +166 -0
  230. package/lib/workflow-persistence.js +160 -0
  231. package/package.json +57 -0
@@ -0,0 +1,274 @@
1
+ /**
2
+ * Devran AI Kit — Configuration Validator
3
+ *
4
+ * Runtime JSON schema validation for engine configuration files.
5
+ * Catches configuration corruption and drift before they cause
6
+ * runtime failures.
7
+ *
8
+ * @module lib/config-validator
9
+ * @author Emre Dursun
10
+ * @since v3.2.0
11
+ */
12
+
13
+ 'use strict';
14
+
15
+ const fs = require('fs');
16
+ const path = require('path');
17
+ const { AGENT_DIR, ENGINE_DIR, HOOKS_DIR } = require('./constants');
18
+
19
+ /**
20
+ * @typedef {object} ValidationResult
21
+ * @property {boolean} valid - Whether the config is valid
22
+ * @property {string[]} errors - Validation error messages
23
+ * @property {string[]} warnings - Non-critical warnings
24
+ */
25
+
26
+ /**
27
+ * Schema definitions for engine configuration files.
28
+ * Each schema defines required fields, their types, and constraints.
29
+ */
30
+ const SCHEMAS = {
31
+ 'manifest.json': {
32
+ required: ['schemaVersion', 'kitVersion', 'capabilities'],
33
+ types: {
34
+ schemaVersion: 'string',
35
+ kitVersion: 'string',
36
+ capabilities: 'object',
37
+ },
38
+ nested: {
39
+ capabilities: {
40
+ required: ['agents', 'commands', 'skills', 'workflows'],
41
+ types: {
42
+ agents: 'object',
43
+ commands: 'object',
44
+ skills: 'object',
45
+ workflows: 'object',
46
+ },
47
+ },
48
+ },
49
+ },
50
+
51
+ 'workflow-state.json': {
52
+ required: ['currentPhase', 'phases', 'transitions'],
53
+ types: {
54
+ currentPhase: 'string',
55
+ phases: 'object',
56
+ transitions: 'array',
57
+ },
58
+ validators: {
59
+ currentPhase: (value) => {
60
+ const validPhases = ['IDLE', 'EXPLORE', 'PLAN', 'IMPLEMENT', 'VERIFY', 'REVIEW', 'DEPLOY', 'MAINTAIN'];
61
+ return validPhases.includes(value) ? null : `Invalid phase: ${value}. Valid: ${validPhases.join(', ')}`;
62
+ },
63
+ },
64
+ },
65
+
66
+ 'loading-rules.json': {
67
+ required: ['domainRules', 'contextBudget', 'planningMandates'],
68
+ types: {
69
+ domainRules: 'array',
70
+ contextBudget: 'object',
71
+ planningMandates: 'object',
72
+ },
73
+ nested: {
74
+ contextBudget: {
75
+ required: ['maxAgentsPerSession', 'maxSkillsPerSession'],
76
+ types: {
77
+ maxAgentsPerSession: 'number',
78
+ maxSkillsPerSession: 'number',
79
+ },
80
+ },
81
+ planningMandates: {
82
+ required: ['alwaysLoadRules', 'alwaysLoadSkills', 'crossCuttingSections'],
83
+ types: {
84
+ alwaysLoadRules: 'array',
85
+ alwaysLoadSkills: 'array',
86
+ crossCuttingSections: 'array',
87
+ specialistContributors: 'object',
88
+ },
89
+ },
90
+ },
91
+ arrayItemSchema: {
92
+ domainRules: {
93
+ required: ['domain', 'keywords'],
94
+ types: {
95
+ domain: 'string',
96
+ keywords: 'array',
97
+ loadAgents: 'array',
98
+ loadSkills: 'array',
99
+ },
100
+ },
101
+ },
102
+ },
103
+
104
+ 'reliability-config.json': {
105
+ required: ['errorBudget'],
106
+ types: {
107
+ errorBudget: 'object',
108
+ },
109
+ nested: {
110
+ errorBudget: {
111
+ required: ['thresholds', 'resetCadence'],
112
+ types: {
113
+ thresholds: 'object',
114
+ resetCadence: 'string',
115
+ },
116
+ },
117
+ },
118
+ },
119
+
120
+ 'hooks.json': {
121
+ required: ['hooks'],
122
+ types: {
123
+ hooks: 'array',
124
+ },
125
+ arrayItemSchema: {
126
+ hooks: {
127
+ required: ['event'],
128
+ types: {
129
+ event: 'string',
130
+ },
131
+ },
132
+ },
133
+ },
134
+ };
135
+
136
+ /**
137
+ * Validates a value's type.
138
+ *
139
+ * @param {*} value - Value to check
140
+ * @param {string} expectedType - Expected type string
141
+ * @returns {boolean}
142
+ */
143
+ function checkType(value, expectedType) {
144
+ if (expectedType === 'array') {
145
+ return Array.isArray(value);
146
+ }
147
+ return typeof value === expectedType;
148
+ }
149
+
150
+ /**
151
+ * Validates a configuration object against its schema.
152
+ *
153
+ * @param {object} config - Parsed configuration object
154
+ * @param {object} schema - Schema definition
155
+ * @param {string} [prefix=''] - Field path prefix for nested errors
156
+ * @returns {ValidationResult}
157
+ */
158
+ function validateAgainstSchema(config, schema, prefix = '') {
159
+ const errors = [];
160
+ const warnings = [];
161
+
162
+ // Check required fields
163
+ for (const field of (schema.required || [])) {
164
+ const fieldPath = prefix ? `${prefix}.${field}` : field;
165
+ if (config[field] === undefined || config[field] === null) {
166
+ errors.push(`Missing required field: ${fieldPath}`);
167
+ }
168
+ }
169
+
170
+ // Check types
171
+ for (const [field, expectedType] of Object.entries(schema.types || {})) {
172
+ const fieldPath = prefix ? `${prefix}.${field}` : field;
173
+ if (config[field] !== undefined && config[field] !== null) {
174
+ if (!checkType(config[field], expectedType)) {
175
+ errors.push(`Invalid type for ${fieldPath}: expected ${expectedType}, got ${Array.isArray(config[field]) ? 'array' : typeof config[field]}`);
176
+ }
177
+ }
178
+ }
179
+
180
+ // Run custom validators
181
+ for (const [field, validator] of Object.entries(schema.validators || {})) {
182
+ if (config[field] !== undefined) {
183
+ const error = validator(config[field]);
184
+ if (error) {
185
+ errors.push(error);
186
+ }
187
+ }
188
+ }
189
+
190
+ // Validate nested schemas
191
+ for (const [field, nestedSchema] of Object.entries(schema.nested || {})) {
192
+ if (config[field] && typeof config[field] === 'object' && !Array.isArray(config[field])) {
193
+ const nestedResult = validateAgainstSchema(config[field], nestedSchema, prefix ? `${prefix}.${field}` : field);
194
+ errors.push(...nestedResult.errors);
195
+ warnings.push(...nestedResult.warnings);
196
+ }
197
+ }
198
+
199
+ // Validate array items
200
+ for (const [field, itemSchema] of Object.entries(schema.arrayItemSchema || {})) {
201
+ if (Array.isArray(config[field])) {
202
+ config[field].forEach((item, index) => {
203
+ if (typeof item === 'object' && item !== null) {
204
+ const itemResult = validateAgainstSchema(item, itemSchema, `${field}[${index}]`);
205
+ errors.push(...itemResult.errors);
206
+ warnings.push(...itemResult.warnings);
207
+ }
208
+ });
209
+ }
210
+ }
211
+
212
+ return { valid: errors.length === 0, errors, warnings };
213
+ }
214
+
215
+ /**
216
+ * Validates a specific engine configuration file.
217
+ *
218
+ * @param {string} projectRoot - Root directory of the project
219
+ * @param {string} configName - Configuration file name (e.g., 'manifest.json')
220
+ * @returns {ValidationResult}
221
+ */
222
+ function validateConfig(projectRoot, configName) {
223
+ const schema = SCHEMAS[configName];
224
+ if (!schema) {
225
+ return { valid: false, errors: [`No schema defined for: ${configName}`], warnings: [] };
226
+ }
227
+
228
+ const configDir = configName === 'manifest.json'
229
+ ? path.join(projectRoot, AGENT_DIR)
230
+ : configName === 'hooks.json'
231
+ ? path.join(projectRoot, AGENT_DIR, HOOKS_DIR)
232
+ : path.join(projectRoot, AGENT_DIR, ENGINE_DIR);
233
+
234
+ const configPath = path.join(configDir, configName);
235
+
236
+ if (!fs.existsSync(configPath)) {
237
+ return { valid: false, errors: [`Config file not found: ${configPath}`], warnings: [] };
238
+ }
239
+
240
+ let config;
241
+ try {
242
+ config = JSON.parse(fs.readFileSync(configPath, 'utf-8'));
243
+ } catch (parseError) {
244
+ return { valid: false, errors: [`Invalid JSON: ${parseError.message}`], warnings: [] };
245
+ }
246
+
247
+ return validateAgainstSchema(config, schema);
248
+ }
249
+
250
+ /**
251
+ * Validates all known engine configuration files.
252
+ *
253
+ * @param {string} projectRoot - Root directory of the project
254
+ * @returns {{ totalConfigs: number, validConfigs: number, results: Object.<string, ValidationResult> }}
255
+ */
256
+ function validateAllConfigs(projectRoot) {
257
+ const entries = Object.keys(SCHEMAS).map(
258
+ (configName) => [configName, validateConfig(projectRoot, configName)]
259
+ );
260
+ const results = Object.fromEntries(entries);
261
+ const validCount = entries.filter(([, result]) => result.valid).length;
262
+
263
+ return {
264
+ totalConfigs: Object.keys(SCHEMAS).length,
265
+ validConfigs: validCount,
266
+ results,
267
+ };
268
+ }
269
+
270
+ module.exports = {
271
+ validateConfig,
272
+ validateAllConfigs,
273
+ SCHEMAS,
274
+ };
@@ -0,0 +1,252 @@
1
+ /**
2
+ * Devran AI Kit — Agent Conflict Detection
3
+ *
4
+ * Tracks file ownership by agents and detects concurrent modifications.
5
+ * Uses JSON-based locks for cross-platform compatibility.
6
+ *
7
+ * @module lib/conflict-detector
8
+ * @author Emre Dursun
9
+ * @since v3.0.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ const fs = require('fs');
15
+ const path = require('path');
16
+
17
+ const { AGENT_DIR, ENGINE_DIR } = require('./constants');
18
+ const { writeJsonAtomic } = require('./io');
19
+ const FILE_LOCKS_FILE = 'file-locks.json';
20
+
21
+ /** Default lock TTL in milliseconds (30 minutes) */
22
+ const DEFAULT_LOCK_TTL_MS = 30 * 60 * 1000;
23
+
24
+ /**
25
+ * @typedef {object} FileLock
26
+ * @property {string} filePath - Relative path to the claimed file
27
+ * @property {string} agent - Agent name holding the lock
28
+ * @property {string} claimedAt - ISO timestamp
29
+ * @property {number} ttlMs - Time-to-live in milliseconds
30
+ */
31
+
32
+ /**
33
+ * @typedef {object} ConflictReport
34
+ * @property {string} filePath - Path with conflict
35
+ * @property {string[]} agents - Agents claiming this file
36
+ * @property {'warning' | 'blocking'} severity - Conflict severity
37
+ */
38
+
39
+ /**
40
+ * Resolves the file locks path.
41
+ *
42
+ * @param {string} projectRoot - Root directory of the project
43
+ * @returns {string}
44
+ */
45
+ function resolveLocksPath(projectRoot) {
46
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, FILE_LOCKS_FILE);
47
+ }
48
+
49
+ /**
50
+ * Loads current file locks, filtering out stale ones.
51
+ *
52
+ * @param {string} projectRoot - Root directory of the project
53
+ * @returns {FileLock[]}
54
+ */
55
+ function loadLocks(projectRoot) {
56
+ const locksPath = resolveLocksPath(projectRoot);
57
+
58
+ if (!fs.existsSync(locksPath)) {
59
+ return [];
60
+ }
61
+
62
+ try {
63
+ const data = JSON.parse(fs.readFileSync(locksPath, 'utf-8'));
64
+ const locks = data.locks || [];
65
+ const now = Date.now();
66
+
67
+ // Filter out expired locks
68
+ const activeLocks = locks.filter((lock) => {
69
+ const claimedTime = new Date(lock.claimedAt).getTime();
70
+ return (now - claimedTime) < (lock.ttlMs || DEFAULT_LOCK_TTL_MS);
71
+ });
72
+
73
+ // Persist pruned list if stale locks were removed
74
+ if (activeLocks.length < locks.length) {
75
+ writeLocks(projectRoot, activeLocks);
76
+ }
77
+
78
+ return activeLocks;
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Writes locks to disk atomically.
86
+ *
87
+ * @param {string} projectRoot - Root directory of the project
88
+ * @param {FileLock[]} locks - Current locks
89
+ * @returns {void}
90
+ */
91
+ function writeLocks(projectRoot, locks) {
92
+ const locksPath = resolveLocksPath(projectRoot);
93
+ const data = {
94
+ schemaVersion: '1.0.0',
95
+ lastUpdated: new Date().toISOString(),
96
+ locks,
97
+ };
98
+
99
+ writeJsonAtomic(locksPath, data);
100
+ }
101
+
102
+ /**
103
+ * Claims a file for an agent.
104
+ *
105
+ * @param {string} projectRoot - Root directory of the project
106
+ * @param {string} filePath - Relative path to file
107
+ * @param {string} agent - Agent name
108
+ * @param {number} [ttlMs] - Lock TTL in milliseconds
109
+ * @returns {{ success: boolean, conflict?: ConflictReport }}
110
+ */
111
+ function claimFile(projectRoot, filePath, agent, ttlMs) {
112
+ const locks = loadLocks(projectRoot);
113
+ const normalizedPath = filePath.replace(/\\/g, '/');
114
+ const lockTtl = ttlMs || DEFAULT_LOCK_TTL_MS;
115
+
116
+ // Check for existing claim by a different agent
117
+ const existingLock = locks.find((l) => l.filePath === normalizedPath && l.agent !== agent);
118
+
119
+ if (existingLock) {
120
+ return {
121
+ success: false,
122
+ conflict: {
123
+ filePath: normalizedPath,
124
+ agents: [existingLock.agent, agent],
125
+ severity: 'blocking',
126
+ },
127
+ };
128
+ }
129
+
130
+ // Update or create lock immutably
131
+ const existingIndex = locks.findIndex((l) => l.filePath === normalizedPath && l.agent === agent);
132
+ const now = new Date().toISOString();
133
+
134
+ let updatedLocks;
135
+ if (existingIndex !== -1) {
136
+ updatedLocks = locks.map((l, i) =>
137
+ i === existingIndex
138
+ ? { ...l, claimedAt: now, ttlMs: lockTtl }
139
+ : l
140
+ );
141
+ } else {
142
+ updatedLocks = [
143
+ ...locks,
144
+ {
145
+ filePath: normalizedPath,
146
+ agent,
147
+ claimedAt: now,
148
+ ttlMs: lockTtl,
149
+ },
150
+ ];
151
+ }
152
+
153
+ writeLocks(projectRoot, updatedLocks);
154
+ return { success: true };
155
+ }
156
+
157
+ /**
158
+ * Releases a file lock held by an agent.
159
+ *
160
+ * @param {string} projectRoot - Root directory of the project
161
+ * @param {string} filePath - Relative path to file
162
+ * @param {string} agent - Agent name
163
+ * @returns {{ success: boolean }}
164
+ */
165
+ function releaseFile(projectRoot, filePath, agent) {
166
+ const locks = loadLocks(projectRoot);
167
+ const normalizedPath = filePath.replace(/\\/g, '/');
168
+ const filteredLocks = locks.filter((l) => !(l.filePath === normalizedPath && l.agent === agent));
169
+
170
+ if (filteredLocks.length === locks.length) {
171
+ return { success: false };
172
+ }
173
+
174
+ writeLocks(projectRoot, filteredLocks);
175
+ return { success: true };
176
+ }
177
+
178
+ /**
179
+ * Detects all current file conflicts.
180
+ *
181
+ * @param {string} projectRoot - Root directory of the project
182
+ * @returns {ConflictReport[]}
183
+ */
184
+ function detectConflicts(projectRoot) {
185
+ const locks = loadLocks(projectRoot);
186
+ /** @type {Map<string, string[]>} */
187
+ const fileAgents = new Map();
188
+
189
+ for (const lock of locks) {
190
+ const agents = fileAgents.get(lock.filePath) || [];
191
+ if (!agents.includes(lock.agent)) {
192
+ agents.push(lock.agent);
193
+ }
194
+ fileAgents.set(lock.filePath, agents);
195
+ }
196
+
197
+ /** @type {ConflictReport[]} */
198
+ const conflicts = [];
199
+
200
+ for (const [filePath, agents] of fileAgents.entries()) {
201
+ if (agents.length > 1) {
202
+ conflicts.push({
203
+ filePath,
204
+ agents,
205
+ severity: agents.length > 2 ? 'blocking' : 'warning',
206
+ });
207
+ }
208
+ }
209
+
210
+ return conflicts;
211
+ }
212
+
213
+ /**
214
+ * Gets file ownership information.
215
+ *
216
+ * @param {string} projectRoot - Root directory of the project
217
+ * @returns {Array<{ filePath: string, agent: string, claimedAt: string }>}
218
+ */
219
+ function getFileOwnership(projectRoot) {
220
+ const locks = loadLocks(projectRoot);
221
+
222
+ return locks.map((lock) => ({
223
+ filePath: lock.filePath,
224
+ agent: lock.agent,
225
+ claimedAt: lock.claimedAt,
226
+ }));
227
+ }
228
+
229
+ /**
230
+ * Generates a full conflict report with metrics.
231
+ *
232
+ * @param {string} projectRoot - Root directory of the project
233
+ * @returns {{ activeLocks: number, conflicts: ConflictReport[], hasBlockingConflict: boolean }}
234
+ */
235
+ function reportConflicts(projectRoot) {
236
+ const locks = loadLocks(projectRoot);
237
+ const conflicts = detectConflicts(projectRoot);
238
+
239
+ return {
240
+ activeLocks: locks.length,
241
+ conflicts,
242
+ hasBlockingConflict: conflicts.some((c) => c.severity === 'blocking'),
243
+ };
244
+ }
245
+
246
+ module.exports = {
247
+ claimFile,
248
+ releaseFile,
249
+ detectConflicts,
250
+ getFileOwnership,
251
+ reportConflicts,
252
+ };
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Devran AI Kit — Shared Constants
3
+ *
4
+ * Central definition of directory names and paths used across
5
+ * all runtime modules. Prevents drift from duplicated strings.
6
+ * Frozen to prevent accidental mutation at runtime.
7
+ *
8
+ * @module lib/constants
9
+ * @since v3.2.0
10
+ */
11
+
12
+ 'use strict';
13
+
14
+ /** Root agent configuration directory name */
15
+ const AGENT_DIR = '.agent';
16
+
17
+ /** Engine subdirectory within .agent */
18
+ const ENGINE_DIR = 'engine';
19
+
20
+ /** Hooks subdirectory within .agent */
21
+ const HOOKS_DIR = 'hooks';
22
+
23
+ /** Skills subdirectory within .agent */
24
+ const SKILLS_DIR = 'skills';
25
+
26
+ /** Plugins subdirectory within .agent */
27
+ const PLUGINS_DIR = 'plugins';
28
+
29
+ /** Cursor IDE configuration directory */
30
+ const CURSOR_DIR = '.cursor';
31
+
32
+ /** OpenCode IDE configuration directory */
33
+ const OPENCODE_DIR = '.opencode';
34
+
35
+ /** Codex IDE configuration directory */
36
+ const CODEX_DIR = '.codex';
37
+
38
+ module.exports = Object.freeze({
39
+ AGENT_DIR,
40
+ ENGINE_DIR,
41
+ HOOKS_DIR,
42
+ SKILLS_DIR,
43
+ PLUGINS_DIR,
44
+ CURSOR_DIR,
45
+ OPENCODE_DIR,
46
+ CODEX_DIR,
47
+ });