diff --git a/install.sh b/install.sh index 3637e0f..a8937e5 100644 --- a/install.sh +++ b/install.sh @@ -48,10 +48,17 @@ fi # other insta on PATH shadowing ours? (shells use the first hit) first_hit="$(command -v insta 2>/dev/null || true)" if [ -n "$first_hit" ] && [ "$first_hit" != "$INSTALL_DIR/$BIN" ]; then - echo "! another insta is first on your PATH: $first_hit" - case "$first_hit" in - */node_modules/*|*npm*|*/.nvm/*) echo "! (npm-installed — update it with: npm update -g insta, or remove it to use the binary)" ;; - esac + # Not a conflict if the first hit is just the symlink WE created into an on-PATH dir + # (see the linking step below) — it resolves straight back to our binary. Warn only for a + # genuinely different insta (e.g. an npm-installed one) that would actually shadow ours. + if [ -L "$first_hit" ] && [ "$(readlink "$first_hit" 2>/dev/null)" = "$INSTALL_DIR/$BIN" ]; then + : # our own symlink → the binary; nothing to warn about + else + echo "! another insta is first on your PATH: $first_hit" + case "$first_hit" in + */node_modules/*|*npm*|*/.nvm/*) echo "! (npm-installed — update it with: npm update -g insta, or remove it to use the binary)" ;; + esac + fi fi # ---- detect platform ---- @@ -168,7 +175,10 @@ fi # ---- next steps (the 3-command wow: real infra, then a full isolated clone of it) ---- echo -echo "Get started:" -echo " insta login --oauth github # cloud — or run insta-oss locally and skip this" -echo " insta project create demo && insta deploy . --port 3000" -echo " insta branch create preview # clones db + storage + app into an isolated env" +echo "Next steps:" +echo " insta login --oauth github # connect to the cloud (or run insta-oss locally to skip)" +echo " insta project create demo # postgres + storage + compute, provisioned in one shot" +echo " insta deploy . --port 3000 # ship your app and get a live URL" +echo " insta branch create preview # clone db + storage + app into an isolated env" +echo +echo "Your coding agents now know InstaCloud — you can just ask them to do the above." diff --git a/src/commands/setup.ts b/src/commands/setup.ts index 6ab3c31..b1a08fb 100644 --- a/src/commands/setup.ts +++ b/src/commands/setup.ts @@ -73,12 +73,15 @@ export function summarizeInstall(output: string): string { export type Runner = (cmd: string, args: string[]) => Promise<{ ok: boolean; output?: string }> -// Capture stdout+stderr silently (don't stream) so we can print our own clean summary. We keep -// the child's stdin inherited in case the tool ever needs a TTY, but with -y it shouldn't. +// Capture stdout+stderr silently (don't stream) so we can print our own clean summary. +// stdin is 'ignore', NOT 'inherit': under the canonical `curl … | sh` install, stdin is the +// piped install script itself — a child that inherits it (npx/skills reads for keypresses even +// with -y) consumes the rest of the script, so the shell never runs the trailing "Get started" +// guidance. Ignoring stdin keeps the installer's own output intact. (-y means no prompt anyway.) const defaultRunner: Runner = (cmd, args) => new Promise((resolve) => { const env = { ...process.env, AI_AGENT: process.env.AI_AGENT || 'insta', FORCE_COLOR: '0' } - const p = spawn(cmd, args, { stdio: ['inherit', 'pipe', 'pipe'], env }) + const p = spawn(cmd, args, { stdio: ['ignore', 'pipe', 'pipe'], env }) let output = '' const grab = (chunk: Buffer) => { output += chunk.toString() } p.stdout?.on('data', grab) @@ -95,7 +98,7 @@ export async function setupAgent(opts: { yes?: boolean }, run: Runner = defaultR if (!opts.yes && !process.stdout.isTTY) { info('non-interactive shell — assuming -y') } - info('setting up coding-agent skills … (~20s)') + info('setting up coding-agent skills …') const res = await run('npx', SETUP_ARGS) if (!res.ok) { info(' skill install failed — install manually with:')