From ceb84c7790a625dbc07f6cf9b745fe16af8819dc Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 21:12:43 +0000 Subject: [PATCH 01/12] refactor(project): lift project command to the top level --- src/handlers/index.tsx | 22 +++++- src/handlers/project/add/index.ts | 15 +--- src/handlers/project/add/types.ts | 6 +- src/handlers/project/index.ts | 124 ++++++++---------------------- 4 files changed, 57 insertions(+), 110 deletions(-) diff --git a/src/handlers/index.tsx b/src/handlers/index.tsx index c6687c9d12..6bd9e54f86 100644 --- a/src/handlers/index.tsx +++ b/src/handlers/index.tsx @@ -9,7 +9,7 @@ import { createPaymentHandler } from "./payment/index.tsx"; import { createRuntimeHandler } from "./runtime/index.tsx"; import { DebugKey, JsonKey, RegionKey } from "./keys.tsx"; import { createConfigHandler } from "./config/"; -import { createProjectHandler } from "./project/index.ts"; +import { createProjectHandlers } from "./project/index.ts"; import { createUpdateHandler } from "./update/index.tsx"; import { renderTui } from "../tui"; import { @@ -46,7 +46,21 @@ export function createRootHandler(core: Core, config: RootHandlerConfig): Router const root = new Router( "agentcore", "the platform for production AI agents", - ).supportedTuiCommands("project", "harness", "identity", "runtime", "memory", "gateway", "eval"); + ).supportedTuiCommands( + "create", + "invoke", + "build", + "deploy", + "status", + "add", + "remove", + "harness", + "identity", + "runtime", + "memory", + "gateway", + "eval", + ); // `agentcore --version` prints the build-time package version. root.version(PACKAGE_VERSION); @@ -76,7 +90,9 @@ export function createRootHandler(core: Core, config: RootHandlerConfig): Router // Install sub handlers. Registration order is menu/help order; project is // the primary workflow, so it goes first. - root.handler(createProjectHandler({ core, io })); + createProjectHandlers(core, io).forEach((handler) => { + root.handler(handler); + }); root.handler(createHarnessHandler(core, io)); root.handler(createIdentityHandler(core, io)); root.handler(createRuntimeHandler(core, io)); diff --git a/src/handlers/project/add/index.ts b/src/handlers/project/add/index.ts index 5479cad99c..cf4213ef83 100644 --- a/src/handlers/project/add/index.ts +++ b/src/handlers/project/add/index.ts @@ -1,7 +1,6 @@ -import { withProject, withTuiWhenInteractive } from "../../../middleware/"; +import { withTuiWhenInteractive } from "../../../middleware/"; import { Router } from "../../../router"; import { renderTui } from "../../../tui"; -import type { Core } from "../../types"; import { createAddConfigBundleHandler } from "./config-bundle"; import { createAddCredentialsHandler } from "./credentials"; import { createAddHarnessHandler } from "./harness"; @@ -20,10 +19,7 @@ import { createAddPaymentConnectorHandler } from "./payment-connector"; import { createAddPaymentManagerHandler } from "./payment-manager"; import { createAddRuntimeEndpointHandler } from "./runtime-endpoint"; -export function createAddProjectResourceHandler( - config: AddProjectResourceConfig, - core: Core, -): Router { +export function createAddProjectResourceHandler(config: AddProjectResourceConfig): Router { // The resources with a wizard of their own. Every other resource is listed in // the add menu as command line only and opens its help instead (see // CliOnlyScreen). @@ -31,16 +27,13 @@ export function createAddProjectResourceHandler( "runtime", "memory", ); - projectAdd.default(renderTui(core, config.io)); + projectAdd.default(renderTui(config.core, config.io)); // withProject first, so it is the outermost wrapper: a resource added outside // a project gets the CLI's own not-found guidance, and the resolved project // seeds the wizard through ProjectKey. withTuiWhenInteractive then opens that // wizard for a bare `add ` on a TTY; it is inert for a resource // declared command-line only above, and for flags, --json and non-TTY runs. - projectAdd.use( - withProject({ projectManager: config.projectManager, cwd: process.cwd() }), - withTuiWhenInteractive(core, config.io), - ); + projectAdd.use(withTuiWhenInteractive(config.core, config.io)); projectAdd.handler(createAddConfigBundleHandler(config)); projectAdd.handler(createAddHarnessHandler(config)); projectAdd.handler(createAddMemoryHandler(config)); diff --git a/src/handlers/project/add/types.ts b/src/handlers/project/add/types.ts index 3201bf88e3..9f5627e9ac 100644 --- a/src/handlers/project/add/types.ts +++ b/src/handlers/project/add/types.ts @@ -1,9 +1,7 @@ import type { AppIO } from "../../../io"; -import type { CoreBedrockAgentImporter } from "../../../core/project/bedrockAgentImport"; -import type { ProjectManager } from "../types"; +import type { Core } from "../../types"; export type AddProjectResourceConfig = { - projectManager: ProjectManager; io: AppIO; - bedrockAgentImporter: CoreBedrockAgentImporter; + core: Core; }; diff --git a/src/handlers/project/index.ts b/src/handlers/project/index.ts index 60c25832b8..934d660bd0 100644 --- a/src/handlers/project/index.ts +++ b/src/handlers/project/index.ts @@ -1,17 +1,15 @@ -import { Router } from "../../router"; +import { type Handler } from "../../router"; import { checkPort, openBrowser, startHttpServer, watchFile, type AppIO } from "../../io"; import { CodeZipDevRunner } from "../../core/dev/codezip"; import { ContainerDevRunner } from "../../core/dev/container"; import { InspectorAssets } from "../../core/dev/inspectorAssets"; import { startOtelCollector } from "../../core/dev/otel/collector"; import { withProject, withTuiWhenInteractive } from "../../middleware"; -import { renderTui } from "../../tui"; import type { Core } from "../types"; import { createCreateProjectHandler } from "./create"; import { createRemoveProjectHandler } from "./remove"; import { createDevProjectHandler } from "./dev"; import { loadDevEnvironment } from "./dev/environment"; -import { createDeployProjectHandler } from "./deploy"; import { createStatusProjectHandler } from "./status"; import { createBuildProjectHandler } from "./build"; import type { ProjectManager } from "./types"; @@ -21,102 +19,44 @@ import { createProjectInvokeHandler } from "./invoke"; import { createProjectLogHandler } from "./log"; import { createProjectTracesHandler } from "./traces"; -type ProjectHandlerConfig = { - core: Core; - io: AppIO; -}; - -export function createProjectHandler({ core, io }: ProjectHandlerConfig): Router { +export function createProjectHandlers(core: Core, io: AppIO): Handler[] { const projectManager: ProjectManager = core.projectManager; - const config = { projectManager, io, bedrockAgentImporter: core.bedrockAgentImporter }; - // The subcommands with a screen of their own. Every other subcommand is - // listed in the menu as command line only and opens its help instead (see - // CliOnlyScreen). `add` is a group: it has the resource menu, and which of - // its resources have a wizard is declared on that router. - const project = new Router("project", "manage an AgentCore project").supportedTuiCommands( - "create", - "invoke", - "build", - "deploy", - "status", - "add", - "remove", - ); - // Without a default, a bare `agentcore project` falls back to Commander's help - // and a usage exit code instead of the menu every sibling router opens. - project.default(renderTui(core, io)); + const createHandler = createCreateProjectHandler({ + projectManager, + io, + middlewares: [withTuiWhenInteractive(core, io)], + }); - // A bare `agentcore project create` in an interactive session opens the TUI - // create wizard; any user-supplied flag, --json, or a non-TTY invocation keeps - // the headless handler. The TUI is exposed as middleware so the router runs it - // before flag validation, letting the wizard supply a required flag. - project.handler( - createCreateProjectHandler({ + const projectBoundHandlers = [ + createAddProjectResourceHandler({ core, io }), + createExportProjectResourceHandler({ projectManager, core, io }), + createRemoveProjectHandler({ projectManager, io }), + createDevProjectHandler({ projectManager, io, - middlewares: [withTuiWhenInteractive(core, io)], - }), - ); - project.handler(createAddProjectResourceHandler(config, core)); - project.handler(createExportProjectResourceHandler({ projectManager, core, io })); - project.handler( - createRemoveProjectHandler({ - projectManager: config.projectManager, - io: config.io, - middlewares: [ - withProject({ projectManager: config.projectManager }), - withTuiWhenInteractive(core, io), - ], + runners: { + CodeZip: new CodeZipDevRunner(), + Container: new ContainerDevRunner(), + }, + loadDevEnvironment, + checkPort, + startTraceCollector: startOtelCollector, + startServer: startHttpServer, + openBrowser, + inspectorAssets: new InspectorAssets(), + isInteractive: () => process.stdout.isTTY === true, + watchFile, }), - ); - project.handler( - withProject({ projectManager: config.projectManager })( - createDevProjectHandler({ - io: config.io, - runners: { - CodeZip: new CodeZipDevRunner(), - Container: new ContainerDevRunner(), - }, - loadDevEnvironment, - checkPort, - startTraceCollector: startOtelCollector, - startServer: startHttpServer, - openBrowser, - inspectorAssets: new InspectorAssets(), - isInteractive: () => process.stdout.isTTY === true, - watchFile, - projectManager: config.projectManager, - }), - ), - ); - project.handler( - withProject({ projectManager: config.projectManager })( - createDeployProjectHandler({ projectManager: config.projectManager, io: config.io }), - ), - ); - project.handler(createProjectInvokeHandler(core, io)); - project.handler(createProjectLogHandler(core, io)); - project.handler(createProjectTracesHandler(core, io)); - // A bare `agentcore project status` in an interactive session opens the TUI - // linked-resources screen; any user-supplied flag, --json, or a non-TTY - // invocation keeps the headless JSON report. withProject runs before the TUI - // so the not-found guidance is the CLI's own and the resolved project seeds - // the screen via ProjectKey. - const withStatusProject = withProject({ projectManager: config.projectManager }); - project.handler( + createProjectInvokeHandler(core, io), + createProjectLogHandler(core, io), + createProjectTracesHandler(core, io), createStatusProjectHandler({ - projectManager: config.projectManager, - middlewares: [withStatusProject, withTuiWhenInteractive(core, io)], + projectManager, + middlewares: [withTuiWhenInteractive(core, io)], }), - ); - // withProject wraps only the commands that require an existing project, so - // `create` (which refuses to nest inside one) stays unaffected. - project.handler( - withProject({ projectManager: config.projectManager })( - createBuildProjectHandler({ projectManager: config.projectManager, io: config.io }), - ), - ); + createBuildProjectHandler({ projectManager, io }), + ].map((h) => withProject({ projectManager })(h)); - return project; + return [createHandler, ...projectBoundHandlers]; } From 42bffbfd40395e7a541eef0255a83a68639bcf03 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 21:27:21 +0000 Subject: [PATCH 02/12] fix(project): preserve lifted command and tui wiring --- src/components/Root.tsx | 44 +++++---------- src/handlers/project/add/index.ts | 15 +++-- src/handlers/project/add/memory/screen.tsx | 4 +- src/handlers/project/add/runtime/screen.tsx | 4 +- src/handlers/project/add/types.ts | 6 +- src/handlers/project/build/screen.tsx | 4 +- src/handlers/project/create/screen.tsx | 4 +- src/handlers/project/deploy/screen.tsx | 4 +- src/handlers/project/index.ts | 62 +++++++++++++-------- src/handlers/project/invoke/index.tsx | 2 +- src/handlers/project/invoke/screen.tsx | 4 +- src/handlers/project/remove/screen.tsx | 16 +++--- src/handlers/project/screen.tsx | 7 --- src/handlers/project/status/screen.tsx | 4 +- 14 files changed, 89 insertions(+), 91 deletions(-) delete mode 100644 src/handlers/project/screen.tsx diff --git a/src/components/Root.tsx b/src/components/Root.tsx index 101ba04364..f86b1b93d6 100644 --- a/src/components/Root.tsx +++ b/src/components/Root.tsx @@ -120,7 +120,7 @@ import { GatewayRuleListScreen } from "../handlers/gateway/rule/list/screen.tsx" import { GatewayRuleGetScreen } from "../handlers/gateway/rule/get/screen.tsx"; import { GatewayInvokeScreen } from "../handlers/gateway/invoke/screen.tsx"; import { GatewayPolicyGenerateScreen } from "../handlers/gateway/policy/screen.tsx"; -import { ProjectScreen } from "../handlers/project/screen.tsx"; +import { RouterScreen } from "./RouterScreen.tsx"; import { CommandFallbackScreen } from "./CliOnlyScreen.tsx"; import { ProjectResourceCreateScreen } from "./ProjectResourceCreateScreen.tsx"; import { BuildProjectScreen } from "../handlers/project/build/screen.tsx"; @@ -242,7 +242,7 @@ function RouteTable({ ctx, core }: ScreenProps) { } /> } /> } /> @@ -868,41 +868,23 @@ function RouteTable({ ctx, core }: ScreenProps) { path="agentcore/identity/oauth2-credential-provider/get/:name/json" element={} /> - } /> } + path="agentcore/add" + element={} /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> } - /> - } - /> - } - /> - } - /> - } - /> - } - /> - } /> } /> {/* Every known command without a screen of its own: a group opens its diff --git a/src/handlers/project/add/index.ts b/src/handlers/project/add/index.ts index cf4213ef83..5479cad99c 100644 --- a/src/handlers/project/add/index.ts +++ b/src/handlers/project/add/index.ts @@ -1,6 +1,7 @@ -import { withTuiWhenInteractive } from "../../../middleware/"; +import { withProject, withTuiWhenInteractive } from "../../../middleware/"; import { Router } from "../../../router"; import { renderTui } from "../../../tui"; +import type { Core } from "../../types"; import { createAddConfigBundleHandler } from "./config-bundle"; import { createAddCredentialsHandler } from "./credentials"; import { createAddHarnessHandler } from "./harness"; @@ -19,7 +20,10 @@ import { createAddPaymentConnectorHandler } from "./payment-connector"; import { createAddPaymentManagerHandler } from "./payment-manager"; import { createAddRuntimeEndpointHandler } from "./runtime-endpoint"; -export function createAddProjectResourceHandler(config: AddProjectResourceConfig): Router { +export function createAddProjectResourceHandler( + config: AddProjectResourceConfig, + core: Core, +): Router { // The resources with a wizard of their own. Every other resource is listed in // the add menu as command line only and opens its help instead (see // CliOnlyScreen). @@ -27,13 +31,16 @@ export function createAddProjectResourceHandler(config: AddProjectResourceConfig "runtime", "memory", ); - projectAdd.default(renderTui(config.core, config.io)); + projectAdd.default(renderTui(core, config.io)); // withProject first, so it is the outermost wrapper: a resource added outside // a project gets the CLI's own not-found guidance, and the resolved project // seeds the wizard through ProjectKey. withTuiWhenInteractive then opens that // wizard for a bare `add ` on a TTY; it is inert for a resource // declared command-line only above, and for flags, --json and non-TTY runs. - projectAdd.use(withTuiWhenInteractive(config.core, config.io)); + projectAdd.use( + withProject({ projectManager: config.projectManager, cwd: process.cwd() }), + withTuiWhenInteractive(core, config.io), + ); projectAdd.handler(createAddConfigBundleHandler(config)); projectAdd.handler(createAddHarnessHandler(config)); projectAdd.handler(createAddMemoryHandler(config)); diff --git a/src/handlers/project/add/memory/screen.tsx b/src/handlers/project/add/memory/screen.tsx index c83063de1f..4e9e2acd84 100644 --- a/src/handlers/project/add/memory/screen.tsx +++ b/src/handlers/project/add/memory/screen.tsx @@ -26,9 +26,9 @@ import { type MemoryInput, } from "./index"; -const BREADCRUMB = ["agentcore", "project", "add", "memory"]; +const BREADCRUMB = ["agentcore", "add", "memory"]; const DESCRIPTION = "add a Memory to the current project"; -const ADD_MENU = "/agentcore/project/add"; +const ADD_MENU = "/agentcore/add"; const STRATEGY_DESCRIPTIONS: Record = { SEMANTIC: "durable facts about the actor", diff --git a/src/handlers/project/add/runtime/screen.tsx b/src/handlers/project/add/runtime/screen.tsx index c7a609063d..62bfeca0cc 100644 --- a/src/handlers/project/add/runtime/screen.tsx +++ b/src/handlers/project/add/runtime/screen.tsx @@ -22,9 +22,9 @@ import { } from "../../shortcuts"; import { toAddRuntimeInput, type RuntimeInput } from "./index"; -const BREADCRUMB = ["agentcore", "project", "add", "runtime"]; +const BREADCRUMB = ["agentcore", "add", "runtime"]; const DESCRIPTION = "add a Runtime to the current project"; -const ADD_MENU = "/agentcore/project/add"; +const ADD_MENU = "/agentcore/add"; const DEFAULT_TEMPLATE: RuntimeTemplateShortcutName = "agent-python-minimal"; diff --git a/src/handlers/project/add/types.ts b/src/handlers/project/add/types.ts index 9f5627e9ac..3201bf88e3 100644 --- a/src/handlers/project/add/types.ts +++ b/src/handlers/project/add/types.ts @@ -1,7 +1,9 @@ import type { AppIO } from "../../../io"; -import type { Core } from "../../types"; +import type { CoreBedrockAgentImporter } from "../../../core/project/bedrockAgentImport"; +import type { ProjectManager } from "../types"; export type AddProjectResourceConfig = { + projectManager: ProjectManager; io: AppIO; - core: Core; + bedrockAgentImporter: CoreBedrockAgentImporter; }; diff --git a/src/handlers/project/build/screen.tsx b/src/handlers/project/build/screen.tsx index 2257f08056..f79d79e0db 100644 --- a/src/handlers/project/build/screen.tsx +++ b/src/handlers/project/build/screen.tsx @@ -6,9 +6,9 @@ import { ProjectGate } from "../ProjectGate"; import type { Project } from "../types"; import { builtMessage } from "./index"; -const BREADCRUMB = ["agentcore", "project", "build"]; +const BREADCRUMB = ["agentcore", "build"]; const DESCRIPTION = "build the project's deployable artifacts"; -const PROJECT_MENU = "/agentcore/project"; +const PROJECT_MENU = "/agentcore"; // BuildProjectScreen runs the same projectManager.build generator the command // runs; ConfirmAction renders its steps through the same TaskList. diff --git a/src/handlers/project/create/screen.tsx b/src/handlers/project/create/screen.tsx index a056c85418..667c3851b7 100644 --- a/src/handlers/project/create/screen.tsx +++ b/src/handlers/project/create/screen.tsx @@ -217,11 +217,11 @@ export function ProjectCreateScreen({ ctx, core }: ScreenProps) { return ( navigate("/agentcore/project")} + onCancel={() => navigate("/agentcore")} onSubmit={() => { // Both of these throw before anything is written, so the wizard reports // them the way it reports a failed create — with the retry still on diff --git a/src/handlers/project/deploy/screen.tsx b/src/handlers/project/deploy/screen.tsx index 32b7b2a695..c066a4de5c 100644 --- a/src/handlers/project/deploy/screen.tsx +++ b/src/handlers/project/deploy/screen.tsx @@ -9,9 +9,9 @@ import { ProjectGate } from "../ProjectGate"; import type { Project } from "../types"; import { declaresNothingDeployable, deployedMessage, teardownQuestion } from "./index"; -const BREADCRUMB = ["agentcore", "project", "deploy"]; +const BREADCRUMB = ["agentcore", "deploy"]; const DESCRIPTION = "deploy the project to AWS"; -const PROJECT_MENU = "/agentcore/project"; +const PROJECT_MENU = "/agentcore"; // DeployProjectScreen runs the same projectManager.deploy generator the command // runs; ConfirmAction renders its steps through the same TaskList. With several diff --git a/src/handlers/project/index.ts b/src/handlers/project/index.ts index 934d660bd0..18a4f7ef27 100644 --- a/src/handlers/project/index.ts +++ b/src/handlers/project/index.ts @@ -10,6 +10,7 @@ import { createCreateProjectHandler } from "./create"; import { createRemoveProjectHandler } from "./remove"; import { createDevProjectHandler } from "./dev"; import { loadDevEnvironment } from "./dev/environment"; +import { createDeployProjectHandler } from "./deploy"; import { createStatusProjectHandler } from "./status"; import { createBuildProjectHandler } from "./build"; import type { ProjectManager } from "./types"; @@ -28,35 +29,48 @@ export function createProjectHandlers(core: Core, io: AppIO): Handler[] { middlewares: [withTuiWhenInteractive(core, io)], }); + const withProjectMiddleware = withProject({ projectManager }); + const config = { projectManager, io, bedrockAgentImporter: core.bedrockAgentImporter }; const projectBoundHandlers = [ - createAddProjectResourceHandler({ core, io }), + createAddProjectResourceHandler(config, core), createExportProjectResourceHandler({ projectManager, core, io }), - createRemoveProjectHandler({ projectManager, io }), - createDevProjectHandler({ - projectManager, - io, - runners: { - CodeZip: new CodeZipDevRunner(), - Container: new ContainerDevRunner(), - }, - loadDevEnvironment, - checkPort, - startTraceCollector: startOtelCollector, - startServer: startHttpServer, - openBrowser, - inspectorAssets: new InspectorAssets(), - isInteractive: () => process.stdout.isTTY === true, - watchFile, - }), + withProjectMiddleware( + createRemoveProjectHandler({ + projectManager, + io, + middlewares: [withTuiWhenInteractive(core, io)], + }), + ), + withProjectMiddleware( + createDevProjectHandler({ + projectManager, + io, + runners: { + CodeZip: new CodeZipDevRunner(), + Container: new ContainerDevRunner(), + }, + loadDevEnvironment, + checkPort, + startTraceCollector: startOtelCollector, + startServer: startHttpServer, + openBrowser, + inspectorAssets: new InspectorAssets(), + isInteractive: () => process.stdout.isTTY === true, + watchFile, + }), + ), + withProjectMiddleware(createDeployProjectHandler({ projectManager, io })), createProjectInvokeHandler(core, io), createProjectLogHandler(core, io), createProjectTracesHandler(core, io), - createStatusProjectHandler({ - projectManager, - middlewares: [withTuiWhenInteractive(core, io)], - }), - createBuildProjectHandler({ projectManager, io }), - ].map((h) => withProject({ projectManager })(h)); + withProjectMiddleware( + createStatusProjectHandler({ + projectManager, + middlewares: [withTuiWhenInteractive(core, io)], + }), + ), + withProjectMiddleware(createBuildProjectHandler({ projectManager, io })), + ]; return [createHandler, ...projectBoundHandlers]; } diff --git a/src/handlers/project/invoke/index.tsx b/src/handlers/project/invoke/index.tsx index 3c5c055b3b..3dbdfc82a3 100644 --- a/src/handlers/project/invoke/index.tsx +++ b/src/handlers/project/invoke/index.tsx @@ -23,6 +23,6 @@ export function createProjectInvokeHandler( "a Runtime or harness invoke subcommand is required with --json", ); } - return renderInvokeTui("/agentcore/project/invoke", ctx, core, io); + return renderInvokeTui("/agentcore/invoke", ctx, core, io); }); } diff --git a/src/handlers/project/invoke/screen.tsx b/src/handlers/project/invoke/screen.tsx index 4ff72e55e4..96ea15319b 100644 --- a/src/handlers/project/invoke/screen.tsx +++ b/src/handlers/project/invoke/screen.tsx @@ -35,9 +35,9 @@ type Destination = | { resourceType: "runtime"; id: string; ctx: Context; qualifier?: string } | { resourceType: "harness"; id: string; ctx: Context }; -const BREADCRUMB = ["agentcore", "project", "invoke"]; +const BREADCRUMB = ["agentcore", "invoke"]; const DESCRIPTION = "invoke a Runtime or harness from the current project"; -const PROJECT_MENU = "/agentcore/project"; +const PROJECT_MENU = "/agentcore"; // The project comes from the launch context when a project command opened the // TUI, and is resolved from the cwd otherwise — the gate reports the CLI's own diff --git a/src/handlers/project/remove/screen.tsx b/src/handlers/project/remove/screen.tsx index daff3bd1b1..600f8713d1 100644 --- a/src/handlers/project/remove/screen.tsx +++ b/src/handlers/project/remove/screen.tsx @@ -148,8 +148,8 @@ const RESOURCE_PICKER_CONFIGS: RemovableResourcePickerConfig[] = [ }, ]; -const PROJECT_MENU = "/agentcore/project"; -const REMOVE_ROOT = "/agentcore/project/remove"; +const PROJECT_MENU = "/agentcore"; +const REMOVE_ROOT = "/agentcore/remove"; // Nothing to navigate or select on an empty list or the nothing-to-remove message. const STATIC_KEY_HINTS = [ @@ -203,7 +203,7 @@ export function ProjectRemoveScreen({ ctx, core }: ScreenProps) { return ( navigate(PROJECT_MENU)} > @@ -252,7 +252,7 @@ function ResourceTypePicker({ project }: { project: Project }) { return ( 0 ? KEY_HINTS : STATIC_KEY_HINTS} > @@ -303,7 +303,7 @@ function ResourcePicker({ return ( @@ -345,7 +345,7 @@ function RemoveConfirm({ return ( + This project has no resources to remove. ); @@ -411,7 +411,7 @@ function RemoveAllConfirm({ project, core }: { project: Project; core: ScreenPro return ( ; -} diff --git a/src/handlers/project/status/screen.tsx b/src/handlers/project/status/screen.tsx index d849fea9f8..4fa1157d32 100644 --- a/src/handlers/project/status/screen.tsx +++ b/src/handlers/project/status/screen.tsx @@ -19,9 +19,9 @@ import { LoadingFrame, ProjectGate } from "../ProjectGate"; const theme = darkTheme; -const BREADCRUMB = ["agentcore", "project", "status"]; +const BREADCRUMB = ["agentcore", "status"]; const DESCRIPTION = "the project's linked resources"; -const PROJECT_MENU = "/agentcore/project"; +const PROJECT_MENU = "/agentcore"; // The detail routes a deployed resource can forward to. Types without a detail // screen are listed but not navigable. From d0c823a01f06edbadb0273ff09728c1fc123a744 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 21:35:37 +0000 Subject: [PATCH 03/12] fix(project): preserve lifted tui behavior --- .../ProjectResourceCreateScreen.tsx | 10 ++++----- src/core/dev/localInvoke.ts | 2 +- src/core/project/backends/cdk.ts | 8 +++---- src/core/project/manager.tsx | 8 +++---- src/core/project/templates/export.ts | 4 ++-- .../project/add/gateway-target/index.ts | 2 +- src/handlers/project/add/harness/index.ts | 2 +- src/handlers/project/build/screen.tsx | 2 +- src/handlers/project/create/index.ts | 2 +- src/handlers/project/create/screen.tsx | 6 ++--- src/handlers/project/export/harness.ts | 2 +- src/handlers/project/index.ts | 22 ++++++++----------- src/handlers/project/status/screen.tsx | 2 +- src/middleware/withProject.tsx | 2 +- 14 files changed, 35 insertions(+), 39 deletions(-) diff --git a/src/components/ProjectResourceCreateScreen.tsx b/src/components/ProjectResourceCreateScreen.tsx index f141fc61c4..87963f72a4 100644 --- a/src/components/ProjectResourceCreateScreen.tsx +++ b/src/components/ProjectResourceCreateScreen.tsx @@ -13,19 +13,19 @@ const RESOURCES: Record< label: "Runtime", pluralLabel: "Runtimes", description: "create an AgentCore Runtime in a project", - addCommand: "agentcore project add runtime", + addCommand: "agentcore add runtime", }, memory: { label: "Memory", pluralLabel: "Memories", description: "create an AgentCore Memory in a project", - addCommand: "agentcore project add memory", + addCommand: "agentcore add memory", }, gateway: { label: "Gateway", pluralLabel: "Gateways", description: "create an AgentCore Gateway in a project", - addCommand: "agentcore project add gateway --name MyGateway", + addCommand: "agentcore add gateway --name MyGateway", }, }; @@ -57,10 +57,10 @@ export function ProjectResourceCreateScreen({ resource }: ProjectResourceCreateS Run these commands from the command line: - {" agentcore project create"} + {" agentcore create"} {" cd "} {` ${config.addCommand}`} - {" agentcore project deploy"} + {" agentcore deploy"} ); } diff --git a/src/core/dev/localInvoke.ts b/src/core/dev/localInvoke.ts index 3364932bc5..92db71231b 100644 --- a/src/core/dev/localInvoke.ts +++ b/src/core/dev/localInvoke.ts @@ -79,7 +79,7 @@ export async function invokeLocalRuntime( const detail = error instanceof Error ? error.message : String(error); throw new InvalidEnvironmentError( `Could not reach local dev server on port ${request.port} (${detail}). Start it with: ` + - `agentcore project dev --mode headless --agent --port ${request.port}`, + `agentcore dev --mode headless --agent --port ${request.port}`, { cause: error }, ); } diff --git a/src/core/project/backends/cdk.ts b/src/core/project/backends/cdk.ts index 3d4cf9d2f4..a2b45dc2ed 100644 --- a/src/core/project/backends/cdk.ts +++ b/src/core/project/backends/cdk.ts @@ -246,7 +246,7 @@ export class CdkBackend implements ProjectBackend { if (credentialName) { throw new ProjectStateError( `Project build cannot resolve credential "${credentialName}" before its first deployment. ` + - `Run 'agentcore project deploy' to provision the credential and build the project.`, + `Run 'agentcore deploy' to provision the credential and build the project.`, { cause: error, meta: { credentialName } }, ); } @@ -414,7 +414,7 @@ export class CdkBackend implements ProjectBackend { throw new ProjectStateError( `Project '${project.name}' declares no resources to deploy, and no stack ` + `'${artifact.stackName}' exists in ${target.account}/${target.region} to remove. ` + - `Add a resource — for example 'agentcore project add runtime' — before deploying.`, + `Add a resource — for example 'agentcore add runtime' — before deploying.`, ); } @@ -490,7 +490,7 @@ export class CdkBackend implements ProjectBackend { if (!stackReference) { throw new ProjectStateError( `Project '${project.name}' is not deployed to target '${target.name}'. ` + - `Run 'agentcore project deploy --target ${target.name}' first.`, + `Run 'agentcore deploy --target ${target.name}' first.`, ); } @@ -499,7 +499,7 @@ export class CdkBackend implements ProjectBackend { if (!stack) { throw new ProjectStateError( `Project '${project.name}' is not deployed to target '${target.name}'. ` + - `Run 'agentcore project deploy --target ${target.name}' first.`, + `Run 'agentcore deploy --target ${target.name}' first.`, ); } diff --git a/src/core/project/manager.tsx b/src/core/project/manager.tsx index 66acb64769..b15b1f89ce 100644 --- a/src/core/project/manager.tsx +++ b/src/core/project/manager.tsx @@ -1052,7 +1052,7 @@ export class FsProjectManager implements ProjectManager { const label = input.resourceType === "runtime" ? "Runtime" : "Harness"; throw new ProjectStateError( `${label} '${input.name}' is not deployed to target '${input.target}'. ` + - `Run 'agentcore project deploy --target ${input.target}' first.`, + `Run 'agentcore deploy --target ${input.target}' first.`, ); } @@ -1085,7 +1085,7 @@ export class FsProjectManager implements ProjectManager { if (targets.length === 0) { throw new ProjectStateError( `No deployment targets are configured for project '${project.name}'. ` + - `Please deploy your project using 'agentcore project deploy'.`, + `Please deploy your project using 'agentcore deploy'.`, ); } @@ -1129,7 +1129,7 @@ export class FsProjectManager implements ProjectManager { `Cannot create the default deployment target for project '${project.name}' because ` + `the AWS account could not be resolved: ${cause.message}\n` + `Check that valid AWS credentials are configured (for example via 'aws configure', ` + - `AWS_PROFILE, or environment variables) and re-run 'agentcore project deploy'.`, + `AWS_PROFILE, or environment variables) and re-run 'agentcore deploy'.`, { cause: error }, ); } @@ -1227,7 +1227,7 @@ export class FsProjectManager implements ProjectManager { type: "step", message: `Warning: could not generate ${lockfile} in ${appDir}. ` + - `Run \`${command.join(" ")}\` there before \`agentcore project dev\` or \`deploy\` — ` + + `Run \`${command.join(" ")}\` there before \`agentcore dev\` or \`deploy\` — ` + "container builds install from it.", }; } diff --git a/src/core/project/templates/export.ts b/src/core/project/templates/export.ts index 969d87006e..596305af47 100644 --- a/src/core/project/templates/export.ts +++ b/src/core/project/templates/export.ts @@ -413,7 +413,7 @@ function attachIdentityProvider( `"${credentialName}" (${apiKeyArn}). A credential entry named "${credentialName}" was ` + `added to agentcore.json. Deploy creates a provider for it scoped to the project and ` + `target, so add ${envVarName}= to agentcore/.env.local before the first ` + - `deploy. \`agentcore project dev\` reads the same variable.`, + `deploy. \`agentcore dev\` reads the same variable.`, }); } @@ -440,7 +440,7 @@ function resolveMemory( category: MEMORY_MANAGED_NOTE_CATEGORY, message: "The harness used managed memory, which the service provisions and owns. The exported " + - "agent has no memory wired. Add a project memory (`agentcore project add memory`) and " + + "agent has no memory wired. Add a project memory (`agentcore add memory`) and " + "re-run the export, or wire memory/session.py to an existing AgentCore Memory by hand.", }); return {}; diff --git a/src/handlers/project/add/gateway-target/index.ts b/src/handlers/project/add/gateway-target/index.ts index 205a19e1fe..fa2f81785d 100644 --- a/src/handlers/project/add/gateway-target/index.ts +++ b/src/handlers/project/add/gateway-target/index.ts @@ -33,7 +33,7 @@ Use --endpoint for an external MCP server or --runtime for a project Runtime. For every complete project Target shape, pass targetType and its configuration here. Supported targetType values: mcpServer, httpRuntime, apiGateway, openApiSchema, smithyModel, lambdaFunctionArn, connector, and passthrough. -Use project add gateway-connector for curated Connector shortcuts.`, +Use agentcore add gateway-connector for curated Connector shortcuts.`, }, ), flag( diff --git a/src/handlers/project/add/harness/index.ts b/src/handlers/project/add/harness/index.ts index 675deb5837..050e7b2e1d 100644 --- a/src/handlers/project/add/harness/index.ts +++ b/src/handlers/project/add/harness/index.ts @@ -6,7 +6,7 @@ import { parseJsonFlag, parseTags } from "../../../utils"; import { InputValidationError } from "../../../../errors"; import { HarnessSpecSchema } from "../../../../projectSchemas/harness"; -/** The model a harness runs on when none is configured; `project create`'s +/** The model a harness runs on when none is configured; `agentcore create`'s * harness path shares it so the two entry points cannot drift. */ export const DEFAULT_HARNESS_MODEL = { provider: "bedrock", diff --git a/src/handlers/project/build/screen.tsx b/src/handlers/project/build/screen.tsx index f79d79e0db..7c7298cf40 100644 --- a/src/handlers/project/build/screen.tsx +++ b/src/handlers/project/build/screen.tsx @@ -44,7 +44,7 @@ function BuildConfirm({ project, core }: { project: Project; core: ScreenProps[" }} successTitle={builtMessage(project)} runningLabel="building…" - nextSteps={["agentcore project deploy"]} + nextSteps={["agentcore deploy"]} onDone={() => navigate(PROJECT_MENU)} doneLabel="go back" onCancel={() => navigate(PROJECT_MENU)} diff --git a/src/handlers/project/create/index.ts b/src/handlers/project/create/index.ts index b2e2041fcd..9f1a754403 100644 --- a/src/handlers/project/create/index.ts +++ b/src/handlers/project/create/index.ts @@ -146,7 +146,7 @@ export const createCreateProjectHandler = (config: CreateProjectHandlerConfig) = }, () => { config.io.stderr.write(`Created project '${name}' in ./${name}\n`); - config.io.stderr.write(`Next steps:\n cd ${name}\n agentcore project deploy\n`); + config.io.stderr.write(`Next steps:\n cd ${name}\n agentcore deploy\n`); }, ); }, diff --git a/src/handlers/project/create/screen.tsx b/src/handlers/project/create/screen.tsx index 667c3851b7..20bced6f30 100644 --- a/src/handlers/project/create/screen.tsx +++ b/src/handlers/project/create/screen.tsx @@ -201,7 +201,7 @@ function providerLabel(provider: HarnessModelProvider): string { // ─── wizard ─────────────────────────────────────────────────────────────────── -// ProjectCreateScreen is the interactive flow behind a bare `agentcore project +// ProjectCreateScreen is the interactive flow behind a bare `agentcore // create`: name → type → (model | template) → review, then the // creation itself, streaming the ProjectManager's progress events. It drives // core.projectManager.create with the same input the flag-driven handler @@ -219,7 +219,7 @@ export function ProjectCreateScreen({ ctx, core }: ScreenProps) { navigate("/agentcore")} onSubmit={() => { @@ -231,7 +231,7 @@ export function ProjectCreateScreen({ ctx, core }: ScreenProps) { }} runningLabel={`creating ${values.name}…`} successLabel={`project created in ./${values.name}`} - successNextSteps={[`cd ${values.name}`, "agentcore project deploy"]} + successNextSteps={[`cd ${values.name}`, "agentcore deploy"]} successHint="enter exits" onDone={() => { ctx.value(TuiExitMessageKey)?.(`Next step:\n cd ${values.name}/ && agentcore`); diff --git a/src/handlers/project/export/harness.ts b/src/handlers/project/export/harness.ts index 4bf495baae..09a6097b1a 100644 --- a/src/handlers/project/export/harness.ts +++ b/src/handlers/project/export/harness.ts @@ -76,7 +76,7 @@ export const createExportHarnessHandler = (config: ExportProjectResourceConfig) config.io.stderr.write(`${line.text}\n`); } config.io.stderr.write( - "Next steps: review the generated code, then `agentcore project build` and `agentcore project deploy`\n", + "Next steps: review the generated code, then `agentcore build` and `agentcore deploy`\n", ); if (jsonOutput) { diff --git a/src/handlers/project/index.ts b/src/handlers/project/index.ts index 18a4f7ef27..dc9bf5320e 100644 --- a/src/handlers/project/index.ts +++ b/src/handlers/project/index.ts @@ -34,13 +34,11 @@ export function createProjectHandlers(core: Core, io: AppIO): Handler[] { const projectBoundHandlers = [ createAddProjectResourceHandler(config, core), createExportProjectResourceHandler({ projectManager, core, io }), - withProjectMiddleware( - createRemoveProjectHandler({ - projectManager, - io, - middlewares: [withTuiWhenInteractive(core, io)], - }), - ), + createRemoveProjectHandler({ + projectManager, + io, + middlewares: [withProjectMiddleware, withTuiWhenInteractive(core, io)], + }), withProjectMiddleware( createDevProjectHandler({ projectManager, @@ -63,12 +61,10 @@ export function createProjectHandlers(core: Core, io: AppIO): Handler[] { createProjectInvokeHandler(core, io), createProjectLogHandler(core, io), createProjectTracesHandler(core, io), - withProjectMiddleware( - createStatusProjectHandler({ - projectManager, - middlewares: [withTuiWhenInteractive(core, io)], - }), - ), + createStatusProjectHandler({ + projectManager, + middlewares: [withProjectMiddleware, withTuiWhenInteractive(core, io)], + }), withProjectMiddleware(createBuildProjectHandler({ projectManager, io })), ]; diff --git a/src/handlers/project/status/screen.tsx b/src/handlers/project/status/screen.tsx index 4fa1157d32..6d6ad4a8fe 100644 --- a/src/handlers/project/status/screen.tsx +++ b/src/handlers/project/status/screen.tsx @@ -255,7 +255,7 @@ function ProjectStatusView({ {nodes.length === 0 ? ( - No resources are declared in this project. Run `agentcore project add` to declare one. + No resources are declared in this project. Run `agentcore add` to declare one. ) : ( diff --git a/src/middleware/withProject.tsx b/src/middleware/withProject.tsx index f2166151f3..d49f8ccc6d 100644 --- a/src/middleware/withProject.tsx +++ b/src/middleware/withProject.tsx @@ -20,7 +20,7 @@ export function projectNotFoundMessage(from: string): string { return ( `No AgentCore project found at ${from} or any parent directory ` + `(looked for agentcore/agentcore.json). ` + - `Run 'agentcore project create' to scaffold one.` + `Run 'agentcore create' to scaffold one.` ); } From b95470cddc3814b6dba1b5818a7081004b4f60b9 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 21:37:17 +0000 Subject: [PATCH 04/12] test(project): update unit coverage for top-level commands --- src/components/CliOnlyScreen.test.tsx | 18 ++- .../ProjectResourceCreateScreen.test.tsx | 10 +- src/core/project/backends/cdk.test.ts | 6 +- .../gateway/gateway.mutations.screen.test.tsx | 12 +- .../gateway/gateway.mutations.test.tsx | 3 +- src/handlers/project/add/add.screen.test.tsx | 25 ++--- .../add/evaluator/code-based/index.test.ts | 2 +- .../evaluator/llm-as-a-judge/index.test.ts | 2 +- .../project/add/gateway-test-support.ts | 2 +- .../project/add/harness/index.test.ts | 2 +- src/handlers/project/add/memory/index.test.ts | 2 +- .../project/add/memory/memory.screen.test.tsx | 23 ++-- .../project/add/online-eval/index.test.ts | 2 +- .../project/add/online-insight/index.test.ts | 2 +- .../project/add/payment-test-support.ts | 2 +- .../add/runtime-endpoint/index.test.ts | 2 +- .../project/add/runtime/index.test.ts | 2 +- .../add/runtime/runtime.screen.test.tsx | 25 ++--- src/handlers/project/build/index.test.ts | 2 +- .../project/buildDeploy.screen.test.tsx | 50 ++++----- .../project/create/create.screen.test.tsx | 57 +++++----- src/handlers/project/deploy/index.test.ts | 2 +- src/handlers/project/export/harness.test.ts | 4 +- src/handlers/project/invoke/index.test.tsx | 6 +- .../project/invoke/invoke.screen.test.tsx | 30 ++--- src/handlers/project/log/harness.test.tsx | 11 +- src/handlers/project/log/runtime.test.tsx | 11 +- src/handlers/project/project.screen.test.tsx | 105 ++++++++++-------- src/handlers/project/project.test.ts | 4 +- src/handlers/project/remove/index.test.ts | 2 +- .../project/remove/remove.screen.test.tsx | 56 +++++----- src/handlers/project/status/index.test.ts | 4 +- .../project/status/status.screen.test.tsx | 20 ++-- src/handlers/project/traces/harness.test.tsx | 11 +- src/handlers/project/traces/runtime.test.tsx | 11 +- src/handlers/root.test.tsx | 12 +- src/testing/projects.ts | 1 - 37 files changed, 268 insertions(+), 273 deletions(-) diff --git a/src/components/CliOnlyScreen.test.tsx b/src/components/CliOnlyScreen.test.tsx index 16e483b1da..ca90406a3d 100644 --- a/src/components/CliOnlyScreen.test.tsx +++ b/src/components/CliOnlyScreen.test.tsx @@ -39,8 +39,22 @@ describe("menus list command-line-only subcommands below a divider", () => { await waitForText(r.lastFrame, "command line only"); expect(menuEntries(r.lastFrame()!)).toEqual({ - screens: ["project", "harness", "identity", "runtime", "memory", "gateway", "eval"], - cliOnly: ["payment", "feedback", "config", "update"], + screens: [ + "create", + "add", + "remove", + "deploy", + "invoke", + "status", + "build", + "harness", + "identity", + "runtime", + "memory", + "gateway", + "eval", + ], + cliOnly: ["export", "dev", "log", "traces", "payment", "feedback", "config", "update"], }); r.unmount(); }); diff --git a/src/components/ProjectResourceCreateScreen.test.tsx b/src/components/ProjectResourceCreateScreen.test.tsx index 92af129cf2..d735f9e3a8 100644 --- a/src/components/ProjectResourceCreateScreen.test.tsx +++ b/src/components/ProjectResourceCreateScreen.test.tsx @@ -18,19 +18,19 @@ const RESOURCES = [ resource: "runtime", label: "Runtime", parentDescription: "inspect AgentCore Runtimes", - addCommand: "agentcore project add runtime", + addCommand: "agentcore add runtime", }, { resource: "memory", label: "Memory", parentDescription: "inspect AgentCore Memories", - addCommand: "agentcore project add memory", + addCommand: "agentcore add memory", }, { resource: "gateway", label: "Gateway", parentDescription: "manage AgentCore Gateways", - addCommand: "agentcore project add gateway --name MyGateway", + addCommand: "agentcore add gateway --name MyGateway", }, ] as const satisfies { resource: ProjectCreateResource; @@ -52,10 +52,10 @@ describe("project resource creation guidance", () => { await waitForText(r.lastFrame, `Create an AgentCore ${label}`); const frame = r.lastFrame()!; - expect(frame).toContain("agentcore project create"); + expect(frame).toContain("agentcore create"); expect(frame).toContain("cd "); expect(frame).toContain(addCommand); - expect(frame).toContain("agentcore project deploy"); + expect(frame).toContain("agentcore deploy"); expect(frame).not.toContain("┌"); await r.press("escape"); diff --git a/src/core/project/backends/cdk.test.ts b/src/core/project/backends/cdk.test.ts index 1a358890ef..a0e857db0e 100644 --- a/src/core/project/backends/cdk.test.ts +++ b/src/core/project/backends/cdk.test.ts @@ -397,7 +397,7 @@ describe("CdkBackend.build", () => { await expect(collect(subject.backend.build(input))).rejects.toThrow( `Project build cannot resolve credential "${name}" before its first deployment. ` + - `Run 'agentcore project deploy' to provision the credential and build the project.`, + `Run 'agentcore deploy' to provision the credential and build the project.`, ); }); }); @@ -1099,7 +1099,7 @@ describe("CdkBackend.resolveDeployedResources", () => { await expect( subject.backend.resolveDeployedResources(input, { target: TARGET }), - ).rejects.toThrow(/not deployed.*project deploy --target default/s); + ).rejects.toThrow(/not deployed.*agentcore deploy --target default/s); expect(subject.stackReads).toEqual([]); expect(subject.accountCredentials).toEqual([]); }); @@ -1111,7 +1111,7 @@ describe("CdkBackend.resolveDeployedResources", () => { await expect( subject.backend.resolveDeployedResources(input, { target: TARGET }), - ).rejects.toThrow(/not deployed.*project deploy --target default/s); + ).rejects.toThrow(/not deployed.*agentcore deploy --target default/s); expect(subject.stackReads[0]?.stackName).toBe(STACK_ARN); }); diff --git a/src/handlers/gateway/gateway.mutations.screen.test.tsx b/src/handlers/gateway/gateway.mutations.screen.test.tsx index 6b0b04941b..88f4501cb8 100644 --- a/src/handlers/gateway/gateway.mutations.screen.test.tsx +++ b/src/handlers/gateway/gateway.mutations.screen.test.tsx @@ -36,10 +36,10 @@ describe("Gateway mutation menus", () => { await screen.press("return"); await waitForText(screen.lastFrame, "Create an AgentCore Gateway"); const frame = screen.lastFrame()!; - expect(frame).toContain("agentcore project create"); + expect(frame).toContain("agentcore create"); expect(frame).toContain("cd "); - expect(frame).toContain("agentcore project add gateway --name MyGateway"); - expect(frame).toContain("agentcore project deploy"); + expect(frame).toContain("agentcore add gateway --name MyGateway"); + expect(frame).toContain("agentcore deploy"); expect(frame).not.toContain("agentcore gateway create"); expect(frame).not.toContain("this command runs from the command line"); expect(screen.core.gateway.calls).toEqual([]); @@ -56,7 +56,7 @@ describe("Gateway mutation menus", () => { screen.lastFrame, enabled ? "this command runs from the command line" : "Create an AgentCore Gateway", ); - expect(screen.lastFrame()?.includes("agentcore project add gateway")).toBe(!enabled); + expect(screen.lastFrame()?.includes("agentcore add gateway")).toBe(!enabled); expect(screen.lastFrame()?.includes("--authorizer-type")).toBe(enabled); await screen.press("escape"); await waitForText(screen.lastFrame, "manage AgentCore Gateways"); @@ -83,10 +83,10 @@ describe("Gateway mutation menus", () => { await waitForText(screen.lastFrame, "Create an AgentCore Gateway"); await screen.resize(50, 12); await screen.write("\u001b[6~"); - await waitForText(screen.lastFrame, "agentcore project deploy"); + await waitForText(screen.lastFrame, "agentcore deploy"); await screen.resize(100, 40); await waitForText(screen.lastFrame, "Create an AgentCore Gateway"); - expect(screen.lastFrame()).toContain("agentcore project deploy"); + expect(screen.lastFrame()).toContain("agentcore deploy"); await screen.press("escape"); await waitForText(screen.lastFrame, "manage AgentCore Gateways"); }); diff --git a/src/handlers/gateway/gateway.mutations.test.tsx b/src/handlers/gateway/gateway.mutations.test.tsx index 25510619e5..3e3f51c516 100644 --- a/src/handlers/gateway/gateway.mutations.test.tsx +++ b/src/handlers/gateway/gateway.mutations.test.tsx @@ -84,8 +84,7 @@ describe("Gateway imperative mutation availability", () => { expect(gateway.commands.find((child) => child.name() === "policy")?.commands[0]?.name()).toBe( "generate", ); - const project = command.commands.find((child) => child.name() === "project")!; - const add = project.commands.find((child) => child.name() === "add")!; + const add = command.commands.find((child) => child.name() === "add")!; expect(add.commands.map((child) => child.name())).toContain("gateway"); const harness = command.commands.find((child) => child.name() === "harness")!; expect(harness.commands.map((child) => child.name())).toContain("create"); diff --git a/src/handlers/project/add/add.screen.test.tsx b/src/handlers/project/add/add.screen.test.tsx index 2d1f74bf0c..c2f47fc99a 100644 --- a/src/handlers/project/add/add.screen.test.tsx +++ b/src/handlers/project/add/add.screen.test.tsx @@ -10,12 +10,11 @@ import { afterEach(cleanupScreens); // addSubcommands reads the resources off the compiled Commander tree, so a -// `project add` resource added later is covered without editing this file. +// An `add` resource added later is covered without editing this file. // `help` is Commander's own, not one of ours. function addSubcommands(): string[] { const root = compiledRootCommand(); - const project = root.commands.find((command) => command.name() === "project")!; - const add = project.commands.find((command) => command.name() === "add")!; + const add = root.commands.find((command) => command.name() === "add")!; return add.commands.map((command) => command.name()).filter((name) => name !== "help"); } @@ -25,7 +24,7 @@ const WITH_SCREENS = ["runtime", "memory"]; describe("project add menu", () => { test("lists every add resource", async () => { - const r = renderScreen("/agentcore/project/add"); + const r = renderScreen("/agentcore/add"); await waitForText(r.lastFrame, "add project resources"); const frame = r.lastFrame()!; @@ -36,7 +35,7 @@ describe("project add menu", () => { }); test("the resources with a wizard are listed above the divider", async () => { - const r = renderScreen("/agentcore/project/add"); + const r = renderScreen("/agentcore/add"); await waitForText(r.lastFrame, "command line only"); const { screens, cliOnly } = menuEntries(r.lastFrame()!); @@ -49,25 +48,25 @@ describe("project add menu", () => { r.unmount(); }); - test("is reachable from the project menu", async () => { - const r = renderScreen("/agentcore/project"); + test("is reachable from the root menu", async () => { + const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "agentcore → project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); await r.write("add"); await waitForText(r.lastFrame, "❯ add"); await r.press("return"); - await waitForText(r.lastFrame, "agentcore → project → add"); + await waitForText(r.lastFrame, "agentcore → add"); r.unmount(); }); - test("esc returns to the project menu", async () => { - const r = renderScreen("/agentcore/project/add"); + test("esc returns to the root menu", async () => { + const r = renderScreen("/agentcore/add"); - await waitForText(r.lastFrame, "agentcore → project → add"); + await waitForText(r.lastFrame, "agentcore → add"); await r.press("escape"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); }); diff --git a/src/handlers/project/add/evaluator/code-based/index.test.ts b/src/handlers/project/add/evaluator/code-based/index.test.ts index 691bc73908..2b1052557e 100644 --- a/src/handlers/project/add/evaluator/code-based/index.test.ts +++ b/src/handlers/project/add/evaluator/code-based/index.test.ts @@ -22,7 +22,7 @@ async function run(args: string[]) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io }; } diff --git a/src/handlers/project/add/evaluator/llm-as-a-judge/index.test.ts b/src/handlers/project/add/evaluator/llm-as-a-judge/index.test.ts index b4a0ca3b92..ad0c3f5323 100644 --- a/src/handlers/project/add/evaluator/llm-as-a-judge/index.test.ts +++ b/src/handlers/project/add/evaluator/llm-as-a-judge/index.test.ts @@ -23,7 +23,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/gateway-test-support.ts b/src/handlers/project/add/gateway-test-support.ts index 5e1ce042b4..3950930e39 100644 --- a/src/handlers/project/add/gateway-test-support.ts +++ b/src/handlers/project/add/gateway-test-support.ts @@ -30,7 +30,7 @@ export function createGatewayProjectTestHarness(directoryPrefix: string) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return io; } diff --git a/src/handlers/project/add/harness/index.test.ts b/src/handlers/project/add/harness/index.test.ts index 0c2d082f80..087fa2acdd 100644 --- a/src/handlers/project/add/harness/index.test.ts +++ b/src/handlers/project/add/harness/index.test.ts @@ -24,7 +24,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/memory/index.test.ts b/src/handlers/project/add/memory/index.test.ts index 91b56989eb..b9336e3ab7 100644 --- a/src/handlers/project/add/memory/index.test.ts +++ b/src/handlers/project/add/memory/index.test.ts @@ -33,7 +33,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient; isTTY?: boole globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/memory/memory.screen.test.tsx b/src/handlers/project/add/memory/memory.screen.test.tsx index aa3f51ae05..e5ab1ae675 100644 --- a/src/handlers/project/add/memory/memory.screen.test.tsx +++ b/src/handlers/project/add/memory/memory.screen.test.tsx @@ -54,7 +54,7 @@ describe("project add memory wizard", () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: Infinity, staleTime: Infinity } }, }); - const r = renderScreen("/agentcore/project/add/memory", { queryClient }); + const r = renderScreen("/agentcore/add/memory", { queryClient }); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("orders_memory"); @@ -104,7 +104,7 @@ describe("project add memory wizard", () => { test("selecting no strategy adds a memory that only keeps raw events", async () => { const projectRoot = await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("events_only"); @@ -129,7 +129,7 @@ describe("project add memory wizard", () => { test("the strategies chosen are ordered as the list draws them", async () => { const projectRoot = await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("ordered_memory"); @@ -158,7 +158,7 @@ describe("project add memory wizard", () => { test("a blank name is refused", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.press("return"); @@ -170,7 +170,7 @@ describe("project add memory wizard", () => { test("a name that breaks the schema's pattern is rejected as it is typed", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("1memory"); @@ -181,7 +181,7 @@ describe("project add memory wizard", () => { test("retention outside the service's range keeps the step", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("short_memory"); @@ -201,7 +201,7 @@ describe("project add memory wizard", () => { test("retention that is not a number keeps the step", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("odd_memory"); @@ -222,7 +222,7 @@ describe("project add memory wizard", () => { // The name is taken, so addResource refuses it — the realistic failure, and // one the user can fix without starting over. await run(["add", "memory", "--name", "orders_memory"]); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.write("orders_memory"); @@ -245,7 +245,7 @@ describe("project add memory wizard", () => { test("esc on the first step returns to the add menu", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/memory"); + const r = renderScreen("/agentcore/add/memory"); await waitForText(r.lastFrame, "what should this Memory be called?"); await r.press("escape"); @@ -271,7 +271,7 @@ describe("project add memory dispatch", () => { async function routeError(io: AppIO, args: string[]): Promise { return buildRoot(io) - .route(["node", "agentcore", "project", "add", "memory", ...args]) + .route(["node", "agentcore", "add", "memory", ...args]) .then( () => undefined, (caught: unknown) => caught, @@ -283,7 +283,7 @@ describe("project add memory dispatch", () => { const { streams, stdin } = ttyTestIO(); const outcome = buildRoot(streams.io) - .route(["node", "agentcore", "project", "add", "memory"]) + .route(["node", "agentcore", "add", "memory"]) .then( () => ({ ok: true as const }), (error: unknown) => ({ ok: false as const, error }), @@ -343,7 +343,6 @@ describe("project add memory dispatch", () => { await buildRoot(streams.io).route([ "node", "agentcore", - "project", "add", "memory", "--name", diff --git a/src/handlers/project/add/online-eval/index.test.ts b/src/handlers/project/add/online-eval/index.test.ts index f48b67583b..69f0597de2 100644 --- a/src/handlers/project/add/online-eval/index.test.ts +++ b/src/handlers/project/add/online-eval/index.test.ts @@ -22,7 +22,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/online-insight/index.test.ts b/src/handlers/project/add/online-insight/index.test.ts index 6fea31485f..7a54e87ae3 100644 --- a/src/handlers/project/add/online-insight/index.test.ts +++ b/src/handlers/project/add/online-insight/index.test.ts @@ -22,7 +22,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/payment-test-support.ts b/src/handlers/project/add/payment-test-support.ts index c3f2ba85f4..534014710d 100644 --- a/src/handlers/project/add/payment-test-support.ts +++ b/src/handlers/project/add/payment-test-support.ts @@ -18,7 +18,7 @@ export function createPaymentProjectTestHarness(directoryPrefix: string) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return io; } diff --git a/src/handlers/project/add/runtime-endpoint/index.test.ts b/src/handlers/project/add/runtime-endpoint/index.test.ts index 25fdd9fd50..451354be1b 100644 --- a/src/handlers/project/add/runtime-endpoint/index.test.ts +++ b/src/handlers/project/add/runtime-endpoint/index.test.ts @@ -22,7 +22,7 @@ async function run(args: string[], opts?: { isTTY?: boolean }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io }; } diff --git a/src/handlers/project/add/runtime/index.test.ts b/src/handlers/project/add/runtime/index.test.ts index 072ae5fca6..3e7652a306 100644 --- a/src/handlers/project/add/runtime/index.test.ts +++ b/src/handlers/project/add/runtime/index.test.ts @@ -24,7 +24,7 @@ async function run(args: string[], opts?: { core?: TestCoreClient }) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/add/runtime/runtime.screen.test.tsx b/src/handlers/project/add/runtime/runtime.screen.test.tsx index 86863a4a54..91d080daf2 100644 --- a/src/handlers/project/add/runtime/runtime.screen.test.tsx +++ b/src/handlers/project/add/runtime/runtime.screen.test.tsx @@ -63,7 +63,7 @@ describe("project add runtime wizard", () => { const queryClient = new QueryClient({ defaultOptions: { queries: { retry: false, gcTime: Infinity, staleTime: Infinity } }, }); - const r = renderScreen("/agentcore/project/add/runtime", { queryClient }); + const r = renderScreen("/agentcore/add/runtime", { queryClient }); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write("orders_agent"); @@ -107,7 +107,7 @@ describe("project add runtime wizard", () => { test("scaffolds the template the user picks", async () => { const projectRoot = await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write("packing_agent"); @@ -134,7 +134,7 @@ describe("project add runtime wizard", () => { test("a blank name is refused", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.press("return"); @@ -146,7 +146,7 @@ describe("project add runtime wizard", () => { test("a name that breaks the schema's pattern is rejected as it is typed", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); // No enter: the name is checked while it is being typed, so the rule is @@ -159,7 +159,7 @@ describe("project add runtime wizard", () => { test("accepts a 48-character name", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write("a".repeat(48)); @@ -171,7 +171,7 @@ describe("project add runtime wizard", () => { test("rejects a name longer than 48 characters", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write("a".repeat(49)); @@ -182,7 +182,7 @@ describe("project add runtime wizard", () => { test("a name that is only valid once trimmed is refused, not silently trimmed", async () => { const projectRoot = await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write(" orders_agent "); @@ -201,7 +201,7 @@ describe("project add runtime wizard", () => { // The name is taken, so addResource refuses it — the realistic failure, and // one the user can fix without starting over. await run(["add", "runtime", "--name", "orders_agent"]); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.write("orders_agent"); @@ -225,7 +225,7 @@ describe("project add runtime wizard", () => { test("esc on the first step returns to the add menu", async () => { await inProject(); - const r = renderScreen("/agentcore/project/add/runtime"); + const r = renderScreen("/agentcore/add/runtime"); await waitForText(r.lastFrame, "what should this runtime be called?"); await r.press("escape"); @@ -251,7 +251,7 @@ describe("project add runtime dispatch", () => { async function routeError(io: AppIO, args: string[]): Promise { return buildRoot(io) - .route(["node", "agentcore", "project", "add", "runtime", ...args]) + .route(["node", "agentcore", "add", "runtime", ...args]) .then( () => undefined, (caught: unknown) => caught, @@ -265,7 +265,7 @@ describe("project add runtime dispatch", () => { // outcome never rejects, so a mid-pump failure cannot trip bun's // unhandled-rejection detection before the final assertion. const outcome = buildRoot(streams.io) - .route(["node", "agentcore", "project", "add", "runtime"]) + .route(["node", "agentcore", "add", "runtime"]) .then( () => ({ ok: true as const }), (error: unknown) => ({ ok: false as const, error }), @@ -296,7 +296,7 @@ describe("project add runtime dispatch", () => { const io = testIO(); const error = await buildRoot(io.io) - .route(["node", "agentcore", "project", "add"]) + .route(["node", "agentcore", "add"]) .then( () => undefined, (caught: unknown) => caught, @@ -340,7 +340,6 @@ describe("project add runtime dispatch", () => { await buildRoot(streams.io).route([ "node", "agentcore", - "project", "add", "runtime", "--name", diff --git a/src/handlers/project/build/index.test.ts b/src/handlers/project/build/index.test.ts index 0e74aabce6..80e62eab13 100644 --- a/src/handlers/project/build/index.test.ts +++ b/src/handlers/project/build/index.test.ts @@ -43,7 +43,7 @@ function testBuildCommand(options: TestBuildOptions = {}) { return { io, - run: (args: string[] = []) => root.route(["node", "agentcore", "project", "build", ...args]), + run: (args: string[] = []) => root.route(["node", "agentcore", "build", ...args]), }; } diff --git a/src/handlers/project/buildDeploy.screen.test.tsx b/src/handlers/project/buildDeploy.screen.test.tsx index ffe4dee297..5931542ea0 100644 --- a/src/handlers/project/buildDeploy.screen.test.tsx +++ b/src/handlers/project/buildDeploy.screen.test.tsx @@ -100,13 +100,13 @@ describe("project build screen", () => { const { backend } = fakeBackend(); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core); - const r = renderScreen("/agentcore/project/build", { core }); + const r = renderScreen("/agentcore/build", { core }); // Both steps settle to ✓, as the inline TaskList leaves them on the // command line; the finished steps' output tails collapse. await waitForText(r.lastFrame, "✔ Built project 'orders'"); const frame = r.lastFrame()!; - expect(frame).toContain("agentcore → project → build"); + expect(frame).toContain("agentcore → build"); // No frame — not even the first — advertised a question. expect(r.frames.some((painted) => painted.includes("(y/N)") || painted.includes("y/n"))).toBe( false, @@ -114,11 +114,11 @@ describe("project build screen", () => { expect(frame).toContain("✓ Synthesizing CloudFormation templates"); expect(frame).toContain("✓ Deploying stack"); expect(frame).not.toContain("cdk synth"); - expect(frame).toContain("agentcore project deploy"); + expect(frame).toContain("agentcore deploy"); - // Enter stays in the TUI: back to the project menu. + // Enter stays in the TUI: back to the root menu. await r.press("return"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); @@ -126,29 +126,29 @@ describe("project build screen", () => { const { backend } = fakeBackend({ failure: new Error("synth exploded") }); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core); - const r = renderScreen("/agentcore/project/build", { core }); + const r = renderScreen("/agentcore/build", { core }); await waitForText(r.lastFrame, "✗ synth exploded"); const frame = r.lastFrame()!; expect(frame).toContain("✓ Synthesizing CloudFormation templates"); expect(frame).toContain("✕ Deploying stack"); expect(frame).toContain("CREATE_IN_PROGRESS | AWS::IAM::Role"); - // With no confirmation to return to, esc leaves for the project menu + // With no confirmation to return to, esc leaves for the root menu // rather than running the build again. await r.press("escape"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); test("reports the CLI's own guidance outside a project", async () => { cleanups.push((await inTempDirectory()).cleanup); - const r = renderScreen("/agentcore/project/build"); + const r = renderScreen("/agentcore/build"); await waitForFlatText(r.lastFrame, "No AgentCore project found"); - expect(flatFrame(r.lastFrame)).toContain("agentcore project create"); + expect(flatFrame(r.lastFrame)).toContain("agentcore create"); // esc is a way off the error, not just ctrl+c. await r.press("escape"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); }); @@ -158,7 +158,7 @@ describe("project deploy screen", () => { const { backend, deploys } = fakeBackend(); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); // A project with resources is not asked anything, as on the command line. await waitForText(r.lastFrame, "✔ Deployed project 'orders' to target 'default'"); @@ -190,7 +190,7 @@ describe("project deploy screen", () => { if (attempts === 1) throw new Error("aws-targets.json is unreadable"); return listTargets(project); }; - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "✗ aws-targets.json is unreadable"); expect(r.lastFrame()).toContain("[r] retry"); @@ -203,17 +203,17 @@ describe("project deploy screen", () => { r.unmount(); }); - test("esc leaves a target-loading failure for the project menu", async () => { + test("esc leaves a target-loading failure for the root menu", async () => { const core = new TestCoreClient({ backends: { CDK: fakeBackend().backend } }); await inProject(core); core.projectManager.listTargets = async () => { throw new Error("aws-targets.json is unreadable"); }; - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "✗ aws-targets.json is unreadable"); await r.press("escape"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); @@ -221,7 +221,7 @@ describe("project deploy screen", () => { const { backend, deploys } = fakeBackend(); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core, { staging: true }); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "choose a deployment target"); const picker = flatFrame(r.lastFrame); @@ -245,12 +245,12 @@ describe("project deploy screen", () => { queries: { retry: false, gcTime: Infinity, staleTime: 0 }, }, }); - const r = renderScreen("/agentcore/project/deploy", { core, queryClient }); + const r = renderScreen("/agentcore/deploy", { core, queryClient }); await waitForText(r.lastFrame, "✔ Deployed project 'orders' to target 'default'"); expect(deploys).toHaveLength(1); await r.press("return"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); await writeFile( join(projectRoot, "agentcore", "aws-targets.json"), @@ -273,12 +273,12 @@ describe("project deploy screen", () => { queries: { retry: false, gcTime: Infinity, staleTime: 0 }, }, }); - const r = renderScreen("/agentcore/project/deploy", { core, queryClient }); + const r = renderScreen("/agentcore/deploy", { core, queryClient }); await waitForText(r.lastFrame, "✔ Deployed project 'orders' to target 'default'"); expect(deploys).toHaveLength(1); await r.press("return"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); await writeFile( join(projectRoot, "agentcore", "agentcore.json"), @@ -299,7 +299,7 @@ describe("project deploy screen", () => { resolveAccount: async () => "887863153624", }); await inProject(core, { targets: false }); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "✔ Deployed project 'orders' to target 'default'"); const frame = flatFrame(r.lastFrame); @@ -313,7 +313,7 @@ describe("project deploy screen", () => { const { backend, deploys } = fakeBackend({ result: { outputs: {}, tornDown: true } }); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core, { empty: true }); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForFlatText(r.lastFrame, "declares no resources to deploy"); // Confirm lays its (y/N) inline, so the question wraps around it. @@ -334,7 +334,7 @@ describe("project deploy screen", () => { const { backend } = fakeBackend({ result: { outputs: {}, tornDown: true } }); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "✔ Removed project 'orders' from target 'default'"); expect(r.lastFrame()).not.toContain("Deployed project"); @@ -345,7 +345,7 @@ describe("project deploy screen", () => { const { backend } = fakeBackend({ failure: new Error("stack rolled back") }); const core = new TestCoreClient({ backends: { CDK: backend } }); await inProject(core); - const r = renderScreen("/agentcore/project/deploy", { core }); + const r = renderScreen("/agentcore/deploy", { core }); await waitForText(r.lastFrame, "✗ stack rolled back"); expect(r.lastFrame()).toContain("✕ Deploying stack"); diff --git a/src/handlers/project/create/create.screen.test.tsx b/src/handlers/project/create/create.screen.test.tsx index 40315adbed..4f98030a19 100644 --- a/src/handlers/project/create/create.screen.test.tsx +++ b/src/handlers/project/create/create.screen.test.tsx @@ -52,7 +52,7 @@ describe("project create wizard", () => { cleanups.push(cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("DemoApp"); @@ -92,7 +92,7 @@ describe("project create wizard", () => { // Success: next steps point at the new directory and deploy. await waitForText(r.lastFrame, "✔ project created in ./DemoApp", 5000); expect(r.lastFrame()).toContain("cd DemoApp"); - expect(r.lastFrame()).toContain("agentcore project deploy"); + expect(r.lastFrame()).toContain("agentcore deploy"); // The manager received exactly the input the flag-driven handler builds // for `project create --name DemoApp`. @@ -119,7 +119,7 @@ describe("project create wizard", () => { cleanups.push((await inTempDirectory()).cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("TunedApp"); @@ -155,7 +155,7 @@ describe("project create wizard", () => { cleanups.push(cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); const apiKeyArn = "arn:aws:bedrock-agentcore:us-east-1:123456789012:token-vault/default/apikeycredentialprovider/OpenAIKey"; @@ -217,7 +217,7 @@ describe("project create wizard", () => { }, 10000); test("switching providers preserves each provider's model input", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); await r.write("ProviderApp"); @@ -239,7 +239,7 @@ describe("project create wizard", () => { }); test("reveals model fields only after enter and hides them again on escape", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); await r.write("ModelApp"); @@ -265,7 +265,7 @@ describe("project create wizard", () => { }); test("the model picker remains readable in an 80x24 terminal", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await r.resize(80, 24); await waitForText(r.lastFrame, "name your project"); @@ -277,7 +277,7 @@ describe("project create wizard", () => { const frame = r.lastFrame()!; const lines = frame.split("\n"); - expect(lines[0]).toContain("agentcore → project → create"); + expect(lines[0]).toContain("agentcore → create"); expect(lines[1]).toBe("─".repeat(80)); expect(lines[2]).toContain("✓ name"); expect(frame).toContain("● bedrock"); @@ -297,7 +297,7 @@ describe("project create wizard", () => { cleanups.push(cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("StrandsApp"); @@ -348,7 +348,7 @@ describe("project create wizard", () => { cleanups.push(cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("HelloApp"); @@ -386,7 +386,7 @@ describe("project create wizard", () => { cleanups.push((await inTempDirectory()).cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("LangChainApp"); @@ -424,7 +424,7 @@ describe("project create wizard", () => { cleanups.push(cleanup); const core = new TestCoreClient(); const inputs = spyOnCreate(core); - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("EmptyApp"); @@ -450,7 +450,7 @@ describe("project create wizard", () => { }, 10000); test("the name step shows the schema's messages and blocks continuing", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); // Submitting an empty name surfaces the schema's required message. @@ -467,7 +467,7 @@ describe("project create wizard", () => { }); test("a reserved name is rejected with the schema's message", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); await r.write("bedrock"); @@ -478,7 +478,7 @@ describe("project create wizard", () => { }); test("a pasted chunk with a trailing return keeps the name clean", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); // A terminal paste (or keystrokes coalesced under load) arrives as one @@ -497,7 +497,7 @@ describe("project create wizard", () => { }); test("esc steps back through the flow and leaves from the first step", async () => { - const r = renderScreen("/agentcore/project/create"); + const r = renderScreen("/agentcore/create"); await waitForText(r.lastFrame, "name your project"); await r.write("DemoApp"); @@ -505,14 +505,14 @@ describe("project create wizard", () => { await waitForText(r.lastFrame, "what kind of agent to start with?"); await r.press("escape"); await waitForText(r.lastFrame, "name your project"); - // Esc on the first step lands on the project menu. + // Esc on the first step lands on the root menu. await r.press("escape"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); r.unmount(); }); - test("selecting create from the project menu opens the wizard", async () => { - const r = renderScreen("/agentcore/project"); + test("selecting create from the root menu opens the wizard", async () => { + const r = renderScreen("/agentcore"); // `create` is the first menu item, so it is already selected. await waitForText(r.lastFrame, "❯ create"); @@ -539,7 +539,7 @@ describe("project create wizard", () => { throw new Error("stopped"); })(); }; - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("DemoApp"); @@ -582,7 +582,7 @@ describe("project create wizard", () => { throw new Error("'git' was not found on your PATH."); })(); }; - const r = renderScreen("/agentcore/project/create", { core }); + const r = renderScreen("/agentcore/create", { core }); await waitForText(r.lastFrame, "name your project"); await r.write("DemoApp"); @@ -624,7 +624,7 @@ describe("project create wizard", () => { const deep = join(path, "n".repeat(120)); await mkdir(deep); process.chdir(deep); - const r = renderScreen("/agentcore/project/create", { platform: "win32" }); + const r = renderScreen("/agentcore/create", { platform: "win32" }); await waitForText(r.lastFrame, "name your project"); await r.write("DemoApp"); @@ -663,7 +663,7 @@ describe("project create dispatch", () => { // outcome never rejects, so a mid-pump failure cannot trip bun's // unhandled-rejection detection before the final assertion. - const outcome = root.route(["node", "agentcore", "project", "create"]).then( + const outcome = root.route(["node", "agentcore", "create"]).then( () => ({ ok: true as const }), (error: unknown) => ({ ok: false as const, error }), ); @@ -702,7 +702,7 @@ describe("project create dispatch", () => { })(); const root = buildRoot(streams.io, core); - const outcome = root.route(["node", "agentcore", "project", "create"]); + const outcome = root.route(["node", "agentcore", "create"]); await waitFor(() => streams.stdout().includes("name your project")); stdin.write("DemoApp"); @@ -728,7 +728,7 @@ describe("project create dispatch", () => { const root = buildRoot(io.io); const error: unknown = await root - .route(["node", "agentcore", "project", "create"]) + .route(["node", "agentcore", "create"]) .then(() => undefined) .catch((caught: unknown) => caught); @@ -741,7 +741,7 @@ describe("project create dispatch", () => { const root = buildRoot(streams.io); const error: unknown = await root - .route(["node", "agentcore", "project", "create", "--skip-git"]) + .route(["node", "agentcore", "create", "--skip-git"]) .then(() => undefined) .catch((caught: unknown) => caught); @@ -754,7 +754,7 @@ describe("project create dispatch", () => { const root = buildRoot(streams.io); const error: unknown = await root - .route(["node", "agentcore", "project", "create", "--json"]) + .route(["node", "agentcore", "create", "--json"]) .then(() => undefined) .catch((caught: unknown) => caught); @@ -771,7 +771,6 @@ describe("project create dispatch", () => { await root.route([ "node", "agentcore", - "project", "create", "--name", "FlagApp", diff --git a/src/handlers/project/deploy/index.test.ts b/src/handlers/project/deploy/index.test.ts index 896eb6a954..fb3fccf416 100644 --- a/src/handlers/project/deploy/index.test.ts +++ b/src/handlers/project/deploy/index.test.ts @@ -116,7 +116,7 @@ function testDeployCommand( return { ...fake, io, - run: (args: string[] = []) => root.route(["node", "agentcore", "project", "deploy", ...args]), + run: (args: string[] = []) => root.route(["node", "agentcore", "deploy", ...args]), }; } diff --git a/src/handlers/project/export/harness.test.ts b/src/handlers/project/export/harness.test.ts index e14b1f89b8..5a91a3286d 100644 --- a/src/handlers/project/export/harness.test.ts +++ b/src/handlers/project/export/harness.test.ts @@ -34,8 +34,8 @@ function testExportCommand() { /** IO captured for the most recent invocation. */ io: undefined as unknown as ReturnType, core, - project: (args: string[]) => route(["project", ...args]), - run: (args: string[] = []) => route(["project", "export", "harness", ...args]), + project: (args: string[]) => route(args), + run: (args: string[] = []) => route(["export", "harness", ...args]), }; return subject; } diff --git a/src/handlers/project/invoke/index.test.tsx b/src/handlers/project/invoke/index.test.tsx index 42c33eb691..b8125865db 100644 --- a/src/handlers/project/invoke/index.test.tsx +++ b/src/handlers/project/invoke/index.test.tsx @@ -168,7 +168,7 @@ async function routedCommand( logger: createSilentLogger(), globalConfigAccessor: new TestGlobalConfigAccessor(), }); - const route = () => root.route(["node", "agentcore", "project", "invoke", ...args]); + const route = () => root.route(["node", "agentcore", "invoke", ...args]); return { core, io, resolved, route }; } @@ -401,7 +401,7 @@ describe("project invoke", () => { expect(code).toBe(ExitCode.FAILURE); expect(errors.join("\n")).toContain(`Could not reach local dev server on port ${port}`); expect(errors.join("\n")).toContain( - `agentcore project dev --mode headless --agent --port ${port}`, + `agentcore dev --mode headless --agent --port ${port}`, ); } finally { errorLog.mockRestore(); @@ -711,6 +711,6 @@ describe("project invoke", () => { await handler.defaultHandler()!.handle(context(project!), {}, {}); - expect(launches).toEqual(["/agentcore/project/invoke"]); + expect(launches).toEqual(["/agentcore/invoke"]); }); }); diff --git a/src/handlers/project/invoke/invoke.screen.test.tsx b/src/handlers/project/invoke/invoke.screen.test.tsx index a7c52a7166..f93760d815 100644 --- a/src/handlers/project/invoke/invoke.screen.test.tsx +++ b/src/handlers/project/invoke/invoke.screen.test.tsx @@ -112,7 +112,7 @@ function core( describe("project invoke picker", () => { test("lists only resources present in the deployed target", async () => { - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: core([ { resourceType: "harness", @@ -129,15 +129,15 @@ describe("project invoke picker", () => { expect(screen.lastFrame()).not.toContain("checkout"); }); - test("esc returns to the project command menu", async () => { - const screen = renderScreen("/agentcore/project/invoke", { + test("esc returns to the root command menu", async () => { + const screen = renderScreen("/agentcore/invoke", { core: core(), withContext: (ctx) => ctx.withValue(ProjectKey, project), }); await waitForText(screen.lastFrame, "checkout"); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); expect(screen.lastFrame()).toContain("invoke"); }); @@ -148,7 +148,7 @@ describe("project invoke picker", () => { requested = target; return { resources: DEPLOYED_RESOURCES, target: STAGING }; }; - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -174,7 +174,7 @@ describe("project invoke picker", () => { value.projectManager.resolveDeployedResources = async () => { throw new Error("No deployment targets are configured for project 'orders'."); }; - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -182,35 +182,35 @@ describe("project invoke picker", () => { await waitForText(screen.lastFrame, "No deployment targets are configured"); expect(screen.lastFrame()).not.toContain("checkout"); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); }); test("reports the CLI's own guidance outside a project", async () => { const { path: directory, cleanup } = await inTempDirectory(); cleanups.push(cleanup); - const screen = renderScreen("/agentcore/project/invoke", { core: core() }); + const screen = renderScreen("/agentcore/invoke", { core: core() }); await waitForFlatText(screen.lastFrame, "No AgentCore project found"); const frame = flatFrame(screen.lastFrame); expect(frame).toContain(directory); - expect(frame).toContain("agentcore project create"); + expect(frame).toContain("agentcore create"); expect(frame).not.toContain("Resolving project"); // esc is a way off the error, not just ctrl+c. await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); }); - test("resolves the enclosing project when opened from the project menu", async () => { + test("resolves the enclosing project when opened from the root menu", async () => { const value = core(); value.projectManager.resolve = async () => project; - const screen = renderScreen("/agentcore/project/invoke", { core: value }); + const screen = renderScreen("/agentcore/invoke", { core: value }); await waitForText(screen.lastFrame, "checkout"); expect(screen.lastFrame()).toContain("support"); }); test("lists project Runtime and Harness resources", async () => { - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: core(), withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -226,7 +226,7 @@ describe("project invoke picker", () => { test("opens the selected Harness chat in the same TUI", async () => { const value = core(); - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); @@ -245,7 +245,7 @@ describe("project invoke picker", () => { test("uses the existing Runtime endpoint picker before its JSON console", async () => { const value = core(); - const screen = renderScreen("/agentcore/project/invoke", { + const screen = renderScreen("/agentcore/invoke", { core: value, withContext: (ctx) => ctx.withValue(ProjectKey, project), }); diff --git a/src/handlers/project/log/harness.test.tsx b/src/handlers/project/log/harness.test.tsx index c33fb43761..72e04735e3 100644 --- a/src/handlers/project/log/harness.test.tsx +++ b/src/handlers/project/log/harness.test.tsx @@ -86,16 +86,7 @@ function command(projectBackend: ProjectBackend) { core, io, run: (args: string[] = []) => - root.route([ - "bun", - "agentcore", - "project", - "log", - "harness", - ...args, - "--region", - "us-east-1", - ]), + root.route(["bun", "agentcore", "log", "harness", ...args, "--region", "us-east-1"]), }; } diff --git a/src/handlers/project/log/runtime.test.tsx b/src/handlers/project/log/runtime.test.tsx index 9a47c47cac..163f3ced9f 100644 --- a/src/handlers/project/log/runtime.test.tsx +++ b/src/handlers/project/log/runtime.test.tsx @@ -104,16 +104,7 @@ function command(projectBackend: ProjectBackend) { core, io, run: (args: string[] = []) => - root.route([ - "bun", - "agentcore", - "project", - "log", - "runtime", - ...args, - "--region", - "us-east-1", - ]), + root.route(["bun", "agentcore", "log", "runtime", ...args, "--region", "us-east-1"]), }; } diff --git a/src/handlers/project/project.screen.test.tsx b/src/handlers/project/project.screen.test.tsx index 948b9735c0..a85402e614 100644 --- a/src/handlers/project/project.screen.test.tsx +++ b/src/handlers/project/project.screen.test.tsx @@ -22,43 +22,42 @@ import { afterEach(cleanupScreens); -// projectSubcommands reads the project group's children off the compiled -// Commander tree, so tests driven by it cover any subcommand added later. -function projectSubcommands(): string[] { +// topLevelSubcommands reads the root's children off the compiled Commander +// tree, so tests driven by it cover any command added later. +function topLevelSubcommands(): string[] { const root = compiledRootCommand(); - const project = root.commands.find((command) => command.name() === "project")!; - return project.commands.map((command) => command.name()); + return root.commands.map((command) => command.name()); } -describe("project menu", () => { - test("lists every project subcommand", async () => { - const r = renderScreen("/agentcore/project"); +describe("root menu", () => { + test("lists every top-level command", async () => { + const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "manage an AgentCore project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); const frame = r.lastFrame()!; - for (const command of projectSubcommands()) { + for (const command of topLevelSubcommands()) { expect(frame).toContain(command); } r.unmount(); }); - test("is reachable from the root menu", async () => { + test("project commands are reachable from the root menu", async () => { const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "manage an AgentCore project"); - await r.write("project"); - await waitForText(r.lastFrame, "❯ project"); + await waitForText(r.lastFrame, "the platform for production AI agents"); + await r.write("add"); + await waitForText(r.lastFrame, "❯ add"); await r.press("return"); - await waitForText(r.lastFrame, "agentcore → project"); - expect(r.lastFrame()).toContain("create"); + await waitForText(r.lastFrame, "agentcore → add"); + expect(r.lastFrame()).toContain("runtime"); r.unmount(); }); test("esc returns to the root menu", async () => { - const r = renderScreen("/agentcore/project"); + const r = renderScreen("/agentcore/add"); - await waitForText(r.lastFrame, "agentcore → project"); + await waitForText(r.lastFrame, "agentcore → add"); await r.press("escape"); await waitForText(r.lastFrame, "the platform for production AI agents"); @@ -66,19 +65,19 @@ describe("project menu", () => { }); }); -// projectCommand resolves a compiled project subcommand by path, for reading +// command resolves a compiled command by path, for reading // the help the CLI-only screen must match. -function projectCommand(...path: string[]) { +function command(...path: string[]) { const root = compiledRootCommand(); - let command = root.commands.find((c) => c.name() === "project")!; - for (const name of path) command = command.commands.find((c) => c.name() === name)!; - return command; + let current = root; + for (const name of path) current = current.commands.find((c) => c.name() === name)!; + return current; } -describe("project menu: command-line-only subcommands", () => { +describe("root menu: command-line-only subcommands", () => { test("create and add runtime expose the shared registry-backed template help", () => { - const createDetails = commandParameterDetails(projectCommand("create"))!; - const addRuntimeDetails = commandParameterDetails(projectCommand("add", "runtime"))!; + const createDetails = commandParameterDetails(command("create"))!; + const addRuntimeDetails = commandParameterDetails(command("add", "runtime"))!; for (const name of RUNTIME_TEMPLATE_SHORTCUT_NAMES) { const description = RUNTIME_TEMPLATE_SHORTCUTS[name].description; @@ -92,14 +91,28 @@ describe("project menu: command-line-only subcommands", () => { }); test("are listed below a divider, after the ones with a screen", async () => { - const r = renderScreen("/agentcore/project"); + const r = renderScreen("/agentcore"); await waitForText(r.lastFrame, "command line only"); - const withScreens = ["create", "deploy", "invoke", "build", "status", "add", "remove"]; + const withScreens = [ + "create", + "add", + "remove", + "deploy", + "invoke", + "status", + "build", + "harness", + "identity", + "runtime", + "memory", + "gateway", + "eval", + ]; const { screens, cliOnly } = menuEntries(r.lastFrame()!); expect(screens.toSorted()).toEqual(withScreens.toSorted()); expect(cliOnly.toSorted()).toEqual( - projectSubcommands() + topLevelSubcommands() .filter((c) => !withScreens.includes(c)) .toSorted(), ); @@ -107,21 +120,21 @@ describe("project menu: command-line-only subcommands", () => { }); test("a group drills down to its leaves' help and back", async () => { - const r = renderScreen("/agentcore/project/add"); + const r = renderScreen("/agentcore/add"); - await waitForText(r.lastFrame, "agentcore → project → add"); + await waitForText(r.lastFrame, "agentcore → add"); await r.write("gateway"); await waitForText(r.lastFrame, "❯ gateway"); await r.press("return"); - await waitForText(r.lastFrame, "agentcore → project → add → gateway"); + await waitForText(r.lastFrame, "agentcore → add → gateway"); const frame = r.lastFrame()!.replace(/\s+/g, " "); expect(frame).toContain("this command runs from the command line"); - expect(frame).toContain("agentcore project add gateway [options]"); + expect(frame).toContain("agentcore add gateway [options]"); expect(frame).toContain("--authorizer-type"); await r.press("escape"); - await waitForText(r.lastFrame, "agentcore → project → add"); + await waitForText(r.lastFrame, "agentcore → add"); r.unmount(); }); @@ -129,7 +142,7 @@ describe("project menu: command-line-only subcommands", () => { // `add gateway-target` has ten options plus a long --target-configuration // write-up, which `--help` appends as "Parameter details"; at 80×24 most of // it is below the fold. - const r = renderScreen("/agentcore/project/add/gateway-target"); + const r = renderScreen("/agentcore/add/gateway-target"); await r.resize(80, 24); await waitForText(r.lastFrame, "this command runs from the command line"); expect(r.lastFrame()).not.toContain("curated Connector shortcuts"); @@ -137,7 +150,9 @@ describe("project menu: command-line-only subcommands", () => { // Scroll to the end: the write-up's last line is the last thing on the page. for (let i = 0; i < 80; i++) await r.press("down"); const bottom = r.lastFrame()!.replace(/\s+/g, " "); - expect(bottom).toContain("Use project add gateway-connector for curated Connector shortcuts."); + expect(bottom).toContain( + "Use agentcore add gateway-connector for curated Connector shortcuts.", + ); // …and the heading was on the way. expect(r.frames.some((frame) => frame.includes("Parameter details:"))).toBe(true); @@ -149,7 +164,7 @@ describe("project menu: command-line-only subcommands", () => { // These three exercise the help viewport, so they need a command-line-only // resource whose help is longer than the terminal: `add payment-manager`. test("growing the terminal after scrolling to the bottom pulls the content back into view", async () => { - const r = renderScreen("/agentcore/project/add/payment-manager"); + const r = renderScreen("/agentcore/add/payment-manager"); await r.resize(80, 24); await waitForText(r.lastFrame, "this command runs from the command line"); for (let i = 0; i < 80; i++) await r.press("down"); @@ -165,7 +180,7 @@ describe("project menu: command-line-only subcommands", () => { }); test("a key that fills its column still stands clear of its value", async () => { - const r = renderScreen("/agentcore/project/add/payment-manager"); + const r = renderScreen("/agentcore/add/payment-manager"); await r.resize(40, 60); // Narrow enough that the intro wraps and the key column hits its cap. await waitForFlatText(r.lastFrame, "this command runs from the command line"); @@ -179,7 +194,7 @@ describe("project menu: command-line-only subcommands", () => { }); test("every option is reachable on a small terminal", async () => { - const r = renderScreen("/agentcore/project/add/payment-manager"); + const r = renderScreen("/agentcore/add/payment-manager"); await r.resize(80, 24); await waitForText(r.lastFrame, "this command runs from the command line"); @@ -192,22 +207,22 @@ describe("project menu: command-line-only subcommands", () => { await r.press("down"); collect(); } - const compiled = projectCommand("add", "payment-manager"); + const compiled = command("add", "payment-manager"); for (const option of compiled.options) { if (option.long && option.long !== "--help") expect(seen).toContain(option.long); } r.unmount(); }); - test("an unknown project path retains the standard help fallback", async () => { - const r = renderScreen("/agentcore/project/no-such-command"); + test("an unknown top-level path retains the standard help fallback", async () => { + const r = renderScreen("/agentcore/no-such-command"); await waitForText(() => r.frames.join("\n"), "Usage:"); expect(r.frames.join("\n")).not.toContain("command line only"); r.unmount(); }); }); -describe("agentcore project (no subcommand)", () => { +describe("agentcore (no subcommand)", () => { // Exercises the real CLI entrypoint; the screen tests mount a path directly // and so never caught the missing default handler. // @@ -224,7 +239,7 @@ describe("agentcore project (no subcommand)", () => { globalConfigAccessor: new TestGlobalConfigAccessor(), }); - const caught: unknown = await root.route(["node", "agentcore", "project"]).then( + const caught: unknown = await root.route(["node", "agentcore"]).then( () => undefined, (error: unknown) => error, ); @@ -242,7 +257,7 @@ describe("agentcore project (no subcommand)", () => { globalConfigAccessor: new TestGlobalConfigAccessor(), }); - await root.route(["node", "agentcore", "project", "--json"]); + await root.route(["node", "agentcore", "--json"]); expect(io.stdout()).toContain("Usage:"); expect(io.stdout()).toContain("create"); diff --git a/src/handlers/project/project.test.ts b/src/handlers/project/project.test.ts index 66afc0712d..11558e9a16 100644 --- a/src/handlers/project/project.test.ts +++ b/src/handlers/project/project.test.ts @@ -28,7 +28,7 @@ async function run( logger: createSilentLogger(), platform: opts?.platform, }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } @@ -223,7 +223,7 @@ describe("project create", () => { expect(io.stderr()).toContain("Syncing Python dependencies with uv"); expect(io.stderr()).toContain("Initializing git repository"); expect(io.stderr()).toContain("Created project 'MyAgent' in ./MyAgent"); - expect(io.stderr()).toContain("Next steps:\n cd MyAgent\n agentcore project deploy"); + expect(io.stderr()).toContain("Next steps:\n cd MyAgent\n agentcore deploy"); }); test("--skip-install and --skip-git run no commands", async () => { diff --git a/src/handlers/project/remove/index.test.ts b/src/handlers/project/remove/index.test.ts index 80090c955a..c09beedec1 100644 --- a/src/handlers/project/remove/index.test.ts +++ b/src/handlers/project/remove/index.test.ts @@ -31,7 +31,7 @@ async function run(args: string[], ioOptions?: TestIOOptions) { globalConfigAccessor: new TestGlobalConfigAccessor(), logger: createSilentLogger(), }); - await root.route(["node", "agentcore", "project", ...args]); + await root.route(["node", "agentcore", ...args]); return { io, core }; } diff --git a/src/handlers/project/remove/remove.screen.test.tsx b/src/handlers/project/remove/remove.screen.test.tsx index 6102accc56..fe314c0799 100644 --- a/src/handlers/project/remove/remove.screen.test.tsx +++ b/src/handlers/project/remove/remove.screen.test.tsx @@ -166,7 +166,7 @@ describe("project remove screen", () => { resourceConfig: { name: "prod", version: 1 }, }, ]); - const r = render("/agentcore/project/remove", core, project); + const r = render("/agentcore/remove", core, project); await waitForText(r.lastFrame, "choose a resource to remove from project orders"); const frame = r.lastFrame()!; @@ -196,7 +196,7 @@ describe("project remove screen", () => { test("lists the resource types the project holds plus an all option", async () => { const core = new TestCoreClient(); const { project } = await createProject(core, POLICY); - const r = render("/agentcore/project/remove", core, project); + const r = render("/agentcore/remove", core, project); await waitForText(r.lastFrame, "choose a resource to remove from project orders"); const frame = r.lastFrame()!; @@ -211,23 +211,23 @@ describe("project remove screen", () => { const core = new TestCoreClient(); const { project } = await createProject(core); process.chdir(project.rootPath); // cd into the project, as a user would; no ProjectKey injected - const r = renderScreen("/agentcore/project/remove", { core }); + const r = renderScreen("/agentcore/remove", { core }); await waitForText(r.lastFrame, "choose a resource to remove from project orders"); expect(r.lastFrame()).not.toContain("No AgentCore project"); r.unmount(); }); - test("esc from the no-project screen returns to the project menu", async () => { + test("esc from the no-project screen returns to the root menu", async () => { const core = new TestCoreClient(); const root = await mkdtemp(join(tmpdir(), "agentcore-no-project-")); temporaryDirectories.push(root); process.chdir(root); - const r = renderScreen("/agentcore/project/remove", { core }); + const r = renderScreen("/agentcore/remove", { core }); await waitForText(r.lastFrame, "No AgentCore project found"); await r.press("escape"); - await waitForText(r.lastFrame, "agentcore → project"); + await waitForText(r.lastFrame, "agentcore"); r.unmount(); }); @@ -238,7 +238,7 @@ describe("project remove screen", () => { resourceType: "runtime", name: project.spec.runtimes[0]!.name, }); - const r = render("/agentcore/project/remove", core, empty); + const r = render("/agentcore/remove", core, empty); await waitForText(r.lastFrame, "This project has no resources to remove."); expect(r.lastFrame()).not.toContain("all"); @@ -251,7 +251,7 @@ describe("project remove screen", () => { test("an empty resource-type list advertises only esc/ctrl+c", async () => { const core = new TestCoreClient(); const { project } = await createProject(core); // only a scaffolded runtime, no harnesses - const r = render("/agentcore/project/remove/harness", core, project); + const r = render("/agentcore/remove/harness", core, project); await waitForText(r.lastFrame, "This project has no harness resources."); expect(r.lastFrame()).toContain("esc"); @@ -266,7 +266,7 @@ describe("project remove screen", () => { resourceType: "runtime", name: project.spec.runtimes[0]!.name, }); - const r = render("/agentcore/project/remove/all", core, empty); + const r = render("/agentcore/remove/all", core, empty); await waitForText(r.lastFrame, "This project has no resources to remove."); // Static message screen — only esc/ctrl+c are advertised. @@ -287,12 +287,12 @@ describe("project remove screen", () => { spec: { ...empty.spec, knowledgeBases: [{ name: "kb" }] as ProjectSpec["knowledgeBases"] }, }; - const picker = render("/agentcore/project/remove", core, withKb); + const picker = render("/agentcore/remove", core, withKb); await waitForText(picker.lastFrame, "choose a resource to remove from project orders"); expect(picker.lastFrame()).toContain("all"); picker.unmount(); - const confirm = render("/agentcore/project/remove/all", core, withKb); + const confirm = render("/agentcore/remove/all", core, withKb); await waitForText(confirm.lastFrame, "Remove every resource from project orders?"); expect(confirm.lastFrame()).toContain("knowledge base"); confirm.unmount(); @@ -306,13 +306,13 @@ describe("project remove screen", () => { })); const { project } = await createProject(core, memories); - const many = render("/agentcore/project/remove/memory", core, project); + const many = render("/agentcore/remove/memory", core, project); await waitForText(many.lastFrame, "choose a memory to remove"); expect(many.lastFrame()).toContain("page"); // ←→/hl page hint on a paged list many.unmount(); // The runtime list has a single entry, so no paging hint. - const few = render("/agentcore/project/remove/runtime", core, project); + const few = render("/agentcore/remove/runtime", core, project); await waitForText(few.lastFrame, "choose a runtime to remove"); expect(few.lastFrame()).not.toContain("page"); few.unmount(); @@ -321,7 +321,7 @@ describe("project remove screen", () => { test("the all row counts the sum of every resource", async () => { const core = new TestCoreClient(); const { project } = await createProject(core, POLICY); - const r = render("/agentcore/project/remove", core, project); + const r = render("/agentcore/remove", core, project); await waitForText(r.lastFrame, "all"); // `all` = the sum of the listed rows: 1 runtime + 1 policy-engine + 1 policy. @@ -332,7 +332,7 @@ describe("project remove screen", () => { test("selecting a type lists that type's resources", async () => { const core = new TestCoreClient(); const { project } = await createProject(core); - const r = render("/agentcore/project/remove", core, project); + const r = render("/agentcore/remove", core, project); await waitForText(r.lastFrame, "runtime"); await r.press("return"); @@ -344,7 +344,7 @@ describe("project remove screen", () => { test("esc on the resource list returns to the resource-type list", async () => { const core = new TestCoreClient(); const { project } = await createProject(core); - const r = render("/agentcore/project/remove/runtime", core, project); + const r = render("/agentcore/remove/runtime", core, project); await waitForText(r.lastFrame, "choose a runtime to remove"); await r.press("escape"); @@ -352,21 +352,21 @@ describe("project remove screen", () => { r.unmount(); }); - test("esc on the resource-type list returns to the project menu", async () => { + test("esc on the resource-type list returns to the root menu", async () => { const core = new TestCoreClient(); const { project } = await createProject(core); - const r = render("/agentcore/project/remove", core, project); + const r = render("/agentcore/remove", core, project); await waitForText(r.lastFrame, "choose a resource to remove from project orders"); await r.press("escape"); - await waitForText(r.lastFrame, "agentcore → project"); + await waitForText(r.lastFrame, "agentcore"); r.unmount(); }); test("confirming a removal deletes the resource from the spec", async () => { const core = new TestCoreClient(); const { project, specPath } = await createProject(core); - const r = render("/agentcore/project/remove/runtime/0", core, project); + const r = render("/agentcore/remove/runtime/0", core, project); await waitForText(r.lastFrame, `Remove runtime '${RUNTIME}' from project orders?`); expect(r.lastFrame()).toContain("(y/N)"); @@ -386,7 +386,7 @@ describe("project remove screen", () => { const { project } = await createProject(core, POLICY); // ProjectKey is set in context (as the command wiring does): useProject uses // it as initialData and never refetches, so the removal must update the cache. - const r = render("/agentcore/project/remove/runtime/0", core, project); + const r = render("/agentcore/remove/runtime/0", core, project); await waitForText(r.lastFrame, `Remove runtime '${RUNTIME}' from project orders?`); await r.write("y"); @@ -408,7 +408,7 @@ describe("project remove screen", () => { const core = new TestCoreClient(); const { project } = await createProject(core, POLICY); process.chdir(project.rootPath); // cwd-resolve path, so the selector refreshes from disk - const r = renderScreen("/agentcore/project/remove/runtime/0", { core }); + const r = renderScreen("/agentcore/remove/runtime/0", { core }); await waitForText(r.lastFrame, `Remove runtime '${RUNTIME}' from project orders?`); await r.write("y"); @@ -431,7 +431,7 @@ describe("project remove screen", () => { test("declining leaves the resource in place", async () => { const core = new TestCoreClient(); const { project, specPath } = await createProject(core); - const r = render("/agentcore/project/remove/runtime/0", core, project); + const r = render("/agentcore/remove/runtime/0", core, project); await waitForText(r.lastFrame, `Remove runtime '${RUNTIME}' from project orders?`); await r.write("n"); @@ -444,7 +444,7 @@ describe("project remove screen", () => { test("lists a nested resource with its parent and removes it", async () => { const core = new TestCoreClient(); const { project, specPath } = await createProject(core, POLICY); - const list = render("/agentcore/project/remove/policy", core, project); + const list = render("/agentcore/remove/policy", core, project); await waitForText(list.lastFrame, "choose a policy to remove"); const frame = list.lastFrame()!; @@ -453,7 +453,7 @@ describe("project remove screen", () => { expect(frame).toContain("denyAll"); // policy name list.unmount(); - const confirm = render("/agentcore/project/remove/policy/0", core, project); + const confirm = render("/agentcore/remove/policy/0", core, project); await waitForText(confirm.lastFrame, "Remove policy 'denyAll' from project orders?"); expect(confirm.lastFrame()).toContain("guard"); // parent shown in the summary await confirm.write("y"); @@ -467,7 +467,7 @@ describe("project remove screen", () => { test("the remove-all summary itemizes the resource types being removed", async () => { const core = new TestCoreClient(); const { project } = await createProject(core, POLICY); - const r = render("/agentcore/project/remove/all", core, project); + const r = render("/agentcore/remove/all", core, project); await waitForText(r.lastFrame, "Remove every resource from project orders?"); const frame = r.lastFrame()!; @@ -480,7 +480,7 @@ describe("project remove screen", () => { test("enter after remove-all refreshes the list when the project is pinned in context", async () => { const core = new TestCoreClient(); const { project } = await createProject(core, POLICY); - const r = render("/agentcore/project/remove/all", core, project); // ProjectKey pinned in context + const r = render("/agentcore/remove/all", core, project); // ProjectKey pinned in context await waitForText(r.lastFrame, "Remove every resource from project orders?"); await r.write("y"); @@ -498,7 +498,7 @@ describe("project remove screen", () => { test("removing all empties every resource collection", async () => { const core = new TestCoreClient(); const { project, specPath } = await createProject(core, POLICY); - const r = render("/agentcore/project/remove/all", core, project); + const r = render("/agentcore/remove/all", core, project); await waitForText(r.lastFrame, "Remove every resource from project orders?"); await r.write("y"); diff --git a/src/handlers/project/status/index.test.ts b/src/handlers/project/status/index.test.ts index c8414663fc..b7381322cf 100644 --- a/src/handlers/project/status/index.test.ts +++ b/src/handlers/project/status/index.test.ts @@ -58,7 +58,7 @@ function statusCommand(backend: ProjectBackend, io = testIO()) { return { io, json: () => JSON.parse(io.stdout()), - run: (args: string[] = []) => root.route(["node", "agentcore", "project", "status", ...args]), + run: (args: string[] = []) => root.route(["node", "agentcore", "status", ...args]), }; } @@ -281,7 +281,7 @@ describe("project status handler", () => { await inProject({ memories: [memory("shortTerm")] }, []); await expect(subject.run()).rejects.toThrow( - /No deployment targets are configured for project 'orders'\. Please deploy your project using 'agentcore project deploy'\./, + /No deployment targets are configured for project 'orders'\. Please deploy your project using 'agentcore deploy'\./, ); expect(subject.targets).toEqual([]); }); diff --git a/src/handlers/project/status/status.screen.test.tsx b/src/handlers/project/status/status.screen.test.tsx index 0c4d63d89a..27eee3596b 100644 --- a/src/handlers/project/status/status.screen.test.tsx +++ b/src/handlers/project/status/status.screen.test.tsx @@ -103,7 +103,7 @@ function core( } function renderStatus(value: TestCoreClient, seed: Project = RUNTIME_PROJECT) { - return renderScreen("/agentcore/project/status", { + return renderScreen("/agentcore/status", { core: value, withContext: (ctx) => ctx.withValue(ProjectKey, seed), }); @@ -241,7 +241,7 @@ describe("project status screen", () => { await screen.press("return"); await waitForText(screen.lastFrame, "credential svc-key has no detail view."); - expect(screen.lastFrame()).toContain("agentcore → project → status"); + expect(screen.lastFrame()).toContain("agentcore → status"); }); test("left and right arrows collapse and expand an agent group", async () => { @@ -255,12 +255,12 @@ describe("project status screen", () => { await waitForText(screen.lastFrame, "runtime"); }); - test("esc returns to the project command menu", async () => { + test("esc returns to the root command menu", async () => { const screen = renderStatus(core()); await waitForGroup(screen); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); }); test("shows resolution errors with the standard treatment", async () => { @@ -273,7 +273,7 @@ describe("project status screen", () => { await waitForText(screen.lastFrame, "No deployment targets are configured"); expect(screen.lastFrame()).toContain("✗"); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); }); test("an empty project reports that nothing is declared", async () => { @@ -338,11 +338,9 @@ describe("project status screen", () => { await screen.press("escape"); await waitForText(screen.lastFrame, "choose a deployment target"); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); // Past the screen that pinned, the menus and what they open fetch in the // launch region again. - await screen.press("escape"); - await waitForText(screen.lastFrame, "❯ project"); await screen.write("harness"); await screen.press("return"); await waitForText(screen.lastFrame, "agentcore → harness"); @@ -355,11 +353,11 @@ describe("project status screen", () => { test("reports the CLI's own guidance outside a project", async () => { cleanups.push((await inTempDirectory()).cleanup); - const screen = renderScreen("/agentcore/project/status", { core: core() }); + const screen = renderScreen("/agentcore/status", { core: core() }); await waitForFlatText(screen.lastFrame, "No AgentCore project found"); - expect(flatFrame(screen.lastFrame)).toContain("agentcore project create"); + expect(flatFrame(screen.lastFrame)).toContain("agentcore create"); await screen.press("escape"); - await waitForText(screen.lastFrame, "manage an AgentCore project"); + await waitForText(screen.lastFrame, "the platform for production AI agents"); }); }); diff --git a/src/handlers/project/traces/harness.test.tsx b/src/handlers/project/traces/harness.test.tsx index 6fc9ace173..d6fb1b9a16 100644 --- a/src/handlers/project/traces/harness.test.tsx +++ b/src/handlers/project/traces/harness.test.tsx @@ -91,16 +91,7 @@ function command(projectBackend: ProjectBackend) { core, io, run: (args: string[]) => - root.route([ - "bun", - "agentcore", - "project", - "traces", - "harness", - ...args, - "--region", - "us-east-1", - ]), + root.route(["bun", "agentcore", "traces", "harness", ...args, "--region", "us-east-1"]), }; } diff --git a/src/handlers/project/traces/runtime.test.tsx b/src/handlers/project/traces/runtime.test.tsx index 582b8fa3b3..990009609e 100644 --- a/src/handlers/project/traces/runtime.test.tsx +++ b/src/handlers/project/traces/runtime.test.tsx @@ -103,16 +103,7 @@ function command(projectBackend: ProjectBackend) { core, io, run: (args: string[]) => - root.route([ - "bun", - "agentcore", - "project", - "traces", - "runtime", - ...args, - "--region", - "us-east-1", - ]), + root.route(["bun", "agentcore", "traces", "runtime", ...args, "--region", "us-east-1"]), }; } diff --git a/src/handlers/root.test.tsx b/src/handlers/root.test.tsx index 2a128f467d..57db288fd3 100644 --- a/src/handlers/root.test.tsx +++ b/src/handlers/root.test.tsx @@ -11,7 +11,17 @@ describe("createRootHandler", () => { }); expect(root.name()).toBe("agentcore"); expect(root.children().map((c) => c.name())).toEqual([ - "project", + "create", + "add", + "export", + "remove", + "dev", + "deploy", + "invoke", + "log", + "traces", + "status", + "build", "harness", "identity", "runtime", diff --git a/src/testing/projects.ts b/src/testing/projects.ts index f20eee8451..bd86ca8064 100644 --- a/src/testing/projects.ts +++ b/src/testing/projects.ts @@ -45,7 +45,6 @@ export async function initProject(options: InitProjectOptions = {}): Promise Date: Wed, 23 Sep 2026 21:52:11 +0000 Subject: [PATCH 05/12] test(e2e): update project commands to top level --- e2eTest/project/templates.test.ts | 40 ++++++------------------------- 1 file changed, 7 insertions(+), 33 deletions(-) diff --git a/e2eTest/project/templates.test.ts b/e2eTest/project/templates.test.ts index afa47b9a7f..c6642869a3 100644 --- a/e2eTest/project/templates.test.ts +++ b/e2eTest/project/templates.test.ts @@ -161,16 +161,7 @@ describe( const created = parseResult( ProjectCreatedSchema, await cli.run( - [ - "project", - "create", - "--name", - projectName, - "--template", - "empty", - "--skip-git", - "--json", - ], + ["create", "--name", projectName, "--template", "empty", "--skip-git", "--json"], projectRoot, ), ); @@ -184,16 +175,7 @@ describe( const added = parseResult( OperationSchema, await cli.run( - [ - "project", - "add", - "runtime", - "--name", - runtime.name, - "--template", - runtime.template, - "--json", - ], + ["add", "runtime", "--name", runtime.name, "--template", runtime.template, "--json"], projectDir, ), ); @@ -220,7 +202,7 @@ describe( }; beforeAll(() => { - dev = cli.start(["project", "dev", "--mode", "headless"], projectDir); + dev = cli.start(["dev", "--mode", "headless"], projectDir); dev.stdout?.on("data", captureDevOutput); dev.stderr?.on("data", captureDevOutput); dev.stdout?.resume(); @@ -261,7 +243,6 @@ describe( LocalRuntimeInvokeResponseSchema, await cli.run( [ - "project", "invoke", "runtime", "--local", @@ -292,7 +273,7 @@ describe( test("deploys all runtimes", { timeout: TIMEOUT_MS.PROJECT_DEPLOY }, async () => { const deployment = parseResult( DeployResponseSchema, - await cli.run(["project", "deploy", "--yes", "--json"], projectDir), + await cli.run(["deploy", "--yes", "--json"], projectDir), ); expect(deployment.message).toContain("Deployed project"); }); @@ -306,7 +287,6 @@ describe( RuntimeInvokeResponseSchema, await cli.run( [ - "project", "invoke", "runtime", "--name", @@ -335,10 +315,7 @@ describe( async (runtime) => { const removed = parseResult( OperationSchema, - await cli.run( - ["project", "remove", "runtime", "--name", runtime.name, "--json"], - projectDir, - ), + await cli.run(["remove", "runtime", "--name", runtime.name, "--json"], projectDir), ); expect(removed.operation).toBe("remove"); }, @@ -350,13 +327,10 @@ describe( async () => { parseResult( JsonObjectSchema, - await cli.run(["project", "remove", "all", "--yes", "--json"], projectDir), + await cli.run(["remove", "all", "--yes", "--json"], projectDir), ); - parseResult( - JsonObjectSchema, - await cli.run(["project", "deploy", "--yes", "--json"], projectDir), - ); + parseResult(JsonObjectSchema, await cli.run(["deploy", "--yes", "--json"], projectDir)); }, ); }, From 52360879434b1ff7aa037c9aa9ae0e8672d03e21 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 21:58:16 +0000 Subject: [PATCH 06/12] docs(project): document top-level project commands --- README.md | 50 ++-- command.md | 267 +++++++++--------- docs/harness-project-configuration.md | 12 +- scripts/generate-command-reference.mjs | 18 +- src/assets/cdk/README.md | 10 +- src/assets/evaluators/python-lambda/README.md | 2 +- .../templates/a2a-python-strands/README.md | 4 +- .../agent-python-langchain/README.md | 10 +- .../templates/agent-python-minimal/README.md | 8 +- .../templates/agent-python-strands/README.md | 6 +- .../agent-typescript-strands/README.md | 6 +- .../agent-typescript-vercel/README.md | 4 +- .../templates/agui-python-strands/README.md | 6 +- .../templates/export-harness-python/README.md | 6 +- .../templates/mcp-python-fastmcp/README.md | 6 +- .../templates/shared/env.local.template | 2 +- src/components/CliOnlyScreen.tsx | 2 +- 17 files changed, 213 insertions(+), 206 deletions(-) diff --git a/README.md b/README.md index 50f13231e0..6cf51764f8 100644 --- a/README.md +++ b/README.md @@ -29,51 +29,51 @@ those details so you can create, deploy, and invoke agents from your terminal. Create a managed Harness project, deploy it, and send a prompt: ```bash -agentcore project create --name MyAssistant +agentcore create --name MyAssistant cd MyAssistant -agentcore project deploy -agentcore project invoke harness --prompt "Hey, what can you do for me?" +agentcore deploy +agentcore invoke harness --prompt "Hey, what can you do for me?" ``` To start with code you own instead, create a Runtime project from a template. Run this alternative from outside an existing project: ```bash -agentcore project create --name MyAgent --template agent-python-strands +agentcore create --name MyAgent --template agent-python-strands ``` ## Command Surface -`project` commands manage local project specifications and their deployments. -Resource commands operate on deployed resources without requiring a local project. - -| Command | Purpose | -| ---------- | ----------------------------------------------------------------------------- | -| `project` | Create, develop, build, deploy, invoke, and inspect a project | -| `harness` | Manage Harnesses, versions, and endpoints; invoke and inspect them | -| `identity` | Manage credential providers | -| `runtime` | Inspect, invoke, and open a shell in deployed Runtimes | -| `memory` | Inspect Memories, actors, sessions, events, and records | -| `gateway` | Inspect and invoke Gateways, inspect targets and rules, and generate policies | -| `payment` | Inspect payment managers, connectors, sessions, instruments, and balances | -| `eval` | Evaluate agents, manage datasets and configurations, and run experiments | -| `feedback` | Submit feedback | -| `config` | Read and write global CLI settings | -| `update` | Check for and install CLI updates | +Project commands manage local project specifications and their deployments. Resource commands +operate on deployed resources without requiring a local project. + +| Command | Purpose | +| -------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------- | +| `create`, `add`, `export`, `remove`, `dev`, `deploy`, `invoke`, `log`, `traces`, `status`, `build` | Create, develop, build, deploy, invoke, and inspect a project | +| `harness` | Manage Harnesses, versions, and endpoints; invoke and inspect them | +| `identity` | Manage credential providers | +| `runtime` | Inspect, invoke, and open a shell in deployed Runtimes | +| `memory` | Inspect Memories, actors, sessions, events, and records | +| `gateway` | Inspect and invoke Gateways, inspect targets and rules, and generate policies | +| `payment` | Inspect payment managers, connectors, sessions, instruments, and balances | +| `eval` | Evaluate agents, manage datasets and configurations, and run experiments | +| `feedback` | Submit feedback | +| `config` | Read and write global CLI settings | +| `update` | Check for and install CLI updates | Use `--help` for subcommands and flags, or browse the [command reference](command.md): ```bash agentcore --help -agentcore project --help +agentcore add --help agentcore runtime invoke --help ``` Supported bare commands open their interactive flows in a terminal. Operation flags select headless behavior for most commands, but invoke commands can use selectors such as `--id` and `--session-id` to seed an interactive console. -Run `agentcore project create` for guided setup. To create a default project -without the wizard, run `agentcore project create --name MyAssistant`. +Run `agentcore create` for guided setup. To create a default project without +the wizard, run `agentcore create --name MyAssistant`. Global flags (declared at the root, available on every command): @@ -129,14 +129,14 @@ export class AgentCoreStack extends Stack { For a Harness, use `this.application.harness("")` instead. -Run `agentcore project deploy` to apply your changes. The names passed to +Run `agentcore deploy` to apply your changes. The names passed to `runtime()` or `harness()` must match the names in your project. If you use an existing execution role through `executionRoleArn`, CDK cannot change its permissions. You'll need to add the required permissions to that role yourself. -Note that `agentcore project status` reports only the resources `agentcore.json` +Note that `agentcore status` reports only the resources `agentcore.json` declares, not the ones you add in the stack. ## Documentation diff --git a/command.md b/command.md index 8ce9f3f124..774c05092a 100644 --- a/command.md +++ b/command.md @@ -9,50 +9,49 @@ This reference was generated from `agentcore --help` for version `1.0.0-rc.4`. - [Global options](#global-options) - [`agentcore`](#agentcore) - [Project commands](#project-commands) - - [`agentcore project`](#agentcore-project) - - [`agentcore project create`](#agentcore-project-create) - - [`agentcore project add`](#agentcore-project-add) - - [`agentcore project add config-bundle`](#agentcore-project-add-config-bundle) - - [`agentcore project add harness`](#agentcore-project-add-harness) - - [`agentcore project add memory`](#agentcore-project-add-memory) - - [`agentcore project add runtime`](#agentcore-project-add-runtime) - - [`agentcore project add online-eval`](#agentcore-project-add-online-eval) - - [`agentcore project add online-insight`](#agentcore-project-add-online-insight) - - [`agentcore project add evaluator`](#agentcore-project-add-evaluator) - - [`agentcore project add evaluator llm-as-a-judge`](#agentcore-project-add-evaluator-llm-as-a-judge) - - [`agentcore project add evaluator code-based`](#agentcore-project-add-evaluator-code-based) - - [`agentcore project add credentials`](#agentcore-project-add-credentials) - - [`agentcore project add credentials api-key`](#agentcore-project-add-credentials-api-key) - - [`agentcore project add credentials oauth`](#agentcore-project-add-credentials-oauth) - - [`agentcore project add credentials payment`](#agentcore-project-add-credentials-payment) - - [`agentcore project add gateway`](#agentcore-project-add-gateway) - - [`agentcore project add gateway-target`](#agentcore-project-add-gateway-target) - - [`agentcore project add gateway-connector`](#agentcore-project-add-gateway-connector) - - [`agentcore project add policy-engine`](#agentcore-project-add-policy-engine) - - [`agentcore project add policy`](#agentcore-project-add-policy) - - [`agentcore project add payment-manager`](#agentcore-project-add-payment-manager) - - [`agentcore project add payment-connector`](#agentcore-project-add-payment-connector) - - [`agentcore project add runtime-endpoint`](#agentcore-project-add-runtime-endpoint) - - [`agentcore project export`](#agentcore-project-export) - - [`agentcore project export harness`](#agentcore-project-export-harness) - - [`agentcore project remove`](#agentcore-project-remove) - - [`agentcore project dev`](#agentcore-project-dev) - - [`agentcore project deploy`](#agentcore-project-deploy) - - [`agentcore project invoke`](#agentcore-project-invoke) - - [`agentcore project invoke runtime`](#agentcore-project-invoke-runtime) - - [`agentcore project invoke harness`](#agentcore-project-invoke-harness) - - [`agentcore project log`](#agentcore-project-log) - - [`agentcore project log runtime`](#agentcore-project-log-runtime) - - [`agentcore project log harness`](#agentcore-project-log-harness) - - [`agentcore project traces`](#agentcore-project-traces) - - [`agentcore project traces runtime`](#agentcore-project-traces-runtime) - - [`agentcore project traces runtime list`](#agentcore-project-traces-runtime-list) - - [`agentcore project traces runtime get`](#agentcore-project-traces-runtime-get) - - [`agentcore project traces harness`](#agentcore-project-traces-harness) - - [`agentcore project traces harness list`](#agentcore-project-traces-harness-list) - - [`agentcore project traces harness get`](#agentcore-project-traces-harness-get) - - [`agentcore project status`](#agentcore-project-status) - - [`agentcore project build`](#agentcore-project-build) + - [`agentcore create`](#agentcore-create) + - [`agentcore add`](#agentcore-add) + - [`agentcore add config-bundle`](#agentcore-add-config-bundle) + - [`agentcore add harness`](#agentcore-add-harness) + - [`agentcore add memory`](#agentcore-add-memory) + - [`agentcore add runtime`](#agentcore-add-runtime) + - [`agentcore add online-eval`](#agentcore-add-online-eval) + - [`agentcore add online-insight`](#agentcore-add-online-insight) + - [`agentcore add evaluator`](#agentcore-add-evaluator) + - [`agentcore add evaluator llm-as-a-judge`](#agentcore-add-evaluator-llm-as-a-judge) + - [`agentcore add evaluator code-based`](#agentcore-add-evaluator-code-based) + - [`agentcore add credentials`](#agentcore-add-credentials) + - [`agentcore add credentials api-key`](#agentcore-add-credentials-api-key) + - [`agentcore add credentials oauth`](#agentcore-add-credentials-oauth) + - [`agentcore add credentials payment`](#agentcore-add-credentials-payment) + - [`agentcore add gateway`](#agentcore-add-gateway) + - [`agentcore add gateway-target`](#agentcore-add-gateway-target) + - [`agentcore add gateway-connector`](#agentcore-add-gateway-connector) + - [`agentcore add policy-engine`](#agentcore-add-policy-engine) + - [`agentcore add policy`](#agentcore-add-policy) + - [`agentcore add payment-manager`](#agentcore-add-payment-manager) + - [`agentcore add payment-connector`](#agentcore-add-payment-connector) + - [`agentcore add runtime-endpoint`](#agentcore-add-runtime-endpoint) + - [`agentcore export`](#agentcore-export) + - [`agentcore export harness`](#agentcore-export-harness) + - [`agentcore remove`](#agentcore-remove) + - [`agentcore dev`](#agentcore-dev) + - [`agentcore deploy`](#agentcore-deploy) + - [`agentcore invoke`](#agentcore-invoke) + - [`agentcore invoke runtime`](#agentcore-invoke-runtime) + - [`agentcore invoke harness`](#agentcore-invoke-harness) + - [`agentcore log`](#agentcore-log) + - [`agentcore log runtime`](#agentcore-log-runtime) + - [`agentcore log harness`](#agentcore-log-harness) + - [`agentcore traces`](#agentcore-traces) + - [`agentcore traces runtime`](#agentcore-traces-runtime) + - [`agentcore traces runtime list`](#agentcore-traces-runtime-list) + - [`agentcore traces runtime get`](#agentcore-traces-runtime-get) + - [`agentcore traces harness`](#agentcore-traces-harness) + - [`agentcore traces harness list`](#agentcore-traces-harness-list) + - [`agentcore traces harness get`](#agentcore-traces-harness-get) + - [`agentcore status`](#agentcore-status) + - [`agentcore build`](#agentcore-build) - [Harness commands](#harness-commands) - [`agentcore harness`](#agentcore-harness) - [`agentcore harness create`](#agentcore-harness-create) @@ -250,18 +249,10 @@ the platform for production AI agents ## Project commands -### `agentcore project` +### `agentcore create` ```text -agentcore project [options] [command] -``` - -manage an AgentCore project - -#### `agentcore project create` - -```text -agentcore project create [options] +agentcore create [options] ``` create a new AgentCore project @@ -275,18 +266,18 @@ create a new AgentCore project - `--skip-install`: skip installing dependencies (npm install, uv sync) (default: false) - `--skip-git`: skip initializing a git repository (default: false) -#### `agentcore project add` +### `agentcore add` ```text -agentcore project add [options] [command] +agentcore add [options] [command] ``` add project resources -##### `agentcore project add config-bundle` +#### `agentcore add config-bundle` ```text -agentcore project add config-bundle [options] +agentcore add config-bundle [options] ``` add a configuration bundle to the current project @@ -300,10 +291,10 @@ add a configuration bundle to the current project - `--commit-message `: message describing the initial configuration - `--kms-key-arn `: customer managed KMS key ARN for component configurations -##### `agentcore project add harness` +#### `agentcore add harness` ```text -agentcore project add harness [options] +agentcore add harness [options] ``` add a harness to the current project @@ -335,10 +326,10 @@ add a harness to the current project - `--tags `: tags as key=value (repeatable) or JSON object - `--dockerfile `: path to local dockerfile to use as the container image for the harness -##### `agentcore project add memory` +#### `agentcore add memory` ```text -agentcore project add memory [options] +agentcore add memory [options] ``` add a Memory to the current project @@ -355,10 +346,10 @@ add a Memory to the current project - `--execution-role-arn `: IAM role the Memory assumes; a default role is created when omitted - `--tags `: tags to apply (JSON object of key/value strings) -##### `agentcore project add runtime` +#### `agentcore add runtime` ```text -agentcore project add runtime [options] +agentcore add runtime [options] ``` add a Runtime to the current project @@ -386,10 +377,10 @@ add a Runtime to the current project - `--filesystem-configurations `: filesystem mount configurations (JSON) - `--tags `: tags as key=value (repeatable) or JSON object -##### `agentcore project add online-eval` +#### `agentcore add online-eval` ```text -agentcore project add online-eval [options] +agentcore add online-eval [options] ``` add an online evaluation config to the current project @@ -407,10 +398,10 @@ add an online evaluation config to the current project - `--enable-on-create `: enable evaluation immediately after deploy (default true; pass false to add it paused) - `--tags `: tags to apply (JSON object of key/value strings) -##### `agentcore project add online-insight` +#### `agentcore add online-insight` ```text -agentcore project add online-insight [options] +agentcore add online-insight [options] ``` add an online insight config to the current project @@ -429,18 +420,18 @@ add an online insight config to the current project - `--enable-on-create `: enable insights immediately after deploy (default true; pass false to add it paused) - `--tags `: tags to apply (JSON object of key/value strings) -##### `agentcore project add evaluator` +#### `agentcore add evaluator` ```text -agentcore project add evaluator [options] [command] +agentcore add evaluator [options] [command] ``` add a custom evaluator to the current project -###### `agentcore project add evaluator llm-as-a-judge` +##### `agentcore add evaluator llm-as-a-judge` ```text -agentcore project add evaluator llm-as-a-judge [options] +agentcore add evaluator llm-as-a-judge [options] ``` add an LLM-as-a-Judge evaluator: another LLM prompted with instructions on how to score a session @@ -457,10 +448,10 @@ add an LLM-as-a-Judge evaluator: another LLM prompted with instructions on how t - `--kms-key-arn `: customer-managed KMS key ARN to encrypt the evaluator - `--tags `: tags to apply (JSON object of key/value strings) -###### `agentcore project add evaluator code-based` +##### `agentcore add evaluator code-based` ```text -agentcore project add evaluator code-based [options] +agentcore add evaluator code-based [options] ``` add a code-based evaluator: scaffold a Python Lambda with custom evaluation logic, or reference an existing Lambda with --lambda-arn @@ -475,18 +466,18 @@ add a code-based evaluator: scaffold a Python Lambda with custom evaluation logi - `--kms-key-arn `: customer-managed KMS key ARN to encrypt the evaluator - `--tags `: tags to apply (JSON object of key/value strings) -##### `agentcore project add credentials` +#### `agentcore add credentials` ```text -agentcore project add credentials [options] [command] +agentcore add credentials [options] [command] ``` add AgentCore Identity credential providers to the current project -###### `agentcore project add credentials api-key` +##### `agentcore add credentials api-key` ```text -agentcore project add credentials api-key [options] +agentcore add credentials api-key [options] ``` add an API key credential provider to the current project @@ -497,10 +488,10 @@ add an API key credential provider to the current project - `--api-key `: the API key (file://path or - for stdin; inline values are rejected) - `--api-key-secret-reference `: external secret reference JSON: {"secretId":"<arn>","jsonKey":"<key>"} -###### `agentcore project add credentials oauth` +##### `agentcore add credentials oauth` ```text -agentcore project add credentials oauth [options] +agentcore add credentials oauth [options] ``` add an OAuth2 credential provider to the current project @@ -516,10 +507,10 @@ add an OAuth2 credential provider to the current project - `--client-secret `: the client secret (file://path or - for stdin; inline values are rejected) - `--client-secret-reference `: external secret reference JSON: {"secretId":"<arn>","jsonKey":"<key>"} -###### `agentcore project add credentials payment` +##### `agentcore add credentials payment` ```text -agentcore project add credentials payment [options] +agentcore add credentials payment [options] ``` add a payment credential provider to the current project @@ -536,10 +527,10 @@ add a payment credential provider to the current project - `--authorization-private-key `: Stripe/Privy authorization private key (file://path or - for stdin; inline values are rejected) - `--authorization-id `: Stripe/Privy authorization identifier -##### `agentcore project add gateway` +#### `agentcore add gateway` ```text -agentcore project add gateway [options] +agentcore add gateway [options] ``` add a Gateway to the current project @@ -558,10 +549,10 @@ add a Gateway to the current project - `--exception-level `: exception detail level: debug - `--tags `: tags as repeated key=value or a JSON object -##### `agentcore project add gateway-target` +#### `agentcore add gateway-target` ```text -agentcore project add gateway-target [options] +agentcore add gateway-target [options] ``` add a Target to a project Gateway @@ -578,10 +569,10 @@ add a Target to a project Gateway - `--credential-name `: name of a compatible credential declared in this project - `--scope `: OAuth scope -##### `agentcore project add gateway-connector` +#### `agentcore add gateway-connector` ```text -agentcore project add gateway-connector [options] +agentcore add gateway-connector [options] ``` add a connector-backed Target to a project Gateway @@ -594,10 +585,10 @@ add a connector-backed Target to a project Gateway - `--connector-configuration `: complete connector agentCoreGateways[].targets[] object (JSON; inline, file://<path>, or - for stdin) - `--knowledge-base `: external ten-character Knowledge Base ID; only for bedrock-knowledge-bases -##### `agentcore project add policy-engine` +#### `agentcore add policy-engine` ```text -agentcore project add policy-engine [options] +agentcore add policy-engine [options] ``` add a Policy Engine to the current project @@ -611,10 +602,10 @@ add a Policy Engine to the current project - `--attach-to-gateways `: names of project Gateways to attach this engine to - `--attach-mode `: attached Gateway enforcement mode: log-only or enforce (default enforce) -##### `agentcore project add policy` +#### `agentcore add policy` ```text -agentcore project add policy [options] +agentcore add policy [options] ``` add a Cedar Policy to a project Policy Engine @@ -629,10 +620,10 @@ add a Cedar Policy to a project Policy Engine - `--enforcement-mode `: enforcement mode: active or log-only - `--authorization-phase `: authorization phase: initiate or return-output (default inferred from the statement) -##### `agentcore project add payment-manager` +#### `agentcore add payment-manager` ```text -agentcore project add payment-manager [options] +agentcore add payment-manager [options] ``` add a payment manager to the current project @@ -651,10 +642,10 @@ add a payment manager to the current project - `--tool-allowlist `: tools eligible for automatic payment - `--network-preferences `: preferred payment networks -##### `agentcore project add payment-connector` +#### `agentcore add payment-connector` ```text -agentcore project add payment-connector [options] +agentcore add payment-connector [options] ``` add a connector to a project payment manager @@ -666,10 +657,10 @@ add a connector to a project payment manager - `--credential `: an existing payment credential to reuse - `--quick-create`: create a CoinbaseCDP connector through Quick Create (default: false) -##### `agentcore project add runtime-endpoint` +#### `agentcore add runtime-endpoint` ```text -agentcore project add runtime-endpoint [options] +agentcore add runtime-endpoint [options] ``` add a named endpoint (version alias) to a runtime @@ -681,18 +672,18 @@ add a named endpoint (version alias) to a runtime - `--version `: the runtime version this endpoint points to (default: 1) - `--description `: description of the endpoint -#### `agentcore project export` +### `agentcore export` ```text -agentcore project export [options] [command] +agentcore export [options] [command] ``` convert project resources into editable code you own -##### `agentcore project export harness` +#### `agentcore export harness` ```text -agentcore project export harness [options] +agentcore export harness [options] ``` convert a harness into an editable Strands Runtime agent @@ -703,10 +694,10 @@ convert a harness into an editable Strands Runtime agent - `--arn `: the ARN of a deployed harness to fetch from the service and export - `--target-agent-name `: the name of the generated Runtime agent (default <harnessName>Agent) -#### `agentcore project remove` +### `agentcore remove` ```text -agentcore project remove [options] [resource] +agentcore remove [options] [resource] ``` remove a resource from the project @@ -724,10 +715,10 @@ remove a resource from the project - `--runtime `: name of the parent runtime for a runtime-endpoint - `--yes`: skip the confirmation prompt when removing all resources (default: false) -#### `agentcore project dev` +### `agentcore dev` ```text -agentcore project dev [options] +agentcore dev [options] ``` run the project locally for development @@ -740,10 +731,10 @@ run the project locally for development - `--mode `: how to run: browser (Agent Inspector web UI) or headless (agents stream to the terminal) (default: "headless") - `--ui-port `: port for the Agent Inspector web UI (browser mode) -#### `agentcore project deploy` +### `agentcore deploy` ```text -agentcore project deploy [options] +agentcore deploy [options] ``` deploy the project to AWS @@ -753,18 +744,18 @@ deploy the project to AWS - `--target `: name of the aws-targets.json entry to deploy; the default target is created automatically from your AWS account and region on first deploy (default: "default") - `--yes`: confirm removing the target's stack when the project declares nothing to deploy (default: false) -#### `agentcore project invoke` +### `agentcore invoke` ```text -agentcore project invoke [options] [command] +agentcore invoke [options] [command] ``` invoke a Runtime or harness from the current project -##### `agentcore project invoke runtime` +#### `agentcore invoke runtime` ```text -agentcore project invoke runtime [options] +agentcore invoke runtime [options] ``` invoke a Runtime from the current project @@ -793,10 +784,10 @@ invoke a Runtime from the current project - `--baggage `: the W3C baggage - `--output-file `: the response output file -##### `agentcore project invoke harness` +#### `agentcore invoke harness` ```text -agentcore project invoke harness [options] +agentcore invoke harness [options] ``` invoke a harness from the current project @@ -809,18 +800,18 @@ invoke a harness from the current project - `--session-id `: the Runtime session ID to continue (33-100 characters) - `--qualifier `: the harness endpoint qualifier to invoke (default DEFAULT) -#### `agentcore project log` +### `agentcore log` ```text -agentcore project log [options] [command] +agentcore log [options] [command] ``` inspect logs for resources in the current project -##### `agentcore project log runtime` +#### `agentcore log runtime` ```text -agentcore project log runtime [options] +agentcore log runtime [options] ``` stream or search logs for a Runtime in the current project @@ -837,10 +828,10 @@ stream or search logs for a Runtime in the current project - `--query `: CloudWatch Logs filter pattern - `--limit `: maximum number of log records to return in search mode -##### `agentcore project log harness` +#### `agentcore log harness` ```text -agentcore project log harness [options] +agentcore log harness [options] ``` stream or search logs for a Harness in the current project @@ -857,26 +848,26 @@ stream or search logs for a Harness in the current project - `--query `: CloudWatch Logs filter pattern - `--limit `: maximum number of log records to return in search mode -#### `agentcore project traces` +### `agentcore traces` ```text -agentcore project traces [options] [command] +agentcore traces [options] [command] ``` inspect traces for resources in the current project -##### `agentcore project traces runtime` +#### `agentcore traces runtime` ```text -agentcore project traces runtime [options] [command] +agentcore traces runtime [options] [command] ``` inspect a Runtime's traces -###### `agentcore project traces runtime list` +##### `agentcore traces runtime list` ```text -agentcore project traces runtime list [options] +agentcore traces runtime list [options] ``` list a Runtime's recent traces @@ -890,10 +881,10 @@ list a Runtime's recent traces - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -###### `agentcore project traces runtime get` +##### `agentcore traces runtime get` ```text -agentcore project traces runtime get [options] +agentcore traces runtime get [options] ``` download a trace's log records to a JSON file @@ -911,18 +902,18 @@ download a trace's log records to a JSON file - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -##### `agentcore project traces harness` +#### `agentcore traces harness` ```text -agentcore project traces harness [options] [command] +agentcore traces harness [options] [command] ``` inspect a Harness's traces -###### `agentcore project traces harness list` +##### `agentcore traces harness list` ```text -agentcore project traces harness list [options] +agentcore traces harness list [options] ``` list a Harness's recent traces @@ -936,10 +927,10 @@ list a Harness's recent traces - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -###### `agentcore project traces harness get` +##### `agentcore traces harness get` ```text -agentcore project traces harness get [options] +agentcore traces harness get [options] ``` download a Harness trace's log records to a JSON file @@ -957,10 +948,10 @@ download a Harness trace's log records to a JSON file - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -#### `agentcore project status` +### `agentcore status` ```text -agentcore project status [options] +agentcore status [options] ``` show the status of the project's deployed resources @@ -969,10 +960,10 @@ show the status of the project's deployed resources - `--target `: name of the aws-targets.json entry to report on (default: "default") -#### `agentcore project build` +### `agentcore build` ```text -agentcore project build [options] +agentcore build [options] ``` build the project's deployable artifacts diff --git a/docs/harness-project-configuration.md b/docs/harness-project-configuration.md index 8ff83b86a9..a5e415ced5 100644 --- a/docs/harness-project-configuration.md +++ b/docs/harness-project-configuration.md @@ -17,7 +17,7 @@ ## Harness Project Files -`project create` (without `--template`) and `project add harness` share the same +`agentcore create` (without `--template`) and `agentcore add harness` share the same scaffolding flow. Each harness has `app//harness.yaml` and `app//system-prompt.md`: @@ -50,7 +50,7 @@ memory: mode: managed ``` -Edit the file, then run `agentcore project deploy` from the project directory to +Edit the file, then run `agentcore deploy` from the project directory to apply changes. A local edit does not update an already deployed harness. The examples below are separate alternatives or sections to add to your file. Replace a section when switching modes rather than keeping fields from both. @@ -151,7 +151,7 @@ systemPrompt: | Path-shaped inline values ending in `.md` or `.txt` are rejected by the current schema. Use the conventional `system-prompt.md` file instead. -`project add harness --system-prompt "Your instructions"` writes the supplied +`agentcore add harness --system-prompt "Your instructions"` writes the supplied text to `system-prompt.md`, leaving `systemPrompt` out of the generated YAML. ## Memory @@ -379,7 +379,7 @@ skills: For a private repository, store its access token in an AgentCore Identity API-key credential provider. The project deployment path resolves `auth.credentialName` -from the [project credentials](../command.md#agentcore-project-add-credentials) declared in +from the [project credentials](../command.md#agentcore-add-credentials) declared in `agentcore.json`: ```yaml @@ -471,7 +471,7 @@ rather than being silently skipped. Exporting a Harness to a code-owned Runtime has additional limits: filesystem skills are rejected, and bundled AWS skills are omitted with an explanation in `EXPORT_NOTES.md`. S3 and Git sources are supported by the exporter. See -[Export a Harness](../command.md#agentcore-project-export-harness). +[Export a Harness](../command.md#agentcore-export-harness). See [Harness skills](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/harness-skills.html) and the [Agent Skills format](https://agentskills.io/specification) for source @@ -551,7 +551,7 @@ dockerfile: Dockerfile ``` The path is relative to the directory containing `harness.yaml`. If supplied -through `project add harness --dockerfile`, the CLI copies the file into the +through `agentcore add harness --dockerfile`, the CLI copies the file into the harness directory as `Dockerfile`. Alternatively, reference a pre-built ECR image: diff --git a/scripts/generate-command-reference.mjs b/scripts/generate-command-reference.mjs index 106f16f89a..f60b440a0c 100644 --- a/scripts/generate-command-reference.mjs +++ b/scripts/generate-command-reference.mjs @@ -9,7 +9,23 @@ const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url)); const REPOSITORY_ROOT = resolve(SCRIPT_DIR, ".."); const DEFAULT_GROUPS = [ { id: "global-options", title: "Global options", commands: [] }, - { id: "project", title: "Project commands", commands: ["project"] }, + { + id: "project", + title: "Project commands", + commands: [ + "create", + "add", + "export", + "remove", + "dev", + "deploy", + "invoke", + "log", + "traces", + "status", + "build", + ], + }, { id: "harness", title: "Harness commands", commands: ["harness"] }, { id: "identity", title: "Identity commands", commands: ["identity"] }, { id: "runtime", title: "Runtime commands", commands: ["runtime"] }, diff --git a/src/assets/cdk/README.md b/src/assets/cdk/README.md index 68fcdfa410..7bec9847f5 100644 --- a/src/assets/cdk/README.md +++ b/src/assets/cdk/README.md @@ -17,9 +17,9 @@ the conventional `system-prompt.md` file in the harness directory. You normally do not run this app directly: ```bash -agentcore project build # synthesizes the CloudFormation templates into agentcore/cdk/cdk.out -agentcore project deploy # synthesizes, then deploys the stack for the selected target -agentcore project status # reports the resources agentcore.json declares +agentcore build # synthesizes the CloudFormation templates into agentcore/cdk/cdk.out +agentcore deploy # synthesizes, then deploys the stack for the selected target +agentcore status # reports the resources agentcore.json declares ``` `npm run build` compiles the app, and `npx cdk synth` / `npx cdk diff` work from this directory too. @@ -43,10 +43,10 @@ checkout.addEnvironmentVariable('ORDERS_TABLE', orders.tableName); orders.grantReadData(this.application.harness('support')); // any AWS L2 grant works too ``` -Then run `agentcore project deploy` again. An unknown name fails at synth and lists the names that exist. +Then run `agentcore deploy` again. An unknown name fails at synth and lists the names that exist. If a runtime or harness is configured with an `executionRoleArn`, CDK cannot modify that imported role: every grant emits a synth-time warning listing the permissions that were not attached, and the role must already carry them. -`agentcore project status` reports only the resources `agentcore.json` declares; resources you add here are visible +`agentcore status` reports only the resources `agentcore.json` declares; resources you add here are visible through CloudFormation (`aws cloudformation describe-stack-resources`). diff --git a/src/assets/evaluators/python-lambda/README.md b/src/assets/evaluators/python-lambda/README.md index 7c4732b98a..8e6cb06b20 100644 --- a/src/assets/evaluators/python-lambda/README.md +++ b/src/assets/evaluators/python-lambda/README.md @@ -28,5 +28,5 @@ def handler(input: EvaluatorInput, context) -> EvaluatorOutput: return EvaluatorOutput(value=1.0, label="Pass", explanation="…") ``` -Then `agentcore project deploy` packages this directory into the evaluator +Then `agentcore deploy` packages this directory into the evaluator Lambda and registers the evaluator. diff --git a/src/assets/templates/a2a-python-strands/README.md b/src/assets/templates/a2a-python-strands/README.md index e6509f8e5f..7da24eb69a 100644 --- a/src/assets/templates/a2a-python-strands/README.md +++ b/src/assets/templates/a2a-python-strands/README.md @@ -21,11 +21,11 @@ def my_tool(param: str) -> str: ## Developing locally -`agentcore project dev` starts the agent locally on `0.0.0.0:9000`. Fetch its agent card at +`agentcore dev` starts the agent locally on `0.0.0.0:9000`. Fetch its agent card at `http://127.0.0.1:9000/.well-known/agent-card.json` and send it messages by posting A2A JSON-RPC to `http://127.0.0.1:9000/`. ## Deployment -`agentcore project deploy` deploys the agent into Amazon Bedrock AgentCore. Invoke it with the +`agentcore deploy` deploys the agent into Amazon Bedrock AgentCore. Invoke it with the AWS CLI (`bedrock-agentcore invoke-agent-runtime`) using an A2A JSON-RPC payload. diff --git a/src/assets/templates/agent-python-langchain/README.md b/src/assets/templates/agent-python-langchain/README.md index 306d9ff0b2..fe41ce72f7 100644 --- a/src/assets/templates/agent-python-langchain/README.md +++ b/src/assets/templates/agent-python-langchain/README.md @@ -13,7 +13,7 @@ session, and streams its response. between turns. - `model/load.py`: creates the Bedrock chat model with `init_chat_model`. - `pyproject.toml`: Python dependencies, managed with - [uv](https://docs.astral.sh/uv/). `agentcore project create` has already run + [uv](https://docs.astral.sh/uv/). `agentcore create` has already run `uv sync` for you (unless you passed `--skip-install`), so `.venv/` is ready. ## Develop @@ -21,8 +21,8 @@ session, and streams its response. Run the agent locally from the project root: ```bash -agentcore project dev -agentcore project invoke runtime --local --name {{name}} --payload '{"prompt":"What is 2 plus 3?"}' +agentcore dev +agentcore invoke runtime --local --name {{name}} --payload '{"prompt":"What is 2 plus 3?"}' ``` Environment variables for local development go in `agentcore/.env.local` @@ -31,8 +31,8 @@ Environment variables for local development go in `agentcore/.env.local` ## Deploy ```bash -agentcore project deploy -agentcore project invoke runtime --payload '{"prompt":"Hello!"}' +agentcore deploy +agentcore invoke runtime --payload '{"prompt":"Hello!"}' ``` Traces are collected automatically: AgentCore Runtime starts the agent under diff --git a/src/assets/templates/agent-python-minimal/README.md b/src/assets/templates/agent-python-minimal/README.md index 2093a041fc..b8bc638f7b 100644 --- a/src/assets/templates/agent-python-minimal/README.md +++ b/src/assets/templates/agent-python-minimal/README.md @@ -9,7 +9,7 @@ invocation — a starting point you own and grow into a real agent. - `main.py` — the agent. A `BedrockAgentCoreApp` wraps the entrypoint that receives each invocation payload and returns the response. - `pyproject.toml` — Python dependencies, managed with - [uv](https://docs.astral.sh/uv/). `agentcore project create` has already run + [uv](https://docs.astral.sh/uv/). `agentcore create` has already run `uv sync` for you (unless you passed `--skip-install`), so `.venv/` is ready. ## Develop @@ -17,7 +17,7 @@ invocation — a starting point you own and grow into a real agent. Run the agent locally from the project root: ```bash -agentcore project dev +agentcore dev ``` Environment variables for local development go in `agentcore/.env.local` @@ -26,6 +26,6 @@ Environment variables for local development go in `agentcore/.env.local` ## Deploy ```bash -agentcore project deploy -agentcore project invoke runtime --payload '{"prompt":"Hello!"}' +agentcore deploy +agentcore invoke runtime --payload '{"prompt":"Hello!"}' ``` diff --git a/src/assets/templates/agent-python-strands/README.md b/src/assets/templates/agent-python-strands/README.md index 0414cf1be7..98120c90b3 100644 --- a/src/assets/templates/agent-python-strands/README.md +++ b/src/assets/templates/agent-python-strands/README.md @@ -47,14 +47,14 @@ If installation was successful, a virtual environment is already created with de Activate the environment with `source .venv/bin/activate` on macOS/Linux, `.venv\Scripts\activate.bat` in Windows Command Prompt, or `.\.venv\Scripts\activate.ps1` in Windows PowerShell. -`agentcore project dev` will start a local server on 0.0.0.0:8080. +`agentcore dev` will start a local server on 0.0.0.0:8080. # Deployment -After providing credentials, `agentcore project deploy` will deploy your project into Amazon Bedrock AgentCore. +After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore. Invoke the deployed Runtime with its native payload: ```bash -agentcore project invoke runtime --payload '{"prompt":"Hello!"}' +agentcore invoke runtime --payload '{"prompt":"Hello!"}' ``` diff --git a/src/assets/templates/agent-typescript-strands/README.md b/src/assets/templates/agent-typescript-strands/README.md index 6dbf92a69d..07d4845fb8 100644 --- a/src/assets/templates/agent-typescript-strands/README.md +++ b/src/assets/templates/agent-typescript-strands/README.md @@ -37,14 +37,14 @@ The `sessionId` is not in the body. It comes from the If installation was successful, `node_modules/` is already populated with dependencies. -`agentcore project dev` will start a local server using `tsx watch main.ts` for hot reload on 0.0.0.0:8080. +`agentcore dev` will start a local server using `tsx watch main.ts` for hot reload on 0.0.0.0:8080. # Deployment -After providing credentials, `agentcore project deploy` will deploy your project into Amazon Bedrock AgentCore. +After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore. Invoke the deployed Runtime with its native payload: ```bash -agentcore project invoke runtime --payload '{"prompt":"Hello!","actorId":"user-123"}' +agentcore invoke runtime --payload '{"prompt":"Hello!","actorId":"user-123"}' ``` diff --git a/src/assets/templates/agent-typescript-vercel/README.md b/src/assets/templates/agent-typescript-vercel/README.md index b5592fe267..94815bd8f7 100644 --- a/src/assets/templates/agent-typescript-vercel/README.md +++ b/src/assets/templates/agent-typescript-vercel/README.md @@ -15,8 +15,8 @@ defines an HTTP server that streams tokens from Amazon Bedrock via the Vercel AI If installation was successful, `node_modules/` is already populated with dependencies. -`agentcore project dev` will start a local server using `tsx watch main.ts` for hot reload on 0.0.0.0:8080. +`agentcore dev` will start a local server using `tsx watch main.ts` for hot reload on 0.0.0.0:8080. # Deployment -After providing credentials, `agentcore project deploy` will deploy your project into Amazon Bedrock AgentCore. +After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore. diff --git a/src/assets/templates/agui-python-strands/README.md b/src/assets/templates/agui-python-strands/README.md index 9d9e880e55..7b84681720 100644 --- a/src/assets/templates/agui-python-strands/README.md +++ b/src/assets/templates/agui-python-strands/README.md @@ -22,17 +22,17 @@ def my_tool(param: str) -> str: ## Developing locally -`agentcore project dev` starts the agent locally on `0.0.0.0:8080`. Post an AG-UI +`agentcore dev` starts the agent locally on `0.0.0.0:8080`. Post an AG-UI `RunAgentInput` body to `http://127.0.0.1:8080/invocations` to invoke it, and check its health at `http://127.0.0.1:8080/ping`. ## Deployment -`agentcore project deploy` deploys the agent into Amazon Bedrock AgentCore. Invoke the deployed +`agentcore deploy` deploys the agent into Amazon Bedrock AgentCore. Invoke the deployed runtime with an AG-UI `RunAgentInput` payload: ```bash -agentcore project invoke runtime --name {{ name }} \ +agentcore invoke runtime --name {{ name }} \ --payload '{"threadId":"t1","runId":"r1","state":{},"messages":[{"id":"m1","role":"user","content":"Hello!"}],"tools":[],"context":[],"forwardedProps":{}}' ``` diff --git a/src/assets/templates/export-harness-python/README.md b/src/assets/templates/export-harness-python/README.md index 5714aafbf2..391f294ea2 100644 --- a/src/assets/templates/export-harness-python/README.md +++ b/src/assets/templates/export-harness-python/README.md @@ -33,14 +33,14 @@ If installation was successful, a virtual environment is already created with de Activate the environment with `source .venv/bin/activate` on macOS/Linux, `.venv\Scripts\activate.bat` in Windows Command Prompt, or `.\.venv\Scripts\activate.ps1` in Windows PowerShell. -`agentcore project dev` will start a local server on 0.0.0.0:8080. +`agentcore dev` will start a local server on 0.0.0.0:8080. # Deployment -After providing credentials, `agentcore project deploy` will deploy your project into Amazon Bedrock AgentCore. +After providing credentials, `agentcore deploy` will deploy your project into Amazon Bedrock AgentCore. Invoke the deployed Runtime with its native payload: ```bash -agentcore project invoke runtime --payload '{"prompt":"Hello!"}' +agentcore invoke runtime --payload '{"prompt":"Hello!"}' ``` diff --git a/src/assets/templates/mcp-python-fastmcp/README.md b/src/assets/templates/mcp-python-fastmcp/README.md index af0cf4c46d..1497916786 100644 --- a/src/assets/templates/mcp-python-fastmcp/README.md +++ b/src/assets/templates/mcp-python-fastmcp/README.md @@ -23,10 +23,10 @@ def my_tool(param: str) -> str: If installation was successful, a virtual environment is already created with dependencies installed. -`agentcore project dev` starts the server locally on `0.0.0.0:8000`. List and call tools by +`agentcore dev` starts the server locally on `0.0.0.0:8000`. List and call tools by sending JSON-RPC to `http://127.0.0.1:8000/mcp`. ## Deployment -`agentcore project deploy` deploys the server into Amazon Bedrock AgentCore. Invoke it with -`agentcore project invoke runtime`, supplying an MCP JSON-RPC payload (e.g. `tools/list`, `tools/call`). +`agentcore deploy` deploys the server into Amazon Bedrock AgentCore. Invoke it with +`agentcore invoke runtime`, supplying an MCP JSON-RPC payload (e.g. `tools/list`, `tools/call`). diff --git a/src/assets/templates/shared/env.local.template b/src/assets/templates/shared/env.local.template index fb931a12b3..97af2bb1b2 100644 --- a/src/assets/templates/shared/env.local.template +++ b/src/assets/templates/shared/env.local.template @@ -1,5 +1,5 @@ # Environment variables for local development. -# `agentcore project dev` loads this file into your agent's process. Values here +# `agentcore dev` loads this file into your agent's process. Values here # override injected values except PORT, FASTMCP_PORT, and LOCAL_DEV, which the # CLI owns. While trace collection is on (the default), the CLI also owns the # OTEL_* and AGENT_OBSERVABILITY_ENABLED variables so traces reach its local diff --git a/src/components/CliOnlyScreen.tsx b/src/components/CliOnlyScreen.tsx index 350c955d55..05da1255bc 100644 --- a/src/components/CliOnlyScreen.tsx +++ b/src/components/CliOnlyScreen.tsx @@ -13,7 +13,7 @@ import { darkTheme } from "./ui/_core.js"; const theme = darkTheme; export interface CliOnlyScreenProps extends ScreenProps { - // path is the command's path, e.g. ["agentcore", "project", "dev"]. + // path is the command's path, e.g. ["agentcore", "dev"]. path: string[]; } From 677c0a9ff8b779c39f85cebc62d560edfbdd3c86 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:01:01 +0000 Subject: [PATCH 07/12] test(tui): update root menu navigation expectations --- src/components/RouterScreen.test.tsx | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/src/components/RouterScreen.test.tsx b/src/components/RouterScreen.test.tsx index 1ccd88beb7..f9095c029e 100644 --- a/src/components/RouterScreen.test.tsx +++ b/src/components/RouterScreen.test.tsx @@ -46,8 +46,8 @@ describe("menu rendering", () => { test("highlights the first option by default", async () => { const r = renderScreen("/agentcore"); await waitForText(r.lastFrame, "harness"); - // The focus caret marks the highlighted row; the first option is project. - expect(r.lastFrame()).toContain("❯ project"); + // The focus caret marks the highlighted row; the first option is create. + expect(r.lastFrame()).toContain("❯ create"); r.unmount(); }); }); @@ -89,37 +89,33 @@ describe("filtering", () => { describe("navigation", () => { test("down arrow moves the highlight to the next option", async () => { const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "❯ project"); + await waitForText(r.lastFrame, "❯ create"); await r.press("down"); - await waitForText(r.lastFrame, "❯ harness"); + await waitForText(r.lastFrame, "❯ add"); await r.press("down"); - await waitForText(r.lastFrame, "❯ identity"); + await waitForText(r.lastFrame, "❯ remove"); r.unmount(); }); test("up arrow does not move past the first option", async () => { const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "❯ project"); + await waitForText(r.lastFrame, "❯ create"); await r.press("up"); await tick(20); // Still on the first option. - expect(r.lastFrame()).toContain("❯ project"); + expect(r.lastFrame()).toContain("❯ create"); r.unmount(); }); test("enter navigates into the highlighted subcommand's screen", async () => { const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "❯ project"); + await waitForText(r.lastFrame, "❯ create"); - await r.press("down"); - await waitForText(r.lastFrame, "❯ harness"); await r.press("return"); - // The harness screen is itself a RouterScreen showing harness subcommands. - await waitForText(r.lastFrame, "agentcore → harness"); - expect(r.lastFrame()).toContain("list"); + await waitForText(r.lastFrame, "name your project"); r.unmount(); }); @@ -130,17 +126,17 @@ describe("navigation", () => { await r.press("escape"); // Back at the root menu (breadcrumb no longer includes harness). await waitForText(r.lastFrame, "the platform for production AI agents"); - expect(r.lastFrame()).toContain("❯ project"); + expect(r.lastFrame()).toContain("❯ create"); r.unmount(); }); test("esc at the root menu is a no-op (no parent to go to)", async () => { const r = renderScreen("/agentcore"); - await waitForText(r.lastFrame, "❯ project"); + await waitForText(r.lastFrame, "❯ create"); await r.press("escape"); await tick(20); - expect(r.lastFrame()).toContain("❯ project"); + expect(r.lastFrame()).toContain("❯ create"); r.unmount(); }); }); From 8f820780aa096b6a9bcd15847a71b7af99e28622 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:14:38 +0000 Subject: [PATCH 08/12] fix(project): update payment manager guidance --- src/handlers/project/add/payment-manager/index.test.ts | 2 +- src/handlers/project/add/payment-manager/index.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/handlers/project/add/payment-manager/index.test.ts b/src/handlers/project/add/payment-manager/index.test.ts index 3f20473206..25acde9e2b 100644 --- a/src/handlers/project/add/payment-manager/index.test.ts +++ b/src/handlers/project/add/payment-manager/index.test.ts @@ -94,7 +94,7 @@ describe("project add payment-manager", () => { expect(result.notes).toEqual([ "Warning: auto-payment is ENABLED for manager 'payments'. Agents can automatically settle " + "402 responses without human approval. Use --no-auto-payment to require manual approval.", - "Warning: project add payment-manager does not modify runtime source code. " + + "Warning: agentcore add payment-manager does not modify runtime source code. " + "Configure the Payments SDK or plugin in supported runtimes before invoking payment-enabled agents.", ]); expect(io.stderr()).not.toContain("auto-payment is ENABLED"); diff --git a/src/handlers/project/add/payment-manager/index.ts b/src/handlers/project/add/payment-manager/index.ts index ceccbe8967..b3968d0efa 100644 --- a/src/handlers/project/add/payment-manager/index.ts +++ b/src/handlers/project/add/payment-manager/index.ts @@ -74,7 +74,7 @@ export const createAddPaymentManagerHandler = (config: AddProjectResourceConfig) } if (project.spec.runtimes.length > 0) { notes.push( - "Warning: project add payment-manager does not modify runtime source code. " + + "Warning: agentcore add payment-manager does not modify runtime source code. " + "Configure the Payments SDK or plugin in supported runtimes before invoking payment-enabled agents.", ); } From 4c3a9df00e188c90defdf74102f4768698dd11b9 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:40:54 +0000 Subject: [PATCH 09/12] fix(project): address top-level command review feedback --- CONTRIBUTING.md | 2 +- command.md | 267 +++++++++--------- e2eTest/project/templates.test.ts | 4 +- src/core/project/backends/cdk.test.ts | 2 +- src/core/project/backends/cdk.ts | 2 +- src/core/project/backends/cdk/credentials.ts | 2 +- src/core/project/backends/cdk/stackReader.ts | 2 +- src/core/project/manager.tsx | 2 +- src/handlers/project/add/index.ts | 2 +- .../project/add/memory/memory.screen.test.tsx | 2 +- .../add/runtime/runtime.screen.test.tsx | 19 +- .../project/create/create.screen.test.tsx | 2 +- src/handlers/project/create/index.ts | 2 +- src/handlers/project/export/types.ts | 2 +- src/handlers/project/project.screen.test.tsx | 265 ----------------- src/io/openBrowser.ts | 2 +- src/projectSchemas/aws-targets.ts | 2 +- src/projectSchemas/harness.test.ts | 2 +- src/testing/projects.ts | 6 +- 19 files changed, 175 insertions(+), 414 deletions(-) delete mode 100644 src/handlers/project/project.screen.test.tsx diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 1c316f5086..3283799629 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -85,7 +85,7 @@ npm i -g "./$TARBALL" - **The CLI looks frozen in a PowerShell window**: legacy conhost pauses all output while text is selected (the title bar shows `Select`). Press `Esc`. Windows Terminal does not do this. -- **`project create` refuses a long path**: Windows caps paths at 260 characters +- **`agentcore create` refuses a long path**: Windows caps paths at 260 characters unless `LongPathsEnabled` is set, and the CDK app's `node_modules` puts its deepest file 155 characters below the project root (aws-cdk-lib's own shipped fixtures), so the project root must be at most 104 characters. Create the diff --git a/command.md b/command.md index 774c05092a..8ce9f3f124 100644 --- a/command.md +++ b/command.md @@ -9,49 +9,50 @@ This reference was generated from `agentcore --help` for version `1.0.0-rc.4`. - [Global options](#global-options) - [`agentcore`](#agentcore) - [Project commands](#project-commands) - - [`agentcore create`](#agentcore-create) - - [`agentcore add`](#agentcore-add) - - [`agentcore add config-bundle`](#agentcore-add-config-bundle) - - [`agentcore add harness`](#agentcore-add-harness) - - [`agentcore add memory`](#agentcore-add-memory) - - [`agentcore add runtime`](#agentcore-add-runtime) - - [`agentcore add online-eval`](#agentcore-add-online-eval) - - [`agentcore add online-insight`](#agentcore-add-online-insight) - - [`agentcore add evaluator`](#agentcore-add-evaluator) - - [`agentcore add evaluator llm-as-a-judge`](#agentcore-add-evaluator-llm-as-a-judge) - - [`agentcore add evaluator code-based`](#agentcore-add-evaluator-code-based) - - [`agentcore add credentials`](#agentcore-add-credentials) - - [`agentcore add credentials api-key`](#agentcore-add-credentials-api-key) - - [`agentcore add credentials oauth`](#agentcore-add-credentials-oauth) - - [`agentcore add credentials payment`](#agentcore-add-credentials-payment) - - [`agentcore add gateway`](#agentcore-add-gateway) - - [`agentcore add gateway-target`](#agentcore-add-gateway-target) - - [`agentcore add gateway-connector`](#agentcore-add-gateway-connector) - - [`agentcore add policy-engine`](#agentcore-add-policy-engine) - - [`agentcore add policy`](#agentcore-add-policy) - - [`agentcore add payment-manager`](#agentcore-add-payment-manager) - - [`agentcore add payment-connector`](#agentcore-add-payment-connector) - - [`agentcore add runtime-endpoint`](#agentcore-add-runtime-endpoint) - - [`agentcore export`](#agentcore-export) - - [`agentcore export harness`](#agentcore-export-harness) - - [`agentcore remove`](#agentcore-remove) - - [`agentcore dev`](#agentcore-dev) - - [`agentcore deploy`](#agentcore-deploy) - - [`agentcore invoke`](#agentcore-invoke) - - [`agentcore invoke runtime`](#agentcore-invoke-runtime) - - [`agentcore invoke harness`](#agentcore-invoke-harness) - - [`agentcore log`](#agentcore-log) - - [`agentcore log runtime`](#agentcore-log-runtime) - - [`agentcore log harness`](#agentcore-log-harness) - - [`agentcore traces`](#agentcore-traces) - - [`agentcore traces runtime`](#agentcore-traces-runtime) - - [`agentcore traces runtime list`](#agentcore-traces-runtime-list) - - [`agentcore traces runtime get`](#agentcore-traces-runtime-get) - - [`agentcore traces harness`](#agentcore-traces-harness) - - [`agentcore traces harness list`](#agentcore-traces-harness-list) - - [`agentcore traces harness get`](#agentcore-traces-harness-get) - - [`agentcore status`](#agentcore-status) - - [`agentcore build`](#agentcore-build) + - [`agentcore project`](#agentcore-project) + - [`agentcore project create`](#agentcore-project-create) + - [`agentcore project add`](#agentcore-project-add) + - [`agentcore project add config-bundle`](#agentcore-project-add-config-bundle) + - [`agentcore project add harness`](#agentcore-project-add-harness) + - [`agentcore project add memory`](#agentcore-project-add-memory) + - [`agentcore project add runtime`](#agentcore-project-add-runtime) + - [`agentcore project add online-eval`](#agentcore-project-add-online-eval) + - [`agentcore project add online-insight`](#agentcore-project-add-online-insight) + - [`agentcore project add evaluator`](#agentcore-project-add-evaluator) + - [`agentcore project add evaluator llm-as-a-judge`](#agentcore-project-add-evaluator-llm-as-a-judge) + - [`agentcore project add evaluator code-based`](#agentcore-project-add-evaluator-code-based) + - [`agentcore project add credentials`](#agentcore-project-add-credentials) + - [`agentcore project add credentials api-key`](#agentcore-project-add-credentials-api-key) + - [`agentcore project add credentials oauth`](#agentcore-project-add-credentials-oauth) + - [`agentcore project add credentials payment`](#agentcore-project-add-credentials-payment) + - [`agentcore project add gateway`](#agentcore-project-add-gateway) + - [`agentcore project add gateway-target`](#agentcore-project-add-gateway-target) + - [`agentcore project add gateway-connector`](#agentcore-project-add-gateway-connector) + - [`agentcore project add policy-engine`](#agentcore-project-add-policy-engine) + - [`agentcore project add policy`](#agentcore-project-add-policy) + - [`agentcore project add payment-manager`](#agentcore-project-add-payment-manager) + - [`agentcore project add payment-connector`](#agentcore-project-add-payment-connector) + - [`agentcore project add runtime-endpoint`](#agentcore-project-add-runtime-endpoint) + - [`agentcore project export`](#agentcore-project-export) + - [`agentcore project export harness`](#agentcore-project-export-harness) + - [`agentcore project remove`](#agentcore-project-remove) + - [`agentcore project dev`](#agentcore-project-dev) + - [`agentcore project deploy`](#agentcore-project-deploy) + - [`agentcore project invoke`](#agentcore-project-invoke) + - [`agentcore project invoke runtime`](#agentcore-project-invoke-runtime) + - [`agentcore project invoke harness`](#agentcore-project-invoke-harness) + - [`agentcore project log`](#agentcore-project-log) + - [`agentcore project log runtime`](#agentcore-project-log-runtime) + - [`agentcore project log harness`](#agentcore-project-log-harness) + - [`agentcore project traces`](#agentcore-project-traces) + - [`agentcore project traces runtime`](#agentcore-project-traces-runtime) + - [`agentcore project traces runtime list`](#agentcore-project-traces-runtime-list) + - [`agentcore project traces runtime get`](#agentcore-project-traces-runtime-get) + - [`agentcore project traces harness`](#agentcore-project-traces-harness) + - [`agentcore project traces harness list`](#agentcore-project-traces-harness-list) + - [`agentcore project traces harness get`](#agentcore-project-traces-harness-get) + - [`agentcore project status`](#agentcore-project-status) + - [`agentcore project build`](#agentcore-project-build) - [Harness commands](#harness-commands) - [`agentcore harness`](#agentcore-harness) - [`agentcore harness create`](#agentcore-harness-create) @@ -249,10 +250,18 @@ the platform for production AI agents ## Project commands -### `agentcore create` +### `agentcore project` ```text -agentcore create [options] +agentcore project [options] [command] +``` + +manage an AgentCore project + +#### `agentcore project create` + +```text +agentcore project create [options] ``` create a new AgentCore project @@ -266,18 +275,18 @@ create a new AgentCore project - `--skip-install`: skip installing dependencies (npm install, uv sync) (default: false) - `--skip-git`: skip initializing a git repository (default: false) -### `agentcore add` +#### `agentcore project add` ```text -agentcore add [options] [command] +agentcore project add [options] [command] ``` add project resources -#### `agentcore add config-bundle` +##### `agentcore project add config-bundle` ```text -agentcore add config-bundle [options] +agentcore project add config-bundle [options] ``` add a configuration bundle to the current project @@ -291,10 +300,10 @@ add a configuration bundle to the current project - `--commit-message `: message describing the initial configuration - `--kms-key-arn `: customer managed KMS key ARN for component configurations -#### `agentcore add harness` +##### `agentcore project add harness` ```text -agentcore add harness [options] +agentcore project add harness [options] ``` add a harness to the current project @@ -326,10 +335,10 @@ add a harness to the current project - `--tags `: tags as key=value (repeatable) or JSON object - `--dockerfile `: path to local dockerfile to use as the container image for the harness -#### `agentcore add memory` +##### `agentcore project add memory` ```text -agentcore add memory [options] +agentcore project add memory [options] ``` add a Memory to the current project @@ -346,10 +355,10 @@ add a Memory to the current project - `--execution-role-arn `: IAM role the Memory assumes; a default role is created when omitted - `--tags `: tags to apply (JSON object of key/value strings) -#### `agentcore add runtime` +##### `agentcore project add runtime` ```text -agentcore add runtime [options] +agentcore project add runtime [options] ``` add a Runtime to the current project @@ -377,10 +386,10 @@ add a Runtime to the current project - `--filesystem-configurations `: filesystem mount configurations (JSON) - `--tags `: tags as key=value (repeatable) or JSON object -#### `agentcore add online-eval` +##### `agentcore project add online-eval` ```text -agentcore add online-eval [options] +agentcore project add online-eval [options] ``` add an online evaluation config to the current project @@ -398,10 +407,10 @@ add an online evaluation config to the current project - `--enable-on-create `: enable evaluation immediately after deploy (default true; pass false to add it paused) - `--tags `: tags to apply (JSON object of key/value strings) -#### `agentcore add online-insight` +##### `agentcore project add online-insight` ```text -agentcore add online-insight [options] +agentcore project add online-insight [options] ``` add an online insight config to the current project @@ -420,18 +429,18 @@ add an online insight config to the current project - `--enable-on-create `: enable insights immediately after deploy (default true; pass false to add it paused) - `--tags `: tags to apply (JSON object of key/value strings) -#### `agentcore add evaluator` +##### `agentcore project add evaluator` ```text -agentcore add evaluator [options] [command] +agentcore project add evaluator [options] [command] ``` add a custom evaluator to the current project -##### `agentcore add evaluator llm-as-a-judge` +###### `agentcore project add evaluator llm-as-a-judge` ```text -agentcore add evaluator llm-as-a-judge [options] +agentcore project add evaluator llm-as-a-judge [options] ``` add an LLM-as-a-Judge evaluator: another LLM prompted with instructions on how to score a session @@ -448,10 +457,10 @@ add an LLM-as-a-Judge evaluator: another LLM prompted with instructions on how t - `--kms-key-arn `: customer-managed KMS key ARN to encrypt the evaluator - `--tags `: tags to apply (JSON object of key/value strings) -##### `agentcore add evaluator code-based` +###### `agentcore project add evaluator code-based` ```text -agentcore add evaluator code-based [options] +agentcore project add evaluator code-based [options] ``` add a code-based evaluator: scaffold a Python Lambda with custom evaluation logic, or reference an existing Lambda with --lambda-arn @@ -466,18 +475,18 @@ add a code-based evaluator: scaffold a Python Lambda with custom evaluation logi - `--kms-key-arn `: customer-managed KMS key ARN to encrypt the evaluator - `--tags `: tags to apply (JSON object of key/value strings) -#### `agentcore add credentials` +##### `agentcore project add credentials` ```text -agentcore add credentials [options] [command] +agentcore project add credentials [options] [command] ``` add AgentCore Identity credential providers to the current project -##### `agentcore add credentials api-key` +###### `agentcore project add credentials api-key` ```text -agentcore add credentials api-key [options] +agentcore project add credentials api-key [options] ``` add an API key credential provider to the current project @@ -488,10 +497,10 @@ add an API key credential provider to the current project - `--api-key `: the API key (file://path or - for stdin; inline values are rejected) - `--api-key-secret-reference `: external secret reference JSON: {"secretId":"<arn>","jsonKey":"<key>"} -##### `agentcore add credentials oauth` +###### `agentcore project add credentials oauth` ```text -agentcore add credentials oauth [options] +agentcore project add credentials oauth [options] ``` add an OAuth2 credential provider to the current project @@ -507,10 +516,10 @@ add an OAuth2 credential provider to the current project - `--client-secret `: the client secret (file://path or - for stdin; inline values are rejected) - `--client-secret-reference `: external secret reference JSON: {"secretId":"<arn>","jsonKey":"<key>"} -##### `agentcore add credentials payment` +###### `agentcore project add credentials payment` ```text -agentcore add credentials payment [options] +agentcore project add credentials payment [options] ``` add a payment credential provider to the current project @@ -527,10 +536,10 @@ add a payment credential provider to the current project - `--authorization-private-key `: Stripe/Privy authorization private key (file://path or - for stdin; inline values are rejected) - `--authorization-id `: Stripe/Privy authorization identifier -#### `agentcore add gateway` +##### `agentcore project add gateway` ```text -agentcore add gateway [options] +agentcore project add gateway [options] ``` add a Gateway to the current project @@ -549,10 +558,10 @@ add a Gateway to the current project - `--exception-level `: exception detail level: debug - `--tags `: tags as repeated key=value or a JSON object -#### `agentcore add gateway-target` +##### `agentcore project add gateway-target` ```text -agentcore add gateway-target [options] +agentcore project add gateway-target [options] ``` add a Target to a project Gateway @@ -569,10 +578,10 @@ add a Target to a project Gateway - `--credential-name `: name of a compatible credential declared in this project - `--scope `: OAuth scope -#### `agentcore add gateway-connector` +##### `agentcore project add gateway-connector` ```text -agentcore add gateway-connector [options] +agentcore project add gateway-connector [options] ``` add a connector-backed Target to a project Gateway @@ -585,10 +594,10 @@ add a connector-backed Target to a project Gateway - `--connector-configuration `: complete connector agentCoreGateways[].targets[] object (JSON; inline, file://<path>, or - for stdin) - `--knowledge-base `: external ten-character Knowledge Base ID; only for bedrock-knowledge-bases -#### `agentcore add policy-engine` +##### `agentcore project add policy-engine` ```text -agentcore add policy-engine [options] +agentcore project add policy-engine [options] ``` add a Policy Engine to the current project @@ -602,10 +611,10 @@ add a Policy Engine to the current project - `--attach-to-gateways `: names of project Gateways to attach this engine to - `--attach-mode `: attached Gateway enforcement mode: log-only or enforce (default enforce) -#### `agentcore add policy` +##### `agentcore project add policy` ```text -agentcore add policy [options] +agentcore project add policy [options] ``` add a Cedar Policy to a project Policy Engine @@ -620,10 +629,10 @@ add a Cedar Policy to a project Policy Engine - `--enforcement-mode `: enforcement mode: active or log-only - `--authorization-phase `: authorization phase: initiate or return-output (default inferred from the statement) -#### `agentcore add payment-manager` +##### `agentcore project add payment-manager` ```text -agentcore add payment-manager [options] +agentcore project add payment-manager [options] ``` add a payment manager to the current project @@ -642,10 +651,10 @@ add a payment manager to the current project - `--tool-allowlist `: tools eligible for automatic payment - `--network-preferences `: preferred payment networks -#### `agentcore add payment-connector` +##### `agentcore project add payment-connector` ```text -agentcore add payment-connector [options] +agentcore project add payment-connector [options] ``` add a connector to a project payment manager @@ -657,10 +666,10 @@ add a connector to a project payment manager - `--credential `: an existing payment credential to reuse - `--quick-create`: create a CoinbaseCDP connector through Quick Create (default: false) -#### `agentcore add runtime-endpoint` +##### `agentcore project add runtime-endpoint` ```text -agentcore add runtime-endpoint [options] +agentcore project add runtime-endpoint [options] ``` add a named endpoint (version alias) to a runtime @@ -672,18 +681,18 @@ add a named endpoint (version alias) to a runtime - `--version `: the runtime version this endpoint points to (default: 1) - `--description `: description of the endpoint -### `agentcore export` +#### `agentcore project export` ```text -agentcore export [options] [command] +agentcore project export [options] [command] ``` convert project resources into editable code you own -#### `agentcore export harness` +##### `agentcore project export harness` ```text -agentcore export harness [options] +agentcore project export harness [options] ``` convert a harness into an editable Strands Runtime agent @@ -694,10 +703,10 @@ convert a harness into an editable Strands Runtime agent - `--arn `: the ARN of a deployed harness to fetch from the service and export - `--target-agent-name `: the name of the generated Runtime agent (default <harnessName>Agent) -### `agentcore remove` +#### `agentcore project remove` ```text -agentcore remove [options] [resource] +agentcore project remove [options] [resource] ``` remove a resource from the project @@ -715,10 +724,10 @@ remove a resource from the project - `--runtime `: name of the parent runtime for a runtime-endpoint - `--yes`: skip the confirmation prompt when removing all resources (default: false) -### `agentcore dev` +#### `agentcore project dev` ```text -agentcore dev [options] +agentcore project dev [options] ``` run the project locally for development @@ -731,10 +740,10 @@ run the project locally for development - `--mode `: how to run: browser (Agent Inspector web UI) or headless (agents stream to the terminal) (default: "headless") - `--ui-port `: port for the Agent Inspector web UI (browser mode) -### `agentcore deploy` +#### `agentcore project deploy` ```text -agentcore deploy [options] +agentcore project deploy [options] ``` deploy the project to AWS @@ -744,18 +753,18 @@ deploy the project to AWS - `--target `: name of the aws-targets.json entry to deploy; the default target is created automatically from your AWS account and region on first deploy (default: "default") - `--yes`: confirm removing the target's stack when the project declares nothing to deploy (default: false) -### `agentcore invoke` +#### `agentcore project invoke` ```text -agentcore invoke [options] [command] +agentcore project invoke [options] [command] ``` invoke a Runtime or harness from the current project -#### `agentcore invoke runtime` +##### `agentcore project invoke runtime` ```text -agentcore invoke runtime [options] +agentcore project invoke runtime [options] ``` invoke a Runtime from the current project @@ -784,10 +793,10 @@ invoke a Runtime from the current project - `--baggage `: the W3C baggage - `--output-file `: the response output file -#### `agentcore invoke harness` +##### `agentcore project invoke harness` ```text -agentcore invoke harness [options] +agentcore project invoke harness [options] ``` invoke a harness from the current project @@ -800,18 +809,18 @@ invoke a harness from the current project - `--session-id `: the Runtime session ID to continue (33-100 characters) - `--qualifier `: the harness endpoint qualifier to invoke (default DEFAULT) -### `agentcore log` +#### `agentcore project log` ```text -agentcore log [options] [command] +agentcore project log [options] [command] ``` inspect logs for resources in the current project -#### `agentcore log runtime` +##### `agentcore project log runtime` ```text -agentcore log runtime [options] +agentcore project log runtime [options] ``` stream or search logs for a Runtime in the current project @@ -828,10 +837,10 @@ stream or search logs for a Runtime in the current project - `--query `: CloudWatch Logs filter pattern - `--limit `: maximum number of log records to return in search mode -#### `agentcore log harness` +##### `agentcore project log harness` ```text -agentcore log harness [options] +agentcore project log harness [options] ``` stream or search logs for a Harness in the current project @@ -848,26 +857,26 @@ stream or search logs for a Harness in the current project - `--query `: CloudWatch Logs filter pattern - `--limit `: maximum number of log records to return in search mode -### `agentcore traces` +#### `agentcore project traces` ```text -agentcore traces [options] [command] +agentcore project traces [options] [command] ``` inspect traces for resources in the current project -#### `agentcore traces runtime` +##### `agentcore project traces runtime` ```text -agentcore traces runtime [options] [command] +agentcore project traces runtime [options] [command] ``` inspect a Runtime's traces -##### `agentcore traces runtime list` +###### `agentcore project traces runtime list` ```text -agentcore traces runtime list [options] +agentcore project traces runtime list [options] ``` list a Runtime's recent traces @@ -881,10 +890,10 @@ list a Runtime's recent traces - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -##### `agentcore traces runtime get` +###### `agentcore project traces runtime get` ```text -agentcore traces runtime get [options] +agentcore project traces runtime get [options] ``` download a trace's log records to a JSON file @@ -902,18 +911,18 @@ download a trace's log records to a JSON file - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -#### `agentcore traces harness` +##### `agentcore project traces harness` ```text -agentcore traces harness [options] [command] +agentcore project traces harness [options] [command] ``` inspect a Harness's traces -##### `agentcore traces harness list` +###### `agentcore project traces harness list` ```text -agentcore traces harness list [options] +agentcore project traces harness list [options] ``` list a Harness's recent traces @@ -927,10 +936,10 @@ list a Harness's recent traces - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -##### `agentcore traces harness get` +###### `agentcore project traces harness get` ```text -agentcore traces harness get [options] +agentcore project traces harness get [options] ``` download a Harness trace's log records to a JSON file @@ -948,10 +957,10 @@ download a Harness trace's log records to a JSON file - `--since `: window start: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default 12h ago) - `--until `: window end: "5m", "1h", "2d", ISO 8601, epoch ms, or "now" (default now) -### `agentcore status` +#### `agentcore project status` ```text -agentcore status [options] +agentcore project status [options] ``` show the status of the project's deployed resources @@ -960,10 +969,10 @@ show the status of the project's deployed resources - `--target `: name of the aws-targets.json entry to report on (default: "default") -### `agentcore build` +#### `agentcore project build` ```text -agentcore build [options] +agentcore project build [options] ``` build the project's deployable artifacts diff --git a/e2eTest/project/templates.test.ts b/e2eTest/project/templates.test.ts index c6642869a3..66d2e98aa2 100644 --- a/e2eTest/project/templates.test.ts +++ b/e2eTest/project/templates.test.ts @@ -224,11 +224,11 @@ describe( // the server may take a bit to get ready, so we retry on a timeout. const response = await retry(async () => { if (!dev) { - throw new Error(`project dev did not start. \nstdout/stdout = ${pendingOutput}`); + throw new Error(`agentcore dev did not start. \nstdout/stdout = ${pendingOutput}`); } if (dev.exitCode !== null) { throw new Error( - `project dev exited with code ${dev.exitCode ?? "unknown"}. \nstdout/stdout = ${pendingOutput}`, + `agentcore dev exited with code ${dev.exitCode ?? "unknown"}. \nstdout/stdout = ${pendingOutput}`, ); } diff --git a/src/core/project/backends/cdk.test.ts b/src/core/project/backends/cdk.test.ts index a0e857db0e..59ddd6cc6e 100644 --- a/src/core/project/backends/cdk.test.ts +++ b/src/core/project/backends/cdk.test.ts @@ -760,7 +760,7 @@ describe("CdkBackend.deploy", () => { }); test("hands teardown the target and the providers recorded before the deploy overwrote them", async () => { - // The `project remove all` shape: the spec declares nothing, so provisioning + // The `agentcore remove all` shape: the spec declares nothing, so provisioning // returns nothing and rewrites the credentials map to empty before teardown runs. // The recorded providers are the only remaining record of what to delete, and // the target name is what scopes their provider names. diff --git a/src/core/project/backends/cdk.ts b/src/core/project/backends/cdk.ts index a2b45dc2ed..2ffeae274a 100644 --- a/src/core/project/backends/cdk.ts +++ b/src/core/project/backends/cdk.ts @@ -436,7 +436,7 @@ export class CdkBackend implements ProjectBackend { yield { type: "step", message: `Removing stack ${artifact.stackName}` }; yield* this.runCdk({ kind: "destroy", stackArtifactId: artifact.id }, options); // After the stack, since a resource in it may still be using the provider. The - // declared credentials are included because `project remove all` empties the spec + // declared credentials are included because `agentcore remove all` empties the spec // before the deploy that gets here, so what it recorded is all that names them. yield* this.removeCredentials(project, { credentials: options.credentials, diff --git a/src/core/project/backends/cdk/credentials.ts b/src/core/project/backends/cdk/credentials.ts index b2820ddbdf..c76c7b42ab 100644 --- a/src/core/project/backends/cdk/credentials.ts +++ b/src/core/project/backends/cdk/credentials.ts @@ -29,7 +29,7 @@ export type DeployedCredential = { clientSecretArn?: string; /** * Which kind of provider the ARN belongs to. Recorded so a teardown knows which - * providers it owns without the spec that declared them — `project remove all` + * providers it owns without the spec that declared them — `agentcore remove all` * empties the spec before the deploy that tears the target down. */ authorizerType?: CredentialType; diff --git a/src/core/project/backends/cdk/stackReader.ts b/src/core/project/backends/cdk/stackReader.ts index a0a4bf88fb..c4fceaf2b9 100644 --- a/src/core/project/backends/cdk/stackReader.ts +++ b/src/core/project/backends/cdk/stackReader.ts @@ -34,7 +34,7 @@ function cloudFormationDescriber( * * This is only the read: interpreting the stack's status and outputs (deployed * vs. in-progress vs. failed, which outputs to surface) is left to the caller — - * e.g. `project status` — which owns that shape. + * e.g. `agentcore status` — which owns that shape. */ export async function describeStack( region: string, diff --git a/src/core/project/manager.tsx b/src/core/project/manager.tsx index b15b1f89ce..22f6b8f03a 100644 --- a/src/core/project/manager.tsx +++ b/src/core/project/manager.tsx @@ -227,7 +227,7 @@ export class FsProjectManager implements ProjectManager { } // A harness project scaffolds through the same addResource flow that - // `project add harness` uses, so a create-time harness and an added one can + // `agentcore add harness` uses, so a create-time harness and an added one can // never drift apart. if (input.scaffoldHarnessInput) { const scaffolded = await this.resolve({ filePath: destination }); diff --git a/src/handlers/project/add/index.ts b/src/handlers/project/add/index.ts index 5479cad99c..b1f6fe5053 100644 --- a/src/handlers/project/add/index.ts +++ b/src/handlers/project/add/index.ts @@ -38,7 +38,7 @@ export function createAddProjectResourceHandler( // wizard for a bare `add ` on a TTY; it is inert for a resource // declared command-line only above, and for flags, --json and non-TTY runs. projectAdd.use( - withProject({ projectManager: config.projectManager, cwd: process.cwd() }), + withProject({ projectManager: config.projectManager }), withTuiWhenInteractive(core, config.io), ); projectAdd.handler(createAddConfigBundleHandler(config)); diff --git a/src/handlers/project/add/memory/memory.screen.test.tsx b/src/handlers/project/add/memory/memory.screen.test.tsx index e5ab1ae675..f3c751b524 100644 --- a/src/handlers/project/add/memory/memory.screen.test.tsx +++ b/src/handlers/project/add/memory/memory.screen.test.tsx @@ -256,7 +256,7 @@ describe("project add memory wizard", () => { }); // These drive the real CLI entrypoint rather than mounting the screen, because -// what they cover is the routing in front of it: a bare `project add memory` has +// what they cover is the routing in front of it: a bare `agentcore add memory` has // to reach the wizard, and everything else has to stay headless. describe("project add memory dispatch", () => { function buildRoot(io: AppIO) { diff --git a/src/handlers/project/add/runtime/runtime.screen.test.tsx b/src/handlers/project/add/runtime/runtime.screen.test.tsx index 91d080daf2..80fd6f4ddc 100644 --- a/src/handlers/project/add/runtime/runtime.screen.test.tsx +++ b/src/handlers/project/add/runtime/runtime.screen.test.tsx @@ -8,6 +8,7 @@ import { flatFrame, cleanupScreens, createSilentLogger, + initProject, TestCoreClient, TestGlobalConfigAccessor, testIO, @@ -236,7 +237,7 @@ describe("project add runtime wizard", () => { }); // These drive the real CLI entrypoint rather than mounting the screen, because -// what they cover is the routing in front of it: a bare `project add runtime` +// what they cover is the routing in front of it: a bare `agentcore add runtime` // has to reach the wizard, and everything else has to stay headless. describe("project add runtime dispatch", () => { function buildRoot(io: AppIO) { @@ -348,4 +349,20 @@ describe("project add runtime dispatch", () => { expect(await runtimeInSpec(projectRoot, "flag_agent")).toBeDefined(); }, 10000); + + test("resolves the project from the invocation cwd when the root is reused", async () => { + const first = await initProject({ name: "FirstProject" }); + const root = buildRoot(testIO().io); + const second = await initProject({ name: "SecondProject" }); + + try { + await root.route(["node", "agentcore", "add", "runtime", "--name", "after_chdir"]); + + expect(await runtimeInSpec(second.projectRoot, "after_chdir")).toBeDefined(); + expect(await runtimeInSpec(first.projectRoot, "after_chdir")).toBeUndefined(); + } finally { + await second.cleanup(); + await first.cleanup(); + } + }); }); diff --git a/src/handlers/project/create/create.screen.test.tsx b/src/handlers/project/create/create.screen.test.tsx index 4f98030a19..fb111a9759 100644 --- a/src/handlers/project/create/create.screen.test.tsx +++ b/src/handlers/project/create/create.screen.test.tsx @@ -95,7 +95,7 @@ describe("project create wizard", () => { expect(r.lastFrame()).toContain("agentcore deploy"); // The manager received exactly the input the flag-driven handler builds - // for `project create --name DemoApp`. + // for `agentcore create --name DemoApp`. expect(inputs).toEqual([ { name: "DemoApp", diff --git a/src/handlers/project/create/index.ts b/src/handlers/project/create/index.ts index 9f1a754403..3b87e3edc2 100644 --- a/src/handlers/project/create/index.ts +++ b/src/handlers/project/create/index.ts @@ -160,7 +160,7 @@ type HarnessPathFlagValues = { "api-base"?: string; }; -// The harness input validates against the same schema `project add harness` +// The harness input validates against the same schema `agentcore add harness` // uses, before any file is written; the manager then scaffolds it through the // same addResource path. Exported so the TUI create wizard builds its harness // input through the exact same translation as the flag-driven path. diff --git a/src/handlers/project/export/types.ts b/src/handlers/project/export/types.ts index b4d8b7cd12..07760d9930 100644 --- a/src/handlers/project/export/types.ts +++ b/src/handlers/project/export/types.ts @@ -2,7 +2,7 @@ import type { AppIO } from "../../../io"; import type { Core } from "../../types"; import type { ProjectManager } from "../types"; -/** Dependencies for `project export` handlers. */ +/** Dependencies for `agentcore export` handlers. */ export type ExportProjectResourceConfig = { projectManager: ProjectManager; /** Service clients, for exporting a harness fetched by ARN. */ diff --git a/src/handlers/project/project.screen.test.tsx b/src/handlers/project/project.screen.test.tsx deleted file mode 100644 index a85402e614..0000000000 --- a/src/handlers/project/project.screen.test.tsx +++ /dev/null @@ -1,265 +0,0 @@ -import { test, expect, describe, afterEach } from "bun:test"; -import { - renderScreen, - waitForFlatText, - waitForText, - cleanupScreens, - compiledRootCommand, - createSilentLogger, - menuEntries, - TestCoreClient, - TestGlobalConfigAccessor, - testIO, -} from "../../testing"; -import { ExitCode, InvalidEnvironmentError } from "../../errors"; -import { commandParameterDetails } from "../../router"; -import { createRootHandler } from "../index"; -import { - EMPTY_TEMPLATE_NAME, - RUNTIME_TEMPLATE_SHORTCUT_NAMES, - RUNTIME_TEMPLATE_SHORTCUTS, -} from "./shortcuts"; - -afterEach(cleanupScreens); - -// topLevelSubcommands reads the root's children off the compiled Commander -// tree, so tests driven by it cover any command added later. -function topLevelSubcommands(): string[] { - const root = compiledRootCommand(); - return root.commands.map((command) => command.name()); -} - -describe("root menu", () => { - test("lists every top-level command", async () => { - const r = renderScreen("/agentcore"); - - await waitForText(r.lastFrame, "the platform for production AI agents"); - const frame = r.lastFrame()!; - for (const command of topLevelSubcommands()) { - expect(frame).toContain(command); - } - r.unmount(); - }); - - test("project commands are reachable from the root menu", async () => { - const r = renderScreen("/agentcore"); - - await waitForText(r.lastFrame, "the platform for production AI agents"); - await r.write("add"); - await waitForText(r.lastFrame, "❯ add"); - await r.press("return"); - - await waitForText(r.lastFrame, "agentcore → add"); - expect(r.lastFrame()).toContain("runtime"); - r.unmount(); - }); - - test("esc returns to the root menu", async () => { - const r = renderScreen("/agentcore/add"); - - await waitForText(r.lastFrame, "agentcore → add"); - await r.press("escape"); - - await waitForText(r.lastFrame, "the platform for production AI agents"); - r.unmount(); - }); -}); - -// command resolves a compiled command by path, for reading -// the help the CLI-only screen must match. -function command(...path: string[]) { - const root = compiledRootCommand(); - let current = root; - for (const name of path) current = current.commands.find((c) => c.name() === name)!; - return current; -} - -describe("root menu: command-line-only subcommands", () => { - test("create and add runtime expose the shared registry-backed template help", () => { - const createDetails = commandParameterDetails(command("create"))!; - const addRuntimeDetails = commandParameterDetails(command("add", "runtime"))!; - - for (const name of RUNTIME_TEMPLATE_SHORTCUT_NAMES) { - const description = RUNTIME_TEMPLATE_SHORTCUTS[name].description; - expect(createDetails).toContain(name); - expect(createDetails).toContain(description); - expect(addRuntimeDetails).toContain(name); - expect(addRuntimeDetails).toContain(description); - } - expect(createDetails).toContain(EMPTY_TEMPLATE_NAME); - expect(addRuntimeDetails).not.toContain(EMPTY_TEMPLATE_NAME); - }); - - test("are listed below a divider, after the ones with a screen", async () => { - const r = renderScreen("/agentcore"); - - await waitForText(r.lastFrame, "command line only"); - const withScreens = [ - "create", - "add", - "remove", - "deploy", - "invoke", - "status", - "build", - "harness", - "identity", - "runtime", - "memory", - "gateway", - "eval", - ]; - const { screens, cliOnly } = menuEntries(r.lastFrame()!); - expect(screens.toSorted()).toEqual(withScreens.toSorted()); - expect(cliOnly.toSorted()).toEqual( - topLevelSubcommands() - .filter((c) => !withScreens.includes(c)) - .toSorted(), - ); - r.unmount(); - }); - - test("a group drills down to its leaves' help and back", async () => { - const r = renderScreen("/agentcore/add"); - - await waitForText(r.lastFrame, "agentcore → add"); - await r.write("gateway"); - await waitForText(r.lastFrame, "❯ gateway"); - await r.press("return"); - - await waitForText(r.lastFrame, "agentcore → add → gateway"); - const frame = r.lastFrame()!.replace(/\s+/g, " "); - expect(frame).toContain("this command runs from the command line"); - expect(frame).toContain("agentcore add gateway [options]"); - expect(frame).toContain("--authorizer-type"); - - await r.press("escape"); - await waitForText(r.lastFrame, "agentcore → add"); - r.unmount(); - }); - - test("help longer than the terminal scrolls, and the parameter details are reachable", async () => { - // `add gateway-target` has ten options plus a long --target-configuration - // write-up, which `--help` appends as "Parameter details"; at 80×24 most of - // it is below the fold. - const r = renderScreen("/agentcore/add/gateway-target"); - await r.resize(80, 24); - await waitForText(r.lastFrame, "this command runs from the command line"); - expect(r.lastFrame()).not.toContain("curated Connector shortcuts"); - - // Scroll to the end: the write-up's last line is the last thing on the page. - for (let i = 0; i < 80; i++) await r.press("down"); - const bottom = r.lastFrame()!.replace(/\s+/g, " "); - expect(bottom).toContain( - "Use agentcore add gateway-connector for curated Connector shortcuts.", - ); - // …and the heading was on the way. - expect(r.frames.some((frame) => frame.includes("Parameter details:"))).toBe(true); - - for (let i = 0; i < 80; i++) await r.press("up"); - await waitForText(r.lastFrame, "this command runs from the command line"); - r.unmount(); - }); - - // These three exercise the help viewport, so they need a command-line-only - // resource whose help is longer than the terminal: `add payment-manager`. - test("growing the terminal after scrolling to the bottom pulls the content back into view", async () => { - const r = renderScreen("/agentcore/add/payment-manager"); - await r.resize(80, 24); - await waitForText(r.lastFrame, "this command runs from the command line"); - for (let i = 0; i < 80; i++) await r.press("down"); - expect(r.lastFrame()).not.toContain("this command runs from the command line"); - - // Tall enough for the whole help: the offset must fall back to the top - // rather than leave a mostly blank viewport. Height only — a width change - // reflows the content, which would mask a clamp that read a stale height. - await r.resize(80, 120); - await waitForText(r.lastFrame, "this command runs from the command line"); - expect(r.lastFrame()).toContain("--default-spend-limit"); - r.unmount(); - }); - - test("a key that fills its column still stands clear of its value", async () => { - const r = renderScreen("/agentcore/add/payment-manager"); - await r.resize(40, 60); - // Narrow enough that the intro wraps and the key column hits its cap. - await waitForFlatText(r.lastFrame, "this command runs from the command line"); - const lines = r.lastFrame()!.split("\n"); - // "--description " wraps within the capped column… - expect(lines.some((line) => /^\s+\s{2,}\S/.test(line))).toBe(true); - // …and no line runs a key straight into its value (checked case-insensitively; - // this is a terminal layout check, not an HTML filter). - expect(lines.some((line) => /<[a-z-]+>[a-z]/i.test(line))).toBe(false); - r.unmount(); - }); - - test("every option is reachable on a small terminal", async () => { - const r = renderScreen("/agentcore/add/payment-manager"); - await r.resize(80, 24); - await waitForText(r.lastFrame, "this command runs from the command line"); - - const seen = new Set(); - const collect = () => { - for (const match of r.lastFrame()!.matchAll(/--[a-z][a-z-]*/g)) seen.add(match[0]); - }; - collect(); - for (let i = 0; i < 60; i++) { - await r.press("down"); - collect(); - } - const compiled = command("add", "payment-manager"); - for (const option of compiled.options) { - if (option.long && option.long !== "--help") expect(seen).toContain(option.long); - } - r.unmount(); - }); - - test("an unknown top-level path retains the standard help fallback", async () => { - const r = renderScreen("/agentcore/no-such-command"); - await waitForText(() => r.frames.join("\n"), "Usage:"); - expect(r.frames.join("\n")).not.toContain("command line only"); - r.unmount(); - }); -}); - -describe("agentcore (no subcommand)", () => { - // Exercises the real CLI entrypoint; the screen tests mount a path directly - // and so never caught the missing default handler. - // - // Asserts renderTui's TTY guard rather than a rendered frame: Ink only writes - // frames incrementally when interactive (`!isInCi && isTTY`), so asserting on - // frames here would pass locally and time out under CI. Reaching the guard at - // all proves the group routed to the TUI — Commander help neither throws nor - // touches stderr. - test("routes to the TUI rather than printing Commander help", async () => { - const io = testIO(); - const root = createRootHandler(new TestCoreClient(), { - io: io.io, - logger: createSilentLogger(), - globalConfigAccessor: new TestGlobalConfigAccessor(), - }); - - const caught: unknown = await root.route(["node", "agentcore"]).then( - () => undefined, - (error: unknown) => error, - ); - - expect(caught).toBeInstanceOf(InvalidEnvironmentError); - expect((caught as InvalidEnvironmentError).exitCode).toBe(ExitCode.USAGE); - expect(io.stdout()).toBe(""); - }); - - test("prints help instead of the TUI under --json", async () => { - const io = testIO(); - const root = createRootHandler(new TestCoreClient(), { - io: io.io, - logger: createSilentLogger(), - globalConfigAccessor: new TestGlobalConfigAccessor(), - }); - - await root.route(["node", "agentcore", "--json"]); - - expect(io.stdout()).toContain("Usage:"); - expect(io.stdout()).toContain("create"); - }); -}); diff --git a/src/io/openBrowser.ts b/src/io/openBrowser.ts index 1d6bfbe683..9e4990c405 100644 --- a/src/io/openBrowser.ts +++ b/src/io/openBrowser.ts @@ -5,7 +5,7 @@ export type BrowserOpener = (url: string) => Promise; /** * Open a URL in the user's default browser, best-effort: failures resolve * quietly because the URL is always printed as well, so a machine without a - * browser association must not fail `project dev`. + * browser association must not fail `agentcore dev`. */ export const openBrowser: BrowserOpener = (url) => { const [command, args] = diff --git a/src/projectSchemas/aws-targets.ts b/src/projectSchemas/aws-targets.ts index d3dde8a719..b3dc0c5ec3 100644 --- a/src/projectSchemas/aws-targets.ts +++ b/src/projectSchemas/aws-targets.ts @@ -29,7 +29,7 @@ export const AgentCoreRegionSchema = z.enum([ ]); /** - * The target `project deploy` uses when --target is omitted. Only this target + * The target `agentcore deploy` uses when --target is omitted. Only this target * is ever synthesized from the environment when aws-targets.json lacks it; * named targets must be defined explicitly so a typo cannot invent one. */ diff --git a/src/projectSchemas/harness.test.ts b/src/projectSchemas/harness.test.ts index e6f84bb2b9..4f4b26dd20 100644 --- a/src/projectSchemas/harness.test.ts +++ b/src/projectSchemas/harness.test.ts @@ -42,7 +42,7 @@ describe("harness custom validation", () => { }); // The pinned @aws/agentcore-cdk rejects additionalParams on every provider but lite_llm, and // re-parses harness.yaml at synth — so accepting it here would defer the failure to - // `project build` instead of surfacing it at authoring time. + // `agentcore build` instead of surfacing it at authoring time. it("accepts additional parameters only for the lite_llm provider", () => { expect( HarnessModelSchema.safeParse({ diff --git a/src/testing/projects.ts b/src/testing/projects.ts index bd86ca8064..8857cd4f74 100644 --- a/src/testing/projects.ts +++ b/src/testing/projects.ts @@ -8,9 +8,9 @@ import { TestCoreClient } from "./TestCoreClient"; import { TestGlobalConfigAccessor } from "./globalConfig"; export type InitProjectOptions = { - /** Project name passed to `project create`. */ + /** Project name passed to `agentcore create`. */ name?: string; - /** Extra flags appended to the `project create` command, e.g. `["--template", "empty"]`. */ + /** Extra flags appended to the `agentcore create` command, e.g. `["--template", "empty"]`. */ flags?: string[]; /** Temp directory prefix, for recognizable paths while debugging. */ prefix?: string; @@ -25,7 +25,7 @@ export type InitializedProject = { cleanup: () => Promise; }; -/** Scaffolds a project with `project create` and cds into it so withProject resolves it. */ +/** Scaffolds a project with `agentcore create` and cds into it so withProject resolves it. */ export async function initProject(options: InitProjectOptions = {}): Promise { const { name = "TestProject", From 71463bce90863ad03570255fc616efae3f4a46ef Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:44:05 +0000 Subject: [PATCH 10/12] test(project): remove cwd regression coverage --- .../project/add/runtime/runtime.screen.test.tsx | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/src/handlers/project/add/runtime/runtime.screen.test.tsx b/src/handlers/project/add/runtime/runtime.screen.test.tsx index 80fd6f4ddc..2a795d96ee 100644 --- a/src/handlers/project/add/runtime/runtime.screen.test.tsx +++ b/src/handlers/project/add/runtime/runtime.screen.test.tsx @@ -8,7 +8,6 @@ import { flatFrame, cleanupScreens, createSilentLogger, - initProject, TestCoreClient, TestGlobalConfigAccessor, testIO, @@ -349,20 +348,4 @@ describe("project add runtime dispatch", () => { expect(await runtimeInSpec(projectRoot, "flag_agent")).toBeDefined(); }, 10000); - - test("resolves the project from the invocation cwd when the root is reused", async () => { - const first = await initProject({ name: "FirstProject" }); - const root = buildRoot(testIO().io); - const second = await initProject({ name: "SecondProject" }); - - try { - await root.route(["node", "agentcore", "add", "runtime", "--name", "after_chdir"]); - - expect(await runtimeInSpec(second.projectRoot, "after_chdir")).toBeDefined(); - expect(await runtimeInSpec(first.projectRoot, "after_chdir")).toBeUndefined(); - } finally { - await second.cleanup(); - await first.cleanup(); - } - }); }); From 57a1a4c9f6354851f92710802ca273bfa0448395 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:51:06 +0000 Subject: [PATCH 11/12] refactor(project): wire leaf handlers through middleware config --- src/handlers/project/build/index.ts | 4 ++- src/handlers/project/deploy/index.ts | 10 +++++- src/handlers/project/dev/index.ts | 4 ++- src/handlers/project/index.ts | 47 ++++++++++++++++------------ 4 files changed, 42 insertions(+), 23 deletions(-) diff --git a/src/handlers/project/build/index.ts b/src/handlers/project/build/index.ts index c6d5a0444e..cbe0f42ee3 100644 --- a/src/handlers/project/build/index.ts +++ b/src/handlers/project/build/index.ts @@ -1,4 +1,4 @@ -import { createHandler, ProjectKey } from "../../../router"; +import { createHandler, ProjectKey, type Middleware } from "../../../router"; import type { AppIO } from "../../../io"; import { runWithProgress } from "../../../tui/progress"; import { JsonKey } from "../../keys"; @@ -8,6 +8,7 @@ import type { Project, ProjectManager } from "../types"; type BuildProjectHandlerConfig = { projectManager: ProjectManager; io: AppIO; + middlewares?: Middleware[]; }; /** The line both entry points print once a build finishes. */ @@ -19,6 +20,7 @@ export const createBuildProjectHandler = (config: BuildProjectHandlerConfig) => createHandler({ name: "build", description: "build the project's deployable artifacts", + middlewares: config.middlewares, handle: async (ctx) => { // withProject has already resolved the enclosing project. const project = ctx.require(ProjectKey); diff --git a/src/handlers/project/deploy/index.ts b/src/handlers/project/deploy/index.ts index 6eae2d6964..d4eb9cb739 100644 --- a/src/handlers/project/deploy/index.ts +++ b/src/handlers/project/deploy/index.ts @@ -3,7 +3,13 @@ import z from "zod"; import { UserCancellationError } from "../../../errors/errors"; import type { AppIO } from "../../../io"; import { DEFAULT_TARGET_NAME } from "../../../projectSchemas/aws-targets"; -import { createHandler, flag, GlobalConfigAccessorKey, ProjectKey } from "../../../router"; +import { + createHandler, + flag, + GlobalConfigAccessorKey, + ProjectKey, + type Middleware, +} from "../../../router"; import { JsonRendererKey } from "../../../tui"; import { runWithProgress } from "../../../tui/progress"; import { JsonKey, RegionKey } from "../../keys"; @@ -13,6 +19,7 @@ import type { DeployResult, Project, ProjectManager, TeardownConfirmationHandler type DeployProjectHandlerConfig = { projectManager: ProjectManager; io: AppIO; + middlewares?: Middleware[]; }; /** The line both entry points print once a deploy finishes. */ @@ -45,6 +52,7 @@ export const createDeployProjectHandler = (config: DeployProjectHandlerConfig) = createHandler({ name: "deploy", description: "deploy the project to AWS", + middlewares: config.middlewares, flags: [ flag( "target", diff --git a/src/handlers/project/dev/index.ts b/src/handlers/project/dev/index.ts index 1b5ce4f70c..7a389e34e8 100644 --- a/src/handlers/project/dev/index.ts +++ b/src/handlers/project/dev/index.ts @@ -15,7 +15,7 @@ import { UserCancellationError, } from "../../../errors"; import type { AppIO, BrowserOpener, FileWatcher, PortChecker, startHttpServer } from "../../../io"; -import { createHandler, flag, ProjectKey } from "../../../router"; +import { createHandler, flag, ProjectKey, type Middleware } from "../../../router"; import { JsonRendererKey, type JsonRenderer } from "../../../tui"; import { JsonKey, RegionKey } from "../../keys"; import type { Project, ProjectManager } from "../types"; @@ -27,6 +27,7 @@ const UI_DEFAULT_PORT = 8081; export type DevProjectHandlerConfig = { io: AppIO; + middlewares?: Middleware[]; runners: { CodeZip: DevRunner; Container: DevRunner }; loadDevEnvironment: DevEnvironmentLoader; checkPort: PortChecker; @@ -94,6 +95,7 @@ export const createDevProjectHandler = (config: DevProjectHandlerConfig) => createHandler({ name: "dev", description: "run the project locally for development", + middlewares: config.middlewares, flags: [ flag("agent", "Runtime to run", z.string().optional()), flag( diff --git a/src/handlers/project/index.ts b/src/handlers/project/index.ts index dc9bf5320e..f552223274 100644 --- a/src/handlers/project/index.ts +++ b/src/handlers/project/index.ts @@ -39,25 +39,28 @@ export function createProjectHandlers(core: Core, io: AppIO): Handler[] { io, middlewares: [withProjectMiddleware, withTuiWhenInteractive(core, io)], }), - withProjectMiddleware( - createDevProjectHandler({ - projectManager, - io, - runners: { - CodeZip: new CodeZipDevRunner(), - Container: new ContainerDevRunner(), - }, - loadDevEnvironment, - checkPort, - startTraceCollector: startOtelCollector, - startServer: startHttpServer, - openBrowser, - inspectorAssets: new InspectorAssets(), - isInteractive: () => process.stdout.isTTY === true, - watchFile, - }), - ), - withProjectMiddleware(createDeployProjectHandler({ projectManager, io })), + createDevProjectHandler({ + projectManager, + io, + middlewares: [withProjectMiddleware], + runners: { + CodeZip: new CodeZipDevRunner(), + Container: new ContainerDevRunner(), + }, + loadDevEnvironment, + checkPort, + startTraceCollector: startOtelCollector, + startServer: startHttpServer, + openBrowser, + inspectorAssets: new InspectorAssets(), + isInteractive: () => process.stdout.isTTY === true, + watchFile, + }), + createDeployProjectHandler({ + projectManager, + io, + middlewares: [withProjectMiddleware], + }), createProjectInvokeHandler(core, io), createProjectLogHandler(core, io), createProjectTracesHandler(core, io), @@ -65,7 +68,11 @@ export function createProjectHandlers(core: Core, io: AppIO): Handler[] { projectManager, middlewares: [withProjectMiddleware, withTuiWhenInteractive(core, io)], }), - withProjectMiddleware(createBuildProjectHandler({ projectManager, io })), + createBuildProjectHandler({ + projectManager, + io, + middlewares: [withProjectMiddleware], + }), ]; return [createHandler, ...projectBoundHandlers]; From 9a74e1c4aeeabfe2ce0ff418fa4559c777912d11 Mon Sep 17 00:00:00 2001 From: Hweinstock Date: Wed, 23 Sep 2026 22:54:52 +0000 Subject: [PATCH 12/12] revert(project): drop noisy add cwd fix --- src/handlers/project/add/index.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/handlers/project/add/index.ts b/src/handlers/project/add/index.ts index b1f6fe5053..5479cad99c 100644 --- a/src/handlers/project/add/index.ts +++ b/src/handlers/project/add/index.ts @@ -38,7 +38,7 @@ export function createAddProjectResourceHandler( // wizard for a bare `add ` on a TTY; it is inert for a resource // declared command-line only above, and for flags, --json and non-TTY runs. projectAdd.use( - withProject({ projectManager: config.projectManager }), + withProject({ projectManager: config.projectManager, cwd: process.cwd() }), withTuiWhenInteractive(core, config.io), ); projectAdd.handler(createAddConfigBundleHandler(config));