Metadata-Version: 2.4
Name: yotei-runner
Version: 0.1.12
Summary: Scheduler for unattended Codex tasks with Telegram notifications
Author: massun-onibakuchi
License-Expression: MIT
Project-URL: Homepage, https://github.com/massun-onibakuchi/yotei
Project-URL: Repository, https://github.com/massun-onibakuchi/yotei
Project-URL: Issues, https://github.com/massun-onibakuchi/yotei/issues
Keywords: automation,cli,codex,scheduler,telegram
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 :: Only
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Programming Language :: Python :: 3.14
Classifier: Topic :: Software Development :: Build Tools
Classifier: Topic :: System :: Systems Administration
Requires-Python: >=3.12
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: pytest>=8.3.0; extra == "dev"
Requires-Dist: pytest-cov>=5.0.0; extra == "dev"
Dynamic: license-file

# Yotei

Yotei is a small scheduler for unattended Codex tasks.

## Install

```sh
uv tool install -U yotei-runner
```

Yotei expects the `codex` CLI to already be installed and authenticated on the
machine where scheduled runs execute. The scheduler invokes the configured Codex
binary directly and does not manage Codex login state for you.

The published package name is `yotei-runner`. The installed command remains
`yotei`.

## Quick Start

Initialize a user-level config file:

```sh
yotei config init
```

Schedule a task in the current directory:

```sh
yotei schedule \
  --task repo-review \
  --when "weekdays 09:30" \
  --prompt "Review the latest repository changes."
```

Run one scheduler pass:

```sh
yotei run --once
```

Inspect current tasks:

```sh
yotei status
```

Start one task immediately without changing its normal schedule:

```sh
yotei kickstart --task repo-review
```

## Config Discovery And Paths

Config discovery order is:

1. `--config <path>`
2. `YOTEI_CONFIG`
3. `SCHEDULED_AGENT_RUNNER_CONFIG`
4. `.automation/yotei/config.toml` in the current directory or a parent
5. `.automation/scheduled-agent-runner/config.toml` in the current directory or a parent
6. `${XDG_CONFIG_HOME:-~/.config}/yotei/config.toml`

By default, `yotei config init` writes:

- config: `${XDG_CONFIG_HOME:-~/.config}/yotei/config.toml`
- state DB: `${XDG_STATE_HOME:-~/.local/state}/yotei/state.sqlite3`
- logs: `${XDG_STATE_HOME:-~/.local/state}/yotei/logs/`

Yotei uses the same XDG-style defaults on Linux and macOS when the XDG
variables are unset. That keeps installs portable and predictable across
machines.

## Workspace Semantics

Each task stores its own absolute `workspace_root`.

- `yotei schedule` defaults `--workspace` to the current directory
- `yotei schedule --workspace <path>` stores an explicit workspace
- `yotei edit --workspace <path>` repairs or changes a task workspace later
- `yotei status` shows each task's stored workspace
- `yotei run` executes Codex with that persisted workspace as `cwd`

If a stored workspace no longer exists on a machine, Yotei fails that run
cleanly and tells you to repair it with `yotei edit --workspace`.

## Codex Permissions

Each task stores a Codex sandbox mode and forwards it to `codex exec`.

- `--read-only` stores `read-only`
- `--workspace-write` stores `workspace-write`
- `--yolo` stores `danger-full-access`
- `--sandbox <mode>` accepts any Codex sandbox mode directly

Set `codex.default_sandbox_mode` in config to choose the default for new tasks:

```toml
[codex]
default_sandbox_mode = "workspace-write"
```

Use these flags with `yotei schedule` or `yotei edit` to override the default
for a task. If the config omits `default_sandbox_mode`, new tasks default to
`read-only`, matching Codex's default. For example:

```sh
yotei schedule \
  --task repo-fix \
  --when "every 30m" \
  --prompt "Fix the next failing test." \
  --workspace /path/to/repo \
  --yolo
```

