Skip to content

feat(setup): --create [name] — provision a new project during agent setup - #159

Merged
Fermionic-Lyu merged 3 commits into
mainfrom
feat/setup-create
Aug 29, 2026
Merged

feat(setup): --create [name] — provision a new project during agent setup#159
Fermionic-Lyu merged 3 commits into
mainfrom
feat/setup-create

Conversation

@Fermionic-Lyu

@Fermionic-Lyu Fermionic-Lyu commented Aug 29, 2026

Copy link
Copy Markdown
Member

insta setup agent --project <id> binds the working directory to an existing project. A fresh machine with no project yet still needed a second command, so this adds the other half.

--create [name]

npx -y insta@latest setup agent --create           # project named after this directory
npx -y insta@latest setup agent --create my-app

Runs the same insta project create path in-process, after the same interactive login offer --project flows through. Behaviour is deliberately identical to --project on every edge:

--project <id> --create [name]
no session (-y, non-TTY, declined login) skipped + manual hint, exit 0 same
operation fails (bad id / name taken) exit 1, stops before the success summary same
interactive TTY, no session offers browser login, then acts same

What planProject does and does not reject

The only thing rejected up front is --create together with --project — a contradictory request whose verdict cannot change, so it fails while the machine is still untouched (same reasoning as the existing --env / $INSTA_API_URL conflict).

Name resolution stays in projectCreate. An earlier revision of this branch also rejected a generic cwd (~/projects, /tmp) up front, which meant --create pasted in the most likely directory aborted the whole setup with nothing installed — the opposite of both siblings, since insta project create treats that case as guidance at exit 0 and --project never aborts setup at all.

Tests

5 added in test/setup-agent.test.ts. The ordering guard is mutation-verified: moving planProject below switchEnv/ensure turns it red.

npm run typecheck and npm test (47 files, 672 tests) pass.

Docs: mirrored into skills/insta/cli-reference.mdInsForge/insta-skills#65.

…setup

`--project <id>` binds the directory to an existing project; a fresh machine
with no project yet still needed a second command. `--create` runs the same
`insta project create` path in-process, and `planProject` rejects the one
request that cannot succeed — both flags at once — before the CLI, skill or
MCP have landed.

Name resolution stays in `projectCreate`: a generic cwd gets that command's
guidance rather than aborting a setup that has installed nothing yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@agent-zhang-beihai agent-zhang-beihai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed by Yang Dong

This adds project provisioning to agent setup by routing --create through the existing creation flow. Named projects work, but the nameless form creates incorrectly named projects on native Windows, so I would not merge it as it stands.

Nameless --create uses the full Windows path as the project name

important · defect · correctness · src/commands/setup.ts:475

When --create has no name, this passes undefined to projectCreate, whose pre-existing resolver extracts the directory using only /. On native Windows, a cwd such as C:\Users\me\my-app therefore becomes c-users-me-my-app rather than my-app, and that wrong name is provisioned and linked; this change depends on that pre-existing problem not existing. Use node:path’s platform-aware basename for both cwd and home resolution.

Evidence

read-the-codesrc/index.ts:91-97, src/commands/setup.ts:385-400, src/commands/setup.ts:467-475, src/commands/project.ts:39-58, src/commands/project.ts:68-81, test/create-name.test.ts:9-27, .claude/skills/developing-insta-cli/SKILL.md:40-47

@agent-zhang-beihai agent-zhang-beihai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Reviewed by Wang Miao

--create [name] extends the existing --project one-liner path so setup can provision a fresh project in the same process, with the flag pair rejected up front (before the env switch and CLI install) and creation failures stopping setup with exit 1 — the same contract --project already had. The plan/execute split is tested at both ends, all 672 tests and typecheck pass here, and the create path reuses projectCreate rather than re-implementing provisioning. I'd merge it; one cosmetic issue below that doesn't block.

The printed retry command echoes the raw --create value, not the name that would actually be created

minor · defect · correctness · src/commands/setup.ts:469

retry interpolates project.name verbatim, but projectCreate resolves the name through slugifyName (src/commands/project.ts:39-41). For insta setup agent --create "My App" with no session, the hint prints insta project create My App; commander 12 keeps excess args, so pasting it creates a project called my rather than the my-app setup would have made — a silently different name, not an error. Common single-token values are already slugs so this is invisible for the console-generated one-liner; interpolating slugifyName(project.name) (or quoting it) makes the two paths agree.

Evidence

read-the-codesrc/commands/setup.ts:462-482, src/commands/project.ts:39-68, src/index.ts:113 (project create [name]); verified commander 12.1.0 excess-argument behaviour against the vendored node_modules/commander.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

