uptide 0.10.0-next.20261013182821 → 0.10.0

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,14 +3,14 @@ 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,compareVersions,createFsSurfaceCache,createNpmFetcher,createTypescriptAdapter,defaultCacheDir,dependencyGroups,describeServices,detectIn,diagnosticKey,diagnostics,editDiff,failedTestFiles,genericPack,gitLineMoves,globRegExp,groupPeerBlockers,heldBackText,importedByText,install,installedManifest,isFailure,knownTool,lineMoves,loadRegistryConfig,majorsBehind,mapWithLimit,markPreexisting,minVersion,moved,newDiagnostics,newestAllowed,packageManager,packumentUrl,parentOf,parseVersion,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-JHCJ2PPM.js";import{require_ts_morph}from"./chunk-GHXBU767.js";import{assertVerifiedTree,publicationBlockers,publish}from"./chunk-AU5ICYIX.js";import{IntegrityError,NoTypesError,PackageNotFoundError,UPTIDE_COMMAND,UptideError,VersionNotFoundError,command,errorCode,git,projectRoot,uptideCommand,uptideVersionInfo}from"./chunk-J2XYTRQU.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 whether it may call an 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:"false: no code is sent to an LLM provider; rule-based fixes only.",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,compareVersions,createFsSurfaceCache,createNpmFetcher,createTypescriptAdapter,defaultCacheDir,dependencyGroups,describeServices,detectIn,diagnosticKey,diagnostics,editDiff,failedTestFiles,genericPack,gitLineMoves,globRegExp,groupPeerBlockers,heldBackText,importedByText,install,installedManifest,isFailure,knownTool,lineMoves,loadRegistryConfig,majorsBehind,mapWithLimit,markPreexisting,minVersion,moved,newDiagnostics,newestAllowed,packageManager,packumentUrl,parentOf,parseVersion,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-FORMDFF5.js";import{require_ts_morph}from"./chunk-GHXBU767.js";import{assertVerifiedTree,publicationBlockers,publish}from"./chunk-T4ECETFZ.js";import{IntegrityError,NoTypesError,PackageNotFoundError,UPTIDE_COMMAND,UptideError,VersionNotFoundError,command,errorCode,git,projectRoot,uptideCommand,uptideVersionInfo}from"./chunk-UKCFG3OM.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(`
10
10
  `)[0]?.replace(/ at line.*$/,"")}`));let nodeAt=path=>{for(let i=path.length;i>=0;i--){let node=path.length===0||i===0?doc.contents:doc.getIn(path.slice(0,i),!0);if(node&&typeof node=="object"&&"range"in node)return node}},at=(path,message)=>`${lineOf(nodeAt(path)?.range?.[0])}${pathText(path)} ${message}`,secrets=[],scan=(node,path)=>{if((0,import_yaml.isMap)(node))for(let pair of node.items){let name=(0,import_yaml.isScalar)(pair.key)?String(pair.key.value):"";keyLike(name)&&secrets.push(`${lineOf(pair.key?.range?.[0])}"${name}" looks like a credential setting`),scan(pair.value,[...path,name])}else(0,import_yaml.isSeq)(node)?node.items.forEach((item,i)=>{scan(item,[...path,i])}):(0,import_yaml.isScalar)(node)&&typeof node.value=="string"&&CREDENTIAL.test(node.value)&&secrets.push(at(path,"looks like a credential"))};if(scan(doc.contents,[]),secrets.length)throw invalid([...secrets,"API keys belong in the repository secret (ANTHROPIC_API_KEY) or an environment variable, never in this file. If this one was committed, rotate it."]);let value=doc.toJS();if(value==null)throw invalid(["the file is empty; it needs at least `version: 1`"]);let record=value;if(typeof record=="object"&&!Array.isArray(record)){if(typeof record.version=="number"&&record.version>1)throw invalid([at(["version"],`is ${record.version}; this uptide reads version 1. Update uptide to read this file.`)]);if("draft"in record)throw invalid([at(["draft"],"is not a setting: Uptide pull requests are always drafts")])}let problems=validate(UPTIDE_CONFIG_SCHEMA,value).map(e=>at(e.path,e.message));if(typeof record.schedule=="string"&&record.schedule!==MANUAL_SCHEDULE){let cron=cronProblem(record.schedule);cron&&problems.push(at(["schedule"],`${cron}, or manual for no scheduled runs`))}if(problems.length)throw invalid(problems);let given=value;return{config:withDefaults(given),given}}function packageSelected(config,name){let matches=globs=>globs.some(glob=>globRegExp(glob).test(name));return matches(config.packages.deny)?!1:config.packages.allow.length===0||matches(config.packages.allow)}var ONBOARDING_INPUTS=["package.json","package-lock.json","npm-shrinkwrap.json","pnpm-lock.yaml","pnpm-workspace.yaml","yarn.lock",".yarnrc.yml","bun.lock","bun.lockb",".nvmrc",".node-version",".tool-versions"],OnboardingRefusal=class extends UptideError{constructor(reason,message){super("ONBOARDING_REFUSED",message),this.reason=reason}reason},NODE_MAJORS=[24,22,20,26,18,25,23,21,19,16],DEFAULT_NODE="24",DECLARED=/^(npm|pnpm|yarn|bun)@(\d+\.\d+\.\d+(?:-[\w.]+)?)(?:\+[\w.-]+)?$/;function repoFacts(snapshot2){let files2=snapshot2.files,manifestText=files2["package.json"];if(manifestText===void 0)throw new OnboardingRefusal("not-javascript","There is no package.json at the repository root. Uptide works on JavaScript and TypeScript repositories with a package.json and a lockfile at the root.");let manifest2;try{if(manifest2=JSON.parse(manifestText),!manifest2||typeof manifest2!="object"||Array.isArray(manifest2))throw new Error}catch{throw new OnboardingRefusal("invalid-manifest","package.json at the root is not valid JSON.")}let name=typeof manifest2.name=="string"?manifest2.name:void 0;return{...snapshot2.slug?{slug:snapshot2.slug}:{},defaultBranch:snapshot2.defaultBranch,...name?{name}:{},manager:managerFacts(files2,manifest2.packageManager),node:nodeFacts(files2,manifest2),workspaces:workspaceGlobs(files2,manifest2.workspaces)}}var LOCKFILES=[["package-lock.json","npm"],["npm-shrinkwrap.json","npm"],["pnpm-lock.yaml","pnpm"],["yarn.lock","yarn"],["bun.lock","bun"],["bun.lockb","bun"]];function managerFacts(files2,field){let declared;if(field!==void 0){let match2=typeof field=="string"?DECLARED.exec(field):null;if(!match2)throw new OnboardingRefusal("invalid-manifest",'package.json "packageManager" is not in the form name@version (for example pnpm@10.17.1), so Corepack cannot use it.');declared={name:match2[1],version:match2[2],raw:field}}let present2=LOCKFILES.filter(([file])=>files2[file]!==void 0);if(declared?.name==="bun"||!declared&&present2.length>0&&present2.every(([,m])=>m==="bun"))throw new OnboardingRefusal("bun",`This repository uses Bun (${declared?`packageManager is ${declared.raw}`:present2.map(([f])=>f).join(", ")}). Uptide supports npm, pnpm and Yarn; Bun is not supported yet.`);if(present2.length===0)throw new OnboardingRefusal("no-lockfile","There is no lockfile at the repository root (package-lock.json, pnpm-lock.yaml or yarn.lock). Uptide installs exactly what the lockfile pins; commit one first.");let pick;if(declared){if(pick=present2.find(([,m])=>m===declared.name),!pick)throw new OnboardingRefusal("manager-mismatch",`package.json says packageManager ${declared.raw}, but the lockfile is ${present2.map(([f])=>f).join(", ")}. Make them agree (regenerate the lockfile with ${declared.name}, or change packageManager), then try again.`)}else{if([...new Set(present2.map(([,m])=>m))].length>1)throw new OnboardingRefusal("several-lockfiles",`There are several lockfiles (${present2.map(([f])=>f).join(", ")}) and no "packageManager" in package.json to say which one is used. Remove the stale ones, or set packageManager.`);pick=present2[0]}let[lockfile,name]=pick,text=files2[lockfile]??"",base={name,lockfile,...declared?{declared:declared.raw}:{}};if(name==="npm"){let version22=Number(/"lockfileVersion"\s*:\s*(\d+)/.exec(text)?.[1]);if(version22!==2&&version22!==3)throw new OnboardingRefusal("unsupported-lockfile",`${lockfile} has lockfileVersion ${Number.isNaN(version22)?"missing":version22}; Uptide needs version 2 or 3 (npm 7 or newer). Run \`npm install --package-lock-only\` with a current npm and commit the result.`);return{...base,...declared?{version:declared.version}:{},install:"npm ci --ignore-scripts --no-audit --no-fund"}}if(name==="pnpm"){let lockVersion=/^lockfileVersion:\s*['"]?([\d.]+)/m.exec(text)?.[1],lockMajor=Number(lockVersion?.split(".")[0]);if(declared){let major=import_semver.default.major(declared.version);if(lockMajor>=9&&major<9)throw new OnboardingRefusal("manager-mismatch",`package.json says packageManager ${declared.raw}, but pnpm-lock.yaml was written by pnpm 9 or newer (lockfileVersion ${lockVersion}). Make them agree, then try again.`)}let implied=lockMajor>=9?"10":lockMajor===6?"8":lockVersion==="5.4"?"7":void 0;if(!declared&&!implied)throw new OnboardingRefusal("unknown-manager-version",`pnpm-lock.yaml has lockfileVersion ${lockVersion??"missing"}, which does not say which pnpm wrote it. Set "packageManager" in package.json (for example pnpm@10.17.1).`);return{...base,version:declared?.version??implied,install:"pnpm install --frozen-lockfile --ignore-scripts"}}let berry=/^__metadata:/m.test(text),yarnrc=readYarnrc(files2[".yarnrc.yml"]);if(declared){let major=import_semver.default.major(declared.version);if(berry!==major>=2)throw new OnboardingRefusal("manager-mismatch",`package.json says packageManager ${declared.raw}, but yarn.lock is in the Yarn ${berry?"Berry (2 or newer)":"classic (1)"} format. Make them agree, then try again.`)}if(!berry)return{...base,flavor:"classic",...declared?{version:declared.version}:{},install:"yarn install --frozen-lockfile --ignore-scripts --non-interactive"};let pathVersion=/yarn-(\d+\.\d+\.\d+[\w.-]*)\.c?js$/.exec(yarnrc.yarnPath??"")?.[1],version2=declared?.version??pathVersion;if(!declared&&!yarnrc.yarnPath)throw new OnboardingRefusal("unknown-manager-version",'yarn.lock is in the Yarn Berry format, but neither "packageManager" in package.json nor yarnPath in .yarnrc.yml says which Yarn to run. Set packageManager (for example yarn@4.9.2).');if(yarnrc.nodeLinker!=="node-modules"&&yarnrc.nodeLinker!=="pnpm")throw new OnboardingRefusal("yarn-pnp",`This repository uses Yarn Plug'n'Play (no "nodeLinker: node-modules" in .yarnrc.yml). Uptide reads installed packages from node_modules, so Plug'n'Play is not supported.`);let skipBuilds=version2&&import_semver.default.valid(version2)&&import_semver.default.major(version2)<3?"--skip-builds":"--mode=skip-build";return{...base,flavor:"berry",...version2?{version:version2}:{},install:`yarn install --immutable ${skipBuilds}`}}function readYarnrc(text){if(text===void 0)return{};try{let doc=(0,import_yaml2.parse)(text);return{...typeof doc?.yarnPath=="string"?{yarnPath:doc.yarnPath}:{},...typeof doc?.nodeLinker=="string"?{nodeLinker:doc.nodeLinker}:{}}}catch{return{}}}function nodeFacts(files2,manifest2){for(let file of[".nvmrc",".node-version"]){let text=files2[file]?.trim();if(text)return{file,source:`${file} (${text.split(/\s/)[0]})`}}let tool=/^nodejs\s+(\S+)/m.exec(files2[".tool-versions"]??"")?.[1];if(tool)return{file:".tool-versions",source:`.tool-versions (${tool})`};let volta=manifest2.volta?.node;if(typeof volta=="string"&&import_semver.default.valid(volta))return{version:volta,source:`package.json volta.node (${volta})`};let engines=manifest2.engines?.node;if(typeof engines=="string"&&engines.trim()){let range=import_semver.default.validRange(engines),major=range?NODE_MAJORS.find(m=>import_semver.default.intersects(range,`${m}.x`)):void 0;if(major===void 0)throw new OnboardingRefusal("unsupported-node",`package.json engines.node is "${engines}", which no Node release Uptide can run on satisfies (${[...NODE_MAJORS].sort((a,b)=>a-b).join(", ")}). Add an .nvmrc with the version CI should use.`);return{version:String(major),source:`package.json engines.node (${engines})`}}return{version:DEFAULT_NODE,source:`no .nvmrc, .node-version or engines.node; Node ${DEFAULT_NODE} (LTS)`}}function workspaceGlobs(files2,field){let strings=list3=>Array.isArray(list3)?list3.filter(p=>typeof p=="string"):[],pnpm=files2["pnpm-workspace.yaml"];if(pnpm!==void 0)try{return strings((0,import_yaml2.parse)(pnpm)?.packages)}catch{throw new OnboardingRefusal("invalid-manifest","pnpm-workspace.yaml is not valid YAML.")}return strings(Array.isArray(field)?field:field?.packages)}var PINNED_ACTIONS={checkout:{repo:"actions/checkout",sha:"3d3c42e5aac5ba805825da76410c181273ba90b1",tag:"v7.0.1"},setupNode:{repo:"actions/setup-node",sha:"949feb2413d6458794dcd2491c4babbbce0c15c1",tag:"v7.1.0"},pnpm:{repo:"pnpm/action-setup",sha:"ea17c68df8912ef543352723c149a84f56e3d413",tag:"v6.1.0"}},UPTIDE_ACTION={repo:"uptide-dev/uptide",sha:"2045c7dc0fcb7cd86522595de06308f154b1d8bf",tag:"v0.9.0",released:!0},COREPACK_VERSION="0.36.0",usesLine=action=>`${action.repo}@${action.sha} # ${action.tag}`,WORKFLOW_PATH=".github/workflows/uptide.yml",DOCS="https://github.com/uptide-dev/uptide/blob/main/docs";function managerLabel(m){let name=m.name==="yarn"?`Yarn ${m.flavor==="berry"?"Berry":"classic"}`:m.name,version2=m.version?` ${m.version}`:"",how=m.declared?"from packageManager, run through Corepack":m.name==="pnpm"?"implied by the lockfile, set up with pnpm/action-setup":m.flavor==="berry"?"the yarnPath in .yarnrc.yml":"the version on the runner";return`${name}${version2} (${how})`}var nodeInput=node=>node.file?`node-version-file: ${node.file}`:`node-version: '${node.version}'`;function setupSteps(facts){let m=facts.manager,node=nodeInput(facts.node),setupNode=cache=>[` - uses: ${usesLine(PINNED_ACTIONS.setupNode)}`," with:",` ${node}`,...cache?[` cache: ${m.name}`]:[]].join(`
11
11
  `),corepack=enable=>[` - name: Enable Corepack for ${m.declared}`," run: |",` npm install --global corepack@${COREPACK_VERSION}`,` ${enable}`].join(`
12
12
  `);return m.declared&&m.name==="npm"?[setupNode(!0),corepack("corepack enable npm")]:m.declared?[setupNode(!1),corepack("corepack enable"),setupNode(!0)]:m.name==="pnpm"?[[` - uses: ${usesLine(PINNED_ACTIONS.pnpm)}`," with:",` version: '${m.version}'`].join(`
13
- `),setupNode(!0)]:[setupNode(!0)]}function renderWorkflow(facts,config){let action=[" # Reads uptide.yml, runs Uptide, opens draft pull requests and uploads the results JSON.",` - uses: ${usesLine(UPTIDE_ACTION)}`," with:",` config: ${UPTIDE_CONFIG_FILE}`," mode: ${{ inputs.mode || 'fix' }}"," package: ${{ inputs.package }}",...config.llm.enabled?[" anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}"]:[]].join(`
13
+ `),setupNode(!0)]:[setupNode(!0)]}function renderWorkflow(facts,config){let action=[" # Reads uptide.yml, runs Uptide, opens draft pull requests and uploads the results JSON.",` - uses: ${usesLine(UPTIDE_ACTION)}`," with:",` config: ${UPTIDE_CONFIG_FILE}`," mode: ${{ inputs.mode || 'fix' }}"," package: ${{ inputs.package }}"," anthropic-api-key: ${{ secrets.ANTHROPIC_API_KEY }}"].join(`
14
14
  `),trigger=config.schedule===MANUAL_SCHEDULE?' # uptide.yml "schedule" is manual: runs start from the Actions tab or the Uptide dashboard.\n # After changing it there, run `npx uptide init --force`.\n':` schedule:
15
15
  # uptide.yml "schedule" (UTC). After changing it there, run \`npx uptide init --force\`.
16
16
  - cron: '${config.schedule}'
@@ -82,9 +82,8 @@ schedule: '${config.schedule}'
82
82
  maxOpenPrs: ${config.maxOpenPrs}
83
83
 
84
84
  llm:
85
- # Assisted fixes for call sites no rule covers. false: no code is sent to an LLM.
86
- enabled: ${config.llm.enabled}
87
- # Who receives that code, with this repository's own key (secret ANTHROPIC_API_KEY).
85
+ # Who receives the code around call sites no rule covers, with this repository's own key
86
+ # (secret ANTHROPIC_API_KEY). The Uptide workflow does not run without that key.
88
87
  provider: ${config.llm.provider}
89
88
 
90
89
  # LLM budget in USD; the SDK may overshoot by one turn.
@@ -93,7 +92,7 @@ maxCost: ${config.maxCost}
93
92
  # How assisted fixes are applied: auto (SDK for Anthropic when installed, patch otherwise), patch (one edit per call site),
94
93
  # or sdk (a Claude Agent SDK session).
95
94
  executor: ${config.executor}
96
- `}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function describeSchedule(cron){if(cron===MANUAL_SCHEDULE)return"only when someone starts it";let m=/^(\d{1,2}) (\d{1,2}) \* \* ([0-7])$/.exec(cron.trim());if(!m)return`on the cron schedule \`${cron}\` (UTC)`;let pad=n=>n.padStart(2,"0");return`every ${WEEKDAYS[Number(m[3])%7]} at ${pad(m[2])}:${pad(m[1])} UTC`}function renderPrBody(facts,config){let m=facts.manager,llm=config.llm.enabled,detected=[`- Package manager: ${managerLabel(m)}`,`- Lockfile: \`${m.lockfile}\`, installed with \`${m.install}\``,`- Node: ${facts.node.source}`,`- Workspaces: ${facts.workspaces.length?facts.workspaces.map(w=>`\`${w}\``).join(", "):"none (a single package)"}`,`- Default branch: \`${facts.defaultBranch}\``];return`This pull request adds Uptide to this repository. Nothing runs until it is merged.
95
+ `}var WEEKDAYS=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];function describeSchedule(cron){if(cron===MANUAL_SCHEDULE)return"only when someone starts it";let m=/^(\d{1,2}) (\d{1,2}) \* \* ([0-7])$/.exec(cron.trim());if(!m)return`on the cron schedule \`${cron}\` (UTC)`;let pad=n=>n.padStart(2,"0");return`every ${WEEKDAYS[Number(m[3])%7]} at ${pad(m[2])}:${pad(m[1])} UTC`}function renderPrBody(facts,config){let m=facts.manager,detected=[`- Package manager: ${managerLabel(m)}`,`- Lockfile: \`${m.lockfile}\`, installed with \`${m.install}\``,`- Node: ${facts.node.source}`,`- Workspaces: ${facts.workspaces.length?facts.workspaces.map(w=>`\`${w}\``).join(", "):"none (a single package)"}`,`- Default branch: \`${facts.defaultBranch}\``];return`This pull request adds Uptide to this repository. Nothing runs until it is merged.
97
96
 
98
97
  Uptide finds dependency upgrades that break your code, migrates the code, and checks the result with your own compiler and tests. Each migration arrives as a draft pull request for you to review.
99
98
 
@@ -128,26 +127,22 @@ With that proof, the Uptide GitHub App gives the run a token that expires within
128
127
 
129
128
  ## Set the API key
130
129
 
131
- ${llm?`Assisted fixes need an Anthropic API key, stored as a repository secret named \`ANTHROPIC_API_KEY\`:
130
+ Uptide needs an Anthropic API key, stored as a repository secret named \`ANTHROPIC_API_KEY\`:
132
131
 
133
- - In GitHub: Settings, Secrets and variables, Actions, New repository secret.
132
+ - In GitHub: Settings \u2192 Secrets and variables \u2192 Actions \u2192 New repository secret.
134
133
  - Or with the GitHub CLI: \`gh secret set ANTHROPIC_API_KEY\`
135
134
 
136
- Without the secret, Uptide still runs and applies only its rule-based fixes. The key is passed to the Uptide step alone, never to the install step or to your tests.`:"Not needed: the LLM is turned off in uptide.yml."}
137
-
138
- ## Turn the LLM off
139
-
140
- Set \`enabled: false\` under \`llm\` in \`uptide.yml\`. Uptide then sends no code anywhere and applies only rule-based fixes. You can also delete the \`ANTHROPIC_API_KEY\` secret.
135
+ Without the secret, the workflow does not run Uptide: the job fails and its summary says what to add. The key is passed to the Uptide step alone, never to the install step or to your tests.
141
136
 
142
137
  ## Remove Uptide
143
138
 
144
- Delete \`${WORKFLOW_PATH}\` and \`${UPTIDE_CONFIG_FILE}\`, and the \`ANTHROPIC_API_KEY\` secret if you added it. If you installed the Uptide GitHub App, uninstall it in Settings, GitHub Apps. To not start at all, close this pull request.
139
+ Delete \`${WORKFLOW_PATH}\` and \`${UPTIDE_CONFIG_FILE}\`, and the \`ANTHROPIC_API_KEY\` secret. If you installed the Uptide GitHub App, uninstall it in Settings, GitHub Apps. To not start at all, close this pull request.
145
140
 
146
141
  ## Detected in this repository
147
142
 
148
143
  ${detected.join(`
