oh-my-opencode 4.14.0 → 4.14.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 (140) hide show
  1. package/.agents/skills/codex-qa/references/isolation.md +1 -1
  2. package/README.md +1 -1
  3. package/dist/cli/doctor/framework/types.d.ts +2 -0
  4. package/dist/cli/index.js +223 -1447
  5. package/dist/cli-node/index.js +223 -1447
  6. package/dist/index.js +4255 -714
  7. package/dist/plugin/native-skills.d.ts +9 -0
  8. package/dist/plugin/system-transform.d.ts +1 -1
  9. package/dist/skills/frontend/ATTRIBUTION.md +7 -1
  10. package/dist/skills/frontend/SKILL.md +4 -3
  11. package/dist/skills/frontend/references/design/README.md +4 -3
  12. package/dist/skills/frontend/references/design/_INDEX.md +5 -3
  13. package/dist/skills/frontend/references/design/aside.md +209 -0
  14. package/dist/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
  15. package/dist/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  16. package/dist/skills/lcx-doctor/SKILL.md +48 -13
  17. package/dist/skills/lcx-doctor/agents/openai.yaml +1 -1
  18. package/dist/skills/lcx-report-bug/SKILL.md +44 -13
  19. package/dist/skills/lcx-report-bug/agents/openai.yaml +1 -1
  20. package/dist/skills/visual-qa/SKILL.md +5 -5
  21. package/dist/skills/visual-qa/references/agent-browser-setup.md +5 -4
  22. package/dist/skills/visual-qa/scripts/cli.test.ts +7 -2
  23. package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
  24. package/dist/skills/visual-qa/scripts/visual-qa.mjs +530 -0
  25. package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
  26. package/dist/tools/delegate-task/types.d.ts +2 -0
  27. package/dist/tools/skill/types.d.ts +1 -0
  28. package/package.json +26 -13
  29. package/packages/git-bash-mcp/dist/cli.js +2 -0
  30. package/packages/lsp-daemon/dist/cli.js +15 -4
  31. package/packages/lsp-daemon/dist/ensure-daemon.d.ts +1 -0
  32. package/packages/lsp-daemon/dist/ensure-daemon.js +9 -3
  33. package/packages/lsp-daemon/dist/index.js +15 -4
  34. package/packages/lsp-daemon/dist/paths.d.ts +1 -0
  35. package/packages/lsp-daemon/dist/paths.js +6 -1
  36. package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
  37. package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -7
  38. package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
  39. package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +1 -1
  40. package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +123 -10
  41. package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +123 -10
  42. package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
  43. package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +126 -9
  44. package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +141 -0
  45. package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
  46. package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
  47. package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
  48. package/packages/omo-codex/plugin/components/lsp/dist/cli.js +90 -25
  49. package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +2 -0
  50. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +2 -0
  51. package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +56 -0
  52. package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
  53. package/packages/omo-codex/plugin/components/lsp/src/cli.ts +2 -5
  54. package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +2 -0
  55. package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +65 -0
  56. package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +6 -1
  57. package/packages/omo-codex/plugin/components/rules/dist/cli.js +8 -106
  58. package/packages/omo-codex/plugin/components/rules/package.json +1 -1
  59. package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +4 -17
  60. package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
  61. package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
  62. package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +17 -5
  63. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +63 -1
  64. package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +80 -50
  65. package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
  66. package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +1 -1
  67. package/packages/omo-codex/plugin/components/ultrawork/directive.md +4 -12
  68. package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
  69. package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +7 -33
  70. package/packages/omo-codex/plugin/components/ulw-loop/directive.md +4 -12
  71. package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -2
  72. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
  73. package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +4 -4
  74. package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/sample-quality-gate.json +7 -7
  75. package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +3 -1
  76. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-doc.test.ts +2 -2
  77. package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +7 -6
  78. package/packages/omo-codex/plugin/package-lock.json +16 -56
  79. package/packages/omo-codex/plugin/package.json +1 -1
  80. package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -1
  81. package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -3
  82. package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +4 -3
  83. package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +5 -3
  84. package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +209 -0
  85. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
  86. package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  87. package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +48 -13
  88. package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
  89. package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +44 -13
  90. package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
  91. package/packages/omo-codex/plugin/skills/teammode/SKILL.md +17 -5
  92. package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +63 -1
  93. package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +80 -50
  94. package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
  95. package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +4 -4
  96. package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +5 -5
  97. package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +5 -4
  98. package/packages/omo-codex/plugin/skills/visual-qa/scripts/visual-qa.mjs +530 -0
  99. package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +23 -0
  100. package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +38 -2
  101. package/packages/omo-codex/plugin/test/component-bin-names.test.mjs +12 -11
  102. package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +99 -1
  103. package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +62 -8
  104. package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +1 -0
  105. package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +102 -0
  106. package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +41 -1
  107. package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +93 -0
  108. package/packages/omo-codex/scripts/install-bin-links.test.mjs +6 -0
  109. package/packages/omo-codex/scripts/install-cache-copy.test.mjs +28 -0
  110. package/packages/omo-codex/scripts/install-config-autonomous-features.test.mjs +43 -3
  111. package/packages/omo-codex/scripts/install-delegated-command.test.mjs +93 -0
  112. package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -41
  113. package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +21 -2
  114. package/packages/omo-codex/scripts/install-local.test.mjs +0 -1
  115. package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -1
  116. package/packages/shared-skills/skills/frontend/SKILL.md +4 -3
  117. package/packages/shared-skills/skills/frontend/references/design/README.md +4 -3
  118. package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +5 -3
  119. package/packages/shared-skills/skills/frontend/references/design/aside.md +209 -0
  120. package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
  121. package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
  122. package/packages/shared-skills/skills/lcx-doctor/SKILL.md +48 -13
  123. package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +1 -1
  124. package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +44 -13
  125. package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +1 -1
  126. package/packages/shared-skills/skills/visual-qa/SKILL.md +5 -5
  127. package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +5 -4
  128. package/packages/shared-skills/skills/visual-qa/scripts/cli.test.ts +7 -2
  129. package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
  130. package/packages/shared-skills/skills/visual-qa/scripts/visual-qa.mjs +530 -0
  131. package/dist/cli/sparkshell-appserver-websocket.d.ts +0 -4
  132. package/dist/cli/sparkshell-appserver.d.ts +0 -16
  133. package/dist/cli/sparkshell-condense.d.ts +0 -10
  134. package/dist/cli/sparkshell-parse.d.ts +0 -24
  135. package/dist/cli/sparkshell-session-context.d.ts +0 -20
  136. package/dist/cli/sparkshell-spark.d.ts +0 -23
  137. package/dist/cli/sparkshell.d.ts +0 -33
  138. package/dist/shared/sparkshell-awareness.d.ts +0 -5
  139. package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +0 -106
  140. package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +0 -355
