opencode-workflow-guard 1.1.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/ACKNOWLEDGEMENTS.md +23 -0
- package/LICENSE +21 -0
- package/README.md +110 -0
- package/package.json +46 -0
- package/src/workflow-guard-ui.ts +48 -0
- package/src/workflow-guard.ts +3305 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# Acknowledgements & Ecosystem Inspirations
|
|
2
|
+
|
|
3
|
+
`opencode-workflow-guard` is built on original, clean-room TypeScript code, but draws design inspiration, best practices, and workflow ideas from the broader [OpenCode](https://opencode.ai) community and ecosystem:
|
|
4
|
+
|
|
5
|
+
## Community Projects & Inspirations
|
|
6
|
+
|
|
7
|
+
* **[`opencode-shell-strategy`](https://github.com/JRedeker/opencode-shell-strategy)** by [@JRedeker](https://github.com/JRedeker)
|
|
8
|
+
* Inspired the interactive TTY detection patterns in **Policy 22** (Non-Interactive Shell & TTY Hang Guard) to prevent subshell freezes on interactive prompts.
|
|
9
|
+
|
|
10
|
+
* **[`envsitter-guard`](https://github.com/boxpositron/envsitter-guard)** by [@boxpositron](https://github.com/boxpositron)
|
|
11
|
+
* Inspired the safe variable schema masking approach in **Policy 17** (Secret-File Read Block & Schema Masking) so agents can discover environment variable names without exposing live secret values.
|
|
12
|
+
|
|
13
|
+
* **[`opencode-snip`](https://github.com/VincentHardouin/opencode-snip)** by [@VincentHardouin](https://github.com/VincentHardouin)
|
|
14
|
+
* Inspired the token-efficient test output truncation and failure prioritization in **Policy 10** (Evidence-Based Verification).
|
|
15
|
+
|
|
16
|
+
* **[`opencode-worktree`](https://github.com/kdcokenny/opencode-worktree)** by [@kdcokenny](https://github.com/kdcokenny)
|
|
17
|
+
* Inspired the isolated-worktree development workflow behind the **`guard_worktree_create` / `guard_worktree_cleanup`** tools (snapshot-commit before removal, `node_modules` sharing, storage outside the repository), re-implemented here as native in-process tools with zero external dependencies.
|
|
18
|
+
|
|
19
|
+
* **[`changesets`](https://github.com/changesets/changesets)** by the [Changesets Team](https://github.com/changesets/changesets)
|
|
20
|
+
* For the fragment-based changelog and versioning model that prevents merge conflicts across parallel feature PRs.
|
|
21
|
+
|
|
22
|
+
* **[OpenCode Team at Anomaly](https://github.com/anomalyco/opencode)**
|
|
23
|
+
* For building the OpenCode terminal AI coding agent and its extensible plugin architecture (`@opencode-ai/plugin`).
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ultus-net
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# opencode-workflow-guard
|
|
2
|
+
|
|
3
|
+
OpenCode plugin enforcing workflow discipline, agent focus, and deterministic safety boundaries through **hard plugin hooks** — not prompt instructions that LLMs can ignore.
|
|
4
|
+
|
|
5
|
+
Integrates with OpenCode's native todo system (`todowrite`), prevents accidental destructive actions, gates protected branches, enforces testing evidence before task finalization, and stops secret leakage.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
## Quick Start
|
|
10
|
+
|
|
11
|
+
### 1. Installation
|
|
12
|
+
|
|
13
|
+
Add to your project's `opencode.json` or global `~/.config/opencode/opencode.json`:
|
|
14
|
+
|
|
15
|
+
```json
|
|
16
|
+
{
|
|
17
|
+
"$schema": "https://opencode.ai/config.json",
|
|
18
|
+
"plugin": [
|
|
19
|
+
"opencode-workflow-guard"
|
|
20
|
+
]
|
|
21
|
+
}
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
*Requires OpenCode >= 1.18.* See [docs/installation.md](docs/installation.md) for local manual installation and TUI companion badge setup.
|
|
25
|
+
|
|
26
|
+
### 2. Verification
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
npm run typecheck # Strict TypeScript check (0 errors)
|
|
30
|
+
npm test # Run 320+ unit and adversarial tests
|
|
31
|
+
npm run test:all # Run full verification suite (typecheck + unit + live e2e)
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
---
|
|
35
|
+
|
|
36
|
+
## Repository Structure
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
opencode-workflow-guard/
|
|
40
|
+
├── src/ # Production plugin sources
|
|
41
|
+
│ ├── workflow-guard.ts # Core server hooks & 23 deterministic policies
|
|
42
|
+
│ └── workflow-guard-ui.ts # TUI companion prompt badge
|
|
43
|
+
├── test/ # Test suites
|
|
44
|
+
│ ├── test.mts # 320+ in-memory unit & adversarial tests
|
|
45
|
+
│ └── test-e2e.mts # Live runtime OpenCode loader tests
|
|
46
|
+
├── docs/ # In-depth documentation & guides
|
|
47
|
+
│ ├── installation.md # Full setup options, worktrees & permissions
|
|
48
|
+
│ ├── policies.md # Detailed 23-policy reference & overrides
|
|
49
|
+
│ ├── troubleshooting.md # Error diagnosis & bypass explanations
|
|
50
|
+
│ └── testing.md # Test architecture & coverage matrix
|
|
51
|
+
├── .changeset/ # Fragment-based versioning & release changelogs
|
|
52
|
+
├── ACKNOWLEDGEMENTS.md # Open-source community credits & inspirations
|
|
53
|
+
├── CONTRIBUTING.md # Development guide, test patterns & PR rules
|
|
54
|
+
├── CHANGELOG.md # Version history
|
|
55
|
+
└── SECURITY.md # Vulnerability disclosure policy
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## Summary of Enforced Policies
|
|
61
|
+
|
|
62
|
+
| # | Policy Group | Summary |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| **1** | **Task Gate & Lifecycle** | Gates file mutations on active `todowrite` tasks. Prevents silent task deletion; allows flexible completion order. |
|
|
65
|
+
| **2 & 7** | **Branch Protection** | Blocks direct edits & commits on `main`/`master`. Hard-blocks `git push ... main` and forced refspecs. |
|
|
66
|
+
| **3** | **PR Changelog & Changesets** | Requires PRs to include changesets (`.changeset/*.md`), `CHANGELOG.md` updates, or a `Changelog:` body section. |
|
|
67
|
+
| **4 & 9** | **Destructive CLI & Laundering** | Blocks destructive infrastructure, database, and system mutations unless overridden by user environment (`WORKFLOW_GUARD_ALLOW_LIVE=1`). Scans script payloads for smuggled commands. |
|
|
68
|
+
| **5** | **MCP Mutation Guard** | Blocks mutating GitHub & Azure DevOps MCP tools while allowing read-only inspection tools. |
|
|
69
|
+
| **6** | **Settings Tamper Guard** | Blocks agent modification of `opencode.json`, `.opencode/*`, or the guard's own plugin files. |
|
|
70
|
+
| **8** | **Workspace Boundary Guard** | Confines file mutations and shell redirects strictly within the project workspace root (symlink & `../` escape safe). |
|
|
71
|
+
| **10** | **Evidence-Based Verification** | Runs test verification before final task completion. Binds freshness to git state with token-efficient output snipping. |
|
|
72
|
+
| **11, 12, 17** | **Secret Protection & Masking** | Scans file payloads for API keys, scrubs sensitive env vars in agent subshells, and provides safe redacted `.env` variable schema masks (`KEY=********`). |
|
|
73
|
+
| **13 & 14** | **Audit Trail & Attribution** | Durable JSONL audit logging (`~/.local/state/opencode/workflow-guard/workflow-guard.jsonl`) with subagent hierarchy breadcrumbs. |
|
|
74
|
+
| **15 & 16** | **Compaction & TUI Feedback** | Preserves active task plans across context compactions; emits real-time TUI warning toasts and native desktop alerts. |
|
|
75
|
+
| **18** | **Interpreter Inline Evasion** | Scans inline scripts (`python -c`, `node -e`, `base64 | sh`) for smuggled live commands or config tampering. |
|
|
76
|
+
| **19 & 20** | **Pre-Flight Conflicts & Freshness** | Verifies clean `git merge-tree` mergeability and checks local base branch freshness before opening PRs. |
|
|
77
|
+
| **21** | **Documentation Synchronization** | Verifies documentation is updated when introducing new public features or policies. |
|
|
78
|
+
| **22** | **TTY Hang Guard** | Blocks interactive editors (`vim`, `nano`), pagers (`less`), and commands missing non-interactive flags (`npm init` without `-y`). |
|
|
79
|
+
| **23** | **Package Supply-Chain Hygiene** | Blocks destructive `npm audit fix --force`, global unversioned installs (`npm i -g`), and direct CLI `npm publish`. |
|
|
80
|
+
|
|
81
|
+
👉 **For complete policy specifications and override rules, see [docs/policies.md](docs/policies.md).**
|
|
82
|
+
|
|
83
|
+
---
|
|
84
|
+
|
|
85
|
+
## Custom Tools Registered in OpenCode
|
|
86
|
+
|
|
87
|
+
* `guard_status`: Inspect active guardrails, current branch protection, and verification/review status.
|
|
88
|
+
* `guard_audit`: View recent audit log entries recorded in `workflow-guard.jsonl`.
|
|
89
|
+
* `guard_why`: Simulate and explain whether a specific tool call or command would be blocked.
|
|
90
|
+
* `record_review`: Record secondary reviewer subagent approval or critique across the 5 core review axes.
|
|
91
|
+
* `guard_worktree_create`: Create an isolated git worktree (with shared `node_modules` symlink) for concurrent subagent execution; rejects invalid and protected branch names.
|
|
92
|
+
* `guard_worktree_cleanup`: Snapshot-commit remaining changes and remove an isolated worktree directory.
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## Documentation & Community
|
|
97
|
+
|
|
98
|
+
* [Installation Guide](docs/installation.md)
|
|
99
|
+
* [Policies & Overrides Reference](docs/policies.md)
|
|
100
|
+
* [Troubleshooting Guide](docs/troubleshooting.md)
|
|
101
|
+
* [Testing Guide](docs/testing.md)
|
|
102
|
+
* [Contributing Guide](CONTRIBUTING.md)
|
|
103
|
+
* [Acknowledgements & Ecosystem Credits](ACKNOWLEDGEMENTS.md)
|
|
104
|
+
* [Security Policy](SECURITY.md)
|
|
105
|
+
|
|
106
|
+
---
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
[MIT](LICENSE)
|
package/package.json
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-workflow-guard",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "OpenCode plugin enforcing workflow discipline through deterministic tool.execute.before hooks",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "src/workflow-guard.ts",
|
|
8
|
+
"publishConfig": {
|
|
9
|
+
"access": "public"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/ultus-net/opencode-workflow-guard.git"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"src/",
|
|
17
|
+
"README.md",
|
|
18
|
+
"LICENSE",
|
|
19
|
+
"ACKNOWLEDGEMENTS.md"
|
|
20
|
+
],
|
|
21
|
+
"keywords": [
|
|
22
|
+
"opencode",
|
|
23
|
+
"opencode-plugin",
|
|
24
|
+
"guard",
|
|
25
|
+
"workflow"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"test": "node test/test.mts",
|
|
29
|
+
"test:install": "node test/test-e2e.mts",
|
|
30
|
+
"test:all": "npm run typecheck && npm test && npm run test:install",
|
|
31
|
+
"typecheck": "tsc --noEmit -p tsconfig.json",
|
|
32
|
+
"build": "tsc --noEmit -p tsconfig.json",
|
|
33
|
+
"changeset": "changeset",
|
|
34
|
+
"version-packages": "changeset version",
|
|
35
|
+
"prepublishOnly": "npm run typecheck"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"@opencode-ai/plugin": "^1.18.21"
|
|
39
|
+
},
|
|
40
|
+
"devDependencies": {
|
|
41
|
+
"@changesets/cli": "^3.0.1",
|
|
42
|
+
"@opentui/solid": "^0.5.6",
|
|
43
|
+
"@types/node": "^26.2.0",
|
|
44
|
+
"typescript": "^7.0.2"
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import type { TuiPlugin, TuiPluginModule } from "@opencode-ai/plugin/tui";
|
|
2
|
+
import type { JSX } from "@opentui/solid";
|
|
3
|
+
import { createElement, insert, setProp } from "@opentui/solid";
|
|
4
|
+
|
|
5
|
+
type Child = JSX.Element | string | number | null | undefined | false;
|
|
6
|
+
|
|
7
|
+
function element(
|
|
8
|
+
tag: string,
|
|
9
|
+
props: Record<string, unknown>,
|
|
10
|
+
children: Child[] = [],
|
|
11
|
+
) {
|
|
12
|
+
const node = createElement(tag);
|
|
13
|
+
for (const [key, value] of Object.entries(props)) {
|
|
14
|
+
if (value !== undefined) setProp(node, key, value);
|
|
15
|
+
}
|
|
16
|
+
for (const child of children) {
|
|
17
|
+
if (child === null || child === undefined || child === false) continue;
|
|
18
|
+
insert(node, child);
|
|
19
|
+
}
|
|
20
|
+
return node as unknown as JSX.Element;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function text(props: Record<string, unknown>, children: Child[]) {
|
|
24
|
+
return element("text", props, children);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const BADGE = "🛡️ [Workflow Guard: Active]";
|
|
28
|
+
|
|
29
|
+
export const WorkflowGuardTui: TuiPlugin = async (api) => {
|
|
30
|
+
api.slots.register({
|
|
31
|
+
order: 1,
|
|
32
|
+
slots: {
|
|
33
|
+
home_prompt_right() {
|
|
34
|
+
const theme = api.theme.current;
|
|
35
|
+
return text({ fg: theme.success }, [BADGE]);
|
|
36
|
+
},
|
|
37
|
+
session_prompt_right() {
|
|
38
|
+
const theme = api.theme.current;
|
|
39
|
+
return text({ fg: theme.success }, [BADGE]);
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default {
|
|
46
|
+
id: "workflow-guard-ui",
|
|
47
|
+
tui: WorkflowGuardTui,
|
|
48
|
+
} satisfies TuiPluginModule;
|