Metadata-Version: 2.4
Name: ozzylabs-opshub
Version: 0.2.3
Summary: Local-first operational memory and execution hub for humans and AI agents
Project-URL: Homepage, https://github.com/ozzy-labs/opshub
Project-URL: Issues, https://github.com/ozzy-labs/opshub/issues
Author: ozzy-labs
License: MIT
License-File: LICENSE
Keywords: ai-agents,event-sourcing,local-first,operational-memory
Classifier: License :: OSI Approved :: MIT License
Classifier: Operating System :: POSIX
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Office/Business
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.13
Requires-Dist: alembic>=1.13
Requires-Dist: jinja2>=3.1
Requires-Dist: pydantic-settings>=2.3
Requires-Dist: pydantic>=2.7
Requires-Dist: pyyaml>=6
Requires-Dist: sqlalchemy>=2.0
Requires-Dist: sqlite-vec>=0.1
Requires-Dist: structlog>=24.1
Requires-Dist: typer>=0.12
Provides-Extra: api-embedding-openai
Requires-Dist: openai>=1.40; extra == 'api-embedding-openai'
Provides-Extra: api-embedding-voyage
Requires-Dist: voyageai>=0.2; extra == 'api-embedding-voyage'
Provides-Extra: connectors-box
Requires-Dist: boxsdk<4,>=3.10; extra == 'connectors-box'
Provides-Extra: connectors-github
Requires-Dist: httpx>=0.27; extra == 'connectors-github'
Requires-Dist: pygithub>=2.3; extra == 'connectors-github'
Provides-Extra: connectors-ms365
Requires-Dist: httpx>=0.27; extra == 'connectors-ms365'
Requires-Dist: msal>=1.30; extra == 'connectors-ms365'
Provides-Extra: connectors-slack
Requires-Dist: slack-sdk>=3.30; extra == 'connectors-slack'
Provides-Extra: dev
Requires-Dist: mypy>=1.11; extra == 'dev'
Requires-Dist: pyright>=1.1.380; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest-cov>=5; extra == 'dev'
Requires-Dist: pytest-xdist>=3.6; extra == 'dev'
Requires-Dist: pytest>=8.2; extra == 'dev'
Requires-Dist: ruff>=0.6; extra == 'dev'
Requires-Dist: types-jsonschema>=4.21; extra == 'dev'
Requires-Dist: types-pyyaml>=6.0; extra == 'dev'
Provides-Extra: encryption
Requires-Dist: keyring>=24; extra == 'encryption'
Requires-Dist: sqlcipher3-binary>=0.5; extra == 'encryption'
Provides-Extra: llm-anthropic
Requires-Dist: anthropic>=0.40; extra == 'llm-anthropic'
Provides-Extra: llm-ollama
Requires-Dist: httpx>=0.27; extra == 'llm-ollama'
Provides-Extra: llm-openai
Requires-Dist: openai>=1.50; extra == 'llm-openai'
Provides-Extra: local-embedding
Requires-Dist: sentence-transformers>=3.0; extra == 'local-embedding'
Provides-Extra: mcp
Requires-Dist: mcp>=1.0; extra == 'mcp'
Provides-Extra: secrets
Requires-Dist: keyring>=24; extra == 'secrets'
Provides-Extra: vector
Requires-Dist: numpy>=2.0; extra == 'vector'
Description-Content-Type: text/markdown

# OpsHub

