# CLI, Skill & Runtime Packaging

> The command-line and skill-facing workflow: NPX command parsing, API key handling, Python runner setup, virtual GitHub review execution, release runtime caching, and portable skill invocation.

- Repository: AsyncFuncAI/AsyncReview
- GitHub: https://github.com/AsyncFuncAI/AsyncReview
- Human wiki: https://grok-wiki.com/public/wiki/asyncfuncai-asyncreview-fb80a82d8c3a
- Complete Markdown: https://grok-wiki.com/public/wiki/asyncfuncai-asyncreview-fb80a82d8c3a/llms-full.txt

## Source Files

- `npx/src/index.ts`
- `npx/src/cli.ts`
- `npx/src/python-runner.ts`
- `npx/src/launcher.ts`
- `cli/main.py`
- `cli/virtual_runner.py`
- `cli/github_fetcher.py`
- `skills/asyncreview/SKILL.md`

---

<details>
<summary>Relevant source files</summary>
The following files were used as context for generating this wiki page:
- [npx/src/index.ts](npx/src/index.ts)
- [npx/src/cli.ts](npx/src/cli.ts)
- [npx/src/api-key.ts](npx/src/api-key.ts)
- [npx/src/python-runner.ts](npx/src/python-runner.ts)
- [npx/src/launcher.ts](npx/src/launcher.ts)
- [npx/package.json](npx/package.json)
- [npx/python/cli/main.py](npx/python/cli/main.py)
- [npx/python/cli/virtual_runner.py](npx/python/cli/virtual_runner.py)
- [npx/python/cli/github_fetcher.py](npx/python/cli/github_fetcher.py)
- [npx/python/cli/local_fetcher.py](npx/python/cli/local_fetcher.py)
- [npx/python/cli/repo_tools.py](npx/python/cli/repo_tools.py)
- [npx/python/cli/local_repo_tools.py](npx/python/cli/local_repo_tools.py)
- [npx/python/cli/output_formatter.py](npx/python/cli/output_formatter.py)
- [scripts/build_runtime_local.sh](scripts/build_runtime_local.sh)
- [skills/asyncreview/SKILL.md](skills/asyncreview/SKILL.md)
- [pyproject.toml](pyproject.toml)
</details>

# CLI, Skill & Runtime Packaging

AsyncReview exposes a command-line review workflow that can be used directly through `npx asyncreview` or indirectly through a portable skill definition. The workflow starts in a small Node.js wrapper, resolves credentials, launches packaged Python review code, and optionally downloads a cached release runtime before handing control to the real CLI.

This page covers the packaging and runtime path rather than the full review intelligence. It focuses on command parsing, API key handling, Python and Deno setup, GitHub and local review execution, release runtime caching, and how the skill file invokes the same command surface.

## Runtime Entry Points

The npm package publishes `asyncreview` as `./dist/launcher.js`, not the direct Commander entrypoint. The launcher reads the package version, detects the host platform, downloads a matching runtime tarball from GitHub Releases when the cached runtime is missing, then executes the cached `bin/asyncreview` with the original arguments. Sources: [npx/package.json:1-18](), [npx/src/launcher.ts:24-30](), [npx/src/launcher.ts:31-87](), [npx/src/launcher.ts:172-211]()

The direct TypeScript CLI still exists inside the runtime. It defines the `review` command and options such as `--url`, `--path`, `--question`, `--expert`, `--output`, `--quiet`, `--model`, `--api`, and `--github-token`. It performs the first mutual-exclusion check for `--url` versus `--path`, then delegates to `runReview`. Sources: [npx/src/index.ts:12-40](), [npx/src/cli.ts:17-30]()

```text
User / skill
  |
  | npx asyncreview review ...
  v
npm bin: dist/launcher.js
  |
  | cache miss: download asyncreview-runtime-v<version>-<platform>.tar.gz
  v
cached runtime/bin/asyncreview
  |
  | exec node app/dist/index.js
  v
Node CLI -> Python module cli.main -> VirtualReviewRunner
```

