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,452 @@
1
+ /**
2
+ * Cleanup Executor - Execute code cleanup operations
3
+ */
4
+
5
+ /**
6
+ * Generate environment variable name from URL or value
7
+ * @param {string} value - The value to convert
8
+ * @param {string} type - Type of config (url, port)
9
+ * @returns {string} Environment variable name
10
+ */
11
+ function generateEnvVarName(value, type) {
12
+ if (type === 'port') {
13
+ return 'PORT';
14
+ }
15
+
16
+ // Extract hostname for URL-based env var names
17
+ try {
18
+ const url = new URL(value);
19
+ const hostname = url.hostname.replace(/[^a-zA-Z0-9]/g, '_').toUpperCase();
20
+ if (hostname === 'LOCALHOST') {
21
+ return 'API_URL';
22
+ }
23
+ return `${hostname}_URL`;
24
+ } catch {
25
+ return 'API_URL';
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Replace hardcoded URL/port with environment variable
31
+ * @param {string} code - Source code
32
+ * @param {Object} issue - Issue describing the hardcoded value
33
+ * @returns {Promise<Object>} Result with code and envVar
34
+ */
35
+ async function extractHardcodedConfig(code, issue) {
36
+ const { type, value } = issue;
37
+ const envVar = generateEnvVarName(value, type);
38
+
39
+ let newCode = code;
40
+
41
+ if (type === 'port') {
42
+ // Replace port assignments like: const port = 3000;
43
+ const portPattern = new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*${value}\\s*;`, 'g');
44
+ newCode = newCode.replace(portPattern, (match, keyword, varName) => {
45
+ return `${keyword} ${varName} = process.env.${envVar} || ${value};`;
46
+ });
47
+
48
+ // Also replace direct port usage
49
+ if (newCode === code) {
50
+ newCode = newCode.replace(
51
+ new RegExp(`\\b${value}\\b`, 'g'),
52
+ `(process.env.${envVar} || ${value})`
53
+ );
54
+ }
55
+ } else if (type === 'url') {
56
+ const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
57
+
58
+ // Check if URL is used in a function call (fetch, axios, etc.) with a path appended
59
+ const urlWithPathPattern = new RegExp(`(['"])${escapedValue}(/[^'"]+)\\1`, 'g');
60
+ const hasPath = urlWithPathPattern.test(code);
61
+
62
+ if (hasPath) {
63
+ // Replace URL with env var, keeping the path separate
64
+ newCode = code.replace(urlWithPathPattern, (match, q1, path) => {
65
+ return `process.env.${envVar} + '${path}'`;
66
+ });
67
+ } else {
68
+ // Check if it's a simple assignment (const x = 'url';)
69
+ const assignmentPattern = new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*(['"])${escapedValue}\\3\\s*;`, 'g');
70
+ const isAssignment = assignmentPattern.test(code);
71
+
72
+ if (isAssignment) {
73
+ // For assignments, use fallback pattern
74
+ newCode = code.replace(
75
+ new RegExp(`(const|let|var)\\s+(\\w+)\\s*=\\s*(['"])${escapedValue}\\3\\s*;`, 'g'),
76
+ (match, keyword, varName, q) => {
77
+ return `${keyword} ${varName} = process.env.${envVar} || ${q}${value}${q};`;
78
+ }
79
+ );
80
+ } else {
81
+ // For inline usage without path, just use env var
82
+ newCode = code.replace(
83
+ new RegExp(`(['"])${escapedValue}\\1`, 'g'),
84
+ `process.env.${envVar}`
85
+ );
86
+ }
87
+ }
88
+ }
89
+
90
+ return {
91
+ code: newCode,
92
+ envVar
93
+ };
94
+ }
95
+
96
+ /**
97
+ * Migrate file from flat folder (services/) to entity folder
98
+ * @param {Object} options - Migration options
99
+ * @param {Object} deps - Injected dependencies
100
+ * @returns {Promise<Object>} Result with newPath and updatedImports count
101
+ */
102
+ async function migrateFlatFolder(options, deps = {}) {
103
+ const { sourcePath, entity, projectPath } = options;
104
+ const { fs, glob } = deps;
105
+
106
+ // Calculate new path
107
+ const fileName = sourcePath.split('/').pop();
108
+ const newPath = `src/${entity}/${fileName}`;
109
+ const fullNewPath = `${projectPath}/${newPath}`;
110
+ const fullSourcePath = `${projectPath}/${sourcePath}`;
111
+
112
+ // Create entity directory
113
+ await fs.mkdir(`${projectPath}/src/${entity}`, { recursive: true });
114
+
115
+ // Read source file
116
+ const content = await fs.readFile(fullSourcePath);
117
+
118
+ // Move file (rename)
119
+ await fs.rename(fullSourcePath, fullNewPath);
120
+
121
+ // Update imports in other files
122
+ let updatedImports = 0;
123
+
124
+ if (glob) {
125
+ const files = await glob(`${projectPath}/src/**/*.{ts,js,tsx,jsx}`);
126
+
127
+ for (const file of files) {
128
+ if (file === fullNewPath) continue;
129
+
130
+ try {
131
+ const fileContent = await fs.readFile(file);
132
+
133
+ // Extract the folder structure from source path (e.g., services/user.service)
134
+ const pathParts = sourcePath.replace(/^src\//, '').replace(/\.(ts|js|tsx|jsx)$/, '');
135
+ const newImportBase = `${entity}/${fileName.replace(/\.(ts|js|tsx|jsx)$/, '')}`;
136
+
137
+ // Match various import patterns:
138
+ // - './services/user.service'
139
+ // - '../services/user.service'
140
+ // - 'src/services/user.service'
141
+ const importPatterns = [
142
+ // Relative import: ./services/user.service or ../services/user.service
143
+ new RegExp(`(['"])\\.{1,2}/${pathParts.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\1`, 'g'),
144
+ // Absolute-style import: src/services/user.service
145
+ new RegExp(`(['"])${sourcePath.replace(/\.(ts|js|tsx|jsx)$/, '').replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\1`, 'g')
146
+ ];
147
+
148
+ let updatedContent = fileContent;
149
+ let fileUpdated = false;
150
+
151
+ for (const pattern of importPatterns) {
152
+ if (pattern.test(updatedContent)) {
153
+ updatedContent = updatedContent.replace(pattern, (match, quote) => {
154
+ // Determine the prefix (relative or absolute)
155
+ const hasRelative = match.includes('./');
156
+ const prefix = hasRelative ? './' : '';
157
+ return `${quote}${prefix}${newImportBase}${quote}`;
158
+ });
159
+ fileUpdated = true;
160
+ }
161
+ }
162
+
163
+ if (fileUpdated) {
164
+ await fs.writeFile(file, updatedContent);
165
+ updatedImports++;
166
+ }
167
+ } catch (err) {
168
+ // Skip files that can't be read
169
+ }
170
+ }
171
+ }
172
+
173
+ return {
174
+ newPath,
175
+ updatedImports
176
+ };
177
+ }
178
+
179
+ /**
180
+ * Extract inline interface to separate types file
181
+ * @param {string} code - Source code with inline interface
182
+ * @param {Object} options - Extraction options
183
+ * @returns {Promise<Object>} Result with serviceCode, typesCode, typesPath
184
+ */
185
+ async function extractInlineInterface(code, options) {
186
+ const { interfaceName, entity } = options;
187
+
188
+ // Match the interface definition
189
+ const interfacePattern = new RegExp(
190
+ `(interface\\s+${interfaceName}\\s*\\{[^}]*\\})`,
191
+ 's'
192
+ );
193
+ const match = code.match(interfacePattern);
194
+
195
+ if (!match) {
196
+ throw new Error(`Interface ${interfaceName} not found in code`);
197
+ }
198
+
199
+ const interfaceCode = match[1];
200
+
201
+ // Remove interface from service code
202
+ let serviceCode = code.replace(interfacePattern, '').trim();
203
+
204
+ // Add import statement at the top
205
+ const importStatement = `import { ${interfaceName} } from './types/${entity}.types';`;
206
+
207
+ // Insert import after any existing imports or at the beginning
208
+ const lastImportMatch = serviceCode.match(/^(import .+;\n)+/m);
209
+ if (lastImportMatch) {
210
+ serviceCode = serviceCode.replace(
211
+ lastImportMatch[0],
212
+ lastImportMatch[0] + importStatement + '\n'
213
+ );
214
+ } else {
215
+ serviceCode = importStatement + '\n\n' + serviceCode;
216
+ }
217
+
218
+ // Create types file content
219
+ const typesCode = `export ${interfaceCode}`;
220
+ const typesPath = `src/${entity}/types/${entity}.types.ts`;
221
+
222
+ return {
223
+ serviceCode,
224
+ typesCode,
225
+ typesPath
226
+ };
227
+ }
228
+
229
+ /**
230
+ * Replace magic strings with named constants
231
+ * @param {string} code - Source code
232
+ * @param {Object} options - Replacement options
233
+ * @returns {Promise<Object>} Result with code, constants, constantsFile, constantsPath
234
+ */
235
+ async function replaceMagicStrings(code, options) {
236
+ const { strings, entity } = options;
237
+ const constants = [];
238
+ let newCode = code;
239
+
240
+ for (const { value, suggestedName } of strings) {
241
+ const name = suggestedName || `CONST_${value.toUpperCase().replace(/[^A-Z0-9]/g, '_')}`;
242
+
243
+ // Replace occurrences of the string literal
244
+ const escapedValue = value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
245
+ const pattern = new RegExp(`(['"])${escapedValue}\\1`, 'g');
246
+ newCode = newCode.replace(pattern, name);
247
+
248
+ constants.push({ name, value });
249
+ }
250
+
251
+ // Generate constants file content
252
+ const constantsFile = constants
253
+ .map(c => `export const ${c.name} = '${c.value}';`)
254
+ .join('\n');
255
+
256
+ const constantsPath = entity
257
+ ? `src/${entity}/constants/${entity}.constants.ts`
258
+ : 'src/constants/index.ts';
259
+
260
+ return {
261
+ code: newCode,
262
+ constants,
263
+ constantsFile,
264
+ constantsPath
265
+ };
266
+ }
267
+
268
+ /**
269
+ * Parse function signature from code
270
+ * @param {string} funcStr - Function string
271
+ * @returns {Object} Parsed function info
272
+ */
273
+ function parseFunctionSignature(funcStr) {
274
+ // Match function name and parameters
275
+ const funcMatch = funcStr.match(
276
+ /(?:async\s+)?(?:function\s+)?(\w+)\s*\(([^)]*)\)(?:\s*:\s*(\w+))?/
277
+ );
278
+
279
+ if (!funcMatch) return null;
280
+
281
+ const [, name, paramsStr, returnType] = funcMatch;
282
+
283
+ // Parse parameters
284
+ const params = paramsStr
285
+ .split(',')
286
+ .map(p => p.trim())
287
+ .filter(p => p)
288
+ .map(p => {
289
+ const [paramName, paramType] = p.split(':').map(s => s.trim());
290
+ return { name: paramName, type: paramType || 'any' };
291
+ });
292
+
293
+ return { name, params, returnType: returnType || 'any' };
294
+ }
295
+
296
+ /**
297
+ * Generate JSDoc comment for a function
298
+ * @param {Object} funcInfo - Function info from parsing
299
+ * @returns {string} JSDoc comment
300
+ */
301
+ function generateJsDoc(funcInfo) {
302
+ const { params, returnType } = funcInfo;
303
+
304
+ let jsdoc = '/**\n';
305
+ jsdoc += ` * TODO: Add description\n`;
306
+
307
+ for (const param of params) {
308
+ jsdoc += ` * @param {${param.type}} ${param.name}\n`;
309
+ }
310
+
311
+ if (returnType && returnType !== 'void') {
312
+ jsdoc += ` * @returns {${returnType}}\n`;
313
+ }
314
+
315
+ jsdoc += ' */\n';
316
+
317
+ return jsdoc;
318
+ }
319
+
320
+ /**
321
+ * Add JSDoc comments to exported functions missing them
322
+ * @param {string} code - Source code
323
+ * @returns {Promise<Object>} Result with updated code
324
+ */
325
+ async function addMissingJsDoc(code) {
326
+ const lines = code.split('\n');
327
+ const result = [];
328
+
329
+ for (let i = 0; i < lines.length; i++) {
330
+ const line = lines[i];
331
+
332
+ // Check if this is an exported function or public method
333
+ const isExportedFunc = /^\s*export\s+(?:async\s+)?function\s+\w+/.test(line);
334
+ const isPublicMethod = /^\s*(?:public\s+)?(?:async\s+)?(\w+)\s*\([^)]*\)/.test(line)
335
+ && !line.includes('private')
336
+ && !line.includes('//');
337
+
338
+ if (isExportedFunc || (isPublicMethod && line.includes('class') === false)) {
339
+ // Check if previous line(s) have JSDoc
340
+ let hasJsDoc = false;
341
+ let j = i - 1;
342
+
343
+ // Skip empty lines
344
+ while (j >= 0 && lines[j].trim() === '') {
345
+ j--;
346
+ }
347
+
348
+ // Check for JSDoc end
349
+ if (j >= 0 && lines[j].trim() === '*/') {
350
+ // Find JSDoc start
351
+ while (j >= 0 && !lines[j].includes('/**')) {
352
+ j--;
353
+ }
354
+ if (j >= 0 && lines[j].includes('/**')) {
355
+ hasJsDoc = true;
356
+ }
357
+ }
358
+
359
+ if (!hasJsDoc && (isExportedFunc || isPublicMethod)) {
360
+ // Parse function signature
361
+ const funcInfo = parseFunctionSignature(line);
362
+
363
+ if (funcInfo && funcInfo.params.length > 0) {
364
+ // Add JSDoc
365
+ const jsDoc = generateJsDoc(funcInfo);
366
+ result.push(jsDoc.trim());
367
+ }
368
+ }
369
+ }
370
+
371
+ result.push(line);
372
+ }
373
+
374
+ return { code: result.join('\n') };
375
+ }
376
+
377
+ /**
378
+ * Create a git commit for cleanup changes
379
+ * @param {Object} options - Commit options
380
+ * @param {Object} deps - Injected dependencies
381
+ * @returns {Promise<void>}
382
+ */
383
+ async function commitChanges(options, deps = {}) {
384
+ const { type, entity, description, files } = options;
385
+ const { exec } = deps;
386
+
387
+ // Stage files
388
+ const filesToAdd = files || ['.'];
389
+ for (const file of Array.isArray(filesToAdd) ? filesToAdd : [filesToAdd]) {
390
+ await exec(`git add ${file}`);
391
+ }
392
+
393
+ // Create commit message in conventional commit format
394
+ const commitType = type === 'migrate' ? 'refactor' : type;
395
+ const scope = entity || 'cleanup';
396
+ const message = `${commitType}(${scope}): ${description}`;
397
+
398
+ await exec(`git commit -m "${message}"`);
399
+ }
400
+
401
+ /**
402
+ * Run full cleanup on a project
403
+ * @param {string} projectPath - Path to project
404
+ * @param {Object} options - Cleanup options and dependencies
405
+ * @returns {Promise<Object>} Cleanup results
406
+ */
407
+ async function runCleanup(projectPath, options = {}) {
408
+ const { injectStandards, auditProject } = options;
409
+
410
+ const result = {
411
+ standardsInjected: null,
412
+ issuesFixed: 0,
413
+ commits: []
414
+ };
415
+
416
+ // Step 1: Ensure standards files exist
417
+ if (injectStandards) {
418
+ result.standardsInjected = await injectStandards(projectPath);
419
+ }
420
+
421
+ // Step 2: Run audit
422
+ if (auditProject) {
423
+ const auditResults = await auditProject(projectPath);
424
+
425
+ // Count total issues that could be fixed
426
+ const issueCategories = [
427
+ 'flatFolders',
428
+ 'hardcodedUrls',
429
+ 'magicStrings',
430
+ 'inlineInterfaces',
431
+ 'jsDocCoverage'
432
+ ];
433
+
434
+ for (const category of issueCategories) {
435
+ if (auditResults[category]?.issues) {
436
+ result.issuesFixed += auditResults[category].issues.length;
437
+ }
438
+ }
439
+ }
440
+
441
+ return result;
442
+ }
443
+
444
+ module.exports = {
445
+ extractHardcodedConfig,
446
+ migrateFlatFolder,
447
+ extractInlineInterface,
448
+ replaceMagicStrings,
449
+ addMissingJsDoc,
450
+ commitChanges,
451
+ runCleanup
452
+ };