@principal-ai/quality-lens-cli 0.1.77 → 0.1.78
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/cli.js +1 -1
- package/package.json +2 -2
package/dist/cli.js
CHANGED
|
@@ -357,7 +357,7 @@ NOTE: as of Ohm v16, there is no default action for iteration nodes \u2014 see `
|
|
|
357
357
|
`)[0];return p.length>200?p.substring(0,197)+"...":p}formatTestFailureMessage(s,u){let y=u.replace(/\u001b\[[0-9;]*m/g,"").split(`
|
|
358
358
|
`)[0].substring(0,150);return`Test "${s}" failed: ${y}`}getEmptyResults(){return{numFailedTestSuites:0,numFailedTests:0,numPassedTestSuites:0,numPassedTests:0,numPendingTestSuites:0,numPendingTests:0,numRuntimeErrorTestSuites:0,numTodoTests:0,numTotalTestSuites:0,numTotalTests:0,openHandles:[],snapshot:{added:0,didUpdate:!1,failure:!1,filesAdded:0,filesRemoved:0,filesRemovedList:[],filesUnmatched:0,filesUpdated:0,matched:0,total:0,unchecked:0,uncheckedKeysByFile:[],unmatched:0,updated:0},startTime:Date.now(),success:!0,testResults:[],wasInterrupted:!1}}normalizeResults(s){let u=this.getEmptyResults(),p=s;return{...u,...p,testResults:(p.testResults||[]).map(y=>({...y,assertionResults:y.assertionResults||[],perfStats:y.perfStats||{}}))}}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new vgr.PathMapper(s)}return this.pathMapper}async execute(){if(this.config?.tool){let s=this.config.tool.args||[];s.some(p=>p==="--json")||this.addArgsToNpmScript(["--json"]),s.some(p=>p.includes("--no-colors")||p.includes("--colors=false"))||this.addArgsToNpmScript(["--no-colors"]),s.some(p=>p==="--coverage")||this.addArgsToNpmScript(["--coverage"])}return super.execute()}};Xce.JestLens=a4e});var qht=mt(kR=>{"use strict";Object.defineProperty(kR,"__esModule",{value:!0});kR.KnipLens=kR.SOURCE_EXTENSIONS=void 0;var Dgr=K1(),bgr=HT(),Cgr=Q1();kR.SOURCE_EXTENSIONS=new Set([".ts",".tsx",".js",".jsx",".mjs",".cjs",".mts",".cts"]);var o4e=class l extends Dgr.BaseLens{name="knip";description="Find unused files, exports, and dependencies";languages=["javascript","typescript"];supportedTools=["knip"];requirements=bgr.KNIP_REQUIREMENTS;pathMapper;injectedSourceFiles;constructor(s,u={}){super(s,u),this.injectedSourceFiles=u.sourceFiles}static calculateQualityScore(s){if(!s.success||s.error)return 0;let u=s.metrics;if(!u?.custom)return 100;let p=u.custom,y=(p.unlistedDependencies||0)*15,E=(p.unresolvedImports||0)*10,F=(p.unusedFiles||0)*5,R=(p.unusedDependencies||0)*2,z=(p.unusedDevDependencies||0)*1,K=(p.unusedExports||0)*1,re=(p.unusedTypes||0)*.5,pe=y+E+F+R+z+K+re,U=Math.max(0,Math.min(100,100-pe));return Math.round(U*100)/100}parse(s){let u=s.stderr||s.stdout;if(!u)return this.getEmptyOutput();try{let p=JSON.parse(u);return this.normalizeOutput(p)}catch(p){this.debug("Failed to parse Knip output as JSON, trying to extract from npm wrapper:",p);let y=u.split(`
|
|
359
359
|
`);for(let E of y){let F=E.trim();if(F.startsWith("{"))try{let R=JSON.parse(F);return this.debug("Successfully extracted JSON from npm wrapper output"),this.normalizeOutput(R)}catch{this.debug("Line started with { but was not valid JSON:",F.substring(0,50));continue}}if(s.stderr&&!s.stderr.trim().startsWith("{"))throw new Error(`Knip execution error: ${s.stderr}`);return this.debug("No valid JSON found in output, returning empty results"),this.getEmptyOutput()}}format(s){let u=[],p=this.getPathMapper();for(let[U,Me]of Object.entries(s.files||{})){let Se=p.toRelative(U);u.push({file:Se,line:1,column:1,severity:"warning",message:`Unused file: ${Me.join(", ")||"file not referenced"}`,rule:"unused-file",source:this.name,category:"files"})}for(let[U,Me]of s.dependencies||[])u.push({file:p.toRelative(U+"/package.json"),line:1,column:1,severity:"warning",message:`Unused dependency: ${Me}`,rule:"unused-dependency",source:this.name,category:"dependencies"});for(let[U,Me]of s.devDependencies||[])u.push({file:p.toRelative(U+"/package.json"),line:1,column:1,severity:"info",message:`Unused devDependency: ${Me}`,rule:"unused-devDependency",source:this.name,category:"dependencies"});for(let[U,Me]of Object.entries(s.unlisted||{})){let Se=p.toRelative(U);for(let Ee of Me)u.push({file:Se,line:1,column:1,severity:"error",message:`Unlisted dependency: ${Ee}`,rule:"unlisted-dependency",source:this.name,category:"dependencies"})}for(let[U,Me]of Object.entries(s.unresolved||{})){let Se=p.toRelative(U);for(let Ee of Me)u.push({file:Se,line:1,column:1,severity:"error",message:`Unresolved import: ${Ee}`,rule:"unresolved-import",source:this.name,category:"imports"})}for(let[U,Me]of Object.entries(s.exports||{})){let Se=p.toRelative(U);for(let Ee of Me){let{line:Xe,col:ft,name:jt}=this.parseExportInfo(Ee);u.push({file:Se,line:Xe||1,column:ft||1,severity:"warning",message:`Unused export: ${jt}`,rule:"unused-export",source:this.name,category:"exports"})}}for(let[U,Me]of Object.entries(s.nsExports||{})){let Se=p.toRelative(U);for(let Ee of Me){let{line:Xe,col:ft,name:jt}=this.parseExportInfo(Ee);u.push({file:Se,line:Xe||1,column:ft||1,severity:"warning",message:`Unused namespace export: ${jt}`,rule:"unused-ns-export",source:this.name,category:"exports"})}}for(let[U,Me]of Object.entries(s.types||{})){let Se=p.toRelative(U);for(let Ee of Me){let{line:Xe,col:ft,name:jt}=this.parseExportInfo(Ee);u.push({file:Se,line:Xe||1,column:ft||1,severity:"info",message:`Unused type: ${jt}`,rule:"unused-type",source:this.name,category:"types"})}}for(let[U,Me]of Object.entries(s.classMembers||{})){let Se=p.toRelative(U);for(let Ee of Me){let{line:Xe,col:ft,name:jt}=this.parseExportInfo(Ee);u.push({file:Se,line:Xe||1,column:ft||1,severity:"info",message:`Unused class member: ${jt}`,rule:"unused-class-member",source:this.name,category:"class-members"})}}for(let[U,Me]of Object.entries(s.enumMembers||{})){let Se=p.toRelative(U);for(let Ee of Me){let{line:Xe,col:ft,name:jt}=this.parseExportInfo(Ee);u.push({file:Se,line:Xe||1,column:ft||1,severity:"info",message:`Unused enum member: ${jt}`,rule:"unused-enum-member",source:this.name,category:"enum-members"})}}for(let[U,Me]of Object.entries(s.duplicates||{})){let Se=p.toRelative(U);for(let[Ee,Xe]of Me)u.push({file:Se,line:1,column:1,severity:"warning",message:`Duplicate export: ${Xe} (original: ${Ee})`,rule:"duplicate-export",source:this.name,category:"duplicates"})}let y=this.findAllSourceFiles(),E=new Set([...Object.keys(s.files||{}),...Object.keys(s.exports||{}),...Object.keys(s.nsExports||{}),...Object.keys(s.types||{}),...Object.keys(s.nsTypes||{}),...Object.keys(s.classMembers||{}),...Object.keys(s.enumMembers||{}),...Object.keys(s.unlisted||{}),...Object.keys(s.unresolved||{}),...Object.keys(s.duplicates||{})].map(U=>p.toRelative(U))),F=[...new Set([...y,...E])],R=F.length,z={error:u.filter(U=>U.severity==="error").length,warning:u.filter(U=>U.severity==="warning").length,info:u.filter(U=>U.severity==="info").length,hint:u.filter(U=>U.severity==="hint").length},K={};for(let U of u){let Me=U.category||"uncategorized";K[Me]=(K[Me]||0)+1}let re=this.buildFileMetrics(F,u),pe={lensName:this.name,tool:this.config?.tool.name||"knip",timestamp:Date.now(),success:!0,issues:u,fileMetrics:re,metrics:{filesAnalyzed:R,totalIssues:u.length,issuesBySeverity:z,executionTime:0,custom:{unusedFiles:Object.keys(s.files||{}).length,unusedDependencies:(s.dependencies||[]).length,unusedDevDependencies:(s.devDependencies||[]).length,unlistedDependencies:Object.values(s.unlisted||{}).flat().length,unresolvedImports:Object.values(s.unresolved||{}).flat().length,unusedExports:Object.values(s.exports||{}).flat().length,unusedTypes:Object.values(s.types||{}).flat().length,issuesByCategory:K}}};return pe.qualityScore=l.calculateQualityScore(pe),pe}parseExportInfo(s){let u=s.match(/^(.+?)\s*\((\d+):(\d+)\)$/);return u?{name:u[1].trim(),line:parseInt(u[2],10),col:parseInt(u[3],10)}:{name:s}}getEmptyOutput(){return{files:{},dependencies:[],devDependencies:[],optionalPeerDependencies:[],unlisted:{},binaries:{},unresolved:{},exports:{},nsExports:{},classMembers:{},types:{},nsTypes:{},enumMembers:{},duplicates:{}}}isV5Format(s){let u=Array.isArray(s.files),p=Array.isArray(s.issues);return u||p}convertV5ToV4(s){let u=this.getEmptyOutput();if(s.files&&Array.isArray(s.files))for(let p of s.files)u.files[p]=[];if(s.issues&&Array.isArray(s.issues))for(let p of s.issues){let{file:y}=p;if(p.dependencies&&Array.isArray(p.dependencies))for(let E of p.dependencies)u.dependencies.push(["",E]);if(p.devDependencies&&Array.isArray(p.devDependencies))for(let E of p.devDependencies)u.devDependencies.push(["",E]);if(p.optionalPeerDependencies&&Array.isArray(p.optionalPeerDependencies))for(let E of p.optionalPeerDependencies)u.optionalPeerDependencies.push(["",E]);if(p.unlisted&&Array.isArray(p.unlisted)&&(u.unlisted[y]||(u.unlisted[y]=[]),u.unlisted[y].push(...p.unlisted)),p.binaries&&Array.isArray(p.binaries)&&(u.binaries[y]||(u.binaries[y]=[]),u.binaries[y].push(...p.binaries)),p.unresolved&&Array.isArray(p.unresolved)&&(u.unresolved[y]||(u.unresolved[y]=[]),u.unresolved[y].push(...p.unresolved)),p.exports&&Array.isArray(p.exports)){u.exports[y]||(u.exports[y]=[]);for(let E of p.exports)u.exports[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.nsExports&&Array.isArray(p.nsExports)){u.nsExports[y]||(u.nsExports[y]=[]);for(let E of p.nsExports)u.nsExports[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.types&&Array.isArray(p.types)){u.types[y]||(u.types[y]=[]);for(let E of p.types)u.types[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.nsTypes&&Array.isArray(p.nsTypes)){u.nsTypes[y]||(u.nsTypes[y]=[]);for(let E of p.nsTypes)u.nsTypes[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.classMembers&&Array.isArray(p.classMembers)){u.classMembers[y]||(u.classMembers[y]=[]);for(let E of p.classMembers)u.classMembers[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.enumMembers&&Array.isArray(p.enumMembers)){u.enumMembers[y]||(u.enumMembers[y]=[]);for(let E of p.enumMembers)u.enumMembers[y].push(`${E.name} (${E.line}:${E.col})`)}if(p.duplicates&&Array.isArray(p.duplicates)){u.duplicates[y]||(u.duplicates[y]=[]);for(let E of p.duplicates)u.duplicates[y].push([E.name,E.name])}}return u}normalizeOutput(s){return this.isV5Format(s)?(this.debug("Detected Knip v5+ format, converting to v4 format"),this.convertV5ToV4(s)):{...this.getEmptyOutput(),...s}}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new Cgr.PathMapper(s)}return this.pathMapper}findAllSourceFiles(){return this.injectedSourceFiles?this.injectedSourceFiles:(this.debug("No source files injected, file metrics will only include files with issues"),[])}async execute(){return this.config?.tool&&((this.config.tool.args||[]).some(p=>p.includes("--reporter")||p==="-r")||this.addArgsToNpmScript(["--reporter","json"])),super.execute()}};kR.KnipLens=o4e});var Jht=mt(Zce=>{"use strict";Object.defineProperty(Zce,"__esModule",{value:!0});Zce.PrettierLens=void 0;var Sgr=K1(),Egr=HT(),xgr=Q1(),c4e=class l extends Sgr.BaseLens{name="prettier";description="Code formatter";languages=["javascript","typescript","json","css","html","markdown"];supportedTools=["prettier"];requirements=Egr.PRETTIER_REQUIREMENTS;pathMapper;static calculateQualityScore(s){if(!s.success||s.error)return 0;let u=s.metrics;if(!u)return 100;let p=Math.max(u.filesAnalyzed,1),y=u.issuesBySeverity.error;if(y===0)return 100;let E=Math.max(0,100-y/p*100);return Math.round(E*100)/100}parse(s){let u=[],p=[],y=s.stderr||s.stdout||"";if(!y)return{filesNeedingFormatting:u,filesChecked:0,analyzedFilePaths:p};let E=y.split(`
|
|
360
|
-
`).map(z=>z.trim()).filter(Boolean),F=/^\[debug\] resolve config from ["'](.+?)["']$/;for(let z of E){let K=z.match(F);if(K){p.push(K[1]);continue}if(z.startsWith("Checking formatting...")||z.startsWith("All matched files")||z.startsWith("Code style issues found")||z.startsWith("Code style issues")||z.startsWith("Ignored unknown option")||z.includes("prettier --write")||z.startsWith(">")||z.startsWith("$")||z.startsWith("[debug]")||z.match(/^(npm|yarn|pnpm|bun)\s+/)||z==="")continue;let re=z.replace(/^\[warn\]\s*/,"");re&&!re.match(/^\d+.*files?.*checked/i)&&!re.match(/Code style issues/)&&u.push(re)}let R=p.length;if(R===0){let z=y.match(/Code style issues found in (\d+) file/i);if(z){let pe=parseInt(z[1],10);pe===u.length&&(R=pe)}let re=(s.stderr||"").match(/Checked (\d+) files/i)||y.match(/Checked (\d+) files/i);re&&(R=parseInt(re[1],10))}return{filesNeedingFormatting:u,filesChecked:R,analyzedFilePaths:p}}format(s){let u=[],p=[],y=this.getPathMapper(),E=s.analyzedFilePaths.map(re=>y.toRelative(re)),F=new Set(s.filesNeedingFormatting.map(re=>y.toRelative(re)));for(let re of E){let pe=F.has(re);p.push({path:re,hasIssues:pe})}for(let re of s.filesNeedingFormatting){let pe=y.toRelative(re);u.push({file:pe,line:1,column:1,severity:"error",message:"File is not formatted according to Prettier rules",rule:"prettier",source:this.name,category:"formatting"})}let R=s.filesChecked||s.filesNeedingFormatting.length,z=this.buildFileMetrics(E,u),K={lensName:this.name,tool:this.config?.tool.name||"prettier",timestamp:Date.now(),success:!0,issues:u,analyzedFiles:p,fileMetrics:z,metrics:{filesAnalyzed:R,totalIssues:u.length,issuesBySeverity:{error:u.length,warning:0,info:0,hint:0},executionTime:0,custom:{filesNeedingFormatting:s.filesNeedingFormatting.length,formattingCompliance:R>0?Math.round((1-s.filesNeedingFormatting.length/R)*100*100)/100:100}}};return K.qualityScore=l.calculateQualityScore(K),K}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new xgr.PathMapper(s)}return this.pathMapper}async execute(){if(this.config?.tool){let s=this.config.tool.args||[];s.some(E=>E==="--check"||E==="-c")||this.addArgsToNpmScript(["--check"]),s.some(E=>E==="--no-error-on-unmatched-pattern")||this.addArgsToNpmScript(["--no-error-on-unmatched-pattern"]),s.some(E=>E.startsWith("--log-level")||E.startsWith("--loglevel"))||this.addArgsToNpmScript(["--log-level","debug"
|
|
360
|
+
`).map(z=>z.trim()).filter(Boolean),F=/^\[debug\] resolve config from ["'](.+?)["']$/;for(let z of E){let K=z.match(F);if(K){p.push(K[1]);continue}if(z.startsWith("Checking formatting...")||z.startsWith("All matched files")||z.startsWith("Code style issues found")||z.startsWith("Code style issues")||z.startsWith("Ignored unknown option")||z.includes("prettier --write")||z.startsWith(">")||z.startsWith("$")||z.startsWith("[debug]")||z.match(/^(npm|yarn|pnpm|bun)\s+/)||z==="")continue;let re=z.replace(/^\[warn\]\s*/,"");re&&!re.match(/^\d+.*files?.*checked/i)&&!re.match(/Code style issues/)&&u.push(re)}let R=p.length;if(R===0){let z=y.match(/Code style issues found in (\d+) file/i);if(z){let pe=parseInt(z[1],10);pe===u.length&&(R=pe)}let re=(s.stderr||"").match(/Checked (\d+) files/i)||y.match(/Checked (\d+) files/i);re&&(R=parseInt(re[1],10))}return{filesNeedingFormatting:u,filesChecked:R,analyzedFilePaths:p}}format(s){let u=[],p=[],y=this.getPathMapper(),E=s.analyzedFilePaths.map(re=>y.toRelative(re)),F=new Set(s.filesNeedingFormatting.map(re=>y.toRelative(re)));for(let re of E){let pe=F.has(re);p.push({path:re,hasIssues:pe})}for(let re of s.filesNeedingFormatting){let pe=y.toRelative(re);u.push({file:pe,line:1,column:1,severity:"error",message:"File is not formatted according to Prettier rules",rule:"prettier",source:this.name,category:"formatting"})}let R=s.filesChecked||s.filesNeedingFormatting.length,z=this.buildFileMetrics(E,u),K={lensName:this.name,tool:this.config?.tool.name||"prettier",timestamp:Date.now(),success:!0,issues:u,analyzedFiles:p,fileMetrics:z,metrics:{filesAnalyzed:R,totalIssues:u.length,issuesBySeverity:{error:u.length,warning:0,info:0,hint:0},executionTime:0,custom:{filesNeedingFormatting:s.filesNeedingFormatting.length,formattingCompliance:R>0?Math.round((1-s.filesNeedingFormatting.length/R)*100*100)/100:100}}};return K.qualityScore=l.calculateQualityScore(K),K}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new xgr.PathMapper(s)}return this.pathMapper}async execute(){if(this.config?.tool){let s=this.config.tool.args||[];s.some(E=>E==="--check"||E==="-c")||this.addArgsToNpmScript(["--check"]),s.some(E=>E==="--no-error-on-unmatched-pattern")||this.addArgsToNpmScript(["--no-error-on-unmatched-pattern"]),s.some(E=>E.startsWith("--log-level")||E.startsWith("--loglevel"))||this.addArgsToNpmScript(["--log-level","debug"])}return super.execute()}};Zce.PrettierLens=c4e});var Uht=mt(FR=>{"use strict";Object.defineProperty(FR,"__esModule",{value:!0});FR.TypeScriptLens=FR.TS_SOURCE_EXTENSIONS=void 0;var Tgr=K1(),Agr=HT(),kgr=Q1();FR.TS_SOURCE_EXTENSIONS=new Set([".ts",".tsx",".js",".jsx",".mts",".mjs",".cts",".cjs"]);var Ub;(function(l){l[l.Warning=0]="Warning",l[l.Error=1]="Error",l[l.Suggestion=2]="Suggestion",l[l.Message=3]="Message"})(Ub||(Ub={}));var u4e=class l extends Tgr.BaseLens{name="typescript";description="TypeScript type checking";languages=["typescript","javascript"];supportedTools=["tsc","typescript"];requirements=Agr.TYPESCRIPT_REQUIREMENTS;pathMapper;injectedSourceFiles;constructor(s,u={}){super(s,u),this.injectedSourceFiles=u.sourceFiles}static calculateQualityScore(s){if(!s.success||s.error)return 0;let u=s.metrics;if(!u)return 100;let p=Math.max(u.custom?.filesWithErrors||u.filesAnalyzed,1),{error:y,warning:E,info:F}=u.issuesBySeverity;if(y===0&&E===0&&F===0)return 100;let R=y/p*8,z=E/p*2,K=F/p*.5,re=R+z+K,pe=Math.max(0,Math.min(100,100-re));return Math.round(pe*100)/100}parse(s){let u=[],p=[];if(s.stdout){let K=s.stdout.split(`
|
|
361
361
|
`).filter(re=>re.trim().length>0);for(let re of K){let pe=re.trim();(pe.endsWith(".ts")||pe.endsWith(".tsx"))&&!pe.includes("/node_modules/")&&p.push(pe)}}let y=p.length,E=s.stderr||"";if(!E)return{errors:u,filesAnalyzed:y,analyzedFilePaths:p};let F=/^(.+?)\((\d+),(\d+)\):\s+(error|warning)\s+(TS\d+):\s+(.+)$/gm,R;for(;(R=F.exec(E))!==null;){let[,K,re,pe,U,Me,Se]=R;u.push({file:K,line:parseInt(re,10),column:parseInt(pe,10),code:Me,message:Se,category:U==="error"?Ub.Error:Ub.Warning})}let z=/^([^:]+):(\d+):(\d+)\s+-\s+(error|warning)\s+(TS\d+):\s+(.+)$/gm;for(;(R=z.exec(E))!==null;){let[,K,re,pe,U,Me,Se]=R;u.some(Xe=>Xe.file===K&&Xe.line===parseInt(re,10)&&Xe.column===parseInt(pe,10)&&Xe.code===Me)||u.push({file:K,line:parseInt(re,10),column:parseInt(pe,10),code:Me,message:Se,category:U==="error"?Ub.Error:Ub.Warning})}return{errors:u,filesAnalyzed:y,analyzedFilePaths:p}}format(s){let u=[],p=this.getPathMapper(),y;if(s.analyzedFilePaths.length>0)y=s.analyzedFilePaths.map(Se=>p.toRelative(Se));else if(y=this.findAllSourceFiles(),y.length===0&&s.errors.length>0){let Se=[...new Set(s.errors.map(Ee=>p.toRelative(Ee.file)))];y=Se,this.debug(`Using ${Se.length} files extracted from errors as fallback`)}for(let Se of s.errors){let Ee=p.toRelative(Se.file);u.push({file:Ee,line:Se.line,column:Se.column,severity:this.mapTSSeverity(Se.category),message:Se.message,rule:Se.code,source:this.name,category:this.categorizeError(Se.code)})}let E=new Set(s.errors.map(Se=>Se.file)).size,F=s.errors.filter(Se=>Se.category===Ub.Error).length,R=s.errors.filter(Se=>Se.category===Ub.Warning).length,z=s.errors.filter(Se=>Se.category===Ub.Suggestion).length,K={};for(let Se of s.errors)K[Se.code]=(K[Se.code]||0)+1;let re=Object.entries(K).sort((Se,Ee)=>Ee[1]-Se[1]).slice(0,5),pe=this.buildFileMetrics(y,u),U=y.length,Me={lensName:this.name,tool:this.config?.tool.name||"tsc",timestamp:Date.now(),success:!0,issues:u,fileMetrics:pe,metrics:{filesAnalyzed:U,totalIssues:u.length,issuesBySeverity:{error:F,warning:R,info:z,hint:0},executionTime:0,custom:{filesWithErrors:E,totalErrors:F,totalWarnings:R,totalSuggestions:z,uniqueErrorCodes:Object.keys(K).length,topErrors:re.map(([Se,Ee])=>({code:Se,count:Ee}))}}};return Me.qualityScore=l.calculateQualityScore(Me),Me}mapTSSeverity(s){switch(s){case Ub.Error:return"error";case Ub.Warning:return"warning";case Ub.Suggestion:return"info";case Ub.Message:default:return"info"}}categorizeError(s){switch(s){case"TS2307":case"TS2304":case"TS2552":return"resolution";case"TS2322":case"TS2345":case"TS2769":return"type-mismatch";case"TS2339":case"TS2551":return"property-access";case"TS7006":case"TS7031":return"implicit-any"}let u=parseInt(s.replace("TS",""),10);return u>=1e3&&u<2e3?"syntax":u>=2e3&&u<3e3?"semantic":u>=3e3&&u<4e3?"declaration":u>=4e3&&u<5e3?"signature":u>=5e3&&u<6e3?"jsx":u>=6e3&&u<7e3?"messages":u>=7e3&&u<8e3?"noImplicitAny":u>=8e3&&u<9e3?"decorators":u>=17e3&&u<18e3?"jsx-attributes":u>=18e3&&u<19e3?"async":"general"}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new kgr.PathMapper(s)}return this.pathMapper}findAllSourceFiles(){return this.injectedSourceFiles&&this.injectedSourceFiles.length>0?this.injectedSourceFiles:(this.debug("No source files injected, file metrics will only include files from tsc output"),[])}async execute(){if(this.config?.tool){let s=this.config.tool.args||[];!s.some(p=>p==="--noEmit")&&!s.some(p=>p==="--build"||p==="-b")&&this.addArgsToNpmScript(["--noEmit"]),s.some(p=>p.includes("--pretty"))||this.addArgsToNpmScript(["--pretty","false"]),s.some(p=>p==="--listFiles")||this.addArgsToNpmScript(["--listFiles"])}return super.execute()}};FR.TypeScriptLens=u4e});var Vht=mt(KT=>{"use strict";var Fgr=KT&&KT.__createBinding||(Object.create?(function(l,s,u,p){p===void 0&&(p=u);var y=Object.getOwnPropertyDescriptor(s,u);(!y||("get"in y?!s.__esModule:y.writable||y.configurable))&&(y={enumerable:!0,get:function(){return s[u]}}),Object.defineProperty(l,p,y)}):(function(l,s,u,p){p===void 0&&(p=u),l[p]=s[u]})),wgr=KT&&KT.__setModuleDefault||(Object.create?(function(l,s){Object.defineProperty(l,"default",{enumerable:!0,value:s})}):function(l,s){l.default=s}),zht=KT&&KT.__importStar||(function(){var l=function(s){return l=Object.getOwnPropertyNames||function(u){var p=[];for(var y in u)Object.prototype.hasOwnProperty.call(u,y)&&(p[p.length]=y);return p},l(s)};return function(s){if(s&&s.__esModule)return s;var u={};if(s!=null)for(var p=l(s),y=0;y<p.length;y++)p[y]!=="default"&&Fgr(u,s,p[y]);return wgr(u,s),u}})();Object.defineProperty(KT,"__esModule",{value:!0});KT.VitestLens=void 0;var Wht=zht(require("fs")),Igr=zht(require("path")),Pgr=K1(),Ngr=HT(),Ogr=Q1(),l4e=class l extends Pgr.BaseLens{name="vitest";description="Vitest test runner";languages=["javascript","typescript"];supportedTools=["vitest"];requirements=Ngr.VITEST_REQUIREMENTS;pathMapper;static calculateQualityScore(s){if(!s.success||s.error)return 0;let u=s.metrics;if(!u?.custom)return 100;let p=u.custom,y=p.totalTests||0,E=p.passedTests||0,F=p.skippedTests||0;if(y===0)return 0;let z=E/y*100,K=F/y*50;if(z-=K,s.coverage?.line!==void 0){let re=s.coverage.line;re>80?z+=10:re>60&&(z+=5)}return Math.max(0,Math.min(100,Math.round(z*100)/100))}parse(s){let u=s.stdout||s.stderr||"";if(!u)return this.getEmptyResults();try{let p=u.indexOf("{"),y=u.lastIndexOf("}");if(p!==-1&&y!==-1&&y>p){let F=u.substring(p,y+1),R=JSON.parse(F);return this.normalizeResults(R)}let E=JSON.parse(u);return this.normalizeResults(E)}catch(p){return this.debug("Failed to parse Vitest output as JSON:",p),this.parseTextOutput(u)}}parseTextOutput(s){let u=this.getEmptyResults(),p=s.split(`
|
|
362
362
|
`),y=0,E=0,F=0,R=0;for(let z of p){let K=z.match(/Tests\s+(\d+)\s+passed(?:\s*\|\s*(\d+)\s+failed)?(?:\s*\|\s*(\d+)\s+(?:skipped|todo))?/i);K&&(E=parseInt(K[1],10)||0,F=parseInt(K[2],10)||0,R=parseInt(K[3],10)||0,y=E+F+R);let re=z.match(/[✓✗]\s+(.+\.(?:test|spec)\.[jt]sx?)\s+\((\d+)\)/);if(re){let pe=z.includes("\u2713");u.testResults=u.testResults||[],u.testResults.push({name:re[1],status:pe?"pass":"fail"})}}return u.numTotalTests=y,u.numPassedTests=E,u.numFailedTests=F,u.numPendingTests=R,u.success=F===0,u}format(s){let u=[],p=this.getPathMapper();for(let Xe of s.testResults||[]){let ft=p.toRelative(Xe.name),jt=Xe.assertionResults||this.extractTestsFromTasks(Xe.tasks||[]);for(let Gt of jt)if(Gt.status==="fail"){let Or=Gt.error?.message||"Test failed";u.push({file:ft,line:1,column:1,severity:"error",message:`Test "${Gt.name}" failed: ${this.cleanMessage(Or)}`,rule:"test-failed",source:this.name,category:"test-case"})}}let y=s.numTotalTests||0,E=s.numPassedTests||0,F=s.numFailedTests||0,R=(s.numPendingTests||0)+(s.numTodoTests||0),z=s.numTotalTestSuites||s.testResults?.length||0,K=s.numPassedTestSuites||0,re=s.numFailedTestSuites||0,pe=y>0?E/y*100:0,U=(s.testResults||[]).map(Xe=>p.toRelative(Xe.name)),Me=this.buildFileMetrics(U,u),Se={lensName:this.name,tool:this.config?.tool.name||"vitest",timestamp:Date.now(),success:s.success!==!1&&F===0,issues:u,fileMetrics:Me,metrics:{filesAnalyzed:z,totalIssues:u.length,issuesBySeverity:{error:F,warning:0,info:0,hint:0},executionTime:0,custom:{totalTests:y,passedTests:E,failedTests:F,skippedTests:R,testSuites:z,passedSuites:K,failedSuites:re,passRate:Math.round(pe*100)/100}}},Ee=this.readCoverageData(p);return Ee&&(Se.coverage=Ee),Se.qualityScore=l.calculateQualityScore(Se),Se}readCoverageData(s){let u=this.config?.cwd||process.cwd(),p=Igr.join(u,"coverage","coverage-final.json");if(!Wht.existsSync(p)){this.debug("No coverage file found at",p);return}try{let y=Wht.readFileSync(p,"utf-8"),E=JSON.parse(y);return this.buildCoverageData(E,s)}catch(y){this.debug("Failed to read coverage file:",y);return}}buildCoverageData(s,u){let p=[],y=0,E=0,F=0,R=0,z=0,K=0;for(let[re,pe]of Object.entries(s))if(typeof pe=="object"&&pe!==null){let U=u.toRelative(pe.path||re),Me=Object.values(pe.s||{}),Se=Me.length,Ee=Me.filter(vr=>vr>0).length,Xe=Object.values(pe.b||{}).flat(),ft=Xe.length,jt=Xe.filter(vr=>vr>0).length,Gt=Object.values(pe.f||{}),Or=Gt.length,Mr=Gt.filter(vr=>vr>0).length,Fr=Se>0?Ee/Se*100:100;p.push({file:U,lines:Math.round(Fr*100)/100,branches:ft>0?Math.round(jt/ft*1e4)/100:100,functions:Or>0?Math.round(Mr/Or*1e4)/100:100,statements:Math.round(Fr*100)/100}),y+=Se,E+=Ee,F+=ft,R+=jt,z+=Or,K+=Mr}return{line:y>0?Math.round(E/y*1e4)/100:0,branch:F>0?Math.round(R/F*1e4)/100:0,function:z>0?Math.round(K/z*1e4)/100:0,statement:y>0?Math.round(E/y*1e4)/100:0,files:p}}extractTestsFromTasks(s){let u=[];for(let p of s)p.type==="test"&&u.push({name:p.name,status:this.mapTaskState(p.result?.state,p.mode),duration:p.result?.duration,error:p.result?.error}),p.tasks&&u.push(...this.extractTestsFromTasks(p.tasks));return u}mapTaskState(s,u){if(u==="skip"||u==="todo")return u;switch(s){case"pass":return"pass";case"fail":return"fail";case"skip":return"skip";default:return"pending"}}cleanMessage(s){let p=s.replace(/\u001b\[[0-9;]*m/g,"").split(`
|
|
363
363
|
`)[0];return p.length>150?p.substring(0,147)+"...":p}getEmptyResults(){return{numTotalTestSuites:0,numPassedTestSuites:0,numFailedTestSuites:0,numPendingTestSuites:0,numTotalTests:0,numPassedTests:0,numFailedTests:0,numPendingTests:0,numTodoTests:0,startTime:Date.now(),success:!0,testResults:[]}}normalizeResults(s){return{...this.getEmptyResults(),...s}}getPathMapper(){if(!this.pathMapper){let s=this.config?.cwd||process.cwd();this.pathMapper=new Ogr.PathMapper(s)}return this.pathMapper}async execute(){if(this.config?.tool){let s=this.config.tool.args||[];s.some(p=>p.includes("--reporter"))||this.addArgsToNpmScript(["--reporter=json"]),s.some(p=>p==="--run"||p==="--watch=false")||this.addArgsToNpmScript(["--run"])}return super.execute()}};KT.VitestLens=l4e});var $ht=mt(eue=>{"use strict";Object.defineProperty(eue,"__esModule",{value:!0});eue.GitLens=void 0;var Lgr=K1(),Mgr=Q1(),f4e=class l extends Lgr.BaseLens{name="git";description="Version control insights from Git";languages=["*"];supportedTools=["git"];pathMapper;includeCommitDetails=!1;static calculateQualityScore(s){if(!s.success||s.error)return 0;let u=s.metrics;if(!u?.custom)return 100;let p=u.custom;if(!p.hasUncommittedChanges)return 100;let y=(p.modified||0)*2,E=(p.deleted||0)*2,F=(p.untracked||0)*1,R=y+E+F,z=Math.max(0,Math.min(100,100-R));return Math.round(z*100)/100}configure(s){super.configure(s),this.includeCommitDetails=s.includeCommitDetails??!1}async execute(){if(!this.config)throw new Error(`Lens ${this.name} is not configured`);if(!this.executor)throw new Error(`Lens ${this.name} has no executor`);let s=this.config.tool.cwd||this.config.cwd,u=this.config.tool.command||"git",p={},y=[];try{let E=await this.executor.execute(u,["rev-parse","--abbrev-ref","HEAD"],{cwd:s});E.error?(this.debug("Branch command error:",E.error),y.push("branch retrieval failed")):E.exitCode===0?p.branch=E.stdout.trim():(this.debug("Branch command failed with exit code:",E.exitCode),y.push("branch retrieval failed"))}catch(E){this.debug("Failed to get branch:",E),y.push("branch retrieval failed")}try{let E=await this.executor.execute(u,["rev-parse","HEAD"],{cwd:s});E.error?(this.debug("Commit command error:",E.error),y.push("commit retrieval failed")):E.exitCode===0?p.commit=E.stdout.trim():(this.debug("Commit command failed with exit code:",E.exitCode),y.push("commit retrieval failed"))}catch(E){this.debug("Failed to get commit:",E),y.push("commit retrieval failed")}try{let E=await this.executor.execute(u,["status","--porcelain"],{cwd:s});if(E.error)throw new Error(`Git command not found or failed to execute: ${E.error.message}`);if(E.exitCode===0)p.status=E.stdout;else throw new Error(`Git status failed with exit code ${E.exitCode}: ${E.stderr}`)}catch(E){throw this.debug("Failed to get status:",E),y.push("status retrieval failed"),new Error(`Git status failed: ${E}`)}if(this.includeCommitDetails&&p.commit)try{let E=await this.executor.execute(u,["log","-1",'--format="%H|%h|%an|%ae|%ad|%B"',"--date=iso"],{cwd:s});E.exitCode===0&&(p.commitDetails=E.stdout.trim());let F=await this.executor.execute(u,["show","--stat","--format=","HEAD"],{cwd:s});F.exitCode===0&&(p.commitStats=F.stdout.trim())}catch(E){this.debug("Failed to get commit details:",E)}return{stdout:JSON.stringify(p),stderr:y.join(`
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@principal-ai/quality-lens-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.78",
|
|
4
4
|
"description": "CLI tool for running quality lenses on codebases in CI/CD pipelines",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"license": "MIT",
|
|
44
44
|
"devDependencies": {
|
|
45
45
|
"@principal-ai/codebase-composition": "^0.2.25",
|
|
46
|
-
"@principal-ai/codebase-quality-lenses": "^0.1.
|
|
46
|
+
"@principal-ai/codebase-quality-lenses": "^0.1.48",
|
|
47
47
|
"@principal-ai/quality-lens-registry": "^0.2.5",
|
|
48
48
|
"@principal-ai/repository-abstraction": "^0.2.0",
|
|
49
49
|
"@types/jest": "^30.0.0",
|