opencode-plugin-flow 3.3.22 → 4.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +2 -126
- package/README.md +56 -171
- package/dist/cli.js +6 -34
- package/dist/index.js +370 -364
- package/dist/index.js.map +16 -103
- package/package.json +4 -46
package/CHANGELOG.md
CHANGED
|
@@ -1,131 +1,7 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
One short line per release. For the full rationale behind each entry — the constraints,
|
|
4
|
-
the rejected alternatives, and the validation that was run — see
|
|
5
|
-
[`docs/decisions/decision-log.md`](docs/decisions/decision-log.md).
|
|
6
|
-
|
|
7
3
|
## [Unreleased]
|
|
8
4
|
|
|
9
|
-
## [
|
|
10
|
-
|
|
11
|
-
Let strict OpenCode hosts require both `flow_review_record` envelopes by accepting `null` for the inactive sibling while preserving wrong-scope rejection.
|
|
12
|
-
|
|
13
|
-
## [3.3.21] - 2026-06-14
|
|
14
|
-
|
|
15
|
-
Nest `flow_review_record` payloads under `featureReview` or `finalReview` so the host schema is simple and the runtime reviewer schemas stay strict.
|
|
16
|
-
|
|
17
|
-
## [3.3.20] - 2026-06-14
|
|
18
|
-
|
|
19
|
-
Hide the Flow reviewer behind `/flow-review` and `/flow-auto`, keeping direct reviewer selection advisory instead of a confusing session entry point.
|
|
20
|
-
|
|
21
|
-
## [3.3.19] - 2026-06-14
|
|
22
|
-
|
|
23
|
-
Ship deslop and UI-quality skills so cleanup and frontend work get evidence-backed rubrics without adding commands, tools, agents, or runtime state.
|
|
24
|
-
|
|
25
|
-
## [3.3.18] - 2026-06-14
|
|
26
|
-
|
|
27
|
-
Split the context-pack, markdown render, and distribution sync hotspots into smaller owner-named modules while preserving the public package, runtime, and workflow surfaces.
|
|
28
|
-
|
|
29
|
-
## [3.3.17] - 2026-06-14
|
|
30
|
-
|
|
31
|
-
Teach every Flow skill to use one shared read-only parallel orchestration protocol, bundle the shared references, and lock recovery-code documentation to the runtime matrix.
|
|
32
|
-
|
|
33
|
-
## [3.3.16] - 2026-06-14
|
|
34
|
-
|
|
35
|
-
Serialize Flow tool mutations through a session transaction so concurrent planning and execution updates compose instead of racing stale reads or stale docs.
|
|
36
|
-
|
|
37
|
-
## [3.3.15] - 2026-06-14
|
|
38
|
-
|
|
39
|
-
Clarify derived signal authority so readiness blockers stay operational, context quality stays advisory, and context packs carry the same handoff contract.
|
|
40
|
-
|
|
41
|
-
## [3.3.14] - 2026-06-14
|
|
42
|
-
|
|
43
|
-
Delete stale architecture/test/docs scaffolding that only protected retired prompt/audit history or implementation-shape assertions, with no runtime or public surface change.
|
|
44
|
-
|
|
45
|
-
## [3.3.13] - 2026-06-14
|
|
46
|
-
|
|
47
|
-
Remove v2 session-resume compatibility, tighten retired-key payload parsing, shrink the current maintainer contract, and drop empty `core`/`workflow` owners from seam enforcement.
|
|
48
|
-
|
|
49
|
-
## [3.3.12] - 2026-06-14
|
|
50
|
-
|
|
51
|
-
Fix-forward the hosted release by making the empty `src/core` guard pass on clean checkouts where Git does not materialize empty directories.
|
|
52
|
-
|
|
53
|
-
## [3.3.11] - 2026-06-14
|
|
54
|
-
|
|
55
|
-
Trim-down pass: fix the dead workspace-root guard branch and mark the trusted-roots flag as advisory; remove the self-referential semantic-invariant registries (keeping the behavioral tests); shrink the strict-object JSON scanner to JSON.parse plus a focused duplicate-key check; and right-size the docs (changelog split into a lean log plus a decision journal, release notes consolidated, single-author framing).
|
|
56
|
-
|
|
57
|
-
## [3.3.10] - 2026-06-14
|
|
58
|
-
|
|
59
|
-
Teach planning and review to fan out read-only discovery to host subagents while Flow execution stays one feature at a time.
|
|
60
|
-
|
|
61
|
-
## [3.3.9] - 2026-06-14
|
|
62
|
-
|
|
63
|
-
Add `flow_context`, a read-only eighth tool that inspects the active session's context pack without mutating `.flow/**`.
|
|
64
|
-
|
|
65
|
-
## [3.3.8] - 2026-06-14
|
|
66
|
-
|
|
67
|
-
Derive `workflowReadiness` and `contextTraceability` so status answers whether a session is ready to move and flags scope drift.
|
|
68
|
-
|
|
69
|
-
## [3.3.7] - 2026-06-14
|
|
70
|
-
|
|
71
|
-
Render the planned context as a derived `docs/context.md` pack and surface advisory context diagnostics in `/flow-status`.
|
|
72
|
-
|
|
73
|
-
## [3.3.6] - 2026-06-14
|
|
74
|
-
|
|
75
|
-
Add maintainer affordances: contribution preflight, an architecture-metrics report, and release-candidate smoke evidence.
|
|
76
|
-
|
|
77
|
-
## [3.3.5] - 2026-06-13
|
|
78
|
-
|
|
79
|
-
Broaden the architecture seam guardrail to enforce the living source-ownership map and run it in `bun run check`.
|
|
80
|
-
|
|
81
|
-
## [3.3.4] - 2026-06-13
|
|
82
|
-
|
|
83
|
-
Prune the pre-v3 doc maze; promote durable lessons into ADR 0001, the skill review checklist, and the maintainer contract.
|
|
84
|
-
|
|
85
|
-
## [3.3.3] - 2026-06-13
|
|
86
|
-
|
|
87
|
-
Split the runtime action hotspot by responsibility while keeping `actions.ts` as a stable compatibility facade.
|
|
88
|
-
|
|
89
|
-
## [3.3.2] - 2026-06-13
|
|
90
|
-
|
|
91
|
-
Make the public docs match the real completion contract: validation evidence, `validationScope`, `featureReview`, and final `finalReview`.
|
|
92
|
-
|
|
93
|
-
## [3.3.1] - 2026-06-13
|
|
94
|
-
|
|
95
|
-
Route tool-surface logging through the same safe `createFlowLog` wrapper as plugin startup.
|
|
96
|
-
|
|
97
|
-
## [3.3.0] - 2026-06-13
|
|
98
|
-
|
|
99
|
-
Add an audit rubric and adversarial findings review so blocking audit findings must survive refutation before shipping.
|
|
100
|
-
|
|
101
|
-
## [3.2.2] - 2026-06-13
|
|
102
|
-
|
|
103
|
-
Critical fix: plugin no longer crashes on load against the generated OpenCode SDK client (unbound `app.log`).
|
|
104
|
-
|
|
105
|
-
## [3.2.1] - 2026-06-13
|
|
106
|
-
|
|
107
|
-
Make a missing `flow_*` backend an explicit stop-and-tell-the-user condition in the skills instead of a silent downgrade.
|
|
108
|
-
|
|
109
|
-
## [3.2.0] - 2026-06-13
|
|
110
|
-
|
|
111
|
-
Surface stale installs: a passive update notice, the running version in `/flow-status`, and documented update steps.
|
|
112
|
-
|
|
113
|
-
## [3.1.0] - 2026-06-12
|
|
114
|
-
|
|
115
|
-
Trim to the v3 surface: five commands and seven tools, with the v2 tool-name redirect stubs removed.
|
|
116
|
-
|
|
117
|
-
## [3.0.1] - 2026-06-12
|
|
118
|
-
|
|
119
|
-
Sync slash commands and the `flow-reviewer` agent as real discoverable files, not just config-hook injections.
|
|
120
|
-
|
|
121
|
-
## [3.0.0] - 2026-06-12
|
|
122
|
-
|
|
123
|
-
The skills-first inversion: hand-authored skills carry the workflow and the plugin shrinks to a safe state backend (18 tools to 7).
|
|
124
|
-
|
|
125
|
-
## [2.1.0] - 2026-06-12
|
|
126
|
-
|
|
127
|
-
Move distribution to npm, adopt the Promise-based permission API, and land Phase 1 of the skills-first overhaul.
|
|
128
|
-
|
|
129
|
-
---
|
|
5
|
+
## [4.0.0] - 2026-06-14
|
|
130
6
|
|
|
131
|
-
|
|
7
|
+
Breaking overhaul: Flow is now a skills-first plugin with a minimal v4 runtime ledger, seven tools, one active `.flow/session.json`, archived history, embedded review evidence on completion, and no context-pack or separate review-decision framework.
|
package/README.md
CHANGED
|
@@ -1,221 +1,106 @@
|
|
|
1
1
|
# Flow Plugin for OpenCode
|
|
2
2
|
|
|
3
|
-
`opencode-plugin-flow`
|
|
3
|
+
`opencode-plugin-flow` is a skills-first workflow helper for OpenCode. The skills carry the planning, execution, validation, cleanup, UI, and review guidance. The plugin code is deliberately small: it keeps a durable `.flow/session.json` ledger and enforces the few binary gates that prompts cannot reliably enforce.
|
|
4
4
|
|
|
5
|
-
Flow is
|
|
6
|
-
|
|
7
|
-
## Quick start
|
|
8
|
-
|
|
9
|
-
Install Flow, open OpenCode in the project you want to work on, then run:
|
|
10
|
-
|
|
11
|
-
```text
|
|
12
|
-
/flow-auto <your goal>
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
Common examples:
|
|
16
|
-
|
|
17
|
-
```text
|
|
18
|
-
/flow-auto Add CSV export to the reports page
|
|
19
|
-
/flow-plan Refactor the authentication middleware
|
|
20
|
-
/flow-review Review this repository for release risks
|
|
21
|
-
/flow-status
|
|
22
|
-
```
|
|
23
|
-
|
|
24
|
-
Flow creates a tracked session under `.flow/**`, drafts a plan, executes one feature at a time, records validation evidence, reviews, and either continues, recovers, or stops with a concrete blocker.
|
|
25
|
-
|
|
26
|
-
It also renders a reviewable context pack for each session so the files, contracts, validation checks, and planning assumptions the agent relied on are visible after compaction or handoff.
|
|
5
|
+
Flow v4 is a breaking simplification. It does not preserve v3 session layouts or retired tool aliases.
|
|
27
6
|
|
|
28
7
|
## Install
|
|
29
8
|
|
|
30
|
-
Add Flow to
|
|
9
|
+
Add Flow to your OpenCode config:
|
|
31
10
|
|
|
32
11
|
```json
|
|
33
12
|
{
|
|
34
|
-
"plugin": ["opencode-plugin-flow@
|
|
13
|
+
"plugin": ["opencode-plugin-flow@4.0.0"]
|
|
35
14
|
}
|
|
36
15
|
```
|
|
37
16
|
|
|
38
|
-
OpenCode
|
|
39
|
-
|
|
40
|
-
On startup the plugin syncs its global skills, commands, and review agent into OpenCode's normal discovery paths:
|
|
17
|
+
Restart OpenCode once. On startup, the plugin syncs its global skills into:
|
|
41
18
|
|
|
42
19
|
```text
|
|
43
|
-
~/.config/opencode/skills/flow/SKILL.md
|
|
44
|
-
~/.config/opencode/skills/flow-
|
|
45
|
-
~/.config/opencode/skills/flow-
|
|
46
|
-
~/.config/opencode/skills/flow-
|
|
47
|
-
~/.config/opencode/skills/flow-
|
|
48
|
-
~/.config/opencode/skills/flow-ui-quality/SKILL.md
|
|
49
|
-
~/.config/opencode/commands/flow-auto.md # slash command pointers
|
|
50
|
-
~/.config/opencode/agents/flow-reviewer.md # hidden read-only review subagent
|
|
20
|
+
~/.config/opencode/skills/flow/SKILL.md
|
|
21
|
+
~/.config/opencode/skills/flow-plan/SKILL.md
|
|
22
|
+
~/.config/opencode/skills/flow-run/SKILL.md
|
|
23
|
+
~/.config/opencode/skills/flow-review/SKILL.md
|
|
24
|
+
~/.config/opencode/skills/flow-deslop/SKILL.md
|
|
25
|
+
~/.config/opencode/skills/flow-ui-quality/SKILL.md
|
|
51
26
|
```
|
|
52
27
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
Sync is ownership-aware: each Flow-owned skill folder carries a `.flow-skill-version` marker with a sha256 line per shipped file, and each synced command/agent file has a sidecar `.flow-version` marker. Folders or files without Flow markers are never touched, and if you edit a Flow-owned file by hand the previous content is backed up next to it before an update replaces it.
|
|
56
|
-
|
|
57
|
-
### Updating
|
|
58
|
-
|
|
59
|
-
OpenCode never auto-updates plugins: the cached install for a given spec string is reused as-is on every startup. Flow therefore checks npm in the background after startup and logs a one-line notice when a newer release exists — it only notifies and never edits your `opencode.json`. Set `FLOW_DISABLE_UPDATE_CHECK=1` to turn the check off.
|
|
60
|
-
|
|
61
|
-
To update with an exact pin (recommended):
|
|
62
|
-
|
|
63
|
-
1. Change the pin in `opencode.json` to the new version (same form as the install snippet above).
|
|
64
|
-
2. Restart OpenCode once to install the new version, and a second time so the freshly re-synced skills, commands, and agents are discovered.
|
|
65
|
-
|
|
66
|
-
If you pinned a range like `@3` instead, the spec string never changes, so the cache entry must be cleared by hand before restarting:
|
|
67
|
-
|
|
68
|
-
```bash
|
|
69
|
-
rm -rf ~/.cache/opencode/packages/opencode-plugin-flow@3
|
|
70
|
-
```
|
|
71
|
-
|
|
72
|
-
`/flow-status` shows the running plugin version in its install check, so you can always confirm which version OpenCode actually loaded.
|
|
73
|
-
|
|
74
|
-
### Per-project skill overrides
|
|
75
|
-
|
|
76
|
-
Skills are plain markdown and deliberately overridable. To customize Flow's behavior for one project — for example a team-specific planning or review rubric — place a project-local skill at:
|
|
28
|
+
Project-local skill overrides still work through OpenCode's normal lookup:
|
|
77
29
|
|
|
78
30
|
```text
|
|
79
31
|
.opencode/skills/flow-plan/SKILL.md
|
|
80
32
|
```
|
|
81
33
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
### Upgrading from the pre-npm (curl) install
|
|
85
|
-
|
|
86
|
-
Releases before 2.1.0 installed a bundled plugin file at `~/.config/opencode/plugins/flow.js`. Once you add the npm plugin entry, that copy would load Flow twice — the plugin warns about this at startup. Remove it with the uninstall command below.
|
|
87
|
-
|
|
88
|
-
### Uninstall
|
|
89
|
-
|
|
90
|
-
```bash
|
|
91
|
-
bunx opencode-plugin-flow uninstall
|
|
92
|
-
```
|
|
34
|
+
## Commands
|
|
93
35
|
|
|
94
|
-
|
|
36
|
+
Commands are thin pointers into skills:
|
|
95
37
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
### The six skills
|
|
99
|
-
|
|
100
|
-
| Skill | What it carries |
|
|
101
|
-
| --- | --- |
|
|
102
|
-
| `flow` | The driving loop: check status, plan, run, review, repeat; stop conditions, when to ask the user, recovery playbook. |
|
|
103
|
-
| `flow-deslop` | Evidence-backed cleanup, code-smell classification, safe refactor workflow, and AI-slop removal guidance. |
|
|
104
|
-
| `flow-plan` | How to decompose work into features, size them, profile the repo, and when a plan is safe to auto-approve. |
|
|
105
|
-
| `flow-run` | One-feature-at-a-time discipline and what counts as validation evidence. |
|
|
106
|
-
| `flow-review` | Review depth criteria, finding taxonomy, and report format. |
|
|
107
|
-
| `flow-ui-quality` | UX/UI design intent, frontend polish, accessibility, responsive states, and visual verification guidance. |
|
|
108
|
-
|
|
109
|
-
Deeper methodology (recovery, shared parallel orchestration, worked plan examples, validation, audit, review, deslop, refactor, UI, and visual verification rubrics) lives in `references/` files next to the skills and is loaded only when needed.
|
|
110
|
-
|
|
111
|
-
### Commands
|
|
112
|
-
|
|
113
|
-
Commands are thin pointers into the skills — the skill content is the real instruction surface.
|
|
114
|
-
|
|
115
|
-
| Command | Use it when... |
|
|
38
|
+
| Command | Purpose |
|
|
116
39
|
| --- | --- |
|
|
117
|
-
| `/flow-auto <goal>` |
|
|
118
|
-
| `/flow-plan <goal>` |
|
|
119
|
-
| `/flow-run
|
|
120
|
-
| `/flow-review
|
|
121
|
-
| `/flow-status` |
|
|
122
|
-
|
|
123
|
-
These five are the whole command surface since v3.1. The v2/v3.0 convenience commands (`/flow-doctor`, `/flow-history`, `/flow-session`, `/flow-reset`) were retired: each was a thin wrapper over a single tool call that works as a plain request — "show the flow history", "close this session as completed", "reset feature X" — and `/flow-doctor` duplicated `/flow-status`. Startup sync removes the retired command files from earlier installs (user-edited copies are kept).
|
|
40
|
+
| `/flow-auto <goal>` | Drive the full skill-guided loop. |
|
|
41
|
+
| `/flow-plan <goal>` | Create or approve a plan. |
|
|
42
|
+
| `/flow-run` | Execute one approved feature. |
|
|
43
|
+
| `/flow-review` | Run a read-only review. |
|
|
44
|
+
| `/flow-status` | Show the active session and next action. |
|
|
124
45
|
|
|
125
|
-
|
|
46
|
+
## Tools
|
|
126
47
|
|
|
127
|
-
The
|
|
48
|
+
The v4 runtime exposes seven tools:
|
|
128
49
|
|
|
129
50
|
| Tool | Purpose |
|
|
130
51
|
| --- | --- |
|
|
131
|
-
| `flow_status` |
|
|
132
|
-
| `
|
|
133
|
-
| `
|
|
134
|
-
| `flow_plan_approve` | Approve the plan, optionally restricted to a feature subset. |
|
|
52
|
+
| `flow_status` | Read the active session and next action. |
|
|
53
|
+
| `flow_plan_save` | Create a session and/or save a draft plan. |
|
|
54
|
+
| `flow_plan_approve` | Approve the draft plan. |
|
|
135
55
|
| `flow_run_start` | Start the next runnable feature. |
|
|
136
|
-
| `flow_feature_complete` | Record
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
|
|
140
|
-
These eight tools are the whole registered surface — the v2 tool-name redirect stubs that shipped in 3.0 were removed in v3.1 as scheduled.
|
|
141
|
-
|
|
142
|
-
### Agents
|
|
143
|
-
|
|
144
|
-
Flow ships one dedicated hidden subagent: `flow-reviewer`, a read-only reviewer used behind `/flow-review` and Flow's review gates. Its read-only boundary is enforced by OpenCode's native per-agent permissions, not by prompt text. Start user-facing work with `/flow-auto` or `/flow-review`; everything else runs in your normal agent, guided by the skills.
|
|
145
|
-
|
|
146
|
-
## What the plugin enforces vs. what skills guide
|
|
147
|
-
|
|
148
|
-
The plugin code enforces only binary runtime gates and persistence safety:
|
|
149
|
-
|
|
150
|
-
1. Completion payloads must carry recorded passing validation evidence. Non-final features require `validationScope: "targeted"`; the feature that completes the session requires `validationScope: "broad"`.
|
|
151
|
-
2. Completion payloads must carry a passing `featureReview`. The final completion payload must also carry a passing `finalReview` whose `reviewDepth` matches the plan's `deliveryPolicy.finalReviewPolicy`.
|
|
152
|
-
3. A session cannot close as `completed` with unfinished target work.
|
|
153
|
-
4. An approved plan cannot be mutated without an explicit reset.
|
|
154
|
-
5. If the session's review policy is strict, a recorded approved reviewer decision is required before completion.
|
|
155
|
-
|
|
156
|
-
Plus: atomic, locked, path-safe writes under `.flow/**`; schema validation of all tool payloads; and the compaction hook that keeps Flow state intact when OpenCode compacts a long session.
|
|
56
|
+
| `flow_feature_complete` | Record completion or blocker evidence for the active feature. |
|
|
57
|
+
| `flow_feature_reset` | Reset one feature and its dependents. |
|
|
58
|
+
| `flow_session_close` | Archive the active session as completed, deferred, or abandoned. |
|
|
157
59
|
|
|
158
|
-
|
|
60
|
+
There is no `flow_context` and no separate review-record tool. Review evidence is part of `flow_feature_complete`: every completed feature needs a passing `featureReview`, and the final feature also needs a passing `finalReview`.
|
|
159
61
|
|
|
160
|
-
|
|
62
|
+
## Runtime Contract
|
|
161
63
|
|
|
162
|
-
|
|
163
|
-
- **Workflow blocker** — derived status says the agent should not proceed until the issue is resolved or explicitly justified. `workflowReadiness.state` values such as `blocked_by_context`, `blocked_by_validation`, and `blocked_by_review` are workflow blockers, not persisted state or runtime invariants.
|
|
164
|
-
- **Advisory diagnostic** — useful planning or review signal, but judgment-owned. `contextQuality` and broad/weak-context diagnostics are advisory unless they expose concrete drift that readiness reports as blocked.
|
|
64
|
+
The runtime owns only safety:
|
|
165
65
|
|
|
166
|
-
|
|
66
|
+
- `.flow/session.json` is the active source of truth.
|
|
67
|
+
- `.flow/history/<session-id>.json` stores closed sessions.
|
|
68
|
+
- Session writes are locked and atomic.
|
|
69
|
+
- Mutable roots cannot be filesystem roots or `$HOME`.
|
|
70
|
+
- Plans cannot be changed after approval.
|
|
71
|
+
- Only one feature can be active at a time.
|
|
72
|
+
- Completion requires passing validation evidence.
|
|
73
|
+
- Non-final completion requires `validationScope: "targeted"`.
|
|
74
|
+
- Final completion requires `validationScope: "broad"` and a passing final review matching the plan's `finalReviewPolicy`.
|
|
75
|
+
- `flow_session_close` accepts `kind: "completed"` only after an approved plan has passed final completion.
|
|
167
76
|
|
|
168
|
-
|
|
77
|
+
Planning quality, decomposition, review depth, validation adequacy, and recovery judgment live in the skills.
|
|
169
78
|
|
|
170
|
-
|
|
79
|
+
## State Layout
|
|
171
80
|
|
|
172
81
|
```text
|
|
173
|
-
.flow/
|
|
174
|
-
.flow/
|
|
175
|
-
.flow/
|
|
176
|
-
.flow/stored/<session-id>/session.json # parked resumable sessions
|
|
177
|
-
.flow/completed/<session-id>-<timestamp>/** # closed session history
|
|
178
|
-
.flow/locks/
|
|
82
|
+
.flow/session.json
|
|
83
|
+
.flow/history/<session-id>.json
|
|
84
|
+
.flow/session.lock/
|
|
179
85
|
```
|
|
180
86
|
|
|
181
|
-
|
|
87
|
+
## Development
|
|
182
88
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
```text
|
|
188
|
-
/flow-status
|
|
89
|
+
```bash
|
|
90
|
+
bun install
|
|
91
|
+
bun run check
|
|
189
92
|
```
|
|
190
93
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
## OpenCode references
|
|
194
|
-
|
|
195
|
-
- Plugins: https://opencode.ai/docs/plugins
|
|
196
|
-
- Skills: https://opencode.ai/docs/skills
|
|
197
|
-
- Agents: https://opencode.ai/docs/agents
|
|
198
|
-
|
|
199
|
-
## Releases
|
|
200
|
-
|
|
201
|
-
Release notes live in [`CHANGELOG.md`](CHANGELOG.md) and under [`docs/releases/`](docs/releases/).
|
|
202
|
-
|
|
203
|
-
## Working on Flow itself
|
|
204
|
-
|
|
205
|
-
- Development guide: [`docs/development.md`](docs/development.md)
|
|
206
|
-
- Maintainer contract: [`docs/maintainer-contract.md`](docs/maintainer-contract.md)
|
|
207
|
-
- Codebase map: [`docs/contributor-map.md`](docs/contributor-map.md)
|
|
208
|
-
|
|
209
|
-
### Package API boundary
|
|
210
|
-
|
|
211
|
-
`opencode-plugin-flow` supports root-only imports:
|
|
94
|
+
The package exports only the OpenCode plugin entrypoint:
|
|
212
95
|
|
|
213
96
|
```ts
|
|
214
97
|
import flowPlugin from "opencode-plugin-flow";
|
|
215
98
|
```
|
|
216
99
|
|
|
217
|
-
|
|
100
|
+
## Uninstall
|
|
218
101
|
|
|
219
|
-
|
|
102
|
+
```bash
|
|
103
|
+
bunx opencode-plugin-flow uninstall
|
|
104
|
+
```
|
|
220
105
|
|
|
221
|
-
|
|
106
|
+
This removes Flow-owned synced skills when they are pristine. User-edited or foreign skill folders are kept.
|
package/dist/cli.js
CHANGED
|
@@ -1,35 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
3
|
-
|
|
4
|
-
`)
|
|
5
|
-
`);
|
|
6
|
-
`)
|
|
7
|
-
`)
|
|
8
|
-
`)){let n=s.indexOf("=");if(n===-1)continue;t.set(s.slice(0,n),s.slice(n+1))}let i=t.get("plugin"),a=t.get("version");if(i!==Q||!a)return null;let o=t.get("hash");return{plugin:i,version:a,hash:o?.startsWith("sha256:")?o.slice(7):null}}import{mkdir as Dt,rm as S,writeFile as Kt}from"node:fs/promises";import{join as Y,sep as Et}from"node:path";import{readFile as ae}from"node:fs/promises";async function w(e){try{return await ae(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}function J(e){return`${["---",`description: ${JSON.stringify(e.description)}`,...e.agent?[`agent: ${JSON.stringify(e.agent)}`]:[],...e.subtask===void 0?[]:[`subtask: ${e.subtask}`],"---"].join(`
|
|
9
|
-
`)}
|
|
10
|
-
|
|
11
|
-
${e.template}
|
|
12
|
-
`}function O(e){return`${["---",`description: ${JSON.stringify(e.description)}`,`mode: ${e.mode}`,...e.hidden===void 0?[]:[`hidden: ${e.hidden}`],...e.reasoningEffort?[`reasoningEffort: ${e.reasoningEffort}`]:[],...e.permission?ie(e.permission):[],"---"].join(`
|
|
13
|
-
`)}
|
|
14
|
-
|
|
15
|
-
${e.prompt}
|
|
16
|
-
`}function x(){return new Map(Object.entries(V).map(([e,t])=>[e,J(t)]))}function _(){return new Map(Object.entries(W).map(([e,t])=>[e,O(t)]))}async function j(e){let t=[],i=[];for(let a of e.names){let o=Y(e.root,`${a}.md`),s=Y(e.root,`.${a}.flow-version`),n=await w(s),r=n===null?null:k(n,e.kind,a);if(r===null)continue;let c=await w(o);if(c!==null&&h(c)!==r.hash){i.push(o);continue}if(!e.dryRun)await S(o,{force:!0}),await S(s,{force:!0}),await S(`${o}.backup`,{force:!0});t.push(o)}return{removed:t,keptUserEdited:i}}function ie(e){if(!e)return[];let t=["permission:"];for(let[i,a]of Object.entries(e)){if(typeof a==="string"){t.push(` ${JSON.stringify(i)}: ${JSON.stringify(a)}`);continue}if(a&&typeof a==="object"){t.push(` ${JSON.stringify(i)}:`);for(let[o,s]of Object.entries(a))t.push(` ${JSON.stringify(o)}: ${JSON.stringify(s)}`)}}return t}async function $({homeDir:e,dryRun:t=!1,logger:i}){let a={removedSkills:[],keptUserEditedSkills:[],removedCommands:[],keptUserEditedCommands:[],removedAgents:[],keptUserEditedAgents:[],removedPreNpmPlugin:null,keptForeignPreNpmPlugin:null},o=l(e,A);for(let c of await we(o)){if(c!=="flow"&&!c.startsWith("flow-"))continue;let d=l(o,c),p=await fe(d);if(p==="foreign")continue;if(p==="user_edited"){a.keptUserEditedSkills.push(d),i?.(`Kept user-edited Flow skill at ${d}; remove it manually if it is no longer needed.`);continue}if(!t)await ve(d);a.removedSkills.push(d),i?.(`${t?"Would remove":"Removed"} Flow skill at ${d}.`)}let s=await j({kind:"command",root:l(e,m),names:R,dryRun:t});for(let c of s.removed)a.removedCommands.push(c),i?.(`${t?"Would remove":"Removed"} retired Flow command at ${c}.`);for(let c of s.keptUserEdited)a.keptUserEditedCommands.push(c),i?.(`Kept user-edited Flow command at ${c}; remove it manually if it is no longer needed.`);await Z({homeDir:e,dryRun:t,logger:i,kind:"command",root:l(e,m),files:x(),removed:a.removedCommands,keptUserEdited:a.keptUserEditedCommands}),await Z({homeDir:e,dryRun:t,logger:i,kind:"agent",root:l(e,I),files:_(),removed:a.removedAgents,keptUserEdited:a.keptUserEditedAgents});let n=l(e,T),r=await f(n);if(r!==null)if(r.startsWith(q)){if(!t)await u(n,{force:!0});a.removedPreNpmPlugin=n,i?.(`${t?"Would remove":"Removed"} pre-npm Flow plugin copy at ${n}.`)}else a.keptForeignPreNpmPlugin=n,i?.(`Kept ${n}: it is not managed by Flow. Remove it manually if it is a stale Flow copy.`);return i?.('Finally, remove "opencode-plugin-flow" from the plugin array in opencode.json and restart OpenCode.'),a}async function fe(e){let t=l(e,"SKILL.md"),i=await f(t),a=await f(l(e,v)),o=a===null?null:F(a);if(o!==null&&a!==null){for(let[r,c]of b(a)){if(r==="SKILL.md")continue;let d=N(e,r);if(d===null)continue;let p=await f(d);if(p!==null&&h(p)!==c)return"user_edited"}if(i===null)return"pristine";if(o.hash!==null&&h(i)===o.hash)return"pristine";return y(i).kind==="valid_generated"?"pristine":"user_edited"}if(i===null)return"foreign";let s=y(i);if(s.kind==="valid_generated")return"pristine";if(s.kind==="invalid_generated")return"user_edited";return"foreign"}function N(e,t){let i=he(l(e,...t.split("/")));if(i!==e&&i.startsWith(`${e}${pe}`))return i;return null}async function ve(e){let t=await f(l(e,v)),i=new Set;if(t!==null)for(let a of b(t).keys()){let o=N(e,a);if(o===null)continue;await u(o,{force:!0}),await u(`${o}.backup`,{force:!0});let s=ue(o);if(s!==e)i.add(s)}await u(l(e,"SKILL.md"),{force:!0}),await u(l(e,v),{force:!0}),await u(l(e,G),{force:!0});for(let a of[...i].sort((o,s)=>s.length-o.length))await U(a);await U(e)}async function U(e){try{await de(e)}catch(t){let i=t.code;if(i!=="ENOENT"&&i!=="ENOTEMPTY")throw t}}async function we(e){try{return(await ce(e,{withFileTypes:!0})).filter((i)=>i.isDirectory()).map((i)=>i.name).sort()}catch(t){if(t.code==="ENOENT")return[];throw t}}async function f(e){try{return await le(e,"utf8")}catch(t){if(t.code==="ENOENT")return null;throw t}}async function Z(e){for(let[t,i]of e.files){let a=l(e.root,`${t}.md`),o=l(e.root,`.${t}.flow-version`),s=await f(a),n=await f(o),r=n===null?null:k(n,e.kind,t);if(s===null&&r===null)continue;if(!(r!==null||s===i))continue;if(s!==null&&r!==null&&h(s)!==r.hash){e.keptUserEdited.push(a),e.logger?.(`Kept user-edited Flow ${e.kind} at ${a}; remove it manually if it is no longer needed.`);continue}if(!e.dryRun)await u(a,{force:!0}),await u(o,{force:!0}),await u(`${a}.backup`,{force:!0});e.removed.push(a),e.logger?.(`${e.dryRun?"Would remove":"Removed"} Flow ${e.kind} at ${a}.`)}if(!e.dryRun)await U(e.root)}var B=`opencode-plugin-flow — Flow plugin lifecycle commands
|
|
17
|
-
|
|
18
|
-
Usage:
|
|
19
|
-
bunx opencode-plugin-flow uninstall [--dry-run]
|
|
20
|
-
|
|
21
|
-
Commands:
|
|
22
|
-
uninstall Remove Flow-owned global skills from ~/.config/opencode/skills/
|
|
23
|
-
and the pre-npm plugin copy at ~/.config/opencode/plugins/flow.js.
|
|
24
|
-
Prints the opencode.json cleanup step; never touches files that
|
|
25
|
-
are not Flow-owned.
|
|
26
|
-
|
|
27
|
-
Options:
|
|
28
|
-
--dry-run Show what would be removed without deleting anything
|
|
29
|
-
--help Show this message`;function D(e){process.stdout.write(`${e}
|
|
30
|
-
`)}function z(e){process.stderr.write(`${e}
|
|
31
|
-
`)}async function me(e){let t=[...e];if(t.length===0||t.includes("--help")||t.includes("-h"))return D(B),0;let i=t.shift();if(i!=="uninstall")return z(`Unknown command: ${i}
|
|
32
|
-
|
|
33
|
-
${B}`),1;let a=!1;for(let o of t){if(o==="--dry-run"){a=!0;continue}return z(`Unknown argument: ${o}
|
|
34
|
-
|
|
35
|
-
${B}`),1}return await $({homeDir:process.env.HOME??ge(),dryRun:a,logger:D}),0}try{process.exitCode=await me(process.argv.slice(2))}catch(e){z(e instanceof Error?e.message:String(e)),process.exitCode=1}
|
|
2
|
+
import{createHash as g}from"node:crypto";import{mkdir as Xe,readdir as w,readFile as y,rm as b,writeFile as $e}from"node:fs/promises";import{dirname as Ye,join as n,normalize as k,sep as x}from"node:path";var _=".flow-skill-version";function p(){return process.env.HOME??process.env.USERPROFILE??""}function I(e=p()){return n(e,".config","opencode","skills")}function R(e){return g("sha256").update(e).digest("hex")}async function u(e){try{return await y(e,"utf8")}catch(a){if(a.code==="ENOENT")return null;throw a}}function F(e){let a=new Map;if(!e)return a;for(let t of e.split(/\r?\n/)){let s=/^file=(.+) sha256=([a-f0-9]{64})$/.exec(t)??/^file=(.+)=sha256:([a-f0-9]{64})$/.exec(t);if(s?.[1]&&s[2])a.set(s[1],s[2]);let i=/^hash=sha256:([a-f0-9]{64})$/.exec(t);if(i?.[1]&&!a.has("SKILL.md"))a.set("SKILL.md",i[1])}return a}function q(e,a){let t=k(n(e,...a.split("/")));if(t!==e&&t.startsWith(`${e}${x}`))return t;throw Error(`Unsafe skill file path '${a}'.`)}async function h(e=p()){let a=I(e),t=[],s=[],i;try{i=await w(a)}catch(o){if(o.code==="ENOENT")return{removed:t,kept:s};throw o}for(let o of i){if(o!=="flow"&&!o.startsWith("flow-"))continue;let r=n(a,o),l=await u(n(r,_));if(l===null){s.push(r);continue}let f=F(l),c=!1;for(let[m,v]of f){let d=await u(q(r,m));if(d!==null&&R(d)!==v){c=!0;break}}if(c){s.push(r);continue}await b(r,{recursive:!0,force:!0}),t.push(r)}return{removed:t,kept:s}}async function A(e){if(e[2]!=="uninstall"){process.stderr.write(`usage: opencode-plugin-flow uninstall
|
|
3
|
+
`),process.exitCode=2;return}let t=await h();for(let s of t.removed)process.stdout.write(`Removed Flow skill: ${s}
|
|
4
|
+
`);for(let s of t.kept)process.stdout.write(`Kept non-Flow or user-edited skill: ${s}
|
|
5
|
+
`);process.stdout.write(`Remove "opencode-plugin-flow" from opencode.json and restart OpenCode.
|
|
6
|
+
`)}A(process.argv).catch((e)=>{process.stderr.write(`${e instanceof Error?e.message:String(e)}
|
|
7
|
+
`),process.exitCode=1});
|