Metadata-Version: 2.5
Name: algorise
Version: 0.4.0
Summary: Unified CLI and MCP server for your Algorise workspace (read knowledge + publish agent output)
License-Expression: MIT
Requires-Python: >=3.12
Requires-Dist: httpx>=0.27.0
Requires-Dist: mcp[cli]>=1.0.0
Description-Content-Type: text/markdown

# Algorise

**Publish agent output into Algorise** — from Claude Code, Cursor, CI, or any script.

Algorise is a CLI and MCP server that connects external coding agents to your
Algorise workspace. Push files, notes, and links; choose where they land; collaborate
with your team and digital employees; pull results back into your local workflow.

- **Publish** — files, URLs, or inline markdown land as reviewable artifacts
- **Route** — send work to your inbox, a Collection, Dataset, or Report
- **Collaborate** — post updates, request review, poll for replies from digital employees

Requires Python 3.12+ and an Algorise account.

## Quick start

```bash
# Install (Python 3.12+; see INSTALL.md for all options)
uv tool install algorise

# Authorize via browser (OAuth — no API keys to paste)
algorise login

# Publish a file into your workspace inbox
algorise publish ./draft.md --title "Draft report" --agent claude-code

# See what you have published
algorise inbox
```

Published work appears in your Algorise dashboard, inbox, and notifications.

## Install

This package installs two commands:

| Command | Purpose |
|---|---|
| `algorise` | CLI for publishing and collaboration |
| `algorise-mcp` | MCP server (stdio) for in-editor agents |

```bash
# Recommended — uv tool (persistent install, no system Python required)
uv tool install algorise

# Run without installing
uvx algorise login

# pip / pipx (needs Python 3.12+)
pipx install algorise
```

Full install notes: [INSTALL.md](./INSTALL.md).

## Authenticate

