@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,321 @@
1
+ /**
2
+ * Devran AI Kit — Skill Marketplace
3
+ *
4
+ * GitHub-based hybrid marketplace for discovering, installing,
5
+ * and managing community skills and plugins.
6
+ *
7
+ * @module lib/marketplace
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
+ const { execFileSync } = require('child_process');
17
+ const { createCircuitBreaker } = require('./circuit-breaker');
18
+ const { createRateLimiter } = require('./rate-limiter');
19
+
20
+ const { AGENT_DIR, ENGINE_DIR } = require('./constants');
21
+ const { writeJsonAtomic } = require('./io');
22
+ const { createLogger } = require('./logger');
23
+ const log = createLogger('marketplace');
24
+ const INDEX_FILE = 'marketplace-index.json';
25
+
26
+ /** Registry index TTL in milliseconds (24 hours) */
27
+ const INDEX_TTL_MS = 24 * 60 * 60 * 1000;
28
+
29
+ /** Git clone timeout in milliseconds */
30
+ const GIT_CLONE_TIMEOUT_MS = 30000;
31
+
32
+ /** Circuit breaker for git clone operations */
33
+ const gitCloneBreaker = createCircuitBreaker('marketplace-git-clone', {
34
+ failureThreshold: 3,
35
+ resetTimeoutMs: 120000,
36
+ });
37
+
38
+ /** Rate limiter for marketplace install operations (5 per minute) */
39
+ const installLimiter = createRateLimiter('marketplace-install', {
40
+ maxTokens: 5,
41
+ refillRateMs: 60000,
42
+ });
43
+
44
+ /**
45
+ * @typedef {object} MarketEntry
46
+ * @property {string} name - Plugin name
47
+ * @property {string} description - Plugin description
48
+ * @property {string} repository - GitHub repository URL
49
+ * @property {string} version - Latest version
50
+ * @property {string[]} tags - Discovery tags
51
+ * @property {string} author - Author name
52
+ */
53
+
54
+ /**
55
+ * Resolves the marketplace index path.
56
+ *
57
+ * @param {string} projectRoot - Root directory
58
+ * @returns {string}
59
+ */
60
+ function resolveIndexPath(projectRoot) {
61
+ return path.join(projectRoot, AGENT_DIR, ENGINE_DIR, INDEX_FILE);
62
+ }
63
+
64
+ /**
65
+ * Loads the marketplace index from disk.
66
+ *
67
+ * @param {string} projectRoot - Root directory
68
+ * @returns {{ entries: MarketEntry[], lastUpdated: string | null }}
69
+ */
70
+ function loadIndex(projectRoot) {
71
+ const filePath = resolveIndexPath(projectRoot);
72
+
73
+ if (!fs.existsSync(filePath)) {
74
+ return { entries: [], lastUpdated: null };
75
+ }
76
+
77
+ try {
78
+ return JSON.parse(fs.readFileSync(filePath, 'utf-8'));
79
+ } catch {
80
+ return { entries: [], lastUpdated: null };
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Writes the marketplace index atomically.
86
+ *
87
+ * @param {string} projectRoot - Root directory
88
+ * @param {{ entries: MarketEntry[], lastUpdated: string | null }} data
89
+ * @returns {void}
90
+ */
91
+ function writeIndex(projectRoot, data) {
92
+ const filePath = resolveIndexPath(projectRoot);
93
+ writeJsonAtomic(filePath, data);
94
+ }
95
+
96
+ /**
97
+ * Checks if the index is stale (older than TTL).
98
+ *
99
+ * @param {string | null} lastUpdated - ISO timestamp of last update
100
+ * @returns {boolean}
101
+ */
102
+ function isIndexStale(lastUpdated) {
103
+ if (!lastUpdated) {
104
+ return true;
105
+ }
106
+
107
+ const age = Date.now() - new Date(lastUpdated).getTime();
108
+ return age > INDEX_TTL_MS;
109
+ }
110
+
111
+ /**
112
+ * Validates file paths in a plugin manifest for path traversal (D-6).
113
+ *
114
+ * @param {object} manifest - Plugin manifest (plugin.json)
115
+ * @returns {{ valid: boolean, violations: string[] }}
116
+ */
117
+ function validateManifestPaths(manifest) {
118
+ const violations = [];
119
+
120
+ /** @type {string[]} */
121
+ const pathFields = [];
122
+
123
+ // Collect all file path references from manifest
124
+ if (manifest.file) {
125
+ pathFields.push(manifest.file);
126
+ }
127
+ if (manifest.files && Array.isArray(manifest.files)) {
128
+ pathFields.push(...manifest.files);
129
+ }
130
+ if (manifest.entry) {
131
+ pathFields.push(manifest.entry);
132
+ }
133
+
134
+ for (const filePath of pathFields) {
135
+ if (typeof filePath !== 'string') {
136
+ violations.push(`Invalid path type: ${typeof filePath}`);
137
+ continue;
138
+ }
139
+
140
+ // Reject absolute paths
141
+ if (path.isAbsolute(filePath)) {
142
+ violations.push(`Absolute path not allowed: ${filePath}`);
143
+ continue;
144
+ }
145
+
146
+ // Reject path traversal
147
+ if (filePath.includes('..')) {
148
+ violations.push(`Path traversal not allowed: ${filePath}`);
149
+ continue;
150
+ }
151
+
152
+ // Reject paths that escape .agent/
153
+ const normalized = path.normalize(filePath);
154
+ if (normalized.startsWith('..') || path.isAbsolute(normalized)) {
155
+ violations.push(`Path escapes sandbox: ${filePath}`);
156
+ }
157
+ }
158
+
159
+ return {
160
+ valid: violations.length === 0,
161
+ violations,
162
+ };
163
+ }
164
+
165
+ /**
166
+ * Searches the marketplace index.
167
+ *
168
+ * @param {string} projectRoot - Root directory
169
+ * @param {string} query - Search query
170
+ * @returns {MarketEntry[]}
171
+ */
172
+ function searchMarket(projectRoot, query) {
173
+ const index = loadIndex(projectRoot);
174
+ const search = query.toLowerCase();
175
+
176
+ return index.entries.filter((entry) => {
177
+ const searchable = [
178
+ entry.name,
179
+ entry.description,
180
+ entry.author,
181
+ ...(entry.tags || []),
182
+ ].join(' ').toLowerCase();
183
+
184
+ return searchable.includes(search);
185
+ });
186
+ }
187
+
188
+ /**
189
+ * Gets detailed info for a specific marketplace entry.
190
+ *
191
+ * @param {string} projectRoot - Root directory
192
+ * @param {string} pluginName - Plugin name
193
+ * @returns {MarketEntry | null}
194
+ */
195
+ function getMarketInfo(projectRoot, pluginName) {
196
+ const index = loadIndex(projectRoot);
197
+ return index.entries.find((e) => e.name === pluginName) || null;
198
+ }
199
+
200
+ /**
201
+ * Installs a plugin from the marketplace via git clone.
202
+ * Validates paths before installation.
203
+ *
204
+ * @param {string} projectRoot - Root directory
205
+ * @param {string} pluginName - Plugin name from the index
206
+ * @returns {{ success: boolean, message: string }}
207
+ */
208
+ function installFromMarket(projectRoot, pluginName) {
209
+ const entry = getMarketInfo(projectRoot, pluginName);
210
+
211
+ if (!entry) {
212
+ return { success: false, message: `Plugin not found in marketplace: ${pluginName}` };
213
+ }
214
+
215
+ // Validate repository URL
216
+ if (!entry.repository || (!entry.repository.startsWith('https://') && !entry.repository.startsWith('git@'))) {
217
+ return { success: false, message: `Invalid repository URL: ${entry.repository}` };
218
+ }
219
+
220
+ // Rate limit check
221
+ const rateCheck = installLimiter.tryAcquire();
222
+ if (!rateCheck.allowed) {
223
+ return {
224
+ success: false,
225
+ message: `Rate limit exceeded — retry after ${Math.ceil(rateCheck.retryAfterMs / 1000)}s`,
226
+ };
227
+ }
228
+
229
+ // Create temp directory for clone
230
+ const tempDir = path.join(projectRoot, AGENT_DIR, ENGINE_DIR, `_temp_${Date.now()}`);
231
+
232
+ try {
233
+ fs.mkdirSync(tempDir, { recursive: true });
234
+
235
+ // Shallow clone with circuit breaker and timeout (uses execFileSync to prevent shell injection)
236
+ try {
237
+ gitCloneBreaker.execute(() => {
238
+ execFileSync(
239
+ 'git',
240
+ ['clone', '--depth', '1', '--single-branch', entry.repository, tempDir],
241
+ { timeout: GIT_CLONE_TIMEOUT_MS, stdio: 'pipe' }
242
+ );
243
+ });
244
+ } catch (gitError) {
245
+ return { success: false, message: `Git clone failed: ${gitError.message || 'timeout or network error'}` };
246
+ }
247
+
248
+ // Validate plugin.json exists
249
+ const pluginJsonPath = path.join(tempDir, 'plugin.json');
250
+ if (!fs.existsSync(pluginJsonPath)) {
251
+ return { success: false, message: 'Plugin missing plugin.json manifest' };
252
+ }
253
+
254
+ // Parse and validate manifest paths (D-6)
255
+ let manifest;
256
+ try {
257
+ manifest = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf-8'));
258
+ } catch {
259
+ return { success: false, message: 'Invalid plugin.json format' };
260
+ }
261
+
262
+ const pathValidation = validateManifestPaths(manifest);
263
+ if (!pathValidation.valid) {
264
+ return {
265
+ success: false,
266
+ message: `Security violation — path traversal detected: ${pathValidation.violations.join('; ')}`,
267
+ };
268
+ }
269
+
270
+ // Delegate to plugin system for actual installation
271
+ try {
272
+ const pluginSystem = require('./plugin-system');
273
+ const result = pluginSystem.installPlugin(tempDir, projectRoot);
274
+ const message = result.errors?.length > 0
275
+ ? result.errors.join('; ')
276
+ : 'Plugin installed successfully';
277
+ return { success: result.success, message };
278
+ } catch (installError) {
279
+ return { success: false, message: `Plugin installation failed: ${installError.message}` };
280
+ }
281
+ } finally {
282
+ // Always cleanup temp directory
283
+ try {
284
+ fs.rmSync(tempDir, { recursive: true, force: true });
285
+ } catch {
286
+ // Cleanup failure is non-critical
287
+ }
288
+ }
289
+ }
290
+
291
+ /**
292
+ * Updates the registry index from the bundled source.
293
+ *
294
+ * @param {string} projectRoot - Root directory
295
+ * @param {object} [options] - Update options
296
+ * @param {boolean} [options.force] - Force update regardless of TTL
297
+ * @returns {{ updated: boolean, entryCount: number }}
298
+ */
299
+ function updateRegistryIndex(projectRoot, options = {}) {
300
+ const currentIndex = loadIndex(projectRoot);
301
+
302
+ if (!options.force && !isIndexStale(currentIndex.lastUpdated)) {
303
+ return { updated: false, entryCount: currentIndex.entries.length };
304
+ }
305
+
306
+ // For MVP, the index is bundled — just stamp the update time
307
+ currentIndex.lastUpdated = new Date().toISOString();
308
+ writeIndex(projectRoot, currentIndex);
309
+
310
+ return { updated: true, entryCount: currentIndex.entries.length };
311
+ }
312
+
313
+ module.exports = {
314
+ searchMarket,
315
+ getMarketInfo,
316
+ installFromMarket,
317
+ updateRegistryIndex,
318
+ // Exported for testing
319
+ validateManifestPaths,
320
+ isIndexStale,
321
+ };