Daniel Kim 979d8e9362 Rework first-run onboarding and the New project modal (v0.1.79) (#126)
* Tighten the onboarding + new-project flows

Onboarding:
- Gate steps 1 and 2 (a signed-in agent, GitHub access) — both are hard
  requirements for chat and project creation, so Continue is disabled
  until they're met.
- Drop the data-dir step: the default suits ~everyone, it's write-once,
  and Settings > Storage already exposes it with a move flow.
- Split harness notes into not-installed vs not-signed-in, each with the
  real command (claude auth login / codex login / opencode auth login).
- Fix OpenCode reporting Connected while unauthenticated (agent_ready
  ignored authentication).

New project modal:
- Auto-open on the onboarding hand-off.
- Only show the fork choice when the user can actually push (new
  /api/github/repo-access), and say what happens when they can't.
- Name the real GitHub account in repo previews (new /api/github/account).
- Drop the branch picker; resolve the repo's real default branch server
  side instead of assuming "main".
- Describe the copy honestly: it's a single-branch, history-less snapshot,
  not a GitHub fork.
- Top-anchor the modal so the tab row doesn't move between tabs.

* Review round 1: fix onboarding lockouts, trapped name field, stale fork mode

- opencode: treat a non-empty `opencode models` list as authenticated, so
  env-key installs (no auth.json) aren't locked out by the new step-1 gate.
  Claude and Codex already had the equivalent fallback.
- Onboarding: surface a load error. A rejected probe left harnesses/git at
  null forever, which the gates read as not-ready — disabling Continue with
  no hint and no way out, on the screen that is the whole app at first boot.
- NewProjectForm: hoist the project-name field out of repoFields. A stalled
  access check used to unmount it, and `valid` needs a name, so the form
  became unsubmittable with no escape.
- NewProjectForm: reset canPush on every repo change and sync repoMode to
  the answer, so a forced "fork" can't linger and silently copy a repo the
  user can push to.
- github.rs: share one authed_get helper (with a 10s timeout — there was
  none), URL-encode owner/repo, drop the now-dead has_push_access wrapper.
- Drop the unread `known`/`defaultBranch` fields from the repo-access
  response and its TS type.
- ProjectsHome: consume the post-onboarding hand-off so a later remount
  can't re-open the modal.
- Deduplicate the paper-repo hint and the two identical code-chip CSS rules;
  tie the modal offset and max-height together with a --modal-top token.

* Review round 2: correct the opencode fallback, unblock SSH-only users

The round-1 opencode fix rested on a false premise — I claimed `opencode
models` only lists authenticated providers. Tested: under a signed-out HOME
it returns nothing, so the fallback never fired for the users it was meant to
rescue, and had the catalog ever been listed it would have marked signed-out
installs Connected. Replaced with an explicit provider env-key check, matching
claude.rs. Verified both directions: env key set -> ready, none -> gated.

- Onboarding: step 2 gets a "Skip — I use SSH keys" escape. ensure_clone
  tries ssh first, so a token isn't actually required for the use-existing
  path, and git_settings_json can't see SSH keys at all — the hard gate was
  locking out a setup the CLI's own startup note calls supported. Also covers
  the git-not-installed dead end, where the token form is unreachable.
- Onboarding: split the shared loadError into per-probe flags. A telemetry
  failure was putting a connectivity error on unrelated gates and hiding the
  actionable hint behind it.
- NewProjectForm: only force "fork" on denied access; the symmetric sync was
  undoing selectPaper's deliberate copy default for writable paper repos.
  Reset repoMode in onRepoChange instead, which is where the repo changes.
- github.rs: drop the deleted has_push_access's doc comment, left welded onto
  authed_get and describing the wrong thing.
- Bump to v0.1.79 (+ Cargo.lock): ui/dist changes don't ship without it.

* Review round 3: detect synced-env keys, unstick the branch fallback

- Harness detection: resolve API keys through orx's synced env file as well
  as the process env (new detect::api_key, used by claude.rs and opencode.rs).
  A key pasted into Settings > Environment is handed to the harness child, so
  chat works — but detect() couldn't see it, and step 1 is a hard gate.
  Verified: key only in ~/.openresearch/env now reports ready for both.
- opencode: widen the provider key list (Gemini/Groq/xAI/DeepSeek). Measured
  rather than assumed that `opencode models` can't stand in for a credential:
  signed out with no keys it still lists 7 free/bundled models, so keying off
  it would report signed-out installs as Connected.
- baseline_branch: fall back to `git ls-remote --symref` when the GitHub API
  can't answer. Without a token the branch resolved to a hardcoded "main" and
  the clone died on any master/dev repo — and the form no longer has a branch
  field to recover with. git's own ssh/https credentials need no token.
- Step 1 no longer gates on a failed probe: detection fans out to per-binary
  version/model commands that can stall, and there's no way out of onboarding.
- Step 2's skip is always offered and reworded "Skip for now": a stale gh
  token still reads as connected, hiding the escape from the users who need
  it most. Promoted to a real button rather than a muted link.

* Rebuild ui/dist after rebase

* Review round 4: stop 404 forcing a history-losing copy

- repo_meta: a 404 means "invisible to this token", which is also how a
  private repo looks to an SSH-only user who can push to it fine. It was
  reported as can_push=false, which force-forks — and this branch removed the
  "Use this repo" button in that state, so the override was gone too. The
  user would silently get a new repo with their history dropped. Report
  unknown instead, matching the fallback used everywhere else.
- remote_default_branch: bound it with a 15s timeout. ls-remote tries ssh then
  https, and a black-holed host blocks on TCP connect ~75s each — the exact
  hazard authed_get got a timeout for last round, reintroduced on the git path
  of the same request. A stall now degrades to "main" instead of hanging.
- git.rs: the new function had been inserted between ensure_clone's doc
  comment and ensure_clone, so the comment described the wrong function and
  ensure_clone was left undocumented.
- Never show a "Detecting…" spinner next to a fetch error: on a failed probe
  both steps now render the retry copy in place of the card, instead of a
  spinner that claims work is in progress beside an enabled Continue.
- onRepoChange no longer resets repoMode in paper mode, where editing the
  auto-filled repo would quietly retarget pushes to the upstream paper repo.

* Review round 5: make step 2 an honest soft gate, fix stale-state contradictions

- Step 2 no longer pretends to gate. Cloning and pushing work over SSH keys,
  so a disabled Continue beside an always-enabled "Skip for now" was two
  buttons doing the same thing — one greyed out, which reads as a bug. One
  Continue, always enabled, and the hint states what's missing instead of
  blocking on it.
- A rejected probe now clears its state as well as setting the error flag.
  Otherwise a failed *re-check* kept the old cards on screen: stale "Not
  signed in" badges and a "sign in to continue" hint next to a Continue that
  harnessError had just un-gated, with no indication the check had failed.
- Mode switches reset repoMode via a chooseMode helper. Leaving paper mode
  carries the paper's repo into the field but not its copy default — and
  because the field is pre-filled, onRepoChange never fired to reset it, so
  "Private copy" stayed selected on a repo the user never chose it for.
- Deduplicate the project-name field and blank-repo hint, both of which were
  inlined a third time in "New blank repo" (flagged since round 2).

* Review round 6: restore the Existing repo name field, kill the footgun

Round 5's nameField refactor turned it into a function but updated only two of
three call sites. `{parsed && nameField}` passes the function itself as a
React child, which renders nothing — so "Existing repo" had no Project name
input and Create stayed disabled. Both tsc and a grep of the call sites missed
it; three reviewers caught it.

Made the class of bug impossible rather than just fixing the call site: the
autoFocus parameter was the only reason nameField was a function, so it's a
plain element again like blankRepoHint. Verified in a browser this time —
name field present and submit enabled in every mode.

- Onboarding: the docstring still called step 2 a hard gate, which round 5
  made false, and it sat below the RETRY_COPY const so it documented the wrong
  symbol. Rewritten and moved.
- Onboarding: sequence-guard load(). A token save landing mid-refresh was
  overwritten by the pre-save snapshot, leaving the card reading "Not
  connected" until the next check; the save now supersedes in-flight probes.

* Review round 7: fix a Re-check lockout, share renderNote, trim comments

- load()'s .finally was sequence-guarded, so a token save (which bumps loadSeq
  to supersede the in-flight probe) left `checking` true forever and both
  Re-check buttons dead for the session. `checking` is "a load is running",
  not data a stale response can corrupt — it shouldn't be guarded. Introduced
  by round 6's guard; verified fixed by superseding a slow load in a browser.
