opencode-ship 0.2.1 → 0.4.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/dist/core.js CHANGED
@@ -1,4 +1,4 @@
1
- // opencode-ship/core v0.2.1
1
+ // opencode-ship/core v0.4.0
2
2
 
3
3
  // src/adapter.js
4
4
  import { readFile, writeFile, mkdir, rename } from "node:fs/promises";
package/dist/plugin.js CHANGED
@@ -1,4 +1,4 @@
1
- // opencode-ship v0.2.1
1
+ // opencode-ship v0.4.0
2
2
  var __defProp = Object.defineProperty;
3
3
  var __getOwnPropNames = Object.getOwnPropertyNames;
4
4
  var __esm = (fn, res) => function __init() {
@@ -40,20 +40,37 @@ var init_hash = __esm({
40
40
  }
41
41
  });
42
42
 
43
+ // src/profile.js
44
+ function isValidProfile(name) {
45
+ return typeof name === "string" && PROFILES.includes(name);
46
+ }
47
+ var PROFILES;
48
+ var init_profile = __esm({
49
+ "src/profile.js"() {
50
+ PROFILES = Object.freeze(["core", "engineering"]);
51
+ }
52
+ });
53
+
43
54
  // src/installer/lock.js
44
55
  var lock_exports = {};
45
56
  __export(lock_exports, {
46
57
  CURRENT_LOCK_SCHEMA: () => CURRENT_LOCK_SCHEMA,
47
58
  computeIntegrity: () => computeIntegrity,
48
59
  lockPath: () => lockPath,
60
+ lockSchemaRevision: () => lockSchemaRevision,
49
61
  migrateLegacyLock: () => migrateLegacyLock,
50
62
  readLock: () => readLock2,
63
+ readValidatedLock: () => readValidatedLock,
51
64
  validateIntegrity: () => validateIntegrity,
65
+ validateLock: () => validateLock,
52
66
  writeLock: () => writeLock
53
67
  });
54
68
  import { readFile as readFile4, writeFile as writeFile4, rename as rename4, mkdir as mkdir4 } from "node:fs/promises";
55
69
  import { existsSync as existsSync4 } from "node:fs";
56
70
  import { dirname as dirname4, resolve as resolve8 } from "node:path";
71
+ function lockSchemaRevision() {
72
+ return CURRENT_LOCK_SCHEMA;
73
+ }
57
74
  function lockPath(repoRoot) {
58
75
  return resolve8(repoRoot, ".opencode", "ship.lock.json");
59
76
  }
@@ -91,6 +108,71 @@ async function validateIntegrity(lock) {
91
108
  const expected = computeIntegrity(lock).lockSha256;
92
109
  return expected === lock.integrity.lockSha256;
93
110
  }
111
+ function validateLock(rawLock) {
112
+ if (rawLock === null || rawLock === void 0) {
113
+ return { ok: true, kind: "missing", issues: [] };
114
+ }
115
+ if (typeof rawLock !== "object" || Array.isArray(rawLock)) {
116
+ return { ok: false, kind: "shape", issues: ["lock root must be an object"] };
117
+ }
118
+ const issues = [];
119
+ let kind = "ok";
120
+ if (rawLock.contractVersion !== CURRENT_LOCK_SCHEMA && rawLock.contractVersion !== 1) {
121
+ issues.push(`unsupported contractVersion: ${JSON.stringify(rawLock.contractVersion)} (expected ${CURRENT_LOCK_SCHEMA} or 1)`);
122
+ kind = "schema";
123
+ }
124
+ const manager = rawLock.manager;
125
+ if (manager === void 0) {
126
+ issues.push("manager section missing");
127
+ kind = kind === "ok" ? "shape" : kind;
128
+ } else if (typeof manager !== "object" || manager === null) {
129
+ issues.push("manager section must be an object");
130
+ kind = kind === "ok" ? "shape" : kind;
131
+ } else if (manager.schemaVersion !== CURRENT_LOCK_SCHEMA && manager.schemaVersion !== 1) {
132
+ issues.push(`unsupported manager.schemaVersion: ${JSON.stringify(manager.schemaVersion)} (expected ${CURRENT_LOCK_SCHEMA} or 1)`);
133
+ kind = "schema";
134
+ } else if (manager.name !== "opencode-ship") {
135
+ issues.push(`unknown manager.name: ${JSON.stringify(manager.name)}`);
136
+ kind = "shape";
137
+ } else if (rawLock.contractVersion === CURRENT_LOCK_SCHEMA && manager.schemaVersion === CURRENT_LOCK_SCHEMA && manager.profile !== void 0 && !isValidProfile(manager.profile)) {
138
+ issues.push(`invalid manager.profile: ${JSON.stringify(manager.profile)} (expected one of: core, engineering)`);
139
+ kind = "shape";
140
+ }
141
+ if (!rawLock.files || !Array.isArray(rawLock.files)) {
142
+ issues.push("files must be an array");
143
+ kind = kind === "ok" ? "shape" : kind;
144
+ }
145
+ if (!rawLock.integrity || typeof rawLock.integrity !== "object") {
146
+ issues.push("integrity section missing");
147
+ kind = kind === "ok" ? "shape" : kind;
148
+ } else {
149
+ const expected = computeIntegrity(rawLock).lockSha256;
150
+ if (expected !== rawLock.integrity.lockSha256) {
151
+ issues.push(`integrity mismatch: stored ${rawLock.integrity.lockSha256} != computed ${expected}`);
152
+ kind = "integrity";
153
+ }
154
+ }
155
+ return { ok: issues.length === 0, kind, issues };
156
+ }
157
+ async function readValidatedLock(repoRoot) {
158
+ const path = lockPath(repoRoot);
159
+ if (!existsSync4(path)) {
160
+ return { kind: "missing", lock: null, issues: [] };
161
+ }
162
+ let raw;
163
+ try {
164
+ const text = await readFile4(path, "utf8");
165
+ raw = JSON.parse(text);
166
+ } catch (e) {
167
+ return {
168
+ kind: "integrity",
169
+ lock: null,
170
+ issues: [`unable to parse lock JSON: ${e?.message ?? String(e)}`]
171
+ };
172
+ }
173
+ const validation = validateLock(raw);
174
+ return { kind: validation.kind, lock: validation.ok ? raw : null, issues: validation.issues };
175
+ }
94
176
  async function migrateLegacyLock(repoRoot) {
95
177
  const legacy = resolve8(repoRoot, ".opencode", "delivery.lock.json");
96
178
  if (!existsSync4(legacy)) return null;
@@ -113,7 +195,8 @@ var init_lock = __esm({
113
195
  "src/installer/lock.js"() {
114
196
  init_hash();
115
197
  init_json_pointer();
116
- CURRENT_LOCK_SCHEMA = 1;
198
+ init_profile();
199
+ CURRENT_LOCK_SCHEMA = 2;
117
200
  }
118
201
  });
119
202
 
@@ -12540,7 +12623,7 @@ function tool(input) {
12540
12623
  tool.schema = external_exports;
12541
12624
 
12542
12625
  // src/plugin.js
12543
- import { resolve as resolve11 } from "node:path";
12626
+ import { resolve as resolve12 } from "node:path";
12544
12627
  import { readFile as readFile5 } from "node:fs/promises";
12545
12628
 
12546
12629
  // src/adapter.js
@@ -12765,7 +12848,7 @@ function parseRepoSlug(slug) {
12765
12848
 
12766
12849
  // src/drivers/gh-cli.js
12767
12850
  function defaultRunner(cwd, env) {
12768
- return (args) => new Promise((resolve12, reject2) => {
12851
+ return (args) => new Promise((resolve13, reject2) => {
12769
12852
  const proc = spawn("gh", args, {
12770
12853
  cwd,
12771
12854
  env,
@@ -12777,7 +12860,7 @@ function defaultRunner(cwd, env) {
12777
12860
  proc.stdout.on("data", (d) => stdout += d.toString());
12778
12861
  proc.stderr.on("data", (d) => stderr += d.toString());
12779
12862
  proc.on("error", reject2);
12780
- proc.on("close", (status) => resolve12({ status: status ?? -1, stdout, stderr }));
12863
+ proc.on("close", (status) => resolve13({ status: status ?? -1, stdout, stderr }));
12781
12864
  });
12782
12865
  }
12783
12866
  function viewFields() {
@@ -14207,13 +14290,18 @@ import { dirname as dirname3, resolve as resolve7 } from "node:path";
14207
14290
  // schema/ship-config.schema.json
14208
14291
  var ship_config_schema_default = {
14209
14292
  $schema: "https://json-schema.org/draft/2020-12/schema",
14210
- $id: "https://github.com/Viktorxyz/opencode-delivery/schema/ship-config.schema.json",
14293
+ $id: "https://github.com/Viktorxyz/opencode-ship/schema/ship-config.schema.json",
14211
14294
  title: "opencode-ship user config",
14212
14295
  type: "object",
14213
14296
  required: ["schemaVersion"],
14214
14297
  additionalProperties: false,
14215
14298
  properties: {
14216
14299
  schemaVersion: { const: 1 },
14300
+ profile: {
14301
+ type: "string",
14302
+ enum: ["core", "engineering"],
14303
+ description: "Active profile (precedence layer 2: ship.config > lock > core default)."
14304
+ },
14217
14305
  owner: {
14218
14306
  type: "string",
14219
14307
  description: "Optional override for the issue/manifest owner field. Defaults to the agent's local user.name."
@@ -14805,8 +14893,14 @@ function flattenShipConfig(ship) {
14805
14893
  return adapter;
14806
14894
  }
14807
14895
 
14896
+ // src/version.js
14897
+ import { readFileSync as readFileSync2, existsSync as existsSync6 } from "node:fs";
14898
+ import { dirname as dirname5, resolve as resolve11 } from "node:path";
14899
+ import { fileURLToPath } from "node:url";
14900
+ var PACKAGE_VERSION = "0.4.0";
14901
+ var TEMPLATE_SET = `v${PACKAGE_VERSION}`;
14902
+
14808
14903
  // src/plugin.js
14809
- var VERSION = "0.2.1";
14810
14904
  var toolDefs = [
14811
14905
  ["delivery_inspect", "Inspect a manifest and a project-local doctor report.", "inspect"],
14812
14906
  ["delivery_issue", "Find or create the issue for a delivery task.", "issue"],
@@ -14843,7 +14937,7 @@ async function resolveRepoSlug(repoRoot, detection, config2) {
14843
14937
  const fromConfig = config2?.value?.project?.repository;
14844
14938
  if (typeof fromConfig === "string" && fromConfig.includes("/")) return fromConfig;
14845
14939
  if (detection?.repository) return detection.repository;
14846
- const gitConfig = await readFile5(resolve11(repoRoot, ".git/config"), "utf8").catch(() => null);
14940
+ const gitConfig = await readFile5(resolve12(repoRoot, ".git/config"), "utf8").catch(() => null);
14847
14941
  if (gitConfig) {
14848
14942
  const m = gitConfig.match(/url\s*=\s*.*?github\.com[:/]([^/]+)\/([^/\s]+?)(?:\.git)?\b/);
14849
14943
  if (m) return `${m[1]}/${m[2]}`;
@@ -14875,7 +14969,7 @@ async function bestEffortCleanupQueue(repoRoot, adapter) {
14875
14969
  return { pending, manifestTasks: tasks.map((t) => t.taskId), ...out };
14876
14970
  }
14877
14971
  async function buildRuntime(worktree) {
14878
- const repoRootAbs = resolve11(worktree ?? process.cwd());
14972
+ const repoRootAbs = resolve12(worktree ?? process.cwd());
14879
14973
  const detection = detectProject(repoRootAbs);
14880
14974
  const legacyAdapter = await loadAdapter(repoRootAbs);
14881
14975
  const config2 = await loadConfig(repoRootAbs);
@@ -14898,7 +14992,7 @@ async function buildRuntime(worktree) {
14898
14992
  repoSlug: repoSlug ?? "owner/repo",
14899
14993
  owner,
14900
14994
  driver,
14901
- packageVersion: VERSION,
14995
+ packageVersion: PACKAGE_VERSION,
14902
14996
  lastTaskId: null,
14903
14997
  cleanupQueueOnStartup: cleanup,
14904
14998
  recover: () => recoverManifestAfterCrash
@@ -40,7 +40,7 @@ with three surfaces:
40
40
 
41
41
  Five output classes are installer-managed and recorded in the lock:
42
42
 
43
- - `.opencode/plugin/opencode-ship.js` — the bundled plugin.
43
+ - `.opencode/plugins/opencode-ship.js` — the bundled plugin.
44
44
  - `.opencode/agents/delivery-reviewer.md`,
45
45
  `.opencode/agents/delivery-verifier.md`.
46
46
  - `.opencode/skills/delivery-workflow/SKILL.md`,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-ship",
3
- "version": "0.2.1",
3
+ "version": "0.4.0",
4
4
  "private": false,
5
5
  "description": "npm-distributed OpenCode installer that materializes the delivery plugin, reviewer/verifier agents, skills, ship config and lock into any consumer repository.",
6
6
  "license": "MIT",
@@ -17,6 +17,10 @@
17
17
  "node": ">=22.6.0",
18
18
  "opencode": ">=1.15.5"
19
19
  },
20
+ "publishConfig": {
21
+ "access": "public",
22
+ "provenance": true
23
+ },
20
24
  "peerDependencies": {
21
25
  "@opencode-ai/plugin": ">=1.15.5 <2"
22
26
  },
@@ -43,6 +47,7 @@
43
47
  "assets",
44
48
  "schema",
45
49
  "docs",
50
+ "THIRD_PARTY_NOTICES.md",
46
51
  "README.md",
47
52
  "CHANGELOG.md",
48
53
  "LICENSE"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/Viktorxyz/opencode-delivery/schema/project-adapter.schema.json",
3
+ "$id": "https://github.com/Viktorxyz/opencode-ship/schema/project-adapter.schema.json",
4
4
  "title": "opencode-ship project adapter",
5
5
  "type": "object",
6
6
  "required": ["contractVersion"],
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/Viktorxyz/opencode-delivery/schema/ship-config.schema.json",
3
+ "$id": "https://github.com/Viktorxyz/opencode-ship/schema/ship-config.schema.json",
4
4
  "title": "opencode-ship user config",
5
5
  "type": "object",
6
6
  "required": ["schemaVersion"],
7
7
  "additionalProperties": false,
8
8
  "properties": {
9
9
  "schemaVersion": { "const": 1 },
10
+ "profile": {
11
+ "type": "string",
12
+ "enum": ["core", "engineering"],
13
+ "description": "Active profile (precedence layer 2: ship.config > lock > core default)."
14
+ },
10
15
  "owner": {
11
16
  "type": "string",
12
17
  "description": "Optional override for the issue/manifest owner field. Defaults to the agent's local user.name."
@@ -1,21 +1,25 @@
1
1
  {
2
2
  "$schema": "https://json-schema.org/draft/2020-12/schema",
3
- "$id": "https://github.com/Viktorxyz/opencode-delivery/schema/ship-lock.schema.json",
3
+ "$id": "https://github.com/Viktorxyz/opencode-ship/schema/ship-lock.schema.json",
4
4
  "title": "opencode-ship install lock",
5
5
  "type": "object",
6
6
  "required": ["contractVersion", "manager", "files", "integrity"],
7
7
  "additionalProperties": false,
8
8
  "properties": {
9
- "contractVersion": { "const": 1 },
9
+ "contractVersion": { "enum": [1, 2] },
10
10
  "manager": {
11
11
  "type": "object",
12
12
  "required": ["schemaVersion", "name", "version", "appliedAt", "config"],
13
13
  "additionalProperties": false,
14
14
  "properties": {
15
- "schemaVersion": { "const": 1 },
15
+ "schemaVersion": { "enum": [1, 2] },
16
16
  "name": { "type": "string", "pattern": "^opencode-ship$" },
17
17
  "version": { "type": "string", "minLength": 1 },
18
18
  "templateSet": { "type": "string", "minLength": 1 },
19
+ "profile": {
20
+ "enum": ["core", "engineering"],
21
+ "description": "Profile the lock was applied under. Required on locks written at schema 2; absent on legacy schema-1 locks (resolved to core by the profile precedence layer)."
22
+ },
19
23
  "appliedAt": { "type": "string", "format": "date-time" },
20
24
  "config": {
21
25
  "type": "object",