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.
- package/.agents/skills/codex-qa/references/isolation.md +1 -1
- package/README.md +1 -1
- package/dist/cli/doctor/framework/types.d.ts +2 -0
- package/dist/cli/index.js +223 -1447
- package/dist/cli-node/index.js +223 -1447
- package/dist/index.js +4255 -714
- package/dist/plugin/native-skills.d.ts +9 -0
- package/dist/plugin/system-transform.d.ts +1 -1
- package/dist/skills/frontend/ATTRIBUTION.md +7 -1
- package/dist/skills/frontend/SKILL.md +4 -3
- package/dist/skills/frontend/references/design/README.md +4 -3
- package/dist/skills/frontend/references/design/_INDEX.md +5 -3
- package/dist/skills/frontend/references/design/aside.md +209 -0
- package/dist/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/dist/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/dist/skills/lcx-doctor/SKILL.md +48 -13
- package/dist/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/dist/skills/lcx-report-bug/SKILL.md +44 -13
- package/dist/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/dist/skills/visual-qa/SKILL.md +5 -5
- package/dist/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/dist/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/dist/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/tools/delegate-task/skill-resolver.d.ts +1 -0
- package/dist/tools/delegate-task/types.d.ts +2 -0
- package/dist/tools/skill/types.d.ts +1 -0
- package/package.json +26 -13
- package/packages/git-bash-mcp/dist/cli.js +2 -0
- package/packages/lsp-daemon/dist/cli.js +15 -4
- package/packages/lsp-daemon/dist/ensure-daemon.d.ts +1 -0
- package/packages/lsp-daemon/dist/ensure-daemon.js +9 -3
- package/packages/lsp-daemon/dist/index.js +15 -4
- package/packages/lsp-daemon/dist/paths.d.ts +1 -0
- package/packages/lsp-daemon/dist/paths.js +6 -1
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +4 -7
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/src/setup.ts +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +123 -10
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/src/mcp-bridge.ts +126 -9
- package/packages/omo-codex/plugin/components/codegraph/test/serve-mcp-bridge.test.ts +141 -0
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/dist/cli.js +90 -25
- package/packages/omo-codex/plugin/components/lsp/dist/codex-hook.js +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.d.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/dist/daemon-cli-path.js +56 -0
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/lsp/src/cli.ts +2 -5
- package/packages/omo-codex/plugin/components/lsp/src/codex-hook.ts +2 -0
- package/packages/omo-codex/plugin/components/lsp/src/daemon-cli-path.ts +65 -0
- package/packages/omo-codex/plugin/components/lsp/test/package-smoke.test.ts +6 -1
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +8 -106
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +4 -17
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/explorer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +7 -33
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +4 -12
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/sample-quality-gate.json +7 -7
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-doc.test.ts +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +7 -6
- package/packages/omo-codex/plugin/package-lock.json +16 -56
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/frontend/ATTRIBUTION.md +7 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/README.md +4 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/omo-codex/plugin/skills/frontend/references/design/aside.md +209 -0
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/omo-codex/plugin/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/omo-codex/plugin/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +17 -5
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +63 -1
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +80 -50
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +4 -4
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +5 -5
- package/packages/omo-codex/plugin/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/omo-codex/plugin/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +23 -0
- package/packages/omo-codex/plugin/test/bootstrap-binlinks.test.mjs +38 -2
- package/packages/omo-codex/plugin/test/component-bin-names.test.mjs +12 -11
- package/packages/omo-codex/plugin/test/component-bundled-cli.test.mjs +99 -1
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +62 -8
- package/packages/omo-codex/plugin/test/node-install-surface.test.mjs +1 -0
- package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +102 -0
- package/packages/omo-codex/plugin/test/teammode-safety-fixture.mjs +41 -1
- package/packages/omo-codex/plugin/test/teammode-safety.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-bin-links.test.mjs +6 -0
- package/packages/omo-codex/scripts/install-cache-copy.test.mjs +28 -0
- package/packages/omo-codex/scripts/install-config-autonomous-features.test.mjs +43 -3
- package/packages/omo-codex/scripts/install-delegated-command.test.mjs +93 -0
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +169 -41
- package/packages/omo-codex/scripts/install-local-entrypoint.test.mjs +21 -2
- package/packages/omo-codex/scripts/install-local.test.mjs +0 -1
- package/packages/shared-skills/skills/frontend/ATTRIBUTION.md +7 -1
- package/packages/shared-skills/skills/frontend/SKILL.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/README.md +4 -3
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +5 -3
- package/packages/shared-skills/skills/frontend/references/design/aside.md +209 -0
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-contribute-bug-fix/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-doctor/SKILL.md +48 -13
- package/packages/shared-skills/skills/lcx-doctor/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/lcx-report-bug/SKILL.md +44 -13
- package/packages/shared-skills/skills/lcx-report-bug/agents/openai.yaml +1 -1
- package/packages/shared-skills/skills/visual-qa/SKILL.md +5 -5
- package/packages/shared-skills/skills/visual-qa/references/agent-browser-setup.md +5 -4
- package/packages/shared-skills/skills/visual-qa/scripts/cli.test.ts +7 -2
- package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +10 -2
- package/packages/shared-skills/skills/visual-qa/scripts/visual-qa.mjs +530 -0
- package/dist/cli/sparkshell-appserver-websocket.d.ts +0 -4
- package/dist/cli/sparkshell-appserver.d.ts +0 -16
- package/dist/cli/sparkshell-condense.d.ts +0 -10
- package/dist/cli/sparkshell-parse.d.ts +0 -24
- package/dist/cli/sparkshell-session-context.d.ts +0 -20
- package/dist/cli/sparkshell-spark.d.ts +0 -23
- package/dist/cli/sparkshell.d.ts +0 -33
- package/dist/shared/sparkshell-awareness.d.ts +0 -5
- package/packages/omo-codex/plugin/components/rules/src/sparkshell-awareness.ts +0 -106
- package/packages/omo-codex/plugin/components/rules/test/sparkshell-awareness.test.ts +0 -355
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
setMemberStatus,
|
|
36
36
|
setMemberWorktree,
|
|
37
37
|
teamExists,
|
|
38
|
+
withTeamLock,
|
|
38
39
|
writeGuideAtomic,
|
|
39
40
|
writeTeamAtomic,
|
|
40
41
|
} from "./team-state.mjs";
|
|
@@ -82,57 +83,71 @@ async function persist(team, dir) {
|
|
|
82
83
|
await writeGuideAtomic(team, buildGuide(team), dir);
|
|
83
84
|
}
|
|
84
85
|
|
|
86
|
+
async function mutateTeam(cwd, sessionId, command, fn) {
|
|
87
|
+
const dir = await assertSafeTeamDir(cwd, sessionId);
|
|
88
|
+
return withTeamLock(dir, command, async () => {
|
|
89
|
+
const team = await readTeam(dir);
|
|
90
|
+
return fn(team, dir);
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
|
|
85
94
|
const handlers = {
|
|
86
95
|
async init(cwd, flags) {
|
|
87
96
|
const teamName = requireFlag(flags, "name");
|
|
88
97
|
const sessionName = requireFlag(flags, "session-name");
|
|
89
98
|
const sessionId = typeof flags.session === "string" ? flags.session : `team-${randomUUID().slice(0, 8)}`;
|
|
90
99
|
const dir = await ensureTeamDir(cwd, sessionId);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
100
|
+
await withTeamLock(dir, "init", async () => {
|
|
101
|
+
if (await teamExists(dir)) {
|
|
102
|
+
process.stdout.write(`exists: ${dir} (left untouched; re-init is a safe no-op)\n`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
const team = buildTeam({
|
|
106
|
+
teamName,
|
|
107
|
+
sessionName,
|
|
108
|
+
sessionId,
|
|
109
|
+
dir,
|
|
110
|
+
worktreeEnabled: flags.worktree === true,
|
|
111
|
+
baseBranch: typeof flags["base-branch"] === "string" ? flags["base-branch"] : "dev",
|
|
112
|
+
});
|
|
113
|
+
await persist(team, dir);
|
|
114
|
+
process.stdout.write(`created: ${dir}\n`);
|
|
115
|
+
process.stdout.write(`team.json + guide.md written; artifacts/ ready. session id: ${sessionId}\n`);
|
|
116
|
+
process.stdout.write(`next: add-member --team ${sessionId} --id A --name "<short role>" --focus "<part/ownership/perspective>" --lens area|ownership|perspective --deliverable "<...>"\n`);
|
|
102
117
|
});
|
|
103
|
-
await persist(team, dir);
|
|
104
|
-
process.stdout.write(`created: ${dir}\n`);
|
|
105
|
-
process.stdout.write(`team.json + guide.md written; artifacts/ ready. session id: ${sessionId}\n`);
|
|
106
|
-
process.stdout.write(`next: add-member --team ${sessionId} --id A --name "<short role>" --focus "<part/ownership/perspective>" --lens area|ownership|perspective --deliverable "<...>"\n`);
|
|
107
118
|
},
|
|
108
119
|
|
|
109
120
|
async "add-member"(cwd, flags) {
|
|
110
121
|
const sessionId = requireFlag(flags, "team");
|
|
111
|
-
const { dir, team } = await loadTeam(cwd, sessionId);
|
|
112
122
|
const memberId = requireFlag(flags, "id").trim();
|
|
113
|
-
|
|
123
|
+
const memberInput = {
|
|
114
124
|
id: memberId,
|
|
115
125
|
name: typeof flags.name === "string" ? flags.name : null,
|
|
116
126
|
focus: requireFlag(flags, "focus"),
|
|
117
127
|
lens: requireFlag(flags, "lens"),
|
|
118
128
|
deliverable: typeof flags.deliverable === "string" ? flags.deliverable : "",
|
|
119
129
|
branch: typeof flags.branch === "string" ? flags.branch : null,
|
|
130
|
+
};
|
|
131
|
+
const { team, member } = await mutateTeam(cwd, sessionId, "add-member", async (team, dir) => {
|
|
132
|
+
addMember(team, memberInput);
|
|
133
|
+
await persist(team, dir);
|
|
134
|
+
return { team, member: team.members.find((m) => m.id === memberId) };
|
|
120
135
|
});
|
|
121
|
-
await persist(team, dir);
|
|
122
|
-
const member = team.members.find((m) => m.id === memberId);
|
|
123
136
|
process.stdout.write(`added member ${memberId} to team ${sessionId}.\n\nSend this as the new thread's first message (title the thread "${member.threadTitle}"):\n---\n${buildMemberPrompt(team, memberId)}\n---\n`);
|
|
124
137
|
},
|
|
125
138
|
|
|
126
139
|
async "bind-thread"(cwd, flags) {
|
|
127
140
|
const sessionId = requireFlag(flags, "team");
|
|
128
|
-
const
|
|
129
|
-
bindThread(team, {
|
|
141
|
+
const input = {
|
|
130
142
|
id: requireFlag(flags, "id"),
|
|
131
143
|
threadId: requireFlag(flags, "thread"),
|
|
132
144
|
cwd: typeof flags.cwd === "string" ? flags.cwd : null,
|
|
133
145
|
worktreePath: typeof flags["worktree-path"] === "string" ? flags["worktree-path"] : null,
|
|
146
|
+
};
|
|
147
|
+
await mutateTeam(cwd, sessionId, "bind-thread", async (team, dir) => {
|
|
148
|
+
bindThread(team, input);
|
|
149
|
+
await persist(team, dir);
|
|
134
150
|
});
|
|
135
|
-
await persist(team, dir);
|
|
136
151
|
process.stdout.write(`bound member ${flags.id} to thread ${flags.thread}.\n`);
|
|
137
152
|
},
|
|
138
153
|
|
|
@@ -144,25 +159,29 @@ const handlers = {
|
|
|
144
159
|
|
|
145
160
|
async "set-status"(cwd, flags) {
|
|
146
161
|
const sessionId = requireFlag(flags, "team");
|
|
147
|
-
const
|
|
148
|
-
setMemberStatus(team, {
|
|
162
|
+
const input = {
|
|
149
163
|
id: requireFlag(flags, "id"),
|
|
150
164
|
status: requireFlag(flags, "status"),
|
|
151
165
|
note: typeof flags.note === "string" ? flags.note : "",
|
|
166
|
+
};
|
|
167
|
+
await mutateTeam(cwd, sessionId, "set-status", async (team, dir) => {
|
|
168
|
+
setMemberStatus(team, input);
|
|
169
|
+
await persist(team, dir);
|
|
152
170
|
});
|
|
153
|
-
await persist(team, dir);
|
|
154
171
|
process.stdout.write(`member ${flags.id} -> ${flags.status}\n`);
|
|
155
172
|
},
|
|
156
173
|
|
|
157
174
|
async "worktree-add"(cwd, flags) {
|
|
158
175
|
const sessionId = requireFlag(flags, "team");
|
|
159
|
-
const
|
|
160
|
-
const
|
|
161
|
-
const result =
|
|
162
|
-
|
|
176
|
+
const memberId = requireFlag(flags, "id");
|
|
177
|
+
const baseBranch = typeof flags["base-branch"] === "string" ? flags["base-branch"] : null;
|
|
178
|
+
const { member, result } = await mutateTeam(cwd, sessionId, "worktree-add", async (team, dir) => {
|
|
179
|
+
const member = memberOrThrow(team, memberId);
|
|
180
|
+
const result = addMemberWorktree(cwd, team, member, { baseBranch });
|
|
181
|
+
setMemberWorktree(team, { id: member.id, path: result.path, branch: result.branch });
|
|
182
|
+
await persist(team, dir);
|
|
183
|
+
return { member, result };
|
|
163
184
|
});
|
|
164
|
-
setMemberWorktree(team, { id: member.id, path: result.path, branch: result.branch });
|
|
165
|
-
await persist(team, dir);
|
|
166
185
|
const note = result.created ? "" : " (already exists)";
|
|
167
186
|
const thread = member.threadId
|
|
168
187
|
? `\nMember thread: ${codexThreadLink(member.threadId)}\nTell that member to: cd "${result.path}"`
|
|
@@ -174,11 +193,15 @@ const handlers = {
|
|
|
174
193
|
|
|
175
194
|
async "worktree-remove"(cwd, flags) {
|
|
176
195
|
const sessionId = requireFlag(flags, "team");
|
|
177
|
-
const
|
|
178
|
-
const
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
196
|
+
const memberId = requireFlag(flags, "id");
|
|
197
|
+
const force = flags.force === true;
|
|
198
|
+
const member = await mutateTeam(cwd, sessionId, "worktree-remove", async (team, dir) => {
|
|
199
|
+
const member = memberOrThrow(team, memberId);
|
|
200
|
+
removeMemberWorktree(cwd, team, member, { force });
|
|
201
|
+
clearMemberWorktree(team, { id: member.id });
|
|
202
|
+
await persist(team, dir);
|
|
203
|
+
return member;
|
|
204
|
+
});
|
|
182
205
|
process.stdout.write(`removed worktree for member ${member.id}\n`);
|
|
183
206
|
},
|
|
184
207
|
|
|
@@ -208,25 +231,30 @@ const handlers = {
|
|
|
208
231
|
|
|
209
232
|
async archive(cwd, flags) {
|
|
210
233
|
const sessionId = requireFlag(flags, "team");
|
|
211
|
-
const
|
|
212
|
-
archive(team, {
|
|
234
|
+
const input = {
|
|
213
235
|
id: typeof flags.id === "string" ? flags.id : null,
|
|
214
236
|
note: typeof flags.note === "string" ? flags.note : "",
|
|
237
|
+
};
|
|
238
|
+
await mutateTeam(cwd, sessionId, "archive", async (team, dir) => {
|
|
239
|
+
archive(team, input);
|
|
240
|
+
await persist(team, dir);
|
|
215
241
|
});
|
|
216
|
-
await persist(team, dir);
|
|
217
242
|
process.stdout.write(flags.id ? `archived member ${flags.id}\n` : `archived team ${sessionId} and closed all members\n`);
|
|
218
243
|
},
|
|
219
244
|
|
|
220
245
|
async delete(cwd, flags) {
|
|
221
246
|
const sessionId = requireFlag(flags, "team");
|
|
222
|
-
const
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
247
|
+
const dir = await assertSafeTeamDir(cwd, sessionId);
|
|
248
|
+
await withTeamLock(dir, "delete", async () => {
|
|
249
|
+
const team = await readTeam(dir);
|
|
250
|
+
const active = team.members.filter((m) => m.status !== "archived");
|
|
251
|
+
if (flags.force !== true && (team.status !== "archived" || active.length > 0)) {
|
|
252
|
+
throw new Error(
|
|
253
|
+
`refused: team "${sessionId}" is not archived or still has ${active.length} active member(s). Archive first, or pass --force to delete anyway.`,
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
await rm(dir, { recursive: true, force: true });
|
|
257
|
+
});
|
|
230
258
|
process.stdout.write(`deleted team state: ${dir}\n`);
|
|
231
259
|
},
|
|
232
260
|
|
|
@@ -247,9 +275,11 @@ const handlers = {
|
|
|
247
275
|
|
|
248
276
|
async guide(cwd, flags) {
|
|
249
277
|
const sessionId = requireFlag(flags, "team");
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
|
|
278
|
+
const guidePath = await mutateTeam(cwd, sessionId, "guide", async (team, dir) => {
|
|
279
|
+
await writeGuideAtomic(team, buildGuide(team), dir);
|
|
280
|
+
return team.paths.guide;
|
|
281
|
+
});
|
|
282
|
+
process.stdout.write(`${guidePath}\n`);
|
|
253
283
|
},
|
|
254
284
|
};
|
|
255
285
|
|
|
@@ -20,7 +20,7 @@ This skill is intentionally compact. The full workflow lives in `references/full
|
|
|
20
20
|
## Non-Negotiables
|
|
21
21
|
|
|
22
22
|
- Use the ulw-loop CLI state under `.omo/ulw-loop`; do not hand-edit goal state.
|
|
23
|
-
- After any compaction or context loss, re-read brief + goals + ledger FIRST
|
|
23
|
+
- After any compaction or context loss, re-read brief + goals + ledger FIRST plus `omo ulw-loop status --json`, then resume; never re-plan from scratch.
|
|
24
24
|
- If `omo ulw-loop create-goals` says the existing aggregate is already complete, start unrelated new work with a fresh `--session-id <new-id>` instead of steering or forcing the completed default state. Use `--force` only to intentionally overwrite completed evidence.
|
|
25
25
|
- Every success criterion needs observable evidence from a real surface: a channel (tmux, HTTP, browser, computer-use) or, for CLI- or data-shaped criteria, an auxiliary surface (CLI stdout, DB diff, parsed config dump).
|
|
26
26
|
- Record evidence through the CLI only after cleanup receipts are available.
|
|
@@ -65,7 +65,7 @@ Codex subagent reliability:
|
|
|
65
65
|
- `.omo/ulw-loop/goals.json`: goals with embedded `successCriteria` per goal.
|
|
66
66
|
- `.omo/ulw-loop/ledger.jsonl`: append-only audit trail.
|
|
67
67
|
- Read artifacts before resuming, steering, or checkpointing.
|
|
68
|
-
- After compaction or context loss, re-read brief + goals + ledger FIRST
|
|
68
|
+
- After compaction or context loss, re-read brief + goals + ledger FIRST, then `omo ulw-loop status --json`. Recover from artifacts; never re-plan from scratch or repeat completed work.
|
|
69
69
|
- Never invent state outside `.omo/ulw-loop` artifacts or `omo ulw-loop status --json`.
|
|
70
70
|
|
|
71
71
|
## Bootstrap
|
|
@@ -194,9 +194,9 @@ omo ulw-loop checkpoint --goal-id <id> --status complete --evidence "<e2e eviden
|
|
|
194
194
|
`--quality-gate-json` shape:
|
|
195
195
|
```json
|
|
196
196
|
{
|
|
197
|
-
"codeReview":{"by":"lazycodex-code-reviewer","recommendation":"APPROVE","codeQualityStatus":"CLEAR","reportPath":"
|
|
198
|
-
"manualQa":{"by":"lazycodex-qa-executor","status":"passed","evidence":"CLI and data surfaces passed.","surfaceEvidence":[{"id":"surface-cli-pass","criterionRef":"C1","surface":"cli","invocation":"omo ulw-loop checkpoint --quality-gate-json sample-quality-gate.json --json","verdict":"passed","artifactRefs":["artifact-cli-pass"]},{"id":"surface-data-pass","criterionRef":"C2","surface":"data","invocation":"diff -u before-ledger.json after-ledger.json","verdict":"passed","artifactRefs":["artifact-data-diff"]}],"adversarialCases":[{"id":"adv-malformed-input","criterionRef":"C3","scenario":"malformed gate input omits manual QA evidence","expectedBehavior":"validator rejects ULW_LOOP_QUALITY_GATE_INVALID","verdict":"passed","artifactRefs":["artifact-cli-reject"]}],"artifactRefs":[{"id":"artifact-cli-pass","kind":"cli-transcript","description":"CLI pass artifact.","path":"
|
|
199
|
-
"gateReview":{"by":"lazycodex-gate-reviewer","recommendation":"APPROVE","reportPath":"
|
|
197
|
+
"codeReview":{"by":"lazycodex-code-reviewer","recommendation":"APPROVE","codeQualityStatus":"CLEAR","reportPath":"test/fixtures/artifacts/code-review.md","evidence":"Diff review passed.","blockers":[]},
|
|
198
|
+
"manualQa":{"by":"lazycodex-qa-executor","status":"passed","evidence":"CLI and data surfaces passed.","surfaceEvidence":[{"id":"surface-cli-pass","criterionRef":"C1","surface":"cli","invocation":"omo ulw-loop checkpoint --quality-gate-json sample-quality-gate.json --json","verdict":"passed","artifactRefs":["artifact-cli-pass"]},{"id":"surface-data-pass","criterionRef":"C2","surface":"data","invocation":"diff -u before-ledger.json after-ledger.json","verdict":"passed","artifactRefs":["artifact-data-diff"]}],"adversarialCases":[{"id":"adv-malformed-input","criterionRef":"C3","scenario":"malformed gate input omits manual QA evidence","expectedBehavior":"validator rejects ULW_LOOP_QUALITY_GATE_INVALID","verdict":"passed","artifactRefs":["artifact-cli-reject"]}],"artifactRefs":[{"id":"artifact-cli-pass","kind":"cli-transcript","description":"CLI pass artifact.","path":"test/fixtures/artifacts/cli-pass.txt"},{"id":"artifact-cli-reject","kind":"log","description":"Reject log artifact.","path":"test/fixtures/artifacts/rejection.txt"},{"id":"artifact-data-diff","kind":"data-diff","description":"Data diff artifact.","path":"test/fixtures/artifacts/data-diff.txt"}]},
|
|
199
|
+
"gateReview":{"by":"lazycodex-gate-reviewer","recommendation":"APPROVE","reportPath":"test/fixtures/artifacts/gate-review.md","evidence":"Gate review passed.","blockers":[]},
|
|
200
200
|
"iteration":{"fullRerun":true,"status":"passed","rerunCommands":["bunx vitest run packages/omo-codex/plugin/components/ulw-loop/test/quality-gate-doc.test.ts"],"evidence":"Focused rerun passed."},
|
|
201
201
|
"criteriaCoverage":{"totalCriteria":3,"passCount":3,"originalIntent":"User wanted artifact-backed completion.","desiredOutcome":"Behavior ships with review and QA evidence.","userOutcomeReview":"Result matches brief and goals.","adversarialClassesCovered":["malformed_input","stale_state"]}
|
|
202
202
|
}
|
|
@@ -35,7 +35,7 @@ Verify a rendered UI against intent using objective script evidence plus two par
|
|
|
35
35
|
- Use when output must match a mock, a baseline, or a stated design intent; when you suspect a regression; when CJK (Korean/Japanese/Chinese) text may clip, misalign, or wrap awkwardly; when a claimed design system might actually be a flat image; when a terminal layout may overflow or its borders may break.
|
|
36
36
|
- Skip when there is no rendered surface (pure backend or library logic with no visual or terminal output). For broad post-implementation review use review-work; this skill is the visual specialist.
|
|
37
37
|
|
|
38
|
-
In the commands below, `$SKILL_DIR` is this skill's own directory (the folder containing this SKILL.md). The bundled
|
|
38
|
+
In the commands below, `$SKILL_DIR` is this skill's own directory (the folder containing this SKILL.md). The bundled Node evidence CLI lives at `scripts/visual-qa.mjs` inside it; the TypeScript source in `scripts/cli.ts` is for development.
|
|
39
39
|
|
|
40
40
|
## Step 1 - Detect the surface
|
|
41
41
|
|
|
@@ -66,11 +66,11 @@ Every gate runs on captures produced AFTER the last edit to the rendered source.
|
|
|
66
66
|
### Web
|
|
67
67
|
|
|
68
68
|
1. Capture a REFERENCE image: the user's mock/target, generated page snapshot, Figma export, source-site capture, or known-good baseline. Save as PNG. If the user provided overview text or annotations, save them next to the image and treat them as part of the reference packet.
|
|
69
|
-
2. Capture the ACTUAL rendered screenshot at the same viewport size. In Codex, when `browser:control-in-app-browser` is available and the page does not need an authenticated user browser session, use that Browser plugin first for navigation, page state inspection, and screenshots. If it is unavailable or lacks the needed capture action, use the project's configured browser tooling (the playwright, agent-browser, or dev-browser skill). Save as PNG. If none is configured or available, install [agent-browser](https://github.com/vercel-labs/agent-browser) (`
|
|
69
|
+
2. Capture the ACTUAL rendered screenshot at the same viewport size. In Codex, when `browser:control-in-app-browser` is available and the page does not need an authenticated user browser session, use that Browser plugin first for navigation, page state inspection, and screenshots. If it is unavailable or lacks the needed capture action, use the project's configured browser tooling (the playwright, agent-browser, or dev-browser skill). Save as PNG. If none is configured or available, install [agent-browser](https://github.com/vercel-labs/agent-browser) (`npm install -g agent-browser && agent-browser install`) and capture with it — see `$SKILL_DIR/references/agent-browser-setup.md` for the full setup, including how to shoot a fixed-viewport screenshot.
|
|
70
70
|
3. Run the diff and keep the JSON:
|
|
71
71
|
|
|
72
72
|
```
|
|
73
|
-
|
|
73
|
+
node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff <reference.png> <actual.png>
|
|
74
74
|
```
|
|
75
75
|
|
|
76
76
|
Key fields: `dimensionsMatch`, `diffRatio` (0..1), `similarityScore` (0..100), `alphaChannelIntact`, `hotspots[]` (grid regions ranked by `diffRatio`).
|
|
@@ -104,7 +104,7 @@ metadata with cleanup receipt.
|
|
|
104
104
|
3. Run the check with the REAL terminal width and keep the JSON:
|
|
105
105
|
|
|
106
106
|
```
|
|
107
|
-
|
|
107
|
+
node "$SKILL_DIR/scripts/visual-qa.mjs" tui-check capture.txt --cols <N>
|
|
108
108
|
```
|
|
109
109
|
|
|
110
110
|
Key fields: `maxWidth`, `overflowLines[]`, `borderMisaligned`, `wideCharColumns[]`, `hasAnsi`.
|
|
@@ -267,7 +267,7 @@ Run this step IN ADDITION to Steps 1-4 when the original user task has a concret
|
|
|
267
267
|
1. Pixel-perfect design-compare subagent (visual oracle). Dispatch a focused, read-only design-compare reviewer (recommend `gpt-5.5` with medium reasoning). It must crop/zoom BOTH the reference (target / Figma export / source-site screenshot / generated page snapshot) and the ACTUAL screenshot into matching regions and read them **pixel-by-pixel** - header, nav, each card, spacing, type ramp, color tokens - not at a glance. It must also compare the overview text or annotations against the rendered content and DOM text. Anchor every claim with the bundled tool:
|
|
268
268
|
|
|
269
269
|
```
|
|
270
|
-
|
|
270
|
+
node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff <reference.png> <actual.png>
|
|
271
271
|
```
|
|
272
272
|
|
|
273
273
|
It judges whether layout geometry, spacing, design tokens (color, type, radius, shadow), and the design itself are identical to the target, region by region. Anything off by more than rounding is a finding.
|
|
@@ -11,11 +11,12 @@ Repo: https://github.com/vercel-labs/agent-browser
|
|
|
11
11
|
## Install
|
|
12
12
|
|
|
13
13
|
```
|
|
14
|
-
|
|
14
|
+
npm install -g agent-browser && agent-browser install
|
|
15
15
|
```
|
|
16
16
|
|
|
17
|
-
`
|
|
18
|
-
managed browser it drives.
|
|
17
|
+
`npm install -g agent-browser` installs the CLI globally; `agent-browser install` downloads the
|
|
18
|
+
managed browser it drives. If Bun is already available, `bun add -g agent-browser && agent-browser install`
|
|
19
|
+
is equivalent, but npm is the default because LazyCodex/Codex installations do not require Bun.
|
|
19
20
|
|
|
20
21
|
Confirm it is ready and discover the current flags:
|
|
21
22
|
|
|
@@ -36,7 +37,7 @@ agent-browser screenshot actual.png # add --full for full-page, --screensho
|
|
|
36
37
|
Then feed the PNG into the diff exactly as in Step 2:
|
|
37
38
|
|
|
38
39
|
```
|
|
39
|
-
|
|
40
|
+
node "$SKILL_DIR/scripts/visual-qa.mjs" image-diff <reference.png> actual.png
|
|
40
41
|
```
|
|
41
42
|
|
|
42
43
|
Match the viewport numbers to whatever the reference/mock was captured at; mismatched
|