Skip to content

feat(code): Scaffold a brand-new app#1888

Open
Twixes wants to merge 29 commits intomainfrom
posthog-code/new-product-flow
Open

feat(code): Scaffold a brand-new app#1888
Twixes wants to merge 29 commits intomainfrom
posthog-code/new-product-flow

Conversation

@Twixes
Copy link
Copy Markdown
Member

@Twixes Twixes commented Apr 26, 2026

Summary

A "New app" creation flow for PostHog Code: end-to-end from "I have an idea" to "I'm watching it run" with PostHog instrumentation already set up. Like a local Lovable.

The user clicks "Scaffold a brand-new app" under the prompt input (or runs New app from the command palette), describes the idea, optionally picks an existing PostHog project, and hits Start building. The agent is launched into a fresh git-initialized scratchpad with a prompt that runs a few rounds of clarification, Socrates-style (picking the stack, making product decisions), builds the project, instruments PostHog, and registers a preview tab. The user can then take it from there and iterate.

When the dev server is up, the preview opens automatically in a 50/50 split next to the chat. When ready, Publish turns the scratchpad into a real GitHub repo and links/creates the PostHog project.

Screenshot 2026-04-26 at 16 18 44@2x Screenshot 2026-04-26 at 20 09 24@2x

What's in the box

(As summarized by Claude - pretty damn long)

  • ScratchpadService — atomic .posthog.json manifest I/O at <userData>/scratchpads/<taskId>/<sanitized-name>/. Manifest is the authoritative source of published/draft state. git init -b main runs at scaffold time so folder-aware UI (file watchers, status bar, Changes panel) doesn't break with "This folder is not a git repository".
  • PostHog API: createProject / updateProject client methods + useCreateProject / useUpdateProject TanStack hooks.

