uneven-ai 1.1.2 → 1.1.4

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/CHANGELOG.md CHANGED
@@ -5,7 +5,29 @@ All notable changes to Uneven AI will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.1.2] - 2026-04-24
8
+ ## [1.1.4] - 2026-04-24
9
+
10
+ ### Changed
11
+ - **Major Dependency Upgrade**: Migrated to **TypeScript 6.0.3** and updated all core npm dependencies (`ts-jest`, `ora`, `mongodb`, `mysql2`, etc.).
12
+ - **Rust Core Update**: Upgraded Rust dependencies including `tokio`, `usearch`, and `candle` for better performance and security.
13
+ - **Improved Init Logic**: The `init` command now performs a clean-up of old state/config before starting, ensuring a fresh installation.
14
+ - **Resilient Path Resolution**: Enhanced the agentic search engine to resolve files even when provided with incomplete paths.
15
+ - **Omni-Support Sync**: Synchronized 60+ file extensions across both TypeScript and Rust layers for consistent project indexing.
16
+
17
+ ## [1.1.3] - 2026-04-24
18
+
19
+ ### Added
20
+
21
+ - **Proactive AutoFix Mode**: The engine now performs "Silent Audits" on file save. It proactively scans for logic bugs, security vulnerabilities, and performance issues even if no terminal error is detected.
22
+ - **Omni-Support: Universal Language Expansion**: Massive update to the knowledge engine, now supporting over 60+ file extensions across all major ecosystems (Web, Mobile, Systems, DevOps, Data Science) synchronized between TS and Rust core.
23
+ - **Agentic Autonomy**: Maria can now perform deep-scan audits directly via chat/ask command, injecting real-time findings into the conversation context.
24
+ - **Intelligent Debouncing**: Refined the fix engine to handle concurrent terminal errors and proactive scans without conflicts.
25
+ - **Dependency Injection**: Refactored orchestration engine for better testability and ESM compatibility.
26
+
27
+ ## [1.1.2] - 2026-04-24 [DEPRECATED]
28
+
29
+ > [!CAUTION]
30
+ > **This version is deprecated.** Users are strongly encouraged to upgrade to **v1.1.3** to benefit from the new Proactive Analysis engine and universal language support.
9
31
 
10
32
  ### Fixed
11
33
 
@@ -1 +1 @@
1
- {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../../../src/application/development/fix/suggest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAA;AAItF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAKzD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CAsCjG"}
1
+ {"version":3,"file":"suggest.d.ts","sourceRoot":"","sources":["../../../../src/application/development/fix/suggest.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qDAAqD,CAAA;AAItF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,EAAE,MAAM,cAAc,CAAA;AAKzD,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,WAAW,GAAG,OAAO,CAAC,aAAa,GAAG,IAAI,CAAC,CA2CjG"}
@@ -1,3 +1,5 @@
1
+ import * as fs from 'fs/promises';
2
+ import * as path from 'path';
1
3
  import { readCodeBlockFromFile } from '../../../infrastructure/utils/error-parser/index.js';
2
4
  import { llmInfer, isNativeAvailable } from '../../../infrastructure/adapters/bridge.js';
3
5
  import { extractField } from './helpers.js';
@@ -29,18 +31,78 @@ export async function doSuggestFix(ctx, error) {
29
31
  }
30
32
  }
31
33
  let suggestion = null;
32
- if (error.type === 'compiler')
34
+ const errorType = error.type;
35
+ if (errorType === 'compiler')
33
36
  suggestion = suggestCompilerFix(error);
34
- else if (error.type === 'runtime')
37
+ else if (errorType === 'runtime')
35
38
  suggestion = suggestRuntimeFix(error);
36
- else if (error.type === 'linter')
39
+ else if (errorType === 'linter')
37
40
  suggestion = suggestLinterFix(error);
41
+ else if (errorType === 'proactive-scan') {
42
+ suggestion = await suggestProactiveScan(ctx, error);
43
+ }
38
44
  if (suggestion) {
39
45
  suggestion.afterCode = ctx.safetyGuard.sanitizeResponse(suggestion.afterCode);
40
46
  suggestion.explanation = ctx.safetyGuard.sanitizeResponse(suggestion.explanation);
41
47
  }
42
48
  return suggestion;
43
49
  }
