Skip to content

feat(opencode): add /experimental slash command to enable experimental flags#14515

Open
aravhawk wants to merge 5 commits intoanomalyco:devfrom
aravhawk:feat/experimental-command
Open

feat(opencode): add /experimental slash command to enable experimental flags#14515
aravhawk wants to merge 5 commits intoanomalyco:devfrom
aravhawk:feat/experimental-command

Conversation

@aravhawk
Copy link
Copy Markdown

@aravhawk aravhawk commented Feb 21, 2026

Issue for this PR

Closes #7717

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Adds a built-in /experimental slash command in the TUI so users can quickly enable experimental flags without manually editing env files.

Changes:

  • Registers /experimental in built-in TUI commands.
  • Adds an Experimental flags dialog that:
    • Lists available experimental env flags.
    • Supports boolean flags by setting KEY=true.
    • Supports numeric flags with a prompt and positive-integer validation.
    • Writes/updates values in project .env.
    • Shows success/error toasts and a restart reminder.
  • Uses dynamic flag discovery from flag metadata so newly added experimental flags show up automatically (instead of a hardcoded list).
  • Adds docs for /experimental in TUI docs.

Why this works:

  • The command is integrated into the existing slash command and command palette system.
  • The dialog reads flag definitions from the same flag layer used by runtime env handling, so UI options stay in sync with available experimental flags.
  • .env update logic replaces existing lines (including export KEY=...) or appends when missing.

How did you verify your code works?

  • Ran bun run --cwd packages/opencode typecheck (pass).
  • Ran bun run --cwd packages/web build (pass).

Screenshots / recordings

  1. Slash command discoverability (/experimental appears in autocomplete):

Slash autocomplete showing /experimental

  1. Experimental flags dialog:

Experimental flags dialog

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

Copy link
Copy Markdown

@Carl-Bowen Carl-Bowen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some strong code, couldn't find much to touch on but hope this helps.

import { Flag } from "@/flag/flag"

function esc(value: string) {
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have a consts file that includes named consts for these regexes for readability and import them in.

const clean = trimmed.startsWith("export ") ? trimmed.slice(7).trim() : trimmed
const index = clean.indexOf("=")
if (index <= 0) return result
result[clean.slice(0, index).trim()] = clean.slice(index + 1).trim()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is basically a homemade dotenv parser. It’s going to break on:

  • quoted values
  • values with = inside them
  • inline comments
  • escaped stuff

We really shouldn’t be parsing .env ourselves. That’s user config. If we corrupt it, that’s a bad day.

Would strongly prefer using a real dotenv parser instead of rolling our own.

const [text, { refetch }] = createResource(
file,
(target) => {
return Bun.file(target).text().catch(() => "")
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This just swallows errors and pretends the file is empty.

If the file exists but has permissions issues or some other problem, we’ll just overwrite it with a new one. That’s kind of scary.

At least surface a toast or log something meaningful here...

}

const output = edit(text() ?? "", flag.key, value)
const saved = await Bun.write(file(), output)
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn’t atomic. If the process crashes mid-write, we could corrupt .env?

Would be safer to write to a temp file and rename 🤔

Maybe not critical, but since this is config mutation, it matters.

const vars = createMemo(() => parse(text() ?? ""))
const flags = createMemo(() => {
const entries = Object.entries(Flag.types)
.filter(([key]) => key.includes("EXPERIMENTAL"))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

From a DRY perspective...

You duplicate flag discovery logic.

You're basically discovering experimental flags in two different ways and merging them.

This logic probably belongs in one place — ideally inside Flag itself.

Instead of:

const flags = createMemo(() => { ... })

You could have:

Flag.getExperimental()

Then this component just consumes it.

That removes duplication and centralises flag knowledge.

@pedro757
Copy link
Copy Markdown

pedro757 commented Feb 25, 2026

Would this cover experinental variables set in opencode.json like continue_loop_on_deny ?

BTW, idk why there are two sets of experimental variables, the ones in config file and the flags

Dialog: safer .env handling, atomic writes, and centralized experimental flags
@aravhawk
Copy link
Copy Markdown
Author

Addressed @ConsultantCarl's feedback

@aravhawk
Copy link
Copy Markdown
Author

Would this cover experinental variables set in opencode.json like continue_loop_on_deny ?

BTW, idk why there are two sets of experimental variables, the ones in config file and the flags

This command only targets environment-based experimental flags (via Flag.getExperimental()), and it writes those to .env.

So it won’t cover config-based experimental options in opencode.json (e.g. experimental.continue_loop_on_deny), since those are part of the config schema and read through Config.get().

We currently have both because they serve different purposes:

  • env flags: process/runtime feature toggles
  • config.experimental: project/user config options (schema-backed)

@Carl-Bowen
Copy link
Copy Markdown

@aravhawk
Looks good. Happy to approve once build passes.

replace the dotenv parser dependency in the tui experimental dialog with a local parser.
this avoids missing-package failures during sdk build, typecheck, and unit jobs in ci.
@aravhawk aravhawk requested a review from Carl-Bowen February 26, 2026 02:49
Copy link
Copy Markdown

@Carl-Bowen Carl-Bowen left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. Annoying I don't have permittance to approve, but if I did I would.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: add "/experimental" command to toggle experimental features

3 participants