@@ -69,22 +69,27 @@ async function run(argv) {
6969 if ( cmd === "init" ) {
7070 const { init } = await import ( "./init.js" ) ;
7171 const noSettings = argv . includes ( "--no-settings" ) ;
72- const { report, bytes, settings } = init ( { targetRoot : process . cwd ( ) , noSettings } ) ;
72+ const { report, bytes, settings, detected } = init ( { targetRoot : process . cwd ( ) , noSettings } ) ;
7373 const wrote = report . filter ( ( r ) => r . action === "written" ) . map ( ( r ) => r . target ) ;
7474 console . log ( `${ BRAND . brand } init — this repo now speaks every AI tool from one source.\n` ) ;
7575 console . log ( ` emitted: ${ wrote . length ? wrote . join ( ", " ) : "(all up to date)" } ` ) ;
7676 console . log (
7777 ` source: AGENTS.md (${ bytes } B) — edit rules in source/, re-run \`${ BRAND . cli } sync\`` ,
7878 ) ;
79- if ( settings ?. action === "merged" ) {
80- console . log (
81- ` settings: merged ${ settings . added . join ( ", " ) } into ${ settings . path } ` ,
82- ) ;
83- } else if ( settings ?. action === "unchanged" ) {
79+ if ( settings ?. action === "merged" && "added" in settings ) {
80+ console . log ( ` settings: merged ${ settings . added . join ( ", " ) } into ${ settings . path } ` ) ;
81+ } else if ( settings ?. action === "unchanged" && "path" in settings ) {
8482 console . log ( ` settings: already up to date (${ settings . path } )` ) ;
8583 } else if ( settings ?. action === "skipped" ) {
8684 console . log ( " settings: skipped (--no-settings)" ) ;
8785 }
86+ if ( detected ) {
87+ console . log ( ` provider: auto-detected ${ detected . name } from ${ detected . source } ` ) ;
88+ } else {
89+ console . log (
90+ ` provider: none detected — set ANTHROPIC_API_KEY, OPENROUTER_API_KEY, or LITELLM_BASE_URL` ,
91+ ) ;
92+ }
8893 console . log ( ` active: tools · crew · guards → \`${ BRAND . cli } catalog\`` ) ;
8994 console . log ( ` verify: \`${ BRAND . cli } doctor\`` ) ;
9095 return ;
@@ -588,7 +593,7 @@ async function run(argv) {
588593 }
589594 const r = scan ( target ) ;
590595 console . log ( `${ BRAND . brand } scan — skill-gate (${ r . scanner } )\n` ) ;
591- if ( r . findings && r . findings . length ) {
596+ if ( r . findings ? .length ) {
592597 for ( const f of r . findings ) console . log ( ` [${ f . sev } ] ${ f . msg } ` ) ;
593598 } else if ( r . raw ) {
594599 console . log ( " " + r . raw . trim ( ) . split ( "\n" ) . slice ( - 6 ) . join ( "\n " ) ) ;
@@ -692,9 +697,14 @@ async function run(argv) {
692697 const { estimateSpendFromLogs } = await import ( "./cost_report.js" ) ;
693698 const est = estimateSpendFromLogs ( ) ;
694699 if ( est && est . totalCost > 0 ) {
695- console . log ( ` $${ est . totalCost . toFixed ( 2 ) } estimated from Claude session logs (${ est . sessions } session(s))` ) ;
700+ console . log (
701+ ` $${ est . totalCost . toFixed ( 2 ) } estimated from Claude session logs (${ est . sessions } session(s))` ,
702+ ) ;
696703 if ( est . byModel . length ) {
697- for ( const m of est . byModel ) console . log ( ` ${ m . model . padEnd ( 30 ) } $${ m . cost . toFixed ( 4 ) } (${ m . inTokens } in / ${ m . outTokens } out)` ) ;
704+ for ( const m of est . byModel )
705+ console . log (
706+ ` ${ m . model . padEnd ( 30 ) } $${ m . cost . toFixed ( 4 ) } (${ m . inTokens } in / ${ m . outTokens } out)` ,
707+ ) ;
698708 }
699709 console . log ( "\n install ccusage for precise tracking: npm i -g ccusage" ) ;
700710 } else {
@@ -861,11 +871,15 @@ async function run(argv) {
861871 if ( sub === "show" ) {
862872 const prov = activeProvider ( process . cwd ( ) ) ;
863873 const config = loadProviders ( process . cwd ( ) ) ;
864- if ( json ) return console . log ( JSON . stringify ( { active : config . active , provider : prov } , null , 2 ) ) ;
874+ if ( json )
875+ return console . log ( JSON . stringify ( { active : config . active , provider : prov } , null , 2 ) ) ;
865876 console . log ( `${ BRAND . brand } config\n` ) ;
866877 console . log ( ` provider: ${ prov . name } (${ prov . label || prov . name } )` ) ;
878+ if ( prov . _autoDetected ) console . log ( ` detected: auto (from ${ prov . _source } )` ) ;
867879 console . log ( ` base URL: ${ prov . baseUrl } ` ) ;
868- console . log ( ` env key: ${ prov . envKey || "(none)" } ${ prov . envKey ? ( process . env [ prov . envKey ] ? " ✓ set" : " ✗ not set" ) : "" } ` ) ;
880+ console . log (
881+ ` env key: ${ prov . envKey || "(none)" } ${ prov . envKey ? ( process . env [ prov . envKey ] ? " ✓ set" : " ✗ not set" ) : "" } ` ,
882+ ) ;
869883 console . log ( ` models:` ) ;
870884 for ( const [ tier , id ] of Object . entries ( prov . models || { } ) )
871885 console . log ( ` ${ tier . padEnd ( 8 ) } ${ id } ` ) ;
@@ -885,23 +899,36 @@ async function run(argv) {
885899 if ( sub === "provider" ) {
886900 const name = argv [ 2 ] ;
887901 if ( ! name ) {
888- console . error ( `usage: ${ BRAND . cli } config provider <name> | ${ BRAND . cli } config provider add <name> --base-url <url> [--key-env <VAR>]` ) ;
902+ console . error (
903+ `usage: ${ BRAND . cli } config provider <name> | ${ BRAND . cli } config provider add <name> --base-url <url> [--key-env <VAR>]` ,
904+ ) ;
889905 process . exitCode = 1 ;
890906 return ;
891907 }
892908 if ( name === "add" ) {
893909 const addName = argv [ 3 ] ;
894- const flagVal = ( f ) => { const i = argv . indexOf ( f ) ; return i >= 0 ? argv [ i + 1 ] : undefined ; } ;
910+ const flagVal = ( f ) => {
911+ const i = argv . indexOf ( f ) ;
912+ return i >= 0 ? argv [ i + 1 ] : undefined ;
913+ } ;
895914 const baseUrl = flagVal ( "--base-url" ) ;
896915 const envKey = flagVal ( "--key-env" ) ;
897916 const label = flagVal ( "--label" ) ;
898917 const r = addProvider ( process . cwd ( ) , addName , { baseUrl, envKey, label } ) ;
899- if ( ! r . ok ) { console . error ( ` ${ r . reason } ` ) ; process . exitCode = 1 ; return ; }
918+ if ( ! r . ok ) {
919+ console . error ( ` ${ r . reason } ` ) ;
920+ process . exitCode = 1 ;
921+ return ;
922+ }
900923 console . log ( ` added provider "${ addName } " → ${ r . provider . baseUrl } ` ) ;
901924 return ;
902925 }
903926 const r = setProvider ( process . cwd ( ) , name ) ;
904- if ( ! r . ok ) { console . error ( ` ${ r . reason } ` ) ; process . exitCode = 1 ; return ; }
927+ if ( ! r . ok ) {
928+ console . error ( ` ${ r . reason } ` ) ;
929+ process . exitCode = 1 ;
930+ return ;
931+ }
905932 console . log ( ` switched to provider "${ name } " (${ r . provider . label || name } )` ) ;
906933 return ;
907934 }
@@ -913,20 +940,91 @@ async function run(argv) {
913940 return ;
914941 }
915942 const r = applyRoute ( tier ) ;
916- if ( ! r . ok ) { console . error ( ` ${ r . reason } ` ) ; process . exitCode = 1 ; return ; }
943+ if ( ! r . ok ) {
944+ console . error ( ` ${ r . reason } ` ) ;
945+ process . exitCode = 1 ;
946+ return ;
947+ }
917948 console . log ( ` model set to ${ r . model } (${ r . modelId } )${ r . prev ? ` — was: ${ r . prev } ` : "" } ` ) ;
918949 console . log ( ` written to ${ r . path } ` ) ;
919950 return ;
920951 }
921- console . error ( `config: unknown subcommand "${ sub } " (show | providers | provider <name> | model <tier>)` ) ;
952+ if ( sub === "gateway" ) {
953+ const { emitGatewayConfig } = await import ( "./route.js" ) ;
954+ const result = emitGatewayConfig ( process . cwd ( ) ) ;
955+ if ( typeof result === "object" && ! result . ok ) {
956+ console . log ( ` ${ result . reason } ` ) ;
957+ return ;
958+ }
959+ console . log ( ` wrote ${ result } ` ) ;
960+ console . log ( `\n setup LiteLLM gateway:` ) ;
961+ console . log ( ` 1. pip install "litellm[proxy]" # pin an exact verified version` ) ;
962+ console . log ( ` 2. litellm --config litellm.config.yaml` ) ;
963+ console . log ( ` 3. export ANTHROPIC_BASE_URL=http://localhost:4000` ) ;
964+ console . log ( `\n then switch to the gateway provider:` ) ;
965+ console . log ( ` ${ BRAND . cli } config provider litellm` ) ;
966+ console . log ( `\n routing flows through: forge route → tier alias → LiteLLM → model` ) ;
967+ return ;
968+ }
969+ if ( sub === "setup" ) {
970+ const { providerStatus, listDetectedProviders } = await import ( "./providers.js" ) ;
971+ const prov = activeProvider ( process . cwd ( ) ) ;
972+ const status = providerStatus ( process . cwd ( ) ) ;
973+ const detected = listDetectedProviders ( ) ;
974+ console . log ( `${ BRAND . brand } config setup\n` ) ;
975+ console . log ( ` active provider: ${ prov . name } (${ prov . label || prov . name } )` ) ;
976+ if ( prov . _autoDetected ) console . log ( ` source: auto-detected from ${ prov . _source } ` ) ;
977+ console . log ( ) ;
978+ for ( const c of status . checks ) {
979+ console . log ( ` ${ c . ok ? "✓" : "✗" } ${ c . detail } ` ) ;
980+ }
981+ console . log ( `\n environment:` ) ;
982+ for ( const e of status . envScan ) {
983+ console . log ( ` ${ e . set ? "✓" : "·" } ${ e . key } ${ e . set ? " (set)" : "" } ` ) ;
984+ }
985+ if ( detected . length ) {
986+ console . log ( `\n available providers (auto-detected):` ) ;
987+ for ( const d of detected ) {
988+ console . log ( ` ${ d . name . padEnd ( 18 ) } ${ d . label . padEnd ( 24 ) } via ${ d . source } ` ) ;
989+ }
990+ }
991+ console . log ( `\n Anthropic Console API key:` ) ;
992+ console . log ( ` 1. Go to console.anthropic.com/settings/keys` ) ;
993+ console . log ( ` 2. Create a key, then: export ANTHROPIC_API_KEY=sk-ant-...` ) ;
994+ console . log ( `\n OpenRouter API key:` ) ;
995+ console . log ( ` 1. Go to openrouter.ai/keys` ) ;
996+ console . log ( ` 2. Create a key, then: export OPENROUTER_API_KEY=sk-or-...` ) ;
997+ console . log ( `\n LiteLLM hosted gateway (no admin access needed):` ) ;
998+ console . log ( ` export LITELLM_BASE_URL=https://your-gateway.example.com` ) ;
999+ console . log ( ` export LITELLM_API_KEY=sk-... # or uses ANTHROPIC_API_KEY` ) ;
1000+ console . log ( `\n LiteLLM self-hosted gateway:` ) ;
1001+ console . log ( ` ${ BRAND . cli } config gateway # emit litellm.config.yaml` ) ;
1002+ console . log ( ` ${ BRAND . cli } config provider litellm` ) ;
1003+ console . log ( `\n quick start:` ) ;
1004+ console . log ( ` ${ BRAND . cli } config provider anthropic # direct Anthropic API` ) ;
1005+ console . log ( ` ${ BRAND . cli } config provider openrouter # OpenRouter multi-model` ) ;
1006+ console . log ( ` ${ BRAND . cli } config provider litellm # LiteLLM self-hosted` ) ;
1007+ console . log ( ` ${ BRAND . cli } config model sonnet # set default model tier` ) ;
1008+ console . log ( ` ${ BRAND . cli } route "<task>" --apply # route + apply model` ) ;
1009+ return ;
1010+ }
1011+ console . error (
1012+ `config: unknown subcommand "${ sub } " (show | providers | provider <name> | model <tier> | gateway | setup)` ,
1013+ ) ;
9221014 process . exitCode = 1 ;
9231015 return ;
9241016 }
9251017 if ( cmd === "route" ) {
9261018 const r = await import ( "./route.js" ) ;
9271019 if ( argv [ 1 ] === "gateway" ) {
928- const path = r . emitGatewayConfig ( process . cwd ( ) ) ;
929- console . log ( ` wrote ${ path } — LiteLLM tiers: forge-simple / forge-medium / forge-complex.` ) ;
1020+ const result = r . emitGatewayConfig ( process . cwd ( ) ) ;
1021+ if ( typeof result === "object" && ! result . ok ) {
1022+ console . log ( ` ${ result . reason } ` ) ;
1023+ return ;
1024+ }
1025+ console . log (
1026+ ` wrote ${ result } — LiteLLM tiers: forge-simple / forge-medium / forge-complex.` ,
1027+ ) ;
9301028 console . log ( " next: pin+install litellm, run it, point ANTHROPIC_BASE_URL at it, then" ) ;
9311029 console . log (
9321030 " REQUEST the tier `forge route` recommends (a plain claude-* request passes through)." ,
@@ -943,14 +1041,20 @@ async function run(argv) {
9431041 . filter ( ( a , i ) => ! FLAGS . has ( a ) && a !== "--provider" && argv [ i ] !== "--provider" )
9441042 . join ( " " ) ;
9451043 if ( ! task ) {
946- console . error ( 'usage: forge route "<task>" [--apply] [--provider <name>] [--json] | forge route gateway' ) ;
1044+ console . error (
1045+ 'usage: forge route "<task>" [--apply] [--provider <name>] [--json] | forge route gateway' ,
1046+ ) ;
9471047 process . exitCode = 1 ;
9481048 return ;
9491049 }
9501050 if ( providerName ) {
9511051 const { setProvider } = await import ( "./providers.js" ) ;
9521052 const sr = setProvider ( process . cwd ( ) , providerName ) ;
953- if ( ! sr . ok ) { console . error ( ` ${ sr . reason } ` ) ; process . exitCode = 1 ; return ; }
1053+ if ( ! sr . ok ) {
1054+ console . error ( ` ${ sr . reason } ` ) ;
1055+ process . exitCode = 1 ;
1056+ return ;
1057+ }
9541058 }
9551059 const rec = r . routeTask ( process . cwd ( ) , task ) ;
9561060 r . meterRoute ( process . cwd ( ) , task , rec ) ;
@@ -973,13 +1077,16 @@ async function run(argv) {
9731077 const { applyRoute } = await import ( "./providers.js" ) ;
9741078 const ar = applyRoute ( rec . key ) ;
9751079 if ( ar . ok ) {
976- if ( ! json ) console . log ( `\n applied: model set to ${ ar . model } (${ ar . modelId } ) in ${ ar . path } ` ) ;
1080+ if ( ! json )
1081+ console . log ( `\n applied: model set to ${ ar . model } (${ ar . modelId } ) in ${ ar . path } ` ) ;
9771082 } else {
9781083 if ( ! json ) console . error ( `\n apply failed: ${ ar . reason } ` ) ;
9791084 process . exitCode = 1 ;
9801085 }
9811086 } else if ( ! json ) {
982- console . log ( `\n advisory · apply: \`${ BRAND . cli } route "<task>" --apply\` · gateway: \`${ BRAND . cli } route gateway\`` ) ;
1087+ console . log (
1088+ `\n advisory · apply: \`${ BRAND . cli } route "<task>" --apply\` · gateway: \`${ BRAND . cli } route gateway\`` ,
1089+ ) ;
9831090 }
9841091 return ;
9851092 }
0 commit comments