tlc-claude-code 1.4.8 → 1.4.9

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 (169) hide show
  1. package/package.json +1 -1
  2. package/server/index.js +229 -14
  3. package/server/lib/compliance/control-mapper.js +401 -0
  4. package/server/lib/compliance/control-mapper.test.js +117 -0
  5. package/server/lib/compliance/evidence-linker.js +296 -0
  6. package/server/lib/compliance/evidence-linker.test.js +121 -0
  7. package/server/lib/compliance/gdpr-checklist.js +416 -0
  8. package/server/lib/compliance/gdpr-checklist.test.js +131 -0
  9. package/server/lib/compliance/hipaa-checklist.js +277 -0
  10. package/server/lib/compliance/hipaa-checklist.test.js +101 -0
  11. package/server/lib/compliance/iso27001-checklist.js +287 -0
  12. package/server/lib/compliance/iso27001-checklist.test.js +99 -0
  13. package/server/lib/compliance/multi-framework-reporter.js +284 -0
  14. package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
  15. package/server/lib/compliance/pci-dss-checklist.js +214 -0
  16. package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
  17. package/server/lib/compliance/trust-centre.js +187 -0
  18. package/server/lib/compliance/trust-centre.test.js +93 -0
  19. package/server/lib/dashboard/api-server.js +155 -0
  20. package/server/lib/dashboard/api-server.test.js +155 -0
  21. package/server/lib/dashboard/health-api.js +199 -0
  22. package/server/lib/dashboard/health-api.test.js +122 -0
  23. package/server/lib/dashboard/notes-api.js +234 -0
  24. package/server/lib/dashboard/notes-api.test.js +134 -0
  25. package/server/lib/dashboard/router-api.js +176 -0
  26. package/server/lib/dashboard/router-api.test.js +132 -0
  27. package/server/lib/dashboard/tasks-api.js +289 -0
  28. package/server/lib/dashboard/tasks-api.test.js +161 -0
  29. package/server/lib/dashboard/tlc-introspection.js +197 -0
  30. package/server/lib/dashboard/tlc-introspection.test.js +138 -0
  31. package/server/lib/dashboard/version-api.js +222 -0
  32. package/server/lib/dashboard/version-api.test.js +112 -0
  33. package/server/lib/dashboard/websocket-server.js +104 -0
  34. package/server/lib/dashboard/websocket-server.test.js +118 -0
  35. package/server/lib/deploy/branch-classifier.js +163 -0
  36. package/server/lib/deploy/branch-classifier.test.js +164 -0
  37. package/server/lib/deploy/deployment-approval.js +299 -0
  38. package/server/lib/deploy/deployment-approval.test.js +296 -0
  39. package/server/lib/deploy/deployment-audit.js +374 -0
  40. package/server/lib/deploy/deployment-audit.test.js +307 -0
  41. package/server/lib/deploy/deployment-executor.js +335 -0
  42. package/server/lib/deploy/deployment-executor.test.js +329 -0
  43. package/server/lib/deploy/deployment-rules.js +163 -0
  44. package/server/lib/deploy/deployment-rules.test.js +188 -0
  45. package/server/lib/deploy/rollback-manager.js +379 -0
  46. package/server/lib/deploy/rollback-manager.test.js +321 -0
  47. package/server/lib/deploy/security-gates.js +236 -0
  48. package/server/lib/deploy/security-gates.test.js +222 -0
  49. package/server/lib/k8s/gitops-config.js +188 -0
  50. package/server/lib/k8s/gitops-config.test.js +59 -0
  51. package/server/lib/k8s/helm-generator.js +196 -0
  52. package/server/lib/k8s/helm-generator.test.js +59 -0
  53. package/server/lib/k8s/kustomize-generator.js +176 -0
  54. package/server/lib/k8s/kustomize-generator.test.js +58 -0
  55. package/server/lib/k8s/network-policy.js +114 -0
  56. package/server/lib/k8s/network-policy.test.js +53 -0
  57. package/server/lib/k8s/pod-security.js +114 -0
  58. package/server/lib/k8s/pod-security.test.js +55 -0
  59. package/server/lib/k8s/rbac-generator.js +132 -0
  60. package/server/lib/k8s/rbac-generator.test.js +57 -0
  61. package/server/lib/k8s/resource-manager.js +172 -0
  62. package/server/lib/k8s/resource-manager.test.js +60 -0
  63. package/server/lib/k8s/secrets-encryption.js +168 -0
  64. package/server/lib/k8s/secrets-encryption.test.js +49 -0
  65. package/server/lib/monitoring/alert-manager.js +238 -0
  66. package/server/lib/monitoring/alert-manager.test.js +106 -0
  67. package/server/lib/monitoring/health-check.js +226 -0
  68. package/server/lib/monitoring/health-check.test.js +176 -0
  69. package/server/lib/monitoring/incident-manager.js +230 -0
  70. package/server/lib/monitoring/incident-manager.test.js +98 -0
  71. package/server/lib/monitoring/log-aggregator.js +147 -0
  72. package/server/lib/monitoring/log-aggregator.test.js +89 -0
  73. package/server/lib/monitoring/metrics-collector.js +337 -0
  74. package/server/lib/monitoring/metrics-collector.test.js +172 -0
  75. package/server/lib/monitoring/status-page.js +214 -0
  76. package/server/lib/monitoring/status-page.test.js +105 -0
  77. package/server/lib/monitoring/uptime-monitor.js +194 -0
  78. package/server/lib/monitoring/uptime-monitor.test.js +109 -0
  79. package/server/lib/network/fail2ban-config.js +294 -0
  80. package/server/lib/network/fail2ban-config.test.js +275 -0
  81. package/server/lib/network/firewall-manager.js +252 -0
  82. package/server/lib/network/firewall-manager.test.js +254 -0
  83. package/server/lib/network/geoip-filter.js +282 -0
  84. package/server/lib/network/geoip-filter.test.js +264 -0
  85. package/server/lib/network/rate-limiter.js +229 -0
  86. package/server/lib/network/rate-limiter.test.js +293 -0
  87. package/server/lib/network/request-validator.js +351 -0
  88. package/server/lib/network/request-validator.test.js +345 -0
  89. package/server/lib/network/security-headers.js +251 -0
  90. package/server/lib/network/security-headers.test.js +283 -0
  91. package/server/lib/network/tls-config.js +210 -0
  92. package/server/lib/network/tls-config.test.js +248 -0
  93. package/server/lib/security/auth-security.js +369 -0
  94. package/server/lib/security/auth-security.test.js +448 -0
  95. package/server/lib/security/cis-benchmark.js +152 -0
  96. package/server/lib/security/cis-benchmark.test.js +137 -0
  97. package/server/lib/security/compose-templates.js +312 -0
  98. package/server/lib/security/compose-templates.test.js +229 -0
  99. package/server/lib/security/container-runtime.js +456 -0
  100. package/server/lib/security/container-runtime.test.js +503 -0
  101. package/server/lib/security/cors-validator.js +278 -0
  102. package/server/lib/security/cors-validator.test.js +310 -0
  103. package/server/lib/security/crypto-utils.js +253 -0
  104. package/server/lib/security/crypto-utils.test.js +409 -0
  105. package/server/lib/security/dockerfile-linter.js +459 -0
  106. package/server/lib/security/dockerfile-linter.test.js +483 -0
  107. package/server/lib/security/dockerfile-templates.js +278 -0
  108. package/server/lib/security/dockerfile-templates.test.js +164 -0
  109. package/server/lib/security/error-sanitizer.js +426 -0
  110. package/server/lib/security/error-sanitizer.test.js +331 -0
  111. package/server/lib/security/headers-generator.js +368 -0
  112. package/server/lib/security/headers-generator.test.js +398 -0
  113. package/server/lib/security/image-scanner.js +83 -0
  114. package/server/lib/security/image-scanner.test.js +106 -0
  115. package/server/lib/security/input-validator.js +352 -0
  116. package/server/lib/security/input-validator.test.js +330 -0
  117. package/server/lib/security/network-policy.js +174 -0
  118. package/server/lib/security/network-policy.test.js +164 -0
  119. package/server/lib/security/output-encoder.js +237 -0
  120. package/server/lib/security/output-encoder.test.js +276 -0
  121. package/server/lib/security/path-validator.js +359 -0
  122. package/server/lib/security/path-validator.test.js +293 -0
  123. package/server/lib/security/query-builder.js +421 -0
  124. package/server/lib/security/query-builder.test.js +318 -0
  125. package/server/lib/security/secret-detector.js +290 -0
  126. package/server/lib/security/secret-detector.test.js +354 -0
  127. package/server/lib/security/secrets-validator.js +137 -0
  128. package/server/lib/security/secrets-validator.test.js +120 -0
  129. package/server/lib/security-testing/dast-runner.js +154 -0
  130. package/server/lib/security-testing/dast-runner.test.js +62 -0
  131. package/server/lib/security-testing/dependency-scanner.js +172 -0
  132. package/server/lib/security-testing/dependency-scanner.test.js +64 -0
  133. package/server/lib/security-testing/pentest-runner.js +230 -0
  134. package/server/lib/security-testing/pentest-runner.test.js +60 -0
  135. package/server/lib/security-testing/sast-runner.js +136 -0
  136. package/server/lib/security-testing/sast-runner.test.js +62 -0
  137. package/server/lib/security-testing/secret-scanner.js +153 -0
  138. package/server/lib/security-testing/secret-scanner.test.js +66 -0
  139. package/server/lib/security-testing/security-gate.js +216 -0
  140. package/server/lib/security-testing/security-gate.test.js +115 -0
  141. package/server/lib/security-testing/security-reporter.js +303 -0
  142. package/server/lib/security-testing/security-reporter.test.js +114 -0
  143. package/server/lib/standards/audit-checker.js +546 -0
  144. package/server/lib/standards/audit-checker.test.js +415 -0
  145. package/server/lib/standards/cleanup-executor.js +452 -0
  146. package/server/lib/standards/cleanup-executor.test.js +293 -0
  147. package/server/lib/standards/refactor-stepper.js +425 -0
  148. package/server/lib/standards/refactor-stepper.test.js +298 -0
  149. package/server/lib/standards/standards-injector.js +167 -0
  150. package/server/lib/standards/standards-injector.test.js +232 -0
  151. package/server/lib/user-management.test.js +284 -0
  152. package/server/lib/vps/backup-manager.js +157 -0
  153. package/server/lib/vps/backup-manager.test.js +59 -0
  154. package/server/lib/vps/caddy-config.js +159 -0
  155. package/server/lib/vps/caddy-config.test.js +48 -0
  156. package/server/lib/vps/compose-orchestrator.js +219 -0
  157. package/server/lib/vps/compose-orchestrator.test.js +50 -0
  158. package/server/lib/vps/database-config.js +208 -0
  159. package/server/lib/vps/database-config.test.js +47 -0
  160. package/server/lib/vps/deploy-script.js +211 -0
  161. package/server/lib/vps/deploy-script.test.js +53 -0
  162. package/server/lib/vps/secrets-manager.js +148 -0
  163. package/server/lib/vps/secrets-manager.test.js +58 -0
  164. package/server/lib/vps/server-hardening.js +174 -0
  165. package/server/lib/vps/server-hardening.test.js +70 -0
  166. package/server/package-lock.json +19 -0
  167. package/server/package.json +1 -0
  168. package/server/templates/CLAUDE.md +37 -0
  169. package/server/templates/CODING-STANDARDS.md +408 -0
@@ -0,0 +1,546 @@
1
+ /**
2
+ * Audit Checker Module
3
+ *
4
+ * Checks project structure and code for TLC standards compliance.
5
+ * @module audit-checker
6
+ */
7
+
8
+ const path = require('path');
9
+ const fs = require('fs').promises;
10
+
11
+ // Try to load glob, but make it optional (tests use mocks)
12
+ let defaultGlob;
13
+ try {
14
+ defaultGlob = require('glob').glob;
15
+ } catch {
16
+ defaultGlob = null;
17
+ }
18
+
19
+ /**
20
+ * Check that CLAUDE.md and CODING-STANDARDS.md exist
21
+ * @param {string} projectPath - Path to project root
22
+ * @param {Object} options - Options with injectable dependencies
23
+ * @param {Object} options.fs - File system module (for testing)
24
+ * @returns {Promise<{passed: boolean, issues: Array<{file: string, type: string}>}>}
25
+ */
26
+ async function checkStandardsFiles(projectPath, options = {}) {
27
+ const fsModule = options.fs || fs;
28
+ const issues = [];
29
+ const requiredFiles = ['CLAUDE.md', 'CODING-STANDARDS.md'];
30
+
31
+ for (const file of requiredFiles) {
32
+ const filePath = path.join(projectPath, file);
33
+ try {
34
+ await fsModule.access(filePath);
35
+ } catch {
36
+ issues.push({ file, type: 'missing' });
37
+ }
38
+ }
39
+
40
+ return {
41
+ passed: issues.length === 0,
42
+ issues
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Detect flat services/, interfaces/, controllers/ folders
48
+ * @param {string} projectPath - Path to project root
49
+ * @param {Object} options - Options with injectable dependencies
50
+ * @param {Function} options.glob - Glob function (for testing)
51
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, folder: string}>}>}
52
+ */
53
+ async function checkFlatFolders(projectPath, options = {}) {
54
+ const globFn = options.glob || defaultGlob;
55
+ const issues = [];
56
+ const flatFolders = ['services', 'interfaces', 'controllers'];
57
+
58
+ for (const folder of flatFolders) {
59
+ const pattern = `src/${folder}/*.*`;
60
+ const files = await globFn(pattern, { cwd: projectPath });
61
+ if (files.length > 0) {
62
+ issues.push({ type: 'flat-folder', folder });
63
+ }
64
+ }
65
+
66
+ return {
67
+ passed: issues.length === 0,
68
+ issues
69
+ };
70
+ }
71
+
72
+ /**
73
+ * Detect inline interfaces in service files
74
+ * @param {string} projectPath - Path to project root
75
+ * @param {Object} options - Options with injectable dependencies
76
+ * @param {Function} options.glob - Glob function (for testing)
77
+ * @param {Function} options.readFile - File read function (for testing)
78
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, file: string}>}>}
79
+ */
80
+ async function checkInlineInterfaces(projectPath, options = {}) {
81
+ const globFn = options.glob || defaultGlob;
82
+ const readFileFn = options.readFile || (async (p) => fs.readFile(p, 'utf-8'));
83
+ const issues = [];
84
+
85
+ const pattern = '**/*.service.ts';
86
+ const files = await globFn(pattern, { cwd: projectPath });
87
+
88
+ for (const file of files) {
89
+ const filePath = path.join(projectPath, file);
90
+ const content = await readFileFn(filePath);
91
+
92
+ // Look for interface declarations (interface X {)
93
+ const interfacePattern = /\binterface\s+\w+\s*\{/;
94
+ if (interfacePattern.test(content)) {
95
+ issues.push({ type: 'inline-interface', file });
96
+ }
97
+ }
98
+
99
+ return {
100
+ passed: issues.length === 0,
101
+ issues
102
+ };
103
+ }
104
+
105
+ /**
106
+ * Detect hardcoded URLs and ports in code
107
+ * @param {string} projectPath - Path to project root
108
+ * @param {Object} options - Options with injectable dependencies
109
+ * @param {Function} options.glob - Glob function (for testing)
110
+ * @param {Function} options.readFile - File read function (for testing)
111
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, file: string, value?: string}>}>}
112
+ */
113
+ async function checkHardcodedUrls(projectPath, options = {}) {
114
+ const globFn = options.glob || defaultGlob;
115
+ const readFileFn = options.readFile || (async (p) => fs.readFile(p, 'utf-8'));
116
+ const issues = [];
117
+
118
+ const pattern = 'src/**/*.{js,ts,jsx,tsx}';
119
+ const files = await globFn(pattern, { cwd: projectPath });
120
+
121
+ for (const file of files) {
122
+ const filePath = path.join(projectPath, file);
123
+ const content = await readFileFn(filePath);
124
+
125
+ // Check for hardcoded URLs (http:// or https://)
126
+ const urlPattern = /['"`](https?:\/\/[^'"`]+)['"`]/g;
127
+ let urlMatch;
128
+ while ((urlMatch = urlPattern.exec(content)) !== null) {
129
+ issues.push({
130
+ type: 'hardcoded-url',
131
+ file,
132
+ value: urlMatch[1]
133
+ });
134
+ }
135
+
136
+ // Check for hardcoded port assignments (const port = 3000)
137
+ const portPattern = /\b(const|let|var)\s+port\s*=\s*(\d+)/g;
138
+ let portMatch;
139
+ while ((portMatch = portPattern.exec(content)) !== null) {
140
+ issues.push({
141
+ type: 'hardcoded-port',
142
+ file,
143
+ value: portMatch[2]
144
+ });
145
+ }
146
+ }
147
+
148
+ return {
149
+ passed: issues.length === 0,
150
+ issues
151
+ };
152
+ }
153
+
154
+ /**
155
+ * Detect magic strings (string comparisons like === 'active')
156
+ * @param {string} projectPath - Path to project root
157
+ * @param {Object} options - Options with injectable dependencies
158
+ * @param {Function} options.glob - Glob function (for testing)
159
+ * @param {Function} options.readFile - File read function (for testing)
160
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, file: string, value: string}>}>}
161
+ */
162
+ async function checkMagicStrings(projectPath, options = {}) {
163
+ const globFn = options.glob || defaultGlob;
164
+ const readFileFn = options.readFile || (async (p) => fs.readFile(p, 'utf-8'));
165
+ const issues = [];
166
+
167
+ // Common exceptions that are acceptable
168
+ const exceptions = [
169
+ 'utf-8', 'utf8', 'utf-16', 'ascii', 'base64', 'hex', 'binary',
170
+ 'application/json', 'text/html', 'text/plain',
171
+ 'GET', 'POST', 'PUT', 'DELETE', 'PATCH',
172
+ 'true', 'false', 'null', 'undefined',
173
+ 'development', 'production', 'test'
174
+ ];
175
+
176
+ const pattern = 'src/**/*.{js,ts,jsx,tsx}';
177
+ const files = await globFn(pattern, { cwd: projectPath });
178
+
179
+ for (const file of files) {
180
+ // Skip test files
181
+ if (file.includes('.test.') || file.includes('.spec.') || file.includes('__tests__')) {
182
+ continue;
183
+ }
184
+
185
+ const filePath = path.join(projectPath, file);
186
+ const content = await readFileFn(filePath);
187
+
188
+ // Skip import/require statements
189
+ const lines = content.split('\n');
190
+ for (const line of lines) {
191
+ // Skip import/require lines
192
+ if (line.trim().startsWith('import') || line.includes('require(')) {
193
+ continue;
194
+ }
195
+
196
+ // Look for string comparisons: === 'value' or == 'value'
197
+ const magicPattern = /===?\s*['"]([^'"]+)['"]/g;
198
+ let match;
199
+ while ((match = magicPattern.exec(line)) !== null) {
200
+ const value = match[1];
201
+ // Skip exceptions
202
+ if (!exceptions.includes(value.toLowerCase())) {
203
+ issues.push({
204
+ type: 'magic-string',
205
+ file,
206
+ value
207
+ });
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ return {
214
+ passed: issues.length === 0,
215
+ issues
216
+ };
217
+ }
218
+
219
+ /**
220
+ * Check seed files are organized per-entity (not in flat src/seeds/)
221
+ * @param {string} projectPath - Path to project root
222
+ * @param {Object} options - Options with injectable dependencies
223
+ * @param {Function} options.glob - Glob function (for testing)
224
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string}>}>}
225
+ */
226
+ async function checkSeedOrganization(projectPath, options = {}) {
227
+ const globFn = options.glob || defaultGlob;
228
+ const issues = [];
229
+
230
+ // Check for flat seeds/ folder at src/seeds/
231
+ // Pattern matches direct files in src/seeds/ (not nested in entity folders)
232
+ const flatSeedsPattern = 'src/seeds/*.*';
233
+ const files = await globFn(flatSeedsPattern, { cwd: projectPath });
234
+
235
+ // Filter to only files actually in src/seeds/ (not entity/seeds/)
236
+ const flatSeeds = files.filter(file => {
237
+ // Normalize path separators
238
+ const normalized = file.replace(/\\/g, '/');
239
+ // Check if file is directly in src/seeds/ (e.g., src/seeds/userSeed.ts)
240
+ // Not in src/{entity}/seeds/ (e.g., src/user/seeds/user.seed.ts)
241
+ return normalized.match(/^src\/seeds\/[^/]+$/);
242
+ });
243
+
244
+ if (flatSeeds.length > 0) {
245
+ issues.push({ type: 'flat-seeds' });
246
+ }
247
+
248
+ return {
249
+ passed: issues.length === 0,
250
+ issues
251
+ };
252
+ }
253
+
254
+ /**
255
+ * Check exported functions have JSDoc comments
256
+ * @param {string} projectPath - Path to project root
257
+ * @param {Object} options - Options with injectable dependencies
258
+ * @param {Function} options.glob - Glob function (for testing)
259
+ * @param {Function} options.readFile - File read function (for testing)
260
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, file: string, function: string}>}>}
261
+ */
262
+ async function checkJsDocCoverage(projectPath, options = {}) {
263
+ const globFn = options.glob || defaultGlob;
264
+ const readFileFn = options.readFile || (async (p) => fs.readFile(p, 'utf-8'));
265
+ const issues = [];
266
+
267
+ const pattern = 'src/**/*.{js,ts}';
268
+ const files = await globFn(pattern, { cwd: projectPath });
269
+
270
+ for (const file of files) {
271
+ const filePath = path.join(projectPath, file);
272
+ const content = await readFileFn(filePath);
273
+ const lines = content.split('\n');
274
+
275
+ for (let i = 0; i < lines.length; i++) {
276
+ const line = lines[i];
277
+
278
+ // Look for export function declarations
279
+ const exportFuncMatch = line.match(/export\s+function\s+(\w+)/);
280
+ if (exportFuncMatch) {
281
+ const funcName = exportFuncMatch[1];
282
+
283
+ // Check if previous non-empty line ends a JSDoc comment
284
+ let hasJsDoc = false;
285
+ for (let j = i - 1; j >= 0; j--) {
286
+ const prevLine = lines[j].trim();
287
+ if (prevLine === '') continue;
288
+ if (prevLine.endsWith('*/')) {
289
+ // Check if this is a JSDoc (starts with /**)
290
+ for (let k = j; k >= 0; k--) {
291
+ if (lines[k].includes('/**')) {
292
+ hasJsDoc = true;
293
+ break;
294
+ }
295
+ if (lines[k].includes('/*') && !lines[k].includes('/**')) {
296
+ break;
297
+ }
298
+ }
299
+ }
300
+ break;
301
+ }
302
+
303
+ if (!hasJsDoc) {
304
+ issues.push({
305
+ type: 'missing-jsdoc',
306
+ file,
307
+ function: funcName
308
+ });
309
+ }
310
+ }
311
+ }
312
+ }
313
+
314
+ return {
315
+ passed: issues.length === 0,
316
+ issues
317
+ };
318
+ }
319
+
320
+ /**
321
+ * Check for deep relative imports (3+ levels: ../../../)
322
+ * @param {string} projectPath - Path to project root
323
+ * @param {Object} options - Options with injectable dependencies
324
+ * @param {Function} options.glob - Glob function (for testing)
325
+ * @param {Function} options.readFile - File read function (for testing)
326
+ * @returns {Promise<{passed: boolean, issues: Array<{type: string, file: string}>}>}
327
+ */
328
+ async function checkImportStyle(projectPath, options = {}) {
329
+ const globFn = options.glob || defaultGlob;
330
+ const readFileFn = options.readFile || (async (p) => fs.readFile(p, 'utf-8'));
331
+ const issues = [];
332
+
333
+ const pattern = 'src/**/*.{js,ts,jsx,tsx}';
334
+ const files = await globFn(pattern, { cwd: projectPath });
335
+
336
+ for (const file of files) {
337
+ const filePath = path.join(projectPath, file);
338
+ const content = await readFileFn(filePath);
339
+
340
+ // Look for deep relative imports (3+ levels)
341
+ // Matches: from '../../../' or from "../../../" or require('../../../')
342
+ const deepImportPattern = /(?:from\s+['"]|require\s*\(\s*['"])(?:\.\.\/){3,}/;
343
+ if (deepImportPattern.test(content)) {
344
+ issues.push({
345
+ type: 'deep-import',
346
+ file
347
+ });
348
+ }
349
+ }
350
+
351
+ return {
352
+ passed: issues.length === 0,
353
+ issues
354
+ };
355
+ }
356
+
357
+ /**
358
+ * Run all audit checks on a project
359
+ * @param {string} projectPath - Path to project root
360
+ * @param {Object} options - Options with injectable dependencies
361
+ * @returns {Promise<Object>} Audit results with summary
362
+ */
363
+ async function auditProject(projectPath, options = {}) {
364
+ const [
365
+ standardsFiles,
366
+ flatFolders,
367
+ inlineInterfaces,
368
+ hardcodedUrls,
369
+ magicStrings,
370
+ seedOrganization,
371
+ jsDocCoverage,
372
+ importStyle
373
+ ] = await Promise.all([
374
+ checkStandardsFiles(projectPath, options),
375
+ checkFlatFolders(projectPath, options),
376
+ checkInlineInterfaces(projectPath, options),
377
+ checkHardcodedUrls(projectPath, options),
378
+ checkMagicStrings(projectPath, options),
379
+ checkSeedOrganization(projectPath, options),
380
+ checkJsDocCoverage(projectPath, options),
381
+ checkImportStyle(projectPath, options)
382
+ ]);
383
+
384
+ const allResults = [
385
+ standardsFiles,
386
+ flatFolders,
387
+ inlineInterfaces,
388
+ hardcodedUrls,
389
+ magicStrings,
390
+ seedOrganization,
391
+ jsDocCoverage,
392
+ importStyle
393
+ ];
394
+
395
+ const totalIssues = allResults.reduce((sum, r) => sum + r.issues.length, 0);
396
+ const passed = allResults.every(r => r.passed);
397
+
398
+ return {
399
+ standardsFiles,
400
+ flatFolders,
401
+ inlineInterfaces,
402
+ hardcodedUrls,
403
+ magicStrings,
404
+ seedOrganization,
405
+ jsDocCoverage,
406
+ importStyle,
407
+ summary: {
408
+ totalIssues,
409
+ passed
410
+ }
411
+ };
412
+ }
413
+
414
+ /**
415
+ * Generate markdown audit report
416
+ * @param {Object} auditResults - Results from auditProject
417
+ * @returns {string} Markdown report content
418
+ */
419
+ function generateReport(auditResults) {
420
+ const { summary } = auditResults;
421
+ const status = summary.passed ? 'PASSED' : 'FAILED';
422
+ const statusIcon = summary.passed ? '[+]' : '[-]';
423
+
424
+ let report = `# Audit Report\n\n`;
425
+ report += `**Status:** ${statusIcon} ${status}\n`;
426
+ report += `**Total Issues:** ${summary.totalIssues}\n\n`;
427
+
428
+ // Standards Files
429
+ if (auditResults.standardsFiles) {
430
+ report += `## Standards Files\n\n`;
431
+ if (auditResults.standardsFiles.passed) {
432
+ report += `[+] All required files present\n\n`;
433
+ } else {
434
+ for (const issue of auditResults.standardsFiles.issues) {
435
+ report += `- ${issue.type}: ${issue.file}\n`;
436
+ }
437
+ report += '\n';
438
+ }
439
+ }
440
+
441
+ // Flat Folders
442
+ if (auditResults.flatFolders) {
443
+ report += `## Folder Organization\n\n`;
444
+ if (auditResults.flatFolders.passed) {
445
+ report += `[+] No flat folders detected\n\n`;
446
+ } else {
447
+ for (const issue of auditResults.flatFolders.issues) {
448
+ report += `- ${issue.type}: ${issue.folder}\n`;
449
+ }
450
+ report += '\n';
451
+ }
452
+ }
453
+
454
+ // Inline Interfaces
455
+ if (auditResults.inlineInterfaces) {
456
+ report += `## Inline Interfaces\n\n`;
457
+ if (auditResults.inlineInterfaces.passed) {
458
+ report += `[+] No inline interfaces in services\n\n`;
459
+ } else {
460
+ for (const issue of auditResults.inlineInterfaces.issues) {
461
+ report += `- ${issue.type}: ${issue.file}\n`;
462
+ }
463
+ report += '\n';
464
+ }
465
+ }
466
+
467
+ // Hardcoded URLs
468
+ if (auditResults.hardcodedUrls) {
469
+ report += `## Hardcoded URLs/Ports\n\n`;
470
+ if (auditResults.hardcodedUrls.passed) {
471
+ report += `[+] No hardcoded URLs or ports\n\n`;
472
+ } else {
473
+ for (const issue of auditResults.hardcodedUrls.issues) {
474
+ report += `- ${issue.type}: ${issue.file}${issue.value ? ` (${issue.value})` : ''}\n`;
475
+ }
476
+ report += '\n';
477
+ }
478
+ }
479
+
480
+ // Magic Strings
481
+ if (auditResults.magicStrings) {
482
+ report += `## Magic Strings\n\n`;
483
+ if (auditResults.magicStrings.passed) {
484
+ report += `[+] No magic strings detected\n\n`;
485
+ } else {
486
+ for (const issue of auditResults.magicStrings.issues) {
487
+ report += `- ${issue.type}: ${issue.file} (${issue.value})\n`;
488
+ }
489
+ report += '\n';
490
+ }
491
+ }
492
+
493
+ // Seed Organization
494
+ if (auditResults.seedOrganization) {
495
+ report += `## Seed Organization\n\n`;
496
+ if (auditResults.seedOrganization.passed) {
497
+ report += `[+] Seeds properly organized per-entity\n\n`;
498
+ } else {
499
+ for (const issue of auditResults.seedOrganization.issues) {
500
+ report += `- ${issue.type}: Use per-entity seed folders instead of flat src/seeds/\n`;
501
+ }
502
+ report += '\n';
503
+ }
504
+ }
505
+
506
+ // JSDoc Coverage
507
+ if (auditResults.jsDocCoverage) {
508
+ report += `## JSDoc Coverage\n\n`;
509
+ if (auditResults.jsDocCoverage.passed) {
510
+ report += `[+] All exported functions have JSDoc\n\n`;
511
+ } else {
512
+ for (const issue of auditResults.jsDocCoverage.issues) {
513
+ report += `- ${issue.type}: ${issue.file} (${issue.function})\n`;
514
+ }
515
+ report += '\n';
516
+ }
517
+ }
518
+
519
+ // Import Style
520
+ if (auditResults.importStyle) {
521
+ report += `## Import Style\n\n`;
522
+ if (auditResults.importStyle.passed) {
523
+ report += `[+] No deep relative imports\n\n`;
524
+ } else {
525
+ for (const issue of auditResults.importStyle.issues) {
526
+ report += `- ${issue.type}: ${issue.file}\n`;
527
+ }
528
+ report += '\n';
529
+ }
530
+ }
531
+
532
+ return report;
533
+ }
534
+
535
+ module.exports = {
536
+ checkStandardsFiles,
537
+ checkFlatFolders,
538
+ checkInlineInterfaces,
539
+ checkHardcodedUrls,
540
+ checkMagicStrings,
541
+ checkSeedOrganization,
542
+ checkJsDocCoverage,
543
+ checkImportStyle,
544
+ auditProject,
545
+ generateReport
546
+ };