feat: customization tool + clearer skill, ready for v0.7.0#424
Conversation
Make customization concrete and verifiable instead of guesswork:
- CONFIG_DOCS: a single { group, summary } metadata source for every config
key + envVarFor(); a test keeps it in lockstep with DEFAULT_CONFIG (no drift).
- scripts/goal-config.mjs (bin: opencode-goal-mode-config): list / explain <key>
/ recipes / recipe <name> / effective '<json>' [--diff] — discover keys, get
the exact opencode.json/env snippet, and preview the resolved config.
- 8 tests covering coverage, env-var naming, and every subcommand.
….7.0 - Rewrote the goal-mode-customization skill and /goal-mode-customize command into a tight discover -> choose -> apply -> verify loop driven by goal-config, with a request->key map and YOLO precedence rule. Much easier for the agent to act on. - README: point at goal-config (list/recipe/effective). - Ship the tool: scripts/goal-config.mjs in files + a third bin + a 'config' script. - Bump to v0.7.0 with a CHANGELOG entry (YOLO + customization tooling + the ~190-issue hardening sweep since 0.6.12); sync package-lock.
devinoldenburg
left a comment
There was a problem hiding this comment.
Review: APPROVE (recommend merge)
I reviewed this rigorously and ran the full verification suite locally on feat/v0.7.0-customization-tooling. No blocking defects found. (Posting as a comment since GitHub blocks --approve on one's own PR — this is an approving recommendation.)
What I verified
1. goal-config.mjs correctness — all subcommands work, exit codes correct
list,explain <key>,recipes,recipe <name>,effective '<json>' [--diff]all produce correct output.- Exit non-zero on bad input:
explain badkey→ 1,recipe nope→ 1,effective '{not json'→ 1, unknown subcommand → 1. Bareeffective(no opts) and bare invocation print help and exit 0 (intended). effective --diffcorrectly shows only changed-from-default; cross-checked againstresolveConfig: full-yolo{yolo,allowDestructive}→blockDestructive/blockNetworkExec/enforceCompletion/restrictSubagents/toastOnBlockall false; safe-yolo keepsblockDestructive=true. MatchesresolveConfig's relax() set exactly (config.js:270-278).- Printed
opencode.json/env snippets are valid and reproduce the claimed effect — re-ran each emittedeffectiveverify command and theGOAL_GUARD_ALLOW_COMMANDS="...,..."env-string form; both resolve as advertised. - Global-bin path: the
../plugins/goal-guard/config.jsimport resolves against the script's own URL, not cwd — ran the bin from/tmp(exit 0). Bothplugins/goal-guard/config.jsandscripts/goal-config.mjsare infiles/npm packoutput, so the relative import holds in an installed global package.
2. CONFIG_DOCS no-drift — covers exactly DEFAULT_CONFIG (30 keys); the test (goal-config.test.mjs:11-19) is a genuine independent cross-check, not tautological. envVarFor is consistent with every fromEnv mapping; spot-checked sessionTtlMs→GOAL_GUARD_SESSION_TTL_MS, blockDestructive, allowCommands.
3. Skill/command accuracy — recipes and the request→key map are correct against real semantics:
- YOLO precedence claim is true:
effective '{"yolo":true,"blockNetworkExec":true}' --diffdoes NOT relaxblockNetworkExec(explicit override wins, config.js:267-269). allowCommandsvsextraDestructivematch guard.js:582-586 — allow-list bypasses the guard entirely (!allowlistedgates both block paths), deny-list extends the destructive classification. Skill wording ("never blocked, whatever the analyzer thinks" / "treated as destructive") is accurate.- full-yolo =
{yolo:true, allowDestructive:true}correctly documented as the only way to drop destructive guarding.
4. Release bump — package.json and package-lock.json both at 0.7.0. node scripts/release-notes.mjs reads package.json version, finds ## v0.7.0, emits a 37-line non-empty section, exits 0. CHANGELOG heading is ## v0.7.0. exports/bin/validator contract intact — validate-opencode-config.mjs passes; the new opencode-goal-mode-config bin points at a packed file.
5. Tests + tooling green
node --test: 669 pass / 0 fail (full suite, including the new goal-config.test.mjs and unchanged config.test.mjs).npm run lint(biome): exit 0; the 3 changed JS files lint clean. The warnings/infos shown are pre-existing across the tree, not introduced here.npm pack --dry-run: exit 0, ships goal-config.mjs + config.js.
Nits (non-blocking)
scripts/goal-config.mjs:60exampleValuefor list keys hardcodes a sample (["^docker compose ", "^rm -rf \\./tmp/"]) rather than echoing the key's actual default — harmless since list defaults are all[], and a concrete example is arguably more useful. No action needed.- The
strictrecipe resolves to "No effective change from defaults" undereffective --diff(it spells out the defaults). Correct and intentional, but a reader running the verify line might briefly expect output. Optional: a one-word note in the recipe blurb. Non-blocking.
Solid, well-tested PR. Recommend merge.
Makes Goal Mode customization actually easy and powerful, and cuts the release to v0.7.0.
What's better
goal-configtool (scripts/goal-config.mjs, binopencode-goal-mode-config,npm run config): the agent/user can now discover → apply → verify instead of guessing.list— every key with type, default, env var, and what it does.explain <key>— the exactopencode.json+ env snippet to set it, and how to verify.recipes/recipe <name>— paste-ready presets (safe-yolo,full-yolo,allow-commands,block-extra,quiet,no-auto-review,strict).effective '<json>' [--diff]— preview the resolved config before committing.CONFIG_DOCSis the single doc source for every key; a test keeps it in lockstep withDEFAULT_CONFIGso docs can never drift./goal-mode-customizecommand into a tight, tool-driven discover → choose → apply → verify loop with a request→key map and the YOLO precedence rule spelled out — far easier for the agent to act on than the previous prose.Release
package-locksynced.Verification
node --test "tests/*.test.mjs"→ 668 pass, 0 fail (8 new goal-config tests).npm run lintclean;validate-opencode-config.mjspasses;release-notes.mjsrenders the v0.7.0 section;check-npm-publish-ready --skip-registryready at 0.7.0;npm pack --dry-runships the tool, skill, and command.Does not touch Issue #2.