- renderNote moved to its own module: ModelPicker, a core chat control, was
  importing it from the first-run walkthrough.
- Drop the redundant {parsed && …} guard (repoFields already returns null),
  a doubled CSS comment block, and two stacked rationale comments.
- Document the opencode agent_ready tightening where the behaviour changes:
  installs with no auth.json and no provider key now read "Not signed in".
2026-07-26 21:48:33 -07:00
…
…
…

OpenResearch CLI (orx)

Important

If you are an OpenResearch user or someone who is interested in autoresearch, we'd love to chat with you. Please email contact@alphaxiv.org if interested.

Run autoresearch on your machine

  • Run research agents in parallel. Spins up agents in different worktrees so you can investigate several different directions at once.
  • Works with Claude Code, Codex, and OpenCode
  • Bring your own compute. Works with SSH, Slurm, Kubernetes, Modal, HuggingFace and more.
  • Give it a goal. Can run the entire autoresearch loop from literature review to experiment analysis.
  • Local and private. Your code and your data stays on your machine.

https://github.com/user-attachments/assets/33b62182-0795-490d-9366-0fb0b4bd49fd

Quick start

curl -LsSf https://openresearch.sh/install.sh | sh
orx up

The dashboard opens at http://127.0.0.1:4791. Give the agent a goal — for example, ask it to reproduce a paper:

