Skip to content

feat: close the eight essential CLI feature gaps - #2146

Open
AlexanderRichey wants to merge 12 commits into
refactorfrom
feat/essential-gaps
Open

feat: close the eight essential CLI feature gaps#2146
AlexanderRichey wants to merge 12 commits into
refactorfrom
feat/essential-gaps

Conversation

@AlexanderRichey

Copy link
Copy Markdown
Contributor

Closes the eight essential feature gaps between the original CLI and the refactor, one commit per gap, each verified live against AWS account 501930284170 (us-east-1) where the gap has a deployable surface.

Gaps closed

1. Harness-first project creation — feat(create): harness-first project creation

Bare project create --name X (and --defaults) now creates a harness project, matching the original's default quick start. Harness-only flags (--model-id, --api-key-arn, --api-base, --additional-params, --no-harness-memory, --max-iterations, --max-tokens, --timeout, --truncation-strategy, --container) flow into the harness spec; the create-time harness scaffolds through the same addResource path as project add harness; mixing harness and runtime-scaffolding flags is a validation error mirroring the original's dispatch.
Live e2e:project create --name GapE2eHarness0830 --model-id us.amazon.nova-lite-v1:0 → zero-config project deploy → stack AgentCore-GapE2eHarness0830-default CREATE_COMPLETE (us-east-1) → harness invoke returned a real Nova Lite response. Stack destroyed after (DELETE_COMPLETE).

2. Bedrock Agent import — feat(create): import a Bedrock Agent as a runtime

project create --type import --agent-id --agent-alias-id (and project add runtime --type import) describes the agent/alias via @aws-sdk/client-bedrock-agent (new dependency), validates the region against the supported list, and scaffolds a proxy runtime from the new bedrock-agent-proxy-python template (InvokeAgent + streamed reply; vended bedrock-agent-policy.json grants bedrock:InvokeAgent via additionalPolicies). Deliberate shape difference: the original translated the agent definition to framework code; this ships the proxy form per the parity spec.
Live e2e: created throwaway Bedrock Agent 6AHPCYGV7S (Nova Lite) + alias JJUMUT2PCQ + service role; imported; deployed AgentCore-GapE2eImport0830-default CREATE_COMPLETE; runtime invokedata: "The capital of France is Paris."; then destroyed the stack and deleted the agent, alias, and role.

3. Export harness → Strands runtime — feat(project): export a harness to a Strands runtime agent

New project export harness (--name for an in-project harness, --arn for any service harness — region-aware from the ARN; --target-agent-name, --build CodeZip|Container, --json). Renders editable Strands code under app/<target>/ through the stock strands template's export context (model config for all four harness providers, system prompt, remote-MCP tools with credential env wiring, inline functions, skills, in-project memory, truncation, execution limits via a vendored hooks/execution_limits.py), registers the runtime in agentcore.json (the harness entry is kept, mirroring the original), writes EXPORT_NOTES.md for anything unmappable, refuses overwrites, and validates the project before any service fetch (the 0.28.1 fix). 73 new unit tests.
Live e2e:GapE2eExport0830 — deploy #1 CREATE_COMPLETE with harness GapE2eExport0830_exportme-ZFg6mVuUYn READY → export → project build → deploy #2 UPDATE_COMPLETE → runtime invoke on the exported runtime streamed a live Nova Lite reply ("Exported agent reporting for duty."); the --arn path was exercised against the deployed harness and built. Stack destroyed (DELETE_COMPLETE).

4. Complete project remove + remove allfeat(remove): cover all addable resources and add remove all

The remove enum now covers every addable type (adds credential, config-bundle, online-eval, online-insight, memory) with .env.local cleanup for credentials; removing a nonexistent resource raises ResourceNotFoundError instead of warn-and-rewrite; project remove all resets every spec collection behind a y/N confirmation with --yes.
Live e2e:GapE2eRemove0830 — added all five newly-removable types, removed them (spec + .env.local verified), deployed CREATE_COMPLETE, remove all --yes, then deploy --yes teardown path → DELETE_COMPLETE.

5. runtime logsfeat(runtime): logs streaming and search (+ refactor(core): extract shared observability helpers)

The precursor commit extracts the runtime log-group derivation and the multi-log-group, fully-paginated Logs Insights runner out of core/eval into src/core/observability.ts (zero behavior change; eval re-pointed) and adds the time-string parser (now, 30s/5m/1h/2d, epoch ms, ISO 8601). runtime logs then ships follow mode by default (StartLiveTail with auto-reconnect over the 3-hour session cap; Ctrl-C exits 130 cleanly) and search mode on --since/--until (FilterLogEvents, paginated, oldest→newest), with --level, --query (composed into a CloudWatch filter pattern), --limit, and --json JSON-Lines. --id works anywhere; inside a project the runtime id resolves live from the target stack's outputs via DescribeStacks (single runtime auto-selects; several → error listing candidates), with the deployment target's region taking precedence.
Live e2e:GapE2eObs0830 deployed CREATE_COMPLETE; 4 invokes; search returned real events; --level/--query/--limit/--json verified; follow mode captured a mid-stream invocation marker and exited 130 on SIGINT; auto-resolve without --id (even with a wrong ambient --region) found the events. Stack destroyed after.

