opencode-ship 1.1.2-rc.2 → 1.1.2

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.
Files changed (76) hide show
  1. package/CHANGELOG.md +96 -1
  2. package/README.md +2 -2
  3. package/THIRD_PARTY_NOTICES.md +1 -1
  4. package/assets/agents/delivery-reviewer.md +1 -0
  5. package/assets/agents/delivery-verifier.md +1 -0
  6. package/assets/agents/ship-controller.md +20 -7
  7. package/assets/agents/ship-final-spec-reviewer.md +5 -3
  8. package/assets/agents/ship-final-standards-reviewer.md +5 -3
  9. package/assets/agents/ship-planner.md +1 -0
  10. package/assets/agents/ship-task-builder.md +1 -0
  11. package/assets/agents/ship-task-reviewer.md +1 -0
  12. package/dist/cli.js +2274 -362
  13. package/dist/core.js +794 -10
  14. package/dist/plugin.js +3487 -1795
  15. package/package.json +9 -4
  16. package/tests/plugin/expected-tools.mjs +2 -2
  17. package/tests/plugin/plugin-load.test.mjs +8 -3
  18. package/assets/_archive/issue-tracker-gitlab.md +0 -46
  19. package/assets/_archive/issue-tracker-local.md +0 -30
  20. package/docs/adr/0001-opencode-ship-redesign.md +0 -168
  21. package/docs/release/1.0.0-execution-plan.md +0 -1063
  22. package/docs/release/1.1.1-stabilization-plan.md +0 -655
  23. package/vendor/upstreams/mattpocock/skills/engineering/code-review/SKILL.md +0 -89
  24. package/vendor/upstreams/mattpocock/skills/engineering/codebase-design/DEEPENING.md +0 -37
  25. package/vendor/upstreams/mattpocock/skills/engineering/codebase-design/DESIGN-IT-TWICE.md +0 -44
  26. package/vendor/upstreams/mattpocock/skills/engineering/codebase-design/SKILL.md +0 -114
  27. package/vendor/upstreams/mattpocock/skills/engineering/domain-modeling/ADR-FORMAT.md +0 -47
  28. package/vendor/upstreams/mattpocock/skills/engineering/domain-modeling/CONTEXT-FORMAT.md +0 -60
  29. package/vendor/upstreams/mattpocock/skills/engineering/domain-modeling/SKILL.md +0 -74
  30. package/vendor/upstreams/mattpocock/skills/engineering/grill-with-docs/SKILL.md +0 -7
  31. package/vendor/upstreams/mattpocock/skills/engineering/prototype/LOGIC.md +0 -79
  32. package/vendor/upstreams/mattpocock/skills/engineering/prototype/SKILL.md +0 -26
  33. package/vendor/upstreams/mattpocock/skills/engineering/prototype/UI.md +0 -112
  34. package/vendor/upstreams/mattpocock/skills/engineering/research/SKILL.md +0 -12
  35. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/SKILL.md +0 -116
  36. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/domain.md +0 -51
  37. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/issue-tracker-github.md +0 -45
  38. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/issue-tracker-gitlab.md +0 -46
  39. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/issue-tracker-local.md +0 -30
  40. package/vendor/upstreams/mattpocock/skills/engineering/setup-matt-pocock-skills/triage-labels.md +0 -15
  41. package/vendor/upstreams/mattpocock/skills/engineering/to-spec/SKILL.md +0 -75
  42. package/vendor/upstreams/mattpocock/skills/engineering/to-tickets/SKILL.md +0 -105
  43. package/vendor/upstreams/mattpocock/skills/engineering/triage/AGENT-BRIEF.md +0 -207
  44. package/vendor/upstreams/mattpocock/skills/engineering/triage/OUT-OF-SCOPE.md +0 -105
  45. package/vendor/upstreams/mattpocock/skills/engineering/triage/SKILL.md +0 -112
  46. package/vendor/upstreams/mattpocock/skills/engineering/wayfinder/SKILL.md +0 -128
  47. package/vendor/upstreams/mattpocock/skills/productivity/grilling/SKILL.md +0 -12
  48. package/vendor/upstreams/mattpocock/skills/productivity/handoff/SKILL.md +0 -16
  49. package/vendor/upstreams/obra/skills/brainstorming/SKILL.md +0 -151
  50. package/vendor/upstreams/obra/skills/brainstorming/spec-document-reviewer-prompt.md +0 -49
  51. package/vendor/upstreams/obra/skills/brainstorming/visual-companion.md +0 -298
  52. package/vendor/upstreams/obra/skills/dispatching-parallel-agents/SKILL.md +0 -167
  53. package/vendor/upstreams/obra/skills/executing-plans/SKILL.md +0 -64
  54. package/vendor/upstreams/obra/skills/receiving-code-review/SKILL.md +0 -205
  55. package/vendor/upstreams/obra/skills/requesting-code-review/SKILL.md +0 -95
  56. package/vendor/upstreams/obra/skills/requesting-code-review/code-reviewer.md +0 -172
  57. package/vendor/upstreams/obra/skills/subagent-driven-development/SKILL.md +0 -503
  58. package/vendor/upstreams/obra/skills/subagent-driven-development/implementer-prompt.md +0 -142
  59. package/vendor/upstreams/obra/skills/subagent-driven-development/re-review-prompt.md +0 -106
  60. package/vendor/upstreams/obra/skills/subagent-driven-development/task-reviewer-prompt.md +0 -185
  61. package/vendor/upstreams/obra/skills/systematic-debugging/CREATION-LOG.md +0 -119
  62. package/vendor/upstreams/obra/skills/systematic-debugging/SKILL.md +0 -283
  63. package/vendor/upstreams/obra/skills/systematic-debugging/condition-based-waiting-example.ts +0 -158
  64. package/vendor/upstreams/obra/skills/systematic-debugging/condition-based-waiting.md +0 -115
  65. package/vendor/upstreams/obra/skills/systematic-debugging/defense-in-depth.md +0 -122
  66. package/vendor/upstreams/obra/skills/systematic-debugging/find-polluter.sh +0 -72
  67. package/vendor/upstreams/obra/skills/systematic-debugging/root-cause-tracing.md +0 -169
  68. package/vendor/upstreams/obra/skills/systematic-debugging/test-academic.md +0 -14
  69. package/vendor/upstreams/obra/skills/systematic-debugging/test-pressure-1.md +0 -58
  70. package/vendor/upstreams/obra/skills/systematic-debugging/test-pressure-2.md +0 -68
  71. package/vendor/upstreams/obra/skills/systematic-debugging/test-pressure-3.md +0 -69
  72. package/vendor/upstreams/obra/skills/test-driven-development/SKILL.md +0 -320
  73. package/vendor/upstreams/obra/skills/test-driven-development/writing-good-tests.md +0 -198
  74. package/vendor/upstreams/obra/skills/verification-before-completion/SKILL.md +0 -120
  75. package/vendor/upstreams/obra/skills/writing-plans/SKILL.md +0 -168
  76. package/vendor/upstreams/obra/skills/writing-plans/plan-document-reviewer-prompt.md +0 -49
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // opencode-ship CLI v1.1.2-rc.2
2
+ // opencode-ship CLI v1.1.2
3
3
  var __defProp = Object.defineProperty;
4
4
  var __getOwnPropNames = Object.getOwnPropertyNames;
