guardrail-core 1.0.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 (189) hide show
  1. package/dist/__tests__/autopilot.test.d.ts +7 -0
  2. package/dist/__tests__/autopilot.test.d.ts.map +1 -0
  3. package/dist/__tests__/autopilot.test.js +156 -0
  4. package/dist/__tests__/tier-config.test.d.ts +9 -0
  5. package/dist/__tests__/tier-config.test.d.ts.map +1 -0
  6. package/dist/__tests__/tier-config.test.js +230 -0
  7. package/dist/__tests__/utils/hash-inline.test.d.ts +2 -0
  8. package/dist/__tests__/utils/hash-inline.test.d.ts.map +1 -0
  9. package/dist/__tests__/utils/hash-inline.test.js +62 -0
  10. package/dist/__tests__/utils/hash.test.d.ts +3 -0
  11. package/dist/__tests__/utils/hash.test.d.ts.map +1 -0
  12. package/dist/__tests__/utils/hash.test.js +95 -0
  13. package/dist/__tests__/utils/simple.test.d.ts +1 -0
  14. package/dist/__tests__/utils/simple.test.d.ts.map +1 -0
  15. package/dist/__tests__/utils/simple.test.js +10 -0
  16. package/dist/__tests__/utils/utils-simple.test.d.ts +1 -0
  17. package/dist/__tests__/utils/utils-simple.test.d.ts.map +1 -0
  18. package/dist/__tests__/utils/utils-simple.test.js +6 -0
  19. package/dist/__tests__/utils/utils.test.d.ts +15 -0
  20. package/dist/__tests__/utils/utils.test.d.ts.map +1 -0
  21. package/dist/__tests__/utils/utils.test.js +172 -0
  22. package/dist/autopilot/autopilot-runner.d.ts +33 -0
  23. package/dist/autopilot/autopilot-runner.d.ts.map +1 -0
  24. package/dist/autopilot/autopilot-runner.js +479 -0
  25. package/dist/autopilot/index.d.ts +6 -0
  26. package/dist/autopilot/index.d.ts.map +1 -0
  27. package/dist/autopilot/index.js +25 -0
  28. package/dist/autopilot/types.d.ts +102 -0
  29. package/dist/autopilot/types.d.ts.map +1 -0
  30. package/dist/autopilot/types.js +18 -0
  31. package/dist/cache/index.d.ts +7 -0
  32. package/dist/cache/index.d.ts.map +1 -0
  33. package/dist/cache/index.js +22 -0
  34. package/dist/cache/redis-cache.d.ts +145 -0
  35. package/dist/cache/redis-cache.d.ts.map +1 -0
  36. package/dist/cache/redis-cache.js +459 -0
  37. package/dist/ci/github-actions.d.ts +77 -0
  38. package/dist/ci/github-actions.d.ts.map +1 -0
  39. package/dist/ci/github-actions.js +277 -0
  40. package/dist/ci/index.d.ts +12 -0
  41. package/dist/ci/index.d.ts.map +1 -0
  42. package/dist/ci/index.js +27 -0
  43. package/dist/ci/pre-commit.d.ts +65 -0
  44. package/dist/ci/pre-commit.d.ts.map +1 -0
  45. package/dist/ci/pre-commit.js +286 -0
  46. package/dist/entitlements.d.ts +149 -0
  47. package/dist/entitlements.d.ts.map +1 -0
  48. package/dist/entitlements.js +464 -0
  49. package/dist/env.d.ts +113 -0
  50. package/dist/env.d.ts.map +1 -0
  51. package/dist/env.js +204 -0
  52. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts +7 -0
  53. package/dist/fix-packs/__tests__/generate-fix-packs.test.d.ts.map +1 -0
  54. package/dist/fix-packs/__tests__/generate-fix-packs.test.js +250 -0
  55. package/dist/fix-packs/generate-fix-packs.d.ts +15 -0
  56. package/dist/fix-packs/generate-fix-packs.d.ts.map +1 -0
  57. package/dist/fix-packs/generate-fix-packs.js +505 -0
  58. package/dist/fix-packs/index.d.ts +8 -0
  59. package/dist/fix-packs/index.d.ts.map +1 -0
  60. package/dist/fix-packs/index.js +23 -0
  61. package/dist/fix-packs/types.d.ts +113 -0
  62. package/dist/fix-packs/types.d.ts.map +1 -0
  63. package/dist/fix-packs/types.js +71 -0
  64. package/dist/index.d.ts +13 -0
  65. package/dist/index.d.ts.map +1 -0
  66. package/dist/index.js +28 -0
  67. package/dist/metrics/prometheus.d.ts +99 -0
  68. package/dist/metrics/prometheus.d.ts.map +1 -0
  69. package/dist/metrics/prometheus.js +306 -0
  70. package/dist/quota-ledger.d.ts +119 -0
  71. package/dist/quota-ledger.d.ts.map +1 -0
  72. package/dist/quota-ledger.js +462 -0
  73. package/dist/rbac/__tests__/permissions.test.d.ts +8 -0
  74. package/dist/rbac/__tests__/permissions.test.d.ts.map +1 -0
  75. package/dist/rbac/__tests__/permissions.test.js +350 -0
  76. package/dist/rbac/index.d.ts +9 -0
  77. package/dist/rbac/index.d.ts.map +1 -0
  78. package/dist/rbac/index.js +32 -0
  79. package/dist/rbac/permissions.d.ts +71 -0
  80. package/dist/rbac/permissions.d.ts.map +1 -0
  81. package/dist/rbac/permissions.js +247 -0
  82. package/dist/rbac/types.d.ts +69 -0
  83. package/dist/rbac/types.d.ts.map +1 -0
  84. package/dist/rbac/types.js +213 -0
  85. package/dist/tier-config.d.ts +203 -0
  86. package/dist/tier-config.d.ts.map +1 -0
  87. package/dist/tier-config.js +675 -0
  88. package/dist/types.d.ts +365 -0
  89. package/dist/types.d.ts.map +1 -0
  90. package/dist/types.js +5 -0
  91. package/dist/utils.d.ts +36 -0
  92. package/dist/utils.d.ts.map +1 -0
  93. package/dist/utils.js +127 -0
  94. package/dist/verified-autofix/__tests__/format-validator.test.d.ts +11 -0
  95. package/dist/verified-autofix/__tests__/format-validator.test.d.ts.map +1 -0
  96. package/dist/verified-autofix/__tests__/format-validator.test.js +285 -0
  97. package/dist/verified-autofix/__tests__/pipeline.test.d.ts +11 -0
  98. package/dist/verified-autofix/__tests__/pipeline.test.d.ts.map +1 -0
  99. package/dist/verified-autofix/__tests__/pipeline.test.js +389 -0
  100. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts +11 -0
  101. package/dist/verified-autofix/__tests__/repo-fingerprint.test.d.ts.map +1 -0
  102. package/dist/verified-autofix/__tests__/repo-fingerprint.test.js +236 -0
  103. package/dist/verified-autofix/__tests__/workspace.test.d.ts +11 -0
  104. package/dist/verified-autofix/__tests__/workspace.test.d.ts.map +1 -0
  105. package/dist/verified-autofix/__tests__/workspace.test.js +314 -0
  106. package/dist/verified-autofix/format-validator.d.ts +101 -0
  107. package/dist/verified-autofix/format-validator.d.ts.map +1 -0
  108. package/dist/verified-autofix/format-validator.js +446 -0
  109. package/dist/verified-autofix/index.d.ts +14 -0
  110. package/dist/verified-autofix/index.d.ts.map +1 -0
  111. package/dist/verified-autofix/index.js +39 -0
  112. package/dist/verified-autofix/pipeline.d.ts +68 -0
  113. package/dist/verified-autofix/pipeline.d.ts.map +1 -0
  114. package/dist/verified-autofix/pipeline.js +330 -0
  115. package/dist/verified-autofix/repo-fingerprint.d.ts +56 -0
  116. package/dist/verified-autofix/repo-fingerprint.d.ts.map +1 -0
  117. package/dist/verified-autofix/repo-fingerprint.js +396 -0
  118. package/dist/verified-autofix/workspace.d.ts +83 -0
  119. package/dist/verified-autofix/workspace.d.ts.map +1 -0
  120. package/dist/verified-autofix/workspace.js +454 -0
  121. package/dist/verified-autofix.d.ts +182 -0
  122. package/dist/verified-autofix.d.ts.map +1 -0
  123. package/dist/verified-autofix.js +1021 -0
  124. package/dist/visualization/dependency-graph.d.ts +79 -0
  125. package/dist/visualization/dependency-graph.d.ts.map +1 -0
  126. package/dist/visualization/dependency-graph.js +399 -0
  127. package/dist/visualization/index.d.ts +5 -0
  128. package/dist/visualization/index.d.ts.map +1 -0
  129. package/dist/visualization/index.js +20 -0
  130. package/package.json +29 -0
  131. package/src/__tests__/autopilot.test.ts +196 -0
  132. package/src/__tests__/tier-config.test.ts +289 -0
  133. package/src/__tests__/utils/hash-inline.test.ts +76 -0
  134. package/src/__tests__/utils/hash.test.ts +119 -0
  135. package/src/__tests__/utils/simple.test.ts +10 -0
  136. package/src/__tests__/utils/utils-simple.test.ts +5 -0
  137. package/src/__tests__/utils/utils.test.ts +203 -0
  138. package/src/autopilot/autopilot-runner.ts +503 -0
  139. package/src/autopilot/index.ts +6 -0
  140. package/src/autopilot/types.ts +119 -0
  141. package/src/cache/index.ts +7 -0
  142. package/src/cache/redis-cache.d.ts +155 -0
  143. package/src/cache/redis-cache.d.ts.map +1 -0
  144. package/src/cache/redis-cache.ts +517 -0
  145. package/src/ci/github-actions.ts +335 -0
  146. package/src/ci/index.ts +12 -0
  147. package/src/ci/pre-commit.ts +338 -0
  148. package/src/db/usage-schema.prisma +114 -0
  149. package/src/entitlements.ts +570 -0
  150. package/src/env.d.ts +68 -0
  151. package/src/env.d.ts.map +1 -0
  152. package/src/env.ts +247 -0
  153. package/src/fix-packs/__tests__/generate-fix-packs.test.ts +317 -0
  154. package/src/fix-packs/generate-fix-packs.ts +577 -0
  155. package/src/fix-packs/index.ts +8 -0
  156. package/src/fix-packs/types.ts +206 -0
  157. package/src/index.d.ts +7 -0
  158. package/src/index.d.ts.map +1 -0
  159. package/src/index.ts +12 -0
  160. package/src/metrics/prometheus.d.ts +104 -0
  161. package/src/metrics/prometheus.d.ts.map +1 -0
  162. package/src/metrics/prometheus.ts +446 -0
  163. package/src/quota-ledger.ts +548 -0
  164. package/src/rbac/__tests__/permissions.test.ts +446 -0
  165. package/src/rbac/index.ts +46 -0
  166. package/src/rbac/permissions.ts +301 -0
  167. package/src/rbac/types.ts +298 -0
  168. package/src/tier-config.json +157 -0
  169. package/src/tier-config.ts +815 -0
  170. package/src/types.d.ts +365 -0
  171. package/src/types.d.ts.map +1 -0
  172. package/src/types.ts +441 -0
  173. package/src/utils.d.ts +36 -0
  174. package/src/utils.d.ts.map +1 -0
  175. package/src/utils.ts +140 -0
  176. package/src/verified-autofix/__tests__/format-validator.test.ts +335 -0
  177. package/src/verified-autofix/__tests__/pipeline.test.ts +419 -0
  178. package/src/verified-autofix/__tests__/repo-fingerprint.test.ts +241 -0
  179. package/src/verified-autofix/__tests__/workspace.test.ts +373 -0
  180. package/src/verified-autofix/format-validator.ts +517 -0
  181. package/src/verified-autofix/index.ts +63 -0
  182. package/src/verified-autofix/pipeline.ts +403 -0
  183. package/src/verified-autofix/repo-fingerprint.ts +459 -0
  184. package/src/verified-autofix/workspace.ts +531 -0
  185. package/src/verified-autofix.ts +1187 -0
  186. package/src/visualization/dependency-graph.d.ts +85 -0
  187. package/src/visualization/dependency-graph.d.ts.map +1 -0
  188. package/src/visualization/dependency-graph.ts +495 -0
  189. package/src/visualization/index.ts +5 -0
