1- import { execSync , spawn } from "node:child_process" ;
1+ import { execSync } from "node:child_process" ;
22import { existsSync , readFileSync , readdirSync } from "node:fs" ;
33import { basename , join , resolve } from "node:path" ;
44import * as p from "@clack/prompts" ;
@@ -8,14 +8,14 @@ import {
88 createDatabase ,
99 setupAppSchema ,
1010} from "./mcp/lib/scaffolding.js" ;
11- import { packageRoot } from "./mcp/config.js" ;
1211
13- // Monorepo root (only valid in dev mode)
14- const monorepoRoot = join ( packageRoot , ".." , ".." ) ;
15-
16- function isDevMode ( ) : boolean {
17- const corePath = join ( monorepoRoot , "packages" , "core" ) ;
18- return existsSync ( corePath ) ;
12+ function isClaudeAvailable ( ) : boolean {
13+ try {
14+ execSync ( "claude --version" , { stdio : "ignore" } ) ;
15+ return true ;
16+ } catch {
17+ return false ;
18+ }
1919}
2020
2121function isCwdEmpty ( ) : boolean {
@@ -28,15 +28,6 @@ function isCwdEmpty(): boolean {
2828 }
2929}
3030
31- function isClaudeAvailable ( ) : boolean {
32- try {
33- execSync ( "claude --version" , { stdio : "ignore" } ) ;
34- return true ;
35- } catch {
36- return false ;
37- }
38- }
39-
4031/**
4132 * Poll Tiger Cloud until the service is ready (status != "creating").
4233 * Returns true if ready, false on timeout.
@@ -79,37 +70,62 @@ function isExisting0pflow(): boolean {
7970 }
8071}
8172
82- function launchClaude ( cwd : string , { yolo = false } : { yolo ?: boolean } = { } ) : void {
83- const prompt =
84- "Welcome to your 0pflow project! What workflow would you like to create? Here are some ideas:\n\n" +
85- '- "Enrich leads from a CSV file with company data"\n' +
86- '- "Monitor website uptime and send Slack alerts"\n' +
87- '- "Sync Salesforce contacts to our database nightly"\n' +
88- '- "Score and route inbound leads based on firmographics"\n\n' +
89- "Describe what you'd like to automate and I'll help you build it with /create-workflow." ;
90-
91- const claudeArgs : string [ ] = [ ] ;
92- if ( isDevMode ( ) ) claudeArgs . push ( "--plugin-dir" , monorepoRoot ) ;
93- if ( yolo ) claudeArgs . push ( "--dangerously-skip-permissions" ) ;
94- claudeArgs . push ( prompt ) ;
95-
96- const child = spawn ( "claude" , claudeArgs , {
97- cwd,
98- stdio : "inherit" ,
73+ const WELCOME_PROMPT =
74+ "Welcome to your 0pflow project! What workflow would you like to create? Here are some ideas:\n\n" +
75+ '- "Enrich leads from a CSV file with company data"\n' +
76+ '- "Monitor website uptime and send Slack alerts"\n' +
77+ '- "Sync Salesforce contacts to our database nightly"\n' +
78+ '- "Score and route inbound leads based on firmographics"\n\n' +
79+ "Describe what you'd like to automate and I'll help you build it with /create-workflow." ;
80+
81+ async function launchDevServer ( cwd : string , { yolo = false } : { yolo ?: boolean } = { } ) : Promise < void > {
82+ // Load .env for DATABASE_URL and NANGO_SECRET_KEY
83+ try {
84+ const { resolveEnv } = await import ( "./env.js" ) ;
85+ resolveEnv ( ) ;
86+ } catch {
87+ // Dev UI can work without env
88+ }
89+
90+ // Detect dev mode (running from monorepo source) for --plugin-dir
91+ const { packageRoot } = await import ( "./mcp/config.js" ) ;
92+ const monorepoRoot = resolve ( packageRoot , ".." , ".." ) ;
93+ const pluginDir = existsSync ( resolve ( monorepoRoot , "packages" , "core" ) ) ? monorepoRoot : undefined ;
94+
95+ const { startDevServer } = await import ( "../dev-ui/index.js" ) ;
96+ const { url } = await startDevServer ( {
97+ projectRoot : cwd ,
98+ databaseUrl : process . env . DATABASE_URL ,
99+ nangoSecretKey : process . env . NANGO_SECRET_KEY ,
100+ claudePluginDir : pluginDir ,
101+ claudeSkipPermissions : yolo ,
102+ claudePrompt : WELCOME_PROMPT ,
99103 } ) ;
100- child . on ( "exit" , ( code ) => process . exit ( code ?? 0 ) ) ;
104+
105+ // Open browser
106+ try {
107+ const cmd = process . platform === "darwin" ? "open" : process . platform === "win32" ? "start" : "xdg-open" ;
108+ execSync ( `${ cmd } ${ url } ` , { stdio : "ignore" } ) ;
109+ } catch {
110+ // Non-fatal — user can open manually
111+ }
101112}
102113
103114export async function runInit ( ) : Promise < void > {
104115 p . intro ( pc . red ( "0pflow" ) + pc . dim ( " — create a new project" ) ) ;
105116
117+ if ( ! isClaudeAvailable ( ) ) {
118+ p . log . error ( "Claude Code CLI not found. Install it from https://claude.ai/code" ) ;
119+ process . exit ( 1 ) ;
120+ }
121+
106122 // ── Existing project check ──────────────────────────────────────────
107123 if ( isExisting0pflow ( ) ) {
108124 const action = await p . select ( {
109125 message : "This directory is already an 0pflow project." ,
110126 options : [
111- { value : "claude" as const , label : "Launch Claude Code " } ,
112- { value : "claude-yolo" as const , label : "Launch Claude Code with --dangerously-skip-permissions" } ,
127+ { value : "claude" as const , label : "Launch Dev UI " } ,
128+ { value : "claude-yolo" as const , label : "Launch Dev UI with --dangerously-skip-permissions" } ,
113129 { value : "new" as const , label : "Create a new project in a subdirectory" } ,
114130 ] ,
115131 } ) ;
@@ -120,12 +136,8 @@ export async function runInit(): Promise<void> {
120136 }
121137
122138 if ( action === "claude" || action === "claude-yolo" ) {
123- if ( ! isClaudeAvailable ( ) ) {
124- p . log . error ( "Claude Code CLI not found. Install it from https://claude.ai/code" ) ;
125- process . exit ( 1 ) ;
126- }
127- p . outro ( pc . green ( "Launching Claude Code..." ) ) ;
128- launchClaude ( process . cwd ( ) , { yolo : action === "claude-yolo" } ) ;
139+ p . outro ( pc . green ( "Launching Dev UI..." ) ) ;
140+ await launchDevServer ( process . cwd ( ) , { yolo : action === "claude-yolo" } ) ;
129141 return ;
130142 }
131143 // fall through to normal wizard (cwdEmpty will be false, so directory defaults to ./<name>)
@@ -388,39 +400,28 @@ export async function runInit(): Promise<void> {
388400 }
389401 }
390402
391- // ── Launch Claude? ──────────────────────────────────────────────────
392- const hasClaude = isClaudeAvailable ( ) ;
393-
394- if ( hasClaude ) {
395- const launchChoice = await p . select ( {
396- message : "Launch Claude Code to design your first workflow?" ,
397- options : [
398- { value : "claude" as const , label : "Yes" } ,
399- { value : "claude-yolo" as const , label : "Yes, with --dangerously-skip-permissions" } ,
400- { value : "no" as const , label : "No, I'll do it later" } ,
401- ] ,
402- } ) ;
403+ // ── Launch Dev UI? ──────────────────────────────────────────────────
404+ const launchChoice = await p . select ( {
405+ message : "Launch Dev UI to design your first workflow?" ,
406+ options : [
407+ { value : "claude" as const , label : "Yes" } ,
408+ { value : "claude-yolo" as const , label : "Yes, with --dangerously-skip-permissions" } ,
409+ { value : "no" as const , label : "No, I'll do it later" } ,
410+ ] ,
411+ } ) ;
403412
404- if ( ! p . isCancel ( launchChoice ) && launchChoice !== "no" ) {
405- p . outro ( pc . green ( "Launching Claude Code..." ) ) ;
406- launchClaude ( resolve ( appPath ) , { yolo : launchChoice === "claude-yolo" } ) ;
407- return ;
408- }
413+ if ( ! p . isCancel ( launchChoice ) && launchChoice !== "no" ) {
414+ p . outro ( pc . green ( "Launching Dev UI..." ) ) ;
415+ await launchDevServer ( resolve ( appPath ) , { yolo : launchChoice === "claude-yolo" } ) ;
416+ return ;
409417 }
410418
411419 // ── Done ────────────────────────────────────────────────────────────
412420 const cdCmd = directory === "." ? "" : `cd ${ directory } && ` ;
413- const pluginFlag = isDevMode ( ) ? ` --plugin-dir ${ monorepoRoot } ` : "" ;
414- const claudeCmd = `${ cdCmd } claude${ pluginFlag } ` ;
415421
416422 p . outro ( pc . green ( "Project created!" ) ) ;
417423 console . log ( ) ;
418- console . log ( pc . bold ( " To launch Claude Code later:" ) ) ;
419- console . log ( pc . cyan ( ` ${ claudeCmd } ` ) ) ;
420- console . log ( ) ;
421- console . log ( pc . bold ( " Example prompts to get started:" ) ) ;
422- console . log ( pc . dim ( ' "Create a workflow that enriches leads from a CSV file"' ) ) ;
423- console . log ( pc . dim ( ' "Build a workflow that monitors website uptime and sends Slack alerts"' ) ) ;
424- console . log ( pc . dim ( ' "Create a data pipeline that syncs Salesforce contacts to our database"' ) ) ;
424+ console . log ( pc . bold ( " To launch the Dev UI later:" ) ) ;
425+ console . log ( pc . cyan ( ` ${ cdCmd } 0pflow dev` ) ) ;
425426 console . log ( ) ;
426427}
0 commit comments