Skip to content

feat(cli): gate standalone resource command families - #2396

Merged
Hweinstock merged 12 commits into
aws:refactorfrom
aidandaly24:feat/gate-imperative-command-families
Sep 24, 2026
Merged

Hweinstock merged 12 commits into
aws:refactorfrom
aidandaly24:feat/gate-imperative-command-families

Conversation

@aidandaly24

@aidandaly24 aidandaly24 commented Sep 23, 2026 •

Copy link
Copy Markdown
Contributor

This PR hides Harness, Identity, Runtime, Memory, Gateway, and Payment commands by default, keeping the CLI and TUI focused on project workflows and evals. A configuration gate (imperative-commands) controls which command families are registered, so command execution, help, and menus stay consistent. The existing Gateway mutation gate and project workflows are preserved, and I've updated the documentation to reflect the default command surface.

The commands are disabled by default or can be disabled using this command:

AWS_PROFILE=deploy bun run src/index.ts config imperative-commands false

To enable the commands:

AWS_PROFILE=deploy bun run src/index.ts config imperative-commands true
Screenshot 2026-09-24 at 10 45 08 AM Screenshot 2026-09-24 at 10 44 58 AM

@github-actions github-actions Bot added the size/xl PR size: XL label Sep 23, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added agentcore-harness-reviewing AgentCore Harness review in progress claude-security-reviewing Claude Code /security-review in progress labels Sep 23, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 23, 2026
@codecov-commenter

codecov-commenter commented Sep 23, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.25%. Comparing base (5250b42) to head (6026e60).

Additional details and impacted files
@@            Coverage Diff            @@
##           refactor    #2396   +/-   ##
=========================================
  Coverage     97.25%   97.25%           
=========================================
  Files           612      612           
  Lines         41004    41019   +15     
=========================================
+ Hits          39878    39893   +15     
  Misses         1126     1126           

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@agentcore-devx-automation agentcore-devx-automation Bot removed the agentcore-harness-reviewing AgentCore Harness review in progress label Sep 23, 2026

@agentcore-devx-automation agentcore-devx-automation Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AgentCore Harness Review

Verdict: Looks good

Nice, tightly scoped gating change. The imperative-commands flag mirrors the existing imperative-mutation-commands pattern, and the test matrix in src/handlers/imperative.test.tsx covers the important combinations (parent undefined/false/true × mutations off/on, CLI rejection without Core calls, root registration without config IO, TUI redirect for hidden menus, CLI-only fallback preservation).

A few things I specifically checked and are fine:

  • TUI redirect for hidden commands — RouterScreen now resolves the command first and issues a <Navigate replace> to the resolved path when the requested path is unreachable. Tests cover harness, runtime/endpoint, harness/version, and the CLI-only payment fallback.
  • Test seeding consistency — Tests that need imperative commands now pass IMPERATIVE_GLOBAL_CONFIG to both createRootHandler({ globalConfig }) and new TestGlobalConfigAccessor({ initialConfigData }), so the synchronous snapshot and any downstream accessor reads agree.
  • Doc generator — optional: true groups are dropped only when the command isn't discovered; missing (ungrouped) commands still fail validation, and unknown now only reports required groups.
  • Gateway mutation gate stays independent — Enabling the parent doesn't auto-enable Gateway CUD; the existing imperative-mutation-commands behavior is preserved and tested.

No serious issues; nothing to change before merge from my side.

@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@aidandaly24
aidandaly24 marked this pull request as ready for review September 24, 2026 14:45
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@github-actions github-actions Bot added size/xl PR size: XL and removed size/xl PR size: XL labels Sep 24, 2026
@agentcore-devx-automation agentcore-devx-automation Bot added the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026
@agentcore-devx-automation

Copy link
Copy Markdown
Contributor

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

@agentcore-devx-automation agentcore-devx-automation Bot removed the claude-security-reviewing Claude Code /security-review in progress label Sep 24, 2026

