diff --git a/.github/workflows/wizard-ci.yml b/.github/workflows/wizard-ci.yml index e922c8349..1ef121435 100644 --- a/.github/workflows/wizard-ci.yml +++ b/.github/workflows/wizard-ci.yml @@ -52,6 +52,10 @@ on: description: 'Post notifications to Slack' type: boolean default: false + snapshots: + description: 'Run real-TUI snapshots instead of the evaluator (opens a review PR)' + type: boolean + default: false # Webhook trigger from external repos (wizard, context-mill, posthog) # POST https://api.github.com/repos/{owner}/{repo}/dispatches @@ -619,19 +623,30 @@ jobs: # PostHog tracking for eval analytics POSTHOG_PROJECT_TOKEN: ${{ secrets.POSTHOG_PROJECT_TOKEN }} + - name: Install Chromium for Playwright (snapshots) + if: ${{ inputs.snapshots }} + run: pnpm exec playwright install --with-deps chromium + - name: Execute wizard id: execute-wizard continue-on-error: true run: | - CMD_ARGS=(pnpm wizard-ci --app "$MATRIX_APP" --base "$INPUT_BASE_BRANCH") - if [ -n "$TRIGGER_ID" ]; then - CMD_ARGS+=(--trigger-id "$TRIGGER_ID") - fi - if [ "$INPUT_EVALUATE" = "true" ]; then - CMD_ARGS+=(--evaluate) + if [ "$SNAPSHOTS" = "true" ]; then + # Snapshot mode: real-TUI visual regression + review PR (no evaluator). + pnpm wizard-ci-snapshot-review "$MATRIX_APP" 2>&1 | tee wizard-output.log + else + CMD_ARGS=(pnpm wizard-ci --app "$MATRIX_APP" --base "$INPUT_BASE_BRANCH") + if [ -n "$TRIGGER_ID" ]; then + CMD_ARGS+=(--trigger-id "$TRIGGER_ID") + fi + if [ "$INPUT_EVALUATE" = "true" ]; then + CMD_ARGS+=(--evaluate) + fi + "${CMD_ARGS[@]}" 2>&1 | tee wizard-output.log fi - "${CMD_ARGS[@]}" 2>&1 | tee wizard-output.log env: + SNAPSHOTS: ${{ inputs.snapshots }} + POSTHOG_WIZARD_PROJECT_ID: '2' MATRIX_APP: ${{ matrix.app }} INPUT_BASE_BRANCH: ${{ needs.discover.outputs.input_base_branch }} TRIGGER_ID: ${{ needs.discover.outputs.trigger_id }} @@ -1022,4 +1037,3 @@ jobs: } ] }" - diff --git a/.github/workflows/wizard-snapshots.yml b/.github/workflows/wizard-snapshots.yml new file mode 100644 index 000000000..37636d707 --- /dev/null +++ b/.github/workflows/wizard-snapshots.yml @@ -0,0 +1,145 @@ +name: Wizard Snapshots + +# Visual-review dispatch: run the wizard e2e on an app, render the TUI to +# side-by-side screenshots (baseline | current), and open a PR for a human to +# eyeball — instead of running the agent evaluator. Merging the PR accepts the +# new baseline. + +permissions: + contents: read + +on: + workflow_dispatch: + inputs: + app: + description: 'App path (e.g. "basic-integration/javascript-node/express-todo")' + required: false + type: string + default: 'basic-integration/javascript-node/express-todo' + wizard_ref: + description: 'Wizard repo branch/tag/sha' + type: string + default: 'main' + context_mill_ref: + description: 'Context Mill repo branch/tag/sha' + type: string + default: 'main' + posthog_ref: + description: 'PostHog repo branch/tag/sha (for MCP)' + type: string + default: 'master' + project_id: + description: 'PostHog project id the key is scoped to (defaults to the WIZARD_SNAPSHOTS_PROJECT_ID repo variable)' + required: false + type: string + posthog_region: + description: 'PostHog region' + type: choice + options: [us, eu] + default: 'us' + repository_dispatch: + types: [wizard-snapshots-trigger] + # Comment `/wizard-ci [app] [wizard_ref]` on a PR to run the e2e and have the + # results posted back as a comment on that PR. + issue_comment: + types: [created] + +jobs: + snapshots: + # Dispatches always run; comment triggers only on a PR, only for the + # `/wizard-ci` command, and only from a repo member/owner/collaborator. + if: >- + github.event_name == 'workflow_dispatch' || + github.event_name == 'repository_dispatch' || + (github.event_name == 'issue_comment' && + github.event.issue.pull_request != null && + startsWith(github.event.comment.body, '/wizard-ci') && + contains(fromJSON('["OWNER","MEMBER","COLLABORATOR"]'), github.event.comment.author_association)) + runs-on: ubuntu-latest + timeout-minutes: 60 + permissions: + contents: write # commit the screenshots to the review branch + pull-requests: write + issues: write # comment + react on the triggering PR + id-token: write + steps: + - name: Generate GitHub App token + id: app-token + uses: actions/create-github-app-token@d72941d797fd3113feb6b93fd0dec494b13a2547 # v1.12.0 + with: + app-id: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_APP_ID }} + private-key: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_PRIVATE_KEY }} + owner: PostHog + repositories: wizard-workbench + + - name: Resolve request + id: req + env: + COMMENT_BODY: ${{ github.event.comment.body }} + run: | + if [ "${{ github.event_name }}" = "issue_comment" ]; then + REST="${COMMENT_BODY#/wizard-ci}" + read -r APP REF _ <<< "$REST" + echo "app=${APP:-basic-integration/javascript-node/express-todo}" >> "$GITHUB_OUTPUT" + echo "wizard_ref=${REF:-main}" >> "$GITHUB_OUTPUT" + echo "comment_pr=${{ github.event.issue.number }}" >> "$GITHUB_OUTPUT" + echo "checkout_ref=refs/pull/${{ github.event.issue.number }}/merge" >> "$GITHUB_OUTPUT" + else + echo "app=${{ inputs.app || github.event.client_payload.app || 'basic-integration/javascript-node/express-todo' }}" >> "$GITHUB_OUTPUT" + echo "wizard_ref=${{ inputs.wizard_ref || github.event.client_payload.wizard_ref || 'main' }}" >> "$GITHUB_OUTPUT" + echo "comment_pr=${{ github.event.client_payload.comment_pr }}" >> "$GITHUB_OUTPUT" + echo "checkout_ref=" >> "$GITHUB_OUTPUT" + fi + + - name: Acknowledge the command + if: github.event_name == 'issue_comment' + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + run: | + gh api "repos/${{ github.repository }}/issues/comments/${{ github.event.comment.id }}/reactions" -f content=eyes >/dev/null || true + + - name: Checkout repository + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + token: ${{ steps.app-token.outputs.token }} + ref: ${{ steps.req.outputs.checkout_ref }} + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '24' + + - name: Setup pnpm + uses: pnpm/action-setup@41ff72655975bd51cab0327fa583b6e92b6d3061 + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Install Chromium for Playwright + run: pnpm exec playwright install --with-deps chromium + + - name: Setup wizard dependencies + # Exports WIZARD_PATH / CONTEXT_MILL_PATH / MCP_PATH. + uses: ./.github/actions/setup-wizard-deps + with: + wizard_ref: ${{ steps.req.outputs.wizard_ref }} + context_mill_ref: ${{ inputs.context_mill_ref || 'main' }} + posthog_ref: ${{ inputs.posthog_ref || 'master' }} + app_token: ${{ steps.app-token.outputs.token }} + save_cache: 'false' + + - name: Render snapshots + report (review PR, and a comment when triggered by /wizard-ci) + env: + GH_TOKEN: ${{ steps.app-token.outputs.token }} + POSTHOG_PERSONAL_API_KEY: ${{ secrets.GH_APP_POSTHOG_WIZARD_CI_BOT_POSTHOG_PERSONAL_KEY }} + POSTHOG_WIZARD_PROJECT_ID: ${{ inputs.project_id || github.event.client_payload.project_id || vars.WIZARD_SNAPSHOTS_PROJECT_ID }} + POSTHOG_REGION: ${{ inputs.posthog_region || 'us' }} + APP: ${{ steps.req.outputs.app }} + COMMENT_PR: ${{ steps.req.outputs.comment_pr }} + run: | + if [ -n "$COMMENT_PR" ]; then + pnpm wizard-ci-snapshot-review "$APP" --comment-pr "$COMMENT_PR" + else + pnpm wizard-ci-snapshot-review "$APP" + fi diff --git a/mprocs.yaml b/mprocs.yaml index 6395e2fd6..59c748c41 100644 --- a/mprocs.yaml +++ b/mprocs.yaml @@ -110,6 +110,14 @@ procs: autostart: false env_file: .env + wizard-ci-snapshots: + # Run the CI-e2e test definitions, render TUI snapshots of each real run, + # and diff against the committed baseline. Prints a per-frame summary and + # writes a side-by-side visual report (report.html). --update to accept. + shell: "pnpm wizard-ci-snapshots" + autostart: false + env_file: .env + # ═══════════════════════════════════════════════════════════════════════════ # PR/BRANCH EVALUATION # ═══════════════════════════════════════════════════════════════════════════ diff --git a/package.json b/package.json index 458080c43..24f914d21 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,8 @@ "scripts": { "evaluate": "tsx services/pr-evaluator/index.ts", "wizard-ci": "tsx services/wizard-ci/index.ts", + "wizard-ci-snapshots": "tsx services/wizard-ci/snapshots.ts", + "wizard-ci-snapshot-review": "tsx services/wizard-ci/snapshot-review.ts", "benchmark": "tsx services/wizard-benchmark/index.ts", "framework-detect": "tsx services/framework-detect/index.ts", "yara-scan": "tsx services/yara-scan/index.ts", @@ -23,6 +25,7 @@ "devDependencies": { "@types/node": "^22.0.0", "@types/sanitize-html": "^2.16.1", + "playwright": "^1.61.0", "tsx": "^4.19.0", "typescript": "^5.8.0" }, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index f21124dcc..3ba11a0c4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -39,6 +39,9 @@ importers: '@types/sanitize-html': specifier: ^2.16.1 version: 2.16.1 + playwright: + specifier: ^1.61.0 + version: 1.61.0 tsx: specifier: ^4.19.0 version: 4.21.0 @@ -414,6 +417,11 @@ packages: fast-content-type-parse@2.0.1: resolution: {integrity: sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==} + fsevents@2.3.2: + resolution: {integrity: sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==} + engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} + os: [darwin] + fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} @@ -450,6 +458,16 @@ packages: picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} + playwright-core@1.61.0: + resolution: {integrity: sha512-caX7TrY3Ml6egyDX0WUcTHDxodl/b51y5wJOdCEA36QviK/s2g081hvmGs8eaE3DWb6NYZQ6BjO/QkNRPenoPA==} + engines: {node: '>=18'} + hasBin: true + + playwright@1.61.0: + resolution: {integrity: sha512-Z+7BeeqQPRRzklHsVFP4KTGIyMxKUmfeRA4WisM6G3/XW6nwGeX6fX9qYaDa+CiUqpOkb2f6X3nar05R3kSuJQ==} + engines: {node: '>=18'} + hasBin: true + postcss@8.5.8: resolution: {integrity: sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==} engines: {node: ^10 || ^12 || >=14} @@ -817,6 +835,9 @@ snapshots: fast-content-type-parse@2.0.1: {} + fsevents@2.3.2: + optional: true + fsevents@2.3.3: optional: true @@ -850,6 +871,14 @@ snapshots: picocolors@1.1.1: {} + playwright-core@1.61.0: {} + + playwright@1.61.0: + dependencies: + playwright-core: 1.61.0 + optionalDependencies: + fsevents: 2.3.2 + postcss@8.5.8: dependencies: nanoid: 3.3.11 diff --git a/services/wizard-ci/e2e.ts b/services/wizard-ci/e2e.ts new file mode 100644 index 000000000..a584acbac --- /dev/null +++ b/services/wizard-ci/e2e.ts @@ -0,0 +1,156 @@ +/** + * `wizard-ci --e2e` — full end-to-end run against prod cloud, driven through the + * REAL wizard TUI. + * + * The wizard repo's `scripts/tui-snapshots.no-jest.ts` runs the real `startTUI` + * in a PTY and drives it through the fixed happy path (skip MCP, skip Slack, + * delete skills, continue past health issues) by store state manipulation — auth + * via the phx key, no browser. It captures every key-moment screen as real-TUI + * text and writes a structured result JSON; this is the orchestration + assertion + * layer over it. + * + * pnpm wizard-ci basic-integration/javascript-node/express-todo --e2e + * pnpm wizard-ci basic-integration/next-js/15-app-router-todo --e2e --project-id 228144 + */ +import { join, basename } from "path"; +import { existsSync, mkdirSync, rmSync, readFileSync, writeFileSync } from "fs"; +import { spawnSync } from "child_process"; + +const WORKBENCH = join(import.meta.dirname, "..", ".."); +const APPS_DIR = join(WORKBENCH, "apps"); + +// Host Claude-Code / Anthropic auth vars: when the wizard's agent subprocess is +// spawned from inside a Claude Code session it defers auth to the host +// (apiKeySource=none → 401). Strip them so it auths with the phx key, exactly +// like a plain CI shell (where these are simply unset, so the strip is a no-op). +const STRIP_ENV = [ + "ANTHROPIC_API_KEY", "ANTHROPIC_BASE_URL", "ANTHROPIC_AUTH_TOKEN", + "CLAUDECODE", "CLAUDE_CODE_ENTRYPOINT", "CLAUDE_CODE_SESSION_ID", + "CLAUDE_CODE_CHILD_SESSION", "CLAUDE_CODE_OAUTH_SCOPES", "CLAUDE_CODE_OAUTH_TOKEN", + "CLAUDE_CODE_SDK_HAS_OAUTH_REFRESH", "CLAUDE_CODE_SDK_HAS_HOST_AUTH_REFRESH", + "CLAUDE_CODE_EXECPATH", "CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES", + "CLAUDE_AGENT_SDK_VERSION", "CLAUDE_CODE_ENABLE_ASK_USER_QUESTION_TOOL", "AI_AGENT", +]; + +export interface E2eOptions { + app?: string; + region?: string; + projectId?: string; + /** true → keep installed skills; default deletes them. */ + keepSkills?: boolean; +} + +function wizardRepo(): string { + const p = process.env.WIZARD_PATH?.replace(/^~/, process.env.HOME || ""); + if (p) return p; + // Default to a sibling wizard checkout next to the workbench. + for (const name of ["wizard-e2e", "wizard"]) { + const sibling = join(WORKBENCH, "..", name); + if (existsSync(sibling)) return sibling; + } + return `${process.env.HOME}/development/wizard`; +} + +/** Where a run drops its real-TUI snapshots — shared with the snapshots flow. */ +export function snapsDirFor(app: string): string { + return `/tmp/wizard-e2e-${basename(app)}-snaps`; +} + +/** Run a single app through the real-TUI e2e and assert. Returns exit code. */ +export function runE2e(opts: E2eOptions): number { + const app = opts.app; + const region = opts.region || process.env.POSTHOG_REGION || "us"; + const projectId = opts.projectId || process.env.POSTHOG_WIZARD_PROJECT_ID || ""; + const apiKey = process.env.POSTHOG_PERSONAL_API_KEY; + + if (!app) { + console.error("✖ --e2e requires an app: pnpm wizard-ci --e2e"); + return 2; + } + if (!apiKey) { + console.error("✖ POSTHOG_PERSONAL_API_KEY is required (the phx key)."); + return 2; + } + if (!projectId) { + console.error("✖ project id required: --project-id or POSTHOG_WIZARD_PROJECT_ID."); + return 2; + } + + const appSrc = join(APPS_DIR, app); + if (!existsSync(appSrc)) { + console.error(`✖ app not found: apps/${app}`); + return 2; + } + + const name = basename(app); + const appDir = `/tmp/wizard-e2e-${name}`; + const resultJson = `/tmp/wizard-e2e-${name}.json`; + const snapsDir = snapsDirFor(app); + + // Always a /tmp copy — never the real fixture. + rmSync(appDir, { recursive: true, force: true }); + mkdirSync(appDir, { recursive: true }); + spawnSync("rsync", ["-a", "--exclude", "node_modules", "--exclude", ".git", `${appSrc}/`, `${appDir}/`], { + stdio: "inherit", + }); + rmSync(snapsDir, { recursive: true, force: true }); + mkdirSync(snapsDir, { recursive: true }); + + const harness = join(wizardRepo(), "scripts", "tui-snapshots.no-jest.ts"); + if (!existsSync(harness)) { + console.error(`✖ wizard e2e harness not found: ${harness}\n Set WIZARD_PATH to the wizard repo.`); + return 2; + } + + console.log(`\n=== wizard-ci --e2e: ${app} (project ${projectId}, ${region}) ===`); + console.log(` policy: skip mcp · skip slack · ${opts.keepSkills ? "keep" : "delete"} skills · continue past health issues\n`); + + const childEnv: NodeJS.ProcessEnv = { ...process.env }; + for (const k of STRIP_ENV) delete childEnv[k]; + childEnv.POSTHOG_PERSONAL_API_KEY = apiKey; + childEnv.APP_DIR = appDir; + childEnv.PROJECT_ID = projectId; + childEnv.POSTHOG_REGION = region; + childEnv.SNAP_OUT = snapsDir; + childEnv.E2E_RESULT_JSON = resultJson; + childEnv.E2E_KEEP_SKILLS = opts.keepSkills ? "true" : "false"; + + const run = spawnSync("npx", ["tsx", harness], { + cwd: wizardRepo(), + stdio: "inherit", + env: childEnv, + }); + + // Structured assertions — the control plane's payoff over stdout-grepping. + let result: { runPhase?: string; hasPosthogDep?: boolean; envFile?: string | null; + screenPath?: string[]; skillsComplete?: boolean; newDeps?: string[] } | null = null; + try { + result = JSON.parse(readFileSync(resultJson, "utf8")); + } catch { + /* harness crashed before writing */ + } + + const checks: Array<[string, boolean]> = result + ? [ + ["agent run completed", result.runPhase === "completed"], + ["posthog dependency added or .env written", !!result.hasPosthogDep || !!result.envFile], + ["full interactive flow reached keep-skills", !!result.screenPath?.includes("keep-skills")], + ["skillsComplete", result.skillsComplete === true], + ] + : [["harness produced a structured result", false]]; + + console.log("\n--- assertions ---"); + for (const [label, ok] of checks) console.log(` ${ok ? "✔" : "✖"} ${label}`); + const passed = run.status === 0 && checks.every(([, ok]) => ok); + + if (result) { + writeFileSync(resultJson, JSON.stringify({ ...result, app, passed }, null, 2)); + console.log(`\nscreen path: ${result.screenPath?.join(" → ")}`); + console.log(`new deps : ${(result.newDeps || []).join(", ") || "(none)"}`); + console.log(`result json: ${resultJson}`); + console.log(`snapshots : ${snapsDir}`); + } + + console.log(`\n${passed ? "✓ E2E PASS" : "✗ E2E FAIL"} — ${app}\n`); + return passed ? 0 : 1; +} diff --git a/services/wizard-ci/index.ts b/services/wizard-ci/index.ts index cefc8e4a6..1bb3ae7e5 100644 --- a/services/wizard-ci/index.ts +++ b/services/wizard-ci/index.ts @@ -54,6 +54,7 @@ import { selectCommand, selectApp, } from "../wizard-run/picker.js"; +import { runE2e } from "./e2e.js"; // ============================================================================ // Config @@ -76,6 +77,12 @@ interface Options { pushOnly: boolean; branch?: string; evaluate: boolean; + /** Drive the real wizard TUI through the full flow + structured asserts. */ + e2e: boolean; + /** Scoped project id for the personal API key (e2e mode). */ + projectId?: string; + /** e2e: keep installed skills instead of deleting them. */ + keepSkills: boolean; } // ============================================================================ @@ -251,11 +258,16 @@ function parseArgs(): Options { clean: false, pushOnly: false, evaluate: false, + e2e: false, + keepSkills: false, }; for (let i = 0; i < args.length; i++) { const arg = args[i]; - if (arg === "--app" || arg === "-a") opts.app = args[++i]; + if (arg === "--e2e") opts.e2e = true; + else if (arg === "--project-id") opts.projectId = args[++i]; + else if (arg === "--keep-skills") opts.keepSkills = true; + else if (arg === "--app" || arg === "-a") opts.app = args[++i]; else if (arg === "--command" || arg === "-c") opts.command = args[++i]; else if (arg === "--product") opts.product = args[++i]; else if (arg === "--trigger-id" || arg === "-t") opts.triggerId = args[++i]; @@ -305,8 +317,18 @@ Evaluation: pnpm wizard-ci --evaluate, -e Run pr-evaluator after PR creation With --local: runs evaluation on local branch (creates branch, commits, runs test-run mode) + +Control-plane e2e (full interactive flow via the real TUI, structured assertions): + pnpm wizard-ci --e2e Drive the real wizard TUI through the full flow + happy path · skip mcp+slack · delete skills + · continue past health issues + pnpm wizard-ci ... --e2e --project-id Scoped-key project (or env POSTHOG_WIZARD_PROJECT_ID) + pnpm wizard-ci ... --e2e --keep-skills Keep installed skills instead of deleting `); process.exit(0); + } else if (!arg.startsWith("-") && !opts.app) { + // Positional app path, e.g. `wizard-ci basic-integration/ --e2e`. + opts.app = arg; } } return opts; @@ -744,6 +766,19 @@ async function runCI( async function main(): Promise { const opts = parseArgs(); + // Control-plane e2e: drive the full interactive flow via the real TUI and + // assert on structured state. + if (opts.e2e) { + process.exit( + runE2e({ + app: opts.app, + region: process.env.POSTHOG_REGION, + projectId: opts.projectId, + keepSkills: opts.keepSkills, + }), + ); + } + // Handle --clean command if (opts.clean) { await cleanBranches(); diff --git a/services/wizard-ci/screenshot.ts b/services/wizard-ci/screenshot.ts new file mode 100644 index 000000000..5b21f84f6 --- /dev/null +++ b/services/wizard-ci/screenshot.ts @@ -0,0 +1,74 @@ +/** + * report.html → PNG screenshots, via headless Chromium (Playwright). + * + * snapshots.ts renders each key moment to HTML (baseline │ current). This + * rasterizes that report — one PNG per frame (the side-by-side row) plus a + * full-flow strip — for attaching to a review PR. + * + * tsx services/wizard-ci/screenshot.ts [--only-changed] + * + * Writes /.png per row (named by the row's data-frame), a + * _flow.png of the whole page, and a shots.json manifest (frame, status, file). + */ +import { chromium } from "playwright"; +import { mkdirSync, rmSync, writeFileSync } from "fs"; +import { join, basename } from "path"; + +interface Shot { + frame: string; + status: string; + file: string; +} + +async function main(): Promise { + const [report, outDir] = process.argv.slice(2).filter((a) => !a.startsWith("--")); + const onlyChanged = process.argv.includes("--only-changed"); + if (!report || !outDir) { + process.stderr.write( + "usage: screenshot [--only-changed]\n", + ); + return 2; + } + + rmSync(outDir, { recursive: true, force: true }); + mkdirSync(outDir, { recursive: true }); + + const browser = await chromium.launch(); + // Wide enough that the two 100-col terminal columns sit side by side without + // wrapping; 2× scale for crisp text. + const page = await browser.newPage({ + viewport: { width: 1920, height: 1080 }, + deviceScaleFactor: 2, + }); + await page.goto(`file://${report}`); + await page.waitForLoadState("networkidle"); + + const rows = page.locator("section.row[data-frame]"); + const count = await rows.count(); + const shots: Shot[] = []; + + for (let i = 0; i < count; i++) { + const row = rows.nth(i); + const frame = (await row.getAttribute("data-frame")) ?? `frame-${i}`; + const status = (await row.getAttribute("data-status")) ?? "unknown"; + if (onlyChanged && status === "same") continue; + const file = `${frame.replace(/\.[^.]+$/, "")}.png`; // 10-run.txt → 10-run.png + await row.scrollIntoViewIfNeeded(); + await row.screenshot({ path: join(outDir, file) }); + shots.push({ frame, status, file }); + } + + // Full-flow strip — the whole report top to bottom. + await page.screenshot({ path: join(outDir, "_flow.png"), fullPage: true }); + + await browser.close(); + writeFileSync(join(outDir, "shots.json"), JSON.stringify(shots, null, 2)); + + process.stdout.write( + `shot ${shots.length} frame(s) + _flow.png → ${outDir}\n` + + `manifest: ${join(outDir, "shots.json")}\n`, + ); + return 0; +} + +main().then((code) => process.exit(code)); diff --git a/services/wizard-ci/snapshot-review.ts b/services/wizard-ci/snapshot-review.ts new file mode 100644 index 000000000..0630564b3 --- /dev/null +++ b/services/wizard-ci/snapshot-review.ts @@ -0,0 +1,206 @@ +/** + * wizard-ci snapshot review: open a PR with side-by-side TUI screenshots for a + * human to eyeball, instead of running the agent evaluator. + * + * 1. wizard-ci-snapshots → a real run, a diff vs the committed baseline, + * and report.html (baseline │ current per frame) + * 2. screenshot.ts → one PNG per changed frame + a _flow.png strip + * 3. commit the PNGs to a review branch and open a PR whose body embeds them + * (via raw.githubusercontent URLs) — the changed frames first. + * + * tsx services/wizard-ci/snapshot-review.ts [--dry-run] [--comment-pr ] + * + * --comment-pr also posts a comment on PR n (the flow strip + a link to the + * full review) — used by the `/wizard-ci` PR-comment trigger. + * + * --dry-run writes the PR body + PNGs under /tmp and skips the PR (for local use). + * In CI the GitHub App token comes from GH_TOKEN / GITHUB_TOKEN and the repo from + * GITHUB_REPOSITORY. + */ +import "dotenv/config"; +import { join, basename } from "path"; +import { + existsSync, + mkdirSync, + rmSync, + cpSync, + readFileSync, + readdirSync, + writeFileSync, +} from "fs"; +import { spawnSync } from "child_process"; +import { snapsDirFor } from "./e2e.js"; +import { commitAndCreatePR, postPRComment } from "../github/index.js"; +import { getRepoRoot, git } from "../github/index.js"; + +const OUT_ROOT = "/tmp/wizard-snapshots"; +const REVIEW_DIR = ".wizard-snapshots"; // committed on the review branch only + +interface Shot { + frame: string; + status: string; + file: string; +} + +function run(cmd: string, args: string[], extraEnv: Record = {}) { + const r = spawnSync(cmd, args, { + stdio: "inherit", + env: { ...process.env, ...extraEnv }, + }); + if (r.status !== 0) throw new Error(`${cmd} ${args.join(" ")} failed`); +} + +const STATUS_EMOJI: Record = { + changed: "🟠", + added: "🔵", + removed: "🔴", + same: "⚪️", +}; + +/** Build the PR body: a summary + each frame's side-by-side image. */ +function prBody( + app: string, + shots: Shot[], + rawBase: string, + runUrl: string | null, +): string { + const changed = shots.filter((s) => s.status !== "same"); + const lines: string[] = []; + lines.push(`## TUI snapshot review — \`${app}\``); + lines.push(""); + lines.push( + `A real \`--e2e\` run, rendered to side-by-side screenshots (baseline │ current). ` + + `**${changed.length}** of ${shots.length} key-moment frames differ. Eyeball them; ` + + `merge this PR to accept the new baseline.`, + ); + if (runUrl) lines.push("", `[CI run](${runUrl})`); + lines.push("", `![flow](${rawBase}/_flow.png)`, ""); + for (const s of changed) { + lines.push(`### ${STATUS_EMOJI[s.status] ?? ""} ${s.frame} — ${s.status}`); + lines.push("", `![${s.frame}](${rawBase}/${s.file})`, ""); + } + return lines.join("\n"); +} + +async function main(): Promise { + const args = process.argv.slice(2); + const app = args.find((a) => !a.startsWith("--")); + const dryRun = args.includes("--dry-run"); + // --comment-pr : also post a comment on that PR linking to the review. + const commentIdx = args.indexOf("--comment-pr"); + const commentPr = commentIdx !== -1 ? Number(args[commentIdx + 1]) : null; + if (!app) { + console.error("usage: snapshot-review [--dry-run] [--comment-pr ]"); + return 2; + } + const name = basename(app); + + // 1. a real run → report.html (the real-TUI screens, baseline vs current). + run("npx", ["tsx", "services/wizard-ci/snapshots.ts", app]); + const report = join(OUT_ROOT, name, "report.html"); + if (!existsSync(report)) { + console.error(`✖ no report at ${report}`); + return 1; + } + + // 2. report.html → PNGs (changed frames only) + shots.json. + const shotsDir = join(OUT_ROOT, name, "shots"); + run("npx", [ + "tsx", + "services/wizard-ci/screenshot.ts", + report, + shotsDir, + "--only-changed", + ]); + const shots: Shot[] = JSON.parse( + readFileSync(join(shotsDir, "shots.json"), "utf8"), + ); + + // 3. assemble the review bundle (PNGs + body) and open / dry-run the PR. + const runId = process.env.GITHUB_RUN_ID ?? "local"; + const branch = `snapshots/${name}/${runId}`; + const repoSlug = process.env.GITHUB_REPOSITORY ?? "PostHog/wizard-workbench"; + const [repoOwner, repoName] = repoSlug.split("/"); + const rawBase = `https://raw.githubusercontent.com/${repoSlug}/${branch}/${REVIEW_DIR}/${name}`; + const runUrl = process.env.GITHUB_RUN_ID + ? `https://github.com/${repoSlug}/actions/runs/${process.env.GITHUB_RUN_ID}` + : null; + const body = prBody(app, shots, rawBase, runUrl); + const title = `📸 TUI snapshots — ${name}`; + + if (dryRun) { + const dest = join(OUT_ROOT, name, "review"); + rmSync(dest, { recursive: true, force: true }); + mkdirSync(dest, { recursive: true }); + cpSync(shotsDir, dest, { recursive: true }); + writeFileSync(join(dest, "PR_BODY.md"), body); + console.log(`\n[dry-run] review bundle → ${dest}`); + console.log(` ${shots.filter((s) => s.status !== "same").length} changed frames, PR body in PR_BODY.md`); + return 0; + } + + // Live: copy PNGs into the repo, commit them to the review branch, open the PR. + const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN; + if (!token) { + console.error("✖ GH_TOKEN / GITHUB_TOKEN required to open the review PR."); + return 2; + } + const repoRoot = getRepoRoot(process.cwd()); + const dest = join(repoRoot, REVIEW_DIR, name); + rmSync(dest, { recursive: true, force: true }); + mkdirSync(dest, { recursive: true }); + cpSync(shotsDir, dest, { recursive: true }); + + // Also commit the raw text snapshots so the PR has a readable, diffable record + // (and so blank renders can be told apart from blank captures). + const txtDir = snapsDirFor(app); + if (existsSync(txtDir)) { + const txtDest = join(dest, "frames"); + mkdirSync(txtDest, { recursive: true }); + for (const f of readdirSync(txtDir)) + if (f.endsWith(".txt") && f !== "latest.txt") + cpSync(join(txtDir, f), join(txtDest, f)); + } + + const baseSha = git("rev-parse HEAD", repoRoot); + console.log(`Opening snapshot-review PR on branch ${branch}…`); + try { + const r = (await commitAndCreatePR({ + repoOwner, + repoName, + repoRoot, + branch, + baseBranch: process.env.SNAPSHOT_BASE ?? "main", + baseSha, + relativePath: join(REVIEW_DIR, name), + commitMessage: `snapshots: ${name} @ ${runId}`, + title, + body, + draft: false, + token, + })) as { prUrl?: string }; + console.log(`✓ review PR: ${r.prUrl ?? "(created)"}`); + + // When triggered by a /wizard-ci PR comment, report back on that PR with the + // flow strip + a link to the full side-by-side review. + if (commentPr) { + const changed = shots.length; // screenshot.ts --only-changed → changed frames only + const comment = [ + `### 📸 TUI snapshot review — \`${app}\``, + "", + `**${changed}** key-moment frame(s) differ from the baseline.` + + (r.prUrl ? ` [Full side-by-side review →](${r.prUrl})` : ""), + "", + `![flow](${rawBase}/_flow.png)`, + ].join("\n"); + postPRComment(commentPr, comment, repoRoot); + console.log(`✓ commented on PR #${commentPr}`); + } + return 0; + } catch (e) { + console.error(`✖ ${e instanceof Error ? e.message : String(e)}`); + return 1; + } +} + +main().then((code) => process.exit(code)); diff --git a/services/wizard-ci/snapshots.ts b/services/wizard-ci/snapshots.ts new file mode 100644 index 000000000..9f5b19217 --- /dev/null +++ b/services/wizard-ci/snapshots.ts @@ -0,0 +1,189 @@ +/** + * wizard-ci snapshots: real-TUI visual-regression for the CI-e2e test definitions. + * + * For each test definition this runs a real `--e2e` agent run, which drives the + * REAL wizard TUI and captures every key-moment screen as text, then diffs each + * against a committed baseline. Differences are surfaced side-by-side for review. + * + * pnpm wizard-ci-snapshots # run + compare, write HTML report + * pnpm wizard-ci-snapshots --update # accept current output as baseline + * + * Requires (in .env, sourced by the `wizard-ci-snapshots` mprocs proc): + * POSTHOG_PERSONAL_API_KEY the phx key (gateway bearer) + * POSTHOG_WIZARD_PROJECT_ID the project the key is scoped to + * POSTHOG_REGION us | eu + * WIZARD_PATH a wizard checkout that has e2e-harness/ (where the run happens) + * + * Drift never fails the command: diffs are surfaced (terminal + report.html), and + * only a genuine failure (run died, no snapshots) exits non-zero. report.html is + * the side-by-side; locally it's surfaced through mprocs. + */ +import "dotenv/config"; +import { join } from "path"; +import { + existsSync, + mkdirSync, + rmSync, + readFileSync, + writeFileSync, + readdirSync, + cpSync, +} from "fs"; +import { runE2e, snapsDirFor } from "./e2e.js"; + +const BASELINE_ROOT = join(import.meta.dirname, "snapshots"); +const OUT_ROOT = "/tmp/wizard-snapshots"; + +/** A CI-e2e test definition: which flow runs against which app. */ +interface TestDef { + name: string; + app: string; +} + +const TEST_DEFS: TestDef[] = [ + { + name: "express-todo", + app: "basic-integration/javascript-node/express-todo", + }, +]; + +type FrameStatus = "same" | "changed" | "added" | "removed"; +interface FrameDiff { + file: string; + status: FrameStatus; + baseline: string | null; + current: string | null; +} + +/** Union the two dirs by filename and classify each real-TUI snapshot. */ +function diffDirs(baselineDir: string, currentDir: string): FrameDiff[] { + const ls = (d: string) => + existsSync(d) ? readdirSync(d).filter((f) => f.endsWith(".txt")) : []; + const files = [...new Set([...ls(baselineDir), ...ls(currentDir)])].sort(); + return files.map((file) => { + const b = existsSync(join(baselineDir, file)) + ? readFileSync(join(baselineDir, file), "utf8") + : null; + const c = existsSync(join(currentDir, file)) + ? readFileSync(join(currentDir, file), "utf8") + : null; + const status: FrameStatus = + b === null ? "added" : c === null ? "removed" : b === c ? "same" : "changed"; + return { file, status, baseline: b, current: c }; + }); +} + +const BADGE: Record = { + same: "#3fb950", + changed: "#d29922", + added: "#58a6ff", + removed: "#f85149", +}; + +function escapeHtml(s: string): string { + return s.replace(/&/g, "&").replace(//g, ">"); +} + +function reportHtml(name: string, diffs: FrameDiff[]): string { + const cell = (s: string | null) => + s === null + ? `
— absent —
` + : `
${escapeHtml(s)}
`; + const rows = diffs + .map( + (d) => ` +
+

