Skip to content
Open
74 changes: 72 additions & 2 deletions design/integration.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,13 +41,76 @@ Hyphenated keys use underscores in package names. Its class declares:
Import and `_register()` the class in `src/specify_cli/integrations/__init__.py`
(both lists alphabetically). This registry is the source of built-in Python
integration behavior. If the agent supports non-interactive workflows, implement
`build_exec_args()` with the full base signature; `options()` declares
install-time `--integration-options`, not per-workflow runtime options.
`build_exec_args()` with the full base signature. The `reasoning_effort` and
`context_size` keywords are required only for the settings it translates; accept
an untranslated one only to reject it through `check_dispatch_settings()` (see
[Per-step dispatch settings](#per-step-dispatch-settings)). `options()`
declares install-time `--integration-options`, not per-workflow runtime options.
Agent-specific native events can be declared on the integration. Set
`multi_install_safe = True` only when the integration emits no files or uses a
static, non-overlapping agent root and command directory; shared dynamic paths
are not safe by default.

### Per-step dispatch settings

Workflow `command` and `prompt` steps can request the settings in
`DISPATCH_SETTINGS` (`reasoning_effort`, then `context_size`) for one CLI
invocation. Values are opaque strings whose meaning belongs to the agent's CLI.
Prompt steps pass requested settings to `build_exec_args()`. Command steps pass
them to `dispatch_command()`, whose base implementation forwards only the
requested keywords to `build_exec_args()`. The *receiving method* is therefore
`build_exec_args()`, or `dispatch_command()` for a command step when the
integration overrides it.

Built-in integrations and external adapters opt in per setting with these
`specify_cli.integrations.base` APIs:

- `supported_dispatch_settings`: a `frozenset` of the settings the integration
translates. Declare it on the class that implements the receiving method.
`effective_dispatch_settings()` reads the declaration from that class only:
a subclass that inherits the method inherits support, but a subclass that
overrides it must declare support again. A declaration on any other class,
or one that is not a set or frozenset, declares nothing.
- `reasoning_effort` and `context_size` keywords: the receiving method accepts
one, defaulting to `None`, for each declared setting. Hosts pass a keyword
only when a step sets that setting, so calls without settings are unchanged.
Treat any value other than `None` as requested; hosts never pass `""`.
Translate the value into CLI arguments placed after those from
`SPECKIT_INTEGRATION_<KEY>_EXTRA_ARGS`, and do not pass it on:
`IntegrationBase.build_exec_args()` and the format-base builders translate
neither setting and raise `DispatchSettingsError` rather than drop a value.
- `validate_dispatch_setting(name, value)`: an optional per-setting hook that
the gate calls before anything runs. The default accepts every value; raise
`ValueError` to reject a value the CLI would ignore or misread, and the host
attributes the failure to that setting. If the receiving method itself must
reject a setting, raise `DispatchSettingsError` with `setting`; without it,
the step error lists every requested setting with its source. Keep the value
out of either error's message: it can come from captured output, which Spec
Kit does not print to the terminal or write to `log.jsonl`.
- `dispatch_setting_hint(setting)`: optional remediation text, such as another
step field to use instead, which the host appends to its error for an
unsupported setting. The default returns `None`.

The host gate, `check_dispatch_settings(integration, settings, *, via)`, cannot
be overridden by integrations. Steps run it before executable detection, with
*via* set to `"dispatch_command"` for command steps and `"build_exec_args"` for
prompt steps; the base `dispatch_command()` runs it with `"build_exec_args"`.
For each requested setting, in `DISPATCH_SETTINGS` order, the gate requires a
string of 1 to 64 letters, digits, `.`, `_`, or `-` that starts with a letter
or digit, support under the ownership rule above, and a receiving method that
accepts the keyword or `**kwargs`. It then calls `validate_dispatch_setting()`.
Each failure raises `DispatchSettingsError`, a `ValueError` whose `setting`
attribute names the setting, so the step can report where its value came from.
An unsupported-setting error lists the integrations that support the setting,
and an error about a malformed value reports only its type and length, never
the value. A translating method can also run the gate itself, with *via*
naming that method, so that direct calls are checked too.

The host cannot observe what the receiving method does with a declared
setting. An integration that declares support and then ignores the value, for
example by absorbing it in `**kwargs`, drops it silently: a declaration is a
promise to translate the setting.

## Output flavors

Choose the smallest base class that matches the agent's native format. The
Expand Down Expand Up @@ -182,6 +245,13 @@ output_json=True, integration_args=None, integration_options=None,
project_root=None)` as appropriate. Every explicitly declared host keyword
parameter must have a default; accepting omitted keywords through `**kwargs`
is also supported. Validation checks both prompt-only and complete host calls.
This required signature does not change for
[per-step dispatch settings](#per-step-dispatch-settings), and installation
does not check dispatch-setting declarations. An adapter that does not declare
support never receives `reasoning_effort` or `context_size`: a step that
requests either fails before the adapter's CLI runs. Adapters opt in like
built-in integrations, and the host gate checks the declaration and the
receiving method's signature when a step requests a setting.
An adapter using only the host API and
standard library needs no pip installation or source-registry edit.
Import-side-effect registration is rejected.
Expand Down
46 changes: 46 additions & 0 deletions docs/integrations/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,52 @@ CLI adapter declares none, tool checking probes its runtime executable.
Version detection is agent-specific; declaring a version constraint does not
supply a universal `--version` invocation.

### Per-step dispatch settings

Workflow `command` and `prompt` steps can request
[`reasoning_effort` and `context_size`](../workflow-steps/types.md#reasoning-effort-and-context-size)
for one CLI invocation. `DISPATCH_SETTINGS` in `specify_cli.integrations.base`
lists them in the order they are checked. Values are opaque strings whose
meaning belongs to the agent's CLI.

The `build_exec_args()` signature above does not change, and an adapter that
does not opt in never receives either keyword: a step that requests one fails
before the CLI runs. Prompt steps pass requested settings to
`build_exec_args()`. Command steps pass them to `dispatch_command()`, whose
base implementation forwards them to `build_exec_args()`. The *receiving
method* is therefore `build_exec_args()`, or `dispatch_command()` for a command
step when the integration overrides it. To opt in:

- Declare `supported_dispatch_settings`, a `frozenset` of the settings the
integration translates, on the class that defines the receiving method. A
subclass that overrides that method must declare support again. A
declaration on any other class, or one that is not a set or frozenset,
declares nothing.
- Accept a `reasoning_effort=None` or `context_size=None` keyword on the
receiving method for each declared setting. The host passes a keyword only
when a step sets that setting, and never passes `""`. Translate the value
into CLI arguments placed after those from
`SPECKIT_INTEGRATION_<KEY>_EXTRA_ARGS`, and do not pass it on:
`IntegrationBase.build_exec_args()` and the format-base builders translate
neither setting and raise `DispatchSettingsError` rather than drop a value.
- Optionally, override `validate_dispatch_setting(name, value)` to raise
`ValueError` for a value the CLI would ignore or misread, and
`dispatch_setting_hint(setting)` to return remediation text that the host
appends to its error for an unsupported setting.

Before the CLI runs, the host gate `check_dispatch_settings()` checks each
requested setting in `DISPATCH_SETTINGS` order. The value must be 1 to 64
letters, digits, `.`, `_`, or `-`, starting with a letter or digit; the
integration must declare the setting as above; and the receiving method must
accept the keyword or `**kwargs`. The gate then calls
`validate_dispatch_setting()`. Each failure raises `DispatchSettingsError`, a
`ValueError` whose `setting` attribute names the setting; if the receiving
method itself must reject a value, raise it with `setting`. Keep the value out
of every error message: it can come from captured output, which Spec Kit does
not print to the terminal or write to `log.jsonl`. The host cannot detect a
declared setting that the receiving method accepts and then ignores, for
example through `**kwargs`, so a declaration is a promise to translate it.

## Setup and file ownership

Format bases implement setup and teardown. Custom layouts may override:
Expand Down
15 changes: 15 additions & 0 deletions docs/integrations/claude.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,3 +69,18 @@ See [runtime events](cli.md#runtime-events).
Workflow steps use `claude -p <prompt>`, with `--model` when configured and
`--output-format json` when structured output is requested. This adapter does
not add a permission-bypass flag itself.

## Workflow Dispatch Settings

Command and prompt steps can set
[`reasoning_effort`](../workflow-steps/types.md#reasoning-effort-and-context-size),
which Claude Code dispatch passes as `--effort <value>`. Claude Code 2.1.318
accepts `low`, `medium`, `high`, `xhigh`, and `max`. It ignores any other
value with only a warning, so Spec Kit rejects other values before Claude Code
starts. To pass a level that a newer Claude Code release adds, set
`SPECKIT_INTEGRATION_CLAUDE_EXTRA_ARGS="--effort <value>"` instead of
`reasoning_effort`; the environment variable applies to every Claude Code
invocation, not just one step.

Spec Kit does not map `context_size` for Claude Code, so a step that sets it
fails. Choose a long-context model with the step's `model` field instead.
9 changes: 9 additions & 0 deletions docs/integrations/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -486,6 +486,15 @@ Docker Agent currently exposes structured agent/safety configuration.
Headless approval flags, model forwarding, and structured-output support are
also adapter-specific, not universal guarantees.

The [`reasoning_effort` and `context_size`](../workflow-steps/types.md#reasoning-effort-and-context-size)
settings of `command` and `prompt` steps instead follow one shared contract.
Spec Kit checks each value and passes it only to an integration that declares
and translates that setting; with any other integration, the step fails before
the CLI runs. GitHub Copilot and Codex CLI translate both settings, and Claude
Code translates `reasoning_effort`; the agent guides describe each mapping.
Adapter authors opt in as described in the
[integration API reference](api.md#per-step-dispatch-settings).

## FAQ

### Can I install multiple integrations in the same project?
Expand Down
21 changes: 21 additions & 0 deletions docs/integrations/codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,24 @@ See [runtime events](cli.md#runtime-events).
Workflow steps use `codex exec <prompt>`, with `--model` when configured and
`--json` when structured output is requested. Codex dev-mode registration writes
skill files rather than symlinking them.

## Workflow Dispatch Settings

Command and prompt steps can set
[`reasoning_effort` and `context_size`](../workflow-steps/types.md#reasoning-effort-and-context-size).
Codex dispatch passes them as configuration overrides after `--model`:

| Step setting | Codex CLI argument | Values |
|---|---|---|
| `reasoning_effort` | `-c model_reasoning_effort="<value>"` | A level the selected model supports, such as `low`, `medium`, `high`, or `xhigh` |
| `context_size` | `-c model_context_window=<value>` | A token count: a positive whole number without leading zeros, at most `9223372036854775807` |

Spec Kit passes `reasoning_effort` through unchanged; Codex CLI 0.165.0
forwards it to the model without checking it. Codex displays the reasoning
effort it is given, so set it from a literal or a workflow input, not from
captured output. A `context_size` that is not a valid token count fails the
step before Codex starts.

`context_size` overrides the context window that Codex assumes for the model.
Codex clamps the value to the model's maximum, and a value below the model's
default shrinks the usable context.
20 changes: 20 additions & 0 deletions docs/integrations/copilot.md
Original file line number Diff line number Diff line change
Expand Up @@ -116,3 +116,23 @@ without interactive approval. Set `SPECKIT_COPILOT_ALLOW_ALL_TOOLS=0` to stop
Spec Kit from adding this flag. The deprecated `SPECKIT_ALLOW_ALL_TOOLS` is
accepted only as a fallback. A configured `--model` and requested
`--output-format json` are forwarded.

## Workflow Dispatch Settings

Command and prompt steps can set
[`reasoning_effort` and `context_size`](../workflow-steps/types.md#reasoning-effort-and-context-size).
Copilot CLI dispatch passes them as native flags after `--model`, in both the
skills and commands layouts:

| Step setting | Copilot CLI flag | Values in Copilot CLI 1.0.106 |
|---|---|---|
| `reasoning_effort` | `--reasoning-effort <value>` | `none`, `minimal`, `low`, `medium`, `high`, `xhigh`, `max` |
| `context_size` | `--context <value>` | `default`, `long_context` |

Spec Kit accepts only these values. Any other value fails the step before
Copilot CLI starts, because Copilot CLI repeats a rejected value in its error
and the value can come from captured output. To pass a value that a newer
Copilot CLI release adds, set
`SPECKIT_INTEGRATION_COPILOT_EXTRA_ARGS="--reasoning-effort <value>"` or
`SPECKIT_INTEGRATION_COPILOT_EXTRA_ARGS="--context <value>"` instead of the
step setting.
2 changes: 2 additions & 0 deletions docs/workflow-steps/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ invalid configuration in `execute()` too.
| `run_id` | `str \| None` | Current run ID |
| `is_resume` | `bool` | Whether this execution is a resume attempt |
| `workflow_dir` | `str \| None` | Source directory of the current workflow definition |
| `default_reasoning_effort` | `str \| None` | Workflow `reasoning_effort` default as written; can be an unevaluated expression |
| `default_context_size` | `str \| None` | Workflow `context_size` default as written; can be an unevaluated expression |

Mappings default to empty dictionaries, boolean flags to false, and optional
paths/identifiers to `None`. Paths are strings, not `Path` objects. Use
Expand Down
Loading