50
+ /**
51
+ * Special handling for proactive code analysis on file save.
52
+ * It asks the LLM to find bugs, logic errors, or style violations.
53
+ */
54
+ async function suggestProactiveScan(ctx, error) {
55
+ if (!error.file)
56
+ return null;
57
+ // Read the file to analyze its content
58
+ const content = await fs.readFile(error.file, 'utf-8').catch(() => '');
59
+ if (!content.trim())
60
+ return null;
61
+ const prompt = `You are a Senior Software Engineer and Security Auditor.
62
+ Analyze the following code for:
63
+ 1. Logical bugs or edge cases not handled.
64
+ 2. Performance bottlenecks or memory leaks.
65
+ 3. Security vulnerabilities (SQLi, XSS, etc.).
66
+ 4. Style or consistency issues with modern best practices.
67
+
68
+ ### CODE TO ANALYZE (${path.basename(error.file)}):
69
+ \`\`\`
70
+ ${content.slice(0, 4000)}
71
+ \`\`\`
72
+
73
+ ### INSTRUCTIONS:
74
+ - If you find NO issues, respond exactly with: NO_ISSUES_FOUND.
75
+ - If you find a potential issue, respond in this exact format (no extra text):
76
+ EXPLANATION: <one sentence describing the issue found>
77
+ FIX: <specific change required>
78
+ AFTER_CODE: <the corrected version of the affected code block>
79
+ CONFIDENCE: <number between 0.0 and 1.0>`;
80
+ try {
81
+ const response = await llmInfer(prompt, 512);
82
+ const text = response.content.trim();
83
+ if (text.includes('NO_ISSUES_FOUND')) {
84
+ return null;
85
+ }
86
+ const explanation = extractField(text, 'EXPLANATION') ?? '';
87
+ const suggestedFix = extractField(text, 'FIX') ?? '';
88
+ const afterCode = extractField(text, 'AFTER_CODE') ?? '';
89
+ const confidence = parseFloat(extractField(text, 'CONFIDENCE') ?? '0.0');
90
+ if (!explanation || !suggestedFix)
91
+ return null;
92
+ return {
93
+ error,
94
+ explanation,
95
+ suggestedFix,
96
+ beforeCode: '', // Not used for proactive scan
97
+ afterCode,
98
+ confidence: isNaN(confidence) ? 0.7 : confidence,
99
+ };
100
+ }
101
+ catch (err) {
102
+ ctx.logger.debug(`Proactive: LLM analysis failed: ${err}`);
103
+ return null;
104
+ }
105
+ }
44
106
  async function suggestRAFFix(ctx, error, context) {
45
107
  const score = context.avgScore;
46
108
  const confidence = score >= 0.85 ? 0.92 : score >= 0.70 ? 0.78 : 0.65;
@@ -1,3 +1,4 @@
1
+ import { FixEngine } from '../../development/fix/index.js';
1
2
  import type { EngineCtx } from './context.js';
2
3
  /**
3
4
  * doAsk — Knowledge Map Strategy
@@ -9,5 +10,5 @@ import type { EngineCtx } from './context.js';
9
10
  export declare function doAsk(ctx: EngineCtx, question: string, onToken?: (token: string) => void, history?: Array<{
10
11
  role: 'user' | 'assistant';
11
12
  content: string;
12
- }>): Promise<string>;
13
+ }>, fixEngineOverride?: FixEngine): Promise<string>;
13
14
  //# sourceMappingURL=ask.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/ask.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C;;;;;;GAMG;AACH,wBAAsB,KAAK,CACzB,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,OAAO,GAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAM,GACnE,OAAO,CAAC,MAAM,CAAC,CAsGjB"}
1
+ {"version":3,"file":"ask.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/ask.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAA;AAE1D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C;;;;;;GAMG;AACH,wBAAsB,KAAK,CACzB,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,MAAM,EAChB,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,EACjC,OAAO,GAAE,KAAK,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAM,EACpE,iBAAiB,CAAC,EAAE,SAAS,GAC5B,OAAO,CAAC,MAAM,CAAC,CA2IjB"}
@@ -1,8 +1,11 @@
1
1
  import * as fs from 'fs/promises';
2
+ import * as fsSync from 'fs';
2
3
  import * as path from 'path';
3
4
  import { isNativeAvailable, llmEmbed, llmInfer, llmInferStream, retrievalSearch, } from '../../../infrastructure/adapters/bridge.js';
4
5
  import { ExternalProviders } from '../../../infrastructure/adapters/providers/index.js';
5
6
  import { resetUnloadTimer } from './lifecycle.js';
7
+ import { FixEngine } from '../../development/fix/index.js';
8
+ import { KnowledgeRetriever } from '../knowledge-retriever.js';
6
9
  /**
7
10
  * doAsk — Knowledge Map Strategy
8
11
  *
@@ -10,7 +13,7 @@ import { resetUnloadTimer } from './lifecycle.js';
10
13
  * 2. Phase 1: Ask LLM which paths are relevant.
11
14
  * 3. Phase 2: Read relevant files and generate final answer.
12
15
  */
13
- export async function doAsk(ctx, question, onToken, history = []) {
16
+ export async function doAsk(ctx, question, onToken, history = [], fixEngineOverride) {
14
17
  if (!ctx.initialized)
15
18
  throw new Error('Uneven not initialized');
16
19
  try {
@@ -26,15 +29,47 @@ export async function doAsk(ctx, question, onToken, history = []) {
26
29
  ctx.logger.debug(`Relevant paths detected: ${relevantPaths.join(', ') || 'none'}`);
27
30
  // 3. Phase 2: Context Assembly (Read files)
28
31
  let context = '## RELEVANT CONTEXT (KNOWLEDGE MAP)\n\n';
32
+ const isAuditRequest = /audit|scan|scanear|analis|bug|error|erro|corrig|fix/i.test(question);
29
33
  if (relevantPaths.length > 0) {
34
+ const retriever = new KnowledgeRetriever(ctx.logger);
35
+ const fixEngine = fixEngineOverride ?? new FixEngine(ctx.logger, ctx.projectRoot, retriever);
30
36
  for (const relPath of relevantPaths) {
31
37
  try {
32
- const absPath = path.resolve(ctx.projectRoot, relPath);
38
+ let absPath = path.resolve(ctx.projectRoot, relPath);
39
+ // Fallback: If LLM returned only filename, try to find it in the files map
40
+ if (!fsSync.existsSync(absPath)) {
41
+ const fullMap = filesMap.split('\n');
42
+ const found = fullMap.find(p => p.endsWith(relPath) || p.endsWith('/' + relPath));
43
+ if (found) {
44
+ absPath = path.resolve(ctx.projectRoot, found);
45
+ }
46
+ }
33
47
  const content = await fs.readFile(absPath, 'utf8');
34
48
  context += `**File: ${relPath}**\n\`\`\`\n${content}\n\`\`\`\n\n`;
49
+ // Agentic Autonomy: If it's an audit request, run a proactive scan too
50
+ if (isAuditRequest) {
51
+ const virtualError = {
52
+ type: 'proactive-scan',
53
+ severity: 'info',
54
+ code: 'SCAN',
55
+ message: 'Agentic audit request.',
56
+ file: absPath,
57
+ line: 0,
58
+ column: 0,
59
+ context: [],
60
+ fullOutput: 'AGENTIC_AUDIT_TRIGGERED'
61
+ };
62
+ const suggestion = await fixEngine.suggestFix(virtualError);
63
+ if (suggestion && suggestion.confidence > 0.6) {
64
+ context += `**[MARIA AUDIT FINDING for ${relPath}]**\n`;
65
+ context += `ISSUE: ${suggestion.explanation}\n`;
66
+ context += `PROPOSED_FIX: ${suggestion.suggestedFix}\n`;
67
+ context += `CODE_CHANGE:\n${suggestion.afterCode}\n\n`;
68
+ }
69
+ }
35
70
  }
36
71
  catch (e) {
37
- ctx.logger.warning(`Failed to read relevant file ${relPath}: ${e}`);
72
+ ctx.logger.warning(`Failed to read or audit relevant file ${relPath}: ${e}`);
38
73
  }
39
74
  }
40
75
  }
@@ -1 +1 @@
1
- {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4CAA4C,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAA;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAA;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAElF,eAAO,MAAM,YAAY,aAIvB,CAAA;AAEF,eAAO,MAAM,oBAAoB,aAM/B,CAAA;AAEF,eAAO,MAAM,kBAAkB,aAA0E,CAAA;AAEzG,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,cAAc,CAAA;IACvB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,WAAW,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAClC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACxC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3C,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,mBAAmB,CAAA;IACjC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;CAChD"}
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/context.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAA;AACzE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,4CAA4C,CAAA;AACxE,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,2CAA2C,CAAA;AAC/E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC/D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,0CAA0C,CAAA;AAC3E,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,+CAA+C,CAAA;AACrF,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,yCAAyC,CAAA;AAElF,eAAO,MAAM,YAAY,aAIvB,CAAA;AAEF,eAAO,MAAM,oBAAoB,aAkB/B,CAAA;AAEF,eAAO,MAAM,kBAAkB,aAA0E,CAAA;AAEzG,MAAM,WAAW,SAAS;IACxB,MAAM,EAAE,YAAY,CAAA;IACpB,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,cAAc,CAAA;IACvB,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,WAAW,EAAE,OAAO,CAAA;IACpB,OAAO,EAAE,OAAO,CAAA;IAChB,WAAW,EAAE,GAAG,CAAC,MAAM,CAAC,CAAA;IACxB,WAAW,EAAE,MAAM,CAAC,OAAO,GAAG,IAAI,CAAA;IAClC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACvC,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAA;IACxC,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,OAAO,CAAC,CAAA;IAC3C,WAAW,EAAE,WAAW,CAAA;IACxB,YAAY,EAAE,mBAAmB,CAAA;IACjC,gBAAgB,EAAE,gBAAgB,CAAA;IAClC,UAAU,EAAE,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI,CAAA;IAC/B,WAAW,EAAE,MAAM,CAAA;IACnB,SAAS,CAAC,IAAI,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,GAAG,IAAI,CAAA;CAChD"}
@@ -4,10 +4,22 @@ export const IGNORED_DIRS = new Set([
4
4
  'tests', 'test', 'spec', 'mocks', '__tests__',
5
5
  ]);
6
6
  export const SUPPORTED_EXTENSIONS = new Set([
7
- '.ts', '.tsx', '.js', '.jsx',
8
- '.json', '.md', '.txt', '.yaml', '.yml',
9
- '.rs', '.py', '.go', '.java', '.cpp', '.c', '.toml',
7
+ // Web & Frontend (Frameworks: React, Vue, Svelte, Astro, etc.)
8
+ '.ts', '.tsx', '.js', '.jsx', '.vue', '.svelte', '.astro', '.html', '.css', '.scss', '.sass', '.less', '.pcss', '.styl',
9
+ '.json', '.md', '.txt', '.yaml', '.yml', '.sql', '.xml', '.svg', '.graphql', '.gql',
10
+ // Templating
11
+ '.pug', '.jade', '.twig', '.hbs', '.ejs', '.liquid',
12
+ // Backend & Systems (Rust, Go, C++, Java, C#, PHP, Ruby, etc.)
13
+ '.rs', '.py', '.pyi', '.go', '.java', '.kt', '.kts', '.swift', '.m', '.h', '.cpp', '.c', '.cc', '.hh', '.hpp',
14
+ '.cs', '.lua', '.php', '.rb', '.ex', '.exs', '.erl', '.hrl', '.fs', '.nim', '.zig', '.sol',
15
+ // Functional Programming
16
+ '.scala', '.sc', '.groovy', '.clj', '.cljs', '.cljc', '.edn', '.hs', '.lhs', '.ml', '.mli', '.pl', '.pro', '.lisp', '.scm', '.cl', '.el',
17
+ // Data Science, AI & Analysis
18
+ '.r', '.jl', '.ipynb', '.f', '.f90', '.f95',
19
+ // Infrastructure, DevOps & Config
20
+ '.toml', '.prisma', '.proto', '.dockerfile', '.dockerignore', '.tf', '.tfvars', '.hcl', '.ini', '.conf', '.editorconfig', '.env.example',
21
+ '.sh', '.bash', '.zsh', '.cbl', '.cob', '.asm', '.s', '.v', '.vhdl',
22
+ // Documents & Data
10
23
  '.xls', '.xlsx', '.csv', '.docx', '.pdf',
11
- '.cbl', '.cob', '.asm', '.s',
12
24
  ]);
13
25
  export const IGNORED_EXTENSIONS = new Set(['.env', '.pem', '.key', '.crt', '.pfx', '.db-shm', '.db-wal']);
@@ -2,6 +2,10 @@ import type { FixEngine } from '../../development/fix/index.js';
2
2
  import type { PendingDiff } from '../../../domain/entities/session/index.js';
3
3
  import type { EngineCtx } from './context.js';
4
4
  export declare function handleErrorFix(ctx: EngineCtx, error: any, fixEngine: FixEngine, confirmBeforeFix: boolean): void;
5
+ /**
6
+ * Handle proactive fixes triggered by file changes (without explicit terminal errors).
7
+ */
8
+ export declare function handleProactiveFix(ctx: EngineCtx, filePath: string, fixEngine: FixEngine, confirmBeforeFix: boolean): void;
5
9
  export declare function runFixForError(ctx: EngineCtx, error: any, fixKey: string, fixEngine: FixEngine, confirmBeforeFix: boolean): Promise<void>;
6
10
  export declare function applyPendingDiff(ctx: EngineCtx, diff: PendingDiff, fixEngine: FixEngine): Promise<void>;
7
11
  //# sourceMappingURL=error-handler.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/error-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAC5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,GAAG,EACV,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,OAAO,GACxB,IAAI,CAoCN;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC,CAsGf;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC7G"}
1
+ {"version":3,"file":"error-handler.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/error-handler.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAiB,MAAM,gCAAgC,CAAA;AAC9E,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,2CAA2C,CAAA;AAE5E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAgB,cAAc,CAC5B,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,GAAG,EACV,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,OAAO,GACxB,IAAI,CAoCN;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,SAAS,EACd,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,OAAO,GACxB,IAAI,CA8CN;AAED,wBAAsB,cAAc,CAClC,GAAG,EAAE,SAAS,EACd,KAAK,EAAE,GAAG,EACV,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,SAAS,EACpB,gBAAgB,EAAE,OAAO,GACxB,OAAO,CAAC,IAAI,CAAC,CAsGf;AAED,wBAAsB,gBAAgB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAwC7G"}
@@ -1,3 +1,4 @@
1
+ import * as path from 'path';
1
2
  export function handleErrorFix(ctx, error, fixEngine, confirmBeforeFix) {
2
3
  const fixKey = `${error.file ?? 'unknown'}:${error.line ?? 0}`;
3
4
  const fileKey = error.file ?? 'unknown';
@@ -30,6 +31,52 @@ export function handleErrorFix(ctx, error, fixEngine, confirmBeforeFix) {
30
31
  ctx.fileFixQueue.delete(fileKey);
31
32
  });
32
33
  }
34
+ /**
35
+ * Handle proactive fixes triggered by file changes (without explicit terminal errors).
36
+ */
37
+ export function handleProactiveFix(ctx, filePath, fixEngine, confirmBeforeFix) {
38
+ const fixKey = `proactive:${filePath}`;
39
+ const fileKey = filePath;
40
+ // Prevent spamming analysis for the same file if already active
41
+ if (ctx.activeFixes.has(fixKey))
42
+ return;
43
+ // Debounce: wait 1.5s after the last save to avoid scanning while typing/saving fast
44
+ const existingTimer = ctx.debounceTimers.get(fixKey);
45
+ if (existingTimer)
46
+ clearTimeout(existingTimer);
47
+ const timer = setTimeout(async () => {
48
+ ctx.debounceTimers.delete(fixKey);
49
+ // If there's an active terminal error fix for this file, skip proactive scan
50
+ if (ctx.activeFixes.has(fileKey)) {
51
+ ctx.logger.debug(`Proactive: skipping scan for ${filePath} because a terminal fix is active.`);
52
+ return;
53
+ }
54
+ ctx.activeFixes.add(fixKey);
55
+ try {
56
+ const virtualError = {
57
+ type: 'proactive-scan',
58
+ severity: 'info',
59
+ code: 'SCAN',
60
+ message: 'Proactive quality and bug scan requested on file save.',
61
+ file: filePath,
62
+ line: 0,
63
+ column: 0,
64
+ context: [],
65
+ fullOutput: 'PROACTIVE_SCAN_TRIGGERED'
66
+ };
67
+ ctx.logger.debug(`Proactive: analyzing ${path.basename(filePath)} for bugs/improvements...`);
68
+ // We ALWAYS force confirmation for proactive fixes to avoid unwanted changes
69
+ await runFixForError(ctx, virtualError, fixKey, fixEngine, true);
70
+ }
71
+ catch (err) {
72
+ ctx.logger.debug(`Proactive: scan failed for ${filePath}: ${err}`);
73
+ }
74
+ finally {
75
+ ctx.activeFixes.delete(fixKey);
76
+ }
77
+ }, 1500); // 1.5s debounce for proactive scans
78
+ ctx.debounceTimers.set(fixKey, timer);
79
+ }
33
80
  export async function runFixForError(ctx, error, fixKey, fixEngine, confirmBeforeFix) {
34
81
  const isActive = ctx.running || ctx.initialized;
35
82
  if (!isActive) {
@@ -1 +1 @@
1
- {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/watcher.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CA0I3D"}
1
+ {"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../../../../src/application/orchestration/engine/watcher.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,cAAc,CAAA;AAE7C,wBAAsB,OAAO,CAAC,GAAG,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAsJ3D"}
@@ -48,10 +48,24 @@ export async function doWatch(ctx) {
48
48
  watcher: new ProcessWatcher({ command: cmd, autoRestart: true, maxRestarts: 3 }, ctx.logger),
49
49
  }));
50
50
  const watchDirs = ctx.config.knowledge?.dirs?.length ? ctx.config.knowledge.dirs : [process.cwd()];
51
- const fileWatcher = new FileWatcher({ paths: watchDirs, extensions: ['.ts', '.js', '.tsx', '.jsx', '.py', '.go', '.rs', '.md'] }, ctx.logger);
51
+ const { SUPPORTED_EXTENSIONS } = await import('./context.js');
52
+ const fileWatcher = new FileWatcher({
53
+ paths: watchDirs,
54
+ extensions: Array.from(SUPPORTED_EXTENSIONS)
55
+ }, ctx.logger);
56
+ const retriever = new KnowledgeRetriever(ctx.logger);
57
+ const fixEngine = new FixEngine(ctx.logger, ctx.projectRoot, retriever, {
58
+ acquire: (name) => ctx.session.acquireLock(name),
59
+ release: (lockId) => ctx.session.releaseLock(lockId),
60
+ });
61
+ const { handleProactiveFix } = await import('./error-handler.js');
52
62
  fileWatcher.on('change', (filePath) => {
53
63
  ctx.logger.info(`FileWatcher: change detected — ${filePath}`);
54
64
  ctx.emitEvent('file-changed', { path: filePath });
65
+ if (autoFix) {
66
+ // Trigger proactive analysis on save
67
+ handleProactiveFix(ctx, filePath, fixEngine, confirmBeforeFix);
68
+ }
55
69
  });
56
70
  fileWatcher.on('add', (filePath) => {
57
71
  ctx.logger.debug(`FileWatcher: new file — ${filePath}`);
@@ -62,11 +76,6 @@ export async function doWatch(ctx) {
62
76
  ctx.emitEvent('file-removed', { path: filePath });
63
77
  });
64
78
  await fileWatcher.start();
65
- const retriever = new KnowledgeRetriever(ctx.logger);
66
- const fixEngine = new FixEngine(ctx.logger, ctx.projectRoot, retriever, {
67
- acquire: (name) => ctx.session.acquireLock(name),
68
- release: (lockId) => ctx.session.releaseLock(lockId),
69
- });
70
79
  let errorCount = 0;
71
80
  let stoppedCount = 0;
72
81
  for (const { label, watcher } of watchers) {
@@ -1 +1 @@
1
- {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/init/command.ts"],"names":[],"mappings":"AAeA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CA2JjD"}
1
+ {"version":3,"file":"command.d.ts","sourceRoot":"","sources":["../../../../src/cli/commands/init/command.ts"],"names":[],"mappings":"AAeA,wBAAsB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC,CAuKjD"}
@@ -41,11 +41,22 @@ export async function initCommand() {
41
41
  console.log(t.dim(' This will only take a moment.'));
42
42
  console.log();
43
43
  const spinner = ora({ text: t.dim('Preparing workspace...'), color: 'blue' }).start();
44
+ // 1. Pre-init Cleanup (as requested by user)
45
+ // We remove existing state/logs but keep models to avoid 1.4GB redownload
46
+ try {
47
+ await fs.rm('.uneven/cache', { recursive: true, force: true });
48
+ await fs.rm('.uneven/logs', { recursive: true, force: true });
49
+ await fs.rm('.uneven/index-state.json', { force: true });
50
+ await fs.rm('.uneven/config.json', { force: true });
51
+ await fs.rm('.uneven/vectors.json', { force: true });
52
+ await fs.rm('uneven-reports', { recursive: true, force: true });
53
+ }
54
+ catch { /* ignore */ }
44
55
  await fs.mkdir('.uneven/cache', { recursive: true });
45
56
  await fs.mkdir('.uneven/models', { recursive: true });
46
57
  await fs.mkdir('.uneven/logs', { recursive: true });
47
58
  await fs.mkdir('uneven-reports', { recursive: true });
48
- spinner.succeed(t.crystal('Workspace ready') + t.dim(' (.uneven/ and uneven-reports/)'));
59
+ spinner.succeed(t.crystal('Workspace prepared') + t.dim(' (Cleaned and ready)'));
49
60
  section('Which brain should power Uneven?');
50
61
  const providers = [
51
62
  { key: '1', id: 'local', label: 'Maria', note: `${chalk.green('Recommended:')} 100% Private and Sovereign` },
@@ -71,13 +82,12 @@ export async function initCommand() {
71
82
  blank();
72
83
  ok(`Brain set to ${t.bright(PROVIDER_LABELS[provider] ?? provider)}`);
73
84
  const newConfigPath = path.join(process.cwd(), 'uneven.config.ts');
74
- if (await fileExists(newConfigPath)) {
75
- warn('uneven.config.ts already exists keeping it');
76
- }
77
- else {
78
- await fs.writeFile(newConfigPath, makeConfig(provider, recommendedThreads, recommendedGpuLayers));
79
- ok('uneven.config.ts created');
85
+ try {
86
+ await fs.rm(newConfigPath, { force: true });
80
87
  }
88
+ catch { /* ignore */ }
89
+ await fs.writeFile(newConfigPath, makeConfig(provider, recommendedThreads, recommendedGpuLayers));
90
+ ok('uneven.config.ts created');
81
91
  await saveConfig({
82
92
  brain: {
83
93
  provider: provider,
@@ -1 +1 @@
1
- {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/cli/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,CAAC;gBACE,MAAM;gBACN,MAAM;iBACN,MAAM;aACN,MAAM;eACN,MAAM;cACN,MAAM;cACN,MAAM;oBACJ,MAAM;CACvB,CAAA;AAUD,eAAO,MAAM,GAAG;IACd,oCAAoC;oBACpB,MAAM,GAAG,IAAI;IAa7B,sBAAsB;eACX,IAAI;IAIf,qCAAqC;kBACvB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;CAW5E,CAAA;AAID,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI3C;AAID,eAAO,MAAM,EAAE,QAAY,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,QAAU,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,QAAU,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,QAAU,MAAM,SAAgD,CAAA;AACjF,eAAO,MAAM,IAAI,QAAU,MAAM,SAAsC,CAAA;AACvE,eAAO,MAAM,KAAK,YAAiC,CAAA;AAInD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGvD;AAID,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMlD,CAAA"}
1
+ {"version":3,"file":"theme.d.ts","sourceRoot":"","sources":["../../src/cli/theme.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAMH,eAAO,MAAM,CAAC;gBACE,MAAM;gBACN,MAAM;iBACN,MAAM;aACN,MAAM;eACN,MAAM;cACN,MAAM;cACN,MAAM;oBACJ,MAAM;CACvB,CAAA;AAUD,eAAO,MAAM,GAAG;IACd,oCAAoC;oBACpB,MAAM,GAAG,IAAI;IAa7B,sBAAsB;eACX,IAAI;IAIf,qCAAqC;kBACvB,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,IAAI;CAW5E,CAAA;AAID,wBAAgB,OAAO,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,CAI3C;AAID,eAAO,MAAM,EAAE,GAAO,KAAK,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,GAAK,KAAK,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,GAAK,KAAK,MAAM,SAAmD,CAAA;AACpF,eAAO,MAAM,IAAI,GAAK,KAAK,MAAM,SAAgD,CAAA;AACjF,eAAO,MAAM,IAAI,GAAK,KAAK,MAAM,SAAsC,CAAA;AACvE,eAAO,MAAM,KAAK,YAAiC,CAAA;AAInD,wBAAgB,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAGvD;AAID,eAAO,MAAM,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAMlD,CAAA"}
@@ -1,5 +1,5 @@
1
1
  export interface ParsedError {
2
- type: 'compiler' | 'runtime' | 'linter' | 'test' | 'unknown';
2
+ type: 'compiler' | 'runtime' | 'linter' | 'test' | 'proactive-scan' | 'unknown';
3
3
  severity: 'error' | 'warning' | 'info';
4
4
  file: string;
5
5
  line: number;
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/utils/error-parser/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,SAAS,CAAA;IAC5D,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../../src/infrastructure/utils/error-parser/types.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,UAAU,GAAG,SAAS,GAAG,QAAQ,GAAG,MAAM,GAAG,gBAAgB,GAAG,SAAS,CAAA;IAC/E,QAAQ,EAAE,OAAO,GAAG,SAAS,GAAG,MAAM,CAAA;IACtC,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;IACZ,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,EAAE,MAAM,CAAA;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,EAAE,MAAM,EAAE,CAAA;IACjB,UAAU,EAAE,MAAM,CAAA;CACnB"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "uneven-ai",
3
- "version": "1.1.2",
3
+ "version": "1.1.4",
4
4
  "publishConfig": {
5
5
  "access": "public",
6
6
  "registry": "https://registry.npmjs.org/"
@@ -70,12 +70,12 @@
70
70
  "exceljs": "^4.4.0",
71
71
  "knex": "^3.2.9",
72
72
  "mammoth": "^1.12.0",
73
- "mongodb": "^7.1.1",
74
- "mysql2": "^3.20.0",
75
- "ora": "^9.3.0",
76
- "pdfjs-dist": "^5.0.0",
73
+ "mongodb": "^7.2.0",
74
+ "mysql2": "^3.22.2",
75
+ "ora": "^9.4.0",
76
+ "pdfjs-dist": "^5.6.205",
77
77
  "pg": "^8.20.0",
78
- "undici": "^8.0.2"
78
+ "undici": "^8.1.0"
79
79
  },
80
80
  "overrides": {
81
81
  "chokidar": "^5.0.0"
@@ -89,9 +89,9 @@
89
89
  "@typescript-eslint/utils": "^8.58.0",
90
90
  "eslint": "^10.2.0",
91
91
  "jest": "^29.0.0",
92
- "jest-junit": "^16.0.0",
93
- "prettier": "^3.8.1",
94
- "ts-jest": "^29.2.5",
95
- "typescript": "^5.7.3"
92
+ "jest-junit": "^17.0.0",
93
+ "prettier": "^3.8.3",
94
+ "ts-jest": "^29.4.9",
95
+ "typescript": "^6.0.3"
96
96
  }
97
97
  }