oy/lib.rs
1//! # oy
2//!
3//! `oy` adds one concise autonomous agent and repeatable repository audit/review workflows to
4//! [OpenCode 2](https://opencode.ai/). Its current local MCP adapter provides deterministic
5//! repository collection, ordered chunks, target-diff input, and normalized Markdown/SARIF
6//! reports while the project moves toward file-backed CLI evidence. OpenCode remains responsible
7//! for model execution, providers, authenticated sessions, permissions, and general coding tools.
8//! oy does not store provider credentials.
9//! The native CLI supports Linux and macOS; Windows users should run it in WSL2.
10//!
11//! ## Start with the CLI
12//!
13//! The command-line interface is the supported automation surface:
14//!
15//! ```text
16//! oy setup --dry-run # preview package/config migration
17//! oy setup # register the version-matched OpenCode package
18//! oy audit # write ISSUES.md
19//! oy review main # write REVIEW.md for git diff main
20//! oy enhance <finding-id> # remediate one reported finding
21//! ```
22//!
23//! See the [getting-started guide](https://oy.adonm.dev/getting-started.html),
24//! [workflow guide](https://oy.adonm.dev/workflows.html), and
25//! [CLI/MCP reference](https://oy.adonm.dev/reference.html) for the user-facing
26//! contract.
27//!
28//! ## Determinism boundary
29//!
30//! Input collection, ordering, limits, and report rendering are deterministic. Findings are
31//! produced by the model selected in opencode and are not deterministic. The collector also
32//! has documented exclusions; “all chunks” does not mean every byte in a repository.
33//!
34//! ## Rust API
35//!
36//! This crate exists primarily to keep the `oy` binary entrypoint small. [`run`] and
37//! [`err_line`] are public for that entrypoint and lightweight embedding, but spawning the
38//! `oy` executable is preferred for automation. Other modules and implementation details are
39//! private and may change without a semver-stable library API commitment.
40//!
41//! ```no_run
42//! # async fn example() -> anyhow::Result<()> {
43//! // Arguments exclude the executable name, just like std::env::args().skip(1).
44//! let exit_code = oy::run(vec!["doctor".into(), "--json".into()]).await?;
45//! assert_eq!(exit_code, 0);
46//! # Ok(())
47//! # }
48//! ```
49
50#![recursion_limit = "256"]
51
52mod artifacts;
53mod audit;
54mod cli;
55mod mcp;
56mod opencode;
57mod review;
58mod tools;
59mod workflow;
60
61pub(crate) use cli::{config, ui};
62
63#[derive(Debug, Clone, Copy, PartialEq, Eq)]
64pub(crate) enum TextDecodeError {
65 Binary,
66 NonUtf8,
67}
68
69pub(crate) fn decode_utf8(raw: Vec<u8>) -> Result<String, TextDecodeError> {
70 if raw.contains(&0) {
71 return Err(TextDecodeError::Binary);
72 }
73 String::from_utf8(raw).map_err(|_| TextDecodeError::NonUtf8)
74}
75
76/// Runs the `oy` command dispatcher with arguments that exclude the executable name.
77///
78/// Normal command and delegated opencode exit statuses are returned as `Ok(code)`. Setup,
79/// filesystem, process-launch, and protocol failures are returned as errors. This function may
80/// update opencode integration config or launch child processes depending on the arguments.
81///
82/// Prefer invoking the `oy` executable when process isolation or concurrent invocations matter;
83/// CLI output configuration is process-global.
84pub async fn run(argv: Vec<String>) -> anyhow::Result<i32> {
85 cli::app::run(argv).await
86}
87
88/// Writes a formatted diagnostic line to standard error.
89///
90/// This is primarily exposed for the binary entrypoint.
91///
92/// ```
93/// oy::err_line(format_args!("error: {}", "example"));
94/// ```
95pub fn err_line(args: std::fmt::Arguments<'_>) {
96 ui::err_line(args);
97}