Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ steps, project setup, headless operation, and removal.
| --- | --- | --- |
| [docs/architecture.md](docs/architecture.md) | Component responsibilities, a short issue-to-PR overview, configuration ownership, scheduler ownership, and shared state. | Start here to understand how the system is divided before locating implementation code. |
| [docs/bot-workflow.md](docs/bot-workflow.md) | Eight Mermaid diagrams and detailed implementation notes: startup and polling; discovery and routing; task phases; sessions and questions; media helpers; verification and publication; feedback, merging, and tab closure; status, retries, and recovery. Includes links to the source for each area. | Use for exact execution order, state transitions, checkpoint behavior, failure paths, and tracing a bot task from issue to merged PR. |
| [docs/configuration.md](docs/configuration.md) | The standard `.opencode/automation.json` format, defaults, setup flags, configuration tracking across Git branches, authors, triggers, checks, base branches, model capabilities, media helpers, custom prompts, signatures, and auto-merge settings. | Use when adding or changing user-facing configuration, defaults, or setup examples. |
| [docs/configuration.md](docs/configuration.md) | The standard `.opencode/automation.json` format, defaults, setup flags, configuration tracking across Git branches, repository file approvals, authors, triggers, checks, base branches, model capabilities, media helpers, custom prompts, signatures, and auto-merge settings. | Use when adding or changing user-facing configuration, defaults, or setup examples. |
| [docs/runtime.md](docs/runtime.md) | User-visible behavior while the bot runs: GitHub questions and permission replies, branch selection, media inputs, prompt loading, follow-up comments, session tabs, runtime sidebar/status freshness, host repository inventory and discovery, local task closure, cancelling rounds while retaining tracking, and routine management commands. | Use when changing issue conversations, session continuation, runtime tools, or TUI behavior. |
| [docs/advanced.md](docs/advanced.md) | Separate scheduler/dispatcher setup, multiple repositories, custom RPC jobs, full options, timeouts, management and retry commands, persistence, reconciliation, locks, and known limits. | Use for low-level configuration, operational troubleshooting, recovery, or ownership/concurrency changes. |
| [docs/installation.md](docs/installation.md) | Loader registration, config-directory precedence, prerequisites, source installation, project-local installation, upgrade conflicts, testing on another machine, and migration limits. | Use when working on packaging, installers, registration, upgrades, or deployment troubleshooting. |
Expand Down Expand Up @@ -72,6 +72,8 @@ the installation block without making remote writes. Keep its markers intact.
- `src/runtime.ts`, `src/worker.ts`, and `src/bridge.ts` implement worker hooks,
runtime installation, and communication with the owner. `src/prompt.ts` loads
instructions; `prompts/bot.md` contains the bundled bot instructions.
`src/repository-permissions.ts` checks canonical path boundaries for opt-in
repository file approvals; `src/runtime.ts` applies them only to bot sessions.
- `src/tui.ts`, `src/ui.ts`, and `src/activity.ts` implement terminal integration
and task activity. `src/sidebar.ts` renders the runtime panel;
`src/runtime-panel.ts` owns polling, freshness and presentation;
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,12 @@ include the full version, for example `## 0.7.0-beta.1`.

### Added

- Per-repository `autoApproveRepositoryFiles` configuration, an opt-in setup
prompt, and `init --auto-approve-repository-files`. Bot sessions and native
workers can automatically access files in the repository and assigned worktree
across rounds/restarts, without global permission changes. Explicit denials,
pending questions, media-helper limits, and shell permissions remain unchanged.

