architect-ai 1.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.
package/dist/lib.d.ts ADDED
@@ -0,0 +1,186 @@
1
+ /**
2
+ * Type definitions for architect-ai CLI
3
+ */
4
+ interface ProjectConfig {
5
+ techStack?: string;
6
+ rules?: string[];
7
+ bypassKeyword?: string;
8
+ commitFormat?: string;
9
+ maxConcurrency?: number;
10
+ cacheEnabled?: boolean;
11
+ }
12
+ interface AuditIssue {
13
+ line: number;
14
+ severity: 'CRITICAL' | 'WARNING' | 'INFO';
15
+ message: string;
16
+ suggestion: string;
17
+ }
18
+ interface AuditResult {
19
+ status: 'PASS' | 'FAIL';
20
+ issues?: AuditIssue[];
21
+ message?: string;
22
+ suggestion?: string;
23
+ }
24
+ interface FileAuditResult {
25
+ filePath: string;
26
+ result: AuditResult;
27
+ duration: number;
28
+ }
29
+ interface CLIOptions {
30
+ files?: string[];
31
+ commit?: boolean;
32
+ bypass?: boolean;
33
+ verbose?: boolean;
34
+ maxConcurrency?: number;
35
+ targetBranch?: string;
36
+ }
37
+ declare const DEFAULT_CONFIG: Required<ProjectConfig>;
38
+
39
+ /**
40
+ * Configuration loader with caching and validation
41
+ */
42
+
43
+ /**
44
+ * Load and validate project configuration
45
+ */
46
+ declare const loadProjectConfig: (cwd?: string) => Promise<ProjectConfig>;
47
+ /**
48
+ * Clear config cache (useful for testing)
49
+ */
50
+ declare const clearConfigCache: () => void;
51
+ /**
52
+ * Validate configuration schema
53
+ */
54
+ declare const validateConfig: (config: unknown) => config is ProjectConfig;
55
+
56
+ /**
57
+ * Git utilities with async support and better error handling
58
+ */
59
+ interface GitDiffOptions {
60
+ targetBranch?: string;
61
+ extensions?: RegExp;
62
+ }
63
+ /**
64
+ * Get last commit message asynchronously
65
+ */
66
+ declare const getLastCommitMessage: () => Promise<string>;
67
+ /**
68
+ * Get changed files against target branch with parallel execution
69
+ */
70
+ declare const getChangedFiles: (options?: GitDiffOptions) => Promise<string[]>;
71
+ /**
72
+ * Check if current directory is a git repository
73
+ */
74
+ declare const isGitRepository: () => Promise<boolean>;
75
+ /**
76
+ * Get current branch name
77
+ */
78
+ declare const getCurrentBranch: () => Promise<string>;
79
+
80
+ /**
81
+ * Parsing utilities for AI responses
82
+ */
83
+
84
+ /**
85
+ * Clean JSON from markdown code blocks
86
+ */
87
+ declare const cleanJSON: (text: string) => string;
88
+ /**
89
+ * Parse AI response to AuditResult with error handling
90
+ */
91
+ declare const parseAuditResponse: (responseText: string) => AuditResult;
92
+ /**
93
+ * Format file size for display
94
+ */
95
+ declare const formatBytes: (bytes: number) => string;
96
+ /**
97
+ * Format duration for display
98
+ */
99
+ declare const formatDuration$1: (ms: number) => string;
100
+
101
+ /**
102
+ * Console output utilities with colors
103
+ */
104
+ declare const log: {
105
+ info: (msg: string) => void;
106
+ success: (msg: string) => void;
107
+ warning: (msg: string) => void;
108
+ error: (msg: string) => void;
109
+ critical: (msg: string) => void;
110
+ audit: (msg: string) => void;
111
+ skip: (msg: string) => void;
112
+ file: (msg: string) => void;
113
+ issue: (severity: string, line: number, message: string) => void;
114
+ progress: (current: number, total: number, label: string) => void;
115
+ progressEnd: () => void;
116
+ divider: () => void;
117
+ header: (title: string) => void;
118
+ };
119
+ /**
120
+ * Format duration for display
121
+ */
122
+ declare const formatDuration: (ms: number) => string;
123
+
124
+ /**
125
+ * Core AI service with singleton pattern and request pooling
126
+ */
127
+
128
+ /**
129
+ * Audit commit message
130
+ */
131
+ declare const auditCommit: (message: string, config: ProjectConfig) => Promise<AuditResult>;
132
+ /**
133
+ * Audit single file
134
+ */
135
+ declare const auditFile: (filePath: string, content: string, systemPrompt: string) => Promise<AuditResult>;
136
+ /**
137
+ * Audit multiple files with concurrency control
138
+ */
139
+ declare const auditFilesWithConcurrency: (files: Array<{
140
+ path: string;
141
+ content: string;
142
+ }>, config: ProjectConfig, maxConcurrency?: number) => Promise<FileAuditResult[]>;
143
+ /**
144
+ * Clear model cache (useful for testing)
145
+ */
146
+ declare const clearModelCache: () => void;
147
+
148
+ /**
149
+ * File service with streaming support and memory optimization
150
+ */
151
+
152
+ interface FileContent {
153
+ path: string;
154
+ content: string;
155
+ size: number;
156
+ }
157
+ interface FileReadResult {
158
+ success: FileContent[];
159
+ skipped: Array<{
160
+ path: string;
161
+ reason: string;
162
+ }>;
163
+ }
164
+ /**
165
+ * Read multiple files efficiently with size limits
166
+ */
167
+ declare const readFilesForAudit: (filePaths: string[]) => Promise<FileReadResult>;
168
+ /**
169
+ * Get file extension without dot
170
+ */
171
+ declare const getFileExtension: (filePath: string) => string;
172
+ /**
173
+ * Check if file is a code file
174
+ */
175
+ declare const isCodeFile: (filePath: string) => boolean;
176
+
177
+ /**
178
+ * Prompt templates for AI-powered code auditing
179
+ */
180
+
181
+ declare const DEFAULT_COMMIT_PROMPT = "\n### ROLE\nYou are a Strict Release Manager. You enforce \"Conventional Commits\" standards.\n### RULES\n1. Format MUST be: `<type>(<scope>): <subject>`\n - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.\n - Example: \"feat(auth): add google login support\"\n2. Subject must be lowercase and imperative (e.g., \"add\" not \"added\").\n3. Message must be descriptive enough to understand the change context.\n";
182
+ declare const BASE_AUDIT_PROMPT = "\n### ROLE & OBJECTIVE\nYou are an Elite Software Architect. Your goal is to enforce \"Clean Code\", \"SOLID Principles\", and \"Maintainability\".\n### GLOBAL STANDARDS\n1. **Clean Code:** Variable/Function names must be semantic. No magic numbers.\n2. **Split Code:** Suggest splitting complex functions/components.\n3. **Performance:** Identify obvious bottlenecks.\n4. **Error Handling:** Ensure proper boundary checks.\n";
183
+ declare const buildSystemPrompt: (config: ProjectConfig) => string;
184
+ declare const buildCommitPrompt: (customFormat?: string) => string;
185
+
186
+ export { type AuditIssue, type AuditResult, BASE_AUDIT_PROMPT, type CLIOptions, DEFAULT_COMMIT_PROMPT, DEFAULT_CONFIG, type FileAuditResult, type FileContent, type FileReadResult, type ProjectConfig, auditCommit, auditFile, auditFilesWithConcurrency, buildCommitPrompt, buildSystemPrompt, cleanJSON, clearConfigCache, clearModelCache, formatBytes, formatDuration, formatDuration$1 as formatDurationParser, getChangedFiles, getCurrentBranch, getFileExtension, getLastCommitMessage, isCodeFile, isGitRepository, loadProjectConfig, log, parseAuditResponse, readFilesForAudit, validateConfig };
package/dist/lib.js ADDED
@@ -0,0 +1,443 @@
1
+ import { readFile, stat } from 'fs/promises';
2
+ import { existsSync } from 'fs';
3
+ import { resolve } from 'path';
4
+ import { exec } from 'child_process';
5
+ import { promisify } from 'util';
6
+ import { GoogleGenerativeAI } from '@google/generative-ai';
7
+
8
+ // Architect AI - CLI
9
+
10
+
11
+ // src/types/index.ts
12
+ var DEFAULT_CONFIG = {
13
+ techStack: "",
14
+ rules: [],
15
+ bypassKeyword: "skip:",
16
+ commitFormat: "",
17
+ maxConcurrency: 5,
18
+ cacheEnabled: true
19
+ };
20
+ var cachedConfig = null;
21
+ var loadProjectConfig = async (cwd = process.cwd()) => {
22
+ if (cachedConfig) {
23
+ return cachedConfig;
24
+ }
25
+ const configPath = resolve(cwd, ".architectrc.json");
26
+ if (!existsSync(configPath)) {
27
+ cachedConfig = { ...DEFAULT_CONFIG };
28
+ return cachedConfig;
29
+ }
30
+ try {
31
+ const content = await readFile(configPath, "utf-8");
32
+ const userConfig = JSON.parse(content);
33
+ cachedConfig = {
34
+ ...DEFAULT_CONFIG,
35
+ ...userConfig
36
+ };
37
+ console.log("\u2699\uFE0F Loaded project-specific rules from .architectrc.json");
38
+ return cachedConfig;
39
+ } catch (error) {
40
+ console.warn("\u26A0\uFE0F Found .architectrc.json but failed to parse it.");
41
+ cachedConfig = { ...DEFAULT_CONFIG };
42
+ return cachedConfig;
43
+ }
44
+ };
45
+ var clearConfigCache = () => {
46
+ cachedConfig = null;
47
+ };
48
+ var validateConfig = (config) => {
49
+ if (typeof config !== "object" || config === null) {
50
+ return false;
51
+ }
52
+ const c = config;
53
+ if (c.techStack !== void 0 && typeof c.techStack !== "string") {
54
+ return false;
55
+ }
56
+ if (c.rules !== void 0 && !Array.isArray(c.rules)) {
57
+ return false;
58
+ }
59
+ if (c.bypassKeyword !== void 0 && typeof c.bypassKeyword !== "string") {
60
+ return false;
61
+ }
62
+ if (c.commitFormat !== void 0 && typeof c.commitFormat !== "string") {
63
+ return false;
64
+ }
65
+ if (c.maxConcurrency !== void 0 && typeof c.maxConcurrency !== "number") {
66
+ return false;
67
+ }
68
+ return true;
69
+ };
70
+ var execAsync = promisify(exec);
71
+ var SUPPORTED_EXTENSIONS = /\.(ts|tsx|js|jsx|mjs|cjs|py|cs|go|java|rs|kt|swift)$/;
72
+ var getLastCommitMessage = async () => {
73
+ try {
74
+ const { stdout } = await execAsync("git log -1 --pretty=%B");
75
+ return stdout.trim();
76
+ } catch {
77
+ return "";
78
+ }
79
+ };
80
+ var getChangedFiles = async (options = {}) => {
81
+ const { targetBranch = "origin/main", extensions = SUPPORTED_EXTENSIONS } = options;
82
+ try {
83
+ const command = `git diff --name-only --diff-filter=ACMR ${targetBranch}...HEAD`;
84
+ const { stdout } = await execAsync(command);
85
+ return parseAndFilterFiles(stdout, extensions);
86
+ } catch {
87
+ try {
88
+ const command = `git diff --name-only --diff-filter=ACMR ${targetBranch}`;
89
+ const { stdout } = await execAsync(command);
90
+ return parseAndFilterFiles(stdout, extensions);
91
+ } catch {
92
+ console.warn("\u26A0\uFE0F Git diff failed. No files to audit.");
93
+ return [];
94
+ }
95
+ }
96
+ };
97
+ var isGitRepository = async () => {
98
+ try {
99
+ await execAsync("git rev-parse --is-inside-work-tree");
100
+ return true;
101
+ } catch {
102
+ return false;
103
+ }
104
+ };
105
+ var getCurrentBranch = async () => {
106
+ try {
107
+ const { stdout } = await execAsync("git branch --show-current");
108
+ return stdout.trim();
109
+ } catch {
110
+ return "unknown";
111
+ }
112
+ };
113
+ var parseAndFilterFiles = (output, extensions) => {
114
+ return output.split("\n").map((file) => file.trim()).filter((file) => file.length > 0).filter((file) => extensions.test(file));
115
+ };
116
+
117
+ // src/utils/parser.ts
118
+ var cleanJSON = (text) => {
119
+ return text.replace(/```json\s*/gi, "").replace(/```\s*/g, "").trim();
120
+ };
121
+ var parseAuditResponse = (responseText) => {
122
+ const cleaned = cleanJSON(responseText);
123
+ try {
124
+ const parsed = JSON.parse(cleaned);
125
+ if (!parsed.status || !["PASS", "FAIL"].includes(parsed.status)) {
126
+ return {
127
+ status: "FAIL",
128
+ message: "Invalid AI response format",
129
+ issues: []
130
+ };
131
+ }
132
+ return parsed;
133
+ } catch {
134
+ const jsonMatch = cleaned.match(/\{[\s\S]*\}/);
135
+ if (jsonMatch) {
136
+ try {
137
+ return JSON.parse(jsonMatch[0]);
138
+ } catch {
139
+ }
140
+ }
141
+ return {
142
+ status: "FAIL",
143
+ message: "Failed to parse AI response",
144
+ issues: []
145
+ };
146
+ }
147
+ };
148
+ var formatBytes = (bytes) => {
149
+ if (bytes === 0) return "0 B";
150
+ const k = 1024;
151
+ const sizes = ["B", "KB", "MB", "GB"];
152
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
153
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(2))} ${sizes[i]}`;
154
+ };
155
+ var formatDuration = (ms) => {
156
+ if (ms < 1e3) return `${ms}ms`;
157
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
158
+ return `${(ms / 6e4).toFixed(1)}m`;
159
+ };
160
+
161
+ // src/utils/logger.ts
162
+ var colors = {
163
+ reset: "\x1B[0m",
164
+ bold: "\x1B[1m",
165
+ dim: "\x1B[2m",
166
+ red: "\x1B[31m",
167
+ green: "\x1B[32m",
168
+ yellow: "\x1B[33m",
169
+ blue: "\x1B[34m",
170
+ magenta: "\x1B[35m",
171
+ cyan: "\x1B[36m"};
172
+ var log = {
173
+ info: (msg) => console.log(`${colors.blue}\u2139${colors.reset} ${msg}`),
174
+ success: (msg) => console.log(`${colors.green}\u2705${colors.reset} ${msg}`),
175
+ warning: (msg) => console.warn(`${colors.yellow}\u26A0\uFE0F${colors.reset} ${msg}`),
176
+ error: (msg) => console.error(`${colors.red}\u274C${colors.reset} ${msg}`),
177
+ critical: (msg) => console.error(`${colors.red}${colors.bold}\u{1F6A8}${colors.reset} ${msg}`),
178
+ audit: (msg) => console.log(`${colors.cyan}\u{1F50D}${colors.reset} ${msg}`),
179
+ skip: (msg) => console.log(`${colors.magenta}\u23E9${colors.reset} ${msg}`),
180
+ file: (msg) => console.log(`${colors.dim} ${msg}${colors.reset}`),
181
+ // Issue formatting
182
+ issue: (severity, line, message) => {
183
+ const color = severity === "CRITICAL" ? colors.red : severity === "WARNING" ? colors.yellow : colors.blue;
184
+ console.log(` ${color}[${severity}] Line ${line}: ${message}${colors.reset}`);
185
+ },
186
+ // Progress bar
187
+ progress: (current, total, label) => {
188
+ const percent = Math.round(current / total * 100);
189
+ const barLength = 20;
190
+ const filled = Math.round(current / total * barLength);
191
+ const bar = "\u2588".repeat(filled) + "\u2591".repeat(barLength - filled);
192
+ process.stdout.write(`\r${colors.cyan}${bar}${colors.reset} ${percent}% | ${label}`);
193
+ },
194
+ progressEnd: () => {
195
+ console.log();
196
+ },
197
+ // Divider
198
+ divider: () => console.log(`${colors.dim}${"\u2500".repeat(50)}${colors.reset}`),
199
+ // Header
200
+ header: (title) => {
201
+ console.log();
202
+ console.log(`${colors.bold}${colors.cyan}\u{1F3D7}\uFE0F ${title}${colors.reset}`);
203
+ console.log(`${colors.dim}${"\u2500".repeat(50)}${colors.reset}`);
204
+ }
205
+ };
206
+ var formatDuration2 = (ms) => {
207
+ if (ms < 1e3) return `${Math.round(ms)}ms`;
208
+ if (ms < 6e4) return `${(ms / 1e3).toFixed(1)}s`;
209
+ return `${(ms / 6e4).toFixed(1)}m`;
210
+ };
211
+
212
+ // src/config/prompts.ts
213
+ var DEFAULT_COMMIT_PROMPT = `
214
+ ### ROLE
215
+ You are a Strict Release Manager. You enforce "Conventional Commits" standards.
216
+ ### RULES
217
+ 1. Format MUST be: \`<type>(<scope>): <subject>\`
218
+ - Types: feat, fix, docs, style, refactor, perf, test, build, ci, chore, revert.
219
+ - Example: "feat(auth): add google login support"
220
+ 2. Subject must be lowercase and imperative (e.g., "add" not "added").
221
+ 3. Message must be descriptive enough to understand the change context.
222
+ `;
223
+ var BASE_AUDIT_PROMPT = `
224
+ ### ROLE & OBJECTIVE
225
+ You are an Elite Software Architect. Your goal is to enforce "Clean Code", "SOLID Principles", and "Maintainability".
226
+ ### GLOBAL STANDARDS
227
+ 1. **Clean Code:** Variable/Function names must be semantic. No magic numbers.
228
+ 2. **Split Code:** Suggest splitting complex functions/components.
229
+ 3. **Performance:** Identify obvious bottlenecks.
230
+ 4. **Error Handling:** Ensure proper boundary checks.
231
+ `;
232
+ var buildSystemPrompt = (config) => {
233
+ const parts = [BASE_AUDIT_PROMPT];
234
+ if (config.techStack) {
235
+ parts.push(`
236
+ ### TECH STACK CONTEXT
237
+ The code is written in: ${config.techStack}
238
+ `);
239
+ }
240
+ if (config.rules && config.rules.length > 0) {
241
+ parts.push(`
242
+ ### PROJECT SPECIFIC RULES (HIGHEST PRIORITY)
243
+ `);
244
+ config.rules.forEach((rule, index) => {
245
+ parts.push(`${index + 1}. ${rule}
246
+ `);
247
+ });
248
+ }
249
+ parts.push(`
250
+ ### OUTPUT FORMAT (JSON ONLY)
251
+ { "status": "PASS" | "FAIL", "issues": [{ "line": number, "severity": "CRITICAL" | "WARNING", "message": "string", "suggestion": "string" }] }`);
252
+ return parts.join("");
253
+ };
254
+ var buildCommitPrompt = (customFormat) => {
255
+ const parts = [];
256
+ if (customFormat) {
257
+ parts.push(`
258
+ ### ROLE
259
+ You are a Strict Release Manager.
260
+ ### RULES (CUSTOM COMPANY POLICY)
261
+ You must enforce the following strict commit message format:
262
+ "${customFormat}"
263
+
264
+ Any commit message NOT following this pattern must be REJECTED.
265
+ `);
266
+ } else {
267
+ parts.push(DEFAULT_COMMIT_PROMPT);
268
+ }
269
+ parts.push(`
270
+ ### OUTPUT (JSON ONLY)
271
+ { "status": "PASS" | "FAIL", "message": "Reason for failure", "suggestion": "Corrected example" }`);
272
+ return parts.join("");
273
+ };
274
+
275
+ // src/services/ai.ts
276
+ var modelInstance = null;
277
+ var getModel = () => {
278
+ if (modelInstance) {
279
+ return modelInstance;
280
+ }
281
+ const apiKey = process.env.GEMINI_API_KEY;
282
+ if (!apiKey) {
283
+ throw new Error("GEMINI_API_KEY environment variable is required");
284
+ }
285
+ const genAI = new GoogleGenerativeAI(apiKey);
286
+ modelInstance = genAI.getGenerativeModel({
287
+ model: "gemini-1.5-pro",
288
+ generationConfig: {
289
+ temperature: 0.2,
290
+ maxOutputTokens: 8192
291
+ }
292
+ });
293
+ return modelInstance;
294
+ };
295
+ var auditCommit = async (message, config) => {
296
+ log.audit(`Auditing Commit Message: "${message}"...`);
297
+ const systemPrompt = buildCommitPrompt(config.commitFormat);
298
+ const model = getModel();
299
+ try {
300
+ const result = await model.generateContent([
301
+ systemPrompt,
302
+ `Commit Message: "${message}"`
303
+ ]);
304
+ return parseAuditResponse(result.response.text());
305
+ } catch (error) {
306
+ log.warning("AI check failed. Skipping commit check.");
307
+ return { status: "PASS", message: "AI unavailable - skipped" };
308
+ }
309
+ };
310
+ var auditFile = async (filePath, content, systemPrompt) => {
311
+ const model = getModel();
312
+ try {
313
+ const result = await model.generateContent([
314
+ systemPrompt,
315
+ `Code to review:
316
+ ${content}`
317
+ ]);
318
+ return parseAuditResponse(result.response.text());
319
+ } catch (error) {
320
+ return {
321
+ status: "FAIL",
322
+ message: `Error auditing file: ${error instanceof Error ? error.message : "Unknown error"}`,
323
+ issues: []
324
+ };
325
+ }
326
+ };
327
+ var auditFilesWithConcurrency = async (files, config, maxConcurrency = 5) => {
328
+ const systemPrompt = buildSystemPrompt(config);
329
+ const results = [];
330
+ for (let i = 0; i < files.length; i += maxConcurrency) {
331
+ const batch = files.slice(i, i + maxConcurrency);
332
+ const batchPromises = batch.map(async (file) => {
333
+ const startTime = performance.now();
334
+ log.audit(`Auditing: ${file.path}`);
335
+ const result = await auditFile(file.path, file.content, systemPrompt);
336
+ const duration = performance.now() - startTime;
337
+ return {
338
+ filePath: file.path,
339
+ result,
340
+ duration
341
+ };
342
+ });
343
+ const batchResults = await Promise.all(batchPromises);
344
+ results.push(...batchResults);
345
+ log.progress(
346
+ Math.min(i + maxConcurrency, files.length),
347
+ files.length,
348
+ `${results.length}/${files.length} files audited`
349
+ );
350
+ }
351
+ log.progressEnd();
352
+ return results;
353
+ };
354
+ var clearModelCache = () => {
355
+ modelInstance = null;
356
+ };
357
+ var MAX_FILE_SIZE = 500 * 1024;
358
+ var readFilesForAudit = async (filePaths) => {
359
+ const result = {
360
+ success: [],
361
+ skipped: []
362
+ };
363
+ const readPromises = filePaths.map(async (filePath) => {
364
+ const absolutePath = resolve(filePath);
365
+ if (!existsSync(absolutePath)) {
366
+ return { path: filePath, skipped: true, reason: "File not found" };
367
+ }
368
+ try {
369
+ const stats = await stat(absolutePath);
370
+ if (stats.size > MAX_FILE_SIZE) {
371
+ return {
372
+ path: filePath,
373
+ skipped: true,
374
+ reason: `File too large (${formatBytes(stats.size)})`
375
+ };
376
+ }
377
+ const content = await readFile(absolutePath, "utf-8");
378
+ return {
379
+ path: filePath,
380
+ content,
381
+ size: stats.size,
382
+ skipped: false
383
+ };
384
+ } catch (error) {
385
+ return {
386
+ path: filePath,
387
+ skipped: true,
388
+ reason: error instanceof Error ? error.message : "Read error"
389
+ };
390
+ }
391
+ });
392
+ const results = await Promise.all(readPromises);
393
+ for (const item of results) {
394
+ if (item.skipped) {
395
+ result.skipped.push({ path: item.path, reason: item.reason });
396
+ } else {
397
+ result.success.push({
398
+ path: item.path,
399
+ content: item.content,
400
+ size: item.size
401
+ });
402
+ }
403
+ }
404
+ if (result.skipped.length > 0) {
405
+ log.warning(`Skipped ${result.skipped.length} file(s):`);
406
+ for (const { path, reason } of result.skipped) {
407
+ log.file(`${path}: ${reason}`);
408
+ }
409
+ }
410
+ return result;
411
+ };
412
+ var getFileExtension = (filePath) => {
413
+ const match = filePath.match(/\.([^.]+)$/);
414
+ return match?.[1] ?? "";
415
+ };
416
+ var isCodeFile = (filePath) => {
417
+ const codeExtensions = /* @__PURE__ */ new Set([
418
+ "ts",
419
+ "tsx",
420
+ "js",
421
+ "jsx",
422
+ "mjs",
423
+ "cjs",
424
+ "py",
425
+ "cs",
426
+ "go",
427
+ "java",
428
+ "rs",
429
+ "kt",
430
+ "swift",
431
+ "cpp",
432
+ "c",
433
+ "h",
434
+ "hpp",
435
+ "rb",
436
+ "php"
437
+ ]);
438
+ return codeExtensions.has(getFileExtension(filePath));
439
+ };
440
+
441
+ export { BASE_AUDIT_PROMPT, DEFAULT_COMMIT_PROMPT, DEFAULT_CONFIG, auditCommit, auditFile, auditFilesWithConcurrency, buildCommitPrompt, buildSystemPrompt, cleanJSON, clearConfigCache, clearModelCache, formatBytes, formatDuration2 as formatDuration, formatDuration as formatDurationParser, getChangedFiles, getCurrentBranch, getFileExtension, getLastCommitMessage, isCodeFile, isGitRepository, loadProjectConfig, log, parseAuditResponse, readFilesForAudit, validateConfig };
442
+ //# sourceMappingURL=lib.js.map
443
+ //# sourceMappingURL=lib.js.map