6. runtime tracesfeat(runtime): traces list and get

runtime traces list (--id/project auto-resolve, --limit default 20, --since default 12h, --until, --json) aggregates recent traceIds with timestamps and session ids via the shared Insights runner (filtering empty-traceId infra records before aggregation — fixes an off-by-one the original had). runtime traces get <traceId> validates the id format and downloads every record to --output or agentcore/.cli/traces/<runtime>-<traceId>.json. No hardcoded console URLs (deep link deliberately skipped).
Live e2e: all four invocations' traces listed with matching session ids ~3 minutes after invoking; traces get wrote 10 records to the default project path; invalid/missing trace ids and missing log group produce guided errors.

7. --versionfeat(cli): add --version

Bare agentcore --version/-V prints the build-time version (verified under bun run and in the bundled dist/index.js). Implemented as a root-only pre-parse intercept so leaf --version <value> flags (harness version get, etc.) are not shadowed.

8. project create TUI wizard — feat(tui): project create wizard

Bare agentcore project create in a terminal now opens a wizard instead of exiting with NotImplementedError: name (validated live against ProjectNameSchema, schema messages shown inline) → project type (harness — the default, per gap 1 — or scaffolded agent) → harness model id or agent template with the strands memory choice (hello-world skips the memory question) → confirmation summary → live ProjectManager progress → success screen with cd/deploy next steps. No forked creation logic: the wizard builds the same CreateProjectInput as the flag-driven handler (resolveScaffoldHarnessInput, now exported, for the harness path; resolveRuntimeTemplateShortcut for templates) and iterates core.projectManager.create in the cwd, npm install and git init included; a create() failure renders and exits nonzero via useApp().exit(error). Dispatch is conservative: only a bare, flagless invocation on a TTY opens the wizard (withTuiOnEmptyFlagsAndArgs, TTY-gated at registration) — any user-supplied flag, --json, or a non-TTY session behaves exactly as before, enforced by making --name optional at the Commander layer and re-validated in the handler with the identical error wording. The other six project subcommands keep their not-implemented stubs. 16 ink-testing-library tests cover the flows, validation, esc navigation, error teardown, and all four dispatch cases.
Live check: wizard driven end-to-end in a real pty — name typed, harness type and default model accepted, review confirmed, creation ran (npm install + git init), success screen, exit 0 — producing a project spec-identical to the flag path's. Doing so surfaced a pre-existing UI-kit bug: Ink hands coalesced keystrokes (and every terminal paste) to useInput as one chunk whose key.return is false, so TextInput stored a trailing \r as an invisible character that failed name validation with a message the user couldn't act on. Fixed in fix(ui): strip control bytes from coalesced text-input chunks (C0/DEL stripped from appended text, matching ink-text-input's \r/\n handling; stripped newlines deliberately don't submit), with a paste regression test.

