Developer tools
Projects with iohr
What iohr project init does step by step, the Planner plan, the project's space in InOrbit, reading your site, the inorbit.toml record, the atlas, memory, Roles and iohr sync.
iohr project init NAME sets up a project in one command and tells you what you now have:
a folder from a template, a git repository with one commit, an atlas of documents, Roles
and memory your agents can read, a Planner plan, and the project's own space in InOrbit.
iohr project create and iohr init are the same command.
iohr project init my-shop$ iohr-preview project init my-shop --crawl https://example.com --dry-run┌ iohr project init (dry run)│◇ Which template?│ coordinated The default: the demo, wired to a Planner pl...│◇ Which language?│ Python 3.12 or newer python3 found · python 3.14.7, 3.15.2...│◇ Where should it go?│ ./my-shop in the folder you are in│◇ Where should this project keep its ADRs, notes and crawl?│ In the project folder .inorbit/ in the project: travels with the repository│◇ Crawl ──────────────────────────────────────────────────────────────────────────────╮│ ││ This reads the public pages of example.com on this machine, with iohr-agent: at ││ most 10 requests a second to one host (a slower Crawl-delay in robots.txt wins), ││ robots.txt followed, up to 200 pages or 5 minutes. A link to another name in its ││ domain is asked about, once per name, before it is followed; with no terminal ││ none is followed (--crawl-subdomains follows them). It keeps the text of each page ││ for a local search index (an older iohr-agent makes one more request per page, ││ at the same pace and under the same rules). What it finds stays in this project, ││ under .inorbit/crawl/example.com/ and atlas/. Nothing is sent to InOrbit. │├──────────────────────────────────────────────────────────────────────────────────────╯│◇ What init will do ────────────────────────────────────────────────────────────────╮│ ││ 1. report the toolchain check: Python 3.14.7 is behind the latest stable, ││ 3.15.2; upgrade with `mise upgrade python` (asked first, never run by ││ default) ││ 2. check your sign-in (one call to /v1/me) ││ 3. make or find the project's space in InOrbit under the chosen team, and link ││ this folder to it ││ 4. write 87 files into ./my-shop (in the folder you are in) ││ 5. import the built-in roles into your InOrbit account (they stay in the ││ project when you are signed out) ││ 6. create or find the project's Planner plan and write its id into ││ coordination.toml ││ 7. ask the project's shape (monolith, microservices or combined) and file the ││ project-shape item in the Planner plan ││ 8. read https://example.com/ on this machine with iohr-agent (up to 200 pages ││ or 5 minutes, at most 10 a second; a subdomain only after you say yes) into ││ .inorbit/crawl/example.com/, keep each page's text for a local search index ││ (an older iohr-agent reads each page once more, at the same pace), then ││ write atlas facts, a local knowledge base and one memory entry; nothing is ││ sent to InOrbit ││ 9. make a git repository with one commit ││ 10. register the project: state and cache under ││ ./my-shop/.inorbit ││ 11. run the project once with Python 3.12 or newer and read its heartbeat back ││ (smoke test) │├────────────────────────────────────────────────────────────────────────────────────╯│◇ 87 files, none written ───────────────────────────────────────────────╮│ ││ extension.toml (1928 bytes) ││ .gitignore (106 bytes) ││ panel/index.html (1932 bytes) ││ README.md (2450 bytes) ││ inorbit.toml (1502 bytes) ││ coordination.toml (974 bytes) ││ src/my_shop/__init__.py (132 bytes) ││ src/my_shop/__main__.py (4194 bytes) ││ src/my_shop/channel.py (2165 bytes) ││ tests/test_selftest.py (1664 bytes) ││ .inorbit/space (191 bytes) ││ .inorbit/.gitignore (79 bytes) ││ atlas/README.md (1737 bytes) ││ atlas/prd/0001-what-my-shop-is-for.md (664 bytes) ││ atlas/rfc/0001-how-my-shop-is-built.md (692 bytes) ││ atlas/adr/0001-keep-project-documents-in-atlas.md (689 bytes) ││ atlas/studies/0001-the-first-measurement-for-my-shop.md (564 bytes) ││ atlas/canvas/start.canvas.json (869 bytes) ││ atlas/models/README.md (348 bytes) ││ atlas/roles/security.md (20661 bytes) ││ atlas/roles/python-engineer.md (3494 bytes) ││ .inorbit/memory/ (64 files, 33859 bytes) ││ AGENTS.md (1421 bytes) ││ IO.md (535 bytes) │├────────────────────────────────────────────────────────────────────────╯│└ dry run: nothing was changed
Where each part stands
Everything on this page is in the release, except where and how the project keeps its data, which
is only in iohr-preview, the next build.
| Part | Build |
|---|---|
Templates, the record, the atlas, memory, Roles, iohr sync, IO.md, --role | The release |
The Planner plan, the project's space with --team and --visibility, iohr project sync, --crawl | The release |
| Extensions offered at the end of init | The release |
Where the project keeps its data (--keep-in, --copy-data) and its shape (--shape) | iohr-preview |
What each step does
At a terminal, init is guided: it asks for the name when you leave it out, offers the
templates as a menu, asks which folder to use and shows each step as a spinner that ends ok,
warn or fail. --template and --yes skip the menu and the questions. Without a terminal, or
with --json, it asks nothing and prints plain lines.
- Pick a template. The default is
coordinated. The menu starts with "Describe it, your agent builds it" whenclaudeorcodexis on your path.--describe "text"does the same from a script: your agent works in the background, init draws its progress and exits. - Pick the folder. The default is
./NAME. At a terminal a menu offers the current folder, the[project] rootfrom your config, or a folder beside the git repository you are in.--dirnames it. - Write a starter
IO.md. The notes?reads for this folder (see The?prompt). AnIO.mdyou have is never overwritten;--no-io-mdskips it. - Make the project's space. Signed in, init makes the project in InOrbit before it scaffolds the folder (see below).
- Scaffold the folder. The template's code, a README that says how to build and test it with the language's own tools, the record and the link (below) and the atlas (below). Starters are labelled stubs.
- Write Roles and memory. The built-in Roles go to
atlas/roles/, and their recall lines become seed memories in.inorbit/memory/. The agent-instructions file (AGENTS.mdorCLAUDE.md) gets a short section telling agents to useiohr memory. - Make the Planner plan. For a template that has a
coordination.toml, such ascoordinated(see below). - Read your site. With
--crawl, or when you say yes at a terminal because your description names a site (see below). - Make the git repository. One commit, with
.inorbit/included. - Register it locally. State and cache for the project go under
~/.inorbit/projects/NAME, never in the repository. - Run a smoke test. The template's own test, to prove the project runs.
--no-smokeskips it. - Offer extensions. Init looks at the template and your description and offers the
extensions in the catalogue that fit (see Extensions).
The answer defaults to no. With
--yes,--jsonor no terminal, it prints theiohr ext installline instead.
Init does not open an editor itself; InOrbit Desktop asks you before Code opens the folder.
What init does not do yet is listed under --verbose.
Every step can be run again, and nothing you have written is overwritten. To see all of it
without changing anything, add --dry-run (or --plan): it runs the questions and prints
would: ... for each step.
iohr project init my-shop --template demo --dry-run --yesTemplates
$ iohr project template listNAME STATUS ORIGIN DEFAULT DESCRIPTIONblank ● live built-in A starter project in your language: one runnable file, Roles and memorydemo ● live built-in * A private macOS service that heartbeats and serves a tiny local panel (Python, TypeScript, Go or Rust, no dependencies)coordinated ● live built-in The demo, wired for coordination: a coordination.toml naming your Planner plan and an ask exampleNext: iohr project template show blank · iohr project template new NAME
| Template | What it writes |
|---|---|
coordinated | The default: the demo project, wired to a Planner plan that init makes, and an ask example |
demo | A private macOS service that heartbeats and serves a tiny local panel, with no dependencies |
blank | The smallest project in your language: one runnable file, Roles and memory, no InOrbit service |
Each takes Python, TypeScript, Go or Rust; --lang picks one without the menu. --set KEY=VALUE
answers one of the template's questions and --answers FILE answers them from a file. A
template can also be a folder, gh:owner/repo (with an optional //subdir and @ref) or
git:URL. [project] template in your config changes the default.
iohr project template show coordinated # what it asks, its languages, files and steps
iohr project template new my-template # make one of your own
iohr project template lint ./my-template # check its folderDescribe it, your agent builds it
--describe "text" hands the project to claude or codex on your path. --agent-turns
(default 150), --agent-timeout in seconds (default 1800) and --agent-model (default
sonnet, or IOHR_INIT_AGENT_MODEL) bound the run.
The Planner plan
The coordinated template has a coordination.toml. Signed in, init finds the plan named
after the project, or makes it, and writes its id under [plan] there, so your agents and asks
report to the same plan. Running init again keeps a plan that is already set.
iohr project init my-shop --plan-id <plan-id> # use a plan you already have
iohr project init my-shop --no-plan # leave [plan] emptySigned out, the step is skipped and says how to make the plan later: sign in, run
iohr coordinator plan create my-shop --external-id project:my-shop and put the id under
[plan] in coordination.toml.
The project in InOrbit
Signed in, init makes the project in InOrbit under your account, before it scaffolds the folder, and
links the folder to it in .inorbit/space. When the name is already a project in that account,
it says so: pick another name, or rename the folder and run iohr project sync.
| Flag | What it does |
|---|---|
--team ACCOUNT | The account it goes under, by id or slug. The default is the account you are signed in to; at a terminal with more than one, init asks |
--visibility team|managers | team, the default, lets everyone in the account see it; managers lets only its owners and admins see it |
--no-sync | Make the folder only; nothing is sent |
When InOrbit cannot be reached, or you are signed out, the step ends queued instead of failing.
Make the space later from the folder:
iohr project sync --dry-run # what it would do
iohr project sync # make the space and link the folderiohr project sync takes the same --team and --visibility, and never gives a folder that is
already linked a second space: a teammate who cloned it is told whether they can open the
linked space.
Where the project keeps its data
In iohr-preview, init asks where to keep what iohr learns about the project: in the project
folder (.inorbit/, so it travels with the repository) or in your home folder
(~/.inorbit/projects/NAME/, so it stays on this machine). That covers the project's notes,
what a crawl keeps and the project's state. The choice is written as [project] data in
inorbit.toml.
iohr-preview project init my-shop --keep-in home
iohr-preview project init my-shop --keep-in project --copy-data--keep-in (or IOHR_INIT_KEEP_IN) answers without asking. Nothing is moved unless you
choose, and --copy-data copies what is already there, never deletes. With no choice recorded,
a project keeps the layout older versions made: notes and crawl in the project, state in your
home folder. atlas/, inorbit.toml and .inorbit/space always stay in the project.
The project's shape
Also in iohr-preview, init asks whether the project is a monolith, microservices or combined.
The question is an item in the project's Planner plan. Answered at a terminal, the answer is
written to [project] shape in inorbit.toml and ? knows it. Without a terminal, the item
stays open for you to answer from the Mac app or the phone. --shape (or IOHR_INIT_SHAPE)
answers it up front.
Reading your site
--crawl URL reads your public site into the project, so ? and ?? can answer from it
with the page as the source. It needs the
inorbit/agent extension; init says how to install it when it is missing.
iohr project init my-shop --crawl https://example.com
iohr atlas search "opening hours" # search what it read
iohr atlas facts # the facts it found, each with its sourceIt runs on this machine and follows robots.txt. Before it starts, it says what it will do.
| Flag | Default | What it bounds |
|---|---|---|
--crawl-pages | 200 | Pages read |
--crawl-minutes | 5 | Minutes spent |
--crawl-rate | 10 | Requests a second to one host, 1 to 50 (IOHR_CRAWL_RATE); a slower Crawl-delay wins |
--crawl-subdomains | off | Also read the site's subdomains |
What it finds stays in the project: the pages and their index under .inorbit/crawl/<host>/,
and a study in atlas/studies/ that iohr sync can send to Decisions like any other
document. Nothing is sent to InOrbit by the crawl. --no-crawl reads no site and asks nothing about one.
The record and the link
Two committed files tie the folder to InOrbit. Neither holds a secret.
inorbit.tomlat the root is the project's record: its name and id, its kind, the template it came from, and the links that stay empty until each exists (space_id,listing,memory_namespace)..inorbit/spacenames the space the project records to, asid,nameandaccount. Init writes it when it makes the project in InOrbit, andiohr project syncwrites it later. A link to a local stack goes in.inorbit/space.localinstead, which git ignores.
Both files are committed, so a teammate who clones the repository has the same record and the same space link.
The atlas
atlas/ is a plain folder you can read and edit: prd/, rfc/, adr/, studies/,
canvas/, models/ and roles/. A document is Markdown with front matter that maps one to
one onto a Decisions document.
$ iohr atlas new rfc "Cache the catalogue"└ atlas/rfc/0002-cache-the-catalogue.md written (number 0002)
iohr atlas init adds the folder to a project that was made without it.
Sync
iohr sync sends the atlas to the space named in .inorbit/space, through Decisions. It
creates what is new, saves a new version of what changed, and when a document changed in
Decisions since you read it, it says so and overwrites nothing. Ids and versions are written
back into the front matter and stay local. The canvas and 3D folders are never sent.
iohr sync --dry-run # what would be created or saved; sends nothing
iohr sync # send itWith no space linked, it stops and says how to link one:
$ iohr synciohr: no Decisions space: give `--space ID`, or link the project with `/inorbit:link <space>`
Sync goes one way today: from the folder to Decisions. Nothing pulls documents back, and
nothing syncs in the background; iohr sync is the only thing that sends documents.
Memory
iohr memory keeps what a project has learned as Markdown files with a reason and a source,
so an agent can find it by topic instead of being told again.
$ iohr memory remember "We use sqlx" --why "typed SQL"id we-use-sqlx-for-typed-sql-313b4dstatus keptscope projectfile .inorbit/memory/we-use-sqlx-for-typed-sql-313b4d.md
iohr memory recall "database" # ranked by word match, recency and pin
iohr memory list
iohr memory check # missing reasons, stale or dangling entries, gaps
iohr memory forget <id>There are three scopes, picked with --scope: project (in the repository, the default
inside one), user (yours, on this machine) and account (kept in InOrbit, off until you
turn sync on). A text that looks like a secret is refused, and a memory is never written to
a log.
Roles
The built-in Roles are written to atlas/roles/: security for every project and one
engineer for the project's language (rust-engineer, python-engineer, typescript-engineer
or go-engineer), and base-engineer too when your account has none. Each has a short core and
recall lines, with the primary source each line comes from.
Signed in, init sends them to your account as an import and says per Role what happened:
written, upgraded, unchanged, or left alone because you changed your copy.
iohr atlas roles import --upgrade is how you take the newer catalogue on purpose. Only an owner
or admin of the account may import. Signed out, iohr sync or iohr atlas roles import does it
later.
--role NAME adds a Role to the project. Init looks for it in the new project's atlas/roles/,
then in your account, then in the built-in catalogue. The Roles also answer every ask in the
project's session: see Answers from your Roles.
Teams, visibility and sharing
The project lives in InOrbit under the account you chose with --team, seen by everyone in it
or only its owners and admins (--visibility). That setting is who can see the project, not a
list of members.
The folder is shared the way code is shared: through git. inorbit.toml, .inorbit/space,
.inorbit/memory/ and atlas/ are committed. A teammate who clones the repository, signs in
and runs iohr sync sends documents to the same space, with the access the space gives them.
What iohr does not do yet: a Keychain slot for the project's own token, a private listing in
the Marketplace, a memory namespace of its own, and its own monitors and notifications channel.
Sync does not pull documents back either (see Sync). Under --verbose,
init reports each of these as not yet, with the thing it waits on, so you can see what is
left rather than guess.