@wispbit/local 1.0.86 → 1.0.87
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/cli-bundle.js +1 -1
- package/package.json +1 -1
package/cli-bundle.js
CHANGED
|
@@ -291,7 +291,7 @@ ${e}`);let r={id:String(++this.requestId),command:"shutdown"};this.serverProcess
|
|
|
291
291
|
${u}`:r.message;c(new Error(l))}else a(r);else if(i)if(i==="success")a(o);else{let u=this.getRecentStderr(),l=u?`${s||"Request failed"}. Runtime logs:
|
|
292
292
|
${u}`:s||"Request failed";c(new Error(l))}else Ae.log("[Runtime] Unknown response format"),c(new Error("Unknown response format"))}getRuntimePath(){let e=globalThis.__POWERLINT_ASSETS__;if(!e?.runtime)throw new Error("Runtime binary not found. This should be set by the Bun executable build. Make sure you're running the compiled Bun executable, not the source code directly.");return Ae.log(`[RuntimeProvider] Using runtime from Bun executable: ${e.runtime}`),e.runtime}};import{homedir as gMt}from"os";import{join as RMt}from"path";import{promises as XW}from"fs";import*as By from"path";var kg=class{groups;constructor(e){this.groups=e}patternCount(){return this.groups.length}patternCost(){return this.groups.reduce((e,n)=>e+n.cost*n.fragments.length,0)}patternDensity(){let e=this.patternCount();return e>0?this.patternCost()/e:0}fragmentOccurrences(e){let n=this.groups.find(r=>r.hash===e);return n?n.fragments.length:0}patternHash(e){return this.groups[e]?.hash}fileCount(){let e=new Set;for(let n of this.groups)for(let r of n.fragments)e.add(r.file);return e.size}title(e){let n=this.groups.find(o=>o.hash===e);if(!n||n.fragments.length===0)return"Unknown pattern";let r=n.fragments[0];return`${r.file.split("/").pop()||r.file}:${r.start_line+1}-${r.end_line+1} (${n.fragments.length} occurrences)`}fragmentsByFile(e){let n=new Map,r=this.groups.find(i=>i.hash===e);if(!r)return n;for(let i of r.fragments){let o=n.get(i.file)||[];o.push(i),n.set(i.file,o)}return n}getGroups(){return this.groups}async getFragmentText(e,n){let r=By.join(n,e.file),i=await XW.readFile(r,"utf-8");return Buffer.from(i,"utf-8").slice(e.start_index,e.end_index).toString("utf-8")}getFragmentsInFile(e){let n=[];for(let r of this.groups)for(let i of r.fragments)i.file===e&&n.push(i);return n}getStatistics(){let e=this.groups.reduce((n,r)=>n+r.fragments.length,0);return{patternCount:this.patternCount(),totalCost:this.patternCost(),averageCost:this.patternDensity(),fileCount:this.fileCount(),totalFragments:e}}async extractFragmentSnippet(e,n,r){let i;if(r.has(e.file))i=r.get(e.file);else{let s=By.join(n,e.file);i=await XW.readFile(s,"utf-8"),r.set(e.file,i)}return Buffer.from(i,"utf-8").slice(e.start_index,e.end_index).toString("utf-8")}async loadFileContents(e){let n=new Map,r=new Set;for(let i of this.groups)for(let o of i.fragments)r.add(o.file);return await Promise.all(Array.from(r).map(async i=>{let o=By.join(e,i),s=await XW.readFile(o,"utf-8");n.set(i,s)})),n}extractFragmentContext(e,n,r=20){let i=n.get(e.file);if(!i)throw new Error(`File content not available for ${e.file}`);let o=i.split(`
|
|
293
293
|
`),s=e.start_line,a=Math.max(0,s-r),c=Math.min(o.length-1,s+r),u=o.slice(a,c+1).join(`
|
|
294
|
-
`);return{file:e.file,range:{start:{line:a+1,column:1},end:{line:c+1,column:o[c]?.length||1}},text:u}}extractGroupContext(e,n,r=20){return e.fragments.map(i=>this.extractFragmentContext(i,n,r))}clusterBySharedFiles(){if(this.groups.length===0)return[];let e=new Map;this.groups.forEach((o,s)=>{let a=new Set;for(let c of o.fragments)a.add(c.file);for(let c of a)e.has(c)||e.set(c,[]),e.get(c).push(s)});let n=new qW(this.groups.length);for(let o of e.values())for(let s=1;s<o.length;s++)n.union(o[0],o[s]);let r=new Map;for(let o=0;o<this.groups.length;o++){let s=n.find(o);r.has(s)||r.set(s,[]),r.get(s).push(o)}let i=[];for(let o of r.values()){let s=o.map(c=>this.groups[c]),a=new Set;for(let c of s)for(let u of c.fragments)a.add(u.file);i.push({groups:s,files:a})}return i}},qW=class{parent=new Map;rank=new Map;constructor(e){for(let n=0;n<e;n++)this.parent.set(n,n),this.rank.set(n,0)}find(e){return this.parent.get(e)!==e&&this.parent.set(e,this.find(this.parent.get(e))),this.parent.get(e)}union(e,n){let r=this.find(e),i=this.find(n);if(r===i)return;let o=this.rank.get(r),s=this.rank.get(i);o<s?this.parent.set(r,i):o>s?this.parent.set(i,r):(this.parent.set(i,r),this.rank.set(r,o+1))}};var Fy=class t{static INDEX_FOLDER=RMt(gMt(),".wispbit","indexes");provider;initialized=!1;constructor(e){this.provider=e}async initializeProvider(){this.initialized||(await this.provider.initialize(),this.initialized=!0)}async initialize(e){await this.initializeProvider();let n=await this.provider.sendRequest({command:"initialize_workspaces",workspace_configs:e.workspaceConfigs.map(r=>({workspace_root:r.workspaceRoot,base_commit_sha:r.baseCommitSha})),index_folder:t.INDEX_FOLDER,ignores:e.ignores});if("error"in n&&n.error)throw new Error(`Failed to initialize workspaces: ${n.error}`);return{fullScan:n.full_scan||!1}}async getDuplicates(e,n){await this.initializeProvider();let r=await this.provider.sendRequest({command:"get_duplicates",workspace_root:e,file_path:n});if("error"in r&&r.error)throw new Error(`Failed to get duplicates: ${r.error}`);let i=r;return new kg(i)}async getDuplicatesForFile(e,n){await this.initializeProvider();let r=await this.provider.sendRequest({command:"get_duplicates",workspace_root:e,file_path:n});if("error"in r&&r.error)throw new Error(`Failed to get duplicates for file: ${r.error}`);let i=r;return new kg(i)}invalidationQueue=Promise.resolve();async invalidateFiles(e,n){let r=this.invalidationQueue.then(async()=>{await this.initializeProvider();let i=Date.now(),o=await this.provider.sendRequest({command:"invalidate_files",workspace_root:e,files:n.map(a=>({file_path:a.filePath,content_sha:a.contentSha}))}),s=Date.now()-i;if(Ae.log(`[RuntimeManager] Invalidated ${n.length} file(s) in ${s}ms for ${e}`),"error"in o&&o.error)throw new Error(`Failed to invalidate files: ${o.error}`)}).catch(i=>{throw Ae.log(`[RuntimeManager] Invalidation failed: ${i.message}`),i});this.invalidationQueue=r.catch(()=>{}),await r}isInitialized(){return this.initialized}async shutdown(){this.initialized&&(Ae.log("[RuntimeManager] Shutdown requested, waiting for in-flight operations..."),await this.invalidationQueue.catch(()=>{}),Ae.log("[RuntimeManager] All operations complete, shutting down runtime..."),await this.provider.shutdown(),this.initialized=!1)}};var ky=class{contexts=new Map;workspacePath;apiKey;lspClient;sharedRuntimeManager=null;RULES_CACHE_TTL_MS=300*1e3;constructor(e){this.workspacePath=e.workspacePath,this.apiKey=e.apiKey,this.lspClient=e.lspClient}async discoverAndInitialize(e,n){let r=await rO(this.workspacePath),i=[];for(let s of r){let a=await this.initializeContext(s).catch(c=>(n?.(s,c),null));if(a){let{baseCommitSha:c}=await ga(s);i.push({repoRoot:s,context:a,baseCommitSha:c}),this.contexts.set(s,a),e?.(s,a)}}let o=[];for(let{repoRoot:s,context:a,baseCommitSha:c}of i)a.config.getEnableDeduplicator()&&o.push({workspaceRoot:s,baseCommitSha:c});if(o.length>0){let s=new Gy(o[0].workspaceRoot);this.sharedRuntimeManager=new Fy(s);for(let{context:a}of i)a.config.getEnableDeduplicator()&&(a.runtimeManager=this.sharedRuntimeManager,a.duplicateManager=new Vy(this.sharedRuntimeManager,a.config,a.environment))}}async initializeContext(e){let n=new Kr({workspaceRoot:e}),r=await $o.initialize(n,{apiKey:this.apiKey,lspClient:this.lspClient});if("failed"in r)throw new Error(`Config initialization failed for ${e}: ${r.error}`);let i=r,o=new jo(i,n),s=new zr,a=new La(i,n,s);return{repoRoot:e,environment:n,config:i,ruleProvider:o,eventEmitter:s,ruleExecutor:a,cachedRules:null,rulesById:new Map,rulesCacheTimestamp:0,rulesLoadingPromise:null,lastCommitSelector:null,currentExecutionRules:[],completedRulesCount:0,isExecuting:!1,currentExecutionPromise:null,runtimeManager:null,duplicateManager:null,executionContext:null}}findContextForFile(e){let n=e.toLowerCase();return Array.from(this.contexts.entries()).filter(([i])=>{let o=i.toLowerCase();return n.startsWith(o+NMt)||n===o}).sort((i,o)=>o[0].length-i[0].length)[0]?.[1]||null}getAllContexts(){return Array.from(this.contexts.values())}getRepoCount(){return this.contexts.size}hasRepositories(){return this.contexts.size>0}getContext(e){return this.contexts.get(e)}async initializeRuntimeManager(){if(!this.sharedRuntimeManager)return!1;let e=[],n=[];for(let r of this.contexts.values())if(r.config.getEnableDeduplicator()){let{baseCommitSha:i}=await ga(r.repoRoot);e.push({workspaceRoot:r.repoRoot,baseCommitSha:i}),n.length===0&&(n=r.config.getIgnoredGlobs())}return e.length===0?!1:(await this.sharedRuntimeManager.initialize({workspaceConfigs:e,ignores:n}),!0)}async shutdown(){for(let e of this.contexts.values())e.ruleExecutor.clearCache(),e.duplicateManager&&e.duplicateManager.clearCache();this.sharedRuntimeManager&&(await this.sharedRuntimeManager.shutdown(),this.sharedRuntimeManager=null),this.contexts.clear()}};function LWe(t,e,n){if(t.fingerprint)return`${e}:${t.fingerprint}`;let r=t.text?.slice(0,100)||"",i=`${t.range.start.line}:${t.range.start.column}-${t.range.end.line}:${t.range.end.column}`;return`${e}:${n}:${i}:${CMt(r)}`}function CMt(t){let e=0;for(let n=0;n<t.length;n++){let r=t.charCodeAt(n);e=(e<<5)-e+r,e=e&e}return e.toString(36)}function yWe(t){return t.end.line-t.start.line+1>20?{start:{line:t.start.line-1,character:t.start.column-1},end:{line:t.start.line-1,character:Number.MAX_SAFE_INTEGER}}:{start:{line:t.start.line-1,character:t.start.column-1},end:{line:t.end.line-1,character:t.end.column-1}}}var QW=class{connection;documentManager;repositoryContextManager;workspacePath;apiKey;lspClient;batchProgressReporter=null;batchProgressToken="wispbit-batch-lint";progressTokenCreated=!1;fixQueue=[];isProcessingFix=!1;branchCheckTimer=null;BRANCH_CHECK_DEBOUNCE_MS=1e3;branchCheckInterval=null;BRANCH_CHECK_INTERVAL_MS=3e4;executionDebounceTimer=null;EXECUTION_DEBOUNCE_MS=1e3;changedFiles=new Set;hasRunInitialCheck=!1;isExecutingInitial=!1;dismissalsLoadingPromises=new Map;fixesLoadingPromises=new Map;localDismissals=[];constructor(e){this.workspacePath=e.workspacePath,this.apiKey=e.apiKey,this.lspClient=e.lspClient,this.connection=(0,mr.createConnection)(mr.ProposedFeatures.all,process.stdin,process.stdout),this.documentManager=new xy,this.repositoryContextManager=new ky({workspacePath:this.workspacePath,apiKey:this.apiKey,lspClient:this.lspClient}),this.registerHandlers()}addLocalDismissal(e){this.localDismissals.push(e)}isLocallyDismissed(e,n,r,i,o){return this.localDismissals.some(s=>s.matchId===o&&s.uri===e?!0:s.uri===e&&s.ruleId===n?s.startLine<=i&&s.endLine>=r:!1)}clearLocalDismissalsForUri(e){this.localDismissals=this.localDismissals.filter(n=>n.uri!==e)}clearAllLocalDismissals(){this.localDismissals=[]}findContextForFile(e){let n=Uo(e);return this.repositoryContextManager.findContextForFile(n)}pathToNormalizedUri(e){let n=OMt(e).toString();return decodeURIComponent(n)}getDiagnosticsForUri(e){let n=this.documentManager.getDiagnostics(e);if(n.length===0){let r=oi(e),i=this.documentManager.getAllDiagnosticUris();for(let o of i)if(oi(o)===r){n=this.documentManager.getDiagnostics(o);break}}return n}handleRuleComplete(e,n,r){if(!e.isExecuting||e.currentExecutionRules.length===0)return;e.completedRulesCount++;let i=Math.round(e.completedRulesCount/e.currentExecutionRules.length*100);this.updateBatchProgress(i,n)}async handleRuleFileComplete(e,n,r,i){if(!e.isExecuting||e.currentExecutionRules.length===0)return;let o=e.currentExecutionRules.find(u=>u.id===n);if(!o){this.connection.console.log(`[${e.repoRoot}] Warning: Rule ${n} not found in current execution`);return}let s=CE(e.repoRoot,r),a=this.pathToNormalizedUri(s);if(i.length===0){this.updateDiagnosticsForRule(a,o.internalId,[]);return}let c=await this.createDiagnosticsForFile(e,r,o.internalId,i,e.currentExecutionRules);this.updateDiagnosticsForRule(a,o.internalId,c)}async handleRuleFilePartial(e,n,r,i,o){if(!e.isExecuting||e.currentExecutionRules.length===0)return;let s=e.currentExecutionRules.find(l=>l.internalId===n);if(!s){this.connection.console.log(`[${e.repoRoot}] Warning: Rule ${n} not found in current execution`);return}let a=CE(e.repoRoot,r),c=this.pathToNormalizedUri(a);if(i.length===0){this.updateDiagnosticsForRule(c,s.internalId,[]);return}let u=await this.createDiagnosticsForFile(e,r,s.internalId,i,e.currentExecutionRules);this.updateDiagnosticsForRule(c,s.internalId,u)}async convertDuplicateResultsToDiagnostics(e,n,r,i){let o=[];for(let s of n)for(let a of s.matches){let c=LWe(a,"DUPLICATE",i);if(this.isLocallyDismissed(i,"DUPLICATE",a.range.start.line,a.range.end.line,c))continue;let u=yWe(a.range),l=a.evidence?.map(f=>{let T=CE(e.repoRoot,f.file.path);return{location:{uri:this.pathToNormalizedUri(T),range:{start:{line:f.range.start.line-1,character:f.range.start.column-1},end:{line:f.range.end.line-1,character:f.range.end.column-1}}},message:"Duplicate location"}}),E=null;a.edits&&a.edits.length>0&&(E=await this.computeFixForMatch(a,e).catch(f=>(this.connection.console.log(`Warning: Failed to compute fix for duplicate: ${f}`),null)));let d={range:u,severity:mr.DiagnosticSeverity.Warning,message:a.description||"Duplicate code detected",code:"DUPLICATE",source:"wispbit",relatedInformation:l,data:{uri:i,match:a,matchId:c,fix:E?{diff:E.diff,diffs:E.diffs,autofix:!1}:null,ruleId:"DUPLICATE",filePath:a.file.path,rule:{id:"DUPLICATE",displayId:"DUPLICATE",summary:null}}};o.push(d)}return o}async handleDuplicateFileComplete(e,n,r,i,o){let s=CE(e.repoRoot,n),a=this.pathToNormalizedUri(s),c=await this.createDiagnosticsForFile(e,n,"DUPLICATE",r,[],this.convertDuplicateResultsToDiagnostics.bind(this));this.updateDiagnosticsForRule(a,"DUPLICATE",c)}setupProgressListenersForContext(e){e.eventEmitter.on("rules:progress",n=>{this.updateBatchProgress(n.percentage,n.ruleId)}),e.eventEmitter.on("rules:rule-complete",n=>{this.handleRuleComplete(e,n.ruleId,n.matches)}),e.eventEmitter.on("rules:rule-file-complete",async n=>{await this.handleRuleFileComplete(e,n.ruleId,n.filePath,n.matches)}),e.eventEmitter.on("rules:rule-file-partial",async n=>{await this.handleRuleFilePartial(e,n.ruleId,n.filePath,n.matches,n.totalMatches)})}toRelativePath(e,n){if(!n){let r=this.pathToNormalizedUri(e);n=this.findContextForFile(r)||void 0}return n?DWe(n.repoRoot,e):DWe(this.workspacePath,e)}updateDiagnosticsForRule(e,n,r){this.documentManager.setDiagnosticsForRule(e,n,r);let i=this.documentManager.getDiagnostics(e);this.connection.sendDiagnostics({uri:e,diagnostics:i})}removeDiagnosticByMatchId(e,n){let r=this.documentManager.getDiagnostics(e),i=r.find(a=>a.data?.matchId===n);if(!i)return;let o=i.data?.rule?.id;if(!o)return;let s=r.filter(a=>a.data?.rule?.id===o&&a.data?.matchId!==n);this.updateDiagnosticsForRule(e,o,s)}clearAllDiagnostics(){let e=this.documentManager.getAllDiagnosticUris();for(let n of e)this.clearDiagnosticsForFile(n);this.clearAllLocalDismissals()}clearDiagnosticsForFile(e){this.documentManager.clearDiagnostics(e),this.clearLocalDismissalsForUri(e),this.connection.sendDiagnostics({uri:e,diagnostics:[]})}handleDocumentChangesWithRanges(e,n){let r=this.documentManager.getDiagnostics(e);if(r.length===0)return;this.connection.console.log(`Document changed: ${e}, ${n.length} change(s), ${r.length} diagnostic(s)`);let i=new Set;for(let o of n){if(!o.range){this.connection.console.log("Full document change detected - removing all diagnostics");for(let s of r)s.data?.matchId&&i.add(s.data.matchId);continue}for(let s of r)this.rangesOverlap(o.range,s.range)&&s.data?.matchId&&i.add(s.data.matchId)}for(let o of i)this.removeDiagnosticByMatchId(e,o);i.size>0&&this.connection.console.log(`Removed ${i.size} diagnostic(s) due to document edits`)}rangesOverlap(e,n){return e.end.line<n.start.line||e.start.line>n.end.line?!1:e.start.line<=n.end.line&&e.end.line>=n.start.line}async createDiagnosticsForFile(e,n,r,i,o,s){let a=i.length;this.documentManager.hasDismissals(n)||await this.loadDismissalsForFile(e,n),this.documentManager.hasFixes(n)||await this.loadFixesForFile(e,n);let c={ruleId:r,matches:i},u=this.filterDismissedResults([c],n),l=u[0]?.matches.length||0,E=a-l,d=await this.filterFixedResults(e,u,n),f=d[0]?.matches.length||0,T=l-f,p=CE(e.repoRoot,n),S=this.pathToNormalizedUri(p),m=await(s||this.convertResultsToDiagnostics.bind(this))(e,d,o,S);if(a>0){let h=o.find(P=>P.internalId===r)?.id||r,C=[];E>0&&C.push(`${E} dismissed`),T>0&&C.push(`${T} fixed`),C.length>0?this.connection.console.log(`[${e.repoRoot}] ${n}: ${m.length} diagnostic(s) created, ${C.join(", ")} (rule: ${h})`):this.connection.console.log(`[${e.repoRoot}] ${n}: ${m.length} diagnostic(s) created (rule: ${h})`)}return m}async getFileContent(e){let n=this.pathToNormalizedUri(e),r=this.documentManager.getDocument(n);return r?r.getText():await JW.readFile(e,"utf-8").catch(()=>null)}async computeFixForMatch(e,n){if(!e.edits||e.edits.length===0)return null;let r=await jc(e.edits,e.file.path,n.repoRoot,o=>this.getFileContent(o));return r?{fileChanges:r.fileChanges.map(o=>({...o,fileUri:this.pathToNormalizedUri(o.filePath)})),diff:r.diff,diffs:r.diffs}:null}async loadRulesWithCache(e){let r=Date.now()-e.rulesCacheTimestamp;return e.cachedRules&&r<this.repositoryContextManager.RULES_CACHE_TTL_MS?(this.connection.console.log(`[${e.repoRoot}] Using cached rules (age: ${Math.round(r/1e3)}s / ${this.repositoryContextManager.RULES_CACHE_TTL_MS/1e3}s)`),e.cachedRules):e.rulesLoadingPromise?(this.connection.console.log(`[${e.repoRoot}] Rules are already being loaded, waiting for existing request...`),e.rulesLoadingPromise):(this.connection.console.log(`[${e.repoRoot}] Loading fresh rules from API...`),e.rulesLoadingPromise=e.ruleProvider.loadAllRules().then(i=>{let o=i.filter(s=>s.execution==="llm");e.cachedRules=o,e.rulesCacheTimestamp=Date.now(),e.rulesById.clear();for(let s of o)e.rulesById.set(s.id,s);if(this.connection.console.log(`[${e.repoRoot}] Loaded ${o.length} rules (cache valid for ${this.repositoryContextManager.RULES_CACHE_TTL_MS/1e3}s)`),o.length>0){this.connection.console.log(`[${e.repoRoot}] Fetched rules:`);for(let s of o){let a=s.autofix||!1,c="";a&&(c=" (autofix)"),this.connection.console.log(`[${e.repoRoot}] - ${s.id}: ${s.message}${c}`)}}return o}).finally(()=>{e.rulesLoadingPromise=null}),await e.rulesLoadingPromise)}async loadDismissalsForFile(e,n){if(this.documentManager.hasDismissals(n))return;let r=this.dismissalsLoadingPromises.get(n);if(r)return await r;let i=(async()=>{let s=(await e.ruleProvider.getDismissals([n])).map(a=>({ruleId:a.ruleId,file:a.match.file.path,startLine:a.match.range.start.line,endLine:a.match.range.end.line,fingerprint:a.match.fingerprint,dismissedAt:a.createdAt,dismissalId:a.dismissalId}));this.documentManager.setDismissals(n,s)})().finally(()=>{this.dismissalsLoadingPromises.delete(n)});return this.dismissalsLoadingPromises.set(n,i),await i}async loadFixesForFile(e,n){if(this.documentManager.hasFixes(n))return;let r=this.fixesLoadingPromises.get(n);if(r)return await r;let i=(async()=>{let o=await e.ruleProvider.getFixes([n]);this.documentManager.setFixes(n,o)})().finally(()=>{this.fixesLoadingPromises.delete(n)});return this.fixesLoadingPromises.set(n,i),await i}filterDismissedResults(e,n){let r=this.documentManager.getDismissals(n);return r.length===0?e:e.map(i=>{let o=r.filter(u=>u.ruleId===i.ruleId);if(o.length===0)return i;let s=i.matches.map(u=>({file:n,startLine:u.range.start.line,endLine:u.range.end.line,fingerprint:u.fingerprint})),c=Nh(s,o).map(u=>i.matches.find(l=>l.range.start.line===u.startLine&&l.range.end.line===u.endLine&&l.fingerprint===u.fingerprint));return{...i,matches:c}})}async filterFixedResults(e,n,r){let i=this.documentManager.getFixes(r);if(i.length===0)return n;this.connection.console.log(`[${e.repoRoot}] Filtering fixes for ${r}: ${i.length} fix(es) loaded`);let o=CE(e.repoRoot,r),s=await this.getFileContent(o);return s===null?n:n.map(a=>{let c=i.filter(S=>S.ruleId===a.ruleId);if(c.length===0)return a;let u=new Set;for(let S of c)S.match?.edits?.some(O=>O.oldString&&R1(s,O.oldString))&&S.match.fingerprint&&u.add(S.match.fingerprint);let l=c.length-u.size;u.size>0&&this.connection.console.log(`[${e.repoRoot}] ${r}: ${u.size} fix(es) reverted, ${l} still active`);let E=a.matches.map(S=>({file:r,startLine:S.range.start.line,endLine:S.range.end.line,fingerprint:S.fingerprint})),d=c.filter(S=>!S.match.fingerprint||!u.has(S.match.fingerprint)).map(S=>({file:r,startLine:S.match.range.start.line,endLine:S.match.range.end.line,fingerprint:S.match.fingerprint})),T=Nh(E,d).map(S=>a.matches.find(m=>m.range.start.line===S.startLine&&m.range.end.line===S.endLine&&m.fingerprint===S.fingerprint)),p=a.matches.length-T.length;return p>0&&this.connection.console.log(`[${e.repoRoot}] ${r}: Filtered out ${p} match(es) with active fixes`),{...a,matches:T}})}async convertResultsToDiagnostics(e,n,r,i){let o=[],s=new Set;for(let a of n){let c=r.find(u=>u.internalId===a.ruleId);if(!c){this.connection.console.log(`Warning: Could not find rule with internal ID ${a.ruleId}`);continue}for(let u of a.matches){let l=`${u.range.start.line}-${u.range.end.line}`;if(s.has(l))continue;s.add(l);let E=LWe(u,c.internalId,i);if(this.isLocallyDismissed(i,c.internalId,u.range.start.line,u.range.end.line,E))continue;let d=null;u.edits&&u.edits.length>0&&(d=await this.computeFixForMatch(u,e).catch(p=>(this.connection.console.log(`Warning: Failed to compute fix for match: ${p}`),null)));let T={range:yWe(u.range),severity:mr.DiagnosticSeverity.Warning,message:u.description||c.message,code:c.id,codeDescription:{href:`https://app.wispbit.com/rules/${c.internalId}`},source:"wispbit",data:{uri:i,match:u,matchId:E,fix:d?{diff:d.diff,diffs:d.diffs,autofix:c.autofix||!1}:null,rule:{id:c.internalId,displayId:c.id,summary:c.summary||null}}};o.push(T)}}return o}triggerExecution(){this.executionDebounceTimer&&(clearTimeout(this.executionDebounceTimer),this.executionDebounceTimer=null),this.executionDebounceTimer=setTimeout(()=>{this.executionDebounceTimer=null,this.executeQueuedFiles()},this.EXECUTION_DEBOUNCE_MS)}lintDocument(e){if(!this.documentManager.getDocument(e)){this.connection.console.log(`Document not found: ${e}`);return}let r=Uo(e),i=this.toRelativePath(r);this.connection.console.log(`File changed: ${i}`),this.changedFiles.add(e),this.triggerExecution()}async executeQueuedFiles(){let e=this.repositoryContextManager.getAllContexts();if(e.length===0){this.connection.console.log("No repositories found, skipping execution");return}if(!this.hasRunInitialCheck){if(this.isExecutingInitial){this.connection.console.log("Initial check already in progress, files will be queued for next run");return}this.changedFiles.clear(),this.isExecutingInitial=!0;try{await this.repositoryContextManager.initializeRuntimeManager()&&this.connection.console.log("RuntimeManager initialized successfully"),this.hasRunInitialCheck=!0,this.cleanupProgress(),await this.startBatchProgress(0);try{for(let i of e)await this.executeForContext(i,null);this.cleanupProgress()}catch(i){this.connection.console.error(`Error executing rules: ${i.message}`),this.connection.console.error(i.stack),this.cleanupProgress()}}finally{this.isExecutingInitial=!1}}else{let r=Array.from(this.changedFiles);if(this.changedFiles.clear(),r.length===0){this.connection.console.log("No files to check, skipping incremental execution");return}this.connection.console.log(`Incremental check for ${r.length} file(s)`),await this.startBatchProgress(0);try{for(let i of e)await this.executeForContext(i,r);this.cleanupProgress()}catch(i){this.connection.console.error(`Error executing rules: ${i.message}`),this.connection.console.error(i.stack),await FS(i),this.cleanupProgress()}}}async executeForContext(e,n){e.isExecuting=!0;try{let r=await this.loadRulesWithCache(e);e.completedRulesCount=0,e.currentExecutionRules=r;let{commitSelector:i,include:o}=await ga(e.repoRoot);e.lastCommitSelector=i;let s=await Pa.initialize(e.config,e.environment,"diff",e.eventEmitter,void 0,{include:o,commitSelector:i,skipPreExistingViolations:!0});if(e.executionContext=s,e.runtimeManager){let a=[];if(n!==null&&n.length>0?a=n.filter(c=>this.findContextForFile(c)?.repoRoot===e.repoRoot).map(c=>({filePath:this.toRelativePath(Uo(c),e)})):a=s.filePaths.map(c=>({filePath:c})),a.length>0){this.connection.console.log(`[${e.repoRoot}] Starting invalidation of ${a.length} file(s) in RuntimeManager...`);let c=Date.now();await e.runtimeManager.invalidateFiles(e.repoRoot,a);let u=Date.now()-c;this.connection.console.log(`[${e.repoRoot}] Completed invalidation in ${u}ms`)}}if(n!==null&&n.length>0){let a=n.filter(c=>this.findContextForFile(c)?.repoRoot===e.repoRoot);if(a.length===0){this.connection.console.log(`[${e.repoRoot}] No changed files in this repository, skipping`);return}for(let c of a){let u=this.toRelativePath(Uo(c),e);this.connection.console.log(`[${e.repoRoot}] Executing rules for ${u}`),e.completedRulesCount=0;let l=e.ruleExecutor.execute(r,s,{relativePath:u,emitErrors:!0}),E=e.duplicateManager?e.duplicateManager.execute(s,u,(d,f,T,p)=>{this.handleDuplicateFileComplete(e,d,f,T,p)}):Promise.resolve();await Promise.all([l,E])}}else{let a=e.ruleExecutor.execute(r,s,{emitErrors:!0}),c=e.duplicateManager?(async()=>{let u=e.duplicateManager;this.connection.console.log(`[${e.repoRoot}] Starting duplicate detection...`);let l=Date.now();await u.execute(s,void 0,(d,f,T,p)=>{this.handleDuplicateFileComplete(e,d,f,T,p)});let E=Date.now()-l;this.connection.console.log(`[${e.repoRoot}] Completed duplicate detection in ${E}ms`)})():Promise.resolve();await Promise.all([a,c])}}catch(r){this.connection.console.error(`[${e.repoRoot}] Error executing rules: ${r.message}`),this.connection.console.error(r.stack),await FS(r)}finally{e.isExecuting=!1}}async startBatchProgress(e){if(this.batchProgressReporter)this.batchProgressReporter.report(e,"Checking workspace");else try{this.progressTokenCreated||(await this.connection.sendRequest("window/workDoneProgress/create",{token:this.batchProgressToken}),this.progressTokenCreated=!0),this.batchProgressReporter=await this.connection.window.createWorkDoneProgress(),this.batchProgressReporter.begin("wispbit",e,"Checking workspace",!1)}catch(n){this.connection.console.log(`Failed to create batch progress: ${n}`)}}cleanupProgress(){this.batchProgressReporter&&(this.batchProgressReporter.done(),this.batchProgressReporter=null)}updateBatchProgress(e,n){if(!this.batchProgressReporter)return;let r=`Checking workspace (${n})`;this.batchProgressReporter.report(e,r)}async processFixQueue(){if(!this.isProcessingFix){for(this.isProcessingFix=!0;this.fixQueue.length>0;){let e=this.fixQueue.shift();e&&await e()}this.isProcessingFix=!1}}handleQuickfix(e){let{uri:n,matchId:r}=e[0];this.fixQueue.push(async()=>{await this.applyQuickfix(n,r)}),this.processFixQueue()}async applyQuickfix(e,n){let i=this.getDiagnosticsForUri(e).find(T=>T.data?.matchId===n);if(!i){this.connection.window.showErrorMessage("Could not apply fix"),this.connection.console.error(`Diagnostic not found for matchId: ${n}`);return}let o=i.data?.match,s=i.data?.rule;if(!o?.edits||o.edits.length===0){this.connection.window.showErrorMessage("No fix available"),this.connection.console.error(`No fix available for matchId ${n}, rule ${s?.id}`);return}let a=this.findContextForFile(e);if(!a){this.connection.window.showErrorMessage("File is not in any repository"),this.connection.console.error(`No repository context found for ${e}`);return}let c=Uo(e),u=this.toRelativePath(c,a);this.connection.console.log(`[${a.repoRoot}] Applying fix for matchId ${n}, rule ${s?.id}`);let l=await this.computeFixForMatch(o,a);if(!l){this.connection.window.showErrorMessage("Failed to compute fix"),this.connection.console.error(`[${a.repoRoot}] Failed to compute fix for matchId ${n}, rule ${s?.id}`);return}let E=Uo(e),d=[];for(let T of l.fileChanges){let S=T.filePath===E?e:T.fileUri,m=this.documentManager.getDocument(S),O=m?m.lineCount:T.oldContent.split(`
|
|
294
|
+
`);return{file:e.file,range:{start:{line:a+1,column:1},end:{line:c+1,column:o[c]?.length||1}},text:u}}extractGroupContext(e,n,r=20){return e.fragments.map(i=>this.extractFragmentContext(i,n,r))}clusterBySharedFiles(){if(this.groups.length===0)return[];let e=new Map;this.groups.forEach((o,s)=>{let a=new Set;for(let c of o.fragments)a.add(c.file);for(let c of a)e.has(c)||e.set(c,[]),e.get(c).push(s)});let n=new qW(this.groups.length);for(let o of e.values())for(let s=1;s<o.length;s++)n.union(o[0],o[s]);let r=new Map;for(let o=0;o<this.groups.length;o++){let s=n.find(o);r.has(s)||r.set(s,[]),r.get(s).push(o)}let i=[];for(let o of r.values()){let s=o.map(c=>this.groups[c]),a=new Set;for(let c of s)for(let u of c.fragments)a.add(u.file);i.push({groups:s,files:a})}return i}},qW=class{parent=new Map;rank=new Map;constructor(e){for(let n=0;n<e;n++)this.parent.set(n,n),this.rank.set(n,0)}find(e){return this.parent.get(e)!==e&&this.parent.set(e,this.find(this.parent.get(e))),this.parent.get(e)}union(e,n){let r=this.find(e),i=this.find(n);if(r===i)return;let o=this.rank.get(r),s=this.rank.get(i);o<s?this.parent.set(r,i):o>s?this.parent.set(i,r):(this.parent.set(i,r),this.rank.set(r,o+1))}};var Fy=class t{static INDEX_FOLDER=RMt(gMt(),".wispbit","indexes");provider;initialized=!1;constructor(e){this.provider=e}async initializeProvider(){this.initialized||(await this.provider.initialize(),this.initialized=!0)}async initialize(e){await this.initializeProvider();let n=await this.provider.sendRequest({command:"initialize_workspaces",workspace_configs:e.workspaceConfigs.map(r=>({workspace_root:r.workspaceRoot,base_commit_sha:r.baseCommitSha})),index_folder:t.INDEX_FOLDER,ignores:e.ignores});if("error"in n&&n.error)throw new Error(`Failed to initialize workspaces: ${n.error}`);return{fullScan:n.full_scan||!1}}async getDuplicates(e,n){await this.initializeProvider();let r=await this.provider.sendRequest({command:"get_duplicates",workspace_root:e,file_path:n});if("error"in r&&r.error)throw new Error(`Failed to get duplicates: ${r.error}`);let i=r;return new kg(i)}async getDuplicatesForFile(e,n){await this.initializeProvider();let r=await this.provider.sendRequest({command:"get_duplicates",workspace_root:e,file_path:n});if("error"in r&&r.error)throw new Error(`Failed to get duplicates for file: ${r.error}`);let i=r;return new kg(i)}invalidationQueue=Promise.resolve();async invalidateFiles(e,n){let r=this.invalidationQueue.then(async()=>{await this.initializeProvider();let i=Date.now(),o=await this.provider.sendRequest({command:"invalidate_files",workspace_root:e,files:n.map(a=>({file_path:a.filePath,content_sha:a.contentSha}))}),s=Date.now()-i;if(Ae.log(`[RuntimeManager] Invalidated ${n.length} file(s) in ${s}ms for ${e}`),"error"in o&&o.error)throw new Error(`Failed to invalidate files: ${o.error}`)}).catch(i=>{throw Ae.log(`[RuntimeManager] Invalidation failed: ${i.message}`),i});this.invalidationQueue=r.catch(()=>{}),await r}isInitialized(){return this.initialized}async shutdown(){this.initialized&&(Ae.log("[RuntimeManager] Shutdown requested, waiting for in-flight operations..."),await this.invalidationQueue.catch(()=>{}),Ae.log("[RuntimeManager] All operations complete, shutting down runtime..."),await this.provider.shutdown(),this.initialized=!1)}};var ky=class{contexts=new Map;workspacePath;apiKey;lspClient;sharedRuntimeManager=null;RULES_CACHE_TTL_MS=300*1e3;constructor(e){this.workspacePath=e.workspacePath,this.apiKey=e.apiKey,this.lspClient=e.lspClient}async discoverAndInitialize(e,n){let r=await rO(this.workspacePath),i=[];for(let s of r){let a=await this.initializeContext(s).catch(c=>(n?.(s,c),null));if(a){let{baseCommitSha:c}=await ga(s);i.push({repoRoot:s,context:a,baseCommitSha:c}),this.contexts.set(s,a),e?.(s,a)}}let o=[];for(let{repoRoot:s,context:a,baseCommitSha:c}of i)a.config.getEnableDeduplicator()&&o.push({workspaceRoot:s,baseCommitSha:c});if(o.length>0){let s=new Gy(o[0].workspaceRoot);this.sharedRuntimeManager=new Fy(s);for(let{context:a}of i)a.config.getEnableDeduplicator()&&(a.runtimeManager=this.sharedRuntimeManager,a.duplicateManager=new Vy(this.sharedRuntimeManager,a.config,a.environment))}}async initializeContext(e){let n=new Kr({workspaceRoot:e}),r=await $o.initialize(n,{apiKey:this.apiKey,lspClient:this.lspClient});if("failed"in r)throw new Error(`Config initialization failed for ${e}: ${r.error}`);let i=r,o=new jo(i,n),s=new zr,a=new La(i,n,s);return{repoRoot:e,environment:n,config:i,ruleProvider:o,eventEmitter:s,ruleExecutor:a,cachedRules:null,rulesById:new Map,rulesCacheTimestamp:0,rulesLoadingPromise:null,lastCommitSelector:null,currentExecutionRules:[],completedRulesCount:0,isExecuting:!1,currentExecutionPromise:null,runtimeManager:null,duplicateManager:null,executionContext:null}}findContextForFile(e){let n=e.toLowerCase();return Array.from(this.contexts.entries()).filter(([i])=>{let o=i.toLowerCase();return n.startsWith(o+NMt)||n===o}).sort((i,o)=>o[0].length-i[0].length)[0]?.[1]||null}getAllContexts(){return Array.from(this.contexts.values())}getRepoCount(){return this.contexts.size}hasRepositories(){return this.contexts.size>0}getContext(e){return this.contexts.get(e)}async initializeRuntimeManager(){if(!this.sharedRuntimeManager)return!1;let e=[],n=[];for(let r of this.contexts.values())if(r.config.getEnableDeduplicator()){let{baseCommitSha:i}=await ga(r.repoRoot);e.push({workspaceRoot:r.repoRoot,baseCommitSha:i}),n.length===0&&(n=r.config.getIgnoredGlobs())}return e.length===0?!1:(await this.sharedRuntimeManager.initialize({workspaceConfigs:e,ignores:n}),!0)}async shutdown(){for(let e of this.contexts.values())e.ruleExecutor.clearCache(),e.duplicateManager&&e.duplicateManager.clearCache();this.sharedRuntimeManager&&(await this.sharedRuntimeManager.shutdown(),this.sharedRuntimeManager=null),this.contexts.clear()}};function LWe(t,e,n){if(t.fingerprint)return`${e}:${t.fingerprint}`;let r=t.text?.slice(0,100)||"",i=`${t.range.start.line}:${t.range.start.column}-${t.range.end.line}:${t.range.end.column}`;return`${e}:${n}:${i}:${CMt(r)}`}function CMt(t){let e=0;for(let n=0;n<t.length;n++){let r=t.charCodeAt(n);e=(e<<5)-e+r,e=e&e}return e.toString(36)}function yWe(t){return t.end.line-t.start.line+1>20?{start:{line:t.start.line-1,character:t.start.column-1},end:{line:t.start.line-1,character:Number.MAX_SAFE_INTEGER}}:{start:{line:t.start.line-1,character:t.start.column-1},end:{line:t.end.line-1,character:t.end.column-1}}}var QW=class{connection;documentManager;repositoryContextManager;workspacePath;apiKey;lspClient;batchProgressReporter=null;batchProgressToken="wispbit-batch-lint";progressTokenCreated=!1;fixQueue=[];isProcessingFix=!1;branchCheckTimer=null;BRANCH_CHECK_DEBOUNCE_MS=1e3;branchCheckInterval=null;BRANCH_CHECK_INTERVAL_MS=3e4;executionDebounceTimer=null;EXECUTION_DEBOUNCE_MS=1e3;changedFiles=new Set;hasRunInitialCheck=!1;isExecutingInitial=!1;dismissalsLoadingPromises=new Map;fixesLoadingPromises=new Map;localDismissals=[];constructor(e){this.workspacePath=e.workspacePath,this.apiKey=e.apiKey,this.lspClient=e.lspClient,this.connection=(0,mr.createConnection)(mr.ProposedFeatures.all,process.stdin,process.stdout),this.documentManager=new xy,this.repositoryContextManager=new ky({workspacePath:this.workspacePath,apiKey:this.apiKey,lspClient:this.lspClient}),this.registerHandlers()}addLocalDismissal(e){this.localDismissals.push(e)}isLocallyDismissed(e,n,r,i,o){return this.localDismissals.some(s=>s.matchId===o&&s.uri===e?!0:s.uri===e&&s.ruleId===n?s.startLine<=i&&s.endLine>=r:!1)}clearLocalDismissalsForUri(e){this.localDismissals=this.localDismissals.filter(n=>n.uri!==e)}clearAllLocalDismissals(){this.localDismissals=[]}findContextForFile(e){let n=Uo(e);return this.repositoryContextManager.findContextForFile(n)}pathToNormalizedUri(e){let n=OMt(e).toString();return decodeURIComponent(n)}getDiagnosticsForUri(e){let n=this.documentManager.getDiagnostics(e);if(n.length===0){let r=oi(e),i=this.documentManager.getAllDiagnosticUris();for(let o of i)if(oi(o)===r){n=this.documentManager.getDiagnostics(o);break}}return n}handleRuleComplete(e,n,r){if(!e.isExecuting||e.currentExecutionRules.length===0)return;e.completedRulesCount++;let i=Math.round(e.completedRulesCount/e.currentExecutionRules.length*100);this.updateBatchProgress(i,n)}async handleRuleFileComplete(e,n,r,i){if(!e.isExecuting||e.currentExecutionRules.length===0)return;let o=e.currentExecutionRules.find(u=>u.id===n);if(!o){this.connection.console.log(`[${e.repoRoot}] Warning: Rule ${n} not found in current execution`);return}let s=CE(e.repoRoot,r),a=this.pathToNormalizedUri(s);if(i.length===0){this.updateDiagnosticsForRule(a,o.internalId,[]);return}let c=await this.createDiagnosticsForFile(e,r,o.internalId,i,e.currentExecutionRules);this.updateDiagnosticsForRule(a,o.internalId,c)}async handleRuleFilePartial(e,n,r,i,o){if(!e.isExecuting||e.currentExecutionRules.length===0)return;let s=e.currentExecutionRules.find(l=>l.internalId===n);if(!s){this.connection.console.log(`[${e.repoRoot}] Warning: Rule ${n} not found in current execution`);return}let a=CE(e.repoRoot,r),c=this.pathToNormalizedUri(a);if(i.length===0){this.updateDiagnosticsForRule(c,s.internalId,[]);return}let u=await this.createDiagnosticsForFile(e,r,s.internalId,i,e.currentExecutionRules);this.updateDiagnosticsForRule(c,s.internalId,u)}async convertDuplicateResultsToDiagnostics(e,n,r,i){let o=[];for(let s of n)for(let a of s.matches){let c=LWe(a,"DUPLICATE",i);if(this.isLocallyDismissed(i,"DUPLICATE",a.range.start.line,a.range.end.line,c))continue;let u=yWe(a.range),l=a.evidence?.map(f=>{let T=CE(e.repoRoot,f.file.path);return{location:{uri:this.pathToNormalizedUri(T),range:{start:{line:f.range.start.line-1,character:f.range.start.column-1},end:{line:f.range.end.line-1,character:f.range.end.column-1}}},message:"Duplicate location"}}),E=null;a.edits&&a.edits.length>0&&(E=await this.computeFixForMatch(a,e).catch(f=>(this.connection.console.log(`Warning: Failed to compute fix for duplicate: ${f}`),null)));let d={range:u,severity:mr.DiagnosticSeverity.Warning,message:a.description||"Duplicate code detected",code:"DUPLICATE",source:"wispbit",relatedInformation:l,data:{uri:i,match:a,matchId:c,fix:E?{diff:E.diff,diffs:E.diffs,autofix:!1}:null,ruleId:"DUPLICATE",filePath:a.file.path,rule:{id:"DUPLICATE",displayId:"DUPLICATE",summary:null}}};o.push(d)}return o}async handleDuplicateFileComplete(e,n,r,i,o){let s=CE(e.repoRoot,n),a=this.pathToNormalizedUri(s),c=await this.createDiagnosticsForFile(e,n,"DUPLICATE",r,[],this.convertDuplicateResultsToDiagnostics.bind(this));this.updateDiagnosticsForRule(a,"DUPLICATE",c)}setupProgressListenersForContext(e){e.eventEmitter.on("rules:progress",n=>{this.updateBatchProgress(n.percentage,n.ruleId)}),e.eventEmitter.on("rules:rule-complete",n=>{this.handleRuleComplete(e,n.ruleId,n.matches)}),e.eventEmitter.on("rules:rule-file-complete",async n=>{await this.handleRuleFileComplete(e,n.ruleId,n.filePath,n.matches)}),e.eventEmitter.on("rules:rule-file-partial",async n=>{await this.handleRuleFilePartial(e,n.ruleId,n.filePath,n.matches,n.totalMatches)})}normalizePathSeparators(e){return e.replace(/\\/g,"/")}toRelativePath(e,n){if(!n){let r=this.pathToNormalizedUri(e);n=this.findContextForFile(r)||void 0}return n?this.normalizePathSeparators(DWe(n.repoRoot,e)):this.normalizePathSeparators(DWe(this.workspacePath,e))}updateDiagnosticsForRule(e,n,r){this.documentManager.setDiagnosticsForRule(e,n,r);let i=this.documentManager.getDiagnostics(e);this.connection.sendDiagnostics({uri:e,diagnostics:i})}removeDiagnosticByMatchId(e,n){let r=this.documentManager.getDiagnostics(e),i=r.find(a=>a.data?.matchId===n);if(!i)return;let o=i.data?.rule?.id;if(!o)return;let s=r.filter(a=>a.data?.rule?.id===o&&a.data?.matchId!==n);this.updateDiagnosticsForRule(e,o,s)}clearAllDiagnostics(){let e=this.documentManager.getAllDiagnosticUris();for(let n of e)this.clearDiagnosticsForFile(n);this.clearAllLocalDismissals()}clearDiagnosticsForFile(e){this.documentManager.clearDiagnostics(e),this.clearLocalDismissalsForUri(e),this.connection.sendDiagnostics({uri:e,diagnostics:[]})}handleDocumentChangesWithRanges(e,n){let r=this.documentManager.getDiagnostics(e);if(r.length===0)return;this.connection.console.log(`Document changed: ${e}, ${n.length} change(s), ${r.length} diagnostic(s)`);let i=new Set;for(let o of n){if(!o.range){this.connection.console.log("Full document change detected - removing all diagnostics");for(let s of r)s.data?.matchId&&i.add(s.data.matchId);continue}for(let s of r)this.rangesOverlap(o.range,s.range)&&s.data?.matchId&&i.add(s.data.matchId)}for(let o of i)this.removeDiagnosticByMatchId(e,o);i.size>0&&this.connection.console.log(`Removed ${i.size} diagnostic(s) due to document edits`)}rangesOverlap(e,n){return e.end.line<n.start.line||e.start.line>n.end.line?!1:e.start.line<=n.end.line&&e.end.line>=n.start.line}async createDiagnosticsForFile(e,n,r,i,o,s){let a=i.length;this.documentManager.hasDismissals(n)||await this.loadDismissalsForFile(e,n),this.documentManager.hasFixes(n)||await this.loadFixesForFile(e,n);let c={ruleId:r,matches:i},u=this.filterDismissedResults([c],n),l=u[0]?.matches.length||0,E=a-l,d=await this.filterFixedResults(e,u,n),f=d[0]?.matches.length||0,T=l-f,p=CE(e.repoRoot,n),S=this.pathToNormalizedUri(p),m=await(s||this.convertResultsToDiagnostics.bind(this))(e,d,o,S);if(a>0){let h=o.find(P=>P.internalId===r)?.id||r,C=[];E>0&&C.push(`${E} dismissed`),T>0&&C.push(`${T} fixed`),C.length>0?this.connection.console.log(`[${e.repoRoot}] ${n}: ${m.length} diagnostic(s) created, ${C.join(", ")} (rule: ${h})`):this.connection.console.log(`[${e.repoRoot}] ${n}: ${m.length} diagnostic(s) created (rule: ${h})`)}return m}async getFileContent(e){let n=this.pathToNormalizedUri(e),r=this.documentManager.getDocument(n);return r?r.getText():await JW.readFile(e,"utf-8").catch(()=>null)}async computeFixForMatch(e,n){if(!e.edits||e.edits.length===0)return null;let r=await jc(e.edits,e.file.path,n.repoRoot,o=>this.getFileContent(o));return r?{fileChanges:r.fileChanges.map(o=>({...o,fileUri:this.pathToNormalizedUri(o.filePath)})),diff:r.diff,diffs:r.diffs}:null}async loadRulesWithCache(e){let r=Date.now()-e.rulesCacheTimestamp;return e.cachedRules&&r<this.repositoryContextManager.RULES_CACHE_TTL_MS?(this.connection.console.log(`[${e.repoRoot}] Using cached rules (age: ${Math.round(r/1e3)}s / ${this.repositoryContextManager.RULES_CACHE_TTL_MS/1e3}s)`),e.cachedRules):e.rulesLoadingPromise?(this.connection.console.log(`[${e.repoRoot}] Rules are already being loaded, waiting for existing request...`),e.rulesLoadingPromise):(this.connection.console.log(`[${e.repoRoot}] Loading fresh rules from API...`),e.rulesLoadingPromise=e.ruleProvider.loadAllRules().then(i=>{let o=i.filter(s=>s.execution==="llm");e.cachedRules=o,e.rulesCacheTimestamp=Date.now(),e.rulesById.clear();for(let s of o)e.rulesById.set(s.id,s);if(this.connection.console.log(`[${e.repoRoot}] Loaded ${o.length} rules (cache valid for ${this.repositoryContextManager.RULES_CACHE_TTL_MS/1e3}s)`),o.length>0){this.connection.console.log(`[${e.repoRoot}] Fetched rules:`);for(let s of o){let a=s.autofix||!1,c="";a&&(c=" (autofix)"),this.connection.console.log(`[${e.repoRoot}] - ${s.id}: ${s.message}${c}`)}}return o}).finally(()=>{e.rulesLoadingPromise=null}),await e.rulesLoadingPromise)}async loadDismissalsForFile(e,n){if(this.documentManager.hasDismissals(n))return;let r=this.dismissalsLoadingPromises.get(n);if(r)return await r;let i=(async()=>{let s=(await e.ruleProvider.getDismissals([n])).map(a=>({ruleId:a.ruleId,file:a.match.file.path,startLine:a.match.range.start.line,endLine:a.match.range.end.line,fingerprint:a.match.fingerprint,dismissedAt:a.createdAt,dismissalId:a.dismissalId}));this.documentManager.setDismissals(n,s)})().finally(()=>{this.dismissalsLoadingPromises.delete(n)});return this.dismissalsLoadingPromises.set(n,i),await i}async loadFixesForFile(e,n){if(this.documentManager.hasFixes(n))return;let r=this.fixesLoadingPromises.get(n);if(r)return await r;let i=(async()=>{let o=await e.ruleProvider.getFixes([n]);this.documentManager.setFixes(n,o)})().finally(()=>{this.fixesLoadingPromises.delete(n)});return this.fixesLoadingPromises.set(n,i),await i}filterDismissedResults(e,n){let r=this.documentManager.getDismissals(n);return r.length===0?e:e.map(i=>{let o=r.filter(u=>u.ruleId===i.ruleId);if(o.length===0)return i;let s=i.matches.map(u=>({file:n,startLine:u.range.start.line,endLine:u.range.end.line,fingerprint:u.fingerprint})),c=Nh(s,o).map(u=>i.matches.find(l=>l.range.start.line===u.startLine&&l.range.end.line===u.endLine&&l.fingerprint===u.fingerprint));return{...i,matches:c}})}async filterFixedResults(e,n,r){let i=this.documentManager.getFixes(r);if(i.length===0)return n;this.connection.console.log(`[${e.repoRoot}] Filtering fixes for ${r}: ${i.length} fix(es) loaded`);let o=CE(e.repoRoot,r),s=await this.getFileContent(o);return s===null?n:n.map(a=>{let c=i.filter(S=>S.ruleId===a.ruleId);if(c.length===0)return a;let u=new Set;for(let S of c)S.match?.edits?.some(O=>O.oldString&&R1(s,O.oldString))&&S.match.fingerprint&&u.add(S.match.fingerprint);let l=c.length-u.size;u.size>0&&this.connection.console.log(`[${e.repoRoot}] ${r}: ${u.size} fix(es) reverted, ${l} still active`);let E=a.matches.map(S=>({file:r,startLine:S.range.start.line,endLine:S.range.end.line,fingerprint:S.fingerprint})),d=c.filter(S=>!S.match.fingerprint||!u.has(S.match.fingerprint)).map(S=>({file:r,startLine:S.match.range.start.line,endLine:S.match.range.end.line,fingerprint:S.match.fingerprint})),T=Nh(E,d).map(S=>a.matches.find(m=>m.range.start.line===S.startLine&&m.range.end.line===S.endLine&&m.fingerprint===S.fingerprint)),p=a.matches.length-T.length;return p>0&&this.connection.console.log(`[${e.repoRoot}] ${r}: Filtered out ${p} match(es) with active fixes`),{...a,matches:T}})}async convertResultsToDiagnostics(e,n,r,i){let o=[],s=new Set;for(let a of n){let c=r.find(u=>u.internalId===a.ruleId);if(!c){this.connection.console.log(`Warning: Could not find rule with internal ID ${a.ruleId}`);continue}for(let u of a.matches){let l=`${u.range.start.line}-${u.range.end.line}`;if(s.has(l))continue;s.add(l);let E=LWe(u,c.internalId,i);if(this.isLocallyDismissed(i,c.internalId,u.range.start.line,u.range.end.line,E))continue;let d=null;u.edits&&u.edits.length>0&&(d=await this.computeFixForMatch(u,e).catch(p=>(this.connection.console.log(`Warning: Failed to compute fix for match: ${p}`),null)));let T={range:yWe(u.range),severity:mr.DiagnosticSeverity.Warning,message:u.description||c.message,code:c.id,codeDescription:{href:`https://app.wispbit.com/rules/${c.internalId}`},source:"wispbit",data:{uri:i,match:u,matchId:E,fix:d?{diff:d.diff,diffs:d.diffs,autofix:c.autofix||!1}:null,rule:{id:c.internalId,displayId:c.id,summary:c.summary||null}}};o.push(T)}}return o}triggerExecution(){this.executionDebounceTimer&&(clearTimeout(this.executionDebounceTimer),this.executionDebounceTimer=null),this.executionDebounceTimer=setTimeout(()=>{this.executionDebounceTimer=null,this.executeQueuedFiles()},this.EXECUTION_DEBOUNCE_MS)}lintDocument(e){if(!this.documentManager.getDocument(e)){this.connection.console.log(`Document not found: ${e}`);return}let r=Uo(e),i=this.toRelativePath(r);this.connection.console.log(`File changed: ${i}`),this.changedFiles.add(e),this.triggerExecution()}async executeQueuedFiles(){let e=this.repositoryContextManager.getAllContexts();if(e.length===0){this.connection.console.log("No repositories found, skipping execution");return}if(!this.hasRunInitialCheck){if(this.isExecutingInitial){this.connection.console.log("Initial check already in progress, files will be queued for next run");return}this.changedFiles.clear(),this.isExecutingInitial=!0;try{await this.repositoryContextManager.initializeRuntimeManager()&&this.connection.console.log("RuntimeManager initialized successfully"),this.hasRunInitialCheck=!0,this.cleanupProgress(),await this.startBatchProgress(0);try{for(let i of e)await this.executeForContext(i,null);this.cleanupProgress()}catch(i){this.connection.console.error(`Error executing rules: ${i.message}`),this.connection.console.error(i.stack),this.cleanupProgress()}}finally{this.isExecutingInitial=!1}}else{let r=Array.from(this.changedFiles);if(this.changedFiles.clear(),r.length===0){this.connection.console.log("No files to check, skipping incremental execution");return}this.connection.console.log(`Incremental check for ${r.length} file(s)`),await this.startBatchProgress(0);try{for(let i of e)await this.executeForContext(i,r);this.cleanupProgress()}catch(i){this.connection.console.error(`Error executing rules: ${i.message}`),this.connection.console.error(i.stack),await FS(i),this.cleanupProgress()}}}async executeForContext(e,n){e.isExecuting=!0;try{let r=await this.loadRulesWithCache(e);e.completedRulesCount=0,e.currentExecutionRules=r;let{commitSelector:i,include:o}=await ga(e.repoRoot);e.lastCommitSelector=i;let s=await Pa.initialize(e.config,e.environment,"diff",e.eventEmitter,void 0,{include:o,commitSelector:i,skipPreExistingViolations:!0});if(e.executionContext=s,e.runtimeManager){let a=[];if(n!==null&&n.length>0?a=n.filter(c=>this.findContextForFile(c)?.repoRoot===e.repoRoot).map(c=>({filePath:this.toRelativePath(Uo(c),e)})):a=s.filePaths.map(c=>({filePath:c})),a.length>0){this.connection.console.log(`[${e.repoRoot}] Starting invalidation of ${a.length} file(s) in RuntimeManager...`);let c=Date.now();await e.runtimeManager.invalidateFiles(e.repoRoot,a);let u=Date.now()-c;this.connection.console.log(`[${e.repoRoot}] Completed invalidation in ${u}ms`)}}if(n!==null&&n.length>0){let a=n.filter(c=>this.findContextForFile(c)?.repoRoot===e.repoRoot);if(a.length===0){this.connection.console.log(`[${e.repoRoot}] No changed files in this repository, skipping`);return}for(let c of a){let u=this.toRelativePath(Uo(c),e);this.connection.console.log(`[${e.repoRoot}] Executing rules for ${u}`),e.completedRulesCount=0;let l=e.ruleExecutor.execute(r,s,{relativePath:u,emitErrors:!0}),E=e.duplicateManager?e.duplicateManager.execute(s,u,(d,f,T,p)=>{this.handleDuplicateFileComplete(e,d,f,T,p)}):Promise.resolve();await Promise.all([l,E])}}else{let a=e.ruleExecutor.execute(r,s,{emitErrors:!0}),c=e.duplicateManager?(async()=>{let u=e.duplicateManager;this.connection.console.log(`[${e.repoRoot}] Starting duplicate detection...`);let l=Date.now();await u.execute(s,void 0,(d,f,T,p)=>{this.handleDuplicateFileComplete(e,d,f,T,p)});let E=Date.now()-l;this.connection.console.log(`[${e.repoRoot}] Completed duplicate detection in ${E}ms`)})():Promise.resolve();await Promise.all([a,c])}}catch(r){this.connection.console.error(`[${e.repoRoot}] Error executing rules: ${r.message}`),this.connection.console.error(r.stack),await FS(r)}finally{e.isExecuting=!1}}async startBatchProgress(e){if(this.batchProgressReporter)this.batchProgressReporter.report(e,"Checking workspace");else try{this.progressTokenCreated||(await this.connection.sendRequest("window/workDoneProgress/create",{token:this.batchProgressToken}),this.progressTokenCreated=!0),this.batchProgressReporter=await this.connection.window.createWorkDoneProgress(),this.batchProgressReporter.begin("wispbit",e,"Checking workspace",!1)}catch(n){this.connection.console.log(`Failed to create batch progress: ${n}`)}}cleanupProgress(){this.batchProgressReporter&&(this.batchProgressReporter.done(),this.batchProgressReporter=null)}updateBatchProgress(e,n){if(!this.batchProgressReporter)return;let r=`Checking workspace (${n})`;this.batchProgressReporter.report(e,r)}async processFixQueue(){if(!this.isProcessingFix){for(this.isProcessingFix=!0;this.fixQueue.length>0;){let e=this.fixQueue.shift();e&&await e()}this.isProcessingFix=!1}}handleQuickfix(e){let{uri:n,matchId:r}=e[0];this.fixQueue.push(async()=>{await this.applyQuickfix(n,r)}),this.processFixQueue()}async applyQuickfix(e,n){let i=this.getDiagnosticsForUri(e).find(T=>T.data?.matchId===n);if(!i){this.connection.window.showErrorMessage("Could not apply fix"),this.connection.console.error(`Diagnostic not found for matchId: ${n}`);return}let o=i.data?.match,s=i.data?.rule;if(!o?.edits||o.edits.length===0){this.connection.window.showErrorMessage("No fix available"),this.connection.console.error(`No fix available for matchId ${n}, rule ${s?.id}`);return}let a=this.findContextForFile(e);if(!a){this.connection.window.showErrorMessage("File is not in any repository"),this.connection.console.error(`No repository context found for ${e}`);return}let c=Uo(e),u=this.toRelativePath(c,a);this.connection.console.log(`[${a.repoRoot}] Applying fix for matchId ${n}, rule ${s?.id}`);let l=await this.computeFixForMatch(o,a);if(!l){this.connection.window.showErrorMessage("Failed to compute fix"),this.connection.console.error(`[${a.repoRoot}] Failed to compute fix for matchId ${n}, rule ${s?.id}`);return}let E=Uo(e),d=[];for(let T of l.fileChanges){let S=T.filePath===E?e:T.fileUri,m=this.documentManager.getDocument(S),O=m?m.lineCount:T.oldContent.split(`
|
|
295
295
|
`).length,h=Uo(S),C=!!m;C||(C=await JW.access(h).then(()=>!0).catch(()=>!1)),C||d.push(mr.CreateFile.create(S,{overwrite:!1,ignoreIfExists:!0}));let P={textDocument:{uri:S,version:m?.version??null},edits:[mr.TextEdit.replace({start:{line:0,character:0},end:{line:O,character:0}},T.newContent)]};d.push(P)}let f=await this.connection.workspace.applyEdit({documentChanges:d});f.applied?this.connection.console.log(`[${a.repoRoot}] Applied fix successfully`):this.connection.console.log(`[${a.repoRoot}] Could not apply fix. Reason: ${f.failureReason}, Change: ${f.failedChange}`),await a.ruleProvider.createFix(s?.id,o).catch(T=>{this.connection.console.log(`[${a.repoRoot}] Failed to track fix: ${T.message}`)}),this.removeDiagnosticByMatchId(e,n),this.documentManager.clearFixes(u)}async handleDismiss(e){let{uri:n,matchId:r,ruleId:i,startLine:o,endLine:s}=e[0],a=this.findContextForFile(n);if(!a){this.connection.window.showErrorMessage("File is not in any repository"),this.connection.console.error(`No repository context found for ${n}`);return}let c=Uo(n),u=this.toRelativePath(c,a),l=this.getDiagnosticsForUri(n),E=l.find(S=>S.data?.matchId===r);if(!E&&i&&o!==void 0&&s!==void 0&&(E=l.find(S=>{let m=S.data?.rule?.id,O=S.range.start.line+1,h=S.range.end.line+1;return m===i&&O<=s&&h>=o})),!E){this.connection.window.showErrorMessage("Could not dismiss violation"),this.connection.console.error(`[${a.repoRoot}] Diagnostic not found for matchId: ${r}`);return}let d=E.data?.match,f=E.data?.rule?.id,T=E.data?.matchId;if(!d||!f||!T){this.connection.window.showErrorMessage("Could not dismiss violation"),this.connection.console.error(`[${a.repoRoot}] Match not found for matchId: ${r}`);return}this.addLocalDismissal({uri:n,ruleId:f,startLine:E.range.start.line+1,endLine:E.range.end.line+1,matchId:T,timestamp:Date.now()}),this.removeDiagnosticByMatchId(n,T),await a.ruleProvider.createDismissal(f,d).catch(S=>{this.connection.console.log(`[${a.repoRoot}] Failed to create dismissal: ${S.message}`)});let p=E.data?.rule?.displayId||f;this.connection.console.log(`[${a.repoRoot}] Dismissed violation: ${p} with matchId ${T}`),this.documentManager.clearDismissals(u)}async handleGetDismissals(e){let{uri:n}=e[0];if(!n||typeof n!="string")return this.connection.console.log(`Invalid URI provided to getDismissals: ${n}`),[];if(!n.startsWith("file://"))return this.connection.console.log(`URI must be a file:// URL, got: ${n}`),[];let r=this.findContextForFile(n);if(!r)return this.connection.console.log(`No repository context found for ${n}`),[];let i=Uo(n),o=this.toRelativePath(i,r);await this.loadDismissalsForFile(r,o);let s=this.documentManager.getDismissals(o),a=await this.loadRulesWithCache(r),c=this.documentManager.getDocument(n);if(!c){let u=oi(n),l=this.documentManager.getAllDocumentUris();for(let E of l)if(oi(E)===u){c=this.documentManager.getDocument(E);break}}return s.map(u=>{let l=a.find(f=>f.internalId===u.ruleId),E="";if(c){let f=c.getText({start:{line:u.startLine-1,character:0},end:{line:u.startLine-1,character:100}});E=f.trim().substring(0,60),f.trim().length>60&&(E+="...")}let d="";if(u.dismissedAt){let f=new Date,T=new Date(u.dismissedAt),p=f.getTime()-T.getTime(),S=Math.floor(p/6e4),m=Math.floor(S/60),O=Math.floor(m/24);S<1?d="just now":S<60?d=`${S} minute${S===1?"":"s"} ago`:m<24?d=`${m} hour${m===1?"":"s"} ago`:d=`${O} day${O===1?"":"s"} ago`}return{dismissalId:u.dismissalId,ruleId:u.ruleId,displayId:l?.id||u.ruleId,ruleName:l?.message||"Unknown rule",line:u.startLine,fingerprint:u.fingerprint,fileName:o,codeSnippet:E,timeAgo:d}})}async handleUndismiss(e){let{uri:n,dismissalId:r}=e[0],i=this.findContextForFile(n);if(!i){this.connection.window.showErrorMessage("File is not in any repository"),this.connection.console.error(`No repository context found for ${n}`);return}let o=Uo(n),s=this.toRelativePath(o,i),c=this.documentManager.getDismissals(s).find(f=>f.dismissalId===r);if(!c){this.connection.window.showErrorMessage("Dismissal not found"),this.connection.console.error(`[${i.repoRoot}] Dismissal not found with ID ${r}`);return}await i.ruleProvider.deleteDismissal(c.ruleId,c.file,c.fingerprint);let l=(await this.loadRulesWithCache(i)).find(f=>f.internalId===c.ruleId),E=l?.id||c.ruleId,d=l?.message||"Unknown rule";this.connection.console.log(`Undismissed violation: ${E} at line ${c.startLine}`),this.connection.window.showInformationMessage(`Undismissed violation: ${E} at line ${c.startLine} - ${d}`),this.documentManager.clearDismissals(s),this.triggerExecution()}async handleRemember(e){let{uri:n,range:r,selectedText:i,note:o}=e[0],s=this.findContextForFile(n);if(!s){this.connection.window.showErrorMessage("File is not in any repository"),this.connection.console.error(`No repository context found for ${n}`);return}let a=Uo(n),c=this.toRelativePath(a,s);if(!o||o.trim().length===0){this.connection.window.showErrorMessage("Note is required to remember a pattern");return}let l=this.getDiagnosticsForUri(n).filter(E=>E.source==="wispbit"&&E.data?.match).map(E=>({...E.data.match,ruleId:E.data.rule?.id}));this.connection.console.log(`[${s.repoRoot}] Remembering pattern with ${l.length} matches: ${i.substring(0,100)}...`),this.connection.console.log(`[${s.repoRoot}] Note: ${o}`);try{await s.ruleProvider.rememberPattern(c,i,{start:{line:r.start.line+1,column:r.start.character+1},end:{line:r.end.line+1,column:r.end.character+1}},o.trim(),l),this.connection.window.showInformationMessage(`Pattern remembered: ${o.trim()}`)}catch(E){this.connection.console.log(`Failed to remember: ${E.message}`),this.connection.window.showErrorMessage(`Failed to remember: ${E.message}`)}}truncateMessage(e,n=60){return e.length<=n?e:e.substring(0,n)+"..."}generateCodeActions(e,n){let r=[],i=n.filter(c=>c.source==="wispbit"),o=i.filter(c=>c.data?.fix),s=o.length>1&&o.some((c,u)=>o.slice(u+1).some(l=>c.range.start.line<=l.range.end.line&&c.range.end.line>=l.range.start.line)),a=!1;for(let c of i){let u=c.data?.fix,l=u?.autofix||!1,E=u&&(!s||!a);if(u){let f=this.truncateMessage(c.message);r.push({title:`Apply fix: ${f}`,kind:"quickfix",command:{title:"Apply Fix",command:"wispbit.quickfix",arguments:[{uri:e,matchId:c.data?.matchId}]},diagnostics:[c],isPreferred:E}),E&&(a=!0),l&&r.push({title:`Fix: ${f}`,kind:"source.fixAll",command:{title:"Apply Fix",command:"wispbit.quickfix",arguments:[{uri:e,matchId:c.data?.matchId}]},diagnostics:[c],isPreferred:!1})}let d=this.truncateMessage(c.message);r.push({title:`Dismiss: ${d}`,kind:"quickfix",command:{title:"Dismiss",command:"wispbit.dismiss",arguments:[{uri:e,matchId:c.data?.matchId}]},diagnostics:[c]})}return r}generateHover(e,n){let r=e.find(d=>n.line<d.range.start.line||n.line>d.range.end.line?!1:d.range.start.line===d.range.end.line?n.character>=d.range.start.character&&n.character<=d.range.end.character:n.line===d.range.start.line?n.character>=d.range.start.character:n.line===d.range.end.line?n.character<=d.range.end.character:!0);if(!r)return null;let i=String(r.code||""),o;for(let d of this.repositoryContextManager.getAllContexts())if(o=d.rulesById.get(i),o)break;let s=r.data?.fix,a=r.data?.match?.file?.path,c=r.data?.uri,l=(c?this.findContextForFile(c):null)?.repoRoot||this.workspacePath,E="";if(s?.diffs&&s.diffs.length>0)for(let{file:d,diff:f}of s.diffs){if(!(d.path===a)){let p=CE(l,d.path),S=this.pathToNormalizedUri(p);E+=`*[${d.path}](${S})*
|
|
296
296
|
`}E+="```diff\n",E+=f,E+="\n```\n\n"}return o?.summary&&(o.summary.content&&(E+=`**Summary:** ${o.summary.content}
|
|
297
297
|
|