3 issues found across 3 files

You’re at about 93% 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="src/commands/setup.ts">

<violation number="1" location="src/commands/setup.ts:400">
P2: On native Windows, bare `--create` provisions a name derived from the entire drive path rather than this directory because `projectCreate` receives `undefined`. Make the default-name resolution platform-safe before using this callback.</violation>

<violation number="2" location="src/commands/setup.ts:469">
P2: When a named create fails, the printed retry command is not equivalent for names containing spaces or shell punctuation. Emit the normalized slug (or shell-escape the name) instead of interpolating `project.name` directly.</violation>

<violation number="3" location="src/commands/setup.ts:475">
P2: If `projectCreate` fails after its POST, this catch tells the user to create the project again even though the server-side project already exists. Preserve or surface the created project ID and direct recovery to linking it, rather than retrying a non-idempotent create.</violation>
</file>

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

Re-trigger cubic

Comment thread src/commands/setup.ts
try {
await link(opts.project)
if (project.kind === 'link') await link(project.id)
else await create(project.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: If projectCreate fails after its POST, this catch tells the user to create the project again even though the server-side project already exists. Preserve or surface the created project ID and direct recovery to linking it, rather than retrying a non-idempotent create.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/setup.ts, line 475:

<comment>If `projectCreate` fails after its POST, this catch tells the user to create the project again even though the server-side project already exists. Preserve or surface the created project ID and direct recovery to linking it, rather than retrying a non-idempotent create.</comment>

<file context>
@@ -441,24 +457,27 @@ export async function setupAgent(
       try {
-        await link(opts.project)
+        if (project.kind === 'link') await link(project.id)
+        else await create(project.name)
       } catch (e) {
         // Stop here — like the skill-install failure above, finishing with the success summary
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Declining — the window is one non-idempotent line, and it is identical on insta project create.

After the POST, projectCreate calls writeProject, then tryInstallObserve (wrapped in try/catch, project.ts:19-27) and installSkills (whose entire body is inside a try/catch, ensure-skills.ts:80-102) — neither can throw. Only writeProject can, so the reachable case is a project created server-side with ./.insta/project.json unwritten.

That case predates this PR: insta project create my-app fails the same way, and rerunning it is the same second project. Distinguishing pre- from post-POST failure means restructuring projectCreate, which belongs with that command rather than with a flag that calls it.

Comment thread src/commands/setup.ts Outdated
// code instead of pretending setup succeeded.
if (project.kind !== 'none') {
const linking = project.kind === 'link'
const retry = linking ? `insta project link ${project.id}` : `insta project create${project.name ? ` ${project.name}` : ''}`

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When a named create fails, the printed retry command is not equivalent for names containing spaces or shell punctuation. Emit the normalized slug (or shell-escape the name) instead of interpolating project.name directly.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/setup.ts, line 469:

<comment>When a named create fails, the printed retry command is not equivalent for names containing spaces or shell punctuation. Emit the normalized slug (or shell-escape the name) instead of interpolating `project.name` directly.</comment>

<file context>
@@ -441,24 +457,27 @@ export async function setupAgent(
+  // code instead of pretending setup succeeded.
+  if (project.kind !== 'none') {
+    const linking = project.kind === 'link'
+    const retry = linking ? `insta project link ${project.id}` : `insta project create${project.name ? ` ${project.name}` : ''}`
     if (!loggedIn) {
-      info(`  not logged in — project not linked; run \`insta login\`, then \`insta project link ${opts.project}\``)
</file context>
Suggested change
const retry = linking ? `insta project link ${project.id}` : `insta project create${project.name ? ` ${project.name}` : ''}`
const retry = linking ? `insta project link ${project.id}` : `insta project create${project.name ? ` ${project.name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '').slice(0, 40)}` : ''}`

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in eae0de0 — the hint now prints slugifyName(project.name), so --create "My App" says insta project create my-app: the name setup itself would have made, and one that survives a paste.

Comment thread src/commands/setup.ts
stdoutTty: !!process.stdout.isTTY,
},
link: (id: string) => Promise<void> = projectLink,
create: (name?: string) => Promise<void> = (n) => projectCreate(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.

P2: On native Windows, bare --create provisions a name derived from the entire drive path rather than this directory because projectCreate receives undefined. Make the default-name resolution platform-safe before using this callback.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/setup.ts, line 400:

<comment>On native Windows, bare `--create` provisions a name derived from the entire drive path rather than this directory because `projectCreate` receives `undefined`. Make the default-name resolution platform-safe before using this callback.</comment>

<file context>
@@ -384,10 +397,13 @@ export async function setupAgent(
     stdoutTty: !!process.stdout.isTTY,
   },
   link: (id: string) => Promise<void> = projectLink,
+  create: (name?: string) => Promise<void> = (n) => projectCreate(n, {}),
 ): Promise<void> {
   if (!opts.yes && !process.stdout.isTTY) {
</file context>

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Declining — pre-existing, and unchanged in kind by this PR.

resolveProjectName splits on / only on origin/main too: git show origin/main:src/commands/project.ts line 51 is cwd.split('/').filter(Boolean).pop(), and every case in test/create-name.test.ts uses a POSIX path. So insta project create with no argument already produces c-users-me-my-app on native Windows; --create reaches the same resolver with the same result.

What this PR changes is the number of entry points, not the outcome or its severity. Fixing it means changing resolveProjectName for insta project create as well — including how GENERIC_DIRS and the home-dir comparison behave on Windows paths — which is a wider change than this PR should carry silently.

Comment thread test/setup-agent.test.ts
`--create "My App"` printed `insta project create My App`; commander drops the
excess arg on paste, so it would have made `my` rather than the `my-app` setup
itself would have made.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@Fermionic-Lyu

Copy link
Copy Markdown
Member Author

Round 1 — one fix, one decline.

Fixed (Wang Miao, cubic 469): the retry hint now prints slugifyName(project.name). --create "My App" said insta project create My App, which commander truncates to my on paste — a silently different project than setup would have made. eae0de0. The existing no-session test now uses My App and pins the hint at my-app, so the two paths cannot drift apart again.

Declined (Yang Dong, cubic 400) — Windows basename in resolveProjectName: pre-existing, and unchanged in kind by this PR.

git show origin/main:src/commands/project.ts line 51 is already cwd.split('/').filter(Boolean).pop(), and every case in test/create-name.test.ts uses a POSIX path. insta project create with no argument therefore already yields c-users-me-my-app on native Windows — --create reaches that same resolver and gets the same answer.

The finding is real about the CLI; it is not about this diff. What the PR adds is another entry point to it, not a worse outcome or a new one. And fixing it properly means changing resolveProjectName itself — which is insta project create's default-name path, with its own questions about how GENERIC_DIRS and the home-directory comparison behave once Windows paths parse correctly. That is a change with its own blast radius and its own tests, and burying it inside a flag PR is how it would ship unreviewed.

Since it is the blocking finding: if you'd rather this PR carry the resolver fix anyway, say so and I'll add it with the Windows cases in create-name.test.ts — but it will be visibly changing project create's behaviour, not --create's.

Checks: test and test-windows green, typecheck and 672 tests pass locally.

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 3 files

You’re at about 93% 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="src/commands/setup.ts">

<violation number="1" location="src/commands/setup.ts:477">
P2: When `projectCreate` reaches the API but local link persistence fails, this catch treats the project as uncreated and advises rerunning create. Report the existing project and recovery path instead, because the retry can fail with name-taken while the first project remains unlinked.</violation>
</file>

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

Re-trigger cubic

Comment thread src/commands/setup.ts
try {
await link(opts.project)
if (project.kind === 'link') await link(project.id)
else await create(project.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2: When projectCreate reaches the API but local link persistence fails, this catch treats the project as uncreated and advises rerunning create. Report the existing project and recovery path instead, because the retry can fail with name-taken while the first project remains unlinked.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At src/commands/setup.ts, line 477:

<comment>When `projectCreate` reaches the API but local link persistence fails, this catch treats the project as uncreated and advises rerunning create. Report the existing project and recovery path instead, because the retry can fail with name-taken while the first project remains unlinked.</comment>

<file context>
@@ -441,24 +457,29 @@ export async function setupAgent(
       try {
-        await link(opts.project)
+        if (project.kind === 'link') await link(project.id)
+        else await create(project.name)
       } catch (e) {
         // Stop here — like the skill-install failure above, finishing with the success summary
</file context>

Comment thread test/setup-agent.test.ts
Splitting the shared project block leaves the link arm's guard green while the
create arm walks into the success summary.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@jwfing jwfing left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM - approved.

@Fermionic-Lyu
Fermionic-Lyu merged commit ef06b1f into main Aug 29, 2026
3 checks passed
@Fermionic-Lyu
Fermionic-Lyu deleted the feat/setup-create branch August 29, 2026 21:54
Fermionic-Lyu added a commit that referenced this pull request Aug 29, 2026
npm version resyncs package-lock.json alongside package.json.
Carries `setup agent --create [name]` (#159).

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
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.

2 participants