@@ -0,0 +1,530 @@
1
+ import { createRequire } from "node:module";
2
+ var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
+
4
+ // packages/shared-skills/skills/visual-qa/scripts/cli.ts
5
+ import { readFileSync } from "node:fs";
6
+
7
+ // packages/shared-skills/skills/visual-qa/scripts/image-diff.ts
8
+ var GRID_SIZE = 8;
9
+ function round4(value) {
10
+ return Math.round(value * 1e4) / 1e4;
11
+ }
12
+ function pixelsDiffer(ref, refOffset, act, actOffset) {
13
+ return ref[refOffset] !== act[actOffset] || ref[refOffset + 1] !== act[actOffset + 1] || ref[refOffset + 2] !== act[actOffset + 2] || ref[refOffset + 3] !== act[actOffset + 3];
14
+ }
15
+ function buildHotspots(cellDiff, cellTotal, cols, rows, overlapWidth, overlapHeight) {
16
+ const hotspots = [];
17
+ for (let gridY = 0;gridY < rows; gridY++) {
18
+ for (let gridX = 0;gridX < cols; gridX++) {
19
+ const index = gridY * cols + gridX;
20
+ const diff = cellDiff[index] ?? 0;
21
+ const total = cellTotal[index] ?? 0;
22
+ if (diff === 0 || total === 0)
23
+ continue;
24
+ const left = Math.floor(gridX * overlapWidth / cols);
25
+ const right = Math.floor((gridX + 1) * overlapWidth / cols);
26
+ const top = Math.floor(gridY * overlapHeight / rows);
27
+ const bottom = Math.floor((gridY + 1) * overlapHeight / rows);
28
+ hotspots.push({
29
+ gridX,
30
+ gridY,
31
+ x: left,
32
+ y: top,
33
+ width: right - left,
34
+ height: bottom - top,
35
+ diffRatio: round4(diff / total)
36
+ });
37
+ }
38
+ }
39
+ hotspots.sort((a, b) => b.diffRatio - a.diffRatio);
40
+ return hotspots;
41
+ }
42
+ function buildSummary(similarityScore, diffPixels, totalPixels, dimensionsMatch, hotspotCount) {
43
+ const parts = [`${similarityScore}/100 similarity`, `${diffPixels}/${totalPixels} pixels differ`];
44
+ if (!dimensionsMatch)
45
+ parts.push("dimensions differ");
46
+ if (hotspotCount > 0)
47
+ parts.push(`${hotspotCount} hotspot region(s)`);
48
+ return `${parts.join("; ")}.`;
49
+ }
50
+ function diffImages(reference, actual) {
51
+ const overlapWidth = Math.min(reference.width, actual.width);
52
+ const overlapHeight = Math.min(reference.height, actual.height);
53
+ const totalPixels = overlapWidth * overlapHeight;
54
+ const cols = Math.max(1, Math.min(GRID_SIZE, overlapWidth));
55
+ const rows = Math.max(1, Math.min(GRID_SIZE, overlapHeight));
56
+ const cellDiff = new Array(cols * rows).fill(0);
57
+ const cellTotal = new Array(cols * rows).fill(0);
58
+ let diffPixels = 0;
59
+ for (let y = 0;y < overlapHeight; y++) {
60
+ const cellY = Math.min(rows - 1, Math.floor(y * rows / overlapHeight));
61
+ for (let x = 0;x < overlapWidth; x++) {
62
+ const cellX = Math.min(cols - 1, Math.floor(x * cols / overlapWidth));
63
+ const cellIndex = cellY * cols + cellX;
64
+ cellTotal[cellIndex] = (cellTotal[cellIndex] ?? 0) + 1;
65
+ const refOffset = (y * reference.width + x) * 4;
66
+ const actOffset = (y * actual.width + x) * 4;
67
+ if (pixelsDiffer(reference.rgba, refOffset, actual.rgba, actOffset)) {
68
+ diffPixels++;
69
+ cellDiff[cellIndex] = (cellDiff[cellIndex] ?? 0) + 1;
70
+ }
71
+ }
72
+ }
73
+ const diffRatio = totalPixels === 0 ? 0 : diffPixels / totalPixels;
74
+ const similarityScore = Math.round((1 - diffRatio) * 100);
75
+ const hotspots = buildHotspots(cellDiff, cellTotal, cols, rows, overlapWidth, overlapHeight);
76
+ const dimensionsMatch = reference.width === actual.width && reference.height === actual.height;
77
+ const alphaChannelIntact = !(reference.hasTransparentPixels && !actual.hasTransparentPixels);
78
+ return {
79
+ command: "image-diff",
80
+ dimensionsMatch,
81
+ reference: { width: reference.width, height: reference.height },
82
+ actual: { width: actual.width, height: actual.height },
83
+ totalPixels,
84
+ diffPixels,
85
+ diffRatio: round4(diffRatio),
86
+ similarityScore,
87
+ alphaChannelIntact,
88
+ hotspots,
89
+ summary: buildSummary(similarityScore, diffPixels, totalPixels, dimensionsMatch, hotspots.length)
90
+ };
91
+ }
92
+
93
+ // packages/shared-skills/skills/visual-qa/scripts/png-decode.ts
94
+ import { Buffer as Buffer2 } from "node:buffer";
95
+ import { inflateSync } from "node:zlib";
96
+
97
+ // packages/shared-skills/skills/visual-qa/scripts/png-crc.ts
98
+ import { Buffer } from "node:buffer";
99
+ var PNG_SIGNATURE = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]);
100
+ function buildCrcTable() {
101
+ const table = new Uint32Array(256);
102
+ for (let n = 0;n < 256; n++) {
103
+ let c = n;
104
+ for (let k = 0;k < 8; k++) {
105
+ c = (c & 1) === 1 ? 3988292384 ^ c >>> 1 : c >>> 1;
106
+ }
107
+ table[n] = c >>> 0;
108
+ }
109
+ return table;
110
+ }
111
+ var CRC_TABLE = buildCrcTable();
112
+
113
+ // packages/shared-skills/skills/visual-qa/scripts/png-decode.ts
114
+ class PngDecodeError extends Error {
115
+ name = "PngDecodeError";
116
+ }
117
+ function readChunks(buffer) {
118
+ const chunks = [];
119
+ let offset = 8;
120
+ while (offset + 8 <= buffer.length) {
121
+ const length = buffer.readUInt32BE(offset);
122
+ const type = buffer.toString("ascii", offset + 4, offset + 8);
123
+ const dataStart = offset + 8;
124
+ const dataEnd = dataStart + length;
125
+ if (dataEnd + 4 > buffer.length)
126
+ break;
127
+ chunks.push({ type, data: buffer.subarray(dataStart, dataEnd) });
128
+ offset = dataEnd + 4;
129
+ }
130
+ return chunks;
131
+ }
132
+ function channelsForColorType(colorType) {
133
+ switch (colorType) {
134
+ case 0:
135
+ return 1;
136
+ case 2:
137
+ return 3;
138
+ case 4:
139
+ return 2;
140
+ case 6:
141
+ return 4;
142
+ default:
143
+ throw new PngDecodeError(`unsupported color type ${colorType}`);
144
+ }
145
+ }
146
+ function parseHeader(data) {
147
+ if (data.length < 13) {
148
+ throw new PngDecodeError("invalid IHDR chunk length");
149
+ }
150
+ const colorType = data[9] ?? 0;
151
+ return {
152
+ width: data.readUInt32BE(0),
153
+ height: data.readUInt32BE(4),
154
+ bitDepth: data[8] ?? 0,
155
+ colorType,
156
+ channels: channelsForColorType(colorType)
157
+ };
158
+ }
159
+ function paeth(a, b, c) {
160
+ const p = a + b - c;
161
+ const pa = Math.abs(p - a);
162
+ const pb = Math.abs(p - b);
163
+ const pc = Math.abs(p - c);
164
+ if (pa <= pb && pa <= pc)
165
+ return a;
166
+ if (pb <= pc)
167
+ return b;
168
+ return c;
169
+ }
170
+ function unfilterRow(filterType, row, prev, bpp) {
171
+ const out = Buffer2.alloc(row.length);
172
+ for (let i = 0;i < row.length; i++) {
173
+ const raw = row[i] ?? 0;
174
+ const a = i >= bpp ? out[i - bpp] ?? 0 : 0;
175
+ const b = prev ? prev[i] ?? 0 : 0;
176
+ const c = i >= bpp && prev ? prev[i - bpp] ?? 0 : 0;
177
+ switch (filterType) {
178
+ case 0:
179
+ out[i] = raw;
180
+ break;
181
+ case 1:
182
+ out[i] = raw + a & 255;
183
+ break;
184
+ case 2:
185
+ out[i] = raw + b & 255;
186
+ break;
187
+ case 3:
188
+ out[i] = raw + (a + b >> 1) & 255;
189
+ break;
190
+ case 4:
191
+ out[i] = raw + paeth(a, b, c) & 255;
192
+ break;
193
+ default:
194
+ throw new PngDecodeError(`unsupported filter type ${filterType}`);
195
+ }
196
+ }
197
+ return out;
198
+ }
199
+ function decodePixels(idat, width, height, bpp) {
200
+ const inflated = inflateSync(idat);
201
+ const rowBytes = width * bpp;
202
+ if (inflated.length < height * (rowBytes + 1)) {
203
+ throw new PngDecodeError("truncated image data");
204
+ }
205
+ const pixels = Buffer2.alloc(width * height * bpp);
206
+ let prev = null;
207
+ for (let y = 0;y < height; y++) {
208
+ const rowStart = y * (rowBytes + 1);
209
+ const filterType = inflated[rowStart] ?? 0;
210
+ const filtered = inflated.subarray(rowStart + 1, rowStart + 1 + rowBytes);
211
+ const row = unfilterRow(filterType, filtered, prev, bpp);
212
+ row.copy(pixels, y * rowBytes);
213
+ prev = row;
214
+ }
215
+ return pixels;
216
+ }
217
+ function normalizeToRgba(pixels, pixelCount, channels) {
218
+ const rgba = new Uint8Array(pixelCount * 4);
219
+ let hasTransparent = false;
220
+ for (let i = 0;i < pixelCount; i++) {
221
+ const src = i * channels;
222
+ let r = 0;
223
+ let g = 0;
224
+ let b = 0;
225
+ let a = 255;
226
+ switch (channels) {
227
+ case 1: {
228
+ const v = pixels[src] ?? 0;
229
+ r = v;
230
+ g = v;
231
+ b = v;
232
+ break;
233
+ }
234
+ case 2: {
235
+ const v = pixels[src] ?? 0;
236
+ r = v;
237
+ g = v;
238
+ b = v;
239
+ a = pixels[src + 1] ?? 255;
240
+ break;
241
+ }
242
+ case 3: {
243
+ r = pixels[src] ?? 0;
244
+ g = pixels[src + 1] ?? 0;
245
+ b = pixels[src + 2] ?? 0;
246
+ break;
247
+ }
248
+ default: {
249
+ r = pixels[src] ?? 0;
250
+ g = pixels[src + 1] ?? 0;
251
+ b = pixels[src + 2] ?? 0;
252
+ a = pixels[src + 3] ?? 255;
253
+ }
254
+ }
255
+ const dst = i * 4;
256
+ rgba[dst] = r;
257
+ rgba[dst + 1] = g;
258
+ rgba[dst + 2] = b;
259
+ rgba[dst + 3] = a;
260
+ if (a < 255)
261
+ hasTransparent = true;
262
+ }
263
+ return { rgba, hasTransparent };
264
+ }
265
+ function decodePng(buffer) {
266
+ if (buffer.length < 8 || !buffer.subarray(0, 8).equals(PNG_SIGNATURE)) {
267
+ throw new PngDecodeError("not a PNG file (bad signature)");
268
+ }
269
+ const chunks = readChunks(buffer);
270
+ const ihdr = chunks.find((chunk) => chunk.type === "IHDR");
271
+ if (ihdr === undefined) {
272
+ throw new PngDecodeError("missing IHDR chunk");
273
+ }
274
+ const header = parseHeader(ihdr.data);
275
+ if (header.bitDepth !== 8) {
276
+ throw new PngDecodeError(`unsupported bit depth ${header.bitDepth}`);
277
+ }
278
+ const idatChunks = chunks.filter((chunk) => chunk.type === "IDAT");
279
+ if (idatChunks.length === 0) {
280
+ throw new PngDecodeError("missing IDAT chunk");
281
+ }
282
+ const idat = Buffer2.concat(idatChunks.map((chunk) => chunk.data));
283
+ const pixels = decodePixels(idat, header.width, header.height, header.channels);
284
+ const normalized = normalizeToRgba(pixels, header.width * header.height, header.channels);
285
+ return {
286
+ width: header.width,
287
+ height: header.height,
288
+ rgba: normalized.rgba,
289
+ hasAlphaChannel: header.colorType === 4 || header.colorType === 6,
290
+ hasTransparentPixels: normalized.hasTransparent
291
+ };
292
+ }
293
+
294
+ // packages/shared-skills/skills/visual-qa/scripts/ansi.ts
295
+ var ESC = String.fromCharCode(27);
296
+ var CSI = String.fromCharCode(155);
297
+ var ANSI_PATTERN = new RegExp(`[${ESC}${CSI}][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]`, "g");
298
+ function stripAnsi(input) {
299
+ return input.replace(ANSI_PATTERN, "");
300
+ }
301
+ function hasAnsi(input) {
302
+ return stripAnsi(input) !== input;
303
+ }
304
+
305
+ // packages/shared-skills/skills/visual-qa/scripts/east-asian-width.ts
306
+ var ZERO_WIDTH_RANGES = [
307
+ { start: 768, end: 879 },
308
+ { start: 1155, end: 1161 },
309
+ { start: 1425, end: 1469 },
310
+ { start: 1552, end: 1562 },
311
+ { start: 1611, end: 1631 },
312
+ { start: 1648, end: 1648 },
313
+ { start: 1750, end: 1756 },
314
+ { start: 4448, end: 4607 },
315
+ { start: 8203, end: 8207 },
316
+ { start: 8234, end: 8238 },
317
+ { start: 8288, end: 8292 },
318
+ { start: 8400, end: 8447 },
319
+ { start: 65056, end: 65071 },
320
+ { start: 65279, end: 65279 }
321
+ ];
322
+ var WIDE_RANGES = [
323
+ { start: 4352, end: 4447 },
324
+ { start: 8986, end: 8987 },
325
+ { start: 11904, end: 12350 },
326
+ { start: 12353, end: 13311 },
327
+ { start: 13312, end: 19903 },
328
+ { start: 19968, end: 40959 },
329
+ { start: 40960, end: 42191 },
330
+ { start: 43360, end: 43391 },
331
+ { start: 44032, end: 55203 },
332
+ { start: 63744, end: 64255 },
333
+ { start: 65040, end: 65049 },
334
+ { start: 65072, end: 65135 },
335
+ { start: 65280, end: 65376 },
336
+ { start: 65504, end: 65510 },
337
+ { start: 110592, end: 110959 },
338
+ { start: 127488, end: 127743 },
339
+ { start: 127744, end: 129791 },
340
+ { start: 131072, end: 262141 }
341
+ ];
342
+ function inRanges(codePoint, ranges) {
343
+ for (const range of ranges) {
344
+ if (codePoint >= range.start && codePoint <= range.end) {
345
+ return true;
346
+ }
347
+ }
348
+ return false;
349
+ }
350
+ function charWidth(codePoint) {
351
+ if (codePoint === 0)
352
+ return 0;
353
+ if (codePoint < 32)
354
+ return 0;
355
+ if (codePoint >= 127 && codePoint <= 159)
356
+ return 0;
357
+ if (inRanges(codePoint, ZERO_WIDTH_RANGES))
358
+ return 0;
359
+ if (inRanges(codePoint, WIDE_RANGES))
360
+ return 2;
361
+ return 1;
362
+ }
363
+ function stringWidth(text) {
364
+ let total = 0;
365
+ for (const char of text) {
366
+ const codePoint = char.codePointAt(0);
367
+ if (codePoint === undefined)
368
+ continue;
369
+ total += charWidth(codePoint);
370
+ }
371
+ return total;
372
+ }
373
+
374
+ // packages/shared-skills/skills/visual-qa/scripts/tui-grid.ts
375
+ var BOX_DRAWING_START = 9472;
376
+ var BOX_DRAWING_END = 9599;
377
+ var MAX_WIDE_COLUMNS = 64;
378
+ function splitLines(text) {
379
+ const lines = text.split(/\r?\n/);
380
+ if (lines.length > 1 && lines[lines.length - 1] === "")
381
+ lines.pop();
382
+ return lines;
383
+ }
384
+ function isFrameLine(plain) {
385
+ for (const char of plain) {
386
+ const codePoint = char.codePointAt(0);
387
+ if (codePoint !== undefined && codePoint >= BOX_DRAWING_START && codePoint <= BOX_DRAWING_END) {
388
+ return true;
389
+ }
390
+ }
391
+ return false;
392
+ }
393
+ function wideStartColumns(plain) {
394
+ const columns = [];
395
+ let column = 0;
396
+ for (const char of plain) {
397
+ const codePoint = char.codePointAt(0);
398
+ if (codePoint === undefined)
399
+ continue;
400
+ const width = charWidth(codePoint);
401
+ if (width === 2)
402
+ columns.push(column);
403
+ column += width;
404
+ }
405
+ return columns;
406
+ }
407
+ function summarize(lineCount, maxWidth, expectedColumns, overflowCount, borderMisaligned, containsAnsi) {
408
+ const parts = [`${lineCount} line(s)`, `max width ${maxWidth}/${expectedColumns}`];
409
+ if (overflowCount > 0)
410
+ parts.push(`${overflowCount} overflow line(s)`);
411
+ if (borderMisaligned)
412
+ parts.push("borders misaligned");
413
+ if (containsAnsi)
414
+ parts.push("contains ANSI");
415
+ return `${parts.join("; ")}.`;
416
+ }
417
+ function checkTui(text, expectedColumns) {
418
+ const lines = splitLines(text);
419
+ const lineWidths = [];
420
+ const overflowLines = [];
421
+ const wideColumns = new Set;
422
+ const frameWidths = new Set;
423
+ for (let index = 0;index < lines.length; index++) {
424
+ const plain = stripAnsi(lines[index] ?? "");
425
+ const width = stringWidth(plain);
426
+ lineWidths.push(width);
427
+ if (expectedColumns > 0 && width > expectedColumns) {
428
+ overflowLines.push({ line: index + 1, width });
429
+ }
430
+ if (isFrameLine(plain))
431
+ frameWidths.add(width);
432
+ for (const column of wideStartColumns(plain)) {
433
+ if (wideColumns.size < MAX_WIDE_COLUMNS)
434
+ wideColumns.add(column);
435
+ }
436
+ }
437
+ const maxWidth = lineWidths.reduce((max, width) => width > max ? width : max, 0);
438
+ const borderMisaligned = frameWidths.size > 1;
439
+ const containsAnsi = hasAnsi(text);
440
+ return {
441
+ command: "tui-check",
442
+ expectedColumns,
443
+ lineCount: lines.length,
444
+ lineWidths,
445
+ maxWidth,
446
+ overflowLines,
447
+ borderMisaligned,
448
+ wideCharColumns: [...wideColumns].sort((a, b) => a - b),
449
+ hasAnsi: containsAnsi,
450
+ summary: summarize(lines.length, maxWidth, expectedColumns, overflowLines.length, borderMisaligned, containsAnsi)
451
+ };
452
+ }
453
+
454
+ // packages/shared-skills/skills/visual-qa/scripts/cli.ts
455
+ class CliError extends Error {
456
+ name = "CliError";
457
+ }
458
+ var DEFAULT_COLUMNS = 80;
459
+ var COLS_FLAG = "--cols";
460
+ function runImageDiff(args) {
461
+ const referencePath = args[0];
462
+ const actualPath = args[1];
463
+ if (referencePath === undefined || actualPath === undefined) {
464
+ throw new CliError("usage: image-diff <reference.png> <actual.png>");
465
+ }
466
+ const reference = decodePng(readFileSync(referencePath));
467
+ const actual = decodePng(readFileSync(actualPath));
468
+ return diffImages(reference, actual);
469
+ }
470
+ function parseColumns(args) {
471
+ for (let index = 0;index < args.length; index++) {
472
+ const arg = args[index] ?? "";
473
+ if (arg === COLS_FLAG) {
474
+ const parsed = Number(args[index + 1]);
475
+ if (!Number.isInteger(parsed) || parsed <= 0) {
476
+ throw new CliError(`${COLS_FLAG} requires a positive integer`);
477
+ }
478
+ return parsed;
479
+ }
480
+ if (arg.startsWith(`${COLS_FLAG}=`)) {
481
+ const parsed = Number(arg.slice(COLS_FLAG.length + 1));
482
+ if (!Number.isInteger(parsed) || parsed <= 0) {
483
+ throw new CliError(`${COLS_FLAG} requires a positive integer`);
484
+ }
485
+ return parsed;
486
+ }
487
+ }
488
+ return DEFAULT_COLUMNS;
489
+ }
490
+ function runTuiCheck(args) {
491
+ const capturePath = args[0];
492
+ if (capturePath === undefined || capturePath.startsWith("--")) {
493
+ throw new CliError("usage: tui-check <capture.txt> [--cols N]");
494
+ }
495
+ const text = readFileSync(capturePath, "utf8");
496
+ return checkTui(text, parseColumns(args.slice(1)));
497
+ }
498
+ function run(argv) {
499
+ const command = argv[0];
500
+ const rest = argv.slice(1);
501
+ switch (command) {
502
+ case "image-diff":
503
+ return runImageDiff(rest);
504
+ case "tui-check":
505
+ return runTuiCheck(rest);
506
+ default:
507
+ throw new CliError(`unknown command "${command ?? ""}"; expected "image-diff" or "tui-check"`);
508
+ }
509
+ }
510
+ function main(argv) {
511
+ try {
512
+ const result = run(argv);
513
+ process.stdout.write(`${JSON.stringify(result, null, 2)}
514
+ `);
515
+ } catch (error) {
516
+ const message = error instanceof Error ? error.message : String(error);
517
+ process.stderr.write(`visual-qa error: ${message}
518
+ `);
519
+ process.exitCode = 1;
520
+ }
521
+ }
522
+ if (__require.main == __require.module) {
523
+ main(process.argv.slice(2));
524
+ }
525
+ export {
526
+ runTuiCheck,
527
+ runImageDiff,
528
+ run,
529
+ CliError
530
+ };
@@ -10,6 +10,7 @@ type ResolveSkillContentOptions = {
10
10
  targetAgent?: string;
11
11
  nativeSkills?: DelegateTaskToolOptions["nativeSkills"];
12
12
  nativeSkillEntries?: NativeSkillEntry[];
13
+ getLoadedSkills?: DelegateTaskToolOptions["getLoadedSkills"];
13
14
  };