5
5
  var __esm = (fn, res) => function __init() {
@@ -110,7 +110,7 @@ import { fileURLToPath as fileURLToPath2 } from "node:url";
110
110
  var PACKAGE_VERSION, TEMPLATE_SET;
111
111
  var init_version = __esm({
112
112
  "src/version.js"() {
113
- PACKAGE_VERSION = "1.1.2-rc.2";
113
+ PACKAGE_VERSION = "1.1.2";
114
114
  TEMPLATE_SET = `v${PACKAGE_VERSION}`;
115
115
  }
116
116
  });
@@ -359,6 +359,9 @@ var init_catalog = __esm({
359
359
  function unescape(token) {
360
360
  return token.replace(/~1/g, "/").replace(/~0/g, "~");
361
361
  }
362
+ function escape(token) {
363
+ return token.replace(/~/g, "~0").replace(/\//g, "~1");
364
+ }
362
365
  function* parsePointer(pointer) {
363
366
  if (typeof pointer !== "string" || pointer === "") return;
364
367
  if (pointer[0] !== "/") {
@@ -421,6 +424,9 @@ function stableStringify(value) {
421
424
  }
422
425
  return JSON.stringify(value);
423
426
  }
427
+ function pointerPath(segments) {
428
+ return "/" + segments.map((s) => escape(String(s))).join("/");
429
+ }
424
430
  var init_json_pointer = __esm({
425
431
  "src/installer/json-pointer.js"() {
426
432
  }
@@ -858,6 +864,126 @@ var init_config = __esm({
858
864
  }
859
865
  });
860
866
 
867
+ // src/installer/lock.js
868
+ import { readFile as readFile2, writeFile as writeFile2, rename as rename2, mkdir as mkdir2 } from "node:fs/promises";
869
+ import { existsSync as existsSync5 } from "node:fs";
870
+ import { dirname as dirname4, resolve as resolve5, posix } from "node:path";
871
+ function lockPath(repoRoot) {
872
+ return resolve5(repoRoot, ".opencode", "ship.lock.json");
873
+ }
874
+ function computeIntegrity(lock) {
875
+ const { integrity: _ignored, ...without } = lock ?? {};
876
+ void _ignored;
877
+ return {
878
+ lockSha256: bytesHashString(stableStringify(without))
879
+ };
880
+ }
881
+ function normalizeLegacyLock(lock) {
882
+ if (!lock || typeof lock !== "object") return lock;
883
+ const { cleanupPending: _drop, ...rest } = lock;
884
+ void _drop;
885
+ return rest;
886
+ }
887
+ function validateLock(rawLock) {
888
+ if (rawLock === null || rawLock === void 0) {
889
+ return { ok: true, kind: "missing", issues: [] };
890
+ }
891
+ if (typeof rawLock !== "object" || Array.isArray(rawLock)) {
892
+ return { ok: false, kind: "shape", issues: ["lock root must be an object"] };
893
+ }
894
+ const issues = [];
895
+ let kind = "ok";
896
+ if (rawLock.contractVersion !== CURRENT_LOCK_SCHEMA && rawLock.contractVersion !== 3 && rawLock.contractVersion !== 2 && rawLock.contractVersion !== 1) {
897
+ issues.push(`unsupported contractVersion: ${JSON.stringify(rawLock.contractVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 3, 2, or 1)`);
898
+ kind = "schema";
899
+ }
900
+ const manager = rawLock.manager;
901
+ if (manager === void 0) {
902
+ issues.push("manager section missing");
903
+ kind = kind === "ok" ? "shape" : kind;
904
+ } else if (typeof manager !== "object" || manager === null) {
905
+ issues.push("manager section must be an object");
906
+ kind = kind === "ok" ? "shape" : kind;
907
+ } else if (manager.schemaVersion !== CURRENT_LOCK_SCHEMA && manager.schemaVersion !== 3 && manager.schemaVersion !== 2 && manager.schemaVersion !== 1) {
908
+ issues.push(`unsupported manager.schemaVersion: ${JSON.stringify(manager.schemaVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 3, 2, or 1)`);
909
+ kind = "schema";
910
+ } else if (manager.name !== "opencode-ship") {
911
+ issues.push(`unknown manager.name: ${JSON.stringify(manager.name)}`);
912
+ kind = "shape";
913
+ } else if (rawLock.contractVersion >= 2 && manager.schemaVersion >= 2 && manager.profile !== void 0 && manager.profile !== "core" && manager.profile !== "engineering") {
914
+ issues.push(`invalid manager.profile: ${JSON.stringify(manager.profile)} (expected one of: engineering, core [legacy])`);
915
+ kind = "shape";
916
+ }
917
+ if (!rawLock.files || !Array.isArray(rawLock.files)) {
918
+ issues.push("files must be an array");
919
+ kind = kind === "ok" ? "shape" : kind;
920
+ } else {
921
+ for (const entry of rawLock.files) {
922
+ if (!entry || typeof entry !== "object" || !isSafeManagedPath(entry.path)) {
923
+ issues.push(`unsafe managed file path: ${JSON.stringify(entry?.path)}`);
924
+ kind = kind === "ok" ? "shape" : kind;
925
+ }
926
+ }
927
+ }
928
+ if (!rawLock.integrity || typeof rawLock.integrity !== "object") {
929
+ issues.push("integrity section missing");
930
+ kind = kind === "ok" ? "shape" : kind;
931
+ } else {
932
+ const expected = computeIntegrity(rawLock).lockSha256;
933
+ if (expected !== rawLock.integrity.lockSha256) {
934
+ issues.push(`integrity mismatch: stored ${rawLock.integrity.lockSha256} != computed ${expected}`);
935
+ kind = "integrity";
936
+ }
937
+ }
938
+ return { ok: issues.length === 0, kind, issues };
939
+ }
940
+ function isSafeManagedPath(value) {
941
+ if (typeof value !== "string" || value.length === 0 || value.includes("\\")) return false;
942
+ if (posix.isAbsolute(value) || posix.normalize(value) !== value) return false;
943
+ return value.split("/").every((part) => part !== "" && part !== "." && part !== "..");
944
+ }
945
+ async function readValidatedLock(repoRoot) {
946
+ const path = lockPath(repoRoot);
947
+ if (!existsSync5(path)) {
948
+ return { kind: "missing", lock: null, issues: [] };
949
+ }
950
+ let raw;
951
+ try {
952
+ const text = await readFile2(path, "utf8");
953
+ raw = JSON.parse(text);
954
+ } catch (e) {
955
+ return {
956
+ kind: "integrity",
957
+ lock: null,
958
+ issues: [`unable to parse lock JSON: ${e?.message ?? String(e)}`]
959
+ };
960
+ }
961
+ const validation = validateLock(raw);
962
+ if (validation.ok) {
963
+ return {
964
+ kind: validation.kind,
965
+ lock: normalizeLegacyLock(raw),
966
+ issues: []
967
+ };
968
+ }
969
+ return { kind: validation.kind, lock: null, issues: validation.issues };
970
+ }
971
+ function isSetupComplete(lock) {
972
+ if (!lock || typeof lock !== "object") return false;
973
+ const manager = lock.manager;
974
+ if (!manager || typeof manager !== "object") return false;
975
+ return manager.setupComplete === true;
976
+ }
977
+ var CURRENT_LOCK_SCHEMA;
978
+ var init_lock = __esm({
979
+ "src/installer/lock.js"() {
980
+ init_hash();
981
+ init_json_pointer();
982
+ init_profile();
983
+ CURRENT_LOCK_SCHEMA = 4;
984
+ }
985
+ });
986
+
861
987
  // src/installer/plan-mode-permissions.js
862
988
  function planModePermissions() {
863
989
  return {
@@ -865,7 +991,8 @@ function planModePermissions() {
865
991
  bash: DENY_DEFAULT,
866
992
  edit: {
867
993
  "*": DENY_DEFAULT,
868
- [PLANS_GLOB]: ALLOW_PLANS
994
+ [PLANS_GLOB]: ALLOW_PLANS,
995
+ [SUPERPOWERS_GLOB]: ALLOW_PLANS
869
996
  },
870
997
  webfetch: DENY_DEFAULT,
871
998
  task: DENY_DEFAULT,
@@ -881,13 +1008,64 @@ function planModePermissions() {
881
1008
  }
882
1009
  };
883
1010
  }
884
- var PLAN_PATH_PREFIX, DENY_DEFAULT, ALLOW_PLANS, PLANS_GLOB;
1011
+ function promotePlanEditIfString(doc) {
1012
+ const edit = readPath(doc, ["agent", "plan", "permission", "edit"]);
1013
+ if (edit === void 0) {
1014
+ return { doc, record: null };
1015
+ }
1016
+ if (typeof edit === "string") {
1017
+ const next = setPath(doc, ["agent", "plan", "permission", "edit"], { "*": edit });
1018
+ const record = {
1019
+ pointer: PLAN_EDIT_PARENT_POINTER,
1020
+ previous: { existed: true, value: edit },
1021
+ promotion: true
1022
+ };
1023
+ return { doc: next, record };
1024
+ }
1025
+ if (edit && typeof edit === "object" && !Array.isArray(edit)) {
1026
+ if (Object.prototype.hasOwnProperty.call(edit, SUPERPOWERS_GLOB) && edit[SUPERPOWERS_GLOB] === "deny") {
1027
+ return { doc, record: { conflict: true, pointer: PLAN_EDIT_GLOB_POINTER } };
1028
+ }
1029
+ return { doc, record: null };
1030
+ }
1031
+ return { doc, record: null };
1032
+ }
1033
+ function readPath(doc, tokens) {
1034
+ let cursor = doc;
1035
+ for (const token of tokens) {
1036
+ if (cursor === null || cursor === void 0) return void 0;
1037
+ cursor = cursor[token];
1038
+ }
1039
+ return cursor;
1040
+ }
1041
+ function setPath(doc, tokens, value) {
1042
+ if (!doc || typeof doc !== "object" || Array.isArray(doc)) return doc;
1043
+ const root = Array.isArray(doc) ? [...doc] : { ...doc };
1044
+ let cursor = root;
1045
+ for (let i = 0; i < tokens.length - 1; i += 1) {
1046
+ const key = tokens[i];
1047
+ const next = cursor[key];
1048
+ const copy = next && typeof next === "object" && !Array.isArray(next) ? { ...next } : {};
1049
+ cursor[key] = copy;
1050
+ cursor = copy;
1051
+ }
1052
+ cursor[tokens[tokens.length - 1]] = value;
1053
+ return root;
1054
+ }
1055
+ var PLAN_PATH_PREFIX, SUPERPOWERS_PATH_PREFIX, DENY_DEFAULT, ALLOW_PLANS, PLANS_GLOB, SUPERPOWERS_GLOB, PLAN_EDIT_GLOB, PLAN_EDIT_PLANS_GLOB, PLAN_EDIT_GLOB_POINTER, PLAN_EDIT_PLANS_GLOB_POINTER, PLAN_EDIT_PARENT_POINTER;
885
1056
  var init_plan_mode_permissions = __esm({
886
1057
  "src/installer/plan-mode-permissions.js"() {
887
1058
  PLAN_PATH_PREFIX = ".git/opencode-ship/plans";
1059
+ SUPERPOWERS_PATH_PREFIX = "docs/superpowers";
888
1060
  DENY_DEFAULT = "deny";
889
1061
  ALLOW_PLANS = "allow";
890
1062
  PLANS_GLOB = `${PLAN_PATH_PREFIX}/**`;
1063
+ SUPERPOWERS_GLOB = `${SUPERPOWERS_PATH_PREFIX}/**`;
1064
+ PLAN_EDIT_GLOB = SUPERPOWERS_GLOB;
1065
+ PLAN_EDIT_PLANS_GLOB = PLANS_GLOB;
1066
+ PLAN_EDIT_GLOB_POINTER = "/agent/plan/permission/edit/docs~1superpowers~1**";
1067
+ PLAN_EDIT_PLANS_GLOB_POINTER = "/agent/plan/permission/edit/.git~1opencode-ship~1plans~1**";
1068
+ PLAN_EDIT_PARENT_POINTER = "/agent/plan/permission/edit";
891
1069
  }
892
1070
  });
893
1071
 
@@ -907,21 +1085,21 @@ __export(root_config_exports, {
907
1085
  readRootConfig: () => readRootConfig,
908
1086
  synthesizeDefaultRootConfig: () => synthesizeDefaultRootConfig
909
1087
  });
910
- import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
911
- import { readFile as readFile2 } from "node:fs/promises";
912
- import { resolve as resolve5 } from "node:path";
1088
+ import { existsSync as existsSync6, readFileSync as readFileSync3 } from "node:fs";
1089
+ import { readFile as readFile3 } from "node:fs/promises";
1090
+ import { resolve as resolve6 } from "node:path";
913
1091
  function findRootConfig(repoRoot) {
914
1092
  for (const rel of ROOT_PATH_CANDIDATES) {
915
- const abs = resolve5(repoRoot, rel);
916
- if (existsSync5(abs)) return { path: abs, relative: rel, format: rel.endsWith(".jsonc") ? "jsonc" : "json" };
1093
+ const abs = resolve6(repoRoot, rel);
1094
+ if (existsSync6(abs)) return { path: abs, relative: rel, format: rel.endsWith(".jsonc") ? "jsonc" : "json" };
917
1095
  }
918
1096
  return { path: null, relative: ROOT_PATH_CANDIDATES[0], format: "json" };
919
1097
  }
920
1098
  function defaultRootConfigPath(repoRoot) {
921
- return resolve5(repoRoot, ROOT_PATH_CANDIDATES[0]);
1099
+ return resolve6(repoRoot, ROOT_PATH_CANDIDATES[0]);
922
1100
  }
923
1101
  function readRootConfig(absPath) {
924
- if (!existsSync5(absPath)) {
1102
+ if (!existsSync6(absPath)) {
925
1103
  return { ok: false, error: { kind: "missing", path: absPath } };
926
1104
  }
927
1105
  const raw = readFileSync3(absPath, "utf8");
@@ -952,15 +1130,15 @@ function stripJsonc(text) {
952
1130
  let stripped = "";
953
1131
  let i = 0;
954
1132
  let inString = false;
955
- let escape = false;
1133
+ let escape2 = false;
956
1134
  while (i < text.length) {
957
1135
  const ch = text[i];
958
1136
  if (inString) {
959
1137
  stripped += ch;
960
- if (escape) {
961
- escape = false;
1138
+ if (escape2) {
1139
+ escape2 = false;
962
1140
  } else if (ch === "\\") {
963
- escape = true;
1141
+ escape2 = true;
964
1142
  } else if (ch === '"') {
965
1143
  inString = false;
966
1144
  }
@@ -1011,6 +1189,7 @@ function applyOwnedPointers(rootDoc, { pointerEntries = POINTER_ENTRIES, allowEq
1011
1189
  desired: entry.value
1012
1190
  });
1013
1191
  }
1192
+ result.doc = doc;
1014
1193
  return result;
1015
1194
  }
1016
1195
  function applyPlanModeOwnership(rootDoc, { pointer = PLAN_MODE_POINTER, block = planModePermissions().build } = {}) {
@@ -1025,20 +1204,11 @@ function synthesizeDefaultRootConfig() {
1025
1204
  return {
1026
1205
  $schema: "https://opencode.ai/config.json",
1027
1206
  agent: {
1028
- build: {
1207
+ plan: {
1029
1208
  permission: {
1030
- delivery_inspect: "allow",
1031
- delivery_issue: "allow",
1032
- delivery_worktree: "allow",
1033
- delivery_verify: "deny",
1034
- delivery_review: "deny",
1035
- delivery_pr: "allow",
1036
- delivery_ready: "allow",
1037
- delivery_merge: "ask",
1038
- delivery_cleanup: "allow",
1039
- task: {
1040
- "delivery-reviewer": "allow",
1041
- "delivery-verifier": "allow"
1209
+ edit: {
1210
+ [PLAN_EDIT_GLOB]: "allow",
1211
+ [PLAN_EDIT_PLANS_GLOB]: "allow"
1042
1212
  }
1043
1213
  }
1044
1214
  }
@@ -1306,114 +1476,6 @@ var init_root_config = __esm({
1306
1476
  }
1307
1477
  });
1308
1478
 
1309
- // src/installer/lock.js
1310
- import { readFile as readFile4, writeFile as writeFile2, rename as rename2, mkdir as mkdir2 } from "node:fs/promises";
1311
- import { existsSync as existsSync8 } from "node:fs";
1312
- import { dirname as dirname4, resolve as resolve6 } from "node:path";
1313
- function lockPath(repoRoot) {
1314
- return resolve6(repoRoot, ".opencode", "ship.lock.json");
1315
- }
1316
- function computeIntegrity(lock) {
1317
- const { integrity: _ignored, ...without } = lock ?? {};
1318
- void _ignored;
1319
- return {
1320
- lockSha256: bytesHashString(stableStringify(without))
1321
- };
1322
- }
1323
- function normalizeLegacyLock(lock) {
1324
- if (!lock || typeof lock !== "object") return lock;
1325
- const { cleanupPending: _drop, ...rest } = lock;
1326
- void _drop;
1327
- return rest;
1328
- }
1329
- function validateLock(rawLock) {
1330
- if (rawLock === null || rawLock === void 0) {
1331
- return { ok: true, kind: "missing", issues: [] };
1332
- }
1333
- if (typeof rawLock !== "object" || Array.isArray(rawLock)) {
1334
- return { ok: false, kind: "shape", issues: ["lock root must be an object"] };
1335
- }
1336
- const issues = [];
1337
- let kind = "ok";
1338
- if (rawLock.contractVersion !== CURRENT_LOCK_SCHEMA && rawLock.contractVersion !== 3 && rawLock.contractVersion !== 2 && rawLock.contractVersion !== 1) {
1339
- issues.push(`unsupported contractVersion: ${JSON.stringify(rawLock.contractVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 3, 2, or 1)`);
1340
- kind = "schema";
1341
- }
1342
- const manager = rawLock.manager;
1343
- if (manager === void 0) {
1344
- issues.push("manager section missing");
1345
- kind = kind === "ok" ? "shape" : kind;
1346
- } else if (typeof manager !== "object" || manager === null) {
1347
- issues.push("manager section must be an object");
1348
- kind = kind === "ok" ? "shape" : kind;
1349
- } else if (manager.schemaVersion !== CURRENT_LOCK_SCHEMA && manager.schemaVersion !== 3 && manager.schemaVersion !== 2 && manager.schemaVersion !== 1) {
1350
- issues.push(`unsupported manager.schemaVersion: ${JSON.stringify(manager.schemaVersion)} (expected ${CURRENT_LOCK_SCHEMA}, 3, 2, or 1)`);
1351
- kind = "schema";
1352
- } else if (manager.name !== "opencode-ship") {
1353
- issues.push(`unknown manager.name: ${JSON.stringify(manager.name)}`);
1354
- kind = "shape";
1355
- } else if (rawLock.contractVersion >= 2 && manager.schemaVersion >= 2 && manager.profile !== void 0 && manager.profile !== "core" && manager.profile !== "engineering") {
1356
- issues.push(`invalid manager.profile: ${JSON.stringify(manager.profile)} (expected one of: engineering, core [legacy])`);
1357
- kind = "shape";
1358
- }
1359
- if (!rawLock.files || !Array.isArray(rawLock.files)) {
1360
- issues.push("files must be an array");
1361
- kind = kind === "ok" ? "shape" : kind;
1362
- }
1363
- if (!rawLock.integrity || typeof rawLock.integrity !== "object") {
1364
- issues.push("integrity section missing");
1365
- kind = kind === "ok" ? "shape" : kind;
1366
- } else {
1367
- const expected = computeIntegrity(rawLock).lockSha256;
1368
- if (expected !== rawLock.integrity.lockSha256) {
1369
- issues.push(`integrity mismatch: stored ${rawLock.integrity.lockSha256} != computed ${expected}`);
1370
- kind = "integrity";
1371
- }
1372
- }
1373
- return { ok: issues.length === 0, kind, issues };
1374
- }
1375
- async function readValidatedLock(repoRoot) {
1376
- const path = lockPath(repoRoot);
1377
- if (!existsSync8(path)) {
1378
- return { kind: "missing", lock: null, issues: [] };
1379
- }
1380
- let raw;
1381
- try {
1382
- const text = await readFile4(path, "utf8");
1383
- raw = JSON.parse(text);
1384
- } catch (e) {
1385
- return {
1386
- kind: "integrity",
1387
- lock: null,
1388
- issues: [`unable to parse lock JSON: ${e?.message ?? String(e)}`]
1389
- };
1390
- }
1391
- const validation = validateLock(raw);
1392
- if (validation.ok) {
1393
- return {
1394
- kind: validation.kind,
1395
- lock: normalizeLegacyLock(raw),
1396
- issues: []
1397
- };
1398
- }
1399
- return { kind: validation.kind, lock: null, issues: validation.issues };
1400
- }
1401
- function isSetupComplete(lock) {
1402
- if (!lock || typeof lock !== "object") return false;
1403
- const manager = lock.manager;
1404
- if (!manager || typeof manager !== "object") return false;
1405
- return manager.setupComplete === true;
1406
- }
1407
- var CURRENT_LOCK_SCHEMA;
1408
- var init_lock = __esm({
1409
- "src/installer/lock.js"() {
1410
- init_hash();
1411
- init_json_pointer();
1412
- init_profile();
1413
- CURRENT_LOCK_SCHEMA = 4;
1414
- }
1415
- });
1416
-
1417
1479
  // src/installer/agent-renderer.js
1418
1480
  var agent_renderer_exports = {};
1419
1481
  __export(agent_renderer_exports, {
@@ -1482,37 +1544,6 @@ var init_agent_renderer = __esm({
1482
1544
  }
1483
1545
  });
1484
1546
 
1485
- // src/installer/setup-pending.js
1486
- import { existsSync as existsSync14, readFileSync as readFileSync5, unlinkSync, writeFile as writeFile7, mkdir as mkdirAsync } from "node:fs";
1487
- import { promisify } from "node:util";
1488
- import { resolve as resolve13, dirname as dirname9 } from "node:path";
1489
- function setupPendingPath(repoRoot) {
1490
- return resolve13(repoRoot, REL_PATH);
1491
- }
1492
- async function writeSetupPending(repoRoot, payload) {
1493
- const path = setupPendingPath(repoRoot);
1494
- await mkdirAsyncAsync(dirname9(path), { recursive: true });
1495
- await writeFileAsync(path, JSON.stringify(payload, null, 2) + "\n", "utf8");
1496
- }
1497
- function clearSetupPending(repoRoot) {
1498
- const path = setupPendingPath(repoRoot);
1499
- if (!existsSync14(path)) return false;
1500
- try {
1501
- unlinkSync(path);
1502
- return true;
1503
- } catch {
1504
- return false;
1505
- }
1506
- }
1507
- var writeFileAsync, mkdirAsyncAsync, REL_PATH;
1508
- var init_setup_pending = __esm({
1509
- "src/installer/setup-pending.js"() {
1510
- writeFileAsync = promisify(writeFile7);
1511
- mkdirAsyncAsync = promisify(mkdirAsync);
1512
- REL_PATH = ".opencode/ship.setup-pending.json";
1513
- }
1514
- });
1515
-
1516
1547
  // src/installer/setup-state.js
1517
1548
  var setup_state_exports = {};
1518
1549
  __export(setup_state_exports, {
@@ -1521,9 +1552,9 @@ __export(setup_state_exports, {
1521
1552
  modelsComplete: () => modelsComplete,
1522
1553
  setupComplete: () => setupComplete
1523
1554
  });
1524
- import { existsSync as existsSync15 } from "node:fs";
1555
+ import { existsSync as existsSync14 } from "node:fs";
1525
1556
  import { readFile as readFile10 } from "node:fs/promises";
1526
- import { resolve as resolve14, dirname as dirname10 } from "node:path";
1557
+ import { resolve as resolve13 } from "node:path";
1527
1558
  function modelsComplete(repoRoot, configValue) {
1528
1559
  if (configValue === void 0) return false;
1529
1560
  return hasCompletedModels(configValue);
@@ -1531,12 +1562,12 @@ function modelsComplete(repoRoot, configValue) {
1531
1562
  async function setupComplete(repoRoot, configValue) {
1532
1563
  const missing = [];
1533
1564
  for (const rel of REQUIRED_DOCS) {
1534
- const path = resolve14(repoRoot, rel);
1535
- if (!existsSync15(path)) missing.push(rel);
1565
+ const path = resolve13(repoRoot, rel);
1566
+ if (!existsSync14(path)) missing.push(rel);
1536
1567
  }
1537
- const agentsPath = resolve14(repoRoot, "AGENTS.md");
1568
+ const agentsPath = resolve13(repoRoot, "AGENTS.md");
1538
1569
  let agentsOk = false;
1539
- if (existsSync15(agentsPath)) {
1570
+ if (existsSync14(agentsPath)) {
1540
1571
  try {
1541
1572
  const raw = await readFile10(agentsPath, "utf8");
1542
1573
  agentsOk = /##\s+Ship workflow\b/.test(raw);
@@ -1549,9 +1580,7 @@ async function setupComplete(repoRoot, configValue) {
1549
1580
  }
1550
1581
  const lockResult = await readValidatedLock(repoRoot);
1551
1582
  const cfgOk = modelsComplete(repoRoot, configValue);
1552
- const markerPath = setupPendingPath(repoRoot);
1553
- if (existsSync15(markerPath)) missing.push("setup-pending marker");
1554
- const lockOk = lockResult.kind === "ok" || lockResult.kind === "missing";
1583
+ const lockOk = lockResult.kind === "ok" && lockResult.lock?.contractVersion === CURRENT_LOCK_SCHEMA && lockResult.lock?.manager?.schemaVersion === CURRENT_LOCK_SCHEMA;
1555
1584
  const ok = cfgOk && lockOk && missing.length === 0;
1556
1585
  return {
1557
1586
  ok,
@@ -1564,7 +1593,6 @@ var init_setup_state = __esm({
1564
1593
  "src/installer/setup-state.js"() {
1565
1594
  init_config();
1566
1595
  init_lock();
1567
- init_setup_pending();
1568
1596
  REQUIRED_DOCS = [
1569
1597
  "docs/agents/issue-tracker.md",
1570
1598
  "docs/agents/domain.md",
@@ -1577,161 +1605,1827 @@ var init_setup_state = __esm({
1577
1605
  markerPath: SETUP_PENDING_REL_PATH
1578
1606
  });
1579
1607
  }
1580
- });
1581
-
1582
- // src/installer/cli-args.js
1583
- init_profile();
1584
- var USAGE = `opencode-ship <command> [options]
1585
-
1586
- Commands:
1587
- init Install managed files in this project. One-liner: pnpm dlx opencode-ship@latest init
1588
- diff Show what would change without writing.
1589
- update Apply pending updates after recovering the journal.
1590
- doctor Validate environment, lock, and references.
1591
- setup-complete Sole writer of lock.manager.setupComplete = true. Validates models + docs + AGENTS.md.
1592
- uninstall Remove managed files that still match the lock.
1593
- --version Print the version and exit.
1594
- --help Show this usage and exit.
1595
-
1596
- Options:
1597
- --root <path> Project root (defaults to cwd).
1598
- --profile engineering Override active profile (engineering only).
1599
- --force-config Rewrite the user config from detection (init only).
1600
- --force-root-config Create opencode.json when absent (init only).
1601
- --strict-doctor Fail init when doctor reports unhealthy checks.
1602
- --replace-managed Replace locally-modified managed files (update only).
1603
- --purge-config Remove ship.config.json when uninstalling.
1604
- --planner-model <id> Strong planner model id (init only, optional).
1605
- --builder-model <id> Cheap builder model id (init only, optional).
1606
- --final-reviewer-model <id> Final Standards + Spec reviewer model id (init only, optional).
1607
- --json Emit a JSON envelope instead of human output.
1608
+ });
1609
+
1610
+ // src/installer/cli-args.js
1611
+ init_profile();
1612
+ var USAGE = `opencode-ship <command> [options]
1613
+
1614
+ Commands:
1615
+ init Install managed files in this project. One-liner: pnpm dlx opencode-ship@latest init
1616
+ diff Show what would change without writing.
1617
+ update Apply pending updates after recovering the journal.
1618
+ doctor Validate environment, lock, and references.
1619
+ setup-complete Sole writer of lock.manager.setupComplete = true. Validates models + docs + AGENTS.md.
1620
+ uninstall Remove managed files that still match the lock.
1621
+ --version Print the version and exit.
1622
+ --help Show this usage and exit.
1623
+
1624
+ Options:
1625
+ --root <path> Project root (defaults to cwd).
1626
+ --profile engineering Override active profile (engineering only).
1627
+ --force-config Rewrite the user config from detection (init only).
1628
+ --force-root-config Create opencode.json when absent (init only).
1629
+ --strict-doctor Fail init when doctor reports unhealthy checks.
1630
+ --replace-managed Replace locally-modified managed files (update only).
1631
+ --purge-config Remove ship.config.json when uninstalling.
1632
+ --planner-model <id> Strong planner model id (init only, optional).
1633
+ --builder-model <id> Cheap builder model id (init only, optional).
1634
+ --final-reviewer-model <id> Final Standards + Spec reviewer model id (init only, optional).
1635
+ --json Emit a JSON envelope instead of human output.
1636
+
1637
+ After init succeeds, restart OpenCode and run /setup-ship-workflow.
1638
+ The setup skill calls 'opencode-ship setup-complete' at the end so
1639
+ the lock.manager.setupComplete flag flips to true and the ship
1640
+ controller can dispatch.
1641
+ `;
1642
+ var MODEL_ID_RE = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
1643
+ function parseFlags(argv) {
1644
+ const options = {
1645
+ rootPath: null,
1646
+ profile: null,
1647
+ json: false,
1648
+ replaceManaged: false,
1649
+ purgeConfig: false,
1650
+ forceConfig: false,
1651
+ forceRootConfig: false,
1652
+ strictDoctor: false,
1653
+ plannerModel: null,
1654
+ builderModel: null,
1655
+ finalReviewerModel: null
1656
+ };
1657
+ for (let i = 0; i < argv.length; i++) {
1658
+ const arg = argv[i];
1659
+ if (arg === "--json") options.json = true;
1660
+ else if (arg === "--replace-managed") options.replaceManaged = true;
1661
+ else if (arg === "--purge-config") options.purgeConfig = true;
1662
+ else if (arg === "--force-config") options.forceConfig = true;
1663
+ else if (arg === "--force-root-config") options.forceRootConfig = true;
1664
+ else if (arg === "--strict-doctor") options.strictDoctor = true;
1665
+ else if (arg === "--root") options.rootPath = argv[++i];
1666
+ else if (arg === "--profile") {
1667
+ const value = argv[++i];
1668
+ if (value === void 0) {
1669
+ return { error: "--profile requires a value" };
1670
+ }
1671
+ if (value === "core") {
1672
+ return {
1673
+ error: "the 'core' profile was removed in opencode-ship 1.1.0; only 'engineering' is supported. Run /setup-ship-workflow to migrate."
1674
+ };
1675
+ }
1676
+ if (!isValidProfile(value)) {
1677
+ return { error: `unknown profile '${value}' (expected one of: ${PROFILES.join(", ")})` };
1678
+ }
1679
+ options.profile = value;
1680
+ } else if (arg === "--planner-model" || arg === "--builder-model" || arg === "--final-reviewer-model") {
1681
+ const value = argv[++i];
1682
+ if (value === void 0) return { error: `${arg} requires a value` };
1683
+ if (!MODEL_ID_RE.test(value)) {
1684
+ return { error: `${arg} must be a "<provider>/<model>" id, got ${JSON.stringify(value)}` };
1685
+ }
1686
+ if (arg === "--planner-model") options.plannerModel = value;
1687
+ else if (arg === "--builder-model") options.builderModel = value;
1688
+ else options.finalReviewerModel = value;
1689
+ } else if (arg === "-h" || arg === "--help") return { help: true };
1690
+ else if (arg === "-v" || arg === "--version") return { version: true };
1691
+ else return { error: `unknown flag ${arg}` };
1692
+ }
1693
+ return options;
1694
+ }
1695
+ function parseCommand(argv) {
1696
+ for (const arg of argv) {
1697
+ if (arg === "--help" || arg === "-h") return { command: "help" };
1698
+ if (arg === "--version" || arg === "-v") return { command: "version" };
1699
+ }
1700
+ const [cmd, ...rest] = argv;
1701
+ if (!cmd) return { command: "help" };
1702
+ const flags = parseFlags(rest);
1703
+ if ("help" in flags) return { command: "help" };
1704
+ if ("version" in flags) return { command: "version" };
1705
+ if ("error" in flags) return { error: flags.error };
1706
+ switch (cmd) {
1707
+ case "init":
1708
+ case "diff":
1709
+ case "update":
1710
+ case "doctor":
1711
+ case "setup-complete":
1712
+ case "uninstall":
1713
+ return { command: cmd, options: flags };
1714
+ default:
1715
+ return { error: `unknown command ${cmd}` };
1716
+ }
1717
+ }
1718
+ function helpText() {
1719
+ return USAGE;
1720
+ }
1721
+
1722
+ // src/installer/commands/init.js
1723
+ import { promisify as promisify2 } from "node:util";
1724
+ import { writeFile as writeFile8, mkdir as mkdirAsync2 } from "node:fs/promises";
1725
+ import { dirname as dirname10, resolve as resolvePath } from "node:path";
1726
+
1727
+ // src/installer/executor.js
1728
+ init_catalog();
1729
+ init_version();
1730
+ import { existsSync as existsSync13 } from "node:fs";
1731
+ import { mkdir as mkdir6, readFile as readFile9, rename as rename5, unlink as unlink3, writeFile as writeFile6 } from "node:fs/promises";
1732
+ import { dirname as dirname8, resolve as resolve12 } from "node:path";
1733
+
1734
+ // src/installer/planner.js
1735
+ init_catalog();
1736
+ init_hash();
1737
+ init_config();
1738
+ init_lock();
1739
+ init_json_pointer();
1740
+ init_root_config();
1741
+ import { existsSync as existsSync8 } from "node:fs";
1742
+ import { readFile as readFile4, stat } from "node:fs/promises";
1743
+
1744
+ // src/installer/root-reconciliation.js
1745
+ init_root_config();
1746
+ init_json_pointer();
1747
+ init_hash();
1748
+ init_plan_mode_permissions();
1749
+ import { existsSync as existsSync7 } from "node:fs";
1750
+ import { join } from "node:path";
1751
+
1752
+ // src/installer/root-permissions.js
1753
+ init_json_pointer();
1754
+ init_plan_mode_permissions();
1755
+ var ASK = "ask";
1756
+ var ALLOW = "allow";
1757
+ var DENY = "deny";
1758
+ var SUBAGENT_DEPTH = 2;
1759
+ var CONTROLLER_TASK_ALLOW = [
1760
+ "ship-planner",
1761
+ "ship-task-builder",
1762
+ "ship-task-reviewer",
1763
+ "ship-final-standards-reviewer",
1764
+ "ship-final-spec-reviewer",
1765
+ "delivery-verifier"
1766
+ ];
1767
+ var PUBLIC_TOOL_IDS = [
1768
+ "delivery_cleanup",
1769
+ "delivery_github_read",
1770
+ "delivery_inspect",
1771
+ "delivery_issue",
1772
+ "delivery_issue_close",
1773
+ "delivery_issue_comment",
1774
+ "delivery_issue_labels",
1775
+ "delivery_issue_link",
1776
+ "delivery_merge",
1777
+ "delivery_pr",
1778
+ "delivery_publish",
1779
+ "delivery_ready",
1780
+ "delivery_review",
1781
+ "delivery_sync",
1782
+ "delivery_verify",
1783
+ "delivery_worktree",
1784
+ "ship_final_review",
1785
+ "ship_plan_approve",
1786
+ "ship_plan_start",
1787
+ "ship_plan_submit",
1788
+ "ship_resume",
1789
+ "ship_run_start",
1790
+ "ship_skill_audit",
1791
+ "ship_skill_discover",
1792
+ "ship_skill_install",
1793
+ "ship_skill_uninstall",
1794
+ "ship_status",
1795
+ "ship_task_commit",
1796
+ "ship_task_complete",
1797
+ "ship_task_report",
1798
+ "ship_task_review",
1799
+ "ship_task_start"
1800
+ ];
1801
+ var BUILD_TOOL_ALLOW = [
1802
+ "delivery_cleanup",
1803
+ "delivery_inspect",
1804
+ "delivery_issue",
1805
+ "delivery_pr",
1806
+ "delivery_ready",
1807
+ "delivery_worktree",
1808
+ "ship_status",
1809
+ "ship_resume"
1810
+ ];
1811
+ var BUILD_TOOL_ASK = [
1812
+ "ship_plan_approve",
1813
+ "delivery_merge",
1814
+ "delivery_issue_close",
1815
+ "ship_skill_install"
1816
+ ];
1817
+ var CONTROLLER_TOOL_ALLOW = [
1818
+ "delivery_inspect",
1819
+ "delivery_cleanup",
1820
+ "delivery_github_read",
1821
+ "delivery_issue",
1822
+ "delivery_issue_comment",
1823
+ "delivery_issue_labels",
1824
+ "delivery_issue_link",
1825
+ "delivery_worktree",
1826
+ "delivery_pr",
1827
+ "delivery_ready",
1828
+ "delivery_publish",
1829
+ "delivery_sync",
1830
+ "ship_plan_start",
1831
+ "ship_run_start",
1832
+ "ship_task_start",
1833
+ "ship_task_commit",
1834
+ "ship_task_complete",
1835
+ "ship_resume",
1836
+ "ship_status",
1837
+ "ship_skill_discover",
1838
+ "ship_skill_install",
1839
+ "ship_skill_audit",
1840
+ "ship_skill_uninstall"
1841
+ ];
1842
+ var CONTROLLER_TOOL_ASK = [
1843
+ "ship_plan_approve",
1844
+ "delivery_merge",
1845
+ "delivery_issue_close"
1846
+ ];
1847
+ var H = "git";
1848
+ var RESET = "--hard";
1849
+ var PUSH = "--force";
1850
+ var PUSH_SHORT = "-f";
1851
+ var STASH = "stash";
1852
+ var WTRF = "--force";
1853
+ var BRDEL = "-D";
1854
+ var FORBIDDEN_BASH_GLOBS = [
1855
+ `${H} ${RESET} *`,
1856
+ `${H} ${PUSH} *`,
1857
+ `${H} ${PUSH_SHORT} *`,
1858
+ `git clean -fd *`,
1859
+ `${H} ${STASH} *`,
1860
+ `${H} worktree remove ${WTRF} *`,
1861
+ `${H} branch ${BRDEL} *`
1862
+ ];
1863
+ var FORBIDDEN_BASH_GLOBS_CONTROLLER = [
1864
+ ...FORBIDDEN_BASH_GLOBS,
1865
+ "rm -rf *",
1866
+ "rm -rf /*"
1867
+ ];
1868
+ function denyMap(globs) {
1869
+ const out = { "*": "allow" };
1870
+ for (const g of globs) out[g] = "deny";
1871
+ return out;
1872
+ }
1873
+ function toolPermissionMap(allow, ask) {
1874
+ const out = { "*": DENY };
1875
+ for (const id of PUBLIC_TOOL_IDS) out[id] = DENY;
1876
+ for (const id of allow) out[id] = ALLOW;
1877
+ for (const id of ask) out[id] = ASK;
1878
+ return out;
1879
+ }
1880
+ function rootPermissionMatrix() {
1881
+ return {
1882
+ subagentDepth: SUBAGENT_DEPTH,
1883
+ build: {
1884
+ permission: {
1885
+ task: {
1886
+ "*": "deny",
1887
+ "ship-controller": "allow",
1888
+ "general": "allow",
1889
+ "plan": "deny",
1890
+ "delivery-reviewer": "allow",
1891
+ "delivery-verifier": "allow"
1892
+ },
1893
+ bash: {
1894
+ ...denyMap(FORBIDDEN_BASH_GLOBS),
1895
+ "rm *": "ask"
1896
+ }
1897
+ },
1898
+ tools: {
1899
+ ...toolPermissionMap(BUILD_TOOL_ALLOW, BUILD_TOOL_ASK)
1900
+ }
1901
+ },
1902
+ shipController: {
1903
+ permission: {
1904
+ task: {
1905
+ "*": "deny",
1906
+ ...Object.fromEntries(CONTROLLER_TASK_ALLOW.map((t) => [t, "allow"]))
1907
+ },
1908
+ bash: denyMap(FORBIDDEN_BASH_GLOBS_CONTROLLER)
1909
+ },
1910
+ tools: {
1911
+ ...toolPermissionMap(CONTROLLER_TOOL_ALLOW, CONTROLLER_TOOL_ASK)
1912
+ }
1913
+ }
1914
+ };
1915
+ }
1916
+ function matrixLeafPointers() {
1917
+ const matrix = rootPermissionMatrix();
1918
+ const out = [];
1919
+ out.push({ pointer: "/subagent_depth", strategy: "value", value: matrix.subagentDepth, scope: "engineering" });
1920
+ const agents = [
1921
+ { name: "build", block: matrix.build },
1922
+ { name: "ship-controller", block: matrix.shipController }
1923
+ ];
1924
+ for (const agent of agents) {
1925
+ const perm = agent.block.permission;
1926
+ for (const category of Object.keys(perm)) {
1927
+ const map = perm[category];
1928
+ for (const key of Object.keys(map)) {
1929
+ const v = map[key];
1930
+ if (v === void 0 || v === null) continue;
1931
+ out.push({
1932
+ pointer: pointerPath(["agent", agent.name, "permission", category, key]),
1933
+ strategy: "value",
1934
+ value: v,
1935
+ scope: "engineering"
1936
+ });
1937
+ }
1938
+ }
1939
+ const tools = agent.block.tools;
1940
+ if (tools && typeof tools === "object") {
1941
+ for (const key of Object.keys(tools)) {
1942
+ const v = tools[key];
1943
+ if (v === void 0 || v === null) continue;
1944
+ out.push({
1945
+ pointer: pointerPath(["agent", agent.name, "permission", key]),
1946
+ strategy: "value",
1947
+ value: v,
1948
+ scope: "engineering"
1949
+ });
1950
+ }
1951
+ }
1952
+ }
1953
+ out.push({ pointer: PLAN_EDIT_GLOB_POINTER, strategy: "value", value: "allow", scope: "engineering" });
1954
+ out.push({ pointer: PLAN_EDIT_PLANS_GLOB_POINTER, strategy: "value", value: "allow", scope: "engineering" });
1955
+ return out;
1956
+ }
1957
+
1958
+ // node_modules/jsonc-parser/lib/esm/impl/scanner.js
1959
+ function createScanner(text, ignoreTrivia = false) {
1960
+ const len = text.length;
1961
+ let pos = 0, value = "", tokenOffset = 0, token = 16, lineNumber = 0, lineStartOffset = 0, tokenLineStartOffset = 0, prevTokenLineStartOffset = 0, scanError = 0;
1962
+ function scanHexDigits(count, exact) {
1963
+ let digits = 0;
1964
+ let value2 = 0;
1965
+ while (digits < count || !exact) {
1966
+ let ch = text.charCodeAt(pos);
1967
+ if (ch >= 48 && ch <= 57) {
1968
+ value2 = value2 * 16 + ch - 48;
1969
+ } else if (ch >= 65 && ch <= 70) {
1970
+ value2 = value2 * 16 + ch - 65 + 10;
1971
+ } else if (ch >= 97 && ch <= 102) {
1972
+ value2 = value2 * 16 + ch - 97 + 10;
1973
+ } else {
1974
+ break;
1975
+ }
1976
+ pos++;
1977
+ digits++;
1978
+ }
1979
+ if (digits < count) {
1980
+ value2 = -1;
1981
+ }
1982
+ return value2;
1983
+ }
1984
+ function setPosition(newPosition) {
1985
+ pos = newPosition;
1986
+ value = "";
1987
+ tokenOffset = 0;
1988
+ token = 16;
1989
+ scanError = 0;
1990
+ }
1991
+ function scanNumber() {
1992
+ let start = pos;
1993
+ if (text.charCodeAt(pos) === 48) {
1994
+ pos++;
1995
+ } else {
1996
+ pos++;
1997
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
1998
+ pos++;
1999
+ }
2000
+ }
2001
+ if (pos < text.length && text.charCodeAt(pos) === 46) {
2002
+ pos++;
2003
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
2004
+ pos++;
2005
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
2006
+ pos++;
2007
+ }
2008
+ } else {
2009
+ scanError = 3;
2010
+ return text.substring(start, pos);
2011
+ }
2012
+ }
2013
+ let end = pos;
2014
+ if (pos < text.length && (text.charCodeAt(pos) === 69 || text.charCodeAt(pos) === 101)) {
2015
+ pos++;
2016
+ if (pos < text.length && text.charCodeAt(pos) === 43 || text.charCodeAt(pos) === 45) {
2017
+ pos++;
2018
+ }
2019
+ if (pos < text.length && isDigit(text.charCodeAt(pos))) {
2020
+ pos++;
2021
+ while (pos < text.length && isDigit(text.charCodeAt(pos))) {
2022
+ pos++;
2023
+ }
2024
+ end = pos;
2025
+ } else {
2026
+ scanError = 3;
2027
+ }
2028
+ }
2029
+ return text.substring(start, end);
2030
+ }
2031
+ function scanString() {
2032
+ let result = "", start = pos;
2033
+ while (true) {
2034
+ if (pos >= len) {
2035
+ result += text.substring(start, pos);
2036
+ scanError = 2;
2037
+ break;
2038
+ }
2039
+ const ch = text.charCodeAt(pos);
2040
+ if (ch === 34) {
2041
+ result += text.substring(start, pos);
2042
+ pos++;
2043
+ break;
2044
+ }
2045
+ if (ch === 92) {
2046
+ result += text.substring(start, pos);
2047
+ pos++;
2048
+ if (pos >= len) {
2049
+ scanError = 2;
2050
+ break;
2051
+ }
2052
+ const ch2 = text.charCodeAt(pos++);
2053
+ switch (ch2) {
2054
+ case 34:
2055
+ result += '"';
2056
+ break;
2057
+ case 92:
2058
+ result += "\\";
2059
+ break;
2060
+ case 47:
2061
+ result += "/";
2062
+ break;
2063
+ case 98:
2064
+ result += "\b";
2065
+ break;
2066
+ case 102:
2067
+ result += "\f";
2068
+ break;
2069
+ case 110:
2070
+ result += "\n";
2071
+ break;
2072
+ case 114:
2073
+ result += "\r";
2074
+ break;
2075
+ case 116:
2076
+ result += " ";
2077
+ break;
2078
+ case 117:
2079
+ const ch3 = scanHexDigits(4, true);
2080
+ if (ch3 >= 0) {
2081
+ result += String.fromCharCode(ch3);
2082
+ } else {
2083
+ scanError = 4;
2084
+ }
2085
+ break;
2086
+ default:
2087
+ scanError = 5;
2088
+ }
2089
+ start = pos;
2090
+ continue;
2091
+ }
2092
+ if (ch >= 0 && ch <= 31) {
2093
+ if (isLineBreak(ch)) {
2094
+ result += text.substring(start, pos);
2095
+ scanError = 2;
2096
+ break;
2097
+ } else {
2098
+ scanError = 6;
2099
+ }
2100
+ }
2101
+ pos++;
2102
+ }
2103
+ return result;
2104
+ }
2105
+ function scanNext() {
2106
+ value = "";
2107
+ scanError = 0;
2108
+ tokenOffset = pos;
2109
+ lineStartOffset = lineNumber;
2110
+ prevTokenLineStartOffset = tokenLineStartOffset;
2111
+ if (pos >= len) {
2112
+ tokenOffset = len;
2113
+ return token = 17;
2114
+ }
2115
+ let code = text.charCodeAt(pos);
2116
+ if (isWhiteSpace(code)) {
2117
+ do {
2118
+ pos++;
2119
+ value += String.fromCharCode(code);
2120
+ code = text.charCodeAt(pos);
2121
+ } while (isWhiteSpace(code));
2122
+ return token = 15;
2123
+ }
2124
+ if (isLineBreak(code)) {
2125
+ pos++;
2126
+ value += String.fromCharCode(code);
2127
+ if (code === 13 && text.charCodeAt(pos) === 10) {
2128
+ pos++;
2129
+ value += "\n";
2130
+ }
2131
+ lineNumber++;
2132
+ tokenLineStartOffset = pos;
2133
+ return token = 14;
2134
+ }
2135
+ switch (code) {
2136
+ // tokens: []{}:,
2137
+ case 123:
2138
+ pos++;
2139
+ return token = 1;
2140
+ case 125:
2141
+ pos++;
2142
+ return token = 2;
2143
+ case 91:
2144
+ pos++;
2145
+ return token = 3;
2146
+ case 93:
2147
+ pos++;
2148
+ return token = 4;
2149
+ case 58:
2150
+ pos++;
2151
+ return token = 6;
2152
+ case 44:
2153
+ pos++;
2154
+ return token = 5;
2155
+ // strings
2156
+ case 34:
2157
+ pos++;
2158
+ value = scanString();
2159
+ return token = 10;
2160
+ // comments
2161
+ case 47:
2162
+ const start = pos - 1;
2163
+ if (text.charCodeAt(pos + 1) === 47) {
2164
+ pos += 2;
2165
+ while (pos < len) {
2166
+ if (isLineBreak(text.charCodeAt(pos))) {
2167
+ break;
2168
+ }
2169
+ pos++;
2170
+ }
2171
+ value = text.substring(start, pos);
2172
+ return token = 12;
2173
+ }
2174
+ if (text.charCodeAt(pos + 1) === 42) {
2175
+ pos += 2;
2176
+ const safeLength = len - 1;
2177
+ let commentClosed = false;
2178
+ while (pos < safeLength) {
2179
+ const ch = text.charCodeAt(pos);
2180
+ if (ch === 42 && text.charCodeAt(pos + 1) === 47) {
2181
+ pos += 2;
2182
+ commentClosed = true;
2183
+ break;
2184
+ }
2185
+ pos++;
2186
+ if (isLineBreak(ch)) {
2187
+ if (ch === 13 && text.charCodeAt(pos) === 10) {
2188
+ pos++;
2189
+ }
2190
+ lineNumber++;
2191
+ tokenLineStartOffset = pos;
2192
+ }
2193
+ }
2194
+ if (!commentClosed) {
2195
+ pos++;
2196
+ scanError = 1;
2197
+ }
2198
+ value = text.substring(start, pos);
2199
+ return token = 13;
2200
+ }
2201
+ value += String.fromCharCode(code);
2202
+ pos++;
2203
+ return token = 16;
2204
+ // numbers
2205
+ case 45:
2206
+ value += String.fromCharCode(code);
2207
+ pos++;
2208
+ if (pos === len || !isDigit(text.charCodeAt(pos))) {
2209
+ return token = 16;
2210
+ }
2211
+ // found a minus, followed by a number so
2212
+ // we fall through to proceed with scanning
2213
+ // numbers
2214
+ case 48:
2215
+ case 49:
2216
+ case 50:
2217
+ case 51:
2218
+ case 52:
2219
+ case 53:
2220
+ case 54:
2221
+ case 55:
2222
+ case 56:
2223
+ case 57:
2224
+ value += scanNumber();
2225
+ return token = 11;
2226
+ // literals and unknown symbols
2227
+ default:
2228
+ while (pos < len && isUnknownContentCharacter(code)) {
2229
+ pos++;
2230
+ code = text.charCodeAt(pos);
2231
+ }
2232
+ if (tokenOffset !== pos) {
2233
+ value = text.substring(tokenOffset, pos);
2234
+ switch (value) {
2235
+ case "true":
2236
+ return token = 8;
2237
+ case "false":
2238
+ return token = 9;
2239
+ case "null":
2240
+ return token = 7;
2241
+ }
2242
+ return token = 16;
2243
+ }
2244
+ value += String.fromCharCode(code);
2245
+ pos++;
2246
+ return token = 16;
2247
+ }
2248
+ }
2249
+ function isUnknownContentCharacter(code) {
2250
+ if (isWhiteSpace(code) || isLineBreak(code)) {
2251
+ return false;
2252
+ }
2253
+ switch (code) {
2254
+ case 125:
2255
+ case 93:
2256
+ case 123:
2257
+ case 91:
2258
+ case 34:
2259
+ case 58:
2260
+ case 44:
2261
+ case 47:
2262
+ return false;
2263
+ }
2264
+ return true;
2265
+ }
2266
+ function scanNextNonTrivia() {
2267
+ let result;
2268
+ do {
2269
+ result = scanNext();
2270
+ } while (result >= 12 && result <= 15);
2271
+ return result;
2272
+ }
2273
+ return {
2274
+ setPosition,
2275
+ getPosition: () => pos,
2276
+ scan: ignoreTrivia ? scanNextNonTrivia : scanNext,
2277
+ getToken: () => token,
2278
+ getTokenValue: () => value,
2279
+ getTokenOffset: () => tokenOffset,
2280
+ getTokenLength: () => pos - tokenOffset,
2281
+ getTokenStartLine: () => lineStartOffset,
2282
+ getTokenStartCharacter: () => tokenOffset - prevTokenLineStartOffset,
2283
+ getTokenError: () => scanError
2284
+ };
2285
+ }
2286
+ function isWhiteSpace(ch) {
2287
+ return ch === 32 || ch === 9;
2288
+ }
2289
+ function isLineBreak(ch) {
2290
+ return ch === 10 || ch === 13;
2291
+ }
2292
+ function isDigit(ch) {
2293
+ return ch >= 48 && ch <= 57;
2294
+ }
2295
+ var CharacterCodes;
2296
+ (function(CharacterCodes2) {
2297
+ CharacterCodes2[CharacterCodes2["lineFeed"] = 10] = "lineFeed";
2298
+ CharacterCodes2[CharacterCodes2["carriageReturn"] = 13] = "carriageReturn";
2299
+ CharacterCodes2[CharacterCodes2["space"] = 32] = "space";
2300
+ CharacterCodes2[CharacterCodes2["_0"] = 48] = "_0";
2301
+ CharacterCodes2[CharacterCodes2["_1"] = 49] = "_1";
2302
+ CharacterCodes2[CharacterCodes2["_2"] = 50] = "_2";
2303
+ CharacterCodes2[CharacterCodes2["_3"] = 51] = "_3";
2304
+ CharacterCodes2[CharacterCodes2["_4"] = 52] = "_4";
2305
+ CharacterCodes2[CharacterCodes2["_5"] = 53] = "_5";
2306
+ CharacterCodes2[CharacterCodes2["_6"] = 54] = "_6";
2307
+ CharacterCodes2[CharacterCodes2["_7"] = 55] = "_7";
2308
+ CharacterCodes2[CharacterCodes2["_8"] = 56] = "_8";
2309
+ CharacterCodes2[CharacterCodes2["_9"] = 57] = "_9";
2310
+ CharacterCodes2[CharacterCodes2["a"] = 97] = "a";
2311
+ CharacterCodes2[CharacterCodes2["b"] = 98] = "b";
2312
+ CharacterCodes2[CharacterCodes2["c"] = 99] = "c";
2313
+ CharacterCodes2[CharacterCodes2["d"] = 100] = "d";
2314
+ CharacterCodes2[CharacterCodes2["e"] = 101] = "e";
2315
+ CharacterCodes2[CharacterCodes2["f"] = 102] = "f";
2316
+ CharacterCodes2[CharacterCodes2["g"] = 103] = "g";
2317
+ CharacterCodes2[CharacterCodes2["h"] = 104] = "h";
2318
+ CharacterCodes2[CharacterCodes2["i"] = 105] = "i";
2319
+ CharacterCodes2[CharacterCodes2["j"] = 106] = "j";
2320
+ CharacterCodes2[CharacterCodes2["k"] = 107] = "k";
2321
+ CharacterCodes2[CharacterCodes2["l"] = 108] = "l";
2322
+ CharacterCodes2[CharacterCodes2["m"] = 109] = "m";
2323
+ CharacterCodes2[CharacterCodes2["n"] = 110] = "n";
2324
+ CharacterCodes2[CharacterCodes2["o"] = 111] = "o";
2325
+ CharacterCodes2[CharacterCodes2["p"] = 112] = "p";
2326
+ CharacterCodes2[CharacterCodes2["q"] = 113] = "q";
2327
+ CharacterCodes2[CharacterCodes2["r"] = 114] = "r";
2328
+ CharacterCodes2[CharacterCodes2["s"] = 115] = "s";
2329
+ CharacterCodes2[CharacterCodes2["t"] = 116] = "t";
2330
+ CharacterCodes2[CharacterCodes2["u"] = 117] = "u";
2331
+ CharacterCodes2[CharacterCodes2["v"] = 118] = "v";
2332
+ CharacterCodes2[CharacterCodes2["w"] = 119] = "w";
2333
+ CharacterCodes2[CharacterCodes2["x"] = 120] = "x";
2334
+ CharacterCodes2[CharacterCodes2["y"] = 121] = "y";
2335
+ CharacterCodes2[CharacterCodes2["z"] = 122] = "z";
2336
+ CharacterCodes2[CharacterCodes2["A"] = 65] = "A";
2337
+ CharacterCodes2[CharacterCodes2["B"] = 66] = "B";
2338
+ CharacterCodes2[CharacterCodes2["C"] = 67] = "C";
2339
+ CharacterCodes2[CharacterCodes2["D"] = 68] = "D";
2340
+ CharacterCodes2[CharacterCodes2["E"] = 69] = "E";
2341
+ CharacterCodes2[CharacterCodes2["F"] = 70] = "F";
2342
+ CharacterCodes2[CharacterCodes2["G"] = 71] = "G";
2343
+ CharacterCodes2[CharacterCodes2["H"] = 72] = "H";
2344
+ CharacterCodes2[CharacterCodes2["I"] = 73] = "I";
2345
+ CharacterCodes2[CharacterCodes2["J"] = 74] = "J";
2346
+ CharacterCodes2[CharacterCodes2["K"] = 75] = "K";
2347
+ CharacterCodes2[CharacterCodes2["L"] = 76] = "L";
2348
+ CharacterCodes2[CharacterCodes2["M"] = 77] = "M";
2349
+ CharacterCodes2[CharacterCodes2["N"] = 78] = "N";
2350
+ CharacterCodes2[CharacterCodes2["O"] = 79] = "O";
2351
+ CharacterCodes2[CharacterCodes2["P"] = 80] = "P";
2352
+ CharacterCodes2[CharacterCodes2["Q"] = 81] = "Q";
2353
+ CharacterCodes2[CharacterCodes2["R"] = 82] = "R";
2354
+ CharacterCodes2[CharacterCodes2["S"] = 83] = "S";
2355
+ CharacterCodes2[CharacterCodes2["T"] = 84] = "T";
2356
+ CharacterCodes2[CharacterCodes2["U"] = 85] = "U";
2357
+ CharacterCodes2[CharacterCodes2["V"] = 86] = "V";
2358
+ CharacterCodes2[CharacterCodes2["W"] = 87] = "W";
2359
+ CharacterCodes2[CharacterCodes2["X"] = 88] = "X";
2360
+ CharacterCodes2[CharacterCodes2["Y"] = 89] = "Y";
2361
+ CharacterCodes2[CharacterCodes2["Z"] = 90] = "Z";
2362
+ CharacterCodes2[CharacterCodes2["asterisk"] = 42] = "asterisk";
2363
+ CharacterCodes2[CharacterCodes2["backslash"] = 92] = "backslash";
2364
+ CharacterCodes2[CharacterCodes2["closeBrace"] = 125] = "closeBrace";
2365
+ CharacterCodes2[CharacterCodes2["closeBracket"] = 93] = "closeBracket";
2366
+ CharacterCodes2[CharacterCodes2["colon"] = 58] = "colon";
2367
+ CharacterCodes2[CharacterCodes2["comma"] = 44] = "comma";
2368
+ CharacterCodes2[CharacterCodes2["dot"] = 46] = "dot";
2369
+ CharacterCodes2[CharacterCodes2["doubleQuote"] = 34] = "doubleQuote";
2370
+ CharacterCodes2[CharacterCodes2["minus"] = 45] = "minus";
2371
+ CharacterCodes2[CharacterCodes2["openBrace"] = 123] = "openBrace";
2372
+ CharacterCodes2[CharacterCodes2["openBracket"] = 91] = "openBracket";
2373
+ CharacterCodes2[CharacterCodes2["plus"] = 43] = "plus";
2374
+ CharacterCodes2[CharacterCodes2["slash"] = 47] = "slash";
2375
+ CharacterCodes2[CharacterCodes2["formFeed"] = 12] = "formFeed";
2376
+ CharacterCodes2[CharacterCodes2["tab"] = 9] = "tab";
2377
+ })(CharacterCodes || (CharacterCodes = {}));
2378
+
2379
+ // node_modules/jsonc-parser/lib/esm/impl/string-intern.js
2380
+ var cachedSpaces = new Array(20).fill(0).map((_, index) => {
2381
+ return " ".repeat(index);
2382
+ });
2383
+ var maxCachedValues = 200;
2384
+ var cachedBreakLinesWithSpaces = {
2385
+ " ": {
2386
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
2387
+ return "\n" + " ".repeat(index);
2388
+ }),
2389
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
2390
+ return "\r" + " ".repeat(index);
2391
+ }),
2392
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
2393
+ return "\r\n" + " ".repeat(index);
2394
+ })
2395
+ },
2396
+ " ": {
2397
+ "\n": new Array(maxCachedValues).fill(0).map((_, index) => {
2398
+ return "\n" + " ".repeat(index);
2399
+ }),
2400
+ "\r": new Array(maxCachedValues).fill(0).map((_, index) => {
2401
+ return "\r" + " ".repeat(index);
2402
+ }),
2403
+ "\r\n": new Array(maxCachedValues).fill(0).map((_, index) => {
2404
+ return "\r\n" + " ".repeat(index);
2405
+ })
2406
+ }
2407
+ };
2408
+ var supportedEols = ["\n", "\r", "\r\n"];
2409
+
2410
+ // node_modules/jsonc-parser/lib/esm/impl/format.js
2411
+ function format(documentText, range, options) {
2412
+ let initialIndentLevel;
2413
+ let formatText;
2414
+ let formatTextStart;
2415
+ let rangeStart;
2416
+ let rangeEnd;
2417
+ if (range) {
2418
+ rangeStart = range.offset;
2419
+ rangeEnd = rangeStart + range.length;
2420
+ formatTextStart = rangeStart;
2421
+ while (formatTextStart > 0 && !isEOL(documentText, formatTextStart - 1)) {
2422
+ formatTextStart--;
2423
+ }
2424
+ let endOffset = rangeEnd;
2425
+ while (endOffset < documentText.length && !isEOL(documentText, endOffset)) {
2426
+ endOffset++;
2427
+ }
2428
+ formatText = documentText.substring(formatTextStart, endOffset);
2429
+ initialIndentLevel = computeIndentLevel(formatText, options);
2430
+ } else {
2431
+ formatText = documentText;
2432
+ initialIndentLevel = 0;
2433
+ formatTextStart = 0;
2434
+ rangeStart = 0;
2435
+ rangeEnd = documentText.length;
2436
+ }
2437
+ const eol = getEOL(options, documentText);
2438
+ const eolFastPathSupported = supportedEols.includes(eol);
2439
+ let numberLineBreaks = 0;
2440
+ let indentLevel = 0;
2441
+ let indentValue;
2442
+ if (options.insertSpaces) {
2443
+ indentValue = cachedSpaces[options.tabSize || 4] ?? repeat(cachedSpaces[1], options.tabSize || 4);
2444
+ } else {
2445
+ indentValue = " ";
2446
+ }
2447
+ const indentType = indentValue === " " ? " " : " ";
2448
+ let scanner = createScanner(formatText, false);
2449
+ let hasError = false;
2450
+ function newLinesAndIndent() {
2451
+ if (numberLineBreaks > 1) {
2452
+ return repeat(eol, numberLineBreaks) + repeat(indentValue, initialIndentLevel + indentLevel);
2453
+ }
2454
+ const amountOfSpaces = indentValue.length * (initialIndentLevel + indentLevel);
2455
+ if (!eolFastPathSupported || amountOfSpaces > cachedBreakLinesWithSpaces[indentType][eol].length) {
2456
+ return eol + repeat(indentValue, initialIndentLevel + indentLevel);
2457
+ }
2458
+ if (amountOfSpaces <= 0) {
2459
+ return eol;
2460
+ }
2461
+ return cachedBreakLinesWithSpaces[indentType][eol][amountOfSpaces];
2462
+ }
2463
+ function scanNext() {
2464
+ let token = scanner.scan();
2465
+ numberLineBreaks = 0;
2466
+ while (token === 15 || token === 14) {
2467
+ if (token === 14 && options.keepLines) {
2468
+ numberLineBreaks += 1;
2469
+ } else if (token === 14) {
2470
+ numberLineBreaks = 1;
2471
+ }
2472
+ token = scanner.scan();
2473
+ }
2474
+ hasError = token === 16 || scanner.getTokenError() !== 0;
2475
+ return token;
2476
+ }
2477
+ const editOperations = [];
2478
+ function addEdit(text, startOffset, endOffset) {
2479
+ if (!hasError && (!range || startOffset < rangeEnd && endOffset > rangeStart) && documentText.substring(startOffset, endOffset) !== text) {
2480
+ editOperations.push({ offset: startOffset, length: endOffset - startOffset, content: text });
2481
+ }
2482
+ }
2483
+ let firstToken = scanNext();
2484
+ if (options.keepLines && numberLineBreaks > 0) {
2485
+ addEdit(repeat(eol, numberLineBreaks), 0, 0);
2486
+ }
2487
+ if (firstToken !== 17) {
2488
+ let firstTokenStart = scanner.getTokenOffset() + formatTextStart;
2489
+ let initialIndent = indentValue.length * initialIndentLevel < 20 && options.insertSpaces ? cachedSpaces[indentValue.length * initialIndentLevel] : repeat(indentValue, initialIndentLevel);
2490
+ addEdit(initialIndent, formatTextStart, firstTokenStart);
2491
+ }
2492
+ while (firstToken !== 17) {
2493
+ let firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
2494
+ let secondToken = scanNext();
2495
+ let replaceContent = "";
2496
+ let needsLineBreak = false;
2497
+ while (numberLineBreaks === 0 && (secondToken === 12 || secondToken === 13)) {
2498
+ let commentTokenStart = scanner.getTokenOffset() + formatTextStart;
2499
+ addEdit(cachedSpaces[1], firstTokenEnd, commentTokenStart);
2500
+ firstTokenEnd = scanner.getTokenOffset() + scanner.getTokenLength() + formatTextStart;
2501
+ needsLineBreak = secondToken === 12;
2502
+ replaceContent = needsLineBreak ? newLinesAndIndent() : "";
2503
+ secondToken = scanNext();
2504
+ }
2505
+ if (secondToken === 2) {
2506
+ if (firstToken !== 1) {
2507
+ indentLevel--;
2508
+ }
2509
+ ;
2510
+ if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 1) {
2511
+ replaceContent = newLinesAndIndent();
2512
+ } else if (options.keepLines) {
2513
+ replaceContent = cachedSpaces[1];
2514
+ }
2515
+ } else if (secondToken === 4) {
2516
+ if (firstToken !== 3) {
2517
+ indentLevel--;
2518
+ }
2519
+ ;
2520
+ if (options.keepLines && numberLineBreaks > 0 || !options.keepLines && firstToken !== 3) {
2521
+ replaceContent = newLinesAndIndent();
2522
+ } else if (options.keepLines) {
2523
+ replaceContent = cachedSpaces[1];
2524
+ }
2525
+ } else {
2526
+ switch (firstToken) {
2527
+ case 3:
2528
+ case 1:
2529
+ indentLevel++;
2530
+ if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
2531
+ replaceContent = newLinesAndIndent();
2532
+ } else {
2533
+ replaceContent = cachedSpaces[1];
2534
+ }
2535
+ break;
2536
+ case 5:
2537
+ if (options.keepLines && numberLineBreaks > 0 || !options.keepLines) {
2538
+ replaceContent = newLinesAndIndent();
2539
+ } else {
2540
+ replaceContent = cachedSpaces[1];
2541
+ }
2542
+ break;
2543
+ case 12:
2544
+ replaceContent = newLinesAndIndent();
2545
+ break;
2546
+ case 13:
2547
+ if (numberLineBreaks > 0) {
2548
+ replaceContent = newLinesAndIndent();
2549
+ } else if (!needsLineBreak) {
2550
+ replaceContent = cachedSpaces[1];
2551
+ }
2552
+ break;
2553
+ case 6:
2554
+ if (options.keepLines && numberLineBreaks > 0) {
2555
+ replaceContent = newLinesAndIndent();
2556
+ } else if (!needsLineBreak) {
2557
+ replaceContent = cachedSpaces[1];
2558
+ }
2559
+ break;
2560
+ case 10:
2561
+ if (options.keepLines && numberLineBreaks > 0) {
2562
+ replaceContent = newLinesAndIndent();
2563
+ } else if (secondToken === 6 && !needsLineBreak) {
2564
+ replaceContent = "";
2565
+ }
2566
+ break;
2567
+ case 7:
2568
+ case 8:
2569
+ case 9:
2570
+ case 11:
2571
+ case 2:
2572
+ case 4:
2573
+ if (options.keepLines && numberLineBreaks > 0) {
2574
+ replaceContent = newLinesAndIndent();
2575
+ } else {
2576
+ if ((secondToken === 12 || secondToken === 13) && !needsLineBreak) {
2577
+ replaceContent = cachedSpaces[1];
2578
+ } else if (secondToken !== 5 && secondToken !== 17) {
2579
+ hasError = true;
2580
+ }
2581
+ }
2582
+ break;
2583
+ case 16:
2584
+ hasError = true;
2585
+ break;
2586
+ }
2587
+ if (numberLineBreaks > 0 && (secondToken === 12 || secondToken === 13)) {
2588
+ replaceContent = newLinesAndIndent();
2589
+ }
2590
+ }
2591
+ if (secondToken === 17) {
2592
+ if (options.keepLines && numberLineBreaks > 0) {
2593
+ replaceContent = newLinesAndIndent();
2594
+ } else {
2595
+ replaceContent = options.insertFinalNewline ? eol : "";
2596
+ }
2597
+ }
2598
+ const secondTokenStart = scanner.getTokenOffset() + formatTextStart;
2599
+ addEdit(replaceContent, firstTokenEnd, secondTokenStart);
2600
+ firstToken = secondToken;
2601
+ }
2602
+ return editOperations;
2603
+ }
2604
+ function repeat(s, count) {
2605
+ let result = "";
2606
+ for (let i = 0; i < count; i++) {
2607
+ result += s;
2608
+ }
2609
+ return result;
2610
+ }
2611
+ function computeIndentLevel(content, options) {
2612
+ let i = 0;
2613
+ let nChars = 0;
2614
+ const tabSize = options.tabSize || 4;
2615
+ while (i < content.length) {
2616
+ let ch = content.charAt(i);
2617
+ if (ch === cachedSpaces[1]) {
2618
+ nChars++;
2619
+ } else if (ch === " ") {
2620
+ nChars += tabSize;
2621
+ } else {
2622
+ break;
2623
+ }
2624
+ i++;
2625
+ }
2626
+ return Math.floor(nChars / tabSize);
2627
+ }
2628
+ function getEOL(options, text) {
2629
+ for (let i = 0; i < text.length; i++) {
2630
+ const ch = text.charAt(i);
2631
+ if (ch === "\r") {
2632
+ if (i + 1 < text.length && text.charAt(i + 1) === "\n") {
2633
+ return "\r\n";
2634
+ }
2635
+ return "\r";
2636
+ } else if (ch === "\n") {
2637
+ return "\n";
2638
+ }
2639
+ }
2640
+ return options && options.eol || "\n";
2641
+ }
2642
+ function isEOL(text, offset) {
2643
+ return "\r\n".indexOf(text.charAt(offset)) !== -1;
2644
+ }
2645
+
2646
+ // node_modules/jsonc-parser/lib/esm/impl/parser.js
2647
+ var ParseOptions;
2648
+ (function(ParseOptions2) {
2649
+ ParseOptions2.DEFAULT = {
2650
+ allowTrailingComma: false
2651
+ };
2652
+ })(ParseOptions || (ParseOptions = {}));
2653
+ function parse(text, errors = [], options = ParseOptions.DEFAULT) {
2654
+ let currentProperty = null;
2655
+ let currentParent = [];
2656
+ const previousParents = [];
2657
+ function onValue(value) {
2658
+ if (Array.isArray(currentParent)) {
2659
+ currentParent.push(value);
2660
+ } else if (currentProperty !== null) {
2661
+ currentParent[currentProperty] = value;
2662
+ }
2663
+ }
2664
+ const visitor = {
2665
+ onObjectBegin: () => {
2666
+ const object = {};
2667
+ onValue(object);
2668
+ previousParents.push(currentParent);
2669
+ currentParent = object;
2670
+ currentProperty = null;
2671
+ },
2672
+ onObjectProperty: (name) => {
2673
+ currentProperty = name;
2674
+ },
2675
+ onObjectEnd: () => {
2676
+ currentParent = previousParents.pop();
2677
+ },
2678
+ onArrayBegin: () => {
2679
+ const array = [];
2680
+ onValue(array);
2681
+ previousParents.push(currentParent);
2682
+ currentParent = array;
2683
+ currentProperty = null;
2684
+ },
2685
+ onArrayEnd: () => {
2686
+ currentParent = previousParents.pop();
2687
+ },
2688
+ onLiteralValue: onValue,
2689
+ onError: (error, offset, length) => {
2690
+ errors.push({ error, offset, length });
2691
+ }
2692
+ };
2693
+ visit(text, visitor, options);
2694
+ return currentParent[0];
2695
+ }
2696
+ function parseTree(text, errors = [], options = ParseOptions.DEFAULT) {
2697
+ let currentParent = { type: "array", offset: -1, length: -1, children: [], parent: void 0 };
2698
+ function ensurePropertyComplete(endOffset) {
2699
+ if (currentParent.type === "property") {
2700
+ currentParent.length = endOffset - currentParent.offset;
2701
+ currentParent = currentParent.parent;
2702
+ }
2703
+ }
2704
+ function onValue(valueNode) {
2705
+ currentParent.children.push(valueNode);
2706
+ return valueNode;
2707
+ }
2708
+ const visitor = {
2709
+ onObjectBegin: (offset) => {
2710
+ currentParent = onValue({ type: "object", offset, length: -1, parent: currentParent, children: [] });
2711
+ },
2712
+ onObjectProperty: (name, offset, length) => {
2713
+ currentParent = onValue({ type: "property", offset, length: -1, parent: currentParent, children: [] });
2714
+ currentParent.children.push({ type: "string", value: name, offset, length, parent: currentParent });
2715
+ },
2716
+ onObjectEnd: (offset, length) => {
2717
+ ensurePropertyComplete(offset + length);
2718
+ currentParent.length = offset + length - currentParent.offset;
2719
+ currentParent = currentParent.parent;
2720
+ ensurePropertyComplete(offset + length);
2721
+ },
2722
+ onArrayBegin: (offset, length) => {
2723
+ currentParent = onValue({ type: "array", offset, length: -1, parent: currentParent, children: [] });
2724
+ },
2725
+ onArrayEnd: (offset, length) => {
2726
+ currentParent.length = offset + length - currentParent.offset;
2727
+ currentParent = currentParent.parent;
2728
+ ensurePropertyComplete(offset + length);
2729
+ },
2730
+ onLiteralValue: (value, offset, length) => {
2731
+ onValue({ type: getNodeType(value), offset, length, parent: currentParent, value });
2732
+ ensurePropertyComplete(offset + length);
2733
+ },
2734
+ onSeparator: (sep2, offset, length) => {
2735
+ if (currentParent.type === "property") {
2736
+ if (sep2 === ":") {
2737
+ currentParent.colonOffset = offset;
2738
+ } else if (sep2 === ",") {
2739
+ ensurePropertyComplete(offset);
2740
+ }
2741
+ }
2742
+ },
2743
+ onError: (error, offset, length) => {
2744
+ errors.push({ error, offset, length });
2745
+ }
2746
+ };
2747
+ visit(text, visitor, options);
2748
+ const result = currentParent.children[0];
2749
+ if (result) {
2750
+ delete result.parent;
2751
+ }
2752
+ return result;
2753
+ }
2754
+ function findNodeAtLocation(root, path) {
2755
+ if (!root) {
2756
+ return void 0;
2757
+ }
2758
+ let node = root;
2759
+ for (let segment of path) {
2760
+ if (typeof segment === "string") {
2761
+ if (node.type !== "object" || !Array.isArray(node.children)) {
2762
+ return void 0;
2763
+ }
2764
+ let found = false;
2765
+ for (const propertyNode of node.children) {
2766
+ if (Array.isArray(propertyNode.children) && propertyNode.children[0].value === segment && propertyNode.children.length === 2) {
2767
+ node = propertyNode.children[1];
2768
+ found = true;
2769
+ break;
2770
+ }
2771
+ }
2772
+ if (!found) {
2773
+ return void 0;
2774
+ }
2775
+ } else {
2776
+ const index = segment;
2777
+ if (node.type !== "array" || index < 0 || !Array.isArray(node.children) || index >= node.children.length) {
2778
+ return void 0;
2779
+ }
2780
+ node = node.children[index];
2781
+ }
2782
+ }
2783
+ return node;
2784
+ }
2785
+ function visit(text, visitor, options = ParseOptions.DEFAULT) {
2786
+ const _scanner = createScanner(text, false);
2787
+ const _jsonPath = [];
2788
+ let suppressedCallbacks = 0;
2789
+ function toNoArgVisit(visitFunction) {
2790
+ return visitFunction ? () => suppressedCallbacks === 0 && visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
2791
+ }
2792
+ function toOneArgVisit(visitFunction) {
2793
+ return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter()) : () => true;
2794
+ }
2795
+ function toOneArgVisitWithPath(visitFunction) {
2796
+ return visitFunction ? (arg) => suppressedCallbacks === 0 && visitFunction(arg, _scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice()) : () => true;
2797
+ }
2798
+ function toBeginVisit(visitFunction) {
2799
+ return visitFunction ? () => {
2800
+ if (suppressedCallbacks > 0) {
2801
+ suppressedCallbacks++;
2802
+ } else {
2803
+ let cbReturn = visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter(), () => _jsonPath.slice());
2804
+ if (cbReturn === false) {
2805
+ suppressedCallbacks = 1;
2806
+ }
2807
+ }
2808
+ } : () => true;
2809
+ }
2810
+ function toEndVisit(visitFunction) {
2811
+ return visitFunction ? () => {
2812
+ if (suppressedCallbacks > 0) {
2813
+ suppressedCallbacks--;
2814
+ }
2815
+ if (suppressedCallbacks === 0) {
2816
+ visitFunction(_scanner.getTokenOffset(), _scanner.getTokenLength(), _scanner.getTokenStartLine(), _scanner.getTokenStartCharacter());
2817
+ }
2818
+ } : () => true;
2819
+ }
2820
+ const onObjectBegin = toBeginVisit(visitor.onObjectBegin), onObjectProperty = toOneArgVisitWithPath(visitor.onObjectProperty), onObjectEnd = toEndVisit(visitor.onObjectEnd), onArrayBegin = toBeginVisit(visitor.onArrayBegin), onArrayEnd = toEndVisit(visitor.onArrayEnd), onLiteralValue = toOneArgVisitWithPath(visitor.onLiteralValue), onSeparator = toOneArgVisit(visitor.onSeparator), onComment = toNoArgVisit(visitor.onComment), onError = toOneArgVisit(visitor.onError);
2821
+ const disallowComments = options && options.disallowComments;
2822
+ const allowTrailingComma = options && options.allowTrailingComma;
2823
+ function scanNext() {
2824
+ while (true) {
2825
+ const token = _scanner.scan();
2826
+ switch (_scanner.getTokenError()) {
2827
+ case 4:
2828
+ handleError(
2829
+ 14
2830
+ /* ParseErrorCode.InvalidUnicode */
2831
+ );
2832
+ break;
2833
+ case 5:
2834
+ handleError(
2835
+ 15
2836
+ /* ParseErrorCode.InvalidEscapeCharacter */
2837
+ );
2838
+ break;
2839
+ case 3:
2840
+ handleError(
2841
+ 13
2842
+ /* ParseErrorCode.UnexpectedEndOfNumber */
2843
+ );
2844
+ break;
2845
+ case 1:
2846
+ if (!disallowComments) {
2847
+ handleError(
2848
+ 11
2849
+ /* ParseErrorCode.UnexpectedEndOfComment */
2850
+ );
2851
+ }
2852
+ break;
2853
+ case 2:
2854
+ handleError(
2855
+ 12
2856
+ /* ParseErrorCode.UnexpectedEndOfString */
2857
+ );
2858
+ break;
2859
+ case 6:
2860
+ handleError(
2861
+ 16
2862
+ /* ParseErrorCode.InvalidCharacter */
2863
+ );
2864
+ break;
2865
+ }
2866
+ switch (token) {
2867
+ case 12:
2868
+ case 13:
2869
+ if (disallowComments) {
2870
+ handleError(
2871
+ 10
2872
+ /* ParseErrorCode.InvalidCommentToken */
2873
+ );
2874
+ } else {
2875
+ onComment();
2876
+ }
2877
+ break;
2878
+ case 16:
2879
+ handleError(
2880
+ 1
2881
+ /* ParseErrorCode.InvalidSymbol */
2882
+ );
2883
+ break;
2884
+ case 15:
2885
+ case 14:
2886
+ break;
2887
+ default:
2888
+ return token;
2889
+ }
2890
+ }
2891
+ }
2892
+ function handleError(error, skipUntilAfter = [], skipUntil = []) {
2893
+ onError(error);
2894
+ if (skipUntilAfter.length + skipUntil.length > 0) {
2895
+ let token = _scanner.getToken();
2896
+ while (token !== 17) {
2897
+ if (skipUntilAfter.indexOf(token) !== -1) {
2898
+ scanNext();
2899
+ break;
2900
+ } else if (skipUntil.indexOf(token) !== -1) {
2901
+ break;
2902
+ }
2903
+ token = scanNext();
2904
+ }
2905
+ }
2906
+ }
2907
+ function parseString(isValue) {
2908
+ const value = _scanner.getTokenValue();
2909
+ if (isValue) {
2910
+ onLiteralValue(value);
2911
+ } else {
2912
+ onObjectProperty(value);
2913
+ _jsonPath.push(value);
2914
+ }
2915
+ scanNext();
2916
+ return true;
2917
+ }
2918
+ function parseLiteral() {
2919
+ switch (_scanner.getToken()) {
2920
+ case 11:
2921
+ const tokenValue = _scanner.getTokenValue();
2922
+ let value = Number(tokenValue);
2923
+ if (isNaN(value)) {
2924
+ handleError(
2925
+ 2
2926
+ /* ParseErrorCode.InvalidNumberFormat */
2927
+ );
2928
+ value = 0;
2929
+ }
2930
+ onLiteralValue(value);
2931
+ break;
2932
+ case 7:
2933
+ onLiteralValue(null);
2934
+ break;
2935
+ case 8:
2936
+ onLiteralValue(true);
2937
+ break;
2938
+ case 9:
2939
+ onLiteralValue(false);
2940
+ break;
2941
+ default:
2942
+ return false;
2943
+ }
2944
+ scanNext();
2945
+ return true;
2946
+ }
2947
+ function parseProperty() {
2948
+ if (_scanner.getToken() !== 10) {
2949
+ handleError(3, [], [
2950
+ 2,
2951
+ 5
2952
+ /* SyntaxKind.CommaToken */
2953
+ ]);
2954
+ return false;
2955
+ }
2956
+ parseString(false);
2957
+ if (_scanner.getToken() === 6) {
2958
+ onSeparator(":");
2959
+ scanNext();
2960
+ if (!parseValue()) {
2961
+ handleError(4, [], [
2962
+ 2,
2963
+ 5
2964
+ /* SyntaxKind.CommaToken */
2965
+ ]);
2966
+ }
2967
+ } else {
2968
+ handleError(5, [], [
2969
+ 2,
2970
+ 5
2971
+ /* SyntaxKind.CommaToken */
2972
+ ]);
2973
+ }
2974
+ _jsonPath.pop();
2975
+ return true;
2976
+ }
2977
+ function parseObject() {
2978
+ onObjectBegin();
2979
+ scanNext();
2980
+ let needsComma = false;
2981
+ while (_scanner.getToken() !== 2 && _scanner.getToken() !== 17) {
2982
+ if (_scanner.getToken() === 5) {
2983
+ if (!needsComma) {
2984
+ handleError(4, [], []);
2985
+ }
2986
+ onSeparator(",");
2987
+ scanNext();
2988
+ if (_scanner.getToken() === 2 && allowTrailingComma) {
2989
+ break;
2990
+ }
2991
+ } else if (needsComma) {
2992
+ handleError(6, [], []);
2993
+ }
2994
+ if (!parseProperty()) {
2995
+ handleError(4, [], [
2996
+ 2,
2997
+ 5
2998
+ /* SyntaxKind.CommaToken */
2999
+ ]);
3000
+ }
3001
+ needsComma = true;
3002
+ }
3003
+ onObjectEnd();
3004
+ if (_scanner.getToken() !== 2) {
3005
+ handleError(7, [
3006
+ 2
3007
+ /* SyntaxKind.CloseBraceToken */
3008
+ ], []);
3009
+ } else {
3010
+ scanNext();
3011
+ }
3012
+ return true;
3013
+ }
3014
+ function parseArray() {
3015
+ onArrayBegin();
3016
+ scanNext();
3017
+ let isFirstElement = true;
3018
+ let needsComma = false;
3019
+ while (_scanner.getToken() !== 4 && _scanner.getToken() !== 17) {
3020
+ if (_scanner.getToken() === 5) {
3021
+ if (!needsComma) {
3022
+ handleError(4, [], []);
3023
+ }
3024
+ onSeparator(",");
3025
+ scanNext();
3026
+ if (_scanner.getToken() === 4 && allowTrailingComma) {
3027
+ break;
3028
+ }
3029
+ } else if (needsComma) {
3030
+ handleError(6, [], []);
3031
+ }
3032
+ if (isFirstElement) {
3033
+ _jsonPath.push(0);
3034
+ isFirstElement = false;
3035
+ } else {
3036
+ _jsonPath[_jsonPath.length - 1]++;
3037
+ }
3038
+ if (!parseValue()) {
3039
+ handleError(4, [], [
3040
+ 4,
3041
+ 5
3042
+ /* SyntaxKind.CommaToken */
3043
+ ]);
3044
+ }
3045
+ needsComma = true;
3046
+ }
3047
+ onArrayEnd();
3048
+ if (!isFirstElement) {
3049
+ _jsonPath.pop();
3050
+ }
3051
+ if (_scanner.getToken() !== 4) {
3052
+ handleError(8, [
3053
+ 4
3054
+ /* SyntaxKind.CloseBracketToken */
3055
+ ], []);
3056
+ } else {
3057
+ scanNext();
3058
+ }
3059
+ return true;
3060
+ }
3061
+ function parseValue() {
3062
+ switch (_scanner.getToken()) {
3063
+ case 3:
3064
+ return parseArray();
3065
+ case 1:
3066
+ return parseObject();
3067
+ case 10:
3068
+ return parseString(true);
3069
+ default:
3070
+ return parseLiteral();
3071
+ }
3072
+ }
3073
+ scanNext();
3074
+ if (_scanner.getToken() === 17) {
3075
+ if (options.allowEmptyContent) {
3076
+ return true;
3077
+ }
3078
+ handleError(4, [], []);
3079
+ return false;
3080
+ }
3081
+ if (!parseValue()) {
3082
+ handleError(4, [], []);
3083
+ return false;
3084
+ }
3085
+ if (_scanner.getToken() !== 17) {
3086
+ handleError(9, [], []);
3087
+ }
3088
+ return true;
3089
+ }
3090
+ function getNodeType(value) {
3091
+ switch (typeof value) {
3092
+ case "boolean":
3093
+ return "boolean";
3094
+ case "number":
3095
+ return "number";
3096
+ case "string":
3097
+ return "string";
3098
+ case "object": {
3099
+ if (!value) {
3100
+ return "null";
3101
+ } else if (Array.isArray(value)) {
3102
+ return "array";
3103
+ }
3104
+ return "object";
3105
+ }
3106
+ default:
3107
+ return "null";
3108
+ }
3109
+ }
1608
3110
 
1609
- After init succeeds, restart OpenCode and run /setup-ship-workflow.
1610
- The setup skill calls 'opencode-ship setup-complete' at the end so
1611
- the lock.manager.setupComplete flag flips to true and the ship
1612
- controller can dispatch.
1613
- `;
1614
- var MODEL_ID_RE = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
1615
- function parseFlags(argv) {
1616
- const options = {
1617
- rootPath: null,
1618
- profile: null,
1619
- json: false,
1620
- replaceManaged: false,
1621
- purgeConfig: false,
1622
- forceConfig: false,
1623
- forceRootConfig: false,
1624
- strictDoctor: false,
1625
- plannerModel: null,
1626
- builderModel: null,
1627
- finalReviewerModel: null
1628
- };
1629
- for (let i = 0; i < argv.length; i++) {
1630
- const arg = argv[i];
1631
- if (arg === "--json") options.json = true;
1632
- else if (arg === "--replace-managed") options.replaceManaged = true;
1633
- else if (arg === "--purge-config") options.purgeConfig = true;
1634
- else if (arg === "--force-config") options.forceConfig = true;
1635
- else if (arg === "--force-root-config") options.forceRootConfig = true;
1636
- else if (arg === "--strict-doctor") options.strictDoctor = true;
1637
- else if (arg === "--root") options.rootPath = argv[++i];
1638
- else if (arg === "--profile") {
1639
- const value = argv[++i];
3111
+ // node_modules/jsonc-parser/lib/esm/impl/edit.js
3112
+ function setProperty(text, originalPath, value, options) {
3113
+ const path = originalPath.slice();
3114
+ const errors = [];
3115
+ const root = parseTree(text, errors);
3116
+ let parent = void 0;
3117
+ let lastSegment = void 0;
3118
+ while (path.length > 0) {
3119
+ lastSegment = path.pop();
3120
+ parent = findNodeAtLocation(root, path);
3121
+ if (parent === void 0 && value !== void 0) {
3122
+ if (typeof lastSegment === "string") {
3123
+ value = { [lastSegment]: value };
3124
+ } else {
3125
+ value = [value];
3126
+ }
3127
+ } else {
3128
+ break;
3129
+ }
3130
+ }
3131
+ if (!parent) {
3132
+ if (value === void 0) {
3133
+ throw new Error("Can not delete in empty document");
3134
+ }
3135
+ return withFormatting(text, { offset: root ? root.offset : 0, length: root ? root.length : 0, content: JSON.stringify(value) }, options);
3136
+ } else if (parent.type === "object" && typeof lastSegment === "string" && Array.isArray(parent.children)) {
3137
+ const existing = findNodeAtLocation(parent, [lastSegment]);
3138
+ if (existing !== void 0) {
1640
3139
  if (value === void 0) {
1641
- return { error: "--profile requires a value" };
3140
+ if (!existing.parent) {
3141
+ throw new Error("Malformed AST");
3142
+ }
3143
+ const propertyIndex = parent.children.indexOf(existing.parent);
3144
+ let removeBegin;
3145
+ let removeEnd = existing.parent.offset + existing.parent.length;
3146
+ if (propertyIndex > 0) {
3147
+ let previous = parent.children[propertyIndex - 1];
3148
+ removeBegin = previous.offset + previous.length;
3149
+ } else {
3150
+ removeBegin = parent.offset + 1;
3151
+ if (parent.children.length > 1) {
3152
+ let next = parent.children[1];
3153
+ removeEnd = next.offset;
3154
+ }
3155
+ }
3156
+ return withFormatting(text, { offset: removeBegin, length: removeEnd - removeBegin, content: "" }, options);
3157
+ } else {
3158
+ return withFormatting(text, { offset: existing.offset, length: existing.length, content: JSON.stringify(value) }, options);
1642
3159
  }
1643
- if (value === "core") {
1644
- return {
1645
- error: "the 'core' profile was removed in opencode-ship 1.1.0; only 'engineering' is supported. Run /setup-ship-workflow to migrate."
1646
- };
3160
+ } else {
3161
+ if (value === void 0) {
3162
+ return [];
1647
3163
  }
1648
- if (!isValidProfile(value)) {
1649
- return { error: `unknown profile '${value}' (expected one of: ${PROFILES.join(", ")})` };
3164
+ const newProperty = `${JSON.stringify(lastSegment)}: ${JSON.stringify(value)}`;
3165
+ const index = options.getInsertionIndex ? options.getInsertionIndex(parent.children.map((p) => p.children[0].value)) : parent.children.length;
3166
+ let edit;
3167
+ if (index > 0) {
3168
+ let previous = parent.children[index - 1];
3169
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
3170
+ } else if (parent.children.length === 0) {
3171
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty };
3172
+ } else {
3173
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty + "," };
1650
3174
  }
1651
- options.profile = value;
1652
- } else if (arg === "--planner-model" || arg === "--builder-model" || arg === "--final-reviewer-model") {
1653
- const value = argv[++i];
1654
- if (value === void 0) return { error: `${arg} requires a value` };
1655
- if (!MODEL_ID_RE.test(value)) {
1656
- return { error: `${arg} must be a "<provider>/<model>" id, got ${JSON.stringify(value)}` };
3175
+ return withFormatting(text, edit, options);
3176
+ }
3177
+ } else if (parent.type === "array" && typeof lastSegment === "number" && Array.isArray(parent.children)) {
3178
+ const insertIndex = lastSegment;
3179
+ if (insertIndex === -1) {
3180
+ const newProperty = `${JSON.stringify(value)}`;
3181
+ let edit;
3182
+ if (parent.children.length === 0) {
3183
+ edit = { offset: parent.offset + 1, length: 0, content: newProperty };
3184
+ } else {
3185
+ const previous = parent.children[parent.children.length - 1];
3186
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
1657
3187
  }
1658
- if (arg === "--planner-model") options.plannerModel = value;
1659
- else if (arg === "--builder-model") options.builderModel = value;
1660
- else options.finalReviewerModel = value;
1661
- } else if (arg === "-h" || arg === "--help") return { help: true };
1662
- else if (arg === "-v" || arg === "--version") return { version: true };
1663
- else return { error: `unknown flag ${arg}` };
3188
+ return withFormatting(text, edit, options);
3189
+ } else if (value === void 0 && parent.children.length >= 0) {
3190
+ const removalIndex = lastSegment;
3191
+ const toRemove = parent.children[removalIndex];
3192
+ let edit;
3193
+ if (parent.children.length === 1) {
3194
+ edit = { offset: parent.offset + 1, length: parent.length - 2, content: "" };
3195
+ } else if (parent.children.length - 1 === removalIndex) {
3196
+ let previous = parent.children[removalIndex - 1];
3197
+ let offset = previous.offset + previous.length;
3198
+ let parentEndOffset = parent.offset + parent.length;
3199
+ edit = { offset, length: parentEndOffset - 2 - offset, content: "" };
3200
+ } else {
3201
+ edit = { offset: toRemove.offset, length: parent.children[removalIndex + 1].offset - toRemove.offset, content: "" };
3202
+ }
3203
+ return withFormatting(text, edit, options);
3204
+ } else if (value !== void 0) {
3205
+ let edit;
3206
+ const newProperty = `${JSON.stringify(value)}`;
3207
+ if (!options.isArrayInsertion && parent.children.length > lastSegment) {
3208
+ const toModify = parent.children[lastSegment];
3209
+ edit = { offset: toModify.offset, length: toModify.length, content: newProperty };
3210
+ } else if (parent.children.length === 0 || lastSegment === 0) {
3211
+ edit = { offset: parent.offset + 1, length: 0, content: parent.children.length === 0 ? newProperty : newProperty + "," };
3212
+ } else {
3213
+ const index = lastSegment > parent.children.length ? parent.children.length : lastSegment;
3214
+ const previous = parent.children[index - 1];
3215
+ edit = { offset: previous.offset + previous.length, length: 0, content: "," + newProperty };
3216
+ }
3217
+ return withFormatting(text, edit, options);
3218
+ } else {
3219
+ throw new Error(`Can not ${value === void 0 ? "remove" : options.isArrayInsertion ? "insert" : "modify"} Array index ${insertIndex} as length is not sufficient`);
3220
+ }
3221
+ } else {
3222
+ throw new Error(`Can not add ${typeof lastSegment !== "number" ? "index" : "property"} to parent of type ${parent.type}`);
1664
3223
  }
1665
- return options;
1666
3224
  }
1667
- function parseCommand(argv) {
1668
- for (const arg of argv) {
1669
- if (arg === "--help" || arg === "-h") return { command: "help" };
1670
- if (arg === "--version" || arg === "-v") return { command: "version" };
3225
+ function withFormatting(text, edit, options) {
3226
+ if (!options.formattingOptions) {
3227
+ return [edit];
1671
3228
  }
1672
- const [cmd, ...rest] = argv;
1673
- if (!cmd) return { command: "help" };
1674
- const flags = parseFlags(rest);
1675
- if ("help" in flags) return { command: "help" };
1676
- if ("version" in flags) return { command: "version" };
1677
- if ("error" in flags) return { error: flags.error };
1678
- switch (cmd) {
1679
- case "init":
1680
- case "diff":
1681
- case "update":
1682
- case "doctor":
1683
- case "setup-complete":
1684
- case "uninstall":
1685
- return { command: cmd, options: flags };
1686
- default:
1687
- return { error: `unknown command ${cmd}` };
3229
+ let newText = applyEdit(text, edit);
3230
+ let begin = edit.offset;
3231
+ let end = edit.offset + edit.content.length;
3232
+ if (edit.length === 0 || edit.content.length === 0) {
3233
+ while (begin > 0 && !isEOL(newText, begin - 1)) {
3234
+ begin--;
3235
+ }
3236
+ while (end < newText.length && !isEOL(newText, end)) {
3237
+ end++;
3238
+ }
1688
3239
  }
3240
+ const edits = format(newText, { offset: begin, length: end - begin }, { ...options.formattingOptions, keepLines: false });
3241
+ for (let i = edits.length - 1; i >= 0; i--) {
3242
+ const edit2 = edits[i];
3243
+ newText = applyEdit(newText, edit2);
3244
+ begin = Math.min(begin, edit2.offset);
3245
+ end = Math.max(end, edit2.offset + edit2.length);
3246
+ end += edit2.content.length - edit2.length;
3247
+ }
3248
+ const editLength = text.length - (newText.length - end) - begin;
3249
+ return [{ offset: begin, length: editLength, content: newText.substring(begin, end) }];
1689
3250
  }
1690
- function helpText() {
1691
- return USAGE;
3251
+ function applyEdit(text, edit) {
3252
+ return text.substring(0, edit.offset) + edit.content + text.substring(edit.offset + edit.length);
1692
3253
  }
1693
3254
 
1694
- // src/installer/commands/init.js
1695
- import { promisify as promisify2 } from "node:util";
1696
- import { writeFile as writeFile8, mkdir as mkdirAsync2 } from "node:fs/promises";
1697
- import { dirname as dirname11, resolve as resolvePath } from "node:path";
1698
-
1699
- // src/installer/executor.js
1700
- init_catalog();
1701
- init_version();
1702
- import { existsSync as existsSync13 } from "node:fs";
1703
- import { mkdir as mkdir6, readFile as readFile9, rename as rename5, unlink as unlink3, writeFile as writeFile6 } from "node:fs/promises";
1704
- import { dirname as dirname8, resolve as resolve12 } from "node:path";
3255
+ // node_modules/jsonc-parser/lib/esm/main.js
3256
+ var ScanError;
3257
+ (function(ScanError2) {
3258
+ ScanError2[ScanError2["None"] = 0] = "None";
3259
+ ScanError2[ScanError2["UnexpectedEndOfComment"] = 1] = "UnexpectedEndOfComment";
3260
+ ScanError2[ScanError2["UnexpectedEndOfString"] = 2] = "UnexpectedEndOfString";
3261
+ ScanError2[ScanError2["UnexpectedEndOfNumber"] = 3] = "UnexpectedEndOfNumber";
3262
+ ScanError2[ScanError2["InvalidUnicode"] = 4] = "InvalidUnicode";
3263
+ ScanError2[ScanError2["InvalidEscapeCharacter"] = 5] = "InvalidEscapeCharacter";
3264
+ ScanError2[ScanError2["InvalidCharacter"] = 6] = "InvalidCharacter";
3265
+ })(ScanError || (ScanError = {}));
3266
+ var SyntaxKind;
3267
+ (function(SyntaxKind2) {
3268
+ SyntaxKind2[SyntaxKind2["OpenBraceToken"] = 1] = "OpenBraceToken";
3269
+ SyntaxKind2[SyntaxKind2["CloseBraceToken"] = 2] = "CloseBraceToken";
3270
+ SyntaxKind2[SyntaxKind2["OpenBracketToken"] = 3] = "OpenBracketToken";
3271
+ SyntaxKind2[SyntaxKind2["CloseBracketToken"] = 4] = "CloseBracketToken";
3272
+ SyntaxKind2[SyntaxKind2["CommaToken"] = 5] = "CommaToken";
3273
+ SyntaxKind2[SyntaxKind2["ColonToken"] = 6] = "ColonToken";
3274
+ SyntaxKind2[SyntaxKind2["NullKeyword"] = 7] = "NullKeyword";
3275
+ SyntaxKind2[SyntaxKind2["TrueKeyword"] = 8] = "TrueKeyword";
3276
+ SyntaxKind2[SyntaxKind2["FalseKeyword"] = 9] = "FalseKeyword";
3277
+ SyntaxKind2[SyntaxKind2["StringLiteral"] = 10] = "StringLiteral";
3278
+ SyntaxKind2[SyntaxKind2["NumericLiteral"] = 11] = "NumericLiteral";
3279
+ SyntaxKind2[SyntaxKind2["LineCommentTrivia"] = 12] = "LineCommentTrivia";
3280
+ SyntaxKind2[SyntaxKind2["BlockCommentTrivia"] = 13] = "BlockCommentTrivia";
3281
+ SyntaxKind2[SyntaxKind2["LineBreakTrivia"] = 14] = "LineBreakTrivia";
3282
+ SyntaxKind2[SyntaxKind2["Trivia"] = 15] = "Trivia";
3283
+ SyntaxKind2[SyntaxKind2["Unknown"] = 16] = "Unknown";
3284
+ SyntaxKind2[SyntaxKind2["EOF"] = 17] = "EOF";
3285
+ })(SyntaxKind || (SyntaxKind = {}));
3286
+ var parse2 = parse;
3287
+ var ParseErrorCode;
3288
+ (function(ParseErrorCode2) {
3289
+ ParseErrorCode2[ParseErrorCode2["InvalidSymbol"] = 1] = "InvalidSymbol";
3290
+ ParseErrorCode2[ParseErrorCode2["InvalidNumberFormat"] = 2] = "InvalidNumberFormat";
3291
+ ParseErrorCode2[ParseErrorCode2["PropertyNameExpected"] = 3] = "PropertyNameExpected";
3292
+ ParseErrorCode2[ParseErrorCode2["ValueExpected"] = 4] = "ValueExpected";
3293
+ ParseErrorCode2[ParseErrorCode2["ColonExpected"] = 5] = "ColonExpected";
3294
+ ParseErrorCode2[ParseErrorCode2["CommaExpected"] = 6] = "CommaExpected";
3295
+ ParseErrorCode2[ParseErrorCode2["CloseBraceExpected"] = 7] = "CloseBraceExpected";
3296
+ ParseErrorCode2[ParseErrorCode2["CloseBracketExpected"] = 8] = "CloseBracketExpected";
3297
+ ParseErrorCode2[ParseErrorCode2["EndOfFileExpected"] = 9] = "EndOfFileExpected";
3298
+ ParseErrorCode2[ParseErrorCode2["InvalidCommentToken"] = 10] = "InvalidCommentToken";
3299
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfComment"] = 11] = "UnexpectedEndOfComment";
3300
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfString"] = 12] = "UnexpectedEndOfString";
3301
+ ParseErrorCode2[ParseErrorCode2["UnexpectedEndOfNumber"] = 13] = "UnexpectedEndOfNumber";
3302
+ ParseErrorCode2[ParseErrorCode2["InvalidUnicode"] = 14] = "InvalidUnicode";
3303
+ ParseErrorCode2[ParseErrorCode2["InvalidEscapeCharacter"] = 15] = "InvalidEscapeCharacter";
3304
+ ParseErrorCode2[ParseErrorCode2["InvalidCharacter"] = 16] = "InvalidCharacter";
3305
+ })(ParseErrorCode || (ParseErrorCode = {}));
3306
+ function modify(text, path, value, options) {
3307
+ return setProperty(text, path, value, options);
3308
+ }
3309
+ function applyEdits(text, edits) {
3310
+ let sortedEdits = edits.slice(0).sort((a, b) => {
3311
+ const diff = a.offset - b.offset;
3312
+ if (diff === 0) {
3313
+ return a.length - b.length;
3314
+ }
3315
+ return diff;
3316
+ });
3317
+ let lastModifiedOffset = text.length;
3318
+ for (let i = sortedEdits.length - 1; i >= 0; i--) {
3319
+ let e = sortedEdits[i];
3320
+ if (e.offset + e.length <= lastModifiedOffset) {
3321
+ text = applyEdit(text, e);
3322
+ } else {
3323
+ throw new Error("Overlapping edit");
3324
+ }
3325
+ lastModifiedOffset = e.offset;
3326
+ }
3327
+ return text;
3328
+ }
1705
3329
 
1706
- // src/installer/planner.js
1707
- init_catalog();
1708
- init_hash();
1709
- init_config();
1710
- init_json_pointer();
1711
- init_root_config();
1712
- import { existsSync as existsSync7 } from "node:fs";
1713
- import { readFile as readFile3, stat } from "node:fs/promises";
3330
+ // src/installer/jsonc-edit.js
3331
+ var JsoncEditError = class extends Error {
3332
+ constructor(message, path) {
3333
+ super(`${message} (path=${path ?? "<root>"})`);
3334
+ this.name = "JsoncEditError";
3335
+ this.path = path ?? null;
3336
+ }
3337
+ };
3338
+ function pointerToPath(pointer) {
3339
+ if (typeof pointer !== "string" || pointer.length === 0) return [];
3340
+ return pointer.split("/").slice(1).map(decodePointerToken);
3341
+ }
3342
+ function decodePointerToken(token) {
3343
+ return token.replace(/~1/g, "/").replace(/~0/g, "~");
3344
+ }
3345
+ function applyJsoncEdits(source, edits) {
3346
+ if (typeof source !== "string") {
3347
+ throw new JsoncEditError("source must be a string", null);
3348
+ }
3349
+ if (!Array.isArray(edits)) {
3350
+ throw new JsoncEditError("edits must be an array", null);
3351
+ }
3352
+ if (edits.length === 0) {
3353
+ return Buffer.from(source, "utf8");
3354
+ }
3355
+ const sorted = [...edits].sort((a, b) => {
3356
+ const da = a.pointer.split("/").length;
3357
+ const db = b.pointer.split("/").length;
3358
+ return da - db;
3359
+ });
3360
+ let working = source;
3361
+ for (const edit of sorted) {
3362
+ if (!edit || typeof edit.pointer !== "string" || !edit.pointer.startsWith("/")) {
3363
+ throw new JsoncEditError(`invalid pointer ${JSON.stringify(edit?.pointer)}`, edit?.pointer ?? null);
3364
+ }
3365
+ if (edit.op !== "set" && edit.op !== "delete") {
3366
+ throw new JsoncEditError(`invalid op ${edit.op}`, edit.pointer);
3367
+ }
3368
+ const path = pointerToPath(edit.pointer);
3369
+ const value = edit.op === "delete" ? void 0 : edit.value;
3370
+ let editOps;
3371
+ try {
3372
+ editOps = modify(working, path, value, {});
3373
+ } catch (err) {
3374
+ throw new JsoncEditError(`modify failed at ${edit.pointer}: ${err?.message ?? err}`, edit.pointer);
3375
+ }
3376
+ working = applyEdits(working, editOps);
3377
+ }
3378
+ const errors = [];
3379
+ parse2(working, errors, { allowTrailingComma: true });
3380
+ if (errors.length > 0) {
3381
+ throw new JsoncEditError(`result is not valid JSON: ${errors[0]?.error ?? errors[0]}`, null);
3382
+ }
3383
+ return Buffer.from(working, "utf8");
3384
+ }
3385
+ function diffPointers(before, after, pointers) {
3386
+ const edits = [];
3387
+ for (const pointer of pointers) {
3388
+ const prev = readPointer(before, pointer);
3389
+ const next = readPointer(after, pointer);
3390
+ if (next === void 0) {
3391
+ edits.push({ pointer, op: (
3392
+ /** @type {"delete"} */
3393
+ "delete"
3394
+ ) });
3395
+ } else if (prev === void 0 || !stableJsonEqual(prev, next)) {
3396
+ edits.push({ pointer, value: next, op: (
3397
+ /** @type {"set"} */
3398
+ "set"
3399
+ ) });
3400
+ }
3401
+ }
3402
+ return edits;
3403
+ }
3404
+ function readPointer(doc, pointer) {
3405
+ if (!doc || typeof doc !== "object") return void 0;
3406
+ const tokens = pointer.split("/").slice(1).map(decodePointerToken);
3407
+ let cursor = doc;
3408
+ for (const tok of tokens) {
3409
+ if (cursor === null || cursor === void 0) return void 0;
3410
+ cursor = cursor[tok];
3411
+ }
3412
+ return cursor === void 0 ? void 0 : cursor;
3413
+ }
3414
+ function stableJsonEqual(a, b) {
3415
+ return canonicalize(a) === canonicalize(b);
3416
+ }
3417
+ function canonicalize(value) {
3418
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
3419
+ if (Array.isArray(value)) return `[${value.map(canonicalize).join(",")}]`;
3420
+ const out = {};
3421
+ const keys = Object.keys(value).sort();
3422
+ for (const k of keys) out[k] = canonicalize(value[k]);
3423
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${out[k]}`).join(",")}}`;
3424
+ }
1714
3425
 
1715
3426
  // src/installer/root-reconciliation.js
1716
- init_root_config();
1717
- init_json_pointer();
1718
- init_hash();
1719
- init_plan_mode_permissions();
1720
- import { existsSync as existsSync6 } from "node:fs";
1721
- import { join } from "node:path";
1722
3427
  function desiredPointersForProfile(profile) {
1723
- return POINTER_ENTRIES.map((entry) => ({
1724
- pointer: entry.pointer,
1725
- strategy: (
1726
- /** @type {"value" | "object-entry" | "array-member"} */
1727
- entry.strategy
1728
- ),
1729
- scope: (
1730
- /** @type {Profile} */
1731
- "engineering"
1732
- ),
1733
- value: entry.value
1734
- }));
3428
+ return matrixLeafPointers();
1735
3429
  }
1736
3430
  async function planRootReconciliation(input) {
1737
3431
  const profile = input.profile;
@@ -1754,7 +3448,7 @@ async function planRootReconciliation(input) {
1754
3448
  previousDocument: input.previousDocument
1755
3449
  });
1756
3450
  }
1757
- const fileMissing = !existsSync6(target);
3451
+ const fileMissing = !existsSync7(target);
1758
3452
  if (fileMissing && !input.forceRepair) {
1759
3453
  if (mode === "profile-transition") {
1760
3454
  return {
@@ -1780,6 +3474,9 @@ async function planRootReconciliation(input) {
1780
3474
  let doc;
1781
3475
  if (fileMissing && input.forceRepair) {
1782
3476
  doc = synthesizeDefaultRootConfig();
3477
+ for (const descriptor of descriptors) {
3478
+ doc = setPointer(doc, descriptor.pointer, descriptor.value);
3479
+ }
1783
3480
  const bytes = Buffer.from(formatRootConfig(doc), "utf8");
1784
3481
  return {
1785
3482
  kind: "create",
@@ -1842,7 +3539,22 @@ function reconcileRecordsAfterTransition(descriptors, previousRecords) {
1842
3539
  return out;
1843
3540
  }
1844
3541
  function planInstallRoot({ doc, target, relPath, descriptors, previousRecords }) {
1845
- const result = applyOwnedPointers(doc.value, {
3542
+ const promotion = promotePlanEditIfString(doc.value);
3543
+ if (promotion.record && promotion.record.conflict) {
3544
+ return {
3545
+ kind: "conflict",
3546
+ op: "root-config",
3547
+ target,
3548
+ relPath,
3549
+ reason: `consumer-owned Plan edit glob is set to deny: ${promotion.record.pointer}`,
3550
+ edits: [{ kind: "conflict", pointer: promotion.record.pointer, reason: "consumer-denied" }],
3551
+ pointerRecords: previousRecords,
3552
+ format: doc.format,
3553
+ document: doc.value
3554
+ };
3555
+ }
3556
+ const sourceDoc = promotion.doc;
3557
+ const result = applyOwnedPointers(sourceDoc, {
1846
3558
  pointerEntries: descriptors.map((d) => ({ pointer: d.pointer, strategy: d.strategy, value: d.value })),
1847
3559
  allowEqualValues: true
1848
3560
  });
@@ -1852,21 +3564,65 @@ function planInstallRoot({ doc, target, relPath, descriptors, previousRecords })
1852
3564
  if (s.reason === "already equal") continue;
1853
3565
  edits.push({ kind: "conflict", pointer: s.pointer, reason: s.reason, existing: s.existing, desired: s.desired });
1854
3566
  }
3567
+ const reorderPointers = equalExceptionsBeforeNewWildcard(sourceDoc, descriptors);
1855
3568
  let docForWrite = result.doc;
3569
+ for (const descriptor of reorderPointers) {
3570
+ docForWrite = setPointer(removePointer(docForWrite, descriptor.pointer), descriptor.pointer, descriptor.value);
3571
+ }
1856
3572
  let bytes;
1857
3573
  try {
1858
- const { value: sourceValue } = parseRootConfigPreservingOrder(doc.raw ?? "");
1859
- if (sourceValue && typeof sourceValue === "object") {
1860
- docForWrite = sourceValue;
1861
- for (const a of result.applied) {
1862
- docForWrite = setPointer(docForWrite, a.pointer, a.value);
3574
+ const isJsonc = doc.format === "jsonc";
3575
+ if (isJsonc) {
3576
+ const pointerEdits = diffPointers(
3577
+ doc.value,
3578
+ docForWrite,
3579
+ result.applied.map((a) => a.pointer)
3580
+ );
3581
+ for (const descriptor of reorderPointers) {
3582
+ pointerEdits.push({ pointer: descriptor.pointer, op: "delete" });
3583
+ pointerEdits.push({ pointer: descriptor.pointer, value: descriptor.value, op: "set" });
3584
+ }
3585
+ if (promotion.record) {
3586
+ pointerEdits.push({ pointer: promotion.record.pointer, op: (
3587
+ /** @type {"delete"} */
3588
+ "delete"
3589
+ ) });
3590
+ pointerEdits.push({ pointer: promotion.record.pointer, value: promotion.record.previous.value, op: (
3591
+ /** @type {"set"} */
3592
+ "set"
3593
+ ) });
3594
+ }
3595
+ bytes = applyJsoncEdits(doc.raw ?? "", pointerEdits);
3596
+ } else {
3597
+ const { value: sourceValue } = parseRootConfigPreservingOrder(doc.raw ?? "");
3598
+ if (sourceValue && typeof sourceValue === "object") {
3599
+ docForWrite = sourceValue;
3600
+ if (promotion.record) {
3601
+ docForWrite = setPointer(docForWrite, promotion.record.pointer, { "*": promotion.record.previous.value });
3602
+ }
3603
+ for (const a of result.applied) {
3604
+ docForWrite = setPointer(docForWrite, a.pointer, a.value);
3605
+ }
3606
+ for (const descriptor of reorderPointers) {
3607
+ docForWrite = setPointer(removePointer(docForWrite, descriptor.pointer), descriptor.pointer, descriptor.value);
3608
+ }
1863
3609
  }
3610
+ bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
1864
3611
  }
1865
- bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
1866
3612
  } catch {
1867
3613
  bytes = Buffer.from(formatRootConfig(result.doc), "utf8");
1868
3614
  }
1869
3615
  const records = mergePointerRecords(descriptors, previousRecords, result, doc.before);
3616
+ if (promotion.record) {
3617
+ records.push({
3618
+ pointer: promotion.record.pointer,
3619
+ strategy: "value",
3620
+ scope: "engineering",
3621
+ installedSha256: null,
3622
+ previous: promotion.record.previous,
3623
+ promotion: true
3624
+ });
3625
+ }
1870
3626
  return {
1871
3627
  kind: edits.some((e) => e.kind === "conflict") ? "conflict" : edits.length ? "update" : "noop",
1872
3628
  op: "root-config",
@@ -1882,8 +3638,36 @@ function planInstallRoot({ doc, target, relPath, descriptors, previousRecords })
1882
3638
  reason: edits.length === 0 ? "no installer-owned entries missing" : `apply ${result.applied.length} / skip ${result.skipped.length}`
1883
3639
  };
1884
3640
  }
3641
+ function equalExceptionsBeforeNewWildcard(doc, descriptors) {
3642
+ const wildcardParents = /* @__PURE__ */ new Set();
3643
+ for (const descriptor of descriptors) {
3644
+ if (!descriptor.pointer.endsWith("/*") || getPointer(doc, descriptor.pointer) !== void 0) continue;
3645
+ wildcardParents.add(descriptor.pointer.slice(0, descriptor.pointer.lastIndexOf("/")));
3646
+ }
3647
+ return descriptors.filter((descriptor) => {
3648
+ if (descriptor.pointer.endsWith("/*")) return false;
3649
+ const parent = descriptor.pointer.slice(0, descriptor.pointer.lastIndexOf("/"));
3650
+ if (!wildcardParents.has(parent)) return false;
3651
+ const existing = getPointer(doc, descriptor.pointer);
3652
+ return existing !== void 0 && stableStringify(existing) === stableStringify(descriptor.value);
3653
+ });
3654
+ }
1885
3655
  function planProfileTransitionRoot({ doc, target, relPath, descriptors, previousRecords }) {
1886
3656
  const desired = descriptors;
3657
+ const promotion = promotePlanEditIfString(doc.value);
3658
+ if (promotion.record && promotion.record.conflict) {
3659
+ return {
3660
+ kind: "conflict",
3661
+ op: "root-config",
3662
+ target,
3663
+ relPath,
3664
+ reason: `consumer-owned Plan edit glob is set to deny: ${promotion.record.pointer}`,
3665
+ edits: [{ kind: "conflict", pointer: promotion.record.pointer, reason: "consumer-denied" }],
3666
+ pointerRecords: previousRecords,
3667
+ format: doc.format,
3668
+ document: doc.value
3669
+ };
3670
+ }
1887
3671
  const drift = [];
1888
3672
  for (const rec of previousRecords) {
1889
3673
  const stillDesired = desired.some((d) => d.pointer === rec.pointer && d.scope === rec.scope);
@@ -1909,7 +3693,7 @@ function planProfileTransitionRoot({ doc, target, relPath, descriptors, previous
1909
3693
  pointerRecords: previousRecords
1910
3694
  };
1911
3695
  }
1912
- let next = JSON.parse(JSON.stringify(doc.value));
3696
+ let next = JSON.parse(JSON.stringify(promotion.doc));
1913
3697
  const edits = [];
1914
3698
  const mergedRecords = previousRecords.map((r) => ({ ...r }));
1915
3699
  const recordIndex = new Map(mergedRecords.map((r, idx) => [r.pointer, idx]));
@@ -1983,22 +3767,68 @@ function planProfileTransitionRoot({ doc, target, relPath, descriptors, previous
1983
3767
  let docForWrite = next;
1984
3768
  let bytes;
1985
3769
  try {
1986
- const { value: sourceValue } = parseRootConfigPreservingOrder(doc.raw ?? "");
1987
- if (sourceValue && typeof sourceValue === "object") {
1988
- docForWrite = sourceValue;
3770
+ const isJsonc = doc.format === "jsonc";
3771
+ if (isJsonc) {
3772
+ const pointerEdits = [];
1989
3773
  for (const e of edits) {
1990
3774
  if (e.kind === "create" || e.kind === "restore") {
1991
- docForWrite = setPointer(docForWrite, e.pointer, e.value);
3775
+ pointerEdits.push({ pointer: e.pointer, value: e.value, op: (
3776
+ /** @type {"set"} */
3777
+ "set"
3778
+ ) });
1992
3779
  } else if (e.kind === "remove") {
1993
- docForWrite = removePointer(docForWrite, e.pointer);
3780
+ pointerEdits.push({ pointer: e.pointer, op: (
3781
+ /** @type {"delete"} */
3782
+ "delete"
3783
+ ) });
3784
+ }
3785
+ }
3786
+ if (promotion.record) {
3787
+ pointerEdits.push({ pointer: promotion.record.pointer, op: (
3788
+ /** @type {"delete"} */
3789
+ "delete"
3790
+ ) });
3791
+ pointerEdits.push({ pointer: promotion.record.pointer, value: promotion.record.previous.value, op: (
3792
+ /** @type {"set"} */
3793
+ "set"
3794
+ ) });
3795
+ }
3796
+ bytes = applyJsoncEdits(doc.raw ?? "", pointerEdits);
3797
+ const reparsed = parse2(bytes.toString("utf8"), void 0, { allowTrailingComma: true });
3798
+ docForWrite = collapseEmptyAncestors(reparsed) ?? reparsed;
3799
+ } else {
3800
+ const { value: sourceValue } = parseRootConfigPreservingOrder(doc.raw ?? "");
3801
+ if (sourceValue && typeof sourceValue === "object") {
3802
+ docForWrite = sourceValue;
3803
+ if (promotion.record) {
3804
+ docForWrite = setPointer(docForWrite, promotion.record.pointer, { "*": promotion.record.previous.value });
3805
+ }
3806
+ for (const e of edits) {
3807
+ if (e.kind === "create" || e.kind === "restore") {
3808
+ docForWrite = setPointer(docForWrite, e.pointer, e.value);
3809
+ } else if (e.kind === "remove") {
3810
+ docForWrite = removePointer(docForWrite, e.pointer);
3811
+ }
1994
3812
  }
1995
3813
  }
3814
+ docForWrite = collapseEmptyAncestors(docForWrite) ?? docForWrite;
3815
+ bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
1996
3816
  }
1997
- docForWrite = collapseEmptyAncestors(docForWrite) ?? docForWrite;
1998
- bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
1999
3817
  } catch {
2000
3818
  bytes = Buffer.from(formatRootConfig(collapseEmptyAncestors(next) ?? next), "utf8");
2001
3819
  }
3820
+ if (promotion.record) {
3821
+ if (!recordIndex.has(promotion.record.pointer)) {
3822
+ mergedRecords.push({
3823
+ pointer: promotion.record.pointer,
3824
+ strategy: "value",
3825
+ scope: "engineering",
3826
+ installedSha256: null,
3827
+ previous: promotion.record.previous,
3828
+ promotion: true
3829
+ });
3830
+ }
3831
+ }
2002
3832
  return {
2003
3833
  kind: edits.some((e) => e.kind === "conflict") ? "conflict" : edits.some((e) => e.kind === "create" || e.kind === "remove" || e.kind === "restore") ? "update" : "noop",
2004
3834
  op: "root-config",
@@ -2061,7 +3891,10 @@ function planUninstallRoot({ target, relPath, previousRecords, previousDocument
2061
3891
  }
2062
3892
  let doc = docResult.value;
2063
3893
  const edits = [];
2064
- for (const rec of previousRecords) {
3894
+ const sortedRecords = [...previousRecords].sort((a, b) => {
3895
+ return b.pointer.split("/").length - a.pointer.split("/").length;
3896
+ });
3897
+ for (const rec of sortedRecords) {
2065
3898
  if (rec.previous && rec.previous.existed) {
2066
3899
  doc = setPointer(doc, rec.pointer, rec.previous.value);
2067
3900
  edits.push({ kind: "restore", pointer: rec.pointer, value: rec.previous.value });
@@ -2076,19 +3909,56 @@ function planUninstallRoot({ target, relPath, previousRecords, previousDocument
2076
3909
  let docForWrite = doc;
2077
3910
  let bytes;
2078
3911
  try {
2079
- const { value: sourceValue } = parseRootConfigPreservingOrder(docResult.raw ?? "");
2080
- if (sourceValue && typeof sourceValue === "object") {
2081
- docForWrite = sourceValue;
3912
+ const isJsonc = docResult.format === "jsonc";
3913
+ if (isJsonc) {
3914
+ const collapsed = collapseEmptyAncestors(doc) ?? doc;
3915
+ const prunePointers = /* @__PURE__ */ new Set();
3916
+ for (const e of edits) {
3917
+ if (e.kind !== "remove") continue;
3918
+ const tokens = e.pointer.split("/").slice(1);
3919
+ for (let i = tokens.length - 1; i > 0; i--) {
3920
+ const ancestor = `/${tokens.slice(0, i).join("/")}`;
3921
+ if (getPointer(collapsed, ancestor) === void 0) prunePointers.add(ancestor);
3922
+ }
3923
+ }
3924
+ const topPrunes = [...prunePointers].sort((a, b) => a.length - b.length).filter((pointer, index, all) => !all.slice(0, index).some((parent) => pointer.startsWith(`${parent}/`)));
3925
+ const pointerEdits = [];
2082
3926
  for (const e of edits) {
2083
3927
  if (e.kind === "restore") {
2084
- docForWrite = setPointer(docForWrite, e.pointer, e.value);
2085
- } else if (e.kind === "remove") {
2086
- docForWrite = removePointer(docForWrite, e.pointer);
3928
+ pointerEdits.push({ pointer: e.pointer, value: e.value, op: (
3929
+ /** @type {"set"} */
3930
+ "set"
3931
+ ) });
3932
+ } else if (e.kind === "remove" && !topPrunes.some((parent) => e.pointer === parent || e.pointer.startsWith(`${parent}/`))) {
3933
+ pointerEdits.push({ pointer: e.pointer, op: (
3934
+ /** @type {"delete"} */
3935
+ "delete"
3936
+ ) });
3937
+ }
3938
+ }
3939
+ for (const pointer of topPrunes) {
3940
+ pointerEdits.push({ pointer, op: (
3941
+ /** @type {"delete"} */
3942
+ "delete"
3943
+ ) });
3944
+ }
3945
+ bytes = applyJsoncEdits(docResult.raw ?? "", pointerEdits);
3946
+ docForWrite = parse2(bytes.toString("utf8"), void 0, { allowTrailingComma: true });
3947
+ } else {
3948
+ const { value: sourceValue } = parseRootConfigPreservingOrder(docResult.raw ?? "");
3949
+ if (sourceValue && typeof sourceValue === "object") {
3950
+ docForWrite = sourceValue;
3951
+ for (const e of edits) {
3952
+ if (e.kind === "restore") {
3953
+ docForWrite = setPointer(docForWrite, e.pointer, e.value);
3954
+ } else if (e.kind === "remove") {
3955
+ docForWrite = removePointer(docForWrite, e.pointer);
3956
+ }
2087
3957
  }
2088
3958
  }
3959
+ docForWrite = collapseEmptyAncestors(docForWrite) ?? docForWrite;
3960
+ bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
2089
3961
  }
2090
- docForWrite = collapseEmptyAncestors(docForWrite);
2091
- bytes = Buffer.from(formatRootConfigPreserving(docForWrite), "utf8");
2092
3962
  } catch {
2093
3963
  bytes = Buffer.from(formatRootConfig(collapseEmptyAncestors(doc)), "utf8");
2094
3964
  }
@@ -2112,7 +3982,9 @@ function collapseEmptyAncestors(doc) {
2112
3982
  const out = {};
2113
3983
  for (const k of Object.keys(doc)) {
2114
3984
  if (k === "__sourceOrder__") continue;
2115
- out[k] = collapseEmptyAncestors(doc[k]);
3985
+ const collapsed = collapseEmptyAncestors(doc[k]);
3986
+ if (collapsed === void 0) continue;
3987
+ out[k] = collapsed;
2116
3988
  }
2117
3989
  if (Array.isArray(doc.__sourceOrder__)) {
2118
3990
  out.__sourceOrder__ = doc.__sourceOrder__.filter((k) => k in out);
@@ -2165,6 +4037,7 @@ function mergePointerRecords(descriptors, previousRecords, result, beforeSnapsho
2165
4037
  };
2166
4038
  } else {
2167
4039
  out.push(next);
4040
+ index.set(a.pointer, out.length - 1);
2168
4041
  }
2169
4042
  }
2170
4043
  for (const s of result.skipped) {
@@ -2179,20 +4052,21 @@ function mergePointerRecords(descriptors, previousRecords, result, beforeSnapsho
2179
4052
  installedSha256: bytesHashString(stableStringify(existing ?? null)),
2180
4053
  previous
2181
4054
  });
4055
+ index.set(s.pointer, out.length - 1);
2182
4056
  }
2183
4057
  return out;
2184
4058
  }
2185
4059
 
2186
4060
  // src/installer/planner.js
2187
4061
  async function readBytes(path) {
2188
- if (!existsSync7(path)) return null;
2189
- const buf = await readFile3(path);
4062
+ if (!existsSync8(path)) return null;
4063
+ const buf = await readFile4(path);
2190
4064
  const fileStat = await stat(path);
2191
4065
  return { bytes: buf, hash: bytesHashString(buf.toString("utf8")), mode: fileStat.mode & 511 };
2192
4066
  }
2193
4067
  async function readDesiredBytes(source) {
2194
- if (!source || !existsSync7(source)) return null;
2195
- const buf = await readFile3(source);
4068
+ if (!source || !existsSync8(source)) return null;
4069
+ const buf = await readFile4(source);
2196
4070
  return { bytes: buf, hash: bytesHashString(buf.toString("utf8")) };
2197
4071
  }
2198
4072
  function lookupLockedFile(lock, targetPath) {
@@ -2332,6 +4206,16 @@ async function planUninstall({ repoRoot, lock }) {
2332
4206
  const plan = [];
2333
4207
  const activeProfile = lock?.manager?.profile ?? "core";
2334
4208
  for (const entry of lock.files ?? []) {
4209
+ if (!isSafeManagedPath(entry?.path)) {
4210
+ plan.push({
4211
+ kind: "conflict",
4212
+ op: "file",
4213
+ target: repoRoot,
4214
+ relPath: entry?.path ?? null,
4215
+ reason: "managed file path is unsafe; refusing filesystem access"
4216
+ });
4217
+ continue;
4218
+ }
2335
4219
  const targetPath = `${repoRoot}/${entry.path}`;
2336
4220
  const current = await readBytes(targetPath);
2337
4221
  if (!current) continue;
@@ -3326,7 +5210,7 @@ async function assembleLock({ repoRoot, plan, lock, configPlan, rootPlan, profil
3326
5210
  manager: {
3327
5211
  schemaVersion: CURRENT_LOCK_SCHEMA,
3328
5212
  name: "opencode-ship",
3329
- version: "1.1.2-rc.2",
5213
+ version: "1.1.2",
3330
5214
  templateSet: TEMPLATE_SET_ID,
3331
5215
  profile: resolvedProfile,
3332
5216
  appliedAt: (/* @__PURE__ */ new Date()).toISOString(),
@@ -3345,7 +5229,7 @@ async function assembleLock({ repoRoot, plan, lock, configPlan, rootPlan, profil
3345
5229
  files
3346
5230
  };
3347
5231
  }
3348
- async function commitInstall(preview, { json, command, fullSetupComplete = false }) {
5232
+ async function commitInstall(preview, { json, command, fullSetupComplete = false, transactionEntries = [] }) {
3349
5233
  if (!preview.ok) {
3350
5234
  return {
3351
5235
  ok: false,
@@ -3405,6 +5289,7 @@ async function commitInstall(preview, { json, command, fullSetupComplete = false
3405
5289
  relPath: rootPlan.relPath
3406
5290
  });
3407
5291
  }
5292
+ txPlan.push(...transactionEntries);
3408
5293
  const tx = await executePlan({
3409
5294
  repoRoot,
3410
5295
  plan: txPlan,
@@ -3474,12 +5359,12 @@ function relativeTemplate(source) {
3474
5359
  // src/installer/commands/doctor.js
3475
5360
  init_lock();
3476
5361
  init_config();
3477
- import { existsSync as existsSync16, readFileSync as readFileSync6 } from "node:fs";
5362
+ import { existsSync as existsSync15, readFileSync as readFileSync5 } from "node:fs";
3478
5363
  import { spawnSync as spawnSync2 } from "node:child_process";
3479
5364
  init_hash();
3480
5365
  init_catalog();
3481
5366
  init_root_config();
3482
- import { resolve as resolve15 } from "node:path";
5367
+ import { resolve as resolve14 } from "node:path";
3483
5368
 
3484
5369
  // src/installer/report.js
3485
5370
  var REPORT_VERSION = 1;
@@ -3568,9 +5453,9 @@ function checkPackageIntegrity() {
3568
5453
  function buildSourceHashIndex() {
3569
5454
  const idx = /* @__PURE__ */ new Map();
3570
5455
  for (const entry of CATALOG) {
3571
- if (!existsSync16(entry.source)) continue;
5456
+ if (!existsSync15(entry.source)) continue;
3572
5457
  try {
3573
- const buf = readFileSync6(entry.source, "utf8");
5458
+ const buf = readFileSync5(entry.source, "utf8");
3574
5459
  idx.set(entry.source, bytesHashString(buf));
3575
5460
  } catch {
3576
5461
  }
@@ -3581,13 +5466,13 @@ async function checkCatalogInstall(repoRoot, sourceHashes, profile, renderedAgen
3581
5466
  const rows = [];
3582
5467
  const scoped = profile ? filterCatalogByProfile(CATALOG, profile) : CATALOG;
3583
5468
  for (const entry of scoped) {
3584
- const target = resolve15(repoRoot, entry.path);
3585
- if (!existsSync16(target)) {
5469
+ const target = resolve14(repoRoot, entry.path);
5470
+ if (!existsSync15(target)) {
3586
5471
  rows.push(`${entry.id}: missing`);
3587
5472
  continue;
3588
5473
  }
3589
5474
  try {
3590
- const buf = readFileSync6(target, "utf8");
5475
+ const buf = readFileSync5(target, "utf8");
3591
5476
  const actual = bytesHashString(buf);
3592
5477
  const rendered = renderedAgentMap.get(entry.path);
3593
5478
  const expected = rendered ? rendered.sha256 : sourceHashes.get(entry.source);
@@ -3643,12 +5528,12 @@ async function checkManagedHashes(repoRoot, validatedLock) {
3643
5528
  const drift = [];
3644
5529
  const renderedAgents = await loadRenderedAgentOverrides(repoRoot);
3645
5530
  for (const entry of validatedLock.lock.files ?? []) {
3646
- const p = resolve15(repoRoot, entry.path);
3647
- if (!existsSync16(p)) {
5531
+ const p = resolve14(repoRoot, entry.path);
5532
+ if (!existsSync15(p)) {
3648
5533
  drift.push(`missing:${entry.path}`);
3649
5534
  continue;
3650
5535
  }
3651
- const buf = readFileSync6(p, "utf8");
5536
+ const buf = readFileSync5(p, "utf8");
3652
5537
  const actual = bytesHashString(buf);
3653
5538
  if (actual !== entry.sha256) drift.push(`drift:${entry.path}`);
3654
5539
  }
@@ -3781,7 +5666,28 @@ async function runDoctor({ rootPath, profile, json, writeOutput = true }) {
3781
5666
  // src/installer/commands/init.js
3782
5667
  init_catalog();
3783
5668
  init_config();
3784
- init_setup_pending();
5669
+
5670
+ // src/installer/setup-pending.js
5671
+ import { existsSync as existsSync16, readFileSync as readFileSync6, unlinkSync, writeFile as writeFile7, mkdir as mkdirAsync } from "node:fs";
5672
+ import { promisify } from "node:util";
5673
+ import { resolve as resolve15, dirname as dirname9 } from "node:path";
5674
+ var writeFileAsync = promisify(writeFile7);
5675
+ var mkdirAsyncAsync = promisify(mkdirAsync);
5676
+ var REL_PATH = ".opencode/ship.setup-pending.json";
5677
+ function setupPendingPath(repoRoot) {
5678
+ return resolve15(repoRoot, REL_PATH);
5679
+ }
5680
+ function isSetupPending(repoRoot) {
5681
+ return existsSync16(setupPendingPath(repoRoot));
5682
+ }
5683
+ async function writeSetupPending(repoRoot, payload) {
5684
+ const path = setupPendingPath(repoRoot);
5685
+ await mkdirAsyncAsync(dirname9(path), { recursive: true });
5686
+ await writeFileAsync(path, JSON.stringify(payload, null, 2) + "\n", "utf8");
5687
+ }
5688
+ var SETUP_PENDING_REL_PATH2 = REL_PATH;
5689
+
5690
+ // src/installer/commands/init.js
3785
5691
  var writeFileAsync2 = promisify2(writeFile8);
3786
5692
  var mkdirAsyncAsync2 = promisify2(mkdirAsync2);
3787
5693
  async function runInit(options) {
@@ -4046,7 +5952,6 @@ async function runDiff(options) {
4046
5952
 
4047
5953
  // src/installer/commands/update.js
4048
5954
  init_catalog();
4049
- init_setup_pending();
4050
5955
  async function runUpdate(options) {
4051
5956
  try {
4052
5957
  validateCatalog();
@@ -4124,7 +6029,6 @@ function emitFailure2(code, message, json, command) {
4124
6029
  // src/installer/commands/setup-complete.js
4125
6030
  init_config();
4126
6031
  init_setup_state();
4127
- init_setup_pending();
4128
6032
  import { existsSync as existsSync17 } from "node:fs";
4129
6033
  import { resolve as resolve16 } from "node:path";
4130
6034
  async function runSetupComplete(options) {
@@ -4132,7 +6036,6 @@ async function runSetupComplete(options) {
4132
6036
  if (!existsSync17(resolve16(repoRoot, ".git"))) {
4133
6037
  return emitFailure3(2, "not a git repository", options.json);
4134
6038
  }
4135
- clearSetupPending(repoRoot);
4136
6039
  const config = await loadConfig(repoRoot);
4137
6040
  const configValue = config?.ok ? config.value : null;
4138
6041
  const state = await setupComplete(repoRoot, configValue);
@@ -4145,6 +6048,7 @@ async function runSetupComplete(options) {
4145
6048
  if (!state.ok) {
4146
6049
  return emitFailure3(6, `setup incomplete: ${diagnostics.join("; ")}`, options.json, { state });
4147
6050
  }
6051
+ const markerWasPresent = isSetupPending(repoRoot);
4148
6052
  const preview = await previewInstall({
4149
6053
  rootPath: repoRoot,
4150
6054
  profile: "engineering",
@@ -4161,7 +6065,14 @@ async function runSetupComplete(options) {
4161
6065
  const committed = await commitInstall(preview, {
4162
6066
  json: options.json,
4163
6067
  command: "setup-complete",
4164
- fullSetupComplete: true
6068
+ fullSetupComplete: true,
6069
+ transactionEntries: markerWasPresent ? [{
6070
+ op: "file",
6071
+ kind: "delete",
6072
+ target: setupPendingPath(repoRoot),
6073
+ relPath: SETUP_PENDING_REL_PATH2,
6074
+ reason: "clear setup-pending marker in the setup-complete transaction"
6075
+ }] : []
4165
6076
  });
4166
6077
  if (committed.extra?.exitCode !== 0) {
4167
6078
  return emitFailure3(committed.extra?.exitCode ?? 1, "commit failed", options.json, { committed });
@@ -4172,6 +6083,7 @@ async function runSetupComplete(options) {
4172
6083
  command: "setup-complete",
4173
6084
  status: "ok",
4174
6085
  setupComplete: true,
6086
+ markerWasPresent,
4175
6087
  requirements: SETUP_REQUIREMENTS,
4176
6088
  plan: serializePlan(committed.plan ?? []),
4177
6089
  summary: committed.summary ?? {},