diff --git a/opencode.json b/opencode.json new file mode 100644 index 0000000..1326c5d --- /dev/null +++ b/opencode.json @@ -0,0 +1,57 @@ +{ + "$schema": "https://opencode.ai/config.json", + "autoupdate": false, + "share": "disabled", + "permission": { + "external_directory": { + "*": "deny", + "~/.local/share/opencode/tool-output/*": "allow", + "/var/folders/*/T/opencode/*": "allow", + "/tmp/opencode/*": "allow" + }, + "webfetch": "deny", + "websearch": "deny", + "question": "deny", + "task": "deny", + "doom_loop": "deny", + "edit": { + "*": "allow", + "AGENTS.md": "deny", + "CLAUDE.md": "deny", + "opencode.json": "deny", + "docs/design.md": "deny", + "docs/decisions.md": "deny", + "docs/inference-contract.md": "deny", + "docs/specs/**": "deny", + "docs/plans/**": "deny", + "tools/**": "deny" + }, + "bash": { + "*": "allow", + "git push*": "deny", + "git reset*": "deny", + "git rebase*": "deny", + "git commit --amend*": "deny", + "git switch*": "deny", + "git checkout*": "deny", + "find /*": "deny", + "locate *": "deny", + "sudo *": "deny", + "rm -rf /*": "deny", + "curl *": "deny", + "wget *": "deny" + } + }, + "experimental": { + "continue_loop_on_deny": true + }, + "agent": { + "implement": { + "description": "Implements exactly one closed task file from docs/plans, then commits and logs it.", + "mode": "primary", + "temperature": 0.6, + "top_p": 0.95, + "steps": 400 + } + } +} diff --git a/tools/run-plan.sh b/tools/run-plan.sh index 03d161d..9a34b3c 100755 --- a/tools/run-plan.sh +++ b/tools/run-plan.sh @@ -8,7 +8,9 @@ # OPENCODE_MODEL model to use (default straylight/laguna-s-2.1; OpenCode's own default differs). # The part after the provider (`ornith-1.5-35b-a3b`) names the model in each # commit's `Co-Authored-By: ` trailer and in the log. -# OPENCODE_FLAGS extra flags for `opencode run`, for example "--pure" or "--auto" +# OPENCODE_FLAGS extra flags for `opencode run`, for example "--pure" +# Each session runs as the `implement` agent of ./opencode.json: its permissions deny edits to the +# plans and design documents, reads outside the repository, subagents and pushes. # DRY_RUN=1 print what would be run and stop set -eu @@ -32,7 +34,7 @@ for task in "$plan"/[0-9][0-9]-*.md; do prompt="Read \`$task\` and do exactly that task. You are $agent: write $agent in the log row's Model column, and end the commit message with this line, exactly: $trailer" if [ "${DRY_RUN:-0}" = 1 ]; then - echo "opencode run -m $model ${OPENCODE_FLAGS:-} --title $milestone/$name \"$prompt\"" + echo "opencode run --agent implement -m $model ${OPENCODE_FLAGS:-} --title $milestone/$name \"$prompt\"" continue fi if [ -n "$(git status --porcelain)" ]; then @@ -44,7 +46,7 @@ for task in "$plan"/[0-9][0-9]-*.md; do echo "=== $milestone/$name: started $(date '+%H:%M:%S')" # A new `opencode run` is a new session: nothing carries over from the task before. # shellcheck disable=SC2086 - if ! opencode run -m "$model" ${OPENCODE_FLAGS:-} --title "$milestone/$name" "$prompt" \ + if ! opencode run --agent implement -m "$model" ${OPENCODE_FLAGS:-} --title "$milestone/$name" "$prompt" \ > "$runs/$name.log" 2>&1; then echo "run-plan: opencode exited with an error in $name; see $runs/$name.log" >&2 exit 1