Sources: [npx/src/launcher.ts:138-170](), [scripts/build_runtime_local.sh:124-171](), [npx/src/python-runner.ts:282-329]()

## Command Options And Dispatch

At the Node layer, `review` accepts either a GitHub URL or local path. `--question` is optional only when `--expert` is supplied, and `--output` defaults to `text`. Sources: [npx/src/index.ts:17-29]()

The packaged Python CLI in `npx/python/cli/main.py` mirrors those review sources with an argparse mutually exclusive group for `--url` and `--path`. It also adds `--submit`, which is valid only for GitHub URL reviews and posts the review result as a GitHub issue/PR comment. Sources: [npx/python/cli/main.py:227-333](), [npx/python/cli/main.py:141-154]()

| Option | Owner | Behavior |
| --- | --- | --- |
| `--url`, `-u` | Node and Python | Review a GitHub PR or issue URL. |
| `--path`, `-p` | Node and Python | Review a local directory. |
| `--question`, `-q` | Node and Python | Supplies the user question; optional with `--expert`. |
| `--expert` | Node and Python | Replaces or augments the question with an expert review prompt. |
| `--output`, `-o` | Node and Python | Selects `text`, `markdown`, or `json`. |
| `--quiet` | Node and Python | Suppresses progress output and prints raw final output. |
| `--model`, `-m` | Node and Python | Overrides the configured model name. |
| `--api` | Node only | Supplies the Gemini API key before spawning Python. |
| `--github-token` | Node only | Supplies the GitHub token before spawning Python. |
| `--submit` | Python packaged CLI | Posts the final result back to GitHub; URL mode only. |

Sources: [npx/src/index.ts:17-29](), [npx/python/cli/main.py:254-301](), [npx/python/cli/main.py:305-330]()

## Credential Resolution

The Node CLI resolves credentials before invoking Python. `getApiKey` checks `--api`, then `GEMINI_API_KEY`, then prompts interactively for a Gemini API key. `getGitHubToken` checks `--github-token`, then `GITHUB_TOKEN`, then `gh auth token`, and finally prompts if a token is required. Sources: [npx/src/api-key.ts:8-42](), [npx/src/api-key.ts:44-102]()

`runReview` always resolves an API key, but only resolves a GitHub token when URL mode is used. The resolved values are passed into the Python process through environment variables: `GEMINI_API_KEY` is always set and `GITHUB_TOKEN` is included only when available. Sources: [npx/src/cli.ts:29-64](), [npx/src/python-runner.ts:318-327]()

This implementation is BYOK in the practical sense: callers can provide their own model API key and GitHub token through flags, environment variables, or the GitHub CLI. The current code is not provider-neutral at the model layer, because both the credential name and DSPy model prefix are Gemini-specific. A future provider-neutral extension would need to abstract `GEMINI_API_KEY` and the `gemini/` model prefix rather than only changing the skill file. Sources: [npx/src/api-key.ts:14-24](), [npx/python/cli/virtual_runner.py:201-218]()

## Python Runner Setup

`npx/src/python-runner.ts` owns the bridge from Node to Python. It computes `NPX_ROOT`, detects bundled mode by checking for a sibling `pydeps/` directory, and builds `PYTHONPATH` from bundled dependencies plus `app/python` or from the local `npx/python` tree in development. Sources: [npx/src/python-runner.ts:12-31]()

The runner prefers a local virtual environment Python at `npx/.venv/bin/python`, then falls back to `python3` or `python` if the version is Python 3.11 or newer. It can create a virtual environment and install the Python package with `pip install .`, although the main execution path chooses the venv Python only when `cli.main`, `rich`, and `dspy` import successfully. Sources: [npx/src/python-runner.ts:44-82](), [npx/src/python-runner.ts:163-178](), [npx/src/python-runner.ts:186-245](), [npx/src/python-runner.ts:268-281]()

The spawned command is:

```text
python -m cli.main review [--url URL | --path PATH] --output FORMAT [-q QUESTION] [--expert] [--quiet] [--model MODEL]
```

Sources: [npx/src/python-runner.ts:282-313]()

The Python package metadata declares Python `>=3.11`, exposes `asyncreview = "cli.main:main"`, and packages both `cr` and `cli`. Sources: [pyproject.toml:1-7](), [pyproject.toml:32-41]()

## GitHub Review Execution

For GitHub reviews, the packaged Python CLI validates the URL, selects either the expert prompt or the supplied question, constructs a `VirtualReviewRunner`, and calls `runner.review(url, actual_question)`. Sources: [npx/python/cli/main.py:70-118]()

GitHub URL parsing supports pull request URLs and issue URLs. PR fetching collects metadata, changed files with patches, commits, discussion comments, branch information, and the PR head SHA. Issue fetching collects metadata, labels, body, and comments. Sources: [npx/python/cli/github_fetcher.py:15-48](), [npx/python/cli/github_fetcher.py:62-158](), [npx/python/cli/github_fetcher.py:161-202]()

`VirtualReviewRunner.review` creates `RepoTools(owner, repo, head_sha)` so follow-up file reads use the PR head commit for consistency. It then builds the review context and runs DSPy RLM asynchronously with a tool set for fetching files, listing directories, and searching code. Sources: [npx/python/cli/virtual_runner.py:225-285](), [npx/python/cli/virtual_runner.py:93-182]()

`RepoTools` uses the GitHub API with optional token authorization, path sanitization, bounded concurrency, simple retry/backoff for rate limits, a small file cache, a 200 KB per-file limit, and explicit skip/error stubs for inaccessible or unsupported files. Sources: [npx/python/cli/repo_tools.py:13-35](), [npx/python/cli/repo_tools.py:40-51](), [npx/python/cli/repo_tools.py:85-115](), [npx/python/cli/repo_tools.py:136-215]()

## Local Directory Review

Local review uses the same RLM runner but swaps the repository tools. The Python CLI validates the local path, builds either an expert prompt or the supplied question, then calls `runner.review_local(abs_path, actual_question)`. Sources: [npx/python/cli/main.py:157-224]()

`build_local_context` includes the absolute path, git branch and repository name when available, recent commits, staged and unstaged diff content, and a bounded project structure. The diff is truncated at 100 KB. Sources: [npx/python/cli/local_fetcher.py:9-24](), [npx/python/cli/local_fetcher.py:53-95](), [npx/python/cli/local_fetcher.py:98-155](), [npx/python/cli/local_fetcher.py:235-295]()

`LocalRepoTools` implements the same fetch/list/search interface against the filesystem. It resolves paths under the configured root, rejects traversal outside that root, skips common generated directories, caps file reads at the shared 200 KB limit, and searches text-like source extensions with `grep` using an argument list instead of `shell=True`. Sources: [npx/python/cli/local_repo_tools.py:36-65](), [npx/python/cli/local_repo_tools.py:67-98](), [npx/python/cli/local_repo_tools.py:99-150](), [npx/python/cli/local_repo_tools.py:152-198]()

## RLM Configuration And Output

The runner configures DSPy lazily on first use. It prefixes model names with `gemini/` when needed, disables LM caching, builds a Deno-backed Python interpreter command, and passes custom tools into `dspy.RLM` with the signature `context, question -> answer, sources`. Sources: [npx/python/cli/virtual_runner.py:184-223]()

The CLI formats output after the runner returns `answer`, `sources`, and metadata. Text output is plain terminal text, markdown output includes a `### Sources` section for embedding in docs or skills, and JSON output contains `answer`, `sources`, `model`, and optional metadata. Sources: [npx/python/cli/main.py:120-139](), [npx/python/cli/main.py:205-224](), [npx/python/cli/output_formatter.py:7-23](), [npx/python/cli/output_formatter.py:26-43](), [npx/python/cli/output_formatter.py:46-65]()

