@shiplightai/quality-tools
Advanced tools
Sorry, the diff of this file is too big to display
+21
| MIT License | ||
| Copyright (c) 2026 Shiplight | ||
| Permission is hereby granted, free of charge, to any person obtaining a copy | ||
| of this software and associated documentation files (the "Software"), to deal | ||
| in the Software without restriction, including without limitation the rights | ||
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
| copies of the Software, and to permit persons to whom the Software is | ||
| furnished to do so, subject to the following conditions: | ||
| The above copyright notice and this permission notice shall be included in all | ||
| copies or substantial portions of the Software. | ||
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
| SOFTWARE. |
+18
-13
| #!/usr/bin/env node | ||
| import{a as C,b as I,e as P,f as g,g as v,k as R,l as O,m as S,r as m,t as E,u as d}from"./chunk-6T2Z6PY5.js";import{existsSync as D,mkdirSync as J,readFileSync as G,writeFileSync as Q}from"node:fs";import{dirname as L,join as y,resolve as B}from"node:path";function l(e,t,o){let n=e[t+1];if(n===void 0||n.startsWith("--"))throw new Error(`${o} requires a value.`);return{nextIndex:t+1,value:n}}function f(e,t){let o=Number(e);if(!Number.isInteger(o)||o<0)throw new Error(`${t} must be a non-negative integer.`);return o}function T(e,t){let o=Number.parseInt(e,10);if(!Number.isInteger(o)||o<=0)throw new Error(`${t} must be a positive integer.`);return o}function j(e){let[t,o]=e.split("=",2);if(t===void 0||t.length===0)throw new Error("--profile-run must use <profile-id>=<positive-run-id>.");return{profileId:t,runId:T(o??"","--profile-run")}}function h(e,t){if(e.length>1)throw new Error("Only one project path can be provided.");return e[0]??t}function V(){console.log(`Generate ranked runtime-quality recommendations and write them to a repo-owned JSON file. | ||
| import{c as I,d as P,e as R,g as O,h as S,k as E,l as v,m as g,n as y,s as m,u as j,v as d}from"./chunk-HEVFR2MU.js";import{existsSync as J,mkdirSync as Q,readFileSync as G,writeFileSync as B}from"node:fs";import{dirname as V,join as b,resolve as L}from"node:path";function l(e,t,n){let o=e[t+1];if(o===void 0||o.startsWith("--"))throw new Error(`${n} requires a value.`);return{nextIndex:t+1,value:o}}function f(e,t){let n=Number(e);if(!Number.isInteger(n)||n<0)throw new Error(`${t} must be a non-negative integer.`);return n}function D(e,t){let n=Number.parseInt(e,10);if(!Number.isInteger(n)||n<=0)throw new Error(`${t} must be a positive integer.`);return n}function q(e){let[t,n]=e.split("=",2);if(t===void 0||t.length===0)throw new Error("--profile-run must use <profile-id>=<positive-run-id>.");return{profileId:t,runId:D(n??"","--profile-run")}}function h(e,t){if(e.length>1)throw new Error("Only one project path can be provided.");return e[0]??t}function W(){console.log(`Calculate the available quality scores and write them, with any ranked runtime | ||
| recommendations, to a repo-owned JSON file. | ||
| Without --observation-set the command reports the static scores derived from the | ||
| graph alone (coverage, evidence confidence, structure confidence). The Quality | ||
| score needs runtime observations, so it is reported as unavailable. | ||
| Usage: | ||
| quality-tools analyze --project-path <repo-root> --observation-set <set-id> [options] | ||
| quality-tools analyze --project-path <repo-root> [--observation-set <set-id>] [options] | ||
| Options: | ||
| --project-path <path> Target repo root. Defaults to the positional repo path or current directory. | ||
| --observation-set <id> Saved observation-set id to execute. Required. | ||
| --observation-set <id> Saved observation-set id to execute. Optional; without it only the static scores are reported. | ||
| --view <id> Optional saved QC view id. Defaults to whole-project. | ||
| --output <path> Override the output path. Defaults to .quality/generated/recommendations/<set>--<scope>.json | ||
| --output <path> Override the output path. Defaults to .quality/generated/recommendations/<set>--<scope>.json, or static--<scope>.json without an observation set. | ||
| --limit <n> Limit the number of emitted ranked recommendations. | ||
@@ -17,5 +22,5 @@ --branch <name> Optional branch override for the saved observation set run selection. | ||
| --help Show this help. | ||
| `)}function W(e){let t={},o=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="--help"||r==="-h"){t.help=!0;continue}if(r==="--project-path"){let i=l(e,n,r);t.projectPath=i.value,n=i.nextIndex;continue}if(r==="--observation-set"){let i=l(e,n,r);t.observationSetId=i.value,n=i.nextIndex;continue}if(r==="--view"){let i=l(e,n,r);t.viewId=i.value,n=i.nextIndex;continue}if(r==="--output"){let i=l(e,n,r);t.output=i.value,n=i.nextIndex;continue}if(r==="--limit"){let i=l(e,n,r);t.limit=f(i.value,r),n=i.nextIndex;continue}if(r==="--branch"){let i=l(e,n,r);t.branch=i.value,n=i.nextIndex;continue}if(r==="--commit"){let i=l(e,n,r);t.commit=i.value,n=i.nextIndex;continue}if(r==="--profile-run"){let i=l(e,n,r);t.profiles=[...t.profiles??[],j(i.value)],n=i.nextIndex;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);o.push(r)}return{projectPath:t.projectPath??h(o,"."),observationSetId:t.observationSetId,viewId:t.viewId,output:t.output,limit:t.limit,selection:t.branch===void 0&&t.commit===void 0&&(t.profiles?.length??0)===0?void 0:{...t.branch===void 0?{}:{branch:t.branch},...t.commit===void 0?{}:{commit:t.commit},...t.profiles===void 0?{}:{profiles:t.profiles}},help:t.help}}function Y(e){let t={...process.env},o=process.cwd(),n=B(e),r=[y(o,".env.local"),y(o,"apps/web/.env.local"),y(n,".env.local")];for(let i of r)Z(i,t);return t}function X(e){let t=e.trim();if(t.length<2)return t;let o=t[0];if(o!=='"'&&o!=="'"||t[t.length-1]!==o)return t;let n=t.slice(1,-1);return o==='"'?n.replace(/\\n/g,` | ||
| `).replace(/\\r/g,"\r").replace(/\\t/g," "):n}function Z(e,t){if(!D(e))return;let o=G(e,"utf8");for(let n of o.split(/\r?\n/u)){let r=n.trim();if(r.length===0||r.startsWith("#"))continue;let i=r.startsWith("export ")?r.slice(7).trimStart():r,u=i.indexOf("=");if(u<=0)continue;let s=i.slice(0,u).trim();!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(s)||t[s]!==void 0||(t[s]=X(i.slice(u+1)))}}function K(e){try{return m({repo:e,format:"json",includeCovered:!0}).records.map(t=>({quality_map:t.quality_map,expectation_id:t.expectation_id,prompt:t.prompt}))}catch{return[]}}async function q(e){try{let t=W(e);if(t.help)return V(),{exitCode:0};if(t.observationSetId===void 0||t.observationSetId.length===0)throw new Error("--observation-set is required.");let o=await E({projectPath:t.projectPath,observationSetId:t.observationSetId,viewId:t.viewId,output:t.output,limit:t.limit,selection:t.selection,env:Y(t.projectPath),fixPromptRecords:K(t.projectPath)});return J(L(o.outputPath),{recursive:!0}),Q(o.outputPath,`${JSON.stringify(o.file,null,2)} | ||
| `,"utf8"),console.log(o.outputPath),{exitCode:0}}catch(t){return d(t)}}import{mkdirSync as tt,writeFileSync as et}from"node:fs";import{dirname as nt}from"node:path";function ot(){console.log(`Generate coding-agent fix prompts from quality-evidence quality maps. | ||
| `)}function Y(e){let t={},n=[];for(let o=0;o<e.length;o+=1){let r=e[o];if(r==="--help"||r==="-h"){t.help=!0;continue}if(r==="--project-path"){let i=l(e,o,r);t.projectPath=i.value,o=i.nextIndex;continue}if(r==="--observation-set"){let i=l(e,o,r);t.observationSetId=i.value,o=i.nextIndex;continue}if(r==="--view"){let i=l(e,o,r);t.viewId=i.value,o=i.nextIndex;continue}if(r==="--output"){let i=l(e,o,r);t.output=i.value,o=i.nextIndex;continue}if(r==="--limit"){let i=l(e,o,r);t.limit=f(i.value,r),o=i.nextIndex;continue}if(r==="--branch"){let i=l(e,o,r);t.branch=i.value,o=i.nextIndex;continue}if(r==="--commit"){let i=l(e,o,r);t.commit=i.value,o=i.nextIndex;continue}if(r==="--profile-run"){let i=l(e,o,r);t.profiles=[...t.profiles??[],q(i.value)],o=i.nextIndex;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);n.push(r)}return{projectPath:t.projectPath??h(n,"."),observationSetId:t.observationSetId,viewId:t.viewId,output:t.output,limit:t.limit,selection:t.branch===void 0&&t.commit===void 0&&(t.profiles?.length??0)===0?void 0:{...t.branch===void 0?{}:{branch:t.branch},...t.commit===void 0?{}:{commit:t.commit},...t.profiles===void 0?{}:{profiles:t.profiles}},help:t.help}}function X(e){let t={...process.env},n=process.cwd(),o=L(e),r=[b(n,".env.local"),b(n,"apps/web/.env.local"),b(o,".env.local")];for(let i of r)K(i,t);return t}function Z(e){let t=e.trim();if(t.length<2)return t;let n=t[0];if(n!=='"'&&n!=="'"||t[t.length-1]!==n)return t;let o=t.slice(1,-1);return n==='"'?o.replace(/\\n/g,` | ||
| `).replace(/\\r/g,"\r").replace(/\\t/g," "):o}function K(e,t){if(!J(e))return;let n=G(e,"utf8");for(let o of n.split(/\r?\n/u)){let r=o.trim();if(r.length===0||r.startsWith("#"))continue;let i=r.startsWith("export ")?r.slice(7).trimStart():r,u=i.indexOf("=");if(u<=0)continue;let a=i.slice(0,u).trim();!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(a)||t[a]!==void 0||(t[a]=Z(i.slice(u+1)))}}function tt(e){try{return m({repo:e,format:"json",includeCovered:!0}).records.map(t=>({quality_map:t.quality_map,expectation_id:t.expectation_id,prompt:t.prompt}))}catch{return[]}}async function _(e){try{let t=Y(e);if(t.help)return W(),{exitCode:0};if(t.observationSetId!==void 0&&t.observationSetId.length===0)throw new Error("--observation-set must name a saved observation set.");let n=await j({projectPath:t.projectPath,observationSetId:t.observationSetId,viewId:t.viewId,output:t.output,limit:t.limit,selection:t.selection,env:X(t.projectPath),fixPromptRecords:tt(t.projectPath)});Q(V(n.outputPath),{recursive:!0}),B(n.outputPath,`${JSON.stringify(n.file,null,2)} | ||
| `,"utf8");let o=n.file.quality_score_availability;return o.status!=="available"&&o.reason!==void 0&&console.error(`Quality score unavailable: ${o.reason}`),console.log(n.outputPath),{exitCode:0}}catch(t){return d(t)}}import{mkdirSync as et,writeFileSync as ot}from"node:fs";import{dirname as nt}from"node:path";function rt(){console.log(`Generate coding-agent fix prompts from quality-evidence quality maps. | ||
@@ -33,4 +38,4 @@ Usage: | ||
| --help Show this help. | ||
| `)}function rt(e){let t={},o=[];for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="--help"||r==="-h"){t.help=!0;continue}if(r==="--project-path"){let i=l(e,n,r);t.projectPath=i.value,n=i.nextIndex;continue}if(r==="--format"){let i=l(e,n,r);if(i.value!=="markdown"&&i.value!=="json")throw new Error("--format must be markdown or json.");t.format=i.value,n=i.nextIndex;continue}if(r==="--output"){let i=l(e,n,r);t.output=i.value,n=i.nextIndex;continue}if(r==="--limit"){let i=l(e,n,r);t.limit=f(i.value,r),n=i.nextIndex;continue}if(r==="--target"){let i=l(e,n,r);t.target=i.value,n=i.nextIndex;continue}if(r==="--include-covered"){t.includeCovered=!0;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);o.push(r)}return{projectPath:t.projectPath??h(o,"."),format:t.format??"markdown",output:t.output,limit:t.limit,target:t.target,includeCovered:t.includeCovered??!1,help:t.help}}function _(e){try{let t=rt(e);if(t.help)return ot(),{exitCode:0};let o=m({repo:t.projectPath,format:t.format,output:t.output,limit:t.limit,target:t.target,includeCovered:t.includeCovered});return o.outputPath!==void 0?(tt(nt(o.outputPath),{recursive:!0}),et(o.outputPath,`${o.output} | ||
| `,"utf8")):console.log(o.output),{exitCode:0}}catch(t){return d(t)}}import{mkdirSync as it,readFileSync as U,writeFileSync as st}from"node:fs";import{dirname as at}from"node:path";function $(){console.log(`Produce and validate the canonical quality-observations JSON artifact. | ||
| `)}function it(e){let t={},n=[];for(let o=0;o<e.length;o+=1){let r=e[o];if(r==="--help"||r==="-h"){t.help=!0;continue}if(r==="--project-path"){let i=l(e,o,r);t.projectPath=i.value,o=i.nextIndex;continue}if(r==="--format"){let i=l(e,o,r);if(i.value!=="markdown"&&i.value!=="json")throw new Error("--format must be markdown or json.");t.format=i.value,o=i.nextIndex;continue}if(r==="--output"){let i=l(e,o,r);t.output=i.value,o=i.nextIndex;continue}if(r==="--limit"){let i=l(e,o,r);t.limit=f(i.value,r),o=i.nextIndex;continue}if(r==="--target"){let i=l(e,o,r);t.target=i.value,o=i.nextIndex;continue}if(r==="--include-covered"){t.includeCovered=!0;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);n.push(r)}return{projectPath:t.projectPath??h(n,"."),format:t.format??"markdown",output:t.output,limit:t.limit,target:t.target,includeCovered:t.includeCovered??!1,help:t.help}}function $(e){try{let t=it(e);if(t.help)return rt(),{exitCode:0};let n=m({repo:t.projectPath,format:t.format,output:t.output,limit:t.limit,target:t.target,includeCovered:t.includeCovered});return n.outputPath!==void 0?(et(nt(n.outputPath),{recursive:!0}),ot(n.outputPath,`${n.output} | ||
| `,"utf8")):console.log(n.output),{exitCode:0}}catch(t){return d(t)}}import{mkdirSync as at,readFileSync as N,writeFileSync as st}from"node:fs";import{dirname as ut}from"node:path";function k(){console.log(`Produce and validate the canonical quality-observations JSON artifact. | ||
@@ -54,3 +59,3 @@ Usage: | ||
| Canonical statuses: pass, fail, error, skipped. | ||
| `)}function ut(e){let t=[],o={dirty:!1,help:!1};for(let n=0;n<e.length;n+=1){let r=e[n];if(r==="--help"||r==="-h"){o.help=!0;continue}if(r==="--dirty"){o.dirty=!0;continue}let u={"--output":"output","--commit":"commit","--branch":"branch","--observed-at":"observedAt","--run-id":"runId","--run-url":"runUrl","--path":"path","--test-case":"testCase","--status":"status","--note":"note"}[r];if(u!==void 0){let s=l(e,n,r);o[u]=s.value,n=s.nextIndex;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);t.push(r)}return{positional:t,...o}}function p(e,t){if(e===void 0||e.trim().length===0)throw new Error(`${t} is required.`);return e.trim()}function lt(e,t){let o=Date.parse(e);if(Number.isNaN(o))throw new Error(`${t} must be a valid timestamp.`);return new Date(o).toISOString()}function dt(e,t){if(!(t===void 0||e.GITHUB_SERVER_URL===void 0||e.GITHUB_REPOSITORY===void 0))return`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}/actions/runs/${t}`}function N(e,t=process.env){let o=p(e.commit??t.GITHUB_SHA,"--commit or GITHUB_SHA"),n=e.branch??t.GITHUB_REF_NAME,r=lt(e.observedAt??new Date().toISOString(),"--observed-at"),i=e.runId??t.GITHUB_RUN_ID,u=e.runUrl??dt(t,i);return{schema_version:1,revision:{commit:o,...n===void 0||n.trim().length===0?{}:{branch:n.trim()},...e.dirty?{dirty:!0}:{}},...i===void 0?{}:{run:{id:i,...u===void 0?{}:{url:u}}},observed_at:r}}function b(e){return p(e.output,"--output")}function w(e,t){let o=g(v(t));if(o.status!=="valid"||o.document===void 0)throw new Error(o.diagnostics.map(n=>n.message).join(" "));it(at(e),{recursive:!0}),st(e,v(o.document),"utf8"),console.log(e)}function ct(e,t,o){let n=e.testFile??e.testClass;if(n===void 0)throw new Error(`Converted observation ${e.observationId} has no proof path.`);let r=o&&e.testProject!==void 0?`${e.testCase??"test"} [${e.testProject}]`:e.testCase;return{path:n,...r===void 0?{}:{test_case:r},status:e.status,...e.observedAt===t?{}:{observed_at:e.observedAt},...e.note===void 0?{}:{note:e.note}}}function k(e){return`${(e.testFile??e.testClass??"").replaceAll("\\","/")}::${e.testCase?.trim().toLowerCase()??""}`}function pt(e){if(e.status!=="valid")throw new Error(e.diagnostics.map(t=>t.message).join(" "))}function mt(e,t,o){let n=N(o),r=U(t,"utf8"),i={...n.revision},u=e==="junit"?C({report_xml:r,observed_at:n.observed_at,revision:i}):I({report_json:r,observed_at:n.observed_at,revision:i});pt(u);let s=new Map;u.observations.forEach(a=>{let c=k(a);s.set(c,(s.get(c)??0)+1)}),w(b(o),{...n,observations:u.observations.map(a=>ct(a,n.observed_at,(s.get(k(a))??0)>1))})}function ft(e){let t=e.status;if(t===void 0||!new Set(["pass","fail","error","skipped"]).has(t))throw new Error("--status must be one of: pass, fail, error, skipped.");let o=N(e);w(b(e),{...o,observations:[{path:p(e.path,"--path"),...e.testCase===void 0?{}:{test_case:p(e.testCase,"--test-case")},status:t,...e.note===void 0?{}:{note:p(e.note,"--note")}}]})}function A(e){let t=g(U(e,"utf8"));if(t.status!=="valid"||t.document===void 0)throw new Error(`${e}: ${t.diagnostics.map(o=>o.message).join(" ")}`);return t.document}function ht(e){return`${e.path.replaceAll("\\","/")}::${e.test_case?.trim().toLowerCase()??""}`}function gt(e){let t=e.path.replaceAll("\\","/"),o=e.test_case?.trim().toLowerCase();return o===void 0?t:`${t} :: ${o}`}function vt(e,t){if(e.length===0)throw new Error("observations merge requires at least one manifest path.");let o=e.map(A),n=o[0];for(let s of o.slice(1)){if(s.revision.commit!==n.revision.commit)throw new Error("Cannot merge quality observations from different revisions.");if(s.run?.id!==n.run?.id)throw new Error("Cannot merge quality observations from different runs.")}let r=new Map,i=new Set;if(o.forEach(s=>{s.observations.forEach(a=>{let c=ht(a),x=r.get(c);x!==void 0?i.add(x):r.set(c,gt(a))})}),i.size>0)throw new Error(`Cannot merge duplicate observation identities: ${[...i].join(", ")}.`);let u=o.map(s=>s.observed_at).sort((s,a)=>Date.parse(a)-Date.parse(s))[0];w(b(t),{schema_version:1,revision:n.revision,...n.run===void 0?{}:{run:n.run},observed_at:u,observations:o.flatMap(s=>s.observations.map(a=>({...a,...a.observed_at===void 0&&s.observed_at!==u?{observed_at:s.observed_at}:{}})))})}async function M(e){try{let[t,...o]=e;if(t===void 0||t==="--help"||t==="-h")return $(),{exitCode:0};let n=ut(o);if(n.help)return $(),{exitCode:0};if(t==="schema")return process.stdout.write(P()),{exitCode:0};if(t==="validate"){if(n.positional.length!==1)throw new Error("observations validate requires one manifest path.");return A(n.positional[0]),{exitCode:0}}if(t==="record"){if(n.positional.length>0)throw new Error("observations record does not accept positional paths.");return ft(n),{exitCode:0}}if(t==="from-junit"||t==="from-playwright"){if(n.positional.length!==1)throw new Error(`observations ${t} requires one report path.`);return mt(t==="from-junit"?"junit":"playwright",n.positional[0],n),{exitCode:0}}if(t==="merge")return vt(n.positional,n),{exitCode:0};throw new Error(`Unknown observations subcommand: ${t}`)}catch(t){return d(t)}}function yt(){console.log(`Print the canonical quality-map JSON Schema (draft 2020-12) to stdout. | ||
| `)}function lt(e){let t=[],n={dirty:!1,help:!1};for(let o=0;o<e.length;o+=1){let r=e[o];if(r==="--help"||r==="-h"){n.help=!0;continue}if(r==="--dirty"){n.dirty=!0;continue}let u={"--output":"output","--commit":"commit","--branch":"branch","--observed-at":"observedAt","--run-id":"runId","--run-url":"runUrl","--path":"path","--test-case":"testCase","--status":"status","--note":"note"}[r];if(u!==void 0){let a=l(e,o,r);n[u]=a.value,o=a.nextIndex;continue}if(r.startsWith("--"))throw new Error(`Unknown option: ${r}`);t.push(r)}return{positional:t,...n}}function p(e,t){if(e===void 0||e.trim().length===0)throw new Error(`${t} is required.`);return e.trim()}function dt(e,t){let n=Date.parse(e);if(Number.isNaN(n))throw new Error(`${t} must be a valid timestamp.`);return new Date(n).toISOString()}function ct(e,t){if(!(t===void 0||e.GITHUB_SERVER_URL===void 0||e.GITHUB_REPOSITORY===void 0))return`${e.GITHUB_SERVER_URL}/${e.GITHUB_REPOSITORY}/actions/runs/${t}`}function A(e,t=process.env){let n=p(e.commit??t.GITHUB_SHA,"--commit or GITHUB_SHA"),o=e.branch??t.GITHUB_REF_NAME,r=dt(e.observedAt??new Date().toISOString(),"--observed-at"),i=e.runId??t.GITHUB_RUN_ID,u=e.runUrl??ct(t,i);return{schema_version:1,revision:{commit:n,...o===void 0||o.trim().length===0?{}:{branch:o.trim()},...e.dirty?{dirty:!0}:{}},...i===void 0?{}:{run:{id:i,...u===void 0?{}:{url:u}}},observed_at:r}}function w(e){return p(e.output,"--output")}function x(e,t){let n=g(y(t));if(n.status!=="valid"||n.document===void 0)throw new Error(n.diagnostics.map(o=>o.message).join(" "));at(ut(e),{recursive:!0}),st(e,y(n.document),"utf8"),console.log(e)}function pt(e,t,n){let o=e.testFile??e.testClass;if(o===void 0)throw new Error(`Converted observation ${e.observationId} has no proof path.`);let r=n&&e.testProject!==void 0?`${e.testCase??"test"} [${e.testProject}]`:e.testCase;return{path:o,...r===void 0?{}:{test_case:r},status:e.status,...e.observedAt===t?{}:{observed_at:e.observedAt},...e.note===void 0?{}:{note:e.note}}}function U(e){return v({path:e.testFile??e.testClass??"",test_case:e.testCase})}function mt(e){if(e.status!=="valid")throw new Error(e.diagnostics.map(t=>t.message).join(" "))}function ft(e,t,n){let o=A(n),r=N(t,"utf8"),i={...o.revision},u=e==="junit"?O({report_xml:r,observed_at:o.observed_at,revision:i}):S({report_json:r,observed_at:o.observed_at,revision:i});mt(u);let a=new Map;u.observations.forEach(s=>{let c=U(s);a.set(c,(a.get(c)??0)+1)}),x(w(n),{...o,observations:u.observations.map(s=>pt(s,o.observed_at,(a.get(U(s))??0)>1))})}function ht(e){let t=e.status;if(t===void 0||!new Set(["pass","fail","error","skipped"]).has(t))throw new Error("--status must be one of: pass, fail, error, skipped.");let n=A(e);x(w(e),{...n,observations:[{path:p(e.path,"--path"),...e.testCase===void 0?{}:{test_case:p(e.testCase,"--test-case")},status:t,...e.note===void 0?{}:{note:p(e.note,"--note")}}]})}function M(e){let t=g(N(e,"utf8"));if(t.status!=="valid"||t.document===void 0)throw new Error(`${e}: ${t.diagnostics.map(n=>n.message).join(" ")}`);return t.document}function vt(e){let t=e.path.replaceAll("\\","/"),n=e.test_case?.trim();return n===void 0?t:`${t} :: ${n}`}function gt(e,t){if(e.length===0)throw new Error("observations merge requires at least one manifest path.");let n=e.map(M),o=n[0];for(let a of n.slice(1)){if(a.revision.commit!==o.revision.commit)throw new Error("Cannot merge quality observations from different revisions.");if(a.run?.id!==o.run?.id)throw new Error("Cannot merge quality observations from different runs.")}let r=new Map,i=new Set;if(n.forEach(a=>{a.observations.forEach(s=>{let c=v(s),C=r.get(c);C!==void 0?i.add(C):r.set(c,vt(s))})}),i.size>0)throw new Error(`Cannot merge duplicate observation identities: ${[...i].join(", ")}.`);let u=n.map(a=>a.observed_at).sort((a,s)=>Date.parse(s)-Date.parse(a))[0];x(w(t),{schema_version:1,revision:o.revision,...o.run===void 0?{}:{run:o.run},observed_at:u,observations:n.flatMap(a=>a.observations.map(s=>({...s,...s.observed_at===void 0&&a.observed_at!==u?{observed_at:a.observed_at}:{}})))})}async function z(e){try{let[t,...n]=e;if(t===void 0||t==="--help"||t==="-h")return k(),{exitCode:0};let o=lt(n);if(o.help)return k(),{exitCode:0};if(t==="schema")return process.stdout.write(E()),{exitCode:0};if(t==="validate"){if(o.positional.length!==1)throw new Error("observations validate requires one manifest path.");return M(o.positional[0]),{exitCode:0}}if(t==="record"){if(o.positional.length>0)throw new Error("observations record does not accept positional paths.");return ht(o),{exitCode:0}}if(t==="from-junit"||t==="from-playwright"){if(o.positional.length!==1)throw new Error(`observations ${t} requires one report path.`);return ft(t==="from-junit"?"junit":"playwright",o.positional[0],o),{exitCode:0}}if(t==="merge")return gt(o.positional,o),{exitCode:0};throw new Error(`Unknown observations subcommand: ${t}`)}catch(t){return d(t)}}function yt(){console.log(`Print the canonical quality-map JSON Schema (draft 2020-12) to stdout. | ||
@@ -64,3 +69,3 @@ Usage: | ||
| quality-tools schema > quality-map.schema.json | ||
| `)}function z(e){if(e.includes("--help")||e.includes("-h"))return yt(),{exitCode:0};try{return process.stdout.write(R()),{exitCode:0}}catch(t){return d(t)}}import{relative as bt,resolve as wt}from"node:path";function xt(){console.log(`Validate a quality-map YAML file against this quality-tools version's engine. | ||
| `)}function T(e){if(e.includes("--help")||e.includes("-h"))return yt(),{exitCode:0};try{return process.stdout.write(I()),{exitCode:0}}catch(t){return d(t)}}import{relative as bt,resolve as wt}from"node:path";function xt(){console.log(`Validate a quality-map YAML file against this quality-tools version's engine. | ||
@@ -73,3 +78,3 @@ Usage: | ||
| invalid or has any error-severity diagnostic (warnings do not fail). | ||
| `)}function F(e){if(e.includes("--help")||e.includes("-h"))return xt(),{exitCode:0};try{let t=e.filter(a=>!a.startsWith("-"));if(t.length!==1)throw new Error("validate takes exactly one <map-path>. Run 'quality-tools validate --help'.");let o=wt(t[0]),n=S({projectRelativePath:bt(process.cwd(),o)||t[0],resolvedLocalPath:o}),r=O(n);for(let a of r.diagnostics){let c=`${a.severity.toUpperCase()} [${a.code}] ${a.yamlPath}: ${a.message}`;console.error(c)}let i=r.diagnostics.filter(a=>a.severity==="error").length,u=r.diagnostics.filter(a=>a.severity==="warning").length;return r.status==="invalid"||i>0?(console.error(`\u2717 ${t[0]}: invalid (${i} error${i===1?"":"s"}, ${u} warning${u===1?"":"s"})`),{exitCode:1}):(console.error(`\u2713 ${t[0]}: valid${u>0?` (${u} warning${u===1?"":"s"})`:""}`),{exitCode:0})}catch(t){return d(t)}}function H(){console.log(`Quality evidence tools. | ||
| `)}function F(e){if(e.includes("--help")||e.includes("-h"))return xt(),{exitCode:0};try{let t=e.filter(s=>!s.startsWith("-"));if(t.length!==1)throw new Error("validate takes exactly one <map-path>. Run 'quality-tools validate --help'.");let n=wt(t[0]),o=R({projectRelativePath:bt(process.cwd(),n)||t[0],resolvedLocalPath:n}),r=P(o);for(let s of r.diagnostics){let c=`${s.severity.toUpperCase()} [${s.code}] ${s.yamlPath}: ${s.message}`;console.error(c)}let i=r.diagnostics.filter(s=>s.severity==="error").length,u=r.diagnostics.filter(s=>s.severity==="warning").length;return r.status==="invalid"||i>0?(console.error(`\u2717 ${t[0]}: invalid (${i} error${i===1?"":"s"}, ${u} warning${u===1?"":"s"})`),{exitCode:1}):(console.error(`\u2713 ${t[0]}: valid${u>0?` (${u} warning${u===1?"":"s"})`:""}`),{exitCode:0})}catch(t){return d(t)}}function H(){console.log(`Quality evidence tools. | ||
@@ -87,2 +92,2 @@ Usage: | ||
| Run "quality-tools <command> --help" for command options. | ||
| `)}async function Ct(e){let[t]=e;return t===void 0||t==="--help"||t==="-h"?(H(),{exitCode:0}):t==="analyze"?q(e.slice(1)):t==="fix-prompts"?_(e.slice(1)):t==="observations"?M(e.slice(1)):t==="schema"?z(e.slice(1)):t==="validate"?F(e.slice(1)):(console.error(`Unknown command: ${t}`),H(),{exitCode:1})}var It=await Ct(process.argv.slice(2));process.exitCode=It.exitCode; | ||
| `)}async function Ct(e){let[t]=e;return t===void 0||t==="--help"||t==="-h"?(H(),{exitCode:0}):t==="analyze"?_(e.slice(1)):t==="fix-prompts"?$(e.slice(1)):t==="observations"?z(e.slice(1)):t==="schema"?T(e.slice(1)):t==="validate"?F(e.slice(1)):(console.error(`Unknown command: ${t}`),H(),{exitCode:1})}var It=await Ct(process.argv.slice(2));process.exitCode=It.exitCode; |
+1
-1
@@ -1,1 +0,1 @@ | ||
| import{c as e,d as t,e as a,f as i,g as o,h as p,i as n,j as r,k as s,l as y,m as l,n as u,o as m,p as c,q as d,r as R,s as M,t as Q,u as x}from"./chunk-6T2Z6PY5.js";export{n as GAP_CATEGORIES,e as QUALITY_OBSERVATION_SCHEMA_VERSION,r as buildQualityMapJsonSchema,t as buildQualityObservationManifestJsonSchema,Q as buildRecommendationExport,m as collectFixPrompts,d as fixPromptsOutputPath,R as generateFixPrompts,p as ingestObservationManifest,l as parseQualityMap,u as parseQualityMaps,i as parseQualityObservationManifest,x as printCommandError,M as recommendationExportOutputPath,c as renderFixPromptsMarkdown,s as serializeQualityMapJsonSchema,o as serializeQualityObservationManifest,a as serializeQualityObservationManifestJsonSchema,y as validateQualityMap}; | ||
| import{a as e,b as t,c as a,d as i,e as o,f as p,i as n,j as r,k as s,m as y,n as l,o as u,p as m,q as c,r as d,s as R,t as M,u as Q,v as x}from"./chunk-HEVFR2MU.js";export{e as GAP_CATEGORIES,n as QUALITY_OBSERVATION_SCHEMA_VERSION,t as buildQualityMapJsonSchema,r as buildQualityObservationManifestJsonSchema,Q as buildRecommendationExport,m as collectFixPrompts,d as fixPromptsOutputPath,R as generateFixPrompts,u as ingestObservationManifest,o as parseQualityMap,p as parseQualityMaps,y as parseQualityObservationManifest,x as printCommandError,M as recommendationExportOutputPath,c as renderFixPromptsMarkdown,a as serializeQualityMapJsonSchema,l as serializeQualityObservationManifest,s as serializeQualityObservationManifestJsonSchema,i as validateQualityMap}; |
+11
-12
| { | ||
| "name": "@shiplightai/quality-tools", | ||
| "version": "0.3.1", | ||
| "version": "0.3.2", | ||
| "type": "module", | ||
@@ -40,9 +40,2 @@ "description": "Quality graph analysis and canonical workflow-observation tools.", | ||
| }, | ||
| "scripts": { | ||
| "build": "tsup", | ||
| "check:size": "node scripts/check-package-size.mjs", | ||
| "clean": "rm -rf dist", | ||
| "prepublishOnly": "pnpm -w test && pnpm build && pnpm check:size", | ||
| "typecheck": "tsc -p tsconfig.json --noEmit" | ||
| }, | ||
| "dependencies": { | ||
@@ -54,8 +47,14 @@ "adm-zip": "0.5.16", | ||
| "devDependencies": { | ||
| "@shiplightai/quality-core": "workspace:*", | ||
| "@shiplightai/quality-map": "workspace:*", | ||
| "@types/node": "25.9.1", | ||
| "tsup": "8.3.5", | ||
| "typescript": "5.9.3" | ||
| "typescript": "5.9.3", | ||
| "@shiplightai/quality-core": "0.2.0", | ||
| "@shiplightai/quality-map": "0.1.0" | ||
| }, | ||
| "scripts": { | ||
| "build": "tsup", | ||
| "check:size": "node scripts/check-package-size.mjs", | ||
| "clean": "rm -rf dist", | ||
| "typecheck": "tsc -p tsconfig.json --noEmit" | ||
| } | ||
| } | ||
| } |
+23
-13
@@ -6,12 +6,12 @@ # @shiplightai/quality-tools | ||
| These examples require `@shiplightai/quality-tools` 0.3.0. Pin the interface so | ||
| an unpublished or stale `latest` tag cannot silently run 0.2.x: | ||
| These examples require `@shiplightai/quality-tools` 0.3.2. Pin the interface so | ||
| an older release cannot silently provide a different command contract: | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations --help | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations --help | ||
| ``` | ||
| Inside this source checkout, use | ||
| `pnpm exec tsx packages/quality-tools/src/cli.ts` in place of the pinned `npx` | ||
| prefix until 0.3.0 is published. | ||
| `pnpm exec tsx packages/quality-tools/src/cli.ts` to exercise changes that have | ||
| not reached the published package yet. | ||
@@ -25,3 +25,3 @@ ## Produce workflow observations | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations from-junit reports/junit.xml \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations from-junit reports/junit.xml \ | ||
| --output quality-observations.json | ||
@@ -31,3 +31,3 @@ ``` | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations from-playwright reports/playwright.json \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations from-playwright reports/playwright.json \ | ||
| --output quality-observations.json | ||
@@ -40,3 +40,3 @@ ``` | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations record \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations record \ | ||
| --path .github/workflows/publish.yml \ | ||
@@ -51,5 +51,5 @@ --test-case tarball-size \ | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations merge shard-*.json \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations merge shard-*.json \ | ||
| --output quality-observations.json | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations validate quality-observations.json | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations validate quality-observations.json | ||
| ``` | ||
@@ -60,3 +60,3 @@ | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 observations schema | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 observations schema | ||
| ``` | ||
@@ -70,3 +70,3 @@ | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 analyze \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 analyze \ | ||
| --project-path . \ | ||
@@ -81,6 +81,16 @@ --observation-set release-workflows-runtime-review \ | ||
| For an assessment without runtime observations, omit `--observation-set`: | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 analyze --project-path . | ||
| ``` | ||
| It loads no runtime results and writes `static--<scope>.json` carrying coverage, | ||
| evidence confidence, and structure confidence. The Quality score needs runtime | ||
| observations, and `quality_score_availability` explains when it is absent. | ||
| ## Generate fix prompts | ||
| ```bash | ||
| npx --yes @shiplightai/quality-tools@^0.3.0 fix-prompts \ | ||
| npx --yes @shiplightai/quality-tools@^0.3.2 fix-prompts \ | ||
| --project-path . \ | ||
@@ -87,0 +97,0 @@ --output .quality/fix-prompts.md |
Sorry, the diff of this file is too big to display
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Long strings
Supply chain riskContains long string literals, which may be a sign of obfuscated or packed code.
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
Minified code
QualityThis package contains minified code. This may be harmless in some cases where minified code is included in packaged libraries, however packages on npm should not minify code.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
154252
3.46%10
11.11%1042
1.56%93
12.05%7
16.67%2
100%