149
144
  `)}
150
- `}var TRUST_URL="https://uptide-app.vercel.app/trust";function llmLine(config){if(!config.llm.enabled)return"**No code sent to an LLM**: assisted fixes are off in uptide.yml.";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&&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) for now. Use provider anthropic, or set llm.enabled to false.`);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 existsSync6,mkdirSync as mkdirSync4,mkdtempSync as mkdtempSync2,readdirSync,readFileSync as readFileSync12,realpathSync as realpathSync8,rmSync as rmSync3,statSync as statSync22,writeFileSync as writeFileSync7}from"fs";import{tmpdir as tmpdir2}from"os";import{basename,dirname as dirname9,isAbsolute as isAbsolute3,join as join12,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 readFileSync11,realpathSync as realpathSync7,writeFileSync as writeFileSync6}from"fs";import{dirname as dirname8,join as join11,resolve as resolve7}from"path";import{mkdirSync as mkdirSync2,readFileSync as readFileSync10,realpathSync as realpathSync6,writeFileSync as writeFileSync5}from"fs";import{dirname as dirname7,join as join10,relative as relative5,resolve as resolve6}from"path";import{readFileSync as readFileSync5,realpathSync as realpathSync2,writeFileSync as writeFileSync2}from"fs";import{join as join4}from"path";import{setTimeout as delay}from"timers/promises";import{readFileSync as readFileSync4,realpathSync,writeFileSync}from"fs";import{isAbsolute,relative,resolve as resolve22}from"path";import{existsSync as existsSync22,readFileSync as readFileSync3}from"fs";import{join as join32}from"path";import{execFileSync}from"child_process";import{mkdtempSync,rmSync,writeFileSync as writeFileSync3}from"fs";import{tmpdir}from"os";import{join as join6}from"path";import{existsSync as existsSync32,readFileSync as readFileSync6,realpathSync as realpathSync3}from"fs";import{dirname as dirname3,join as join5,relative as relative2,resolve as resolve3}from"path";import{existsSync as existsSync4,readFileSync as readFileSync7}from"fs";import{dirname as dirname4,join as join7,relative as relative3}from"path";import{mkdirSync,readFileSync as readFileSync8,realpathSync as realpathSync4,writeFileSync as writeFileSync4}from"fs";import{dirname as dirname5,join as join8,resolve as resolve4}from"path";import{createHash}from"crypto";import{existsSync as existsSync5,lstatSync,readFileSync as readFileSync9,realpathSync as realpathSync5,rmSync as rmSync2}from"fs";import{dirname as dirname6,isAbsolute as isAbsolute2,join as join9,relative as relative4,resolve as resolve5}from"path";import{readFileSync as readFileSync13,writeFileSync as writeFileSync8}from"fs";import{dirname as dirname10,join as join13}from"path";import{existsSync as existsSync7,mkdtempSync as mkdtempSync3,readFileSync as readFileSync14,rmSync as rmSync4,writeFileSync as writeFileSync9}from"fs";import{tmpdir as tmpdir3}from"os";import{isAbsolute as isAbsolute4,join as join14,resolve as resolve9}from"path";import{readFileSync as readFileSync16}from"fs";import{join as join16,resolve as resolve11}from"path";import{existsSync as existsSync8,readFileSync as readFileSync15}from"fs";import{dirname as dirname11,join as join15,resolve as resolve10}from"path";import{existsSync as existsSync9,readdirSync as readdirSync2,readFileSync as readFileSync17,writeFileSync as writeFileSync10}from"fs";import{join as join17}from"path";import{existsSync as existsSync11,readFileSync as readFileSync19,writeFileSync as writeFileSync12}from"fs";import{join as join19}from"path";import{existsSync as existsSync10,mkdirSync as mkdirSync5,readFileSync as readFileSync18,renameSync,rmSync as rmSync5,writeFileSync as writeFileSync11}from"fs";import{join as join18}from"path";import{spawnSync}from"child_process";import{existsSync as existsSync12,mkdirSync as mkdirSync6,readFileSync as readFileSync20,writeFileSync as writeFileSync13}from"fs";import{join as join20,relative as relative6}from"path";import{readFileSync as readFileSync21}from"fs";import{dirname as dirname12,join as join21,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 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(`
145
+ `}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 existsSync6,mkdirSync as mkdirSync4,mkdtempSync as mkdtempSync2,readdirSync,readFileSync as readFileSync12,realpathSync as realpathSync8,rmSync as rmSync3,statSync as statSync22,writeFileSync as writeFileSync7}from"fs";import{tmpdir as tmpdir2}from"os";import{basename,dirname as dirname9,isAbsolute as isAbsolute3,join as join12,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 readFileSync11,realpathSync as realpathSync7,writeFileSync as writeFileSync6}from"fs";import{dirname as dirname8,join as join11,resolve as resolve7}from"path";import{mkdirSync as mkdirSync2,readFileSync as readFileSync10,realpathSync as realpathSync6,writeFileSync as writeFileSync5}from"fs";import{dirname as dirname7,join as join10,relative as relative5,resolve as resolve6}from"path";import{readFileSync as readFileSync5,realpathSync as realpathSync2,writeFileSync as writeFileSync2}from"fs";import{join as join4}from"path";import{setTimeout as delay}from"timers/promises";import{readFileSync as readFileSync4,realpathSync,writeFileSync}from"fs";import{isAbsolute,relative,resolve as resolve22}from"path";import{existsSync as existsSync22,readFileSync as readFileSync3}from"fs";import{join as join32}from"path";import{execFileSync}from"child_process";import{mkdtempSync,rmSync,writeFileSync as writeFileSync3}from"fs";import{tmpdir}from"os";import{join as join6}from"path";import{existsSync as existsSync32,readFileSync as readFileSync6,realpathSync as realpathSync3}from"fs";import{dirname as dirname3,join as join5,relative as relative2,resolve as resolve3}from"path";import{existsSync as existsSync4,readFileSync as readFileSync7}from"fs";import{dirname as dirname4,join as join7,relative as relative3}from"path";import{mkdirSync,readFileSync as readFileSync8,realpathSync as realpathSync4,writeFileSync as writeFileSync4}from"fs";import{dirname as dirname5,join as join8,resolve as resolve4}from"path";import{createHash}from"crypto";import{existsSync as existsSync5,lstatSync,readFileSync as readFileSync9,realpathSync as realpathSync5,rmSync as rmSync2}from"fs";import{dirname as dirname6,isAbsolute as isAbsolute2,join as join9,relative as relative4,resolve as resolve5}from"path";import{readFileSync as readFileSync13,writeFileSync as writeFileSync8}from"fs";import{dirname as dirname10,join as join13}from"path";import{existsSync as existsSync7,mkdtempSync as mkdtempSync3,readFileSync as readFileSync14,rmSync as rmSync4,writeFileSync as writeFileSync9}from"fs";import{tmpdir as tmpdir3}from"os";import{isAbsolute as isAbsolute4,join as join14,resolve as resolve9}from"path";import{readFileSync as readFileSync16}from"fs";import{join as join16,resolve as resolve11}from"path";import{existsSync as existsSync8,readFileSync as readFileSync15}from"fs";import{dirname as dirname11,join as join15,resolve as resolve10}from"path";import{existsSync as existsSync9,readdirSync as readdirSync2,readFileSync as readFileSync17,writeFileSync as writeFileSync10}from"fs";import{join as join17}from"path";import{existsSync as existsSync11,readFileSync as readFileSync19,writeFileSync as writeFileSync12}from"fs";import{join as join19}from"path";import{existsSync as existsSync10,mkdirSync as mkdirSync5,readFileSync as readFileSync18,renameSync,rmSync as rmSync5,writeFileSync as writeFileSync11}from"fs";import{join as join18}from"path";import{spawnSync}from"child_process";import{existsSync as existsSync12,mkdirSync as mkdirSync6,readFileSync as readFileSync20,writeFileSync as writeFileSync13}from"fs";import{join as join20,relative as relative6}from"path";import{readFileSync as readFileSync21}from"fs";import{dirname as dirname12,join as join21,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 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(`
151
146
  `);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(`