Verification

  • Full gate green on the merged result: bun test (2513 pass, 0 fail, 184 files), bun run typecheck, bun run lint:check.
  • Final live smoke on the merged build: project create --name GapE2eSmoke0831 (bare → harness project) → zero-config project deploy (target auto-provisioned: account 501930284170, us-east-1) → stack AgentCore-GapE2eSmoke0831-default CREATE_COMPLETE (verified via DescribeStacks) with harness READY → harness invoke returned the requested reply verbatim ("smoke test ok") → runtime logs --id harness_GapE2eSmoke0831_GapE2eSmoke0831-H1UyMN5OLm --since 15m returned real CloudWatch events from the runtime log group; bare runtime logs without --id in this harness-only project fails with the intended actionable error ("stack exports no runtime ids … pass --id" — harness stacks export an AgentRuntimeArn but no RuntimeId output, and the auto-resolver's contract is runtime outputs). Stack destroyed after (confirmed gone via DescribeStacks).
  • All e2e stacks/agents/roles created for this work were destroyed; CDKToolkit bootstrap stacks remain in us-east-1, us-west-2, eu-west-1 (pre-existing).

Also included: a leading chore commit that scopes bun test/tsc to the source tree (generated out/ scaffolds and stale dist/ output were failing the gate) and fixes two react(set-state-in-effect) findings newer oxlint raises.

Stacked on #2145 (feat(deploy): auto-provision the default deployment target from STS): its commit is the base of this branch because every live e2e here rides the zero-config deploy. The diff reduces to just this work once #2145 merges.

🤖 Generated with Claude Code

https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P

AlexanderRicheyand others added 12 commits August 30, 2026 23:01
`project deploy` now synthesizes the `default` target when
aws-targets.json is missing, empty, or lacks a `default` entry: the
account comes from STS GetCallerIdentity and the region from the CLI's
already-resolved effective region, validated against
AgentCoreRegionSchema before anything is written. Existing entries are
preserved byte-for-byte, the synthesized entry is reported on stderr,
and the deploy proceeds in the same invocation. Named targets still
require explicit configuration, and unsupported regions or
unresolvable credentials fail with actionable errors before the file
is touched.
Also restores the vended CDK app (bin/cdk.ts, lib/cdk-stack.ts,
test/cdk.test.ts, package.json) to its last publishable state: it
referenced an AgentCorePayments L3 that no released @aws/agentcore-cdk
exports, so every fresh scaffold failed `cdk synth` at tsc before
reaching AWS.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
…eck gate
Leftover e2e scaffolds under out/ (and stale dist/ build output) were being
swept up by bun test and tsc, failing the gate on files that are not part of
the source tree. Scope both to the repo source, and fix the two
react(set-state-in-effect) findings oxlint 1.80 raises in usePagedList and
DataTable by deriving/adjusting state during render instead of in effects.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Bare `project create --name X` (and --defaults) now creates a harness
project — a managed agent configured by spec — matching the original CLI's
default quick start. The harness-only flags (--model-id, --api-key-arn,
--api-base, --additional-params, --no-harness-memory, --max-iterations,
--max-tokens, --timeout, --truncation-strategy, --container) flow into the
harness spec, which is validated up front against the same schema
`project add harness` uses and scaffolded through the same addResource
path, so the two entry points cannot drift. Runtime scaffolding is selected
by --template or the runtime flags exactly as before; mixing the two flag
families is a validation error, mirroring the original's dispatch.
Tests that relied on the previous hello-world default now scaffold it
explicitly via --template.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
`project create --type import --agent-id <id> --agent-alias-id <id>` (and
the same on `project add runtime`) wraps an existing Amazon Bedrock Agent
as an AgentCore runtime: the agent and alias are described through
@aws-sdk/client-bedrock-agent to validate they exist (with the global
--region naming the agent's region, validated against the supported list),
and a proxy runtime is scaffolded from a new bedrock-agent-proxy-python
template that forwards prompts via InvokeAgent and streams the reply. The
vended bedrock-agent-policy.json grants the execution role
bedrock:InvokeAgent on the alias through the runtime's additionalPolicies,
so the project deploys and invokes like any other.
The original CLI translated the Bedrock Agent's definition into native
framework code; this ships the proxy shape instead — the agent stays the
brain, the runtime is the AgentCore front door.
bun.lock: adds the @aws-sdk/client-bedrock-agent entry; the surrounding
churn is bun regenerating the lock format on install.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
project remove now accepts credential, config-bundle, online-eval,
online-insight, and memory; removing a credential also deletes the
.env.local keys it reserved (EnvLocalFile gains removeKeys with
snapshot/rollback). Removing a resource that does not exist now throws
ResourceNotFoundError instead of warn-and-rewrite. New `project remove
all` empties every resource collection in agentcore.json (spec-level;
app/ code and aws-targets.json are kept) behind a y/N prompt with
--yes for non-interactive use.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Bare `agentcore --version` (and -V) prints the package version embedded at
build time and exits 0, in dev, bundled, and compiled forms. Handled as a
pre-parse intercept on the root router rather than a Commander version
option: a root-level --version option would shadow subcommands that declare
their own `--version <value>` flag (harness version get, runtime version
get, dataset --version, …). Root-only, matching the original CLI.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
`agentcore project export harness` converts a harness into an editable
Python Strands runtime agent: it renders the strands-http-python template
under app/<target-agent-name>/ with a context mapped from the harness spec
(model incl. Bedrock Mantle/OpenAI/Gemini/LiteLLM, system prompt, remote
MCP + inline-function tools, path/s3/git skills, in-project memory,
truncation, and execution limits via a vendored hooks/execution_limits.py),
registers the runtime in agentcore.json (the harness entry stays), installs
its deps with uv sync, and writes an EXPORT_NOTES.md in the agent directory
listing precise manual follow-ups for everything that cannot be mapped
mechanically (gateway/browser/code-interpreter tools, external/managed
memory, aws skills, custom Dockerfiles, ...).
The harness comes from `--name` (in-project files) or `--arn` (fetched from
the service via GetHarness, using the region embedded in the ARN); exactly
one is required, and the project is resolved and validated by withProject
before any service fetch. `--target-agent-name` defaults to
<harnessName>Agent, `--build` overrides the CodeZip default (containerUri /
dockerfile harnesses auto-select Container), and `--json` emits a
machine-readable summary on stdout while progress and notes stay on stderr.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Move the runtime log-group derivation, the Insights query-value sanitizer,
and the paginating CloudWatch Logs Insights query runner out of the private
scope of src/core/eval.tsx into src/core/observability.ts so the upcoming
runtime logs/traces commands can share them. The runner's row-ceiling error
becomes an injectable policy (InsightsRowLimit); eval passes its existing
message, so behavior is unchanged. Also adds parseTimeString, porting the
old CLI's time-parser semantics (now / relative 5m-1h-2d / epoch ms / ISO
8601) with a typed InputValidationError on invalid input.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Adds `agentcore runtime logs`: by default it live-tails the runtime's
CloudWatch log group via StartLiveTail (reconnecting transparently over the
~3h session cap; Ctrl+C exits 130), and with --since/--until it switches to
a bounded FilterLogEvents search, paginated to completion oldest-to-newest.
--level/--query compose into a server-side filter pattern (ported from the
old CLI's filter-pattern.ts), --limit caps search results, and --json emits
JSON Lines. A missing log group maps to "has the runtime been invoked yet?"
guidance.
Addressing follows `runtime invoke --id`; without --id inside a project the
deployed runtime is resolved live from the target stack's CloudFormation
outputs (default target, one runtime auto-selects, several list candidates),
through a new ObservabilityClient wired into CoreClient and the Core
contract. The runtime router's TUI dispatch is limited to its existing
children so a bare `runtime logs` streams instead of opening the TUI. The
gitignore's blanket `logs` entry is scoped to the repo root so the handler
directory is trackable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Adds `agentcore runtime traces list` and `agentcore runtime traces get
<traceId>`. list aggregates the runtime's telemetry with a Logs Insights
`stats ... by traceId` query (newest first, default 12h window, --limit
default 20) and renders a traceId/timestamp/sessionId table, or a single
JSON document with --json; an empty result prints a stderr notice that
traces take 2-3 minutes to appear. get validates the trace-id format,
downloads every log record of the trace (@message JSON-parsed when
possible), writes them to --output or agentcore/.cli/traces/
<runtime>-<traceId>.json inside a project (./<traceId>.json outside), and
prints the written path on stdout.
Both leaves share `runtime logs`' addressing: --id anywhere, or automatic
resolution of the project's deployed runtime from its stack outputs. The
queries run through the shared Insights runner extracted in the earlier
observability refactor.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
A bare `agentcore project create` in a terminal now opens an interactive
wizard: name (validated live against ProjectNameSchema, schema messages
inline) → project type → harness model id or agent template (with the
strands memory choice; hello-world skips it) → confirmation summary →
live ProjectManager progress → success screen with next steps. The
wizard builds the same CreateProjectInput as the flag-driven handler —
resolveScaffoldHarnessInput (now exported) for the harness path,
resolveRuntimeTemplateShortcut for templates — and iterates
core.projectManager.create in the cwd, npm install and git init
included. A create() failure renders and tears the TUI down via
useApp().exit(error), so the process exits nonzero.
Dispatch: only a bare, flagless invocation on a TTY opens the wizard
(withTuiOnEmptyFlagsAndArgs, TTY-gated at registration). Any
user-supplied flag, --json, or a non-TTY session stays headless. To make
the middleware reachable, --name is optional at the Commander layer and
enforced inside handle with the same "required option '--name <name>'
not specified" wording — so `project create --defaults` and bare
non-TTY invocations fail exactly as before. The other six project
subcommands keep their not-implemented stubs.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
Ink delivers rapidly typed keystrokes and every terminal paste as one
multi-character useInput event whose key.return is false even when the
chunk ends in "\r" — so TextInput appended the control byte into the
value as an invisible character. In the create wizard's name step that
left a visually valid name failing ProjectNameSchema with a message the
user couldn't act on ("must ... contain only alphanumeric characters")
and enter refusing to advance. Found by driving the wizard end-to-end
in a real pty during the final live smoke.
A single-line input must never store control bytes: strip C0 controls
and DEL from appended text (the official ink-text-input strips \r/\n
for the same reason). A stripped chunk's newline is deliberately not a
submit — matching readline and browser single-line paste semantics.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XfADH54TZn2SSGUUixft8P
@github-actionsgithub-actionsBot added the size/xl PR size: XL label Aug 31, 2026
@agentcore-devx-automationagentcore-devx-automationBot added claude-security-reviewing Claude Code /security-review in progress agentcore-harness-reviewing AgentCore Harness review in progress labels Aug 31, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

Claude Security Review: no high-confidence findings. (run)

@agentcore-devx-automationagentcore-devx-automationBot removed claude-security-reviewing Claude Code /security-review in progress agentcore-harness-reviewing AgentCore Harness review in progress labels Aug 31, 2026
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xlPR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant

@AlexanderRichey