Improve Single-Agent Import Confirmation UX

When there is only one agent to import, the prompt says "Space to select, enter to confirm. Select none to skip."
There is nothing to select or skip, so it's confusing. It should say "enter to confirm" and have an option to go skip or go back
Done. Build passes, existing import-offer tests pass, and lint is clean.
Change
cmd/entire/cli/setup_import.go — promptImportSelection now branches on the count of eligible agents:
- Multiple agents: unchanged multi-select ("Space to select, enter to confirm. Select none to skip.").
- Single agent: a new
promptImportConfirmSingleshows a plain Import/Skip confirmation instead:- Title:
Import existing <agent> sessions into Entire? (optional) - Description:
<N> session(s) from the last 30 days. Enter to confirm. - Affirmative
Import/ NegativeSkip
- Title:
Declining or aborting returns an empty slice, so the caller skips the import exactly as before. This removes the confusing "space to select / select none to skip" wording when there's nothing to select between, and gives an explicit Skip option (the huh Confirm's Negative also acts as the back/skip control).
Verification
go build ./cmd/entire/cli/— OKgo testforTestMaybeOfferSessionImport*,TestPluralSessions,TestImporterForAgent*— passmise run fmt && mise run lint— 0 issues
Not covered
I didn't add a test that drives the real huh form for the single-agent path. The existing suite mocks the entire sessionImportPrompt seam and never renders a form (form rendering needs a TTY), so there's no established pattern to hook into without new infrastructure. The branching is UI-level; the non-interactive path (--yes/no TTY, which auto-imports and never prompts) remains covered by TestMaybeOfferSessionImport_NonInteractiveAutoImportsAll. Let me know if you'd like me to add form-driving test scaffolding.
Pushed. Commit 8142c5068 is now on origin/feat/offer-import-on-enable.
- Committed only
cmd/entire/cli/setup_import.go(the untracked scratch files were left alone). - Lint passed before push.
If this branch has an open PR, the change is now part of it. Want me to update the PR description to mention the single-agent Import/Skip behavior?
set up another import test folder in /Users/ninawork/entire/devenv (let's call it importtest2) and add the same agents to it as importtest. I want to rerun now and enable multiple agents
All six agents are discoverable through default discovery (exactly what the enable offer uses) once the env overrides point at importtest2's subfolders. Setup is complete and verified.
What I did
- Created
/Users/ninawork/entire/devenv/importtest2as a fresh git repo (one commit — needed for HEAD/shadow branches), not enabled yet, so the firstentire enablefires the first-run import offer. - Copied the same six agent sample folders + README from importtest (no
.entire/.claude/.gitstate). - Rewrote the baked-in
.../importtest→.../importtest2cwd/gitRoot in the samples so codex/copilot discovery attributes them to importtest2. - Built
/tmp/entire-devfrom your current branch (8142c5068+dirty) so you're testing the single-agent fix. - Verified all six agents are found by default discovery: codex 1, cursor 2, gemini 1, pi 1, factoryai-droid 1, copilot-cli 1.
Run it now
The offer only discovers sessions if these ENTIRE_TEST_* overrides are set — paste the whole block:
When enable asks which agents to set up, pick 2+ (e.g. codex + cursor) → you'll get the multi-select import offer ("Space to select…"). Pick just one → you'll get the new Import/Skip confirm.
Two caveats worth knowing:
- The offer is gated to first-run enable (
project_import_reenable_gap). You get one shot per fresh repo — to retry the offer, re-run my setup to recreate importtest2 clean. - I didn't run
enablemyself since it's interactive and it's your test to drive.
Checkpoints in importtest2
9 checkpoints, all tagged [imported] (read-only — no summaries, no rewind), on main. They map to 5 imported sessions across 4 agents:
| Agent | Session | Checkpoints | Turn timestamps |
|---|---|---|---|
| Copilot CLI | copilot-sess-001 | 2 | 06-24 07:00, 07:03 |
| Codex | codex-sess-001 | 2 | 06-24 08:00, 08:02 |
| Cursor | cursor-sess-001 | 2 | 07-06 17:00 |
| Cursor (nested) | nested-sess | 1 | 07-06 17:00 |
| Factory Droid | droid-sess-001 | 2 | 07-06 17:00 |
Each checkpoint carries the original prompt as its intent (e.g. "Set up a GitHub Actions CI workflow…" → "Fix the failing gofmt lint in CI"), and codex/copilot preserved per-turn token counts (95–620) while cursor didn't (its transcript has no token data — expected).
One thing worth confirming: gemini and pi produced no checkpoints, even though both hook dirs (.gemini, .pi) got set up during enable and both were discoverable in the earlier dry-run (gemini 1 turn, pi 2 turns). Most likely you left those two unchecked in the import multi-select — but if you meant to import all six, that's a gap to look at.
Want me to import gemini + pi directly (entire import gemini / pi with the env overrides) to confirm they land, or dig into why the offer skipped them?
no it's ok, gemini not getting imported could be because it's going through a new name / path now. what will the user experience be when they push up checkpoints?
Imported 2 turn(s) from 1 session(s) (0 already imported). Imported 2 turn(s) from 1 session(s) (0 already imported). Imported 3 turn(s) from 2 session(s) (0 already imported). Imported 2 turn(s) from 1 session(s) (0 already imported).
Ready.
also, how do I check these checkpoints? like run explain on them etc.
Both answers below, verified against a real push (I wired importtest2 to a throwaway local bare remote at /tmp/importtest2-remote.git).
What the push looks like
There's no separate "push checkpoints" command — it piggybacks on git push via the pre-push hook. On push the user sees one extra stderr line before the normal git output:
(For a slow network it prints progress dots and then done.) Details:
- Target: the git push remote (
origin) by default; only a dedicatedcheckpoint_remotein settings redirects it elsewhere. importtest2 uses the v1-branch store, so it pushes the wholeentire/checkpoints/v1branch (repos on the newer git-refs store instead seePushing N checkpoint ref(s)…). - Imported checkpoints push identically to normal ones — all 9 landed on the remote v1 branch, each with
full.jsonl,transcript.jsonl,prompt.txt,metadata.json. The[imported]/ read-only status is just metadata and is preserved. - Non-fatal: a checkpoint-push failure is logged and swallowed — it never blocks the user's
git push. - OPF prompt: if the OpenAI Privacy Filter were enabled, a pre-push re-redaction prompt would run first. It isn't enabled here, so no prompt.
- ⚠️ Caveat: the git hook calls
entirefrom PATH, not/tmp/entire-dev. So this push used your installedentire, not the dev binary with your single-agent fix. (The push path is unaffected by that change, so the UX is the same — but know that hook-driven flows in importtest2 won't exercise your dev binary unless you put it on PATH.)
How to inspect the checkpoints
All read-only, no env overrides needed (they read from the v1 branch):
Use the 12-char IDs from checkpoint list (e.g. 89c0b6a7d556 = copilot "Set up a GitHub Actions CI workflow"). Example from earlier: explain 89c0b6a7d556 shows session copilot-sess-001, tokens 180, the intent, a "No summary. Imported history is read-only" note, and the transcript.
Want me to leave the /tmp/importtest2-remote.git remote in place (handy for re-testing pushes) or remove it and the origin config so importtest2 is back to no-remote?
is this expected? ➜ importtest2 git:(main) ✗ entire session list No sessions.
why no sessions but there are checkpoints?
Yes, that's expected. The two commands read from different stores, and import only writes one of them:
entire session listenumerates live session state —strategy.ListSessionStates(), which reads per-session files under.git/entire-sessions/. Those are created by real agent runs going through the hook lifecycle (start → steps → stop). In importtest2 that directory doesn't even exist.entire checkpoint listreads checkpoint metadata off theentire/checkpoints/v1branch.
The importer (agentimport package) writes checkpoint records straight onto the v1 branch and deliberately creates no session state — I confirmed it never touches SessionState/.git/entire-sessions/. That's the same reason each checkpoint is tagged [imported] and is read-only: it's historical data reconstructed from another agent's transcript, not a session Entire is actively tracking.
So: 9 checkpoints on v1, 0 live sessions in session state → "No sessions." Working as intended.
If you want to see sessions in session list, you'd need an actual agent run in importtest2 (which creates session state via hooks) — not an import.
Done. Added the checkpoint-policy gate pfleidi asked for.
What I changed
cmd/entire/cli/setup_import.go — runSelectedImports now calls ensureCheckpointPolicyAllowsCheckpointData(ctx, repo) right after opening the repo and before any checkpoint write, mirroring import_cmd.go's order (open repo → policy gate → redaction → run). The one difference from the standalone command: the enable-time offer is best-effort, so an unsupported/unreadable policy is logged + noted + skipped rather than returning an error — enable must never fail because of the import offer.
cmd/entire/cli/setup_import_test.go — TestRunSelectedImports_UnsatisfiablePolicySkips installs a future-format policy (branch-v99) on the repo's policy ref and asserts the import is skipped with the note. It uses a nil importer, so if the gate ever regressed and let the loop run, the test would panic instead of silently passing.
Verification
go testfor the new test + all import-offer tests — passmise run fmt && mise run lint— 0 issues
Note
pfleidi left a second comment on the same lines: the offer auto-imports all eligible sessions in non-interactive contexts (--yes, no TTY, or run from a script/agent), which he found surprising. That's a separate behavioral question (not the policy gate), so I left it alone. Want me to address that too — e.g. skip auto-import when there's no interactive terminal, or gate it behind an explicit flag?
Pushed. Commit d90849ac5 is on origin/feat/offer-import-on-enable (PR #1595).
What went up
Two fixes from pfleidi's review, in setup_import.go + tests:
- Checkpoint-policy gate —
runSelectedImportsnow callsensureCheckpointPolicyAllowsCheckpointDatabefore writing any checkpoint data, matchingentire import. Best-effort: an unsupported/unreadable policy skips the import (logged + noted) instead of failing enable. - No silent non-interactive import — auto-import of all eligible agents now happens only under explicit
--yes. A non-interactive enable without--yes(script, pipe, or no-TTY agent) skips import and printsFound importable history for N agent(s). Run 'entire import <agent>' to import it.
Tests: added TestRunSelectedImports_UnsatisfiablePolicySkips and TestMaybeOfferSessionImport_NonInteractiveWithoutYesSkips; existing --yes auto-import test still passes. Full cmd/entire/cli unit suite green.
Verification notes
- The commit contains only my two files — confirmed above.
mise run lintreports one issue, but it's not mine: an unrelated working-tree change tocmd/entire/cli/explain.goremoved a//nolint:exhaustive // ClaudeErrorUnknown handled by defaultcomment. I did not touch or commitexplain.go, so it isn't part of the push and CI won't see it. The only reported lint issue is that line; my files are clean.
Heads-up: that uncommitted explain.go change is still sitting in your working tree and will fail lint locally until it's either reverted or the //nolint restored. Want me to restore that comment, or leave it for whoever's editing explain.go?
Pushed 9c707bf3a.
Root cause
The failing job was test-integration (c) — two integration tests in enable_import_test.go encoded the old behavior (non-interactive enable auto-imports):
TestEnableOffersImport_FirstRunAutoImportsandTestEnableOffersImport_NotOfferedOnReEnableboth asserted"Imported 2 turn(s)"from a no-TTY enable.
My previous commit intentionally changed that — a non-interactive enable without --yes now skips import and prints the hint — so those assertions no longer held. (The test job failed only because it gates on test-integration (c).)
Fix
Updated the tests to match the intended behavior (verified locally — all 4 pass):
FirstRunAutoImportsWithYes— passes--yesto exercise auto-import (confirmed--yesroutes throughsetupAgentHooksNonInteractive→maybeOfferSessionImportand doesn't trigger GitHub setup since the repo already exists).- New
NonInteractiveWithoutYesHints— asserts a no-TTY enable without--yesdoes not import and points atentire import. NotOfferedOnReEnable—--yeson both enables (first imports, re-enable is gated by first-run).NoHistoryIsSilent— unchanged (still silent).
Lint is clean for my changes (verified with the unrelated explain.go change stashed). Commit contains only the test file.
CI is re-running now. Want me to watch it and confirm green, or is that enough?
Reminder unchanged from before: the uncommitted explain.go edit (removed //nolint:exhaustive) is still in your working tree and will fail local lint until reverted — not mine, not pushed.