- Cancel a single bot round while retaining issue/PR tracking, with durable stop
recovery, preserved draft worktrees and fresh worktrees for later feedback.
Resume tracking a locally closed task without replaying its abandoned round
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,14 @@ and respects `XDG_CONFIG_HOME` and `OPENCODE_CONFIG_DIR`.
3. Load the project with the [headless command below](#run-without-the-tui), or
open it with `opencode2 /absolute/path/to/your-project`.

The wizard can enable automatic file access within this repository and its bot
worktrees. Choose **yes** at the file-access prompt, or pass
`--auto-approve-repository-files` to `init`. Existing projects can set
`"autoApproveRepositoryFiles": true` in their configuration. This does not change
global permissions or approve arbitrary shell commands. See
[repository file approvals](docs/configuration.md#repository-file-approvals) for
scope, restart steps, and pending questions.

Settings are saved to `/absolute/path/to/your-project/.opencode/automation.json`.
If it already exists, edit it directly and skip `init`. Repeat setup for each
repository; the plugin is installed only once. After editing settings, restart
Expand Down
6 changes: 4 additions & 2 deletions docs/advanced.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ OpenCode service must be running for polling to work.
| `ownerDirectory` | Absolute path of the checkout that owns automation. Worker worktrees do not activate another scheduler. |
| `stateDirectory` | Shared location for queues, locks, and worktrees. Keep it consistent across components and restarts. |
| `repositories` | Repositories with existing local checkouts, default base branches, allowed authors, and checks. A natural-language request can override the base before work starts. |
| `repositories[].autoApproveRepositoryFiles` | Opt-in file-access approval for that repository and the assigned task worktree, including worktrees outside the checkout. Defaults off; does not approve shell commands. |
| `allowedAuthors` | GitHub users authorized to request work and approve merging. Merging also requires repository write access. |
| `checks` | Arrays of executable arguments, e.g. `[["npm", "test"]]`. `[]` skips dispatcher test commands; the PR distinguishes this from agent-reported tests. No implicit shell. |
| `routes` | Maps full mentions to agents and models available in OpenCode. |
Expand All @@ -47,8 +48,9 @@ OpenCode service must be running for polling to work.
Other plugins can expose idempotent RPC methods for custom scheduler jobs. A
transport timeout does not prove the server never executed a request.

The executor uses the configured OpenCode permissions. Interactive permission
requests are posted to the issue and suspend the task. An authorized author must
The executor uses the configured OpenCode permissions. The optional repository
file policy handles eligible `ask` decisions first; see
[repository file approvals](configuration.md#repository-file-approvals). Remaining requests are posted to the issue and suspend the task. An authorized author must
reply with the exact `/allow QUESTION_ID` or `/deny QUESTION_ID` command. Explicit
OpenCode deny rules remain. Install project dependencies before running it or
include suitable setup commands in your checks.
Expand Down
4 changes: 4 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ The easy setup writes a per-project `.opencode/automation.json`. Global plugin
loaders remain inactive in projects without configuration. Account defaults come
from GitHub authentication, while the wizard queries OpenCode for a model default.
User-configured values are preserved rather than replaced during upgrades.
An opt-in repository file-access policy is stored in that project's configuration
and passed to its worktree runtime. Permission hooks apply it to associated bot
sessions and native workers using canonical paths, while retaining explicit
denials, shell rules, and the media helper's no-tools restriction.

The primary checkout owns scheduling. Worker worktrees do not start additional
schedulers. A shared Git-directory state folder holds the queue and locks; separate
Expand Down
26 changes: 23 additions & 3 deletions docs/bot-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ flowchart TD
Owner -->|Yes| Config[Use nonempty plugin options or read .opencode/automation.json]
Config -->|No project config| Inactive
Config --> Register[Register primary checkout in local user inventory without activating other owners]
Register --> Resolve[Validate settings and resolve GitHub auth, routes and defaults]
Register --> Resolve[Validate repository file policy and resolve GitHub auth, routes and defaults]
Resolve --> Metadata[Register resolved repositories and base branches]
Metadata --> GH[Acquire github lock and load queue.json]
GH --> RPC[Register runtime bridge and dispatcher RPC]
Expand Down Expand Up @@ -297,7 +297,20 @@ sequenceDiagram
end
end
D->>S: Wait for completion
opt Clarification or permission required
opt OpenCode permission evaluation
S->>R: Permission action, resources and current effect
alt Explicit OpenCode allow or deny
R-->>S: Preserve effect
else Exact saved decision for this main session
R-->>S: Apply saved allow or deny
else Repo file policy enabled, eligible action and all paths inside repo or task worktree
Note over R: Resolve canonical paths, exclude media helpers and unanswered questions
R-->>S: Allow file access without an issue question
else Permission still needs approval
R-->>S: Deny this attempt and use the question flow below
end
end
opt Clarification or remaining permission question
S->>R: ask_issue / intercepted question / permission ask
R->>D: Register against main task session
D->>D: Persist pending question
Expand Down Expand Up @@ -357,6 +370,13 @@ sequenceDiagram
phases or enforced review gates. The executor's `verifying` phase remains
separate. A prose blocker in the final summary does not set `blocked` status;
user-input blockers must go through `ask_issue`.
- `autoApproveRepositoryFiles` is a repository opt-in, carried in the generated
worktree runtime settings. It handles `external_directory`, `read`, and `edit`
requests for canonical paths within the configured checkout or assigned
worktree. Native workers inherit task association through parent lookup; new
rounds use the same repository policy. Explicit denials, pending questions,
media-helper restrictions, and shell rules are unchanged. See
[configuration and reload behavior](configuration.md#repository-file-approvals).
- One unresolved question is retained at a time. Runtime hooks remove tools and
reject non-question tool execution while a question is pending. Native subagent
questions are attached to the main task; the reply resumes the main session.
Expand Down Expand Up @@ -389,7 +409,7 @@ sequenceDiagram
dispatcher advances to `verifying`.

Sources: [executor.ts — runSession](../src/executor.ts),
[runtime.ts](../src/runtime.ts), [prompt.ts](../src/prompt.ts),
[runtime.ts](../src/runtime.ts), [repository-permissions.ts](../src/repository-permissions.ts), [prompt.ts](../src/prompt.ts),
[dispatcher.ts — workOnce, question, publishQuestion, restartWorkflow](../src/dispatcher.ts).

## 5. Optional media inspection
Expand Down
45 changes: 44 additions & 1 deletion docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Use a model available in your own OpenCode 2 installation. Optional fields:

| Field | Purpose |
| --- | --- |
| `autoApproveRepositoryFiles` | Opt-in automatic `external_directory`, `read`, and `edit` approval within this repository and its assigned task worktree; omitted/`false` keeps existing permission behavior. Shell rules are unchanged. |
| `baseBranch` | Base for new worktrees and PRs; defaults to the GitHub default branch. |
| `capabilities` | Main model support: `text`, `vision`, `audio`; defaults to `["text"]`. |
| `mediaModel` | Separate helper model and its capabilities; example below. |
Expand Down Expand Up @@ -80,11 +81,53 @@ cd /absolute/path/to/your-project
"$HOME/.local/bin/opencode2-automation" init --model provider/model --skip-tests --yes
```

Optional flags: `--base-branch develop`, `--capabilities text`,
Optional flags: `--auto-approve-repository-files`, `--base-branch develop`, `--capabilities text`,
`--media-model provider/vision-model`, `--media-capabilities text,vision`,
`--system-prompt .opencode/bot.md`. With `--yes`, supply a helper explicitly
if you want media support with a text-only main model.

## Repository file approvals

During interactive `init`, choose **yes** for "Automatically approve file access
in this repository and its task worktrees". The default is **no**. Noninteractive
setup opts in with `--auto-approve-repository-files`; `--yes` alone does not enable it.
For an existing project, add this field to its existing `.opencode/automation.json`
without rerunning `init` or replacing the other settings:

```json
{
"model": "provider/model",
"autoApproveRepositoryFiles": true
}
```

This is a plugin setting for this repository, not a global OpenCode permission.
The runtime resolves the configured checkout path automatically, and also includes
the assigned task worktree when advanced state storage places it outside the
checkout. The policy follows new rounds and native subagents through their main
task; it is not tied to a previous session's `/allow` reply. Other repositories
and ordinary non-bot sessions are unaffected. No user-wide configuration is written.

Only `ask` decisions for `external_directory`, `read`, and `edit` qualify. Every
resource must resolve inside the repository or assigned worktree. Symlink targets
are checked, including existing parents of new files. Paths to siblings, symlink
escapes, unknown patterns, and unresolvable boundaries use the normal approval
flow. Explicit OpenCode denials and exact saved `/deny` decisions remain effective.
Media helpers remain read-only and cannot use tools. Shell, network, subagent
launch, and other action permissions are unchanged; a shell command can affect
files outside its working directory, so its location does not grant blanket consent.

After changing the setting, restart the idle service and activate the owner again.
The executor refreshes generated worktree runtime settings before continuing a
saved session. An already-pending permission question still needs its exact
`/allow QUESTION_ID` or `/deny QUESTION_ID` reply; enabling this option does not
answer it or bypass unrelated pending questions. Existing configs remain opt-out.
Set the field to `false` and reload to disable automatic file approval; previously
saved explicit approvals still have their original session scope.

The model must still build, edit, and test in its assigned worktree. Permission to
access another checkout does not make that checkout the correct validation target.

## Questions, branches, media, and bot instructions

- **Questions:** reply in the issue as an account in `authors`; no repeated
Expand Down
3 changes: 2 additions & 1 deletion docs/installation.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ GitHub user, not the repository owner. The model default is queried from the run
OpenCode service; without it, the model is required. Command-line flags override
prompts. `--skip-tests` explicitly disables tests; Enter otherwise accepts the
shown test command or `skip`.
The wizard also asks for model capabilities, a vision helper if the main model
The wizard also asks whether to auto-approve repository file access (default no),
plus model capabilities, a vision helper if the main model
lacks vision, and the base branch. Existing JSON files can be extended manually;
see [runtime settings](runtime.md).

Expand Down
10 changes: 9 additions & 1 deletion docs/runtime.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,15 @@ not by excluding the posting account's login. GitHub Bot accounts and unauthoriz
authors are also excluded. A regular comment from the shared account can answer
a question; the bot's own marked question, acknowledgement, or other post cannot.

For permission requests, use the exact `/allow QUESTION_ID` or `/deny QUESTION_ID`
When `autoApproveRepositoryFiles` is enabled for the repository, the runtime
approves eligible file-access requests within the checkout and assigned worktree,
including native subagents and later rounds. It still honors explicit denials and
does not grant shell permissions or tools to media helpers. A new session does not
need to repeat `/allow` for these eligible file requests. Existing unanswered
questions still require their explicit replies. See
[repository file approvals](configuration.md#repository-file-approvals).

For permission requests that still require a reply, use the exact `/allow QUESTION_ID` or `/deny QUESTION_ID`
shown in the question as your entire reply. Plain conversation does not grant
permission. The decision is scoped to the operation and resource set in the
current main session and its workers; explicit OpenCode deny rules still apply.
Expand Down
6 changes: 6 additions & 0 deletions prompts/bot.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,9 @@ repository inspection in the implementation session.
- Preserve existing work, including changes from earlier rounds. Inspect the
current state before editing; do not assume a fresh checkout.
- Work only in the assigned worktree and retain its branch and pinned base.
"Repository root" means that worktree's root for edits, builds, and tests.
Do not substitute the primary checkout's build or executable. Automatic file
permission within the repository does not change the assigned worktree.
- Do not switch branches, push, merge, open PRs, or post directly to GitHub.
The dispatcher owns publication and appends the configured message signature.
- Do not change automation configuration, credentials, or permissions merely
Expand All @@ -57,6 +60,9 @@ repository inspection in the implementation session.
verification, or delegation while waiting.
- A delegated worker that asks must return control to the main agent.
The dispatcher delivers the reply to the main session.
- A repository may enable automatic file-access approval for its checkout and
assigned worktree. The runtime applies it; do not ask again for an operation
it already permits. This does not grant blanket shell approval or bypass denials.
- If permission approval is pending, stop. Never bypass a denied operation.
Approval requires an authorized user's exact `/allow QUESTION_ID` or
`/deny QUESTION_ID` reply in the issue. Never supply that approval yourself.
Expand Down
1 change: 1 addition & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export type Route = z.infer<typeof Route>;
export const Repository = z.object({
repo: z.string().regex(/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/),
directory: absolute,
autoApproveRepositoryFiles: z.boolean().optional(),
baseBranch: BranchName,
allowedAuthors: z.array(name).min(1),
checks: z.array(z.array(z.string().min(1)).min(1)),
Expand Down
3 changes: 2 additions & 1 deletion src/easy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { z } from "zod";
import { GithubOptions, SchedulerOptions, MergeOptions, Capabilities, BranchName } from "./config.js";

export const EasyOptions = z.object({
autoApproveRepositoryFiles: z.boolean().optional(),
baseBranch: BranchName.optional(),
capabilities: Capabilities.optional(),
mediaModel: z.object({ model: z.string().regex(/^[^/\s]+\/\S+$/), capabilities: Capabilities }).strict().optional(),
Expand Down Expand Up @@ -80,7 +81,7 @@ export async function resolveEasy(directory: string, raw: unknown, execute = run
const slash = options.model.indexOf("/");
const stateDirectory = join(common, "opencode2-automation");
const github = GithubOptions.parse({ systemPromptFile: options.systemPromptFile, signature: options.signature ?? `${login}[OpenCode2]`, autoMerge: options.autoMerge, ownerDirectory: root, stateDirectory,
repositories: [{ repo, directory: root, baseBranch, allowedAuthors: options.authors ?? [login], checks: check === false ? [] : [check] }],
repositories: [{ repo, directory: root, autoApproveRepositoryFiles: options.autoApproveRepositoryFiles, baseBranch, allowedAuthors: options.authors ?? [login], checks: check === false ? [] : [check] }],
routes: { [options.trigger]: { agent: "build", capabilities: options.capabilities, mediaModel: options.mediaModel ? { capabilities: options.mediaModel.capabilities, model: { providerID: options.mediaModel.model.split("/")[0], id: options.mediaModel.model.slice(options.mediaModel.model.indexOf("/") + 1) } } : undefined, model: { providerID: options.model.slice(0, slash), id: options.model.slice(slash + 1) } } },
});
const scheduler = SchedulerOptions.parse({ ownerDirectory: root, stateDirectory, jobs: [{ id: "github-issues", everySeconds: options.everySeconds }] });
Expand Down
Loading