Skip to content

jack vibe: launch agent interactively with intent prompt #11

@hellno

Description

@hellno

Motivation

When a user runs jack vibe "build a chat app with rooms", they want to watch an AI agent build it and jump in when needed. Today the flow is fragmented:

  • jack vibe runs the agent in one-shot mode (claude -p with --permission-mode acceptEdits) — the user can't interact
  • jack new --open launches the agent interactively but with a generic prompt ("say hi and offer to help build") — the user's intent is lost
  • There's no flow that combines both: create project → launch agent interactively with the user's intent → user watches and takes over

This matters more now that jack creates projects in a central location (~/.jack/projects/), because the user doesn't need to think about where to put things. The ideal UX is:

jack vibe "real-time chat with persistent rooms"
→ project created at ~/.jack/projects/chat-rooms/
→ deployed to https://user-chat-rooms.runjack.xyz
→ Claude Code opens interactively with the intent as its first prompt
→ user watches it work, jumps in whenever they want

Current State

What exists

jack vibe "<intent>" (index.ts) — alias for jack new with intent: args[0]

  • Calls newProject() which creates the project from a template
  • If intent is provided, runs runAgentOneShot() (non-interactive claude -p) to customize the project
  • After one-shot completes, the session ends — user has to manually open the project

jack new --open (new.ts:112-143) — launches agent interactively after project creation

  • Calls launchAgent() which spawns claude <prompt> with stdio: "inherit" (interactive)
  • The initial prompt is built by buildInitialPrompt() — only includes project name and URL
  • Does NOT include the user's intent phrase
  • User's intent is lost between the one-shot customization and the interactive launch

buildInitialPrompt() (agents.ts:434-439):

function buildInitialPrompt(context: AgentLaunchContext): string | null {
  if (!context.url) return null;
  return `Project "${context.projectName}" is live at ${context.url}\n\nRead CLAUDE.md and AGENTS.md for project context, then say hi and offer to help build.`;
}
  • Generic "say hi" prompt — doesn't tell the agent what to build
  • Only fires if URL exists
  • AgentLaunchContext has no intent field

runAgentOneShot() (agents.ts:640-915):

  • Runs claude -p "<prompt>" --permission-mode acceptEdits --output-format stream-json
  • Non-interactive — user sees status updates but can't intervene
  • Good for automated customization, wrong for "watch and take over" UX

launchAgent() (agents.ts:472-506):

  • Spawns claude <prompt> with stdio: "inherit" — fully interactive
  • User can take over immediately
  • This is the right primitive for the desired UX

Key insight: claude <prompt> (no -p flag) is interactive

claude -p = pipe mode (non-interactive, for automation)
claude <prompt> = starts interactive session with initial prompt (user can take over)

Jack already uses the interactive form in launchAgent(). The gap is wiring the intent through.

Proposed Changes

1. Add intent to AgentLaunchContext

export interface AgentLaunchContext {
  projectName?: string;
  url?: string | null;
  intent?: string;  // User's intent phrase from `jack vibe`
}

2. Update buildInitialPrompt() to include intent

function buildInitialPrompt(context: AgentLaunchContext): string | null {
  const parts: string[] = [];

  if (context.url) {
    parts.push(`Project "${context.projectName}" is live at ${context.url}`);
  }

  if (context.intent) {
    parts.push(`Intent: "${context.intent}"`);
    parts.push('Read CLAUDE.md and AGENTS.md for project context, then implement the intent above.');
    parts.push('The project is already deployed — use `jack ship` to redeploy after changes.');
  } else {
    parts.push('Read CLAUDE.md and AGENTS.md for project context, then say hi and offer to help build.');
  }

  return parts.length > 0 ? parts.join('\n\n') : null;
}

3. Change jack vibe default behavior

When intent is provided:

  1. Create project from best-match template (existing)
  2. Deploy (existing)
  3. Instead of one-shot → launch agent interactively with the intent as the initial prompt
  4. User sees Claude Code start working, can take over any time

The one-shot path could remain available via a flag (--headless or --auto) for CI/scripting use cases.

4. Wire intent through jack new --open

Pass intent to launchAgent():

const launchResult = await launchAgent(preferred.launch, result.targetDir, {
  projectName: result.projectName,
  url: result.workerUrl,
  intent: options.intent,  // from jack vibe's intent phrase
});

Files to Change

File Change
apps/cli/src/lib/agents.ts Add intent to AgentLaunchContext, update buildInitialPrompt()
apps/cli/src/commands/new.ts Pass intent to launchAgent() when --open or via jack vibe
apps/cli/src/index.ts Wire jack vibe to use interactive launch instead of one-shot

Non-Goals

  • Changing one-shot mode (runAgentOneShot) — it's useful for headless/CI
  • Adding new agent integrations — use existing launchAgent() primitive
  • Changing project creation location — ~/.jack/projects/ is already the default

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions