Skip to content

Rework one-click deploy to read persona specs#145

Closed
khaliqgant wants to merge 1 commit into
feat/one-click-read-personafrom
codex/one-click-read-persona-cli
Closed

Rework one-click deploy to read persona specs#145
khaliqgant wants to merge 1 commit into
feat/one-click-read-personafrom
codex/one-click-read-persona-cli

Conversation

@khaliqgant
Copy link
Copy Markdown
Member

Summary

  • Change deploy --one-click to accept a persona.json/persona.ts path instead of an agent manifest
  • Load/compile the persona, derive deploy requirements from PersonaSpec, and render the Layer A plan
  • Delegate to existing cloud deploy with mode=cloud and onExists=update

Validation

  • corepack pnpm --filter @agentworkforce/cli test
  • corepack pnpm --filter @agentworkforce/cli build
  • git diff --check

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented May 26, 2026

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8bf51730-6060-45ce-b2c5-74e87956e8b1

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/one-click-read-persona-cli

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a new --one-click deployment feature to the CLI, allowing users to quickly deploy a persona (JSON or TS) to the cloud by parsing its requirements, resolving inputs, displaying a deployment plan, and executing the deployment. Feedback focuses on fixing a routing bug where the inline --one-click=path format is not correctly recognized, and resolving a logic issue in the plan rendering where required inputs are always shown as "none" because they are filtered out once resolved.

