feat(nextactions): send Insta-Hints and render Next: affordance#32
Conversation
jwfing
left a comment
There was a problem hiding this comment.
Summary
Small, well-scoped change that sends Insta-Hints: 1 at the single ApiClient.fetch choke point and renders a Next: affordance from the response body after the four workflow-spine commands; the core logic is a pure, unit-tested nextActionsLines() that degrades gracefully on unknown ops.
Requirements context
No matching spec/plan found in this repo. The PR body references docs/superpowers/specs/2026-07-13-platform-next-actions-design.md, but there is no docs/ directory at the PR head (c6d4949) — the design doc and the server contract appear to live in the dependency InsForge/insta-platform#30. This review therefore assesses against the PR description and the CLI's own command surface (src/index.ts). I verified locally: tsc --noEmit is clean and vitest is 50/50 green.
Findings
Critical
(none)
Suggestion
Functionality — metrics/logs mapping passes args.target through verbatim (src/util.ts:47-48). The CLI's metrics/logs commands only accept target: db|compute (src/index.ts:127,130). The default ?? 'compute' is correct and tested, but if the platform ever emits a neutral op with args.target set to the service vocabulary (services are postgres|storage|compute), the rendered hint would be insta metrics postgres — which is not a runnable command. Worth confirming the platform contract (#30) emits db/compute, or normalizing postgres → db here. Non-blocking since I can't see what #30 actually sends.
Software engineering — mapped-op-with-args cases beyond metrics/logs are untested (test/util.test.ts:25-56). Good coverage on mapped/unknown/gated/empty and the metrics/logs default. The deploy branch-flag path (a.branch → --branch), secrets.set, and approvals.approve mappings aren't asserted, so a future edit to those command strings wouldn't be caught by a test. A couple more one-line cases would lock the rendered commands.
Functionality — Next: prints before trailing side-effects in projectCreate (src/commands/project.ts:30). renderNextActions(out.nextActions) runs before tryInstallObserve() and installSkills(), both of which emit their own output. That pushes the Next: block up into scrollback rather than leaving it as the last thing the user sees. Consider rendering hints after the install steps for the create path so the affordance stays at the bottom. Cosmetic.
Information
- Security —
secrets.sethint interpolatesargs.valueinto printed output (src/util.ts:45). The rendered string is only displayed, never executed, so there's no injection risk. Just flagging that if the platform ever populatesargs.valuewith a real secret it would be echoed to the terminal/stdout — the placeholder<value>fallback is the right default; keep it that way server-side. - Security — no auth/authorization surface touched. The only header change is a static
Insta-Hints: 1(src/api.ts:59); no secrets, tokens, or PII are added to requests or responses.nextAction.reason/argsare platform-controlled strings printed viainfo()— displayed, not evaluated. - Functionality — none of the four wired commands (
project create,services add,branch create,deploy) has a--jsonmode, so appending human-readableNext:lines can't corrupt machine-readable output. Reads are correctly sourced from the body:out.nextActionsforrequest()callers andres.body.nextActionsforrawRequest()callers. The gated (HTTP 202) path returns early viahandleApprovalbefore rendering, which is correct. - Performance — no concerns. Pure string building over a small array; no queries, loops of concern, or hot-path work.
Verdict
approved (informational — a human still gives the explicit GitHub approval). Zero Critical findings; the Suggestions above are non-blocking. Clean, tested, conventional diff.
Fermionic-Lyu
left a comment
There was a problem hiding this comment.
LGTM, Approved. (Relaying John-bot's approved verdict — approved with the maintainer account, since John-bot can't approve its own PR.)
There was a problem hiding this comment.
2 issues found across 7 files
Heads up: you’re close to your included review allowance. Set a flex budget so reviews don’t pause.
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="src/util.ts">
<violation number="1" location="src/util.ts:45">
P2: The `deploy` next-action mapping renders `insta deploy [--branch <b>]` but the deploy command requires a `<dir>` or `--image <url>` — running the hint as-is will fail with a usage error. Consider including a placeholder for the required argument, e.g. `insta deploy <dir> --branch <name>`. The mapping also doesn't handle `--group` which the actual command supports and the deploy response includes (`res.body.group`).</violation>
<violation number="2" location="src/util.ts:46">
P3: The `secrets.set` mapping uses `<NAME>` (uppercase) for the name placeholder while `service.add` uses `<name>` (lowercase). Keeps the affordance consistent.</violation>
</file>
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
| // Neutral op → an `insta` command string. Unknown ops fall back to reason-only (no crash). | ||
| const OP_COMMAND: Record<string, (a: Record<string, unknown>) => string> = { | ||
| 'service.add': (a) => `insta services add ${a.type ?? '<type>'} ${a.name ?? '<name>'}`, | ||
| deploy: (a) => `insta deploy${a.branch ? ` --branch ${a.branch}` : ''}`, |
There was a problem hiding this comment.
P2: The deploy next-action mapping renders insta deploy [--branch <b>] but the deploy command requires a <dir> or --image <url> — running the hint as-is will fail with a usage error. Consider including a placeholder for the required argument, e.g. insta deploy <dir> --branch <name>. The mapping also doesn't handle --group which the actual command supports and the deploy response includes (res.body.group).
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/util.ts, line 45:
<comment>The `deploy` next-action mapping renders `insta deploy [--branch <b>]` but the deploy command requires a `<dir>` or `--image <url>` — running the hint as-is will fail with a usage error. Consider including a placeholder for the required argument, e.g. `insta deploy <dir> --branch <name>`. The mapping also doesn't handle `--group` which the actual command supports and the deploy response includes (`res.body.group`).</comment>
<file context>
@@ -37,6 +37,34 @@ export function handleApproval(res: { status: number; body: any }): boolean {
+// Neutral op → an `insta` command string. Unknown ops fall back to reason-only (no crash).
+const OP_COMMAND: Record<string, (a: Record<string, unknown>) => string> = {
+ 'service.add': (a) => `insta services add ${a.type ?? '<type>'} ${a.name ?? '<name>'}`,
+ deploy: (a) => `insta deploy${a.branch ? ` --branch ${a.branch}` : ''}`,
+ 'secrets.set': (a) => `insta secrets set ${a.name ?? '<NAME>'} ${a.value ?? '<value>'}`,
+ metrics: (a) => `insta metrics ${a.target ?? 'compute'}`,
</file context>
| deploy: (a) => `insta deploy${a.branch ? ` --branch ${a.branch}` : ''}`, | |
| deploy: (a) => `insta deploy ${a.dir ?? '<dir>'}${a.branch ? ` --branch ${a.branch}` : ''}${a.group ? ` --group ${a.group}` : ''}`, |
| const OP_COMMAND: Record<string, (a: Record<string, unknown>) => string> = { | ||
| 'service.add': (a) => `insta services add ${a.type ?? '<type>'} ${a.name ?? '<name>'}`, | ||
| deploy: (a) => `insta deploy${a.branch ? ` --branch ${a.branch}` : ''}`, | ||
| 'secrets.set': (a) => `insta secrets set ${a.name ?? '<NAME>'} ${a.value ?? '<value>'}`, |
There was a problem hiding this comment.
P3: The secrets.set mapping uses <NAME> (uppercase) for the name placeholder while service.add uses <name> (lowercase). Keeps the affordance consistent.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/util.ts, line 46:
<comment>The `secrets.set` mapping uses `<NAME>` (uppercase) for the name placeholder while `service.add` uses `<name>` (lowercase). Keeps the affordance consistent.</comment>
<file context>
@@ -37,6 +37,34 @@ export function handleApproval(res: { status: number; body: any }): boolean {
+const OP_COMMAND: Record<string, (a: Record<string, unknown>) => string> = {
+ 'service.add': (a) => `insta services add ${a.type ?? '<type>'} ${a.name ?? '<name>'}`,
+ deploy: (a) => `insta deploy${a.branch ? ` --branch ${a.branch}` : ''}`,
+ 'secrets.set': (a) => `insta secrets set ${a.name ?? '<NAME>'} ${a.value ?? '<value>'}`,
+ metrics: (a) => `insta metrics ${a.target ?? 'compute'}`,
+ logs: (a) => `insta logs ${a.target ?? 'compute'}`,
</file context>
| 'secrets.set': (a) => `insta secrets set ${a.name ?? '<NAME>'} ${a.value ?? '<value>'}`, | |
| 'secrets.set': (a) => `insta secrets set ${a.name ?? '<name>'} ${a.value ?? '<value>'}`, |
Ships: optional project-create name (paste-runnable one-liner, #34), next-action Next: hints (#32), insta compute start/stop/suspend/status lifecycle commands, agents.instacloud.com canonical docs (#31). Claude-Session: https://claude.ai/code/session_01GMbAe5K1RfwaAcge5inX7P Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Summary
Send
Insta-Hints: 1and render aNext:affordance after workflow-spine commands (project create, service add, branch create, deploy).Insta-Hints: 1added at the singleApiClient.fetchchoke point.nextActionsLines()+renderNextActions()insrc/util.tswith a neutral-op→instacommand map; an unknown op degrades to reason-only (never crashes); gated hints are marked. ReadsnextActionsfrom the response body.Depends on InsForge/insta-platform#30. Design:
docs/superpowers/specs/2026-07-13-platform-next-actions-design.md.Test plan
npm run typecheck— cleannpm test— 50/50; unit tests cover mapped/unknown/gated/empty ops and thatmetrics/logsrender with thecomputetarget.🤖 Generated with Claude Code
Summary by cubic
Show a "Next:" hint after project create, service add, branch create, and deploy. Also send
Insta-Hints: 1on all API calls to request server-provided next steps.Insta-Hints: 1inApiClient.fetchto request next actions.nextActionsfrom response bodies and render a "Next:" affordance.nextActionsLines()andrenderNextActions(); unknown ops fall back to reason-only; gated actions are marked.project create,services add,branch create, anddeploy.metrics/logshints tocomputeso commands run out of the box.Written for commit c6d4949. Summary will update on new commits.