## Release Runtime Caching

The release runtime is a platform-specific tarball named `asyncreview-runtime-v<version>-<platform>.tar.gz`. The launcher maps OS and CPU architecture to keys such as `darwin-arm64`, `linux-x64`, or `windows-x64`, then stores extracted runtimes below the user cache directory by version and platform. Sources: [npx/src/launcher.ts:31-75](), [npx/src/launcher.ts:138-163]()

The local build script stages a self-contained runtime by compiling the TypeScript runtime entrypoint, copying `npx/python/cli` and `npx/python/cr`, installing production Node dependencies, installing Python dependencies into `pydeps/`, downloading a Deno binary, and writing `bin/asyncreview`. Sources: [scripts/build_runtime_local.sh:38-71](), [scripts/build_runtime_local.sh:73-123](), [scripts/build_runtime_local.sh:124-171]()

At runtime, `bin/asyncreview` adds bundled Deno to `PATH`, sets `PYTHONPATH` to `pydeps` and `app/python`, sets `DENO_DIR`, verifies Python dependencies once, optionally reinstalls them for the user's Python version, and finally executes `node app/dist/index.js`. Sources: [scripts/build_runtime_local.sh:130-170]()

## Skill-Facing Invocation

The repository includes a portable skill definition at `skills/asyncreview/SKILL.md`. Its frontmatter describes the skill as AI-powered GitHub PR/Issue review and restricts the tool surface to `Bash(npx asyncreview:*)`, which keeps skill invocation file-based and portable across agents that understand this skill format. Sources: [skills/asyncreview/SKILL.md:1-5]()

The skill workflow tells an agent to check `GEMINI_API_KEY`, determine whether the repository is private, set `GITHUB_TOKEN` when needed, formulate a specific question, and run `npx asyncreview review --url <URL> -q "<question>"`. It documents PR and issue URL formats, output formats, private repository handling, and expert review examples. Sources: [skills/asyncreview/SKILL.md:19-28](), [skills/asyncreview/SKILL.md:31-45](), [skills/asyncreview/SKILL.md:47-83](), [skills/asyncreview/SKILL.md:144-178](), [skills/asyncreview/SKILL.md:254-283]()

For Grok-Wiki or other catalog-style integrations, the portable boundary should remain the skill file plus the `npx asyncreview` command. The skill source can come from a local file, repository, or catalog entry without changing the runtime contract. Provider neutrality at that integration layer means the catalog should not assume a hosted connector or a specific model provider; however, this repository's current executable implementation still expects Gemini credentials and Gemini-style model names. Sources: [skills/asyncreview/SKILL.md:1-5](), [npx/src/api-key.ts:8-24](), [npx/python/cli/virtual_runner.py:201-218]()

## Notes On Repository Context

No `STRATEGY.md` or `docs/solutions/**` sources were present in the inspected checkout, so this page uses repository code and the local AsyncReview skill file as the source of truth. The root `cli/main.py` exists, but the Node runner's `PYTHON_CODE_ROOT` points to `npx/python`, and the runtime build script copies `npx/python/cli` and `npx/python/cr`; therefore packaged runtime behavior should be verified against `npx/python/cli/*`. Sources: [npx/src/python-runner.ts:12-21](), [scripts/build_runtime_local.sh:65-71]()

## Summary

AsyncReview packages a skill-friendly review workflow as a small npm launcher, a cached platform runtime, and a Python DSPy runner. The command surface is intentionally simple for users and agents, while the runtime handles credential resolution, Python path setup, GitHub or local context assembly, agentic repository exploration tools, and output formatting. The portable skill layer is file-based, but the current model execution path is Gemini-specific and would need an explicit provider abstraction to become fully provider-neutral. Sources: [npx/src/launcher.ts:172-211](), [npx/src/python-runner.ts:315-329](), [npx/python/cli/virtual_runner.py:184-223](), [skills/asyncreview/SKILL.md:19-28]()
