Sign in
Developer tools
0%

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
A dry run of iohr-preview, signed in, each question answered with its default. It asks the template, the language, the folder and where to keep the project's data, says what reading the site will do, lists every step and every file, and changes nothing. Without --dry-run the same steps run. The one path from this machine is shortened to ./my-shop.

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.

PartBuild
Templates, the record, the atlas, memory, Roles, iohr sync, IO.md, --roleThe release
The Planner plan, the project's space with --team and --visibility, iohr project sync, --crawlThe release
Extensions offered at the end of initThe 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.

  1. Pick a template. The default is coordinated. The menu starts with "Describe it, your agent builds it" when claude or codex is on your path. --describe "text" does the same from a script: your agent works in the background, init draws its progress and exits.
  2. Pick the folder. The default is ./NAME. At a terminal a menu offers the current folder, the [project] root from your config, or a folder beside the git repository you are in. --dir names it.
  3. Write a starter IO.md. The notes ? reads for this folder (see The ? prompt). An IO.md you have is never overwritten; --no-io-md skips it.
  4. Make the project's space. Signed in, init makes the project in InOrbit before it scaffolds the folder (see below).
  5. 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.
  6. 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.md or CLAUDE.md) gets a short section telling agents to use iohr memory.
  7. Make the Planner plan. For a template that has a coordination.toml, such as coordinated (see below).
  8. Read your site. With --crawl, or when you say yes at a terminal because your description names a site (see below).
  9. Make the git repository. One commit, with .inorbit/ included.
  10. Register it locally. State and cache for the project go under ~/.inorbit/projects/NAME, never in the repository.
  11. Run a smoke test. The template's own test, to prove the project runs. --no-smoke skips it.
  12. 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, --json or no terminal, it prints the iohr ext install line 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 --yes

Templates

$ iohr project template list
NAME STATUS ORIGIN DEFAULT DESCRIPTION
blank ● live built-in A starter project in your language: one runnable file, Roles and memory
demo ● 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 example
Next: iohr project template show blank · iohr project template new NAME
As it prints, with the templates that are not released left off.
TemplateWhat it writes
coordinatedThe default: the demo project, wired to a Planner plan that init makes, and an ask example
demoA private macOS service that heartbeats and serves a tiny local panel, with no dependencies
blankThe 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 folder

Describe 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] empty

Signed 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.

FlagWhat it does
--team ACCOUNTThe 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|managersteam, the default, lets everyone in the account see it; managers lets only its owners and admins see it
--no-syncMake 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 folder

iohr 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 source

It runs on this machine and follows robots.txt. Before it starts, it says what it will do.

FlagDefaultWhat it bounds
--crawl-pages200Pages read
--crawl-minutes5Minutes spent
--crawl-rate10Requests a second to one host, 1 to 50 (IOHR_CRAWL_RATE); a slower Crawl-delay wins
--crawl-subdomainsoffAlso 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.

Two committed files tie the folder to InOrbit. Neither holds a secret.

  • inorbit.toml at 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/space names the space the project records to, as id, name and account. Init writes it when it makes the project in InOrbit, and iohr project sync writes it later. A link to a local stack goes in .inorbit/space.local instead, 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)
One new document, numbered after the ones already there.

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 it

With no space linked, it stops and says how to link one:

$ iohr sync
iohr: no Decisions space: give `--space ID`, or link the project with `/inorbit:link <space>`
A project that has no space yet.

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-313b4d
status kept
scope project
file .inorbit/memory/we-use-sqlx-for-typed-sql-313b4d.md
One memory, kept in the project and committed with the code.
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.