2026-10-29 15:23:42 +08:00
import { existsSync , readFileSync , writeFileSync } from "node:fs" ;
import { homedir } from "node:os" ;
import { join } from "node:path" ;
import { writeAmpRegistrationBackup } from "./cli/ampRegistrationBackup.js" ;
2026-10-30 04:25:48 +08:00
import { registerToolFamilies } from "./contracts/toolFamilyIds.js" ;
2026-10-21 19:06:44 +08:00
import { registerGpuCommands } from "./cli/gpuCommands.js" ;
2026-10-20 17:48:52 +08:00
import { registerNativeFunctionMatchingCommands } from "./cli/nativeFunctionMatchingCommands.js" ;
2026-10-13 03:35:35 +08:00
import { registerCaptureCommands } from "./cli/captureCommands.js" ;
2026-10-13 04:59:02 +08:00
import { registerSignalCommands } from "./cli/signalCommands.js" ;
2026-10-11 20:49:00 +09:00
import { registerWasmCommands } from "./cli/wasmCommands.js" ;
2026-10-22 12:48:09 +01:00
import { registerApkSignatureCommands } from "./cli/apkSignatureCommands.js" ;
2026-10-09 20:02:52 +08:00
import { snapshotEnvironment } from "./process/snapshotEnvironment.js" ;
import { createDirectAnalysis } from "./composition/directAnalysis.js" ;
2026-10-08 15:32:05 +08:00
import { registerEvmCommands } from "./cli/evmCommands.js" ;
2026-10-20 04:01:36 +08:00
import { registerDesktopApplicationCommands } from "./cli/desktopApplicationCommands.js" ;
2026-10-11 23:34:02 -04:00
import { registerGoCommands } from "./cli/goCommands.js" ;
2026-10-21 00:50:50 +08:00
import { registerModelCommands } from "./cli/modelCommands.js" ;
2026-10-20 08:20:47 +09:00
import { registerIl2cppCommands } from "./cli/il2cppCommands.js" ;
2026-10-13 11:50:30 -07:00
import { registerRustCommands } from "./cli/rustCommands.js" ;
2026-10-22 04:00:08 -07:00
import { registerPythonPycCommands } from "./cli/pythonPycCommands.js" ;
2026-10-22 02:46:15 -07:00
import { registerErlangCommands } from "./cli/erlangCommands.js" ;
2026-10-13 02:33:23 +08:00
import { registerSqliteCommands } from "./cli/sqliteCommands.js" ;
2026-10-08 21:45:48 -07:00
import { registerAnalysisViewCommands } from "./cli/analysisViewCommands.js" ;
2026-10-08 14:22:08 +08:00
import { registerBinaryDiagnosticsCommands } from "./cli/binaryDiagnosticsCommands.js" ;
2026-10-12 11:01:58 -03:00
import { registerFridaCommands } from "./cli/fridaCommands.js" ;
import type { FridaInstrumentationPort } from "./application/frida/FridaInstrumentationPort.js" ;
2026-10-27 20:53:29 -07:00
import { Cli , z } from "incur" ;
2026-10-28 09:12:40 +05:45
import {
adaptCliOutputArgumentsForIncur ,
2026-11-05 23:01:30 +05:00
classifyMcpAddDiscovery ,
expandMcpAddEqualsArguments ,
mcpAddAgents ,
2026-10-28 09:12:40 +05:45
normalizeCliArguments ,
2026-10-29 08:57:38 +08:00
splitIncurBuiltinArguments ,
2026-10-28 09:12:40 +05:45
} from "./cliBuiltinArguments.js" ;
2026-10-29 08:57:38 +08:00
import {
parseCliOutputArguments ,
renderCliDocument ,
renderCliOutputArgumentError ,
} from "./cliOutput.js" ;
2026-07-12 07:44:21 +08:00
2026-10-23 15:21:14 +08:00
import { createLogger } from "./logger.js" ;
import { parseLogLevel } from "./config/processStart.js" ;
2026-07-12 07:44:21 +08:00
import { PRODUCT_IDENTITY } from "./identity.js" ;
2026-10-28 19:34:28 +08:00
import { currentClientRegistrationCommand } from "./application/ClientRegistrationIdentity.js" ;
import { clientRegistrationCommandText } from "./cli/clientRegistrationCommandText.js" ;
2026-07-18 04:11:29 +00:00
import { registerSetupCommands } from "./cli/setupCommands.js" ;
import { registerCoreAnalysisCommands } from "./cli/coreAnalysisCommands.js" ;
import { registerUtilityCommands } from "./cli/utilityCommands.js" ;
import { registerArtifactCommands } from "./cli/artifactCommands.js" ;
import { registerManagedCommands } from "./cli/managedCommands.js" ;
2026-10-06 22:16:04 +08:00
import { registerFirmwareCommands } from "./cli/firmwareCommands.js" ;
2026-10-06 20:46:48 +08:00
import { registerAndroidCommands } from "./cli/androidCommands.js" ;
2026-10-10 17:04:12 +02:00
import { registerApktoolCommands } from "./cli/apktoolCommands.js" ;
2026-10-13 06:53:03 +02:00
import { registerPcapCommands } from "./cli/pcapCommands.js" ;
2026-10-12 16:35:55 +02:00
import { registerFlutterCommands } from "./cli/flutterCommands.js" ;
2026-10-10 17:03:27 +02:00
import { registerJebCommands } from "./cli/jebCommands.js" ;
2026-10-10 17:01:24 +02:00
import { registerAdbCommands } from "./cli/adbCommands.js" ;
2026-07-13 03:15:01 +00:00
import { registerEvidenceCommands } from "./cliEvidenceCommands.js" ;
2026-10-19 12:06:29 -07:00
import { registerEnvironmentCommands } from "./cli/environmentCommands.js" ;
2026-10-06 05:04:25 +08:00
import { registerProcessCommands } from "./cli/processCommands.js" ;
2026-11-04 23:14:04 -08:00
import { registerDesktopCaptureCommands } from "./cli/desktopCaptureCommands.js" ;
2026-10-06 05:04:25 +08:00
import { registerBrowserCommands } from "./cli/browserCommands.js" ;
2026-10-07 05:24:32 +08:00
import { registerWebScriptCommands } from "./cli/webScriptCommands.js" ;
2026-10-07 14:59:52 +08:00
import { registerWebModuleCommands } from "./cli/webModuleCommands.js" ;
2026-10-07 16:21:18 +08:00
import { registerWebSourceLocationCommands } from "./cli/webSourceLocationCommands.js" ;
2026-10-07 21:45:07 +08:00
import { registerWebRuntimeCommands } from "./cli/webRuntimeCommands.js" ;
2026-10-08 07:53:41 +08:00
import { registerWebNetworkCaptureCommands } from "./cli/webNetworkCaptureCommands.js" ;
2026-10-07 12:46:13 +08:00
import { registerJavaScriptRecoveryCommands } from "./cli/javascriptRecoveryCommands.js" ;
2026-10-06 05:04:25 +08:00
import { registerAdvancedBrowserCommands } from "./cli/browserAdvancedCommands.js" ;
2026-07-24 13:18:58 +00:00
import { registerBrowserScenarioCommands } from "./cliBrowserScenarioCommands.js" ;
2026-10-06 05:04:25 +08:00
import { registerElectronCommands } from "./cli/electronCommands.js" ;
2026-10-23 23:51:07 +08:00
import { registerJavaScriptRuntimeObservationCommands } from "./cli/javascriptRuntimeCommands.js" ;
2026-10-06 05:04:25 +08:00
import { registerApplicationCommands } from "./cli/applicationCommands.js" ;
2026-10-08 20:11:11 -07:00
import { presentOmittableDefaults } from "./cli/omittableDefaults.js" ;
2026-07-18 04:11:29 +00:00
import type { CliInstance } from "./cli/types.js" ;
2026-10-10 06:22:20 +08:00
import {
streamCliCommandResults ,
type CliResultOutput ,
2026-10-13 03:33:27 +08:00
} from "./cli/cliResultOutput.js" ;
2026-10-14 04:47:34 +08:00
import { registerReverseEngineeringCommands } from "./cli/reverseEngineeringCommands.js" ;
import { registerCutterCommands } from "./cli/cutterCommands.js" ;
2026-10-22 00:22:16 -07:00
import { excludeDeclaredSensitiveArguments } from "./cli/explicitSensitiveArguments.js" ;
2026-10-24 02:51:40 +08:00
import { rejectSurplusCliArguments } from "./cli/strictPositionals.js" ;
2026-11-04 06:54:29 -05:00
import {
encodeCliArgumentErrors ,
presentCliArgumentErrors ,
} from "./cli/argumentErrors.js" ;
2026-10-29 01:28:04 +08:00
import { completionHelpNote , createCliCompletions } from "./cli/completions.js" ;
2026-11-06 18:20:56 +09:00
import { reportUnknownMcpSubcommand } from "./cli/mcpBuiltinCommands.js" ;
2026-10-29 14:17:10 +05:30
import {
closeCliGuidanceResources ,
registerGuidanceCommands ,
} from "./cli/guidanceCommands.js" ;
import { closeCliJavaScriptResources } from "./cli/javascriptApplicationAnalysis.js" ;
2026-10-28 11:42:39 +05:30
import { registerGuidedWorkflowCommands } from "./cli/guidedWorkflowCommands.js" ;
2026-07-12 07:44:21 +08:00
2026-10-29 14:17:10 +05:30
/** Close every process-owned resource used by CLI commands. */
export const closeCliResources = async () : Promise < void > => {
const results = await Promise . allSettled ([
closeCliJavaScriptResources (),
closeCliGuidanceResources (),
]);
const failures = results . flatMap (( result ) =>
result . status === "rejected" ? [ result . reason ] : [],
);
if ( failures . length === 1 ) throw failures [ 0 ];
if ( failures . length > 1 )
throw new AggregateError ( failures , "CLI resource cleanup failed" );
};
2026-10-21 19:01:20 +08:00
2026-07-12 08:45:35 +08:00
/**
* Build the one-shot Incur CLI without starting Hopper at import time.
* Analysis commands acquire and close their own sessions; bare `mcp` and
* `--mcp` are intercepted by the executable dispatcher before this module loads.
*/
2026-10-05 20:00:16 +08:00
export const createCli = (
2026-10-09 20:02:52 +08:00
selectedEnvironment : Readonly <
Record < string , string | undefined >
> = process . env ,
2026-10-08 03:44:51 +08:00
resultOutput? : CliResultOutput ,
2026-10-12 11:01:58 -03:00
fridaInstrumentation? : FridaInstrumentationPort ,
2026-10-05 20:00:16 +08:00
) : CliInstance => {
2026-10-09 20:02:52 +08:00
const environment = snapshotEnvironment ( selectedEnvironment );
const analysis = createDirectAnalysis ( environment );
2026-10-21 03:44:49 +08:00
// The executable reports an invalid level before every command except doctor.
// Doctor keeps going so its readiness report can name REA_LOG_LEVEL.
2026-10-12 06:07:37 -07:00
const level =
environment . REA_LOG_LEVEL === undefined
? undefined
: parseLogLevel ( environment . REA_LOG_LEVEL );
2026-07-12 10:57:34 +08:00
const logger = createLogger (
"cli" ,
2026-10-12 06:07:37 -07:00
level ? . ok === true ? level . value : "silent" ,
2026-07-12 10:57:34 +08:00
);
2026-10-27 20:53:29 -07:00
const timedCli = Cli . create ( PRODUCT_IDENTITY . cliBinary , {
2026-07-14 01:12:27 +00:00
version : PRODUCT_IDENTITY.packageVersion ,
2026-07-13 19:17:15 +00:00
description : "Reverse engineer anything from your terminal or agent." ,
2026-10-27 20:53:29 -07:00
globals : z.object ({
timings : z
. boolean ()
. default ( false )
. describe ( "Write measured command timing to stderr." ),
}),
2026-07-12 07:44:21 +08:00
mcp : {
2026-10-28 19:34:28 +08:00
command : clientRegistrationCommandText (
currentClientRegistrationCommand ({ environment }),
),
2026-07-12 07:44:21 +08:00
instructions :
2026-07-12 20:49:30 +08:00
"Ask what software, artifact, protocol, or behavior the user wants to understand, then choose the available investigation capabilities that can produce evidence." ,
2026-07-12 07:44:21 +08:00
},
2026-10-05 23:27:46 -07:00
sync : false ,
2026-10-18 05:33:21 +08:00
update : {
// Incur's update:false only suppresses notices. Reject its explicit
// installer so ambiguous flags cannot bypass REA's verified workflow.
install : () => {
throw new Error (
"Use 'rea update' to update the identified installation. The '--update' alias accepts only global output flags." ,
);
},
},
2026-07-12 07:44:21 +08:00
});
2026-10-27 20:53:29 -07:00
timedCli . use ( async ( context , next ) => {
if ( ! context . globals . timings ) return next ();
const started = performance . now ();
try {
await next ();
} finally {
// This interval covers CLI command dispatch, including result delivery.
// It is not a provider-only or analysis-only measurement.
process . stderr . write (
` ${ JSON . stringify ({ rea_timings : { version : 1 , command_ms : Math.round ( performance . now () - started ) } })} \ n` ,
);
}
});
// Command registrars do not inspect global options; preserve their existing
// type while Incur owns parsing and the middleware owns this added flag.
const cli = timedCli as unknown as CliInstance ;
2026-10-09 20:02:52 +08:00
registerSetupCommands ( cli , logger , environment );
2026-10-30 04:25:48 +08:00
registerToolFamilies ({
direct : () =>
registerCoreAnalysisCommands (
cli ,
logger ,
analysis . runDirectAnalysis ,
environment ,
),
// Enhanced workflows are MCP tools. Their CLI entry points live with core analysis.
enhanced : () => undefined ,
native : () => {
registerReverseEngineeringCommands ( cli , logger , environment );
registerCutterCommands ( cli , logger , environment );
registerUtilityCommands ( cli , logger , environment , analysis );
registerBinaryDiagnosticsCommands ( cli , logger , environment );
},
artifact : () => {
registerWasmCommands ( cli , logger , environment );
registerApkSignatureCommands ( cli , logger , environment );
registerCaptureCommands ( cli , logger , environment );
registerSignalCommands ( cli , logger , environment );
registerEvmCommands ( cli , logger , environment );
registerDesktopApplicationCommands ( cli , logger );
registerGoCommands ( cli , logger );
registerIl2cppCommands ( cli , logger );
registerModelCommands ( cli , logger );
registerGpuCommands ( cli , logger );
registerRustCommands ( cli , logger );
registerPythonPycCommands ( cli , logger );
registerErlangCommands ( cli , logger );
registerSqliteCommands ( cli , logger , environment );
registerArtifactCommands ( cli , logger , analysis . runProviderAnalysis );
},
managed : () =>
registerManagedCommands ( cli , logger , analysis . runProviderAnalysis ),
firmware : () => registerFirmwareCommands ( cli , logger , environment ),
android : () => registerAndroidCommands ( cli , logger , environment ),
pcap : () => registerPcapCommands ( cli , logger , environment ),
flutter : () => registerFlutterCommands ( cli , logger ),
frida : () => registerFridaCommands ( cli , logger , fridaInstrumentation ),
apktool : () => registerApktoolCommands ( cli , logger , environment ),
jeb : () => registerJebCommands ( cli , logger , environment ),
adb : () => registerAdbCommands ( cli , logger , environment ),
browser : () => {
registerBrowserCommands ( cli , logger );
registerWebRuntimeCommands ( cli , logger );
registerWebNetworkCaptureCommands ( cli , logger , environment );
registerAdvancedBrowserCommands ( cli , logger );
registerBrowserScenarioCommands ( cli , logger , environment );
},
electron : () => registerElectronCommands ( cli , logger , environment ),
"javascript-runtime" : () =>
registerJavaScriptRuntimeObservationCommands ( cli , logger , environment ),
application : () => {
registerGuidanceCommands ( cli , logger );
registerGuidedWorkflowCommands ( cli , logger );
registerAnalysisViewCommands ( cli , logger );
registerEvidenceCommands ( cli , logger );
registerEnvironmentCommands ( cli , logger , environment );
registerProcessCommands ( cli , logger , environment );
2026-11-04 23:14:04 -08:00
registerDesktopCaptureCommands ( cli , logger , environment );
2026-10-30 04:25:48 +08:00
registerJavaScriptRecoveryCommands ( cli , logger , environment );
registerWebScriptCommands ( cli , logger );
registerWebModuleCommands ( cli , logger , environment );
registerWebSourceLocationCommands ( cli , logger , environment );
registerApplicationCommands ( cli , logger );
},
session : () =>
registerNativeFunctionMatchingCommands ( cli , logger , environment ),
});
2026-10-08 20:11:11 -07:00
presentOmittableDefaults ( cli );
2026-10-10 06:22:20 +08:00
if ( resultOutput !== undefined ) streamCliCommandResults ( cli , resultOutput );
2026-10-24 02:51:40 +08:00
const serveStrict = rejectSurplusCliArguments ( cli );
2026-10-24 03:10:45 +08:00
presentCliArgumentErrors ( cli );
2026-10-22 00:22:16 -07:00
const serveDeclared = excludeDeclaredSensitiveArguments ( cli );
2026-11-04 06:54:29 -05:00
encodeCliArgumentErrors ( cli );
2026-10-18 05:33:21 +08:00
const serve = cli . serve . bind ( cli );
2026-10-27 20:53:29 -07:00
const complete = createCliCompletions (
cli as unknown as Parameters < typeof createCliCompletions >[ 0 ],
environment ,
);
2026-10-29 01:28:04 +08:00
cli . serve = ( arguments_ = process . argv . slice ( 2 ), options ) => {
if ( complete ( arguments_ , options ?? {})) return Promise . resolve ();
2026-11-03 19:50:33 +13:00
let normalized = normalizeCliArguments ( arguments_ );
2026-10-29 08:57:38 +08:00
const { command , flags } = splitIncurBuiltinArguments ( normalized );
const builtin = command [ 0 ] === PRODUCT_IDENTITY . cliBinary ? 1 : 0 ;
2026-11-05 23:01:30 +05:00
const discovery = classifyMcpAddDiscovery ( flags );
2026-10-29 08:57:38 +08:00
if (
command [ builtin ] === "mcp" &&
command [ builtin + 1 ] === "add" &&
2026-11-05 23:01:30 +05:00
discovery !== "bypass"
2026-10-29 08:57:38 +08:00
) {
const write =
options ? . stdout ?? (( text : string ) => process . stdout . write ( text ));
2026-11-05 23:01:30 +05:00
if ( discovery === "version" ) {
2026-10-29 08:57:38 +08:00
write ( ` ${ PRODUCT_IDENTITY . packageVersion } \ n` );
return Promise . resolve ();
}
2026-11-05 23:01:30 +05:00
if ( discovery === "schema" ) {
2026-10-29 08:57:38 +08:00
const output = parseCliOutputArguments ( normalized );
if ( ! output . validation . ok )
write ( renderCliOutputArgumentError ( output . validation ));
else if ( output . format === "csv" || output . format === "tsv" )
write (
renderCliOutputArgumentError ({
ok : false ,
format : "json" ,
code : "UNSUPPORTED_OUTPUT_COMBINATION" ,
message :
"CSV/TSV output cannot represent the mcp add schema discovery error. Use a document format instead." ,
}),
);
else
write (
renderCliDocument (
{
code : "SCHEMA_UNAVAILABLE" ,
message :
"Schema discovery is not supported for mcp add. Use 'rea mcp add --help' for its options." ,
},
output . format ,
process . stdout . isTTY === true ,
),
);
(
options ? . exit ??
(( code : number ) => {
process . exitCode = code ;
})
)( 1 );
return Promise . resolve ();
}
2026-11-04 19:37:31 +08:00
// Incur's `mcp add` silently skips a valued option with no value and
// swallows the next flag as the value: the registration succeeds without
// the requested agent or command. Reject a trailing --agent/--command/-c,
// a value position occupied by another flag, and an empty `--flag=` form
// up front.
const valued = [ "--agent" , "--command" , "-c" ];
let missingValue : string | undefined ;
for ( let index = 0 ; index < command . length ; index += 1 ) {
const next = command [ index + 1 ];
if (
valued . includes ( command [ index ] ! ) &&
( next === undefined || next . startsWith ( "-" ))
) {
missingValue = command [ index ];
break ;
}
}
const emptyValue = command . find (
( token ) =>
( token . startsWith ( "--agent=" ) ||
token . startsWith ( "--command=" ) ||
token . startsWith ( "-c=" )) &&
token . length === token . indexOf ( "=" ) + 1 ,
);
if ( missingValue !== undefined || emptyValue !== undefined ) {
const flag = missingValue ?? emptyValue ! ;
const message = `Missing value for flag: ${ flag . replace ( /=.*$/ , "" ) } ` ;
const field =
flag . includes ( "command" ) || flag === "-c" || flag . startsWith ( "-c=" )
? "command"
: "agent" ;
const output = parseCliOutputArguments ( normalized );
if ( ! output . validation . ok )
write ( renderCliOutputArgumentError ( output . validation ));
else
write (
renderCliDocument (
{
code : "VALIDATION_ERROR" ,
message ,
fieldErrors : [
{
code : "invalid_value" ,
missing : false ,
path : field ,
message ,
},
],
},
output . format ,
process . stdout . isTTY === true ,
),
);
(
options ? . exit ??
(( code : number ) => {
process . exitCode = code ;
})
)( 1 );
return Promise . resolve ();
}
2026-10-29 15:23:42 +08:00
// Incur's mcp add parser only understands spaced `--agent amp` forms; an
// equals form like `--agent=amp` is silently ignored, which would drop
// the requested agent and register against the default target. Normalize
// equals forms back to spaced forms before handing over to Incur.
2026-11-05 23:01:30 +05:00
// Shared with the project-scope guard through
// `expandMcpAddEqualsArguments`, so both read the same agent selection.
const serveArguments = expandMcpAddEqualsArguments ( normalized );
2026-10-29 15:23:42 +08:00
// Incur's Amp registration overwrites an existing rea entry (dropping its
// environment map) and reports success even when the settings file
// prevents a save. Guard that path: reject a settings file REA cannot
// update, back up the file, preserve the caller's environment, and verify
// the registration landed.
2026-11-03 19:50:33 +13:00
// SyncMcp.register also writes Amp when no agents are selected. Mode
// switches return before registration and must not touch its settings.
// Use the same normalized arguments for selection and dispatch, including
// non-Amp equals forms. Incur consumes command values before agent flags.
normalized = serveArguments ;
const registration = splitIncurBuiltinArguments ( serveArguments );
const rest = registration . command . slice ( builtin + 2 );
2026-11-05 23:01:30 +05:00
const agents = mcpAddAgents ( rest );
2026-10-29 15:23:42 +08:00
const ampRequested =
2026-11-03 19:50:33 +13:00
! [
"--help" ,
"-h" ,
"--llms" ,
"--llms-full" ,
"--mcp" ,
"--update" ,
"--incur-update-check" ,
]. some (( flag ) => registration . flags . has ( flag )) &&
( agents . length === 0 || agents . includes ( "amp" ));
2026-10-29 15:23:42 +08:00
if ( ampRequested ) {
const settingsPath = join ( homedir (), ".config" , "amp" , "settings.json" );
let priorEnv : unknown ;
let guardError : string | undefined ;
let settingsText : string | undefined ;
let parsedSettings : Record < string , unknown > | undefined ;
try {
if ( existsSync ( settingsPath ))
settingsText = readFileSync ( settingsPath , "utf8" );
} catch ( cause ) {
guardError = `Unable to read Amp settings.json: ${
cause instanceof Error ? cause.message : String ( cause )
} ` ;
}
if ( settingsText !== undefined ) {
try {
const parsed : unknown = JSON . parse ( settingsText );
if (
typeof parsed !== "object" ||
parsed === null ||
Array . isArray ( parsed )
)
guardError = "Amp settings.json must contain a JSON object." ;
else parsedSettings = parsed as Record < string , unknown >;
} catch ( cause ) {
guardError = `Amp settings.json is not valid JSON: ${
cause instanceof Error ? cause.message : String ( cause )
} ` ;
}
}
if ( parsedSettings !== undefined && settingsText !== undefined ) {
const servers = parsedSettings [ "amp.mcpServers" ];
if (
servers !== undefined &&
( typeof servers !== "object" ||
servers === null ||
Array . isArray ( servers ))
)
guardError =
"amp.mcpServers must be an object in Amp settings.json." ;
else {
const entry =
typeof servers === "object" && servers !== null
? ( servers as Record < string , unknown >)[ "rea" ]
: undefined ;
if ( typeof entry === "object" && entry !== null )
priorEnv = ( entry as Record < string , unknown >)[ "env" ];
}
}
const reportValidationError = ( message : string ) : void => {
const output = parseCliOutputArguments ( normalized );
if ( ! output . validation . ok )
write ( renderCliOutputArgumentError ( output . validation ));
else
write (
renderCliDocument (
{
code : "VALIDATION_ERROR" ,
message ,
fieldErrors : [
{
code : "invalid_value" ,
missing : false ,
path : "amp" ,
message ,
},
],
},
output . format ,
process . stdout . isTTY === true ,
),
);
(
options ? . exit ??
(( code : number ) => {
process . exitCode = code ;
})
)( 1 );
};
if ( guardError !== undefined ) {
reportValidationError ( ` ${ guardError } Fix it before registering.` );
return Promise . resolve ();
}
const runRegistration = ( backupPath? : string ) : Promise < void > => {
2026-11-03 19:50:33 +13:00
let failed = false ;
2026-10-29 15:23:42 +08:00
const result = serveStrict (
serveArguments ,
2026-11-03 19:50:33 +13:00
{
... options ,
exit : ( code ) => {
failed ||= code !== 0 ;
if ( options ? . exit !== undefined ) options . exit ( code );
else process . exitCode = code ;
},
},
2026-10-29 15:23:42 +08:00
( tokens , selected ) =>
serveDeclared ( tokens , selected , ( declared , declaredOptions ) =>
serve (
adaptCliOutputArgumentsForIncur ( declared ),
declaredOptions ,
),
),
);
return result . then (() => {
2026-11-03 19:50:33 +13:00
if ( failed ) return ;
2026-10-29 15:23:42 +08:00
let failure : string | undefined ;
try {
if ( ! existsSync ( settingsPath ))
failure = "Amp settings.json was not created." ;
else {
const text = readFileSync ( settingsPath , "utf8" );
const parsed = JSON . parse ( text ) as Record < string , unknown >;
const servers = parsed [ "amp.mcpServers" ];
if (
typeof servers !== "object" ||
servers === null ||
Array . isArray ( servers ) ||
! ( "rea" in servers )
)
failure =
"Amp settings.json does not contain the rea registration after mcp add." ;
else {
const entry = ( servers as Record < string , unknown >)[ "rea" ];
if (
priorEnv !== undefined &&
typeof entry === "object" &&
entry !== null &&
( entry as Record < string , unknown >)[ "env" ] === undefined
) {
( entry as Record < string , unknown >)[ "env" ] = priorEnv ;
writeFileSync (
settingsPath ,
` ${ JSON . stringify ( parsed , null , 2 ) } \ n` ,
);
}
}
}
} catch ( cause ) {
failure = cause instanceof Error ? cause.message : String ( cause );
}
if ( failure !== undefined ) {
const output = parseCliOutputArguments ( normalized );
if ( output . validation . ok )
write (
renderCliDocument (
{
code : "VALIDATION_ERROR" ,
message : failure ,
fieldErrors : [
{
code : "invalid_value" ,
missing : false ,
path : "amp" ,
message : failure ,
},
],
},
output . format ,
process . stdout . isTTY === true ,
),
);
(
options ? . exit ??
(( code : number ) => {
process . exitCode = code ;
})
)( 1 );
} else if ( backupPath !== undefined )
process . stderr . write (
`Amp MCP registration backup retained at ${ backupPath } \ n` ,
);
});
};
if ( settingsText === undefined ) return runRegistration ();
return writeAmpRegistrationBackup ( settingsPath , settingsText ). then (
( backup ) => {
if ( ! backup . ok ) {
const { kind , path , detail } = backup . error ;
reportValidationError (
`Unable to create Amp settings backup ( ${ kind } ) at ${ path } : ${ detail } ` ,
);
return ;
}
return runRegistration ( backup . value );
},
( cause : unknown ) => {
const detail =
cause instanceof Error ? cause.message : String ( cause );
reportValidationError (
`Unable to create Amp settings backup: ${ detail } ` ,
);
},
);
}
2026-10-29 08:57:38 +08:00
}
2026-10-29 01:28:04 +08:00
const result = serveStrict ( normalized , options ?? {}, ( tokens , selected ) =>
serveDeclared ( tokens , selected , ( declared , declaredOptions ) =>
2026-11-06 18:20:56 +09:00
reportUnknownMcpSubcommand ( cli . name , declared , declaredOptions )
? Promise . resolve ()
: serve ( adaptCliOutputArgumentsForIncur ( declared ), declaredOptions ),
2026-10-29 01:28:04 +08:00
),
);
const note = completionHelpNote ( normalized );
if ( note === undefined ) return result ;
return result . then (() => {
const write =
options ? . stdout ?? (( text : string ) => process . stdout . write ( text ));
write ( ` \ n ${ note } ` );
});
};
2026-07-13 03:15:01 +00:00
return cli ;
};