Auth uses **browser OAuth** (OAuth 2.0 device-authorization grant). Tokens are
short-lived, refreshable, centrally revocable, and act as you. A token starts in
the organization that was active when you approved it; see
[Selecting an organization](#selecting-an-organization) to act in another one you
belong to.

```bash
algorise login
```

This prints a verification URL and user code, opens your browser, and waits for
approval (you must already be signed in to Algorise). The session is saved to
`~/.algorise/config` (mode 600).

```bash
algorise whoami   # profile, URL, org/user/scopes
algorise logout   # clear the active profile's session
```

Active sessions can be listed and revoked in the product under
**Settings → Connected CLIs**.

## Typical workflow

```bash
# 1. Publish work from your repo or agent session
algorise publish ./analysis.md --title "Competitor scan" --agent cursor

# 2. Route directly into a knowledge base (optional)
algorise publish ./policy.pdf --to collection --collection <collection-id>

# 3. Request a human review from the CLI
algorise review <artifact-id> "Please verify the sources"

# 4. After a digital-employee handoff in the UI, poll for replies
algorise pull <agent-session-id>
```

Review, approve, and hand off to digital employees in the Algorise UI. Bridge
delivers work into the workspace; it does not auto-publish on your behalf outside
Algorise.

## Profiles

One installed CLI can target multiple Algorise workspaces or accounts with
per-profile saved tokens — similar to `aws --profile`.

```bash
algorise login --profile work      # sign in to one workspace
algorise login --profile personal  # sign in to another

algorise profiles                  # list profiles (* = active)
algorise use work                  # switch active profile
algorise --profile personal whoami # one-off override

algorise logout --profile personal # clear one profile
algorise logout --all              # clear everything
```

Profiles are stored in `~/.algorise/config`. A legacy flat config is migrated
automatically into a `default` profile on first use.

## Selecting an organization

A session token is minted against **one** organization — whichever was active in
the browser when you approved the CLI. If you belong to several (an operator
provisioning tenants usually does), select the one you want to act in:

```bash
algorise org list                  # * marks the org this profile is set to
algorise org use beta-foods        # persist a selection for this profile
algorise whoami                    # confirms the effective org, by name

algorise --org acme-retail agents  # one-off: does NOT change the saved selection
algorise --org 9a1b44d0-... collection create --name "Ops KB"
```

`--org` accepts a **uuid or a slug**; a slug is resolved against your
memberships before anything is written, so a typo fails immediately rather than
at write time.

Order of precedence: `--org` > `ALGORISE_BRIDGE_ORG` > the profile's saved org >
no header at all (the token's own org).

The selection travels as an `X-Algorise-Org` request header. It is a request,
not an assertion: the server re-verifies that you are a live member of that org
on every call and returns **403** if you are not — it never silently falls back
to the token's org. `algorise org list` deliberately sends no org header, since
it is how you discover which orgs are valid.

Every command that writes prints its target first, so a cross-tenant write is
visible in scrollback and in CI logs:

```
-> Beta Foods (9a1b44d0...) on profile 'try'
```

Add `--output json` to any command to get the raw API body and no banner, for
scripting:

```bash
COL=$(algorise --output json collection create --name "Ops KB" | jq -r .id)
algorise upload ./ops/*.pdf --collection "$COL" --wait
```

### Resolution order

For each invocation, API URL and token resolve in this order (highest first):

1. `--api-url URL` (one-off; token from the selected profile or env)
2. `--profile NAME`
3. `ALGORISE_BRIDGE_PROFILE` env
4. `ALGORISE_BRIDGE_API_URL` / `ALGORISE_API_URL` (+ token env) — ad-hoc, no profile (CI)
5. Active profile in `~/.algorise/config`
6. Local dev fallback (`http://localhost:7100`)

## Environment variables

Most users only run `algorise login`. These exist for CI and headless runners:

| Variable | Purpose |
|---|---|
| `ALGORISE_BRIDGE_API_URL` | API base URL (falls back through `ALGORISE_API_URL`, saved config, release default, then localhost) |
| `ALGORISE_BRIDGE_DEFAULT_URL` | Release-time public API base, baked into published builds |
| `ALGORISE_BRIDGE_TOKEN` | Session token override (falls back to `ALGORISE_TOKEN`, then saved config) |
| `ALGORISE_BRIDGE_PROFILE` | Named profile (same as `--profile`; used by the MCP server, which has no CLI flags) |
| `ALGORISE_BRIDGE_ORG` | Organization uuid to act in when no profile selection applies (CI / ad-hoc); `--org` overrides it |
| `ALGORISE_TOOL_SECRET` | Credential for `tool create` (never a flag — a flag would land in shell history and `ps`) |

When no URL is configured, commands that need the API fail with a clear message
instead of silently hitting localhost.

## CLI reference

| Command | Description |
|---|---|
| `algorise [--profile NAME] [--api-url URL] [--org SLUG_OR_UUID] [--output text\|json] <command>` | Global selectors: backend, organization, output format |
| `algorise login [--profile] [--api-url] [--name] [--scopes] [--write] [--no-browser]` | Browser OAuth; saves token under profile (`--write` also requests the authoring scopes) |
| `algorise logout [--profile] [--all]` | Clear session(s) |
| `algorise whoami` | Show resolved profile, URL, **effective org** (by name), user, scopes |
| `algorise org list` | List the organizations you belong to (`*` = selected for this profile) |
| `algorise org use SLUG_OR_UUID` | Persist the organization this profile acts in |
| `algorise profiles` (alias `envs`) | List configured profiles |
| `algorise use NAME` | Set the active profile |
| `algorise publish PATH [--title] [--summary] [--type] [--agent] [--to …] [--collection ID] [--dataset ID]` | Publish a file |
| `algorise publish --url URL [--title]` | Publish a URL as a link |
| `algorise publish --text "…" [--title]` | Publish inline markdown as a note |
| `algorise note TEXT [--title] [--summary] [--agent]` | Publish an inline note |
| `algorise artifacts [--target] [--limit]` | List published artifacts |
| `algorise inbox [--limit]` | List artifacts with compiled previews |
| `algorise update ARTIFACT_ID BODY [--review]` | Post a progress update |
| `algorise notify TITLE MESSAGE [--url]` | Send a standalone notification |
| `algorise review ARTIFACT_ID [BODY]` | Request review and show the thread |
| `algorise pull SESSION [--since ISO]` | Poll a collaboration session for replies |
| `algorise quota` | Show remaining ingest quota |
| `algorise search QUERY [--types T1,T2] [--limit N]` | Search the knowledge base, skills, agents, conversations (`read`) |
| `algorise collections [COLLECTION_ID]` | List accessible collections, or documents in one (`read`) |
| `algorise skills [SKILL_ID] [--all] [--limit N]` | List org skills, or show one's full instructions (`read`) |
| `algorise agents [AGENT_ID] [--status S] [--limit N]` | List custom agents, or show one's configuration (`read`) |
| `algorise routines [TASK_ID] [--status S] [--limit N]` | List scheduled routines, or show one in full (`read`) |
| `algorise tools [TOOL_ID] [--all]` | List custom tools, or show one in full (`read`) |
| `algorise instructions [INSTRUCTION_ID] [--limit N]` | List instructions, or show one in full (`read`) |
| `algorise workflows [WORKFLOW_ID] [--status S] [--limit N]` | List workflows, or show one's graph (`read`) |
| `algorise agent create --name N --display-name D [--description] [--system-prompt \| --system-prompt-file] [--model] [--temperature] [--skill ID]… [--collection ID]… [--json FILE]` | Create a custom agent as a personal draft (`agents:write`) |
| `algorise skill create --name N --description D (--file PATH \| --instructions TEXT)` | Create an org skill with private visibility (`skills:write`) |
| `algorise skill update SKILL_ID [--name] [--description] [--file \| --instructions]` | Update an org skill; sends only the fields you pass (`skills:write`) |
| `algorise routine create (--file routine.json \| --title T --prompt P --schedule S [--at] [--day-of-week] [--day-of-month] [--cron] [--run-at] [--tz] [--agent-id] [--timeout])` | Create a scheduled routine (`routines:write`) |
| `algorise collection create --name N [--description D] [--json FILE]` | Create a knowledge collection in DRAFT state (`collections:write`) |
| `algorise upload PATH [PATH …] --collection ID [--folder ID] [--title T] [--replace ID] [--wait] [--timeout S] [--continue-on-error]` | Upload documents into a collection; also `algorise doc upload` (`collections:write`) |
| `algorise tool create (--file tool.json \| --display-name X [--name slug] [--description] [--base-url URL] [--auth-type …] [--auth-header NAME])` | Create a custom HTTP tool, private visibility (`tools:write`) |
| `algorise instruction create --name N --description D --trigger T (--content C \| --content-file PATH)` | Create an @-trigger instruction, personal visibility (`instructions:write`) |
| `algorise workflow create (--file wf.json \| --name N --display-name D [--description] [--icon])` | Create a workflow and set its graph (`workflows:write`) |
| `algorise agent update AGENT_ID [--name] [--display-name] [--description] [--system-prompt \| --system-prompt-file] [--model] [--temperature] [--skill ID]… [--collection ID]… [--add-tool SPEC]… [--remove-tool SPEC]… [--json FILE]` | Update a custom agent, including its tool assignment (`agents:write`) |
| `algorise agent delete AGENT_ID` | Delete a custom agent (`agents:write`) |
| `algorise tools --available` | List the assignable tool catalogue: builtin, custom, Composio, workflow (`read`) |
| `algorise datasets [NAME_OR_ID] [--limit N] [--sample N]` | List datasets, or show one's columns and types; `--sample` adds real rows (`read`) |
| `algorise dataset create PATH [--name N] [--table T] [--description D] [--header-row N] [--exclude COL]… [--type COL=TYPE]… [--team UUID]… [--org-wide] [--highlight] [--dry-run]` | Import a CSV/Excel file as one dataset (`datasets:write`) |
| `algorise dataset create-multi PATH [--sheet NAME]… [--all-sheets] [--sheet-header SHEET=ROW]… [--prefix P] [--exclude SHEET:COL]… [--type SHEET:COL=TYPE]… [--team UUID]… [--org-wide] [--highlight] [--dry-run]` | Import an Excel workbook as one dataset per sheet, all-or-nothing (`datasets:write`) |
| `algorise apps [APP_KEY]` | List mini apps, or show one's tabs and numbered blocks (`read`) |
| `algorise app templates` | List the gallery templates `app create --template` can clone (`read`) |
| `algorise app create [--title T] [--description D] [--icon I] [--template KEY] [--dataset-binding NAME=DATASET_ID]…` | Create a mini app, blank or cloned from a gallery template (`apps:write`) |
| `algorise app add-block APP_KEY --type T (--json FILE \| --content TEXT) [--tab-id ID]` | Add a panel to an app, optionally into a specific tab (`apps:write`) |
| `algorise app update-block APP_KEY BLOCK_ID (--json FILE \| --content TEXT) [--type T]` | Replace a panel's data/content/type (`apps:write`) |
| `algorise app delete-block APP_KEY BLOCK_ID` | Delete a panel (`apps:write`) |
| `algorise app reorder-blocks APP_KEY --order REF[,REF…] [--tab-id ID]` | Reorder panels by block id or 1-based position; partial orders front-load (`apps:write`) |
| `algorise app add-tab APP_KEY --label L` | Add a tab; the first tab folds existing panels into "Overview" (`apps:write`) |
| `algorise app rename-tab APP_KEY TAB_ID --label L` | Rename a tab (`apps:write`) |
| `algorise app delete-tab APP_KEY TAB_ID` | Delete a tab and its panels (`apps:write`) |

Apps hold at most 10 tabs of 10 blocks each. Panels bind to a dataset by **name**,
not id — `algorise datasets NAME --sample 3` is the way to get the exact column
names and value shapes a `group_by` / `value_column` needs before writing one.

### Write scopes

Authoring commands need a session minted with the matching write scope. Existing
sessions carry `publish,read` only and keep working unchanged for everything else:

```bash
# requests agents:write, skills:write, routines:write, collections:write,
# tools:write, instructions:write, workflows:write, apps:write, datasets:write
# (plus read)
algorise login --write
```

`--run-at` is sent as **local wall-clock** time together with `--tz`; the server
converts it. Do not pre-convert to UTC.

### Provisioning notes

None of these create routes is idempotent and none takes an idempotency key:

- collection names are unique per org, so re-running `collection create` returns **409**;
- documents auto-rename on a title collision, so a retried `upload` **silently duplicates**;
- tool names collide as **422** — the CLI always sends an explicit `name` so the
  collision is deterministic rather than silently suffixed with `-2`;
- a duplicate instruction trigger surfaces as **400** (not 409).

`upload` uploads **serially** and, by default, aborts on the first failure;
`--continue-on-error` uploads the rest and exits 1 with a summary. Unsupported
file types are rejected locally, before the request, because the server accepts
the upload and only discovers the problem in a background task. `--wait` polls
each job's status until it is `completed` or `failed`.

`algorise quota` reports the Bridge ingest byte caps only. The per-plan
collection and document **count** caps — the ones that actually return **402**
mid-run — have no read endpoint, so a bulk run can still fail partway.

`workflow create` is **two calls**: `POST /workflows` then a `PATCH` carrying
`graph_definition` (the create route has no graph field). There is no rollback —
if the PATCH fails, the CLI says so explicitly and the workflow exists with an
empty graph. Workflows are created in `draft`; validate and activate them in the
Workflow Builder.

`tool create` never takes the credential as a flag. Set `ALGORISE_TOOL_SECRET`
or put `auth_secret` in the `--file` body.

### Importing datasets

`dataset create` is **two calls**: the file is uploaded for a preview (nothing is
written, the preview expires) and then confirmed into a real PostgreSQL table.
`--dry-run` stops after the first call and prints the inferred schema, which is
how you check the types before committing to a table:

```bash
algorise dataset create sales.xlsx --dry-run
algorise dataset create sales.xlsx --exclude notes --type amount=NUMERIC --team <uuid>
```

Datasets are **private to you** by default. `--team` shares with specific teams;
`--org-wide` is organization-admins only. Table names are unique per org, so a
name collision is a **409** — pass `--table` (or `--prefix`, below) to resolve it.

`dataset create-multi` imports an Excel workbook as one dataset per sheet. It is
all-or-nothing server-side: if any sheet fails, every table created by that
request is rolled back. Start with a dry run, since only it can tell you the
sheet names and detected header rows:

```bash
algorise dataset create-multi 2026-budget.xlsx --dry-run
algorise dataset create-multi 2026-budget.xlsx --sheet Q1 --sheet Q2 \
  --sheet-header Q1=2 --prefix "Budget 2026"
```

By default only sheets the server flags as likely tables are imported — a notes
or keyword tab would otherwise become a one-column dataset. `--all-sheets` takes
them anyway; `--sheet` names them explicitly. `--sheet-header SHEET=ROW` forces a
0-indexed header row for one sheet (`-1` = headerless, synthetic column names),
which is what messy workbooks with title rows above the real header need.
`--prefix` prepends to every dataset **and** table name, so two workbooks that
both contain a `Sheet1` do not collide.

Because per-sheet flags need to say which sheet they mean, `--exclude` and
`--type` take a `SHEET:COLUMN` form here rather than the bare column name the
single-file command uses. A selector matching no column — or naming a sheet that
is not being imported — is an error, not a silent no-op.

### Destinations (`--to`)

Type is inferred from the file extension; `--type` overrides. Default destination
is the reviewable inbox.

| Extension | Type | Valid `--to` targets |
|---|---|---|
| pdf, docx, pptx, md, mdx, txt | `document` | inbox, collection, report |
| html, htm, xml | `page` | inbox, report |
| png, jpg, jpeg, svg, mp4 | `media` | inbox, collection |
| csv, json | `data` | inbox, dataset |
| (`--url`) | `link` | inbox |
| (`--text`) | `note` | inbox, collection |

`--collection ID` / `--dataset ID` imply the matching `--to`. Collection and
dataset targets embed content and are metered — check `algorise quota` first.
Over-cap ingests return **HTTP 402**; saturated org queues return **HTTP 429**.

## MCP server

The bundled MCP server (`algorise-mcp`, stdio transport) exposes the same
capabilities to in-editor agents. It reuses the CLI session in `~/.algorise/config`.

Run `algorise login` once, then register the server.

**Claude Code / Cursor** (`.mcp.json`):

```json
{
  "mcpServers": {
    "algorise": {
      "command": "uvx",
      "args": ["--from", "algorise", "algorise-mcp"]
    }
  }
}
```

If installed in a virtualenv, use `"command": "algorise-mcp"` instead.

Override API URL or token per server via `env` (`ALGORISE_BRIDGE_API_URL`,
`ALGORISE_BRIDGE_TOKEN`). For multiple backends, register separate server entries
with `ALGORISE_BRIDGE_PROFILE` in each `env` block.

### MCP tools

| Tool | Description |
|---|---|
| `publish_note(text, title?, summary?, agent?)` | Publish an inline markdown/MDX note |
| `publish_artifact(..., to?, collection_id?, dataset_id?)` | Publish content to a destination |
| `post_update(artifact_id, body, review_requested?)` | Post a progress update or request review |
| `notify(title, message, action_url?)` | Send a standalone notification |
| `get_inbox(limit?)` | List recently published artifacts (with previews) |
| `get_quota()` | Remaining ingest quota headroom |
| `request_review(artifact_id, body)` | Request a human review |
| `get_artifact(artifact_id)` | Fetch one artifact and its review state |
| `list_artifact_replies(artifact_id, since?)` | Replay an artifact's collaboration thread |
| `pull_updates(agent_session_id, since?)` | Poll a session for new replies |
| `create_skill(name, description, instructions)` | Create an org skill with private visibility (`skills:write`) |

## License

MIT
