diff --git a/packages/omo-codex/plugin/components/start-work-continuation/.gitattributes b/packages/omo-codex/plugin/components/start-work-continuation/.gitattributes new file mode 100644 index 000000000..9363fdb74 --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/.gitattributes @@ -0,0 +1,13 @@ +# Normalize line endings: store LF in git, check out LF on every platform. +# Required so biome's --check passes on Windows (default core.autocrlf=true). +* text=auto eol=lf + +# Explicit binary types +*.png binary +*.jpg binary +*.jpeg binary +*.gif binary +*.ico binary +*.zip binary +*.tgz binary +*.gz binary diff --git a/packages/omo-codex/plugin/components/start-work-continuation/.gitignore b/packages/omo-codex/plugin/components/start-work-continuation/.gitignore new file mode 100644 index 000000000..746087d37 --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/.gitignore @@ -0,0 +1,3 @@ +dist/ +node_modules/ +*.log diff --git a/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md b/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md new file mode 100644 index 000000000..7c8528b5e --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/AGENTS.md @@ -0,0 +1,43 @@ +# Repository Conventions + +Conventions for human contributors and AI agents working on this repository. + +## Stack + +- Node >=20 runtime. +- npm package manager. +- TypeScript 6 strict mode. +- Biome 2 linting and formatting. +- Vitest 4 test runner. + +## Forbidden + +- No `as any` or `as unknown`. +- No `@ts-ignore` or `@ts-expect-error`. +- No enums. +- No non-null assertions. +- No default exports. `vitest.config.ts` is exempt because the framework requires that shape. + +## File Ceiling + +- Keep each `src/` TypeScript file under 250 pure LOC. +- Split by responsibility before a file reaches the ceiling. + +## Test Discipline + +- Use Vitest with nested `describe` names in `#given`, `#when`, and `#then` form, or inline `// given`, `// when`, and `// then` comments. +- Never use Arrange-Act-Assert comments. +- Keep fixtures in `test/fixtures/`. + +## Build and Hooks + +- Build output goes to `dist/`. +- `hooks/hooks.json` registers Codex `Stop` and `SubagentStop` hooks. +- Hook commands run `node ${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js hook stop` and `node ${PLUGIN_ROOT}/components/start-work-continuation/dist/cli.js hook subagent-stop`. + +## Constraints + +- Never let the hook block a Codex turn because of malformed input. +- Never make a network call from the hook. +- Keep the directive in `directive.md`. Do not inline it into TypeScript files. +- The hook only continues sessions listed in `.omo/boulder.json` as `codex:`. diff --git a/packages/omo-codex/plugin/components/start-work-continuation/CHANGELOG.md b/packages/omo-codex/plugin/components/start-work-continuation/CHANGELOG.md new file mode 100644 index 000000000..72bf8e5ef --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/CHANGELOG.md @@ -0,0 +1,5 @@ +# Changelog + +## 0.1.0 - 2026-05-28 + +- Initial release: Stop and SubagentStop continuation injection. diff --git a/packages/omo-codex/plugin/components/start-work-continuation/LICENSE b/packages/omo-codex/plugin/components/start-work-continuation/LICENSE new file mode 100644 index 000000000..09aac3c3b --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Yeongyu Kim + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/packages/omo-codex/plugin/components/start-work-continuation/NOTICE b/packages/omo-codex/plugin/components/start-work-continuation/NOTICE new file mode 100644 index 000000000..5195cba89 --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/NOTICE @@ -0,0 +1,5 @@ +codex-start-work-continuation +Copyright (c) 2026 Yeongyu Kim + +This product includes software released under the MIT License. +See LICENSE for the full text. diff --git a/packages/omo-codex/plugin/components/start-work-continuation/README.md b/packages/omo-codex/plugin/components/start-work-continuation/README.md new file mode 100644 index 000000000..12500f3a4 --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/README.md @@ -0,0 +1,55 @@ +# codex-start-work-continuation + +Codex Stop-hook continuation injector for the omo-codex `start-work` skill. + +It reads `.omo/boulder.json` in the hook payload `cwd`, resolves the active work, inspects the active plan for incomplete top-level checkboxes, and emits Codex Stop-hook JSON when the plan still has work: + +```json +{"decision":"block","reason":""} +``` + +The `reason` is loaded from `directive.md` on every invocation and filled with current plan state. The hook returns no output when `stop_hook_active` is `true`, when no active Boulder work exists, when the work is completed, when the active work is not tied to `codex:`, or when all top-level plan checkboxes are complete. + +This pairs with the `start-work` skill at `plugin/skills/start-work/SKILL.md`. That skill writes `.omo/boulder.json` with Codex session ids prefixed as `codex:` so the hook can continue only its own active Codex session. + +## Counted plan checkboxes + +Only column-0 checkboxes under these sections are counted: + +- `## TODOs` +- `## Final Verification Wave` + +Nested checkboxes under `### Acceptance Criteria`, `### Evidence`, and `### Definition of Done` are ignored. + +## Smoke test + +```bash +TMP=$(mktemp -d) +mkdir -p "$TMP/.omo/plans" +cat > "$TMP/.omo/plans/test.md" < "$TMP/.omo/boulder.json" < + +You are mid-flight on a Prometheus work plan. The turn just ended without finishing the plan. This is an automatic continuation — keep going. Do NOT ask the user whether to continue; the contract is auto-continue until every top-level checkbox is `- [x]`. + +# State + +- Plan: `{{PLAN_NAME}}` +- Plan file: `{{PLAN_PATH}}` +- Boulder state: `{{BOULDER_PATH}}` +- Remaining top-level checkboxes: `{{REMAINING_COUNT}}` of `{{TOTAL_COUNT}}` +- Next incomplete task: `{{NEXT_TASK_LABEL}}` +{{WORKTREE_BLOCK}} +- Ledger: `{{LEDGER_PATH}}` +- Your session id in boulder.json: `codex:{{SESSION_ID}}` + +# What to do this turn + +1. Read `{{PLAN_PATH}}` AND `{{LEDGER_PATH}}` first — ground truth for what remains and what evidence has already been recorded. The plan checkbox and the ledger are the only sources of truth; do not trust your own memory of prior turns. +2. Pick the FIRST unchecked top-level checkbox in `## TODOs` or `## Final Verification Wave`. Ignore nested checkboxes under Acceptance Criteria / Evidence / Definition of Done. +3. Follow the `start-work` skill in full. The skill is already loaded from your earlier turn — re-read its file at `packages/omo-codex/plugin/skills/start-work/SKILL.md` if you have lost context. +4. Decompose the checkbox into atomic sub-tasks. Dispatch them in PARALLEL via `spawn_agent` calls in this same response unless a sub-task has a NAMED blocking dependency (input from another sub-task or shared file). +5. Every sub-task message MUST include all 7 sections and name one Manual-QA channel with its exact tool and exact invocation (the literal `curl` / `send-keys` / `page.click` with concrete inputs and the binary PASS/FAIL observable), plus the applicable ultraqa adversarial classes, a captured artifact, and a cleanup receipt. Channels: HTTP call (`curl -i`); tmux (`send-keys` + `capture-pane`); browser use — use Chrome to drive the page, else download and use agent-browser (https://github.com/vercel-labs/agent-browser); computer use — OS-level GUI automation for a desktop app. Tests are the floor; the channel artifact plus probed adversarial classes are the ceiling. All are required. +6. After verification of ALL sub-tasks under this checkbox: `apply_patch` the plan to change `- [ ]` → `- [x]`, re-read the plan to confirm the count decreased, append a `task-completed` line to the ledger, then continue. +7. Do not start fresh on a sub-agent failure. Re-dispatch the same `task_name` with a fix-message: `FAILED: ` + `Diagnosis: ` + `Fix: `. + +# Hard constraints + +- No production code before a failing test exists. When the change touches existing behavior, PIN it first: write a baseline characterization test that passes on the unchanged code, with exact inputs, exact observable, and exact assertion. PIN → RED → GREEN → SURFACE. +- No `--dry-run` as evidence. No "should work". No "tests pass" as completion proof. +- No `as any` / `@ts-ignore` / `@ts-expect-error`. No deleting failing tests. +- Probe every applicable ultraqa adversarial class (malformed input, prompt injection, cancel/resume, stale state, dirty worktree, hung or long commands, flaky tests, misleading success output, repeated interruptions) and capture the observable for each. A clean happy-path artifact alone is NOT a PASS when an applicable class went unprobed; record skipped classes with a one-line not-applicable reason. +- Cleanup receipt is mandatory. Register each QA resource teardown (scripts, tmux assets, browser / agent-browser sessions, PIDs, ports, containers, temp dirs) as its own todo the moment it spawns, then execute it. Leftover PIDs / `tmux` sessions / browser contexts / bound ports / containers / temp dirs = BLOCKED, not PASS. +- The worktree path (if set in boulder.json) governs every file edit and command. Do not stray into the main repo. +- session_ids you write to boulder.json MUST be prefixed `codex:`. Bare ids on read are legacy `opencode:`. + +# Stop conditions for THIS turn + +- A top-level checkbox flipped to `- [x]` after the 5-phase QA gate (Phase 1 read, Phase 2 automated, Phase 3 channel scenario, Phase 4 adversarial-class probing, Phase 5 gate decision). Then the Stop hook will re-evaluate; if more checkboxes remain you will be continued again. +- 3 same-failure cycles on one sub-task → escalate via `spawn_agent(agent_type="codex-ultrawork-reviewer", ...)` and stop dispatch. +- Safety boundary (destructive command, secret exfiltration, production write) → stop and surface a safe substitute. +- All top-level checkboxes `- [x]` AND (if gate triggered) `codex-ultrawork-reviewer` approved unconditionally → print the ORCHESTRATION COMPLETE block and end. + +# Output discipline + +- Surface only state changes: sub-agent dispatched, channel scenario PASS/FAIL with artifact path, checkbox marked, evidence appended to ledger. +- Do NOT print "Should I continue?" — the Stop hook handles continuation. +- Do NOT restate the full plan. Do NOT recap prior turns. The ledger and the plan file are the durable record. + +Begin now. Pick the next checkbox, dispatch the parallel sub-agents, verify, mark, continue. + + diff --git a/packages/omo-codex/plugin/components/start-work-continuation/package.json b/packages/omo-codex/plugin/components/start-work-continuation/package.json new file mode 100644 index 000000000..251463baf --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/package.json @@ -0,0 +1,53 @@ +{ + "name": "@code-yeongyu/codex-start-work-continuation", + "version": "0.1.0", + "description": "Codex Stop hook continuation injector for omo-codex start-work plans.", + "type": "module", + "packageManager": "npm@11.12.1", + "license": "MIT", + "homepage": "https://github.com/code-yeongyu/codex-start-work-continuation", + "repository": { + "type": "git", + "url": "git+https://github.com/code-yeongyu/codex-start-work-continuation.git" + }, + "bugs": { + "url": "https://github.com/code-yeongyu/codex-start-work-continuation/issues" + }, + "keywords": [ + "codex", + "codex-plugin", + "start-work", + "continuation", + "hooks", + "boulder" + ], + "bin": { + "omo-start-work-continuation": "./dist/cli.js" + }, + "scripts": { + "build": "tsc -p tsconfig.build.json", + "test": "vitest --run", + "test:watch": "vitest", + "typecheck": "tsc --noEmit", + "lint": "biome check .", + "lint:fix": "biome check --write .", + "check": "tsc --noEmit && biome check . && npm run build" + }, + "files": [ + "dist", + "directive.md", + "hooks", + "README.md", + "LICENSE", + "NOTICE" + ], + "devDependencies": { + "@biomejs/biome": "2.4.15", + "@types/node": "^25.7.0", + "typescript": "^6.0.3", + "vitest": "^4.1.5" + }, + "engines": { + "node": ">=20.0.0" + } +} diff --git a/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.build.json b/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.build.json new file mode 100644 index 000000000..5b5bbcafd --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.build.json @@ -0,0 +1,12 @@ +{ + "extends": "./tsconfig.json", + "compilerOptions": { + "allowImportingTsExtensions": false, + "declaration": true, + "outDir": "dist", + "rootDir": "src", + "noEmit": false + }, + "include": ["src/**/*"], + "exclude": ["test/**/*"] +} diff --git a/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.json b/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.json new file mode 100644 index 000000000..3ac7ab0e2 --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/tsconfig.json @@ -0,0 +1,27 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "NodeNext", + "moduleResolution": "NodeNext", + "lib": ["ESNext"], + "strict": true, + "exactOptionalPropertyTypes": true, + "noUncheckedIndexedAccess": true, + "noPropertyAccessFromIndexSignature": true, + "verbatimModuleSyntax": true, + "noImplicitOverride": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noUnusedLocals": true, + "noUnusedParameters": true, + "esModuleInterop": true, + "allowImportingTsExtensions": true, + "skipLibCheck": true, + "forceConsistentCasingInFileNames": true, + "resolveJsonModule": true, + "useDefineForClassFields": false, + "types": ["node"], + "noEmit": true + }, + "include": ["src/**/*", "test/**/*", "vitest.config.ts"] +} diff --git a/packages/omo-codex/plugin/components/start-work-continuation/vitest.config.ts b/packages/omo-codex/plugin/components/start-work-continuation/vitest.config.ts new file mode 100644 index 000000000..5453488cc --- /dev/null +++ b/packages/omo-codex/plugin/components/start-work-continuation/vitest.config.ts @@ -0,0 +1,10 @@ +import { defineConfig } from "vitest/config"; + +export default defineConfig({ + test: { + include: ["test/**/*.test.ts"], + environment: "node", + pool: "threads", + isolate: true, + }, +});