14
15
  export declare function resolveSkillContent(skills: string[], options: ResolveSkillContentOptions): Promise<{
15
16
  content: string | undefined;
@@ -1,6 +1,7 @@
1
1
  import type { BackgroundManager } from "../../features/background-agent";
2
2
  import type { CategoriesConfig, GitMasterConfig, BrowserAutomationProvider, AgentOverrides, SisyphusAgentConfig } from "../../config/schema";
3
3
  import type { ModelFallbackControllerAccessor } from "../../hooks/model-fallback";
4
+ import type { LoadedSkill } from "../../features/opencode-skill-loader/types";
4
5
  import type { SessionPromptAsyncData, SessionPromptData, SessionStatusData } from "@opencode-ai/sdk";
5
6
  import type { AvailableCategory, AvailableSkill } from "../../agents/dynamic-agent-prompt-builder";
6
7
  type SessionPathInput = {
@@ -148,6 +149,7 @@ export interface DelegateTaskToolOptions {
148
149
  } | undefined>;
149
150
  dirs(): string[] | Promise<string[]>;
150
151
  };
152
+ getLoadedSkills?: () => Promise<LoadedSkill[]>;
151
153
  }
152
154
  import type { DelegatedModelConfig } from "../../shared/model-resolution-types";
153
155
  export type { DelegatedModelConfig };