@@ -0,0 +1,459 @@
1
+ /**
2
+ * Repo Fingerprint - Project Detection & Configuration
3
+ *
4
+ * Detects project characteristics:
5
+ * 1. Package manager (pnpm, yarn, npm)
6
+ * 2. Build tool (turbo, nx, none)
7
+ * 3. Framework (next, vite, cra, etc.)
8
+ * 4. Test runner (jest, vitest, mocha, etc.)
9
+ * 5. Language features (TypeScript, ESLint, etc.)
10
+ */
11
+
12
+ import * as fs from 'fs';
13
+ import * as path from 'path';
14
+
15
+ // ============================================================================
16
+ // TYPES
17
+ // ============================================================================
18
+
19
+ export type PackageManager = 'pnpm' | 'yarn' | 'npm' | 'bun' | 'unknown';
20
+ export type BuildTool = 'turbo' | 'nx' | 'lerna' | 'none';
21
+ export type Framework = 'next' | 'vite' | 'cra' | 'remix' | 'gatsby' | 'nuxt' | 'svelte' | 'angular' | 'express' | 'fastify' | 'none';
22
+ export type TestRunner = 'jest' | 'vitest' | 'mocha' | 'ava' | 'playwright' | 'cypress' | 'none';
23
+
24
+ export interface RepoFingerprint {
25
+ packageManager: PackageManager;
26
+ buildTool: BuildTool;
27
+ framework: Framework;
28
+ testRunner: TestRunner;
29
+ hasTypeScript: boolean;
30
+ hasESLint: boolean;
31
+ hasPrettier: boolean;
32
+ hasBuildScript: boolean;
33
+ hasTestScript: boolean;
34
+ isMonorepo: boolean;
35
+ workspaces: string[];
36
+ nodeVersion?: string;
37
+ dependencies: Record<string, string>;
38
+ devDependencies: Record<string, string>;
39
+ }
40
+
41
+ export interface FingerprintResult {
42
+ fingerprint: RepoFingerprint;
43
+ confidence: number;
44
+ detectionNotes: string[];
45
+ }
46
+
47
+ // ============================================================================
48
+ // DETECTION FUNCTIONS
49
+ // ============================================================================
50
+
51
+ /**
52
+ * Detect package manager from lock files
53
+ */
54
+ function detectPackageManager(projectPath: string): PackageManager {
55
+ if (fs.existsSync(path.join(projectPath, 'pnpm-lock.yaml'))) {
56
+ return 'pnpm';
57
+ }
58
+ if (fs.existsSync(path.join(projectPath, 'yarn.lock'))) {
59
+ return 'yarn';
60
+ }
61
+ if (fs.existsSync(path.join(projectPath, 'bun.lockb'))) {
62
+ return 'bun';
63
+ }
64
+ if (fs.existsSync(path.join(projectPath, 'package-lock.json'))) {
65
+ return 'npm';
66
+ }
67
+ return 'unknown';
68
+ }
69
+
70
+ /**
71
+ * Detect build tool (monorepo orchestrator)
72
+ */
73
+ function detectBuildTool(projectPath: string, pkg: PackageJson): BuildTool {
74
+ // Turbo
75
+ if (
76
+ fs.existsSync(path.join(projectPath, 'turbo.json')) ||
77
+ pkg.devDependencies?.['turbo'] ||
78
+ pkg.dependencies?.['turbo']
79
+ ) {
80
+ return 'turbo';
81
+ }
82
+
83
+ // Nx
84
+ if (
85
+ fs.existsSync(path.join(projectPath, 'nx.json')) ||
86
+ pkg.devDependencies?.['nx'] ||
87
+ pkg.dependencies?.['nx']
88
+ ) {
89
+ return 'nx';
90
+ }
91
+
92
+ // Lerna
93
+ if (
94
+ fs.existsSync(path.join(projectPath, 'lerna.json')) ||
95
+ pkg.devDependencies?.['lerna']
96
+ ) {
97
+ return 'lerna';
98
+ }
99
+
100
+ return 'none';
101
+ }
102
+
103
+ /**
104
+ * Detect framework from dependencies
105
+ */
106
+ function detectFramework(pkg: PackageJson): Framework {
107
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
108
+
109
+ // Next.js
110
+ if (deps['next']) {
111
+ return 'next';
112
+ }
113
+
114
+ // Remix
115
+ if (deps['@remix-run/react'] || deps['remix']) {
116
+ return 'remix';
117
+ }
118
+
119
+ // Gatsby
120
+ if (deps['gatsby']) {
121
+ return 'gatsby';
122
+ }
123
+
124
+ // Nuxt
125
+ if (deps['nuxt'] || deps['nuxt3']) {
126
+ return 'nuxt';
127
+ }
128
+
129
+ // SvelteKit
130
+ if (deps['@sveltejs/kit'] || deps['svelte']) {
131
+ return 'svelte';
132
+ }
133
+
134
+ // Angular
135
+ if (deps['@angular/core']) {
136
+ return 'angular';
137
+ }
138
+
139
+ // Vite (generic)
140
+ if (deps['vite']) {
141
+ return 'vite';
142
+ }
143
+
144
+ // Create React App
145
+ if (deps['react-scripts']) {
146
+ return 'cra';
147
+ }
148
+
149
+ // Express
150
+ if (deps['express']) {
151
+ return 'express';
152
+ }
153
+
154
+ // Fastify
155
+ if (deps['fastify']) {
156
+ return 'fastify';
157
+ }
158
+
159
+ return 'none';
160
+ }
161
+
162
+ /**
163
+ * Detect test runner
164
+ */
165
+ function detectTestRunner(projectPath: string, pkg: PackageJson): TestRunner {
166
+ const deps = { ...pkg.dependencies, ...pkg.devDependencies };
167
+
168
+ // Vitest
169
+ if (deps['vitest']) {
170
+ return 'vitest';
171
+ }
172
+
173
+ // Jest
174
+ if (deps['jest'] || fs.existsSync(path.join(projectPath, 'jest.config.js')) ||
175
+ fs.existsSync(path.join(projectPath, 'jest.config.ts'))) {
176
+ return 'jest';
177
+ }
178
+
179
+ // Mocha
180
+ if (deps['mocha']) {
181
+ return 'mocha';
182
+ }
183
+
184
+ // AVA
185
+ if (deps['ava']) {
186
+ return 'ava';
187
+ }
188
+
189
+ // Playwright
190
+ if (deps['@playwright/test'] || deps['playwright']) {
191
+ return 'playwright';
192
+ }
193
+
194
+ // Cypress
195
+ if (deps['cypress']) {
196
+ return 'cypress';
197
+ }
198
+
199
+ return 'none';
200
+ }
201
+
202
+ /**
203
+ * Detect if project is a monorepo
204
+ */
205
+ function detectMonorepo(projectPath: string, pkg: PackageJson): { isMonorepo: boolean; workspaces: string[] } {
206
+ // Check package.json workspaces
207
+ if (pkg.workspaces) {
208
+ const workspaces = Array.isArray(pkg.workspaces)
209
+ ? pkg.workspaces
210
+ : pkg.workspaces.packages || [];
211
+ return { isMonorepo: true, workspaces };
212
+ }
213
+
214
+ // Check pnpm-workspace.yaml
215
+ const pnpmWorkspacePath = path.join(projectPath, 'pnpm-workspace.yaml');
216
+ if (fs.existsSync(pnpmWorkspacePath)) {
217
+ try {
218
+ const content = fs.readFileSync(pnpmWorkspacePath, 'utf8');
219
+ const workspaces: string[] = [];
220
+ const matches = content.match(/packages:\s*\n((?:\s+-\s*['"]?[^\n]+['"]?\n?)+)/);
221
+ if (matches && matches[1]) {
222
+ const lines = matches[1].split('\n');
223
+ for (const line of lines) {
224
+ const match = line.match(/^\s*-\s*['"]?([^'"]+)['"]?/);
225
+ if (match && match[1]) {
226
+ workspaces.push(match[1]);
227
+ }
228
+ }
229
+ }
230
+ return { isMonorepo: true, workspaces };
231
+ } catch {
232
+ return { isMonorepo: true, workspaces: [] };
233
+ }
234
+ }
235
+
236
+ // Check for common monorepo directories
237
+ const monorepoIndicators = ['packages', 'apps', 'libs', 'modules'];
238
+ for (const dir of monorepoIndicators) {
239
+ const dirPath = path.join(projectPath, dir);
240
+ if (fs.existsSync(dirPath) && fs.statSync(dirPath).isDirectory()) {
241
+ // Check if any subdirectory has a package.json
242
+ try {
243
+ const subdirs = fs.readdirSync(dirPath);
244
+ for (const subdir of subdirs) {
245
+ if (fs.existsSync(path.join(dirPath, subdir, 'package.json'))) {
246
+ return { isMonorepo: true, workspaces: [`${dir}/*`] };
247
+ }
248
+ }
249
+ } catch {
250
+ // Ignore read errors
251
+ }
252
+ }
253
+ }
254
+
255
+ return { isMonorepo: false, workspaces: [] };
256
+ }
257
+
258
+ /**
259
+ * Read Node.js version from .nvmrc or similar
260
+ */
261
+ function readNodeVersion(projectPath: string): string | undefined {
262
+ const versionFiles = ['.nvmrc', '.node-version', '.tool-versions'];
263
+
264
+ for (const file of versionFiles) {
265
+ const filePath = path.join(projectPath, file);
266
+ if (fs.existsSync(filePath)) {
267
+ try {
268
+ const content = fs.readFileSync(filePath, 'utf8').trim();
269
+ if (file === '.tool-versions') {
270
+ const match = content.match(/nodejs\s+([^\s]+)/);
271
+ return match ? match[1] : undefined;
272
+ }
273
+ return content.replace(/^v/, '');
274
+ } catch {
275
+ // Ignore read errors
276
+ }
277
+ }
278
+ }
279
+
280
+ return undefined;
281
+ }
282
+
283
+ // ============================================================================
284
+ // TYPES
285
+ // ============================================================================
286
+
287
+ interface PackageJson {
288
+ name?: string;
289
+ scripts?: Record<string, string>;
290
+ dependencies?: Record<string, string>;
291
+ devDependencies?: Record<string, string>;
292
+ workspaces?: string[] | { packages: string[] };
293
+ }
294
+
295
+ // ============================================================================
296
+ // MAIN FINGERPRINT FUNCTION
297
+ // ============================================================================
298
+
299
+ /**
300
+ * Analyze a project and return its fingerprint
301
+ */
302
+ export function fingerprintRepo(projectPath: string): FingerprintResult {
303
+ const notes: string[] = [];
304
+ let confidence = 100;
305
+
306
+ // Read package.json
307
+ const pkgPath = path.join(projectPath, 'package.json');
308
+ let pkg: PackageJson = {};
309
+
310
+ if (fs.existsSync(pkgPath)) {
311
+ try {
312
+ pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
313
+ } catch (e) {
314
+ notes.push(`Failed to parse package.json: ${(e as Error).message}`);
315
+ confidence -= 20;
316
+ }
317
+ } else {
318
+ notes.push('No package.json found');
319
+ confidence -= 30;
320
+ }
321
+
322
+ const packageManager = detectPackageManager(projectPath);
323
+ const buildTool = detectBuildTool(projectPath, pkg);
324
+ const framework = detectFramework(pkg);
325
+ const testRunner = detectTestRunner(projectPath, pkg);
326
+ const { isMonorepo, workspaces } = detectMonorepo(projectPath, pkg);
327
+ const nodeVersion = readNodeVersion(projectPath);
328
+
329
+ // TypeScript detection
330
+ const hasTypeScript = !!(
331
+ fs.existsSync(path.join(projectPath, 'tsconfig.json')) ||
332
+ pkg.devDependencies?.['typescript'] ||
333
+ pkg.dependencies?.['typescript']
334
+ );
335
+
336
+ // ESLint detection
337
+ const hasESLint = !!(
338
+ fs.existsSync(path.join(projectPath, '.eslintrc.js')) ||
339
+ fs.existsSync(path.join(projectPath, '.eslintrc.json')) ||
340
+ fs.existsSync(path.join(projectPath, '.eslintrc.yml')) ||
341
+ fs.existsSync(path.join(projectPath, 'eslint.config.js')) ||
342
+ pkg.devDependencies?.['eslint']
343
+ );
344
+
345
+ // Prettier detection
346
+ const hasPrettier = !!(
347
+ fs.existsSync(path.join(projectPath, '.prettierrc')) ||
348
+ fs.existsSync(path.join(projectPath, '.prettierrc.js')) ||
349
+ fs.existsSync(path.join(projectPath, '.prettierrc.json')) ||
350
+ fs.existsSync(path.join(projectPath, 'prettier.config.js')) ||
351
+ pkg.devDependencies?.['prettier']
352
+ );
353
+
354
+ // Script detection
355
+ const hasBuildScript = !!(pkg.scripts?.['build']);
356
+ const hasTestScript = !!(pkg.scripts?.['test']);
357
+
358
+ // Add detection notes
359
+ if (packageManager === 'unknown') {
360
+ notes.push('Could not detect package manager');
361
+ confidence -= 10;
362
+ }
363
+
364
+ if (framework === 'none') {
365
+ notes.push('No major framework detected');
366
+ }
367
+
368
+ if (testRunner === 'none') {
369
+ notes.push('No test runner detected');
370
+ }
371
+
372
+ const fingerprint: RepoFingerprint = {
373
+ packageManager,
374
+ buildTool,
375
+ framework,
376
+ testRunner,
377
+ hasTypeScript,
378
+ hasESLint,
379
+ hasPrettier,
380
+ hasBuildScript,
381
+ hasTestScript,
382
+ isMonorepo,
383
+ workspaces,
384
+ nodeVersion,
385
+ dependencies: pkg.dependencies || {},
386
+ devDependencies: pkg.devDependencies || {},
387
+ };
388
+
389
+ return {
390
+ fingerprint,
391
+ confidence: Math.max(0, confidence),
392
+ detectionNotes: notes,
393
+ };
394
+ }
395
+
396
+ /**
397
+ * Get appropriate install command for package manager
398
+ */
399
+ export function getInstallCommand(packageManager: PackageManager): string {
400
+ switch (packageManager) {
401
+ case 'pnpm':
402
+ return 'pnpm install --frozen-lockfile';
403
+ case 'yarn':
404
+ return 'yarn install --frozen-lockfile';
405
+ case 'bun':
406
+ return 'bun install --frozen-lockfile';
407
+ case 'npm':
408
+ default:
409
+ return 'npm ci';
410
+ }
411
+ }
412
+
413
+ /**
414
+ * Get appropriate build command
415
+ */
416
+ export function getBuildCommand(fingerprint: RepoFingerprint): string {
417
+ if (fingerprint.buildTool === 'turbo') {
418
+ return 'npx turbo run build';
419
+ }
420
+ if (fingerprint.buildTool === 'nx') {
421
+ return 'npx nx run-many --target=build';
422
+ }
423
+ if (fingerprint.hasBuildScript) {
424
+ return 'npm run build';
425
+ }
426
+ return 'echo "No build script"';
427
+ }
428
+
429
+ /**
430
+ * Get appropriate test command
431
+ */
432
+ export function getTestCommand(fingerprint: RepoFingerprint): string {
433
+ switch (fingerprint.testRunner) {
434
+ case 'vitest':
435
+ return 'npx vitest run';
436
+ case 'jest':
437
+ return 'npx jest --passWithNoTests';
438
+ case 'mocha':
439
+ return 'npx mocha';
440
+ case 'ava':
441
+ return 'npx ava';
442
+ case 'playwright':
443
+ return 'npx playwright test';
444
+ case 'cypress':
445
+ return 'npx cypress run';
446
+ default:
447
+ return fingerprint.hasTestScript ? 'npm test' : 'echo "No test runner"';
448
+ }
449
+ }
450
+
451
+ /**
452
+ * Get typecheck command if TypeScript is present
453
+ */
454
+ export function getTypecheckCommand(fingerprint: RepoFingerprint): string | null {
455
+ if (!fingerprint.hasTypeScript) {
456
+ return null;
457
+ }
458
+ return 'npx tsc --noEmit';
459
+ }