Advanced Codex execution options can be stored as raw `codex exec` arguments
after `--`. Yotei still owns the prompt, session mode, model, workspace, and
JSONL output parsing, so passthrough arguments must start with an option:

```sh
yotei schedule \
  --task structured-review \
  --when "every 2h" \
  --prompt "Review the repository and return structured findings." \
  --workspace /path/to/repo \
  -- \
  --profile prod \
  --config 'model_reasoning_effort="high"' \
  --ephemeral \
  --output-schema /path/to/schema.json
```

Use `yotei edit --task <id> -- <codex exec args...>` to replace stored
passthrough arguments, or `yotei edit --task <id> --clear-codex-exec-args` to
remove them.

## Scheduler Ownership

Yotei's first portable release expects one active scheduler process per state
database. Do not run multiple long-lived `yotei run` processes against the same
`state.sqlite3`; the current baseline documents this ownership contract instead
of using cross-platform lock files or database leases.

Use `yotei run` for the normal long-running scheduler. It polls for due tasks,
runs them from each task's persisted workspace, writes run logs, and keeps
polling until interrupted.

Use `yotei run --once` for tests, smoke checks, service hooks, or manual
maintenance. It performs one scheduler pass, including due tasks and at most one
queued run, then exits. The same single-runner policy applies while that pass is
running.

Use `yotei kickstart --task <id>` when you want one stored task to run right
now. Kickstart uses the task's existing prompt, model, session mode, chat id, and
workspace, starts the immediate run in a detached worker, and returns control to
your shell without waiting for Codex to finish. It does not recalculate
`next_run_at` or change whether the task is paused. If that task already has an
active run, Yotei queues one immediate run.

## Telegram

Telegram notifications are optional. Set `telegram.bot_token` and
`telegram.chat_id`, then keep `notifications.send_on_start`, `send_on_success`,
and `send_on_failure` enabled only for the events you want.

When start notifications are enabled, Yotei edits the start message with
progress steps from the Codex JSONL stream. Edits are paced by
`notifications.progress_interval_seconds` and the final success/failure
notification is still sent as its own message.

Use a literal token or an environment reference such as:

```toml
[telegram]
bot_token = "env:TG_BOT_TOKEN"
chat_id = "env:TG_CHAT_ID"
```

`yotei schedule --chat-id <id>` overrides `telegram.chat_id` for one task.

Notification delivery failures do not fail Codex runs. Yotei records concise
notification errors in run metadata and scheduler logs without writing the bot
token back into summaries or logs.

## Schedule Grammar

`yotei schedule --when` and `yotei edit --when` accept these forms:

- `in <int>m`, `in <int>h`, `once in <int>m`, `once in <int>h`
- `every <int>m`, `every <int>h`
- `daily H:MM`, `weekdays H:MM`
- `<day-list> H:MM`, where days are `mon,tue,wed,thu,fri,sat,sun`
- `cron "<minute> <hour> <day-of-month> <month> <day-of-week>"`

Clock times must use 24-hour `H:MM` or `HH:MM` form. Cron expressions use five
numeric fields. Supported cron field syntax is `*`, comma lists, ranges, and
slash steps. Names, `?`, `L`, `W`, `#`, wraparound ranges, and advanced cron
semantics are not supported. Day-of-month and day-of-week both have to match.

## Backup And Restore

Back up these paths together:

- `${XDG_CONFIG_HOME:-~/.config}/yotei/config.toml`
- `${XDG_STATE_HOME:-~/.local/state}/yotei/state.sqlite3`
- `${XDG_STATE_HOME:-~/.local/state}/yotei/logs/`

To restore on another machine:

1. Install `yotei-runner` and make sure the target machine has a working `codex` CLI.
2. Restore the config file, state DB, and logs into the same XDG locations or
   pass `--config` explicitly.
3. Run `yotei status` and repair any task whose stored workspace path is no
   longer valid on the new machine with `yotei edit --workspace`.
4. Start the scheduler with `yotei run` or test one pass with `yotei run --once`.