@@ -21,6 +21,7 @@ export interface SkillLoadOptions {
21
21
  opencodeOnly?: boolean;
22
22
  /** Pre-merged skills to use instead of discovering */
23
23
  skills?: LoadedSkill[];
24
+ getLoadedSkills?: () => Promise<LoadedSkill[]>;
24
25
  /** Pre-discovered commands to use instead of discovering */
25
26
  commands?: CommandInfo[];
26
27
  /** MCP manager for querying skill-embedded MCP servers */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-my-opencode",
3
- "version": "4.14.0",
3
+ "version": "4.14.2",
4
4
  "description": "The Best AI Agent Harness - Batteries-Included OpenCode Plugin with Multi-Model Orchestration, Parallel Background Agents, and Crafted LSP/AST Tools",
5
5
  "main": "./dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -65,7 +65,20 @@
65
65
  "packages/omo-codex/marketplace.json",
66
66
  "packages/omo-codex/lazycodex-repository",
67
67
  "packages/omo-codex/plugin",
68
+ "packages/omo-codex/plugin/components/bootstrap/dist/cli.js",
69
+ "packages/omo-codex/plugin/components/bootstrap/scripts/bootstrap.ps1",
70
+ "packages/omo-codex/plugin/components/bootstrap/scripts/node-dispatch.ps1",
71
+ "packages/omo-codex/plugin/components/codegraph/dist/cli.js",
72
+ "packages/omo-codex/plugin/components/codegraph/dist/serve.js",
73
+ "packages/omo-codex/plugin/components/comment-checker/dist/cli.js",
74
+ "packages/omo-codex/plugin/components/git-bash/dist/cli.js",
75
+ "packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js",
76
+ "packages/omo-codex/plugin/components/lsp/dist/cli.js",
77
+ "packages/omo-codex/plugin/components/rules/dist/cli.js",
68
78
  "packages/omo-codex/plugin/components/start-work-continuation/dist/cli.js",
79
+ "packages/omo-codex/plugin/components/teammode/dist/cli.js",
80
+ "packages/omo-codex/plugin/components/telemetry/dist/cli.js",
81
+ "packages/omo-codex/plugin/components/ultrawork/dist/cli.js",
69
82
  "packages/omo-codex/plugin/components/ulw-loop/dist/cli.js",
70
83
  "packages/omo-codex/plugin/.codex-plugin",
71
84
  "!packages/omo-codex/plugin/node_modules",
@@ -178,18 +191,18 @@
178
191
  "typescript": "^6.0.3"
179
192
  },
