1- import { coerce , major , satisfies , validRange } from 'semver' ;
1+ import { coerce , satisfies , validRange } from 'semver' ;
22
33import packageJson from '../package.json' with { type : 'json' } ;
4- import { performSelfUpdate } from './self-update.js' ;
54
65const ANSI_YELLOW = '\u001b[33m' ;
76const ANSI_RED = '\u001b[31m' ;
8- const ANSI_GREEN = '\u001b[32m' ;
97const ANSI_RESET = '\u001b[0m' ;
108
119export interface VersionCheckResult {
@@ -15,8 +13,12 @@ export interface VersionCheckResult {
1513}
1614
1715/**
18- * Validate and check the installed version against a required semver range.
19- * Throws on malformed range strings.
16+ * Advisory version checks for project `required_version`.
17+ *
18+ * A mismatched installed version never prompts, self-updates, or exits by
19+ * default. Commands may print the returned warning to help users diagnose
20+ * failures, while explicit `--strict` callers can still opt into a hard gate.
21+ * Malformed ranges remain configuration errors.
2022 */
2123export function checkVersion ( requiredVersion : string ) : VersionCheckResult {
2224 const currentVersion = packageJson . version ;
@@ -34,23 +36,30 @@ export function checkVersion(requiredVersion: string): VersionCheckResult {
3436 } ;
3537}
3638
39+ function formatVersionMismatch ( result : VersionCheckResult ) : string {
40+ return `agentv ${ result . currentVersion } does not satisfy this project's required_version ${ result . requiredRange } ` ;
41+ }
42+
43+ export function formatRequiredVersionWarning ( result : VersionCheckResult ) : string {
44+ return `${ ANSI_YELLOW } Warning: ${ formatVersionMismatch ( result ) } . Run \`agentv self update\`.${ ANSI_RESET } ` ;
45+ }
46+
47+ export function formatRequiredVersionFailureNote ( result : VersionCheckResult ) : string {
48+ return `note: ${ formatVersionMismatch ( result ) } - this may be the cause. Run \`agentv self update\`.` ;
49+ }
50+
3751/**
38- * Run the version compatibility check and handle user interaction .
52+ * Run the version compatibility check.
3953 *
40- * - If the version satisfies the range, returns silently.
54+ * - If the version satisfies the range, returns the satisfied result silently.
4155 * - If the range is malformed, prints an error and exits with code 1.
42- * - If the version is below the range:
43- * - Interactive (TTY): warns and prompts "Update now? (Y/n)".
44- * Y → runs self-update inline (constrained to the config range),
45- * then exits with a message to re-run the command.
46- * N → continues the command as-is.
47- * - Non-interactive: warns to stderr, continues (unless strict).
48- * - Strict mode: warns and exits with code 1.
56+ * - If the version is below the range, warns to stderr and continues.
57+ * - Strict mode remains an explicit opt-in hard failure.
4958 */
50- export async function enforceRequiredVersion (
59+ export function enforceRequiredVersion (
5160 requiredVersion : string ,
5261 options ?: { strict ?: boolean } ,
53- ) : Promise < void > {
62+ ) : VersionCheckResult {
5463 let result : VersionCheckResult ;
5564 try {
5665 result = checkVersion ( requiredVersion ) ;
@@ -60,59 +69,19 @@ export async function enforceRequiredVersion(
6069 }
6170
6271 if ( result . satisfied ) {
63- return ;
72+ return result ;
6473 }
6574
66- const warning = ` ${ ANSI_YELLOW } Warning: This project requires agentv ${ result . requiredRange } but you have ${ result . currentVersion } . ${ ANSI_RESET } ` ;
75+ const warning = formatRequiredVersionWarning ( result ) ;
6776
6877 if ( options ?. strict ) {
69- console . error ( ` ${ warning } \n Run \`agentv self update\` to upgrade.` ) ;
78+ console . error ( warning ) ;
7079 console . error (
7180 `${ ANSI_RED } Aborting: --strict mode requires the installed version to satisfy the required range.${ ANSI_RESET } ` ,
7281 ) ;
7382 process . exit ( 1 ) ;
7483 }
7584
76- if ( process . stdin . isTTY && process . stdout . isTTY ) {
77- console . warn ( warning ) ;
78- const shouldUpdate = await promptUpdate ( ) ;
79- if ( shouldUpdate ) {
80- await runInlineUpdate ( result . currentVersion , result . requiredRange ) ;
81- }
82- // N → continue the command without interruption
83- } else {
84- // Non-interactive: warn to stderr and continue
85- process . stderr . write ( `${ warning } \n Run \`agentv self update\` to upgrade.\n` ) ;
86- }
87- }
88-
89- async function promptUpdate ( ) : Promise < boolean > {
90- const { confirm } = await import ( '@inquirer/prompts' ) ;
91- return confirm ( { message : 'Update now?' , default : true } ) ;
92- }
93-
94- async function runInlineUpdate ( currentVersion : string , versionRange : string ) : Promise < void > {
95- // Cap at the current major version to avoid unintended breaking changes.
96- // e.g., if current is 4.14.2 and range is ">=4.1.0", install ">=4.1.0 <5.0.0"
97- // so that a hypothetical 5.0.0 is never pulled in by auto-update.
98- const currentMajor = major ( coerce ( currentVersion ) ?? currentVersion ) ;
99- const safeRange = `${ versionRange } <${ currentMajor + 1 } .0.0` ;
100-
101- console . log ( '' ) ;
102- const result = await performSelfUpdate ( { currentVersion, versionRange : safeRange } ) ;
103-
104- if ( ! result . success ) {
105- console . error ( `${ ANSI_RED } Update failed. Run \`agentv self update\` manually.${ ANSI_RESET } ` ) ;
106- process . exit ( 1 ) ;
107- }
108-
109- if ( result . newVersion ) {
110- console . log (
111- `\n${ ANSI_GREEN } Update complete: ${ currentVersion } → ${ result . newVersion } ${ ANSI_RESET } ` ,
112- ) ;
113- } else {
114- console . log ( `\n${ ANSI_GREEN } Update complete.${ ANSI_RESET } ` ) ;
115- }
116- console . log ( 'Please re-run your command.' ) ;
117- process . exit ( 0 ) ;
85+ process . stderr . write ( `${ warning } \n` ) ;
86+ return result ;
11887}
0 commit comments