Repository navigation
[Feature]: Add local (--dev) installation for custom workflow step types #4695
Description
Problem Statement
Custom workflow step types are the only first-class Spec-Kit component that cannot be installed from a local path. Every other artifact supports local development, but steps are catalog/HTTPS-only:
| Component | Local/dev install | Custom URL |
|---|---|---|
| Extension | specify extension add --dev <dir> |
--from <url> |
| Preset | specify preset add --dev <dir> |
--from <url> |
| Workflow | specify workflow add <path> --dev |
--from <url> |
| Bundle | specify bundle install <path> |
— |
| Step | none | none |
specify workflow step add accepts only a catalog step id and fetches step.yml/__init__.py (and optional extra_files) over HTTPS (src/specify_cli/workflows/step/command_add.py, _safe_fetch rejects non-HTTPS). There is no --dev or --from.
This makes custom steps impossible to iterate on or test from a checkout:
- A developer must publish the step package to an HTTPS catalog (or run a localhost catalog server) before they can run it once.
- CI cannot install a step from the repository under test without a network round-trip to a hosted catalog.
- Bundles that declare
provides.stepscannot be developed or validated offline —bundle installdelegates step installs toworkflow_step_add(component.id)(bundler/services/primitives.py), which always resolves through the step catalog. - Manually copying files into
.specify/workflows/steps/<id>/works at runtime (load_custom_stepsscans the directory) but is undocumented, unvalidated, and leaves the registry out of sync.
Net effect: the step type is second-class relative to extensions, presets, and workflows, and the only way to develop one is a throwaway localhost HTTPS/HTTP catalog.
Proposed Solution
Bring specify workflow step add to feature parity with the other components:
- Add
--dev <dir>to install a custom step from a local package directory containingstep.yml,__init__.py, and anyextra_files. It copies the package into.specify/workflows/steps/<id>/and records it in the step registry withsource: local, mirroringpreset add --dev/extension add --dev. - Add
--from <url>to install a single step package directly from a URL, mirroring the other components'--from. - Apply the same safety guards the catalog path already uses: validate
step.type_key, reject ids that collide with built-in step types, reject duplicate installs (with a consistent--force/reinstall semantic), and keep the symlink/..path-escape guards on the destination. - Optionally support
bundle install <local dir>resolvingprovides.stepsfrom the local bundle directory instead of the catalog stack, so step-bearing bundles work offline.
Alternatives Considered
- Manual copy into
.specify/workflows/steps/<id>/— works becauseload_custom_stepsscans the directory, but it's undocumented, skips validation, and doesn't register provenance, soworkflow step list/remove/bundle tracking miss it. - Localhost step catalog —
_validate_catalog_urlalready permitshttp://localhost, so a developer can run a local server and register it as a catalog. This is the current workaround, but it is heavy, undocumented, and not usable in CI without extra plumbing. - Ship the step inside a preset/extension archive — not possible: the engine only loads custom steps from
.specify/workflows/steps/.
Component
Specify CLI (initialization, commands)
AI Agent (if applicable)
Not applicable
Use Cases
- Iterating on a custom step type while developing a bundle that uses it, without publishing to a catalog on every change.
- Running a bundle's integration tests in CI against a local checkout of the step package.
- Authoring a step locally, then publishing it to a catalog only once it is stable.
- Offline development of step-bearing bundles.
Acceptance Criteria
-
specify workflow step add --dev <dir>installs a local step package into.specify/workflows/steps/<id>/. - The installed step is loaded by
workflow run,workflow resume, andworkflow add, and appears inworkflow step list. -
--devvalidatesstep.yml/__init__.py, rejects collisions with built-in step types, and rejects duplicate installs with the same semantics as the catalog path. - Destination path/symlink safety guards match the catalog install path.
- Registry provenance records a local source (consistent with presets/extensions), so
workflow step removecleans up correctly. -
--from <url>installs a single step package from a URL. -
bundle install <local dir>resolves and installsprovides.stepsfrom the local bundle source (or the limitation is explicitly documented). - Docs (
docs/reference/workflows.mdstep-type section) and tests cover the new paths.
Additional Context
- Command surface:
src/specify_cli/workflows/step/command_add.py(workflow_step_add(step_id),_safe_fetchHTTPS-only). - Catalog/URL validation:
src/specify_cli/workflows/step/catalog/_domain.py(_validate_catalog_url). - Loader:
src/specify_cli/workflows/__init__.py(load_custom_steps) — confirms local directory loading already works at runtime; only installation tooling is missing. - Bundle delegation:
bundler/services/primitives.py(steps→workflow_step_add). - Parity references:
install_from_directoryinsrc/specify_cli/presets/__init__.py, and--devhandling for extensions and workflows. - Motivating use case: a bundle that ships a custom step to consolidate run setup currently has to choose between a network-only component and a localhost catalog workaround.
AI Disclosure
Drafted with opencode (model deepseek-v4.1-flash), human-supervised and reviewed before submission.
Activity
github-actions commentedon Sep 23, 2026
Feature assessment — local-step-install · Stage 1/5: Intake\n\n# Idea Intake: Local installation for custom workflow steps
feature-assess)Idea (as captured)
The source is issue #4695, raised by
markuswondrak. No issue comments added further scope.Restated
The request proposes bringing custom workflow-step installation to parity with other Spec Kit component types by supporting local development packages and direct URL packages. It also proposes improving offline/local bundle handling for bundles that provide custom steps.
Origin & Context
markuswondrak(issue author)First-Glance Unknowns
--fromaccept the same package forms as local installation, or only a URL exposing the required files?]bundle installrequired in the first release or should it be explicitly deferred?]github-actions commentedon Sep 23, 2026
Feature assessment — local-step-install · Stage 2/5: Research\n\n# Idea Research: Local installation for custom workflow steps
Users & Demand
src/specify_cli/workflows/__init__.py,src/specify_cli/workflows/step/catalog/_domain.py] (confidence: high, cited).Prior Art
--devfor local YAML files/directories/archives and--fromfor custom URLs — [source:src/specify_cli/workflows/command_add.py,docs/reference/workflows.md] (confidence: high, cited).src/specify_cli/extensions/command_add.py,src/specify_cli/presets/command_add.py] (confidence: high, cited).step.yml,__init__.py, and optional extra files, validates the declared type key, applies package limits, stages atomically, and records registry provenance as"source": "catalog"— [source:src/specify_cli/workflows/step/command_add.py] (confidence: high, cited)..specify/workflows/steps/, rejects symlinked paths/files, validates the package shape, imports a matchingStepBase, and registers it — [source:src/specify_cli/workflows/__init__.py] (confidence: high, cited).workflow_step_add(component.id), and the bundler rejects offline step installation because it assumes catalog/network resolution — [source:src/specify_cli/bundles/primitives.py] (confidence: high, cited).Market & Context
src/specify_cli/workflows/step/command_add.py] (confidence: medium, cited).src/specify_cli/bundles/primitives.py] (confidence: medium, cited).Data & Constraints
step.ymland__init__.py; catalog installs may include boundedextra_files, validatestep.type_key, reject built-in collisions, and enforce size/file limits — [source:src/specify_cli/workflows/step/command_add.py] (confidence: high, cited)..specify/workflows/steps/after resolution, and registry writes refuse symlinked parents or registry files — [source:src/specify_cli/workflows/step/_helpers.py,src/specify_cli/workflows/step/catalog/_domain.py] (confidence: high, cited).--frompath must preserve those download controls — [source:src/specify_cli/workflows/step/command_add.py,src/specify_cli/workflows/step/catalog/_domain.py] (confidence: high, cited).steps, but the current step primitive has no bundled/local-source branch — [source:src/specify_cli/bundles/manifest.py,src/specify_cli/bundles/primitives.py] (confidence: high, cited).Evidence Against the Idea
src/specify_cli/workflows/step/command_add.py] (confidence: medium, cited).src/specify_cli/bundles/primitives.py] (confidence: medium, cited).Gaps & Open Questions
--fromaccept a directory/archive URL or individual file URLs?]Sources
https://github.com/github/spec-kit/issues/4695(host: github.com, policy: allowlisted)src/specify_cli/workflows/step/command_add.py(repository)src/specify_cli/workflows/step/catalog/_domain.py(repository)src/specify_cli/workflows/__init__.py(repository)src/specify_cli/bundles/primitives.py(repository)docs/reference/workflows.md(repository)github-actions commentedon Sep 23, 2026
Feature assessment — local-step-install · Stage 3/5: Problem\n\n# Problem Definition: Local installation for custom workflow steps
Problem Statement
Developers and CI maintainers who create or consume custom workflow steps cannot install a step package from the local checkout or a direct package URL, while comparable Spec Kit components support those paths. As a result, validating a custom step or a bundle that provides one requires catalog publication, a localhost catalog workaround, or undocumented manual copying, which increases iteration friction and weakens reproducibility.
Affected Users & Stakeholders
Goals
Non-Goals
Success Metrics
workflow step list, loads for workflow execution/resume, and can be removed cleanly (baseline: unsupported; target: covered by automated tests)._StepKindManagerrequires network; target: offline integration test).Cost of Inaction
Custom-step development remains less capable than extension, preset, and workflow development. Authors continue to publish unstable packages or maintain local catalog infrastructure, and CI cannot validate local step-bearing bundles through the supported installation path. Manual copies remain possible but outside registry and documentation guarantees.
Open Questions
step.ymlplus__init__.pyfiles?]--from?]github-actions commentedon Sep 23, 2026
Feature assessment — local-step-install · Stage 4/5: Concept\n\n# Concept: Local installation for custom workflow steps
Options
Option A — Parity-first local step install
Option B — Full component-parity release
Option C — Documented local catalog workaround
Recommendation
Recommend Option A. The issue and repository evidence clearly support a parity gap at the single-step install boundary, while bundle-local resolution is a distinct expansion. A medium-sized, shared install path can target the stated success metrics for local authoring and CI without committing the project to the larger bundle dependency problem before its requirements are known.
Out of Scope (for the recommended option)
Assumptions to Validate
step.yml,__init__.py, and optional companion files is the minimum useful local source.github-actions commentedon Sep 23, 2026
Feature assessment — local-step-install · Stage 5/5: Decision — verdict needs-clarification\n\n# Decision: Local installation for custom workflow steps
Scorecard
Verdict & Rationale
The idea is promising and strategically aligned, but it is not ready for a
goverdict. The recommended parity-first concept is plausible, yet the evidence and scope are blocked by unanswered package-format, provenance, reinstall, and bundle-local-resolution questions. Per the assessment gate, clarify those questions and revisit research/shape before handing the work to specification.If needs-clarification
--fromURL contract and remote package provenance should be supported?]--forcesemantics are required?]bundle installa first-release requirement, and how are source paths and version pins represented?]If go — Handoff to
/speckit-specifyNot applicable: the verdict is
needs-clarification.mnriem commentedon Sep 23, 2026
@markuswondrak Go for it! Keeping mind that we want to indeed align as much as possible with how the other commands operate in this space. If you have to make a call please document it.
markuswondrak commentedon Sep 23, 2026
Clarification resolution
Following the assessment and the maintainer direction to proceed while aligning with comparable commands, the open questions are resolved as follows.
specify workflow step add <step_id> --dev <directory>and a remote.zip,.tar.gz, or.tgzpackage through--from <url>. Barestep.ymland other individual-file URL forms are out of scope.<step_id>required in every mode and requirestep.ymlto declare the samestep.type_key.--devand--fromtake values, are mutually exclusive, and reject empty values. This preserves the existing step ID positional while following the preset-style local source option.step.ymland__init__.py; reject symlinks and special files; apply the existing 512-file/50 MiB limits; and use the shared safe archive extraction, path-containment, HTTPS/loopback, redirect, and bounded-download protections. Installation validates__init__.pyas a required safe regular file but does not import untrusted Python merely to inspect it. Runtime loading continues to require a matchingStepBasesubclass.--forceuniformly for catalog, local, and URL installs. Replacement must stage and validate the new package before transactionally replacing the old package and registry entry, with rollback on failure. A separateworkflow step updatecommand is deferred.--frominstallation uses the workflow/extension default-deny confirmation before any network request and warns that the package contains executable Python.catalog,local, orurl. Retaincatalog_nameonly for catalog installs. Do not persist local paths or URLs in the project registry.bundle.yml, or ZIP artifacts in the first release. Issue acceptance allows this limitation to be documented. A follow-up must define the bundle-relative source contract, containment/symlink rules, artifact behavior, and fallback policy before implementation.docs/reference/workflows.md, and document the deferred bundle-local limitation indocs/reference/bundles.md.These decisions leave the feature ready for specification while documenting the calls required where extension, preset, and workflow conventions differ.
Posted on behalf of @markuswondrak by OpenCode (model:
github-copilot/gpt-5.6-sol, autonomous); this comment was fully AI-drafted from the issue discussion and repository analysis.mnriem commentedon Sep 23, 2026
Eagerly awaiting the PR :)
feat(workflows): install custom step types from local dirs and archiv…
feat(workflows): install custom step types from local dirs and archiv…