@jariy17 jariy17 left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

* Schema for the global config file. All fields should be optional with defaults defined.
*/
export const globalConfigFileSchema = z.object({
"imperative-commands": z.boolean().optional(),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is the interaction between these two flags? will imperative-mutation-commands only work if both are enabled?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes it would only work if you have the imperative command enabled as well. So both would need to be set to true. In my opinion we could probably remove the other flag in general, but I thought it was OOS from this PR and could be done separately.

Comment thread src/handlers/index.tsx
logger: Logger;
globalConfigAccessor: GlobalConfigAccessor;
/** Resolved startup settings used when constructing command trees. */
globalConfig?: GlobalConfig;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

whats the difference between globalConfig and globalConfigAccessor? can we remove globalConfig in favor of the accessor?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

globalConfig is the settings object already loaded by await globalConfigAccessor.get() at startup. We use those values when building the command tree, so disabled commands are excluded from help and TUI menus.

globalConfigAccessor is still needed by handlers such as config to read and save settings. Passing both does not load the file twice. The accessor caches the loaded value.

Using only the accessor is possible, but its get() is asynchronous while command-tree construction is currently synchronous, so that would require a separate API change.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This system came from the first PR where I hid the gateway CUD commands and so this is mainly a trade off coming from specifically hiding commands based on saved configuration. If you think it'd be worth it to make the accesssor's get() synchronous then I could make the change in this PR.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I looked into this further. The current setup works, and I’d probably lean to keep it for this PR. We could remove the separate globalConfig argument by making the builder read from the accessor, but that would also require changing the shared test setup and its callers to all await renderScreen. I think that cleanup belongs in a separate PR where we also possibly remove the old mutation config flag. The accessor’s get() can stay async.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I see, thanks for explaining. perhaps moving the compile to be async makes sense as a future clean up to avoid what feels like duplicate parameters.

My concern is that its difficult to know where each is used without looking at the code, which means callers have to know the implementation details of createRootHandler to properly use it.

};
}

export function renderImperativeScreen(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if i called this method with options that don't include the feature flag, wouldn't the imperative screen not be rendered?

If so, that feels misleading.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test helper uses an imperative enabled preset when globalConfig is omitted, but honors explicitly supplied config, including a disabled flag. We could rename it to renderScreenWithImperativeDefaults to make clear that it supplies defaults rather than forcing imperative commands on.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would it be easier to inline?

renderScreen(path, {
    ...options,
    globalConfig: IMPERATIVE_GLOBAL_CONFIG,
  });

i know its more code, but then its explicit about what config its using. not a blocker.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I was looking into this for a bit. I think this is probably the cleanest method.

Comment thread src/tui/tui.test.tsx
io: io.io,
logger: createSilentLogger(),
globalConfigAccessor: new TestGlobalConfigAccessor(),
globalConfigAccessor: new TestGlobalConfigAccessor({ initialConfigData: globalConfig }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why do we need to pass global config twice?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We seed the fake accessor with the same settings used to build the command tree. Otherwise, enabling commands through the globalConfig leaves the fake config service reporting that they’re disabled. These tests don’t test that read, but keeping the fixture consistent will avoid that mismatch.

// and open their help instead (see CliOnlyScreen).
export function RouterScreen({ ctx, path, tuiOnlyCommands = [] }: RouterScreenProps) {
export function RouterScreen(props: RouterScreenProps) {
const command = resolveCommand(props.ctx.require(CommandKey), props.path);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why exactly does the behavior here need to change? How is this change related to the goal of putting the resource commands behind a feature flag?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The issue is that some existing actions on those screens still return to the Harness menu afterward. Hiding the command won't remove the existing navigation paths. The check essentially says if if this menu isn’t available anymore then go to an available parent menu before showing any choices. When testing this it worked well for me, but there could be a better solution for this.

@Hweinstock

Copy link
Copy Markdown
Contributor

approving and merging to unblock other work.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size/xl PR size: XL

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants