Sign In

@shiplightai/quality-tools

Package Overview
Dependencies
Maintainers
1
Versions
8
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@shiplightai/quality-tools - npm Package Compare versions

Comparing version
0.3.1
to
0.3.2
dist/chunk-HEVFR2MU.js

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 +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};
{
"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