${d.status} ${d.file}

+
+
baseline
${cell(d.baseline)}
+
current
${cell(d.current)}
+
+
`, + ) + .join(""); + const changed = diffs.filter((d) => d.status !== "same").length; + return `wizard-ci snapshots — ${name} + +

wizard-ci TUI snapshots — ${name}

+
${diffs.length} key-moment frames · ${changed} changed/added/removed · review the side-by-side below
+${rows} +`; +} + +async function main(): Promise { + const args = process.argv.slice(2); + const update = args.includes("--update"); + const projectId = + process.env.POSTHOG_WIZARD_PROJECT_ID || + args[args.indexOf("--project-id") + 1] || + ""; + + let totalChanged = 0; + for (const def of TEST_DEFS) { + console.log(`\n=== snapshots: ${def.name} (${def.app}) ===`); + + const code = runE2e({ app: def.app, projectId }); + if (code !== 0) { + console.error(`✖ e2e run failed for ${def.name} (exit ${code})`); + return code; + } + const currentDir = snapsDirFor(def.app); + if (!existsSync(currentDir) || readdirSync(currentDir).filter((f) => f.endsWith(".txt")).length === 0) { + console.error(`✖ no snapshots at ${currentDir}`); + return 1; + } + + const baselineDir = join(BASELINE_ROOT, def.name); + if (update) { + rmSync(baselineDir, { recursive: true, force: true }); + mkdirSync(baselineDir, { recursive: true }); + cpSync(currentDir, baselineDir, { recursive: true }); + console.log(`✓ baseline updated → ${baselineDir}`); + continue; + } + + const diffs = diffDirs(baselineDir, currentDir); + const changed = diffs.filter((d) => d.status !== "same"); + const reportDir = join(OUT_ROOT, def.name); + mkdirSync(reportDir, { recursive: true }); + const report = join(reportDir, "report.html"); + writeFileSync(report, reportHtml(def.name, diffs)); + + console.log(`\n--- ${def.name}: ${diffs.length} frames, ${changed.length} differ ---`); + for (const d of diffs) { + const mark = d.status === "same" ? "·" : d.status === "changed" ? "~" : d.status === "added" ? "+" : "-"; + console.log(` ${mark} ${d.file}`); + if (d.status === "changed" && d.current) { + console.log(d.current.split("\n").map((l) => ` ${l}`).join("\n")); + } + } + console.log(`\nvisual report — open it: open ${report}`); + if (!existsSync(baselineDir)) + console.log(`(no baseline yet — run with --update to seed it)`); + totalChanged += changed.length; + } + + // Drift never fails: report the diffs and exit 0 (real failures returned + // non-zero earlier). Accept a new baseline with --update. + if (totalChanged) + console.log( + `\nℹ ${totalChanged} frame(s) changed — review the report above. ` + + `Accept with --update if the new run looks right.`, + ); + else console.log(`\n✓ snapshots match baseline.`); + + return 0; +} + +main().then((code) => process.exit(code)); diff --git a/services/wizard-ci/snapshots/express-todo/00-intro.ans b/services/wizard-ci/snapshots/express-todo/00-intro.ans new file mode 100644 index 000000000..c68fd24a4 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/00-intro.ans @@ -0,0 +1,8 @@ + ███ PostHog Wizard starting up + + We'll use AI to analyze your project and complete work. + .env* file contents will not leave your machine. + + + ⠋ Detecting project framework... + diff --git a/services/wizard-ci/snapshots/express-todo/01-health-check.ans b/services/wizard-ci/snapshots/express-todo/01-health-check.ans new file mode 100644 index 000000000..fd9ac9d77 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/01-health-check.ans @@ -0,0 +1 @@ + ⠋ Checking service status... \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/02-auth.ans b/services/wizard-ci/snapshots/express-todo/02-auth.ans new file mode 100644 index 000000000..890d69096 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/02-auth.ans @@ -0,0 +1,9 @@ +PostHog Setup Wizard +✔ Framework: Node.js + +How does the wizard use your data? +• Source files are read by Claude for AI context +• .env* and secrets stay on your machine +• Press [I] for full privacy & usage info + +⠋ Waiting for authentication... \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/03-auth.ans b/services/wizard-ci/snapshots/express-todo/03-auth.ans new file mode 100644 index 000000000..890d69096 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/03-auth.ans @@ -0,0 +1,9 @@ +PostHog Setup Wizard +✔ Framework: Node.js + +How does the wizard use your data? +• Source files are read by Claude for AI context +• .env* and secrets stay on your machine +• Press [I] for full privacy & usage info + +⠋ Waiting for authentication... \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/04-run.ans b/services/wizard-ci/snapshots/express-todo/04-run.ans new file mode 100644 index 000000000..82d05447c --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/04-run.ans @@ -0,0 +1,7 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ◆ Using provided API key (CI mode - OAuth bypassed) + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/05-run.ans b/services/wizard-ci/snapshots/express-todo/05-run.ans new file mode 100644 index 000000000..82d05447c --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/05-run.ans @@ -0,0 +1,7 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ◆ Using provided API key (CI mode - OAuth bypassed) + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/06-run.ans b/services/wizard-ci/snapshots/express-todo/06-run.ans new file mode 100644 index 000000000..964aeb865 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/06-run.ans @@ -0,0 +1,8 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Using provided API key (CI mode - OAuth bypassed) + ◆ Initializing Claude agent... + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/07-run.ans b/services/wizard-ci/snapshots/express-todo/07-run.ans new file mode 100644 index 000000000..fc6e815c0 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/07-run.ans @@ -0,0 +1,8 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Initializing Claude agent... + ◆ Verbose logs: /tmp/posthog-wizard.log + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/08-run.ans b/services/wizard-ci/snapshots/express-todo/08-run.ans new file mode 100644 index 000000000..edb02fbd1 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/08-run.ans @@ -0,0 +1,8 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verbose logs: /tmp/posthog-wizard.log + ◆ Agent initialized. Let's get cooking! + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/09-run.ans b/services/wizard-ci/snapshots/express-todo/09-run.ans new file mode 100644 index 000000000..90efe6a02 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/09-run.ans @@ -0,0 +1,8 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Agent initialized. Let's get cooking! + ◆ Writing your PostHog setup with events, error capture and more... + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/10-run.ans b/services/wizard-ci/snapshots/express-todo/10-run.ans new file mode 100644 index 000000000..af2e559fa --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/10-run.ans @@ -0,0 +1,8 @@ + Learn Tasks + + ⠋ Analyzing project... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/11-run.ans b/services/wizard-ci/snapshots/express-todo/11-run.ans new file mode 100644 index 000000000..a343d062e --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/11-run.ans @@ -0,0 +1,10 @@ + Learn Tasks + + ◻ Plan event tracking + + ⠋ Progress: 0/1 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/12-run.ans b/services/wizard-ci/snapshots/express-todo/12-run.ans new file mode 100644 index 000000000..a343d062e --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/12-run.ans @@ -0,0 +1,10 @@ + Learn Tasks + + ◻ Plan event tracking + + ⠋ Progress: 0/1 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/13-run.ans b/services/wizard-ci/snapshots/express-todo/13-run.ans new file mode 100644 index 000000000..7cfc65c97 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/13-run.ans @@ -0,0 +1,11 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + + ⠋ Progress: 0/2 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/14-run.ans b/services/wizard-ci/snapshots/express-todo/14-run.ans new file mode 100644 index 000000000..7cfc65c97 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/14-run.ans @@ -0,0 +1,11 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + + ⠋ Progress: 0/2 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/15-run.ans b/services/wizard-ci/snapshots/express-todo/15-run.ans new file mode 100644 index 000000000..2fad5e853 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/15-run.ans @@ -0,0 +1,12 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + + ⠋ Progress: 0/3 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/16-run.ans b/services/wizard-ci/snapshots/express-todo/16-run.ans new file mode 100644 index 000000000..2fad5e853 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/16-run.ans @@ -0,0 +1,12 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + + ⠋ Progress: 0/3 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/17-run.ans b/services/wizard-ci/snapshots/express-todo/17-run.ans new file mode 100644 index 000000000..307572ea8 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/17-run.ans @@ -0,0 +1,13 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + + ⠋ Progress: 0/4 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/18-run.ans b/services/wizard-ci/snapshots/express-todo/18-run.ans new file mode 100644 index 000000000..307572ea8 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/18-run.ans @@ -0,0 +1,13 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + + ⠋ Progress: 0/4 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/19-run.ans b/services/wizard-ci/snapshots/express-todo/19-run.ans new file mode 100644 index 000000000..cee0cded9 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/19-run.ans @@ -0,0 +1,14 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + + ⠋ Progress: 0/5 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/20-run.ans b/services/wizard-ci/snapshots/express-todo/20-run.ans new file mode 100644 index 000000000..cee0cded9 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/20-run.ans @@ -0,0 +1,14 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + + ⠋ Progress: 0/5 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/21-run.ans b/services/wizard-ci/snapshots/express-todo/21-run.ans new file mode 100644 index 000000000..be9799eff --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/21-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 0/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/22-run.ans b/services/wizard-ci/snapshots/express-todo/22-run.ans new file mode 100644 index 000000000..be9799eff --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/22-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◻ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 0/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/23-run.ans b/services/wizard-ci/snapshots/express-todo/23-run.ans new file mode 100644 index 000000000..60b20a8b2 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/23-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ▶ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 0/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Writing your PostHog setup with events, error capture and more... + ◆ Checking project structure. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/24-run.ans b/services/wizard-ci/snapshots/express-todo/24-run.ans new file mode 100644 index 000000000..786b5bfff --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/24-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ▶ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 0/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Checking project structure. + ◆ Verifying PostHog dependencies. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/25-run.ans b/services/wizard-ci/snapshots/express-todo/25-run.ans new file mode 100644 index 000000000..e6b7f2588 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/25-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ▶ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 0/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verifying PostHog dependencies. + ◆ Generating events based on project. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/26-run.ans b/services/wizard-ci/snapshots/express-todo/26-run.ans new file mode 100644 index 000000000..bb7d71e0d --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/26-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◻ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 1/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verifying PostHog dependencies. + ◆ Generating events based on project. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/27-run.ans b/services/wizard-ci/snapshots/express-todo/27-run.ans new file mode 100644 index 000000000..e1c5f00a3 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/27-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ▶ Install PostHog SDK + ◻ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 1/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verifying PostHog dependencies. + ◆ Generating events based on project. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/28-run.ans b/services/wizard-ci/snapshots/express-todo/28-run.ans new file mode 100644 index 000000000..0e36463a9 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/28-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ▶ Install PostHog SDK + ▶ Configure environment variables + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 1/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verifying PostHog dependencies. + ◆ Generating events based on project. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/29-run.ans b/services/wizard-ci/snapshots/express-todo/29-run.ans new file mode 100644 index 000000000..3410c94af --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/29-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ▶ Install PostHog SDK + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 2/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Verifying PostHog dependencies. + ◆ Generating events based on project. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/30-run.ans b/services/wizard-ci/snapshots/express-todo/30-run.ans new file mode 100644 index 000000000..26d17ce0b --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/30-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ▶ Install PostHog SDK + ◻ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 2/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Generating events based on project. + ◆ Inserting PostHog capture code + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/31-run.ans b/services/wizard-ci/snapshots/express-todo/31-run.ans new file mode 100644 index 000000000..18e4df848 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/31-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ▶ Install PostHog SDK + ▶ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 2/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Generating events based on project. + ◆ Inserting PostHog capture code + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/32-run.ans b/services/wizard-ci/snapshots/express-todo/32-run.ans new file mode 100644 index 000000000..96d69a2a3 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/32-run.ans @@ -0,0 +1,17 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ▶ Install PostHog SDK + ▶ Implement PostHog and capture events + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 2/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Inserting PostHog capture code + ◆ Edited `index.js` — added PostHog init with  + `setupExpressRequestContext`/`setupExpressErrorHandler`, and `capture` calls for `todo_created`,  + `todo_updated`, `todo_completed`, and `todo_deleted`. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/33-run.ans b/services/wizard-ci/snapshots/express-todo/33-run.ans new file mode 100644 index 000000000..af0b44815 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/33-run.ans @@ -0,0 +1,17 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ▶ Install PostHog SDK + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 3/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Inserting PostHog capture code + ◆ Edited `index.js` — added PostHog init with  + `setupExpressRequestContext`/`setupExpressErrorHandler`, and `capture` calls for `todo_created`,  + `todo_updated`, `todo_completed`, and `todo_deleted`. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/34-run.ans b/services/wizard-ci/snapshots/express-todo/34-run.ans new file mode 100644 index 000000000..abea1ee85 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/34-run.ans @@ -0,0 +1,17 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ▶ Install PostHog SDK + ◻ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 3/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Edited `index.js` — added PostHog init with  + `setupExpressRequestContext`/`setupExpressErrorHandler`, and `capture` calls for `todo_created`,  + `todo_updated`, `todo_completed`, and `todo_deleted`. + ◆ Finding and correcting errors. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/35-run.ans b/services/wizard-ci/snapshots/express-todo/35-run.ans new file mode 100644 index 000000000..91364697e --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/35-run.ans @@ -0,0 +1,17 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ▶ Install PostHog SDK + ▶ Validate integration + ◻ Create PostHog dashboard + + ⠋ Progress: 3/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Edited `index.js` — added PostHog init with  + `setupExpressRequestContext`/`setupExpressErrorHandler`, and `capture` calls for `todo_created`,  + `todo_updated`, `todo_completed`, and `todo_deleted`. + ◆ Finding and correcting errors. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/36-run.ans b/services/wizard-ci/snapshots/express-todo/36-run.ans new file mode 100644 index 000000000..36b08f547 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/36-run.ans @@ -0,0 +1,17 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ▶ Install PostHog SDK + ◻ Create PostHog dashboard + + ⠋ Progress: 4/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Edited `index.js` — added PostHog init with  + `setupExpressRequestContext`/`setupExpressErrorHandler`, and `capture` calls for `todo_created`,  + `todo_updated`, `todo_completed`, and `todo_deleted`. + ◆ Finding and correcting errors. + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/37-run.ans b/services/wizard-ci/snapshots/express-todo/37-run.ans new file mode 100644 index 000000000..8ee6f7d43 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/37-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ▶ Install PostHog SDK + ◻ Create PostHog dashboard + + ⠋ Progress: 4/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Finding and correcting errors. + ◆ Configured dashboard: (creating now) + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/38-run.ans b/services/wizard-ci/snapshots/express-todo/38-run.ans new file mode 100644 index 000000000..665b8bc05 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/38-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ▶ Install PostHog SDK + ▶ Create PostHog dashboard + + ⠋ Progress: 4/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Finding and correcting errors. + ◆ Configured dashboard: (creating now) + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/39-run.ans b/services/wizard-ci/snapshots/express-todo/39-run.ans new file mode 100644 index 000000000..741293159 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/39-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Install PostHog SDK + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ▶ Create PostHog dashboard + + ⠋ Progress: 5/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Finding and correcting errors. + ◆ Configured dashboard: (creating now) + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/40-run.ans b/services/wizard-ci/snapshots/express-todo/40-run.ans new file mode 100644 index 000000000..11a95e436 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/40-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Install PostHog SDK + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ▶ Create PostHog dashboard + + ⠋ Progress: 5/6 completed +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Configured dashboard: (creating now) + ◆ Created setup report: /private/tmp/wizard-e2e-express-todo/posthog-setup-report.md + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/41-run.ans b/services/wizard-ci/snapshots/express-todo/41-run.ans new file mode 100644 index 000000000..d9fefd704 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/41-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Install PostHog SDK + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ◼ Create PostHog dashboard + + ⠋ Cleaning up... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Configured dashboard: (creating now) + ◆ Created setup report: /private/tmp/wizard-e2e-express-todo/posthog-setup-report.md + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/42-run.ans b/services/wizard-ci/snapshots/express-todo/42-run.ans new file mode 100644 index 000000000..3084dc61d --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/42-run.ans @@ -0,0 +1,15 @@ + Learn Tasks + + ◼ Plan event tracking + ◼ Install PostHog SDK + ◼ Configure environment variables + ◼ Implement PostHog and capture events + ◼ Validate integration + ◼ Create PostHog dashboard + + ⠋ Cleaning up... +──────────────────────────────────────────────────────────────────────────────────────────────────── + ┊ Created setup report: /private/tmp/wizard-e2e-express-todo/posthog-setup-report.md + ◆ PostHog integration complete + +  Status   Tail logs   Visualizer   HN  \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/43-outro.ans b/services/wizard-ci/snapshots/express-todo/43-outro.ans new file mode 100644 index 000000000..7845ae6cc --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/43-outro.ans @@ -0,0 +1,23 @@ +✔ Successfully installed PostHog! + +Dashboard: https://us.posthog.com/project/228144/dashboard/1746617?utm_source=wizard&utm_medium=cli& +utm_content=outro-dashboard + +Check ./posthog-setup-report.md for details + +What the agent did: +• Analyzed your Node.js project structure +• Installed the posthog-node package +• Created PostHog initialization with proper configuration +• Configured graceful shutdown for event flushing +• Added example code for events, feature flags, and error capture +• Added environment variables to .env file + +Learn more: +https://posthog.com/docs/libraries/node?utm_source=wizard&utm_medium=cli&utm_content=outro-docs + +Note: This wizard uses an LLM agent to analyze and modify your project. Please review the changes  +made. +How did this work for you? Drop us a line: wizard@posthog.com + +Press any key to continue \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/44-mcp.ans b/services/wizard-ci/snapshots/express-todo/44-mcp.ans new file mode 100644 index 000000000..91a69e63a --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/44-mcp.ans @@ -0,0 +1,3 @@ +Install the MCP so you can chat to your data + +Detecting supported editors... \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/45-slack-connect.ans b/services/wizard-ci/snapshots/express-todo/45-slack-connect.ans new file mode 100644 index 000000000..11e7acbb9 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/45-slack-connect.ans @@ -0,0 +1,2 @@ + +⠋ Checking for an existing Slack connection... \ No newline at end of file diff --git a/services/wizard-ci/snapshots/express-todo/46-keep-skills.ans b/services/wizard-ci/snapshots/express-todo/46-keep-skills.ans new file mode 100644 index 000000000..64cbb0cf7 --- /dev/null +++ b/services/wizard-ci/snapshots/express-todo/46-keep-skills.ans @@ -0,0 +1,3 @@ +Keep the skills? + +Checking installed skills... \ No newline at end of file