diff --git a/src/cli/commands/add/__tests__/add-agent.test.ts b/src/cli/commands/add/__tests__/add-agent.test.ts index cee0dfbeb..3b6a36c36 100644 --- a/src/cli/commands/add/__tests__/add-agent.test.ts +++ b/src/cli/commands/add/__tests__/add-agent.test.ts @@ -272,6 +272,49 @@ describe('add agent command', () => { const agent = projectSpec.runtimes.find((a: { name: string }) => a.name === agentName); expect(agent, 'Agent should be in agentcore.json').toBeTruthy(); expect(agent.codeLocation.includes(codeDir), `codeLocation should reference ${codeDir}`).toBeTruthy(); + + // Existing user code must not be overwritten by the starter stub + const entrypoint = await readFile(join(projectDir, codeDir, 'main.py'), 'utf-8'); + expect(entrypoint).toBe('# existing code\n'); + }); + + it('vends a runtime-contract starter at the resolved --entrypoint for a fresh Python BYO agent', async () => { + const agentName = `ByoStub${Date.now()}`; + const codeDir = `stub-agent-${Date.now()}`; + + const result = await runCLI( + [ + 'add', + 'agent', + '--name', + agentName, + '--type', + 'byo', + '--code-location', + codeDir, + '--entrypoint', + 'agent.py', + '--language', + 'Python', + '--framework', + 'Strands', + '--model-provider', + 'Bedrock', + '--json', + ], + projectDir + ); + + expect(result.exitCode, `stdout: ${result.stdout}`).toBe(0); + + // Honors --entrypoint: written to agent.py, not a hardcoded main.py + expect(await exists(join(projectDir, codeDir, 'main.py'))).toBe(false); + const entrypoint = await readFile(join(projectDir, codeDir, 'agent.py'), 'utf-8'); + expect(entrypoint).toContain('from bedrock_agentcore.runtime import BedrockAgentCoreApp'); + expect(entrypoint).toContain('app = BedrockAgentCoreApp()'); + expect(entrypoint).toContain('@app.entrypoint'); + expect(entrypoint).toContain('app.run()'); + expect(entrypoint).not.toContain('def handler(event, context)'); }); it('requires code-location for BYO path', async () => { diff --git a/src/cli/primitives/AgentPrimitive.tsx b/src/cli/primitives/AgentPrimitive.tsx index 80ffa0fbf..c0d3d8875 100644 --- a/src/cli/primitives/AgentPrimitive.tsx +++ b/src/cli/primitives/AgentPrimitive.tsx @@ -74,12 +74,13 @@ import { requireTTY } from '../tui/guards/tty'; import type { GenerateConfig, MemoryOption } from '../tui/screens/generate/types'; import { BasePrimitive } from './BasePrimitive'; import { CredentialPrimitive } from './CredentialPrimitive'; +import { BYO_PYTHON_ENTRYPOINT_STUB, BYO_TYPESCRIPT_ENTRYPOINT_STUB } from './constants'; import { buildAuthorizerConfigFromJwtConfig, createManagedOAuthCredential } from './auth-utils'; import { computeDefaultCredentialEnvVarName } from './credential-utils'; import type { AddResult, AddScreenComponent, RemovableResource } from './types'; import { DescribeSubnetsCommand, EC2Client } from '@aws-sdk/client-ec2'; import type { Command } from '@commander-js/extra-typings'; -import { mkdirSync } from 'fs'; +import { existsSync, mkdirSync, writeFileSync } from 'fs'; import { dirname, join } from 'path'; /** @@ -671,6 +672,23 @@ export class AgentPrimitive extends BasePrimitive )} - {!isCreate && ( - - - Copy your agent code to {config.codeLocation} before deploying. - - - Ensure {config.entrypoint} is the entrypoint file in that folder. - - - )} + {!isCreate && + (config.language === 'Python' || config.language === 'TypeScript' ? ( + + + Your agent entrypoint is {config.entrypoint} in{' '} + {config.codeLocation}. A starter was added if it did not already exist — edit it + with your agent logic. + + + ) : ( + + + Copy your agent code to {config.codeLocation} before deploying. + + + Ensure {config.entrypoint} is the entrypoint file in that folder. + + + ))} {isImport && config.bedrockAgentId && ( diff --git a/src/cli/tui/screens/create/CreateScreen.tsx b/src/cli/tui/screens/create/CreateScreen.tsx index 9b9e7907f..b77f0e9d5 100644 --- a/src/cli/tui/screens/create/CreateScreen.tsx +++ b/src/cli/tui/screens/create/CreateScreen.tsx @@ -94,8 +94,15 @@ function buildExitMessage( // BYO code location reminder if (agentConfig?.agentType === 'byo') { - lines.push(`\x1b[33mCopy your agent code to \x1b[36m${agentConfig.codeLocation}\x1b[33m before deploying.\x1b[0m`); - lines.push(`\x1b[2mEnsure \x1b[36m${agentConfig.entrypoint}\x1b[2m is the entrypoint file in that folder.\x1b[0m`); + const vendedStub = agentConfig.language === 'Python' || agentConfig.language === 'TypeScript'; + if (vendedStub) { + lines.push( + `\x1b[2mYour agent entrypoint is \x1b[36m${agentConfig.entrypoint}\x1b[2m in \x1b[36m${agentConfig.codeLocation}\x1b[2m. A starter was added if it didn't already exist — edit it with your agent logic.\x1b[0m` + ); + } else { + lines.push(`\x1b[33mCopy your agent code to \x1b[36m${agentConfig.codeLocation}\x1b[33m before deploying.\x1b[0m`); + lines.push(`\x1b[2mEnsure \x1b[36m${agentConfig.entrypoint}\x1b[2m is the entrypoint file in that folder.\x1b[0m`); + } lines.push(''); }