180
193
  "optionalDependencies": {
181
- "oh-my-opencode-darwin-arm64": "4.14.0",
182
- "oh-my-opencode-darwin-x64": "4.14.0",
183
- "oh-my-opencode-darwin-x64-baseline": "4.14.0",
184
- "oh-my-opencode-linux-arm64": "4.14.0",
185
- "oh-my-opencode-linux-arm64-musl": "4.14.0",
186
- "oh-my-opencode-linux-x64": "4.14.0",
187
- "oh-my-opencode-linux-x64-baseline": "4.14.0",
188
- "oh-my-opencode-linux-x64-musl": "4.14.0",
189
- "oh-my-opencode-linux-x64-musl-baseline": "4.14.0",
190
- "oh-my-opencode-windows-arm64": "4.14.0",
191
- "oh-my-opencode-windows-x64": "4.14.0",
192
- "oh-my-opencode-windows-x64-baseline": "4.14.0"
194
+ "oh-my-opencode-darwin-arm64": "4.14.2",
195
+ "oh-my-opencode-darwin-x64": "4.14.2",
196
+ "oh-my-opencode-darwin-x64-baseline": "4.14.2",
197
+ "oh-my-opencode-linux-arm64": "4.14.2",
198
+ "oh-my-opencode-linux-arm64-musl": "4.14.2",
199
+ "oh-my-opencode-linux-x64": "4.14.2",
200
+ "oh-my-opencode-linux-x64-baseline": "4.14.2",
201
+ "oh-my-opencode-linux-x64-musl": "4.14.2",
202
+ "oh-my-opencode-linux-x64-musl-baseline": "4.14.2",
203
+ "oh-my-opencode-windows-arm64": "4.14.2",
204
+ "oh-my-opencode-windows-x64": "4.14.2",
205
+ "oh-my-opencode-windows-x64-baseline": "4.14.2"
193
206
  },
