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
|
@@ -182,6 +182,7 @@ export async function runPostToolUseHook(
|
|
|
182
182
|
completedPostCompactKind !== undefined
|
|
183
183
|
? withPostCompactBudget(dynamicConfig, { model: input.model, transcriptPath: input.transcript_path })
|
|
184
184
|
: dynamicConfig,
|
|
185
|
+
input.model,
|
|
185
186
|
);
|
|
186
187
|
hydrateEngineState(engine, cachePath);
|
|
187
188
|
debugTimer.lap("hydrate", {
|
|
@@ -189,7 +190,7 @@ export async function runPostToolUseHook(
|
|
|
189
190
|
dynamicTargetFingerprints: engine.state.dynamicTargetFingerprints.size,
|
|
190
191
|
staticDedup: engine.state.staticDedup.size,
|
|
191
192
|
});
|
|
192
|
-
const dynamicTargetFingerprints = fingerprintDynamicTargets(input.cwd, targetPaths, config);
|
|
193
|
+
const dynamicTargetFingerprints = fingerprintDynamicTargets(input.cwd, targetPaths, config, input.model);
|
|
193
194
|
debugTimer.lap("fingerprint", { fingerprints: dynamicTargetFingerprints.length });
|
|
194
195
|
const pendingTargetFingerprints = dynamicTargetFingerprints.filter(
|
|
195
196
|
(target) => engine.state.dynamicTargetFingerprints.get(target.cacheKey) !== target.fingerprint,
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
import { statSync } from "node:fs";
|
|
2
2
|
import { resolve } from "node:path";
|
|
3
|
-
import { isSameOrChildPath, toPosixPath, uniqueStrings } from "./path-utils.js";
|
|
4
|
-
import { createRuleDiscoveryCache, findRuleCandidates } from "@oh-my-opencode/rules-engine/engine";
|
|
5
|
-
import { hashContent } from "@oh-my-opencode/rules-engine/engine";
|
|
6
|
-
import { sortCandidates } from "@oh-my-opencode/rules-engine/engine";
|
|
7
|
-
import { findProjectRoot } from "@oh-my-opencode/rules-engine/engine";
|
|
8
|
-
import { disabledSourcesFromConfig } from "@oh-my-opencode/rules-engine/engine";
|
|
9
3
|
import type { PiRulesConfig, RuleCandidate } from "@oh-my-opencode/rules-engine/engine";
|
|
4
|
+
import {
|
|
5
|
+
createRuleDiscoveryCache,
|
|
6
|
+
disabledSourcesFromConfig,
|
|
7
|
+
findProjectRoot,
|
|
8
|
+
findRuleCandidates,
|
|
9
|
+
hashContent,
|
|
10
|
+
sortCandidates,
|
|
11
|
+
} from "@oh-my-opencode/rules-engine/engine";
|
|
12
|
+
import { isSameOrChildPath, toPosixPath, uniqueStrings } from "./path-utils.js";
|
|
10
13
|
|
|
11
14
|
export interface DynamicTargetFingerprint {
|
|
12
15
|
targetPath: string;
|
|
@@ -18,6 +21,7 @@ export function fingerprintDynamicTargets(
|
|
|
18
21
|
cwd: string,
|
|
19
22
|
targetPaths: ReadonlyArray<string>,
|
|
20
23
|
config: PiRulesConfig,
|
|
24
|
+
model?: string,
|
|
21
25
|
): DynamicTargetFingerprint[] {
|
|
22
26
|
const disabledSources = disabledSourcesFromConfig(config);
|
|
23
27
|
const discoveryCache = createRuleDiscoveryCache();
|
|
@@ -34,6 +38,7 @@ export function fingerprintDynamicTargets(
|
|
|
34
38
|
targetFile: string;
|
|
35
39
|
disabledSources?: ReadonlySet<string>;
|
|
36
40
|
cache: ReturnType<typeof createRuleDiscoveryCache>;
|
|
41
|
+
model?: string;
|
|
37
42
|
} = {
|
|
38
43
|
projectRoot,
|
|
39
44
|
targetFile: targetPath,
|
|
@@ -42,6 +47,9 @@ export function fingerprintDynamicTargets(
|
|
|
42
47
|
if (disabledSources !== undefined) {
|
|
43
48
|
findOptions.disabledSources = disabledSources;
|
|
44
49
|
}
|
|
50
|
+
if (model !== undefined) {
|
|
51
|
+
findOptions.model = model;
|
|
52
|
+
}
|
|
45
53
|
const candidates = findRuleCandidates(findOptions);
|
|
46
54
|
const candidateFingerprint = sortCandidates(candidates).map(fingerprintCandidate).join("\u0001");
|
|
47
55
|
const cacheKey = dynamicTargetCacheKey(targetPath);
|
|
@@ -52,6 +60,7 @@ export function fingerprintDynamicTargets(
|
|
|
52
60
|
[
|
|
53
61
|
"v1",
|
|
54
62
|
config.enabledSources === "auto" ? "auto" : config.enabledSources.join(","),
|
|
63
|
+
model ?? "",
|
|
55
64
|
projectRoot ?? "",
|
|
56
65
|
cacheKey,
|
|
57
66
|
candidateFingerprint,
|
|
@@ -14,12 +14,17 @@ interface RulesEngineFactoryOptions {
|
|
|
14
14
|
|
|
15
15
|
const componentRoot = dirname(dirname(fileURLToPath(import.meta.url)));
|
|
16
16
|
|
|
17
|
-
export function createRulesEngine(
|
|
17
|
+
export function createRulesEngine(
|
|
18
|
+
options: RulesEngineFactoryOptions,
|
|
19
|
+
config = configFromEnvironment(options.env),
|
|
20
|
+
model?: string,
|
|
21
|
+
) {
|
|
18
22
|
const platform = options.platform ?? process.platform;
|
|
19
23
|
const pluginRoot = options.env?.["PLUGIN_ROOT"] ?? process.env["PLUGIN_ROOT"] ?? componentRoot;
|
|
20
24
|
|
|
21
25
|
return createEngine(config, {
|
|
22
|
-
findCandidates: (finderOptions) =>
|
|
26
|
+
findCandidates: (finderOptions) =>
|
|
27
|
+
findRuleCandidates({ ...finderOptions, platform, pluginRoot, ...(model === undefined ? {} : { model }) }),
|
|
23
28
|
findProjectRoot,
|
|
24
29
|
readFile: (path) => {
|
|
25
30
|
try {
|
|
@@ -47,7 +47,7 @@ export function runStaticInjection(
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
const effectiveConfig = eventName === "UserPromptSubmit" ? withPromptBudget(config) : config;
|
|
50
|
-
const engine = createRulesEngine(options, effectiveConfig);
|
|
50
|
+
const engine = createRulesEngine(options, effectiveConfig, model);
|
|
51
51
|
hydrateEngineState(engine, cachePath);
|
|
52
52
|
engine.state.cwd = cwd;
|
|
53
53
|
|
|
@@ -89,7 +89,7 @@ function runPostCompactRecovery(input: PostCompactRecoveryInput): string {
|
|
|
89
89
|
model: input.model,
|
|
90
90
|
transcriptPath: input.transcriptPath,
|
|
91
91
|
});
|
|
92
|
-
const engine = createRulesEngine(input.options, effectiveConfig);
|
|
92
|
+
const engine = createRulesEngine(input.options, effectiveConfig, input.model);
|
|
93
93
|
hydrateEngineState(engine, input.cachePath);
|
|
94
94
|
engine.state.cwd = input.cwd;
|
|
95
95
|
|
|
@@ -28,6 +28,7 @@ type SessionCache = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
const CLI_PATH = fileURLToPath(new URL("../dist/cli.js", import.meta.url));
|
|
31
|
+
const COMPONENT_ROOT = fileURLToPath(new URL("..", import.meta.url));
|
|
31
32
|
|
|
32
33
|
function runHookCli(input: string, subcommand = "post-tool-use", env: NodeJS.ProcessEnv = {}): Promise<CliResult> {
|
|
33
34
|
return new Promise((resolve, reject) => {
|
|
@@ -70,6 +71,12 @@ const RULES_ONLY_ENV = {
|
|
|
70
71
|
CODEX_RULES_ENABLED_SOURCES: ".omo/rules",
|
|
71
72
|
};
|
|
72
73
|
|
|
74
|
+
const DYNAMIC_BUNDLED_ONLY_ENV = {
|
|
75
|
+
CODEX_RULES_MODE: "dynamic",
|
|
76
|
+
CODEX_RULES_ENABLED_SOURCES: "plugin-bundled",
|
|
77
|
+
PLUGIN_ROOT: COMPONENT_ROOT,
|
|
78
|
+
};
|
|
79
|
+
|
|
73
80
|
afterEach(() => {
|
|
74
81
|
for (const directory of tempDirectories.splice(0)) {
|
|
75
82
|
rmSync(directory, { recursive: true, force: true });
|
|
@@ -411,6 +418,31 @@ describe("codex rules hooks", () => {
|
|
|
411
418
|
expect(readSessionCache(pluginData).dynamicTargetFingerprints).toEqual(cachedState.dynamicTargetFingerprints);
|
|
412
419
|
});
|
|
413
420
|
|
|
421
|
+
it("#given bundled dynamic rules already injected for gpt-5.5 #when same target switches to gpt-5.6 #then emits the gpt-5.6 rule", async () => {
|
|
422
|
+
// given
|
|
423
|
+
const { root, pluginData } = makeTempProject();
|
|
424
|
+
const filePath = path.join(root, "src", "app.ts");
|
|
425
|
+
const input = postToolUseInput(root, filePath);
|
|
426
|
+
const firstOutput = await runPostToolUseHook(input, {
|
|
427
|
+
pluginDataRoot: pluginData,
|
|
428
|
+
env: DYNAMIC_BUNDLED_ONLY_ENV,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
// when
|
|
432
|
+
const secondOutput = await runPostToolUseHook(
|
|
433
|
+
{ ...input, model: "gpt-5.6-codex" },
|
|
434
|
+
{ pluginDataRoot: pluginData, env: DYNAMIC_BUNDLED_ONLY_ENV },
|
|
435
|
+
);
|
|
436
|
+
|
|
437
|
+
// then
|
|
438
|
+
const firstContext = parseHookOutput(firstOutput).hookSpecificOutput?.additionalContext ?? "";
|
|
439
|
+
const secondContext = parseHookOutput(secondOutput).hookSpecificOutput?.additionalContext ?? "";
|
|
440
|
+
expect(firstContext).toContain("based on GPT-5.5");
|
|
441
|
+
expect(firstContext).not.toContain("based on GPT-5.6");
|
|
442
|
+
expect(secondContext).toContain("based on GPT-5.6");
|
|
443
|
+
expect(secondContext).not.toContain("based on GPT-5.5");
|
|
444
|
+
});
|
|
445
|
+
|
|
414
446
|
it("#given default auto sources #when excluded AGENTS.md changes #then PostToolUse fingerprint stays stable", async () => {
|
|
415
447
|
// given
|
|
416
448
|
const { root, pluginData } = makeTempProject();
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { tmpdir } from "node:os";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { afterEach, describe, expect, it } from "vitest";
|
|
5
|
+
|
|
6
|
+
import { type CodexSessionStartInput, runSessionStartHook } from "../src/codex-hook.js";
|
|
7
|
+
import { findPluginBundledCandidates } from "@oh-my-opencode/rules-engine/engine";
|
|
8
|
+
|
|
9
|
+
const GPT_55_VARIANT_PATH = "bundled-rules/hephaestus/gpt-5.5.md";
|
|
10
|
+
const GPT_56_VARIANT_PATH = "bundled-rules/hephaestus/gpt-5.6.md";
|
|
11
|
+
const BUNDLED_ONLY_ENV = {
|
|
12
|
+
CODEX_RULES_ENABLED_SOURCES: "plugin-bundled",
|
|
13
|
+
};
|
|
14
|
+
const tempDirectories: string[] = [];
|
|
15
|
+
let originalPluginRoot: string | undefined;
|
|
16
|
+
|
|
17
|
+
afterEach(() => {
|
|
18
|
+
restoreEnv("PLUGIN_ROOT", originalPluginRoot);
|
|
19
|
+
for (const directory of tempDirectories.splice(0)) {
|
|
20
|
+
rmSync(directory, { recursive: true, force: true });
|
|
21
|
+
}
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function makeProject(): { readonly root: string; readonly pluginData: string } {
|
|
25
|
+
originalPluginRoot = process.env["PLUGIN_ROOT"];
|
|
26
|
+
process.env["PLUGIN_ROOT"] = process.cwd();
|
|
27
|
+
const root = mkdtempSync(join(tmpdir(), "codex-rules-hephaestus-variant-project-"));
|
|
28
|
+
const pluginData = mkdtempSync(join(tmpdir(), "codex-rules-hephaestus-variant-data-"));
|
|
29
|
+
tempDirectories.push(root, pluginData);
|
|
30
|
+
writeFileSync(join(root, "package.json"), JSON.stringify({ name: "fixture" }));
|
|
31
|
+
return { root, pluginData };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function sessionStartInput(root: string, model: string): CodexSessionStartInput {
|
|
35
|
+
return {
|
|
36
|
+
session_id: "session-1",
|
|
37
|
+
transcript_path: null,
|
|
38
|
+
cwd: root,
|
|
39
|
+
hook_event_name: "SessionStart",
|
|
40
|
+
model,
|
|
41
|
+
permission_mode: "default",
|
|
42
|
+
source: "startup",
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function restoreEnv(name: string, value: string | undefined): void {
|
|
47
|
+
if (value === undefined) {
|
|
48
|
+
delete process.env[name];
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
process.env[name] = value;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe("Hephaestus bundled rule model variants", () => {
|
|
55
|
+
it("#given packaged bundled rules #when discovering with a gpt-5.5 model #then only the gpt-5.5 variant is included", () => {
|
|
56
|
+
const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd(), model: "gpt-5.5" });
|
|
57
|
+
const paths = candidates.map((candidate) => candidate.relativePath);
|
|
58
|
+
|
|
59
|
+
expect(paths).toContain(GPT_55_VARIANT_PATH);
|
|
60
|
+
expect(paths).not.toContain(GPT_56_VARIANT_PATH);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it("#given packaged bundled rules #when discovering with a gpt-5.6 family model #then only the gpt-5.6 variant is included", () => {
|
|
64
|
+
const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd(), model: "gpt-5.6-codex" });
|
|
65
|
+
const paths = candidates.map((candidate) => candidate.relativePath);
|
|
66
|
+
|
|
67
|
+
expect(paths).toContain(GPT_56_VARIANT_PATH);
|
|
68
|
+
expect(paths).not.toContain(GPT_55_VARIANT_PATH);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it("#given packaged bundled rules #when discovering without a model #then the gpt-5.5 variant is the fallback", () => {
|
|
72
|
+
const candidates = findPluginBundledCandidates({ pluginRoot: process.cwd() });
|
|
73
|
+
const paths = candidates.map((candidate) => candidate.relativePath);
|
|
74
|
+
|
|
75
|
+
expect(paths).toContain(GPT_55_VARIANT_PATH);
|
|
76
|
+
expect(paths).not.toContain(GPT_56_VARIANT_PATH);
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
it("#given a gpt-5.5 session #when SessionStart runs #then the gpt-5.5 identity is injected in full", async () => {
|
|
80
|
+
const { root, pluginData } = makeProject();
|
|
81
|
+
|
|
82
|
+
const output = await runSessionStartHook(sessionStartInput(root, "gpt-5.5"), {
|
|
83
|
+
pluginDataRoot: pluginData,
|
|
84
|
+
env: BUNDLED_ONLY_ENV,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
expect(output).toContain(`Instructions from: ${join(process.cwd(), GPT_55_VARIANT_PATH)}`);
|
|
88
|
+
expect(output).toContain("based on GPT-5.5");
|
|
89
|
+
expect(output).not.toContain("based on GPT-5.6");
|
|
90
|
+
expect(output).not.toContain("[Truncated. Full:");
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("#given a gpt-5.6 session #when SessionStart runs #then the gpt-5.6 identity is injected in full", async () => {
|
|
94
|
+
const { root, pluginData } = makeProject();
|
|
95
|
+
|
|
96
|
+
const output = await runSessionStartHook(sessionStartInput(root, "gpt-5.6-codex"), {
|
|
97
|
+
pluginDataRoot: pluginData,
|
|
98
|
+
env: BUNDLED_ONLY_ENV,
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
expect(output).toContain(`Instructions from: ${join(process.cwd(), GPT_56_VARIANT_PATH)}`);
|
|
102
|
+
expect(output).toContain("based on GPT-5.6");
|
|
103
|
+
expect(output).not.toContain("based on GPT-5.5");
|
|
104
|
+
expect(output).not.toContain("[Truncated. Full:");
|
|
105
|
+
});
|
|
106
|
+
});
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
"type": "command",
|
|
8
8
|
"command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook stop",
|
|
9
9
|
"timeout": 10,
|
|
10
|
-
"statusMessage": "(OmO 4.
|
|
10
|
+
"statusMessage": "(OmO 4.17.0) Checking Start-Work Continuation"
|
|
11
11
|
}
|
|
12
12
|
]
|
|
13
13
|
}
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"type": "command",
|
|
20
20
|
"command": "node \"${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js\" hook subagent-stop",
|
|
21
21
|
"timeout": 10,
|
|
22
|
-
"statusMessage": "(OmO 4.
|
|
22
|
+
"statusMessage": "(OmO 4.17.0) Checking Start-Work Continuation"
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
package/packages/omo-codex/plugin/components/start-work-continuation/test/codex-hook.test.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mkdirSync, mkdtempSync,
|
|
1
|
+
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
|
2
2
|
import { tmpdir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import { afterEach, describe, expect, it } from "vitest";
|
|
@@ -96,84 +96,6 @@ describe("start-work Stop hook", () => {
|
|
|
96
96
|
expect(output).toBe("");
|
|
97
97
|
});
|
|
98
98
|
|
|
99
|
-
it("#given active codex work #when continuation directive is emitted #then subagent guidance is reliable", () => {
|
|
100
|
-
// given
|
|
101
|
-
const workspace = createWorkspace({
|
|
102
|
-
boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
|
|
103
|
-
planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
|
|
104
|
-
});
|
|
105
|
-
const fs = createMemoryFs();
|
|
106
|
-
|
|
107
|
-
// when
|
|
108
|
-
const output = runStopHook(createStopInput(workspace), fs);
|
|
109
|
-
|
|
110
|
-
// then
|
|
111
|
-
const parsed = parseBlockOutput(output);
|
|
112
|
-
expect(parsed.reason).toMatch(/TASK:/);
|
|
113
|
-
expect(parsed.reason).toMatch(/fork_context:\s*false/);
|
|
114
|
-
expect(parsed.reason).toMatch(/wait_agent.*mailbox signals/);
|
|
115
|
-
expect(parsed.reason).toMatch(/TASK STILL ACTIVE/);
|
|
116
|
-
expect(parsed.reason).toMatch(/respawn.*smaller/);
|
|
117
|
-
expect(parsed.reason).toMatch(/WORKING:/);
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
it("#given active codex work #when continuation directive is emitted #then QA weight is tier-scoped without echo bloat", () => {
|
|
121
|
-
// given
|
|
122
|
-
const workspace = createWorkspace({
|
|
123
|
-
boulderJson: createBoulderJson({ sessionIds: ["codex:sess_abc"], status: "active" }),
|
|
124
|
-
planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
|
|
125
|
-
});
|
|
126
|
-
const fs = createMemoryFs();
|
|
127
|
-
|
|
128
|
-
// when
|
|
129
|
-
const output = runStopHook(createStopInput(workspace), fs);
|
|
130
|
-
|
|
131
|
-
// then
|
|
132
|
-
const parsed = parseBlockOutput(output);
|
|
133
|
-
expect(parsed.reason).toMatch(/LIGHT/);
|
|
134
|
-
expect(parsed.reason).toMatch(/HEAVY/);
|
|
135
|
-
expect(parsed.reason).toMatch(/When unsure[^.]{0,30}HEAVY/);
|
|
136
|
-
expect(parsed.reason).toMatch(/mirrors its implementation/);
|
|
137
|
-
expect((parsed.reason.match(/malformed input, prompt injection/g) ?? []).length).toBe(1);
|
|
138
|
-
expect(parsed.reason.split(/\s+/).filter(Boolean).length).toBeLessThanOrEqual(1135);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it("#given active codex work #when continuation directive is emitted #then PR lifecycle stays worktree-bound", () => {
|
|
142
|
-
// given
|
|
143
|
-
const workspace = createWorkspace({
|
|
144
|
-
boulderJson: createBoulderJson({
|
|
145
|
-
sessionIds: ["codex:sess_abc"],
|
|
146
|
-
status: "active",
|
|
147
|
-
worktreePath: "/tmp/worktree",
|
|
148
|
-
}),
|
|
149
|
-
planMarkdown: ["# Plan", "", "## TODOs", "- [ ] First"].join("\n"),
|
|
150
|
-
});
|
|
151
|
-
const fs = createMemoryFs();
|
|
152
|
-
|
|
153
|
-
// when
|
|
154
|
-
const output = runStopHook(createStopInput(workspace), fs);
|
|
155
|
-
|
|
156
|
-
// then
|
|
157
|
-
const parsed = parseBlockOutput(output);
|
|
158
|
-
expect(parsed.reason).toContain("PR or branch implementation/review/merge work requires a task-owned git worktree");
|
|
159
|
-
expect(parsed.reason).toContain("Treat the main worktree as read-only context");
|
|
160
|
-
expect(parsed.reason).toContain("create/update the PR, wait for CI/review/Cubic gates, merge by default");
|
|
161
|
-
expect(parsed.reason).toContain("Do not create a PR, PR handoff, branch handoff, merge");
|
|
162
|
-
});
|
|
163
|
-
|
|
164
|
-
it("#given stop hook source #when inspected #then it remains Boulder-only without planning bootstrap logic", () => {
|
|
165
|
-
// given
|
|
166
|
-
const hook = readFileSync(new URL("../src/codex-hook.ts", import.meta.url), "utf8");
|
|
167
|
-
|
|
168
|
-
// then
|
|
169
|
-
expect(hook).toMatch(/readContinuationState/);
|
|
170
|
-
expect(hook).toMatch(/START_WORK_CONTINUATION_DIRECTIVE/);
|
|
171
|
-
expect(hook).toMatch(/decision:\s*"block"/);
|
|
172
|
-
expect(hook).not.toMatch(
|
|
173
|
-
/\bulw-plan\b|\bspawn_agent\b|\brequest_user_input\b|bootstrap|selectable plan|Phase 1|Create or update Boulder state/i,
|
|
174
|
-
);
|
|
175
|
-
});
|
|
176
|
-
|
|
177
99
|
it("#given active work belongs to another harness #when hook runs #then returns empty output", () => {
|
|
178
100
|
// given
|
|
179
101
|
const workspace = createWorkspace({
|
|
@@ -7,18 +7,19 @@
|
|
|
7
7
|
Codex team-mode component. Two deliverables in one dir:
|
|
8
8
|
|
|
9
9
|
- **PostToolUse hook** (`@sisyphuslabs/codex-teammode`): fires after `create_thread` / `codex_app.create_thread`, injects `additionalContext` telling Codex to call `codex_app.set_thread_title` NOW with the real task/role. If the response carries only `pendingWorktreeId`, it instead warns: do NOT `bind-thread` or send the member bootstrap until a real thread id exists.
|
|
10
|
-
- **`teammode` skill**: script-driven orchestration of a named team of Codex threads. Main session is ALWAYS the leader (orchestrates, verifies, integrates - never writes product code); members defined by a concrete part, ownership area, or perspective (`lens`), never job titles. Durable state under `.omo/teams/{session_id}/`: `team.json` + auto-generated `guide.md` member field manual + `artifacts/` exchange dir.
|
|
10
|
+
- **`teammode` skill**: script-driven orchestration of a named team of Codex workers on ONE immutable transport chosen at init: `multi_agent_v2` (preferred; members are native flat `spawn_agent` agents addressed by `task_name` / `/root/<task_name>` agent path, messaged with `send_message`/`followup_task`) or `codex_app` (fallback; members are app threads addressed by thread id). The leader inspects the active tool list and announces the selected transport to the user BEFORE `init`. Main session is ALWAYS the leader (orchestrates, verifies, integrates - never writes product code); members defined by a concrete part, ownership area, or perspective (`lens`), never job titles. Durable state under `.omo/teams/{session_id}/`: `team.json` + auto-generated `guide.md` member field manual + `artifacts/` exchange dir.
|
|
11
11
|
|
|
12
12
|
## KEY FILES
|
|
13
13
|
|
|
14
14
|
- `src/codex-hook.ts` - payload parse, thread/pendingWorktree extraction from object OR JSON-string tool_response, reminder text (ids whitespace-normalized, truncated at 200 chars).
|
|
15
15
|
- `src/cli.ts` - argv dispatch; only `hook post-tool-use`.
|
|
16
16
|
- `hooks/hooks.json` - matcher `^(create_thread|codex_app\.create_thread)$`, runs `node ${PLUGIN_ROOT}/dist/cli.js hook post-tool-use`, timeout 10.
|
|
17
|
-
- `skills/teammode/SKILL.md` - leader protocol: team-vs-subagent decision matrix, ulw-plan parallel-wave mapping, "Let members work - do not rush them" (4-reason message gate; quiet between heartbeats = working, not stalled), archive/delete/stop rules.
|
|
18
|
-
- `skills/teammode/scripts/team.mjs` - controller CLI: init / add-member / bind-thread / member-prompt / set-status / worktree-add / worktree-remove / integrate / archive / delete / status / guide. Zero npm deps, node builtins only, runs under node or bun on all 3 OSes.
|
|
19
|
-
- `skills/teammode/scripts/team-
|
|
17
|
+
- `skills/teammode/SKILL.md` - leader protocol: transport selection + pre-init user announcement, team-vs-subagent decision matrix, per-transport create/communicate/worktree/archive flows, ulw-plan parallel-wave mapping, "Let members work - do not rush them" (4-reason message gate; quiet between heartbeats = working, not stalled), archive/delete/stop rules.
|
|
18
|
+
- `skills/teammode/scripts/team.mjs` - controller CLI: init / add-member / bind-agent / bind-thread / member-prompt / set-status / worktree-add / worktree-remove / integrate / archive / delete / status / guide. Zero npm deps, node builtins only, runs under node or bun on all 3 OSes.
|
|
19
|
+
- `skills/teammode/scripts/team-transport.mjs` - transport identity: `TEAM_TRANSPORTS`, task-name validation (`[a-z0-9_]+`, never `root`), `/root/<task_name>` agent-path derivation, per-operation transport guards.
|
|
20
|
+
- `skills/teammode/scripts/team-state.mjs` - state model + persistence: per-team `.team.lock` dir lock (`owner.json`; `OMO_TEAMMODE_LOCK_TIMEOUT_MS` / `OMO_TEAMMODE_LOCK_RETRY_MS`), atomic tmp+rename writes, symlink- and escape-guarded team dirs, schemaVersion 3 (legacy 2 migrates in memory to `codex_app` and persists on the next mutation), `MIN_MEMBERS = 2`, unique focus/name invariants plus per-transport identity invariants (taskName/agentPath on V2, threadTitle on codex_app).
|
|
20
21
|
- `skills/teammode/scripts/team-worktree.mjs` - git worktree provisioning + `merge --no-ff --no-edit` integration; conflict leaves the tree mid-merge for the leader to resolve and re-run.
|
|
21
|
-
- `skills/teammode/scripts/team-guide.mjs` - pure string builders for `guide.md` + the short member bootstrap trigger (`codex://threads/<id>` deep links, `WORKING:` / `BLOCKED:` heartbeat rules, English-only member traffic
|
|
22
|
+
- `skills/teammode/scripts/team-guide.mjs` - pure string builders for `guide.md` + the short member bootstrap trigger, branched by transport (V2: `/root` leader target + `members[].agentPath` peers + `send_message`/`followup_task`; codex_app: `codex_app.send_message_to_thread` + `codex://threads/<id>` deep links), `WORKING:` / `BLOCKED:` heartbeat rules, English-only member traffic.
|
|
22
23
|
- `test/thread-title-hook.test.ts` - hook unit tests (vitest-style imports, run via `bun test`).
|
|
23
24
|
|
|
24
25
|
## WHERE TO LOOK
|
|
@@ -26,8 +27,9 @@ Codex team-mode component. Two deliverables in one dir:
|
|
|
26
27
|
| Task | Location |
|
|
27
28
|
|------|----------|
|
|
28
29
|
| Hook reminder text | `src/codex-hook.ts` `threadTitleReminder` + `test/thread-title-hook.test.ts`; `plugin/test/teammode-thread-title.test.mjs` pins the exact strings |
|
|
29
|
-
| Skill protocol text | `skills/teammode/SKILL.md`; contract tests `plugin/test/teammode-{communication,worktree,safety,archive-ambiguity,thread-links}.test.mjs` pin phrases |
|
|
30
|
-
| team.json shape | `team-state.mjs` (single source of the shape; `validateTeam`
|
|
30
|
+
| Skill protocol text | `skills/teammode/SKILL.md`; contract tests `plugin/test/teammode-{transport,communication,worktree,safety,archive-ambiguity,thread-links}.test.mjs` pin phrases |
|
|
31
|
+
| team.json shape | `team-state.mjs` (single source of the shape; `validateTeam` migrates schemaVersion 2 to 3 and rejects anything else) |
|
|
32
|
+
| Transport rules | `team-transport.mjs` (`parseTeamTransport`, `parseTaskName`, `assertTransport`) + `plugin/test/teammode-transport.test.mjs` |
|
|
31
33
|
| Branch naming, worktree paths, merge | `team-worktree.mjs` (`team/<sessionId>/<memberId>` branches, worktrees confined to `<team dir>/worktrees/<memberId>`) |
|
|
32
34
|
| Hook wiring | `hooks/hooks.json` (component) + `plugin/hooks/post-tool-use-checking-thread-title-hygiene.json` (aggregate; adds `commandWindows` powershell node-dispatch) |
|
|
33
35
|
|
|
@@ -40,7 +42,8 @@ Codex team-mode component. Two deliverables in one dir:
|
|
|
40
42
|
- All mutating subcommands serialize through `withTeamLock` and re-read committed state before writing. "team state is locked by ..." means the mutation did NOT happen; retry, or inspect `.team.lock/owner.json` after a crash.
|
|
41
43
|
- Archive ambiguity: "Ambiguous Codex thread id" from `codex_app.set_thread_archived` is an app-thread archival blocker, not a team-state blocker. Record it via `archive --note`, never claim the thread was archived, never delete team state before evidence is copied or the user accepts the loss.
|
|
42
44
|
- `integrate` lands member branches with merge commits only (`--no-ff`); never squash or rebase.
|
|
43
|
-
- `bind-thread`
|
|
44
|
-
-
|
|
45
|
+
- `bind-agent`/`bind-thread` refuse while the team has fewer than 2 members; a single-member team is a subagent, not a team. Member focus and name must be unique (case/whitespace-insensitive); V2 adds unique taskName/agentPath, codex_app adds unique threadTitle.
|
|
46
|
+
- Binding is transport-strict and mutation-safe: `bind-thread` on a V2 team and `bind-agent` on a codex_app team fail before persisting, leaving `team.json` byte-identical; `bind-agent` also rejects any path other than the member's precomputed `/root/<task_name>`.
|
|
47
|
+
- On `multi_agent_v2` teams a member IS a durable flat `spawn_agent` agent (spawned with `fork_turns: "none"`, re-tasked via `followup_task`, no title/deep-link/archive primitive - `interrupt_agent` + durable team state is the archive story). On `codex_app` teams every member is a real `codex_app.create_thread` thread bound via `bind-thread`, and a spawned in-process subagent is never a substitute there. V2 has no spawn-time cwd: create worktrees BEFORE spawn so the bootstrap carries the path.
|
|
45
48
|
- The hook must stay silent (empty stdout) on unrelated tools and malformed payloads; it never blocks a Codex turn.
|
|
46
49
|
- `init` with an explicit `--session <leader thread id>` makes `leader.sessionId` messageable; without it members cannot report and the leader is stuck polling. Re-running `init` is a safe no-op.
|