Commit 76ec00d
feat: plugin ecosystem + step.workflow_call (#331)
* feat(wfctl): add plugin install --url for direct URL installs
Adds --url flag to wfctl plugin install that downloads a tar.gz archive
from a direct URL, extracts plugin.json to identify the plugin name,
installs to the plugin directory, and records the SHA-256 checksum in
the lockfile.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat(wfctl): enhanced plugin init scaffold with full project structure
Extends wfctl plugin init to generate cmd/workflow-plugin-<name>/main.go,
internal/provider.go, internal/steps.go, go.mod, .goreleaser.yml, CI/release
GitHub Actions workflows, Makefile, and README.md. Adds --module flag for
custom Go module paths. Preserves existing plugin.json and .go skeleton for
backward compatibility.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* feat: engine auto-fetch for declared external plugins on startup
Add AutoFetchPlugin and AutoFetchDeclaredPlugins to plugin/autofetch.go,
which shell out to wfctl to download plugins not found locally. Extend
WorkflowConfig with a new PluginsConfig / ExternalPluginDecl type so
configs can declare plugins with autoFetch: true and an optional version
constraint. StdEngine gains SetExternalPluginDir and calls
AutoFetchDeclaredPlugins in BuildFromConfig before module loading.
The server's buildEngine registers the plugin dir so auto-fetch is active
at runtime. If wfctl is absent, a warning is logged and startup continues.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: resolve all CI lint failures
- Use struct conversion for staticIndexEntry → PluginSummary (staticcheck S1016)
- Remove unused updateLockfile and writePluginJSON functions
- Add nilerr annotations for intentional nil returns in integrity.go
- Add gosec annotation for exec.Command in autofetch.go
- Fix TestLoadRegistryConfigDefault to use DefaultRegistryConfig() directly
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address Copilot review feedback on engine PR
- integrity.go: fail closed when lockfile exists but is unreadable or
unparseable, preventing integrity enforcement bypass
- autofetch.go: extract stripVersionConstraint helper; detect compound
version constraints and fall back to latest; check both pluginName and
workflow-plugin-<name> alternate form for installed-check; log restart
warning when new plugins are downloaded (they require a server restart)
- autofetch_test.go: test stripVersionConstraint directly instead of
duplicating the logic inline; add compound-constraint cases
- engine.go: clarify comment that auto-fetched plugins need a restart
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
* fix: address all Copilot review comments on engine PR (#330)
- generator.go: use plugin/external/sdk imports and types (PluginProvider,
StepInstance, StepResult, StepTypes/CreateStep) instead of plugin/sdk
- PLUGIN_AUTHORING.md: update examples to match external SDK interfaces
- plugin_install.go: hash installed binary (not archive) for lockfile,
add hashFileSHA256 helper, add install mode mutual exclusivity check,
update installFromLocal to write lockfile, normalize plugin names
- plugin_lockfile.go: add registry param to updateLockfileWithChecksum,
pass version/registry in installFromLockfile, remove dir on mismatch
- registry_source.go: validate URL in NewStaticRegistrySource
- config.go: clarify Version field forwarding semantics
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: address remaining Copilot review comments on engine PR (#330)
- registry_source.go: use explicit field assignment for PluginSummary
instead of struct type conversion (clearer, avoids tag confusion)
- plugin_lockfile.go: don't pass @Version in installFromLockfile to
prevent lockfile overwrite before checksum verification
- plugin_install.go: add verifyInstalledPlugin() call in installFromURL
for parity with registry installs
- engine.go: add TODO to move auto-fetch before plugin discovery so
newly fetched plugins are available without restart
- integrity_test.go: add tests for unreadable and malformed lockfile
to verify fail-closed behavior
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: suppress S1016 lint, add generator project structure tests
- registry_source.go: add nolint:gosimple for S1016 — explicit field
assignment preferred for clarity across different struct tags
- generator_test.go: add TestGenerateProjectStructure verifying all
generated files exist and use correct external SDK imports/types
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: move auto-fetch before plugin discovery so fetched plugins load immediately
Auto-fetch was running inside BuildFromConfig, which executes after
external plugins are already discovered and loaded. Plugins downloaded
by auto-fetch required a server restart to take effect.
Move auto-fetch to buildEngine in cmd/server/main.go, before
DiscoverPlugins/LoadPlugin. Remove the now-unused externalPluginDir
field and SetExternalPluginDir from the engine.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: add step.workflow_call for cross-pipeline dispatch
Enables pipelines to call other pipelines by name with full context
forwarding. Supports template-resolved workflow names and stop_pipeline
option. Required for WebSocket message routing to game-specific pipelines.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address all 9 Copilot review comments on PR #331
- registry_source.go: switch GitHubRegistrySource to use registryHTTPClient
(timeout-configured) for both ListPlugins and FetchManifest; update comment
- autofetch.go: scan for AutoFetch=true entries before exec.LookPath to avoid
misleading startup warning when no plugins need auto-fetch
- autofetch.go: add internal autoFetchPlugin helper that accepts *slog.Logger
and emits structured log entries when a logger is available; public
AutoFetchPlugin delegates to it; AutoFetchDeclaredPlugins passes its logger
- autofetch_test.go: rename TestAutoFetchPlugin_CorrectArgs to
TestAutoFetchPlugin_SkipsWhenExists to match what the test actually asserts
- integrity.go: replace os.ReadFile + sha256.Sum256 with streaming
os.Open + io.Copy into sha256.New() to keep memory bounded for large binaries
- integrity_test.go: add t.Skip guard in UnreadableLockfile test when the file
is actually readable (Windows / root environments)
- pipeline_step_workflow_call.go: use resolved workflowName (not s.workflow)
in async return payload and sync error message for consistency
- docs/PLUGIN_AUTHORING.md: clarify the distinction between plugin.json name
(short, used by engine) and the registry/provider manifest name
(workflow-plugin- prefixed), and which is used for dependency resolution
- config/config.go: MergeApplicationConfig now merges Plugins.External from
each referenced workflow file into the combined config, deduplicated by name
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: remove duplicate hashFileSHA256 from merge conflict resolution
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address new PR #331 review comments
- docs/PLUGIN_AUTHORING.md: close unclosed code fence after StepProvider example
- cmd/wfctl/plugin_install.go: streaming hashFileSHA256 via io.Copy + sha256.New()
- cmd/wfctl/plugin_install.go: warn on hash failure instead of silent empty checksum
- config/merge_test.go: add TestMergeApplicationConfig_PluginDedup covering first-definition-wins dedup
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
* fix: address 4 new PR #331 review comments
- pipeline_step_workflow_call.go: propagate stop_pipeline in async mode
- integrity.go: "open plugin binary" instead of "read" for os.Open error
- plugin_install.go: lowercase "warning:" for consistency
- merge_test.go: use filepath.Join and check writeFileContent errors
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>1 parent db6c04d commit 76ec00d
12 files changed
Lines changed: 217 additions & 58 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
29 | 29 | | |
30 | 30 | | |
31 | 31 | | |
32 | | - | |
33 | | - | |
34 | | - | |
| 32 | + | |
| 33 | + | |
| 34 | + | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
81 | 81 | | |
82 | 82 | | |
83 | 83 | | |
84 | | - | |
85 | | - | |
| 84 | + | |
| 85 | + | |
86 | 86 | | |
87 | | - | |
| 87 | + | |
88 | 88 | | |
89 | 89 | | |
90 | | - | |
| 90 | + | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
94 | 94 | | |
95 | | - | |
96 | | - | |
| 95 | + | |
| 96 | + | |
97 | 97 | | |
98 | 98 | | |
99 | 99 | | |
| |||
165 | 165 | | |
166 | 166 | | |
167 | 167 | | |
168 | | - | |
| 168 | + | |
| 169 | + | |
169 | 170 | | |
170 | | - | |
171 | | - | |
172 | | - | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
173 | 175 | | |
174 | | - | |
| 176 | + | |
175 | 177 | | |
176 | 178 | | |
177 | 179 | | |
| |||
516 | 518 | | |
517 | 519 | | |
518 | 520 | | |
519 | | - | |
| 521 | + | |
520 | 522 | | |
521 | 523 | | |
522 | 524 | | |
| |||
536 | 538 | | |
537 | 539 | | |
538 | 540 | | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
539 | 555 | | |
540 | 556 | | |
541 | 557 | | |
| |||
590 | 606 | | |
591 | 607 | | |
592 | 608 | | |
593 | | - | |
594 | | - | |
595 | | - | |
| 609 | + | |
596 | 610 | | |
597 | | - | |
| 611 | + | |
598 | 612 | | |
599 | | - | |
| 613 | + | |
600 | 614 | | |
601 | 615 | | |
602 | 616 | | |
| |||
693 | 707 | | |
694 | 708 | | |
695 | 709 | | |
696 | | - | |
697 | | - | |
698 | | - | |
699 | | - | |
700 | | - | |
701 | | - | |
702 | | - | |
703 | | - | |
704 | | - | |
705 | | - | |
706 | 710 | | |
707 | 711 | | |
708 | 712 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
94 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
95 | 97 | | |
96 | 98 | | |
97 | 99 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
64 | 64 | | |
65 | 65 | | |
66 | 66 | | |
67 | | - | |
| 67 | + | |
68 | 68 | | |
69 | 69 | | |
70 | 70 | | |
| |||
90 | 90 | | |
91 | 91 | | |
92 | 92 | | |
93 | | - | |
| 93 | + | |
| 94 | + | |
| 95 | + | |
| 96 | + | |
| 97 | + | |
94 | 98 | | |
95 | 99 | | |
96 | 100 | | |
| |||
206 | 210 | | |
207 | 211 | | |
208 | 212 | | |
209 | | - | |
210 | | - | |
| 213 | + | |
| 214 | + | |
| 215 | + | |
| 216 | + | |
| 217 | + | |
| 218 | + | |
| 219 | + | |
211 | 220 | | |
212 | 221 | | |
213 | 222 | | |
| |||
226 | 235 | | |
227 | 236 | | |
228 | 237 | | |
229 | | - | |
| 238 | + | |
| 239 | + | |
230 | 240 | | |
231 | 241 | | |
232 | 242 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
419 | 419 | | |
420 | 420 | | |
421 | 421 | | |
| 422 | + | |
| 423 | + | |
| 424 | + | |
| 425 | + | |
| 426 | + | |
| 427 | + | |
| 428 | + | |
| 429 | + | |
| 430 | + | |
| 431 | + | |
| 432 | + | |
| 433 | + | |
| 434 | + | |
| 435 | + | |
| 436 | + | |
| 437 | + | |
| 438 | + | |
| 439 | + | |
| 440 | + | |
422 | 441 | | |
423 | 442 | | |
424 | 443 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
2 | 2 | | |
3 | 3 | | |
4 | 4 | | |
| 5 | + | |
5 | 6 | | |
6 | 7 | | |
7 | 8 | | |
| |||
533 | 534 | | |
534 | 535 | | |
535 | 536 | | |
| 537 | + | |
| 538 | + | |
| 539 | + | |
| 540 | + | |
| 541 | + | |
| 542 | + | |
| 543 | + | |
| 544 | + | |
| 545 | + | |
| 546 | + | |
| 547 | + | |
| 548 | + | |
| 549 | + | |
| 550 | + | |
| 551 | + | |
| 552 | + | |
| 553 | + | |
| 554 | + | |
| 555 | + | |
| 556 | + | |
| 557 | + | |
| 558 | + | |
| 559 | + | |
| 560 | + | |
| 561 | + | |
| 562 | + | |
| 563 | + | |
| 564 | + | |
| 565 | + | |
| 566 | + | |
| 567 | + | |
| 568 | + | |
| 569 | + | |
| 570 | + | |
| 571 | + | |
| 572 | + | |
| 573 | + | |
| 574 | + | |
| 575 | + | |
| 576 | + | |
| 577 | + | |
| 578 | + | |
| 579 | + | |
| 580 | + | |
| 581 | + | |
| 582 | + | |
| 583 | + | |
| 584 | + | |
| 585 | + | |
| 586 | + | |
| 587 | + | |
| 588 | + | |
| 589 | + | |
| 590 | + | |
| 591 | + | |
| 592 | + | |
| 593 | + | |
| 594 | + | |
| 595 | + | |
| 596 | + | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
101 | 101 | | |
102 | 102 | | |
103 | 103 | | |
104 | | - | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
105 | 115 | | |
106 | 116 | | |
107 | 117 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
31 | 31 | | |
32 | 32 | | |
33 | 33 | | |
| 34 | + | |
34 | 35 | | |
35 | 36 | | |
36 | 37 | | |
| |||
86 | 87 | | |
87 | 88 | | |
88 | 89 | | |
| 90 | + | |
| 91 | + | |
| 92 | + | |
| 93 | + | |
89 | 94 | | |
90 | 95 | | |
91 | 96 | | |
| |||
101 | 106 | | |
102 | 107 | | |
103 | 108 | | |
104 | | - | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
105 | 114 | | |
106 | | - | |
| 115 | + | |
107 | 116 | | |
108 | 117 | | |
109 | 118 | | |
| |||
130 | 139 | | |
131 | 140 | | |
132 | 141 | | |
133 | | - | |
| 142 | + | |
134 | 143 | | |
135 | 144 | | |
136 | 145 | | |
| |||
139 | 148 | | |
140 | 149 | | |
141 | 150 | | |
142 | | - | |
| 151 | + | |
143 | 152 | | |
144 | 153 | | |
145 | 154 | | |
| |||
153 | 162 | | |
154 | 163 | | |
155 | 164 | | |
156 | | - | |
| 165 | + | |
157 | 166 | | |
158 | 167 | | |
159 | 168 | | |
| |||
0 commit comments