194
207
  "overrides": {
195
208
  "hono": "^4.12.18",
@@ -381,6 +381,8 @@ async function runMcpStdioServer(input, output, options = {}) {
381
381
  output,
382
382
  handler: handleGitBashMcpRequest,
383
383
  handlerOptions: options,
384
+ idleTimeoutMs: 0,
385
+ log: options.lifecycleLog,
384
386
  parseErrorResponse: () => errorResponse(null, -32601, "Method not found")
385
387
  });
386
388
  }
@@ -3405,6 +3405,7 @@ function unlinkQuietly(path) {
3405
3405
  var PROBE_TIMEOUT_MS = 500;
3406
3406
  var DEFAULT_READY_TIMEOUT_MS = 5000;
3407
3407
  var DEFAULT_POLL_INTERVAL_MS = 100;
3408
+ var CODEX_LSP_DAEMON_CLI_ENV = "CODEX_LSP_DAEMON_CLI";
3408
3409
 
3409
3410
  class DaemonUnreachableError extends Error {
3410
3411
  constructor(socketPath) {
@@ -3465,7 +3466,7 @@ function spawnDaemonProcess(paths) {
3465
3466
  mkdirSync3(dirname5(paths.log), { recursive: true });
3466
3467
  const logFd = openSync2(paths.log, "a");
3467
3468
  try {
3468
- const cliPath = fileURLToPath3(new URL("./cli.js", import.meta.url));
3469
+ const cliPath = resolveDaemonCliPath();
3469
3470
  const child = spawn2(execPath, [cliPath, "daemon"], {
3470
3471
  detached: true,
3471
3472
  stdio: ["ignore", logFd, logFd]
@@ -3475,6 +3476,12 @@ function spawnDaemonProcess(paths) {
3475
3476
  closeSync2(logFd);
3476
3477
  }
3477
3478
  }
3479
+ function resolveDaemonCliPath(env = process.env) {
3480
+ const override = env[CODEX_LSP_DAEMON_CLI_ENV]?.trim();
3481
+ if (override)
3482
+ return override;
3483
+ return fileURLToPath3(new URL("./cli.js", import.meta.url));
3484
+ }
3478
3485
  function defaultEnsureDaemonDeps() {
3479
3486
  return {
3480
3487
  probe: (socketPath) => probeSocket(socketPath),
@@ -3485,8 +3492,7 @@ function defaultEnsureDaemonDeps() {
3485
3492
  },
3486
3493
  spawnDaemon: (paths) => spawnDaemonProcess(paths),
3487
3494
  sleep: (ms) => new Promise((resolve6) => {
3488
- const timer = setTimeout(resolve6, ms);
3489
- timer.unref?.();
3495
+ setTimeout(resolve6, ms);
3490
3496
  }),
3491
3497
  now: () => Date.now()
3492
3498
  };
@@ -3499,6 +3505,7 @@ import { homedir as homedir3, tmpdir } from "node:os";
3499
3505
  import { join as join8 } from "node:path";
3500
3506
  var requireFromHere = createRequire(import.meta.url);
3501
3507
  var MAX_SOCKET_PATH_LENGTH = 100;
3508
+ var CODEX_LSP_DAEMON_VERSION_ENV = "CODEX_LSP_DAEMON_VERSION";
3502
3509
  function resolveDaemonVersion(requireFn = requireFromHere) {
3503
3510
  for (const candidate of ["./package.json", "../package.json"]) {
3504
3511
  try {
@@ -3520,7 +3527,7 @@ function daemonBaseDir(env = process.env) {
3520
3527
  const home = codexHome && codexHome.length > 0 ? codexHome : join8(homedir3(), ".codex");
3521
3528
  return join8(home, "codex-lsp", "daemon");
3522
3529
  }
3523
- function daemonPaths(env = process.env, version = resolveDaemonVersion()) {
3530
+ function daemonPaths(env = process.env, version = resolveDaemonVersionFromEnv(env) ?? resolveDaemonVersion()) {
3524
3531
  const dir = join8(daemonBaseDir(env), `v${version}`);
3525
3532
  return {
3526
3533
  version,
@@ -3531,6 +3538,10 @@ function daemonPaths(env = process.env, version = resolveDaemonVersion()) {
3531
3538
  log: join8(dir, "daemon.log")
3532
3539
  };
3533
3540
  }
3541
+ function resolveDaemonVersionFromEnv(env = process.env) {
3542
+ const version = env[CODEX_LSP_DAEMON_VERSION_ENV]?.trim();
3543
+ return version && version.length > 0 ? version : null;
3544
+ }
3534
3545
  function resolveSocketPath(dir, version) {
3535
3546
  const digest = createHash("sha256").update(dir).digest("hex").slice(0, 16);
3536
3547
  if (process.platform === "win32") {