opencode-ship 0.2.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 +57 -0
- package/LICENSE +21 -0
- package/README.md +136 -0
- package/assets/agents/delivery-reviewer.md +80 -0
- package/assets/agents/delivery-verifier.md +61 -0
- package/assets/skills/delivery-workflow/SKILL.md +62 -0
- package/assets/skills/planning-research-checkpoint/SKILL.md +37 -0
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +2484 -0
- package/dist/core.d.ts +1 -0
- package/dist/core.js +1851 -0
- package/dist/plugin.d.ts +4 -0
- package/dist/plugin.js +15060 -0
- package/docs/adr/0001-opencode-ship-redesign.md +168 -0
- package/package.json +62 -0
- package/schema/project-adapter.example.json +50 -0
- package/schema/project-adapter.schema.json +132 -0
- package/schema/project-opencode-shim.json +42 -0
- package/schema/ship-config.schema.json +136 -0
- package/schema/ship-lock.schema.json +112 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# ADR 0001 — `opencode-ship`: npm-distributed, single-command installer
|
|
2
|
+
|
|
3
|
+
Status: Accepted
|
|
4
|
+
|
|
5
|
+
## Context
|
|
6
|
+
|
|
7
|
+
The current package, `opencode-delivery`, is a private source-level ESM
|
|
8
|
+
core library. Consumer projects adopt it by copying a project plugin
|
|
9
|
+
shim into `opencode.json` and pinning a Git commit hash. Each new
|
|
10
|
+
release is a manual PR into every consumer repo. There is no CLI, no
|
|
11
|
+
generated plugin bundle, no managed-file policy, and no upgrade path
|
|
12
|
+
short of consumer-side edits.
|
|
13
|
+
|
|
14
|
+
Consumer pain observed in the wild:
|
|
15
|
+
|
|
16
|
+
- Every onboarding requires editing `opencode.json`, copying the shim,
|
|
17
|
+
copying the two agents, copying the two skills, copying the
|
|
18
|
+
`delivery.json` adapter, copying the workflow lock, and remembering to
|
|
19
|
+
rename the plugin when the package moves.
|
|
20
|
+
- Updates require a Git ref ping and a manual diff; tooling cannot
|
|
21
|
+
reason about who owns which file.
|
|
22
|
+
- Cleanup is only triggered manually and silently fails when the local
|
|
23
|
+
branch head SHA drifts from the manifest.
|
|
24
|
+
|
|
25
|
+
## Decision
|
|
26
|
+
|
|
27
|
+
Re-establish the package as `opencode-ship`, a publishable npm module
|
|
28
|
+
with three surfaces:
|
|
29
|
+
|
|
30
|
+
1. A self-contained compiled ESM OpenCode plugin at the package root
|
|
31
|
+
(`opencode-ship`) that registers the canonical nine `delivery_*`
|
|
32
|
+
tools and removes the need for any consumer-side plugin wrapper.
|
|
33
|
+
2. A first-class CLI (`opencode-ship` binary) materialising every
|
|
34
|
+
required asset in the consumer repo through five idempotent commands.
|
|
35
|
+
3. A `opencode-ship/core` subpath that preserves the existing library
|
|
36
|
+
surface so consumer scripts that already import from the package can
|
|
37
|
+
migrate at their own pace.
|
|
38
|
+
|
|
39
|
+
### Managed-file policy
|
|
40
|
+
|
|
41
|
+
Five output classes are installer-managed and recorded in the lock:
|
|
42
|
+
|
|
43
|
+
- `.opencode/plugin/opencode-ship.js` — the bundled plugin.
|
|
44
|
+
- `.opencode/agents/delivery-reviewer.md`,
|
|
45
|
+
`.opencode/agents/delivery-verifier.md`.
|
|
46
|
+
- `.opencode/skills/delivery-workflow/SKILL.md`,
|
|
47
|
+
`.opencode/skills/planning-research-checkpoint/SKILL.md`.
|
|
48
|
+
- `.opencode/ship.config.json` — user-owned, written only on the
|
|
49
|
+
first `init` if missing.
|
|
50
|
+
- `.opencode/ship.lock.json` — installer-managed.
|
|
51
|
+
|
|
52
|
+
Root `opencode.json` / `opencode.jsonc` is a shared document. The
|
|
53
|
+
installer owns only the Build-agent delivery permissions and the
|
|
54
|
+
delivery subagent delegation allow-list; everything else stays.
|
|
55
|
+
|
|
56
|
+
`update` only replaces managed files whose hashes still match the
|
|
57
|
+
previous manifest. Modified managed files are refused with a precise
|
|
58
|
+
conflict report; the user either restores the upstream bytes (the
|
|
59
|
+
preferred repair) or opts into `--replace-managed`. We never silently
|
|
60
|
+
overwrite.
|
|
61
|
+
|
|
62
|
+
### Reconciliation algorithm
|
|
63
|
+
|
|
64
|
+
For each managed target, three hashes are computed:
|
|
65
|
+
|
|
66
|
+
- `B` = the SHA-256 stored in the previous lock for that path.
|
|
67
|
+
- `C` = the SHA-256 of the bytes currently on disk.
|
|
68
|
+
- `D` = the SHA-256 of the bytes we want to install.
|
|
69
|
+
|
|
70
|
+
The combination determines the action:
|
|
71
|
+
|
|
72
|
+
| Condition | Plan |
|
|
73
|
+
| --- | --- |
|
|
74
|
+
| No lock, target absent | `create` |
|
|
75
|
+
| No lock, target present | `report` (unowned collision) |
|
|
76
|
+
| `C == B == D` | `noop` |
|
|
77
|
+
| `C == B`, `D != B` | `update` |
|
|
78
|
+
| `C == D`, `C != B` | `converge` (refresh lock only) |
|
|
79
|
+
| `C != B`, `C != D` | `conflict` (refuse) |
|
|
80
|
+
| Target removed, `C == B` | `delete` |
|
|
81
|
+
| Target removed, `C != B` | `conflict` |
|
|
82
|
+
|
|
83
|
+
Conflict semantics are uniform across `init`, `update`, and `uninstall`.
|
|
84
|
+
|
|
85
|
+
### Transactions
|
|
86
|
+
|
|
87
|
+
Multi-file updates are recoverable but not truly atomic. The transaction
|
|
88
|
+
layer:
|
|
89
|
+
|
|
90
|
+
1. Acquires an exclusive repository lock file under `.git/opencode-ship/`.
|
|
91
|
+
2. Re-reads and re-hashes every target before staging.
|
|
92
|
+
3. Stages each target as a sibling temporary file and `fsync`s it.
|
|
93
|
+
4. Writes a journal recording original paths, backup paths, hashes,
|
|
94
|
+
and operation order.
|
|
95
|
+
5. Renames each managed target to a backup, then promotes the staged
|
|
96
|
+
copy into place, then `fsync`s the parent directory.
|
|
97
|
+
6. Promotes the new lock last as the commit marker.
|
|
98
|
+
7. Removes backups and journal after commit.
|
|
99
|
+
|
|
100
|
+
Pre-commit failures roll back in reverse order. Post-commit failure
|
|
101
|
+
treats the plan as committed and surfaces a degraded-cleanup warning;
|
|
102
|
+
the journal is cleaned on the next mutating command.
|
|
103
|
+
|
|
104
|
+
### CLI surface
|
|
105
|
+
|
|
106
|
+
```
|
|
107
|
+
opencode-ship init [--root <path>] [--config <path>] [--force-config]
|
|
108
|
+
opencode-ship diff [--root <path>] [--json] [--config <path>]
|
|
109
|
+
opencode-ship update [--root <path>] [--json] [--replace-managed]
|
|
110
|
+
opencode-ship doctor [--root <path>] [--json]
|
|
111
|
+
opencode-ship uninstall [--root <path>] [--json] [--purge-config]
|
|
112
|
+
opencode-ship --version
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
Exit codes:
|
|
116
|
+
|
|
117
|
+
- `0` success / no-op
|
|
118
|
+
- `1` expected negative result (`diff` saw changes; `doctor` unhealthy)
|
|
119
|
+
- `2` invalid input, unsupported project, ambiguous detection
|
|
120
|
+
- `3` ownership/hash/structural conflict
|
|
121
|
+
- `4` filesystem, staging, rollback, or transaction failure
|
|
122
|
+
- `5` unsupported lock/config schema
|
|
123
|
+
|
|
124
|
+
### Plugin + cleanup hardening
|
|
125
|
+
|
|
126
|
+
The bundled plugin no longer assumes `minimax/MiniMax-M3`; model
|
|
127
|
+
inheritance is the default. The verifier agent gains explicit denials
|
|
128
|
+
for the other eight tools so isolation is symmetric to the reviewer.
|
|
129
|
+
The Build agent gains explicit denials for `delivery_review` and
|
|
130
|
+
`delivery_verify`, plus an `ask` rule for `delivery_merge`. The
|
|
131
|
+
GitHub driver replaces the unsupported `gh pr view --json merged` field
|
|
132
|
+
with a typed `gh pr view ... --json state,mergedAt`.
|
|
133
|
+
|
|
134
|
+
Post-merge cleanup moves to immediate, atomic-enough execution after
|
|
135
|
+
an explicit user-authorised merge: validate the preconditions, persist
|
|
136
|
+
`cleanup-pending`, perform the worktree removal and CAS-style branch
|
|
137
|
+
deletion, seal the manifest, retry on the next delivery task if any
|
|
138
|
+
step failed. No force removal and no OpenCode restart.
|
|
139
|
+
|
|
140
|
+
### Compatibility strategy
|
|
141
|
+
|
|
142
|
+
Legacy `.opencode/delivery.json`, `.opencode/delivery.lock.json`, the
|
|
143
|
+
two agents, the two skills, and the generic `delivery.ts` wrapper
|
|
144
|
+
shape are recognised. Migration is opt-in by running `init` from a
|
|
145
|
+
checkout that already has those files. The consumer keeps the
|
|
146
|
+
existing manifest directory (`opencode-delivery/` under the Git common
|
|
147
|
+
dir) untouched to avoid stranding in-flight deliveries.
|
|
148
|
+
|
|
149
|
+
## Consequences
|
|
150
|
+
|
|
151
|
+
- Consumer repo touches only `.opencode/` and root
|
|
152
|
+
`opencode.json`/`.jsonc`. Initial onboarding becomes
|
|
153
|
+
`pnpm dlx opencode-ship init`.
|
|
154
|
+
- Upgrades become `opencode-ship update`, with conflicts surfaced
|
|
155
|
+
instead of overwritten.
|
|
156
|
+
- The library surface stays public through `opencode-ship/core`.
|
|
157
|
+
- v0.1.x becomes a legacy package; v0.2.0 is the first npm-distributed
|
|
158
|
+
release.
|
|
159
|
+
- The lock introduces a migration concern that the installer's
|
|
160
|
+
`doctor` must surface if the lock format is too old.
|
|
161
|
+
- A `cleanupPending` schedule runs only on consumer demand (the next
|
|
162
|
+
delivery start), never on package installation.
|
|
163
|
+
|
|
164
|
+
## Non-goals
|
|
165
|
+
|
|
166
|
+
- Publishing to a registry is deferred until this PR is approved.
|
|
167
|
+
- Migrating Leo into `opencode-ship` consumers is a separate follow-up.
|
|
168
|
+
- Changing the lifecycle state machine semantics.
|
package/package.json
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "opencode-ship",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"private": false,
|
|
5
|
+
"description": "npm-distributed OpenCode installer that materializes the delivery plugin, reviewer/verifier agents, skills, ship config and lock into any consumer repository.",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=22.6.0",
|
|
10
|
+
"opencode": ">=1.15.5"
|
|
11
|
+
},
|
|
12
|
+
"peerDependencies": {
|
|
13
|
+
"@opencode-ai/plugin": ">=1.15.5 <2"
|
|
14
|
+
},
|
|
15
|
+
"main": "./dist/plugin.js",
|
|
16
|
+
"types": "./dist/plugin.d.ts",
|
|
17
|
+
"exports": {
|
|
18
|
+
".": {
|
|
19
|
+
"types": "./dist/plugin.d.ts",
|
|
20
|
+
"import": "./dist/plugin.js"
|
|
21
|
+
},
|
|
22
|
+
"./core": {
|
|
23
|
+
"import": "./dist/core.js"
|
|
24
|
+
},
|
|
25
|
+
"./schema/project-adapter.schema.json": "./schema/project-adapter.schema.json",
|
|
26
|
+
"./schema/ship-config.schema.json": "./schema/ship-config.schema.json",
|
|
27
|
+
"./schema/ship-lock.schema.json": "./schema/ship-lock.schema.json",
|
|
28
|
+
"./package.json": "./package.json"
|
|
29
|
+
},
|
|
30
|
+
"bin": {
|
|
31
|
+
"opencode-ship": "./dist/cli.js"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist",
|
|
35
|
+
"assets",
|
|
36
|
+
"schema",
|
|
37
|
+
"docs",
|
|
38
|
+
"README.md",
|
|
39
|
+
"CHANGELOG.md",
|
|
40
|
+
"LICENSE"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "node scripts/build.mjs",
|
|
44
|
+
"prepack": "node scripts/prepack.mjs",
|
|
45
|
+
"lint": "node scripts/lint.mjs",
|
|
46
|
+
"typecheck": "node scripts/typecheck.mjs",
|
|
47
|
+
"test": "node scripts/run-all-tests.mjs",
|
|
48
|
+
"test:all": "node scripts/run-all-tests.mjs",
|
|
49
|
+
"test:installer": "node --test --test-concurrency=1 --test-reporter=spec tests/installer/**/*.test.mjs",
|
|
50
|
+
"test:plugin": "node --test --test-concurrency=1 --test-reporter=spec tests/plugin/**/*.test.mjs",
|
|
51
|
+
"test:package": "node --test --test-concurrency=1 --test-reporter=spec tests/package/*.test.mjs",
|
|
52
|
+
"format:check": "node scripts/format-check.mjs",
|
|
53
|
+
"verify": "node scripts/verify.mjs"
|
|
54
|
+
},
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@opencode-ai/plugin": "1.18.10",
|
|
57
|
+
"@types/node": "^22.10.2",
|
|
58
|
+
"esbuild": "^0.25.0",
|
|
59
|
+
"tsx": "^4.23.1",
|
|
60
|
+
"typescript": "^5.9.3"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://github.com/Viktorxyz/opencode-delivery/schema/project-adapter.schema.json",
|
|
3
|
+
"contractVersion": 1,
|
|
4
|
+
"repository": {
|
|
5
|
+
"remote": "origin",
|
|
6
|
+
"defaultBranch": { "discover": true }
|
|
7
|
+
},
|
|
8
|
+
"forge": {
|
|
9
|
+
"driver": "github",
|
|
10
|
+
"issueRequired": true,
|
|
11
|
+
"draftAfterFirstCommit": true,
|
|
12
|
+
"issueClosingSyntax": true
|
|
13
|
+
},
|
|
14
|
+
"worktree": {
|
|
15
|
+
"root": ".worktrees",
|
|
16
|
+
"branchTemplate": "{actor}/{slug}",
|
|
17
|
+
"bootstrap": [["<project-owned-bootstrap-argv>"]]
|
|
18
|
+
},
|
|
19
|
+
"verification": {
|
|
20
|
+
"commands": [
|
|
21
|
+
{ "id": "canonical", "argv": ["<project-owned-canonical-argv>"], "timeoutMs": 1200000 }
|
|
22
|
+
],
|
|
23
|
+
"requireCleanDiffAfter": true,
|
|
24
|
+
"invalidateOnHeadChange": true
|
|
25
|
+
},
|
|
26
|
+
"review": {
|
|
27
|
+
"agent": "delivery-reviewer",
|
|
28
|
+
"required": true,
|
|
29
|
+
"invalidateOnHeadChange": true
|
|
30
|
+
},
|
|
31
|
+
"ci": {
|
|
32
|
+
"driver": "github-status-checks",
|
|
33
|
+
"requiredChecks": ["delivery-verify"],
|
|
34
|
+
"wait": true,
|
|
35
|
+
"flakyRetry": 1
|
|
36
|
+
},
|
|
37
|
+
"ready": {
|
|
38
|
+
"requires": ["review", "local-verification", "remote-ci"],
|
|
39
|
+
"stopAfterReady": true
|
|
40
|
+
},
|
|
41
|
+
"merge": {
|
|
42
|
+
"strategy": "squash",
|
|
43
|
+
"policy": "explicit-user-request-only",
|
|
44
|
+
"requireFreshGates": true
|
|
45
|
+
},
|
|
46
|
+
"cleanup": {
|
|
47
|
+
"when": "next-task",
|
|
48
|
+
"requires": ["pr-merged", "worktree-clean", "no-unpublished-commits"]
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/Viktorxyz/opencode-delivery/schema/project-adapter.schema.json",
|
|
4
|
+
"title": "opencode-ship project adapter",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["contractVersion"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"contractVersion": { "const": 1 },
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "object",
|
|
12
|
+
"additionalProperties": false,
|
|
13
|
+
"properties": {
|
|
14
|
+
"remote": { "type": "string", "minLength": 1 },
|
|
15
|
+
"defaultBranch": {
|
|
16
|
+
"anyOf": [
|
|
17
|
+
{ "type": "string", "minLength": 1 },
|
|
18
|
+
{ "type": "object", "additionalProperties": false, "properties": { "discover": { "type": "boolean" }, "name": { "type": "string", "minLength": 1 } } }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"forge": {
|
|
24
|
+
"type": "object",
|
|
25
|
+
"additionalProperties": false,
|
|
26
|
+
"properties": {
|
|
27
|
+
"driver": { "const": "github" },
|
|
28
|
+
"issueRequired": { "type": "boolean" },
|
|
29
|
+
"draftAfterFirstCommit": { "type": "boolean" },
|
|
30
|
+
"issueClosingSyntax": { "type": "boolean" }
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"worktree": {
|
|
34
|
+
"type": "object",
|
|
35
|
+
"additionalProperties": false,
|
|
36
|
+
"properties": {
|
|
37
|
+
"root": { "type": "string", "minLength": 1 },
|
|
38
|
+
"branchTemplate": { "type": "string", "minLength": 1 },
|
|
39
|
+
"bootstrap": {
|
|
40
|
+
"type": "array",
|
|
41
|
+
"items": {
|
|
42
|
+
"type": "array",
|
|
43
|
+
"items": { "type": "string", "minLength": 1 },
|
|
44
|
+
"minItems": 1
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"verification": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"additionalProperties": false,
|
|
52
|
+
"properties": {
|
|
53
|
+
"commands": {
|
|
54
|
+
"type": "array",
|
|
55
|
+
"minItems": 1,
|
|
56
|
+
"items": {
|
|
57
|
+
"type": "object",
|
|
58
|
+
"required": ["id", "argv"],
|
|
59
|
+
"additionalProperties": false,
|
|
60
|
+
"properties": {
|
|
61
|
+
"id": { "type": "string", "minLength": 1 },
|
|
62
|
+
"argv": {
|
|
63
|
+
"type": "array",
|
|
64
|
+
"items": { "type": "string", "minLength": 1 },
|
|
65
|
+
"minItems": 1
|
|
66
|
+
},
|
|
67
|
+
"timeoutMs": { "type": "integer", "minimum": 1 }
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"requireCleanDiffAfter": { "type": "boolean" },
|
|
72
|
+
"invalidateOnHeadChange": { "type": "boolean" }
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
"review": {
|
|
76
|
+
"type": "object",
|
|
77
|
+
"additionalProperties": false,
|
|
78
|
+
"properties": {
|
|
79
|
+
"agent": { "type": "string", "minLength": 1 },
|
|
80
|
+
"required": { "type": "boolean" },
|
|
81
|
+
"invalidateOnHeadChange": { "type": "boolean" }
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
"ci": {
|
|
85
|
+
"type": "object",
|
|
86
|
+
"additionalProperties": false,
|
|
87
|
+
"properties": {
|
|
88
|
+
"driver": { "const": "github-status-checks" },
|
|
89
|
+
"requiredChecks": {
|
|
90
|
+
"type": "array",
|
|
91
|
+
"items": { "type": "string", "minLength": 1 },
|
|
92
|
+
"uniqueItems": true
|
|
93
|
+
},
|
|
94
|
+
"wait": { "type": "boolean" },
|
|
95
|
+
"flakyRetry": { "type": "integer", "enum": [0, 1] }
|
|
96
|
+
}
|
|
97
|
+
},
|
|
98
|
+
"ready": {
|
|
99
|
+
"type": "object",
|
|
100
|
+
"additionalProperties": false,
|
|
101
|
+
"properties": {
|
|
102
|
+
"requires": {
|
|
103
|
+
"type": "array",
|
|
104
|
+
"items": { "enum": ["review", "local-verification", "remote-ci"] },
|
|
105
|
+
"uniqueItems": true
|
|
106
|
+
},
|
|
107
|
+
"stopAfterReady": { "type": "boolean" }
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"merge": {
|
|
111
|
+
"type": "object",
|
|
112
|
+
"additionalProperties": false,
|
|
113
|
+
"properties": {
|
|
114
|
+
"strategy": { "const": "squash" },
|
|
115
|
+
"policy": { "const": "explicit-user-request-only" },
|
|
116
|
+
"requireFreshGates": { "type": "boolean" }
|
|
117
|
+
}
|
|
118
|
+
},
|
|
119
|
+
"cleanup": {
|
|
120
|
+
"type": "object",
|
|
121
|
+
"additionalProperties": false,
|
|
122
|
+
"properties": {
|
|
123
|
+
"when": { "const": "next-task" },
|
|
124
|
+
"requires": {
|
|
125
|
+
"type": "array",
|
|
126
|
+
"items": { "enum": ["pr-merged", "worktree-clean", "no-unpublished-commits"] },
|
|
127
|
+
"uniqueItems": true
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://opencode.ai/config.json",
|
|
3
|
+
"description": "Project plugin shim that loads the reusable opencode-delivery package. Pin the version by replacing <commit-sha> with a real commit SHA before adopting.",
|
|
4
|
+
"plugin": ["https://github.com/Viktorxyz/opencode-delivery#<commit-sha>"],
|
|
5
|
+
"agent": {
|
|
6
|
+
"build": {
|
|
7
|
+
"permission": {
|
|
8
|
+
"task": {
|
|
9
|
+
"*": "deny",
|
|
10
|
+
"explore": "allow",
|
|
11
|
+
"general": "allow",
|
|
12
|
+
"reviewer": "allow",
|
|
13
|
+
"verifier": "allow",
|
|
14
|
+
"delivery-reviewer": "allow",
|
|
15
|
+
"delivery-verifier": "allow",
|
|
16
|
+
"plan": "deny"
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"permission": {
|
|
22
|
+
"bash": {
|
|
23
|
+
"git commit --no-verify *": "deny",
|
|
24
|
+
"git push --force *": "deny",
|
|
25
|
+
"git push --force-with-lease *": "deny",
|
|
26
|
+
"git push -f *": "deny",
|
|
27
|
+
"git reset --hard *": "deny",
|
|
28
|
+
"git stash *": "deny",
|
|
29
|
+
"git worktree remove --force *": "deny",
|
|
30
|
+
"git branch -D *": "deny",
|
|
31
|
+
"git worktree remove *": "ask",
|
|
32
|
+
"git rebase *": "ask",
|
|
33
|
+
"git merge *": "ask",
|
|
34
|
+
"gh pr merge --auto *": "deny",
|
|
35
|
+
"gh pr merge *": "ask",
|
|
36
|
+
"gh pr close *": "ask",
|
|
37
|
+
"gh issue delete *": "deny",
|
|
38
|
+
"gh api *": "ask",
|
|
39
|
+
"*": "allow"
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
+
"$id": "https://github.com/Viktorxyz/opencode-delivery/schema/ship-config.schema.json",
|
|
4
|
+
"title": "opencode-ship user config",
|
|
5
|
+
"type": "object",
|
|
6
|
+
"required": ["schemaVersion"],
|
|
7
|
+
"additionalProperties": false,
|
|
8
|
+
"properties": {
|
|
9
|
+
"schemaVersion": { "const": 1 },
|
|
10
|
+
"owner": {
|
|
11
|
+
"type": "string",
|
|
12
|
+
"description": "Optional override for the issue/manifest owner field. Defaults to the agent's local user.name."
|
|
13
|
+
},
|
|
14
|
+
"project": {
|
|
15
|
+
"type": "object",
|
|
16
|
+
"additionalProperties": false,
|
|
17
|
+
"properties": {
|
|
18
|
+
"remote": { "type": "string", "minLength": 1 },
|
|
19
|
+
"repository": { "type": "string", "pattern": "^[A-Za-z0-9_.-]+/[A-Za-z0-9_.-]+$" },
|
|
20
|
+
"defaultBranch": { "type": "string", "minLength": 1 },
|
|
21
|
+
"packageManager": { "enum": ["npm", "pnpm", "yarn", "bun"] },
|
|
22
|
+
"detectOverrides": { "type": "boolean", "description": "Permit detection to refresh previously persisted values." }
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"delivery": {
|
|
26
|
+
"type": "object",
|
|
27
|
+
"additionalProperties": false,
|
|
28
|
+
"properties": {
|
|
29
|
+
"worktree": {
|
|
30
|
+
"type": "object",
|
|
31
|
+
"additionalProperties": false,
|
|
32
|
+
"properties": {
|
|
33
|
+
"root": { "type": "string", "minLength": 1 },
|
|
34
|
+
"branchTemplate": { "type": "string", "minLength": 1 },
|
|
35
|
+
"bootstrap": {
|
|
36
|
+
"type": "array",
|
|
37
|
+
"items": {
|
|
38
|
+
"type": "array",
|
|
39
|
+
"items": { "type": "string", "minLength": 1 },
|
|
40
|
+
"minItems": 1
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
"verification": {
|
|
46
|
+
"type": "object",
|
|
47
|
+
"additionalProperties": false,
|
|
48
|
+
"properties": {
|
|
49
|
+
"commands": {
|
|
50
|
+
"type": "array",
|
|
51
|
+
"minItems": 1,
|
|
52
|
+
"items": {
|
|
53
|
+
"type": "object",
|
|
54
|
+
"required": ["id", "argv"],
|
|
55
|
+
"additionalProperties": false,
|
|
56
|
+
"properties": {
|
|
57
|
+
"id": { "type": "string", "minLength": 1 },
|
|
58
|
+
"argv": {
|
|
59
|
+
"type": "array",
|
|
60
|
+
"items": { "type": "string", "minLength": 1 },
|
|
61
|
+
"minItems": 1
|
|
62
|
+
},
|
|
63
|
+
"timeoutMs": { "type": "integer", "minimum": 1 }
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"requireCleanDiffAfter": { "type": "boolean" },
|
|
68
|
+
"invalidateOnHeadChange": { "type": "boolean" }
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
"review": {
|
|
72
|
+
"type": "object",
|
|
73
|
+
"additionalProperties": false,
|
|
74
|
+
"properties": {
|
|
75
|
+
"agent": { "type": "string", "minLength": 1 },
|
|
76
|
+
"required": { "type": "boolean" },
|
|
77
|
+
"invalidateOnHeadChange": { "type": "boolean" }
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"ci": {
|
|
81
|
+
"type": "object",
|
|
82
|
+
"additionalProperties": false,
|
|
83
|
+
"properties": {
|
|
84
|
+
"driver": { "const": "github-status-checks" },
|
|
85
|
+
"requiredChecks": {
|
|
86
|
+
"type": "array",
|
|
87
|
+
"items": { "type": "string", "minLength": 1 },
|
|
88
|
+
"uniqueItems": true
|
|
89
|
+
},
|
|
90
|
+
"wait": { "type": "boolean" },
|
|
91
|
+
"flakyRetry": { "type": "integer", "enum": [0, 1] }
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
"ready": {
|
|
95
|
+
"type": "object",
|
|
96
|
+
"additionalProperties": false,
|
|
97
|
+
"properties": {
|
|
98
|
+
"requires": {
|
|
99
|
+
"type": "array",
|
|
100
|
+
"items": { "enum": ["review", "local-verification", "remote-ci"] },
|
|
101
|
+
"uniqueItems": true
|
|
102
|
+
},
|
|
103
|
+
"stopAfterReady": { "type": "boolean" }
|
|
104
|
+
}
|
|
105
|
+
},
|
|
106
|
+
"merge": {
|
|
107
|
+
"type": "object",
|
|
108
|
+
"additionalProperties": false,
|
|
109
|
+
"properties": {
|
|
110
|
+
"strategy": { "const": "squash" },
|
|
111
|
+
"policy": { "const": "explicit-user-request-only" },
|
|
112
|
+
"requireFreshGates": { "type": "boolean" }
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
"cleanup": {
|
|
116
|
+
"type": "object",
|
|
117
|
+
"additionalProperties": false,
|
|
118
|
+
"properties": {
|
|
119
|
+
"when": { "const": "next-task" },
|
|
120
|
+
"requireUnpublishedGuard": { "type": "boolean" }
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
},
|
|
125
|
+
"tasks": {
|
|
126
|
+
"type": "object",
|
|
127
|
+
"description": "Optional override of the managed-file paths. Use only to relocate a target.",
|
|
128
|
+
"additionalProperties": false,
|
|
129
|
+
"properties": {
|
|
130
|
+
"pluginPath": { "type": "string", "pattern": "^\\.opencode/.+\\.js$" },
|
|
131
|
+
"agentsDir": { "type": "string", "pattern": "^\\.opencode/agents/?$" },
|
|
132
|
+
"skillsDir": { "type": "string", "pattern": "^\\.opencode/skills/?$" }
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|