/reproduce-paper <paper URL or title> on <compute>

or turn one into an interactive marimo notebook:

/paper-to-marimo <paper URL or title> on <compute>

The dashboard

orx up runs a single local process on 127.0.0.1 — an embedded web UI plus a JSON/SSE API over a local SQLite store. From there you get:

  • Agent chat — a research assistant with full project context, backed by your locally installed harness: Claude Code, Codex, or OpenCode (pick the harness and model in the UI). Ask it to analyze runs, dig into results, edit code, and spin up new experiments.
  • The experiment tree — every experiment is a git branch: a runnable snapshot of your code. The root is your baseline; children are variants measured against it, so lineage stays explicit.
  • Runs — launch on your compute (Modal, Hugging Face Jobs, Kubernetes, Slurm, any SSH box, your own machine) or OpenResearch managed GPUs, and watch live logs, statuses, git diffs, files, and linked W&B runs stream in.
  • Autoresearch — describe a goal and let the agent run autonomously toward it: proposing, launching, and analyzing experiments.

Everything binds to loopback only; nothing on the dashboard's paths leaves your machine except the compute and paper-search calls you initiate.

On a remote machine

Develop from your laptop while the dashboard runs next to your GPUs:

orx up --remote user@host        # or an ~/.ssh/config alias; append :PORT for a custom SSH port

This starts orx up on the remote box over SSH, tunnels the port back, and opens your browser locally. Note the remote server is unauthenticated on that host's loopback, so other users on the same box can reach it.

Commands

Run orx --help (or orx <command> --help) for full usage. The highlights:

Area Commands
Dashboard up
Auth login, logout
Projects projects, explore, project, create-project, env
Experiments experiments, create-experiment, exp status/cmd/run/cancel
Runs & evidence runs, logs, search-logs, artifacts, artifact, wandb, query, chart, report
Compute compute, instance create
Literature lit, paper (alphaXiv full-text search — no login required)
Agent integration install-skills, skill
Maintenance version, update, telemetry

orx install-skills drops the OpenResearch skill into your local coding agents (Claude Code, Codex, OpenCode, Cursor) so they can drive orx themselves — orx login offers this too.

Installing

The install script above fetches the latest prebuilt release (macOS and Linux, x86_64 and arm64) and is the same as:

curl -LsSf https://github.com/alphaXiv/openresearch-cli/releases/latest/download/openresearch-cli-installer.sh | sh

orx update keeps script-installed binaries current; interactive terminals also get a once-a-day background check with a one-line stderr notice (silence it with ORX_NO_UPDATE_CHECK=1).

From source

Requires Rust (stable) via rustup. The prebuilt dashboard UI is committed at ui/dist, so a plain build works:

cargo build --release          # binary at target/release/orx
cargo install --path .         # or install onto your PATH (~/.cargo/bin)

To hack on the dashboard UI itself (Vite + React, embedded into the binary at build time):

cd ui && pnpm install && pnpm build

Run the tests with cargo test.

Configuration

  • API URL — defaults to production (https://api.openresearch.sh); override with --api-url or OPENRESEARCH_API_URL.
  • Credentials — orx login opens your browser, mints a personal access token, and stores it at ${XDG_CONFIG_HOME:-~/.config}/openresearch/credentials.json (mode 0600). Sent as Authorization: Bearer … on every request.

Usage analytics

orx sends anonymous usage analytics to help prioritize features. It's opt-out, and the orx up onboarding surfaces the choice on first run.

  • Collected: command name, a random per-install UUID, CLI version, OS/arch, a CI flag, and coarse event labels (e.g. "a run launched on modal").
  • Never collected: code, prompts, file contents or paths, project or experiment ids/names, repo names, tokens, emails — nothing identifying. The install UUID is not tied to your account.
orx telemetry off        # persistent, per-machine
orx telemetry status     # current state + the anonymous install id
orx <cmd> --no-telemetry # per-run

Events are fire-and-forget on a background task and never block a command.

S
Description
GitHub Trending: alphaXiv/OpenResearch
Readme MIT
935 MiB
Languages
Rust 70.9%
TypeScript 20.9%
JavaScript 6.4%
Python 0.8%
HTML 0.3%
Other 0.6%