Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,8 @@ The TUI harness provides MCP tools for programmatically driving the AgentCore CL

### Getting Started

1. Run `npm run build` to compile the project before using the harness tools.
1. Run `npm run build:harness` to compile both the CLI and the MCP harness binary. The harness is dev-only tooling and
is not included in the standard `npm run build`.
2. Call `tui_launch` to start a TUI session. It returns a `sessionId` that all subsequent tool calls require.
- `tui_launch({})` with no arguments defaults to `command="node"`, `args=["dist/cli/index.mjs"]` (the AgentCore CLI).
- The `cwd` parameter determines what the TUI sees: if `cwd` is a directory with an `agentcore.config.json`, the TUI
Expand Down Expand Up @@ -357,6 +358,6 @@ When `tui_send_keys` doesn't change the screen:

When `tui_launch` returns an error:

1. Ensure `npm run build` was run recently -- the CLI binary at `dist/cli/index.mjs` must be up to date.
1. Ensure `npm run build:harness` was run recently -- both the CLI binary and the MCP harness must be up to date.
2. Check that `cwd` points to a valid directory.
3. The error response includes the screen content at time of failure -- use it to diagnose.
14 changes: 10 additions & 4 deletions esbuild.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -61,11 +61,17 @@ fs.chmodSync('./dist/cli/index.mjs', '755');

console.log('CLI build complete: dist/cli/index.mjs');

// MCP harness build — only run if the entry point source exists.
// The source file is created separately; skip gracefully until then.
// ---------------------------------------------------------------------------
// MCP harness build — opt-in via BUILD_HARNESS=1
//
// The TUI harness is dev-only tooling for AI agents and integration tests.
// It is NOT shipped to end users. Build it separately with:
// BUILD_HARNESS=1 node esbuild.config.mjs
// npm run build:harness
// ---------------------------------------------------------------------------
const mcpEntryPoint = './src/tui-harness/mcp/index.ts';

if (fs.existsSync(mcpEntryPoint)) {
if (process.env.BUILD_HARNESS === '1' && fs.existsSync(mcpEntryPoint)) {
await esbuild.build({
entryPoints: [mcpEntryPoint],
outfile: './dist/mcp-harness/index.mjs',
Expand All @@ -91,6 +97,6 @@ if (fs.existsSync(mcpEntryPoint)) {
fs.chmodSync('./dist/mcp-harness/index.mjs', '755');

console.log('MCP harness build complete: dist/mcp-harness/index.mjs');
} else {
} else if (process.env.BUILD_HARNESS === '1') {
console.log(`MCP harness build skipped: entry point ${mcpEntryPoint} does not exist yet`);
}
151 changes: 151 additions & 0 deletions integ-tests/tui/add-flow.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/**
* Integration tests for TUI add-resource flow.
*
* Verifies navigation into the Add Resource screen, drilling into the
* Add Agent wizard, and backing out via Escape at each level.
*
* These tests launch the real CLI entry point against a minimal project
* directory (no npm install required) and interact with the Ink-based TUI
* through the headless PTY harness.
*/
import { TuiSession, isAvailable } from '../../src/tui-harness/index.js';
import { createMinimalProjectDir } from './helpers.js';
import { join } from 'path';
import { afterEach, describe, expect, it } from 'vitest';

const CLI_ENTRY = join(process.cwd(), 'dist/cli/index.mjs');

describe.skipIf(!isAvailable)('TUI add-resource flow', () => {
let session: TuiSession | undefined;
let cleanup: (() => Promise<void>) | undefined;

afterEach(async () => {
if (session?.alive) await session.close();
session = undefined;
await cleanup?.();
cleanup = undefined;
});

// ---------------------------------------------------------------------------
// (a) Navigate to Add Resource screen
// ---------------------------------------------------------------------------
it('navigates from HelpScreen to Add Resource screen', async () => {
const project = await createMinimalProjectDir();
cleanup = project.cleanup;

session = await TuiSession.launch({
command: 'node',
args: [CLI_ENTRY],
cwd: project.dir,
});

// Wait for the HelpScreen to render with its command list.
await session.waitFor('Commands', 10000);

// Type 'add' to filter the command list, then press Enter to select it.
await session.sendKeys('add');
await session.waitFor('add', 3000);
await session.sendSpecialKey('enter');

// Confirm the Add Resource screen has rendered.
const screen = await session.waitFor('Add Resource', 10000);
const text = screen.lines.join('\n');

expect(text).toContain('Agent');
expect(text).toContain('Memory');
expect(text).toContain('Identity');
});

// ---------------------------------------------------------------------------
// (b) Navigate to Add Agent wizard
// ---------------------------------------------------------------------------
it('navigates from Add Resource to Add Agent wizard', async () => {
const project = await createMinimalProjectDir();
cleanup = project.cleanup;

session = await TuiSession.launch({
command: 'node',
args: [CLI_ENTRY],
cwd: project.dir,
});

await session.waitFor('Commands', 10000);

// Navigate to Add Resource screen.
await session.sendKeys('add');
await session.waitFor('add', 3000);
await session.sendSpecialKey('enter');
await session.waitFor('Add Resource', 10000);

// Agent is the first item in the list -- press Enter to select it.
await session.sendSpecialKey('enter');

// Wait for the Add Agent wizard to appear. It may show "Add Agent"
// as a title or prompt for "Agent name".
const screen = await session.waitFor(/Add Agent|Agent name/, 10000);
const text = screen.lines.join('\n');

// The screen should contain some form of agent name input prompt.
expect(text).toMatch(/Add Agent|Agent name/);
});

// ---------------------------------------------------------------------------
// (c) Back from Add Agent to Add Resource
// ---------------------------------------------------------------------------
it('returns from Add Agent to Add Resource via Escape', async () => {
const project = await createMinimalProjectDir();
cleanup = project.cleanup;

session = await TuiSession.launch({
command: 'node',
args: [CLI_ENTRY],
cwd: project.dir,
});

await session.waitFor('Commands', 10000);

// Navigate: HelpScreen -> Add Resource -> Add Agent
await session.sendKeys('add');
await session.waitFor('add', 3000);
await session.sendSpecialKey('enter');
await session.waitFor('Add Resource', 10000);
await session.sendSpecialKey('enter');
await session.waitFor(/Add Agent|Agent name/, 10000);

// Press Escape to go back to Add Resource.
await session.sendSpecialKey('escape');

const screen = await session.waitFor('Add Resource', 5000);
const text = screen.lines.join('\n');
expect(text).toContain('Add Resource');
});

// ---------------------------------------------------------------------------
// (d) Back from Add Resource to HelpScreen
// ---------------------------------------------------------------------------
it('returns from Add Resource to HelpScreen via Escape', async () => {
const project = await createMinimalProjectDir();
cleanup = project.cleanup;

session = await TuiSession.launch({
command: 'node',
args: [CLI_ENTRY],
cwd: project.dir,
});

await session.waitFor('Commands', 10000);

// Navigate to Add Resource screen.
await session.sendKeys('add');
await session.waitFor('add', 3000);
await session.sendSpecialKey('enter');
await session.waitFor('Add Resource', 10000);

// Press Escape to go back to HelpScreen.
await session.sendSpecialKey('escape');

const screen = await session.waitFor('Commands', 5000);
const text = screen.lines.join('\n');
expect(text).toContain('Commands');
});
});
Loading