opencode-ship 1.1.0 → 1.1.1-rc.9
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 +86 -11
- package/README.md +14 -13
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/assets/agents/ship-controller.md +1 -1
- package/assets/agents/ship-final-spec-reviewer.md +1 -1
- package/assets/agents/ship-final-standards-reviewer.md +1 -1
- package/assets/agents/ship-planner.md +1 -1
- package/assets/agents/ship-task-builder.md +1 -1
- package/assets/agents/ship-task-reviewer.md +1 -1
- package/dist/cli.js +1411 -1202
- package/dist/core.js +57 -4
- package/dist/plugin.js +203 -233
- package/docs/release/1.1.1-stabilization-plan.md +655 -0
- package/package.json +1 -1
- package/schema/ship-config.schema.json +24 -2
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/SKILL.md +0 -0
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/domain.md +0 -0
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/issue-tracker-github.md +0 -0
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/issue-tracker-gitlab.md +0 -0
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/issue-tracker-local.md +0 -0
- /package/assets/skills/{setup-engineering-workflow → setup-ship-workflow}/triage-labels.md +0 -0
package/dist/plugin.js
CHANGED
|
@@ -1,208 +1,10 @@
|
|
|
1
|
-
// opencode-ship v1.1.
|
|
1
|
+
// opencode-ship v1.1.1-rc.9
|
|
2
2
|
var __defProp = Object.defineProperty;
|
|
3
|
-
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
4
|
-
var __esm = (fn, res) => function __init() {
|
|
5
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
6
|
-
};
|
|
7
3
|
var __export = (target, all) => {
|
|
8
4
|
for (var name in all)
|
|
9
5
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
10
6
|
};
|
|
11
7
|
|
|
12
|
-
// src/profile.js
|
|
13
|
-
function isValidProfile(name) {
|
|
14
|
-
return typeof name === "string" && PROFILES.includes(name);
|
|
15
|
-
}
|
|
16
|
-
var PROFILES;
|
|
17
|
-
var init_profile = __esm({
|
|
18
|
-
"src/profile.js"() {
|
|
19
|
-
PROFILES = Object.freeze(["engineering"]);
|
|
20
|
-
}
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
// src/installer/json-pointer.js
|
|
24
|
-
function isObject2(v) {
|
|
25
|
-
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
26
|
-
}
|
|
27
|
-
function stableStringify(value) {
|
|
28
|
-
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
29
|
-
if (isObject2(value)) {
|
|
30
|
-
const keys = Object.keys(value).sort();
|
|
31
|
-
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
32
|
-
}
|
|
33
|
-
return JSON.stringify(value);
|
|
34
|
-
}
|
|
35
|
-
function canonicalJson(value) {
|
|
36
|
-
return stableStringify(value);
|
|
37
|
-
}
|
|
38
|
-
var init_json_pointer = __esm({
|
|
39
|
-
"src/installer/json-pointer.js"() {
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
|
|
43
|
-
// src/installer/hash.js
|
|
44
|
-
import { createHash as createHash8 } from "node:crypto";
|
|
45
|
-
function bytesHash(buffer) {
|
|
46
|
-
return createHash8("sha256").update(buffer).digest("hex");
|
|
47
|
-
}
|
|
48
|
-
function bytesHashString(text) {
|
|
49
|
-
return bytesHash(Buffer.from(text, "utf8"));
|
|
50
|
-
}
|
|
51
|
-
var init_hash = __esm({
|
|
52
|
-
"src/installer/hash.js"() {
|
|
53
|
-
init_json_pointer();
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
// src/installer/lock.js
|
|
58
|
-
var lock_exports = {};
|
|
59
|
-
__export(lock_exports, {
|
|
60
|
-
CURRENT_LOCK_SCHEMA: () => CURRENT_LOCK_SCHEMA,
|
|
61
|
-
computeIntegrity: () => computeIntegrity,
|
|
62
|
-
lockPath: () => lockPath,
|
|
63
|
-
lockSchemaRevision: () => lockSchemaRevision,
|
|
64
|
-
migrateLegacyLock: () => migrateLegacyLock,
|
|
65
|
-
readLock: () => readLock2,
|
|
66
|
-
readValidatedLock: () => readValidatedLock,
|
|
67
|
-
validateIntegrity: () => validateIntegrity,
|
|
68
|
-
validateLock: () => validateLock,
|
|
69
|
-
writeLock: () => writeLock
|
|
70
|
-
});
|
|
71
|
-
import { readFile as readFile11, writeFile as writeFile10, rename as rename4, mkdir as mkdir12 } from "node:fs/promises";
|
|
72
|
-
import { existsSync as existsSync10 } from "node:fs";
|
|
73
|
-
import { dirname as dirname6, resolve as resolve10 } from "node:path";
|
|
74
|
-
function lockSchemaRevision() {
|
|
75
|
-
return CURRENT_LOCK_SCHEMA;
|
|
76
|
-
}
|
|
77
|
-
function lockPath(repoRoot) {
|
|
78
|
-
return resolve10(repoRoot, ".opencode", "ship.lock.json");
|
|
79
|
-
}
|
|
80
|
-
async function readLock2(repoRoot) {
|
|
81
|
-
const path = lockPath(repoRoot);
|
|
82
|
-
if (!existsSync10(path)) return null;
|
|
83
|
-
try {
|
|
84
|
-
const raw = await readFile11(path, "utf8");
|
|
85
|
-
const parsed = JSON.parse(raw);
|
|
86
|
-
return parsed;
|
|
87
|
-
} catch {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
async function writeLock(repoRoot, lock) {
|
|
92
|
-
const path = lockPath(repoRoot);
|
|
93
|
-
await mkdir12(dirname6(path), { recursive: true });
|
|
94
|
-
const integrity = computeIntegrity(lock);
|
|
95
|
-
const finalLock = { ...lock, integrity };
|
|
96
|
-
const raw = JSON.stringify(finalLock, null, 2) + "\n";
|
|
97
|
-
const tmp = `${path}.tmp`;
|
|
98
|
-
await writeFile10(tmp, raw, "utf8");
|
|
99
|
-
await rename4(tmp, path);
|
|
100
|
-
return path;
|
|
101
|
-
}
|
|
102
|
-
function computeIntegrity(lock) {
|
|
103
|
-
const { integrity: _ignored, ...without } = lock ?? {};
|
|
104
|
-
void _ignored;
|
|
105
|
-
return {
|
|
106
|
-
lockSha256: bytesHashString(stableStringify(without))
|
|
107
|
-
};
|
|
108
|
-
}
|
|
109
|
-
async function validateIntegrity(lock) {
|
|
110
|
-
if (!lock?.integrity?.lockSha256) return false;
|
|
111
|
-
const expected = computeIntegrity(lock).lockSha256;
|
|
112
|
-
return expected === lock.integrity.lockSha256;
|
|
113
|
-
}
|
|
114
|
-
function validateLock(rawLock) {
|
|
115
|
-
if (rawLock === null || rawLock === void 0) {
|
|
116
|
-
return { ok: true, kind: "missing", issues: [] };
|
|
117
|
-
}
|
|
118
|
-
if (typeof rawLock !== "object" || Array.isArray(rawLock)) {
|
|
119
|
-
return { ok: false, kind: "shape", issues: ["lock root must be an object"] };
|
|
120
|
-
}
|
|
121
|
-
const issues = [];
|
|
122
|
-
let kind = "ok";
|
|
123
|
-
if (rawLock.contractVersion !== CURRENT_LOCK_SCHEMA && rawLock.contractVersion !== 1 && rawLock.contractVersion !== 2) {
|
|
124
|
-
issues.push(`unsupported contractVersion: ${JSON.stringify(rawLock.contractVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 2, or 1)`);
|
|
125
|
-
kind = "schema";
|
|
126
|
-
}
|
|
127
|
-
const manager = rawLock.manager;
|
|
128
|
-
if (manager === void 0) {
|
|
129
|
-
issues.push("manager section missing");
|
|
130
|
-
kind = kind === "ok" ? "shape" : kind;
|
|
131
|
-
} else if (typeof manager !== "object" || manager === null) {
|
|
132
|
-
issues.push("manager section must be an object");
|
|
133
|
-
kind = kind === "ok" ? "shape" : kind;
|
|
134
|
-
} else if (manager.schemaVersion !== CURRENT_LOCK_SCHEMA && manager.schemaVersion !== 2 && manager.schemaVersion !== 1) {
|
|
135
|
-
issues.push(`unsupported manager.schemaVersion: ${JSON.stringify(manager.schemaVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 2, or 1)`);
|
|
136
|
-
kind = "schema";
|
|
137
|
-
} else if (manager.name !== "opencode-ship") {
|
|
138
|
-
issues.push(`unknown manager.name: ${JSON.stringify(manager.name)}`);
|
|
139
|
-
kind = "shape";
|
|
140
|
-
} else if (rawLock.contractVersion >= 2 && manager.schemaVersion >= 2 && manager.profile !== void 0 && !isValidProfile(manager.profile)) {
|
|
141
|
-
issues.push(`invalid manager.profile: ${JSON.stringify(manager.profile)} (expected one of: core, engineering)`);
|
|
142
|
-
kind = "shape";
|
|
143
|
-
}
|
|
144
|
-
if (!rawLock.files || !Array.isArray(rawLock.files)) {
|
|
145
|
-
issues.push("files must be an array");
|
|
146
|
-
kind = kind === "ok" ? "shape" : kind;
|
|
147
|
-
}
|
|
148
|
-
if (!rawLock.integrity || typeof rawLock.integrity !== "object") {
|
|
149
|
-
issues.push("integrity section missing");
|
|
150
|
-
kind = kind === "ok" ? "shape" : kind;
|
|
151
|
-
} else {
|
|
152
|
-
const expected = computeIntegrity(rawLock).lockSha256;
|
|
153
|
-
if (expected !== rawLock.integrity.lockSha256) {
|
|
154
|
-
issues.push(`integrity mismatch: stored ${rawLock.integrity.lockSha256} != computed ${expected}`);
|
|
155
|
-
kind = "integrity";
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return { ok: issues.length === 0, kind, issues };
|
|
159
|
-
}
|
|
160
|
-
async function readValidatedLock(repoRoot) {
|
|
161
|
-
const path = lockPath(repoRoot);
|
|
162
|
-
if (!existsSync10(path)) {
|
|
163
|
-
return { kind: "missing", lock: null, issues: [] };
|
|
164
|
-
}
|
|
165
|
-
let raw;
|
|
166
|
-
try {
|
|
167
|
-
const text = await readFile11(path, "utf8");
|
|
168
|
-
raw = JSON.parse(text);
|
|
169
|
-
} catch (e) {
|
|
170
|
-
return {
|
|
171
|
-
kind: "integrity",
|
|
172
|
-
lock: null,
|
|
173
|
-
issues: [`unable to parse lock JSON: ${e?.message ?? String(e)}`]
|
|
174
|
-
};
|
|
175
|
-
}
|
|
176
|
-
const validation = validateLock(raw);
|
|
177
|
-
return { kind: validation.kind, lock: validation.ok ? raw : null, issues: validation.issues };
|
|
178
|
-
}
|
|
179
|
-
async function migrateLegacyLock(repoRoot) {
|
|
180
|
-
const legacy = resolve10(repoRoot, ".opencode", "delivery.lock.json");
|
|
181
|
-
if (!existsSync10(legacy)) return null;
|
|
182
|
-
try {
|
|
183
|
-
const raw = await readFile11(legacy, "utf8");
|
|
184
|
-
const parsed = JSON.parse(raw);
|
|
185
|
-
if (parsed.contractVersion !== 1 || typeof parsed.adapterSha256 !== "string") return null;
|
|
186
|
-
return {
|
|
187
|
-
kind: "legacy-lock",
|
|
188
|
-
sourcePath: legacy,
|
|
189
|
-
payload: { contractVersion: 1, adapterSha256: parsed.adapterSha256, writtenAt: parsed.writtenAt ?? null },
|
|
190
|
-
sha256: bytesHashString(raw)
|
|
191
|
-
};
|
|
192
|
-
} catch {
|
|
193
|
-
return null;
|
|
194
|
-
}
|
|
195
|
-
}
|
|
196
|
-
var CURRENT_LOCK_SCHEMA;
|
|
197
|
-
var init_lock = __esm({
|
|
198
|
-
"src/installer/lock.js"() {
|
|
199
|
-
init_hash();
|
|
200
|
-
init_json_pointer();
|
|
201
|
-
init_profile();
|
|
202
|
-
CURRENT_LOCK_SCHEMA = 3;
|
|
203
|
-
}
|
|
204
|
-
});
|
|
205
|
-
|
|
206
8
|
// node_modules/zod/v4/classic/external.js
|
|
207
9
|
var external_exports = {};
|
|
208
10
|
__export(external_exports, {
|
|
@@ -14211,6 +14013,12 @@ function bucketFor(check2) {
|
|
|
14211
14013
|
if (check2.state === "failure") return "fail";
|
|
14212
14014
|
return "pending";
|
|
14213
14015
|
}
|
|
14016
|
+
function finalReviewGateSnapshot(manifest) {
|
|
14017
|
+
return {
|
|
14018
|
+
standards: manifest?.finalStandardsReview ?? null,
|
|
14019
|
+
spec: manifest?.finalSpecReview ?? null
|
|
14020
|
+
};
|
|
14021
|
+
}
|
|
14214
14022
|
function gateSnapshot({ manifest, prHead, checks }) {
|
|
14215
14023
|
const required2 = manifest.adapter?.ci?.requiredChecks ?? [];
|
|
14216
14024
|
const observed = checks ?? [];
|
|
@@ -14232,6 +14040,7 @@ function gateSnapshot({ manifest, prHead, checks }) {
|
|
|
14232
14040
|
prHead: prHead ?? null,
|
|
14233
14041
|
reviewerSha: manifest?.lastReviewerSha ?? null,
|
|
14234
14042
|
verifierSha: manifest?.lastVerifierSha ?? null,
|
|
14043
|
+
finalReviews: finalReviewGateSnapshot(manifest ?? {}),
|
|
14235
14044
|
checks: observed,
|
|
14236
14045
|
missingChecks: missing,
|
|
14237
14046
|
failingChecks: failing,
|
|
@@ -14241,10 +14050,39 @@ function gateSnapshot({ manifest, prHead, checks }) {
|
|
|
14241
14050
|
function checkGates({ manifest, prHead, checks, requires }) {
|
|
14242
14051
|
const snap = gateSnapshot({ manifest, prHead, checks });
|
|
14243
14052
|
const need = new Set(requires ?? ["review", "local-verification", "remote-ci"]);
|
|
14053
|
+
const hasDualFinalReview = Boolean(
|
|
14054
|
+
manifest?.finalStandardsReview || manifest?.finalSpecReview
|
|
14055
|
+
);
|
|
14244
14056
|
if (need.has("review")) {
|
|
14245
|
-
if (
|
|
14246
|
-
|
|
14247
|
-
|
|
14057
|
+
if (hasDualFinalReview) {
|
|
14058
|
+
const standards = manifest?.finalStandardsReview;
|
|
14059
|
+
const spec = manifest?.finalSpecReview;
|
|
14060
|
+
if (!standards || !standards.headSha) {
|
|
14061
|
+
return { ok: false, reason: "missing-final-review", axis: "standards", snapshot: snap };
|
|
14062
|
+
}
|
|
14063
|
+
if (!spec || !spec.headSha) {
|
|
14064
|
+
return { ok: false, reason: "missing-final-review", axis: "spec", snapshot: snap };
|
|
14065
|
+
}
|
|
14066
|
+
if (standards.headSha !== prHead) {
|
|
14067
|
+
return { ok: false, reason: "head-changed-after-final-review", axis: "standards", snapshot: snap };
|
|
14068
|
+
}
|
|
14069
|
+
if (spec.headSha !== prHead) {
|
|
14070
|
+
return { ok: false, reason: "head-changed-after-final-review", axis: "spec", snapshot: snap };
|
|
14071
|
+
}
|
|
14072
|
+
if (standards.headSha !== spec.headSha) {
|
|
14073
|
+
return { ok: false, reason: "final-review-head-mismatch", snapshot: snap };
|
|
14074
|
+
}
|
|
14075
|
+
if ((standards.packageHash ?? null) !== (spec.packageHash ?? null)) {
|
|
14076
|
+
return { ok: false, reason: "final-review-package-mismatch", snapshot: snap };
|
|
14077
|
+
}
|
|
14078
|
+
if ((standards.verdict ?? null) !== "pass" || (spec.verdict ?? null) !== "pass") {
|
|
14079
|
+
return { ok: false, reason: "final-review-failed", snapshot: snap };
|
|
14080
|
+
}
|
|
14081
|
+
} else {
|
|
14082
|
+
if (!manifest?.lastReviewerSha) return { ok: false, reason: "missing-review", snapshot: snap };
|
|
14083
|
+
if (manifest.lastReviewerSha !== prHead) {
|
|
14084
|
+
return { ok: false, reason: "head-changed-after-review", snapshot: snap };
|
|
14085
|
+
}
|
|
14248
14086
|
}
|
|
14249
14087
|
}
|
|
14250
14088
|
if (need.has("local-verification")) {
|
|
@@ -14272,6 +14110,17 @@ function gateFailureEnvelope(result) {
|
|
|
14272
14110
|
return { kind: "missing-gate", gate: "review" };
|
|
14273
14111
|
case "missing-verifier":
|
|
14274
14112
|
return { kind: "missing-gate", gate: "local-verification" };
|
|
14113
|
+
case "missing-final-review":
|
|
14114
|
+
return {
|
|
14115
|
+
kind: "missing-final-review",
|
|
14116
|
+
axis: result.axis
|
|
14117
|
+
};
|
|
14118
|
+
case "final-review-head-mismatch":
|
|
14119
|
+
return { kind: "final-review-head-mismatch" };
|
|
14120
|
+
case "final-review-package-mismatch":
|
|
14121
|
+
return { kind: "final-review-package-mismatch" };
|
|
14122
|
+
case "final-review-failed":
|
|
14123
|
+
return { kind: "final-review-failed" };
|
|
14275
14124
|
case "head-changed-after-review":
|
|
14276
14125
|
return {
|
|
14277
14126
|
kind: "head-changed-after-review",
|
|
@@ -14284,6 +14133,12 @@ function gateFailureEnvelope(result) {
|
|
|
14284
14133
|
headSha: result.snapshot.prHead ?? "",
|
|
14285
14134
|
verifierSha: result.snapshot.verifierSha ?? ""
|
|
14286
14135
|
};
|
|
14136
|
+
case "head-changed-after-final-review":
|
|
14137
|
+
return {
|
|
14138
|
+
kind: "head-changed-after-final-review",
|
|
14139
|
+
axis: result.axis,
|
|
14140
|
+
headSha: result.snapshot.prHead ?? ""
|
|
14141
|
+
};
|
|
14287
14142
|
case "ci-missing":
|
|
14288
14143
|
return { kind: "ci-missing", missing: result.snapshot.missingChecks };
|
|
14289
14144
|
case "ci-failing":
|
|
@@ -14918,8 +14773,11 @@ function createPublishTool(deps) {
|
|
|
14918
14773
|
};
|
|
14919
14774
|
}
|
|
14920
14775
|
|
|
14776
|
+
// src/profile.js
|
|
14777
|
+
var PROFILES = Object.freeze(["engineering"]);
|
|
14778
|
+
var LEGACY_PROFILES = Object.freeze(["core"]);
|
|
14779
|
+
|
|
14921
14780
|
// src/installer/engineering-config.js
|
|
14922
|
-
init_profile();
|
|
14923
14781
|
var MODEL_ID_RE = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
|
|
14924
14782
|
var DEFAULTS = Object.freeze({
|
|
14925
14783
|
planner: "openai/gpt-5.6-sol",
|
|
@@ -15005,8 +14863,23 @@ function createPlanStartTool(deps) {
|
|
|
15005
14863
|
};
|
|
15006
14864
|
}
|
|
15007
14865
|
|
|
14866
|
+
// src/installer/json-pointer.js
|
|
14867
|
+
function isObject2(v) {
|
|
14868
|
+
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
14869
|
+
}
|
|
14870
|
+
function stableStringify(value) {
|
|
14871
|
+
if (Array.isArray(value)) return `[${value.map(stableStringify).join(",")}]`;
|
|
14872
|
+
if (isObject2(value)) {
|
|
14873
|
+
const keys = Object.keys(value).sort();
|
|
14874
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(value[k])}`).join(",")}}`;
|
|
14875
|
+
}
|
|
14876
|
+
return JSON.stringify(value);
|
|
14877
|
+
}
|
|
14878
|
+
function canonicalJson(value) {
|
|
14879
|
+
return stableStringify(value);
|
|
14880
|
+
}
|
|
14881
|
+
|
|
15008
14882
|
// src/workflow/plan.js
|
|
15009
|
-
init_json_pointer();
|
|
15010
14883
|
import { createHash as createHash3 } from "node:crypto";
|
|
15011
14884
|
function isPlainObject2(v) {
|
|
15012
14885
|
return v !== null && typeof v === "object" && !Array.isArray(v);
|
|
@@ -15463,8 +15336,8 @@ function createRunStartTool(deps) {
|
|
|
15463
15336
|
if (!workflowId || !SAFE_ID_RE4.test(workflowId)) {
|
|
15464
15337
|
return failure("run-start", "workflowId required (safe id)", { operationId: opId, retryable: false });
|
|
15465
15338
|
}
|
|
15466
|
-
const
|
|
15467
|
-
const expectedModels =
|
|
15339
|
+
const configValue = deps.configValue ?? deps.config?.value ?? null;
|
|
15340
|
+
const expectedModels = configValue?.workflow?.models ?? null;
|
|
15468
15341
|
if (!expectedModels) {
|
|
15469
15342
|
return failure("run-start", "run-start requires configured workflow.models", { operationId: opId, retryable: false });
|
|
15470
15343
|
}
|
|
@@ -15577,7 +15450,9 @@ var EVENT_KINDS = Object.freeze({
|
|
|
15577
15450
|
TASK_REVIEW: "task-review",
|
|
15578
15451
|
COMMIT: "commit",
|
|
15579
15452
|
TASK_COMPLETE: "task-complete",
|
|
15453
|
+
ALL_TASKS_DONE: "all-tasks-done",
|
|
15580
15454
|
FINAL_REVIEW: "final-review",
|
|
15455
|
+
READY_PENDING: "ready-pending",
|
|
15581
15456
|
READY: "ready",
|
|
15582
15457
|
MERGE: "merge",
|
|
15583
15458
|
BLOCKED: "blocked"
|
|
@@ -15656,6 +15531,16 @@ function reduce(state, event) {
|
|
|
15656
15531
|
event: recorded(EVENT_KINDS.TASK_DISPATCH, { taskId: event.data.taskId, briefHash: event.data.briefHash, round })
|
|
15657
15532
|
};
|
|
15658
15533
|
}
|
|
15534
|
+
case EVENT_KINDS.TASK_REPORT: {
|
|
15535
|
+
if (state.state !== STATES2.RUNNING && state.state !== STATES2.FIX_PENDING || state.activeTask === null) {
|
|
15536
|
+
throw new Error(`run reducer: TASK_REPORT requires running with active task`);
|
|
15537
|
+
}
|
|
15538
|
+
ensureActiveTask(state, event.data.taskId);
|
|
15539
|
+
return {
|
|
15540
|
+
state: nextState({ state: STATES2.RUNNING, round: state.round + 1 }),
|
|
15541
|
+
event: recorded(EVENT_KINDS.TASK_REPORT, { taskId: event.data.taskId, reportHash: event.data.reportHash })
|
|
15542
|
+
};
|
|
15543
|
+
}
|
|
15659
15544
|
case EVENT_KINDS.TASK_REVIEW: {
|
|
15660
15545
|
if (state.state !== STATES2.RUNNING && state.state !== STATES2.FIX_PENDING || state.activeTask === null) {
|
|
15661
15546
|
throw new Error(`run reducer: TASK_REVIEW requires running with active task`);
|
|
@@ -15693,15 +15578,43 @@ function reduce(state, event) {
|
|
|
15693
15578
|
if (state.state !== STATES2.COMMITTED) {
|
|
15694
15579
|
throw new Error(`run reducer: TASK_COMPLETE requires state=committed, got ${state.state}`);
|
|
15695
15580
|
}
|
|
15581
|
+
const moreTasks = event.data.moreTasks === false ? false : true;
|
|
15582
|
+
const completedTasks = [...state.completedTasks, state.taskReady?.taskId].filter(Boolean);
|
|
15583
|
+
const nextStateObj = moreTasks ? { state: STATES2.RUNNING, activeTask: null, taskReady: null, round: 0, failures: 0, completedTasks } : { state: STATES2.ALL_TASKS_DONE, activeTask: null, taskReady: null, completedTasks };
|
|
15696
15584
|
return {
|
|
15697
|
-
state: nextState(
|
|
15698
|
-
event: recorded(EVENT_KINDS.TASK_COMPLETE, { taskId: event.data.taskId })
|
|
15585
|
+
state: nextState(nextStateObj),
|
|
15586
|
+
event: recorded(EVENT_KINDS.TASK_COMPLETE, { taskId: event.data.taskId, moreTasks })
|
|
15587
|
+
};
|
|
15588
|
+
}
|
|
15589
|
+
case EVENT_KINDS.FINAL_REVIEW: {
|
|
15590
|
+
if (state.state !== STATES2.ALL_TASKS_DONE && state.state !== STATES2.READY_PENDING) {
|
|
15591
|
+
throw new Error(`run reducer: FINAL_REVIEW requires state=all-tasks-done|ready-pending, got ${state.state}`);
|
|
15592
|
+
}
|
|
15593
|
+
const nextReadyPending = state.state === STATES2.READY_PENDING ? state : { ...state, state: STATES2.READY_PENDING, finalReview: { [event.data.axis]: event.data.review } };
|
|
15594
|
+
const finalReview = nextReadyPending.finalReview ?? {};
|
|
15595
|
+
finalReview[event.data.axis] = event.data.review;
|
|
15596
|
+
const bothRecorded = finalReview.standards && finalReview.spec;
|
|
15597
|
+
return {
|
|
15598
|
+
state: nextState({
|
|
15599
|
+
state: STATES2.READY_PENDING,
|
|
15600
|
+
finalReview
|
|
15601
|
+
}),
|
|
15602
|
+
event: recorded(EVENT_KINDS.FINAL_REVIEW, {
|
|
15603
|
+
axis: event.data.axis,
|
|
15604
|
+
verdict: event.data.verdict,
|
|
15605
|
+
headSha: event.data.headSha,
|
|
15606
|
+
mergeBaseSha: event.data.mergeBaseSha,
|
|
15607
|
+
packageHash: event.data.packageHash
|
|
15608
|
+
})
|
|
15699
15609
|
};
|
|
15700
15610
|
}
|
|
15701
15611
|
case EVENT_KINDS.READY: {
|
|
15702
15612
|
if (state.state !== STATES2.COMMITTED && state.state !== STATES2.READY_PENDING) {
|
|
15703
15613
|
throw new Error(`run reducer: READY requires state=committed|ready-pending, got ${state.state}`);
|
|
15704
15614
|
}
|
|
15615
|
+
if (state.state === STATES2.READY_PENDING && (!state.finalReview?.standards || !state.finalReview?.spec)) {
|
|
15616
|
+
throw new Error(`run reducer: READY requires both Standards and Spec final reviews`);
|
|
15617
|
+
}
|
|
15705
15618
|
return {
|
|
15706
15619
|
state: nextState({ state: STATES2.READY, activeTask: null, completedTasks: state.completedTasks }),
|
|
15707
15620
|
event: recorded(EVENT_KINDS.READY, { headSha: event.data.headSha })
|
|
@@ -16125,8 +16038,8 @@ var ship_config_schema_default = {
|
|
|
16125
16038
|
schemaVersion: { enum: [1, 2] },
|
|
16126
16039
|
profile: {
|
|
16127
16040
|
type: "string",
|
|
16128
|
-
enum: ["engineering"],
|
|
16129
|
-
description: "Active profile. Engineering is the only supported profile in 1.1.0."
|
|
16041
|
+
enum: ["engineering", "core"],
|
|
16042
|
+
description: "Active profile. Engineering is the only supported profile in 1.1.0; core is accepted on read for legacy consumer migration."
|
|
16130
16043
|
},
|
|
16131
16044
|
owner: {
|
|
16132
16045
|
type: "string",
|
|
@@ -16289,6 +16202,28 @@ var ship_config_schema_default = {
|
|
|
16289
16202
|
}
|
|
16290
16203
|
}
|
|
16291
16204
|
}
|
|
16205
|
+
},
|
|
16206
|
+
skillDiscovery: {
|
|
16207
|
+
type: "object",
|
|
16208
|
+
additionalProperties: false,
|
|
16209
|
+
description: "Trusted-auto skill discovery policy. Default mode is trusted-auto with the canonical owner allowlist and install-count threshold.",
|
|
16210
|
+
properties: {
|
|
16211
|
+
mode: {
|
|
16212
|
+
type: "string",
|
|
16213
|
+
enum: ["suggest-only", "trusted-auto", "disabled"]
|
|
16214
|
+
},
|
|
16215
|
+
trustedOwners: {
|
|
16216
|
+
type: "array",
|
|
16217
|
+
items: { type: "string", pattern: "^[A-Za-z0-9_.-]+$" }
|
|
16218
|
+
},
|
|
16219
|
+
minInstalls: { type: "integer", minimum: 0 },
|
|
16220
|
+
maxAutoInstall: { type: "integer", minimum: 0, maximum: 20 },
|
|
16221
|
+
blocklist: {
|
|
16222
|
+
type: "array",
|
|
16223
|
+
items: { type: "string", pattern: "^[A-Za-z0-9_./-]+$" }
|
|
16224
|
+
},
|
|
16225
|
+
requireImmutableRef: { type: "boolean" }
|
|
16226
|
+
}
|
|
16292
16227
|
}
|
|
16293
16228
|
}
|
|
16294
16229
|
};
|
|
@@ -16394,9 +16329,16 @@ function validateSchema(value, schema) {
|
|
|
16394
16329
|
return { ok: issues.length === 0, issues };
|
|
16395
16330
|
}
|
|
16396
16331
|
|
|
16332
|
+
// src/installer/hash.js
|
|
16333
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
16334
|
+
function bytesHash(buffer) {
|
|
16335
|
+
return createHash8("sha256").update(buffer).digest("hex");
|
|
16336
|
+
}
|
|
16337
|
+
function bytesHashString(text) {
|
|
16338
|
+
return bytesHash(Buffer.from(text, "utf8"));
|
|
16339
|
+
}
|
|
16340
|
+
|
|
16397
16341
|
// src/installer/config.js
|
|
16398
|
-
init_json_pointer();
|
|
16399
|
-
init_hash();
|
|
16400
16342
|
function configPath(repoRoot) {
|
|
16401
16343
|
return resolve9(repoRoot, ".opencode", "ship.config.json");
|
|
16402
16344
|
}
|
|
@@ -16467,8 +16409,23 @@ function renderDefaultConfig(detection, overrides = {}) {
|
|
|
16467
16409
|
};
|
|
16468
16410
|
}
|
|
16469
16411
|
|
|
16470
|
-
// src/
|
|
16471
|
-
|
|
16412
|
+
// src/installer/lock.js
|
|
16413
|
+
import { readFile as readFile11, writeFile as writeFile10, rename as rename4, mkdir as mkdir12 } from "node:fs/promises";
|
|
16414
|
+
import { existsSync as existsSync10 } from "node:fs";
|
|
16415
|
+
import { dirname as dirname6, resolve as resolve10 } from "node:path";
|
|
16416
|
+
function lockPath(repoRoot) {
|
|
16417
|
+
return resolve10(repoRoot, ".opencode", "ship.lock.json");
|
|
16418
|
+
}
|
|
16419
|
+
async function readLock2(repoRoot) {
|
|
16420
|
+
const path = lockPath(repoRoot);
|
|
16421
|
+
if (!existsSync10(path)) return null;
|
|
16422
|
+
try {
|
|
16423
|
+
const raw = await readFile11(path, "utf8");
|
|
16424
|
+
return JSON.parse(raw);
|
|
16425
|
+
} catch {
|
|
16426
|
+
return null;
|
|
16427
|
+
}
|
|
16428
|
+
}
|
|
16472
16429
|
|
|
16473
16430
|
// src/installer/detection/project.js
|
|
16474
16431
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
@@ -16616,17 +16573,14 @@ function detectProject(repoRoot = process.cwd()) {
|
|
|
16616
16573
|
// src/installer/cleanup.js
|
|
16617
16574
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
16618
16575
|
import { resolve as pathResolve } from "node:path";
|
|
16619
|
-
|
|
16620
|
-
init_json_pointer();
|
|
16621
|
-
init_hash();
|
|
16622
|
-
init_lock();
|
|
16576
|
+
import { existsSync as existsSync12, readFileSync as readFileSync2, writeFileSync, mkdirSync } from "node:fs";
|
|
16623
16577
|
function spawn5(repoRoot, args) {
|
|
16624
16578
|
const r = spawnSync6("git", ["-C", repoRoot, ...args], { encoding: "utf8" });
|
|
16625
16579
|
return { status: r.status ?? -1, stdout: r.stdout ?? "", stderr: r.stderr ?? "" };
|
|
16626
16580
|
}
|
|
16627
16581
|
function casDeleteBranch2(repoRoot, branch, expectedSha) {
|
|
16628
16582
|
const argv = ["update-ref", "-d"];
|
|
16629
|
-
if (expectedSha && /^[0-
|
|
16583
|
+
if (expectedSha && /^[0-9f]{7,}$/i.test(expectedSha)) {
|
|
16630
16584
|
argv.push(`refs/heads/${branch}`, expectedSha);
|
|
16631
16585
|
} else {
|
|
16632
16586
|
argv.push(`refs/heads/${branch}`);
|
|
@@ -16640,19 +16594,26 @@ function safeRemoveWorktree2(repoRoot, target) {
|
|
|
16640
16594
|
function worktreeRootOf(adapter) {
|
|
16641
16595
|
return adapter?.worktree?.root ?? ".worktrees";
|
|
16642
16596
|
}
|
|
16643
|
-
async function
|
|
16644
|
-
|
|
16597
|
+
async function cleanupPendingPath(repoRoot) {
|
|
16598
|
+
const common = await resolveGitCommonDir(repoRoot);
|
|
16599
|
+
return pathResolve(opencodeShipStateDir(common), "cleanup-pending.json");
|
|
16645
16600
|
}
|
|
16646
|
-
async function
|
|
16647
|
-
const
|
|
16648
|
-
|
|
16601
|
+
async function loadCleanupPending(repoRoot) {
|
|
16602
|
+
const path = await cleanupPendingPath(repoRoot);
|
|
16603
|
+
if (!existsSync12(path)) return [];
|
|
16604
|
+
try {
|
|
16605
|
+
const raw = await readFileSync2(path, "utf8");
|
|
16606
|
+
const parsed = JSON.parse(raw);
|
|
16607
|
+
return Array.isArray(parsed) ? parsed : [];
|
|
16608
|
+
} catch {
|
|
16609
|
+
return [];
|
|
16610
|
+
}
|
|
16649
16611
|
}
|
|
16650
|
-
async function
|
|
16651
|
-
const
|
|
16652
|
-
|
|
16653
|
-
|
|
16654
|
-
|
|
16655
|
-
return lock;
|
|
16612
|
+
async function saveCleanupPending(repoRoot, entries) {
|
|
16613
|
+
const path = await cleanupPendingPath(repoRoot);
|
|
16614
|
+
const dir = pathResolve(path, "..");
|
|
16615
|
+
if (!existsSync12(dir)) mkdirSync(dir, { recursive: true });
|
|
16616
|
+
writeFileSync(path, JSON.stringify(dedupePending(entries), null, 2) + "\n", "utf8");
|
|
16656
16617
|
}
|
|
16657
16618
|
function dedupePending(entries) {
|
|
16658
16619
|
const seen = /* @__PURE__ */ new Set();
|
|
@@ -16665,6 +16626,12 @@ function dedupePending(entries) {
|
|
|
16665
16626
|
}
|
|
16666
16627
|
return out;
|
|
16667
16628
|
}
|
|
16629
|
+
async function appendCleanupPending(repoRoot, entry) {
|
|
16630
|
+
const current = await loadCleanupPending(repoRoot);
|
|
16631
|
+
const next = [...current, entry];
|
|
16632
|
+
await saveCleanupPending(repoRoot, next);
|
|
16633
|
+
return next;
|
|
16634
|
+
}
|
|
16668
16635
|
function reject(reason, extra = {}) {
|
|
16669
16636
|
return { ok: false, reason, ...extra };
|
|
16670
16637
|
}
|
|
@@ -16774,10 +16741,10 @@ function flattenShipConfig(ship) {
|
|
|
16774
16741
|
}
|
|
16775
16742
|
|
|
16776
16743
|
// src/version.js
|
|
16777
|
-
import { readFileSync as
|
|
16744
|
+
import { readFileSync as readFileSync3, existsSync as existsSync13 } from "node:fs";
|
|
16778
16745
|
import { dirname as dirname7, resolve as resolve13 } from "node:path";
|
|
16779
16746
|
import { fileURLToPath } from "node:url";
|
|
16780
|
-
var PACKAGE_VERSION = "1.1.
|
|
16747
|
+
var PACKAGE_VERSION = "1.1.1-rc.9";
|
|
16781
16748
|
var TEMPLATE_SET = `v${PACKAGE_VERSION}`;
|
|
16782
16749
|
|
|
16783
16750
|
// src/plugin.js
|
|
@@ -17178,11 +17145,14 @@ var factories = {
|
|
|
17178
17145
|
runStart: {
|
|
17179
17146
|
args: {
|
|
17180
17147
|
workflowId: tool.schema.string(),
|
|
17148
|
+
revision: tool.schema.number().optional(),
|
|
17149
|
+
sha256: tool.schema.string().optional(),
|
|
17181
17150
|
operationId: tool.schema.string().optional()
|
|
17182
17151
|
},
|
|
17183
17152
|
build: (rt) => createRunStartTool({
|
|
17184
17153
|
repoRoot: rt.repoRoot,
|
|
17185
|
-
owner: rt.owner
|
|
17154
|
+
owner: rt.owner,
|
|
17155
|
+
configValue: rt.configValue
|
|
17186
17156
|
})
|
|
17187
17157
|
},
|
|
17188
17158
|
taskReport: {
|