152
147
  `),output=[],cursor=0,hunks=0;for(;lines.length;){let header=lines.shift(),h=/^@@ -(\d+)(?:,(\d+))? \+\d+(?:,(\d+))? @@.*$/.exec(header??"");if(!h)throw new Error("invalid hunk or additional file in patch");let declared={old:Number(h[2]??1),next:Number(h[3]??1)},body=[];for(;lines.length&&!lines[0]?.startsWith("@@");)body.push(lines.shift()??"");let old=[],next=[],unescaped=body.map(raw=>raw.includes('\\"')&&!source.includes('\\"')?raw.replaceAll('\\"','"'):raw),added=unescaped.filter(l=>l.startsWith("+")).length,removedLines=unescaped.filter(l=>l.startsWith("-")).length;if(added>0&&removedLines===0&&declared.next===declared.old){let at=unescaped.findIndex(l=>l.startsWith("+")),above=unescaped[at-1];if(added===1&&above!==void 0&&(above===""||above.startsWith(" ")))unescaped[at-1]=`-${above.slice(1)}`;else throw new Error(`hunk at line ${h[1]} adds ${added} lines with no \`-\` line, yet its header says the length is unchanged: a replaced line needs its \`-\` line before the \`+\` line`)}let blankContext=[];for(let raw of unescaped){if(raw.startsWith("--- ")||raw.startsWith("+++ ")||raw.startsWith("diff --git"))throw new Error("additional file in patch");let line=raw===""?" ":raw,mode=line[0],content=line.slice(1);if(![" ","-","+"].includes(mode??""))throw new Error(`invalid patch line: ${line}`);if(mode===" "&&content.trim()===""&&blankContext.push({old:old.length,next:next.length}),(mode===" "||mode==="-")&&old.push(content),(mode===" "||mode==="+")&&next.push(content),mode==="+"&&/@ts-(?:ignore|nocheck|expect-error)|\bas\s+any\b|\bas\s+unknown\s+as\b/.test(content))throw new Error("diagnostic suppression is not a migration")}let start=Number(h[1])-1,matching=o2=>i=>i>=cursor&&i<=original.length&&o2.every((line,n2)=>original[i+n2]===line),[o,n]=[old,next],matches=matching(o);if(!matches(start)&&blankContext.length){let without=(list22,at)=>list22.filter((_,i)=>!at.includes(i)),[oc,nc]=[without(old,blankContext.map(b=>b.old)),without(next,blankContext.map(b=>b.next))];(matching(oc)(start)||original.map((_,i)=>i).filter(matching(oc)).length===1)&&([o,n]=[oc,nc],matches=matching(o))}if(!matches(start)){let candidates=o.length?original.map((_,i)=>i).filter(matches):[];if(candidates.length!==1)throw new Error(`patch context does not match uniquely (hunk at line ${start+1}); preserve exact source text and use minimal hunks`);start=candidates[0]}if(regions){let prefix=0,suffix=0;for(;prefix<o.length&&prefix<n.length&&o[prefix]===n[prefix];)prefix++;for(;suffix<o.length-prefix&&suffix<n.length-prefix&&o[o.length-1-suffix]===n[n.length-1-suffix];)suffix++;let first=start+prefix+1,last=start+o.length-suffix;if(!regions.some(r=>first>=r.line&&Math.max(first,last)<=r.endLine))throw new Error(`patch edits outside the related declaration in ${file}`)}output.push(...original.slice(cursor,start),...n),cursor=start+o.length,hunks++}if(!hunks)throw new Error("empty patch");return output.push(...original.slice(cursor)),output.join(`
153
148
  `)}function applySitePatch(files2,patch){if(patch.length>1e6)throw new Error("patch exceeds 1 MB");let clean=patch.trimEnd().replace(/^```(?:diff)?\s*\n/,"").replace(/\n```\s*$/,""),headers=[...clean.matchAll(/^--- a\/(.+)\n\+\+\+ b\/(.+)$/gm)];if(!headers.length)throw new Error("patch must modify exactly the reported file or a related declaration");let edits=new Map;for(let[i,header]of headers.entries()){let file=header[1]??"",allowed=files2.get(file);if(!allowed||file!==header[2]||edits.has(file))throw new Error(`patch file is not editable: ${file}`);let start=i===0?0:header.index,end=headers[i+1]?.index??clean.length,part=clean.slice(start,end).replace(/\ndiff --git [^\n]*(?:\nindex [^\n]*)?\n?$/,"").trimEnd();edits.set(file,applyFilePatch(allowed.source,file,part,allowed.regions))}return edits}var quote=arg=>/^[\w@%+=:,./-]+$/.test(arg)?arg:`'${arg.replaceAll("'","'\\''")}'`,list2=files2=>files2.map(quote).join(" "),has=(root,names)=>names.some(name=>existsSync22(join32(root,name))),manifest=root=>{try{return JSON.parse(readFileSync3(join32(root,"package.json"),"utf8"))}catch{return{}}},installed=(root,bin)=>existsSync22(join32(root,"node_modules/.bin",bin));function detectStyle(root){let tools=[];return has(root,["biome.json","biome.jsonc"])&&installed(root,"biome")&&tools.push({name:"biome",format:files2=>`biome format --write ${list2(files2)}`,lint:files2=>`biome check ${list2(files2)}`}),(has(root,[".prettierrc",".prettierrc.json",".prettierrc.yaml",".prettierrc.yml",".prettierrc.js",".prettierrc.cjs",".prettierrc.mjs","prettier.config.js","prettier.config.cjs","prettier.config.mjs","prettier.config.ts"])||manifest(root).prettier!==void 0)&&installed(root,"prettier")&&tools.push({name:"prettier",format:files2=>`prettier --write ${list2(files2)}`,lint:files2=>`prettier --check ${list2(files2)}`}),(has(root,["eslint.config.js","eslint.config.mjs","eslint.config.cjs","eslint.config.ts",".eslintrc",".eslintrc.json",".eslintrc.js",".eslintrc.cjs",".eslintrc.yaml",".eslintrc.yml"])||manifest(root).eslintConfig!==void 0)&&installed(root,"eslint")&&tools.push({name:"eslint",lint:files2=>`eslint ${list2(files2)}`}),tools}var run=(root,script,timeoutMs)=>command(root,process.env.SHELL??"/bin/sh",["-c",script],timeoutMs,{PATH:[join32(root,"node_modules/.bin"),process.env.PATH].join(":")}),present=(root,files2)=>[...new Set(files2)].filter(file=>existsSync22(join32(root,file))).sort(),ANSI=new RegExp("\x1B\\[[0-9;]*[A-Za-z]","g");async function formatFiles(root,files2,timeoutMs=12e4){let targets=present(root,files2);if(targets.length===0)return[];let ran=[];for(let tool of detectStyle(root))tool.format&&(await run(root,tool.format(targets),timeoutMs),ran.push(tool.name));return ran}async function lintFiles(root,files2,baseline=[],timeoutMs=12e4){let targets=present(root,files2);if(targets.length===0)return[];let results=[];for(let tool of detectStyle(root)){let script=tool.lint(targets),result=await run(root,script,timeoutMs),before=baseline.find(b=>b.tool===tool.name);results.push({tool:tool.name,status:!result.code&&!result.timeout?"passed":before?.status==="failed"?"pre-existing":"failed",command:script,files:targets.length,output:result.code||result.timeout?result.output.replace(ANSI,"").split(root).join("<repo>").slice(-4e3):""})}return results}function safeFile(root,file){if(isAbsolute(file)||file.split(/[\\/]/).includes("..")||file.includes("\0"))throw new Error(`patch path outside repository: ${file}`);let path=resolve22(root,file);if(!realpathSync(path).startsWith(`${realpathSync(root)}/`))throw new Error(`patch symlink escapes repository: ${file}`);return path}function commit(root,message,files2){files2.length&&(git(root,"add","--",...files2.map(f=>relative(root,f))),git(root,"diff","--cached","--name-only")&&git(root,"commit","-m",message))}function applyFollowUps(input){let{root,rule}=input,applied=0,byFile=new Map;for(let edit of input.edits)byFile.set(edit.file,[...byFile.get(edit.file)??[],edit]);for(let[name,group]of byFile){let file=safeFile(root,name),lines=readFileSync4(file,"utf8").split(`
@@ -399,7 +394,7 @@ ${reach.map(l=>` ${l}`).join(`
399
394
  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=realpathSync6(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;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:[...packages].sort((a,b)=>compareVersions(a.installed,b.installed))[0]?.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(readFileSync10(join10(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,join10(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=readFileSync10(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=readFileSync10(file,"utf8");writeFileSync5(file,edit.text),files2.push(file),sites.push({finding:{...base,usage:{...base.usage,file:edit.file,line:edit.line??original.split(`
400
395
  `).length,column:1,endLine:edit.text.split(`
401
396
  `).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}:{}});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(`
402
- `).filter(Boolean),branch,sites,verification:{baseline,target:targetErrors,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:sdkMissing?{...llm,sdkMissing:!0}:llm,...behavior?{behavior}:{},timingMs:Date.now()-started,prBody:join10(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(dirname7(result.prBody),{recursive:!0}),writeFileSync5(result.prBody,prBody(result));let storedRun=join10(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-J4ZOFUSU-ZF77F7GT.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(`
397
+ `).filter(Boolean),branch,sites,verification:{baseline,target:targetErrors,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:sdkMissing?{...llm,sdkMissing:!0}:llm,...behavior?{behavior}:{},timingMs:Date.now()-started,prBody:join10(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(dirname7(result.prBody),{recursive:!0}),writeFileSync5(result.prBody,prBody(result));let storedRun=join10(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-U36HVRJW-TDM7QXGG.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(`
403
398
  `));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(`
404
399
  `)[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(`
405
400
  `))}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=realpathSync7(resolve7(options.cwd));confirmServices(root,options);let file=resolve7(options.run??join11(root,".uptide/report.json")),report;try{report=JSON.parse(readFileSync11(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(`
@@ -480,6 +475,6 @@ it('${o.package.replaceAll("'","\\'")}: every fixture rewrites and detects exact
480
475
  `}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=join20(packs,"registry.ts");if(!existsSync12(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(readFileSync20(registryFile,"utf8")),home=join20(packs,dir);if(existsSync12(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=[[join20(home,"index.ts"),indexFile(options,constant)],[join20(home,"fixtures",RULE,"before.ts"),before],[join20(home,"fixtures",RULE,"after.ts"),after],[join20(home,`${dir}.test.ts`),testFile(options,constant)],[join20(home,"ground-truth.json"),`${JSON.stringify(truth,null,2)}
481
476
  `]];for(let[file,text]of files2)mkdirSync6(join20(file,".."),{recursive:!0}),writeFileSync13(file,text);writeFileSync13(registryFile,renderRegistry([...entries,{dir,name:constant}]));let written=[...files2.map(([file])=>file),registryFile],biome=join20(options.root,"node_modules",".bin","biome"),formattable=written.filter(file=>!file.includes(`${dir}/fixtures/`)),formatted=existsSync12(biome)&&spawnSync(biome,["format","--write",...formattable],{cwd:options.root,stdio:"ignore"}).status===0,verification={status:"candidate",truth:truthDigest(readFileSync20(join20(home,"ground-truth.json"),"utf8")),repos:0,breaking:{precision:1,recall:1,falsePositives:0}},verificationFile=join20(home,"verification.json");return writeFileSync13(verificationFile,`${JSON.stringify(verification,null,2)}
482
477
  `),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(`
483
- `)[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(readFileSync21(join21(dir,"package.json"),"utf8"));for(let name of Object.keys({...manifest2.dependencies,...manifest2.devDependencies}))for(let at=dir;;at=dirname12(at))try{let pkg=JSON.parse(readFileSync21(join21(at,"node_modules",name,"package.json"),"utf8"));installed2[name]??={version:pkg.version,peers:pkg.peerDependencies??{}};break}catch{if(dirname12(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.10.0-next.20261013182821";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(`
478
+ `)[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(readFileSync21(join21(dir,"package.json"),"utf8"));for(let name of Object.keys({...manifest2.dependencies,...manifest2.devDependencies}))for(let at=dir;;at=dirname12(at))try{let pkg=JSON.parse(readFileSync21(join21(at,"node_modules",name,"package.json"),"utf8"));installed2[name]??={version:pkg.version,peers:pkg.peerDependencies??{}};break}catch{if(dirname12(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.10.0";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(`
484
479
  `)}
485
480
  `}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,scaffoldPack,require_picocolors,VERSION,INVOCATION,runnable,EXIT,CliError,explain,renderError,runJob};