Creation flow

  • "New app" entry points: button under PromptInput and command palette item.
  • Product Creation Dialog: collects name, idea, rounds (1..N, default 3), and PostHog project (existing picker or "set up at publish later"). Reuses ModeSelector / UnifiedModelSelector / ReasoningLevelSelector from the New-task input as a standalone right-aligned toolbar, plus the same submit hotkeys. Defaults the execution mode to auto regardless of the user's New-task preference. Submit shows a loading curtain with rotating cheeky messages ("Convincing the silicon to think…", "Bribing the file system…") while the saga runs.
  • ScratchpadCreationSaga orchestrates: task_creationscratchpad_dir (with git init) → folder_registrationworkspace_creation. Each step has a tested rollback. Agent connection runs after the saga (not as a saga step) so a slow/failing connect can't undo the workspace after the user navigated to it.
  • posthog_code__askClarification MCP tool + custom ClarificationBlock UI. New in-process Streamable-HTTP MCP server (PosthogCodeMcpService) registered alongside the existing PostHog MCP via auth-adapter.ts::buildMcpServers. Per-request server + transport pattern, so tool discovery survives agent reconnects after app restart. Round cap is a soft UX guard — agent recovers via its own loop.
  • posthog_code__registerPreview MCP tool + PreviewService (node-pty, port denylist [22, 25, 80, 443, 5432, 6379, 8123, 9000], cwd traversal guard, manifest persistence, auto-resume on app relaunch, AbortController-cancellable health probe). New panel tab type renders the dev server in an Electron <webview> with shared partition: \"preview\". Preview opens in a 50/50 horizontal split next to the chat (multiple previews stack as tabs in the right pane).
  • Scaffolding prompt tells the agent to: send a hello explaining what's about to happen (incl. "a preview tab will open automatically"), run Socratic clarification, scaffold a mainstream production-grade stack, WebFetch Anthropic's frontend-design skill (https://raw.githubusercontent.com/anthropics/claude-code/main/plugins/frontend-design/skills/frontend-design/SKILL.md) and follow it for any UI work, run the PostHog instrumentation slash-skills, and call registerPreview once the dev server is up. Hard guardrails: never write .posthog.json directly, no remote add/push, mainstream stacks only.

Lifecycle

  • Sidebar: NotePencil draft icon for unpublished scratchpads, group-section icon swap when every task in the group is a draft, hover Trash button. useDeleteScratchpad runs preview unregister → dir delete → task delete → cache invalidate. Linked PostHog projects are never auto-deleted (drafts only ever reference user-picked or publish-time-created projects).
  • Publish flow: PublishDialog does project link (create-new with editable name or pick existing) when the manifest has no projectId, repo name + visibility (private default), gh-token gating, repo conflict (422) inline retry, secret-leakage guard. Service-side publish runs git init -b main (no-op if already initialized) + git add . && git commit -m \"Initial commit\" + POST /user/repos + git push -u origin main + manifest patch. Renderer hook then optionally renames the linked PostHog project. The secret-leakage walk uses git ls-files --others --cached --exclude-standard -z so gitignore semantics (negation, nested gitignores, global gitignore) are handled correctly without a bespoke matcher.

Architectural notes

  • Workspace gets an orthogonal scratchpad: boolean axisWorkspaceMode is not extended (per the plan's Key Technical Decisions). Migrated via 0006_scratchpad_workspaces.sql.
  • Agent connection is post-saga, not a saga step so connection failures don't roll back the task/scratchpad after navigation. Errors are surfaced via toast on the task screen.
  • Tasks are titled Building \${productName} so the sidebar can distinguish multiple drafts at a glance. Product name still drives the directory name.
  • auto execution mode is the dialog default — scaffolding-from-scratch needs to run scaffolders, install deps, and start servers without per-step approval.
  • No automatic GitHub remote cleanup on partial publish failure — explicitly rejected by the plan. Failures after the remote is created leave it intact and require manual git push.
  • Scratchpad .gitignore ships with node_modules/, .env*, dist/, build/, .DS_Store, *.pem, *.key, .next/, .vite/, .posthog.json{,.tmp}.

Testing

  • pnpm --filter code typecheck — clean
  • pnpm --filter code test — passes; the only failures in the full suite are pre-existing flaky archive/service.integration.test.ts worktree tests (unrelated to this work)
  • pnpm lint — clean (the one Biome warning surfaced is in packages/agent/src/handoff-checkpoint.ts and pre-existing on main)

Post-Deploy Monitoring & Validation

  • What to monitor/search
    • Logs (electron-log scopes): scratchpad-service, scratchpad-creation-saga, task-creation-saga, preview-service, posthog-code-mcp, delete-scratchpad, scratchpad-publish, product-creation-dialog, useDraftTaskIds
    • Visual: sidebar shows NotePencil icon next to drafts; Publish button appears in the task header for drafts only; preview opens in a right pane on dev-server ready
  • Validation checks
    • ls -la \"\$HOME/Library/Application Support/@posthog/posthog-code/scratchpads/\" after creating an app → directory + <sanitized-name>/.posthog.json and .git/ present
    • cat \"\$HOME/.../scratchpads/<taskId>/<name>/.posthog.json\"{ projectId: number | null, published: false, ... }
    • Agent reports posthog_code__registerPreview and posthog_code__askClarification in its tool list (verified by per-request MCP server fix; previously broke on app restart)
  • Expected healthy behavior
    • Saga completes within a few seconds; navigation to task-detail happens before the loading curtain disappears (no "Select a repository folder" flash).
    • Preview tab opens within ~30s of posthog_code__registerPreview resolving (60s health-probe ceiling, abortable on shutdown).
    • Trash flow removes dir, kills previews, deletes task — all best-effort with toasts on partial failures.
  • Failure signal(s) / rollback trigger
    • Saga rollback toasts in renderer + scratchpad-creation-saga error logs.
    • Preview port denylist rejection or cwd traversal rejection in preview-service logs.
    • Manifest write contention (should not happen — agent prompt forbids direct writes; service serializes via per-taskId mutex).
    • Rollback trigger: revert this PR if the saga consistently leaves orphaned PostHog projects, scratchpad directories, or GitHub repos. Manual cleanup procedure documented in the deletion flow.
  • Validation window & owner
    • Window: 7 days post-merge for the dogfood cohort; broader rollout once preview lifecycle is exercised in the wild.
    • Owner: @Twixes
  • Operational notes
    • GitHub repo creation is not idempotent — failures after POST /user/repos succeeds (e.g. push failure) leave the remote intact and require manual git push from the user. Documented in the dialog UX.
    • PostHog projects created at publish time use the user-supplied name verbatim. Existing projects the user picked are never renamed by the publish flow.

Plan reference

  • docs/plans/2026-04-25-001-feat-new-product-creation-flow-plan.md — frontmatter `status` updated to `completed`. Some details (manifest `projectId` nullability, agent-session-outside-saga, 50/50 preview split, auto execution mode default, "new app" copy, WebFetch'd frontend-design skill) evolved during implementation; the plan reflects the original architecture and this PR is the source of truth for the shipped behavior.

Created with PostHog Code

Twixes added 2 commits April 26, 2026 09:15
Implements all 3 phases of the plan at
docs/plans/2026-04-25-001-feat-new-product-creation-flow-plan.md:

Phase 1 — Foundations
- ScratchpadService with on-disk layout at <userData>/scratchpads/<taskId>/
  <sanitized-name>/, atomic .posthog.json manifest, full DI/tRPC wiring
- PostHog API: createProject / updateProject / deleteProject + hooks

Phase 2 — Creation flow + agent capabilities
- "New product" entry points: button under PromptInput + command palette
- ProductCreationDialog with project picker, rounds selector (3..5), and
  ScratchpadCreationSaga with rollbacks (project / task / dir / workspace /
  agent session)
- posthog_code__askClarification MCP tool + ClarificationBlock UI;
  in-process Streamable-HTTP MCP server registered alongside existing servers
- posthog_code__registerPreview MCP tool + PreviewService (node-pty,
  port denylist, cwd traversal guard, manifest persistence, auto-resume
  on app relaunch) + Electron <webview> preview tab

Phase 3 — Lifecycle
- Sidebar: NotePencil draft icon, Trash button, group icon swap,
  multi-step useDeleteScratchpad (preview kill, dir delete, conditional
  project delete, task delete)
- Publish flow: secret-leakage guard, git init + commit + GitHub
  POST /user/repos + push, manifest patch, project rename via renderer hook,
  PublishDialog wired into TaskDetail header for drafts only

Workspace gets an orthogonal `scratchpad: boolean` axis (does NOT extend
WorkspaceMode); migrated via 0006_scratchpad_workspaces.sql.

1033/1033 tests passing; 82 new tests across the new units.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Reads as "We'll ask up to [3|4|5] rounds of clarifying questions..."
instead of separating the prose from the picker.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
@greptile-apps
Copy link
Copy Markdown

greptile-apps Bot commented Apr 26, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/scratchpads/hooks/useDeleteScratchpad.ts
Line: 42-53

Comment:
**Active agent session not disconnected before task deletion**

`deleteScratchpadImperative` kills previews and deletes the scratchpad directory and PostHog task, but never calls `disconnectFromTask`. If an agent is actively running when the user clicks "Trash", it continues executing inside the now-deleted directory and will make calls against a task that no longer exists — producing confusing error toasts and potentially leaving orphaned processes.

The saga rollback explicitly uses `getSessionService().disconnectFromTask(taskId)` for this very reason (see `scratchpad-creation.ts` step `agent_session` rollback). The same call should be inserted here, ideally before step 3 (scratchpad directory deletion).

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/code/src/main/services/preview/service.ts
Line: 389-397

Comment:
**`fetch` inside `waitForHealth` has no per-request timeout**

Each `fetch(opts.url)` call has no `AbortSignal` or timeout option. If the spawned server accepts the TCP connection but stalls before sending headers (e.g. slow startup, misconfigured framework), a single `fetch` can block for Node's default socket timeout (minutes), making the loop run far past the intended `HEALTH_TIMEOUT_MS = 60 s` deadline.

A short `AbortSignal.timeout(opts.intervalMs)` wrapped in a `try/catch` (since abort throws) is the minimal fix and keeps the poll rate honest.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: apps/code/src/main/services/scratchpad/service.ts
Line: 356-358

Comment:
**`git commit` will fail when no global git identity is configured**

The commit step runs without `-c user.email=...` / `-c user.name=...`:
```
await this.runGit(scratchpadPath, ["commit", "-m", "Initial commit"]);
```
If the user has never run `git config --global user.email`, git emits "Author identity unknown … Please tell me who you are." — returned as a `git_error` with a message that makes it look like a bug rather than a missing config. Adding inline `-c` flags (e.g. `user.email=noreply@posthog.com`, `user.name=PostHog Code`) makes the initial commit unconditionally succeed without touching the user's global config.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "fix(code): inline rounds selector into t..." | Re-trigger Greptile

Comment on lines +42 to +53
// 2. Kill all running previews for this task. Best-effort.
await trpcClient.preview.unregister.mutate({ taskId }).catch((err) => {
log.warn("Failed to unregister previews", { taskId, err });
});

// 3. Delete the scratchpad directory + manifest.
try {
await trpcClient.scratchpad.delete.mutate({ taskId });
} catch (err) {
log.error("Failed to delete scratchpad directory", { taskId, err });
toast.error("Failed to delete draft files");
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Active agent session not disconnected before task deletion

deleteScratchpadImperative kills previews and deletes the scratchpad directory and PostHog task, but never calls disconnectFromTask. If an agent is actively running when the user clicks "Trash", it continues executing inside the now-deleted directory and will make calls against a task that no longer exists — producing confusing error toasts and potentially leaving orphaned processes.

The saga rollback explicitly uses getSessionService().disconnectFromTask(taskId) for this very reason (see scratchpad-creation.ts step agent_session rollback). The same call should be inserted here, ideally before step 3 (scratchpad directory deletion).

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/renderer/features/scratchpads/hooks/useDeleteScratchpad.ts
Line: 42-53

Comment:
**Active agent session not disconnected before task deletion**

`deleteScratchpadImperative` kills previews and deletes the scratchpad directory and PostHog task, but never calls `disconnectFromTask`. If an agent is actively running when the user clicks "Trash", it continues executing inside the now-deleted directory and will make calls against a task that no longer exists — producing confusing error toasts and potentially leaving orphaned processes.

The saga rollback explicitly uses `getSessionService().disconnectFromTask(taskId)` for this very reason (see `scratchpad-creation.ts` step `agent_session` rollback). The same call should be inserted here, ideally before step 3 (scratchpad directory deletion).

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +389 to +397
try {
const res = await fetch(opts.url, { method: "GET" });
const code = res.status;
if ((code >= 200 && code < 400) || code === 404) {
return true;
}
} catch {
// Server isn't up yet — keep waiting.
}
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 fetch inside waitForHealth has no per-request timeout

Each fetch(opts.url) call has no AbortSignal or timeout option. If the spawned server accepts the TCP connection but stalls before sending headers (e.g. slow startup, misconfigured framework), a single fetch can block for Node's default socket timeout (minutes), making the loop run far past the intended HEALTH_TIMEOUT_MS = 60 s deadline.

A short AbortSignal.timeout(opts.intervalMs) wrapped in a try/catch (since abort throws) is the minimal fix and keeps the poll rate honest.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/main/services/preview/service.ts
Line: 389-397

Comment:
**`fetch` inside `waitForHealth` has no per-request timeout**

Each `fetch(opts.url)` call has no `AbortSignal` or timeout option. If the spawned server accepts the TCP connection but stalls before sending headers (e.g. slow startup, misconfigured framework), a single `fetch` can block for Node's default socket timeout (minutes), making the loop run far past the intended `HEALTH_TIMEOUT_MS = 60 s` deadline.

A short `AbortSignal.timeout(opts.intervalMs)` wrapped in a `try/catch` (since abort throws) is the minimal fix and keeps the poll rate honest.

How can I resolve this? If you propose a fix, please make it concise.

Comment on lines +356 to +358
]).catch(() => undefined);
await this.runGit(scratchpadPath, ["add", "."]);
await this.runGit(scratchpadPath, ["commit", "-m", "Initial commit"]);
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 git commit will fail when no global git identity is configured

The commit step runs without -c user.email=... / -c user.name=...:

await this.runGit(scratchpadPath, ["commit", "-m", "Initial commit"]);

If the user has never run git config --global user.email, git emits "Author identity unknown … Please tell me who you are." — returned as a git_error with a message that makes it look like a bug rather than a missing config. Adding inline -c flags (e.g. user.email=noreply@posthog.com, user.name=PostHog Code) makes the initial commit unconditionally succeed without touching the user's global config.

Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/code/src/main/services/scratchpad/service.ts
Line: 356-358

Comment:
**`git commit` will fail when no global git identity is configured**

The commit step runs without `-c user.email=...` / `-c user.name=...`:
```
await this.runGit(scratchpadPath, ["commit", "-m", "Initial commit"]);
```
If the user has never run `git config --global user.email`, git emits "Author identity unknown … Please tell me who you are." — returned as a `git_error` with a message that makes it look like a bug rather than a missing config. Adding inline `-c` flags (e.g. `user.email=noreply@posthog.com`, `user.name=PostHog Code`) makes the initial commit unconditionally succeed without touching the user's global config.

How can I resolve this? If you propose a fix, please make it concise.

Twixes added 22 commits April 26, 2026 09:57
Replaces the SegmentedControl with a small inline number input bounded
to [1..5]. Reads naturally as part of the sentence and supports the full
range without needing 5 separate buttons.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Inline-aligned with the surrounding sentence, exposes the full 1..5 range
(default 3, capped at 5).

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Lets users scaffold a scratchpad without linking a PostHog project. Manifest
projectId becomes nullable; saga gains a third mutually-exclusive mode
(skipProject). When set:

- No project is created or linked.
- Scaffolding prompt skips the instrumentation step entirely.
- Publish refuses to run with a clear "link a project before publishing"
  message instead of attempting GitHub work it can't complete.

useDeleteScratchpad already tolerated a missing projectId; updated the null
check to be explicit.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The Product Creation Dialog's project picker now reads from
@features/projects/hooks/useProjects — the same hook the sidebar's
"Change project" dialog uses — instead of a separate posthog-projects
hook that fetched the same data through a different endpoint.

Drops the now-unused listProjects client method and the posthog-projects
useProjects hook.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The classic.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Scratchpads are mostly throwaway, so creating a real PostHog project at
scaffold time produces clutter. New flow:

Creation dialog (Product Creation):
- Two options: "Let's do this later" (default) and "Use existing project".
- "Auto-create new project" removed entirely.

Scaffolding:
- PostHog instrumentation skills always run, regardless of project link
  state. The agent reads API key + host from env vars (POSTHOG_API_KEY,
  POSTHOG_HOST) and writes a .env.example. No hardcoded keys/IDs.

Publish:
- If the manifest has no projectId, the Publish dialog shows a project
  link step: create new project (with editable name) or pick existing.
  The chosen projectId is patched into the manifest before the publish
  flow runs. Existing manifests with projectId already set behave the
  same as before.

Saga:
- Drops autoCreateProject input + the posthog_project step entirely.
- Output drops autoCreatedProject; projectId stays nullable.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Auto-create at scaffold time was the only thing that produced the
prefix; now that's gone. Cleaning up the dead code:

- usePublishScratchpad: drops the rename step (no prefix to strip)
  and the productName input. Hook just publishes.
- useDeleteScratchpad: drops the project deletion path entirely.
  Drafts only ever reference user-picked projects, which we never
  delete.
- posthogClient.deleteProject + useDeleteProject hook: deleted; no
  consumers remain.
- DraftTaskHeaderActions: takes taskTitle directly instead of
  fetching the linked project to derive a name.
- PublishDialog: stops threading productName through to the publish
  hook; only uses it as a pre-fill for the create-new-project name.

Tests trimmed accordingly.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Bug fix: the rounds banner was nested inside the PostHog project section,
visually attaching it to the project picker. Now it lives at the top as
the framing copy.

Polish:
- Title size 4 + small rocket icon — this is a beginning, not a row in a
  settings list
- Banner moved directly below the title and tinted with accent colors so
  it reads as a system-level promise rather than body chrome
- Trim banner copy: "rounds of clarifying questions to shape your
  product." → "rounds of questions before scaffolding."
- Indent the project picker / helper text under their respective radios
  so the relationship is unambiguous
- Trim the "later" helper to one line
- Submit button: "Create product" → "Start building" with a rocket icon
- Voice fix: "What's the name of your new thing?" → "What are we calling
  it?" (was the only label that didn't use first-person agent voice)

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The IPC layer can drop null property values, so the server saw
undefined and rejected with "expected: number" — even though the
schema was nullable. Switch to nullish() and normalize to null
in the service so the manifest always stores null when no project
is linked.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Two bugs after clicking Start building:

1. The user landed on a blank "Select repository folder" view instead of
   the new task. The saga passed a synthetic `folderId` (the scratchpad
   path) without registering the scratchpad in the folders DB, so
   navigateToTask's folder lookup failed and fell into an auto-recreate
   branch that fought with the workspace we just created. Fix: register
   the scratchpad as a real folder before workspace creation and pass
   the folder's UUID through.

2. Reopening the dialog showed it stuck "loading" with no progress.
   `agent_session` was a saga step, so the dialog's submitting state
   stayed on for the entire (slow) connectToTask call. If that step
   failed it would also roll back the workspace + scratchpad + task,
   leaving the user staring at a now-deleted task. Fix: pull
   agent_session out of the saga. The saga ends after workspace
   creation, returns the prepared initialPrompt, and the dialog kicks
   off connectToTask in the background with toast-on-failure.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The folder-aware UI (file watchers, status bar, "Changes" panel) all
assume a git directory and surface "This folder is not a git repository"
the moment a fresh scratchpad opens. Holding off git init until publish
time was too clever — just init it as part of scaffolding.

ScratchpadService.scaffoldEmpty now runs `git init -b main` immediately
after creating the directory. Scaffolding prompt no longer forbids git;
it tells the agent the dir is already a fresh repo on main with no
commits, so it can use git normally (with the obvious "don't push" /
"don't init in subdirs" guardrails). Publish-time `git init` is now a
no-op for already-initialized repos.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The saga was creating the PostHog task but never invalidating the
tasks-list TanStack Query cache. Side effects:

- New task didn't appear in the sidebar until the 30s poll (so the user
  couldn't navigate back to it after leaving).
- task-detail couldn't resolve the just-created task in the cache, so
  it fell back to "Select a repository folder" until the poll fired.

After saga success, call useCreateTask().invalidateTasks(task) — same
helper the regular task-creation flow uses — to optimistically prime the
cache and trigger a refetch.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Product name lives in the scratchpad directory name and the agent's
first message — no need to repeat it in the task title.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The hook no longer deletes any PostHog project (project linking happens
at publish time via explicit user action; we never delete user-picked
projects). Update the confirm message to match.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
- Title is now `Building ${productName}` so each scratchpad gets a
  distinct sidebar entry while still keeping the product name out of
  the verbose phrase.
- Embeds the same `PromptInput` component the New task input uses, so
  the dialog inherits: execution mode (incl. bypass permissions),
  model + adapter, reasoning level, file attachments, command palette
  (`@` mentions, `/` skills).
- Saga gains an optional `filePaths` input that is woven into the
  agent's first turn via `buildPromptBlocks`, so attached files become
  resource_link blocks the agent can read while clarifying.
- Adapter, executionMode, model, reasoningLevel are forwarded to both
  the saga and the subsequent `connectToTask` call.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
- Drop PromptInput in favour of a plain TextArea — the dialog doesn't
  need a "Send" button, just the text.
- Reuse the same ModeSelector / UnifiedModelSelector / ReasoningLevelSelector
  the New task input uses, but rendered as a standalone toolbar above the
  action row instead of being baked into the editor.
- Toolbar and action row are both right-aligned.
- Default mode is now "auto" for both adapters (was "plan" for Claude).
- Submitting state takes over the dialog as a loading curtain with a
  rotating set of unhinged messages, and we navigate first / close last
  so the empty TaskInput never bleeds through during the transition.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
The scaffolding flow has the agent open a preview via the
posthog_code__registerPreview tool once the dev server is up — but
this happens silently. Add a one-liner to the prompt instructing the
agent to send a short hello at the start that explains what's about
to happen, including that a preview tab will open by itself.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
MCP server (the headline fix):
- The stateless StreamableHTTP pattern requires a fresh McpServer +
  transport per HTTP request. The previous code shared one transport
  across requests, which worked for the first connection but broke
  tools/list on agent reconnect after an app restart — agents would
  report the registerPreview/askClarification tools as "not in this
  session's tool list". Now we build the server inside the request
  handler and tear it down on response close.

Dialog defaults:
- New product dialog always defaults the mode to "auto" regardless of
  the user's New-task mode preference; settings-based fallback dropped.

Repo helpers:
- REPO_NAME_RE + sanitizeRepoName moved to @shared/utils/repo so the
  PublishDialog and the main-process publisher validate identically.
- PublishDialog now uses useCreateProject() instead of poking
  posthogClient.createProject directly — picks up cache invalidation.

Secret-leakage check:
- Replaced the hand-rolled gitignore matcher (~70 lines) with
  `git ls-files --others --cached --exclude-standard -z`. Correct
  semantics for negation, nested gitignores, etc., for free.

Saga utilities:
- createSagaLogger(scope) extracted; both task and scratchpad sagas
  use it.
- taskCreationStepConfig() helper — the create-then-rollback pattern
  for the shared task_creation step now lives in one place.

Performance:
- ScratchpadService.list() walks taskDirs concurrently and uses
  withFileTypes:true to drop the per-entry stat round-trip.
- getScratchpadPath same treatment.
- findOffendingFiles parallelizes sibling walks.
- ensureGitignore uses writeFile {flag:"wx"} instead of access+write.
- PreviewService.resumeFromManifest spawns previews concurrently.
- PreviewService health probe takes an AbortController and aborts on
  killProcess/shutdown so dead probes don't run to completion.
- useDraftTaskIds debounces (150 ms) the cache invalidation triggered
  by scratchpad lifecycle events; bursty publish-time manifest
  patches no longer thrash the sidebar.

Code quality:
- PublishDialog body extracted into a child component keyed by useId
  + conditional mount, so each open gets fresh form state with no
  reset effect.
- A handful of redundant casts dropped from preview/service and the
  product dialog.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
addPreviewTab now puts the preview alongside the chat instead of in the
main tab list:

1. If a preview tab with the same name already exists, update its URL
   and activate (re-registration may have changed the port).
2. Else, if there's already a non-main leaf panel (the user has a split
   open), drop the preview in there as a new tab.
3. Otherwise, split the main panel 50/50 horizontally and put the
   preview in the new right pane so the chat stays visible.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
PostHog Code doesn't preinstall Anthropic's frontend-design skill (the
bundled posthog plugin only carries instrument-* skills), but it MAY be
available in the session via Claude Code's plugin marketplace. Tell the
agent to invoke /frontend-design if present; otherwise apply the same
principles inline so we don't end up with cookie-cutter purple-on-white
AI defaults.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Loading a Claude Code skill at runtime is, in practice, the same as
WebFetching its SKILL.md and following the contents. Drop the
conditional /frontend-design dance and just point the agent at
raw.githubusercontent.com — works regardless of whether the user has
the plugin installed locally.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Per user feedback, "product" sounds too serious — "app" keeps the door
open for hackathons and weekend projects. Updated:

- Sidebar entry button under PromptInput
- Command palette entry ("New app" / "Create new app")
- Dialog title + loading curtain ("Create a new app", "Preparing your app")
- Inline error copy ("Failed to create app")
- Analytics action enum value (new-product → new-app)
- Scaffolding prompt phrasing the agent reads

Internal identifiers (file/component/store/feature names) keep the
existing "product" naming since they're not user-visible — renaming
them is churn for no UX gain.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
@Twixes Twixes changed the title feat(code): add new product creation flow with Socratic scaffolding feat(code): add new app creation flow with Socratic scaffolding Apr 26, 2026
Twixes added 3 commits April 26, 2026 14:50
Lift the framing banner from "I'll ask up to N rounds" into a small
hero block with a punchy lead, a Sparkle icon, and a one-paragraph
explanation that articulates why this isn't just a fancy New Task:
clarification first, agent picks the stack, PostHog wired from commit
1, live preview opens automatically. Subtle accent gradient ties the
block to the rocket-icon title.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Reordered the agent's workflow so a live preview comes up as fast as
possible:

  1. Clarify
  2. Scaffold the bare minimum that runs (no features yet)
  3. Start the dev server + register the preview NOW
  4. Build it out (with the user watching)
  5. Wire up PostHog instrumentation

The whole thing is one big template-string now instead of an array of
joined fragments — easier to read and easier to edit. The intro message
also tells the user to expect a preview "as fast as possible", and the
build-out step calls out that HMR / re-registration is fine so the
agent isn't shy about restarting.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
…n AskUserQuestion

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
Twixes added 2 commits April 26, 2026 16:07
ModeSelector, UnifiedModelSelector, and ReasoningLevelSelector now accept
a portalContainer ref. When set, the menu content is portaled into that
container instead of <body>, keeping it inside the dialog's FocusScope
so focus and pointer events behave normally. ProductCreationDialog
threads the ref through. modal={false} stops Base UI from rendering its
own backdrop on top of the dialog content.

Generated-By: PostHog Code
Task-Id: 91418382-614e-4310-b210-6dc2d047ea27
The agent now uses Claude's built-in AskUserQuestion to ask up to N
clarifying rounds before scaffolding. Round count is selected inline
in the dialog banner via SegmentedControl and threaded through the
saga + scaffolding prompt as a budget hint — no host-side enforcement.

Generated-By: PostHog Code
Task-Id: 142b4c29-fa5f-4e02-9bc9-de8b22313475
@Twixes Twixes changed the title feat(code): add new app creation flow with Socratic scaffolding feat(code): Scaffold a brand-new app Apr 27, 2026
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.

1 participant