Metadata-Version: 2.4
Name: solly
Version: 0.4.0
Summary: Solly: a persistent, provider-agnostic AI orchestration layer for software engineering workflows.
Author: CreativesCastle
License-Expression: LicenseRef-Proprietary
Project-URL: Homepage, https://github.com/creativescastle/solly
Project-URL: Repository, https://github.com/creativescastle/solly
Project-URL: Issues, https://github.com/creativescastle/solly/issues
Keywords: ai,orchestration,developer-tools,cli,engineering-workflow
Classifier: Development Status :: 3 - Alpha
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Operating System :: MacOS
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development
Classifier: Topic :: Software Development :: Build Tools
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: PyYAML>=6.0
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn>=0.29
Requires-Dist: jinja2>=3.1
Requires-Dist: python-multipart>=0.0.9

# Solly

Your AI work assistant that understands your company and your projects.

## Install

```bash
curl -fsSL https://install.solly.dev | sh
```

> **Not live yet — use `uv tool install solly` today.** The command above is
> the permanent install contract and the one worth learning: it lets the
> distribution underneath change without the command users know changing.
> The endpoint isn't serving `install.sh` yet, so until it is:
>
> ```bash
> uv tool install solly
> ```
>
> Verified working against the published release. See
> [Hosting the installer](docs/publishing.md#hosting-the-installer) for what
> wiring the domain up involves.

## Start

```bash
solly
```

## Update

```bash
solly update
```

That's the whole lifecycle. Solly installs as a single command, needs
nothing on the machine beforehand — not even Python — and updates itself
in place without ever touching what you've taught it.

---

## Getting started, in six steps

1. **Install Solly** — the command above. Run `solly doctor` any time to
   confirm the install is healthy.
2. **Create a company** — the top-level thing Solly knows about. Knowledge
   never leaks between companies.
   ```bash
   solly company init acme
   ```
3. **Connect a project** to that company.
   ```bash
   solly project connect --company acme ~/Projects/MyProject
   ```
4. **Onboard** — tell Solly who you are and how your organization is
   shaped. It records exactly what you write and never invents the rest.
   ```bash
   cd ~/Projects/MyProject
   solly onboard init      # writes a blank onboarding.yaml — you fill it in
   solly onboard apply     # turns exactly what you wrote into brain records
   ```
5. **Add knowledge** — give it the files you trust. Solly never scans a
   project on its own; it reads only paths you name.
   ```bash
   solly ingest ./knowledge
   ```
6. **Use your assistant.**
   ```bash
   solly status
   solly dashboard
   ```

(Just want a project workspace, with no company or brain? `solly init`
still does exactly that, unchanged.)

Everything Solly learns lives in files — under `~/.solly` for
company-level knowledge and under each project's own `.solly/` — and all
of it survives updating, reinstalling, or uninstalling the engine.

Contributing to Solly's own source rather than using it? See
[Development](#contributor-installation) — a deliberately separate install
mode that `solly update` will never overwrite.

---

## What Solly is

- **One engine, many projects.** Solly is not tied to any single company
  or product. It resolves which project you're in, loads only that
  project's own context, and never mixes state across projects.
- **Deterministic first, LLM second.** Routing, risk classification,
  verification, and workspace migration are plain Python — no model calls,
  no added context. LLM judgment is reserved for planning, implementation,
  and review, where it's actually needed. See `docs/architecture.md`.
- **Files, not chat history.** Plans, build results, reviews, and
  telemetry are persisted as files under a project's `.solly/` workspace
  (or, for engine-global state, under `~/.solly`) — durable across
  sessions and Solly upgrades, not dependent on conversation memory.
- **Today's capability:** AI-assisted software engineering via three
  Claude Code skills — `solly-plan`, `solly-build`, `solly-review` (see
  `integrations/claude-code/skills/`; these three currently require a
  git checkout — see `docs/publishing.md` for exactly why, and why that's
  not fixable by Python packaging). The `solly` CLI itself has no such
  requirement — a `pip`/`uv tool` install has complete, verified runtime
  behavior on its own (same doc). The CLI in this README is the
  installation, lifecycle, and workspace-migration foundation the skills
  (and a future interactive assistant) run on.

---

## User Installation

This is the path for using Solly on your own projects. If you're
contributing to Solly's own source instead, skip to "Contributor
Installation" below.

### Prerequisites

- macOS or Linux, on arm64 or x86_64 (Windows: run inside WSL).
- Nothing else. The installer bootstraps everything it needs — including
  Python itself — from a bare machine.

### Install

```bash
curl -fsSL https://install.solly.dev | sh
```

**While `install.solly.dev` is being wired up**, use either of these — both
verified against the published release:

```bash
uv tool install solly                # from PyPI, no checkout needed
sh install.sh                        # from a checkout of this repository
```

`sh install.sh` runs the exact same script the URL will serve, so it is the
faithful way to exercise the installer today. It still installs the *stable*
released version from PyPI, not the checkout it was run from.

The installer prints what it did and nothing more:

```text
Installing Solly...

✓ Platform: macOS arm64
✓ uv ready
✓ solly 0.3.0 installed
✓ CLI available

Run:
  solly
```

Verify it yourself any time:

```bash
solly --version
solly doctor
```

**What the installer does, and doesn't.** It's a small bootstrap script
(`install.sh` in this repository — reading an installer before piping it to
a shell is always reasonable). It detects your platform and refuses
anything it doesn't support; installs [`uv`](https://docs.astral.sh/uv/) if
missing; installs or upgrades Solly; then verifies the CLI actually runs
rather than assuming a successful download means a working install. It
never creates a company or a project, never reads or writes `~/.solly` or
any project's `.solly/`, never asks for credentials or GitHub
authentication, never uses `sudo`, and installs only for your user. It is
safe to run repeatedly — every step converges on "latest released
version," so re-running it is also a valid way to upgrade.

Options, if you need them (`sh install.sh --verbose` from a checkout takes
the same flags):

```bash
curl -fsSL https://install.solly.dev | sh -s -- --verbose          # show package-manager output
curl -fsSL https://install.solly.dev | sh -s -- --version 0.3.0    # pin an exact release
curl -fsSL https://install.solly.dev | sh -s -- --no-modify-path   # never touch shell config
curl -fsSL https://install.solly.dev | sh -s -- --dry-run          # report, change nothing
```

### Advanced installation

Solly is distributed as a Python package on PyPI and installed with
[`uv`](https://docs.astral.sh/uv/). You never need to know that — the
installer above handles it — but if you'd rather drive it yourself, or
`install.solly.dev` is unreachable:

```bash
uv tool install solly              # same result as the installer
uv tool install solly==0.3.0       # a specific release
uv tool upgrade solly              # what `solly update` runs for you
```

(GitHub/raw-content hosts occasionally return `HTTP 429` under rate
limiting — if `curl` reports that, wait a few seconds and retry, or use
`uv tool install solly`, which talks to PyPI instead.)

### Which install do I have?

Solly distinguishes two install modes, and reports which one it is:

```bash
solly doctor
```

```text
Solly 0.3.0
Install mode: stable
Engine home: /Users/you/.solly
Executable: /Users/you/.local/bin/solly
```

- **stable** — a released version from PyPI. `solly update` upgrades it.
- **editable** — a source checkout you're developing against. `solly
  update` reports it and refuses to overwrite it, and names the checkout
  path so there's no ambiguity about which source is live.

`solly doctor` makes no network calls. To ask whether a newer release
exists, use `solly update --check`.

### First-time setup, start to finish

```bash
mkdir -p ~/AIWorkspace && cd ~/AIWorkspace
solly workspace init
```

```
Workspace root: ~/AIWorkspace
✓ Created .solly/workspace.yaml (name: AIWorkspace)
✓ Registered as "aiworkspace" in the Solly workspace-root registry
```

This is optional and purely organizational (see "Register a workspace
root" below) — Solly does not require it, does not require exactly one
per machine, and does not require your projects to live under it. You
can register any number of workspace roots at arbitrary absolute paths
(`~/Work`, `~/Personal`, `/Volumes/Clients`, ...); `solly workspace list`
shows every one registered on this machine.

Next, create the company this work belongs to. A company owns the brain —
the shared, cross-project store of who's who and what's true (see
"Companies and the Brain" below):

```bash
solly company init acme --name "Acme Corp"
```

```
Company: Acme Corp
Root: ~/.solly/companies/acme
✓ Created company.yaml
✓ Created: assistant/, brain/, decisions/, inbox/, sources/

Acme Corp is ready.
  Next: connect a project with `solly project connect --company acme <path>`
```

Then connect a project folder to it. This creates or updates only Solly's
own `.solly/` state inside that folder and leaves every other file in it
alone — connecting an existing repository is safe:

```bash
solly project connect --company acme ~/Projects/MyProject
```

```
✓ Connected ~/Projects/MyProject to company 'acme'
✓ Created .solly/project.yaml (name: myproject)
✓ Workspace migrated 1 → 2
  created: assistant/, context/, decisions/, knowledge/, plans/, reviews/, sessions/, tasks/, architecture.md, current-state.md
✓ Registered as "myproject" in the Solly project registry
✓ Validation passed

Next: cd ~/Projects/MyProject && solly onboard
```

The folder doesn't have to exist yet — pass `--create` and Solly makes
it. Without that flag a missing path is an error, because a typo is far
likelier than a wish to create a directory. A folder already connected to
a *different* company is refused rather than reassigned, with nothing
written.

The project's slug comes from its folder name, and a slug identifies one
project across your whole Solly home. So if you already have a `docs`
project, connecting a second folder called `docs` is refused rather than
quietly taking the name over — give it one of its own with `--name`:

```bash
solly project connect --company acme --name procore-docs ~/Work/procore/docs
```

`solly init --name <slug>` does the same from inside a folder. Renaming a
project that's already registered is `solly project rename`, which moves
its knowledge along with the name.

(`solly init` and `solly init --company acme` still work exactly as
before, if you'd rather initialize a project from inside it.)

Now teach Solly who you are. `solly onboard init` writes a blank,
commented `onboarding.yaml` under the company; you fill in what you know
and apply it:

```bash
cd ~/Projects/MyProject
solly onboard init      # prints the absolute path to edit
# ...edit onboarding.yaml...
solly onboard apply
```

```
  + person: Nadia Amir — new person
  + responsibility: Related Items across the platform — for Nadia Amir
  + priority: Ship the external-item badge — for Nadia Amir
  + area: related-items — new area (project myproject)

  4 to create, 0 to update, 0 already recorded
  Provenance: onboarding source created, snapshot created
```

Every field is optional, blank stays blank, and nothing is ever inferred
from what you leave out. Run `solly onboard` any time for status without
writing, or `solly onboard apply --dry-run` to see the plan. Applying
twice is a no-op — the second run reports everything `already recorded`.

Finally, give Solly the files you want it to know about, by naming their
path explicitly:

```bash
solly ingest ./knowledge
```

```
  + knowledge/roadmap.md — 4 passage(s)

  1 added, 0 updated, 0 unchanged, 0 skipped, 0 error(s)

Sources and passages are stored. Claim extraction has not run — nothing
in these files has been interpreted into facts yet.
```

Files are read where they are and never moved, copied, or modified.
**Solly never scans a project on its own** — nothing enters the brain
unless you name its path.

```bash
solly status
```

```
Solly 0.3.0
Project: myproject (acme)
Root: ~/AIWorkspace/Projects/MyProject
Workspace: ~/AIWorkspace/Projects/MyProject/.solly
Workspace root: AIWorkspace (~/AIWorkspace)
Workspace schema version: 2 (current)
Migration required: no
Workspace health: ok
Capabilities: engineering
```

```bash
solly doctor    # read-only diagnostics; add --fix to apply the same safe migration `solly init` runs
```

Optionally, start the dashboard:

```bash
solly dashboard --company acme
```

opens the Today/Ask Solly/Projects/Decisions/Knowledge dashboard at
`http://127.0.0.1:8765`.

A workspace root registered this way looks like:

```text
~/AIWorkspace/
├── .solly/
├── Solly/
└── Projects/
```

— but this is a convention, not a requirement. Nothing above enforces
this shape; projects scattered across unrelated directories, or a
workspace root with a different name entirely, work identically.

---

## Register a workspace root (optional)

If your projects live together under one parent directory (e.g.
`~/AIWorkspace`, containing `Solly/`, `Legible/`, `KinderOS/`, ...),
register that parent as a workspace root once:

```bash
cd ~/AIWorkspace
solly workspace init
```

This creates `~/AIWorkspace/.solly/workspace.yaml` — identity metadata
only (schema version, name, type). It holds no project or company state;
each project inside still gets its own `.solly/` via `solly init` (below),
run individually per project. Registering a workspace root is entirely
optional and purely informational (`solly status`/`solly workspace
status` will report which root, if any, contains the current project) —
nothing else changes behavior. You can register any number of workspace
roots at arbitrary absolute paths (`solly workspace list` shows them
all); Solly never assumes there's exactly one, or that your projects live
under any particular directory.

```bash
solly workspace list      # every registered workspace root
solly workspace status    # the root containing the current directory, if any
```

## Initialize Solly inside a project

From the project's root directory:

```bash
cd my-project
solly init
```

This creates (or, if `.solly/` already exists, safely updates) the
project's workspace: `.solly/project.yaml` (created with defaults derived
from the directory name if absent, otherwise left untouched beyond adding
any genuinely missing fields), a `workspace.json` schema-version marker,
and the canonical directories described below. It registers the project
by name so it can be resolved from anywhere afterward. **`solly init` is
always safe to re-run** — every step it takes is additive or merge-only;
it never overwrites or deletes anything a project already has.

If `.solly/` predates workspace versioning (no `workspace.json`), `solly
init` detects that and migrates it in place through the same versioned
migration framework `solly update` uses — see "Updating Solly does not
replace your project's knowledge," below.

To do the same thing from outside the project, and attach it to a company
in one step:

```bash
solly project connect --company acme /path/to/my-project
```

Identical workspace setup, with three differences: it targets exactly the
path you name instead of walking up from the current directory, the
company must already exist (it won't create one implicitly), and a folder
already connected to a different company is refused rather than
reassigned. Pass `--create` to make a folder that isn't there yet, and
`--name <slug>` to register it under a slug other than the one its folder
name would produce.

## Start Solly

```bash
solly
```

Locates the nearest `.solly/` workspace (walking up from your current
directory), validates it, and confirms Solly is ready for that project.
This milestone's `solly` does not yet run a full interactive assistant —
today, use the `/solly-plan`, `/solly-build`, and `/solly-review` Claude
Code skills, which already operate on the same `.solly/` workspace `solly`
resolves. `solly` resolving readiness here is deliberate: the interactive
assistant becomes this same command's default behavior in a future
milestone, without changing how you invoke it.

## Companies and the Brain

Above projects sits a company layer: `solly company init <slug>` creates
a company workspace under `~/.solly/companies/<slug>/`, with its own
isolated knowledge store (one SQLite file per company — cross-company
leakage is structurally impossible, not just avoided by convention). A
project links to its company via `solly project connect --company <slug>
<path>`, or via `solly init --company <slug>` from inside the project.

```bash
solly company init procore --name Procore
solly company list                  # every company registered on this machine
solly project connect --company procore ~/Projects/RelatedItems

cd ~/Projects/RelatedItems
solly onboard init && solly onboard apply    # who you are, from a file you edit
solly ingest ./knowledge                     # files you explicitly name

solly brain add --company procore --type decision \
  --title "External items get a badge" --text "..."

solly dashboard --company procore   # opens the Today/Ask Solly/Projects/
                                     # Decisions/Knowledge dashboard at
                                     # http://127.0.0.1:8765
```

Everything that enters a brain is explicit. `solly onboard apply` creates
records only for facts the file states, `solly ingest` only reads paths
you name, and `solly brain add` (and the dashboard's "+ Add to Brain") is
manual — Solly never invents company, personal, or project knowledge, and
never goes looking for it. Ask Solly currently does real evidence
retrieval against what you've added (with an honest "I don't have enough
information" when nothing matches) — LLM-based synthesis on top of that
evidence is a deliberate fast-follow, not yet wired up.

### Onboarding

`~/.solly/companies/<slug>/onboarding.yaml` is a hand-edited file, one per
company, describing you and the people you work with. The template is
blank and commented:

```yaml
schema_version: 1
person:
  name:
  preferred_name:
  title:
  team:
  responsibilities: []
  priorities: []
team:
  members: []        # - name: / title: / team: / aliases: []
relationships: []    # - person: / type: / target:
areas: []            # - name: / slug: / description: / project:
```

- `solly onboard` — status: which company, where the file is, whether it
  validates, and what applying it would create, update, or leave alone.
  Exits non-zero only when the file is actually invalid.
- `solly onboard init` — writes the template if absent; an existing file
  is left byte-for-byte untouched.
- `solly onboard apply [--dry-run]` — validates, then creates records for
  exactly what the file states. Idempotent: run it as many times as you
  like. Edit the file and re-run to add more; it merges rather than
  replaces, and never deletes a record you've already got.

Because every write is keyed by a stable reference derived from the file,
re-applying an unchanged file writes nothing at all. Provenance is
recorded — each applied file produces a source and a snapshot of its
contents, so every person and area can be traced back to the file that
asserted it.

### Areas

An area is a durable slice of work — "Related Items", "Billing",
"Onboarding" — either company-wide or scoped to one project. Areas exist
**only** when you declare them, in `onboarding.yaml`'s `areas:` list.
Solly never derives them from directory names, ticket labels, or ingested
text. Once one exists, `solly ingest --area <slug>` attributes the files
you ingest to it.

### Ingestion

```bash
solly ingest <path> [--area SLUG] [--dry-run]
```

`<path>` is a file or directory inside the current project. Files are read
in place — never moved, copied, or modified — and the brain stores the
path relative to the project root, so a source stays identifiable if the
project moves.

- **Supported formats:** `.md`, `.markdown`, `.txt`. Anything else is
  counted and reported as skipped, not silently ignored. No PDF or docx
  yet.
- **Directory walks** skip `.solly/`, `.git/`, and any dotted directory,
  and refuse to follow a symlink out of the folder you named.
- **Re-ingesting is cheap and safe.** Each file's content is hashed;
  unchanged files are reported `unchanged` and write nothing, while
  changed ones get a new snapshot alongside the old one. Nothing is
  overwritten and no history is lost.
- **Registered is not understood.** Ingestion stores sources and splits
  them into passages, and stops there. No claims are extracted, so
  nothing in an ingested file is treated as a fact yet — the output says
  so explicitly every time.

A project's slug (its identity in the registry, `project.yaml`, and any
brain rows scoped to it) is independent of its directory name — set once
at first `solly init`, from the directory name at that time. To correct
it later without touching the directory on disk:

```bash
solly project rename relateditems related-items
```

Updates the registry key, `project.yaml`'s `name` field, and migrates any
brain rows already scoped to the old slug — all in one operation, so the
registry and `project.yaml` never fall out of sync.

To remove a stale registry entry (e.g. a project whose directory no
longer exists) without touching anything on disk:

```bash
solly project unregister some-old-slug
```

Refuses by default if the slug still resolves to a live project on disk
(`.solly/project.yaml` present) — pass `--force` to unregister one
anyway. Either way, this only ever removes the registry entry; the
project's own files, `.solly/` workspace, and company brain data are
never touched.

## Using Solly from Claude Code

There is nothing to install for this — no skill, no plugin, no MCP server.
Every command above is deterministic, prints a plain-text report, and
exits non-zero on failure, which is all an agent in your terminal needs.
So you can describe what you want in English and let Claude Code run the
commands:

> "Set up Solly for this repo under my Acme company, then ingest the docs
> folder."

```bash
solly company init acme
solly project connect --company acme .
solly ingest ./docs
```

Three properties make this reliable rather than merely possible, and
they're the reason the commands are shaped the way they are:

- **No command ever prompts.** Every input is a flag or a file, so nothing
  blocks waiting on a human who isn't watching the terminal.
- **Nothing is destructive or ambiguous.** Re-running any of these is
  safe, a missing path is an error instead of a guess, and a conflicting
  company is refused instead of reassigned — an agent can't quietly do
  the wrong thing by retrying.
- **The output says what happened, per fact.** `+`, `~`, and `=` prefixes
  distinguish created, updated, and already-recorded, and errors name the
  specific field or file at fault — so an agent reading stdout can report
  the outcome accurately instead of inferring it from an exit code.

What an agent should *not* do on your behalf is decide what Solly knows.
`onboarding.yaml` is yours to write, and ingestion only reads paths you
name; asking Claude Code to "figure out who my team is" or "ingest
everything relevant" defeats the point. The right division of labor is
that it runs the commands and reads the reports, while the facts stay
yours.

For the plan/build/review workflows there *are* real Claude Code skills —
`/solly-plan`, `/solly-build`, `/solly-review` (see
`integrations/claude-code/skills/`).

## Updating Solly

```bash
solly update
```

is the single supported way to update — run it inside any registered
project. It works in four stages, each reported separately so you can see
exactly which part of an update did or didn't happen:

```text
Solly update

Engine
✓ 0.3.0 → 0.4.0

Company data
✓ Brain schema 3 → 4

Project
✓ Workspace 2 → 3
✓ Existing knowledge and decisions preserved

Validation
✓ Healthy

Solly is ready.
```

1. **Engine.** For a stable install, upgrades the package, then actually
   executes the newly-installed `solly` to confirm the upgrade produced
   something that runs — a distinct verified step, not assumed from the
   package manager's exit code. **For an editable install this stage is
   skipped**, with the checkout path named.
2. **Company data.** Applies the company brain's additive schema DDL, if
   that company has a brain. It never creates one: an engine upgrade will
   not bring a company's knowledge store into being as a side effect.
3. **Project.** Migrates this project's `.solly/` workspace if its schema
   is behind — the same safe, additive migration `solly init` runs. This
   stage runs for both install modes, so `solly update` is also the right
   command for bringing a workspace current when the engine itself didn't
   change.
4. **Validation.** Confirms the result is healthy.

The ordering isn't cosmetic. After a successful engine upgrade, Solly
re-executes the newly installed binary before running any migrations —
otherwise 0.4.0's migrations would be applied by 0.3.0's code, which is
precisely the situation a new migration exists for. If that handoff can't
happen (usually because the executable isn't on `PATH`), Solly says so and
tells you to re-run rather than silently migrating with the old code.

To see what an update *would* do, including whether a newer release exists:

```bash
solly update --check
```

This changes nothing. It's also the only command in Solly that contacts
the network — everything else, `solly doctor` included, works entirely
offline, and a failed version check is reported as unknown rather than
being allowed to fail the command.

### An editable install is never overwritten

```text
Solly update

Engine
• Editable install detected — engine update skipped.
  Source: /Users/you/AIWorkspace/Solly
  Update the source with git, then rerun `solly update` to apply migrations.
```

Company and project migrations still run, so a developer's workspace stays
current without their checkout ever being replaced from PyPI.

**The distribution pipeline this is built on**, end to end:

```text
Developer  →  GitHub  →  GitHub Release (tag)  →  PyPI  →  user's `uv tool upgrade solly`
```

A tagged, published GitHub Release triggers an automated, token-free
publish to PyPI (`.github/workflows/publish.yml`, PyPI Trusted
Publishing — see `docs/publishing.md` for the full pipeline and the
one-time PyPI-side setup it depends on). Once a release has gone through
that pipeline, `uv tool upgrade solly` — and therefore `solly update` and
the bootstrap installer above — pulls it from PyPI, the same as any other
published Python package. Until the first release has gone through this
pipeline, the fallback GitHub install
(`uv tool install git+https://github.com/creativescastle/solly.git`)
remains the working path.

## Uninstalling Solly

Solly separates **the engine** (the installed program) from **your data**
(what you've taught it). Uninstalling removes only the first.

### Remove the engine

```bash
uv tool uninstall solly
```

That's the complete uninstall. It deliberately does **not** touch:

- `~/.solly` — the engine-global home (project and workspace-root
  registries, company brains, telemetry)
- any workspace root's `.solly/workspace.yaml`
- any project's `.solly/` workspace (plans, decisions, reviews, knowledge)

All of that is independent of whether the `solly` CLI happens to be
installed. Reinstalling later — by any route, `uv tool install solly`
included — picks every registered project and company back up exactly where
it left off, with nothing to restore.

There is intentionally no `solly uninstall` command. A program that can
delete its own durable data is one accidental flag away from deleting
history that has no backup, and the engine removal above is a single
command that any package manager already provides.

### Remove your data (separate, deliberate, irreversible)

Rare, and not something any Solly command will ever do for you. This is
real, non-recoverable company and project history:

```bash
rm -rf ~/.solly                              # every company brain, every registry entry
rm -rf /path/to/some-workspace-root/.solly   # one workspace root's identity marker
rm -rf /path/to/some-project/.solly          # one project's plans/decisions/knowledge
```

Nothing in Solly runs these — not `uv tool uninstall`, not `solly update`,
not `solly doctor --fix`. They're documented here only so that an
intentional full removal doesn't require guessing at paths.

## Troubleshooting

- **`solly: command not found` right after installing.** Almost always
  `PATH`, not a failed install. A piped `curl | sh` runs in a subprocess
  and cannot change your *current* terminal's `PATH`; only new terminals
  pick up shell-profile changes. The installer detects this case and tells
  you which of the two applies. The deterministic fix:

  ```bash
  uv tool update-shell   # appends the tool directory to your shell config
  ```

  Then open a new terminal. To use `solly` in the *same* window without
  opening a new one:

  ```bash
  export PATH="$HOME/.local/bin:$PATH"
  ```

  To add it yourself instead, put that same line in `~/.zshrc` (zsh) or
  `~/.bashrc` — or `~/.bash_profile` on macOS (bash). The installer never
  edits shell config without saying so, and `--no-modify-path` stops it
  from doing so at all.
- **`uv: command not found` after the installer claims success.** Same
  cause and same fix — `uv` installs into the same user bin directory.
- **`curl` returns `HTTP 429`.** GitHub/raw-content hosts rate-limit
  anonymous requests occasionally. Wait a few seconds and retry, or install
  directly from PyPI: `uv tool install solly`.
- **The installer fails and you can't tell why.** Re-run it with
  `--verbose` to see the package manager's own output, which is suppressed
  by default:
  `curl -fsSL https://install.solly.dev | sh -s -- --verbose`
- **`curl: (6) Could not resolve host: install.solly.dev`.** The install
  endpoint isn't live yet — this is expected, not a problem with your
  machine. Use `uv tool install solly`, or `sh install.sh` from a checkout.
  See [Hosting the installer](docs/publishing.md#hosting-the-installer).
- **`sh: set: Illegal option -o pipefail` (or similar) while installing.**
  This shouldn't happen — the installer is strict POSIX shell precisely so
  that `| sh` works where `/bin/sh` is dash (Debian, Ubuntu). If you see
  it, you have an old copy of the script; fetch it again.
- **`solly update`'s engine stage reports the new engine "did not run when
  invoked."** The files were replaced but the new executable didn't start
  cleanly — run `solly --version` yourself to see the actual error (usually
  a missing new dependency after an interrupted upgrade). `uv tool install
  --reinstall solly` resolves most cases.
- **`solly update` says it couldn't hand off to the newly installed
  engine.** The upgrade succeeded, but migrations ran under the old code.
  Fix `PATH` as above, then run `solly update` again — it's idempotent.
- **Something looks wrong with a project's workspace.** `solly doctor`
  is always the first thing to run — read-only diagnostics that never
  modify anything unless you pass `--fix`.
- **None of the above.** `solly status` and `solly doctor`'s output is
  safe to share when asking for help — neither ever prints secrets or
  full config contents, per their own design (see "Check health" below).

## Check health

```bash
solly status         # concise project/workspace summary — never dumps secrets or config contents
solly doctor         # read-only diagnostics: install mode, engine home, outdated state, registry drift
solly doctor --fix   # applies the same safe, additive migration `solly init` does — nothing else
solly update --check # what an update would change, plus the latest released version
```

`solly doctor` works outside a project too, reporting on the engine alone —
which is what a freshly installed Solly with no projects yet actually needs
to answer. It's offline; `solly update --check` is where the network lives.

`solly doctor` never deletes or silently rewrites your data; `--fix` is
the one explicit, opt-in exception, and it only ever runs the same
additive migration described below.

## What `.solly/` contains

```
.solly/
  project.yaml      identity, capabilities, verification commands (project-owned)
  workspace.json     schema version + migration history (Solly-managed)
  current-state.md    short "where things stand" snapshot
  architecture.md      project-specific constraints Solly should respect
  plans/                 one file per plan (solly-plan)
  tasks/                   one file per build task/result (solly-build)
  reviews/                  one file per review (solly-review)
  decisions/                 decision records — both solly-plan/review's and
                               solly brain add --type decision's write-ups
  knowledge/                  raw files copied in via `solly brain add`
  context/                     reserved
  assistant/                    reserved
  sessions/                      reserved
```

No Solly engine source code lives here — ever. Telemetry
(`docs/telemetry.md`), the project registry, and a project's *company*
are all engine-global, under `~/.solly` (overridable via `SOLLY_HOME`),
not per-project — so a project's `.solly/` stays small, portable, and safe
to commit to that project's own repository.

Note that `solly ingest` writes nothing here: it records a *reference* to
each file where it already lives, in the company brain. `.solly/knowledge/`
holds only the text you paste in via `solly brain add`, which has no file
of its own to point at.

The company side, engine-global, one directory per company:

```
~/.solly/companies/<slug>/
  company.yaml       identity + company schema version
  onboarding.yaml     who you are — you edit this, `solly onboard apply` reads it
  brain/brain.db       this company's brain: people, areas, sources, passages,
                        decisions, claims (one SQLite file per company)
  sources/              reserved
  decisions/             company-level decision write-ups
  inbox/                  reserved
  assistant/               reserved
  personal/                 deprecated (see below)
```

Companies created before Solly 0.4.0 also have `personal/<user>/*.md`
stub files. Those were never read by anything, so they're no longer
created — `onboarding.yaml` replaces them. Existing ones are left exactly
where they are and never read; `solly company init` mentions them if it
finds any, so an older tree stays explainable. Delete them at your leisure
or don't.

## Updating Solly does not replace your project's knowledge

This is the single most important guarantee of the update/migration
system: **`solly update` and `solly init` never reset, overwrite, or
delete anything already in a project's `.solly/` workspace.** Every
workspace migration (`src/solly/workspace/migrations/`) is additive by
design — it only creates files/directories that don't exist yet, records
what it did in `workspace.json`'s migration history, and is safe to
interrupt or re-run at any point. Your plans, build results, reviews,
decisions, and any files you've added yourself survive indefinitely across
Solly upgrades. If a future migration ever needs to transform (not just
add) an existing file, it's required to write a recovery copy first and to
fail loudly rather than risk data it can't guarantee it preserved.

---

## Contributor Installation

This is the path for working on Solly's own source — not for using Solly
on your other projects (see "User Installation" above for that).

```bash
git clone https://github.com/creativescastle/solly.git Solly
cd Solly
uv tool install --editable .
```

This installs the same `solly` console script, but backed by your local
checkout — edits to the checkout take effect immediately, without
reinstalling. **An editable install never pulls from PyPI or GitHub** —
it only ever reflects whatever is currently on disk in that checkout.
`git pull` in the checkout is how a development install "updates"; see
"Updating Solly" above for exactly how `solly update` treats an editable
install differently (it skips the engine-upgrade step with a clear
message, rather than pretending to upgrade something that has no
"latest release" to move to). If the pulled commits changed
`pyproject.toml`'s dependencies or added a new top-level package, `git
pull` alone won't reflect that — rerun `uv tool install --reinstall
--editable .` as well.

Confirm which mode you're in at any time:

```bash
solly doctor
```

```text
Install mode: editable
Source: /Users/you/AIWorkspace/Solly
```

The install mechanism is intentionally split across two small modules so
the distribution channel — PyPI, editable, and future channels like
Homebrew or a standalone binary — never requires touching
workspace/migration logic:

- `src/solly/engine_install.py` **observes**: install mode, source path,
  installed version, and (only when asked) the latest released version.
  Read-only and offline apart from that one explicit lookup.
- `src/solly/cli/engine_upgrade.py` **acts**: performs the upgrade by
  shelling out to the same command a user would run, and re-executes the
  new binary so migrations run under the version that ships them.

### Verifying installation end to end

Two checks beyond the unit tests:

```bash
./scripts/verify-install.sh                   # clean-room install/update/data-preservation
python3 scripts/check_release_consistency.py  # version agreement across every artifact
```

`verify-install.sh` builds a real wheel and exercises a fresh install, a
reinstall over existing data, an editable developer install, and byte-level
preservation of `~/.solly` and a project's `.solly/` — each in a throwaway
`HOME` and `SOLLY_HOME`, so it can never see or modify your real data.
`check_release_consistency.py` is what CI runs to make sure
`pyproject.toml`, the git tag, the built wheel, and PyPI can't drift apart
(see `docs/publishing.md`).

### Running the test suite

```bash
git clone https://github.com/creativescastle/solly.git Solly
cd Solly
uv venv .venv
uv pip install --python .venv/bin/python -e .
.venv/bin/python -m unittest discover -s tests -t .
```

The package must be installed (editable is fine) before running the test
suite — `tests/` imports `solly.*` as a real installed package, not via a
`sys.path` trick. `tests/__init__.py` isolates `SOLLY_HOME` and
`SOLLY_TELEMETRY_PATH` to throwaway temp directories for the whole test
session, so running tests never touches a real installation's `~/.solly`.

`uv run pytest` is the equivalent one-liner if you're using `uv` for the
whole workflow (`uv sync` first to pick up the `dev` dependency group).

See `CLAUDE.md` for the full architecture and operating rules, and
`docs/architecture.md` / `docs/project-integration.md` /
`docs/publishing.md` / `docs/telemetry.md` for the deeper documentation
this README intentionally stays short and points to rather than
duplicating.

## Versioning

Several independent version numbers, each tracking a different thing and
bumped separately — an application release doesn't imply any of the
others changed:

- **Solly application version** (`solly --version`, `pyproject.toml`'s
  `[project].version`) — bumps on engine/CLI releases.
- **Project workspace schema version** (`solly status`, `.solly/workspace.json`) —
  bumps only when a project's `.solly/` structure changes
  (`src/solly/workspace/schema.py`).
- **Company workspace schema version** (`~/.solly/companies/<slug>/company.yaml`) —
  bumps only when the company workspace structure changes
  (`src/solly/company/schema.py`).
- **Brain schema version** (`brain.db`'s internal `brain_meta` table) —
  bumps only when the Source/Decision/ContextEntry table shapes change
  (`src/solly/brain/schema.py`).

Releases are tagged semantic versions (`vX.Y.Z`) against this repository's
GitHub remote and published to PyPI from that tag (see "Updating Solly"
above, `docs/publishing.md`); `solly --version` is always the
authoritative, programmatically-checkable answer for which engine version
is installed.