[![PyPI](https://img.shields.io/pypi/v/ozzylabs-opshub.svg)](https://pypi.org/project/ozzylabs-opshub/)
[![CI](https://github.com/ozzy-labs/opshub/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/ozzy-labs/opshub/actions/workflows/ci.yaml)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
[![Python 3.13+](https://img.shields.io/badge/python-3.13%2B-blue.svg)](https://www.python.org/downloads/)

English | [日本語](README.ja.md)

**Local-first secretary agent platform — auditable operational memory for humans and AI agents.**

OpsHub is the local-first **secretary agent platform**: an auditable, event-sourced operational memory that an AI agent host (Claude Code / Codex CLI / Gemini CLI / GitHub Copilot CLI) can drive on your behalf. Ask your agent "what should I do next?" or "draft a reply to that Slack thread" and it talks to OpsHub over MCP to recall, summarise, and propose — never sending your state to a cloud service.

Three layers ([ADR-0004](docs/adr/0004-agent-runtime-boundary.md)):

1. **You (human)** — ask in natural language.
2. **Secretary agent** — runs in your editor / terminal (Claude Code etc.). OpsHub ships **MCP server (`opshub mcp serve`) + Agent Skills (SKILL.md)** but no LLM runtime; the agent host owns the brain.
3. **OpsHub core (CLI)** — append-only event log + projections + body store + connectors. Same surface, whether you call it from the agent or from the CLI directly.

## Install

OpsHub is distributed on PyPI under the name **`ozzylabs-opshub`** (PyPI has no
namespace concept, so we follow the PEP 423 `<owner>-<package>` convention).
The CLI command stays `opshub`.

```bash
uv tool install ozzylabs-opshub
# or
pipx install ozzylabs-opshub
```

Optional extras (pulled only if needed):

```bash
uv tool install "ozzylabs-opshub[llm-anthropic,connectors-github]"
```

See [Optional dependencies](#optional-dependencies) below for the full extras
matrix.

### Alternative: install directly from GitHub

You can also install from a tagged git ref (no PyPI involvement):

```bash
uv tool install git+https://github.com/ozzy-labs/opshub.git@v0.1.0
```

This is useful for pre-release tags, unreleased fixes on `main`, or air-gapped
environments where PyPI isn't reachable.

## Quickstart

```bash
opshub init                                           # one-time DB + workspace setup
opshub task create "Write blog post about OpsHub"     # create a task
opshub task list                                       # see open tasks

# Once the LLM backend is configured (next section):
opshub brief "current priorities"                      # LLM-summarised briefing
opshub propose generate "what's next?"                 # LLM-proposed next-actions
opshub propose apply <proposal-id> 0                  # operator-approved entity creation
```

All state lives under XDG directories; override via `OPSHUB_*` env vars
(e.g. `OPSHUB_STORAGE__DB_PATH=/custom/path.sqlite`).

## Ask your secretary

Once you wire OpsHub into an agent host over MCP (see [Connect an agent host](#connect-an-agent-host-mcp) below), you can talk to your secretary in plain language. The agent calls the right OpsHub commands behind the scenes.

| You ask | Skill that fires | What it does |
|---|---|---|
| "What should I do next?" / "今日のまとめ" | `daily-brief` / `next-actions` | Top signals from the last 24h + active tasks + untriaged inbox |
| "Draft a reply to that Slack thread" / "返信案考えて" | `reply-draft` | LLM-generated draft grounded in your past sending style (you copy-paste — OpsHub never sends) |
| "Review PR #123" | `pr-review` | Pulls related decisions / tasks / past discussion so the agent can review with context |
| "Find that Box file about X" / "Box にあったあの資料" | `file-lookup` | Full-text + semantic search across Slack / Box / GitHub / MS365 / Box Drive |

The five secretary skills ship through [`ozzy-labs/skills`](https://github.com/ozzy-labs/skills) via the `@ozzylabs/skills` Renovate preset (handbook ADR-0016). See [`docs/secretary-agent.md`](docs/secretary-agent.md) for the full catalog and what OpsHub deliberately does not do (no write-back to SaaS, no always-on runtime, no auto-apply).

## Connect an agent host (MCP)

```bash
uv tool install "ozzylabs-opshub[mcp]"
opshub init
opshub db migrate
opshub mcp tools           # inspect read / write tool surface
```

Then point your agent host (Claude Code etc.) at `opshub mcp serve` as a stdio MCP server. Example for Claude Code (`~/.claude/mcp_servers.json`):

```json
{
  "mcpServers": {
    "opshub": { "command": "opshub", "args": ["mcp", "serve"] }
  }
}
```

Full setup (other hosts, encryption, troubleshooting): [`docs/mcp-setup.md`](docs/mcp-setup.md).

## Configure an LLM backend (optional)

OpsHub is functional without any LLM — `task` / `decision` / `inbox` /
`connector sync` all work standalone. To enable `brief` / `propose`, configure
one of:

```bash
opshub connector auth set llm:anthropic       # Anthropic Claude (recommended)
opshub connector auth set llm:openai          # OpenAI
# Or for local-only:
ollama serve && ollama pull llama3.2:3b
# Then set [llm] backend = "ollama" in ~/.config/opshub/config.toml
```

See [`docs/principles.md`](docs/principles.md) §1 (Local-first) for the design
rationale.

## What's in OpsHub today

Phases 1–8 shipped (2026-05-17, v0.1.0). Phase 9 shipped 2026-05-23. Phase 10 shipped 2026-05-31:

| Phase | Layer | Highlights |
|---|---|---|
| 1 | Foundation | Event store, tasks, CLI, markdown workspace |
| 2 | Coordination | Inbox / decisions / sessions / locks / handoffs |
| 3 | Connectors framework | GitHub connector + workspace file ingest |
| 4 | Semantic recall | Pluggable Embedder (local / OpenAI / Voyage) + sqlite-vec + `recall` + duplicate detection |
| 5 | Briefing | Pluggable LLM (Anthropic / OpenAI) + `brief` + prompt injection mitigation |
| 6 | Action loop | Structured output + Ollama backend + `propose` (human-in-the-loop) |
| 7 | Connectors wave 2 | Slack + Microsoft 365 + Box |
| 8 | Knowledge graph | `links` projection + auto-extraction + `graph` + `--expand-graph` |
| 9 | Local-FS connectors | `box_drive` (Box Drive desktop client → local FS scan, ADR-0019) |
| 10 | Secretary agent platform | Full local body retention (ADR-0020) + encryption at rest (ADR-0021) + MCP server (ADR-0022) + `opshub search` (FTS5) + `opshub mcp serve` + secretary 5 Skills + reply-draft (ADR-0016 §決定 (i)) + ADR-0004 revision (form-A: no agent runtime in core) + ADR-0010 revision (write-back ban) + ADR-0017 revision (reply_draft link types) |

Next: **Phase 11 (MS Office deep-dive — Teams + Outlook body + Word/Excel/PowerPoint extraction)** — see [`docs/phase-10-plan.md`](docs/phase-10-plan.md) §9. Phase 12+ candidates: multi-machine sync, proactive secretary (cron-delegated commands). Longer phase-by-phase narrative lives in
[`docs/architecture.md`](docs/architecture.md) §9 (Phased Delivery).

## Commands

```bash
# Foundation (Phase 1)
opshub task create "draft phase 2 plan"
opshub task list --format md                          # formats: table / md / json

# Coordination (Phase 2)
opshub inbox add "triage the failing nightly build"
opshub inbox triage <id> --to-task "fix nightly build"
opshub inbox list --format md
opshub decision record "adopt sqlite-vec for Phase 4"
opshub decision list
opshub lock acquire task:<task-ulid>                  # ADR-0013 coordination lock
opshub lock release <lock-id>
opshub lock list
opshub session start --scope "phase-3 design"         # work session bracket
opshub agent run begin claude                         # auto-injected into agent runs
opshub agent run end <run-id> --summary "drafted ADR-0017"
opshub session end --summary "EOD wrap"
opshub handoff open --from agent:claude --to ozzy --topic "review"
opshub handoff close <handoff-id> --note "merged"

# Connectors (Phase 3 + Phase 7, ADR-0010 / ADR-0014)
opshub connector auth set github                      # store GitHub PAT in OS keychain
opshub connector sync github                          # incremental sync (OPSHUB_CONNECTOR_GITHUB_REPO=owner/repo)
opshub connector auth set connector:slack             # store Slack OAuth token in OS keychain (User Token preferred, Bot Token also accepted — ADR-0018)
opshub connector sync slack                           # incremental sync ([connectors.slack] channels)
opshub connector auth set connector:ms365             # OAuth paste-code (Microsoft Graph Calendar / OneDrive / Outlook)
opshub connector sync ms365                           # incremental sync per endpoint
opshub connector auth set connector:box               # OAuth paste-code (Box Events API)
opshub connector sync box                             # incremental sync (Box stream_position cursor)
opshub connector sync box_drive                       # Phase 9: scan local Box Drive mount (see docs/box-drive-setup.md)
opshub connector list                                 # show registered connectors

# Workspace + projections
opshub workspace ingest                               # ingest workspace/inbox/*.md (Phase 3)
opshub workspace ingest --dry-run                     # scan only, no writes
opshub workspace generate                             # regenerate markdown workspace from projections
opshub projections rebuild                            # rebuild projections from the event store (idempotent)

# Semantic recall (Phase 4, ADR-0012)
opshub connector auth set embedder:openai             # store OpenAI API key in OS keychain
opshub embeddings rebuild                             # bulk-embed task/decision/inbox/source bodies (Phase 10: now body-based, ADR-0020)
opshub embeddings status                              # show backend + per-entity-type embedded vs pending
opshub embeddings drain                               # retry pending embeddings (auto-embed hook backup)
opshub embeddings find-duplicates -t 0.92             # offline near-duplicate scan
opshub recall "recent decisions about authentication" # semantic search across all entities

# Full-text search across source bodies (Phase 10, ADR-0012 改訂 §4 + ADR-0020)
opshub search "ticket-1234"                           # SQLite FTS5 across Slack / GitHub / Box / MS365 / Box Drive bodies
opshub search "deploy AND failure" --raw              # opt into FTS5 boolean / phrase / prefix syntax
opshub search "channel ID" --connector slack          # restrict to one connector

# Briefing (Phase 5, ADR-0015)
opshub connector auth set llm:anthropic               # store Anthropic API key in OS keychain
opshub brief "phase 5 progress"                       # LLM-backed briefing (markdown to stdout)
opshub brief "phase 5 progress" --save                # also persist under <workspace>/briefings/
opshub brief "phase 5 progress" --format json         # JSON with briefing_id / model / tokens / source_refs
opshub brief "phase 8 review" --expand-graph          # widen LLM context via 1-hop graph expansion

# Action loop (Phase 6, ADR-0016, Phase 10 reply-draft)
opshub propose generate "next steps"                  # LLM proposes task/decision candidates
opshub propose generate "next steps" --from-briefing <id>
opshub propose generate "next steps" --format json
opshub propose generate "next steps" --expand-graph   # 1-hop graph expansion for proposals
opshub propose generate "" --reply-to <source-id>     # Phase 10: reply-draft mode (topic is ignored; ADR-0016 §決定 (i))
opshub propose list                                   # recent proposals (markdown table)
opshub propose list --state pending --limit 10        # filter: pending / applied / rejected
opshub propose apply <proposal-id> <candidate-index>  # operator approval → creates entities (reply-draft saves locally, never sends)
opshub propose reject <proposal-id> <candidate-index> --reason "out of scope"

# Knowledge graph (Phase 8, ADR-0017)
opshub link add task:<task-id> source:<src-id> --type references
opshub link list --from task:<task-id>                # filter by --from / --to / --type
opshub link remove <link-id> --reason "wrong source"  # hard delete (emits LinkDeleted)
opshub graph related task:<task-id> --direction both  # 1-hop neighbours (md / json / dot)
opshub graph trace task:<task-id> --depth 3           # backward provenance walk (default 3, max 10)
opshub graph expand task:<task-id> --depth 2 --format dot

# MCP server surface (Phase 10, ADR-0022)
opshub mcp tools                                       # inspect read / write tool surface (auditable policy-as-data)
opshub mcp tools -f json                               # JSON for diff / scripting
opshub mcp serve                                       # stdio MCP server — agent host spawns this as a subprocess
```

## Optional dependencies

| Extras | Purpose | Heavy? |
|---|---|---|
| `vector` | sqlite-vec for semantic recall | Small |
| `local-embedding` | sentence-transformers (bge-m3, ~500MB) | Heavy |
| `api-embedding-openai` / `api-embedding-voyage` | API embedder backends | Small |
| `llm-anthropic` / `llm-openai` | API LLM backends | Small |
| `llm-ollama` | Ollama daemon client | Small |
| `connectors-github` / `connectors-slack` / `connectors-ms365` / `connectors-box` | SaaS connectors | Small |
| `secrets` | OS keyring backend | Small |
| `encryption` | SQLCipher-backed at-rest encryption (Phase 10, ADR-0021) | Small |
| `mcp` | MCP server SDK for `opshub mcp serve` (Phase 10, ADR-0022) | Small |
| `dev` | Test + lint toolchain | Medium |

## Documentation

- [`docs/principles.md`](docs/principles.md) — design principles (local-first, event-sourced, full local content retention)
- [`docs/architecture.md`](docs/architecture.md) — layered architecture overview
- [`docs/secretary-agent.md`](docs/secretary-agent.md) — Phase 10 secretary agent platform usage (skills catalog, what it can / cannot do)
- [`docs/mcp-setup.md`](docs/mcp-setup.md) — Phase 10 MCP setup for agent hosts (Claude Code etc.)
- [`docs/adr/`](docs/adr/README.md) — Architecture Decision Records
- [`docs/box-drive-setup.md`](docs/box-drive-setup.md) — Phase 9 `box_drive` connector setup (WSL2 / macOS)
- [`docs/upgrading.md`](docs/upgrading.md) — version migration notes (when applicable)
- [`docs/release-notes-v0.1.0.md`](docs/release-notes-v0.1.0.md) — v0.1.0 narrative release notes
- [`docs/RELEASE_RUNBOOK.md`](docs/RELEASE_RUNBOOK.md) — how to cut a release (maintainers)
- [`CHANGELOG.md`](CHANGELOG.md) — release history
- [`CONTRIBUTING.md`](CONTRIBUTING.md) — contribution guidelines
- [`SECURITY.md`](SECURITY.md) — vulnerability disclosure + Phase 10 body retention threat model

## License

MIT. See [LICENSE](LICENSE).
