oh-my-opencode 5.0.0-beta.7 → 5.0.0-beta.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/command/publish.md +44 -16
- package/.agents/skills/publish/SKILL.md +44 -16
- package/.agents/skills/work-with-pr/SKILL.md +37 -23
- package/.opencode/command/publish.md +44 -16
- package/.opencode/skills/work-with-pr/SKILL.md +37 -23
- package/dist/agents/atlas/agent.d.ts +0 -1
- package/dist/agents/sisyphus/grok-4.d.ts +20 -0
- package/dist/agents/sisyphus/index.d.ts +2 -0
- package/dist/agents/sisyphus-agent-config.d.ts +6 -0
- package/dist/agents/sisyphus-agent-factory.d.ts +1 -1
- package/dist/agents/types.d.ts +2 -2
- package/dist/cli/index.js +465 -352
- package/dist/cli/run/on-complete-hook.d.ts +2 -0
- package/dist/cli-node/index.js +465 -352
- package/dist/hooks/atlas/final-wave-approval-gate.test-support.d.ts +50 -0
- package/dist/hooks/atlas/system-reminder-templates.d.ts +0 -1
- package/dist/index.js +283 -38
- package/dist/shared/normalize-sdk-response.d.ts +1 -0
- package/dist/shared/shell-env.d.ts +1 -1
- package/dist/skills/frontend/SKILL.md +10 -7
- package/dist/skills/frontend/references/design/_INDEX.md +1 -0
- package/dist/skills/frontend/references/design/stylegallery.md +80 -0
- package/dist/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
- package/dist/skills/ultimate-browsing/engine/AGENTS.md +110 -0
- package/dist/skills/ultimate-browsing/engine/templates/package.json +1 -1
- package/dist/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
- package/dist/skills/ulw-plan/SKILL.md +2 -2
- package/dist/skills/ulw-plan/references/full-workflow.md +27 -3
- package/dist/skills/ulw-plan/references/intent-clear.md +2 -1
- package/dist/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/dist/tui.js +77 -9
- package/package.json +20 -19
- package/packages/lsp-core/src/lsp/client-diagnostics-concurrency.integration.test.ts +44 -0
- package/packages/lsp-core/src/lsp/client-diagnostics-freshness.integration.test.ts +0 -28
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/bootstrap/package.json +1 -1
- package/packages/omo-codex/plugin/components/codegraph/dist/cli.js +26 -4
- package/packages/omo-codex/plugin/components/codegraph/dist/serve.js +26 -4
- package/packages/omo-codex/plugin/components/codegraph/package.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/comment-checker/package.json +1 -1
- package/packages/omo-codex/plugin/components/git-bash/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/git-bash/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +3 -17
- package/packages/omo-codex/plugin/components/lsp/dist/.omo-runtime-manifest.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lsp/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/rules/package.json +1 -1
- package/packages/omo-codex/plugin/components/rules/test/bundled-rules-priority.test.ts +11 -16
- package/packages/omo-codex/plugin/components/rules/test/bundled-rules.test.ts +16 -23
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-budget.test.ts +9 -7
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-context.test.ts +0 -6
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-dedup.test.ts +6 -4
- package/packages/omo-codex/plugin/components/rules/test/codex-hook-post-compact-directive.test.ts +12 -9
- package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +28 -37
- package/packages/omo-codex/plugin/components/rules/test/formatter.test.ts +37 -69
- package/packages/omo-codex/plugin/components/rules/test/hook-output.test.ts +2 -3
- package/packages/omo-codex/plugin/components/rules/test/windows-git-bash-bundled-rule.test.ts +1 -15
- package/packages/omo-codex/plugin/components/start-work-continuation/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/start-work-continuation/package.json +1 -1
- package/packages/omo-codex/plugin/components/start-work-continuation/test/cli.test.ts +0 -3
- package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts +2 -16
- package/packages/omo-codex/plugin/components/teammode/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/package.json +1 -1
- package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +3 -9
- package/packages/omo-codex/plugin/components/telemetry/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/telemetry/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/hooks/hooks.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/package.json +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/SKILL.md +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +27 -3
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-clear.md +2 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +0 -136
- package/packages/omo-codex/plugin/components/ultrawork/test/skill-pointer.test.ts +0 -2
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +4 -4
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/define-goal.md +5 -3
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/checkpoint-continuation.test.ts +0 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +2 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-hook.test.ts +0 -3
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +2 -35
- package/packages/omo-codex/plugin/components/ulw-loop/test/ultrawork-directive.test.ts +4 -5
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-git-bash-mcp-reminder.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-lsp-diagnostics-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-compact-resetting-project-rule-cache.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-codegraph-init-guidance.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-comments.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-lsp-diagnostics.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-checking-thread-title-hygiene.json +1 -1
- package/packages/omo-codex/plugin/hooks/post-tool-use-matching-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-enforcing-unlimited-goal-budget.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-guarding-ulw-loop-spawns.json +1 -1
- package/packages/omo-codex/plugin/hooks/pre-tool-use-recommending-git-bash-mcp.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-auto-update.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-bootstrap-provisioning.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-checking-codegraph-bootstrap.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/hooks/session-start-recording-session-telemetry.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/stop-checking-ulw-loop-resume.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-checking-start-work-continuation.json +1 -1
- package/packages/omo-codex/plugin/hooks/subagent-stop-verifying-lazycodex-executor-evidence.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ultrawork-trigger.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-checking-ulw-loop-steering.json +1 -1
- package/packages/omo-codex/plugin/hooks/user-prompt-submit-loading-project-rules.json +1 -1
- package/packages/omo-codex/plugin/package-lock.json +20 -20
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/skills/frontend/SKILL.md +10 -7
- package/packages/omo-codex/plugin/skills/frontend/references/design/_INDEX.md +1 -0
- package/packages/omo-codex/plugin/skills/frontend/references/design/stylegallery.md +80 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/AGENTS.md +110 -0
- package/packages/omo-codex/plugin/skills/ultimate-browsing/engine/templates/package.json +1 -1
- package/packages/omo-codex/plugin/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
- package/packages/omo-codex/plugin/skills/ulw-loop/references/define-goal.md +5 -3
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/SKILL.md +2 -2
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +27 -3
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-clear.md +2 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +19 -173
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +4 -24
- package/packages/omo-codex/plugin/test/aggregate-plugin-fixture.mjs +175 -13
- package/packages/omo-codex/plugin/test/aggregate.test.mjs +78 -2
- package/packages/omo-codex/plugin/test/auto-update-release-notes.test.mjs +19 -33
- package/packages/omo-codex/plugin/test/lcx-contribute-bug-fix-template.test.mjs +21 -27
- package/packages/omo-codex/plugin/test/scaffold-plan.test.mjs +0 -36
- package/packages/omo-codex/plugin/test/sync-skills-codex-compatibility.test.mjs +101 -0
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +1 -119
- package/packages/omo-codex/plugin/test/teammode-archive-ambiguity.test.mjs +0 -40
- package/packages/omo-codex/plugin/test/teammode-communication.test.mjs +6 -62
- package/packages/omo-codex/plugin/test/teammode-thread-links.test.mjs +3 -36
- package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +0 -44
- package/packages/omo-codex/plugin/test/teammode-worktree.test.mjs +2 -6
- package/packages/omo-codex/plugin/test/ultrawork-skill-pointer.test.mjs +0 -3
- package/packages/omo-codex/plugin/test/ulw-plan-review-state-contract.test.mjs +0 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +2 -2
- package/packages/omo-codex/scripts/install-lazycodex-version-stamp.test.mjs +7 -2
- package/packages/shared-skills/skills/frontend/SKILL.md +10 -7
- package/packages/shared-skills/skills/frontend/references/design/_INDEX.md +1 -0
- package/packages/shared-skills/skills/frontend/references/design/stylegallery.md +80 -0
- package/packages/shared-skills/skills/ultimate-browsing/ATTRIBUTION.md +37 -10
- package/packages/shared-skills/skills/ultimate-browsing/engine/AGENTS.md +110 -0
- package/packages/shared-skills/skills/ultimate-browsing/engine/templates/package.json +1 -1
- package/packages/shared-skills/skills/ultimate-browsing/references/chrome-stealth.md +11 -11
- package/packages/shared-skills/skills/ulw-plan/SKILL.md +2 -2
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +27 -3
- package/packages/shared-skills/skills/ulw-plan/references/intent-clear.md +2 -1
- package/packages/shared-skills/skills/ulw-plan/references/intent-unclear.md +3 -3
- package/dist/tools/call-omo-agent/background-agent-executor.d.ts +0 -5
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +0 -92
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +0 -314
- package/packages/omo-codex/plugin/test/ulw-plan-scope-contract.test.mjs +0 -24
|
@@ -5,8 +5,6 @@ import test from "node:test";
|
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
6
6
|
import { sharedSkillsRootPath } from "@oh-my-opencode/shared-skills";
|
|
7
7
|
import {
|
|
8
|
-
CONTEXT_PRESSURE_SKILL_BUDGET_BYTES,
|
|
9
|
-
assertPackagedContentMatches,
|
|
10
8
|
componentSkillSources,
|
|
11
9
|
expectedSkills,
|
|
12
10
|
listSkillFiles,
|
|
@@ -16,7 +14,6 @@ import {
|
|
|
16
14
|
|
|
17
15
|
const root = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
18
16
|
const repoRoot = join(root, "..", "..", "..");
|
|
19
|
-
const opencodeOnlyToolPattern = /\b(?:call_omo_agent|background_output|team_[a-z_]+|task)\s*\(/;
|
|
20
17
|
const generatedSkillMetadataFiles = new Set(["agents/openai.yaml"]);
|
|
21
18
|
|
|
22
19
|
async function readPackagedSkillFile(...segments) {
|
|
@@ -131,7 +128,6 @@ test("#given a shared skill name collides with a Codex component skill #when agg
|
|
|
131
128
|
// when / then
|
|
132
129
|
assert.notEqual(removeCodexCompatibilityGuidance(aggregateSkill), removeCodexCompatibilityGuidance(sharedSkill));
|
|
133
130
|
assert.equal(removeCodexCompatibilityGuidance(aggregateSkill), removeCodexCompatibilityGuidance(componentSkill));
|
|
134
|
-
assert.match(aggregateSkill, /multi_agent_v1/);
|
|
135
131
|
});
|
|
136
132
|
|
|
137
133
|
test("#given shared skill source tests #when aggregate Codex skills are synced #then source tests are not packaged", async () => {
|
|
@@ -191,9 +187,7 @@ test("#given synced ulw-loop skill #when Codex hint metadata is inspected #then
|
|
|
191
187
|
// then
|
|
192
188
|
assert.match(skill, /^---\r?\nname: ulw-loop\r?\n/m);
|
|
193
189
|
assert.match(interfaceMetadata, /display_name: "\(OmO\) ulw-loop"/);
|
|
194
|
-
assert.
|
|
195
|
-
assert.match(interfaceMetadata, /short_description: "Goal-like ultrawork loop for systematic decomposition"/);
|
|
196
|
-
assert.match(interfaceMetadata, /default_prompt: "Use \$ulw-loop/);
|
|
190
|
+
assert.match(interfaceMetadata, /^\s*default_prompt:\s*".+"$/m);
|
|
197
191
|
});
|
|
198
192
|
|
|
199
193
|
test("#given synced ulw-loop skill #when Codex hint metadata is inspected #then ulw-loop remains discoverable as an alias", async () => {
|
|
@@ -229,92 +223,11 @@ test("#given synced git-master skill #when inspected #then commits and git histo
|
|
|
229
223
|
|
|
230
224
|
// then
|
|
231
225
|
assert.match(skill, /^---\r?\nname: git-master\r?\n/m);
|
|
232
|
-
assert.match(skill, /MUST USE whenever a task needs a commit or git-history investigation/);
|
|
233
|
-
assert.match(skill, /Commit only the user's requested changes/);
|
|
234
|
-
assert.match(skill, /Choose the Git tool by the question/);
|
|
235
|
-
assert.match(skill, /git log -S "text"/);
|
|
236
|
-
assert.match(skill, /git blame -L start,end -- file/);
|
|
237
226
|
assert.match(interfaceMetadata, /display_name: "\(OmO\) git-master"/);
|
|
238
227
|
assert.match(interfaceMetadata, /- "git commit"/);
|
|
239
228
|
assert.match(interfaceMetadata, /- "history search"/);
|
|
240
229
|
});
|
|
241
230
|
|
|
242
|
-
test("#given synced ulw-loop skill #when worker guidance is inspected #then context-hygiene guidance matches the source", async () => {
|
|
243
|
-
// given
|
|
244
|
-
const sourceSkill = await readFile(
|
|
245
|
-
join(root, "components", "ulw-loop", "skills", "ulw-loop", "references", "full-workflow.md"),
|
|
246
|
-
"utf8",
|
|
247
|
-
);
|
|
248
|
-
const syncedSkill = await readFile(join(root, "skills", "ulw-loop", "SKILL.md"), "utf8");
|
|
249
|
-
const syncedWorkflow = await readFile(join(root, "skills", "ulw-loop", "references", "full-workflow.md"), "utf8");
|
|
250
|
-
// ulw-loop is V2-primary (gpt-5.6 sol/terra use the flat `wait_agent`); the `multi_agent_v1.*`
|
|
251
|
-
// namespace is documented only as the v1 fallback, so the wait_agent refs accept the bare token.
|
|
252
|
-
const requiredPatterns = [
|
|
253
|
-
["wait_agent ref", /\bwait_agent\b/],
|
|
254
|
-
["local spawned-name tracking", /Track spawned agent names locally/],
|
|
255
|
-
["wait_agent mailbox path", /wait_agent.*mailbox signals/],
|
|
256
|
-
["progress status contract", /WORKING:/],
|
|
257
|
-
["long-running plan/reviewer background guidance", /Plan and reviewer agents may run for a long time/],
|
|
258
|
-
["bounded plan/reviewer polling", /wait_agent.*cycles/],
|
|
259
|
-
["exponential backoff wait guard", /double the timeout up to ~5 minutes/],
|
|
260
|
-
["git-master checkpointing", /git-master/],
|
|
261
|
-
["touched-path commit-style probe", /touched-path commit history/],
|
|
262
|
-
["verified work-unit commit", /verified work unit/],
|
|
263
|
-
["observed commit style", /commit in the observed style/],
|
|
264
|
-
];
|
|
265
|
-
|
|
266
|
-
// when / then
|
|
267
|
-
for (const [label, pattern] of requiredPatterns) {
|
|
268
|
-
assert.match(sourceSkill, pattern, `source skill missing ${label}`);
|
|
269
|
-
assert.match(syncedWorkflow, pattern, `synced workflow missing ${label}`);
|
|
270
|
-
}
|
|
271
|
-
assert.match(syncedSkill, /references\/full-workflow\.md/);
|
|
272
|
-
assert.match(syncedSkill, /wait_agent/);
|
|
273
|
-
assert.match(syncedSkill, /close_agent/);
|
|
274
|
-
});
|
|
275
|
-
|
|
276
|
-
test("#given packaged start-work skill #when inspected #then no-plan bootstrap and adversarial verification contracts are shipped", async () => {
|
|
277
|
-
// given
|
|
278
|
-
const skillFile = await readPackagedSkillFile("start-work", "SKILL.md");
|
|
279
|
-
|
|
280
|
-
// when / then
|
|
281
|
-
assertPackagedContentMatches(skillFile, [
|
|
282
|
-
["executes Prometheus plan with Boulder state", /Prometheus work plan[\s\S]*Boulder state/],
|
|
283
|
-
["bootstraps ulw-plan when no selectable plan exists", /no selectable plan[\s\S]*ulw-plan|ulw-plan[\s\S]*no selectable plan/i],
|
|
284
|
-
["does not execute work without an approved plan", /approved plan[\s\S]*(?:before|prior to)[\s\S]*execution|execution[\s\S]*(?:requires|needs)[\s\S]*approved plan/i],
|
|
285
|
-
["keeps hook continuation Boulder-only", /Boulder[\s\S]*(?:continuation|Stop hook)[\s\S]*(?:only|solely)|(?:continuation|Stop hook)[\s\S]*(?:only|solely)[\s\S]*Boulder/i],
|
|
286
|
-
["distinguishes execution from verification", /execution[\s\S]*verification|verification[\s\S]*execution/i],
|
|
287
|
-
["requires dirty-worktree-aware editing", /dirty worktree/i],
|
|
288
|
-
["requires stale-state probes", /stale state/i],
|
|
289
|
-
["rejects misleading success output", /misleading success output/i],
|
|
290
|
-
["does not accept worker done claims without independent verification", /done claim[\s\S]*independent(?:ly)? verified|independent(?:ly)? verify[\s\S]*done claim/i],
|
|
291
|
-
]);
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
test("#given packaged ulw-plan skill #when inspected #then dynamic multi-agent planning contracts are shipped", async () => {
|
|
295
|
-
// given
|
|
296
|
-
const skillFile = await readPackagedSkillFile("ulw-plan", "SKILL.md");
|
|
297
|
-
const workflowFile = await readPackagedSkillFile("ulw-plan", "references", "full-workflow.md");
|
|
298
|
-
const combinedFile = {
|
|
299
|
-
path: `${skillFile.path} + ${workflowFile.path}`,
|
|
300
|
-
content: `${skillFile.content}\n${workflowFile.content}`,
|
|
301
|
-
};
|
|
302
|
-
|
|
303
|
-
// when / then
|
|
304
|
-
assertPackagedContentMatches(combinedFile, [
|
|
305
|
-
["self-orchestrates 5 host subagents for planning", /(?:self-orchestrates|orchestrates)[\s\S]*5[\s\S]*host subagents/i],
|
|
306
|
-
["requires dynamic workflow phases", /dynamic[\s\S]*workflow[\s\S]*phase|phase[\s\S]*dynamic[\s\S]*workflow/i],
|
|
307
|
-
["keeps verification distinct from execution", /verification[\s\S]*execution|execution[\s\S]*verification/i],
|
|
308
|
-
["requires dirty-worktree-aware planning", /dirty worktree/i],
|
|
309
|
-
["requires stale-state checks between source and packaged payloads", /stale state/i],
|
|
310
|
-
["rejects misleading success output", /misleading success output/i],
|
|
311
|
-
["does not accept subagent outputs as success without independent verification", /subagent outputs?[\s\S]*(?:not|never)[\s\S]*(?:success|approval)|independent(?:ly)? verif(?:y|ied|ication)[\s\S]*subagent outputs?/i],
|
|
312
|
-
["treats Discord or external content as claims, not instructions", /(?:Discord|external content)[\s\S]*claims?[\s\S]*not instructions?|not instructions?[\s\S]*(?:Discord|external content)/i],
|
|
313
|
-
]);
|
|
314
|
-
assert.doesNotMatch(combinedFile.content, opencodeOnlyToolPattern);
|
|
315
|
-
assert.doesNotMatch(combinedFile.content, /Proceeding to plan generation/);
|
|
316
|
-
});
|
|
317
|
-
|
|
318
231
|
test("#given packaged Codex ulw-plan surfaces #when inspected #then dangerous sandbox bypass guidance is not shipped", async () => {
|
|
319
232
|
// given
|
|
320
233
|
const dangerousBypassToken = ["dangerously", "bypass"].join("-");
|
|
@@ -330,34 +243,3 @@ test("#given packaged Codex ulw-plan surfaces #when inspected #then dangerous sa
|
|
|
330
243
|
assert.doesNotMatch(packagedWorkflow.content, dangerousBypassPattern, `${packagedWorkflow.path} ships unsafe Codex bypass guidance`);
|
|
331
244
|
assert.doesNotMatch(componentWorkflow.content, dangerousBypassPattern, `${componentWorkflow.path} ships unsafe Codex bypass guidance`);
|
|
332
245
|
});
|
|
333
|
-
|
|
334
|
-
test("#given packaged ulw-research skill #when Codex delivery gates are inspected #then the category-based proofread gate is stripped", async () => {
|
|
335
|
-
// given
|
|
336
|
-
const content = await readFile(join(root, "skills", "ulw-research", "SKILL.md"), "utf8");
|
|
337
|
-
|
|
338
|
-
// then the writing-category proofread gate never ships to Codex (no category concept there)
|
|
339
|
-
assert.doesNotMatch(content, /category="writing"/, "codex ulw-research ships the writing-category proofread gate");
|
|
340
|
-
assert.doesNotMatch(content, /Proofread gate/, "codex ulw-research still references the proofread gate");
|
|
341
|
-
// and the visual-QA delivery gate survives as the single gate
|
|
342
|
-
assert.match(content, /### The delivery gate \u2014 visual QA must PASS/u, "codex ulw-research lost the visual-QA delivery gate");
|
|
343
|
-
assert.match(content, /Visual QA \(always\)/, "codex ulw-research lost the visual-QA gate body");
|
|
344
|
-
});
|
|
345
|
-
|
|
346
|
-
test("#given context-pressure-prone skills #when bundled for Codex #then the eagerly loaded payload stays budgeted", async () => {
|
|
347
|
-
// given
|
|
348
|
-
const skillsRoot = join(root, "skills");
|
|
349
|
-
const skillNames = ["debugging", "ulw-loop"];
|
|
350
|
-
|
|
351
|
-
// when
|
|
352
|
-
let totalBytes = 0;
|
|
353
|
-
for (const skillName of skillNames) {
|
|
354
|
-
const content = await readFile(join(skillsRoot, skillName, "SKILL.md"), "utf8");
|
|
355
|
-
totalBytes += Buffer.byteLength(content, "utf8");
|
|
356
|
-
}
|
|
357
|
-
|
|
358
|
-
// then
|
|
359
|
-
assert.ok(
|
|
360
|
-
totalBytes <= CONTEXT_PRESSURE_SKILL_BUDGET_BYTES,
|
|
361
|
-
`debugging + ulw-loop eager payload is ${totalBytes} bytes, above ${CONTEXT_PRESSURE_SKILL_BUDGET_BYTES}`,
|
|
362
|
-
);
|
|
363
|
-
});
|
|
@@ -1,48 +1,8 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
3
|
-
import { join } from "node:path";
|
|
4
2
|
import test from "node:test";
|
|
5
3
|
|
|
6
|
-
import { root } from "./aggregate-plugin-fixture.mjs";
|
|
7
4
|
import { cleanupTeamRoot, createTeamRoot, readTeamJson, runTeam } from "./teammode-safety-fixture.mjs";
|
|
8
5
|
|
|
9
|
-
const teammodeSkillPath = join("components", "teammode", "skills", "teammode", "SKILL.md");
|
|
10
|
-
|
|
11
|
-
test("#given Codex App thread ids can be ambiguous across hosts #when archive guidance is read #then it separates app-thread archival from team-state archival", () => {
|
|
12
|
-
const body = readFileSync(join(root, teammodeSkillPath), "utf8");
|
|
13
|
-
|
|
14
|
-
assert.match(
|
|
15
|
-
body,
|
|
16
|
-
/Ambiguous Codex thread id|ambiguous across hosts/i,
|
|
17
|
-
"archive guidance must name the Codex App multi-host ambiguity failure instead of only saying the archive tool is unavailable",
|
|
18
|
-
);
|
|
19
|
-
assert.match(
|
|
20
|
-
body,
|
|
21
|
-
/app-thread archival blocker/i,
|
|
22
|
-
"archive guidance must classify ambiguous app-thread ids as app-thread archival blockers",
|
|
23
|
-
);
|
|
24
|
-
assert.match(
|
|
25
|
-
body,
|
|
26
|
-
/record .*team log/i,
|
|
27
|
-
"archive guidance must require a durable team-log record when app-thread archival cannot be proven",
|
|
28
|
-
);
|
|
29
|
-
assert.match(
|
|
30
|
-
body,
|
|
31
|
-
/continue .*team-state archive/i,
|
|
32
|
-
"archive guidance must let the team-state archive proceed after recording the app-thread archival blocker",
|
|
33
|
-
);
|
|
34
|
-
assert.match(
|
|
35
|
-
body,
|
|
36
|
-
/archive --note/i,
|
|
37
|
-
"archive guidance must route ambiguous app-thread archive blockers into archive --note evidence",
|
|
38
|
-
);
|
|
39
|
-
assert.match(
|
|
40
|
-
body,
|
|
41
|
-
/Do not delete/i,
|
|
42
|
-
"archive guidance must keep evidence available until ambiguous app-thread archival is resolved",
|
|
43
|
-
);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
6
|
test("#given app-thread archival is blocked by an ambiguous id #when archive runs with a note #then team state is archived and keeps the blocker in the log", () => {
|
|
47
7
|
const tempRoot = createTeamRoot("omo-codex-teammode-archive-ambiguity-");
|
|
48
8
|
try {
|
|
@@ -3,14 +3,12 @@ import { readFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import { root } from "./aggregate-plugin-fixture.mjs";
|
|
7
6
|
import { cleanupTeamRoot, createTeamRoot, runTeam, teamDir } from "./teammode-safety-fixture.mjs";
|
|
8
7
|
|
|
9
|
-
// The teammode guide.md is what every member actually reads.
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
//
|
|
13
|
-
// These tests pin the fix: a concrete push-communication protocol is the default the manual renders.
|
|
8
|
+
// The teammode guide.md is what every member actually reads. A real leader-session run proved members
|
|
9
|
+
// default to silence when the guide never names the tool or the address book, so the tests below pin
|
|
10
|
+
// only the machine tokens the generator must surface: the cross-thread tool name and the team.json
|
|
11
|
+
// field paths members address messages by. Guide/prompt wording itself is prose and stays unpinned.
|
|
14
12
|
|
|
15
13
|
function buildTwoMemberTeam(tempRoot, sessionId) {
|
|
16
14
|
runTeam(tempRoot, "init", "--name", "Comms", "--session-name", "frequent", "--session", sessionId);
|
|
@@ -39,69 +37,15 @@ test("#given a bound team #when the member field manual renders #then it names t
|
|
|
39
37
|
cleanupTeamRoot(tempRoot);
|
|
40
38
|
}
|
|
41
39
|
});
|
|
42
|
-
|
|
43
|
-
test("#given a bound team #when the member field manual renders #then it gives concrete per-moment cadence triggers, not aspirational 'constantly'", () => {
|
|
44
|
-
const tempRoot = createTeamRoot("omo-codex-teammode-comms-");
|
|
45
|
-
try {
|
|
46
|
-
buildTwoMemberTeam(tempRoot, "comms-cadence");
|
|
47
|
-
const guide = readGuide(tempRoot, "comms-cadence");
|
|
48
|
-
|
|
49
|
-
// then - a concrete heartbeat cadence the model can bind to
|
|
50
|
-
assert.match(guide, /every few tool calls/, "guide must give a concrete heartbeat trigger");
|
|
51
|
-
assert.match(guide, /WORKING:/, "guide must keep the WORKING heartbeat marker");
|
|
52
|
-
assert.match(guide, /BLOCKED:/, "guide must keep the BLOCKED marker");
|
|
53
|
-
// then - an explicit peer-notify rule so cross-cutting findings are pushed, not siloed
|
|
54
|
-
assert.match(guide, /to a \*\*peer\*\*/, "guide must have an explicit peer-notify rule");
|
|
55
|
-
assert.match(guide, /touches their slice/, "guide must trigger peer messages on slice-touching findings");
|
|
56
|
-
// then - the reassurance WHY that ties the two halves together: frequent reporting is what earns
|
|
57
|
-
// uninterrupted focus, so the leader has no reason to interrupt a member who keeps it informed
|
|
58
|
-
assert.match(guide, /uninterrupted focus/i, "guide must tell members frequent updates buy uninterrupted focus from the leader");
|
|
59
|
-
// then - the end-only directive that previously won is gone (it licensed batch-at-the-end reporting)
|
|
60
|
-
assert.doesNotMatch(guide, /the moment your work is complete/, "the standalone end-only report directive must be removed");
|
|
61
|
-
} finally {
|
|
62
|
-
cleanupTeamRoot(tempRoot);
|
|
63
|
-
}
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
test("#given a member bootstrap trigger #when it is generated #then it points at the push tool and a continuous cadence, not an end-of-work report", () => {
|
|
40
|
+
test("#given a member bootstrap trigger #when it is generated #then it names the cross-thread push tool", () => {
|
|
67
41
|
const tempRoot = createTeamRoot("omo-codex-teammode-comms-");
|
|
68
42
|
try {
|
|
69
43
|
buildTwoMemberTeam(tempRoot, "comms-bootstrap");
|
|
70
44
|
const prompt = runTeam(tempRoot, "member-prompt", "--team", "comms-bootstrap", "--id", "A").stdout;
|
|
71
45
|
|
|
72
|
-
// then - the first thing a member reads
|
|
46
|
+
// then - the first thing a member reads names the tool it can push through
|
|
73
47
|
assert.match(prompt, /codex_app\.send_message_to_thread/, "bootstrap must name the push tool");
|
|
74
|
-
assert.match(prompt, /never just one report at the end/, "bootstrap must reject end-only reporting");
|
|
75
|
-
// then - it no longer frames reporting as a one-shot completion event
|
|
76
|
-
assert.doesNotMatch(prompt, /report to the leader the moment you are done/, "the end-only bootstrap line must be removed");
|
|
77
48
|
} finally {
|
|
78
49
|
cleanupTeamRoot(tempRoot);
|
|
79
50
|
}
|
|
80
51
|
});
|
|
81
|
-
|
|
82
|
-
test("#given the leader-facing SKILL.md #when it describes communication #then it requires --session so members can reach the leader and frames members as pushing", () => {
|
|
83
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
84
|
-
|
|
85
|
-
// then - the leader is told members PUSH to it (not only that the leader sends)
|
|
86
|
-
assert.match(skill, /Members push to you/i, "SKILL.md must frame members as pushing to the leader");
|
|
87
|
-
// then - the --session requirement is stated WITH its reason (member->leader push needs a real leader thread)
|
|
88
|
-
assert.match(skill, /--session <your own thread id>/, "SKILL.md must require init --session <your own thread id>");
|
|
89
|
-
assert.match(skill, /stuck polling|cannot report to you/i, "SKILL.md must explain why --session matters");
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
// The leader (a GPT-5.5 main session) over-polls when nothing reframes silence as work: real jobdori
|
|
93
|
-
// run 019f07a6- fired 6 leader->child pings, 4 pure "WORKING CHECK"/"NUDGE" nags that interrupted the
|
|
94
|
-
// child mid-slice. The member guide already mandates frequent reporting; the missing half is telling the
|
|
95
|
-
// leader to WAIT. These tests pin that the leader-facing skill reframes quiet-as-working and gates any
|
|
96
|
-
// outbound ping behind concrete decision rules instead of letting anxiety drive a barrage.
|
|
97
|
-
test("#given the leader-facing SKILL.md #when it covers waiting on members #then it reframes a quiet member as working and gates leader pings behind decision rules", () => {
|
|
98
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
99
|
-
|
|
100
|
-
// then - a quiet member between heartbeats is reframed as working, not a stall to chase
|
|
101
|
-
// (\s+ tolerates markdown line-wrapping between the two words)
|
|
102
|
-
assert.match(skill, /working,\s+not stalled/i, "SKILL.md must reframe a quiet member as working, not stalled");
|
|
103
|
-
// then - outbound leader messages are gated behind explicit decision rules, not anxiety
|
|
104
|
-
assert.match(skill, /Message a member only when/i, "SKILL.md must gate leader pings behind a concrete decision rule");
|
|
105
|
-
// then - the anti-pattern nag pings the real run produced are named as what NOT to send
|
|
106
|
-
assert.match(skill, /are you\s+done\?/i, "SKILL.md must name the 'are you done?' style nag as forbidden");
|
|
107
|
-
});
|
|
@@ -71,46 +71,13 @@ test("#given a worktree-backed team has bound member threads #when guide and sta
|
|
|
71
71
|
const status = runTeam(tempRoot, "status", "--team", sessionId).stdout;
|
|
72
72
|
const prompt = runTeam(tempRoot, "member-prompt", "--team", sessionId, "--id", "A").stdout;
|
|
73
73
|
|
|
74
|
+
// then - guide, status, and bootstrap prompt all surface the member's bound thread link and cwd
|
|
74
75
|
assert.match(guide, /codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
75
76
|
assert.match(guide, /codex:\/\/threads\/019ef2dd-5b99-7ae2-8461-ffa6ca309d23/);
|
|
76
77
|
assert.match(guide, /worktree `\/tmp\/review-worktree`/);
|
|
77
78
|
assert.match(status, /link=codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
78
|
-
assert.match(prompt, /
|
|
79
|
-
assert.match(prompt,
|
|
80
|
-
assert.match(prompt, /Before editing, verify that your assigned worktree exists and contains repo files/);
|
|
81
|
-
assert.match(prompt, /BLOCKED: worktree not ready/);
|
|
82
|
-
} finally {
|
|
83
|
-
cleanupTeamRoot(tempRoot);
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
test("#given a worktree member has no cwd bound yet #when guide and prompt render #then they tell Codex to wait for readiness", () => {
|
|
88
|
-
const tempRoot = createTeamRoot("omo-codex-teammode-worktree-wait-");
|
|
89
|
-
try {
|
|
90
|
-
const sessionId = "worktree-wait";
|
|
91
|
-
runTeam(tempRoot, "init", "--name", "Wait", "--session-name", "worktrees", "--session", sessionId, "--worktree", "--base-branch", "main");
|
|
92
|
-
addMember(tempRoot, sessionId, {
|
|
93
|
-
id: "A",
|
|
94
|
-
name: "worktree-driver",
|
|
95
|
-
focus: "drive a pending Codex worktree thread",
|
|
96
|
-
lens: "ownership",
|
|
97
|
-
deliverable: "ready worktree report",
|
|
98
|
-
});
|
|
99
|
-
addMember(tempRoot, sessionId, {
|
|
100
|
-
id: "B",
|
|
101
|
-
name: "integration-watch",
|
|
102
|
-
focus: "watch integration boundaries",
|
|
103
|
-
lens: "perspective",
|
|
104
|
-
deliverable: "integration notes",
|
|
105
|
-
});
|
|
106
|
-
runTeam(tempRoot, "bind-thread", "--team", sessionId, "--id", "A", "--thread", "019ef350-ee78-72a3-bd5e-e40cebc3d814");
|
|
107
|
-
|
|
108
|
-
const guide = readFileSync(`${teamDir(tempRoot, sessionId)}/guide.md`, "utf8");
|
|
109
|
-
const prompt = runTeam(tempRoot, "member-prompt", "--team", sessionId, "--id", "A").stdout;
|
|
110
|
-
|
|
111
|
-
assert.match(guide, /If Codex returns only `pendingWorktreeId`/);
|
|
112
|
-
assert.match(guide, /wait until Codex surfaces a real thread id/);
|
|
113
|
-
assert.match(prompt, /If this prompt arrived before your Codex worktree checkout is ready/);
|
|
79
|
+
assert.match(prompt, /codex:\/\/threads\/019ef350-ee78-72a3-bd5e-e40cebc3d814/);
|
|
80
|
+
assert.match(prompt, /\/tmp\/review-worktree/);
|
|
114
81
|
} finally {
|
|
115
82
|
cleanupTeamRoot(tempRoot);
|
|
116
83
|
}
|
|
@@ -3,7 +3,6 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import test from "node:test";
|
|
5
5
|
|
|
6
|
-
import { root } from "./aggregate-plugin-fixture.mjs";
|
|
7
6
|
import {
|
|
8
7
|
cleanupTeamRoot,
|
|
9
8
|
createTeamRoot,
|
|
@@ -53,45 +52,6 @@ function addV2Members(tempRoot, sessionId) {
|
|
|
53
52
|
);
|
|
54
53
|
}
|
|
55
54
|
|
|
56
|
-
test("#given flat V2 tools are available #when the leader reads teammode #then transport selection and user announcement precede init", () => {
|
|
57
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
58
|
-
const inspectIndex = skill.indexOf("Inspect your active tool list");
|
|
59
|
-
const initIndex = skill.indexOf('team.mjs" init');
|
|
60
|
-
|
|
61
|
-
assert.notEqual(inspectIndex, -1, "skill must inspect the active tool list");
|
|
62
|
-
assert.notEqual(initIndex, -1, "skill must still document init");
|
|
63
|
-
assert.ok(inspectIndex < initIndex, "transport inspection must happen before init");
|
|
64
|
-
assert.match(skill, /spawn_agent.*task_name[\s\S]*send_message[\s\S]*followup_task[\s\S]*wait_agent[\s\S]*list_agents[\s\S]*interrupt_agent/);
|
|
65
|
-
assert.match(skill, /tell the user|announce.*transport/i);
|
|
66
|
-
assert.match(skill, /MultiAgentV2/i);
|
|
67
|
-
assert.match(skill, /Codex App.*fallback/i);
|
|
68
|
-
});
|
|
69
|
-
|
|
70
|
-
test("#given neither transport's tools are visible #when the leader reads teammode #then search hits are revalidated and fallback is capability-aware without team state", () => {
|
|
71
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
72
|
-
|
|
73
|
-
const searchIndex = skill.indexOf("tool_search");
|
|
74
|
-
const unavailableIndex = skill.indexOf("Teammode unavailable");
|
|
75
|
-
assert.notEqual(searchIndex, -1, "skill must route hidden tools through the tool_search check");
|
|
76
|
-
assert.notEqual(unavailableIndex, -1, "skill must give the leader unavailable announcement templates");
|
|
77
|
-
assert.ok(searchIndex < unavailableIndex, "tool_search must precede the unavailable conclusion");
|
|
78
|
-
assert.match(skill, /revalidate.*COMPLETE.*compatible transport set/is, "a deferred-tool hit must be revalidated as a complete transport");
|
|
79
|
-
assert.match(skill, /another visible plain-subagent mechanism.*spawn.*communicate.*observe/is, "plain-subagent fallback requires a complete visible lifecycle");
|
|
80
|
-
assert.match(skill, /Otherwise continue serially.*capability limitation/is, "missing all transports must continue serially rather than promise workers");
|
|
81
|
-
assert.match(skill, /Do NOT run `init`/, "the fallback must not create team state");
|
|
82
|
-
assert.doesNotMatch(skill, /splitting the work across plain fire-and-forget subagents/i, "the skill must not promise unavailable plain subagents");
|
|
83
|
-
});
|
|
84
|
-
|
|
85
|
-
test("#given a MultiAgentV2 team #when the leader spawns members #then guidance limits calls to exposed V2 fields", () => {
|
|
86
|
-
const skill = readFileSync(join(root, "components", "teammode", "skills", "teammode", "SKILL.md"), "utf8");
|
|
87
|
-
|
|
88
|
-
assert.match(skill, /using only the V2 schema fields/i);
|
|
89
|
-
assert.match(skill, /`task_name`[\s\S]*`message`[\s\S]*`fork_turns`/);
|
|
90
|
-
assert.match(skill, /role, priority, or task-specific routing instruction in `message`/i);
|
|
91
|
-
assert.match(skill, /V2 does not accept[\s\S]*`agent_type`, `model`, `reasoning_effort`, or `service_tier`/);
|
|
92
|
-
assert.match(skill, /inherit the[\s\S]*session model/i);
|
|
93
|
-
});
|
|
94
|
-
|
|
95
55
|
test("#given MultiAgentV2 transport #when members are added and bound #then task names and canonical agent paths form the address book", () => {
|
|
96
56
|
const tempRoot = createTeamRoot("omo-codex-teammode-v2-transport-");
|
|
97
57
|
try {
|
|
@@ -123,7 +83,6 @@ test("#given MultiAgentV2 transport #when members are added and bound #then task
|
|
|
123
83
|
{ id: "B", taskName: "mailbox_delivery", agentPath: "/root/mailbox_delivery", threadId: null, threadTitle: null, status: "active" },
|
|
124
84
|
],
|
|
125
85
|
);
|
|
126
|
-
assert.match(guide, /Transport:.*MultiAgentV2/i);
|
|
127
86
|
assert.match(guide, /members\[\]\.agentPath/);
|
|
128
87
|
assert.match(guide, /send_message/);
|
|
129
88
|
assert.match(guide, /followup_task/);
|
|
@@ -223,9 +182,7 @@ test("#given a V2 team #when the whole team is archived without a note #then no
|
|
|
223
182
|
const archiveEntry = team.log.find((entry) => entry.event === "archive");
|
|
224
183
|
|
|
225
184
|
assert.equal(team.status, "archived");
|
|
226
|
-
assert.doesNotMatch(result.stdout, /closed all members/i, "V2 has no runtime close/archive operation to claim");
|
|
227
185
|
assert.match(result.stdout, /no runtime archive operation/i);
|
|
228
|
-
assert.doesNotMatch(archiveEntry.detail, /all members closed/i);
|
|
229
186
|
assert.match(archiveEntry.detail, /no runtime archive operation/i);
|
|
230
187
|
} finally {
|
|
231
188
|
cleanupTeamRoot(tempRoot);
|
|
@@ -261,7 +218,6 @@ test("#given a codex_app team #when one member is archived #then the existing wo
|
|
|
261
218
|
const result = runTeam(tempRoot, "archive", "--team", "app-archive-member", "--id", "A");
|
|
262
219
|
|
|
263
220
|
assert.match(result.stdout, /archived member A/);
|
|
264
|
-
assert.doesNotMatch(result.stdout, /no runtime archive operation/i);
|
|
265
221
|
} finally {
|
|
266
222
|
cleanupTeamRoot(tempRoot);
|
|
267
223
|
}
|
|
@@ -83,24 +83,21 @@ test("#given a worktree team #when worktree-add A #then a real git worktree on a
|
|
|
83
83
|
assert.match(result.stdout, /A/);
|
|
84
84
|
assert.match(result.stdout, /wait for the real Codex thread id/);
|
|
85
85
|
assert.match(result.stdout, /bind-thread before sending bootstrap/);
|
|
86
|
-
assert.doesNotMatch(result.stdout, /Tell that member to:/);
|
|
87
86
|
} finally {
|
|
88
87
|
cleanupTeamRoot(tempRoot);
|
|
89
88
|
}
|
|
90
89
|
});
|
|
91
90
|
|
|
92
|
-
test("#given worktree-add ran #when the field manual is regenerated #then it
|
|
91
|
+
test("#given worktree-add ran #when the field manual is regenerated #then it shows the member's derived worktree branch", () => {
|
|
93
92
|
const tempRoot = createTeamRoot("omo-codex-teammode-wt-guide-");
|
|
94
93
|
try {
|
|
95
94
|
initGitRepo(tempRoot);
|
|
96
95
|
bootstrapTeam(tempRoot, "wt-guide", { worktree: false });
|
|
97
|
-
// before: no isolation, so the manual tells members to signal collisions instead
|
|
98
|
-
assert.match(readFileSync(join(teamDir(tempRoot, "wt-guide"), "guide.md"), "utf8"), /does not use isolated git worktrees/);
|
|
99
96
|
|
|
100
97
|
runTeam(tempRoot, "worktree-add", "--team", "wt-guide", "--id", "A");
|
|
101
98
|
|
|
99
|
+
// then - the regenerated guide carries the derived branch recorded in team state
|
|
102
100
|
const guide = readFileSync(join(teamDir(tempRoot, "wt-guide"), "guide.md"), "utf8");
|
|
103
|
-
assert.match(guide, /ISOLATION IS ON/);
|
|
104
101
|
assert.match(guide, /team\/wt-guide\/A/, "the member's row must show its derived worktree branch");
|
|
105
102
|
} finally {
|
|
106
103
|
cleanupTeamRoot(tempRoot);
|
|
@@ -225,7 +222,6 @@ test("#given a member branch that cannot be merged #when integrate runs #then it
|
|
|
225
222
|
const out = `${result.stdout}${result.stderr}`;
|
|
226
223
|
assert.notEqual(result.status, 0);
|
|
227
224
|
assert.match(out, /could not integrate|not something we can merge|ghost-branch-404/i, "must surface git's actual reason");
|
|
228
|
-
assert.doesNotMatch(out, /Conflicting files/, "a non-conflict failure must NOT be mislabeled as a merge conflict");
|
|
229
225
|
} finally {
|
|
230
226
|
cleanupTeamRoot(tempRoot);
|
|
231
227
|
}
|
|
@@ -33,7 +33,6 @@ test("#given synced skills #when the ultrawork skill is inspected #then it carri
|
|
|
33
33
|
const directive = readFileSync(join(root, "components", "ultrawork", "directive.md"), "utf8");
|
|
34
34
|
assert.match(skill, /^---\nname: ultrawork\n/);
|
|
35
35
|
assert.equal(skill.endsWith(directive), true);
|
|
36
|
-
assert.match(skill, /## 1\. Create the goal with binding success criteria/);
|
|
37
36
|
});
|
|
38
37
|
|
|
39
38
|
test("#given the built ultrawork CLI #when a ulw prompt is submitted #then a compact skill pointer is emitted", () => {
|
|
@@ -42,10 +41,8 @@ test("#given the built ultrawork CLI #when a ulw prompt is submitted #then a com
|
|
|
42
41
|
const output = JSON.parse(result.stdout);
|
|
43
42
|
const context = output.hookSpecificOutput.additionalContext;
|
|
44
43
|
assert.match(context, /^<ultrawork-mode>/);
|
|
45
|
-
assert.match(context, /First user-visible line this turn MUST be exactly:/);
|
|
46
44
|
assert.match(context, /create_goal/);
|
|
47
45
|
assert.equal(context.includes(skillPath), true);
|
|
48
|
-
assert.equal(context.includes("Tier triage"), false);
|
|
49
46
|
assert.equal(Buffer.byteLength(context, "utf8") < POINTER_MAX_BYTES, true);
|
|
50
47
|
});
|
|
51
48
|
|
|
@@ -54,15 +54,12 @@ function matchesCas(state, expected, clauses) {
|
|
|
54
54
|
|
|
55
55
|
for (const surface of surfaces) {
|
|
56
56
|
test(`#given ${surface.name} #when deeper review becomes required before plan completion #then durable request state covers explicit and automatic review without inventing a digest`, async () => {
|
|
57
|
-
const skill = await readFile(surface.skillPath, "utf8");
|
|
58
57
|
const workflow = await readFile(surface.workflowPath, "utf8");
|
|
59
58
|
const contract = readJsonContract(workflow, "ulw-plan-review-request-state-contract");
|
|
60
59
|
|
|
61
60
|
assert.equal(contract.transition, "replace");
|
|
62
61
|
assert.equal(contract.phase, "review_requested");
|
|
63
62
|
assert.deepEqual(contract.applies_when, ["explicit_review_modifier_before_complete_plan", "intent=unclear_and_nontrivial"]);
|
|
64
|
-
assert.match(skill, /Include `--review-required`[\s\S]{0,180}non-Trivial UNCLEAR/);
|
|
65
|
-
assert.match(workflow, /--draft-only/);
|
|
66
63
|
assert.equal(contract.atomic, true);
|
|
67
64
|
assert.equal(contract.review_required, true);
|
|
68
65
|
assert.equal(contract.plan_path, ".omo/plans/<slug>.md");
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
// omo-codex-install:
|
|
2
|
+
// omo-codex-install:1d8359e77ebe41655f091cd2e455f320cde2f9c681f0651797c47d8fdfa2dd76:c76513d859dcf200fa0178cedc7bbd268cbb9a16c2f2b30d3edd344daec24755
|
|
3
3
|
var __defProp = Object.defineProperty;
|
|
4
4
|
var __returnValue = (v) => v;
|
|
5
5
|
function __exportSetter(name, newValue) {
|
|
@@ -5927,7 +5927,7 @@ var package_default;
|
|
|
5927
5927
|
var init_package = __esm(() => {
|
|
5928
5928
|
package_default = {
|
|
5929
5929
|
name: "@oh-my-opencode/omo-codex",
|
|
5930
|
-
version: "5.0.0-beta.
|
|
5930
|
+
version: "5.0.0-beta.9",
|
|
5931
5931
|
type: "module",
|
|
5932
5932
|
private: true,
|
|
5933
5933
|
description: "Codex harness adapter for oh-my-openagent. Vendored Codex plugin namespace (omo) + TypeScript installer + telemetry.",
|
|
@@ -77,8 +77,13 @@ test("#given sisyphuslabs lazycodex install #when installing locally #then stamp
|
|
|
77
77
|
assert.equal(manifest.version, "4.7.6");
|
|
78
78
|
assert.equal(packageJson.version, "4.7.6");
|
|
79
79
|
assert.equal(componentPackageJson.version, "4.7.6");
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
for (const statusMessage of [
|
|
81
|
+
hooks.hooks.PostToolUse[0].hooks[0].statusMessage,
|
|
82
|
+
componentHooks.hooks.UserPromptSubmit[0].hooks[0].statusMessage,
|
|
83
|
+
]) {
|
|
84
|
+
assert.match(statusMessage, /^\(OmO 4\.7\.6\) \S/);
|
|
85
|
+
assert.doesNotMatch(statusMessage, /0\.1\.0/);
|
|
86
|
+
}
|
|
82
87
|
assert.deepEqual(snapshot, {
|
|
83
88
|
packageName: "lazycodex-ai",
|
|
84
89
|
version: "4.7.6",
|
|
@@ -5,7 +5,7 @@ description: "MUST USE for frontend/web UI/UX/visual work: building, styling, re
|
|
|
5
5
|
|
|
6
6
|
# Frontend
|
|
7
7
|
|
|
8
|
-
This file is a router, not a rulebook. The rules live in four rulesets under `references
|
|
8
|
+
This file is a router, not a rulebook. The rules live in four rulesets under `references/`, and reading them is the work, not the preamble to it. Before touching any file, name the references the request routes to and the one reason each is needed, then read exactly those. Declaring the set first is what makes the choice reviewable: a reference you never named is one you decided to skip, and a reference you named but never opened is a gap you still owe. Freestyling past the routed set produces the generic AI-slop output this skill exists to prevent.
|
|
9
9
|
|
|
10
10
|
**The bar is not clean-and-correct — it is work a senior designer at Linear, Stripe, or Supabase would ship.** Correct-but-flat is a failure, not a finish. Protect the surface as hard as you protect the build: design is a first-class deliverable, not a one-shot decision you lock and walk away from.
|
|
11
11
|
|
|
@@ -14,12 +14,13 @@ This file is a router, not a rulebook. The rules live in four rulesets under `re
|
|
|
14
14
|
| Request involves… | Read |
|
|
15
15
|
|---|---|
|
|
16
16
|
| ANY UI implementation, styling, redesign, mockup, or visual decision | `references/design/README.md` FIRST. It enforces two mandatory gates — the Design System Gate (a `DESIGN.md` must exist before any component is written) and the React Dev Tooling Gate (react-grab / react-scan / react-doctor installed by default) — then routes to the taste and brand references below. |
|
|
17
|
+
| Spatial structure — app shells, scroll ownership, "what goes where", "this layout breaks at X" | ALSO `references/design/layout-skill.md` for the mechanics, then `references/design/stylegallery.md` to fetch a named pattern contract for that exact spatial problem. Both stack on the style skill and add no visual direction. |
|
|
17
18
|
| Interaction or motion work — micro-interactions, animated components, transitions, gestures, hover/press/state feedback, "make it feel alive" | ALSO `references/design/interaction-skill.md`. The beui.dev catalog is the mandatory interaction reference: find the nearest pattern, read its real source through the file's curl recipe, and adapt the mechanism to `DESIGN.md` motion tokens. It stacks on the routed style skill — never replaces it. |
|
|
18
19
|
| Writing or modifying frontend code, OR auditing performance / SEO / accessibility / quality | ALSO `references/perfection/README.md`. Lighthouse 100 in every category, measured on real Playwright Chromium (never the `lighthouse` CLI), achieved through architecture — never by dropping animations or hiding content. |
|
|
19
|
-
| Looking up a concrete style,
|
|
20
|
-
| ANY implementation or redesign that creates or updates `DESIGN.md` — plus explicit operating-layer asks (personas, critique, debt, handoff, synthetic user testing) | `references/designpowers/README.md` + `
|
|
20
|
+
| Looking up a concrete style, palette, font pairing, chart type, landing structure, or UX guideline — or generating a design system from keywords | `references/ui-ux-db/README.md`. A searchable CSV database with a CLI: a lookup tool, not a posture. `design` stays the source of truth for taste and the `DESIGN.md` contract. |
|
|
21
|
+
| ANY implementation or redesign that creates or updates `DESIGN.md` — plus explicit operating-layer asks (personas, critique, debt, handoff, synthetic user testing) | `references/designpowers/README.md` + `lane-c-review.md`. lane-c is the Phase Final flatness/critique reviewer and fills the accessibility-constraint and accepted-debt sections `DESIGN.md` requires. Load other lanes only when their phase applies. |
|
|
21
22
|
|
|
22
|
-
**For implementation work, design + perfection load together.**
|
|
23
|
+
**For implementation work, design + perfection load together.** Beauty with a 2 MB bundle fails; Lighthouse 100 that looks like AI slop fails. Both win or neither does.
|
|
23
24
|
|
|
24
25
|
## Design System and Component Workflow
|
|
25
26
|
|
|
@@ -32,12 +33,13 @@ Every implementation must choose one of these branches before UI code changes:
|
|
|
32
33
|
2. **Greenfield or fresh setup:** if the user gave no concrete visual reference, design research is a build step with named deliverables — not exploration to be budgeted. Exploration-stop instincts ("enough exploration", two-wave caps) do not apply here. Fire every research lane IN PARALLEL before `DESIGN.md` is written, and open `DESIGN.md` with a `## 0. Research Log` section recording each lane's deliverable — a lane with no Research Log line did not run. Skip a lane only when its tool or network is genuinely unavailable, and name the skip in `DESIGN.md`:
|
|
33
34
|
- **Embedded references:** use `references/design/_INDEX.md` to shortlist 2-3 plausible Layer B references, then read exactly one Layer A style skill and one Layer B reference in full — every line, no partial reads (they are 200-500 lines; a sliced read produces the flattened token set this gate exists to prevent). Log the shortlist, the pick, and why. Use `open-design` only when the curated set has no fit; add `ui-ux-db` lookups for palette/type/domain questions.
|
|
34
35
|
- **Lazyweb real-product screens:** READ `references/design/lazyweb.md` FIRST and run its recipe verbatim — do not improvise curl calls against lazyweb.com; the recipe mints its own anonymous token. Log the queries run, how many screens you actually VIEWED, and the layout grammar harvested — never pixel copies.
|
|
36
|
+
- **StyleGallery spatial patterns:** read `references/design/stylegallery.md` and fetch the pattern whose primary spatial problem matches the screen. Log the pattern adopted and the element that owns the scroll.
|
|
35
37
|
- **Imagen concept drafts:** generate 2-3 imagen concept drafts, each seeded with the loaded Layer A + Layer B tokens (palette, type, material); pick the strongest and treat the chosen draft as the reference-fidelity contract. Log the draft paths and the pick.
|
|
36
38
|
Synthesize every lane into `DESIGN.md`. Treat sources as source material, not mood labels: extract tokens, layout grammar, component anatomy, interaction states, motion, and taste decisions, then recombine them into project-specific primitives. Before laying out sections, inventory the content blocks and assign each a job — hook, explain, prove, compare, convert, navigate, retain — then order sections by the visitor's decision path, not by visual symmetry. Never freestyle past the selected references, never copy logos or brand-specific copy. Then run the Primitive Showcase Gate (`references/design/README.md` Phase 0) before any product screen.
|
|
37
39
|
3. **Existing project with `DESIGN.md` or a component system:** read it, follow it, and update it before implementation only when the requested work needs a new token, primitive, state, motion rule, accessibility constraint, accepted debt, or reference-fidelity requirement.
|
|
38
40
|
4. **Existing project with UI but no `DESIGN.md` and no reusable component layer:** STOP and ask the user one focused question: should you preserve the current look with copy-nearby styling, or extract a real `DESIGN.md` plus reusable components before continuing? Do not silently choose.
|
|
39
41
|
|
|
40
|
-
|
|
42
|
+
The resulting `DESIGN.md` is the implementation contract: tokens, typography, spacing, primitives, motion, responsive behavior, accessibility constraints, and accepted debt must be named there before code uses them. Verify component primitives, states, and final screens with real visual QA evidence; pass design-system decisions, implementation evidence, and unresolved debt into `/review-work` for significant implementation work.
|
|
41
43
|
|
|
42
44
|
## Ruleset 1 — design (`references/design/`)
|
|
43
45
|
|
|
@@ -104,7 +106,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
|
|
|
104
106
|
|
|
105
107
|
## Ruleset 4 — designpowers (`references/designpowers/`)
|
|
106
108
|
|
|
107
|
-
`README.md` routes
|
|
109
|
+
`README.md` routes the pinned `Owl-Listener/designpowers` corpus into this workflow. It supplies design context — personas, accessibility and cognitive constraints, critique, debt, handoff, synthetic user testing, motion, role prompts — that must be distilled into `DESIGN.md` first, then used as the implementation contract. It replaces nothing: not this skill, not `/visual-qa`, `/ulw-plan`, `/start-work`, or `/review-work`.
|
|
108
110
|
|
|
109
111
|
## Quick routes — most common requests
|
|
110
112
|
|
|
@@ -120,6 +122,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
|
|
|
120
122
|
| "Audit my site" / "make this page faster" | `perfection/README.md` (+ `perfection/react-perf-tooling.md` if React) |
|
|
121
123
|
| "Mockup image of a fintech app" — no code | `design/imagegen-frontend-mobile.md` (+ a Layer B brand if named) |
|
|
122
124
|
| "What palette/fonts fit a wellness brand?" | `ui-ux-db/README.md` → search CLI |
|
|
125
|
+
| "Where should this go?" / "the layout breaks" / scroll + containment | `design/layout-skill.md` + `design/stylegallery.md` on the current style skill |
|
|
123
126
|
| "What do shipped apps in this space look like?" / design-direction research | `design/lazyweb.md` (curl-only) + `design/_INDEX.md` shortlist |
|
|
124
127
|
| "Set up this React project" | `design/README.md` + `design/react-dev-tooling-skill.md` |
|
|
125
128
|
| "Use designpowers", "make the design workflow stronger", "add personas/accessibility/debt/handoff" | `design/README.md` + `designpowers/README.md` (+ `perfection/README.md` if implementation or audit follows) |
|
|
@@ -127,7 +130,7 @@ Domains: `product` `style` `typography` `color` `landing` `chart` `ux` `react` `
|
|
|
127
130
|
## Shared axioms (all four rulesets agree — apply always)
|
|
128
131
|
|
|
129
132
|
- **No design system = no UI work.** `DESIGN.md` exists before components do; every color, font size, and spacing value traces back to a token in it.
|
|
130
|
-
- **Concrete reference = contract.** When a screenshot,
|
|
133
|
+
- **Concrete reference = contract.** When a screenshot, mockup, or annotated reference exists, match its pixels, copy, component structure, and responsive intent unless the user accepts a deviation.
|
|
131
134
|
- **Never weaken UX OR flatten the surface to buy points.** No dropping animations, hiding content, simplifying interactions, or replacing rendered/lit material with flat fills and flat geometric primitives for a Lighthouse score or a deadline. Hit 100 AND keep the surface dimensional — both, or neither.
|
|
132
135
|
- **No emojis as icons.** SVG icon sets only (Lucide, Heroicons, Radix, Phosphor).
|
|
133
136
|
- **GPU-composited animation only** — `transform`, `opacity`, `filter`; never animate layout properties.
|