# https://github.com/otomata-tech/oto-cli Project Manual

Generated at: 2026-07-28 17:18:10 UTC

## Table of Contents

- [Introduction to oto-cli](#page-overview)
- [Architecture and Auto-Discovery](#page-architecture)
- [Built-in Connectors Reference](#page-connectors)
- [Creating Custom Connectors](#page-custom-connectors)

<a id='page-overview'></a>

## Introduction to oto-cli

### Related Pages

Related topics: [Architecture and Auto-Discovery](#page-architecture), [Built-in Connectors Reference](#page-connectors)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/otomata-tech/oto-cli/blob/main/README.md)
- [pyproject.toml](https://github.com/otomata-tech/oto-cli/blob/main/pyproject.toml)
- [cli.py](https://github.com/otomata-tech/oto-cli/blob/main/cli.py)
- [docs/installation.md](https://github.com/otomata-tech/oto-cli/blob/main/docs/installation.md)
- [docs/secrets.md](https://github.com/otomata-tech/oto-cli/blob/main/docs/secrets.md)
- [oto/commands/__init__.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/__init__.py)
- [oto/tools/browser/__init__.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/tools/browser/__init__.py)
</details>

# Introduction to oto-cli

`oto-cli` is a Python-based command-line tool maintained by Otomata Tech. It ships a unified `oto` entry point that aggregates several day-to-day utilities: configuration management, secret lookup, and a growing browser-automation toolkit. The current major release, `v1.5.0`, introduced plugin-based extension via Python entry-points, allowing optional browser clients to be installed as separate extras. Source: [README.md:1-30]()

## What `oto-cli` Solves

The tool consolidates internal scripts (previously scattered shell utilities, including legacy browser clients in `/data/pro/veille/`) into a single, discoverable, and installable package. It targets three main usage areas:

1. **Configuration management** — `oto config` subcommands for inspecting and editing local config.
2. **Secret retrieval** — `oto config provider secrets` resolves credentials from multiple backends (file, Scaleway, environment variables).
3. **Browser automation** — `oto browser` drives headless browsers against sites such as LinkedIn, Crunchbase, Pappers, Collective, WTTJ, and VivaTech.

Source: [docs/installation.md:1-25]() Source: [docs/secrets.md:1-40]()

## Command Discovery and Plugin Architecture

The root entry point is defined in `cli.py`. At startup, the CLI discovers its subcommands by globbing the `oto/commands/` package directory: `cli.py` iterates over `_commands_dir.glob("*.py")` and imports each module that exposes a command definition. Source: [cli.py:1-40]()

This in-package discovery is simple but limits extensibility: custom or client-specific connectors cannot be added from outside the package without modifying the source tree. Community discussion in issue #9 ("Isoler les connecteurs custom/client du core public via entry-points `oto.commands`") tracks the proposal to introduce a proper Python entry-point under the `oto.commands` group, which would let third-party packages register additional commands without forking the core. Source: [cli.py:1-40]()

A precedent for this plugin model already exists in the browser subsystem. Starting with `v1.5.0`, `oto browser vivatech` is delivered as a separate plugin (`o-browser-vivatech`) that registers through the `o_browser.sites` entry-point group. Users opt in by installing the optional extra `oto-cli[vivatech]`. Source: [pyproject.toml:1-60]() Source: [oto/tools/browser/__init__.py:1-30]()

```
┌──────────────────────────────────────────────────┐
│                 oto (CLI entry)                  │
├──────────────────────────────────────────────────┤
│   cli.py  ──►  glob oto/commands/*.py            │
│              ──►  importlib entry-points         │
│                  (o_browser.sites, etc.)         │
├──────────────────────────────────────────────────┤
│   oto/commands/   │   external plugin packages   │
│   ──────────────  │   ─────────────────────────  │
│   config          │   o-browser-vivatech         │
│   browser (core)  │   o-browser-<custom>         │
│   tools           │                              │
└──────────────────────────────────────────────────┘
```

## Configuration and Secrets

Configuration is read from a local config directory, and the secret-lookup subsystem is pluggable. As of the current release, `oto config provider secrets <file|scaleway>` exposes two first-class providers plus an environment-variable fallback. Internally, Otomata also relies on SOPS for secret management in its `otomata-tech/secrets` repository, but SOPS is not yet a first-class provider in the CLI. Issue #3 ("rework secrets côté CLI") tracks the planned expansion to a unified `oto secrets` lookup with scoped providers, including SOPS, OS keychain, and a clean precedence order between providers. Source: [docs/secrets.md:1-60]()

## Installation and Optional Extras

The package is installed from PyPI or from source via `pip install oto-cli`. Optional features are exposed as pip extras. The `vivatech` extra pulls in the VivaTech browser plugin:

```
pip install "oto-cli[vivatech]"
```

Each extra corresponds to a dependency group declared in `pyproject.toml` and to an external package that registers itself under a known entry-point. This pattern is the reference design for future command-level plugins once issue #9 lands. Source: [pyproject.toml:1-80]() Source: [docs/installation.md:1-40]()

## Project Conventions

- **In-package commands** live under `oto/commands/` and are auto-loaded via glob. Source: [oto/commands/__init__.py:1-20]()
- **Browser clients** are unit-style: a client knows how to fetch data from a single site but does not own "stop on seen" or deduplication logic, which is handled by the orchestrating command (as called out in issue #1 for the Collective and WTTJ clients). Source: [oto/tools/browser/__init__.py:1-30]()
- **Plugins** register through Python entry-points; the `oto-cli` core treats them as opt-in extensions rather than bundled dependencies.

Together these conventions define a CLI that is small in its core, explicit about what it ships, and extensible through standard packaging mechanisms.

---

<a id='page-architecture'></a>

## Architecture and Auto-Discovery

### Related Pages

Related topics: [Introduction to oto-cli](#page-overview), [Built-in Connectors Reference](#page-connectors), [Creating Custom Connectors](#page-custom-connectors)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [oto/cli.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/cli.py)
- [oto/commands/__init__.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/__init__.py)
- [oto/tools/browser/__init__.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/tools/browser/__init__.py)
- [pyproject.toml](https://github.com/otomata-tech/oto-cli/blob/main/pyproject.toml)
- [docs/concepts.md](https://github.com/otomata-tech/oto-cli/blob/main/docs/concepts.md)
- [docs/cli.md](https://github.com/otomata-tech/oto-cli/blob/main/docs/cli.md)
</details>

# Architecture and Auto-Discovery

`oto-cli` is a domain-oriented command-line toolkit organized around a small, explicit core and a directory-based convention for registering user-facing subcommands. The project follows a plugin-style architecture where the `cli.py` entry point bootstraps a Typer application and dynamically wires sub-apps discovered from two places: built-in command modules under `oto/commands/` and external browser-site clients discovered via the `o_browser.sites` Python entry-point group.

## Overall Architecture

The CLI is layered as follows:

- **Entry point (`oto/cli.py`)** — defines the root `typer.Typer` app, sets up logging, configuration providers, and the global `--verbose / --debug` flags. It then mounts sub-apps for each discovered command.
- **Built-in commands (`oto/commands/`)** — each `*.py` file in this directory is treated as a command module exposing a `app: typer.Typer` object. These are loaded eagerly at startup via `importlib`.
- **Toolkit layer (`oto/tools/`)** — non-CLI utility code (browser clients, helpers, IO, scoring) consumed by commands. Tools are not auto-discovered as commands; they are imported explicitly.
- **Plugins (entry-points)** — optional external packages, e.g. `o-browser-vivatech`, that contribute browser-site clients under the `o_browser.sites` group.

Source: [oto/cli.py:1-80]() and [docs/concepts.md:1-60]()

## Command Auto-Discovery via Glob

The primary auto-discovery mechanism is a `pathlib` glob over the `oto/commands/` package. `cli.py` resolves `_commands_dir = Path(__file__).parent / "commands"` and iterates `_commands_dir.glob("*.py")`. For each module file it performs a dynamic `importlib.import_module(f"oto.commands.{stem}")` and, if the module exposes a top-level `app` attribute, mounts it under the root Typer application. Source: [oto/cli.py:30-75]()

This convention has two consequences documented in the codebase:

1. **Drop-in extensibility inside the repo** — adding a new command is as simple as creating a new file under `oto/commands/`. No central registry must be edited. Source: [docs/cli.md:1-40]()
2. **In-package limitation** — discovery is scoped to files shipped inside the `oto.commands` package itself. There is no native hook for third-party packages to inject commands unless they live next to the CLI source. This limitation is the subject of issue #9 ("Isoler les connecteurs custom/client du core public via entry-points `oto.commands`"), which proposes introducing an `oto.commands` entry-point group analogous to the existing `o_browser.sites`. Source: [community issue #9]()

The `__init__.py` of `oto/commands/` is intentionally minimal — it only re-exports subpackages so the glob loader can find them. It does not register commands manually. Source: [oto/commands/__init__.py:1-20]()

## Plugin Auto-Discovery via Entry-Points

A second, narrower form of discovery is used for browser-site clients. Each browser client module lives under `oto/tools/browser/` (for example `linkedin`, `crunchbase`, `pappers`) and exposes a uniform interface. The registry is populated by walking the `o_browser.sites` entry-point group declared in `pyproject.toml`, using `importlib.metadata.entry_points()`. Source: [oto/tools/browser/__init__.py:1-60]() and [pyproject.toml:30-80]()

The release v1.5.0 introduced the first external consumer of this mechanism: the `o-browser-vivatech` package, installable as the optional extra `oto-cli[vivatech]`. After installation, the `oto browser vivatech` command becomes available without any change to `oto-cli` itself. Source: [release notes v1.5.0]()

```mermaid
flowchart LR
    A[oto/cli.py<br/>root Typer app] --> B[oto/commands/*.py<br/>glob + importlib]
    A --> C[oto browser<br/>sub-app]
    C --> D[oto/tools/browser/*<br/>built-in clients]
    C --> E[o_browser.sites<br/>entry-points]
    E --> F[o-browser-vivatech<br/>optional extra]
```

## Configuration and Secrets Wiring

Beyond commands, `cli.py` also wires the configuration system at startup. The `oto config` command exposes provider selection for secrets (`file`, `scaleway`) with environment-variable fallback. SOPS is mentioned in the community discussion (issue #3) as an internal-only provider that is not yet exposed as a first-class option in the public CLI. Source: [docs/concepts.md:60-120]() and [community issue #3]()

The architecture therefore separates three concerns:

| Layer | Discovery mechanism | Mutability |
|-------|--------------------|------------|
| Commands | `Path.glob("*.py")` over `oto/commands/` | Editable in-repo only |
| Browser sites | `importlib.metadata` entry-points `o_browser.sites` | Extensible via pip extras |
| Secrets providers | Hard-coded list (`file`, `scaleway`) + env fallback | Configuration-time choice |

Source: [oto/cli.py:80-140]() and [pyproject.toml:1-40]()

## Practical Implications

For contributors adding commands, the path of least resistance remains placing a new `*.py` under `oto/commands/` with a module-level `app = typer.Typer(...)`. For maintainers distributing client-specific connectors outside the public core, the established pattern is the entry-point group, and issue #9 tracks generalizing that pattern from `o_browser.sites` to a generic `oto.commands` group. Source: [docs/concepts.md:120-180]() and [community issue #9]()

---

<a id='page-connectors'></a>

## Built-in Connectors Reference

### Related Pages

Related topics: [Architecture and Auto-Discovery](#page-architecture), [Creating Custom Connectors](#page-custom-connectors)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [oto/commands/browser.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/browser.py)
- [oto/commands/google.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/google.py)
- [oto/commands/notion.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/notion.py)
- [oto/commands/serper.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/serper.py)
- [oto/commands/search.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/search.py)
- [oto/commands/enrichment.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/enrichment.py)
- [oto/cli.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/cli.py)
</details>

# Built-in Connectors Reference

The **built-in connectors** in `oto-cli` are the command modules shipped under `oto/commands/`. Each module exposes one or more Click subcommands that wrap an external API, headless-browser client, or local enrichment step. They form the *public core* of the CLI: every user who installs `oto-cli` gets them out of the box, and they are the baseline against which custom/client-specific connectors are contrasted (see issue #9).

## Discovery Mechanism

`oto/cli.py` loads built-in connectors by globbing Python files in the `oto/commands/` package:

```python
# oto/cli.py (illustrative)
for _module_path in _commands_dir.glob("*.py"):
    ...
```

This means that **adding a built-in connector is as simple as dropping a new `*.py` file into `oto/commands/`**. There is currently no entry-point registration for built-ins; that gap is exactly what issue #9 ("Isoler les connecteurs custom/client du core public via entry-points `oto.commands`") addresses — custom/client connectors cannot yet extend the core without forking. By contrast, browser **site** connectors already use entry-points (`o_browser.sites`), as demonstrated by `o-browser-vivatech` in v1.5.0.

Source: [oto/cli.py]()

## Connector Catalog

The currently shipped built-in command modules are summarized below. Each module typically registers a Click group (e.g. `oto browser …`) that fans out into site- or action-specific subcommands.

| Module | Command group | Purpose |
|---|---|---|
| `oto/commands/browser.py` | `oto browser …` | Headless-browser clients for external sites |
| `oto/commands/google.py` | `oto google …` | Google APIs (e.g. Sheets/Drive helpers) |
| `oto/commands/notion.py` | `oto notion …` | Notion API helpers |
| `oto/commands/serper.py` | `oto serper …` | Serper.dev search API wrapper |
| `oto/commands/search.py` | `oto search …` | Cross-provider search aggregator |
| `oto/commands/enrichment.py` | `oto enrichment …` | Composite enrichment pipelines |

Source: [oto/commands/browser.py](), [oto/commands/google.py](), [oto/commands/notion.py](), [oto/commands/serper.py](), [oto/commands/search.py](), [oto/commands/enrichment.py]()

## Browser Connectors

The browser subsystem is the largest family of built-in connectors. `oto/commands/browser.py` exposes a generic `oto browser <site>` command that dispatches to per-site clients living in `oto/tools/browser/`. Out-of-the-box sites include **LinkedIn**, **Crunchbase**, and **Pappers**; **Collective** and **WTTJ** were migrated from legacy shell scripts into the same directory (issue #1). Each client is *unitary*: it does not implement "stop on seen" logic — that deduplication is layered on top by the caller.

Site connectors are pluggable: `o-browser-vivatech` is shipped as a separate package that registers itself via the `o_browser.sites` entry-point, so `oto browser vivatech` only appears when the optional extra `oto-cli[vivatech]` is installed (v1.5.0 release notes).

Source: [oto/commands/browser.py]()

## Search, Enrichment, and Knowledge Connectors

- **`oto/commands/search.py`** and **`oto/commands/serper.py`**: `serper` is a thin wrapper around the Serper.dev Google Search API; `search` aggregates results across providers and normalizes output for downstream enrichment.
- **`oto/commands/enrichment.py`**: orchestrates multi-step enrichment by chaining search, browser, and knowledge connectors (Notion/Google) into reusable pipelines.
- **`oto/commands/google.py`** and **`oto/commands/notion.py`**: target the Google Workspace and Notion APIs respectively, typically used to persist enrichment results into Sheets or Notion databases.

Source: [oto/commands/search.py](), [oto/commands/serper.py](), [oto/commands/enrichment.py](), [oto/commands/google.py](), [oto/commands/notion.py]()

## Configuration and Secrets

Built-in connectors share a common secrets-resolution layer. The command `oto config provider secrets <file|scaleway>` switches between two first-class providers — a local encrypted **file** provider and a **Scaleway** secret manager — with environment-variable fallback. SOPS (used internally by Otomata) is not yet exposed as a first-class provider; issue #3 ("rework secrets côté CLI") tracks the roadmap to add it and to scope lookups per connector so that, for example, `serper` only requests the `SERPER_API_KEY` while `notion` only requests the Notion token.

Until entry-point-based loading lands (issue #9), any custom connector that needs to live alongside the built-ins must either be vendored into `oto/commands/` or shipped as a separate plugin using one of the existing entry-point groups such as `o_browser.sites`.

Source: [oto/cli.py]()

---

<a id='page-custom-connectors'></a>

## Creating Custom Connectors

### Related Pages

Related topics: [Architecture and Auto-Discovery](#page-architecture), [Built-in Connectors Reference](#page-connectors)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [docs/create-connector.md](https://github.com/otomata-tech/oto-cli/blob/main/docs/create-connector.md)
- [oto/cli.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/cli.py)
- [oto/commands/__init__.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/__init__.py)
- [pyproject.toml](https://github.com/otomata-tech/oto-cli/blob/main/pyproject.toml)
- [oto/commands/config.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/config.py)
- [oto/commands/secrets.py](https://github.com/otomata-tech/oto-cli/blob/main/oto/commands/secrets.py)
</details>

# Creating Custom Connectors

## Purpose and Scope

Custom connectors in `oto-cli` are Python modules that expose new sub-commands under the `oto` CLI. They allow teams to package client-specific or domain-specific automations (CRM scrapers, internal SaaS bridges, browser site clients, etc.) without modifying the public core. The current discovery mechanism lives entirely inside the package: at startup, the CLI scans a fixed directory and loads every module that matches a glob. `Source: [oto/cli.py]()` documents this loop via `_commands_dir.glob("*.py")`.

This page explains how a connector is structured today, where it must live to be picked up, and what changes are being discussed (entry-points) so that third-party connectors can ship independently of the core package.

## Connector Structure Today

A connector is a single `.py` file under `oto/commands/`. The discovery contract is intentionally minimal:

- File name becomes the command name (without `.py`). Source: [oto/cli.py]()
- The module exposes one or more Typer/Click callbacks decorated with `@app.command(...)`. Source: [oto/commands/__init__.py]()
- Shared runtime helpers (config loader, secret lookup, logger) are imported from sibling modules such as `config.py` and `secrets.py`. Source: [oto/commands/config.py]() and [oto/commands/secrets.py]()

The package's `__init__.py` aggregates a shared Typer `app` and re-exports helpers so each connector file stays short. The pattern is repeated for every built-in connector (`config`, `secrets`, `browser`, etc.). Source: [oto/commands/__init__.py]()

The official guidance file `docs/create-connector.md` walks through this same template: drop a file in `oto/commands/`, decorate a function with `@app.command`, and rely on the core CLI to wire it under `oto <your-connector>`. Source: [docs/create-connector.md]()

## Registration Flow

The current registration is filesystem-based rather than plugin-based. The CLI reads `_commands_dir` from the installed package, iterates `*.py` entries, imports each module, and lets the decorators register commands on the shared `app` object.

```mermaid
flowchart LR
    A[oto CLI start] --> B[Resolve oto/commands dir]
    B --> C[Glob *.py files]
    C --> D[Import each module]
    D --> E[Decorators register on shared Typer app]
    E --> F[oto <cmd> available]
```

This simple model has a limitation that the community has flagged: it does not allow a connector to live outside the installed package. Source: [oto/cli.py]() shows there is no entry-point lookup; only files physically present in the package directory are loaded. As discussed in issue #9, isolating custom/client connectors from the public core via entry-points `oto.commands` is the proposed solution so a client-specific connector can ship as its own distribution and still be discovered at runtime. The latest release v1.5.0 already demonstrates this pattern for browser sites through the `o_browser.sites` entry-point consumed by the `oto browser` namespace. Source: [pyproject.toml]()

## Best Practices and Conventions

When authoring a connector today, follow the conventions enforced by the existing built-ins:

| Concern | Convention | Reference |
|---|---|---|
| File location | `oto/commands/<name>.py` | [oto/cli.py]() |
| Command object | Reuse shared Typer `app` | [oto/commands/__init__.py]() |
| Configuration | Read via `oto/commands/config.py` helpers | [oto/commands/config.py]() |
| Secrets | Read via `oto/commands/secrets.py` providers | [oto/commands/secrets.py]() |
| Optional deps | Declared as extras in `pyproject.toml` | [pyproject.toml]() |

Practical recommendations drawn from the existing code:

1. Keep connector modules thin: defer I/O and auth to shared helpers so the entry-point refactor (issue #9) remains a drop-in change. Source: [oto/commands/secrets.py]()
2. Prefer environment-aware secret lookup (env vars → file provider → Scaleway provider) over hard-coded credentials; this matches the provider rework tracked in issue #3. Source: [oto/commands/secrets.py]()
3. When targeting a niche site (for example a conference exhibitor list), model it as a unitary client under `oto/tools/browser/` so it can later be extracted into a plugin entry-point like `o_browser.sites`, mirroring the v1.5.0 `oto browser vivatech` release. Source: [pyproject.toml]() and issue #1
4. Declare any heavy dependency as an optional extra (e.g. `oto-cli[vivatech]`) rather than a hard requirement, so the core install stays light. Source: [pyproject.toml]()

## Roadmap: Entry-Points for Connectors

Issue #9 proposes replacing the glob-based discovery with a Python entry-point group named `oto.commands`. Each installed package would declare one or more entry-points pointing at importable modules, and `oto/cli.py` would load them at startup in addition to (or instead of) the bundled files. This unblocks client-specific connectors, simplifies vendor distribution, and keeps the public core small. Until that lands, the only supported way to add a connector remains dropping a module into `oto/commands/`. Source: [docs/create-connector.md]() and [oto/cli.py]()

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: otomata-tech/oto-cli

Summary: Found 9 structured pitfall item(s), including 1 high/blocking item(s). Top priority: Installation risk - Installation risk requires verification.

## 1. Installation risk - Installation risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/otomata-tech/oto-cli/issues/3

## 2. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/otomata-tech/oto-cli

## 3. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/otomata-tech/oto-cli

## 4. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/otomata-tech/oto-cli

## 5. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/otomata-tech/oto-cli

## 6. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/otomata-tech/oto-cli

## 7. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/otomata-tech/oto-cli/issues/9

## 8. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/otomata-tech/oto-cli

## 9. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/otomata-tech/oto-cli

<!-- canonical_name: otomata-tech/oto-cli; human_manual_source: deepwiki_human_wiki -->
