fix(workflows): auto-detect project integration instead of hardcoding "copilot"#2502
Open
markuswondrak wants to merge 2 commits intogithub:mainfrom
Open
fix(workflows): auto-detect project integration instead of hardcoding "copilot"#2502markuswondrak wants to merge 2 commits intogithub:mainfrom
markuswondrak wants to merge 2 commits intogithub:mainfrom
Conversation
… "copilot" Resolves the hardcoded "copilot" default in workflow.yml by introducing an "auto" sentinel that reads the active integration from .specify/integration.json at runtime. - Add read_integration_state() to integration_state.py with typed errors - Refactor _read_integration_json() in __init__.py to use it - Add _resolve_default() / _resolve_workflow_integration() to engine - Change workflow.yml default from "copilot" to "auto" - Add "opencode" to supported integrations list - Extend tests for state reader and engine auto-detection Fixes github#2406 Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
Pull request overview
Updates the workflow system to avoid hardcoding the "copilot" integration by introducing an "auto" sentinel that resolves the active integration from project metadata (primarily .specify/integration.json) at runtime, with fallback to legacy init options.
Changes:
- Switches the bundled Speckit workflow’s
integrationinput default from"copilot"to"auto"and allowsopencodein workflow requirements. - Adds integration state reading/resolution helpers (with typed error classes) and wires the workflow engine to resolve
"auto"during input/default integration resolution. - Extends tests to cover integration auto-detection behavior (including persistence across resume) and integration state reader behavior.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| workflows/speckit/workflow.yml | Changes default integration input to "auto" and adds opencode to allowed integrations. |
| src/specify_cli/integration_state.py | Adds read_integration_state() / resolve_project_integration() plus typed errors for invalid/unreadable state. |
| src/specify_cli/workflows/engine.py | Resolves "auto" for inputs/workflow integration, persists resolved integration in run state, and uses it on resume. |
| src/specify_cli/init.py | Delegates integration.json loading to the new integration state reader and maps typed errors to CLI output/exit. |
| tests/integrations/test_integration_state.py | Adds unit tests for reading/validating integration state and project integration resolution. |
| tests/test_workflows.py | Adds workflow-engine tests for "auto" integration resolution and run/resume pinning behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
- integration_state.py: distinguish 'does not exist' (return None) from 'exists but not a regular file' (raise IntegrationStateError), restoring the behavior of the old inline code that used path.exists() + caught OSError - engine.py: call _coerce_input() on resolved defaults so enum validation and type coercion apply to default values, matching the path for user-provided inputs; previously the auto-sentinel resolved via _resolve_default() bypassed _coerce_input() entirely Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment on lines
+197
to
+199
| raise IntegrationStateError(f"Could not read {path}") from exc | ||
| except json.JSONDecodeError as exc: | ||
| raise IntegrationStateError(f"{path} contains invalid JSON") from exc |
Comment on lines
1930
to
1939
| try: | ||
| data = json.loads(path.read_text(encoding="utf-8")) | ||
| except json.JSONDecodeError as exc: | ||
| console.print(f"[red]Error:[/red] {path} contains invalid JSON.") | ||
| console.print(f"Please fix or delete {INTEGRATION_JSON} and retry.") | ||
| console.print(f"[dim]Details:[/dim] {exc}") | ||
| raise typer.Exit(1) | ||
| except OSError as exc: | ||
| console.print(f"[red]Error:[/red] Could not read {path}.") | ||
| console.print(f"Please fix file permissions or delete {INTEGRATION_JSON} and retry.") | ||
| console.print(f"[dim]Details:[/dim] {exc}") | ||
| state = _read_integration_state(project_root) | ||
| except _IntegrationStateSchemaError as exc: | ||
| console.print(f"[red]Error:[/red] {exc}") | ||
| console.print("Please upgrade Spec Kit before modifying integrations.") | ||
| raise typer.Exit(1) | ||
| if not isinstance(data, dict): | ||
| console.print(f"[red]Error:[/red] {path} must contain a JSON object, got {type(data).__name__}.") | ||
| except _IntegrationStateError as exc: | ||
| console.print(f"[red]Error:[/red] {exc}") | ||
| console.print(f"Please fix or delete {INTEGRATION_JSON} and retry.") | ||
| raise typer.Exit(1) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Fixes the hardcoded
"copilot"default inworkflow.ymlthat caused the workflow engine to dispatch to Copilot even when the project was initialized with a different integration (e.g. opencode).Introduces an
"auto"sentinel for theintegrationinput: whenautois the resolved value, the engine reads.specify/integration.jsonto determine the active integration at runtime.Changes:
integration_state.py— newread_integration_state()function with typed error classes (IntegrationStateError,IntegrationStateSchemaError)__init__.py—_read_integration_json()now delegates toread_integration_state()instead of inlining the logicworkflows/engine.py—_resolve_default()/_resolve_workflow_integration()resolve the"auto"sentinel at runtimeworkflow.yml— default changed from"copilot"to"auto"; addedopencodetorequires.integrations.anyFixes #2406
Testing
uv run specify --helpuv sync && uv run pytestAI Disclosure
GitHub Copilot assisted with code generation and test scaffolding.