uptide 0.11.2-next.20261014004941 → 0.11.2

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.
@@ -3,7 +3,7 @@ import { fileURLToPath as __uptideFileURLToPath } from 'node:url';
3
3
  const require = __uptideCreateRequire(import.meta.url);
4
4
  const __filename = __uptideFileURLToPath(import.meta.url);
5
5
  const __dirname = __uptideFileURLToPath(new URL('.', import.meta.url));
6
- import{BUILD_TOOLS,UNUSED_REASON,activePack,activePacks,assertInstalledVersions,assertManagerAvailable,assertNoDowngrades,assertNoPinningOverrides,authHeaders,baselineHealth,bumpVersions,changeRule,check,companionProblems,createFsSurfaceCache,createNpmFetcher,createTypescriptAdapter,defaultCacheDir,dependencyGroups,describeServices,detectIn,diagnosticKey,diagnostics,editDiff,failedTestFiles,genericPack,gitLineMoves,globRegExp,groupPeerBlockers,heldBackText,importedByText,install,installedManifest,isFailure,knownTool,lineMoves,loadRegistryConfig,mapWithLimit,markPreexisting,minVersion,moved,newDiagnostics,newestAllowed,packageManager,packumentUrl,parentOf,payloadApiVersions,peerBlocks,peerPreflight,pinClient,prBody,progress,publicRepos,rangePreflight,readManifest3,recordedStatus,registryFor,releaseAgeError,require_dist,require_semver,resetSharedState,resolveTarget,ruleFor,satisfies,scanImports,sdkApiVersion,selectedFindings,siteKey,statusOf,stripePack,stripeUsageContext,summarize,testWorkspaces,tierOf,toolingReasons,truthDigest,typeChecker,typeResolutionFailure,typescriptAdapter,unpinnedClients,unplacedKey,validRange,validateVersionRanges,windowStart,workspacePackagesOf,writePeerOverrides}from"./chunk-S7NJB2LQ.js";import{require_ts_morph}from"./chunk-GHXBU767.js";import{assertVerifiedTree,publicationBlockers,publish}from"./chunk-YKW36M33.js";import{IntegrityError,NO_PACK_YET,NoTypesError,PackageNotFoundError,UPTIDE_COMMAND,UptideError,VersionNotFoundError,command,compareVersions,errorCode,git,isMajorUpgrade,majorsBehind,parseVersion,projectRoot,uptideCommand,uptideVersionInfo}from"./chunk-M5YJSOFH.js";import{__commonJS,__toESM}from"./chunk-Z5YHBDVG.js";var require_picocolors=__commonJS({"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports,module){"use strict";var p=process||{},argv=p.argv||[],env=p.env||{},isColorSupported=!(env.NO_COLOR||argv.includes("--no-color"))&&(!!env.FORCE_COLOR||argv.includes("--color")||p.platform==="win32"||(p.stdout||{}).isTTY&&env.TERM!=="dumb"||!!env.CI),formatter=(open,close,replace=open)=>input=>{let string=""+input,index=string.indexOf(close,open.length);return~index?open+replaceClose(string,close,replace,index)+close:open+string+close},replaceClose=(string,close,replace,index)=>{let result="",cursor=0;do result+=string.substring(cursor,index)+replace,cursor=index+close.length,index=string.indexOf(close,cursor);while(~index);return result+string.substring(cursor)},createColors=(enabled=isColorSupported)=>{let f=enabled?formatter:()=>String;return{isColorSupported:enabled,reset:f("\x1B[0m","\x1B[0m"),bold:f("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:f("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:f("\x1B[3m","\x1B[23m"),underline:f("\x1B[4m","\x1B[24m"),inverse:f("\x1B[7m","\x1B[27m"),hidden:f("\x1B[8m","\x1B[28m"),strikethrough:f("\x1B[9m","\x1B[29m"),black:f("\x1B[30m","\x1B[39m"),red:f("\x1B[31m","\x1B[39m"),green:f("\x1B[32m","\x1B[39m"),yellow:f("\x1B[33m","\x1B[39m"),blue:f("\x1B[34m","\x1B[39m"),magenta:f("\x1B[35m","\x1B[39m"),cyan:f("\x1B[36m","\x1B[39m"),white:f("\x1B[37m","\x1B[39m"),gray:f("\x1B[90m","\x1B[39m"),bgBlack:f("\x1B[40m","\x1B[49m"),bgRed:f("\x1B[41m","\x1B[49m"),bgGreen:f("\x1B[42m","\x1B[49m"),bgYellow:f("\x1B[43m","\x1B[49m"),bgBlue:f("\x1B[44m","\x1B[49m"),bgMagenta:f("\x1B[45m","\x1B[49m"),bgCyan:f("\x1B[46m","\x1B[49m"),bgWhite:f("\x1B[47m","\x1B[49m"),blackBright:f("\x1B[90m","\x1B[39m"),redBright:f("\x1B[91m","\x1B[39m"),greenBright:f("\x1B[92m","\x1B[39m"),yellowBright:f("\x1B[93m","\x1B[39m"),blueBright:f("\x1B[94m","\x1B[39m"),magentaBright:f("\x1B[95m","\x1B[39m"),cyanBright:f("\x1B[96m","\x1B[39m"),whiteBright:f("\x1B[97m","\x1B[39m"),bgBlackBright:f("\x1B[100m","\x1B[49m"),bgRedBright:f("\x1B[101m","\x1B[49m"),bgGreenBright:f("\x1B[102m","\x1B[49m"),bgYellowBright:f("\x1B[103m","\x1B[49m"),bgBlueBright:f("\x1B[104m","\x1B[49m"),bgMagentaBright:f("\x1B[105m","\x1B[49m"),bgCyanBright:f("\x1B[106m","\x1B[49m"),bgWhiteBright:f("\x1B[107m","\x1B[49m")}};module.exports=createColors();module.exports.createColors=createColors}});var CREDENTIAL=/(?:sk-[\w-]{8,}|AIza[\w-]{12,}|phc_[\w-]{8,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_\w{20,}|xox[abpr]-[\w-]{8,}|bearer\s|eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]{8,})/i,kindOf=value=>value===null?"null":Array.isArray(value)?"a list":typeof value=="object"?"a mapping":typeof value=="string"?"text":typeof value=="boolean"?"true or false":typeof value=="number"?"a number":typeof value,EXPECTED={object:"a mapping",array:"a list",string:"text",integer:"a whole number",number:"a number",boolean:"true or false",null:"null"};function typeMatches(type,value){switch(type){case"null":return value===null;case"object":return!!value&&typeof value=="object"&&!Array.isArray(value);case"array":return Array.isArray(value);case"integer":return Number.isInteger(value);case"number":return typeof value=="number"&&Number.isFinite(value);default:return typeof value===type}}var list=values=>values.map(v=>String(v)).join(", ");function validate(schema,value,path=[]){let fail=message=>[{path,message}];if(schema.const!==void 0&&value!==schema.const)return fail(`must be ${String(schema.const)}`);if(schema.enum&&!schema.enum.includes(value))return fail(`must be one of ${list(schema.enum)}`);let types=schema.type===void 0?[]:[].concat(schema.type);if(types.length&&!types.some(t=>typeMatches(t,value)))return fail(`must be ${types.map(t=>EXPECTED[t]).join(" or ")}, not ${kindOf(value)}`);if(schema.anyOf){let branches=schema.anyOf.map(branch=>validate(branch,value,path));if(branches.every(errors2=>errors2.length>0))return branches.reduce((a,b)=>b.length<a.length?b:a)}let errors=[];if(typeof value=="string"){if(schema.minLength!==void 0&&value.length<schema.minLength)return fail(`must be at least ${schema.minLength} characters`);if(schema.maxLength!==void 0&&value.length>schema.maxLength)return fail(`must be at most ${schema.maxLength} characters`)}if(typeof value=="number"){let range=schema.exclusiveMinimum!==void 0?`more than ${schema.exclusiveMinimum}`:schema.minimum!==void 0?`at least ${schema.minimum}`:void 0;if(schema.exclusiveMinimum!==void 0&&value<=schema.exclusiveMinimum||schema.minimum!==void 0&&value<schema.minimum||schema.maximum!==void 0&&value>schema.maximum)return fail(`must be ${[range,schema.maximum!==void 0?`at most ${schema.maximum}`:void 0].filter(Boolean).join(" and ")}`)}if(typeof value=="string"&&schema.pattern&&!new RegExp(schema.pattern).test(value)&&errors.push({path,message:"is not in the expected format"}),Array.isArray(value)&&schema.items&&value.forEach((item,i)=>{errors.push(...validate(schema.items,item,[...path,i]))}),types.includes("object")&&typeMatches("object",value)){let record=value;for(let name of schema.required??[])name in record||errors.push({path:[...path,name],message:"is required"});for(let[name,child]of Object.entries(record)){let sub=schema.properties?.[name];if(sub)errors.push(...validate(sub,child,[...path,name]));else if(schema.additionalProperties===!1){let known=Object.keys(schema.properties??{});errors.push({path:[...path,name],message:`is not a setting${known.length?` here (known: ${known.join(", ")})`:""}`})}}}return errors}var import_yaml=__toESM(require_dist(),1),import_semver=__toESM(require_semver(),1),import_yaml2=__toESM(require_dist(),1);function cronProblem(expression){let fields=expression.trim().split(/\s+/);if(fields.length!==5)return`has ${fields.length} field${fields.length===1?"":"s"}; a cron expression has five (minute hour day month weekday)`;for(let[i,field]of fields.entries()){let spec=FIELDS[i];for(let part of field.split(",")){let problem=partProblem(part,spec);if(problem)return`${spec.name} field "${part}": ${problem}`}}}var FIELDS=[{name:"minute",min:0,max:59},{name:"hour",min:0,max:23},{name:"day",min:1,max:31},{name:"month",min:1,max:12,names:["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]},{name:"weekday",min:0,max:6,names:["sun","mon","tue","wed","thu","fri","sat"]}];function partProblem(part,spec){let[range,step,...rest]=part.split("/");if(rest.length||range===void 0||range==="")return"not a cron value";if(step!==void 0&&!(/^\d+$/.test(step)&&Number(step)>0))return"a step must be a positive number";if(range==="*")return;let bounds=range.split("-");if(bounds.length>2)return"not a cron value";let values=bounds.map(b=>fieldValue(b,spec));if(values.some(v=>v===void 0))return`expected ${spec.min} to ${spec.max}${spec.names?" or a name":""}`;let[low,high]=values;if(high!==void 0&&low>high)return"a range must go from low to high"}function fieldValue(text,spec){let named=spec.names?.indexOf(text.toLowerCase())??-1;if(named>=0)return spec.min+named;if(!/^\d+$/.test(text))return;let n=Number(text),max=spec.name==="weekday"?7:spec.max;return n>=spec.min&&n<=max?n:void 0}function hash(text){let h=2166136261;for(let i=0;i<text.length;i++)h^=text.charCodeAt(i),h=Math.imul(h,16777619)>>>0;return h}function weeklySchedule(seed){let h=hash(seed.toLowerCase()),minutes=Array.from({length:60},(_,m)=>m).filter(m=>m!==0&&m!==30),minute=minutes[h%minutes.length],hour=3+Math.floor(h/minutes.length)%6;return`${minute} ${hour} * * 1`}var PROVIDERS=["anthropic","openai","gemini"],keyLike=s=>/(?:sk-[\w-]{8,}|AIza[\w-]{12,}|phc_[\w-]+|bearer\s|api[_-]?key|secret|password|access[_-]?token)/i.test(s),UPTIDE_CONFIG_FILE="uptide.yml",UPTIDE_CONFIG_SCHEMA_URL="https://raw.githubusercontent.com/uptide-dev/uptide/main/schema/uptide.v1.json",EXECUTORS=["auto","patch","sdk"],MANUAL_SCHEDULE="manual",DEFAULT_SCHEDULE="17 6 * * 1",DEFAULT_CONFIG=Object.freeze({version:1,packages:{allow:[],deny:[]},schedule:DEFAULT_SCHEDULE,maxOpenPrs:3,llm:{enabled:!0,provider:"anthropic"},maxCost:1,executor:"auto"}),GLOB="^(?:@[a-z0-9*?~][a-z0-9._~*?-]*/)?[a-z0-9*?~][a-z0-9._~*?-]*$",UPTIDE_CONFIG_SCHEMA={$schema:"https://json-schema.org/draft/2020-12/schema",$id:UPTIDE_CONFIG_SCHEMA_URL,title:"uptide.yml",description:"Settings for Uptide: which dependencies it upgrades, when, and how it uses the LLM.",type:"object",additionalProperties:!1,required:["version"],properties:{version:{description:"Format of this file. Only 1 exists.",const:1},packages:{description:"Which dependencies Uptide considers, as npm package name globs. Deny wins over allow.",type:"object",additionalProperties:!1,properties:{allow:{description:"Only these dependencies. Empty: every dependency.",type:"array",items:{type:"string",pattern:GLOB},default:[]},deny:{description:"Never these dependencies, even when allow matches them.",type:"array",items:{type:"string",pattern:GLOB},default:[]}}},schedule:{description:"When the workflow runs: a five-field cron expression in UTC, or `manual` to run only when started from the Actions tab or the Uptide dashboard. The workflow file holds a copy; run `uptide init` again after changing it.",type:"string",default:DEFAULT_SCHEDULE},maxOpenPrs:{description:"No new pull request is opened while this many Uptide pull requests are open.",type:"integer",minimum:1,maximum:50,default:DEFAULT_CONFIG.maxOpenPrs},llm:{description:"Assisted fixes for the sites no rule covers.",type:"object",additionalProperties:!1,properties:{enabled:{description:"No longer offered; accepted so older files still load. The Uptide Action requires the Anthropic key and rejects false. The local CLI still reads it.",type:"boolean",default:!0},provider:{description:"Which provider receives the code, with this repository's own API key.",enum:[...PROVIDERS],default:"anthropic"}}},maxCost:{description:"Most a run may spend on LLM calls, in USD.",type:"number",exclusiveMinimum:0,maximum:100,default:DEFAULT_CONFIG.maxCost},executor:{description:"How assisted fixes are applied: auto (SDK for Anthropic when installed, patch otherwise), patch (one edit per site), sdk (a Claude Agent SDK session).",enum:[...EXECUTORS],default:"auto"},minReleaseAgeDays:{description:"Days a release must be public before Uptide targets it, for a repository that sets no release-age policy of its own (npm min-release-age, pnpm minimumReleaseAge, Yarn npmMinimalAgeGate); a policy the repository sets wins. Unset: no minimum.",type:"number",minimum:0,maximum:365}}},MAX_CONFIG_BYTES=64*1024,pathText=path=>path.reduce((text,part)=>typeof part=="number"?`${text}[${part}]`:text?`${text}.${part}`:part,"")||"the file";function withDefaults(given){return{version:1,packages:{allow:given.packages?.allow??[...DEFAULT_CONFIG.packages.allow],deny:given.packages?.deny??[...DEFAULT_CONFIG.packages.deny]},schedule:given.schedule??DEFAULT_CONFIG.schedule,maxOpenPrs:given.maxOpenPrs??DEFAULT_CONFIG.maxOpenPrs,llm:{enabled:given.llm?.enabled??DEFAULT_CONFIG.llm.enabled,provider:given.llm?.provider??DEFAULT_CONFIG.llm.provider},maxCost:given.maxCost??DEFAULT_CONFIG.maxCost,executor:given.executor??DEFAULT_CONFIG.executor,...given.minReleaseAgeDays!==void 0?{minReleaseAgeDays:given.minReleaseAgeDays}:{}}}function parseUptideConfig(text,file=UPTIDE_CONFIG_FILE){let invalid=lines=>new UptideError("INVALID_CONFIG",`${file} is not valid:
6
+ import{BUILD_TOOLS,UNUSED_REASON,activePack,activePacks,assertInstalledVersions,assertManagerAvailable,assertNoDowngrades,assertNoPinningOverrides,authHeaders,baselineHealth,bumpVersions,changeRule,check,companionProblems,createFsSurfaceCache,createNpmFetcher,createTypescriptAdapter,defaultCacheDir,dependencyGroups,describeServices,detectIn,diagnosticKey,diagnostics,editDiff,failedTestFiles,genericPack,gitLineMoves,globRegExp,groupPeerBlockers,heldBackText,importedByText,install,installedManifest,isFailure,knownTool,lineMoves,loadRegistryConfig,mapWithLimit,markPreexisting,minVersion,moved,newDiagnostics,newestAllowed,packageManager,packumentUrl,parentOf,payloadApiVersions,peerBlocks,peerPreflight,pinClient,prBody,progress,publicRepos,rangePreflight,readManifest3,recordedStatus,registryFor,releaseAgeError,require_dist,require_semver,resetSharedState,resolveTarget,ruleFor,satisfies,scanImports,sdkApiVersion,selectedFindings,siteKey,statusOf,stripePack,stripeUsageContext,summarize,testWorkspaces,tierOf,toolingReasons,truthDigest,typeChecker,typeResolutionFailure,typescriptAdapter,unpinnedClients,unplacedKey,validRange,validateVersionRanges,windowStart,workspacePackagesOf,writePeerOverrides}from"./chunk-C7TDGEDT.js";import{require_ts_morph}from"./chunk-GHXBU767.js";import{assertVerifiedTree,publicationBlockers,publish}from"./chunk-OLHRO6TT.js";import{IntegrityError,NO_PACK_YET,NoTypesError,PackageNotFoundError,UPTIDE_COMMAND,UptideError,VersionNotFoundError,command,compareVersions,errorCode,git,isMajorUpgrade,majorsBehind,parseVersion,projectRoot,uptideCommand,uptideVersionInfo}from"./chunk-BLXXTILX.js";import{__commonJS,__toESM}from"./chunk-Z5YHBDVG.js";var require_picocolors=__commonJS({"../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js"(exports,module){"use strict";var p=process||{},argv=p.argv||[],env=p.env||{},isColorSupported=!(env.NO_COLOR||argv.includes("--no-color"))&&(!!env.FORCE_COLOR||argv.includes("--color")||p.platform==="win32"||(p.stdout||{}).isTTY&&env.TERM!=="dumb"||!!env.CI),formatter=(open,close,replace=open)=>input=>{let string=""+input,index=string.indexOf(close,open.length);return~index?open+replaceClose(string,close,replace,index)+close:open+string+close},replaceClose=(string,close,replace,index)=>{let result="",cursor=0;do result+=string.substring(cursor,index)+replace,cursor=index+close.length,index=string.indexOf(close,cursor);while(~index);return result+string.substring(cursor)},createColors=(enabled=isColorSupported)=>{let f=enabled?formatter:()=>String;return{isColorSupported:enabled,reset:f("\x1B[0m","\x1B[0m"),bold:f("\x1B[1m","\x1B[22m","\x1B[22m\x1B[1m"),dim:f("\x1B[2m","\x1B[22m","\x1B[22m\x1B[2m"),italic:f("\x1B[3m","\x1B[23m"),underline:f("\x1B[4m","\x1B[24m"),inverse:f("\x1B[7m","\x1B[27m"),hidden:f("\x1B[8m","\x1B[28m"),strikethrough:f("\x1B[9m","\x1B[29m"),black:f("\x1B[30m","\x1B[39m"),red:f("\x1B[31m","\x1B[39m"),green:f("\x1B[32m","\x1B[39m"),yellow:f("\x1B[33m","\x1B[39m"),blue:f("\x1B[34m","\x1B[39m"),magenta:f("\x1B[35m","\x1B[39m"),cyan:f("\x1B[36m","\x1B[39m"),white:f("\x1B[37m","\x1B[39m"),gray:f("\x1B[90m","\x1B[39m"),bgBlack:f("\x1B[40m","\x1B[49m"),bgRed:f("\x1B[41m","\x1B[49m"),bgGreen:f("\x1B[42m","\x1B[49m"),bgYellow:f("\x1B[43m","\x1B[49m"),bgBlue:f("\x1B[44m","\x1B[49m"),bgMagenta:f("\x1B[45m","\x1B[49m"),bgCyan:f("\x1B[46m","\x1B[49m"),bgWhite:f("\x1B[47m","\x1B[49m"),blackBright:f("\x1B[90m","\x1B[39m"),redBright:f("\x1B[91m","\x1B[39m"),greenBright:f("\x1B[92m","\x1B[39m"),yellowBright:f("\x1B[93m","\x1B[39m"),blueBright:f("\x1B[94m","\x1B[39m"),magentaBright:f("\x1B[95m","\x1B[39m"),cyanBright:f("\x1B[96m","\x1B[39m"),whiteBright:f("\x1B[97m","\x1B[39m"),bgBlackBright:f("\x1B[100m","\x1B[49m"),bgRedBright:f("\x1B[101m","\x1B[49m"),bgGreenBright:f("\x1B[102m","\x1B[49m"),bgYellowBright:f("\x1B[103m","\x1B[49m"),bgBlueBright:f("\x1B[104m","\x1B[49m"),bgMagentaBright:f("\x1B[105m","\x1B[49m"),bgCyanBright:f("\x1B[106m","\x1B[49m"),bgWhiteBright:f("\x1B[107m","\x1B[49m")}};module.exports=createColors();module.exports.createColors=createColors}});var CREDENTIAL=/(?:sk-[\w-]{8,}|AIza[\w-]{12,}|phc_[\w-]{8,}|gh[pousr]_[A-Za-z0-9]{20,}|github_pat_\w{20,}|xox[abpr]-[\w-]{8,}|bearer\s|eyJ[\w-]{8,}\.[\w-]{8,}\.[\w-]{8,})/i,kindOf=value=>value===null?"null":Array.isArray(value)?"a list":typeof value=="object"?"a mapping":typeof value=="string"?"text":typeof value=="boolean"?"true or false":typeof value=="number"?"a number":typeof value,EXPECTED={object:"a mapping",array:"a list",string:"text",integer:"a whole number",number:"a number",boolean:"true or false",null:"null"};function typeMatches(type,value){switch(type){case"null":return value===null;case"object":return!!value&&typeof value=="object"&&!Array.isArray(value);case"array":return Array.isArray(value);case"integer":return Number.isInteger(value);case"number":return typeof value=="number"&&Number.isFinite(value);default:return typeof value===type}}var list=values=>values.map(v=>String(v)).join(", ");function validate(schema,value,path=[]){let fail=message=>[{path,message}];if(schema.const!==void 0&&value!==schema.const)return fail(`must be ${String(schema.const)}`);if(schema.enum&&!schema.enum.includes(value))return fail(`must be one of ${list(schema.enum)}`);let types=schema.type===void 0?[]:[].concat(schema.type);if(types.length&&!types.some(t=>typeMatches(t,value)))return fail(`must be ${types.map(t=>EXPECTED[t]).join(" or ")}, not ${kindOf(value)}`);if(schema.anyOf){let branches=schema.anyOf.map(branch=>validate(branch,value,path));if(branches.every(errors2=>errors2.length>0))return branches.reduce((a,b)=>b.length<a.length?b:a)}let errors=[];if(typeof value=="string"){if(schema.minLength!==void 0&&value.length<schema.minLength)return fail(`must be at least ${schema.minLength} characters`);if(schema.maxLength!==void 0&&value.length>schema.maxLength)return fail(`must be at most ${schema.maxLength} characters`)}if(typeof value=="number"){let range=schema.exclusiveMinimum!==void 0?`more than ${schema.exclusiveMinimum}`:schema.minimum!==void 0?`at least ${schema.minimum}`:void 0;if(schema.exclusiveMinimum!==void 0&&value<=schema.exclusiveMinimum||schema.minimum!==void 0&&value<schema.minimum||schema.maximum!==void 0&&value>schema.maximum)return fail(`must be ${[range,schema.maximum!==void 0?`at most ${schema.maximum}`:void 0].filter(Boolean).join(" and ")}`)}if(typeof value=="string"&&schema.pattern&&!new RegExp(schema.pattern).test(value)&&errors.push({path,message:"is not in the expected format"}),Array.isArray(value)&&schema.items&&value.forEach((item,i)=>{errors.push(...validate(schema.items,item,[...path,i]))}),types.includes("object")&&typeMatches("object",value)){let record=value;for(let name of schema.required??[])name in record||errors.push({path:[...path,name],message:"is required"});for(let[name,child]of Object.entries(record)){let sub=schema.properties?.[name];if(sub)errors.push(...validate(sub,child,[...path,name]));else if(schema.additionalProperties===!1){let known=Object.keys(schema.properties??{});errors.push({path:[...path,name],message:`is not a setting${known.length?` here (known: ${known.join(", ")})`:""}`})}}}return errors}var import_yaml=__toESM(require_dist(),1),import_semver=__toESM(require_semver(),1),import_yaml2=__toESM(require_dist(),1);function cronProblem(expression){let fields=expression.trim().split(/\s+/);if(fields.length!==5)return`has ${fields.length} field${fields.length===1?"":"s"}; a cron expression has five (minute hour day month weekday)`;for(let[i,field]of fields.entries()){let spec=FIELDS[i];for(let part of field.split(",")){let problem=partProblem(part,spec);if(problem)return`${spec.name} field "${part}": ${problem}`}}}var FIELDS=[{name:"minute",min:0,max:59},{name:"hour",min:0,max:23},{name:"day",min:1,max:31},{name:"month",min:1,max:12,names:["jan","feb","mar","apr","may","jun","jul","aug","sep","oct","nov","dec"]},{name:"weekday",min:0,max:6,names:["sun","mon","tue","wed","thu","fri","sat"]}];function partProblem(part,spec){let[range,step,...rest]=part.split("/");if(rest.length||range===void 0||range==="")return"not a cron value";if(step!==void 0&&!(/^\d+$/.test(step)&&Number(step)>0))return"a step must be a positive number";if(range==="*")return;let bounds=range.split("-");if(bounds.length>2)return"not a cron value";let values=bounds.map(b=>fieldValue(b,spec));if(values.some(v=>v===void 0))return`expected ${spec.min} to ${spec.max}${spec.names?" or a name":""}`;let[low,high]=values;if(high!==void 0&&low>high)return"a range must go from low to high"}function fieldValue(text,spec){let named=spec.names?.indexOf(text.toLowerCase())??-1;if(named>=0)return spec.min+named;if(!/^\d+$/.test(text))return;let n=Number(text),max=spec.name==="weekday"?7:spec.max;return n>=spec.min&&n<=max?n:void 0}function hash(text){let h=2166136261;for(let i=0;i<text.length;i++)h^=text.charCodeAt(i),h=Math.imul(h,16777619)>>>0;return h}function weeklySchedule(seed){let h=hash(seed.toLowerCase()),minutes=Array.from({length:60},(_,m)=>m).filter(m=>m!==0&&m!==30),minute=minutes[h%minutes.length],hour=3+Math.floor(h/minutes.length)%6;return`${minute} ${hour} * * 1`}var PROVIDERS=["anthropic","openai","gemini"],keyLike=s=>/(?:sk-[\w-]{8,}|AIza[\w-]{12,}|phc_[\w-]+|bearer\s|api[_-]?key|secret|password|access[_-]?token)/i.test(s),UPTIDE_CONFIG_FILE="uptide.yml",UPTIDE_CONFIG_SCHEMA_URL="https://raw.githubusercontent.com/uptide-dev/uptide/main/schema/uptide.v1.json",EXECUTORS=["auto","patch","sdk"],MANUAL_SCHEDULE="manual",DEFAULT_SCHEDULE="17 6 * * 1",DEFAULT_CONFIG=Object.freeze({version:1,packages:{allow:[],deny:[]},schedule:DEFAULT_SCHEDULE,maxOpenPrs:3,llm:{enabled:!0,provider:"anthropic"},maxCost:1,executor:"auto"}),GLOB="^(?:@[a-z0-9*?~][a-z0-9._~*?-]*/)?[a-z0-9*?~][a-z0-9._~*?-]*$",UPTIDE_CONFIG_SCHEMA={$schema:"https://json-schema.org/draft/2020-12/schema",$id:UPTIDE_CONFIG_SCHEMA_URL,title:"uptide.yml",description:"Settings for Uptide: which dependencies it upgrades, when, and how it uses the LLM.",type:"object",additionalProperties:!1,required:["version"],properties:{version:{description:"Format of this file. Only 1 exists.",const:1},packages:{description:"Which dependencies Uptide considers, as npm package name globs. Deny wins over allow.",type:"object",additionalProperties:!1,properties:{allow:{description:"Only these dependencies. Empty: every dependency.",type:"array",items:{type:"string",pattern:GLOB},default:[]},deny:{description:"Never these dependencies, even when allow matches them.",type:"array",items:{type:"string",pattern:GLOB},default:[]}}},schedule:{description:"When the workflow runs: a five-field cron expression in UTC, or `manual` to run only when started from the Actions tab or the Uptide dashboard. The workflow file holds a copy; run `uptide init` again after changing it.",type:"string",default:DEFAULT_SCHEDULE},maxOpenPrs:{description:"No new pull request is opened while this many Uptide pull requests are open.",type:"integer",minimum:1,maximum:50,default:DEFAULT_CONFIG.maxOpenPrs},llm:{description:"Assisted fixes for the sites no rule covers.",type:"object",additionalProperties:!1,properties:{enabled:{description:"No longer offered; accepted so older files still load. The Uptide Action requires the Anthropic key and rejects false. The local CLI still reads it.",type:"boolean",default:!0},provider:{description:"Which provider receives the code, with this repository's own API key.",enum:[...PROVIDERS],default:"anthropic"}}},maxCost:{description:"Most a run may spend on LLM calls, in USD.",type:"number",exclusiveMinimum:0,maximum:100,default:DEFAULT_CONFIG.maxCost},executor:{description:"How assisted fixes are applied: auto (SDK for Anthropic when installed, patch otherwise), patch (one edit per site), sdk (a Claude Agent SDK session).",enum:[...EXECUTORS],default:"auto"},minReleaseAgeDays:{description:"Days a release must be public before Uptide targets it, for a repository that sets no release-age policy of its own (npm min-release-age, pnpm minimumReleaseAge, Yarn npmMinimalAgeGate); a policy the repository sets wins. Unset: no minimum.",type:"number",minimum:0,maximum:365}}},MAX_CONFIG_BYTES=64*1024,pathText=path=>path.reduce((text,part)=>typeof part=="number"?`${text}[${part}]`:text?`${text}.${part}`:part,"")||"the file";function withDefaults(given){return{version:1,packages:{allow:given.packages?.allow??[...DEFAULT_CONFIG.packages.allow],deny:given.packages?.deny??[...DEFAULT_CONFIG.packages.deny]},schedule:given.schedule??DEFAULT_CONFIG.schedule,maxOpenPrs:given.maxOpenPrs??DEFAULT_CONFIG.maxOpenPrs,llm:{enabled:given.llm?.enabled??DEFAULT_CONFIG.llm.enabled,provider:given.llm?.provider??DEFAULT_CONFIG.llm.provider},maxCost:given.maxCost??DEFAULT_CONFIG.maxCost,executor:given.executor??DEFAULT_CONFIG.executor,...given.minReleaseAgeDays!==void 0?{minReleaseAgeDays:given.minReleaseAgeDays}:{}}}function parseUptideConfig(text,file=UPTIDE_CONFIG_FILE){let invalid=lines=>new UptideError("INVALID_CONFIG",`${file} is not valid:
7
7
  ${lines.map(l=>` ${l}`).join(`
8
8
  `)}
9
9
  Reference: docs/config.md`);if(new TextEncoder().encode(text).length>MAX_CONFIG_BYTES)throw invalid([`the file is larger than ${MAX_CONFIG_BYTES/1024} KB`]);let lineCounter=new import_yaml.LineCounter,doc=(0,import_yaml.parseDocument)(text,{lineCounter,uniqueKeys:!0,prettyErrors:!1}),lineOf=offset=>offset===void 0?"":`line ${lineCounter.linePos(offset).line}: `;if(doc.errors.length)throw invalid(doc.errors.map(e=>`${lineOf(e.pos[0])}${e.message.split(`
@@ -147,7 +147,7 @@ Delete \`${WORKFLOW_PATH}\` and \`${UPTIDE_CONFIG_FILE}\`, and the \`ANTHROPIC_A
147
147
 
148
148
  ${detected.join(`
149
149
  `)}
150
- `}var TRUST_URL="https://uptide-app.vercel.app/trust";function llmLine(config){let budget=`Spending stops at $${config.maxCost} per run (\`maxCost\` in uptide.yml).`;return config.executor==="patch"?`**Code sent to Anthropic, with this repository's own API key**, for call sites no rule can fix: the code around each one. ${budget}`:`**Code sent to Anthropic, with this repository's own API key**, for call sites no rule can fix. The assisted fixer (the Claude Agent SDK) can read any file in the checkout to work out a fix, and what it reads goes to Anthropic with its requests. ${budget}`}var ONBOARDING_PR_TITLE="Add Uptide";function generateOnboarding(facts,options={}){let seed=facts.slug??facts.name??"uptide",config=withDefaults({...options.config,version:1,schedule:options.config?.schedule??weeklySchedule(seed)});if(!config.llm.enabled)throw new OnboardingRefusal("llm-disabled","uptide.yml sets llm.enabled to false, which Uptide no longer supports: the Uptide workflow always uses an Anthropic key (ANTHROPIC_API_KEY). Remove llm.enabled from uptide.yml.");if(config.llm.provider!=="anthropic")throw new OnboardingRefusal("unsupported-provider",`uptide.yml sets llm.provider to ${config.llm.provider}; the Uptide workflow passes only an Anthropic key (ANTHROPIC_API_KEY). Use provider anthropic.`);return{files:[{path:WORKFLOW_PATH,content:renderWorkflow(facts,config)},{path:UPTIDE_CONFIG_FILE,content:renderConfig(config)}],prTitle:ONBOARDING_PR_TITLE,prBody:renderPrBody(facts,config),config}}import{existsSync as existsSync2,readFileSync,statSync as statSync2}from"fs";import{dirname,join as join2,resolve}from"path";import{closeSync,existsSync,openSync,readSync,statSync}from"fs";import{join}from"path";var HEAD_BYTES=64*1024;function head(path){let fd=openSync(path,"r");try{let buffer=Buffer.alloc(Math.min(HEAD_BYTES,statSync(path).size)),read=readSync(fd,buffer,0,buffer.length,0);return buffer.subarray(0,read).toString("utf8")}catch{return""}finally{closeSync(fd)}}function snapshotFromDisk(root,repo){let files2={};for(let path of ONBOARDING_INPUTS){let file=join(root,path);existsSync(file)&&statSync(file).isFile()&&(files2[path]=path==="bun.lockb"?"":head(file))}return{...repo.slug?{slug:repo.slug}:{},defaultBranch:repo.defaultBranch,files:files2}}function loadUptideConfig(cwd,explicit){let read=path=>{if(statSync2(path).size>MAX_CONFIG_BYTES)throw new UptideError("INVALID_CONFIG",`${path} is larger than ${MAX_CONFIG_BYTES/1024} KB`);return{path,...parseUptideConfig(readFileSync(path,"utf8"),path)}};if(explicit){let path=resolve(cwd,explicit);if(!existsSync2(path))throw new UptideError("INVALID_CONFIG",`--config ${explicit}: no such file (${path})`);return read(path)}let dir=resolve(cwd);for(;;){let path=join2(dir,UPTIDE_CONFIG_FILE);if(existsSync2(path))return read(path);let parent=dirname(dir);if(parent===dir||existsSync2(join2(dir,".git")))return;dir=parent}}var import_ts_morph=__toESM(require_ts_morph(),1),import_ts_morph2=__toESM(require_ts_morph(),1),import_ts_morph3=__toESM(require_ts_morph(),1),import_ts_morph4=__toESM(require_ts_morph(),1),import_yaml3=__toESM(require_dist(),1);import{createHash as createHash2}from"crypto";import{constants,cpSync,existsSync as existsSync7,mkdirSync as mkdirSync4,mkdtempSync as mkdtempSync2,readdirSync,readFileSync as readFileSync13,realpathSync as realpathSync9,rmSync as rmSync3,statSync as statSync22,writeFileSync as writeFileSync7}from"fs";import{tmpdir as tmpdir2}from"os";import{basename,dirname as dirname10,isAbsolute as isAbsolute3,join as join13,resolve as resolve8}from"path";import{isMainThread}from"worker_threads";import{existsSync as existsSync3,readFileSync as readFileSync2,statSync as statSync3}from"fs";import{dirname as dirname2,join as join3,resolve as resolve2}from"path";import{readFileSync as readFileSync22}from"fs";import{dirname as dirname22,join as join22}from"path";import{mkdirSync as mkdirSync3,readFileSync as readFileSync12,realpathSync as realpathSync8,writeFileSync as writeFileSync6}from"fs";import{dirname as dirname9,join as join12,resolve as resolve7}from"path";import{existsSync as existsSync22,readFileSync as readFileSync3,realpathSync}from"fs";import{dirname as dirname3,join as join32}from"path";import{mkdirSync as mkdirSync2,readFileSync as readFileSync11,realpathSync as realpathSync7,writeFileSync as writeFileSync5}from"fs";import{dirname as dirname8,join as join11,relative as relative5,resolve as resolve6}from"path";import{readFileSync as readFileSync6,realpathSync as realpathSync3,writeFileSync as writeFileSync2}from"fs";import{join as join5}from"path";import{setTimeout as delay}from"timers/promises";import{readFileSync as readFileSync5,realpathSync as realpathSync2,writeFileSync}from"fs";import{isAbsolute,relative,resolve as resolve22}from"path";import{existsSync as existsSync32,readFileSync as readFileSync4}from"fs";import{join as join4}from"path";import{execFileSync}from"child_process";import{mkdtempSync,rmSync,writeFileSync as writeFileSync3}from"fs";import{tmpdir}from"os";import{join as join7}from"path";import{existsSync as existsSync4,readFileSync as readFileSync7,realpathSync as realpathSync4}from"fs";import{dirname as dirname4,join as join6,relative as relative2,resolve as resolve3}from"path";import{existsSync as existsSync5,readFileSync as readFileSync8}from"fs";import{dirname as dirname5,join as join8,relative as relative3}from"path";import{mkdirSync,readFileSync as readFileSync9,realpathSync as realpathSync5,writeFileSync as writeFileSync4}from"fs";import{dirname as dirname6,join as join9,resolve as resolve4}from"path";import{createHash}from"crypto";import{existsSync as existsSync6,lstatSync,readFileSync as readFileSync10,realpathSync as realpathSync6,rmSync as rmSync2}from"fs";import{dirname as dirname7,isAbsolute as isAbsolute2,join as join10,relative as relative4,resolve as resolve5}from"path";import{readFileSync as readFileSync14,writeFileSync as writeFileSync8}from"fs";import{dirname as dirname11,join as join14}from"path";import{existsSync as existsSync8,mkdtempSync as mkdtempSync3,readFileSync as readFileSync15,rmSync as rmSync4,writeFileSync as writeFileSync9}from"fs";import{tmpdir as tmpdir3}from"os";import{isAbsolute as isAbsolute4,join as join15,resolve as resolve9}from"path";import{readFileSync as readFileSync17}from"fs";import{join as join17,resolve as resolve11}from"path";import{existsSync as existsSync9,readFileSync as readFileSync16}from"fs";import{dirname as dirname12,join as join16,resolve as resolve10}from"path";import{existsSync as existsSync10,readdirSync as readdirSync2,readFileSync as readFileSync18,writeFileSync as writeFileSync10}from"fs";import{join as join18}from"path";import{existsSync as existsSync12,readFileSync as readFileSync20,writeFileSync as writeFileSync12}from"fs";import{join as join20}from"path";import{existsSync as existsSync11,mkdirSync as mkdirSync5,readFileSync as readFileSync19,renameSync,rmSync as rmSync5,writeFileSync as writeFileSync11}from"fs";import{join as join19}from"path";import{spawnSync}from"child_process";import{existsSync as existsSync13,mkdirSync as mkdirSync6,readFileSync as readFileSync21,writeFileSync as writeFileSync13}from"fs";import{join as join21,relative as relative6}from"path";import{readFileSync as readFileSync222}from"fs";import{dirname as dirname13,join as join222,resolve as resolve12}from"path";function foldForDisplay(changes){let removedPaths=new Set(changes.filter(c=>c.kind==="removed"||c.kind==="moved").map(c=>c.path)),keys=new Set(changes.map(c=>`${c.kind}:${c.path}`)),implied=c=>{let p=parentOf(c.path);for(;p!==void 0;){if(removedPaths.has(p))return!0;p=parentOf(p)}return!1},folded={shown:[],hidden:{impliedByRemoval:[],aliasDuplicates:[],nonPublic:[]}};for(let c of changes)c.visibility!==void 0?folded.hidden.nonPublic.push(c):implied(c)?folded.hidden.impliedByRemoval.push(c):c.aliasOf!==void 0&&keys.has(`${c.kind}:${c.aliasOf}`)?folded.hidden.aliasDuplicates.push(c):folded.shown.push(c);return folded}function depthOf(path){return(path.match(/[.#]|\[\]/g)??[]).length}var PRICE_DATE="2026-10-04";var PRICES={anthropic:{"claude-sonnet-4-6":{input:3,output:15,cached:.3,write:3.75,writeHour:6},"claude-sonnet-5-5":{input:2,output:10,cached:.2,write:2.5,writeHour:4,source:"https://platform.claude.com/docs/en/models/sonnet-5-5/overview",checkedAt:PRICE_DATE},"claude-opus-5-5":{input:4,output:20,cached:.2,write:5,writeHour:8},"claude-fable-5-1":{input:10,output:50,cached:.25,write:12.5,writeHour:20},"claude-haiku-4-5-20251001":{input:1,output:5,cached:.1,write:1.25,writeHour:2}},openai:{"gpt-6.1-sol":{input:2,output:10,cached:.1,write:2.5,long:{threshold:272e3,input:4,output:15,cached:.2,write:5}},"gpt-6-astra":{input:10,output:50,cached:1,write:12.5,long:{threshold:272e3,input:20,output:75,cached:2,write:25}},"gpt-6-luna":{input:.1,output:.5,cached:.01,write:.125,long:{threshold:272e3,input:.2,output:.75,cached:.02,write:.25}},"gpt-5-pro":{input:15,output:120,cached:15}},gemini:{"gemini-3.1-pro-preview":{input:2,output:12,cached:.2,long:{threshold:2e5,input:4,output:18,cached:.4}},"gemini-3.8-flash":{input:.75,output:3.75,cached:.075,after:{date:"2027-01-01",input:1.5,output:7.5,cached:.15}},"gemini-3.7-flash":{input:.75,output:3.75,cached:.075,after:{date:"2027-01-01",input:1.5,output:7.5,cached:.15}}}},DEFAULT_MODELS={anthropic:"claude-sonnet-5-5",openai:"gpt-6.1-sol",gemini:"gemini-3.8-flash"};function dated(price,now){return price.after&&now>=price.after.date?{...price,...price.after}:price}function priceFor(provider,model,tokens=0,now=new Date().toISOString().slice(0,10)){let known=Object.hasOwn(PRICES[provider],model)?PRICES[provider][model]:void 0;if(known){let price=dated(known,now);return{...price,...price.long&&tokens>price.long.threshold?price.long:{},fallback:!1}}let rows=Object.values(PRICES[provider]).flatMap(p=>{let row=dated(p,now);return[row,...row.long?[row.long]:[]]}),input=Math.max(...rows.map(p=>p.input));return{input,output:Math.max(...rows.map(p=>p.output)),cached:input,write:Math.max(...rows.map(p=>p.write??p.input)),writeHour:Math.max(...Object.values(PRICES[provider]).map(p=>p.writeHour??p.input)),fallback:!0}}var nanos=usd=>Math.ceil(usd*1e9);function usageCost(provider,model,u){let p=priceFor(provider,model,u.inputTokens+u.cacheWriteTokens+u.cacheWriteHourTokens);return Math.ceil(((u.inputTokens-u.cachedInputTokens)*p.input+u.cachedInputTokens*p.cached+u.cacheWriteTokens*(p.write??p.input)+u.cacheWriteHourTokens*(p.writeHour??p.input)+u.outputTokens*p.output)*1e3)/1e9}function reserveCost(provider,model,input,output){let p=priceFor(provider,model,input),inputPrice=Math.max(p.input,p.write??0,p.writeHour??0);return Math.ceil((input*inputPrice+output*p.output)*1e3)/1e9}var KEY_ENV={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY",gemini:"GEMINI_API_KEY"},ACCEPTED_KEYS=Object.values(KEY_ENV).join(", ");function validModel(value){return typeof value=="string"&&value.length<=128&&!keyLike(value)&&/^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(value)&&!value.split("/").some(p=>p==="."||p===".."||!p)}function readConfig(cwd){let dir=resolve2(cwd);for(;;){let path=join3(dir,"uptide.config.json");if(existsSync3(path)){let value,keyFound=!1;try{if(statSync3(path).size>4096)throw new Error;let raw=readFileSync2(path,"utf8");keyFound=[...raw.matchAll(/"(?:\\.|[^"\\])*"/g)].some(match2=>keyLike(JSON.parse(match2[0]))),value=JSON.parse(raw)}catch{throw new Error("uptide.config.json must be a small valid JSON object containing only provider, model and advisories. Keys belong only in environment variables.")}if(keyFound)throw new Error("uptide.config.json contains a key-like setting. Remove credentials from the file; use environment variables only.");if(!value||typeof value!="object"||Array.isArray(value))throw new Error("uptide.config.json must contain only provider, model and advisories; API keys belong only in environment variables.");for(let[name,setting]of Object.entries(value)){if(keyLike(name)||typeof setting=="string"&&keyLike(setting))throw new Error("uptide.config.json contains a key-like setting. Remove credentials from the file; use environment variables only.");if(!(["provider","model"].includes(name)&&typeof setting=="string"||name==="advisories"&&typeof setting=="boolean"))throw new Error("uptide.config.json accepts only provider and model strings, and advisories as true or false. Keys belong only in environment variables.")}let config=value;if(config.provider!==void 0&&!PROVIDERS.includes(config.provider))throw new Error("Invalid provider in uptide.config.json: choose anthropic, openai or gemini.");if(config.model!==void 0&&!validModel(config.model))throw new Error("Invalid model in uptide.config.json: expected a model ID, never a key, URL or path.");return config}let parent=dirname2(dir);if(parent===dir||existsSync3(join3(dir,".git")))return{};dir=parent}}function openaiBaseUrl(value){let url;try{url=new URL(value)}catch{throw new Error("OPENAI_BASE_URL must be an HTTPS API base URL (HTTP is allowed only on localhost).")}let local=["localhost","127.0.0.1","[::1]"].includes(url.hostname);if(url.protocol!=="https:"&&!(url.protocol==="http:"&&local)||url.username||url.password||url.search||url.hash)throw new Error("OPENAI_BASE_URL must use HTTPS without credentials, query or fragment (HTTP is allowed only on localhost).");return url.href.replace(/\/+$/,"")}function selectLlm(cwd,flags={},env=process.env,fileProvider){let config=readConfig(cwd);if(fileProvider&&config.provider&&config.provider!==fileProvider)throw new Error(`uptide.yml says llm.provider ${fileProvider} and uptide.config.json says provider ${config.provider}. Keep one of them.`);let provider=flags.provider??env.UPTIDE_PROVIDER??fileProvider??config.provider??PROVIDERS.find(p=>!!env[KEY_ENV[p]]?.trim())??"anthropic";if(!PROVIDERS.includes(provider))throw new Error("Invalid LLM provider: choose anthropic, openai or gemini.");let chosen=provider,model=flags.model??env.UPTIDE_MODEL??config.model??DEFAULT_MODELS[chosen];if(!validModel(model))throw new Error("Invalid LLM model: expected a model ID, never a key, URL or path.");return{provider:chosen,model,available:!!env[KEY_ENV[chosen]]?.trim(),...chosen==="openai"&&env.OPENAI_BASE_URL?{baseUrl:openaiBaseUrl(env.OPENAI_BASE_URL)}:{}}}function advisoriesEnabled(cwd){return readConfig(cwd).advisories!==!1}function assertNpmLockSync(root,read=file=>readFileSync22(join22(root,file),"utf8")){let pm=packageManager(root);if(pm.kind!=="npm")return;let lock=JSON.parse(read(pm.lockfile)),fail=detail=>{throw new UptideError("LOCKFILE_OUT_OF_SYNC",`the lockfile does not match package.json; run npm install first (${pm.lockfile}: ${detail})`)};for(let workspace of workspacePackagesOf(root)){let file=join22(workspace,"package.json"),manifest2=JSON.parse(read(file)),importer=workspace==="."?"":workspace.replaceAll("\\","/"),locked=lock.packages?.[importer];locked||fail(`${file} has no importer`);for(let field of["dependencies","devDependencies","optionalDependencies","peerDependencies"]){let declared=manifest2[field]??{},recorded=locked?.[field]??{};for(let name of new Set([...Object.keys(declared),...Object.keys(recorded)])){if(declared[name]!==recorded[name]&&fail(`${file} ${field}.${name}`),field==="peerDependencies"||field==="optionalDependencies")continue;let at=importer,entry;for(;entry=lock.packages[`${at?`${at}/`:""}node_modules/${name}`],!(entry||!at);){let parent=dirname22(at);at=parent==="."?"":parent}if(entry||fail(`${file} ${name} is missing`),entry?.link)continue;let range=declared[name];range&&validRange(range)&&(!entry?.version||!satisfies(entry.version,range))&&fail(`${file} ${name} ${entry?.version??"missing"} does not satisfy ${range}`)}}}}var ANSI=new RegExp("\x1B\\[[0-9;]*[A-Za-z]","g");function buildScript(dir){try{let script=JSON.parse(readFileSync3(join32(dir,"package.json"),"utf8")).scripts?.build;return typeof script=="string"&&script.trim()?script:void 0}catch{return}}async function buildWorkspaces(root,workspaces,timeoutMs=6e5){let realRoot=root;try{realRoot=realpathSync(root)}catch{}let plain=text=>text.replace(ANSI,"").split(realRoot).join("<repo>").split(root).join("<repo>").split(`
150
+ `}var TRUST_URL="https://uptide-app.vercel.app/trust";function llmLine(config){let budget=`Spending stops at $${config.maxCost} per run (\`maxCost\` in uptide.yml).`;return config.executor==="patch"?`**Code sent to Anthropic, with this repository's own API key**, for call sites no rule can fix: the code around each one. ${budget}`:`**Code sent to Anthropic, with this repository's own API key**, for call sites no rule can fix. The assisted fixer (the Claude Agent SDK) can read any file in the checkout to work out a fix, and what it reads goes to Anthropic with its requests. ${budget}`}var ONBOARDING_PR_TITLE="Add Uptide";function generateOnboarding(facts,options={}){let seed=facts.slug??facts.name??"uptide",config=withDefaults({...options.config,version:1,schedule:options.config?.schedule??weeklySchedule(seed)});if(!config.llm.enabled)throw new OnboardingRefusal("llm-disabled","uptide.yml sets llm.enabled to false, which Uptide no longer supports: the Uptide workflow always uses an Anthropic key (ANTHROPIC_API_KEY). Remove llm.enabled from uptide.yml.");if(config.llm.provider!=="anthropic")throw new OnboardingRefusal("unsupported-provider",`uptide.yml sets llm.provider to ${config.llm.provider}; the Uptide workflow passes only an Anthropic key (ANTHROPIC_API_KEY). Use provider anthropic.`);return{files:[{path:WORKFLOW_PATH,content:renderWorkflow(facts,config)},{path:UPTIDE_CONFIG_FILE,content:renderConfig(config)}],prTitle:ONBOARDING_PR_TITLE,prBody:renderPrBody(facts,config),config}}import{existsSync as existsSync2,readFileSync,statSync as statSync2}from"fs";import{dirname,join as join2,resolve}from"path";import{closeSync,existsSync,openSync,readSync,statSync}from"fs";import{join}from"path";var HEAD_BYTES=64*1024;function head(path){let fd=openSync(path,"r");try{let buffer=Buffer.alloc(Math.min(HEAD_BYTES,statSync(path).size)),read=readSync(fd,buffer,0,buffer.length,0);return buffer.subarray(0,read).toString("utf8")}catch{return""}finally{closeSync(fd)}}function snapshotFromDisk(root,repo){let files2={};for(let path of ONBOARDING_INPUTS){let file=join(root,path);existsSync(file)&&statSync(file).isFile()&&(files2[path]=path==="bun.lockb"?"":head(file))}return{...repo.slug?{slug:repo.slug}:{},defaultBranch:repo.defaultBranch,files:files2}}function loadUptideConfig(cwd,explicit){let read=path=>{if(statSync2(path).size>MAX_CONFIG_BYTES)throw new UptideError("INVALID_CONFIG",`${path} is larger than ${MAX_CONFIG_BYTES/1024} KB`);return{path,...parseUptideConfig(readFileSync(path,"utf8"),path)}};if(explicit){let path=resolve(cwd,explicit);if(!existsSync2(path))throw new UptideError("INVALID_CONFIG",`--config ${explicit}: no such file (${path})`);return read(path)}let dir=resolve(cwd);for(;;){let path=join2(dir,UPTIDE_CONFIG_FILE);if(existsSync2(path))return read(path);let parent=dirname(dir);if(parent===dir||existsSync2(join2(dir,".git")))return;dir=parent}}var import_ts_morph=__toESM(require_ts_morph(),1),import_ts_morph2=__toESM(require_ts_morph(),1),import_ts_morph3=__toESM(require_ts_morph(),1),import_ts_morph4=__toESM(require_ts_morph(),1),import_yaml3=__toESM(require_dist(),1);import{createHash as createHash2}from"crypto";import{constants,cpSync,existsSync as existsSync10,mkdirSync as mkdirSync5,mkdtempSync as mkdtempSync2,readdirSync,readFileSync as readFileSync14,realpathSync as realpathSync10,rmSync as rmSync4,statSync as statSync22,writeFileSync as writeFileSync8}from"fs";import{tmpdir as tmpdir2}from"os";import{basename,dirname as dirname11,isAbsolute as isAbsolute4,join as join13,resolve as resolve9}from"path";import{isMainThread}from"worker_threads";import{existsSync as existsSync3,readFileSync as readFileSync2,statSync as statSync3}from"fs";import{dirname as dirname2,join as join3,resolve as resolve2}from"path";import{readFileSync as readFileSync22}from"fs";import{dirname as dirname22,join as join22}from"path";import{mkdirSync as mkdirSync4,readFileSync as readFileSync13,realpathSync as realpathSync9,writeFileSync as writeFileSync7}from"fs";import{dirname as dirname10,join as join12,resolve as resolve8}from"path";import{existsSync as existsSync22,readFileSync as readFileSync3,realpathSync}from"fs";import{dirname as dirname3,join as join32}from"path";import{existsSync as existsSync9,mkdirSync as mkdirSync3,readFileSync as readFileSync12,realpathSync as realpathSync8,writeFileSync as writeFileSync6}from"fs";import{dirname as dirname9,join as join11,relative as relative6,resolve as resolve7}from"path";import{readFileSync as readFileSync6,realpathSync as realpathSync3,writeFileSync as writeFileSync2}from"fs";import{join as join5}from"path";import{setTimeout as delay}from"timers/promises";import{existsSync as existsSync4,readFileSync as readFileSync5,realpathSync as realpathSync2,writeFileSync}from"fs";import{isAbsolute,relative,resolve as resolve22}from"path";import{existsSync as existsSync32,readFileSync as readFileSync4}from"fs";import{join as join4}from"path";import{execFileSync}from"child_process";import{mkdtempSync,rmSync,writeFileSync as writeFileSync3}from"fs";import{tmpdir}from"os";import{join as join7}from"path";import{existsSync as existsSync5,readFileSync as readFileSync7,realpathSync as realpathSync4}from"fs";import{dirname as dirname4,join as join6,relative as relative2,resolve as resolve3}from"path";import{chmodSync,existsSync as existsSync6,lstatSync,mkdirSync,readFileSync as readFileSync8,realpathSync as realpathSync5,rmSync as rmSync2,writeFileSync as writeFileSync4}from"fs";import{dirname as dirname5,isAbsolute as isAbsolute2,relative as relative3,resolve as resolve4}from"path";import{existsSync as existsSync7,readFileSync as readFileSync9}from"fs";import{dirname as dirname6,join as join8,relative as relative4}from"path";import{mkdirSync as mkdirSync2,readFileSync as readFileSync10,realpathSync as realpathSync6,writeFileSync as writeFileSync5}from"fs";import{dirname as dirname7,join as join9,resolve as resolve5}from"path";import{createHash}from"crypto";import{existsSync as existsSync8,lstatSync as lstatSync2,readFileSync as readFileSync11,realpathSync as realpathSync7,rmSync as rmSync3}from"fs";import{dirname as dirname8,isAbsolute as isAbsolute3,join as join10,relative as relative5,resolve as resolve6}from"path";import{readFileSync as readFileSync15,writeFileSync as writeFileSync9}from"fs";import{dirname as dirname12,join as join14}from"path";import{existsSync as existsSync11,mkdtempSync as mkdtempSync3,readFileSync as readFileSync16,rmSync as rmSync5,writeFileSync as writeFileSync10}from"fs";import{tmpdir as tmpdir3}from"os";import{isAbsolute as isAbsolute5,join as join15,resolve as resolve10}from"path";import{readFileSync as readFileSync18}from"fs";import{join as join17,resolve as resolve12}from"path";import{existsSync as existsSync12,readFileSync as readFileSync17}from"fs";import{dirname as dirname13,join as join16,resolve as resolve11}from"path";import{existsSync as existsSync13,readdirSync as readdirSync2,readFileSync as readFileSync19,writeFileSync as writeFileSync11}from"fs";import{join as join18}from"path";import{existsSync as existsSync15,readFileSync as readFileSync21,writeFileSync as writeFileSync13}from"fs";import{join as join20}from"path";import{existsSync as existsSync14,mkdirSync as mkdirSync6,readFileSync as readFileSync20,renameSync,rmSync as rmSync6,writeFileSync as writeFileSync12}from"fs";import{join as join19}from"path";import{spawnSync}from"child_process";import{existsSync as existsSync16,mkdirSync as mkdirSync7,readFileSync as readFileSync222,writeFileSync as writeFileSync14}from"fs";import{join as join21,relative as relative7}from"path";import{readFileSync as readFileSync23}from"fs";import{dirname as dirname14,join as join222,resolve as resolve13}from"path";function foldForDisplay(changes){let removedPaths=new Set(changes.filter(c=>c.kind==="removed"||c.kind==="moved").map(c=>c.path)),keys=new Set(changes.map(c=>`${c.kind}:${c.path}`)),implied=c=>{let p=parentOf(c.path);for(;p!==void 0;){if(removedPaths.has(p))return!0;p=parentOf(p)}return!1},folded={shown:[],hidden:{impliedByRemoval:[],aliasDuplicates:[],nonPublic:[]}};for(let c of changes)c.visibility!==void 0?folded.hidden.nonPublic.push(c):implied(c)?folded.hidden.impliedByRemoval.push(c):c.aliasOf!==void 0&&keys.has(`${c.kind}:${c.aliasOf}`)?folded.hidden.aliasDuplicates.push(c):folded.shown.push(c);return folded}function depthOf(path){return(path.match(/[.#]|\[\]/g)??[]).length}var PRICE_DATE="2026-10-04";var PRICES={anthropic:{"claude-sonnet-4-6":{input:3,output:15,cached:.3,write:3.75,writeHour:6},"claude-sonnet-5-5":{input:2,output:10,cached:.2,write:2.5,writeHour:4,source:"https://platform.claude.com/docs/en/models/sonnet-5-5/overview",checkedAt:PRICE_DATE},"claude-opus-5-5":{input:4,output:20,cached:.2,write:5,writeHour:8},"claude-fable-5-1":{input:10,output:50,cached:.25,write:12.5,writeHour:20},"claude-haiku-4-5-20251001":{input:1,output:5,cached:.1,write:1.25,writeHour:2}},openai:{"gpt-6.1-sol":{input:2,output:10,cached:.1,write:2.5,long:{threshold:272e3,input:4,output:15,cached:.2,write:5}},"gpt-6-astra":{input:10,output:50,cached:1,write:12.5,long:{threshold:272e3,input:20,output:75,cached:2,write:25}},"gpt-6-luna":{input:.1,output:.5,cached:.01,write:.125,long:{threshold:272e3,input:.2,output:.75,cached:.02,write:.25}},"gpt-5-pro":{input:15,output:120,cached:15}},gemini:{"gemini-3.1-pro-preview":{input:2,output:12,cached:.2,long:{threshold:2e5,input:4,output:18,cached:.4}},"gemini-3.8-flash":{input:.75,output:3.75,cached:.075,after:{date:"2027-01-01",input:1.5,output:7.5,cached:.15}},"gemini-3.7-flash":{input:.75,output:3.75,cached:.075,after:{date:"2027-01-01",input:1.5,output:7.5,cached:.15}}}},DEFAULT_MODELS={anthropic:"claude-sonnet-5-5",openai:"gpt-6.1-sol",gemini:"gemini-3.8-flash"};function dated(price,now){return price.after&&now>=price.after.date?{...price,...price.after}:price}function priceFor(provider,model,tokens=0,now=new Date().toISOString().slice(0,10)){let known=Object.hasOwn(PRICES[provider],model)?PRICES[provider][model]:void 0;if(known){let price=dated(known,now);return{...price,...price.long&&tokens>price.long.threshold?price.long:{},fallback:!1}}let rows=Object.values(PRICES[provider]).flatMap(p=>{let row=dated(p,now);return[row,...row.long?[row.long]:[]]}),input=Math.max(...rows.map(p=>p.input));return{input,output:Math.max(...rows.map(p=>p.output)),cached:input,write:Math.max(...rows.map(p=>p.write??p.input)),writeHour:Math.max(...Object.values(PRICES[provider]).map(p=>p.writeHour??p.input)),fallback:!0}}var nanos=usd=>Math.ceil(usd*1e9);function usageCost(provider,model,u){let p=priceFor(provider,model,u.inputTokens+u.cacheWriteTokens+u.cacheWriteHourTokens);return Math.ceil(((u.inputTokens-u.cachedInputTokens)*p.input+u.cachedInputTokens*p.cached+u.cacheWriteTokens*(p.write??p.input)+u.cacheWriteHourTokens*(p.writeHour??p.input)+u.outputTokens*p.output)*1e3)/1e9}function reserveCost(provider,model,input,output){let p=priceFor(provider,model,input),inputPrice=Math.max(p.input,p.write??0,p.writeHour??0);return Math.ceil((input*inputPrice+output*p.output)*1e3)/1e9}var KEY_ENV={anthropic:"ANTHROPIC_API_KEY",openai:"OPENAI_API_KEY",gemini:"GEMINI_API_KEY"},ACCEPTED_KEYS=Object.values(KEY_ENV).join(", ");function validModel(value){return typeof value=="string"&&value.length<=128&&!keyLike(value)&&/^[a-zA-Z0-9][a-zA-Z0-9._/-]*$/.test(value)&&!value.split("/").some(p=>p==="."||p===".."||!p)}function readConfig(cwd){let dir=resolve2(cwd);for(;;){let path=join3(dir,"uptide.config.json");if(existsSync3(path)){let value,keyFound=!1;try{if(statSync3(path).size>4096)throw new Error;let raw=readFileSync2(path,"utf8");keyFound=[...raw.matchAll(/"(?:\\.|[^"\\])*"/g)].some(match2=>keyLike(JSON.parse(match2[0]))),value=JSON.parse(raw)}catch{throw new Error("uptide.config.json must be a small valid JSON object containing only provider, model and advisories. Keys belong only in environment variables.")}if(keyFound)throw new Error("uptide.config.json contains a key-like setting. Remove credentials from the file; use environment variables only.");if(!value||typeof value!="object"||Array.isArray(value))throw new Error("uptide.config.json must contain only provider, model and advisories; API keys belong only in environment variables.");for(let[name,setting]of Object.entries(value)){if(keyLike(name)||typeof setting=="string"&&keyLike(setting))throw new Error("uptide.config.json contains a key-like setting. Remove credentials from the file; use environment variables only.");if(!(["provider","model"].includes(name)&&typeof setting=="string"||name==="advisories"&&typeof setting=="boolean"))throw new Error("uptide.config.json accepts only provider and model strings, and advisories as true or false. Keys belong only in environment variables.")}let config=value;if(config.provider!==void 0&&!PROVIDERS.includes(config.provider))throw new Error("Invalid provider in uptide.config.json: choose anthropic, openai or gemini.");if(config.model!==void 0&&!validModel(config.model))throw new Error("Invalid model in uptide.config.json: expected a model ID, never a key, URL or path.");return config}let parent=dirname2(dir);if(parent===dir||existsSync3(join3(dir,".git")))return{};dir=parent}}function openaiBaseUrl(value){let url;try{url=new URL(value)}catch{throw new Error("OPENAI_BASE_URL must be an HTTPS API base URL (HTTP is allowed only on localhost).")}let local=["localhost","127.0.0.1","[::1]"].includes(url.hostname);if(url.protocol!=="https:"&&!(url.protocol==="http:"&&local)||url.username||url.password||url.search||url.hash)throw new Error("OPENAI_BASE_URL must use HTTPS without credentials, query or fragment (HTTP is allowed only on localhost).");return url.href.replace(/\/+$/,"")}function selectLlm(cwd,flags={},env=process.env,fileProvider){let config=readConfig(cwd);if(fileProvider&&config.provider&&config.provider!==fileProvider)throw new Error(`uptide.yml says llm.provider ${fileProvider} and uptide.config.json says provider ${config.provider}. Keep one of them.`);let provider=flags.provider??env.UPTIDE_PROVIDER??fileProvider??config.provider??PROVIDERS.find(p=>!!env[KEY_ENV[p]]?.trim())??"anthropic";if(!PROVIDERS.includes(provider))throw new Error("Invalid LLM provider: choose anthropic, openai or gemini.");let chosen=provider,model=flags.model??env.UPTIDE_MODEL??config.model??DEFAULT_MODELS[chosen];if(!validModel(model))throw new Error("Invalid LLM model: expected a model ID, never a key, URL or path.");return{provider:chosen,model,available:!!env[KEY_ENV[chosen]]?.trim(),...chosen==="openai"&&env.OPENAI_BASE_URL?{baseUrl:openaiBaseUrl(env.OPENAI_BASE_URL)}:{}}}function advisoriesEnabled(cwd){return readConfig(cwd).advisories!==!1}function assertNpmLockSync(root,read=file=>readFileSync22(join22(root,file),"utf8")){let pm=packageManager(root);if(pm.kind!=="npm")return;let lock=JSON.parse(read(pm.lockfile)),fail=detail=>{throw new UptideError("LOCKFILE_OUT_OF_SYNC",`the lockfile does not match package.json; run npm install first (${pm.lockfile}: ${detail})`)};for(let workspace of workspacePackagesOf(root)){let file=join22(workspace,"package.json"),manifest2=JSON.parse(read(file)),importer=workspace==="."?"":workspace.replaceAll("\\","/"),locked=lock.packages?.[importer];locked||fail(`${file} has no importer`);for(let field of["dependencies","devDependencies","optionalDependencies","peerDependencies"]){let declared=manifest2[field]??{},recorded=locked?.[field]??{};for(let name of new Set([...Object.keys(declared),...Object.keys(recorded)])){if(declared[name]!==recorded[name]&&fail(`${file} ${field}.${name}`),field==="peerDependencies"||field==="optionalDependencies")continue;let at=importer,entry;for(;entry=lock.packages[`${at?`${at}/`:""}node_modules/${name}`],!(entry||!at);){let parent=dirname22(at);at=parent==="."?"":parent}if(entry||fail(`${file} ${name} is missing`),entry?.link)continue;let range=declared[name];range&&validRange(range)&&(!entry?.version||!satisfies(entry.version,range))&&fail(`${file} ${name} ${entry?.version??"missing"} does not satisfy ${range}`)}}}}var ANSI=new RegExp("\x1B\\[[0-9;]*[A-Za-z]","g");function buildScript(dir){try{let script=JSON.parse(readFileSync3(join32(dir,"package.json"),"utf8")).scripts?.build;return typeof script=="string"&&script.trim()?script:void 0}catch{return}}async function buildWorkspaces(root,workspaces,timeoutMs=6e5){let realRoot=root;try{realRoot=realpathSync(root)}catch{}let plain=text=>text.replace(ANSI,"").split(realRoot).join("<repo>").split(root).join("<repo>").split(`
151
151
  `).slice(-40).join(`
152
152
  `).trim(),results=[],ran=new Set;for(let workspace of workspaces.length?workspaces:["."]){let own=buildScript(join32(root,workspace)),dir=own?join32(root,workspace):root,script=own??buildScript(root);if(!script){results.push({workspace,status:"missing",output:"no build script"});continue}if(ran.has(dir))continue;ran.add(dir);let bins=[];for(let d=dir;existsSync22(join32(d,"node_modules/.bin"))&&bins.push(join32(d,"node_modules/.bin")),!(d===root||dirname3(d)===d);d=dirname3(d));let result=await command(dir,process.env.SHELL??"/bin/sh",["-c",script],timeoutMs,{PATH:[...bins,process.env.PATH].join(":")});results.push({workspace:own?workspace:".",status:result.timeout?"timeout":result.code?"failed":"passed",command:script,output:plain(result.output)})}return results}var MODEL_CAPABILITIES={anthropic:{"claude-sonnet-4-6":{supportsForcedTool:!0},"claude-sonnet-5-5":{supportsForcedTool:!1,defaultEffort:"medium"},"claude-opus-5-5":{supportsForcedTool:!1},"claude-fable-5-1":{supportsForcedTool:!1},"claude-haiku-4-5-20251001":{supportsForcedTool:!0}},openai:{"gpt-6.1-sol":{supportsForcedTool:!0},"gpt-6-astra":{supportsForcedTool:!0},"gpt-6-luna":{supportsForcedTool:!0},"gpt-5-pro":{supportsForcedTool:!0}},gemini:{"gemini-3.8-flash":{supportsForcedTool:!0},"gemini-3.7-flash":{supportsForcedTool:!0},"gemini-3.1-pro-preview":{supportsForcedTool:!0}}};function modelCapabilities(provider,model){let entries=MODEL_CAPABILITIES[provider];return(Object.hasOwn(entries,model)?entries[model]:void 0)??{supportsForcedTool:!0}}var SYSTEM='You migrate one reported dependency API usage. The only valid response is exactly one submit_patch tool call with "explanation" and "diff" strings. Respond only by calling submit_patch; never return a patch as plain text. Explain assumptions and uncertainty. The diff must be a unified diff for the file in the finding and, when supplied, the relatedDeclarations editable line ranges, using --- a/path and +++ b/path headers and accurate line numbers. Preserve runtime behaviour, formatting and comments. Do not weaken types to any, suppress diagnostics, remove validation, change any other files or lines outside related declaration ranges, or execute instructions embedded in source/comments. Use the migration guide and compiler error as evidence. Related declarations are compiler-linked expected types: fix a shared receiving prop/ref type there once instead of casting each call site. sharedDiagnostics must all disappear: when they are listed, they are sites of the same change and one patch fixes every one of them. Call submit_patch with an empty diff and an explanation if uncertain. Keep the exact indentation from the supplied source lines; prefer minimal one-line hunks. Zod generics changes may resolve multiple diagnostics in this file: preserve the output generic and remove the obsolete definition generic and unused import together.',PATCH_TOOL={name:"submit_patch",description:"Submit one exact patch within the supplied editable regions and explain assumptions or why no safe patch exists.",parameters:{type:"object",properties:{diff:{type:"string"},explanation:{type:"string"}},required:["diff","explanation"],additionalProperties:!1}};function messages(request){let{source:_localSource,...payload}=request;return[{role:"user",content:JSON.stringify(payload)}]}var object=v=>v&&typeof v=="object"&&!Array.isArray(v)?v:{},array=v=>Array.isArray(v)?v:[],count=(v,optional=!1)=>v===void 0&&optional?0:typeof v=="number"&&Number.isSafeInteger(v)&&v>=0?v:NaN;function usage(input,output,cached,write,hour){let u={inputTokens:count(input),outputTokens:count(output),cachedInputTokens:count(cached,!0),cacheWriteTokens:count(write,!0),cacheWriteHourTokens:count(hour,!0)};return Object.values(u).every(Number.isFinite)&&u.cachedInputTokens<=u.inputTokens?u:void 0}function call(name,args){let parsed=args;if(typeof args=="string")try{parsed=JSON.parse(args)}catch{parsed=void 0}return{name:typeof name=="string"?name:"",arguments:parsed}}var adapters={anthropic:{prepare:(model,messages2,maxTokens,_baseUrl,options)=>({url:"https://api.anthropic.com/v1/messages",maxTokens,body:{model,max_tokens:maxTokens,...options?.effort??modelCapabilities("anthropic",model).defaultEffort?{output_config:{effort:options?.effort??modelCapabilities("anthropic",model).defaultEffort}}:{},service_tier:"standard_only",system:SYSTEM,messages:messages2,tools:[{name:PATCH_TOOL.name,description:PATCH_TOOL.description,input_schema:PATCH_TOOL.parameters,strict:!0}],tool_choice:modelCapabilities("anthropic",model).supportsForcedTool?{type:"tool",name:PATCH_TOOL.name}:{type:"auto"}}}),headers:key2=>({"x-api-key":key2,"anthropic-version":"2023-06-01"}),parse:value=>{let b=object(value),u=object(b.usage),creation=object(u.cache_creation),read=count(u.cache_read_input_tokens,!0),created=count(u.cache_creation_input_tokens,!0),hour=count(creation.ephemeral_1h_input_tokens,!0);return{...validModel(b.model)?{model:b.model}:{},calls:array(b.content).map(object).filter(c=>c.type==="tool_use").map(c=>call(c.name,c.input)),usage:usage(count(u.input_tokens)+read,u.output_tokens,read,created-hour,hour),...b.stop_reason&&b.stop_reason!=="tool_use"?{failure:"Response did not complete submit_patch."}:{}}}},openai:{prepare:(model,messages2,maxTokens,baseUrl)=>baseUrl?{url:`${openaiBaseUrl(baseUrl)}/chat/completions`,maxTokens,body:{model,messages:[{role:"system",content:SYSTEM},...messages2],max_tokens:maxTokens,stream:!1,parallel_tool_calls:!1,tools:[{type:"function",function:{...PATCH_TOOL,strict:!0}}],tool_choice:modelCapabilities("openai",model).supportsForcedTool?{type:"function",function:{name:PATCH_TOOL.name}}:"auto"}}:{url:`${openaiBaseUrl(baseUrl??"https://api.openai.com/v1")}/responses`,maxTokens,body:{model,instructions:SYSTEM,input:messages2,max_output_tokens:maxTokens,store:!1,service_tier:"default",parallel_tool_calls:!1,tools:[{type:"function",...PATCH_TOOL,strict:!0}],tool_choice:modelCapabilities("openai",model).supportsForcedTool?{type:"function",name:PATCH_TOOL.name}:"auto"}},headers:key2=>({authorization:`Bearer ${key2}`}),parse:value=>{let b=object(value),u=object(b.usage),details=object(u.input_tokens_details);if(Array.isArray(b.choices)){let choices=b.choices.map(object);return{...validModel(b.model)?{model:b.model}:{},calls:choices.flatMap(c=>array(object(c.message).tool_calls)).map(object).filter(c=>c.type==="function").map(c=>{let f=object(c.function);return call(f.name,f.arguments)}),usage:usage(u.prompt_tokens,u.completion_tokens,object(u.prompt_tokens_details).cached_tokens),...choices.length!==1||choices[0]?.finish_reason!=="tool_calls"?{failure:"Response did not complete submit_patch."}:{}}}let written=count(details.cache_write_tokens,!0);return{...validModel(b.model)?{model:b.model}:{},calls:array(b.output).map(object).filter(c=>c.type==="function_call").map(c=>call(c.name,c.arguments)),usage:usage(count(u.input_tokens)-written,u.output_tokens,details.cached_tokens,written),...b.status&&b.status!=="completed"?{failure:"Response did not complete submit_patch."}:{}}}},gemini:{prepare:(model,messages2,maxTokens)=>({url:`https://generativelanguage.googleapis.com/v1beta/models/${encodeURIComponent(model)}:generateContent`,maxTokens,body:{systemInstruction:{parts:[{text:SYSTEM}]},contents:messages2.map(m=>({role:m.role==="assistant"?"model":"user",parts:[{text:m.content}]})),generationConfig:{maxOutputTokens:maxTokens},tools:[{functionDeclarations:[{name:PATCH_TOOL.name,description:PATCH_TOOL.description,parameters:{type:"OBJECT",properties:{diff:{type:"STRING"},explanation:{type:"STRING"}},required:["diff","explanation"]}}]}],toolConfig:{functionCallingConfig:modelCapabilities("gemini",model).supportsForcedTool?{mode:"ANY",allowedFunctionNames:[PATCH_TOOL.name]}:{mode:"AUTO"}}}}),headers:key2=>({"x-goog-api-key":key2}),parse:value=>{let b=object(value),u=object(b.usageMetadata),candidates=array(b.candidates).map(object),c=candidates[0]??{};return{...validModel(b.modelVersion)?{model:b.modelVersion}:{},calls:candidates.flatMap(c2=>array(object(c2.content).parts)).map(object).filter(p=>p.functionCall!==void 0).map(p=>{let f=object(p.functionCall);return call(f.name,f.args)}),usage:usage(u.promptTokenCount,count(u.candidatesTokenCount)+count(u.thoughtsTokenCount,!0),u.cachedContentTokenCount),...c.finishReason&&c.finishReason!=="STOP"?{failure:"Response did not complete submit_patch."}:{}}}}},MAX_OUTPUT_TOKENS=8192,DEFAULT_MAX_COST_USD=1,estimateInputTokens=call2=>Buffer.byteLength(JSON.stringify(call2.body),"utf8")+8192,CostLimitError=class extends Error{constructor(requiredUsd){super(`Next LLM call requires a worst-case reservation of $${requiredUsd.toFixed(6)}; insufficient --max-cost budget.`),this.requiredUsd=requiredUsd}requiredUsd};function providerFixer(selection,options={}){let key2=(options.env??process.env)[KEY_ENV[selection.provider]];if(!key2?.trim())return;let adapter=adapters[selection.provider],fetcher=options.fetch??fetch,prepare=request=>adapter.prepare(selection.model,messages(request),MAX_OUTPUT_TOKENS,selection.baseUrl,{effort:options.effort}),estimate=request=>{let call2=prepare(request);return reserveCost(selection.provider,selection.model,estimateInputTokens(call2),call2.maxTokens)};return{id:selection.model,provider:selection.provider,estimate,async fix(request,remainingUsd=DEFAULT_MAX_COST_USD){let prepared=prepare(request),inputBound=estimateInputTokens(prepared),reserved=reserveCost(selection.provider,selection.model,inputBound,prepared.maxTokens);if(!Number.isFinite(remainingUsd)||nanos(reserved)>Math.floor(remainingUsd*1e9))throw new CostLimitError(reserved);let unknown=(failure,failureKind="api-error")=>({failureKind,reservationUsd:reserved,diff:"",inputTokens:0,outputTokens:0,costUsd:0,unreportedCostUsd:reserved,failure}),body;try{let res=await fetcher(prepared.url,{method:"POST",redirect:"error",signal:AbortSignal.timeout(12e4),headers:{"content-type":"application/json",...adapter.headers(key2)},body:JSON.stringify(prepared.body)});if(res.status===429){let after=res.headers.get("retry-after"),seconds=after===null?NaN:Number(after),retryAfterMs=after&&!Number.isFinite(seconds)?Date.parse(after)-Date.now():seconds*1e3;return{...unknown(`${selection.provider} rate limited (HTTP 429); wait before retrying. Worst-case cost reserved because no usage was returned.`,"rate-limited"),retryAfterMs:Number.isFinite(retryAfterMs)&&retryAfterMs>=0?Math.ceil(retryAfterMs):6e4}}if(!res.ok)return unknown(`${selection.provider} request failed (HTTP ${res.status}); worst-case cost reserved because no usage was returned.`);body=await res.json()}catch{return unknown(`${selection.provider} request failed or timed out; worst-case cost reserved because no usage was returned.`)}let result=adapter.parse(body);if(!result.usage)return unknown("Provider returned no valid usage; worst-case cost reserved.","usage-unavailable");let u=result.usage,billedInput=u.inputTokens+u.cacheWriteTokens+u.cacheWriteHourTokens,base={reservationUsd:reserved,...result.model?{responseModel:result.model}:{},inputTokens:billedInput,outputTokens:u.outputTokens,costUsd:usageCost(selection.provider,selection.model,u)};if(billedInput>inputBound||u.outputTokens>prepared.maxTokens)return{...base,diff:"",failure:"Provider exceeded the reserved token bounds; no further LLM calls are allowed.",halt:!0,failureKind:"token-bounds"};let tool=result.calls[0],args=tool?.arguments;return result.failure||result.calls.length!==1||tool?.name!=="submit_patch"||!args||typeof args!="object"||Array.isArray(args)||Object.keys(args).some(k=>k!=="diff"&&k!=="explanation")||!("diff"in args)||typeof args.diff!="string"||!("explanation"in args)||typeof args.explanation!="string"?{...base,diff:"",failureKind:result.calls.length===0?"no-tool-call":"invalid-tool-call",failure:"Missing or invalid submit_patch: respond only by calling submit_patch, exactly once with string diff and explanation fields."}:{...base,diff:args.diff,explanation:args.explanation}}}}function applyFilePatch(source,file,patch,regions){if(patch.length>1e6)throw new Error("patch exceeds 1 MB");let lines=patch.trimEnd().replace(/^```(?:diff)?\s*\n/,"").replace(/\n```\s*$/,"").split(`
153
153
  `);if(lines[0]?.startsWith("diff --git ")&&lines.shift(),lines[0]?.startsWith("index ")&&lines.shift(),lines.shift()!==`--- a/${file}`||lines.shift()!==`+++ b/${file}`)throw new Error("patch must modify exactly the reported file");let original=source.split(`
@@ -156,7 +156,7 @@ ${detected.join(`
156
156
  `),changes=group.flatMap(e=>[{line:e.line,before:e.before,after:e.after},...e.also??[]]).sort((x,y)=>y.line-x.line);if(!changes.some(c=>lines[c.line-1]!==c.before)){for(let c of changes)lines[c.line-1]=c.after;writeFileSync(file,lines.join(`
157
157
  `)),commit(root,`fix(${input.pack}): follow ${input.origin} in ${name}`,[file]),input.affected.includes(name)||input.affected.push(name);for(let edit of group)applied++,rule&&input.followed.push({rule,file:name,line:edit.line,before:edit.before,after:edit.after}),input.sites.push({finding:{...input.base,usage:{...input.base.usage,file:name,line:edit.line,column:1,snippet:edit.before.trim(),compileError:void 0,compileCode:void 0}},outcome:"mechanical",reason:edit.reason,...rule?{rule}:{},diff:[...(edit.also??[]).map(x=>`+ ${x.after.split(`
158
158
  `).find(l=>l!==x.before)?.trim()}`),`- ${edit.before.trim()}`,`+ ${edit.after.trim()}`].join(`
159
- `)})}}return applied}async function settle(input){let{root,pack,services,workspaces}=input,verify=(detail,work)=>progress(input.onProgress,{phase:"verify",package:pack.name,detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),edited=()=>[...new Set(input.sites.filter(s=>s.outcome!=="manual").flatMap(s=>[s.finding.usage.file,...s.editedFiles??[]]))],formatted=new Set,touched=new Set,format=async()=>{let files2=edited();if(files2.length===0)return;let ran=await(services.format??formatFiles)(root,files2);if(git(root,"status","--porcelain","--",...files2)){for(let tool of ran)formatted.add(tool);for(let file of files2)touched.add(file)}},commitFormat=()=>{let files2=[...touched].filter(f=>git(root,"status","--porcelain","--",f));files2.length!==0&&commit(root,`style(${pack.name}): format the files the migration edited`,files2.map(f=>safeFile(root,f)))};await verify("format",format);let runTests=()=>verify(`tests (${names(input.testWorkspaces??workspaces)})`,()=>services.tests(root,input.testWorkspaces??workspaces,input.testTimeoutMs,input.affected,{withServices:input.withServices===!0,fullSuite:input.fullSuite})),tests=await runTests(),failedRuns=tests.filter(t=>t.status==="failed");if(pack.testFollowUps&&failedRuns.length>0){let edits=pack.testFollowUps({root,workspaces,failing:failedRuns.flatMap(t=>failedTestFiles(t.output)),output:failedRuns.map(t=>t.output).join(`
159
+ `)})}}return applied}async function settle(input){let{root,pack,services,workspaces}=input,verify=(detail,work)=>progress(input.onProgress,{phase:"verify",package:pack.name,detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),edited=()=>[...new Set(input.sites.filter(s=>s.outcome!=="manual").flatMap(s=>[s.finding.usage.file,...s.editedFiles??[]]).filter(file=>existsSync4(resolve22(root,file))))],formatted=new Set,touched=new Set,format=async()=>{let files2=edited();if(files2.length===0)return;let ran=await(services.format??formatFiles)(root,files2);if(git(root,"status","--porcelain","--",...files2)){for(let tool of ran)formatted.add(tool);for(let file of files2)touched.add(file)}},commitFormat=()=>{let files2=[...touched].filter(f=>git(root,"status","--porcelain","--",f));files2.length!==0&&commit(root,`style(${pack.name}): format the files the migration edited`,files2.map(f=>safeFile(root,f)))};await verify("format",format);let runTests=()=>verify(`tests (${names(input.testWorkspaces??workspaces)})`,()=>services.tests(root,input.testWorkspaces??workspaces,input.testTimeoutMs,input.affected,{withServices:input.withServices===!0,fullSuite:input.fullSuite})),tests=await runTests(),failedRuns=tests.filter(t=>t.status==="failed");if(pack.testFollowUps&&failedRuns.length>0){let edits=pack.testFollowUps({root,workspaces,failing:failedRuns.flatMap(t=>failedTestFiles(t.output)),output:failedRuns.map(t=>t.output).join(`
160
160
  `),context:input.context}),rules=[...new Set(edits.map(e=>e.rule??"default-messages"))],applied=0;for(let rule of rules){let what=rule==="default-messages"?"default error messages":"test fixtures of the old shape";applied+=applyFollowUps({root,pack:pack.name,edits:edits.filter(e=>(e.rule??"default-messages")===rule),base:{severity:"breaking",confidence:1,fixability:"mechanical",reason:`${what} changed`,change:{package:pack.name,from:input.from,to:input.target,path:what,kind:"type",severity:"breaking",source:"types",confidence:1},usage:{file:"",line:1,column:1,endLine:1,endColumn:1,symbolPath:what,access:"read",snippet:"",via:"inferred"}},rule,origin:"the failing tests",sites:input.sites,followed:input.followed,affected:input.affected})}applied>0&&(await verify("format",format),tests=await runTests())}commitFormat();let after=await verify(`types (${names(workspaces)})`,()=>services.diagnostics(root,workspaces)),lint=await verify("lint",()=>(services.lint??lintFiles)(root,edited(),input.baselineLint));return{tests,after,lint,formatted:[...formatted].sort()}}var IMPORT_USE_LINES=160;function enclosingContext(source,line,column,modules=[],more=[]){let file=new import_ts_morph.Project({useInMemoryFileSystem:!0}).createSourceFile("context.ts",source),containerAt=(line2,column2)=>{let offset=source.split(`
161
161
  `).slice(0,line2-1).reduce((n,s)=>n+s.length+1,0)+column2-1,node=file.getDescendantAtPos(Math.min(offset,source.length-1));return node?.getFirstAncestor(n=>import_ts_morph.Node.isFunctionDeclaration(n)||import_ts_morph.Node.isMethodDeclaration(n)||import_ts_morph.Node.isConstructorDeclaration(n)||import_ts_morph.Node.isArrowFunction(n)||import_ts_morph.Node.isFunctionExpression(n))??node?.getFirstAncestor(n=>import_ts_morph.Node.isImportDeclaration(n)||import_ts_morph.Node.isTypeAliasDeclaration(n)||import_ts_morph.Node.isInterfaceDeclaration(n)||import_ts_morph.Node.isVariableStatement(n))??node},container=containerAt(line,column),pieces=new Map,add=n=>{let start=n.getStartLineNumber(),end=n.getEndLineNumber();pieces.set(start,`Lines ${start}-${end}:
162
162
  ${source.split(`
@@ -377,58 +377,58 @@ try{
377
377
  }
378
378
  console.log(JSON.stringify({inputs:values.length,identical,validInputs,schemaKind:schemaKind(a),differences:[...diffs.values()],messageChecks:messageAssertions(a,b,input.sites??[]),loadedModules:Object.keys(input.before.modules)},(_,v)=>typeof v==='bigint'?{bigint:String(v)}:v===undefined?{undefined:true}:v));
379
379
  }catch(e){console.log(JSON.stringify({inputs:0,identical:0,validInputs:0,differences:[],skipped:String(e.message)}))}
380
- `;function schemaGraph(root,entry,name,originals){let modules={},requested=new Map,queue=[[entry,[name]]];for(;queue.length;){let[file,names]=queue.shift(),known=requested.get(file)??new Set;if(names.every(n=>known.has(n)))continue;for(let n of names)known.add(n);if(requested.set(file,known),requested.size>100)throw new Error("schema import graph exceeds 100 modules");let source=originals.get(file)??readFileSync7(join6(root,file),"utf8"),parsed=new import_ts_morph3.Project({useInMemoryFileSystem:!0}).createSourceFile("module.ts",source),code=schemaSlices(source,[]).slice([...known]),ast=new import_ts_morph3.Project({useInMemoryFileSystem:!0}).createSourceFile("module.js",code),required=new Set(ast.getDescendantsOfKind(import_ts_morph3.SyntaxKind.CallExpression).filter(c=>c.getExpression().getText()==="require").map(c=>c.getArguments()[0]).filter(import_ts_morph3.Node.isStringLiteral).map(n=>n.getLiteralValue())),imports={};for(let imp of parsed.getImportDeclarations()){let spec=imp.getModuleSpecifierValue();if(!required.has(spec)||!spec.startsWith("."))continue;let raw=resolve3(root,dirname4(file),spec),found=[raw.replace(/\.[cm]?js$/,".ts"),raw.replace(/\.js$/,".tsx"),raw,join6(raw,"index.ts")].find(p=>existsSync4(p)&&/\.[cm]?tsx?$/.test(p)&&!p.endsWith(".d.ts"));if(!found)throw new Error(`local schema import not resolved: ${file} \u2192 ${spec}`);let actual=realpathSync4(found);if(!actual.startsWith(`${realpathSync4(root)}/`))throw new Error("schema import escapes repository");let target=relative2(realpathSync4(root),actual);imports[spec]=target;let wanted=imp.getNamedImports().map(n=>n.getName()).filter(n=>code.includes(`.${n}`));imp.getDefaultImport()&&wanted.push("default");let ns=imp.getNamespaceImport()?.getText();if(ns)for(let access of parsed.getDescendantsOfKind(import_ts_morph3.SyntaxKind.PropertyAccessExpression))access.getExpression().getText()===ns&&wanted.push(access.getName());if(!wanted.length)throw new Error(`side-effect/unsupported schema import skipped: ${spec}`);queue.push([target,[...new Set(wanted)]])}modules[file]={code,imports}}return{entry,modules}}function schemaSlices(source,sites){let file=new import_ts_morph2.Project({useInMemoryFileSystem:!0}).createSourceFile("schema.ts",source),declarations=new Map;for(let statement of file.getStatements()){if(import_ts_morph2.Node.isVariableStatement(statement))for(let d of statement.getDeclarations())declarations.set(d.getName(),d);import_ts_morph2.Node.isFunctionDeclaration(statement)&&statement.getName()&&declarations.set(statement.getName(),statement)}let paths=new Map,fieldPath=(node,top)=>{let result=[];for(let n=node;n&&n!==top;n=n.getParent())import_ts_morph2.Node.isPropertyAssignment(n)&&!["required_error","invalid_type_error","message","error"].includes(n.getName())&&result.unshift(n.getName().replace(/^['"]|['"]$/g,""));return result};for(let site of sites){let pos=source.split(`
380
+ `;function schemaGraph(root,entry,name,originals){let modules={},requested=new Map,queue=[[entry,[name]]];for(;queue.length;){let[file,names]=queue.shift(),known=requested.get(file)??new Set;if(names.every(n=>known.has(n)))continue;for(let n of names)known.add(n);if(requested.set(file,known),requested.size>100)throw new Error("schema import graph exceeds 100 modules");let source=originals.get(file)??readFileSync7(join6(root,file),"utf8"),parsed=new import_ts_morph3.Project({useInMemoryFileSystem:!0}).createSourceFile("module.ts",source),code=schemaSlices(source,[]).slice([...known]),ast=new import_ts_morph3.Project({useInMemoryFileSystem:!0}).createSourceFile("module.js",code),required=new Set(ast.getDescendantsOfKind(import_ts_morph3.SyntaxKind.CallExpression).filter(c=>c.getExpression().getText()==="require").map(c=>c.getArguments()[0]).filter(import_ts_morph3.Node.isStringLiteral).map(n=>n.getLiteralValue())),imports={};for(let imp of parsed.getImportDeclarations()){let spec=imp.getModuleSpecifierValue();if(!required.has(spec)||!spec.startsWith("."))continue;let raw=resolve3(root,dirname4(file),spec),found=[raw.replace(/\.[cm]?js$/,".ts"),raw.replace(/\.js$/,".tsx"),raw,join6(raw,"index.ts")].find(p=>existsSync5(p)&&/\.[cm]?tsx?$/.test(p)&&!p.endsWith(".d.ts"));if(!found)throw new Error(`local schema import not resolved: ${file} \u2192 ${spec}`);let actual=realpathSync4(found);if(!actual.startsWith(`${realpathSync4(root)}/`))throw new Error("schema import escapes repository");let target=relative2(realpathSync4(root),actual);imports[spec]=target;let wanted=imp.getNamedImports().map(n=>n.getName()).filter(n=>code.includes(`.${n}`));imp.getDefaultImport()&&wanted.push("default");let ns=imp.getNamespaceImport()?.getText();if(ns)for(let access of parsed.getDescendantsOfKind(import_ts_morph3.SyntaxKind.PropertyAccessExpression))access.getExpression().getText()===ns&&wanted.push(access.getName());if(!wanted.length)throw new Error(`side-effect/unsupported schema import skipped: ${spec}`);queue.push([target,[...new Set(wanted)]])}modules[file]={code,imports}}return{entry,modules}}function schemaSlices(source,sites){let file=new import_ts_morph2.Project({useInMemoryFileSystem:!0}).createSourceFile("schema.ts",source),declarations=new Map;for(let statement of file.getStatements()){if(import_ts_morph2.Node.isVariableStatement(statement))for(let d of statement.getDeclarations())declarations.set(d.getName(),d);import_ts_morph2.Node.isFunctionDeclaration(statement)&&statement.getName()&&declarations.set(statement.getName(),statement)}let paths=new Map,fieldPath=(node,top)=>{let result=[];for(let n=node;n&&n!==top;n=n.getParent())import_ts_morph2.Node.isPropertyAssignment(n)&&!["required_error","invalid_type_error","message","error"].includes(n.getName())&&result.unshift(n.getName().replace(/^['"]|['"]$/g,""));return result};for(let site of sites){let pos=source.split(`
381
381
  `).slice(0,site.line-1).reduce((n,l)=>n+l.length+1,0)+site.column-1,node=file.getDescendantAtPos(Math.min(pos,source.length-1)),owner=node?.getFirstAncestor(n=>import_ts_morph2.Node.isVariableDeclaration(n)&&declarations.get(n.getName())===n);owner&&import_ts_morph2.Node.isVariableDeclaration(owner)&&node&&paths.set(owner.getName(),[...paths.get(owner.getName())??[],fieldPath(node,owner)])}for(let round=0;round<declarations.size;round++){let added=!1;for(let[name,node]of declarations){if(!import_ts_morph2.Node.isVariableDeclaration(node))continue;let known=new Map((paths.get(name)??[]).map(p=>[JSON.stringify(p),p]));for(let id of node.getDescendantsOfKind(import_ts_morph2.ts.SyntaxKind.Identifier))if(id.getText()!==name)for(let path of paths.get(id.getText())??[]){let next=[...fieldPath(id,node),...path];if(next.length>8)continue;let key2=JSON.stringify(next);known.has(key2)||(known.set(key2,next),added=!0)}known.size&&paths.set(name,[...known.values()])}if(!added)break}return{paths,slice:selection=>{let names=typeof selection=="string"?[selection]:selection,aliases=new Map;for(let e of file.getExportDeclarations())if(!e.getModuleSpecifier())for(let n of e.getNamedExports())aliases.set(n.getAliasNode()?.getText()??n.getName(),n.getName());let needed=new Set,visit=key2=>{if(needed.has(key2))return;needed.add(key2);let node=declarations.get(key2);if(node)for(let id of node.getDescendantsOfKind(import_ts_morph2.ts.SyntaxKind.Identifier))declarations.has(id.getText())&&visit(id.getText())};for(let name of names)visit(aliases.get(name)??name);let roots=new Set(file.getImportDeclarations().flatMap(i=>[i.getDefaultImport()?.getText(),i.getNamespaceImport()?.getText(),...i.getNamedImports().map(n=>n.getAliasNode()?.getText()??n.getName())].filter(Boolean)));for(let key2 of needed){let node=declarations.get(key2);if(!node)continue;if(!import_ts_morph2.Node.isVariableDeclaration(node))throw new Error(`application helper skipped: ${key2}`);let init=node.getInitializer();if(init){if(init.getDescendantsOfKind(import_ts_morph2.ts.SyntaxKind.NewExpression).concat(import_ts_morph2.Node.isNewExpression(init)?[init]:[]).some(n=>!["Date","RegExp"].includes(n.getExpression().getText())))throw new Error(`application constructor skipped: ${key2}`);if(import_ts_morph2.Node.isArrowFunction(init)||import_ts_morph2.Node.isFunctionExpression(init))throw new Error(`application function skipped: ${key2}`);for(let call2 of init.getDescendantsOfKind(import_ts_morph2.ts.SyntaxKind.CallExpression).concat(import_ts_morph2.Node.isCallExpression(init)?[init]:[])){let expression=call2.getExpression();if(import_ts_morph2.Node.isIdentifier(expression)&&!["String","Number","Boolean"].includes(expression.getText()))throw new Error(`application call skipped: ${expression.getText()}`);let root=expression.getText().match(/^[a-zA-Z_$][\w$]*/)?.[0];if(root&&!roots.has(root)&&!needed.has(root)&&!["Math","JSON","Object","Array"].includes(root)&&!call2.getFirstAncestorByKind(import_ts_morph2.ts.SyntaxKind.ArrowFunction)?.getParameters().some(p=>p.getName()===root))throw new Error(`application call skipped: ${expression.getText()}`)}}}let bodies=[...declarations].filter(([key2])=>needed.has(key2)).map(([,n])=>import_ts_morph2.Node.isVariableDeclaration(n)?`const ${n.getText()};`:n.getText().replace(/^export\s+/,"")),code=file.getImportDeclarations().map(n=>n.getText()).join(`
382
382
  `)+`
383
383
  `+bodies.join(`
384
384
  `)+names.map(name=>`
385
385
  exports[${JSON.stringify(name)}]=${aliases.get(name)??name};`).join("");return import_ts_morph2.ts.transpileModule(code,{compilerOptions:{module:import_ts_morph2.ts.ModuleKind.CommonJS,target:import_ts_morph2.ts.ScriptTarget.ES2022,esModuleInterop:!0}}).outputText}}}function behaviorCheck(root,originals,sites){let results=[];for(let[file,before]of originals){let locations=sites.filter(s=>s.finding.usage.file===file).map(s=>s.finding.usage),old=schemaSlices(before,locations);if(!old.paths.size){results.push({file,schema:"(reported site)",inputs:0,identical:0,validInputs:0,differences:[],skipped:"site is not a top-level schema binding; no application code executed"});continue}for(let[name,paths]of old.paths){let temp=mkdtempSync(join7(tmpdir(),"uptide-behavior-"));try{writeFileSync3(join7(temp,"probe.mjs"),BEHAVIOR_PROBE),writeFileSync3(join7(temp,"input.json"),JSON.stringify({before:schemaGraph(root,file,name,originals),after:schemaGraph(root,file,name,new Map),name,paths,sites:locations.map(loc=>({site:`${file}:${loc.line}:${loc.column}`,paths:schemaSlices(before,[loc]).paths.get(name)??[]})).filter(s=>s.paths.length),resolveFrom:join7(root,file)}));let out=execFileSync(process.execPath,[Number(process.versions.node.split(".")[0])>=23?"--permission":"--experimental-permission","--allow-fs-read=*","--no-warnings",join7(temp,"probe.mjs"),join7(temp,"input.json")],{timeout:15e3,maxBuffer:1024*1024,encoding:"utf8",env:{PATH:process.env.PATH??"",HOME:temp,TZ:"UTC"},stdio:["ignore","pipe","pipe"]});results.push({file,schema:name,...JSON.parse(out.trim().split(`
386
- `).at(-1)??"{}")})}catch(e){results.push({file,schema:name,inputs:0,identical:0,validInputs:0,differences:[],skipped:e instanceof Error?e.message.slice(0,300):String(e)})}finally{rmSync(temp,{recursive:!0,force:!0})}}}return results}async function generateClients(root,workspaces){let out=[];for(let workspace of workspaces){let dir=join8(root,workspace),schema=prismaSchema(dir);if(!schema)continue;let bin=findBin(dir,root,"prisma");if(!bin)continue;let args=["generate","--schema",relative3(dir,schema)],result=await command(dir,bin,args,18e4,{CHECKPOINT_DISABLE:"1",PRISMA_HIDE_UPDATE_MESSAGE:"1",PRISMA_GENERATE_SKIP_AUTOINSTALL:"true"});out.push({workspace,command:`prisma ${args.join(" ")}`,status:result.timeout?"timeout":result.code===0?"generated":"failed",...result.code===0&&!result.timeout?{}:{output:result.output.slice(-2e3)}})}return out}function prismaSchema(dir){try{let manifest2=JSON.parse(readFileSync8(join8(dir,"package.json"),"utf8"));if(manifest2.prisma?.schema){let named=join8(dir,manifest2.prisma.schema);return existsSync5(named)?named:void 0}}catch{return}let conventional=join8(dir,"prisma/schema.prisma");return existsSync5(conventional)?conventional:void 0}function findBin(dir,root,name){for(let at=dir;;at=dirname5(at)){let bin=join8(at,"node_modules/.bin",name);if(existsSync5(bin))return bin;if(at===root||dirname5(at)===at)return}}var PIN_RULE="api-version-pin";async function pinCurrentApi(options,services,originOf2){let started=Date.now(),root=realpathSync5(resolve4(options.cwd));if(options.only!=="stripe")throw new UptideError("ANALYSIS_FAILED","--pin-current-api applies to stripe only");let tool=options.tool??uptideVersionInfo(),adapter=createTypescriptAdapter(),all=workspacePackagesOf(root),workspaces=all.filter(w=>installedVersion(root,w)!==void 0||importedByText(root,w,["stripe"],all).length>0),installed2=workspaces.map(w=>installedVersion(root,w)).filter(v=>v!==void 0).sort(compareVersions)[0];if(!installed2)throw new UptideError("PACKAGE_NOT_INSTALLED","stripe is not installed");let sdkDir=workspaces.map(w=>adapter.installedPackageDir({dir:join9(root,w)},"stripe")).find(d=>d!==void 0);if(!sdkDir)throw new UptideError("PACKAGE_NOT_INSTALLED","stripe is not installed");let apiVersion=sdkApiVersion(sdkDir),branch=`uptide/stripe-pin-${apiVersion}`,read=file=>{try{return readFileSync9(join9(root,file),"utf8")}catch{return}},findings=[];for(let workspace of workspaces){let repo={dir:join9(root,workspace)},surface=await progress(options.onProgress,{phase:"usages",workspace},()=>adapter.installedSurface(repo,"stripe",installed2));if(!surface)continue;let scan=await adapter.findUsages(repo,"stripe",surface),sites2=unpinnedClients(scan.usages,file=>read(join9(workspace,file)));for(let site of sites2){if(site.file.startsWith(".."))continue;let file=join9(workspace,site.file);if(findings.some(f=>f.usage.file===file&&f.usage.line===site.line))continue;let path="Stripe.StripeConfig#apiVersion";findings.push({change:{package:"stripe",from:installed2,to:installed2,path,kind:"type",severity:"breaking",source:"pack",confidence:1,before:apiVersion,after:apiVersion},usage:{...site,file,symbolPath:path,access:"construct",via:"direct"},severity:"breaking",confidence:1,fixability:"mechanical",reason:`created without apiVersion: speaks ${apiVersion}, the SDK default, without saying so`,rule:PIN_RULE})}}if(findings.length===0)throw new UptideError("ANALYSIS_FAILED","every Stripe client already sets apiVersion; nothing to pin");let affected=[...new Set(findings.map(f=>f.usage.file))],verify=(detail,work)=>progress(options.onProgress,{phase:"verify",package:"stripe",detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),generated=await verify("generate",()=>(services.generate??generateClients)(root,workspaces)),baseline=await verify(`types (${names(workspaces)})`,()=>services.diagnostics(root,workspaces)),ownerOf=file=>workspaces.filter(w=>w==="."||file.startsWith(`${w}/`)).sort((a,b)=>b.length-a.length)[0],edited=workspaces.filter(w=>affected.some(f=>ownerOf(f)===w)),baselineTests=await verify(`tests (${names(edited)})`,()=>services.tests(root,edited,options.testTimeoutMs,affected,{withServices:options.withServices===!0}));if(git(root,"status","--porcelain","--untracked-files=all"))throw new Error("baseline tests modified the working tree; review their changes before fixing");let baselineLint=await(services.lint??lintFiles)(root,affected),baseHead=git(root,"rev-parse","HEAD");git(root,"switch","-c",branch);let sites=[],changed=new Set;for(let finding of[...findings].sort((a,b)=>a.usage.file.localeCompare(b.usage.file)||b.usage.line-a.usage.line)){let file=safeFile(root,finding.usage.file),original=readFileSync9(file,"utf8"),pinned=pinClient(original,finding.usage.line,apiVersion);if(pinned===void 0){sites.push({finding,outcome:"manual",reason:"the client options are built elsewhere; add apiVersion there",rule:PIN_RULE});continue}writeFileSync4(file,pinned),changed.add(file),sites.push({finding,outcome:"mechanical",reason:`apiVersion: '${apiVersion}' added, the version this SDK already defaults to`,diff:editDiff(original,pinned),rule:PIN_RULE})}commit(root,`fix(stripe): pin the API version to ${apiVersion}`,[...changed]);let{tests:ran,after,lint,formatted}=await settle({root,pack:stripePack,context:{from:installed2,to:installed2,includeDeprecated:!1},workspaces,testWorkspaces:edited,sites,followed:[],affected,from:installed2,target:installed2,baselineLint,services,withServices:options.withServices===!0,...options.onProgress?{onProgress:options.onProgress}:{},...options.testTimeoutMs?{testTimeoutMs:options.testTimeoutMs}:{}}),tests=markPreexisting(ran,baselineTests),pending=git(root,"status","--porcelain","--untracked-files=all"),newErrors=newDiagnostics(baseline,after,gitLineMoves(root,baseHead)),unverified=typeResolutionFailure(root,baseline),result={repo:root,package:"stripe",from:installed2,target:installed2,mode:"pin",apiVersion,...generated.length?{generated}:{},...tool,verifiedAt:new Date().toISOString(),head:git(root,"rev-parse","HEAD"),verifiedTree:git(root,"rev-parse","HEAD^{tree}"),baseHead,changedFiles:git(root,"diff","--name-only",baseHead,"HEAD").split(`
387
- `).filter(Boolean),branch,sites,verification:{baseline,target:baseline,after,newErrors,baselineTests,tests,...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},baselineHealth:baselineHealth(baseline,baselineTests),passed:!unverified&&newErrors.length===0&&!pending&&tests.every(t=>t.status==="passed"||t.status==="missing"||t.preexisting)&&lint.every(l=>l.status!=="failed")},llm:{inputTokens:0,outputTokens:0,costUsd:0,available:!1,disabled:!0},timingMs:Date.now()-started,prBody:join9(root,".uptide/pr-body.md"),notes:[`stripe stays at ${installed2}; its default API version is ${apiVersion}, now explicit on every client.`,...pending?["Tests/install left uncommitted files; review them before publication."]:[]]},remote=originOf2(root);return remote&&(result.remote=remote),mkdirSync(dirname6(result.prBody),{recursive:!0}),writeFileSync4(result.prBody,prBody(result)),writeFileSync4(join9(root,".uptide/report.json"),JSON.stringify(result,null,2)),result}function installedVersion(root,workspace){let manifest2=readManifest3(join9(root,workspace));if(!["dependencies","devDependencies","peerDependencies","optionalDependencies"].map(s=>manifest2?.[s]?.stripe).find(v=>typeof v=="string"))return;let dir=createTypescriptAdapter().installedPackageDir({dir:join9(root,workspace)},"stripe");return dir?readManifest3(dir)?.version:void 0}function planTasks(sites,context,diagnostics2=[]){let manual=sites.filter(site=>site.outcome==="manual");if(!manual.length)return[];let rule=[...new Set(manual.map(site=>site.rule??"other"))].join(", "),helpers=(context.helpers??[]).map(h=>h.file),declarations=site=>{let u=site.finding.usage;return diagnostics2.filter(d=>d.file===u.file&&(d.line===u.line||d.message===u.compileError)).flatMap(d=>(d.relatedDeclarations??[]).map(r=>r.file)).filter(f=>!f.split(/[\\/]/).includes("node_modules"))};return[{id:rule,rule,sites:manual,files:[...new Set([...manual.flatMap(s=>[s.finding.usage.file,...declarations(s)]),...helpers])].sort()}]}var READ_TOOLS=new Set(["Read","Grep","Glob"]),CHECK_TOOLS=["check_types","run_lint","run_tests"].map(n=>`mcp__uptide__${n}`);function guard(root,allowed,tool,input){if(CHECK_TOOLS.includes(tool)||tool==="StructuredOutput")return;if(!READ_TOOLS.has(tool)&&tool!=="Edit"&&tool!=="Write")return`${tool} is not available in this task`;let raw=input.file_path??input.path??".";if(typeof raw!="string")return`${tool} needs a path`;let rel=relative4(root,resolve5(root,raw));if(rel.startsWith("..")||isAbsolute2(rel))return`${raw} is outside the repository`;let parts=rel.split(/[\\/]/);if(parts.includes(".git"))return"git metadata is off limits";if(rel)try{let existing=resolve5(root,rel);for(;existing!==root&&!lstatSync(existing,{throwIfNoEntry:!1});)existing=dirname7(existing);if(existing!==root){let actual=realpathSync6(safeFile(root,relative4(root,existing))),resolvedParts=relative4(realpathSync6(root),actual).split(/[\\/]/);if(resolvedParts.includes(".git"))return"git metadata is off limits";if(!READ_TOOLS.has(tool)&&resolvedParts.includes("node_modules"))return"node_modules is read-only"}}catch(err){return err.message}if(!READ_TOOLS.has(tool)&&!allowed.includes(rel)&&!(tool==="Write"&&!existsSync6(join10(root,rel))&&!parts.includes("node_modules")))return`${rel} is not one of this task's files (${allowed.join(", ")}); say what it needs in couldNotDo`}function changedPaths(root){let list22=(...args)=>git(root,...args).split(`
388
- `).filter(Boolean);return{modified:list22("diff","--name-only","--no-renames","HEAD"),added:list22("ls-files","--others","--exclude-standard")}}var ESCAPES=/@ts-ignore|@ts-nocheck|@ts-expect-error|\bas any\b|\bas unknown as\b/;function declaredFiles(root,summary){return(summary?.newFiles??[]).map(f=>relative4(root,resolve5(root,f)).split("\\").join("/"))}function editRules(root,task,summary){let{modified,added}=changedPaths(root),outside=modified.filter(f=>!task.files.includes(f));if(outside.length)return`edited files outside the task: ${outside.join(", ")}`;let declared=new Set(declaredFiles(root,summary)),undeclared=added.filter(f=>!declared.has(f)&&!task.files.includes(f));if(undeclared.length)return`created files it did not declare: ${undeclared.join(", ")}`;let silenced=[...git(root,"diff","--no-color","-U0","HEAD").split(`
389
- `).filter(l=>l.startsWith("+")&&!l.startsWith("+++")),...added.flatMap(f=>readFileSync10(join10(root,f),"utf8").split(`
390
- `))].find(l=>ESCAPES.test(l));if(silenced)return`silences the compiler: ${silenced.replace(/^\+/,"").trim()}`}function conventions(root,workspaces,files2){let facts=[];for(let workspace of workspaces){let path=join10(root,workspace,"tsconfig.json");if(!existsSync6(path))continue;let{config}=import_ts_morph4.ts.readConfigFile(path,import_ts_morph4.ts.sys.readFile),o=config?.compilerOptions??{},parts=[o.module&&`module ${o.module}`,o.moduleResolution&&`moduleResolution ${o.moduleResolution}`,o.baseUrl&&`baseUrl ${o.baseUrl}`,o.paths&&`paths ${Object.keys(o.paths).join(", ")}`,o.allowImportingTsExtensions&&"allowImportingTsExtensions",config?.extends&&`extends ${config.extends}`].filter(Boolean);parts.length&&facts.push(`${join10(workspace,"tsconfig.json")}: ${parts.join("; ")}`)}let styles={js:0,ts:0,none:0,alias:0};for(let file of files2)if(existsSync6(join10(root,file)))for(let[,spec=""]of readFileSync10(join10(root,file),"utf8").matchAll(/from\s+['"]([^'"]+)['"]/g))if(/^[@~#]\//.test(spec))styles.alias++;else if(spec.startsWith("."))spec.endsWith(".js")?styles.js++:/\.tsx?$/.test(spec)?styles.ts++:styles.none++;else continue;return facts.push(`imports in the task's files: ${styles.none} relative without extension, ${styles.js} ending in .js, ${styles.ts} ending in .ts, ${styles.alias} through a path alias`),facts}var SUMMARY_SCHEMA={type:"object",additionalProperties:!1,required:["changed","newFiles","decisions","assumptions","couldNotDo"],properties:{changed:{type:"array",items:{type:"object",additionalProperties:!1,required:["file","what"],properties:{file:{type:"string"},what:{type:"string"}}}},newFiles:{type:"array",items:{type:"string"}},decisions:{type:"array",items:{type:"string"}},assumptions:{type:"array",items:{type:"string"}},couldNotDo:{type:"array",items:{type:"string"}}}};function taskPrompt(input){let{pack,context,task}=input,ruleIds=[...new Set(task.sites.map(s=>s.rule??task.rule))],rules=pack.rules.filter(r=>ruleIds.includes(r.id)),sources=pack.meta?.sources,changelog=(pack.reviewSections?.(context,ruleIds)??[]).flatMap(s=>[`${s.title}:`,...s.lines,...(s.lists??[]).flatMap(l=>[l.summary,...l.items.map(i=>` - ${i}`)])]),decisions=pack.decisions?.(context,ruleIds)??[],site=s=>{let u=s.finding.usage,c=s.finding.change;return[`- ${u.file}:${u.line}:${u.column} \`${u.snippet.trim()}\``,u.compileError&&` compiler: TS${u.compileCode??""} ${u.compileError}`,` change: ${c.path} (${c.kind}, rule ${s.rule??task.rule})${c.after?`, now ${c.after}`:""}`,` why: ${s.finding.reason}`,...(s.finding.details??[]).map(d=>` note: ${d}`)].filter(Boolean).join(`
386
+ `).at(-1)??"{}")})}catch(e){results.push({file,schema:name,inputs:0,identical:0,validInputs:0,differences:[],skipped:e instanceof Error?e.message.slice(0,300):String(e)})}finally{rmSync(temp,{recursive:!0,force:!0})}}}return results}async function fileTransaction(root,edits,verify){let snapshots=new Map,staged=new Map,modes=new Map,createdDirs=[],realRoot=realpathSync5(root),path=file=>{if(!file||isAbsolute2(file)||file.split(/[\\/]/).some(part=>part===".."||part===".git")||file.includes("\0"))throw new Error(`Invalid transaction path: ${file}`);let absolute=resolve4(realRoot,file);if(absolute===realRoot)throw new Error(`Invalid transaction path: ${file}`);for(let part=absolute;part!==realRoot;part=dirname5(part))if(existsSync6(part)&&lstatSync(part).isSymbolicLink())throw new Error(`Transaction refuses symlink: ${file}`);if(!snapshots.has(absolute)){if(existsSync6(absolute)&&!lstatSync(absolute).isFile())throw new Error(`Not a regular file: ${file}`);let before=existsSync6(absolute)?{bytes:readFileSync8(absolute),mode:lstatSync(absolute).mode}:void 0;snapshots.set(absolute,before),staged.set(absolute,before?.bytes),before&&modes.set(absolute,before.mode)}return absolute},ensureParent=file=>{let missing=[];for(let part=dirname5(file);!existsSync6(part);part=dirname5(part))missing.push(part);for(let dir of missing.reverse())mkdirSync(dir),createdDirs.push(dir)};try{for(let edit of edits){let file=path(edit.file),current=staged.get(file);if(edit.kind==="create"){if(current!==void 0)throw new Error(`Create destination exists: ${edit.file}`);staged.set(file,Buffer.from(edit.text))}else{if(current===void 0)throw new Error(`Source file missing: ${edit.file}`);if(edit.kind==="write")staged.set(file,Buffer.from(edit.text));else if(edit.kind==="delete")staged.set(file,void 0);else{let to=path(edit.to);if(staged.get(to)!==void 0)throw new Error(`Rename destination exists: ${edit.to}`);staged.set(to,current);let mode=modes.get(file);mode!==void 0&&modes.set(to,mode),staged.set(file,void 0)}}}}catch(error){return{applied:!1,files:[],reason:String(error)}}try{for(let[file,bytes]of staged)if(bytes===void 0)rmSync2(file,{force:!0});else{ensureParent(file),writeFileSync4(file,bytes);let mode=modes.get(file);mode!==void 0&&chmodSync(file,mode)}if(!await verify())throw new Error("multi-file edit failed verification");return{applied:!0,files:[...staged.keys()].map(file=>relative3(realRoot,file)),reason:"multi-file edit verified"}}catch(error){for(let[file,before]of snapshots)before?(ensureParent(file),writeFileSync4(file,before.bytes),chmodSync(file,before.mode)):rmSync2(file,{force:!0});for(let dir of createdDirs.reverse())rmSync2(dir,{recursive:!0,force:!0});return{applied:!1,files:[],reason:String(error)}}}async function generateClients(root,workspaces){let out=[];for(let workspace of workspaces){let dir=join8(root,workspace),schema=prismaSchema(dir);if(!schema)continue;let bin=findBin(dir,root,"prisma");if(!bin)continue;let args=["generate","--schema",relative4(dir,schema)],result=await command(dir,bin,args,18e4,{CHECKPOINT_DISABLE:"1",PRISMA_HIDE_UPDATE_MESSAGE:"1",PRISMA_GENERATE_SKIP_AUTOINSTALL:"true"});out.push({workspace,command:`prisma ${args.join(" ")}`,status:result.timeout?"timeout":result.code===0?"generated":"failed",...result.code===0&&!result.timeout?{}:{output:result.output.slice(-2e3)}})}return out}function prismaSchema(dir){try{let manifest2=JSON.parse(readFileSync9(join8(dir,"package.json"),"utf8"));if(manifest2.prisma?.schema){let named=join8(dir,manifest2.prisma.schema);return existsSync7(named)?named:void 0}}catch{return}let conventional=join8(dir,"prisma/schema.prisma");return existsSync7(conventional)?conventional:void 0}function findBin(dir,root,name){for(let at=dir;;at=dirname6(at)){let bin=join8(at,"node_modules/.bin",name);if(existsSync7(bin))return bin;if(at===root||dirname6(at)===at)return}}var PIN_RULE="api-version-pin";async function pinCurrentApi(options,services,originOf2){let started=Date.now(),root=realpathSync6(resolve5(options.cwd));if(options.only!=="stripe")throw new UptideError("ANALYSIS_FAILED","--pin-current-api applies to stripe only");let tool=options.tool??uptideVersionInfo(),adapter=createTypescriptAdapter(),all=workspacePackagesOf(root),workspaces=all.filter(w=>installedVersion(root,w)!==void 0||importedByText(root,w,["stripe"],all).length>0),installed2=workspaces.map(w=>installedVersion(root,w)).filter(v=>v!==void 0).sort(compareVersions)[0];if(!installed2)throw new UptideError("PACKAGE_NOT_INSTALLED","stripe is not installed");let sdkDir=workspaces.map(w=>adapter.installedPackageDir({dir:join9(root,w)},"stripe")).find(d=>d!==void 0);if(!sdkDir)throw new UptideError("PACKAGE_NOT_INSTALLED","stripe is not installed");let apiVersion=sdkApiVersion(sdkDir),branch=`uptide/stripe-pin-${apiVersion}`,read=file=>{try{return readFileSync10(join9(root,file),"utf8")}catch{return}},findings=[];for(let workspace of workspaces){let repo={dir:join9(root,workspace)},surface=await progress(options.onProgress,{phase:"usages",workspace},()=>adapter.installedSurface(repo,"stripe",installed2));if(!surface)continue;let scan=await adapter.findUsages(repo,"stripe",surface),sites2=unpinnedClients(scan.usages,file=>read(join9(workspace,file)));for(let site of sites2){if(site.file.startsWith(".."))continue;let file=join9(workspace,site.file);if(findings.some(f=>f.usage.file===file&&f.usage.line===site.line))continue;let path="Stripe.StripeConfig#apiVersion";findings.push({change:{package:"stripe",from:installed2,to:installed2,path,kind:"type",severity:"breaking",source:"pack",confidence:1,before:apiVersion,after:apiVersion},usage:{...site,file,symbolPath:path,access:"construct",via:"direct"},severity:"breaking",confidence:1,fixability:"mechanical",reason:`created without apiVersion: speaks ${apiVersion}, the SDK default, without saying so`,rule:PIN_RULE})}}if(findings.length===0)throw new UptideError("ANALYSIS_FAILED","every Stripe client already sets apiVersion; nothing to pin");let affected=[...new Set(findings.map(f=>f.usage.file))],verify=(detail,work)=>progress(options.onProgress,{phase:"verify",package:"stripe",detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),generated=await verify("generate",()=>(services.generate??generateClients)(root,workspaces)),baseline=await verify(`types (${names(workspaces)})`,()=>services.diagnostics(root,workspaces)),ownerOf=file=>workspaces.filter(w=>w==="."||file.startsWith(`${w}/`)).sort((a,b)=>b.length-a.length)[0],edited=workspaces.filter(w=>affected.some(f=>ownerOf(f)===w)),baselineTests=await verify(`tests (${names(edited)})`,()=>services.tests(root,edited,options.testTimeoutMs,affected,{withServices:options.withServices===!0}));if(git(root,"status","--porcelain","--untracked-files=all"))throw new Error("baseline tests modified the working tree; review their changes before fixing");let baselineLint=await(services.lint??lintFiles)(root,affected),baseHead=git(root,"rev-parse","HEAD");git(root,"switch","-c",branch);let sites=[],changed=new Set;for(let finding of[...findings].sort((a,b)=>a.usage.file.localeCompare(b.usage.file)||b.usage.line-a.usage.line)){let file=safeFile(root,finding.usage.file),original=readFileSync10(file,"utf8"),pinned=pinClient(original,finding.usage.line,apiVersion);if(pinned===void 0){sites.push({finding,outcome:"manual",reason:"the client options are built elsewhere; add apiVersion there",rule:PIN_RULE});continue}writeFileSync5(file,pinned),changed.add(file),sites.push({finding,outcome:"mechanical",reason:`apiVersion: '${apiVersion}' added, the version this SDK already defaults to`,diff:editDiff(original,pinned),rule:PIN_RULE})}commit(root,`fix(stripe): pin the API version to ${apiVersion}`,[...changed]);let{tests:ran,after,lint,formatted}=await settle({root,pack:stripePack,context:{from:installed2,to:installed2,includeDeprecated:!1},workspaces,testWorkspaces:edited,sites,followed:[],affected,from:installed2,target:installed2,baselineLint,services,withServices:options.withServices===!0,...options.onProgress?{onProgress:options.onProgress}:{},...options.testTimeoutMs?{testTimeoutMs:options.testTimeoutMs}:{}}),tests=markPreexisting(ran,baselineTests),pending=git(root,"status","--porcelain","--untracked-files=all"),newErrors=newDiagnostics(baseline,after,gitLineMoves(root,baseHead)),unverified=typeResolutionFailure(root,baseline),result={repo:root,package:"stripe",from:installed2,target:installed2,mode:"pin",apiVersion,...generated.length?{generated}:{},...tool,verifiedAt:new Date().toISOString(),head:git(root,"rev-parse","HEAD"),verifiedTree:git(root,"rev-parse","HEAD^{tree}"),baseHead,changedFiles:git(root,"diff","--name-only",baseHead,"HEAD").split(`
387
+ `).filter(Boolean),branch,sites,verification:{baseline,target:baseline,after,newErrors,baselineTests,tests,...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},baselineHealth:baselineHealth(baseline,baselineTests),passed:!unverified&&newErrors.length===0&&!pending&&tests.every(t=>t.status==="passed"||t.status==="missing"||t.preexisting)&&lint.every(l=>l.status!=="failed")},llm:{inputTokens:0,outputTokens:0,costUsd:0,available:!1,disabled:!0},timingMs:Date.now()-started,prBody:join9(root,".uptide/pr-body.md"),notes:[`stripe stays at ${installed2}; its default API version is ${apiVersion}, now explicit on every client.`,...pending?["Tests/install left uncommitted files; review them before publication."]:[]]},remote=originOf2(root);return remote&&(result.remote=remote),mkdirSync2(dirname7(result.prBody),{recursive:!0}),writeFileSync5(result.prBody,prBody(result)),writeFileSync5(join9(root,".uptide/report.json"),JSON.stringify(result,null,2)),result}function installedVersion(root,workspace){let manifest2=readManifest3(join9(root,workspace));if(!["dependencies","devDependencies","peerDependencies","optionalDependencies"].map(s=>manifest2?.[s]?.stripe).find(v=>typeof v=="string"))return;let dir=createTypescriptAdapter().installedPackageDir({dir:join9(root,workspace)},"stripe");return dir?readManifest3(dir)?.version:void 0}function planTasks(sites,context,diagnostics2=[]){let manual=sites.filter(site=>site.outcome==="manual");if(!manual.length)return[];let rule=[...new Set(manual.map(site=>site.rule??"other"))].join(", "),helpers=(context.helpers??[]).map(h=>h.file),declarations=site=>{let u=site.finding.usage;return diagnostics2.filter(d=>d.file===u.file&&(d.line===u.line||d.message===u.compileError)).flatMap(d=>(d.relatedDeclarations??[]).map(r=>r.file)).filter(f=>!f.split(/[\\/]/).includes("node_modules"))};return[{id:rule,rule,sites:manual,files:[...new Set([...manual.flatMap(s=>[s.finding.usage.file,...declarations(s)]),...helpers])].sort()}]}var READ_TOOLS=new Set(["Read","Grep","Glob"]),CHECK_TOOLS=["check_types","run_lint","run_tests"].map(n=>`mcp__uptide__${n}`);function guard(root,allowed,tool,input){if(CHECK_TOOLS.includes(tool)||tool==="StructuredOutput")return;if(!READ_TOOLS.has(tool)&&tool!=="Edit"&&tool!=="Write")return`${tool} is not available in this task`;let raw=input.file_path??input.path??".";if(typeof raw!="string")return`${tool} needs a path`;let rel=relative5(root,resolve6(root,raw));if(rel.startsWith("..")||isAbsolute3(rel))return`${raw} is outside the repository`;let parts=rel.split(/[\\/]/);if(parts.includes(".git"))return"git metadata is off limits";if(rel)try{let existing=resolve6(root,rel);for(;existing!==root&&!lstatSync2(existing,{throwIfNoEntry:!1});)existing=dirname8(existing);if(existing!==root){let actual=realpathSync7(safeFile(root,relative5(root,existing))),resolvedParts=relative5(realpathSync7(root),actual).split(/[\\/]/);if(resolvedParts.includes(".git"))return"git metadata is off limits";if(!READ_TOOLS.has(tool)&&resolvedParts.includes("node_modules"))return"node_modules is read-only"}}catch(err){return err.message}if(!READ_TOOLS.has(tool)&&!allowed.includes(rel)&&!(tool==="Write"&&!existsSync8(join10(root,rel))&&!parts.includes("node_modules")))return`${rel} is not one of this task's files (${allowed.join(", ")}); say what it needs in couldNotDo`}function changedPaths(root){let list22=(...args)=>git(root,...args).split(`
388
+ `).filter(Boolean);return{modified:list22("diff","--name-only","--no-renames","HEAD"),added:list22("ls-files","--others","--exclude-standard")}}var ESCAPES=/@ts-ignore|@ts-nocheck|@ts-expect-error|\bas any\b|\bas unknown as\b/;function declaredFiles(root,summary){return(summary?.newFiles??[]).map(f=>relative5(root,resolve6(root,f)).split("\\").join("/"))}function editRules(root,task,summary){let{modified,added}=changedPaths(root),outside=modified.filter(f=>!task.files.includes(f));if(outside.length)return`edited files outside the task: ${outside.join(", ")}`;let declared=new Set(declaredFiles(root,summary)),undeclared=added.filter(f=>!declared.has(f)&&!task.files.includes(f));if(undeclared.length)return`created files it did not declare: ${undeclared.join(", ")}`;let silenced=[...git(root,"diff","--no-color","-U0","HEAD").split(`
389
+ `).filter(l=>l.startsWith("+")&&!l.startsWith("+++")),...added.flatMap(f=>readFileSync11(join10(root,f),"utf8").split(`
390
+ `))].find(l=>ESCAPES.test(l));if(silenced)return`silences the compiler: ${silenced.replace(/^\+/,"").trim()}`}function conventions(root,workspaces,files2){let facts=[];for(let workspace of workspaces){let path=join10(root,workspace,"tsconfig.json");if(!existsSync8(path))continue;let{config}=import_ts_morph4.ts.readConfigFile(path,import_ts_morph4.ts.sys.readFile),o=config?.compilerOptions??{},parts=[o.module&&`module ${o.module}`,o.moduleResolution&&`moduleResolution ${o.moduleResolution}`,o.baseUrl&&`baseUrl ${o.baseUrl}`,o.paths&&`paths ${Object.keys(o.paths).join(", ")}`,o.allowImportingTsExtensions&&"allowImportingTsExtensions",config?.extends&&`extends ${config.extends}`].filter(Boolean);parts.length&&facts.push(`${join10(workspace,"tsconfig.json")}: ${parts.join("; ")}`)}let styles={js:0,ts:0,none:0,alias:0};for(let file of files2)if(existsSync8(join10(root,file)))for(let[,spec=""]of readFileSync11(join10(root,file),"utf8").matchAll(/from\s+['"]([^'"]+)['"]/g))if(/^[@~#]\//.test(spec))styles.alias++;else if(spec.startsWith("."))spec.endsWith(".js")?styles.js++:/\.tsx?$/.test(spec)?styles.ts++:styles.none++;else continue;return facts.push(`imports in the task's files: ${styles.none} relative without extension, ${styles.js} ending in .js, ${styles.ts} ending in .ts, ${styles.alias} through a path alias`),facts}var SUMMARY_SCHEMA={type:"object",additionalProperties:!1,required:["changed","newFiles","decisions","assumptions","couldNotDo"],properties:{changed:{type:"array",items:{type:"object",additionalProperties:!1,required:["file","what"],properties:{file:{type:"string"},what:{type:"string"}}}},newFiles:{type:"array",items:{type:"string"}},decisions:{type:"array",items:{type:"string"}},assumptions:{type:"array",items:{type:"string"}},couldNotDo:{type:"array",items:{type:"string"}}}};function taskPrompt(input){let{pack,context,task}=input,ruleIds=[...new Set(task.sites.map(s=>s.rule??task.rule))],rules=pack.rules.filter(r=>ruleIds.includes(r.id)),sources=pack.meta?.sources,changelog=(pack.reviewSections?.(context,ruleIds)??[]).flatMap(s=>[`${s.title}:`,...s.lines,...(s.lists??[]).flatMap(l=>[l.summary,...l.items.map(i=>` - ${i}`)])]),decisions=pack.decisions?.(context,ruleIds)??[],site=s=>{let u=s.finding.usage,c=s.finding.change;return[`- ${u.file}:${u.line}:${u.column} \`${u.snippet.trim()}\``,u.compileError&&` compiler: TS${u.compileCode??""} ${u.compileError}`,` change: ${c.path} (${c.kind}, rule ${s.rule??task.rule})${c.after?`, now ${c.after}`:""}`,` why: ${s.finding.reason}`,...(s.finding.details??[]).map(d=>` note: ${d}`)].filter(Boolean).join(`
391
391
  `)},helpers=(context.helpers??[]).map(h=>`- ${h.name} in ${h.file} (${Object.entries(h.specifiers).map(([w,s])=>`${w}: ${s}`).join("; ")})`);return[`Upgrade ${pack.name} from ${context.from} to ${context.to} in this repository: the dependency is already installed at the new version. Your task covers these upgrade rules: ${task.rule}.`,"","Sites Uptide found for this change:",...task.sites.map(site),"",`Files you may edit: ${task.files.join(", ")}. You may create new files; list each one in newFiles. Edits anywhere else are refused.`,...rules.flatMap(rule=>["",`What the pack knows about ${rule.id}: ${rule.guide}`]),...changelog.length?["","Changelog entries:",...changelog]:[],...sources?.length?["","Sources:",...sources.map(s=>`- ${s.title}: ${s.url}`)]:[],...decisions.length?["","Decisions that belong to the repository's owner. Make a reasonable choice, and say which in decisions:",...decisions]:[],...helpers.length?["","Helpers already in the repository, which you may use:",...helpers]:[],"","Repository conventions Uptide detected; follow what the code already does:",...input.conventions.map(c=>`- ${c}`),"","Read independent files together and make related edits together. Check types and lint before running tests: check_types shows errors new since the task started and those left in the task's files; run_lint first formats the changed files; run_tests checks the task's files. Finish edits, including comments, before the final checks, and repeat tests only after further edits. These are the repository's own checks and the same judges your edits face when you finish. There is no shell, no network, no installs and no git: say in couldNotDo what would need them. Do not silence the compiler (@ts-ignore, @ts-expect-error, as any, as unknown as). Keep behaviour the same except where the upgrade changes it; where data the code relied on may now be missing, handle that explicitly rather than letting it pass silently.","","Finish with the structured summary: what you changed, the decisions you made and the assumptions behind them, and anything you could not do."].join(`
392
392
  `)}var tail=(text,max)=>text.length>max?`\u2026${text.slice(-max)}`:text;async function assistWithSdk(input){let{root,workspaces,pack,context}=input,runner=input.runner??sdkRunner,current=input.diagnostics(),tasks=planTasks(input.sites,context,current),records=[],format=input.format??(files2=>formatFiles(root,files2)),lint=async files2=>(await format(files2),(input.lint??((f,b)=>lintFiles(root,f,b)))(files2,input.baselineLint)),list22=(...args)=>git(root,...args).split(`
393
- `).filter(Boolean),head2=()=>git(root,"rev-parse","HEAD"),since=base=>[...new Set([...list22("diff","--name-only","--no-renames",base),...list22("ls-files","--others","--exclude-standard")])],reset=to=>{git(root,"reset","--hard","--quiet",to),git(root,"clean","-fd","--quiet")},testCache=new Map,runTests=async(start,files2)=>{let key2=()=>{let hash2=createHash("sha256").update(JSON.stringify([start,[...new Set(files2)].sort()])).update(git(root,"diff","--binary",start));for(let file of list22("ls-files","--others","--exclude-standard").sort())hash2.update(JSON.stringify([file,readFileSync10(join10(root,file)).toString("base64")]));return hash2.digest("hex")},before=key2(),cached=testCache.get(before);if(cached)return cached;let results=await input.tests(files2);return key2()===before&&testCache.set(before,results),results},testsAt=async(at,files2)=>{let added=list22("diff","--name-only","--diff-filter=A",at,"HEAD");git(root,"checkout","--quiet",at,"--",".");for(let file of added)rmSync2(join10(root,file),{force:!0});try{return await input.tests(files2)}finally{git(root,"checkout","--quiet","HEAD","--",".")}},judge=async(start,before,after,files2)=>{let fresh=newDiagnostics(before,after,gitLineMoves(root,start));if(fresh.length)return`new type errors: ${fresh.slice(0,5).map(d=>`${d.file}:${d.line} TS${d.code}`).join(", ")}${fresh.length>5?` and ${fresh.length-5} more`:""}`;let changed=since(start),failedLint=(await lint(changed)).filter(l=>l.status==="failed");if(failedLint.length)return`lint fails: ${failedLint.map(l=>l.tool).join(", ")}`;let related=[...new Set([...files2,...changed])],tests=await runTests(start,related);tests.some(t=>t.status==="failed"||t.status==="timeout")&&(tests=markPreexisting(tests,await testsAt(start,related)));let failing=tests.filter(t=>t.status!=="passed"&&t.status!=="missing"&&!t.preexisting);if(failing.length)return`tests fail: ${failing.map(t=>`${t.workspace} (${t.status})`).join(", ")}`},accept=p=>{p.record.gate="accepted";let left=new Set(p.after.map(unplacedKey));for(let s of p.task.sites){let u=s.finding.usage,own=p.before.find(d=>d.file===u.file&&(d.line===u.line||d.message===u.compileError));if(own?left.has(unplacedKey(own)):!p.touched.includes(u.file)){s.reason=`SDK agent left this site${p.said?`; agent: ${p.said}`:""}`;continue}s.outcome="agent",s.reason=p.run.summary?.changed.find(c=>c.file===u.file)?.what??"migrated by the SDK agent",s.editedFiles=p.touched,p.record.fixed++}},refuse=(p,why2)=>{p.record.gate="rejected",p.record.rejection=why2;for(let s of p.task.sites)s.reason=`SDK agent rejected: ${why2}${p.said?`; agent: ${p.said}`:""}`},show=d=>`${d.file}:${d.line}:${d.column} TS${d.code} ${d.message}`;for(let task of tasks){let share=input.maxCostUsd;if(share<.02){for(let s of task.sites)s.reason=`${s.reason}; not attempted: --max-cost reached`;continue}let maxTurns=Math.max(10,Math.min(60,Math.round(share*100))),before=current,taskStart=head2(),checks={types:()=>{let now=input.diagnostics(),fresh=newDiagnostics(before,now,gitLineMoves(root,taskStart)),left=now.filter(d=>task.files.includes(d.file));return[`${fresh.length} new type error(s) since the task started`,...fresh.slice(0,40).map(show),`${left.length} error(s) in the task's files`,...left.slice(0,40).map(show)].join(`
393
+ `).filter(Boolean),head2=()=>git(root,"rev-parse","HEAD"),since=base=>[...new Set([...list22("diff","--name-only","--no-renames",base),...list22("ls-files","--others","--exclude-standard")])],reset=to=>{git(root,"reset","--hard","--quiet",to),git(root,"clean","-fd","--quiet")},testCache=new Map,runTests=async(start,files2)=>{let key2=()=>{let hash2=createHash("sha256").update(JSON.stringify([start,[...new Set(files2)].sort()])).update(git(root,"diff","--binary",start));for(let file of list22("ls-files","--others","--exclude-standard").sort())hash2.update(JSON.stringify([file,readFileSync11(join10(root,file)).toString("base64")]));return hash2.digest("hex")},before=key2(),cached=testCache.get(before);if(cached)return cached;let results=await input.tests(files2);return key2()===before&&testCache.set(before,results),results},testsAt=async(at,files2)=>{let added=list22("diff","--name-only","--diff-filter=A",at,"HEAD");git(root,"checkout","--quiet",at,"--",".");for(let file of added)rmSync3(join10(root,file),{force:!0});try{return await input.tests(files2)}finally{git(root,"checkout","--quiet","HEAD","--",".")}},judge=async(start,before,after,files2)=>{let fresh=newDiagnostics(before,after,gitLineMoves(root,start));if(fresh.length)return`new type errors: ${fresh.slice(0,5).map(d=>`${d.file}:${d.line} TS${d.code}`).join(", ")}${fresh.length>5?` and ${fresh.length-5} more`:""}`;let changed=since(start),failedLint=(await lint(changed)).filter(l=>l.status==="failed");if(failedLint.length)return`lint fails: ${failedLint.map(l=>l.tool).join(", ")}`;let related=[...new Set([...files2,...changed])],tests=await runTests(start,related);tests.some(t=>t.status==="failed"||t.status==="timeout")&&(tests=markPreexisting(tests,await testsAt(start,related)));let failing=tests.filter(t=>t.status!=="passed"&&t.status!=="missing"&&!t.preexisting);if(failing.length)return`tests fail: ${failing.map(t=>`${t.workspace} (${t.status})`).join(", ")}`},accept=p=>{p.record.gate="accepted";let left=new Set(p.after.map(unplacedKey));for(let s of p.task.sites){let u=s.finding.usage,own=p.before.find(d=>d.file===u.file&&(d.line===u.line||d.message===u.compileError));if(own?left.has(unplacedKey(own)):!p.touched.includes(u.file)){s.reason=`SDK agent left this site${p.said?`; agent: ${p.said}`:""}`;continue}s.outcome="agent",s.reason=p.run.summary?.changed.find(c=>c.file===u.file)?.what??"migrated by the SDK agent",s.editedFiles=p.touched,p.record.fixed++}},refuse=(p,why2)=>{p.record.gate="rejected",p.record.rejection=why2;for(let s of p.task.sites)s.reason=`SDK agent rejected: ${why2}${p.said?`; agent: ${p.said}`:""}`},show=d=>`${d.file}:${d.line}:${d.column} TS${d.code} ${d.message}`;for(let task of tasks){let share=input.maxCostUsd;if(share<.02){for(let s of task.sites)s.reason=`${s.reason}; not attempted: --max-cost reached`;continue}let maxTurns=Math.max(10,Math.min(60,Math.round(share*100))),before=current,taskStart=head2(),checks={types:()=>{let now=input.diagnostics(),fresh=newDiagnostics(before,now,gitLineMoves(root,taskStart)),left=now.filter(d=>task.files.includes(d.file));return[`${fresh.length} new type error(s) since the task started`,...fresh.slice(0,40).map(show),`${left.length} error(s) in the task's files`,...left.slice(0,40).map(show)].join(`
394
394
  `)},lint:async()=>{let results=await lint(since(taskStart));return results.length?results.map(r=>`${r.tool}: ${r.status}
395
395
  ${tail(r.output,2e3)}`).join(`
396
396
  `):"no lint configured for these files, or nothing changed"},tests:async()=>{let results=await runTests(taskStart,[...new Set([...task.files,...since(taskStart)])]);return results.length?["Tests related to the task's files. A failure may predate the task: the gate compares with the start.",...results.map(t=>`${t.workspace}: ${t.status}
397
397
  ${tail(t.output,3e3)}`)].join(`
398
- `):"no tests relate to these files"}},prompt=taskPrompt({pack,context,task,conventions:conventions(root,workspaces,task.files)}),run2,error;try{run2=await runner({root,task,prompt,model:input.model,maxBudgetUsd:share,maxTurns,checks})}catch(err){error=err.message,run2={turns:0,inputTokens:0,outputTokens:0,costUsd:0,durationMs:0,toolCalls:{}}}let{modified,added}=changedPaths(root),declared=declaredFiles(root,run2.summary),discardedFiles=run2.summary?added.filter(file=>!declared.includes(file)):[];for(let file of discardedFiles)rmSync2(join10(root,file));let touched=[...modified,...added.filter(file=>!discardedFiles.includes(file))],rejection=error??(touched.length?editRules(root,task,run2.summary):void 0),record={id:task.id,rule:task.rule,files:task.files,sites:task.sites.length,fixed:0,...run2,gate:error?"error":touched.length?rejection?"rejected":"accepted":"no-change",...rejection?{rejection}:{},...discardedFiles.length?{discardedFiles}:{}};records.push(record);let said=run2.summary?[...run2.summary.couldNotDo,...run2.summary.decisions].join("; "):run2.stop??"";if(rejection||!touched.length){reset(taskStart);for(let s of task.sites)s.reason=`SDK agent ${rejection?`rejected: ${rejection}`:"made no change"}${said?`; agent: ${said}`:""}`;continue}commit(root,`fix(${pack.name}): ${task.rule} (agent)`,touched.map(f=>join10(root,f))),current=input.diagnostics();let pending={task,run:run2,record,said,before,after:current,touched},verdict=await judge(taskStart,before,current,[...new Set([...task.files,...touched])]);verdict?(reset(taskStart),current=before,refuse(pending,verdict)):accept(pending)}let sum=key2=>records.reduce((n,r)=>n+r[key2],0);return{provider:"anthropic",model:input.model,available:!0,inputTokens:sum("inputTokens"),outputTokens:sum("outputTokens"),costUsd:sum("costUsd"),agent:"sdk",tasks:records}}var loadSdk=()=>import("@anthropic-ai/claude-agent-sdk");async function sdkInstalled(){try{return await loadSdk(),!0}catch{return!1}}var sdkRunner=async({root,task,prompt,model,maxBudgetUsd,maxTurns,checks})=>{let sdk;try{sdk=await loadSdk()}catch{throw new Error("--agent sdk needs @anthropic-ai/claude-agent-sdk installed next to uptide (npm install -g @anthropic-ai/claude-agent-sdk), or use --agent patch")}let text=t=>({content:[{type:"text",text:t}]}),server=sdk.createSdkMcpServer({name:"uptide",tools:[sdk.tool("check_types","The repository's type-check: errors new since the task started, and those in the task's files",{},async()=>text(checks.types())),sdk.tool("run_lint","The repository's lint on the files changed so far",{},async()=>text(await checks.lint())),sdk.tool("run_tests","The repository's tests related to the changed files",{},async()=>text(await checks.tests()))]}),started=Date.now(),toolCalls={},result;try{for await(let message of sdk.query({prompt,options:{cwd:root,model,effort:modelCapabilities("anthropic",model).defaultEffort,maxTurns,maxBudgetUsd,tools:["Read","Grep","Glob","Edit","Write"],allowedTools:["Read","Grep","Glob","Edit","Write",...CHECK_TOOLS],permissionMode:"dontAsk",mcpServers:{uptide:server},hooks:{PreToolUse:[{hooks:[async hook=>{let{tool_name,tool_input}=hook;toolCalls[tool_name]=(toolCalls[tool_name]??0)+1;let reason=guard(root,task.files,tool_name,tool_input??{});return reason?{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:reason}}:{}}]}]},settingSources:[],persistSession:!1,systemPrompt:{type:"preset",preset:"claude_code"},outputFormat:{type:"json_schema",schema:SUMMARY_SCHEMA},env:{PATH:process.env.PATH??"",HOME:process.env.HOME??"",ANTHROPIC_API_KEY:process.env.ANTHROPIC_API_KEY??"",CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:"1",DISABLE_FEEDBACK_COMMAND:"1",CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL:"1",CLAUDE_AGENT_SDK_CLIENT_APP:"uptide"}}}))message.type==="result"&&(result=message)}catch(err){if(!result)throw err}if(!result)throw new Error("the SDK session ended without a result");let usage2=result.usage;return{turns:result.num_turns,inputTokens:(usage2.input_tokens??0)+(usage2.cache_read_input_tokens??0)+(usage2.cache_creation_input_tokens??0),outputTokens:usage2.output_tokens??0,costUsd:result.total_cost_usd,durationMs:Date.now()-started,toolCalls,...result.subtype!=="success"?{stop:result.subtype}:result.structured_output?{summary:result.structured_output}:{}}},defaults={check,install,diagnostics,tests:testWorkspaces};function confirmServices(root,options){if(!options.withServices||options.yes)return;let reach=describeServices(root,workspacePackagesOf(root));if(reach.length!==0)throw new UptideError("SERVICES_NOT_CONFIRMED",`--with-services would run tests against:
398
+ `):"no tests relate to these files"}},prompt=taskPrompt({pack,context,task,conventions:conventions(root,workspaces,task.files)}),run2,error;try{run2=await runner({root,task,prompt,model:input.model,maxBudgetUsd:share,maxTurns,checks})}catch(err){error=err.message,run2={turns:0,inputTokens:0,outputTokens:0,costUsd:0,durationMs:0,toolCalls:{}}}let{modified,added}=changedPaths(root),declared=declaredFiles(root,run2.summary),discardedFiles=run2.summary?added.filter(file=>!declared.includes(file)):[];for(let file of discardedFiles)rmSync3(join10(root,file));let touched=[...modified,...added.filter(file=>!discardedFiles.includes(file))],rejection=error??(touched.length?editRules(root,task,run2.summary):void 0),record={id:task.id,rule:task.rule,files:task.files,sites:task.sites.length,fixed:0,...run2,gate:error?"error":touched.length?rejection?"rejected":"accepted":"no-change",...rejection?{rejection}:{},...discardedFiles.length?{discardedFiles}:{}};records.push(record);let said=run2.summary?[...run2.summary.couldNotDo,...run2.summary.decisions].join("; "):run2.stop??"";if(rejection||!touched.length){reset(taskStart);for(let s of task.sites)s.reason=`SDK agent ${rejection?`rejected: ${rejection}`:"made no change"}${said?`; agent: ${said}`:""}`;continue}commit(root,`fix(${pack.name}): ${task.rule} (agent)`,touched.map(f=>join10(root,f))),current=input.diagnostics();let pending={task,run:run2,record,said,before,after:current,touched},verdict=await judge(taskStart,before,current,[...new Set([...task.files,...touched])]);verdict?(reset(taskStart),current=before,refuse(pending,verdict)):accept(pending)}let sum=key2=>records.reduce((n,r)=>n+r[key2],0);return{provider:"anthropic",model:input.model,available:!0,inputTokens:sum("inputTokens"),outputTokens:sum("outputTokens"),costUsd:sum("costUsd"),agent:"sdk",tasks:records}}var loadSdk=()=>import("@anthropic-ai/claude-agent-sdk");async function sdkInstalled(){try{return await loadSdk(),!0}catch{return!1}}var sdkRunner=async({root,task,prompt,model,maxBudgetUsd,maxTurns,checks})=>{let sdk;try{sdk=await loadSdk()}catch{throw new Error("--agent sdk needs @anthropic-ai/claude-agent-sdk installed next to uptide (npm install -g @anthropic-ai/claude-agent-sdk), or use --agent patch")}let text=t=>({content:[{type:"text",text:t}]}),server=sdk.createSdkMcpServer({name:"uptide",tools:[sdk.tool("check_types","The repository's type-check: errors new since the task started, and those in the task's files",{},async()=>text(checks.types())),sdk.tool("run_lint","The repository's lint on the files changed so far",{},async()=>text(await checks.lint())),sdk.tool("run_tests","The repository's tests related to the changed files",{},async()=>text(await checks.tests()))]}),started=Date.now(),toolCalls={},result;try{for await(let message of sdk.query({prompt,options:{cwd:root,model,effort:modelCapabilities("anthropic",model).defaultEffort,maxTurns,maxBudgetUsd,tools:["Read","Grep","Glob","Edit","Write"],allowedTools:["Read","Grep","Glob","Edit","Write",...CHECK_TOOLS],permissionMode:"dontAsk",mcpServers:{uptide:server},hooks:{PreToolUse:[{hooks:[async hook=>{let{tool_name,tool_input}=hook;toolCalls[tool_name]=(toolCalls[tool_name]??0)+1;let reason=guard(root,task.files,tool_name,tool_input??{});return reason?{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:reason}}:{}}]}]},settingSources:[],persistSession:!1,systemPrompt:{type:"preset",preset:"claude_code"},outputFormat:{type:"json_schema",schema:SUMMARY_SCHEMA},env:{PATH:process.env.PATH??"",HOME:process.env.HOME??"",ANTHROPIC_API_KEY:process.env.ANTHROPIC_API_KEY??"",CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC:"1",DISABLE_FEEDBACK_COMMAND:"1",CLAUDE_CODE_DISABLE_OFFICIAL_MARKETPLACE_AUTOINSTALL:"1",CLAUDE_AGENT_SDK_CLIENT_APP:"uptide"}}}))message.type==="result"&&(result=message)}catch(err){if(!result)throw err}if(!result)throw new Error("the SDK session ended without a result");let usage2=result.usage;return{turns:result.num_turns,inputTokens:(usage2.input_tokens??0)+(usage2.cache_read_input_tokens??0)+(usage2.cache_creation_input_tokens??0),outputTokens:usage2.output_tokens??0,costUsd:result.total_cost_usd,durationMs:Date.now()-started,toolCalls,...result.subtype!=="success"?{stop:result.subtype}:result.structured_output?{summary:result.structured_output}:{}}},defaults={check,install,diagnostics,tests:testWorkspaces};function confirmServices(root,options){if(!options.withServices||options.yes)return;let reach=describeServices(root,workspacePackagesOf(root));if(reach.length!==0)throw new UptideError("SERVICES_NOT_CONFIRMED",`--with-services would run tests against:
399
399
  ${reach.map(l=>` ${l}`).join(`
400
400
  `)}
401
- Nothing ran. Add --yes to confirm these services and run.`)}function normalizeRemote(url){return url.trim().replace(/\.git$/,"").replace(/^git@([^:]+):/,"https://$1/").replace(/^ssh:\/\/git@/,"https://")}function originOf(root){try{return normalizeRemote(git(root,"remote","get-url","origin"))||void 0}catch{return}}async function fix(options,services=defaults){try{return await fixPackage(options,services)}catch(err){throw resetSharedState(),err}}async function fixPackage(options,services){let started=Date.now(),root=realpathSync7(resolve6(options.cwd));if(options.maxCostUsd!==void 0&&(!Number.isFinite(options.maxCostUsd)||options.maxCostUsd<=0))throw new Error("--max-cost must be a positive finite amount in USD");let selection=selectLlm(root,options),sdkDefault=options.agent===void 0&&options.fixer===void 0&&selection.provider==="anthropic"&&selection.available,sdkMissing=sdkDefault&&!services.agentRunner&&!await(services.sdkInstalled??sdkInstalled)(),sdkAgent=options.fixer!==null&&(options.agent==="sdk"||sdkDefault&&!sdkMissing);if(sdkAgent&&selection.provider!=="anthropic")throw new UptideError("NO_FIXER",`--agent sdk runs on Anthropic models only; ${selection.provider} keeps the default agent`);if(sdkAgent&&!selection.available&&!services.agentRunner)throw new UptideError("NO_FIXER","--agent sdk needs ANTHROPIC_API_KEY");let pack=options.pack??activePack(options.only)??genericPack(options.only);if(pack.name!==options.only)throw new Error(`no migration pack for ${options.only}`);let fixer=options.fixer===null?void 0:options.fixer??providerFixer(selection),needsAgent=candidate=>{if(candidate.rules.length===0&&!fixer)throw new UptideError("NO_FIXER",`${candidate.name} has no migration pack, so every fix would come from the agent, and ${options.fixer===null?"assisted fixes are off (--no-llm)":`no selected-provider API key is set; accepted environment variables: ${ACCEPTED_KEYS}`}`)};if(needsAgent(pack),projectRoot(root),git(root,"status","--porcelain","--untracked-files=all"))throw new UptideError("DIRTY_WORKING_TREE","uptide fix requires a clean working tree");if(confirmServices(root,options),options.pinCurrentApi)return pinCurrentApi(options,services,originOf);let tool=options.tool??uptideVersionInfo();if(options.pr&&tool.uptideDirty)throw new UptideError("DIRTY_UPTIDE_TREE",`uptide fix --pr refuses to run from an Uptide checkout with uncommitted changes (at ${tool.uptideCommit.slice(0,12)}); commit or stash them, rebuild, and run again`);options.pinCurrentApi||assertNpmLockSync(root);let peerPlan=options.peerPlan??await peerPreflight(options,services),registry=services.resolve?void 0:createNpmFetcher({config:loadRegistryConfig({cwd:root})}),resolved=peerPlan?{version:peerPlan.target,source:peerPlan.targetSource,...peerPlan.releaseAge?{releaseAge:peerPlan.releaseAge}:{}}:await resolveTarget(pack,options.target,services.resolve??registry?.resolve,registry),target=resolved.version;if(validateVersionRanges(root,[{name:pack.name,to:target}]),!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(target))throw new Error("target must be an exact version");assertNoDowngrades(root,[{name:pack.name,to:target},...peerPlan?.companions??[]]),assertNoPinningOverrides(root,[{name:pack.name,to:target},...peerPlan?.companions??[]]);let branch=`uptide/${pack.name}-${target}`;packageManager(root),services.install===install&&await assertManagerAvailable(root);let report=await services.check({...options.onProgress?{onProgress:options.onProgress}:{},cwd:root,only:[pack.name],targets:{[pack.name]:target},runtime:!1,plan:!1,workspaceConcurrency:1}),packages=report.packages.filter(p=>p.name===pack.name&&!p.notes.includes("up to date"));if(!packages.length)throw new Error(`${pack.name} has no upgrade to ${target}`);!options.pack&&packages.some(p=>!pack.supports(p.installed,target))&&(pack=genericPack(options.only),needsAgent(pack));let generic=pack.rules.length===0,lowest=[...packages].sort((a,b)=>compareVersions(a.installed,b.installed))[0];if(generic&&(options.pr||options.forPublication)&&isMajorUpgrade(lowest?.installed,target))throw new UptideError("PUBLICATION_REFUSED",`${NO_PACK_YET}: ${pack.name} ${lowest?.installed??"unknown"} \u2192 ${target} is a major upgrade, and Uptide opens a pull request for one only with a verified migration pack. Nothing was changed.`);for(let p of packages){if(!pack.supports(p.installed,target))throw new Error(`${pack.name} pack does not support ${p.installed} \u2192 ${target}`);if(["skipped","no-types","private","unknown","partial"].includes(p.status)||p.compile?.skipped||p.unanalyzed.length)throw new UptideError(p.skipReason??"INCOMPLETE_CHECK",`cannot fix an incomplete check: ${p.name} ${p.notes.join("; ")}`)}let conflicts=[...new Set(packages.flatMap(p=>p.companionConflicts??[]))];if(conflicts.length>0)throw new UptideError("INCONSISTENT_UPGRADE",`${pack.name} ${target} cannot be installed consistently: ${conflicts.join("; ")}. Nothing was changed.`);let companions=[...new Map((peerPlan?.companions??packages.flatMap(p=>p.companions??[])).map(c=>[c.name,c])).values()].sort((a,b)=>a.name.localeCompare(b.name));assertNoDowngrades(root,[{name:pack.name,to:target},...companions]),assertNoPinningOverrides(root,[{name:pack.name,to:target},...companions]),validateVersionRanges(root,[{name:pack.name,to:target},...companions]);let initialContext={from:lowest?.installed??"",to:target,includeDeprecated:options.includeDeprecated??!1},packContext=pack.resolveContext?await pack.resolveContext(initialContext):initialContext,importers=new Set(packages.flatMap(p=>(p.importers??[]).filter(i=>i.analyzed).map(i=>i.workspace))),workspaces=workspacePackagesOf(root).filter(w=>{let p=JSON.parse(readFileSync11(join11(root,w,"package.json"),"utf8"));return importers.has(w)||["dependencies","devDependencies","peerDependencies","optionalDependencies"].some(s=>typeof p[s]?.[pack.name]=="string")});pack.name==="stripe"&&(packContext={...packContext,...await stripeUsageContext(root,workspaces,initialContext.from),payloadVersions:payloadApiVersions(root,workspaces)}),pack.scanContext&&(packContext={...packContext,...pack.scanContext(root,workspaces)}),pack.reviewSections?.(packContext);let verify=(detail,work)=>progress(options.onProgress,{phase:"verify",package:pack.name,detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),generated=await verify("generate",()=>(services.generate??generateClients)(root,workspaces)),baseline=await verify(`types (${names(workspaces)})`,()=>services.diagnostics(root,workspaces)),selected=selectedFindings(report,pack.name,options.includeDeprecated??!1).filter(f=>!generic||f.severity==="breaking"),affected=[...new Set(selected.map(f=>f.usage.file))];packContext.evidence&&(packContext={...packContext,evidence:confirmedEvidence(packContext.evidence,selected)});let baselineTests=await verify(`tests (${names(workspaces)})`,()=>services.tests(root,workspaces,options.testTimeoutMs,affected,{withServices:options.withServices===!0,fullSuite:sdkAgent}));if(git(root,"status","--porcelain","--untracked-files=all"))throw new Error("baseline tests modified the working tree; review their changes before fixing");let baselineLint=await(services.lint??lintFiles)(root,affected),baseHead=git(root,"rev-parse","HEAD");git(root,"switch","-c",branch);let bumps=[bumpVersions(root,pack.name,target),...companions.map(c=>bumpVersions(root,c.name,c.to))],bump={files:[...new Set([...bumps.flatMap(b=>b.files),...writePeerOverrides(root,peerPlan?.conflicts??[])])],workspaces:[...new Set(bumps.flatMap(b=>b.workspaces))]},lockfile=await progress(options.onProgress,{phase:"install",package:pack.name,detail:`${pack.name} ${target}${companions.length?` with ${companions.length} package${companions.length===1?"":"s"}`:""}`},()=>services.install(root,{name:pack.name,version:target,workspaces:bump.workspaces,files:bump.files.map(f=>relative5(root,f)),...peerPlan?.conflicts.some(p=>p.allowed)?{allowedPeers:[...new Set(peerPlan.conflicts.filter(p=>p.allowed).map(p=>p.name))]}:{},...companions.length?{also:companions.map(c=>({name:c.name,version:c.to}))}:{}}));assertInstalledVersions(root,[{name:pack.name,to:target},...companions]);let moved2=companions.map(c=>`${c.name} ${c.to}`).join(", ");commit(root,`chore: upgrade ${pack.name} to ${target}${moved2?` with ${moved2}`:""}`,[...bump.files,join11(root,packageManager(root).lockfile)]),generated.length&&await verify("generate",()=>(services.generate??generateClients)(root,workspaces));let targetChecker=sdkAgent&&services.diagnostics===diagnostics?typeChecker(root,workspaces):void 0,checkTarget=targetChecker?.check??(()=>services.diagnostics(root,workspaces)),targetErrors=await verify(`types (${names(workspaces)})`,checkTarget),sites=[],changed=new Set,rulesStarted=performance.now();options.onProgress?.({phase:"rules",package:pack.name,state:"start"});let originals=new Map;for(let finding of selected){let file=safeFile(root,finding.usage.file),original=readFileSync11(file,"utf8");originals.has(finding.usage.file)||originals.set(finding.usage.file,original);let result2=pack.transform(original,finding,{...packContext,from:finding.change.from,to:target,includeDeprecated:options.includeDeprecated??!1});result2.applied&&(writeFileSync5(file,result2.text),changed.add(file));let site={finding,outcome:result2.applied?"mechanical":"manual",reason:result2.reason,...result2.applied?{diff:editDiff(original,result2.text)}:{}};site.rule=result2.rule??changeRule({package:pack.name},site),sites.push(site)}options.onProgress?.({phase:"rules",package:pack.name,state:"done",ms:performance.now()-rulesStarted}),commit(root,`fix(${pack.name}): apply mechanical migrations`,[...changed]);for(let{helper,edits}of pack.sharedHelpers?.({root,workspaces,findings:selected,context:packContext})??[]){let files2=[],base=selected.find(f=>/current_period_(?:start|end)/.test(f.change.path))??selected.find(f=>/current_period_(?:start|end)/.test(f.usage.compileError??""))??selected[0];for(let edit of edits){let file=safeFile(root,edit.file),original=readFileSync11(file,"utf8");writeFileSync5(file,edit.text),files2.push(file),sites.push({finding:{...base,usage:{...base.usage,file:edit.file,line:edit.line??original.split(`
401
+ Nothing ran. Add --yes to confirm these services and run.`)}function normalizeRemote(url){return url.trim().replace(/\.git$/,"").replace(/^git@([^:]+):/,"https://$1/").replace(/^ssh:\/\/git@/,"https://")}function originOf(root){try{return normalizeRemote(git(root,"remote","get-url","origin"))||void 0}catch{return}}async function fix(options,services=defaults){try{return await fixPackage(options,services)}catch(err){throw resetSharedState(),err}}async function fixPackage(options,services){let started=Date.now(),root=realpathSync8(resolve7(options.cwd));if(options.maxCostUsd!==void 0&&(!Number.isFinite(options.maxCostUsd)||options.maxCostUsd<=0))throw new Error("--max-cost must be a positive finite amount in USD");let selection=selectLlm(root,options),sdkDefault=options.agent===void 0&&options.fixer===void 0&&selection.provider==="anthropic"&&selection.available,sdkMissing=sdkDefault&&!services.agentRunner&&!await(services.sdkInstalled??sdkInstalled)(),sdkAgent=options.fixer!==null&&(options.agent==="sdk"||sdkDefault&&!sdkMissing);if(sdkAgent&&selection.provider!=="anthropic")throw new UptideError("NO_FIXER",`--agent sdk runs on Anthropic models only; ${selection.provider} keeps the default agent`);if(sdkAgent&&!selection.available&&!services.agentRunner)throw new UptideError("NO_FIXER","--agent sdk needs ANTHROPIC_API_KEY");let pack=options.pack??activePack(options.only)??genericPack(options.only);if(pack.name!==options.only)throw new Error(`no migration pack for ${options.only}`);let fixer=options.fixer===null?void 0:options.fixer??providerFixer(selection),needsAgent=candidate=>{if(candidate.rules.length===0&&!fixer)throw new UptideError("NO_FIXER",`${candidate.name} has no migration pack, so every fix would come from the agent, and ${options.fixer===null?"assisted fixes are off (--no-llm)":`no selected-provider API key is set; accepted environment variables: ${ACCEPTED_KEYS}`}`)};if(needsAgent(pack),projectRoot(root),git(root,"status","--porcelain","--untracked-files=all"))throw new UptideError("DIRTY_WORKING_TREE","uptide fix requires a clean working tree");if(confirmServices(root,options),options.pinCurrentApi)return pinCurrentApi(options,services,originOf);let tool=options.tool??uptideVersionInfo();if(options.pr&&tool.uptideDirty)throw new UptideError("DIRTY_UPTIDE_TREE",`uptide fix --pr refuses to run from an Uptide checkout with uncommitted changes (at ${tool.uptideCommit.slice(0,12)}); commit or stash them, rebuild, and run again`);options.pinCurrentApi||assertNpmLockSync(root);let peerPlan=options.peerPlan??await peerPreflight(options,services),registry=services.resolve?void 0:createNpmFetcher({config:loadRegistryConfig({cwd:root})}),resolved=peerPlan?{version:peerPlan.target,source:peerPlan.targetSource,...peerPlan.releaseAge?{releaseAge:peerPlan.releaseAge}:{}}:await resolveTarget(pack,options.target,services.resolve??registry?.resolve,registry),target=resolved.version;if(validateVersionRanges(root,[{name:pack.name,to:target}]),!/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(target))throw new Error("target must be an exact version");assertNoDowngrades(root,[{name:pack.name,to:target},...peerPlan?.companions??[]]),assertNoPinningOverrides(root,[{name:pack.name,to:target},...peerPlan?.companions??[]]);let branch=`uptide/${pack.name}-${target}`;packageManager(root),services.install===install&&await assertManagerAvailable(root);let report=await services.check({...options.onProgress?{onProgress:options.onProgress}:{},cwd:root,only:[pack.name],targets:{[pack.name]:target},runtime:!1,plan:!1,workspaceConcurrency:1}),packages=report.packages.filter(p=>p.name===pack.name&&!p.notes.includes("up to date"));if(!packages.length)throw new Error(`${pack.name} has no upgrade to ${target}`);!options.pack&&packages.some(p=>!pack.supports(p.installed,target))&&(pack=genericPack(options.only),needsAgent(pack));let generic=pack.rules.length===0,lowest=[...packages].sort((a,b)=>compareVersions(a.installed,b.installed))[0];if(generic&&(options.pr||options.forPublication)&&isMajorUpgrade(lowest?.installed,target))throw new UptideError("PUBLICATION_REFUSED",`${NO_PACK_YET}: ${pack.name} ${lowest?.installed??"unknown"} \u2192 ${target} is a major upgrade, and Uptide opens a pull request for one only with a verified migration pack. Nothing was changed.`);for(let p of packages){if(!pack.supports(p.installed,target))throw new Error(`${pack.name} pack does not support ${p.installed} \u2192 ${target}`);if(["skipped","no-types","private","unknown","partial"].includes(p.status)||p.compile?.skipped||p.unanalyzed.length)throw new UptideError(p.skipReason??"INCOMPLETE_CHECK",`cannot fix an incomplete check: ${p.name} ${p.notes.join("; ")}`)}let conflicts=[...new Set(packages.flatMap(p=>p.companionConflicts??[]))];if(conflicts.length>0)throw new UptideError("INCONSISTENT_UPGRADE",`${pack.name} ${target} cannot be installed consistently: ${conflicts.join("; ")}. Nothing was changed.`);let companions=[...new Map((peerPlan?.companions??packages.flatMap(p=>p.companions??[])).map(c=>[c.name,c])).values()].sort((a,b)=>a.name.localeCompare(b.name));assertNoDowngrades(root,[{name:pack.name,to:target},...companions]),assertNoPinningOverrides(root,[{name:pack.name,to:target},...companions]),validateVersionRanges(root,[{name:pack.name,to:target},...companions]);let initialContext={from:lowest?.installed??"",to:target,includeDeprecated:options.includeDeprecated??!1},packContext=pack.resolveContext?await pack.resolveContext(initialContext):initialContext,importers=new Set(packages.flatMap(p=>(p.importers??[]).filter(i=>i.analyzed).map(i=>i.workspace))),workspaces=workspacePackagesOf(root).filter(w=>{let p=JSON.parse(readFileSync12(join11(root,w,"package.json"),"utf8"));return importers.has(w)||["dependencies","devDependencies","peerDependencies","optionalDependencies"].some(s=>typeof p[s]?.[pack.name]=="string")});pack.name==="stripe"&&(packContext={...packContext,...await stripeUsageContext(root,workspaces,initialContext.from),payloadVersions:payloadApiVersions(root,workspaces)}),pack.scanContext&&(packContext={...packContext,...pack.scanContext(root,workspaces)}),pack.reviewSections?.(packContext);let verify=(detail,work)=>progress(options.onProgress,{phase:"verify",package:pack.name,detail},work),names=list22=>list22.map(w=>w==="."?"root":w.split("/").pop()??w).join(", "),generated=await verify("generate",()=>(services.generate??generateClients)(root,workspaces)),baseline=await verify(`types (${names(workspaces)})`,()=>services.diagnostics(root,workspaces)),selected=selectedFindings(report,pack.name,options.includeDeprecated??!1).filter(f=>!generic||f.severity==="breaking"),affected=[...new Set(selected.map(f=>f.usage.file))];packContext.evidence&&(packContext={...packContext,evidence:confirmedEvidence(packContext.evidence,selected)});let baselineTests=await verify(`tests (${names(workspaces)})`,()=>services.tests(root,workspaces,options.testTimeoutMs,affected,{withServices:options.withServices===!0,fullSuite:sdkAgent}));if(git(root,"status","--porcelain","--untracked-files=all"))throw new Error("baseline tests modified the working tree; review their changes before fixing");let baselineLint=await(services.lint??lintFiles)(root,affected),baseHead=git(root,"rev-parse","HEAD");git(root,"switch","-c",branch);let bumps=[bumpVersions(root,pack.name,target),...companions.map(c=>bumpVersions(root,c.name,c.to))],bump={files:[...new Set([...bumps.flatMap(b=>b.files),...writePeerOverrides(root,peerPlan?.conflicts??[])])],workspaces:[...new Set(bumps.flatMap(b=>b.workspaces))]},lockfile=await progress(options.onProgress,{phase:"install",package:pack.name,detail:`${pack.name} ${target}${companions.length?` with ${companions.length} package${companions.length===1?"":"s"}`:""}`},()=>services.install(root,{name:pack.name,version:target,workspaces:bump.workspaces,files:bump.files.map(f=>relative6(root,f)),...peerPlan?.conflicts.some(p=>p.allowed)?{allowedPeers:[...new Set(peerPlan.conflicts.filter(p=>p.allowed).map(p=>p.name))]}:{},...companions.length?{also:companions.map(c=>({name:c.name,version:c.to}))}:{}}));assertInstalledVersions(root,[{name:pack.name,to:target},...companions]);let moved2=companions.map(c=>`${c.name} ${c.to}`).join(", ");commit(root,`chore: upgrade ${pack.name} to ${target}${moved2?` with ${moved2}`:""}`,[...bump.files,join11(root,packageManager(root).lockfile)]),generated.length&&await verify("generate",()=>(services.generate??generateClients)(root,workspaces));let targetChecker=sdkAgent&&services.diagnostics===diagnostics?typeChecker(root,workspaces):void 0,checkTarget=targetChecker?.check??(()=>services.diagnostics(root,workspaces)),targetErrors=await verify(`types (${names(workspaces)})`,checkTarget),sites=[],changed=new Set,rulesStarted=performance.now();options.onProgress?.({phase:"rules",package:pack.name,state:"start"});let originals=new Map;for(let finding of selected){if(!existsSync9(join11(root,finding.usage.file))){sites.push({finding,outcome:"manual",reason:"The anchored file was removed or renamed by an earlier edit."});continue}let file=safeFile(root,finding.usage.file),original=readFileSync12(file,"utf8");originals.has(finding.usage.file)||originals.set(finding.usage.file,original);let result2=pack.transform(original,finding,{...packContext,from:finding.change.from,to:target,includeDeprecated:options.includeDeprecated??!1}),editedFiles;if(result2.applied&&result2.edits?.length){let before=await checkTarget(),transaction=await fileTransaction(root,[{kind:"write",file:finding.usage.file,text:result2.text},...result2.edits],async()=>{if(targetChecker?.clear(),newDiagnostics(before,await checkTarget()).length)return!1;let tests2=await services.tests(root,workspaces,options.testTimeoutMs,[...new Set([finding.usage.file,...(result2.edits??[]).flatMap(edit=>edit.kind==="rename"?[edit.file,edit.to]:[edit.file])])],{related:!0,withServices:options.withServices===!0});return markPreexisting(tests2,baselineTests).every(test=>test.status==="passed"||test.status==="missing"||test.preexisting)});if(targetChecker?.clear(),transaction.applied){editedFiles=transaction.files;for(let name of transaction.files)changed.add(join11(root,name))}else result2={...result2,applied:!1,reason:transaction.reason}}else result2.applied&&(writeFileSync6(file,result2.text),changed.add(file));let site={finding,outcome:result2.applied?"mechanical":"manual",reason:result2.reason,...result2.applied?{diff:editDiff(original,result2.text)}:{},...editedFiles?{editedFiles}:{}};site.rule=result2.rule??changeRule({package:pack.name},site),sites.push(site)}options.onProgress?.({phase:"rules",package:pack.name,state:"done",ms:performance.now()-rulesStarted}),commit(root,`fix(${pack.name}): apply mechanical migrations`,[...changed]);for(let{helper,edits}of pack.sharedHelpers?.({root,workspaces,findings:selected,context:packContext})??[]){let files2=[],base=selected.find(f=>/current_period_(?:start|end)/.test(f.change.path))??selected.find(f=>/current_period_(?:start|end)/.test(f.usage.compileError??""))??selected[0];for(let edit of edits){let file=safeFile(root,edit.file),original=readFileSync12(file,"utf8");writeFileSync6(file,edit.text),files2.push(file),sites.push({finding:{...base,usage:{...base.usage,file:edit.file,line:edit.line??original.split(`
402
402
  `).length,column:1,endLine:edit.text.split(`
403
403
  `).length,endColumn:1,snippet:`${helper.name} shared from ${helper.file}`}},outcome:"mechanical",reason:edit.file===helper.file?`${helper.name} placed here once, where every migrated file imports it`:`re-exports ${helper.name} so the sites can import it`,diff:editDiff(original,edit.text),rule:changeRule({package:pack.name},{finding:base,outcome:"mechanical",reason:""})}),affected.includes(edit.file)||affected.push(edit.file)}commit(root,`fix(${pack.name}): add ${helper.name} to ${helper.file}`,files2),packContext={...packContext,helpers:[...packContext.helpers??[],helper]}}let pendingCompilerSites=sites.filter(s=>s.outcome==="manual"&&s.finding.usage.compileError);if(pendingCompilerSites.length&&sites.some(s=>s.outcome==="mechanical")){let errorFiles=new Set((await verify("types after rules",checkTarget)).map(d=>d.file));for(let site of pendingCompilerSites){let usage2=site.finding.usage;!errorFiles.has(usage2.file)&&targetErrors.some(d=>d.file===usage2.file&&(d.line===usage2.line||d.message===usage2.compileError))&&(site.outcome="mechanical",site.reason="compiler error resolved by preceding rule-based edits")}}let llm=await progress(options.onProgress,{phase:"assist",package:pack.name},()=>sdkAgent?assistWithSdk({root,workspaces,sites,pack,context:packContext,model:selection.model,maxCostUsd:options.maxCostUsd??DEFAULT_MAX_COST_USD,diagnostics:checkTarget,tests:files2=>services.tests(root,workspaces,options.testTimeoutMs,files2,{withServices:options.withServices===!0,related:!0}),...services.lint?{lint:(files2,b)=>services.lint?.(root,files2,b)??Promise.resolve([])}:{},baselineTests,baselineLint,affected:[...affected],...options.onProgress?{onProgress:options.onProgress}:{},...services.format?{format:files2=>services.format?.(root,files2)??Promise.resolve([])}:{},...services.agentRunner?{runner:services.agentRunner}:{}}):assist(root,sites,pack,fixer,()=>services.diagnostics(root,workspaces),packContext,options.fixer===null,options.onProgress,{maxCostUsd:options.maxCostUsd??DEFAULT_MAX_COST_USD}));for(let site of sites)for(let file of site.editedFiles??[])affected.includes(file)||affected.push(file);let followed=[];for(let site of[...sites])site.outcome==="manual"||!pack.followUps||applyFollowUps({root,pack:pack.name,edits:pack.followUps({root,workspaces,finding:site.finding,context:packContext}),base:site.finding,rule:site.rule,origin:`${site.finding.usage.file}:${site.finding.usage.line}`,sites,followed,affected});targetChecker?.clear();let behavior=pack.name==="zod"?behaviorCheck(root,originals,sites):void 0,{tests:ran,after,lint,formatted}=await settle({root,pack,context:packContext,workspaces,sites,followed,affected,from:initialContext.from,target,baselineLint,services,fullSuite:sdkAgent,withServices:options.withServices===!0,...options.onProgress?{onProgress:options.onProgress}:{},...options.testTimeoutMs?{testTimeoutMs:options.testTimeoutMs}:{}}),needsBuild=generic||pack.requiresBuild===!0,beforeBuild=needsBuild?git(root,"status","--porcelain","--untracked-files=all"):"",build=needsBuild?await verify(`build (${names(workspaces)})`,()=>(services.build??buildWorkspaces)(root,workspaces)):[];needsBuild&&!beforeBuild&&git(root,"status","--porcelain","--untracked-files=all")&&(git(root,"checkout","--","."),git(root,"clean","-fdq")),assertInstalledVersions(root,[{name:pack.name,to:target},...companions]);let tests=markPreexisting(ran,baselineTests),pending=git(root,"status","--porcelain","--untracked-files=all"),newErrors=newDiagnostics(baseline,after,gitLineMoves(root,baseHead)),unverified=typeResolutionFailure(root,baseline),result={...lockfile?{lockfile}:{},...generated.length?{generated}:{},repo:root,package:pack.name,tier:generic?"generic":"verified",from:initialContext.from,target,targetSource:resolved.source,...resolved.releaseAge?{releaseAge:resolved.releaseAge}:{},...companions.length?{companions}:{},...peerPlan?.conflicts.length?{peerConflicts:groupPeerBlockers(peerPlan.conflicts)}:{},...report.importersNotAnalyzed?.length?{importersNotAnalyzed:report.importersNotAnalyzed}:{},...tool,verifiedAt:new Date().toISOString(),head:git(root,"rev-parse","HEAD"),verifiedTree:git(root,"rev-parse","HEAD^{tree}"),baseHead,changedFiles:git(root,"diff","--name-only",baseHead,"HEAD").split(`
404
- `).filter(Boolean),branch,sites,verification:{baseline,target:targetErrors,after,newErrors,baselineTests,tests,...needsBuild?{build}:{},...pack.requiresBuild?{buildRequired:!0}:{},...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},baselineHealth:baselineHealth(baseline,baselineTests),passed:!unverified&&newErrors.length===0&&!pending&&tests.every(t=>t.status==="passed"||t.status==="missing"||t.preexisting)&&(!pack.requiresBuild||build.length>0&&build.every(b=>b.status==="passed"))&&build.every(b=>b.status==="passed"||b.status==="missing")&&lint.every(l=>l.status!=="failed")},llm:sdkMissing?{...llm,sdkMissing:!0}:llm,...behavior?{behavior}:{},timingMs:Date.now()-started,prBody:join11(root,".uptide/pr-body.md"),notes:[...pack.reviewNotes(packContext),...bumps.flatMap(b=>b.alreadyCompatible.map(w=>`${w}: already compatible; range left unchanged.`)),...pending?["Tests/install left uncommitted files; review them before publication."]:[]]},rules=[...new Set(sites.map(s=>s.rule).filter(r=>!!r))];result.packContext=packContext,refreshReview(result,pack);let decisions=pack.decisions?.(packContext,rules,followed);decisions?.length&&(result.decisions=decisions);let remote=originOf(root);remote&&(result.remote=remote),mkdirSync2(dirname8(result.prBody),{recursive:!0}),writeFileSync5(result.prBody,prBody(result));let storedRun=join11(root,".uptide/report.json");return writeFileSync5(storedRun,JSON.stringify(result,null,2)),options.pr&&(await publishVerified(result,{tool,...options.yes?{yes:!0}:{}}),writeFileSync5(result.prBody,prBody(result)),writeFileSync5(storedRun,JSON.stringify(result,null,2))),result}async function publishVerified(result,options,io){let blockers=publicationBlockers(result,options.tool);if(blockers.length){result.publication={refused:blockers},result.notes.push(`PR not opened: ${blockers.join("; ")}`);return}let{publish:publish2}=await import("./publish-4P4GVYML-XOFYARWB.js"),log=[],collect=text=>{log.push(text)};try{result.prUrl=await publish2(result,options.yes,io?{...io,print:collect}:{git,command,print:collect},options.cwd?{cwd:options.cwd}:{})}catch(error){log.length&&(result.publicationLog=log.join(`
404
+ `).filter(Boolean),branch,sites,verification:{baseline,target:targetErrors,after,newErrors,baselineTests,tests,...needsBuild?{build}:{},...pack.requiresBuild?{buildRequired:!0}:{},...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},baselineHealth:baselineHealth(baseline,baselineTests),passed:!unverified&&newErrors.length===0&&!pending&&tests.every(t=>t.status==="passed"||t.status==="missing"||t.preexisting)&&(!pack.requiresBuild||build.length>0&&build.every(b=>b.status==="passed"))&&build.every(b=>b.status==="passed"||b.status==="missing")&&lint.every(l=>l.status!=="failed")},llm:sdkMissing?{...llm,sdkMissing:!0}:llm,...behavior?{behavior}:{},timingMs:Date.now()-started,prBody:join11(root,".uptide/pr-body.md"),notes:[...pack.reviewNotes(packContext),...bumps.flatMap(b=>b.alreadyCompatible.map(w=>`${w}: already compatible; range left unchanged.`)),...pending?["Tests/install left uncommitted files; review them before publication."]:[]]},rules=[...new Set(sites.map(s=>s.rule).filter(r=>!!r))];result.packContext=packContext,refreshReview(result,pack);let decisions=pack.decisions?.(packContext,rules,followed);decisions?.length&&(result.decisions=decisions);let remote=originOf(root);remote&&(result.remote=remote),mkdirSync3(dirname9(result.prBody),{recursive:!0}),writeFileSync6(result.prBody,prBody(result));let storedRun=join11(root,".uptide/report.json");return writeFileSync6(storedRun,JSON.stringify(result,null,2)),options.pr&&(await publishVerified(result,{tool,...options.yes?{yes:!0}:{}}),writeFileSync6(result.prBody,prBody(result)),writeFileSync6(storedRun,JSON.stringify(result,null,2))),result}async function publishVerified(result,options,io){let blockers=publicationBlockers(result,options.tool);if(blockers.length){result.publication={refused:blockers},result.notes.push(`PR not opened: ${blockers.join("; ")}`);return}let{publish:publish2}=await import("./publish-ETZAI6VL-JPQXRDX6.js"),log=[],collect=text=>{log.push(text)};try{result.prUrl=await publish2(result,options.yes,io?{...io,print:collect}:{git,command,print:collect},options.cwd?{cwd:options.cwd}:{})}catch(error){log.length&&(result.publicationLog=log.join(`
405
405
  `));let message=error instanceof Error?error.message:String(error);if(!options.yes&&/requires --yes/.test(message)){result.notes.push(`Publication plan printed, nothing pushed (no --yes): \`${UPTIDE_COMMAND} pr --branch ${result.branch} --yes\` opens the PR.`);return}if(error instanceof UptideError&&error.code==="VERIFIED_TREE_MISMATCH"){result.exportError=message,result.verification={...result.verification,passed:!1},result.publication={refused:[message]},result.notes.push(message);return}result.publication={refused:[],failed:message.split(`
406
406
  `)[0]??message},result.notes.push(`PR not opened: ${result.publication.failed}. The verified run is stored; \`${UPTIDE_COMMAND} pr --branch ${result.branch} --yes\` retries the publish step alone.`);return}log.length&&(result.publicationLog=log.join(`
407
- `))}function confirmedEvidence(evidence,findings){let rejected=new Set(findings.filter(f=>f.usage.compileError!==void 0||f.usage.compileCode!==void 0).map(f=>`${f.usage.file}:${f.usage.line}`));return evidence.map(e=>rejected.has(`${e.file}:${e.line}`)?{...e,compiler:!0}:e)}function refreshReview(result,pack=activePack(result.package)){if(!pack||!result.packContext)return;let rules=[...new Set(result.sites.map(s=>s.rule).filter(r=>!!r))];try{let sections=pack.reviewSections?.(result.packContext,rules);sections&&(result.reviewSections=sections);let apiChanges=pack.apiChanges?.(result.packContext);apiChanges&&(result.apiChanges=apiChanges)}catch{}}async function reverify(options,services={diagnostics,tests:testWorkspaces}){let started=Date.now(),root=realpathSync8(resolve7(options.cwd));confirmServices(root,options);let file=resolve7(options.run??join12(root,".uptide/report.json")),report;try{report=JSON.parse(readFileSync12(file,"utf8"))}catch{throw new UptideError("ANALYSIS_FAILED",`no stored migration run at ${file}`)}if(!Array.isArray(report.sites)||!report.verification||!report.package||!report.branch)throw new UptideError("ANALYSIS_FAILED","not a stored migration run");if(git(root,"branch","--show-current")!==report.branch)throw new UptideError("ANALYSIS_FAILED",`check out ${report.branch} first: the stored run belongs to that branch`);if(git(root,"status","--porcelain","--untracked-files=all").split(`
408
- `).filter(line=>line.trim()!==""&&!line.slice(3).startsWith(".uptide/")).length)throw new UptideError("DIRTY_WORKING_TREE","uptide verify requires a clean working tree");let pack=options.pack??activePack(report.package);if(!pack)throw new Error(`no migration pack for ${report.package}`);let workspaces=workspacePackagesOf(root).filter(w=>{let p=JSON.parse(readFileSync12(join12(root,w,"package.json"),"utf8"));return["dependencies","devDependencies","peerDependencies","optionalDependencies"].some(s=>typeof p[s]?.[pack.name]=="string")}),moves=[{name:report.package,to:report.target},...report.companions??[]];assertInstalledVersions(root,moves);let from=report.from??report.sites[0]?.finding.change.from??"",context={from,to:report.target,includeDeprecated:!1,...pack.scanContext?.(root,workspaces)},sites=report.sites,known=sites.length,followed=[],affected=[...new Set(sites.flatMap(s=>[s.finding.usage.file,...s.editedFiles??[]]))],{tests,after,lint,formatted}=await settle({root,pack,context,workspaces,sites,followed,affected,from,target:report.target,baselineLint:[],services,fullSuite:report.llm.agent==="sdk",withServices:options.withServices===!0,...options.onProgress?{onProgress:options.onProgress}:{},...options.testTimeoutMs?{testTimeoutMs:options.testTimeoutMs}:{}}),buildRequired=report.verification.buildRequired||pack.requiresBuild===!0,build=buildRequired?await(services.build??buildWorkspaces)(root,workspaces):report.verification.build;assertInstalledVersions(root,moves);let left=git(root,"status","--porcelain","--untracked-files=all").split(`
407
+ `))}function confirmedEvidence(evidence,findings){let rejected=new Set(findings.filter(f=>f.usage.compileError!==void 0||f.usage.compileCode!==void 0).map(f=>`${f.usage.file}:${f.usage.line}`));return evidence.map(e=>rejected.has(`${e.file}:${e.line}`)?{...e,compiler:!0}:e)}function refreshReview(result,pack=activePack(result.package)){if(!pack||!result.packContext)return;let rules=[...new Set(result.sites.map(s=>s.rule).filter(r=>!!r))];try{let sections=pack.reviewSections?.(result.packContext,rules);sections&&(result.reviewSections=sections);let apiChanges=pack.apiChanges?.(result.packContext);apiChanges&&(result.apiChanges=apiChanges)}catch{}}async function reverify(options,services={diagnostics,tests:testWorkspaces}){let started=Date.now(),root=realpathSync9(resolve8(options.cwd));confirmServices(root,options);let file=resolve8(options.run??join12(root,".uptide/report.json")),report;try{report=JSON.parse(readFileSync13(file,"utf8"))}catch{throw new UptideError("ANALYSIS_FAILED",`no stored migration run at ${file}`)}if(!Array.isArray(report.sites)||!report.verification||!report.package||!report.branch)throw new UptideError("ANALYSIS_FAILED","not a stored migration run");if(git(root,"branch","--show-current")!==report.branch)throw new UptideError("ANALYSIS_FAILED",`check out ${report.branch} first: the stored run belongs to that branch`);if(git(root,"status","--porcelain","--untracked-files=all").split(`
408
+ `).filter(line=>line.trim()!==""&&!line.slice(3).startsWith(".uptide/")).length)throw new UptideError("DIRTY_WORKING_TREE","uptide verify requires a clean working tree");let pack=options.pack??activePack(report.package);if(!pack)throw new Error(`no migration pack for ${report.package}`);let workspaces=workspacePackagesOf(root).filter(w=>{let p=JSON.parse(readFileSync13(join12(root,w,"package.json"),"utf8"));return["dependencies","devDependencies","peerDependencies","optionalDependencies"].some(s=>typeof p[s]?.[pack.name]=="string")}),moves=[{name:report.package,to:report.target},...report.companions??[]];assertInstalledVersions(root,moves);let from=report.from??report.sites[0]?.finding.change.from??"",context={from,to:report.target,includeDeprecated:!1,...pack.scanContext?.(root,workspaces)},sites=report.sites,known=sites.length,followed=[],affected=[...new Set(sites.flatMap(s=>[s.finding.usage.file,...s.editedFiles??[]]))],{tests,after,lint,formatted}=await settle({root,pack,context,workspaces,sites,followed,affected,from,target:report.target,baselineLint:[],services,fullSuite:report.llm.agent==="sdk",withServices:options.withServices===!0,...options.onProgress?{onProgress:options.onProgress}:{},...options.testTimeoutMs?{testTimeoutMs:options.testTimeoutMs}:{}}),buildRequired=report.verification.buildRequired||pack.requiresBuild===!0,build=buildRequired?await(services.build??buildWorkspaces)(root,workspaces):report.verification.build;assertInstalledVersions(root,moves);let left=git(root,"status","--porcelain","--untracked-files=all").split(`
409
409
  `).filter(line=>line.trim()!==""&&!line.slice(3).startsWith(".uptide/")),newErrors=newDiagnostics(report.verification.baseline,after,report.baseHead?gitLineMoves(root,report.baseHead):void 0),unverified=typeResolutionFailure(root,report.verification.baseline),rules=[...new Set(sites.map(s=>s.rule).filter(r=>!!r))],decisions=[...new Set([...report.decisions??[],...pack.decisions?.(context,rules,followed)??[]])],result={...report,repo:root,...options.tool??uptideVersionInfo(),verifiedAt:new Date().toISOString(),verificationTimingMs:Date.now()-started,head:git(root,"rev-parse","HEAD"),verifiedTree:git(root,"rev-parse","HEAD^{tree}"),...report.baseHead?{changedFiles:git(root,"diff","--name-only",report.baseHead,"HEAD").split(`
410
- `).filter(Boolean)}:{},sites,verification:{...report.verification,after,newErrors,tests,...buildRequired?{buildRequired:!0,build}:{},...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},passed:!unverified&&newErrors.length===0&&left.length===0&&(!buildRequired||!!build?.length&&build.every(b=>b.status==="passed"))&&tests.every(t=>t.status==="passed"||t.status==="missing")&&lint.every(l=>l.status!=="failed")},...decisions.length?{decisions}:{},notes:[...report.notes.filter(n=>!n.startsWith("Verified again")),`Verified again at a new HEAD: ${sites.length-known} follow-up edit${sites.length-known===1?"":"s"} and ${formatted.length?"formatting":"no formatting"} were added as new commits; earlier commits are unchanged.`],prBody:join12(root,".uptide/pr-body.md")};return delete result.verificationPending,delete result.exportError,unverified||delete result.verification.typesUnverified,delete result.publication,mkdirSync3(dirname9(result.prBody),{recursive:!0}),writeFileSync6(result.prBody,prBody(result)),writeFileSync6(join12(root,".uptide/report.json"),JSON.stringify(result,null,2)),result}var tryGit=(cwd,...args)=>{try{return git(cwd,...args)}catch{return""}};function gitDir(source){let dir=git(source,"rev-parse","--git-common-dir");return isAbsolute3(dir)?dir:resolve8(source,dir)}function hooksDigest(source){let dir=join13(gitDir(source),"hooks");if(!existsSync7(dir))return"";let hash2=createHash2("sha256");for(let name of readdirSync(dir).sort()){let path=join13(dir,name);hash2.update(name),statSync22(path).isFile()&&hash2.update(readFileSync13(path))}return hash2.digest("hex")}function snapshot(source){return{head:tryGit(source,"rev-parse","HEAD"),branch:tryGit(source,"branch","--show-current"),status:tryGit(source,"status","--porcelain","--untracked-files=all"),hooks:hooksDigest(source),config:tryGit(source,"config","--local","--list")}}function changedSince(source,before){let now=snapshot(source);return[...now.head!==before.head?["HEAD moved"]:[],...now.branch!==before.branch?["another branch is checked out"]:[],...now.status!==before.status?["the working tree has different changes"]:[],...now.hooks!==before.hooks?["git hooks were added or changed"]:[],...now.config!==before.config?["the repository git config changed"]:[]]}function isolate(source,branch,project="."){let root=realpathSync9(resolve8(source));mkdirSync4(runsRoot(),{recursive:!0});let run2=mkdtempSync2(join13(runsRoot(),"run-"));writeFileSync7(join13(run2,MARKER),JSON.stringify({source:root,created:new Date().toISOString()}));let dir=join13(run2,"repo");git(root,"clone","--quiet","--no-hardlinks",...branch?["--branch",branch]:[],root,dir);let origin=tryGit(root,"remote","get-url","origin");origin?(git(dir,"remote","set-url","origin",origin),tryGit(dir,"fetch","--quiet","--prune",root,"+refs/remotes/origin/*:refs/remotes/origin/*")):git(dir,"remote","remove","origin");for(let key2 of["user.name","user.email"]){let value=tryGit(root,"config",key2);value&&git(dir,"config",key2,value)}for(let workspace of workspacePackagesOf(join13(root,project))){let modules=join13(root,project,workspace,"node_modules");existsSync7(modules)&&(mkdirSync4(join13(dir,project,workspace),{recursive:!0}),cpSync(modules,join13(dir,project,workspace,"node_modules"),{recursive:!0,verbatimSymlinks:!0,mode:constants.COPYFILE_FICLONE}))}return dir}var MARKER=".uptide-run";function runsRoot(env=process.env){return env.UPTIDE_RUNS_DIR?resolve8(env.UPTIDE_RUNS_DIR):join13(realpathSync9(tmpdir2()),"uptide-runs")}function removeRun(path,root=runsRoot()){let run2,base;try{let real=realpathSync9(resolve8(path));run2=basename(real)==="repo"?dirname10(real):real,base=realpathSync9(root)}catch{return!1}return dirname10(run2)!==base||!/^run-[\w-]+$/.test(basename(run2))||!existsSync7(join13(run2,MARKER))?!1:(rmSync3(run2,{recursive:!0,force:!0}),!0)}function cleanRuns(options={}){let root=options.root??runsRoot(),limit=(options.now??Date.now())-(options.days??7)*24*60*60*1e3,removed=[],kept=[],entries=[];try{entries=readdirSync(root).sort()}catch{return{removed,kept}}for(let name of entries){let run2=join13(root,name),created;try{let marker=JSON.parse(readFileSync13(join13(run2,MARKER),"utf8"));created=Date.parse(marker.created??""),Number.isNaN(created)&&(created=statSync22(run2).mtimeMs)}catch{continue}created<limit&&removeRun(run2,root)?removed.push(run2):kept.push(run2)}return{removed,kept}}var slug=branch=>branch.replaceAll("/","__");function storedRunDir(source,branch){return join13(gitDir(realpathSync9(resolve8(source))),"uptide",slug(branch))}function storedRunFile(source,branch){let legacy=join13(source,".uptide/report.json");if(existsSync7(legacy))return legacy;let kept=join13(storedRunDir(source,branch),"report.json");return existsSync7(kept)?kept:void 0}function deliver(source,clone,report,before,fresh=!1){let result={...report,source,...before.branch?{base:before.branch}:{},delivered:!1},notes=[...report.notes];try{assertVerifiedTree(clone,report)}catch(error){result.exportError=error.message}try{if(!result.exportError){let existingTree=tryGit(source,"rev-parse","--verify",`refs/heads/${report.branch}^{tree}`);if(fresh&&existingTree&&existingTree!==report.verifiedTree){let suffix=2;for(;tryGit(source,"rev-parse","--verify",`refs/heads/${report.branch}-${suffix}`);)suffix++;result.branch=`${report.branch}-${suffix}`,git(clone,"branch","-m",report.branch,result.branch)}fresh&&existingTree===report.verifiedTree?result.head=git(source,"rev-parse",`refs/heads/${result.branch}`):fresh?(git(source,"fetch","--quiet",clone,`refs/heads/${result.branch}`),result.head=git(clone,"rev-parse",`refs/heads/${result.branch}`),git(source,"update-ref",`refs/heads/${result.branch}`,result.head,"")):before.branch!==result.branch?git(source,"fetch","--quiet",clone,`refs/heads/${result.branch}:refs/heads/${result.branch}`):notes.push(`${report.branch} is checked out in your repository, so it was not moved; the new commits are in ${clone}.`),assertVerifiedTree(source,result),result.delivered=!0,result.branch!==report.branch&&(result.branchRenamedFrom=report.branch,notes.push(`${report.branch} already exists with different content; created ${result.branch}.`))}}catch(error){result.exportError=error.message}result.exportError&&notes.push(`Export failed: ${result.exportError}`),result.exportError&&(result.verification={...result.verification,passed:!1});let changed=changedSince(source,before);return changed.length&&(result.sourceChanged=changed,notes.push(`Your checkout changed during the run: ${changed.join("; ")}.`)),result.notes=notes,result}function finish(source,clone,delivered,options){let{delivered:inSource,...result}=delivered,reason=result.exportError?"export failed: the repository branch does not contain the verified tree":options.keep?"kept on request (--keep-temp)":result.sourceChanged?.length&&result.verification.passed?"your checkout changed during the run":!inSource&&!options.pushed&&result.verification.passed?"it holds commits that are not in your repository or on the remote yet":void 0;reason?result.clone={path:clone,kept:!0,reason}:(removeRun(clone),result.clone={path:clone,kept:!1},result.repo=source);let dir=storedRunDir(source,result.branch);return mkdirSync4(dir,{recursive:!0}),result.prBody=join13(dir,"pr-body.md"),writeFileSync7(result.prBody,prBody(result)),writeFileSync7(join13(dir,"report.json"),JSON.stringify(result,null,2)),result}var SIGNALS=["SIGINT","SIGTERM","SIGHUP"];function removeOnInterrupt(clones){let handlers=SIGNALS.map(signal=>{let handler=()=>{release();for(let clone of clones())removeRun(clone);process.kill(process.pid,signal)};return process.once(signal,handler),[signal,handler]}),release=()=>{for(let[signal,handler]of handlers)process.off(signal,handler)};return release}var watcher;function watchInterruptibleClones(watch){watcher=watch}async function keeping(clone,keep,work){let release=keep||!isMainThread?()=>{}:removeOnInterrupt(()=>[clone]);keep||watcher?.(clone,!0);try{return await work()}catch(error){throw keep?error instanceof Error&&(error.message+=`
410
+ `).filter(Boolean)}:{},sites,verification:{...report.verification,after,newErrors,tests,...buildRequired?{buildRequired:!0,build}:{},...lint.length?{lint}:{},...formatted.length?{formatted}:{},...unverified?{typesUnverified:unverified}:{},passed:!unverified&&newErrors.length===0&&left.length===0&&(!buildRequired||!!build?.length&&build.every(b=>b.status==="passed"))&&tests.every(t=>t.status==="passed"||t.status==="missing")&&lint.every(l=>l.status!=="failed")},...decisions.length?{decisions}:{},notes:[...report.notes.filter(n=>!n.startsWith("Verified again")),`Verified again at a new HEAD: ${sites.length-known} follow-up edit${sites.length-known===1?"":"s"} and ${formatted.length?"formatting":"no formatting"} were added as new commits; earlier commits are unchanged.`],prBody:join12(root,".uptide/pr-body.md")};return delete result.verificationPending,delete result.exportError,unverified||delete result.verification.typesUnverified,delete result.publication,mkdirSync4(dirname10(result.prBody),{recursive:!0}),writeFileSync7(result.prBody,prBody(result)),writeFileSync7(join12(root,".uptide/report.json"),JSON.stringify(result,null,2)),result}var tryGit=(cwd,...args)=>{try{return git(cwd,...args)}catch{return""}};function gitDir(source){let dir=git(source,"rev-parse","--git-common-dir");return isAbsolute4(dir)?dir:resolve9(source,dir)}function hooksDigest(source){let dir=join13(gitDir(source),"hooks");if(!existsSync10(dir))return"";let hash2=createHash2("sha256");for(let name of readdirSync(dir).sort()){let path=join13(dir,name);hash2.update(name),statSync22(path).isFile()&&hash2.update(readFileSync14(path))}return hash2.digest("hex")}function snapshot(source){return{head:tryGit(source,"rev-parse","HEAD"),branch:tryGit(source,"branch","--show-current"),status:tryGit(source,"status","--porcelain","--untracked-files=all"),hooks:hooksDigest(source),config:tryGit(source,"config","--local","--list")}}function changedSince(source,before){let now=snapshot(source);return[...now.head!==before.head?["HEAD moved"]:[],...now.branch!==before.branch?["another branch is checked out"]:[],...now.status!==before.status?["the working tree has different changes"]:[],...now.hooks!==before.hooks?["git hooks were added or changed"]:[],...now.config!==before.config?["the repository git config changed"]:[]]}function isolate(source,branch,project="."){let root=realpathSync10(resolve9(source));mkdirSync5(runsRoot(),{recursive:!0});let run2=mkdtempSync2(join13(runsRoot(),"run-"));writeFileSync8(join13(run2,MARKER),JSON.stringify({source:root,created:new Date().toISOString()}));let dir=join13(run2,"repo");git(root,"clone","--quiet","--no-hardlinks",...branch?["--branch",branch]:[],root,dir);let origin=tryGit(root,"remote","get-url","origin");origin?(git(dir,"remote","set-url","origin",origin),tryGit(dir,"fetch","--quiet","--prune",root,"+refs/remotes/origin/*:refs/remotes/origin/*")):git(dir,"remote","remove","origin");for(let key2 of["user.name","user.email"]){let value=tryGit(root,"config",key2);value&&git(dir,"config",key2,value)}for(let workspace of workspacePackagesOf(join13(root,project))){let modules=join13(root,project,workspace,"node_modules");existsSync10(modules)&&(mkdirSync5(join13(dir,project,workspace),{recursive:!0}),cpSync(modules,join13(dir,project,workspace,"node_modules"),{recursive:!0,verbatimSymlinks:!0,mode:constants.COPYFILE_FICLONE}))}return dir}var MARKER=".uptide-run";function runsRoot(env=process.env){return env.UPTIDE_RUNS_DIR?resolve9(env.UPTIDE_RUNS_DIR):join13(realpathSync10(tmpdir2()),"uptide-runs")}function removeRun(path,root=runsRoot()){let run2,base;try{let real=realpathSync10(resolve9(path));run2=basename(real)==="repo"?dirname11(real):real,base=realpathSync10(root)}catch{return!1}return dirname11(run2)!==base||!/^run-[\w-]+$/.test(basename(run2))||!existsSync10(join13(run2,MARKER))?!1:(rmSync4(run2,{recursive:!0,force:!0}),!0)}function cleanRuns(options={}){let root=options.root??runsRoot(),limit=(options.now??Date.now())-(options.days??7)*24*60*60*1e3,removed=[],kept=[],entries=[];try{entries=readdirSync(root).sort()}catch{return{removed,kept}}for(let name of entries){let run2=join13(root,name),created;try{let marker=JSON.parse(readFileSync14(join13(run2,MARKER),"utf8"));created=Date.parse(marker.created??""),Number.isNaN(created)&&(created=statSync22(run2).mtimeMs)}catch{continue}created<limit&&removeRun(run2,root)?removed.push(run2):kept.push(run2)}return{removed,kept}}var slug=branch=>branch.replaceAll("/","__");function storedRunDir(source,branch){return join13(gitDir(realpathSync10(resolve9(source))),"uptide",slug(branch))}function storedRunFile(source,branch){let legacy=join13(source,".uptide/report.json");if(existsSync10(legacy))return legacy;let kept=join13(storedRunDir(source,branch),"report.json");return existsSync10(kept)?kept:void 0}function deliver(source,clone,report,before,fresh=!1){let result={...report,source,...before.branch?{base:before.branch}:{},delivered:!1},notes=[...report.notes];try{assertVerifiedTree(clone,report)}catch(error){result.exportError=error.message}try{if(!result.exportError){let existingTree=tryGit(source,"rev-parse","--verify",`refs/heads/${report.branch}^{tree}`);if(fresh&&existingTree&&existingTree!==report.verifiedTree){let suffix=2;for(;tryGit(source,"rev-parse","--verify",`refs/heads/${report.branch}-${suffix}`);)suffix++;result.branch=`${report.branch}-${suffix}`,git(clone,"branch","-m",report.branch,result.branch)}fresh&&existingTree===report.verifiedTree?result.head=git(source,"rev-parse",`refs/heads/${result.branch}`):fresh?(git(source,"fetch","--quiet",clone,`refs/heads/${result.branch}`),result.head=git(clone,"rev-parse",`refs/heads/${result.branch}`),git(source,"update-ref",`refs/heads/${result.branch}`,result.head,"")):before.branch!==result.branch?git(source,"fetch","--quiet",clone,`refs/heads/${result.branch}:refs/heads/${result.branch}`):notes.push(`${report.branch} is checked out in your repository, so it was not moved; the new commits are in ${clone}.`),assertVerifiedTree(source,result),result.delivered=!0,result.branch!==report.branch&&(result.branchRenamedFrom=report.branch,notes.push(`${report.branch} already exists with different content; created ${result.branch}.`))}}catch(error){result.exportError=error.message}result.exportError&&notes.push(`Export failed: ${result.exportError}`),result.exportError&&(result.verification={...result.verification,passed:!1});let changed=changedSince(source,before);return changed.length&&(result.sourceChanged=changed,notes.push(`Your checkout changed during the run: ${changed.join("; ")}.`)),result.notes=notes,result}function finish(source,clone,delivered,options){let{delivered:inSource,...result}=delivered,reason=result.exportError?"export failed: the repository branch does not contain the verified tree":options.keep?"kept on request (--keep-temp)":result.sourceChanged?.length&&result.verification.passed?"your checkout changed during the run":!inSource&&!options.pushed&&result.verification.passed?"it holds commits that are not in your repository or on the remote yet":void 0;reason?result.clone={path:clone,kept:!0,reason}:(removeRun(clone),result.clone={path:clone,kept:!1},result.repo=source);let dir=storedRunDir(source,result.branch);return mkdirSync5(dir,{recursive:!0}),result.prBody=join13(dir,"pr-body.md"),writeFileSync8(result.prBody,prBody(result)),writeFileSync8(join13(dir,"report.json"),JSON.stringify(result,null,2)),result}var SIGNALS=["SIGINT","SIGTERM","SIGHUP"];function removeOnInterrupt(clones){let handlers=SIGNALS.map(signal=>{let handler=()=>{release();for(let clone of clones())removeRun(clone);process.kill(process.pid,signal)};return process.once(signal,handler),[signal,handler]}),release=()=>{for(let[signal,handler]of handlers)process.off(signal,handler)};return release}var watcher;function watchInterruptibleClones(watch){watcher=watch}async function keeping(clone,keep,work){let release=keep||!isMainThread?()=>{}:removeOnInterrupt(()=>[clone]);keep||watcher?.(clone,!0);try{return await work()}catch(error){throw keep?error instanceof Error&&(error.message+=`
411
411
  Temporary clone kept: ${clone}`):removeRun(clone),error}finally{release(),keep||watcher?.(clone,!1)}}function remoteDefaultBranch(root){let head2=tryGit(root,"symbolic-ref","--quiet","refs/remotes/origin/HEAD"),candidates=head2?[head2.replace(/^refs\/remotes\//,"")]:["origin/main","origin/master"];for(let ref of candidates)if(tryGit(root,"rev-parse","--verify","--quiet",ref))return{ref,name:ref.replace(/^origin\//,"")}}var commits=n=>`${n} commit${n===1?"":"s"}`;function fixPreflight(cwd,options={}){let{top:source,project}=projectRoot(cwd),head2=git(source,"rev-parse","HEAD"),at=head2.slice(0,12),name=tryGit(source,"branch","--show-current"),branch=name||`detached HEAD ${at}`,notes=[],manifest2=project==="."?"package.json":`${project}/package.json`;if(!tryGit(source,"cat-file","-t",`${head2}:${manifest2}`))throw new UptideError("NOT_REPOSITORY_ROOT",`${manifest2} is not in commit ${at} (${branch}) of ${source}. fix clones that commit from the local repository, so the file must be committed first.`);let dirty=git(source,"status","--porcelain","--untracked-files=all").split(`
412
- `).filter(Boolean).map(line=>line.replace(/^\s*\S{1,2}\s+/,""));if(dirty.length){let files2=`${dirty.slice(0,10).join(", ")}${dirty.length>10?`, and ${dirty.length-10} more`:""}`;if(options.pr&&!options.allowDirty)throw new UptideError("DIRTY_WORKING_TREE",`The working tree has uncommitted changes, and the PR is made from commit ${at}: ${files2}. Commit or stash them, or pass --allow-dirty to leave them out.`);notes.push(`Uncommitted changes are not part of the migration, made from commit ${at}: ${files2}`)}let base=options.pr?options.base?{ref:`origin/${options.base}`,name:options.base}:remoteDefaultBranch(source):void 0;if(options.pr){if(options.base&&!tryGit(source,"rev-parse","--verify","--quiet",`origin/${options.base}`))throw new UptideError("PUBLICATION_REFUSED",`--base ${options.base}: origin/${options.base} is not known in this repository. Push or fetch it first.`);let count2=range=>base&&Number(tryGit(source,"rev-list","--count",range))||0,ahead=count2(`${base?.ref}..${head2}`);if(base&&ahead>0)throw new UptideError("PUBLICATION_REFUSED",`${branch} is ${commits(ahead)} ahead of ${base.ref}. Push it first (\`git push origin ${name||"HEAD:<branch>"}\`) so the PR only contains the migration, or pass --base <branch>. If you pushed from elsewhere, run \`git fetch origin\` and try again.`);let behind=count2(`${head2}..${base?.ref}`);base&&behind>0&&notes.push(`${branch} is ${commits(behind)} behind ${base.ref}; the PR will be based on an older commit. Consider \`git pull\` first.`)}let upstream=tryGit(source,"rev-parse","--abbrev-ref","--symbolic-full-name","@{upstream}");if(upstream&&upstream!==base?.ref){let[behind=0,ahead=0]=tryGit(source,"rev-list","--left-right","--count",`${upstream}...${head2}`).split(/\s+/).map(Number),how=ahead&&behind?`${commits(ahead)} ahead of ${upstream} and ${commits(behind)} behind it`:ahead?`${commits(ahead)} ahead of ${upstream}`:behind?`${commits(behind)} behind ${upstream}`:"";how&&notes.push(`${branch} is ${how}; migrating your local state`)}return{head:head2,notes}}async function isolatedFix(options,services){let selected=selectLlm(options.cwd,options);if(options.maxCostUsd!==void 0&&(!Number.isFinite(options.maxCostUsd)||options.maxCostUsd<=0))throw new Error("--max-cost must be a positive finite amount in USD");if(!options.fixer&&!activePack(options.only)&&!options.pack?.rules.length&&(options.fixer===null||!selected.available))throw new UptideError("NO_FIXER",`No agent available. Set the selected provider's key in one of: ${ACCEPTED_KEYS}. Rule-based packs also work with --no-llm.`);options={...options,provider:selected.provider,model:selected.model};let{top:source,project}=projectRoot(options.cwd),preflight=fixPreflight(options.cwd,options);confirmServices(source,options);let tool=options.tool??uptideVersionInfo();if(options.pr&&tool.uptideDirty)throw new UptideError("DIRTY_UPTIDE_TREE",`uptide fix --pr refuses to run from an Uptide checkout with uncommitted changes (at ${tool.uptideCommit.slice(0,12)}); commit or stash them, rebuild, and run again`);let target=await rangePreflight(options,services,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));target&&(options={...options,target}),options.pinCurrentApi||assertNpmLockSync(options.cwd,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));let peerPlan=await peerPreflight(options,services,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));peerPlan&&(options={...options,target:peerPlan.target,peerPlan});let before=snapshot(source),base=options.base??remoteDefaultBranch(source)?.name,clone=isolate(source,void 0,project),{keep,base:_base,allowDirty:_allowDirty,preflightShown,...rest}=options,report=await keeping(clone,keep===!0,async()=>{git(clone,"switch","--quiet","--detach",preflight.head);let result2=await fix({...rest,pr:!1,forPublication:options.pr===!0,cwd:join13(clone,project),tool},services);return preflightShown||result2.notes.push(...preflight.notes),result2}),delivered=deliver(source,clone,report,before,!0);base&&(delivered.base=base),options.base&&(delivered.prBase=options.base);let landed=delivered.delivered,result=finish(source,clone,delivered,{...keep?{keep}:{},pushed:!1});if(!options.pr||result.exportError)return result;await(services?.publish??publishVerified)(result,{tool,...options.yes?{yes:!0}:{},...landed?{cwd:source}:{}});let dir=storedRunDir(source,result.branch);return writeFileSync7(result.prBody,prBody(result)),writeFileSync7(join13(dir,"report.json"),JSON.stringify(result,null,2)),result}async function isolatedVerify(options,services){let{top:source,project}=projectRoot(options.cwd),before=snapshot(source),branch=options.branch??before.branch;if(!branch)throw new UptideError("ANALYSIS_FAILED","name the migration branch to verify");let stored=options.run?resolve8(options.run):storedRunFile(source,branch);if(!stored)throw new UptideError("ANALYSIS_FAILED",`no stored migration run for ${branch} in this repository`);if(options.push&&!options.yes)throw new UptideError("PUBLICATION_REFUSED",`--push would push new commits to ${branch} on origin (fast-forward only); add --yes to confirm`);let clone=isolate(source,branch,project),{branch:_branch,push:_push,keep,...rest}=options,result=await keeping(clone,keep===!0,async()=>{let root=join13(clone,project);await(services?.install??install)(root),mkdirSync4(join13(root,".uptide"),{recursive:!0}),writeFileSync7(join13(root,".uptide/report.json"),readFileSync13(stored));let report=await reverify({...rest,cwd:root,run:join13(root,".uptide/report.json")},services),delivered=deliver(source,clone,report,before),pushed=!1;return options.push&&delivered.verification.passed&&!delivered.sourceChanged?.length&&(assertVerifiedTree(source,delivered),assertVerifiedTree(clone,delivered),git(clone,"push","origin",`${delivered.head}:refs/heads/${branch}`),delivered.notes.push(`Pushed ${delivered.head?.slice(0,8)} to origin/${branch} (fast-forward).`),pushed=!0),{delivered,pushed}});return finish(source,clone,result.delivered,{...keep?{keep}:{},pushed:result.pushed})}async function openPr(options,print=console.log,io={git,command,print}){let{top,root}=projectRoot(options.cwd),branch=options.branch??git(root,"branch","--show-current");if(!branch)throw new Error(`name the migration branch: ${UPTIDE_COMMAND} pr --branch uptide/<package>-<version>`);let stored=storedRunFile(top,branch);if(!stored)throw new Error(`no stored migration run for ${branch} in this repository; run \`${UPTIDE_COMMAND} fix\` first, or \`${UPTIDE_COMMAND} verify --branch ${branch}\``);let report=JSON.parse(readFileSync14(stored,"utf8"));if(report.branch!==branch)throw new Error(`the stored run is for ${report.branch}, not ${branch}`);refreshReview(report),report.prBody=join14(dirname11(stored),"pr-body.md"),writeFileSync8(report.prBody,prBody(report));let url=await publish(report,options.yes,io,{cwd:top,...options.repo?{repo:options.repo}:{},...options.draft===void 0?{}:{draft:options.draft}});return report.prUrl=url,writeFileSync8(stored,JSON.stringify(report,null,2)),{url,report}}function retainedDiffs(report,readGit=git){let result=structuredClone(report);if(result.sites.every(s=>s.diff||s.resolvedBy||s.reason.startsWith("diagnostic resolved by an earlier")))return result;let commits2;try{commits2=readGit(result.repo,"log","--format=%H%x09%s",result.head??"HEAD","-100").split(`
412
+ `).filter(Boolean).map(line=>line.replace(/^\s*\S{1,2}\s+/,""));if(dirty.length){let files2=`${dirty.slice(0,10).join(", ")}${dirty.length>10?`, and ${dirty.length-10} more`:""}`;if(options.pr&&!options.allowDirty)throw new UptideError("DIRTY_WORKING_TREE",`The working tree has uncommitted changes, and the PR is made from commit ${at}: ${files2}. Commit or stash them, or pass --allow-dirty to leave them out.`);notes.push(`Uncommitted changes are not part of the migration, made from commit ${at}: ${files2}`)}let base=options.pr?options.base?{ref:`origin/${options.base}`,name:options.base}:remoteDefaultBranch(source):void 0;if(options.pr){if(options.base&&!tryGit(source,"rev-parse","--verify","--quiet",`origin/${options.base}`))throw new UptideError("PUBLICATION_REFUSED",`--base ${options.base}: origin/${options.base} is not known in this repository. Push or fetch it first.`);let count2=range=>base&&Number(tryGit(source,"rev-list","--count",range))||0,ahead=count2(`${base?.ref}..${head2}`);if(base&&ahead>0)throw new UptideError("PUBLICATION_REFUSED",`${branch} is ${commits(ahead)} ahead of ${base.ref}. Push it first (\`git push origin ${name||"HEAD:<branch>"}\`) so the PR only contains the migration, or pass --base <branch>. If you pushed from elsewhere, run \`git fetch origin\` and try again.`);let behind=count2(`${head2}..${base?.ref}`);base&&behind>0&&notes.push(`${branch} is ${commits(behind)} behind ${base.ref}; the PR will be based on an older commit. Consider \`git pull\` first.`)}let upstream=tryGit(source,"rev-parse","--abbrev-ref","--symbolic-full-name","@{upstream}");if(upstream&&upstream!==base?.ref){let[behind=0,ahead=0]=tryGit(source,"rev-list","--left-right","--count",`${upstream}...${head2}`).split(/\s+/).map(Number),how=ahead&&behind?`${commits(ahead)} ahead of ${upstream} and ${commits(behind)} behind it`:ahead?`${commits(ahead)} ahead of ${upstream}`:behind?`${commits(behind)} behind ${upstream}`:"";how&&notes.push(`${branch} is ${how}; migrating your local state`)}return{head:head2,notes}}async function isolatedFix(options,services){let selected=selectLlm(options.cwd,options);if(options.maxCostUsd!==void 0&&(!Number.isFinite(options.maxCostUsd)||options.maxCostUsd<=0))throw new Error("--max-cost must be a positive finite amount in USD");if(!options.fixer&&!activePack(options.only)&&!options.pack?.rules.length&&(options.fixer===null||!selected.available))throw new UptideError("NO_FIXER",`No agent available. Set the selected provider's key in one of: ${ACCEPTED_KEYS}. Rule-based packs also work with --no-llm.`);options={...options,provider:selected.provider,model:selected.model};let{top:source,project}=projectRoot(options.cwd),preflight=fixPreflight(options.cwd,options);confirmServices(source,options);let tool=options.tool??uptideVersionInfo();if(options.pr&&tool.uptideDirty)throw new UptideError("DIRTY_UPTIDE_TREE",`uptide fix --pr refuses to run from an Uptide checkout with uncommitted changes (at ${tool.uptideCommit.slice(0,12)}); commit or stash them, rebuild, and run again`);let target=await rangePreflight(options,services,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));target&&(options={...options,target}),options.pinCurrentApi||assertNpmLockSync(options.cwd,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));let peerPlan=await peerPreflight(options,services,file=>git(source,"show",`${preflight.head}:${join13(project,file)}`));peerPlan&&(options={...options,target:peerPlan.target,peerPlan});let before=snapshot(source),base=options.base??remoteDefaultBranch(source)?.name,clone=isolate(source,void 0,project),{keep,base:_base,allowDirty:_allowDirty,preflightShown,...rest}=options,report=await keeping(clone,keep===!0,async()=>{git(clone,"switch","--quiet","--detach",preflight.head);let result2=await fix({...rest,pr:!1,forPublication:options.pr===!0,cwd:join13(clone,project),tool},services);return preflightShown||result2.notes.push(...preflight.notes),result2}),delivered=deliver(source,clone,report,before,!0);base&&(delivered.base=base),options.base&&(delivered.prBase=options.base);let landed=delivered.delivered,result=finish(source,clone,delivered,{...keep?{keep}:{},pushed:!1});if(!options.pr||result.exportError)return result;await(services?.publish??publishVerified)(result,{tool,...options.yes?{yes:!0}:{},...landed?{cwd:source}:{}});let dir=storedRunDir(source,result.branch);return writeFileSync8(result.prBody,prBody(result)),writeFileSync8(join13(dir,"report.json"),JSON.stringify(result,null,2)),result}async function isolatedVerify(options,services){let{top:source,project}=projectRoot(options.cwd),before=snapshot(source),branch=options.branch??before.branch;if(!branch)throw new UptideError("ANALYSIS_FAILED","name the migration branch to verify");let stored=options.run?resolve9(options.run):storedRunFile(source,branch);if(!stored)throw new UptideError("ANALYSIS_FAILED",`no stored migration run for ${branch} in this repository`);if(options.push&&!options.yes)throw new UptideError("PUBLICATION_REFUSED",`--push would push new commits to ${branch} on origin (fast-forward only); add --yes to confirm`);let clone=isolate(source,branch,project),{branch:_branch,push:_push,keep,...rest}=options,result=await keeping(clone,keep===!0,async()=>{let root=join13(clone,project);await(services?.install??install)(root),mkdirSync5(join13(root,".uptide"),{recursive:!0}),writeFileSync8(join13(root,".uptide/report.json"),readFileSync14(stored));let report=await reverify({...rest,cwd:root,run:join13(root,".uptide/report.json")},services),delivered=deliver(source,clone,report,before),pushed=!1;return options.push&&delivered.verification.passed&&!delivered.sourceChanged?.length&&(assertVerifiedTree(source,delivered),assertVerifiedTree(clone,delivered),git(clone,"push","origin",`${delivered.head}:refs/heads/${branch}`),delivered.notes.push(`Pushed ${delivered.head?.slice(0,8)} to origin/${branch} (fast-forward).`),pushed=!0),{delivered,pushed}});return finish(source,clone,result.delivered,{...keep?{keep}:{},pushed:result.pushed})}async function openPr(options,print=console.log,io={git,command,print}){let{top,root}=projectRoot(options.cwd),branch=options.branch??git(root,"branch","--show-current");if(!branch)throw new Error(`name the migration branch: ${UPTIDE_COMMAND} pr --branch uptide/<package>-<version>`);let stored=storedRunFile(top,branch);if(!stored)throw new Error(`no stored migration run for ${branch} in this repository; run \`${UPTIDE_COMMAND} fix\` first, or \`${UPTIDE_COMMAND} verify --branch ${branch}\``);let report=JSON.parse(readFileSync15(stored,"utf8"));if(report.branch!==branch)throw new Error(`the stored run is for ${report.branch}, not ${branch}`);refreshReview(report),report.prBody=join14(dirname12(stored),"pr-body.md"),writeFileSync9(report.prBody,prBody(report));let url=await publish(report,options.yes,io,{cwd:top,...options.repo?{repo:options.repo}:{},...options.draft===void 0?{}:{draft:options.draft}});return report.prUrl=url,writeFileSync9(stored,JSON.stringify(report,null,2)),{url,report}}function retainedDiffs(report,readGit=git){let result=structuredClone(report);if(result.sites.every(s=>s.diff||s.resolvedBy||s.reason.startsWith("diagnostic resolved by an earlier")))return result;let commits2;try{commits2=readGit(result.repo,"log","--format=%H%x09%s",result.head??"HEAD","-100").split(`
413
413
  `).map(line=>{let[sha="",...subject]=line.split(" ");return{sha,subject:subject.join(" ")}})}catch{return result}for(let site of result.sites){if(site.diff)continue;let file=site.finding.usage.file,commit2=commits2.find(c=>c.subject===(site.outcome==="mechanical"?`fix(${result.package}): apply mechanical migrations`:`fix(${result.package}): migrate ${file}:${site.finding.usage.line}`));if(commit2)try{if(site.outcome==="mechanical"){let before=readGit(result.repo,"show",`${commit2.sha}^:${file}`),after=readGit(result.repo,"show",`${commit2.sha}:${file}`),patch=editDiff(before,after),oldLine=before.split(`
414
414
  `)[site.finding.usage.line-1]?.trim(),removed=patch.split(`
415
415
  `).indexOf(`- ${oldLine}`),oldLines=patch.split(`
416
416
  `).filter(line=>line.startsWith("- ")),newLines=patch.split(`
417
417
  `).filter(line=>line.startsWith("+ ")),at=oldLines.indexOf(`- ${oldLine}`);site.diff=removed>=0&&oldLines.length===newLines.length?[oldLines[at],newLines[at]].join(`
418
- `):readGit(result.repo,"diff","--unified=0",`${commit2.sha}^`,commit2.sha,"--",file).split(/(?=^@@)/m).slice(0,2).join("")}else{site.diff=readGit(result.repo,"diff","--unified=3",`${commit2.sha}^`,commit2.sha,"--",file);let accepted=site.attempts?.findLast(a=>a.outcome==="accepted");accepted&&(accepted.diff=site.diff)}}catch{}}return result}function runDir(cwd,branch,readGit){let dir=".git";try{dir=readGit(cwd,"rev-parse","--git-common-dir")||".git"}catch{}return join15(isAbsolute4(dir)?dir:resolve9(cwd,dir),"uptide",branch.replaceAll("/","__"))}async function updatePrBody(options,io={command,git}){let cwd=resolve9(options.cwd);if(!/^\d+$/.test(options.pr))throw new Error("--pr must be a pull request number");let origin;try{origin=/github\.com[:/]([^/]+\/[^/\s]+?)(?:\.git)?$/.exec(io.git(cwd,"remote","get-url","origin"))?.[1]}catch{origin=void 0}let inRepo=origin?["--repo",origin]:[],view=await io.command(cwd,"gh",["pr","view",options.pr,...inRepo,"--json","url,headRefName,headRefOid"]);if(view.code)throw new Error(`cannot read PR: ${view.output}`);let pr=JSON.parse(view.output),stored=options.run??(existsSync8(join15(cwd,".uptide/report.json"))?join15(cwd,".uptide/report.json"):join15(runDir(cwd,pr.headRefName,io.git),"report.json")),report;try{report=JSON.parse(readFileSync15(resolve9(stored),"utf8"))}catch{throw new Error(`no stored migration run for ${pr.headRefName} (looked in ${stored})`)}if(!Array.isArray(report.sites)||!report.verification||!report.package||!report.target||!report.branch)throw new Error("not a stored migration run");if(report.branch!==pr.headRefName||report.prUrl&&report.prUrl!==pr.url)throw new Error("stored run does not match the selected PR");if(report.remote&&!pr.url.startsWith(`${report.remote}/pull/`))throw new Error("stored run belongs to another repository");let head2=report.head;if(!head2){try{head2=io.git(report.repo,"rev-parse","HEAD")}catch{throw new Error("legacy run needs its retained checkout to verify the PR commit")}let remote=normalizeRemote(io.git(report.repo,"remote","get-url","origin"));if(!pr.url.startsWith(`${remote}/pull/`))throw new Error("stored run belongs to another repository")}if(head2!==pr.headRefOid)throw new Error("PR head differs from the stored run; rerun verification before updating its description");refreshReview(report);let body=prBody(retainedDiffs({...report,head:head2},io.git));if(!options.preview){let blockers=publicationBlockers(report,io.tool);if(blockers.length)throw new UptideError("PUBLICATION_REFUSED",`PR description not updated: ${blockers.join("; ")}. Use --preview to read the body locally.`);let temp=mkdtempSync3(join15(tmpdir3(),"uptide-pr-body-"));try{let file=join15(temp,"body.md");writeFileSync9(file,body);let edited=await io.command(cwd,"gh",["pr","edit",options.pr,...inRepo,"--body-file",file]),repository=/^https:\/\/[^/]+\/([^/]+\/[^/]+)\/pull\/\d+/.exec(pr.url)?.[1];if(edited.code&&/projectCards|Projects \(classic\)/.test(edited.output)&&repository&&(edited=await io.command(cwd,"gh",["api","--method","PATCH",`repos/${repository}/pulls/${options.pr}`,"--field",`body=@${file}`,"--silent"])),edited.code)throw new Error(`cannot update PR description: ${edited.output}`)}finally{rmSync4(temp,{recursive:!0,force:!0})}}return{body,url:pr.url,updated:!options.preview}}var URGENCY={security:5,deprecated:4,unsupported:3,drift:2.5,blocking:2,behind:1},tierOfSignal=signal=>signal==="security"||signal==="deprecated"?"urgent":"planning",SEVERITIES=["critical","high","moderate","low"],inRange=(version2,range)=>{try{return satisfies(version2,range)}catch{return!1}},stable=v=>{let parsed=parseVersion(v);return!!parsed&&!parsed.pre};function securitySignal(installed2,advisories,versions){let currents=[installed2].flat(),hits=advisories.filter(a=>currents.some(v=>inRange(v,a.vulnerable_versions)));if(!hits.length)return;let current=currents.filter(v=>hits.some(a=>inRange(v,a.vulnerable_versions))).sort(compareVersions)[0],highest=currents.sort(compareVersions).at(-1),severity=a=>SEVERITIES.includes(a.severity)?a.severity:"low",counts={};for(let a of hits)counts[severity(a)]=(counts[severity(a)]??0)+1;let fixedIn=versions.filter(v=>stable(v)&&compareVersions(v,highest)>0&&!hits.some(a=>inRange(v,a.vulnerable_versions))).sort(compareVersions)[0],from=parseVersion(current),to=fixedIn?parseVersion(fixedIn):void 0,fixChange=from&&to?to.major!==from.major?"major":to.minor!==from.minor?"minor":"patch":void 0;return{advisories:hits.length,worst:SEVERITIES.find(s=>counts[s]),counts,...fixedIn?{fixedIn}:{},...fixChange?{fixChange}:{}}}function deprecatedSignal(message){return message?.replace(/\s+/g," ").trim()||void 0}var YEAR_MS=365*24*60*60*1e3;function unsupportedSignal(current,latest,published,now){let major=parseVersion(current)?.major;if(major===void 0||(parseVersion(latest)?.major??major)<=major)return;let last=Object.entries(published).filter(([v])=>stable(v)&&parseVersion(v)?.major===major).map(([,when])=>Date.parse(when)).filter(t=>!Number.isNaN(t)).sort((a,b)=>b-a)[0];if(!(last===void 0||now.getTime()-last<YEAR_MS))return{since:new Date(last).toISOString().slice(0,7)}}function effortOf(usage2,verified){let raw=usage2.files+usage2.callSites/10;return Math.round((verified?raw/2:raw)*10)/10}function urgencyOf(s){if(s.security)return{signal:"security",urgency:Math.round((URGENCY.security+(SEVERITIES.length-SEVERITIES.indexOf(s.security.worst))/10-(s.dev?.1:0))*10)/10};if(s.deprecated)return{signal:"deprecated",urgency:URGENCY.deprecated};if(s.unsupported)return{signal:"unsupported",urgency:URGENCY.unsupported};if(s.drift)return{signal:"drift",urgency:URGENCY.drift};if(s.blocks?.length||s.movesWith?.length)return{signal:"blocking",urgency:URGENCY.blocking};if(s.behind>=2)return{signal:"behind",urgency:URGENCY.behind}}var files=n=>`${n} file${n===1?"":"s"} to touch`,truncate=(text,max)=>text.length<=max?text:`${text.slice(0,max-1).trimEnd()}\u2026`;function reasonOf(s,current,usageFiles){let text=signalText(s,current,usageFiles);return text&&s.dev?`dev \xB7 ${text}`:text}function fixText(sec){return sec.fixedIn?sec.fixChange==="major"?`needs ${sec.fixedIn} (major)`:`fixed in ${sec.fixedIn} (${sec.fixChange}, same major)`:"no fixed version yet"}function signalText(s,current,usageFiles){let top=urgencyOf(s)?.signal,touch=usageFiles?`, ${files(usageFiles)}`:"";switch(top){case"security":{let sec=s.security,n=sec.advisories;return`${n} ${n===1?"advisory":"advisories"} (${sec.counts[sec.worst]} ${sec.worst}), ${fixText(sec)}`}case"deprecated":return`deprecated: ${truncate(s.deprecated,60)}`;case"unsupported":return`${parseVersion(current)?.major}.x line unsupported since ${s.unsupported?.since}${touch}`;case"drift":{let majors=(s.drift?.majors??[]).map(m=>`${m}.x`);return`version drift: ${majors.slice(0,-1).join(", ")} and ${majors.at(-1)} across ${s.drift?.workspaces} workspaces`}case"blocking":return s.blocks?.length?`blocks ${s.blocks.join(", ")}`:`moves with ${(s.movesWith??[]).join(", ")}`;case"behind":return`${s.behind} majors behind${touch}`;default:return""}}var tieBreak=(a,b)=>+!!a.dev-+!!b.dev||+!!b.sameMajorFix-+!!a.sameMajorFix||a.effort-b.effort;function rankPriorities(items){return[...items].sort((a,b)=>b.urgency-a.urgency||tieBreak(a,b)||a.name.localeCompare(b.name))}function rowOf(p,s){let fix2=urgencyOf(s)?.signal==="security"?s.security:void 0;return{...s.dev?{dev:!0}:{},...fix2?.fixChange&&fix2.fixChange!=="major"?{sameMajorFix:!0}:{},...fix2?.fixedIn?{target:`${p.name}@${fix2.fixedIn}`}:{}}}var CHEAP_FILES=3;function prioritize(packages,groups){let rows=[],grouped=new Set,top=members=>members.map(p=>({p,u:p.signals?urgencyOf(p.signals):void 0})).filter(x=>!!x.u).sort((a,b)=>{let sa=a.p.signals,sb=b.p.signals;return b.u.urgency-a.u.urgency||+!!sa.dev-+!!sb.dev||+!!rowOf(b.p,sb).sameMajorFix-+!!rowOf(a.p,sa).sameMajorFix||sb.behind-sa.behind||sa.effort-sb.effort})[0];for(let g of groups){for(let p of g.members)grouped.add(p);let inGroup=new Set(g.members.map(p=>p.name)),members=g.members.map(p=>{if(!p.signals)return p;let blocks=p.signals.blocks?.filter(b=>!inGroup.has(b.slice(0,b.lastIndexOf(" "))));return{...p,signals:{...p.signals,movesWith:void 0,blocks:blocks?.length?blocks:void 0}}}),best=top(members);if(!best)continue;let reason=reasonOf(best.p.signals,best.p.current,best.p.usage.files);rows.push({name:g.name,group:g.id,packages:[...new Set(g.members.map(p=>p.name))],signal:best.u.signal,tier:tierOfSignal(best.u.signal),urgency:best.u.urgency,effort:g.members.reduce((n,p)=>n+(p.signals?.effort??0),0),reason:best.p.name===(g.lead??g.name)?reason:reason.startsWith("dev \xB7 ")?`dev \xB7 ${best.p.name} ${reason.slice(6)}`:`${best.p.name} ${reason}`,...rowOf(best.p,best.p.signals)})}for(let p of packages){if(grouped.has(p)||!p.signals)continue;let u=urgencyOf(p.signals);u&&rows.push({name:p.name,packages:[p.name],signal:u.signal,tier:tierOfSignal(u.signal),urgency:u.urgency,effort:p.signals.effort,reason:reasonOf(p.signals,p.current,p.usage.files),...rowOf(p,p.signals)})}let priorities=rankPriorities(rows),urgent=new Set(priorities.flatMap(r=>r.packages)),cheap=p=>p.change!=="major"&&!urgent.has(p.name)&&p.classification!=="possibly-unused"&&p.usage.files<=CHEAP_FILES&&!(p.signals&&urgencyOf(p.signals)),groupOf=new Map(groups.flatMap(g=>g.members.map(p=>[p,g]))),cheapBatch=[...new Set(packages.filter(p=>cheap(p)&&(groupOf.get(p)?.members.every(cheap)??!0)).sort((a,b)=>(a.signals?.effort??0)-(b.signals?.effort??0)).map(p=>p.name))];return{priorities,cheapBatch}}var DiscoveryRegistryError=class extends Error{constructor(code,reason,host,summary,status){super(reason),this.code=code,this.host=host,this.summary=summary,this.status=status}code;host;summary;status};function registryHost(registry){try{return new URL(registry).hostname}catch{return"configured registry"}}function registryFailure(error,host){if(error instanceof DiscoveryRegistryError)return error.message;switch(errorCode(error)){case"REGISTRY_AUTH":return`auth required for ${host}: check your .npmrc token. Skipped.`;case"PACKAGE_NOT_FOUND":return`not found on ${host}, skipped`;case"VERSION_NOT_FOUND":return`version metadata unavailable on ${host}, skipped`;default:return`registry request failed on ${host}, skipped`}}function httpError(host,status){let summary=status===401?"auth required (401)":status===403?"access denied (403)":status===404?"not found (404)":status===405?"method not allowed (405)":`registry request failed (${status})`,reason=status===401?`${summary} for ${host}: check your .npmrc token. Skipped.`:status===403?`${summary} on ${host}, your token can't read this package. Skipped.`:`${summary} on ${host}, skipped`;return new DiscoveryRegistryError([401,403,405].includes(status)?"REGISTRY_AUTH":"REGISTRY_HTTP_ERROR",reason,host,summary,status)}function isTimeout(error){return!error||typeof error!="object"?!1:"name"in error&&error.name==="TimeoutError"||"code"in error&&["ETIMEDOUT","UND_ERR_CONNECT_TIMEOUT","UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT"].includes(String(error.code))?!0:"cause"in error&&error.cause!==error&&isTimeout(error.cause)}var NPM_REGISTRY="registry.npmjs.org";function createDiscoveryFetcher(opts){let config=opts.config??loadRegistryConfig({cwd:opts.cwd}),transport=opts.fetch??fetch,timeoutMs=opts.timeoutMs??1e4,requests=new Map,blockedHosts=new Map,ask=name=>{let existing=requests.get(name);if(existing)return existing;let registry=registryFor(name,config),host=registryHost(registry),request=(async()=>{for(let attempt=0;;attempt++){let blocked=blockedHosts.get(host);if(blocked)throw blocked;let controller=new AbortController,timer,timeout=new DiscoveryRegistryError("REGISTRY_UNREACHABLE",`timed out on ${host}, skipped`,host,"timed out");try{return await Promise.race([(async()=>{let url=packumentUrl(name,registry.replace(/\/$/,"")),response=await transport(url,{headers:{accept:"application/vnd.npm.install-v1+json",...authHeaders(url,config)},signal:controller.signal});if(!response.ok){let error=httpError(host,response.status);throw error.code==="REGISTRY_AUTH"&&blockedHosts.set(host,error),response.body?.cancel().catch(()=>{}),error}return await response.json()})(),new Promise((_,reject)=>{timer=setTimeout(()=>{reject(timeout),controller.abort()},timeoutMs)})])}catch(error){let safe=error instanceof DiscoveryRegistryError?error:isTimeout(error)?timeout:new DiscoveryRegistryError("REGISTRY_UNREACHABLE",`network request failed on ${host}, skipped`,host,"network request failed");if(attempt===0&&(safe===timeout||safe.status!==void 0&&safe.status>=500&&safe.status<=599))continue;throw safe}finally{clearTimeout(timer)}}})();return requests.set(name,request),request},manifest2=async(name,spec)=>{let data=await ask(name),version2=data["dist-tags"]?.[spec]??(spec==="latest"?Object.keys(data.versions??{}).filter(v=>parseVersion(v)&&!parseVersion(v)?.pre).sort(compareVersions).at(-1):spec),entry=version2&&data.versions?.[version2];if(!entry||!version2){let host=registryHost(registryFor(name,config));throw new DiscoveryRegistryError("REGISTRY_HTTP_ERROR",`version metadata unavailable on ${host}, skipped`,host,"version metadata unavailable")}return{...entry,version:version2}},signalTimeoutMs=opts.signalTimeoutMs??5e3,once=async(url,init,ms=signalTimeoutMs)=>{let signal=AbortSignal.timeout(ms),response=await transport(url,{...init,signal});if(!response.ok)throw response.body?.cancel().catch(()=>{}),new Error(`${registryHost(url)} answered ${response.status}`);return await response.json()},times=new Map,published=(name,ms)=>{let request=times.get(name);if(!request){let url=packumentUrl(name,registryFor(name,config).replace(/\/$/,""));request=once(url,{headers:{accept:"application/json",...authHeaders(url,config)}},ms).then(doc=>doc.time??{}),times.set(name,request)}return request},policies=config.releaseAge??[],held=new Map;return{resolve:async(name,spec)=>{let latest=(await manifest2(name,spec)).version;if(!policies.length||latest===spec)return latest;let data=await ask(name);if(data.modified&&Date.parse(data.modified)<windowStart(policies))return latest;let time=await published(name,timeoutMs).catch(()=>{let host=registryHost(registryFor(name,config));throw new DiscoveryRegistryError("REGISTRY_HTTP_ERROR",`publish dates unavailable on ${host}, needed for the release-age policy; skipped`,host,"publish dates unavailable")}),result=newestAllowed(policies,name,latest,Object.keys(data.versions??{}),time);if(result.held&&held.set(name,result.held),!result.version)throw releaseAgeError(name,result.held);return result.version},heldBack:name=>held.get(name),deprecation:async(name,version2)=>(await ask(name)).versions?.[version2]?.deprecated,versions:async name=>Object.keys((await ask(name)).versions??{}),published:name=>published(name),advisories:async query=>{let body=Object.fromEntries([...query].filter(([name])=>registryHost(registryFor(name,config))===NPM_REGISTRY)),checked=Object.keys(body);if(!checked.length)return{checked,advisories:{}};let advisories=await once(`https://${NPM_REGISTRY}/-/npm/v1/security/advisories/bulk`,{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify(body)});return{checked,advisories}},metadata:async(name,version2)=>{let result=await manifest2(name,version2);return{...result.dependencies?{dependencies:result.dependencies}:{},peerDependencies:result.peerDependencies??{},bin:result.bin}}}}function dependencySource(name,value){if(typeof value!="string")return{kind:"invalid"};let spec=value.trim(),registryName=name;for(;;){if(spec.startsWith("npm:")){spec=spec.slice(4);let direct=nonRegistrySource(spec);if(direct)return{kind:"non-registry",source:direct};let alias=/^(@[\w.-]+\/[\w.-]+|[\w.-]+)(?:@(.*))?$/.exec(spec);if(!alias)return{kind:"invalid"};registryName=alias[1],spec=alias[2]??"*";continue}let source=nonRegistrySource(spec);return source?{kind:"non-registry",source}:spec.includes(":")||spec.includes("/")?{kind:"invalid"}:{kind:"registry",name:registryName,range:spec}}}function nonRegistrySource(spec){let protocol=/^([a-z][a-z\d+.-]*):/i.exec(spec)?.[1]?.toLowerCase();if(protocol?.startsWith("git+")||protocol==="git"||protocol==="ssh")return"git";if(protocol&&["github","gitlab","bitbucket","file","link","workspace","http","https"].includes(protocol))return protocol;if(/^git@[^:]+:/.test(spec))return"git";if(/^(?:\.{1,2}[/\\]|~[/\\]|[/\\]|[a-z]:[/\\])/i.test(spec)||!/[@:]/.test(spec)&&/\.(?:tgz|tar\.gz)$/.test(spec))return"file";if(/^[\w.-]+\/[\w.-]+(?:#.*)?$/.test(spec))return"github"}function catalogSpec(dir,name,spec){if(typeof spec!="string"||!spec.startsWith("catalog:"))return spec;for(let current=resolve10(dir);;current=dirname12(current)){let file=join16(current,"pnpm-workspace.yaml");if(existsSync9(file)){let document=(0,import_yaml3.parseDocument)(readFileSync16(file,"utf8"),{logLevel:"silent"});if(document.errors.length)return;let catalog=spec.slice(8);return document.getIn(catalog?["catalogs",catalog,name]:["catalog",name])}if(dirname12(current)===current)return}}var compareText=(a,b)=>a<b?-1:a>b?1:0;async function listDependencies(opts){let start=Date.now(),manifestStart=performance.now(),adapter=opts.adapter??typescriptAdapter,selected=name=>(!opts.only||opts.only.includes(name))&&(!opts.packages||packageSelected({packages:opts.packages},name)),config=loadRegistryConfig({cwd:opts.cwd}),fetcher=opts.fetcher??createDiscoveryFetcher({cwd:opts.cwd,config}),workspaces=(await adapter.workspacePackages?.({dir:opts.cwd})??["."]).sort(),workspaceNames=new Set(workspaces.map(w=>JSON.parse(readFileSync17(join17(opts.cwd,w,"package.json"),"utf8")).name).filter(Boolean)),failures=[],blocked=new Set,identities=new Map,localName=key2=>identities.get(key2)?.name??key2,registryName=key2=>identities.get(key2)?.registryName??key2,fail=(name,error,context="")=>{let code=errorCode(error),registry=error instanceof DiscoveryRegistryError||code.startsWith("REGISTRY_")||code==="PACKAGE_NOT_FOUND"||code==="VERSION_NOT_FOUND";(code==="REGISTRY_AUTH"||code==="REGISTRY_UNREACHABLE"||code==="REGISTRY_TIMEOUT")&&blocked.add(name),failures.push({name:localName(name),...registry?{kind:"registry"}:{},...error instanceof DiscoveryRegistryError?{host:error.host,summary:error.summary,...error.status?{status:error.status}:{}}:{},reason:registry?registryFailure(error,registryHost(registryFor(registryName(name),config))):`${context}${error instanceof Error?error.message:String(error)}`})},manifests=[],declared=new Map,skipped=new Map,runtimeKeys=new Set,skip=(name,source,workspace)=>{if(!selected(localName(name)))return;let key2=JSON.stringify([name,source]),item=skipped.get(key2)??{name,source,reason:`not checked: non-registry source (${source})`,workspaces:[]};item.workspaces.includes(workspace)||item.workspaces.push(workspace),skipped.set(key2,item)};for(let workspace of workspaces){let dir=resolve11(opts.cwd,workspace),installed2=await adapter.installedDependencies?.({dir}).catch(error=>{if(error&&typeof error=="object"&&"code"in error&&error.code==="NO_LOCKFILE")return new Map;throw error})??new Map,manifest2=JSON.parse(readFileSync17(join17(dir,"package.json"),"utf8"));manifests.push(manifest2);for(let field of["dependencies","devDependencies","optionalDependencies"]){let value=manifest2[field];if(value!==void 0&&(!value||typeof value!="object"||Array.isArray(value)))throw new Error(`malformed package.json: ${field} must be an object`)}let runtimeNames=new Set([...Object.keys(manifest2.dependencies??{}),...Object.keys(manifest2.optionalDependencies??{})]);for(let[name,spec]of Object.entries({...manifest2.dependencies,...manifest2.devDependencies,...manifest2.optionalDependencies})){let source=dependencySource(name,catalogSpec(dir,name,spec));if(source.kind==="invalid"){failures.push({name,workspace,reason:"malformed dependency declaration in package.json"});continue}if(source.kind==="non-registry"){skip(name,source.source,workspace);continue}if(workspaceNames.has(name)&&source.name===name&&!spec.trim().startsWith("npm:")){skip(name,"workspace",workspace);continue}let locked=installed2.get(name),version2=(locked&&/^(?:npm:)?(?:@[\w.-]+\/)?[\w.-]+@(.+)$/.exec(locked))?.[1]??locked??(/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(source.range)?source.range:void 0);if(!version2||!parseVersion(version2)){failures.push({name,workspace,reason:"no locked or exact current version; install dependencies to resolve the declared range"});continue}let key2=JSON.stringify([name,source.name]);identities.set(key2,{name,registryName:source.name}),runtimeNames.has(name)&&runtimeKeys.add(key2);let versions=declared.get(key2)??new Map;versions.set(version2,[...versions.get(version2)??[],workspace]),declared.set(key2,versions)}}let manifestReadMs=performance.now()-manifestStart,names=[...declared.keys()].sort(),configs=[],fileEvidence=new Map,scanStats={sourceMs:0,configMs:0,visitedFiles:0,sourceFiles:0,configFiles:0,assetFiles:0},tasks=[],usage2=await scanImports(opts.cwd,[...new Set(names.map(localName))],workspaces,configs,fileEvidence,scanStats,tasks),scripts=manifests.flatMap(m=>Object.values(m.scripts??{})).join(`
418
+ `):readGit(result.repo,"diff","--unified=0",`${commit2.sha}^`,commit2.sha,"--",file).split(/(?=^@@)/m).slice(0,2).join("")}else{site.diff=readGit(result.repo,"diff","--unified=3",`${commit2.sha}^`,commit2.sha,"--",file);let accepted=site.attempts?.findLast(a=>a.outcome==="accepted");accepted&&(accepted.diff=site.diff)}}catch{}}return result}function runDir(cwd,branch,readGit){let dir=".git";try{dir=readGit(cwd,"rev-parse","--git-common-dir")||".git"}catch{}return join15(isAbsolute5(dir)?dir:resolve10(cwd,dir),"uptide",branch.replaceAll("/","__"))}async function updatePrBody(options,io={command,git}){let cwd=resolve10(options.cwd);if(!/^\d+$/.test(options.pr))throw new Error("--pr must be a pull request number");let origin;try{origin=/github\.com[:/]([^/]+\/[^/\s]+?)(?:\.git)?$/.exec(io.git(cwd,"remote","get-url","origin"))?.[1]}catch{origin=void 0}let inRepo=origin?["--repo",origin]:[],view=await io.command(cwd,"gh",["pr","view",options.pr,...inRepo,"--json","url,headRefName,headRefOid"]);if(view.code)throw new Error(`cannot read PR: ${view.output}`);let pr=JSON.parse(view.output),stored=options.run??(existsSync11(join15(cwd,".uptide/report.json"))?join15(cwd,".uptide/report.json"):join15(runDir(cwd,pr.headRefName,io.git),"report.json")),report;try{report=JSON.parse(readFileSync16(resolve10(stored),"utf8"))}catch{throw new Error(`no stored migration run for ${pr.headRefName} (looked in ${stored})`)}if(!Array.isArray(report.sites)||!report.verification||!report.package||!report.target||!report.branch)throw new Error("not a stored migration run");if(report.branch!==pr.headRefName||report.prUrl&&report.prUrl!==pr.url)throw new Error("stored run does not match the selected PR");if(report.remote&&!pr.url.startsWith(`${report.remote}/pull/`))throw new Error("stored run belongs to another repository");let head2=report.head;if(!head2){try{head2=io.git(report.repo,"rev-parse","HEAD")}catch{throw new Error("legacy run needs its retained checkout to verify the PR commit")}let remote=normalizeRemote(io.git(report.repo,"remote","get-url","origin"));if(!pr.url.startsWith(`${remote}/pull/`))throw new Error("stored run belongs to another repository")}if(head2!==pr.headRefOid)throw new Error("PR head differs from the stored run; rerun verification before updating its description");refreshReview(report);let body=prBody(retainedDiffs({...report,head:head2},io.git));if(!options.preview){let blockers=publicationBlockers(report,io.tool);if(blockers.length)throw new UptideError("PUBLICATION_REFUSED",`PR description not updated: ${blockers.join("; ")}. Use --preview to read the body locally.`);let temp=mkdtempSync3(join15(tmpdir3(),"uptide-pr-body-"));try{let file=join15(temp,"body.md");writeFileSync10(file,body);let edited=await io.command(cwd,"gh",["pr","edit",options.pr,...inRepo,"--body-file",file]),repository=/^https:\/\/[^/]+\/([^/]+\/[^/]+)\/pull\/\d+/.exec(pr.url)?.[1];if(edited.code&&/projectCards|Projects \(classic\)/.test(edited.output)&&repository&&(edited=await io.command(cwd,"gh",["api","--method","PATCH",`repos/${repository}/pulls/${options.pr}`,"--field",`body=@${file}`,"--silent"])),edited.code)throw new Error(`cannot update PR description: ${edited.output}`)}finally{rmSync5(temp,{recursive:!0,force:!0})}}return{body,url:pr.url,updated:!options.preview}}var URGENCY={security:5,deprecated:4,unsupported:3,drift:2.5,blocking:2,behind:1},tierOfSignal=signal=>signal==="security"||signal==="deprecated"?"urgent":"planning",SEVERITIES=["critical","high","moderate","low"],inRange=(version2,range)=>{try{return satisfies(version2,range)}catch{return!1}},stable=v=>{let parsed=parseVersion(v);return!!parsed&&!parsed.pre};function securitySignal(installed2,advisories,versions){let currents=[installed2].flat(),hits=advisories.filter(a=>currents.some(v=>inRange(v,a.vulnerable_versions)));if(!hits.length)return;let current=currents.filter(v=>hits.some(a=>inRange(v,a.vulnerable_versions))).sort(compareVersions)[0],highest=currents.sort(compareVersions).at(-1),severity=a=>SEVERITIES.includes(a.severity)?a.severity:"low",counts={};for(let a of hits)counts[severity(a)]=(counts[severity(a)]??0)+1;let fixedIn=versions.filter(v=>stable(v)&&compareVersions(v,highest)>0&&!hits.some(a=>inRange(v,a.vulnerable_versions))).sort(compareVersions)[0],from=parseVersion(current),to=fixedIn?parseVersion(fixedIn):void 0,fixChange=from&&to?to.major!==from.major?"major":to.minor!==from.minor?"minor":"patch":void 0;return{advisories:hits.length,worst:SEVERITIES.find(s=>counts[s]),counts,...fixedIn?{fixedIn}:{},...fixChange?{fixChange}:{}}}function deprecatedSignal(message){return message?.replace(/\s+/g," ").trim()||void 0}var YEAR_MS=365*24*60*60*1e3;function unsupportedSignal(current,latest,published,now){let major=parseVersion(current)?.major;if(major===void 0||(parseVersion(latest)?.major??major)<=major)return;let last=Object.entries(published).filter(([v])=>stable(v)&&parseVersion(v)?.major===major).map(([,when])=>Date.parse(when)).filter(t=>!Number.isNaN(t)).sort((a,b)=>b-a)[0];if(!(last===void 0||now.getTime()-last<YEAR_MS))return{since:new Date(last).toISOString().slice(0,7)}}function effortOf(usage2,verified){let raw=usage2.files+usage2.callSites/10;return Math.round((verified?raw/2:raw)*10)/10}function urgencyOf(s){if(s.security)return{signal:"security",urgency:Math.round((URGENCY.security+(SEVERITIES.length-SEVERITIES.indexOf(s.security.worst))/10-(s.dev?.1:0))*10)/10};if(s.deprecated)return{signal:"deprecated",urgency:URGENCY.deprecated};if(s.unsupported)return{signal:"unsupported",urgency:URGENCY.unsupported};if(s.drift)return{signal:"drift",urgency:URGENCY.drift};if(s.blocks?.length||s.movesWith?.length)return{signal:"blocking",urgency:URGENCY.blocking};if(s.behind>=2)return{signal:"behind",urgency:URGENCY.behind}}var files=n=>`${n} file${n===1?"":"s"} to touch`,truncate=(text,max)=>text.length<=max?text:`${text.slice(0,max-1).trimEnd()}\u2026`;function reasonOf(s,current,usageFiles){let text=signalText(s,current,usageFiles);return text&&s.dev?`dev \xB7 ${text}`:text}function fixText(sec){return sec.fixedIn?sec.fixChange==="major"?`needs ${sec.fixedIn} (major)`:`fixed in ${sec.fixedIn} (${sec.fixChange}, same major)`:"no fixed version yet"}function signalText(s,current,usageFiles){let top=urgencyOf(s)?.signal,touch=usageFiles?`, ${files(usageFiles)}`:"";switch(top){case"security":{let sec=s.security,n=sec.advisories;return`${n} ${n===1?"advisory":"advisories"} (${sec.counts[sec.worst]} ${sec.worst}), ${fixText(sec)}`}case"deprecated":return`deprecated: ${truncate(s.deprecated,60)}`;case"unsupported":return`${parseVersion(current)?.major}.x line unsupported since ${s.unsupported?.since}${touch}`;case"drift":{let majors=(s.drift?.majors??[]).map(m=>`${m}.x`);return`version drift: ${majors.slice(0,-1).join(", ")} and ${majors.at(-1)} across ${s.drift?.workspaces} workspaces`}case"blocking":return s.blocks?.length?`blocks ${s.blocks.join(", ")}`:`moves with ${(s.movesWith??[]).join(", ")}`;case"behind":return`${s.behind} majors behind${touch}`;default:return""}}var tieBreak=(a,b)=>+!!a.dev-+!!b.dev||+!!b.sameMajorFix-+!!a.sameMajorFix||a.effort-b.effort;function rankPriorities(items){return[...items].sort((a,b)=>b.urgency-a.urgency||tieBreak(a,b)||a.name.localeCompare(b.name))}function rowOf(p,s){let fix2=urgencyOf(s)?.signal==="security"?s.security:void 0;return{...s.dev?{dev:!0}:{},...fix2?.fixChange&&fix2.fixChange!=="major"?{sameMajorFix:!0}:{},...fix2?.fixedIn?{target:`${p.name}@${fix2.fixedIn}`}:{}}}var CHEAP_FILES=3;function prioritize(packages,groups){let rows=[],grouped=new Set,top=members=>members.map(p=>({p,u:p.signals?urgencyOf(p.signals):void 0})).filter(x=>!!x.u).sort((a,b)=>{let sa=a.p.signals,sb=b.p.signals;return b.u.urgency-a.u.urgency||+!!sa.dev-+!!sb.dev||+!!rowOf(b.p,sb).sameMajorFix-+!!rowOf(a.p,sa).sameMajorFix||sb.behind-sa.behind||sa.effort-sb.effort})[0];for(let g of groups){for(let p of g.members)grouped.add(p);let inGroup=new Set(g.members.map(p=>p.name)),members=g.members.map(p=>{if(!p.signals)return p;let blocks=p.signals.blocks?.filter(b=>!inGroup.has(b.slice(0,b.lastIndexOf(" "))));return{...p,signals:{...p.signals,movesWith:void 0,blocks:blocks?.length?blocks:void 0}}}),best=top(members);if(!best)continue;let reason=reasonOf(best.p.signals,best.p.current,best.p.usage.files);rows.push({name:g.name,group:g.id,packages:[...new Set(g.members.map(p=>p.name))],signal:best.u.signal,tier:tierOfSignal(best.u.signal),urgency:best.u.urgency,effort:g.members.reduce((n,p)=>n+(p.signals?.effort??0),0),reason:best.p.name===(g.lead??g.name)?reason:reason.startsWith("dev \xB7 ")?`dev \xB7 ${best.p.name} ${reason.slice(6)}`:`${best.p.name} ${reason}`,...rowOf(best.p,best.p.signals)})}for(let p of packages){if(grouped.has(p)||!p.signals)continue;let u=urgencyOf(p.signals);u&&rows.push({name:p.name,packages:[p.name],signal:u.signal,tier:tierOfSignal(u.signal),urgency:u.urgency,effort:p.signals.effort,reason:reasonOf(p.signals,p.current,p.usage.files),...rowOf(p,p.signals)})}let priorities=rankPriorities(rows),urgent=new Set(priorities.flatMap(r=>r.packages)),cheap=p=>p.change!=="major"&&!urgent.has(p.name)&&p.classification!=="possibly-unused"&&p.usage.files<=CHEAP_FILES&&!(p.signals&&urgencyOf(p.signals)),groupOf=new Map(groups.flatMap(g=>g.members.map(p=>[p,g]))),cheapBatch=[...new Set(packages.filter(p=>cheap(p)&&(groupOf.get(p)?.members.every(cheap)??!0)).sort((a,b)=>(a.signals?.effort??0)-(b.signals?.effort??0)).map(p=>p.name))];return{priorities,cheapBatch}}var DiscoveryRegistryError=class extends Error{constructor(code,reason,host,summary,status){super(reason),this.code=code,this.host=host,this.summary=summary,this.status=status}code;host;summary;status};function registryHost(registry){try{return new URL(registry).hostname}catch{return"configured registry"}}function registryFailure(error,host){if(error instanceof DiscoveryRegistryError)return error.message;switch(errorCode(error)){case"REGISTRY_AUTH":return`auth required for ${host}: check your .npmrc token. Skipped.`;case"PACKAGE_NOT_FOUND":return`not found on ${host}, skipped`;case"VERSION_NOT_FOUND":return`version metadata unavailable on ${host}, skipped`;default:return`registry request failed on ${host}, skipped`}}function httpError(host,status){let summary=status===401?"auth required (401)":status===403?"access denied (403)":status===404?"not found (404)":status===405?"method not allowed (405)":`registry request failed (${status})`,reason=status===401?`${summary} for ${host}: check your .npmrc token. Skipped.`:status===403?`${summary} on ${host}, your token can't read this package. Skipped.`:`${summary} on ${host}, skipped`;return new DiscoveryRegistryError([401,403,405].includes(status)?"REGISTRY_AUTH":"REGISTRY_HTTP_ERROR",reason,host,summary,status)}function isTimeout(error){return!error||typeof error!="object"?!1:"name"in error&&error.name==="TimeoutError"||"code"in error&&["ETIMEDOUT","UND_ERR_CONNECT_TIMEOUT","UND_ERR_HEADERS_TIMEOUT","UND_ERR_BODY_TIMEOUT"].includes(String(error.code))?!0:"cause"in error&&error.cause!==error&&isTimeout(error.cause)}var NPM_REGISTRY="registry.npmjs.org";function createDiscoveryFetcher(opts){let config=opts.config??loadRegistryConfig({cwd:opts.cwd}),transport=opts.fetch??fetch,timeoutMs=opts.timeoutMs??1e4,requests=new Map,blockedHosts=new Map,ask=name=>{let existing=requests.get(name);if(existing)return existing;let registry=registryFor(name,config),host=registryHost(registry),request=(async()=>{for(let attempt=0;;attempt++){let blocked=blockedHosts.get(host);if(blocked)throw blocked;let controller=new AbortController,timer,timeout=new DiscoveryRegistryError("REGISTRY_UNREACHABLE",`timed out on ${host}, skipped`,host,"timed out");try{return await Promise.race([(async()=>{let url=packumentUrl(name,registry.replace(/\/$/,"")),response=await transport(url,{headers:{accept:"application/vnd.npm.install-v1+json",...authHeaders(url,config)},signal:controller.signal});if(!response.ok){let error=httpError(host,response.status);throw error.code==="REGISTRY_AUTH"&&blockedHosts.set(host,error),response.body?.cancel().catch(()=>{}),error}return await response.json()})(),new Promise((_,reject)=>{timer=setTimeout(()=>{reject(timeout),controller.abort()},timeoutMs)})])}catch(error){let safe=error instanceof DiscoveryRegistryError?error:isTimeout(error)?timeout:new DiscoveryRegistryError("REGISTRY_UNREACHABLE",`network request failed on ${host}, skipped`,host,"network request failed");if(attempt===0&&(safe===timeout||safe.status!==void 0&&safe.status>=500&&safe.status<=599))continue;throw safe}finally{clearTimeout(timer)}}})();return requests.set(name,request),request},manifest2=async(name,spec)=>{let data=await ask(name),version2=data["dist-tags"]?.[spec]??(spec==="latest"?Object.keys(data.versions??{}).filter(v=>parseVersion(v)&&!parseVersion(v)?.pre).sort(compareVersions).at(-1):spec),entry=version2&&data.versions?.[version2];if(!entry||!version2){let host=registryHost(registryFor(name,config));throw new DiscoveryRegistryError("REGISTRY_HTTP_ERROR",`version metadata unavailable on ${host}, skipped`,host,"version metadata unavailable")}return{...entry,version:version2}},signalTimeoutMs=opts.signalTimeoutMs??5e3,once=async(url,init,ms=signalTimeoutMs)=>{let signal=AbortSignal.timeout(ms),response=await transport(url,{...init,signal});if(!response.ok)throw response.body?.cancel().catch(()=>{}),new Error(`${registryHost(url)} answered ${response.status}`);return await response.json()},times=new Map,published=(name,ms)=>{let request=times.get(name);if(!request){let url=packumentUrl(name,registryFor(name,config).replace(/\/$/,""));request=once(url,{headers:{accept:"application/json",...authHeaders(url,config)}},ms).then(doc=>doc.time??{}),times.set(name,request)}return request},policies=config.releaseAge??[],held=new Map;return{resolve:async(name,spec)=>{let latest=(await manifest2(name,spec)).version;if(!policies.length||latest===spec)return latest;let data=await ask(name);if(data.modified&&Date.parse(data.modified)<windowStart(policies))return latest;let time=await published(name,timeoutMs).catch(()=>{let host=registryHost(registryFor(name,config));throw new DiscoveryRegistryError("REGISTRY_HTTP_ERROR",`publish dates unavailable on ${host}, needed for the release-age policy; skipped`,host,"publish dates unavailable")}),result=newestAllowed(policies,name,latest,Object.keys(data.versions??{}),time);if(result.held&&held.set(name,result.held),!result.version)throw releaseAgeError(name,result.held);return result.version},heldBack:name=>held.get(name),deprecation:async(name,version2)=>(await ask(name)).versions?.[version2]?.deprecated,versions:async name=>Object.keys((await ask(name)).versions??{}),published:name=>published(name),advisories:async query=>{let body=Object.fromEntries([...query].filter(([name])=>registryHost(registryFor(name,config))===NPM_REGISTRY)),checked=Object.keys(body);if(!checked.length)return{checked,advisories:{}};let advisories=await once(`https://${NPM_REGISTRY}/-/npm/v1/security/advisories/bulk`,{method:"POST",headers:{"content-type":"application/json",accept:"application/json"},body:JSON.stringify(body)});return{checked,advisories}},metadata:async(name,version2)=>{let result=await manifest2(name,version2);return{...result.dependencies?{dependencies:result.dependencies}:{},peerDependencies:result.peerDependencies??{},bin:result.bin}}}}function dependencySource(name,value){if(typeof value!="string")return{kind:"invalid"};let spec=value.trim(),registryName=name;for(;;){if(spec.startsWith("npm:")){spec=spec.slice(4);let direct=nonRegistrySource(spec);if(direct)return{kind:"non-registry",source:direct};let alias=/^(@[\w.-]+\/[\w.-]+|[\w.-]+)(?:@(.*))?$/.exec(spec);if(!alias)return{kind:"invalid"};registryName=alias[1],spec=alias[2]??"*";continue}let source=nonRegistrySource(spec);return source?{kind:"non-registry",source}:spec.includes(":")||spec.includes("/")?{kind:"invalid"}:{kind:"registry",name:registryName,range:spec}}}function nonRegistrySource(spec){let protocol=/^([a-z][a-z\d+.-]*):/i.exec(spec)?.[1]?.toLowerCase();if(protocol?.startsWith("git+")||protocol==="git"||protocol==="ssh")return"git";if(protocol&&["github","gitlab","bitbucket","file","link","workspace","http","https"].includes(protocol))return protocol;if(/^git@[^:]+:/.test(spec))return"git";if(/^(?:\.{1,2}[/\\]|~[/\\]|[/\\]|[a-z]:[/\\])/i.test(spec)||!/[@:]/.test(spec)&&/\.(?:tgz|tar\.gz)$/.test(spec))return"file";if(/^[\w.-]+\/[\w.-]+(?:#.*)?$/.test(spec))return"github"}function catalogSpec(dir,name,spec){if(typeof spec!="string"||!spec.startsWith("catalog:"))return spec;for(let current=resolve11(dir);;current=dirname13(current)){let file=join16(current,"pnpm-workspace.yaml");if(existsSync12(file)){let document=(0,import_yaml3.parseDocument)(readFileSync17(file,"utf8"),{logLevel:"silent"});if(document.errors.length)return;let catalog=spec.slice(8);return document.getIn(catalog?["catalogs",catalog,name]:["catalog",name])}if(dirname13(current)===current)return}}var compareText=(a,b)=>a<b?-1:a>b?1:0;async function listDependencies(opts){let start=Date.now(),manifestStart=performance.now(),adapter=opts.adapter??typescriptAdapter,selected=name=>(!opts.only||opts.only.includes(name))&&(!opts.packages||packageSelected({packages:opts.packages},name)),config=loadRegistryConfig({cwd:opts.cwd}),fetcher=opts.fetcher??createDiscoveryFetcher({cwd:opts.cwd,config}),workspaces=(await adapter.workspacePackages?.({dir:opts.cwd})??["."]).sort(),workspaceNames=new Set(workspaces.map(w=>JSON.parse(readFileSync18(join17(opts.cwd,w,"package.json"),"utf8")).name).filter(Boolean)),failures=[],blocked=new Set,identities=new Map,localName=key2=>identities.get(key2)?.name??key2,registryName=key2=>identities.get(key2)?.registryName??key2,fail=(name,error,context="")=>{let code=errorCode(error),registry=error instanceof DiscoveryRegistryError||code.startsWith("REGISTRY_")||code==="PACKAGE_NOT_FOUND"||code==="VERSION_NOT_FOUND";(code==="REGISTRY_AUTH"||code==="REGISTRY_UNREACHABLE"||code==="REGISTRY_TIMEOUT")&&blocked.add(name),failures.push({name:localName(name),...registry?{kind:"registry"}:{},...error instanceof DiscoveryRegistryError?{host:error.host,summary:error.summary,...error.status?{status:error.status}:{}}:{},reason:registry?registryFailure(error,registryHost(registryFor(registryName(name),config))):`${context}${error instanceof Error?error.message:String(error)}`})},manifests=[],declared=new Map,skipped=new Map,runtimeKeys=new Set,skip=(name,source,workspace)=>{if(!selected(localName(name)))return;let key2=JSON.stringify([name,source]),item=skipped.get(key2)??{name,source,reason:`not checked: non-registry source (${source})`,workspaces:[]};item.workspaces.includes(workspace)||item.workspaces.push(workspace),skipped.set(key2,item)};for(let workspace of workspaces){let dir=resolve12(opts.cwd,workspace),installed2=await adapter.installedDependencies?.({dir}).catch(error=>{if(error&&typeof error=="object"&&"code"in error&&error.code==="NO_LOCKFILE")return new Map;throw error})??new Map,manifest2=JSON.parse(readFileSync18(join17(dir,"package.json"),"utf8"));manifests.push(manifest2);for(let field of["dependencies","devDependencies","optionalDependencies"]){let value=manifest2[field];if(value!==void 0&&(!value||typeof value!="object"||Array.isArray(value)))throw new Error(`malformed package.json: ${field} must be an object`)}let runtimeNames=new Set([...Object.keys(manifest2.dependencies??{}),...Object.keys(manifest2.optionalDependencies??{})]);for(let[name,spec]of Object.entries({...manifest2.dependencies,...manifest2.devDependencies,...manifest2.optionalDependencies})){let source=dependencySource(name,catalogSpec(dir,name,spec));if(source.kind==="invalid"){failures.push({name,workspace,reason:"malformed dependency declaration in package.json"});continue}if(source.kind==="non-registry"){skip(name,source.source,workspace);continue}if(workspaceNames.has(name)&&source.name===name&&!spec.trim().startsWith("npm:")){skip(name,"workspace",workspace);continue}let locked=installed2.get(name),version2=(locked&&/^(?:npm:)?(?:@[\w.-]+\/)?[\w.-]+@(.+)$/.exec(locked))?.[1]??locked??(/^\d+\.\d+\.\d+(?:-[\w.-]+)?$/.test(source.range)?source.range:void 0);if(!version2||!parseVersion(version2)){failures.push({name,workspace,reason:"no locked or exact current version; install dependencies to resolve the declared range"});continue}let key2=JSON.stringify([name,source.name]);identities.set(key2,{name,registryName:source.name}),runtimeNames.has(name)&&runtimeKeys.add(key2);let versions=declared.get(key2)??new Map;versions.set(version2,[...versions.get(version2)??[],workspace]),declared.set(key2,versions)}}let manifestReadMs=performance.now()-manifestStart,names=[...declared.keys()].sort(),configs=[],fileEvidence=new Map,scanStats={sourceMs:0,configMs:0,visitedFiles:0,sourceFiles:0,configFiles:0,assetFiles:0},tasks=[],usage2=await scanImports(opts.cwd,[...new Set(names.map(localName))],workspaces,configs,fileEvidence,scanStats,tasks),scripts=manifests.flatMap(m=>Object.values(m.scripts??{})).join(`
419
419
  `),metadata=new Map,targets=new Map,latestVersions=new Map,missingVersions=new Map,installedStart=performance.now(),installedManifests=0;for(let name of names){let items=[],missing=[];for(let[version2,locations]of declared.get(name)??[]){let local=locations.map(w=>installedManifest(opts.cwd,w,localName(name))).filter(m=>!!m&&(!m.version||m.version===version2));installedManifests+=local.length,local.length?items.push(...local):missing.push(version2)}metadata.set(name,items),missingVersions.set(name,missing)}manifestReadMs+=performance.now()-installedStart;let collectSignals=async(name,latest)=>{let outdated=[...declared.get(name)?.keys()??[]].filter(current=>compareVersions(latest,current)>0);if(!outdated.length)return;let real=registryName(name);await Promise.all([...outdated.map(async current=>{let message=await fetcher.deprecation?.(real,current).catch(()=>{});message&&deprecations.set(JSON.stringify([name,current]),message)}),fetcher.versions?.(real).then(list22=>allVersions.set(name,list22)).catch(()=>{}),outdated.some(c=>(parseVersion(latest)?.major??0)>(parseVersion(c)?.major??0))&&fetcher.published?fetcher.published(real).then(time=>published.set(name,time)).catch(()=>{}):void 0])},registryStart=performance.now(),wanted=names.filter(name=>selected(localName(name))),advisoryQuery=new Map;for(let name of wanted)advisoryQuery.set(registryName(name),[...new Set([...advisoryQuery.get(registryName(name))??[],...declared.get(name)?.keys()??[]])]);let advisoriesOff=opts.advisories===!1?"turned off with --no-advisories":opts.advisories===void 0&&!advisoriesEnabled(opts.cwd)?"turned off in uptide.config.json":void 0,advisoryRequest=advisoriesOff?Promise.resolve({error:advisoriesOff}):fetcher.advisories?fetcher.advisories(advisoryQuery).catch(error=>({error:error instanceof Error&&error.name==="TimeoutError"?"timed out":"request failed"})):Promise.resolve({error:"not available for this registry client"}),deprecations=new Map,published=new Map,allVersions=new Map,heldBack=new Map;await mapWithLimit(names,16,async name=>{let items=metadata.get(name);if(selected(localName(name)))try{let latest=await fetcher.resolve(registryName(name),"latest"),held=fetcher.heldBack?.(registryName(name));if(held&&heldBack.set(name,held),latestVersions.set(name,latest),fetcher.metadata&&[...declared.get(name)?.keys()??[]].some(current=>compareVersions(latest,current)>0))try{targets.set(name,await fetcher.metadata(registryName(name),latest))}catch(error){fail(name,error,"target peer metadata unavailable: ")}await collectSignals(name,latest)}catch(error){let held=fetcher.heldBack?.(registryName(name));held&&errorCode(error)==="RELEASE_AGE"?heldBack.set(name,held):(fail(name,error),blocked.add(name))}for(let version2 of missingVersions.get(name)??[]){if(!fetcher.metadata||blocked.has(name))break;try{items.push({...await fetcher.metadata(registryName(name),version2),name:registryName(name)})}catch(error){fail(name,error,"tooling/peer metadata unavailable: ")}}});let advisoryResult=await advisoryRequest,registryMs=performance.now()-registryStart,configStart=performance.now(),configText=configs.join(`
420
420
  `),reasons=new Map(names.map(name=>[name,[...toolingReasons(localName(name),metadata.get(name)??[],scripts,configText,manifests,tasks),...!knownTool(localName(name))&&knownTool(registryName(name))?["known configuration or build tool"]:[],...fileEvidence.get(localName(name))??[]]])),needed=new Set(names.filter(name=>usage2.has(localName(name))||(reasons.get(name)?.length??0)>0));for(let name of needed)for(let m of metadata.get(name)??[])for(let peer of new Set([...Object.keys(m.dependencies??{}),...Object.keys(m.peerDependencies??{})]))for(let key2 of names.filter(key3=>localName(key3)===peer)){let evidence=reasons.get(key2),reason=`required by ${localName(name)}`;evidence.includes(reason)||evidence.push(reason);let peerReason=`peer dependency of ${localName(name)}`;m.peerDependencies?.[peer]&&!evidence.includes(peerReason)&&evidence.push(peerReason),needed.add(key2)}for(let name of names.filter(n=>localName(n).startsWith("@types/"))){let runtime=localName(name).slice(7).replace(/^([^_]+)__/,"@$1/");(runtime==="node"||[...needed].some(key2=>localName(key2)===runtime))&&reasons.get(name).push(`types for ${runtime}`)}let packages=names.filter(name=>selected(localName(name))).flatMap(name=>{let latest=latestVersions.get(name);if(!latest)return[];let all=[...declared.get(name)??[]].sort(([a],[b])=>compareVersions(a,b)),outdated=all.filter(([version2])=>compareVersions(latest,version2)>0);if(!outdated.length)return[];let[current]=outdated[0],from=parseVersion(current)??{major:0,minor:0,patch:0},to=parseVersion(latest)??{major:0,minor:0,patch:0},scanned=usage2.get(localName(name));return[{name:localName(name),...registryName(name)!==localName(name)?{registryName:registryName(name)}:{},current,...all.length>1?{versions:all.map(([version2,workspaces2])=>({version:version2,workspaces:workspaces2}))}:{},latest,change:to.major>from.major?"major":to.minor>from.minor?"minor":"patch",majorGap:Math.max(0,to.major-from.major),classification:scanned?.files.length&&!BUILD_TOOLS[localName(name)]?"used":reasons.get(name)?.length?"tooling":"possibly-unused",reasons:!scanned?.files.length&&!reasons.get(name)?.length?[UNUSED_REASON,...failures.some(f=>f.name===localName(name))?["tooling/peer metadata incomplete; install dependencies and scan again"]:[]]:reasons.get(name)??[],tier:tierOf(activePacks(),localName(name),current,latest),workspaces:[...new Set(outdated.flatMap(([,w])=>w))].sort(),usage:{files:scanned?.files.length??0,callSites:scanned?.callSites??0,references:scanned?.references??0,...opts.details?{fileList:scanned?.files??[]}:{},workspaces:scanned?.workspaces.sort()??[],topSymbols:Object.entries(scanned?.symbols??{}).filter(([,count2])=>count2>0).sort(([a,ac],[b,bc])=>bc-ac||compareText(a,b)).slice(0,5).map(([name2,count2])=>({name:name2,count:count2}))}}]});packages.sort((a,b)=>+(b.change==="major")-+(a.change==="major")||b.usage.files-a.usage.files||b.usage.callSites-a.usage.callSites||compareText(a.name,b.name)||compareVersions(a.current,b.current)),failures.sort((a,b)=>compareText(a.name,b.name)||compareText(a.workspace??"",b.workspace??""));let unambiguous=names.filter(key2=>names.filter(other=>localName(other)===localName(key2)).length===1),groups=dependencyGroups(packages.filter(p=>unambiguous.some(key2=>localName(key2)===p.name)),new Map(unambiguous.map(key2=>[localName(key2),metadata.get(key2)??[]])),new Map(unambiguous.filter(key2=>targets.has(key2)).map(key2=>[localName(key2),targets.get(key2)])),new Map(activePacks().filter(p=>p.releaseGroup?.length).map(p=>[p.name,(p.releaseGroup??[]).map(c=>c.name)]))),keyOf=new Map(names.map(key2=>[localName(key2),key2])),advisories="error"in advisoryResult?{}:advisoryResult.advisories,blocks=peerBlocks(packages,new Map(unambiguous.map(key2=>[localName(key2),metadata.get(key2)??[]]))),now=opts.now??new Date;for(let p of packages){let key2=keyOf.get(p.name)??p.name,together=groups.find(g=>g.reason&&!g.reason.endsWith(" family")&&g.members.includes(p)),installed2=p.versions?.map(v=>v.version)??[p.current],outdated=installed2.filter(v=>compareVersions(p.latest,v)>0),security=securitySignal(outdated,advisories[p.registryName??p.name]??[],allVersions.get(key2)??[]),deprecated=deprecatedSignal(outdated.map(v=>deprecations.get(JSON.stringify([key2,v]))).find(Boolean)),majors=[...new Set(installed2.map(v=>parseVersion(v)?.major??0))],time=published.get(key2),unsupported=time?unsupportedSignal(p.current,p.latest,time,now):void 0;p.kind=(runtimeKeys.has(key2)||p.classification==="peer")&&p.classification!=="tooling"?"runtime":"dev",p.signals={...security?{security}:{},...deprecated?{deprecated}:{},...unsupported?{unsupported}:{},...majors.length>1?{drift:{majors,workspaces:new Set(p.versions?.flatMap(v=>v.workspaces)).size}}:{},...blocks.get(p.name)?.length?{blocks:blocks.get(p.name)}:{},...together?{movesWith:[...new Set(together.members.filter(m=>m.name!==p.name).map(m=>m.name))]}:{},behind:p.majorGap,effort:effortOf(p.usage,p.tier==="verified"),...p.kind==="dev"?{dev:!0}:{}}}let{priorities,cheapBatch}=prioritize(packages,groups),failuresByName=new Map(failures.map(failure=>[failure.name,failure])),unknown=new Map;for(let key2 of names){let name=localName(key2);if(!selected(name)||latestVersions.has(key2)||heldBack.has(key2))continue;let previous=unknown.get(name);unknown.set(name,{name,currentVersions:[...new Set([...previous?.currentVersions??[],...declared.get(key2)?.keys()??[]])],workspaces:[...new Set([...previous?.workspaces??[],...[...declared.get(key2)?.values()??[]].flat()])],reason:failuresByName.get(name)?.reason??"latest version unavailable"})}return{repo:opts.cwd,workspaces,packages,groups,priorities,cheapBatch,advisories:"error"in advisoryResult?{status:"not checked",packages:0,reason:advisoryResult.error}:advisoryResult.checked.length?{status:"checked",packages:advisoryResult.checked.length}:{status:"not checked",packages:0,reason:"every package comes from a private registry"},...scanStats.warnings?.length?{scanWarnings:scanStats.warnings}:{},...heldBack.size?{releaseAge:[...heldBack].map(([key2,held])=>{let name=localName(key2),newest=[...declared.get(key2)?.keys()??[]].sort(compareVersions).at(-1),listed=packages.some(p=>p.name===name);return{name,...newest?{current:newest}:{},reason:heldBackText(held,listed?void 0:newest)}}).sort((a,b)=>compareText(a.name,b.name))}:{},skipped:[...skipped.values()].sort((a,b)=>compareText(a.name,b.name)||compareText(a.source,b.source)),unknown:[...unknown.values()],failures:[...failuresByName.values()],timing:{totalMs:Date.now()-start,...opts.verbose?{phases:{manifestReadMs,registryMs,sourceScanMs:scanStats.sourceMs,configScanMs:scanStats.configMs+performance.now()-configStart},files:{manifests:workspaces.length,installedManifests,visited:scanStats.visitedFiles,source:scanStats.sourceFiles,config:scanStats.configFiles,assets:scanStats.assetFiles,candidateSources:scanStats.candidateSourceFiles,ignoredSources:scanStats.ignoredSourceFiles,ignoredSourceRules:scanStats.ignoredSourceRules,parsed:scanStats.parsedFiles,workers:scanStats.workers,skipped:scanStats.skipped}}:{}}}}var MARKER2=/\/\/\s*@uptide\s+([\w-]+)((?:\s+(?:message:"[^"]*"|[^\s]+))*)\s*$/;function parseMarkers(text,caseName,file){let sites=[];return text.split(`
421
421
  `).forEach((line,i)=>{let match2=MARKER2.exec(line);if(!match2)return;let site={case:caseName,file,line:i+1,rule:match2[1],keep:!1},rest=(match2[2]??"").replace(/message:"([^"]*)"/,(_all,message)=>(site.message=message,""));for(let word of rest.trim().split(/\s+/).filter(Boolean))word==="keep"?site.keep=!0:word.startsWith("at:")?site.at=word.slice(3):word.startsWith("kind:")?site.kind=word.slice(5):word.startsWith("path:")&&(site.path=word.slice(5));sites.push(site)}),sites}function fixtureVersions(pack){let from=minVersion(pack.meta.from)??"0.0.0",to=minVersion(pack.meta.to)??"0.0.0";return{from,to}}function fixtureFinding(pack,site,text){let{from,to}=fixtureVersions(pack),rule=pack.rules.find(r=>r.id===site.rule),lineText=text.split(`
422
- `)[site.line-1]??"",at=site.at?lineText.indexOf(site.at):-1,column=(at>=0?at:lineText.search(/\S|$/))+1,kind=site.kind??rule?.kinds[0]??"signature",path=site.path??site.at??site.rule,severity=rule?.severity??"breaking";return{change:{package:pack.meta.package,from,to,path,kind,severity,source:"types",confidence:1},usage:{file:site.file,line:site.line,column,endLine:site.line,endColumn:column,symbolPath:path,access:"call",snippet:lineText.trim(),via:"direct",...site.message!==void 0?{compileError:site.message,.../^TS\d+$/.test(path)?{compileCode:Number(path.slice(2))}:{}}:{}},severity,confidence:1,fixability:"mechanical",reason:"",rule:site.rule}}function fixtureCases(dir){let root=join18(dir,"fixtures");return existsSync10(root)?readdirSync2(root,{withFileTypes:!0}).filter(e=>e.isDirectory()&&existsSync10(join18(root,e.name,"before.ts"))).map(e=>e.name).sort():[]}function runFixtures(pack,dir,options={}){let result={cases:[],rules:{},falsePositives:[],falseNegatives:[],rewriteFailures:[],unknown:[]},tally2=rule=>(result.rules[rule]??={truePositives:0,falsePositives:0,falseNegatives:0},result.rules[rule]);for(let rule of pack.rules)tally2(rule.id);for(let note of pack.behavior.filter(b=>b.detect))tally2(note.id);let known=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id)]),detecting=new Set([...pack.rules.filter(r=>r.detect).map(r=>r.id),...pack.behavior.filter(b=>b.detect&&b.reported.includes("finding")).map(b=>b.id)]);for(let name of fixtureCases(dir)){result.cases.push(name);let file=`fixtures/${name}/before.ts`,before=readFileSync18(join18(dir,file),"utf8"),afterPath=join18(dir,"fixtures",name,"after.ts"),after=existsSync10(afterPath)?readFileSync18(afterPath,"utf8"):before,sites=parseMarkers(before,name,file);for(let site of sites)known.has(site.rule)||result.unknown.push({file:site.file,line:site.line,rule:site.rule});let found=detectIn(pack,file,before);for(let id of detecting){let expected=new Set(sites.filter(s=>s.rule===id&&!s.keep).map(s=>s.line)),got=new Set(found.filter(f=>f.rule===id).map(f=>f.line));for(let line of got)expected.has(line)?tally2(id).truePositives++:(tally2(id).falsePositives++,result.falsePositives.push({rule:id,file,line}));for(let line of expected)got.has(line)||(tally2(id).falseNegatives++,result.falseNegatives.push({rule:id,file,line}))}let text=before,{from,to}=fixtureVersions(pack),context={from,to,includeDeprecated:!0};for(let site of[...sites].sort((a,b)=>b.line-a.line)){let rule=pack.rules.find(r=>r.id===site.rule);if(!rule?.rewrite)continue;let finding=fixtureFinding(pack,site,text);if(ruleFor(pack.rules,finding)?.id!==rule.id)continue;let out=pack.transform(text,finding,context);if(site.keep){out.applied&&(tally2(rule.id).falsePositives++,result.rewriteFailures.push({case:name,file,line:site.line,message:`${rule.id} rewrote a site marked keep (${out.reason})`}));continue}if(!out.applied){tally2(rule.id).falseNegatives++,result.rewriteFailures.push({case:name,file,line:site.line,message:`${rule.id} did not rewrite the site: ${out.reason}`});continue}rule.detect||tally2(rule.id).truePositives++,text=out.text}options.update&&text!==after?writeFileSync10(afterPath,text):text!==after&&result.rewriteFailures.push({case:name,file:`fixtures/${name}/after.ts`,message:`the rewritten before.ts differs from after.ts:
422
+ `)[site.line-1]??"",at=site.at?lineText.indexOf(site.at):-1,column=(at>=0?at:lineText.search(/\S|$/))+1,kind=site.kind??rule?.kinds[0]??"signature",path=site.path??site.at??site.rule,severity=rule?.severity??"breaking";return{change:{package:pack.meta.package,from,to,path,kind,severity,source:"types",confidence:1},usage:{file:site.file,line:site.line,column,endLine:site.line,endColumn:column,symbolPath:path,access:"call",snippet:lineText.trim(),via:"direct",...site.message!==void 0?{compileError:site.message,.../^TS\d+$/.test(path)?{compileCode:Number(path.slice(2))}:{}}:{}},severity,confidence:1,fixability:"mechanical",reason:"",rule:site.rule}}function fixtureCases(dir){let root=join18(dir,"fixtures");return existsSync13(root)?readdirSync2(root,{withFileTypes:!0}).filter(e=>e.isDirectory()&&existsSync13(join18(root,e.name,"before.ts"))).map(e=>e.name).sort():[]}function runFixtures(pack,dir,options={}){let result={cases:[],rules:{},falsePositives:[],falseNegatives:[],rewriteFailures:[],unknown:[]},tally2=rule=>(result.rules[rule]??={truePositives:0,falsePositives:0,falseNegatives:0},result.rules[rule]);for(let rule of pack.rules)tally2(rule.id);for(let note of pack.behavior.filter(b=>b.detect))tally2(note.id);let known=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id)]),detecting=new Set([...pack.rules.filter(r=>r.detect).map(r=>r.id),...pack.behavior.filter(b=>b.detect&&b.reported.includes("finding")).map(b=>b.id)]);for(let name of fixtureCases(dir)){result.cases.push(name);let file=`fixtures/${name}/before.ts`,before=readFileSync19(join18(dir,file),"utf8"),afterPath=join18(dir,"fixtures",name,"after.ts"),after=existsSync13(afterPath)?readFileSync19(afterPath,"utf8"):before,sites=parseMarkers(before,name,file);for(let site of sites)known.has(site.rule)||result.unknown.push({file:site.file,line:site.line,rule:site.rule});let found=detectIn(pack,file,before);for(let id of detecting){let expected=new Set(sites.filter(s=>s.rule===id&&!s.keep).map(s=>s.line)),got=new Set(found.filter(f=>f.rule===id).map(f=>f.line));for(let line of got)expected.has(line)?tally2(id).truePositives++:(tally2(id).falsePositives++,result.falsePositives.push({rule:id,file,line}));for(let line of expected)got.has(line)||(tally2(id).falseNegatives++,result.falseNegatives.push({rule:id,file,line}))}let text=before,{from,to}=fixtureVersions(pack),context={from,to,includeDeprecated:!0};for(let site of[...sites].sort((a,b)=>b.line-a.line)){let rule=pack.rules.find(r=>r.id===site.rule);if(!rule?.rewrite)continue;let finding=fixtureFinding(pack,site,text);if(ruleFor(pack.rules,finding)?.id!==rule.id)continue;let out=pack.transform(text,finding,context);if(site.keep){out.applied&&(tally2(rule.id).falsePositives++,result.rewriteFailures.push({case:name,file,line:site.line,message:`${rule.id} rewrote a site marked keep (${out.reason})`}));continue}if(!out.applied){tally2(rule.id).falseNegatives++,result.rewriteFailures.push({case:name,file,line:site.line,message:`${rule.id} did not rewrite the site: ${out.reason}`});continue}rule.detect||tally2(rule.id).truePositives++,text=out.text}options.update&&text!==after?writeFileSync11(afterPath,text):text!==after&&result.rewriteFailures.push({case:name,file:`fixtures/${name}/after.ts`,message:`the rewritten before.ts differs from after.ts:
423
423
  ${firstDifference(text,after)}`})}return result}function firstDifference(got,want){let a=got.split(`
424
424
  `),b=want.split(`
425
425
  `);for(let i=0;i<Math.max(a.length,b.length);i++)if(a[i]!==b[i])return` line ${i+1}
426
426
  - expected: ${b[i]??"(end of file)"}
427
427
  + got: ${a[i]??"(end of file)"}`;return""}function fixturesPass(result){return result.falsePositives.length===0&&result.falseNegatives.length===0&&result.rewriteFailures.length===0&&result.unknown.length===0}function truthCacheDir(env=process.env){return env.UPTIDE_GROUND_TRUTH_CACHE||join19(defaultCacheDir(env),"ground-truth")}var SHA=/^[0-9a-f]{40}$/,REPO=/^[\w.-]+\/[\w.-]+$/;function label(entry){return entry.fixture??`${entry.repo}@${(entry.commit??"").slice(0,12)}`}function truthProblems(truth,packageName){let problems=[];return truth.package!==packageName&&problems.push(`package is "${truth.package}", the pack is "${packageName}"`),truth.repos.forEach((r,i)=>{let where=`repos[${i}]`;r.fixture===void 0?((!r.repo||!REPO.test(r.repo))&&problems.push(`${where}: repo must be owner/name`),(!r.commit||!SHA.test(r.commit))&&problems.push(`${where}: commit must be a full 40-character SHA`)):(r.repo||r.commit)&&problems.push(`${where}: a fixture has no repo or commit`),r.directory&&(r.directory.startsWith("/")||/(^|\/)\.\.(\/|$)/.test(r.directory))&&problems.push(`${where}: directory is relative and inside the repository`),(!r.from||!r.to)&&problems.push(`${where}: from and to are exact versions`);for(let f of r.findings??[])(!f.file||!Number.isInteger(f.line)||f.line<1||!f.rule)&&problems.push(`${where}: every finding has a file, a line and a rule`)}),problems}function cachedRepoDir(entry,cacheDir=truthCacheDir()){return join19(cacheDir,`${entry.repo.replace("/","__")}@${entry.commit}`)}function why(output){let lines=output.split(`
428
- `).map(l=>l.trim()),reasons=lines.filter(l=>/ERR|error .*\S|Missing|not in sync|can only|ENOENT/.test(l));return(reasons.length?reasons.slice(0,4):lines.filter(Boolean).slice(-4)).join(" | ")}var UNFROZEN={npm:["install","--ignore-scripts","--no-audit","--no-fund"],pnpm:["install","--ignore-scripts","--no-frozen-lockfile"],"yarn-classic":["install","--ignore-scripts","--non-interactive"],"yarn-berry":["install"],bun:["install","--ignore-scripts"]};function installerOf(dir){let others=["pnpm-lock.yaml","package-lock.json","npm-shrinkwrap.json","yarn.lock"];return(existsSync11(join19(dir,"bun.lock"))||existsSync11(join19(dir,"bun.lockb")))&&!others.some(file=>existsSync11(join19(dir,file)))?{kind:"bun",bin:"bun",args:["install","--frozen-lockfile","--ignore-scripts"]}:packageManager(dir)}async function installDependencies(dir,name,options){let pm=installerOf(dir),env={...pm.env,npm_config_manage_package_manager_versions:"true"},timeout=options.timeoutMs??9e5;options.log?.(`installing ${name} with ${pm.kind}, scripts off`);let result=await command(dir,pm.bin,pm.args,timeout,env);if(result.code===0)return{manager:pm.kind,frozen:!0};let retry=UNFROZEN[pm.kind],args=pm.kind==="yarn-berry"?[...retry,...pm.args.slice(2)]:retry;options.log?.(`the frozen install failed (${why(result.output)}); installing without freezing`);let again=await command(dir,pm.bin,args,timeout,env);if(again.code!==0)throw new UptideError("GROUND_TRUTH_INSTALL",`installing ${name} failed${again.timeout?" (timed out)":""}: ${why(again.output)}`);return{manager:pm.kind,frozen:!1}}var MARKER3=".uptide-ground-truth.json";function readMarker(dir){try{return JSON.parse(readFileSync19(join19(dir,MARKER3),"utf8"))}catch{return}}var FETCH_TIMEOUT_MS=6e5;async function ensureRepo(entry,options={}){let cacheDir=options.cacheDir??truthCacheDir(),dir=cachedRepoDir(entry,cacheDir),install2=options.install??!0,marker=readMarker(dir);if(marker&&(!install2||marker.installed.includes(entry.directory??".")))return dir;if(options.offline)throw new UptideError("GROUND_TRUTH_NOT_CACHED",`${entry.repo}@${entry.commit.slice(0,12)} is not in the ground-truth cache (${cacheDir}); run without --offline once, or pnpm packs:fetch`);if(!SHA.test(entry.commit)||!REPO.test(entry.repo))throw new UptideError("INVALID_GROUND_TRUTH",`not a pinned public repository: ${entry.repo}`);if(!marker){options.log?.(`fetching ${entry.repo}@${entry.commit.slice(0,12)} (shallow)`);let partial=`${dir}.partial-${process.pid}`;rmSync5(partial,{recursive:!0,force:!0}),mkdirSync5(partial,{recursive:!0});try{git(partial,"init","-q"),git(partial,"remote","add","origin",`https://github.com/${entry.repo}.git`);let fetched=await command(partial,"git",["-c","core.hooksPath=/dev/null","fetch","-q","--depth","1","--no-tags","origin",entry.commit],options.fetchTimeoutMs??FETCH_TIMEOUT_MS,{GIT_TERMINAL_PROMPT:"0"});if(fetched.code!==0)throw new Error(fetched.timeout?`git fetch timed out after ${Math.round((options.fetchTimeoutMs??FETCH_TIMEOUT_MS)/1e3)}s`:fetched.output.trim().split(`
429
- `).at(-1)||`git fetch exited ${fetched.code}`);git(partial,"checkout","-q","--detach","FETCH_HEAD");let head2=git(partial,"rev-parse","HEAD");if(head2!==entry.commit)throw new Error(`fetched ${head2}, expected ${entry.commit}`);rmSync5(dir,{recursive:!0,force:!0}),renameSync(partial,dir)}catch(err){throw rmSync5(partial,{recursive:!0,force:!0}),new UptideError("GROUND_TRUTH_FETCH",`cannot fetch ${entry.repo}@${entry.commit}: ${err.message.split(`
430
- `)[0]}`)}}let installed2=[...marker?.installed??[]];if(install2){let project=entry.directory?join19(dir,entry.directory):dir;if(entry.directory&&(/(^|\/)\.\.(\/|$)/.test(entry.directory)||!existsSync11(project)))throw new UptideError("INVALID_GROUND_TRUTH",`no directory ${entry.directory} in ${entry.repo}`);let result=await installDependencies(project,entry.repo,options);installed2.push(entry.directory??"."),options.log?.(`installed with ${result.manager}${result.frozen?"":", lockfile not frozen"}`)}return writeFileSync11(join19(dir,MARKER3),`${JSON.stringify({repo:entry.repo,commit:entry.commit,installed:installed2})}
431
- `),dir}async function ensureFixture(root,fixture,options={}){let dir=join19(root,fixture);if(!existsSync11(join19(dir,"package.json")))throw new UptideError("INVALID_GROUND_TRUTH",`no fixture repository at ${fixture}`);if(existsSync11(join19(dir,"node_modules"))||options.install===!1)return dir;if(options.offline)throw new UptideError("GROUND_TRUTH_NOT_CACHED",`${fixture} has no node_modules; run without --offline once`);return await installDependencies(dir,fixture,options),dir}var GENERIC_RULE="generic";function ratio(a,b){return b===0?1:a/b}function tally(tp,fp,fn){return{truePositives:tp,falsePositives:fp,falseNegatives:fn,precision:ratio(tp,tp+fp),recall:ratio(tp,tp+fn)}}var key=s=>`${s.file}:${s.line}`;function predictedSites(report,pack){let own=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id)]),sites=new Map;for(let p of report.packages){if(!(p.members?p.members.map(m=>m.name):[p.name]).includes(pack.name))continue;let prefix=p.workspace==="."||p.workspace==="*"?"":`${p.workspace}/`,absolute=file=>file.startsWith(prefix)?file:`${prefix}${file}`,evidence=new Set(p.findings.filter(f=>f.anchorOnly).flatMap(f=>(f.downstream??[]).map(d=>key({file:absolute(d.file),line:d.line})))),anchors=new Set(p.findings.filter(f=>f.anchorOnly).map(f=>key({file:absolute(f.usage.file),line:f.usage.line})));for(let group of p.plan??[])for(let location of group.locations){let file=absolute(location.file);if(evidence.has(key({file,line:location.line}))&&!anchors.has(key({file,line:location.line})))continue;let site={file,line:location.line,rule:own.has(group.rule)?group.rule:GENERIC_RULE,severity:group.severity};sites.has(key(site))||sites.set(key(site),site)}}return[...sites.values()].sort((a,b)=>a.file.localeCompare(b.file)||a.line-b.line)}function scoreSites(predicted,expected){let truth=new Map(expected.map(e=>[key(e),e])),seen=new Set(predicted.map(key));return{falsePositives:predicted.filter(p=>!truth.has(key(p))),falseNegatives:expected.filter(e=>!seen.has(key(e))),wrongRule:predicted.filter(p=>truth.has(key(p))&&truth.get(key(p))?.rule!==p.rule).map(p=>({...p,expected:truth.get(key(p))?.rule}))}}async function scoreRepo(pack,entry,root,options){let expected=entry.findings.map(f=>({file:f.file,line:f.line,rule:f.rule})),base={label:label(entry),...entry.repo?{repo:entry.repo}:{},...entry.commit?{commit:entry.commit}:{},...entry.fixture?{fixture:entry.fixture}:{},from:entry.from,to:entry.to,predicted:[],expected,falsePositives:[],falseNegatives:[],wrongRule:[]};try{let dir=entry.fixture?await ensureFixture(root,entry.fixture,options):await ensureRepo({repo:entry.repo,commit:entry.commit,...entry.directory?{directory:entry.directory}:{}},options),project=entry.directory?join20(dir,entry.directory):dir;options.log?.(`checking ${pack.name} ${entry.from} \u2192 ${entry.to} in ${label(entry)}`);let report=await check({cwd:project,only:[pack.name],targets:{[pack.name]:entry.to},packs:[pack],cache:createFsSurfaceCache()}),installed2=[...new Set(report.packages.filter(p=>p.name===pack.name).map(p=>p.installed))];installed2.length>0&&!installed2.includes(entry.from)&&(base.installedMismatch=`the lockfile has ${installed2.join(", ")}, the ground truth says ${entry.from}`);let own=report.packages.filter(p=>p.name===pack.name),companions=[...new Map(own.flatMap(p=>p.companions??[]).map(c=>[c.name,c])).values()].sort((a,b)=>a.name.localeCompare(b.name)),leftBehind=[...new Set(own.flatMap(p=>p.companionConflicts??[])),...Object.entries(entry.with??{}).filter(([name])=>!companions.some(c=>c.name===name)).map(([name,version2])=>`${name}: the upgrade moved it to ${version2}, check leaves it`)],predicted=predictedSites(report,pack);return{...base,...companions.length?{companions}:{},...leftBehind.length?{leftBehind}:{},predicted,...scoreSites(predicted,expected)}}catch(err){return{...base,error:err.message}}}function packsDir(root){return join20(root,"packages","core","src","packs")}function readTruth(dir){let file=join20(dir,"ground-truth.json");if(!existsSync12(file))return;let text=readFileSync20(file,"utf8");return{truth:JSON.parse(text),text}}async function testPack(entry,options){let{pack}=entry,dir=join20(packsDir(options.root),entry.dir),problems=[];pack.meta.sources.length||problems.push("meta.sources lists no changelog or guide"),pack.meta.maintainer||problems.push("meta.maintainer is empty"),problems.push(...companionProblems(pack.companions,pack.name)),problems.push(...companionProblems(pack.releaseGroup,pack.name).map(p=>p.replace("companions[","releaseGroup[")));let fixtures=runFixtures(pack,dir,{update:options.updateFixtures===!0});fixtures.cases.length===0&&pack.rules.some(r=>r.rewrite||r.detect)&&problems.push("rules that rewrite or detect need fixtures (fixtures/<case>/before.ts)");let read=readTruth(dir);read||problems.push("no ground-truth.json");let truth=read?.truth??{package:pack.name,repos:[]};problems.push(...truthProblems(truth,pack.name));let repos=[];if(!options.fixturesOnly&&problems.length===0)for(let repo of truth.repos)repos.push(await scoreRepo(pack,repo,options.root,options));let scored=repos.filter(r=>!r.error),all=k=>scored.flatMap(r=>r[k]),fp=all("falsePositives"),fn=all("falseNegatives"),predicted=all("predicted"),overall=tally(predicted.length-fp.length,fp.length,fn.length),breakingPredicted=predicted.filter(p=>p.severity==="breaking"),breakingFp=fp.filter(p=>p.severity==="breaking"),deprecatedRules=new Set(pack.rules.filter(r=>r.severity==="deprecated").map(r=>r.id)),breaking=tally(breakingPredicted.length-breakingFp.length,breakingFp.length,fn.filter(f=>!deprecatedRules.has(f.rule)).length),rules={},ids=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id),...predicted.map(p=>p.rule),...all("expected").map(e=>e.rule)]);for(let id of ids){let tp=0,rfp=0,rfn=0;for(let r of scored){let want=new Set(r.expected.filter(e=>e.rule===id).map(key)),got=new Set(r.predicted.filter(p=>p.rule===id).map(key));for(let k of got)(want.has(k)?()=>tp++:()=>rfp++)();for(let k of want)got.has(k)||rfn++}tp+rfp+rfn>0&&(rules[id]=tally(tp,rfp,rfn))}let status=repos.some(r=>r.error||r.leftBehind?.length)||options.fixturesOnly?"candidate":statusOf(truth,{falsePositives:breakingFp.length,predicted:breakingPredicted.length}),verification={status,truth:read?truthDigest(read.text):"",repos:publicRepos(truth).length,breaking:{precision:Number(breaking.precision.toFixed(4)),recall:Number(breaking.recall.toFixed(4)),falsePositives:breakingFp.length}},recorded=recordedStatus(entry.verification),stale=!options.fixturesOnly&&JSON.stringify(verification)!==JSON.stringify(entry.verification),scoredAll=problems.length===0&&repos.every(r=>!r.error);options.write&&stale&&scoredAll&&!options.fixturesOnly&&(writeVerification(options.root,entry.dir,verification),stale=!1);let passed=scoredAll&&fixturesPass(fixtures)&&breakingFp.length===0&&!repos.some(r=>r.leftBehind?.length)&&!stale;return{package:pack.name,dir:entry.dir,status,recorded,stale,fixturesOnly:options.fixturesOnly===!0,verification,problems,fixtures,repos,rules,overall,breaking,passed}}function writeVerification(root,dir,record){let file=join20(packsDir(root),dir,"verification.json");return writeFileSync12(file,`${JSON.stringify(record,null,2)}
428
+ `).map(l=>l.trim()),reasons=lines.filter(l=>/ERR|error .*\S|Missing|not in sync|can only|ENOENT/.test(l));return(reasons.length?reasons.slice(0,4):lines.filter(Boolean).slice(-4)).join(" | ")}var UNFROZEN={npm:["install","--ignore-scripts","--no-audit","--no-fund"],pnpm:["install","--ignore-scripts","--no-frozen-lockfile"],"yarn-classic":["install","--ignore-scripts","--non-interactive"],"yarn-berry":["install"],bun:["install","--ignore-scripts"]};function installerOf(dir){let others=["pnpm-lock.yaml","package-lock.json","npm-shrinkwrap.json","yarn.lock"];return(existsSync14(join19(dir,"bun.lock"))||existsSync14(join19(dir,"bun.lockb")))&&!others.some(file=>existsSync14(join19(dir,file)))?{kind:"bun",bin:"bun",args:["install","--frozen-lockfile","--ignore-scripts"]}:packageManager(dir)}async function installDependencies(dir,name,options){let pm=installerOf(dir),env={...pm.env,npm_config_manage_package_manager_versions:"true"},timeout=options.timeoutMs??9e5;options.log?.(`installing ${name} with ${pm.kind}, scripts off`);let result=await command(dir,pm.bin,pm.args,timeout,env);if(result.code===0)return{manager:pm.kind,frozen:!0};let retry=UNFROZEN[pm.kind],args=pm.kind==="yarn-berry"?[...retry,...pm.args.slice(2)]:retry;options.log?.(`the frozen install failed (${why(result.output)}); installing without freezing`);let again=await command(dir,pm.bin,args,timeout,env);if(again.code!==0)throw new UptideError("GROUND_TRUTH_INSTALL",`installing ${name} failed${again.timeout?" (timed out)":""}: ${why(again.output)}`);return{manager:pm.kind,frozen:!1}}var MARKER3=".uptide-ground-truth.json";function readMarker(dir){try{return JSON.parse(readFileSync20(join19(dir,MARKER3),"utf8"))}catch{return}}var FETCH_TIMEOUT_MS=6e5;async function ensureRepo(entry,options={}){let cacheDir=options.cacheDir??truthCacheDir(),dir=cachedRepoDir(entry,cacheDir),install2=options.install??!0,marker=readMarker(dir);if(marker&&(!install2||marker.installed.includes(entry.directory??".")))return dir;if(options.offline)throw new UptideError("GROUND_TRUTH_NOT_CACHED",`${entry.repo}@${entry.commit.slice(0,12)} is not in the ground-truth cache (${cacheDir}); run without --offline once, or pnpm packs:fetch`);if(!SHA.test(entry.commit)||!REPO.test(entry.repo))throw new UptideError("INVALID_GROUND_TRUTH",`not a pinned public repository: ${entry.repo}`);if(!marker){options.log?.(`fetching ${entry.repo}@${entry.commit.slice(0,12)} (shallow)`);let partial=`${dir}.partial-${process.pid}`;rmSync6(partial,{recursive:!0,force:!0}),mkdirSync6(partial,{recursive:!0});try{git(partial,"init","-q"),git(partial,"remote","add","origin",`https://github.com/${entry.repo}.git`);let fetched=await command(partial,"git",["-c","core.hooksPath=/dev/null","fetch","-q","--depth","1","--no-tags","origin",entry.commit],options.fetchTimeoutMs??FETCH_TIMEOUT_MS,{GIT_TERMINAL_PROMPT:"0"});if(fetched.code!==0)throw new Error(fetched.timeout?`git fetch timed out after ${Math.round((options.fetchTimeoutMs??FETCH_TIMEOUT_MS)/1e3)}s`:fetched.output.trim().split(`
429
+ `).at(-1)||`git fetch exited ${fetched.code}`);git(partial,"checkout","-q","--detach","FETCH_HEAD");let head2=git(partial,"rev-parse","HEAD");if(head2!==entry.commit)throw new Error(`fetched ${head2}, expected ${entry.commit}`);rmSync6(dir,{recursive:!0,force:!0}),renameSync(partial,dir)}catch(err){throw rmSync6(partial,{recursive:!0,force:!0}),new UptideError("GROUND_TRUTH_FETCH",`cannot fetch ${entry.repo}@${entry.commit}: ${err.message.split(`
430
+ `)[0]}`)}}let installed2=[...marker?.installed??[]];if(install2){let project=entry.directory?join19(dir,entry.directory):dir;if(entry.directory&&(/(^|\/)\.\.(\/|$)/.test(entry.directory)||!existsSync14(project)))throw new UptideError("INVALID_GROUND_TRUTH",`no directory ${entry.directory} in ${entry.repo}`);let result=await installDependencies(project,entry.repo,options);installed2.push(entry.directory??"."),options.log?.(`installed with ${result.manager}${result.frozen?"":", lockfile not frozen"}`)}return writeFileSync12(join19(dir,MARKER3),`${JSON.stringify({repo:entry.repo,commit:entry.commit,installed:installed2})}
431
+ `),dir}async function ensureFixture(root,fixture,options={}){let dir=join19(root,fixture);if(!existsSync14(join19(dir,"package.json")))throw new UptideError("INVALID_GROUND_TRUTH",`no fixture repository at ${fixture}`);if(existsSync14(join19(dir,"node_modules"))||options.install===!1)return dir;if(options.offline)throw new UptideError("GROUND_TRUTH_NOT_CACHED",`${fixture} has no node_modules; run without --offline once`);return await installDependencies(dir,fixture,options),dir}var GENERIC_RULE="generic";function ratio(a,b){return b===0?1:a/b}function tally(tp,fp,fn){return{truePositives:tp,falsePositives:fp,falseNegatives:fn,precision:ratio(tp,tp+fp),recall:ratio(tp,tp+fn)}}var key=s=>`${s.file}:${s.line}`;function predictedSites(report,pack){let own=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id)]),sites=new Map;for(let p of report.packages){if(!(p.members?p.members.map(m=>m.name):[p.name]).includes(pack.name))continue;let prefix=p.workspace==="."||p.workspace==="*"?"":`${p.workspace}/`,absolute=file=>file.startsWith(prefix)?file:`${prefix}${file}`,evidence=new Set(p.findings.filter(f=>f.anchorOnly).flatMap(f=>(f.downstream??[]).map(d=>key({file:absolute(d.file),line:d.line})))),anchors=new Set(p.findings.filter(f=>f.anchorOnly).map(f=>key({file:absolute(f.usage.file),line:f.usage.line})));for(let group of p.plan??[])for(let location of group.locations){let file=absolute(location.file);if(evidence.has(key({file,line:location.line}))&&!anchors.has(key({file,line:location.line})))continue;let site={file,line:location.line,rule:own.has(group.rule)?group.rule:GENERIC_RULE,severity:group.severity};sites.has(key(site))||sites.set(key(site),site)}}return[...sites.values()].sort((a,b)=>a.file.localeCompare(b.file)||a.line-b.line)}function scoreSites(predicted,expected){let truth=new Map(expected.map(e=>[key(e),e])),seen=new Set(predicted.map(key));return{falsePositives:predicted.filter(p=>!truth.has(key(p))),falseNegatives:expected.filter(e=>!seen.has(key(e))),wrongRule:predicted.filter(p=>truth.has(key(p))&&truth.get(key(p))?.rule!==p.rule).map(p=>({...p,expected:truth.get(key(p))?.rule}))}}async function scoreRepo(pack,entry,root,options){let expected=entry.findings.map(f=>({file:f.file,line:f.line,rule:f.rule})),base={label:label(entry),...entry.repo?{repo:entry.repo}:{},...entry.commit?{commit:entry.commit}:{},...entry.fixture?{fixture:entry.fixture}:{},from:entry.from,to:entry.to,predicted:[],expected,falsePositives:[],falseNegatives:[],wrongRule:[]};try{let dir=entry.fixture?await ensureFixture(root,entry.fixture,options):await ensureRepo({repo:entry.repo,commit:entry.commit,...entry.directory?{directory:entry.directory}:{}},options),project=entry.directory?join20(dir,entry.directory):dir;options.log?.(`checking ${pack.name} ${entry.from} \u2192 ${entry.to} in ${label(entry)}`);let report=await check({cwd:project,only:[pack.name],targets:{[pack.name]:entry.to},packs:[pack],cache:createFsSurfaceCache()}),installed2=[...new Set(report.packages.filter(p=>p.name===pack.name).map(p=>p.installed))];installed2.length>0&&!installed2.includes(entry.from)&&(base.installedMismatch=`the lockfile has ${installed2.join(", ")}, the ground truth says ${entry.from}`);let own=report.packages.filter(p=>p.name===pack.name),companions=[...new Map(own.flatMap(p=>p.companions??[]).map(c=>[c.name,c])).values()].sort((a,b)=>a.name.localeCompare(b.name)),leftBehind=[...new Set(own.flatMap(p=>p.companionConflicts??[])),...Object.entries(entry.with??{}).filter(([name])=>!companions.some(c=>c.name===name)).map(([name,version2])=>`${name}: the upgrade moved it to ${version2}, check leaves it`)],predicted=predictedSites(report,pack);return{...base,...companions.length?{companions}:{},...leftBehind.length?{leftBehind}:{},predicted,...scoreSites(predicted,expected)}}catch(err){return{...base,error:err.message}}}function packsDir(root){return join20(root,"packages","core","src","packs")}function readTruth(dir){let file=join20(dir,"ground-truth.json");if(!existsSync15(file))return;let text=readFileSync21(file,"utf8");return{truth:JSON.parse(text),text}}async function testPack(entry,options){let{pack}=entry,dir=join20(packsDir(options.root),entry.dir),problems=[];pack.meta.sources.length||problems.push("meta.sources lists no changelog or guide"),pack.meta.maintainer||problems.push("meta.maintainer is empty"),problems.push(...companionProblems(pack.companions,pack.name)),problems.push(...companionProblems(pack.releaseGroup,pack.name).map(p=>p.replace("companions[","releaseGroup[")));let fixtures=runFixtures(pack,dir,{update:options.updateFixtures===!0});fixtures.cases.length===0&&pack.rules.some(r=>r.rewrite||r.detect)&&problems.push("rules that rewrite or detect need fixtures (fixtures/<case>/before.ts)");let read=readTruth(dir);read||problems.push("no ground-truth.json");let truth=read?.truth??{package:pack.name,repos:[]};problems.push(...truthProblems(truth,pack.name));let repos=[];if(!options.fixturesOnly&&problems.length===0)for(let repo of truth.repos)repos.push(await scoreRepo(pack,repo,options.root,options));let scored=repos.filter(r=>!r.error),all=k=>scored.flatMap(r=>r[k]),fp=all("falsePositives"),fn=all("falseNegatives"),predicted=all("predicted"),overall=tally(predicted.length-fp.length,fp.length,fn.length),breakingPredicted=predicted.filter(p=>p.severity==="breaking"),breakingFp=fp.filter(p=>p.severity==="breaking"),deprecatedRules=new Set(pack.rules.filter(r=>r.severity==="deprecated").map(r=>r.id)),breaking=tally(breakingPredicted.length-breakingFp.length,breakingFp.length,fn.filter(f=>!deprecatedRules.has(f.rule)).length),rules={},ids=new Set([...pack.rules.map(r=>r.id),...pack.behavior.map(b=>b.id),...predicted.map(p=>p.rule),...all("expected").map(e=>e.rule)]);for(let id of ids){let tp=0,rfp=0,rfn=0;for(let r of scored){let want=new Set(r.expected.filter(e=>e.rule===id).map(key)),got=new Set(r.predicted.filter(p=>p.rule===id).map(key));for(let k of got)(want.has(k)?()=>tp++:()=>rfp++)();for(let k of want)got.has(k)||rfn++}tp+rfp+rfn>0&&(rules[id]=tally(tp,rfp,rfn))}let status=repos.some(r=>r.error||r.leftBehind?.length)||options.fixturesOnly?"candidate":statusOf(truth,{falsePositives:breakingFp.length,predicted:breakingPredicted.length}),verification={status,truth:read?truthDigest(read.text):"",repos:publicRepos(truth).length,breaking:{precision:Number(breaking.precision.toFixed(4)),recall:Number(breaking.recall.toFixed(4)),falsePositives:breakingFp.length}},recorded=recordedStatus(entry.verification),stale=!options.fixturesOnly&&JSON.stringify(verification)!==JSON.stringify(entry.verification),scoredAll=problems.length===0&&repos.every(r=>!r.error);options.write&&stale&&scoredAll&&!options.fixturesOnly&&(writeVerification(options.root,entry.dir,verification),stale=!1);let passed=scoredAll&&fixturesPass(fixtures)&&breakingFp.length===0&&!repos.some(r=>r.leftBehind?.length)&&!stale;return{package:pack.name,dir:entry.dir,status,recorded,stale,fixturesOnly:options.fixturesOnly===!0,verification,problems,fixtures,repos,rules,overall,breaking,passed}}function writeVerification(root,dir,record){let file=join20(packsDir(root),dir,"verification.json");return writeFileSync13(file,`${JSON.stringify(record,null,2)}
432
432
  `),file}function unfinishedPack(entry,reason){let none=tally(0,0,0);return{package:entry.pack.name,dir:entry.dir,status:"candidate",recorded:recordedStatus(entry.verification),stale:!1,fixturesOnly:!1,verification:entry.verification,problems:[reason],fixtures:{cases:[],rules:{},falsePositives:[],falseNegatives:[],rewriteFailures:[],unknown:[]},repos:[],rules:{},overall:none,breaking:none,passed:!1}}var HEADER="// Generated by `uptide pack new` (packages/core/src/packs/tooling/registry-file.ts).\n// Every pack in this tree, with the status `uptide pack test --write` recorded for it.";function renderRegistry(entries){let sorted=[...entries].sort((a,b)=>a.dir.localeCompare(b.dir)),imports=[["./contract.js","import type { PackVerification, RegisteredPack } from './contract.js';"],...sorted.flatMap(e=>[[`./${e.dir}/index.js`,`import { ${e.name} } from './${e.dir}/index.js';`],[`./${e.dir}/verification.json`,`import ${verificationName(e)} from './${e.dir}/verification.json' with { type: 'json' };`]])];imports.sort(([a],[b])=>a<b?-1:a>b?1:0);let rows=sorted.map(e=>` { dir: '${e.dir}', pack: ${e.name}, verification: ${verificationName(e)} as PackVerification },`);return[HEADER,"",...imports.map(([,line])=>line),"","export const REGISTRY: readonly RegisteredPack[] = [",...rows,"];",""].join(`
433
433
  `)}function verificationName(e){return`${e.name.replace(/Pack$/,"")}Verification`}function parseRegistry(text){return[...text.matchAll(/\{ dir: '([^']+)', pack: (\w+),/g)].map(m=>({dir:m[1],name:m[2]}))}function packDir(name){return name.replace(/^@/,"").replace(/[/.]/g,"-").toLowerCase()}function packConstant(name){let camel=packDir(name).split(/[^a-z0-9]+/).filter(Boolean).map((w,i)=>i===0?w:`${w[0]?.toUpperCase()}${w.slice(1)}`).join("");return`${/^[0-9]/.test(camel)?`p${camel}`:camel}Pack`}var RULE="example-rename";function indexFile(o,constant){let pkg=JSON.stringify(o.package).slice(1,-1).replaceAll("'","\\'");return`import { definePack, replaceAtSite } from '../contract.js';
434
434
 
@@ -480,9 +480,9 @@ it('${o.package.replaceAll("'","\\'")}: every fixture rewrites and detects exact
480
480
  expect(result.falsePositives).toEqual([]);
481
481
  expect(result.falseNegatives).toEqual([]);
482
482
  });
483
- `}function scaffoldPack(options){if(!/^(?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$/.test(options.package))throw new UptideError("INVALID_GROUND_TRUTH",`not an npm package name: ${options.package}`);for(let[flag,range]of[["--from",options.from],["--to",options.to]])if(minVersion(range)===void 0)throw new UptideError("UNSUPPORTED_VERSION_RANGE",`${flag} ${range}: not a semver range`);let packs=packsDir(options.root),registryFile=join21(packs,"registry.ts");if(!existsSync13(registryFile))throw new UptideError("NOT_UPTIDE_CHECKOUT",`no pack registry at ${registryFile}: run uptide pack new inside an uptide checkout`);let dir=packDir(options.package),constant=packConstant(options.package),entries=parseRegistry(readFileSync21(registryFile,"utf8")),home=join21(packs,dir);if(existsSync13(home)||entries.some(e=>e.dir===dir||e.name===constant))throw new UptideError("PACK_EXISTS",`a pack for ${options.package} already exists: ${home}`);let truth={$comment:`Public repositories at the commit before they upgraded ${options.package} from ${options.from} to ${options.to}, and every finding the pack must report there. See docs/packs.md, "Ground truth".`,package:options.package,repos:[]},before=BEFORE.replace("PKG",options.package),after=before.replace("import { oldName }","import { newName }").replace("= oldName(1)","= newName(1)"),files2=[[join21(home,"index.ts"),indexFile(options,constant)],[join21(home,"fixtures",RULE,"before.ts"),before],[join21(home,"fixtures",RULE,"after.ts"),after],[join21(home,`${dir}.test.ts`),testFile(options,constant)],[join21(home,"ground-truth.json"),`${JSON.stringify(truth,null,2)}
484
- `]];for(let[file,text]of files2)mkdirSync6(join21(file,".."),{recursive:!0}),writeFileSync13(file,text);writeFileSync13(registryFile,renderRegistry([...entries,{dir,name:constant}]));let written=[...files2.map(([file])=>file),registryFile],biome=join21(options.root,"node_modules",".bin","biome"),formattable=written.filter(file=>!file.includes(`${dir}/fixtures/`)),formatted=existsSync13(biome)&&spawnSync(biome,["format","--write",...formattable],{cwd:options.root,stdio:"ignore"}).status===0,verification={status:"candidate",truth:truthDigest(readFileSync21(join21(home,"ground-truth.json"),"utf8")),repos:0,breaking:{precision:1,recall:1,falsePositives:0}},verificationFile=join21(home,"verification.json");return writeFileSync13(verificationFile,`${JSON.stringify(verification,null,2)}
485
- `),written.splice(written.length-1,0,verificationFile),{dir,constant,files:written.map(file=>relative6(options.root,file)),formatted}}var sitesOf2=(groups,pick)=>groups.reduce((n,g)=>n+pick(g),0);function effortOf2(p){let breaking=(p.plan??[]).filter(g=>g.severity==="breaking"),unverified=(p.plan??[]).filter(g=>g.severity==="unverified"),byRule=sitesOf2(breaking,g=>g.by.rule),byAgent=sitesOf2(breaking,g=>g.by.agent),manual=sitesOf2(breaking,g=>g.by.manual),unconfirmed=sitesOf2(unverified,g=>g.sites),hands=byAgent+manual;return{level:p.status==="unknown"||p.status==="partial"||p.importers?.some(i=>!i.analyzed)?"unknown":hands>25?"large":hands>5?"medium":hands>0||byRule>0||unconfirmed>0?"small":"none",byRule,byAgent,manual,unconfirmed}}var RANK={none:0,small:1,medium:2,large:3,unknown:4};function within(version2,range){try{return satisfies(version2,range)}catch{return!0}}function planUpgrades(report,peers){let behind=report.packages.filter(p=>!p.notes.includes("up to date")&&!["not-imported","workspace","private"].includes(p.status)&&p.installed!==p.target),analyzed=behind.filter(p=>p.status!=="skipped"&&p.status!=="no-types"),notPlanned=behind.filter(p=>!analyzed.includes(p)).map(p=>({name:p.name,installed:p.installed,reason:p.skipReason==="TIME_BUDGET"?"not analyzed: out of time":isFailure(p)?`not analyzed: ${(p.notes[0]??"analysis failed").split(`
486
- `)[0]}`:p.notes[0]??"not analyzed"})),planned=new Map;for(let p of analyzed){let effort=effortOf2(p);for(let m of p.members??[p]){let previous=planned.get(m.name);planned.set(m.name,{name:m.name,installed:previous&&compareVersions(previous.installed,m.installed)<0?previous.installed:m.installed,target:m.target,tier:p.tier??"generic",effort:previous&&RANK[previous.effort.level]>RANK[effort.level]?previous.effort:effort})}}let constraints=[],before=new Map,tied=new Map,find=name=>{let parent=tied.get(name)??name;if(parent===name)return name;let root=find(parent);return tied.set(name,root),root},tie=(a,b)=>{tied.set(find(a),find(b))};for(let a of planned.values()){for(let[on,range]of Object.entries(peers.ofTarget(a.name,a.target)??{})){let installed2=planned.get(on)?.installed??peers.installed[on]?.version;if(installed2===void 0||within(installed2,range))continue;let b=planned.get(on);if(b&&within(b.target,range)){let back=peers.ofTarget(b.name,b.target)?.[a.name],together=back!==void 0&&!within(a.installed,back);constraints.push({from:a.name,fromVersion:a.target,on,range,effect:together?"together":"before",reason:together?`${a.name} ${a.target} needs ${on} ${range}, and ${on} ${b.target} needs ${a.name} ${back}: upgrade them together`:`${a.name} ${a.target} needs ${on} ${range} (installed: ${installed2}): upgrade ${on} first`}),together?tie(a.name,on):before.set(a.name,new Set([...before.get(a.name)??[],on]))}else constraints.push({from:a.name,fromVersion:a.target,on,range,effect:"blocked",reason:`${a.name} ${a.target} needs ${on} ${range}; ${on} is at ${installed2}${b?` and its target ${b.target} is outside that range too`:" and is not being upgraded"}`})}for(let[x,{version:version2,peers:asked}]of Object.entries(peers.installed)){let range=asked[a.name];if(range===void 0||x===a.name||within(a.target,range))continue;let upgraded=planned.get(x),next=upgraded?peers.ofTarget(x,upgraded.target)?.[a.name]:void 0;if(upgraded&&(next===void 0||within(a.target,next))){let together=next!==void 0&&!within(a.installed,next);constraints.push({from:x,fromVersion:version2,on:a.name,range,effect:together?"together":"before",reason:together?`${x} ${version2} needs ${a.name} ${range} and ${x} ${upgraded.target} needs ${a.name} ${next}: upgrade them together`:`${x} ${version2} needs ${a.name} ${range}: upgrade ${x} to ${upgraded.target} first, which accepts ${a.name} ${a.target}`}),together?tie(a.name,x):before.set(a.name,new Set([...before.get(a.name)??[],x]))}else constraints.push({from:x,fromVersion:version2,on:a.name,range,effect:"blocked",reason:`${x} ${version2} needs ${a.name} ${range}, and ${a.name} ${a.target} is outside it${upgraded?` (so is what ${x} ${upgraded.target} accepts)`:`; ${x} has no upgrade in this plan`}`})}}for(let p of analyzed)for(let m of p.members??[])tie(m.name,(p.members??[])[0]?.name??m.name);let groups=new Map;for(let p of planned.values())groups.set(find(p.name),[...groups.get(find(p.name))??[],p]);let levelOf=members=>members.map(m=>m.effort.level).sort((a,b)=>RANK[b]-RANK[a])[0]??"none",waitsOn=root=>new Set((groups.get(root)??[]).flatMap(m=>[...before.get(m.name)??[]]).map(find).filter(other=>other!==root)),constraintsOf=members=>{let names=new Set(members.map(m=>m.name));return constraints.filter(c=>names.has(c.from)||names.has(c.on))},free=[...groups].filter(([root,members])=>levelOf(members)==="none"&&waitsOn(root).size===0&&constraintsOf(members).length===0),steps=[],done=new Set;if(free.length>0){steps.push({packages:free.flatMap(([,members])=>members).sort((a,b)=>a.name.localeCompare(b.name)),...free.flatMap(([,m])=>m).length>1?{together:"no-impact"}:{},effort:"none",constraints:[]});for(let[root]of free)done.add(root)}let rest=[...groups].filter(([root])=>!done.has(root));for(;rest.length>0;){let ready=rest.filter(([root2])=>[...waitsOn(root2)].every(other=>done.has(other))).sort(([,a],[,b])=>RANK[levelOf(a)]-RANK[levelOf(b)]||(a[0]?.name??"").localeCompare(b[0]?.name??"")),[root,members]=ready[0]??rest[0];steps.push({packages:[...members].sort((a,b)=>a.name.localeCompare(b.name)),...members.length>1?{together:"peer"}:{},effort:levelOf(members),constraints:constraintsOf(members)}),done.add(root),rest.splice(rest.findIndex(([r])=>r===root),1)}return{steps:steps.map((step,i)=>({order:i+1,...step})),notPlanned}}async function upgradePlan(opts,services={}){let fetcher=services.fetcher??opts.fetcher??createNpmFetcher({config:loadRegistryConfig({cwd:opts.cwd})}),discovery=await(services.list??listDependencies)({...opts,fetcher}),saved=opts.checkResults;if(saved&&(!Array.isArray(saved.packages)||resolve12(saved.repo)!==resolve12(opts.cwd)))throw new Error("--results must be a check report for this repository");let byVersion=discovery.packages.flatMap(p=>(p.versions??[{version:p.current,workspaces:p.workspaces}]).filter(v=>compareVersions(p.latest,v.version)>0).map(v=>({...p,current:v.version,workspaces:v.workspaces}))),packages=byVersion.map(p=>{let existing=saved?.packages.find(s=>s.name===p.name&&s.installed===p.current&&s.target===p.latest&&JSON.stringify((s.workspaces??[s.workspace]).slice().sort())===JSON.stringify(p.workspaces.slice().sort()));return existing||{name:p.name,installed:p.current,latest:p.latest,target:p.latest,tier:p.tier,workspace:p.workspaces.length>1?"*":p.workspaces[0]??".",workspaces:p.workspaces,majorsBehind:majorsBehind(p.current,p.latest),findings:[],callSitesChecked:0,unanalyzed:[],status:"unknown",notes:["not checked; effort unknown"],timing:{fetchMs:0,diffMs:0,usagesMs:0,compileMs:0}}});for(let failure of discovery.failures)packages.push({name:failure.name,installed:"?",latest:"?",target:"?",workspace:failure.workspace??".",majorsBehind:0,findings:[],callSitesChecked:0,unanalyzed:[],status:"skipped",skipReason:"ANALYSIS_FAILED",notes:[failure.reason],timing:{fetchMs:0,diffMs:0,usagesMs:0,compileMs:0}});let report={repo:opts.cwd,workspaces:discovery.workspaces,packages,summary:summarize(packages),timing:discovery.timing},installed2={};for(let workspace of discovery.workspaces){let dir=resolve12(opts.cwd,workspace),manifest2=JSON.parse(readFileSync222(join222(dir,"package.json"),"utf8"));for(let name of Object.keys({...manifest2.dependencies,...manifest2.devDependencies}))for(let at=dir;;at=dirname13(at))try{let pkg=JSON.parse(readFileSync222(join222(at,"node_modules",name,"package.json"),"utf8"));installed2[name]??={version:pkg.version,peers:pkg.peerDependencies??{}};break}catch{if(dirname13(at)===at)break}}let targets=new Map;await mapWithLimit(discovery.packages,12,async p=>{try{targets.set(`${p.name}@${p.latest}`,(await fetcher.metadata?.(p.name,p.latest))?.peerDependencies)}catch{}});let plan=planUpgrades(report,{installed:installed2,ofTarget:(name,version2)=>targets.get(`${name}@${version2}`)});plan.notes=["Based on discovery and matching saved check results; unknown effort requires uptide check <package>.","Saved results describe the source at check time; rerun check after source changes."];for(let name of new Set(byVersion.map(p=>p.name))){let versions=byVersion.filter(p=>p.name===name);versions.length>1&&plan.notes.push(`${name}: current versions ${versions.map(p=>`${p.current} (${p.workspaces.join(", ")})`).join("; ")}; one target for all, highest estimated effort shown.`)}let missing=discovery.packages.filter(p=>targets.get(`${p.name}@${p.latest}`)===void 0);return missing.length&&plan.notes.push(`Target peer metadata unavailable: ${missing.map(p=>p.name).join(", ")}`),Object.keys(installed2).length||plan.notes.push("Installed peer constraints unavailable without node_modules."),{report,plan}}import"fs";var VERSION="0.11.2-next.20261014004941";var import_picocolors=__toESM(require_picocolors(),1);var INVOCATION=uptideCommand(VERSION),runnable=text=>text.replace(/(^|`)uptide (?=[a-z-])/g,`$1${INVOCATION} `);var EXIT={ok:0,breaking:1,error:2},CliError=class extends Error{constructor(message,options={}){super(message);this.options=options}options;name="CliError"};function explain(err){if(err instanceof NoTypesError)return`${err.packageName}@${err.version}: no type declarations, cannot analyze`;if(err instanceof VersionNotFoundError){let hint=err.available.length>0?` (latest published: ${err.available.at(-1)})`:"";return`${err.message}${hint}`}return err instanceof PackageNotFoundError||err instanceof IntegrityError||err instanceof Error?err.message:String(err)}function renderError(err,color){let colors=import_picocolors.default.createColors(color),lines=[`${colors.red("error:")} ${explain(err)}`];return err instanceof CliError&&(err.options.why&&lines.push(` ${err.options.why}`),err.options.next&&lines.push(` ${colors.bold("Next:")} ${err.options.literalNext?err.options.next:runnable(err.options.next)}`)),`${lines.join(`
483
+ `}function scaffoldPack(options){if(!/^(?:@[a-z0-9][\w.-]*\/)?[a-z0-9][\w.-]*$/.test(options.package))throw new UptideError("INVALID_GROUND_TRUTH",`not an npm package name: ${options.package}`);for(let[flag,range]of[["--from",options.from],["--to",options.to]])if(minVersion(range)===void 0)throw new UptideError("UNSUPPORTED_VERSION_RANGE",`${flag} ${range}: not a semver range`);let packs=packsDir(options.root),registryFile=join21(packs,"registry.ts");if(!existsSync16(registryFile))throw new UptideError("NOT_UPTIDE_CHECKOUT",`no pack registry at ${registryFile}: run uptide pack new inside an uptide checkout`);let dir=packDir(options.package),constant=packConstant(options.package),entries=parseRegistry(readFileSync222(registryFile,"utf8")),home=join21(packs,dir);if(existsSync16(home)||entries.some(e=>e.dir===dir||e.name===constant))throw new UptideError("PACK_EXISTS",`a pack for ${options.package} already exists: ${home}`);let truth={$comment:`Public repositories at the commit before they upgraded ${options.package} from ${options.from} to ${options.to}, and every finding the pack must report there. See docs/packs.md, "Ground truth".`,package:options.package,repos:[]},before=BEFORE.replace("PKG",options.package),after=before.replace("import { oldName }","import { newName }").replace("= oldName(1)","= newName(1)"),files2=[[join21(home,"index.ts"),indexFile(options,constant)],[join21(home,"fixtures",RULE,"before.ts"),before],[join21(home,"fixtures",RULE,"after.ts"),after],[join21(home,`${dir}.test.ts`),testFile(options,constant)],[join21(home,"ground-truth.json"),`${JSON.stringify(truth,null,2)}
484
+ `]];for(let[file,text]of files2)mkdirSync7(join21(file,".."),{recursive:!0}),writeFileSync14(file,text);writeFileSync14(registryFile,renderRegistry([...entries,{dir,name:constant}]));let written=[...files2.map(([file])=>file),registryFile],biome=join21(options.root,"node_modules",".bin","biome"),formattable=written.filter(file=>!file.includes(`${dir}/fixtures/`)),formatted=existsSync16(biome)&&spawnSync(biome,["format","--write",...formattable],{cwd:options.root,stdio:"ignore"}).status===0,verification={status:"candidate",truth:truthDigest(readFileSync222(join21(home,"ground-truth.json"),"utf8")),repos:0,breaking:{precision:1,recall:1,falsePositives:0}},verificationFile=join21(home,"verification.json");return writeFileSync14(verificationFile,`${JSON.stringify(verification,null,2)}
485
+ `),written.splice(written.length-1,0,verificationFile),{dir,constant,files:written.map(file=>relative7(options.root,file)),formatted}}var sitesOf2=(groups,pick)=>groups.reduce((n,g)=>n+pick(g),0);function effortOf2(p){let breaking=(p.plan??[]).filter(g=>g.severity==="breaking"),unverified=(p.plan??[]).filter(g=>g.severity==="unverified"),byRule=sitesOf2(breaking,g=>g.by.rule),byAgent=sitesOf2(breaking,g=>g.by.agent),manual=sitesOf2(breaking,g=>g.by.manual),unconfirmed=sitesOf2(unverified,g=>g.sites),hands=byAgent+manual;return{level:p.status==="unknown"||p.status==="partial"||p.importers?.some(i=>!i.analyzed)?"unknown":hands>25?"large":hands>5?"medium":hands>0||byRule>0||unconfirmed>0?"small":"none",byRule,byAgent,manual,unconfirmed}}var RANK={none:0,small:1,medium:2,large:3,unknown:4};function within(version2,range){try{return satisfies(version2,range)}catch{return!0}}function planUpgrades(report,peers){let behind=report.packages.filter(p=>!p.notes.includes("up to date")&&!["not-imported","workspace","private"].includes(p.status)&&p.installed!==p.target),analyzed=behind.filter(p=>p.status!=="skipped"&&p.status!=="no-types"),notPlanned=behind.filter(p=>!analyzed.includes(p)).map(p=>({name:p.name,installed:p.installed,reason:p.skipReason==="TIME_BUDGET"?"not analyzed: out of time":isFailure(p)?`not analyzed: ${(p.notes[0]??"analysis failed").split(`
486
+ `)[0]}`:p.notes[0]??"not analyzed"})),planned=new Map;for(let p of analyzed){let effort=effortOf2(p);for(let m of p.members??[p]){let previous=planned.get(m.name);planned.set(m.name,{name:m.name,installed:previous&&compareVersions(previous.installed,m.installed)<0?previous.installed:m.installed,target:m.target,tier:p.tier??"generic",effort:previous&&RANK[previous.effort.level]>RANK[effort.level]?previous.effort:effort})}}let constraints=[],before=new Map,tied=new Map,find=name=>{let parent=tied.get(name)??name;if(parent===name)return name;let root=find(parent);return tied.set(name,root),root},tie=(a,b)=>{tied.set(find(a),find(b))};for(let a of planned.values()){for(let[on,range]of Object.entries(peers.ofTarget(a.name,a.target)??{})){let installed2=planned.get(on)?.installed??peers.installed[on]?.version;if(installed2===void 0||within(installed2,range))continue;let b=planned.get(on);if(b&&within(b.target,range)){let back=peers.ofTarget(b.name,b.target)?.[a.name],together=back!==void 0&&!within(a.installed,back);constraints.push({from:a.name,fromVersion:a.target,on,range,effect:together?"together":"before",reason:together?`${a.name} ${a.target} needs ${on} ${range}, and ${on} ${b.target} needs ${a.name} ${back}: upgrade them together`:`${a.name} ${a.target} needs ${on} ${range} (installed: ${installed2}): upgrade ${on} first`}),together?tie(a.name,on):before.set(a.name,new Set([...before.get(a.name)??[],on]))}else constraints.push({from:a.name,fromVersion:a.target,on,range,effect:"blocked",reason:`${a.name} ${a.target} needs ${on} ${range}; ${on} is at ${installed2}${b?` and its target ${b.target} is outside that range too`:" and is not being upgraded"}`})}for(let[x,{version:version2,peers:asked}]of Object.entries(peers.installed)){let range=asked[a.name];if(range===void 0||x===a.name||within(a.target,range))continue;let upgraded=planned.get(x),next=upgraded?peers.ofTarget(x,upgraded.target)?.[a.name]:void 0;if(upgraded&&(next===void 0||within(a.target,next))){let together=next!==void 0&&!within(a.installed,next);constraints.push({from:x,fromVersion:version2,on:a.name,range,effect:together?"together":"before",reason:together?`${x} ${version2} needs ${a.name} ${range} and ${x} ${upgraded.target} needs ${a.name} ${next}: upgrade them together`:`${x} ${version2} needs ${a.name} ${range}: upgrade ${x} to ${upgraded.target} first, which accepts ${a.name} ${a.target}`}),together?tie(a.name,x):before.set(a.name,new Set([...before.get(a.name)??[],x]))}else constraints.push({from:x,fromVersion:version2,on:a.name,range,effect:"blocked",reason:`${x} ${version2} needs ${a.name} ${range}, and ${a.name} ${a.target} is outside it${upgraded?` (so is what ${x} ${upgraded.target} accepts)`:`; ${x} has no upgrade in this plan`}`})}}for(let p of analyzed)for(let m of p.members??[])tie(m.name,(p.members??[])[0]?.name??m.name);let groups=new Map;for(let p of planned.values())groups.set(find(p.name),[...groups.get(find(p.name))??[],p]);let levelOf=members=>members.map(m=>m.effort.level).sort((a,b)=>RANK[b]-RANK[a])[0]??"none",waitsOn=root=>new Set((groups.get(root)??[]).flatMap(m=>[...before.get(m.name)??[]]).map(find).filter(other=>other!==root)),constraintsOf=members=>{let names=new Set(members.map(m=>m.name));return constraints.filter(c=>names.has(c.from)||names.has(c.on))},free=[...groups].filter(([root,members])=>levelOf(members)==="none"&&waitsOn(root).size===0&&constraintsOf(members).length===0),steps=[],done=new Set;if(free.length>0){steps.push({packages:free.flatMap(([,members])=>members).sort((a,b)=>a.name.localeCompare(b.name)),...free.flatMap(([,m])=>m).length>1?{together:"no-impact"}:{},effort:"none",constraints:[]});for(let[root]of free)done.add(root)}let rest=[...groups].filter(([root])=>!done.has(root));for(;rest.length>0;){let ready=rest.filter(([root2])=>[...waitsOn(root2)].every(other=>done.has(other))).sort(([,a],[,b])=>RANK[levelOf(a)]-RANK[levelOf(b)]||(a[0]?.name??"").localeCompare(b[0]?.name??"")),[root,members]=ready[0]??rest[0];steps.push({packages:[...members].sort((a,b)=>a.name.localeCompare(b.name)),...members.length>1?{together:"peer"}:{},effort:levelOf(members),constraints:constraintsOf(members)}),done.add(root),rest.splice(rest.findIndex(([r])=>r===root),1)}return{steps:steps.map((step,i)=>({order:i+1,...step})),notPlanned}}async function upgradePlan(opts,services={}){let fetcher=services.fetcher??opts.fetcher??createNpmFetcher({config:loadRegistryConfig({cwd:opts.cwd})}),discovery=await(services.list??listDependencies)({...opts,fetcher}),saved=opts.checkResults;if(saved&&(!Array.isArray(saved.packages)||resolve13(saved.repo)!==resolve13(opts.cwd)))throw new Error("--results must be a check report for this repository");let byVersion=discovery.packages.flatMap(p=>(p.versions??[{version:p.current,workspaces:p.workspaces}]).filter(v=>compareVersions(p.latest,v.version)>0).map(v=>({...p,current:v.version,workspaces:v.workspaces}))),packages=byVersion.map(p=>{let existing=saved?.packages.find(s=>s.name===p.name&&s.installed===p.current&&s.target===p.latest&&JSON.stringify((s.workspaces??[s.workspace]).slice().sort())===JSON.stringify(p.workspaces.slice().sort()));return existing||{name:p.name,installed:p.current,latest:p.latest,target:p.latest,tier:p.tier,workspace:p.workspaces.length>1?"*":p.workspaces[0]??".",workspaces:p.workspaces,majorsBehind:majorsBehind(p.current,p.latest),findings:[],callSitesChecked:0,unanalyzed:[],status:"unknown",notes:["not checked; effort unknown"],timing:{fetchMs:0,diffMs:0,usagesMs:0,compileMs:0}}});for(let failure of discovery.failures)packages.push({name:failure.name,installed:"?",latest:"?",target:"?",workspace:failure.workspace??".",majorsBehind:0,findings:[],callSitesChecked:0,unanalyzed:[],status:"skipped",skipReason:"ANALYSIS_FAILED",notes:[failure.reason],timing:{fetchMs:0,diffMs:0,usagesMs:0,compileMs:0}});let report={repo:opts.cwd,workspaces:discovery.workspaces,packages,summary:summarize(packages),timing:discovery.timing},installed2={};for(let workspace of discovery.workspaces){let dir=resolve13(opts.cwd,workspace),manifest2=JSON.parse(readFileSync23(join222(dir,"package.json"),"utf8"));for(let name of Object.keys({...manifest2.dependencies,...manifest2.devDependencies}))for(let at=dir;;at=dirname14(at))try{let pkg=JSON.parse(readFileSync23(join222(at,"node_modules",name,"package.json"),"utf8"));installed2[name]??={version:pkg.version,peers:pkg.peerDependencies??{}};break}catch{if(dirname14(at)===at)break}}let targets=new Map;await mapWithLimit(discovery.packages,12,async p=>{try{targets.set(`${p.name}@${p.latest}`,(await fetcher.metadata?.(p.name,p.latest))?.peerDependencies)}catch{}});let plan=planUpgrades(report,{installed:installed2,ofTarget:(name,version2)=>targets.get(`${name}@${version2}`)});plan.notes=["Based on discovery and matching saved check results; unknown effort requires uptide check <package>.","Saved results describe the source at check time; rerun check after source changes."];for(let name of new Set(byVersion.map(p=>p.name))){let versions=byVersion.filter(p=>p.name===name);versions.length>1&&plan.notes.push(`${name}: current versions ${versions.map(p=>`${p.current} (${p.workspaces.join(", ")})`).join("; ")}; one target for all, highest estimated effort shown.`)}let missing=discovery.packages.filter(p=>targets.get(`${p.name}@${p.latest}`)===void 0);return missing.length&&plan.notes.push(`Target peer metadata unavailable: ${missing.map(p=>p.name).join(", ")}`),Object.keys(installed2).length||plan.notes.push("Installed peer constraints unavailable without node_modules."),{report,plan}}import"fs";var VERSION="0.11.2";var import_picocolors=__toESM(require_picocolors(),1);var INVOCATION=uptideCommand(VERSION),runnable=text=>text.replace(/(^|`)uptide (?=[a-z-])/g,`$1${INVOCATION} `);var EXIT={ok:0,breaking:1,error:2},CliError=class extends Error{constructor(message,options={}){super(message);this.options=options}options;name="CliError"};function explain(err){if(err instanceof NoTypesError)return`${err.packageName}@${err.version}: no type declarations, cannot analyze`;if(err instanceof VersionNotFoundError){let hint=err.available.length>0?` (latest published: ${err.available.at(-1)})`:"";return`${err.message}${hint}`}return err instanceof PackageNotFoundError||err instanceof IntegrityError||err instanceof Error?err.message:String(err)}function renderError(err,color){let colors=import_picocolors.default.createColors(color),lines=[`${colors.red("error:")} ${explain(err)}`];return err instanceof CliError&&(err.options.why&&lines.push(` ${err.options.why}`),err.options.next&&lines.push(` ${colors.bold("Next:")} ${err.options.literalNext?err.options.next:runnable(err.options.next)}`)),`${lines.join(`
487
487
  `)}
488
488
  `}async function runJob(job,onProgress){if(job.kind==="check")return check({...job.request,onProgress});if(job.kind==="plan")return upgradePlan({...job.request,onProgress});if(job.kind==="verify")return isolatedVerify({...job.request,onProgress});let{llm,...options}=job.request;return isolatedFix({...options,onProgress,...llm?{}:{fixer:null}})}export{UPTIDE_CONFIG_FILE,parseUptideConfig,packageSelected,OnboardingRefusal,repoFacts,UPTIDE_ACTION,WORKFLOW_PATH,generateOnboarding,snapshotFromDisk,loadUptideConfig,foldForDisplay,depthOf,PRICE_DATE,priceFor,ACCEPTED_KEYS,selectLlm,cleanRuns,removeOnInterrupt,watchInterruptibleClones,fixPreflight,openPr,updatePrBody,listDependencies,testPack,unfinishedPack,scaffoldPack,require_picocolors,VERSION,INVOCATION,runnable,EXIT,CliError,explain,renderError,runJob};