oh-my-opencode 4.16.2 → 4.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.agents/command/omomomo.md +1 -1
- package/.agents/skills/hyperplan/SKILL.md +3 -3
- package/.agents/skills/omomomo/SKILL.md +1 -1
- package/.agents/skills/pre-publish-review/SKILL.md +3 -0
- package/.opencode/command/omomomo.md +1 -1
- package/.opencode/skills/hyperplan/SKILL.md +3 -3
- package/.opencode/skills/pre-publish-review/SKILL.md +3 -0
- package/README.ja.md +4 -4
- package/README.ko.md +4 -4
- package/README.md +4 -4
- package/README.ru.md +4 -4
- package/README.zh-cn.md +4 -4
- package/dist/agents/hephaestus/agent.d.ts +1 -1
- package/dist/agents/hephaestus/gpt-5-6.d.ts +2 -0
- package/dist/agents/momus-gpt-5-6.d.ts +1 -0
- package/dist/agents/types.d.ts +2 -0
- package/dist/cli/index.js +947 -567
- package/dist/cli-node/index.js +947 -567
- package/dist/features/background-agent/error-classifier.d.ts +5 -0
- package/dist/features/builtin-commands/templates/refactor-sections/intro-and-analysis.d.ts +1 -1
- package/dist/features/builtin-commands/templates/remove-ai-slops.d.ts +1 -1
- package/dist/features/team-mode/test-support/async-test-helpers.d.ts +2 -2
- package/dist/index.js +523 -81
- package/dist/plugin/chat-message/types.d.ts +4 -0
- package/dist/plugin/command-execute-before.d.ts +1 -0
- package/dist/plugin/stop-continuation.d.ts +17 -0
- package/dist/shared/live-server-route.d.ts +1 -1
- package/dist/skills/remove-ai-slops/SKILL.md +2 -2
- package/dist/skills/review-work/SKILL.md +1 -1
- package/dist/skills/start-work/SKILL.md +5 -2
- package/dist/skills/ulw-plan/references/full-workflow.md +1 -1
- package/dist/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
- package/dist/skills/ulw-research/SKILL.md +2 -2
- package/dist/skills/visual-qa/SKILL.md +1 -1
- package/dist/tui.js +195 -41
- package/package.json +15 -14
- package/packages/git-bash-mcp/package.json +27 -0
- package/packages/omo-codex/plugin/.codex-plugin/plugin.json +3 -1
- package/packages/omo-codex/plugin/components/bootstrap/dist/cli.js +465 -248
- 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/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/AGENTS.md +2 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/cli.js +6 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/dist/codex-hook.js +6 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/hooks/hooks.json +2 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/package.json +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/src/codex-hook.ts +6 -2
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/cli.test.ts +1 -1
- package/packages/omo-codex/plugin/components/lazycodex-executor-verify/test/codex-hook.test.ts +67 -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/lsp/test/package-smoke.test.ts +0 -13
- package/packages/omo-codex/plugin/components/rules/bundled-rules/hephaestus/gpt-5.6.md +93 -0
- package/packages/omo-codex/plugin/components/rules/dist/cli.js +43 -24
- 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/src/codex-hook.ts +2 -1
- package/packages/omo-codex/plugin/components/rules/src/dynamic-target-fingerprints.ts +15 -6
- package/packages/omo-codex/plugin/components/rules/src/rules-engine-factory.ts +7 -2
- package/packages/omo-codex/plugin/components/rules/src/static-injection.ts +2 -2
- package/packages/omo-codex/plugin/components/rules/test/codex-hook.test.ts +32 -0
- package/packages/omo-codex/plugin/components/rules/test/hephaestus-model-variant.test.ts +106 -0
- 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/codex-hook.test.ts +1 -79
- package/packages/omo-codex/plugin/components/teammode/AGENTS.md +12 -9
- 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/skills/teammode/SKILL.md +136 -76
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-guide.mjs +94 -23
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs +94 -14
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs +55 -0
- package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team.mjs +72 -18
- package/packages/omo-codex/plugin/components/teammode/test/thread-title-hook.test.ts +23 -0
- 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/agents/explorer.toml +23 -40
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-code-reviewer.toml +3 -3
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-gate-reviewer.toml +7 -7
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-qa-executor.toml +5 -5
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-high.toml +26 -0
- package/packages/omo-codex/plugin/components/ultrawork/agents/{lazycodex-executor.toml → lazycodex-worker-low.toml} +7 -5
- package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-worker-medium.toml +26 -0
- package/packages/omo-codex/plugin/components/ultrawork/agents/librarian.toml +20 -48
- package/packages/omo-codex/plugin/components/ultrawork/agents/metis.toml +17 -29
- package/packages/omo-codex/plugin/components/ultrawork/agents/momus.toml +20 -50
- package/packages/omo-codex/plugin/components/ultrawork/agents/plan.toml +22 -43
- package/packages/omo-codex/plugin/components/ultrawork/directive.md +55 -28
- 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/ultrawork/SKILL.md +55 -28
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/components/ultrawork/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
- package/packages/omo-codex/plugin/components/ultrawork/test/codex-hook.test.ts +22 -0
- package/packages/omo-codex/plugin/components/ultrawork/test/package-smoke.test.ts +0 -68
- package/packages/omo-codex/plugin/components/ulw-loop/AGENTS.md +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/CHANGELOG.md +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/README.md +3 -1
- package/packages/omo-codex/plugin/components/ulw-loop/directive.md +55 -28
- package/packages/omo-codex/plugin/components/ulw-loop/dist/checkpoint.js +6 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.d.ts +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli-subcommands.js +13 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/cli.js +405 -25
- package/packages/omo-codex/plugin/components/ulw-loop/dist/codex-goal-instruction.js +12 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/domain-types.d.ts +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.d.ts +7 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/paths.js +16 -1
- package/packages/omo-codex/plugin/components/ulw-loop/dist/plan-crud.js +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.d.ts +6 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate-verdicts.js +20 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.d.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/quality-gate.js +12 -9
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.d.ts +3 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/spawn-guard.js +148 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.d.ts +2 -0
- package/packages/omo-codex/plugin/components/ulw-loop/dist/stop-resume-hook.js +209 -0
- package/packages/omo-codex/plugin/components/ulw-loop/hooks/hooks.json +25 -2
- package/packages/omo-codex/plugin/components/ulw-loop/package.json +1 -1
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/SKILL.md +14 -15
- package/packages/omo-codex/plugin/components/ulw-loop/skills/ulw-loop/references/full-workflow.md +29 -27
- package/packages/omo-codex/plugin/components/ulw-loop/src/checkpoint.ts +6 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli-subcommands.ts +14 -3
- package/packages/omo-codex/plugin/components/ulw-loop/src/cli.ts +10 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/codex-goal-instruction.ts +12 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/domain-types.ts +4 -2
- package/packages/omo-codex/plugin/components/ulw-loop/src/paths.ts +27 -1
- package/packages/omo-codex/plugin/components/ulw-loop/src/plan-crud.ts +1 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate-verdicts.ts +23 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/quality-gate.ts +16 -9
- package/packages/omo-codex/plugin/components/ulw-loop/src/spawn-guard.ts +138 -0
- package/packages/omo-codex/plugin/components/ulw-loop/src/stop-resume-hook.ts +208 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-create-goals.test.ts +12 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/cli-entrypoint.test.ts +4 -1
- package/packages/omo-codex/plugin/components/ulw-loop/test/codex-goal-instruction.test.ts +8 -35
- package/packages/omo-codex/plugin/components/ulw-loop/test/fixtures/quality-gate-builder.ts +24 -13
- package/packages/omo-codex/plugin/components/ulw-loop/test/package-smoke.test.ts +5 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/paths.test.ts +43 -8
- package/packages/omo-codex/plugin/components/ulw-loop/test/quality-gate.test.ts +55 -2
- package/packages/omo-codex/plugin/components/ulw-loop/test/spawn-guard.test.ts +228 -0
- package/packages/omo-codex/plugin/components/ulw-loop/test/stop-resume-hook.test.ts +193 -0
- 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 +18 -0
- 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 +17 -0
- 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 +2 -2
- 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/model-catalog.json +16 -7
- package/packages/omo-codex/plugin/package-lock.json +13 -13
- package/packages/omo-codex/plugin/package.json +1 -1
- package/packages/omo-codex/plugin/scripts/auto-update.mjs +9 -2
- package/packages/omo-codex/plugin/scripts/entry-guard.mjs +26 -0
- package/packages/omo-codex/plugin/scripts/materialize-shared-upstreams.mjs +3 -1
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/catalog.mjs +16 -7
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/multi-agent-v2-guard.mjs +39 -4
- package/packages/omo-codex/plugin/scripts/migrate-codex-config/subagent-limit-guard.mjs +24 -4
- package/packages/omo-codex/plugin/scripts/migrate-codex-config.mjs +3 -3
- package/packages/omo-codex/plugin/scripts/migrate-omo-sot.mjs +2 -2
- package/packages/omo-codex/plugin/scripts/sync-hook-status-messages.mjs +2 -2
- package/packages/omo-codex/plugin/scripts/sync-skills.mjs +14 -6
- package/packages/omo-codex/plugin/scripts/sync-version.mjs +4 -2
- package/packages/omo-codex/plugin/skills/init-deep/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/refactor/SKILL.md +3 -3
- package/packages/omo-codex/plugin/skills/remove-ai-slops/SKILL.md +5 -5
- package/packages/omo-codex/plugin/skills/review-work/SKILL.md +9 -3
- package/packages/omo-codex/plugin/skills/start-work/SKILL.md +6 -3
- package/packages/omo-codex/plugin/skills/teammode/SKILL.md +136 -76
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-guide.mjs +94 -23
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-state.mjs +94 -14
- package/packages/omo-codex/plugin/skills/teammode/scripts/team-transport.mjs +55 -0
- package/packages/omo-codex/plugin/skills/teammode/scripts/team.mjs +72 -18
- package/packages/omo-codex/plugin/skills/ultrawork/SKILL.md +55 -28
- package/packages/omo-codex/plugin/skills/ulw-loop/SKILL.md +14 -15
- package/packages/omo-codex/plugin/skills/ulw-loop/references/full-workflow.md +29 -27
- package/packages/omo-codex/plugin/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/omo-codex/plugin/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
- package/packages/omo-codex/plugin/skills/ulw-research/SKILL.md +4 -4
- package/packages/omo-codex/plugin/skills/visual-qa/SKILL.md +4 -4
- package/packages/omo-codex/plugin/test/aggregate-agents.test.mjs +116 -10
- package/packages/omo-codex/plugin/test/aggregate-hooks.test.mjs +23 -2
- package/packages/omo-codex/plugin/test/aggregate-manifest.test.mjs +1 -1
- package/packages/omo-codex/plugin/test/aggregate-model-catalog.test.mjs +4 -4
- package/packages/omo-codex/plugin/test/aggregate-skills.test.mjs +4 -2
- package/packages/omo-codex/plugin/test/auto-update-restart-notice.test.mjs +33 -0
- package/packages/omo-codex/plugin/test/auto-update.test.mjs +4 -4
- package/packages/omo-codex/plugin/test/component-hook-contract-cases.mjs +2 -2
- package/packages/omo-codex/plugin/test/lcx-bug-skills.test.mjs +4 -101
- package/packages/omo-codex/plugin/test/migrate-codex-config.test.mjs +160 -23
- package/packages/omo-codex/plugin/test/multi-agent-v2-regression.test.mjs +71 -0
- package/packages/omo-codex/plugin/test/subagent-limit-migration.test.mjs +32 -0
- package/packages/omo-codex/plugin/test/sync-skills-orchestration.test.mjs +20 -2
- package/packages/omo-codex/plugin/test/sync-skills-test-support.mjs +1 -1
- package/packages/omo-codex/plugin/test/sync-skills.test.mjs +4 -2
- package/packages/omo-codex/plugin/test/teammode-transport.test.mjs +345 -0
- package/packages/omo-codex/plugin/test/ulw-plan-skill-contract.test.mjs +9 -40
- package/packages/omo-codex/plugin/test/ulw-research-skill-contract.test.mjs +4 -277
- package/packages/omo-codex/scripts/install-config-reasoning.test.mjs +5 -5
- package/packages/omo-codex/scripts/install-config.test.mjs +22 -3
- package/packages/omo-codex/scripts/install-dist/install-local.mjs +508 -286
- package/packages/omo-codex/scripts/install-generated-bundle.test.mjs +111 -1
- package/packages/shared-skills/skills/remove-ai-slops/SKILL.md +2 -2
- package/packages/shared-skills/skills/review-work/SKILL.md +1 -1
- package/packages/shared-skills/skills/start-work/SKILL.md +5 -2
- package/packages/shared-skills/skills/ulw-plan/references/full-workflow.md +1 -1
- package/packages/shared-skills/skills/ulw-plan/scripts/scaffold-plan.mjs +2 -2
- package/packages/shared-skills/skills/ulw-research/SKILL.md +2 -2
- package/packages/shared-skills/skills/visual-qa/SKILL.md +1 -1
- package/dist/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
- package/packages/omo-codex/plugin/components/ulw-loop/test/skill-contract.test.ts +0 -70
- package/packages/omo-codex/plugin/test/ulw-research-epistemic-contract.test.mjs +0 -98
- package/packages/shared-skills/skills/visual-qa/scripts/skill-prompt-contract.test.ts +0 -296
- /package/packages/omo-codex/plugin/components/rules/bundled-rules/{hephaestus.md → hephaestus/gpt-5.5.md} +0 -0
package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-state.mjs
CHANGED
|
@@ -13,6 +13,16 @@ import { lstat, mkdir, readFile, rename, rm, writeFile } from "node:fs/promises"
|
|
|
13
13
|
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
14
14
|
import { setTimeout as delay } from "node:timers/promises";
|
|
15
15
|
|
|
16
|
+
import {
|
|
17
|
+
agentPathForTaskName,
|
|
18
|
+
assertTransport,
|
|
19
|
+
isMultiAgentV2,
|
|
20
|
+
LEADER_AGENT_PATH,
|
|
21
|
+
parseTaskName,
|
|
22
|
+
parseTeamTransport,
|
|
23
|
+
TEAM_SCHEMA_VERSION,
|
|
24
|
+
} from "./team-transport.mjs";
|
|
25
|
+
|
|
16
26
|
export const LENSES = ["area", "ownership", "perspective"];
|
|
17
27
|
export const MEMBER_STATUSES = ["pending", "active", "reported", "blocked", "archived"];
|
|
18
28
|
// A team is never a single member: two or more distinct slices is the minimum. One isolated
|
|
@@ -32,22 +42,25 @@ function isoNow(now) {
|
|
|
32
42
|
return now ?? new Date().toISOString();
|
|
33
43
|
}
|
|
34
44
|
|
|
35
|
-
export function buildTeam({ teamName, sessionName, sessionId = null, dir = null, worktreeEnabled = false, baseBranch = "dev", now }) {
|
|
45
|
+
export function buildTeam({ teamName, sessionName, sessionId = null, dir = null, worktreeEnabled = false, baseBranch = "dev", transport, now }) {
|
|
36
46
|
if (!teamName?.trim()) throw new Error("team name is required");
|
|
37
47
|
if (!sessionName?.trim()) throw new Error("session name is required");
|
|
48
|
+
const teamTransport = parseTeamTransport(transport);
|
|
49
|
+
const v2 = teamTransport === "multi_agent_v2";
|
|
38
50
|
const ts = isoNow(now);
|
|
39
51
|
return {
|
|
40
|
-
schemaVersion:
|
|
52
|
+
schemaVersion: TEAM_SCHEMA_VERSION,
|
|
53
|
+
transport: teamTransport,
|
|
41
54
|
teamId: randomUUID(),
|
|
42
55
|
teamName: teamName.trim(),
|
|
43
56
|
sessionName: sessionName.trim(),
|
|
44
57
|
sessionId,
|
|
45
|
-
threadTitleConvention: `[${teamName.trim()}] <member name>`,
|
|
58
|
+
threadTitleConvention: v2 ? null : `[${teamName.trim()}] <member name>`,
|
|
46
59
|
status: "active",
|
|
47
60
|
createdAt: ts,
|
|
48
61
|
updatedAt: ts,
|
|
49
62
|
archivedAt: null,
|
|
50
|
-
leader: { kind: "main-session", sessionId },
|
|
63
|
+
leader: { kind: "main-session", sessionId: v2 ? null : sessionId, agentPath: v2 ? LEADER_AGENT_PATH : null },
|
|
51
64
|
communication: { memberLanguage: "english", replyToUserInUserLanguage: true },
|
|
52
65
|
worktree: { enabled: Boolean(worktreeEnabled), baseBranch, root: dir ? join(dir, "worktrees") : null },
|
|
53
66
|
paths: dir
|
|
@@ -124,21 +137,44 @@ function assertUniqueMemberThreadTitle(team) {
|
|
|
124
137
|
}
|
|
125
138
|
}
|
|
126
139
|
|
|
127
|
-
function
|
|
140
|
+
function assertUniqueMemberTaskName(team) {
|
|
141
|
+
const seen = new Map();
|
|
142
|
+
for (const member of team.members) {
|
|
143
|
+
const taskName = parseTaskName(member.taskName);
|
|
144
|
+
const previous = seen.get(taskName);
|
|
145
|
+
if (previous) {
|
|
146
|
+
throw new Error(`member taskName "${taskName}" duplicates "${previous.taskName}" (no two members may share an agent path)`);
|
|
147
|
+
}
|
|
148
|
+
if (member.agentPath !== agentPathForTaskName(taskName)) {
|
|
149
|
+
throw new Error(`member "${member.id}" agentPath "${member.agentPath}" does not match task name "${taskName}"`);
|
|
150
|
+
}
|
|
151
|
+
seen.set(taskName, member);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function assertUniqueTransportIdentity(team) {
|
|
156
|
+
if (isMultiAgentV2(team)) assertUniqueMemberTaskName(team);
|
|
157
|
+
else assertUniqueMemberThreadTitle(team);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function assertTeamReadyForBinding(team) {
|
|
128
161
|
if (isUnderstaffed(team)) {
|
|
129
162
|
throw new Error(
|
|
130
|
-
`cannot bind
|
|
163
|
+
`cannot bind members until the team has at least ${MIN_MEMBERS} distinct members; current count is ${team.members.length}`,
|
|
131
164
|
);
|
|
132
165
|
}
|
|
133
166
|
assertUniqueMemberFocus(team);
|
|
134
167
|
assertUniqueMemberName(team);
|
|
135
|
-
|
|
168
|
+
assertUniqueTransportIdentity(team);
|
|
136
169
|
}
|
|
137
170
|
|
|
138
|
-
export function addMember(team, { id, focus, lens, deliverable = "", branch = null, name = null }) {
|
|
171
|
+
export function addMember(team, { id, focus, lens, deliverable = "", branch = null, name = null, taskName = null }) {
|
|
139
172
|
if (!id?.trim()) throw new Error("member id is required");
|
|
140
173
|
if (!focus?.trim()) throw new Error("member focus is required - a concrete part, ownership area, or perspective");
|
|
141
174
|
if (!LENSES.includes(lens)) throw new Error(`invalid lens "${lens}" - use one of: ${LENSES.join(", ")}`);
|
|
175
|
+
const v2 = isMultiAgentV2(team);
|
|
176
|
+
if (!v2 && taskName !== null) throw new Error("--task-name is only valid on multi_agent_v2 teams");
|
|
177
|
+
const memberTaskName = v2 ? parseTaskName(taskName) : null;
|
|
142
178
|
const memberId = id.trim();
|
|
143
179
|
const memberFocus = focus.trim();
|
|
144
180
|
// The member name is the short role label that titles this member's thread. Fall back to the
|
|
@@ -151,14 +187,20 @@ export function addMember(team, { id, focus, lens, deliverable = "", branch = nu
|
|
|
151
187
|
if (duplicateName) {
|
|
152
188
|
throw new Error(`member name "${memberName}" duplicates "${duplicateName.name ?? duplicateName.focus}" (no two members may produce the same thread title)`);
|
|
153
189
|
}
|
|
190
|
+
if (v2) {
|
|
191
|
+
const duplicateTask = team.members.find((m) => m.taskName === memberTaskName);
|
|
192
|
+
if (duplicateTask) throw new Error(`member taskName "${memberTaskName}" duplicates "${duplicateTask.taskName}" (no two members may share an agent path)`);
|
|
193
|
+
}
|
|
154
194
|
team.members.push({
|
|
155
195
|
id: memberId,
|
|
156
196
|
name: memberName,
|
|
157
197
|
focus: memberFocus,
|
|
158
198
|
lens,
|
|
159
199
|
deliverable: deliverable.trim(),
|
|
200
|
+
taskName: memberTaskName,
|
|
201
|
+
agentPath: v2 ? agentPathForTaskName(memberTaskName) : null,
|
|
160
202
|
threadId: null,
|
|
161
|
-
threadTitle: `[${team.teamName}] ${memberName}`,
|
|
203
|
+
threadTitle: v2 ? null : `[${team.teamName}] ${memberName}`,
|
|
162
204
|
cwd: null,
|
|
163
205
|
worktree: { path: null, branch: branch ?? null },
|
|
164
206
|
status: "pending",
|
|
@@ -167,8 +209,9 @@ export function addMember(team, { id, focus, lens, deliverable = "", branch = nu
|
|
|
167
209
|
}
|
|
168
210
|
|
|
169
211
|
export function bindThread(team, { id, threadId, cwd = null, worktreePath = null }) {
|
|
212
|
+
assertTransport(team, "codex_app", "bind-thread");
|
|
170
213
|
if (!threadId?.trim()) throw new Error("thread id is required");
|
|
171
|
-
|
|
214
|
+
assertTeamReadyForBinding(team);
|
|
172
215
|
const m = memberById(team, id);
|
|
173
216
|
m.threadId = threadId.trim();
|
|
174
217
|
m.status = "active";
|
|
@@ -177,6 +220,21 @@ export function bindThread(team, { id, threadId, cwd = null, worktreePath = null
|
|
|
177
220
|
return touch(team, "bind-thread", `member ${id} -> thread ${threadId.trim()}`);
|
|
178
221
|
}
|
|
179
222
|
|
|
223
|
+
export function bindAgent(team, { id, agentPath, cwd = null, worktreePath = null }) {
|
|
224
|
+
assertTransport(team, "multi_agent_v2", "bind-agent");
|
|
225
|
+
if (!agentPath?.trim()) throw new Error("agent path is required");
|
|
226
|
+
assertTeamReadyForBinding(team);
|
|
227
|
+
const m = memberById(team, id);
|
|
228
|
+
const boundPath = agentPath.trim();
|
|
229
|
+
if (boundPath !== m.agentPath) {
|
|
230
|
+
throw new Error(`agent path mismatch for member ${m.id}: expected "${m.agentPath}" (task name "${m.taskName}"), got "${boundPath}"`);
|
|
231
|
+
}
|
|
232
|
+
m.status = "active";
|
|
233
|
+
if (cwd) m.cwd = cwd;
|
|
234
|
+
if (team.worktree.enabled) m.worktree.path = worktreePath ?? cwd ?? m.worktree.path;
|
|
235
|
+
return touch(team, "bind-agent", `member ${id} -> agent ${boundPath}`);
|
|
236
|
+
}
|
|
237
|
+
|
|
180
238
|
export function setMemberStatus(team, { id, status, note = "" }) {
|
|
181
239
|
if (!MEMBER_STATUSES.includes(status)) throw new Error(`invalid status "${status}" - use one of: ${MEMBER_STATUSES.join(", ")}`);
|
|
182
240
|
memberById(team, id).status = status;
|
|
@@ -204,22 +262,44 @@ export function clearMemberWorktree(team, { id }) {
|
|
|
204
262
|
export function archive(team, { id = null, note = "" } = {}) {
|
|
205
263
|
if (id) {
|
|
206
264
|
memberById(team, id).status = "archived";
|
|
207
|
-
|
|
265
|
+
const memberQualifier = isMultiAgentV2(team) ? " (team state only; V2 exposes no runtime archive operation)" : "";
|
|
266
|
+
return touch(team, "archive-member", `member ${id}${memberQualifier}${note ? `: ${note}` : ""}`);
|
|
208
267
|
}
|
|
209
268
|
for (const m of team.members) m.status = "archived";
|
|
210
269
|
team.status = "archived";
|
|
211
270
|
team.archivedAt = isoNow();
|
|
212
|
-
|
|
271
|
+
// The default detail must never claim a runtime closure that did not happen: V2 exposes no
|
|
272
|
+
// runtime archive operation, so only the durable team state is archived there.
|
|
273
|
+
const defaultDetail = isMultiAgentV2(team)
|
|
274
|
+
? "team archived; V2 exposes no runtime archive operation - durable team state is the archive"
|
|
275
|
+
: "team archived; all members closed";
|
|
276
|
+
return touch(team, "archive", note || defaultDetail);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// schemaVersion 2 predates transports; that skill allowed ONLY Codex App threads as members,
|
|
280
|
+
// so a legacy team is migrated in memory as codex_app and persisted on the next mutation.
|
|
281
|
+
function migrateLegacyTeam(team) {
|
|
282
|
+
if (team?.schemaVersion !== 2) return team;
|
|
283
|
+
team.schemaVersion = TEAM_SCHEMA_VERSION;
|
|
284
|
+
team.transport = "codex_app";
|
|
285
|
+
if (team.leader && typeof team.leader === "object") team.leader.agentPath = null;
|
|
286
|
+
for (const member of team.members ?? []) {
|
|
287
|
+
member.taskName = null;
|
|
288
|
+
member.agentPath = null;
|
|
289
|
+
}
|
|
290
|
+
return team;
|
|
213
291
|
}
|
|
214
292
|
|
|
215
293
|
export function validateTeam(team) {
|
|
216
|
-
|
|
294
|
+
migrateLegacyTeam(team);
|
|
295
|
+
if (team?.schemaVersion !== TEAM_SCHEMA_VERSION) throw new Error(`invalid team: schemaVersion must be ${TEAM_SCHEMA_VERSION}`);
|
|
296
|
+
parseTeamTransport(team.transport);
|
|
217
297
|
if (!team.teamId || !team.teamName) throw new Error("invalid team: teamId and teamName are required");
|
|
218
298
|
if (team.leader?.kind !== "main-session") throw new Error("invalid team: leader.kind must be main-session");
|
|
219
299
|
if (!Array.isArray(team.members)) throw new Error("invalid team: members must be an array");
|
|
220
300
|
assertUniqueMemberFocus(team);
|
|
221
301
|
assertUniqueMemberName(team);
|
|
222
|
-
|
|
302
|
+
assertUniqueTransportIdentity(team);
|
|
223
303
|
return team;
|
|
224
304
|
}
|
|
225
305
|
|
package/packages/omo-codex/plugin/components/teammode/skills/teammode/scripts/team-transport.mjs
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// team-transport.mjs - the single source of transport identity for teammode state.
|
|
2
|
+
//
|
|
3
|
+
// A team runs on exactly ONE transport, chosen at init and immutable afterwards:
|
|
4
|
+
// - "multi_agent_v2": members are native Codex MultiAgentV2 agents addressed by task
|
|
5
|
+
// name / canonical agent path (`/root/<task_name>`), messaged with flat `send_message`
|
|
6
|
+
// and `followup_task`.
|
|
7
|
+
// - "codex_app": members are durable Codex App threads addressed by thread id, messaged
|
|
8
|
+
// with `codex_app.send_message_to_thread` (the fallback when flat V2 tools are absent).
|
|
9
|
+
//
|
|
10
|
+
// Zero external dependencies (node builtins only), same portability contract as the
|
|
11
|
+
// sibling scripts.
|
|
12
|
+
|
|
13
|
+
export const TEAM_SCHEMA_VERSION = 3;
|
|
14
|
+
export const TEAM_TRANSPORTS = ["multi_agent_v2", "codex_app"];
|
|
15
|
+
export const DEFAULT_TEAM_TRANSPORT = "codex_app";
|
|
16
|
+
export const LEADER_AGENT_PATH = "/root";
|
|
17
|
+
|
|
18
|
+
// Codex validates V2 task names as lowercase letters, digits, and underscores; "root" is
|
|
19
|
+
// the leader's own path segment and can never name a member.
|
|
20
|
+
const TASK_NAME_PATTERN = /^[a-z0-9_]+$/;
|
|
21
|
+
|
|
22
|
+
export function parseTeamTransport(value = DEFAULT_TEAM_TRANSPORT) {
|
|
23
|
+
if (!TEAM_TRANSPORTS.includes(value)) {
|
|
24
|
+
throw new Error(`invalid transport "${value}" - use one of: ${TEAM_TRANSPORTS.join(", ")}`);
|
|
25
|
+
}
|
|
26
|
+
return value;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function isMultiAgentV2(team) {
|
|
30
|
+
return team?.transport === "multi_agent_v2";
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isCodexApp(team) {
|
|
34
|
+
return team?.transport === "codex_app";
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export function assertTransport(team, transport, operation) {
|
|
38
|
+
if (team?.transport !== transport) {
|
|
39
|
+
throw new Error(`${operation} is only valid on ${transport} teams; this team's transport is "${team?.transport}"`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function parseTaskName(taskName) {
|
|
44
|
+
const trimmed = typeof taskName === "string" ? taskName.trim() : "";
|
|
45
|
+
if (!trimmed) throw new Error("member task name is required on multi_agent_v2 teams (--task-name)");
|
|
46
|
+
if (!TASK_NAME_PATTERN.test(trimmed)) {
|
|
47
|
+
throw new Error(`invalid task name "${trimmed}" - use lowercase letters, digits, and underscores`);
|
|
48
|
+
}
|
|
49
|
+
if (trimmed === "root") throw new Error('invalid task name "root" - it names the leader, never a member');
|
|
50
|
+
return trimmed;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function agentPathForTaskName(taskName) {
|
|
54
|
+
return `${LEADER_AGENT_PATH}/${taskName}`;
|
|
55
|
+
}
|
|
@@ -6,9 +6,10 @@
|
|
|
6
6
|
// Zero external dependencies (node builtins only): runs identically under node and bun on
|
|
7
7
|
// macOS, Linux, and Windows.
|
|
8
8
|
//
|
|
9
|
-
// node "<skill-root>/scripts/team.mjs" init --name "<team>" --session-name "<sess>" [--session <id>] [--worktree] [--base-branch dev]
|
|
10
|
-
// node "<skill-root>/scripts/team.mjs" add-member --team <id> --id A --name "<short role>" --focus "<part/ownership/perspective>" --lens area|ownership|perspective --deliverable "<...>" [--branch <b>]
|
|
11
|
-
// node "<skill-root>/scripts/team.mjs" bind-
|
|
9
|
+
// node "<skill-root>/scripts/team.mjs" init --name "<team>" --session-name "<sess>" [--transport multi_agent_v2|codex_app] [--session <id>] [--worktree] [--base-branch dev]
|
|
10
|
+
// node "<skill-root>/scripts/team.mjs" add-member --team <id> --id A --name "<short role>" --focus "<part/ownership/perspective>" --lens area|ownership|perspective --deliverable "<...>" [--task-name <v2 task name>] [--branch <b>]
|
|
11
|
+
// node "<skill-root>/scripts/team.mjs" bind-agent --team <id> --id A --agent-path /root/<task_name> [--cwd <path>] (multi_agent_v2 teams)
|
|
12
|
+
// node "<skill-root>/scripts/team.mjs" bind-thread --team <id> --id A --thread <thread-id> [--cwd <path>] (codex_app teams)
|
|
12
13
|
// node "<skill-root>/scripts/team.mjs" member-prompt --team <id> --id A
|
|
13
14
|
// node "<skill-root>/scripts/team.mjs" set-status --team <id> --id A --status reported|blocked|active|archived [--note "<...>"]
|
|
14
15
|
// node "<skill-root>/scripts/team.mjs" archive --team <id> [--id A] [--note "<...>"]
|
|
@@ -25,6 +26,7 @@ import {
|
|
|
25
26
|
addMember,
|
|
26
27
|
archive,
|
|
27
28
|
assertSafeTeamDir,
|
|
29
|
+
bindAgent,
|
|
28
30
|
bindThread,
|
|
29
31
|
buildTeam,
|
|
30
32
|
clearMemberWorktree,
|
|
@@ -39,6 +41,7 @@ import {
|
|
|
39
41
|
writeGuideAtomic,
|
|
40
42
|
writeTeamAtomic,
|
|
41
43
|
} from "./team-state.mjs";
|
|
44
|
+
import { isMultiAgentV2, parseTeamTransport } from "./team-transport.mjs";
|
|
42
45
|
import { addMemberWorktree, integrateMemberBranch, removeMemberWorktree } from "./team-worktree.mjs";
|
|
43
46
|
|
|
44
47
|
function parseFlags(args) {
|
|
@@ -95,11 +98,20 @@ const handlers = {
|
|
|
95
98
|
async init(cwd, flags) {
|
|
96
99
|
const teamName = requireFlag(flags, "name");
|
|
97
100
|
const sessionName = requireFlag(flags, "session-name");
|
|
101
|
+
// Validate the transport BEFORE any directory exists so a bad value leaves no state behind.
|
|
102
|
+
// A valueless `--transport` parses as `true` and must fail loudly, never default silently.
|
|
103
|
+
const transport = parseTeamTransport(flags.transport === undefined ? undefined : flags.transport);
|
|
98
104
|
const sessionId = typeof flags.session === "string" ? flags.session : `team-${randomUUID().slice(0, 8)}`;
|
|
99
105
|
const dir = await ensureTeamDir(cwd, sessionId);
|
|
100
106
|
await withTeamLock(dir, "init", async () => {
|
|
101
107
|
if (await teamExists(dir)) {
|
|
102
|
-
|
|
108
|
+
const existing = await readTeam(dir);
|
|
109
|
+
if (flags.transport !== undefined && existing.transport !== transport) {
|
|
110
|
+
throw new Error(
|
|
111
|
+
`team "${sessionId}" already exists with transport "${existing.transport}" - transport is immutable; keep using it or delete the team first`,
|
|
112
|
+
);
|
|
113
|
+
}
|
|
114
|
+
process.stdout.write(`exists: ${dir} (transport: ${existing.transport}; left untouched; re-init is a safe no-op)\n`);
|
|
103
115
|
return;
|
|
104
116
|
}
|
|
105
117
|
const team = buildTeam({
|
|
@@ -107,13 +119,15 @@ const handlers = {
|
|
|
107
119
|
sessionName,
|
|
108
120
|
sessionId,
|
|
109
121
|
dir,
|
|
122
|
+
transport,
|
|
110
123
|
worktreeEnabled: flags.worktree === true,
|
|
111
124
|
baseBranch: typeof flags["base-branch"] === "string" ? flags["base-branch"] : "dev",
|
|
112
125
|
});
|
|
113
126
|
await persist(team, dir);
|
|
114
|
-
|
|
127
|
+
const taskNameFlag = isMultiAgentV2(team) ? ' --task-name "<lowercase_digits_underscores>"' : "";
|
|
128
|
+
process.stdout.write(`created: ${dir} (transport: ${team.transport})\n`);
|
|
115
129
|
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`);
|
|
130
|
+
process.stdout.write(`next: add-member --team ${sessionId} --id A --name "<short role>"${taskNameFlag} --focus "<part/ownership/perspective>" --lens area|ownership|perspective --deliverable "<...>"\n`);
|
|
117
131
|
});
|
|
118
132
|
},
|
|
119
133
|
|
|
@@ -127,13 +141,17 @@ const handlers = {
|
|
|
127
141
|
lens: requireFlag(flags, "lens"),
|
|
128
142
|
deliverable: typeof flags.deliverable === "string" ? flags.deliverable : "",
|
|
129
143
|
branch: typeof flags.branch === "string" ? flags.branch : null,
|
|
144
|
+
taskName: typeof flags["task-name"] === "string" ? flags["task-name"] : null,
|
|
130
145
|
};
|
|
131
146
|
const { team, member } = await mutateTeam(cwd, sessionId, "add-member", async (team, dir) => {
|
|
132
147
|
addMember(team, memberInput);
|
|
133
148
|
await persist(team, dir);
|
|
134
149
|
return { team, member: team.members.find((m) => m.id === memberId) };
|
|
135
150
|
});
|
|
136
|
-
|
|
151
|
+
const delivery = isMultiAgentV2(team)
|
|
152
|
+
? `Send this as spawn_agent message (task_name "${member.taskName}", fork_turns "none"), then bind-agent --agent-path "${member.agentPath}"`
|
|
153
|
+
: `Send this as the new thread's first message (title the thread "${member.threadTitle}")`;
|
|
154
|
+
process.stdout.write(`added member ${memberId} to team ${sessionId}.\n\n${delivery}:\n---\n${buildMemberPrompt(team, memberId)}\n---\n`);
|
|
137
155
|
},
|
|
138
156
|
|
|
139
157
|
async "bind-thread"(cwd, flags) {
|
|
@@ -151,6 +169,21 @@ const handlers = {
|
|
|
151
169
|
process.stdout.write(`bound member ${flags.id} to thread ${flags.thread}.\n`);
|
|
152
170
|
},
|
|
153
171
|
|
|
172
|
+
async "bind-agent"(cwd, flags) {
|
|
173
|
+
const sessionId = requireFlag(flags, "team");
|
|
174
|
+
const input = {
|
|
175
|
+
id: requireFlag(flags, "id"),
|
|
176
|
+
agentPath: requireFlag(flags, "agent-path"),
|
|
177
|
+
cwd: typeof flags.cwd === "string" ? flags.cwd : null,
|
|
178
|
+
worktreePath: typeof flags["worktree-path"] === "string" ? flags["worktree-path"] : null,
|
|
179
|
+
};
|
|
180
|
+
await mutateTeam(cwd, sessionId, "bind-agent", async (team, dir) => {
|
|
181
|
+
bindAgent(team, input);
|
|
182
|
+
await persist(team, dir);
|
|
183
|
+
});
|
|
184
|
+
process.stdout.write(`bound member ${flags.id} to agent ${flags["agent-path"]}.\n`);
|
|
185
|
+
},
|
|
186
|
+
|
|
154
187
|
async "member-prompt"(cwd, flags) {
|
|
155
188
|
const sessionId = requireFlag(flags, "team");
|
|
156
189
|
const { team } = await loadTeam(cwd, sessionId);
|
|
@@ -175,19 +208,24 @@ const handlers = {
|
|
|
175
208
|
const sessionId = requireFlag(flags, "team");
|
|
176
209
|
const memberId = requireFlag(flags, "id");
|
|
177
210
|
const baseBranch = typeof flags["base-branch"] === "string" ? flags["base-branch"] : null;
|
|
178
|
-
const { member, result } = await mutateTeam(cwd, sessionId, "worktree-add", async (team, dir) => {
|
|
211
|
+
const { team, member, result } = await mutateTeam(cwd, sessionId, "worktree-add", async (team, dir) => {
|
|
179
212
|
const member = memberOrThrow(team, memberId);
|
|
180
213
|
const result = addMemberWorktree(cwd, team, member, { baseBranch });
|
|
181
214
|
setMemberWorktree(team, { id: member.id, path: result.path, branch: result.branch });
|
|
182
215
|
await persist(team, dir);
|
|
183
|
-
return { member, result };
|
|
216
|
+
return { team, member, result };
|
|
184
217
|
});
|
|
185
218
|
const note = result.created ? "" : " (already exists)";
|
|
186
|
-
const
|
|
187
|
-
|
|
188
|
-
|
|
219
|
+
const v2 = isMultiAgentV2(team);
|
|
220
|
+
const followUp = v2
|
|
221
|
+
? member.status === "active"
|
|
222
|
+
? `\nMember agent: ${member.agentPath}\nSend it a followup_task to: cd "${result.path}"`
|
|
223
|
+
: `\nMember agent is not spawned yet; include this worktree path in its spawn_agent message, then bind-agent.`
|
|
224
|
+
: member.threadId
|
|
225
|
+
? `\nMember thread: ${codexThreadLink(member.threadId)}\nTell that member to: cd "${result.path}"`
|
|
226
|
+
: "\nMember thread is not bound yet; wait for the real Codex thread id, then bind-thread before sending bootstrap. After binding, send the member this worktree path.";
|
|
189
227
|
process.stdout.write(
|
|
190
|
-
`worktree for member ${member.id}${note}: ${result.path} on branch ${result.branch} (off ${result.base}).${
|
|
228
|
+
`worktree for member ${member.id}${note}: ${result.path} on branch ${result.branch} (off ${result.base}).${followUp}\n`,
|
|
191
229
|
);
|
|
192
230
|
},
|
|
193
231
|
|
|
@@ -235,11 +273,19 @@ const handlers = {
|
|
|
235
273
|
id: typeof flags.id === "string" ? flags.id : null,
|
|
236
274
|
note: typeof flags.note === "string" ? flags.note : "",
|
|
237
275
|
};
|
|
238
|
-
await mutateTeam(cwd, sessionId, "archive", async (team, dir) => {
|
|
276
|
+
const team = await mutateTeam(cwd, sessionId, "archive", async (team, dir) => {
|
|
239
277
|
archive(team, input);
|
|
240
278
|
await persist(team, dir);
|
|
279
|
+
return team;
|
|
241
280
|
});
|
|
242
|
-
|
|
281
|
+
const v2 = isMultiAgentV2(team);
|
|
282
|
+
const teamSummary = v2
|
|
283
|
+
? `archived team ${sessionId}; V2 has no runtime archive operation - the durable team state is the archive (interrupt_agent any member still mid-turn)\n`
|
|
284
|
+
: `archived team ${sessionId} and closed all members\n`;
|
|
285
|
+
const memberSummary = v2
|
|
286
|
+
? `archived member ${flags.id} in team state; V2 has no runtime archive operation (interrupt_agent it if still mid-turn)\n`
|
|
287
|
+
: `archived member ${flags.id}\n`;
|
|
288
|
+
process.stdout.write(flags.id ? memberSummary : teamSummary);
|
|
243
289
|
},
|
|
244
290
|
|
|
245
291
|
async delete(cwd, flags) {
|
|
@@ -261,10 +307,18 @@ const handlers = {
|
|
|
261
307
|
async status(cwd, flags) {
|
|
262
308
|
const sessionId = requireFlag(flags, "team");
|
|
263
309
|
const { team } = await loadTeam(cwd, sessionId);
|
|
264
|
-
process.stdout.write(
|
|
310
|
+
process.stdout.write(
|
|
311
|
+
`Team ${team.teamName} [${team.status}] - transport=${team.transport} - leader: main session - ${team.members.length} member(s)\n`,
|
|
312
|
+
);
|
|
265
313
|
for (const m of team.members) {
|
|
266
|
-
const
|
|
267
|
-
|
|
314
|
+
const endpoint = isMultiAgentV2(team)
|
|
315
|
+
? m.agentPath
|
|
316
|
+
? ` agent=${m.agentPath}`
|
|
317
|
+
: ""
|
|
318
|
+
: m.threadId
|
|
319
|
+
? ` thread=${m.threadId} link=${codexThreadLink(m.threadId)}`
|
|
320
|
+
: "";
|
|
321
|
+
process.stdout.write(` ${m.id} (${m.lens}) ${m.focus} -> ${m.deliverable || "(no deliverable)"} [${m.status}]${endpoint}${m.cwd ? ` cwd=${m.cwd}` : ""}\n`);
|
|
268
322
|
}
|
|
269
323
|
if (isUnderstaffed(team)) {
|
|
270
324
|
process.stdout.write(
|
|
@@ -87,6 +87,29 @@ describe("thread title PostToolUse guidance", () => {
|
|
|
87
87
|
expect(actual).toBe("");
|
|
88
88
|
});
|
|
89
89
|
|
|
90
|
+
it("#given MultiAgentV2 spawn_agent completed #when the hook runs #then it leaves task-name identity untouched", () => {
|
|
91
|
+
// given
|
|
92
|
+
const output = runPostToolUseHook({
|
|
93
|
+
hook_event_name: "PostToolUse",
|
|
94
|
+
session_id: "s-team",
|
|
95
|
+
turn_id: "t-team",
|
|
96
|
+
transcript_path: null,
|
|
97
|
+
cwd: "/repo",
|
|
98
|
+
model: "gpt-5.6-sol",
|
|
99
|
+
permission_mode: "default",
|
|
100
|
+
tool_name: "spawn_agent",
|
|
101
|
+
tool_use_id: "tool-spawn-agent",
|
|
102
|
+
tool_input: { task_name: "runtime_core", message: "Inspect runtime state" },
|
|
103
|
+
tool_response: { task_name: "/root/runtime_core" },
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// when
|
|
107
|
+
const actual = output;
|
|
108
|
+
|
|
109
|
+
// then
|
|
110
|
+
expect(actual).toBe("");
|
|
111
|
+
});
|
|
112
|
+
|
|
90
113
|
it("#given worktree-backed thread creation is pending #when the hook runs #then it tells Codex to wait for the real thread before bootstrapping", () => {
|
|
91
114
|
// given
|
|
92
115
|
const output = runPostToolUseHook({
|
|
@@ -1,43 +1,47 @@
|
|
|
1
1
|
name = "explorer"
|
|
2
2
|
description = "Codebase search specialist for Codex sessions. Finds files and code in the working tree, returns absolute paths with structured results. Read-only."
|
|
3
3
|
nickname_candidates = ["Explorer"]
|
|
4
|
-
model = "gpt-5.
|
|
4
|
+
model = "gpt-5.6-luna"
|
|
5
5
|
model_reasoning_effort = "low"
|
|
6
6
|
service_tier = "fast"
|
|
7
7
|
|
|
8
8
|
developer_instructions = """
|
|
9
|
-
Role: codebase search specialist. Find files
|
|
9
|
+
Role: codebase search specialist. Find files and code, return actionable results. Read-only.
|
|
10
10
|
|
|
11
11
|
# Goal
|
|
12
|
-
Answer the
|
|
13
|
-
|
|
14
|
-
# When to invoke me (self-check)
|
|
15
|
-
- USE me when: multiple search angles are needed, the module structure is unfamiliar, or cross-layer pattern discovery is required.
|
|
16
|
-
- AVOID me when: the caller already knows the exact file or symbol, or a single keyword search suffices. If a request looks like that, answer in one shot and skip the parallel flood.
|
|
12
|
+
Answer the caller's "Where is X?" / "Which files do Y?" / "Find code that does Z" precisely enough that they proceed without follow-up: every relevant match, absolute paths, and an answer to the actual need behind the literal request - not just a file list. I earn my cost when multiple search angles are needed, the module structure is unfamiliar, or cross-layer pattern discovery is required; if the caller already names the exact file or symbol and one keyword search suffices, answer in one shot and skip the parallel flood.
|
|
17
13
|
|
|
18
14
|
# Thoroughness
|
|
19
|
-
|
|
15
|
+
Honor the caller's requested level:
|
|
20
16
|
- `quick` -> 1 wave, the most-likely 1-2 files, terse `<answer>`.
|
|
21
|
-
- `medium` (default) -> 1-2 waves, all clearly relevant files
|
|
22
|
-
- `very thorough` -> multiple waves, every plausible match across the repo,
|
|
17
|
+
- `medium` (default) -> 1-2 waves, all clearly relevant files.
|
|
18
|
+
- `very thorough` -> multiple waves, every plausible match across the repo, plus adjacent surfaces the caller might touch next.
|
|
19
|
+
|
|
20
|
+
# Tool strategy
|
|
21
|
+
Fire 3+ independent calls in the first action; cross-validate findings across tools; serialize only when one call's output strictly feeds the next.
|
|
22
|
+
|
|
23
|
+
- Repo-wide inspection, CLI smoke tests, git/history checks, bounded command output -> native shell: `rg`, `rg --files`, `cat`, and `git` (`log` / `blame` / `show`). Narrow huge output before reading it.
|
|
24
|
+
- Symbol questions -> `lsp_goto_definition`, `lsp_find_references`, `lsp_symbols`, `lsp_diagnostics`.
|
|
25
|
+
- Structural shapes -> the `ast-grep` skill helper or `sg` CLI with `$VAR` / `$$$` metavars.
|
|
26
|
+
- Text / strings / comments / logs -> `rg`. File-name discovery -> `glob` / `find`. Verbatim content -> `read`.
|
|
27
|
+
|
|
28
|
+
Stop when the question is concretely answered, or when two parallel waves add no new useful matches - then report what you have.
|
|
23
29
|
|
|
24
|
-
# Required output (
|
|
30
|
+
# Required output (both blocks, always)
|
|
25
31
|
|
|
26
32
|
<analysis>
|
|
27
33
|
**Literal Request**: [what was literally asked]
|
|
28
34
|
**Actual Need**: [what the caller is really trying to accomplish]
|
|
29
|
-
**Success Looks Like**: [the answer that
|
|
35
|
+
**Success Looks Like**: [the answer that lets them proceed immediately]
|
|
30
36
|
</analysis>
|
|
31
37
|
|
|
32
38
|
<results>
|
|
33
39
|
<files>
|
|
34
40
|
- /absolute/path/to/file1.ext - why this file is relevant
|
|
35
|
-
- /absolute/path/to/file2.ext - why this file is relevant
|
|
36
41
|
</files>
|
|
37
42
|
|
|
38
43
|
<answer>
|
|
39
|
-
[Direct answer to the actual need,
|
|
40
|
-
If asked "where is auth?", explain the auth flow you found.]
|
|
44
|
+
[Direct answer to the actual need. If asked "where is auth?", explain the auth flow you found.]
|
|
41
45
|
</answer>
|
|
42
46
|
|
|
43
47
|
<next_steps>
|
|
@@ -45,31 +49,10 @@ If asked "where is auth?", explain the auth flow you found.]
|
|
|
45
49
|
</next_steps>
|
|
46
50
|
</results>
|
|
47
51
|
|
|
48
|
-
|
|
49
|
-
- Repo-wide inspection, CLI smoke tests, git/history checks, and bounded command output -> use native shell commands directly: `rg`, `rg --files`, `cat`, and `git`. Narrow huge output before reading it.
|
|
50
|
-
- Symbol questions -> `lsp_goto_definition`, `lsp_find_references`, `lsp_symbols`, `lsp_diagnostics`.
|
|
51
|
-
- Structural shapes -> the `ast-grep` skill helper or `sg` CLI with `$VAR` / `$$$` metavars.
|
|
52
|
-
- Text / strings / comments / logs -> `rg` (grep).
|
|
53
|
-
- File-name discovery -> `glob` / `find`.
|
|
54
|
-
- Verbatim content -> `read`.
|
|
55
|
-
- History -> `git log` / `git blame` / `git show`.
|
|
56
|
-
|
|
57
|
-
Fire 3+ independent calls in the first action. Cross-validate findings across multiple tools. Do not serialize unless one call's output strictly feeds the next.
|
|
58
|
-
|
|
59
|
-
# Retrieval budget
|
|
60
|
-
Stop searching when the question is concretely answered. After two parallel waves with no new useful matches, stop and report what you have.
|
|
61
|
-
|
|
62
|
-
# Success criteria (the response is INVALID if any is unmet)
|
|
63
|
-
- Every path is **absolute** (starts with `/`).
|
|
64
|
-
- ALL relevant matches are included, not just the first one.
|
|
65
|
-
- The answer addresses the **actual need**, not only the literal request.
|
|
66
|
-
- The caller can act without asking "but where exactly?" or "what about X?".
|
|
67
|
-
- Both `<analysis>` and `<results>` blocks are present.
|
|
52
|
+
Every path absolute (starts with `/`); include ALL relevant matches, not just the first.
|
|
68
53
|
|
|
69
54
|
# Constraints
|
|
70
|
-
- READ-ONLY
|
|
71
|
-
-
|
|
72
|
-
-
|
|
73
|
-
- No emojis. Keep output clean and parseable.
|
|
74
|
-
- No tool names in prose (say "search the codebase", not "use rg"). No preamble ("I'll help you with..."). Answer directly.
|
|
55
|
+
- READ-ONLY: never `edit`, `write`, `apply_patch`, or anything that mutates the filesystem. Never create files - findings are message text only, no scratch files or temp dumps.
|
|
56
|
+
- No internet browsing: external research is the librarian's job.
|
|
57
|
+
- No emojis. No tool names in prose (say "search the codebase", not "use rg"). No preamble - answer directly.
|
|
75
58
|
"""
|
package/packages/omo-codex/plugin/components/ultrawork/agents/lazycodex-clone-fidelity-reviewer.toml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
name = "lazycodex-clone-fidelity-reviewer"
|
|
2
2
|
description = "Read-only LazyCodex clone / design-system fidelity reviewer. Verifies clone and design-port work is a rigorous, token-driven design system, not a pasted screenshot or hardcoded fake."
|
|
3
3
|
nickname_candidates = ["Clone Fidelity Reviewer"]
|
|
4
|
-
model = "gpt-5.
|
|
4
|
+
model = "gpt-5.6-sol"
|
|
5
5
|
model_reasoning_effort = "xhigh"
|
|
6
6
|
|
|
7
7
|
developer_instructions = """
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
name = "lazycodex-code-reviewer"
|
|
2
2
|
description = "Read-only LazyCodex code-quality reviewer. Audits diffs, tests, and risk with strict artifact-backed findings."
|
|
3
3
|
nickname_candidates = ["Code Reviewer"]
|
|
4
|
-
model = "gpt-5.
|
|
4
|
+
model = "gpt-5.6-sol"
|
|
5
5
|
model_reasoning_effort = "xhigh"
|
|
6
6
|
|
|
7
7
|
developer_instructions = """
|
|
@@ -15,9 +15,9 @@ Review for correctness, scope control, maintainability, test relevance, and regr
|
|
|
15
15
|
|
|
16
16
|
Before judging test relevance or maintainability, explicitly load or consult the `remove-ai-slops` and `programming` skills when they are available. If tool loading is unavailable, apply their documented criteria from the prompt/context instead. Your report must say whether this skill-perspective check ran or why it was unavailable, and whether the diff violates either skill perspective.
|
|
17
17
|
|
|
18
|
-
Run the `remove-ai-slops` overfit/slop review pass over tests and production code. Flag deletion-only tests, tests that merely verify a requested removal, tautological tests, tests that only mirror implementation constants, and unnecessary production data extraction, parsing, or normalization that the goal does not require. Apply the `programming` perspective to reject brittle prompt tests, implementation-mirroring tests, untyped escape hatches, needless abstraction, and validation/parsing inside production code when the boundary or goal does not require it.
|
|
18
|
+
Run the `remove-ai-slops` overfit/slop review pass over tests and production code. Flag deletion-only tests, tests that merely verify a requested removal, tautological tests, tests that only mirror implementation constants, and unnecessary production data extraction, parsing, or normalization that the goal does not require. Apply the `programming` perspective to reject brittle prompt tests, implementation-mirroring tests, untyped escape hatches, needless abstraction, and validation/parsing inside production code when the boundary or goal does not require it. Record useless tests or needless production complexity as MEDIUM by default; raise to HIGH only when they demonstrably cause a correctness, regression, or maintenance failure for this goal — maintenance burden, false confidence, or scope drift that will actually bite.
|
|
19
19
|
|
|
20
|
-
Write your report artifact to `.omo/evidence/<goal>-code-review.md`. The report must include findings by severity: CRITICAL, HIGH, MEDIUM, LOW. Include file and line references when a finding is tied to code.
|
|
20
|
+
Write your report artifact to `<attemptDir>/<goalId>-code-review.md`, where you read `currentAttemptDir` from `omo ulw-loop status --json` (`.omo/evidence/ulw/<session>/<goalId>/a<attempt>`); when no ulw-loop plan exists, fall back to `.omo/evidence/<goal>-code-review.md`. The report must include findings by severity: CRITICAL, HIGH, MEDIUM, LOW. Include file and line references when a finding is tied to code.
|
|
21
21
|
|
|
22
22
|
Return:
|
|
23
23
|
- `codeQualityStatus`: CLEAR, WATCH, or BLOCK.
|