Comment on lines +86 to +89
if (args.includes('--one-click')) {
await runOneClickDeploy(args);
return;
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

The router check args.includes('--one-click') only matches the exact string '--one-click'. If a user passes the inline format --one-click=persona.json (which is explicitly supported and parsed in parseOneClickDeployArgs), the router will fail to delegate to runOneClickDeploy and will instead fall through to the standard deploy command, resulting in an 'unknown flag' error.

Using args.some(...) to check for both formats ensures correct routing.

Suggested change
if (args.includes('--one-click')) {
await runOneClickDeploy(args);
return;
}
if (args.some((arg) => arg === '--one-click' || arg.startsWith('--one-click='))) {
await runOneClickDeploy(args);
return;
}

Comment on lines +344 to +359
function formatOneClickPlan(
persona: PersonaSpec,
requirements: DeployRequirements,
resolvedInputs: Record<string, string>
): string {
const unresolvedInputs = requirements.inputs.filter((input) => !hasResolvedInput(resolvedInputs[input.name]));
return [
'One-click deploy plan',
`Persona: ${persona.id}`,
formatPlanList('Fires on', requirements.firesOn),
formatIntegrationPlan(requirements),
formatInputPlan(unresolvedInputs),
'Platform secrets: none required (shared platform)',
''
].join('\n');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Since resolveOneClickInputs is called before formatOneClickPlan and strictly ensures all required inputs are resolved (or throws an error), unresolvedInputs will always be an empty array. As a result, formatInputPlan(unresolvedInputs) will always return 'Required inputs: none', even if the persona has required inputs that were successfully resolved.

To make the deploy plan accurate and informative, we should pass all required inputs and their resolved values to formatInputPlan so they can be listed.

function formatOneClickPlan( 
  persona: PersonaSpec,
  requirements: DeployRequirements,
  resolvedInputs: Record<string, string>
): string {
  return [
    'One-click deploy plan',
    `Persona: ${persona.id}`,
    formatPlanList('Fires on', requirements.firesOn),
    formatIntegrationPlan(requirements),
    formatInputPlan(requirements.inputs, resolvedInputs),
    'Platform secrets: none required (shared platform)',
    ''
  ].join('\n');
}

Comment on lines +377 to +387
function formatInputPlan(inputs: DeployRequirements['inputs']): string {
if (inputs.length === 0) return 'Required inputs: none';
return [
'Required inputs:',
...inputs.map((input) => {
const env = input.env ? ` env=${input.env}` : '';
const description = input.description ? ` - ${input.description}` : '';
return `- ${input.name}${env}${description}`;
})
].join('\n');
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update formatInputPlan to accept the resolved inputs map and display the resolved values alongside the required inputs.

Suggested change
function formatInputPlan(inputs: DeployRequirements['inputs']): string {
if (inputs.length === 0) return 'Required inputs: none';
return [
'Required inputs:',
...inputs.map((input) => {
const env = input.env ? ` env=${input.env}` : '';
const description = input.description ? ` - ${input.description}` : '';
return `- ${input.name}${env}${description}`;
})
].join('\n');
}
function formatInputPlan(
inputs: DeployRequirements['inputs'],
resolvedInputs: Record<string, string>
): string {
if (inputs.length === 0) return 'Required inputs: none';
return [
'Required inputs:',
...inputs.map((input) => {
const val = resolvedInputs[input.name] ? `=${resolvedInputs[input.name]}` : '';
const env = input.env ? ` env=${input.env}` : '';
const description = input.description ? ` - ${input.description}` : '';
return `- ${input.name}${val}${env}${description}`;
})
].join('\n');
}

assert.match(trap.stdout, /One-click deploy plan/);
assert.match(trap.stdout, /Fires on:\n- github:issues\.opened\n- slack:message\.channels\n- schedule:daily/);
assert.match(trap.stdout, /Integrations:\n- github \(required\): issues\.opened\n- slack \(optional\): message\.channels/);
assert.match(trap.stdout, /Required inputs: none/);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Update the test assertion to match the improved plan output that lists the resolved required inputs.

Suggested change
assert.match(trap.stdout, /Required inputs: none/);
assert.match(trap.stdout, /Required inputs:\n- LEAD=alice env=TRIAGE_LEAD - Owner to mention/);

Copy link
Copy Markdown
Contributor

@cubic-dev-ai cubic-dev-ai Bot left a comment

Choose a reason for hiding this comment

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

2 issues found across 2 files

You’re at about 91% of the monthly reviewed-line limit. You may want to disable incremental reviews to conserve quota. Reviews will continue until that limit is exceeded. If you need help avoiding interruptions, please contact contact@cubic.dev.

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/cli/src/deploy-command.ts">

<violation number="1" location="packages/cli/src/deploy-command.ts:86">
P1: `runDeploy` misses the `--one-click=<path>` form, so valid one-click invocations are routed to the wrong parser.</violation>

<violation number="2" location="packages/cli/src/deploy-command.ts:355">
P2: The plan is built from `unresolvedInputs`, which is empty after successful input resolution, so it misleadingly reports `Required inputs: none` for personas that do require inputs.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

process.exit(args.length === 0 ? 1 : 0);
}

if (args.includes('--one-click')) {
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.

P1: runDeploy misses the --one-click=<path> form, so valid one-click invocations are routed to the wrong parser.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/deploy-command.ts, line 86:

<comment>`runDeploy` misses the `--one-click=<path>` form, so valid one-click invocations are routed to the wrong parser.</comment>

<file context>
@@ -70,6 +83,11 @@ export async function runDeploy(args: readonly string[]): Promise<void> {
     process.exit(args.length === 0 ? 1 : 0);
   }
 
+  if (args.includes('--one-click')) {
+    await runOneClickDeploy(args);
+    return;
</file context>
Suggested change
if (args.includes('--one-click')) {
if (args.some((arg) => arg === '--one-click' || arg.startsWith('--one-click='))) {

`Persona: ${persona.id}`,
formatPlanList('Fires on', requirements.firesOn),
formatIntegrationPlan(requirements),
formatInputPlan(unresolvedInputs),
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.

P2: The plan is built from unresolvedInputs, which is empty after successful input resolution, so it misleadingly reports Required inputs: none for personas that do require inputs.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/cli/src/deploy-command.ts, line 355:

<comment>The plan is built from `unresolvedInputs`, which is empty after successful input resolution, so it misleadingly reports `Required inputs: none` for personas that do require inputs.</comment>

<file context>
@@ -115,6 +133,263 @@ export async function runDeploy(args: readonly string[]): Promise<void> {
+    `Persona: ${persona.id}`,
+    formatPlanList('Fires on', requirements.firesOn),
+    formatIntegrationPlan(requirements),
+    formatInputPlan(unresolvedInputs),
+    'Platform secrets: none required (shared platform)',
+    ''
</file context>

@khaliqgant
Copy link
Copy Markdown
Member Author

Folded into #146 — the CLI commit (dbcd552) has been cherry-picked onto the #146 branch, so persona-kit + CLI now land as a single PR. Closing in favor of #146.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant