okstra 0.102.0 → 0.102.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (27) hide show
  1. package/README.kr.md +5 -6
  2. package/README.md +5 -6
  3. package/docs/for-ai/README.md +2 -3
  4. package/docs/for-ai/skills/{okstra-container.md → okstra-container-build.md} +3 -4
  5. package/docs/for-ai/skills/okstra-inspect.md +1 -2
  6. package/docs/kr/architecture/storage-model.md +1 -2
  7. package/docs/kr/architecture.md +7 -7
  8. package/docs/kr/cli.md +5 -5
  9. package/docs/kr/container.md +1 -1
  10. package/docs/project-structure-overview.md +5 -5
  11. package/docs/superpowers/plans/2026-06-15-internal-skill-resource-migration.md +3 -3
  12. package/docs/superpowers/plans/2026-06-24-manual-user-test-section.md +5 -5
  13. package/docs/superpowers/plans/2026-06-24-okstra-container-build-rename.md +525 -0
  14. package/docs/superpowers/specs/2026-06-13-neutral-tmux-lead-adapter-design.md +3 -3
  15. package/docs/superpowers/specs/2026-06-24-manual-user-test-section-design.md +6 -6
  16. package/docs/superpowers/specs/2026-06-24-okstra-container-build-rename-design.md +110 -0
  17. package/package.json +1 -1
  18. package/runtime/BUILD.json +2 -2
  19. package/runtime/prompts/lead/team-contract.md +2 -2
  20. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  21. package/runtime/prompts/profiles/final-verification.md +1 -1
  22. package/runtime/python/okstra_token_usage/paths.py +1 -2
  23. package/runtime/skills/{okstra-container → okstra-container-build}/SKILL.md +2 -2
  24. package/src/cli-registry.mjs +1 -1
  25. package/src/commands/lifecycle/install.mjs +17 -28
  26. package/src/commands/lifecycle/uninstall.mjs +9 -10
  27. package/src/lib/skill-catalog.mjs +10 -9
@@ -0,0 +1,110 @@
1
+ # okstra-container-build 스킬 rename 설계
2
+
3
+ - 작성일: 2026-06-24
4
+ - 상태: 설계 합의 완료, 구현 계획 대기
5
+ - 범위: 공개 agent skill 이름을 `okstra-container` 에서 `okstra-container-build` 로 변경
6
+
7
+ ## 1. 배경
8
+
9
+ 현재 컨테이너 기능은 세 표면이 분리되어 있다.
10
+
11
+ - 공개 스킬 이름과 설치 대상은 `src/lib/skill-catalog.mjs` 의 `USER_SKILL_NAMES` 가 정본이다([skill-catalog.mjs:7](../../../src/lib/skill-catalog.mjs)).
12
+ - 플러그인 보조 채널은 `.claude-plugin/plugin.json` 의 `skills` 배열을 사용한다([plugin.json:4](../../../.claude-plugin/plugin.json)).
13
+ - 실제 CLI 명령은 `okstra container ...` 이며, Node shim 이 Python `okstra_ctl.container` 로 위임한다([container.mjs:3](../../../src/commands/inspect/container.mjs), [container.py:927](../../../scripts/okstra_ctl/container.py)).
14
+
15
+ 사용자 요청은 스킬 이름을 `okstra-container-build` 로 바꾸는 것이다. 따라서 slash-command / skill discovery 표면은 새 이름으로 바꾸되, 안정된 CLI 명령과 런타임 리소스 이름은 유지한다.
16
+
17
+ ## 2. 목표 / 비목표
18
+
19
+ ### 목표
20
+
21
+ - 설치되는 공개 스킬과 slash command 를 `/okstra-container-build` 로 바꾼다.
22
+ - 신규 설치와 업그레이드 모두에서 옛 `/okstra-container` 가 남지 않게 한다.
23
+ - README, architecture, AI manual, phase prompt 의 사용자-facing 안내를 새 스킬명으로 맞춘다.
24
+ - `okstra container` CLI 와 Python entrypoint 는 유지해 기존 자동화와 테스트 표면을 흔들지 않는다.
25
+
26
+ ### 비목표
27
+
28
+ - `okstra container` CLI command rename.
29
+ - `okstra-container-<slug>` tmux session prefix rename.
30
+ - docker compose 생성 기능 추가. `build` 는 스킬 이름의 suffix 일 뿐, 기존처럼 프로젝트의 `docker-compose.yml` 을 재사용한다.
31
+ - 과거 changelog / 과거 구현 계획 문서의 역사적 문구 전면 수정.
32
+
33
+ ## 3. 검토한 접근
34
+
35
+ | 접근 | 판단 | 이유 |
36
+ |---|---|---|
37
+ | A. 공개 스킬명만 rename, CLI/runtime 이름 유지 | 선택 | 사용자 요청을 만족하면서 `okstra container` 자동화, Python argparse, tmux session 추적을 보존한다. |
38
+ | B. CLI 도 `okstra container-build` 로 rename | 제외 | `src/commands/inspect/container.mjs` 와 `scripts/okstra_ctl/container.py` 의 명령 계약까지 깨져 범위가 커진다. |
39
+ | C. 새 스킬을 추가하고 옛 `okstra-container` alias 를 유지 | 제외 | 설치 후 slash command 가 두 개 보인다. 사용자 요청이 rename 이므로 옛 이름은 upgrade 시 prune 해야 한다. |
40
+
41
+ ## 4. 설계 결정
42
+
43
+ | 결정 | 선택 | 근거 |
44
+ |---|---|---|
45
+ | 스킬 정본 | `USER_SKILL_NAMES` 의 `okstra-container` 를 `okstra-container-build` 로 교체 | installer 와 plugin sync 테스트가 이 목록을 기준으로 맞물린다([install.mjs:631](../../../src/commands/lifecycle/install.mjs), [skill-catalog.test.mjs:14](../../../tests-js/skill-catalog.test.mjs)). |
46
+ | 원천 디렉터리 | `skills/okstra-container/` 를 `skills/okstra-container-build/` 로 이동 | install copy/link mode 모두 `<name>/SKILL.md` 디렉터리명을 사용한다([install.mjs:631](../../../src/commands/lifecycle/install.mjs), [install.mjs:653](../../../src/commands/lifecycle/install.mjs)). |
47
+ | frontmatter | `name: okstra-container-build` | agent skill discovery 는 frontmatter 이름을 사용자 표면으로 노출한다. |
48
+ | CLI | `okstra container <up|status|logs|stop-watcher|down>` 유지 | CLI shim 과 Python parser 는 컨테이너 런타임 API 이름이며 스킬명과 독립적이다([container.mjs:3](../../../src/commands/inspect/container.mjs), [container.py:927](../../../scripts/okstra_ctl/container.py)). |
49
+ | runtime session prefix | `okstra-container-<slug>` 유지 | tmux/docker trace 리소스 이름이며 테스트가 고정한다([ids.py:93](../../../scripts/okstra_ctl/ids.py), [test_container_session_name.py:14](../../../tests/container/test_container_session_name.py)). |
50
+ | upgrade cleanup | 옛 `okstra-container` 를 exact-name prune 대상에 추가 | 설치는 새 스킬을 복사하지만 기존 skill home 의 옛 디렉터리를 자동 삭제하지 않는다([install.mjs:669](../../../src/commands/lifecycle/install.mjs)). |
51
+
52
+ ## 5. 영향 파일
53
+
54
+ ### 소스 / 설치 계약
55
+
56
+ | 파일 | 변경 |
57
+ |---|---|
58
+ | `skills/okstra-container/SKILL.md` | 디렉터리명을 `skills/okstra-container-build/` 로 변경하고 frontmatter `name` 갱신. 본문에서 slash-command 명칭만 새 이름으로 맞추고 `okstra container ...` CLI 예시는 유지. |
59
+ | `src/lib/skill-catalog.mjs` | `USER_SKILL_NAMES` 에서 `okstra-container-build` 로 교체. `OBSOLETE_INTERNAL_SKILL_NAMES` 는 `OBSOLETE_SKILL_NAMES` 로 재명명하고, 옛 공개 스킬 `okstra-container` 를 같은 exact-name prune 목록에 추가한다. |
60
+ | `.claude-plugin/plugin.json` | `./skills/okstra-container-build` 로 교체. |
61
+ | `src/commands/lifecycle/uninstall.mjs` | fallback skill names 에 `okstra-container-build` 를 포함하고, 옛 `okstra-container` 도 fallback cleanup 대상에 유지한다. |
62
+ | `tools/build.mjs` | 코드 변경 없음. `skills/` sync 로 새 디렉터리가 `runtime/skills/okstra-container-build` 로 반영되는지 build 로 검증. |
63
+
64
+ ### 문서 / 프롬프트
65
+
66
+ | 파일군 | 변경 |
67
+ |---|---|
68
+ | `README.md`, `README.kr.md` | slash command 표를 `/okstra-container-build` 로 갱신. |
69
+ | `docs/kr/architecture.md`, `docs/project-structure-overview.md` | 공개 스킬 7종 목록과 skill role 표를 새 이름으로 갱신. CLI `container` 설명은 유지하되 "for the okstra-container-build skill" 로 조정. |
70
+ | `docs/for-ai/README.md`, `docs/for-ai/skills/okstra-container.md` | AI manual 파일명을 `okstra-container-build.md` 로 바꾸고 라우팅 표를 새 이름으로 갱신. |
71
+ | `docs/for-ai/skills/okstra-inspect.md` | "container 는 별도 스킬" 링크를 새 manual 로 갱신. |
72
+ | `prompts/profiles/_implementation-deliverable.md`, `prompts/profiles/final-verification.md` | 사람이 실행할 스킬명은 `/okstra-container-build`, 실제 CLI 명령 예시는 `okstra container up ...` 로 표현해 혼동을 줄인다. |
73
+ | `docs/kr/container.md` | CLI 레퍼런스는 제목과 명령을 유지하고, 단일 진입점 bullet 의 스킬명만 `/okstra-container-build` 로 갱신. |
74
+
75
+ ### 그대로 둘 파일군
76
+
77
+ - `CHANGES.md`, `CHANGELOG.md`: 과거 릴리스 기록이므로 기존 `okstra-container` 항목을 역사로 보존한다. 이번 변경은 새 항목만 추가한다.
78
+ - `docs/superpowers/plans/2026-06-21-okstra-container-local-user-test.md`, `docs/superpowers/specs/2026-06-21-okstra-container-local-user-test-design.md`: 과거 기능 도입 당시 설계/계획 문서라 전체 rewrite 하지 않는다. 현재 문서로 rename 결정을 보완한다.
79
+ - `tests/container/*` 의 `okstra-container-<slug>` 기대값: 런타임 session prefix 검증이므로 유지한다.
80
+
81
+ ## 6. 업그레이드 동작
82
+
83
+ 문제: 현재 install 은 `USER_SKILL_NAMES` 를 설치한 뒤 manifest 를 새 목록으로 쓴다. 하지만 이미 `~/.claude/skills/okstra-container` 또는 `~/.agent/skills/okstra-container` 가 있으면 새 install 만으로 옛 디렉터리가 삭제되지 않는다.
84
+
85
+ 설계:
86
+
87
+ 1. old public skill 이름 `okstra-container` 를 exact-name prune 대상에 넣는다.
88
+ 2. `installSkillsCopy()` / `installSkillsLink()` 의 prune 단계에서 old dir 을 제거한다.
89
+ 3. wildcard `okstra-*` 삭제는 금지한다. 기존 obsolete cleanup 도 exact-name 방식이다([install.mjs:666](../../../src/commands/lifecycle/install.mjs)).
90
+ 4. uninstall fallback 은 manifest 가 없는 오래된 설치도 정리할 수 있게 old/new 이름을 모두 포함한다.
91
+
92
+ ## 7. 테스트 계획
93
+
94
+ - `node --test tests-js/skill-catalog.test.mjs`: plugin manifest 와 `USER_SKILL_NAMES` 순서 동기화.
95
+ - `node --test tests-js/install-runtime.test.mjs`: 새 스킬이 Claude/Codex skill home 에 설치되고 manifest 에 기록되는지 확인. 추가 회귀 테스트로 old `okstra-container` 디렉터리를 seed 한 뒤 install 이 prune 하는지 검증.
96
+ - `node --test tests-js/doctor-runtime.test.mjs`: 비필수 container skill 이 `REQUIRED_SKILLS` 에 들어가지 않는다는 테스트명을 새 이름으로 갱신.
97
+ - `python3 -m pytest tests/contract/test_docs_runtime_contract.py`: plugin manifest 계약과 docs runtime 계약 갱신.
98
+ - `npm run build`: `runtime/skills/okstra-container-build/SKILL.md` 생성과 `runtime/skills/okstra-container/` 부재 확인.
99
+ - 최종 게이트: `npm run check`.
100
+
101
+ ## 8. 사용자 영향
102
+
103
+ - 새 설치와 재설치 후 스킬 호출명은 `/okstra-container-build` 가 된다.
104
+ - 기존 `okstra container up/status/logs/stop-watcher/down` CLI 는 그대로 동작한다.
105
+ - 기존 tmux watcher/session 이름과 docker trace 라벨은 유지된다.
106
+ - 이전 `/okstra-container` 는 upgrade install 과정에서 제거된다.
107
+
108
+ ## 9. 미해결 사항
109
+
110
+ 없음.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.102.0",
3
+ "version": "0.102.2",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.102.0",
3
- "builtAt": "2026-06-24T08:03:17.852Z",
2
+ "package": "0.102.2",
3
+ "builtAt": "2026-06-24T14:47:45.768Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -398,7 +398,7 @@ okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --wr
398
398
 
399
399
  The script reads:
400
400
  - `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for the lead and every Claude-side worker (Claude worker, Report writer worker, plus the Claude wrappers around Codex/Antigravity workers). Sessions are discovered by the recorded team-state `teamName`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `antigravity-worker`, `report-writer`). **For this `agentName` match to work, Lead MUST set the Agent `name` arg to `<workerId>-worker` on every dispatch** (see [agents okstra-lead-contract.md Phase 4 — "Agent `name` on dispatch"](./okstra-lead-contract.md)); a worker dispatched without `name` carries no `agentName`, so the collector cannot attribute its session and records it `unavailable` (now surfaced as a `usageSummary.unattributedTeamSessions` entry rather than dropped silently).
401
- - `~/.codex/sessions/Y/M/D/rollout-*.jsonl` for the underlying Codex CLI session (matched by `cwd` and timestamp window of the wrapper subagent). Last `event_msg.token_count.total_token_usage.total_tokens` is the session total.
401
+ - `~/.agent/sessions/Y/M/D/rollout-*.jsonl` for the underlying Codex CLI session (matched by `cwd` and timestamp window of the wrapper subagent). Last `event_msg.token_count.total_token_usage.total_tokens` is the session total.
402
402
  - `~/.antigravity/tmp/<project>/chats/session-*.json` for the underlying Antigravity CLI session. Sum of per-message `tokens.total`.
403
403
 
404
404
  ### Resulting team-state shape
@@ -424,7 +424,7 @@ The script reads:
424
424
  "totalTokens": 2274011,
425
425
  "source": "claude-jsonl",
426
426
  "sessionId": "<wrapper-session-id>",
427
- "cliSessionPaths": ["/Users/.../.codex/sessions/.../rollout-*.jsonl"],
427
+ "cliSessionPaths": ["/Users/.../.agent/sessions/.../rollout-*.jsonl"],
428
428
  "cliTotalTokens": 5261833,
429
429
  "cliModel": "gpt-5.5"
430
430
  }
@@ -30,7 +30,7 @@ are collected and convergence finished. Phase 1-5 do not need it.
30
30
  - **Pure code changes** (no persisted state, no infra mutation): a reachable revert SHA is sufficient. Record the exact `git revert <SHA>` command that would undo the change, and confirm `git rev-parse <SHA>` resolves.
31
31
  - **Feature-flag-gated changes**: confirm the off-switch path was exercised in this run's validation evidence (i.e. one of the validation commands ran with the flag off and succeeded). A plan that ships a flag without exercising the off-path does NOT satisfy this requirement.
32
32
  - **Schema migrations, config-format changes, or any change with persisted state**: a **dry-run of the rollback step is mandatory**, not preferred. Record the exact rollback command and its captured exit code / stdout. If the migration tool offers no dry-run mode (`--dry-run`, `--plan`, equivalent), the executor MUST refuse to claim rollback verification and instead end the run with a routing recommendation back to `implementation-planning` for a safer rollback strategy. Skipping this step on a stateful change is treated as a `contract-violated` outcome by `final-verification`.
33
- - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Environment line: if the project has a `docker-compose.yml`, use `okstra-container up <task-id>` then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. Base the steps on the approved plan's `Acceptance:` and this run's actual diff — these are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`.
33
+ - **Manual user test draft**: when this run produces a user-observable change (UI / API / CLI / artifact), write `target / environment / steps / expected result` per change into §5.7.9 (data field `implementation.manualUserTest`, `applicable=true` with `items`). Environment line: if the project has a `docker-compose.yml`, use `/okstra-container-build` (which runs `okstra container up <task-id>`) then connect to the published port; otherwise the project's run command (e.g. `npm start`). When there is no user-observable change, set `applicable=false` and give a one-line `exemptionReason` instead of items. Base the steps on the approved plan's `Acceptance:` and this run's actual diff — these are the steps a human (or `final-verification`) re-runs by hand, NOT the automated validation commands in `Validation evidence`.
34
34
  - **Routing recommendation for `final-verification`**: brief note on whether the changes are ready for final-verification phase or need a new error-analysis / planning loop first.
35
35
  - **Follow-up tasks (Section 4 of the final report)**: every item discovered during this run that was *not* delivered MUST appear in the final report's `## 4. Follow-up Tasks (후속 작업)` table with a concrete `Origin`, `New Task ID`, `Suggested task-type`, `Scope`, and `Reason / Why deferred`. Sources include: out-of-scope discoveries that the executor consciously chose not to fold into this run, verifier concerns the executor declined to fix in-place, scope-boundary items from the approved plan that turned out to need their own ticket, and any unresolved `## 1. Clarification Items` row carried over from the approved plan (`Status` ∈ `{open, answered}` at approval time). An empty section is acceptable but only when expressed as the single line `- 후속 작업 없음.` — silence is treated as a contract violation. Rows with `Auto-spawn? = yes` will be materialised by `scripts/okstra-spawn-followups.py` in Phase 7; rows with `Auto-spawn? = no` MUST also appear in `Section 3. Recommended Next Steps` so the user knows to act manually.
36
36
 
@@ -38,7 +38,7 @@
38
38
  - **Read-only command log**: any pre-existing test/validation command executed during this run MUST be listed with its exact command line and exit code. No mutating commands may appear here.
39
39
  - **Two-tier command lookup (shared with `implementation`):** when this phase performs its own independent re-validation, the command source is exactly the same two tiers `implementation` verifiers use — Tier 1 is the originating task brief / approved plan's `validation` set, Tier 2 is `<PROJECT_ROOT>/.okstra/project.json` under `qaCommands`. Auto-detecting tools from manifest files is forbidden; missing tiers are recorded as `qa-command not configured: <category>` rather than guessed. The `cmd` deny-list (`--fix`, `--write`, ` -w`, ` -u`, `--snapshot-update`, `INSTA_UPDATE=<not-no>`, `cargo update`, `npm install` without `ci`, etc.) is enforced identically. NOTE: runtime fail-fast validation (`okstra_ctl.qa_commands.validate_qa_commands`) only fires at `--task-type implementation` run-prep, so this phase MUST self-check each `qaCommands` entry against the deny-list before executing it — if a denied token is present, skip the command and record it as a `Read-only command log` line `qa-command rejected (denied token: <token>): <label>`.
40
40
  - **Tier 3 — stage conformance scripts (whole-task union):** because this phase verifies the **integrated, merged** state, it re-runs conformance against that state rather than per-stage. Read the task-level manifest `<task_root>/qa/conformance-manifest.json` (the directory is the `TASK_QA_PATH` token) and, in **whole-task scope**, run the `runCommand` of **every** `entries[]` item against the merged worktree, refreshing each `<task_root>/qa/result-<stageKey>.json` (`{ "stageKey", "overall": "PASS"|"FAIL"|"MISSING", "ranAt", "requirements" }`). In **single-stage scope**, run only the entry whose `stageKey` matches the verified stage. An entry carrying an `exemption` or user `waiver` is NOT executed — record the skip and reason; a `waiver` becomes a `conditional-accept` condition surfaced in the section 7 Verdict (conformance left unverified by user acknowledgement). Each `runCommand` runs in the worktree cwd with `qaEnv` env (replica DB DSN / app base URL / env file) — **replica / test environment only**, never shared / staging / prod, and the same source/lockfile mutation deny-list applies (a conformance script MAY mutate only its `qaEnv` replica datastore). Interpret each result from the exit code + stdout `QA-RESULT: PASS|FAIL` (last wins) and `REQ <id>: PASS|FAIL: <reason>` lines; no `QA-RESULT` marker → `MISSING`. Any entry whose result is not `PASS` (including `MISSING` or a never-run/missing sidecar) is an **Acceptance Blocker** (`major`+), so the verdict becomes `conditional-accept` / `blocked`. This is the same gate the `validate-run.py` Tier 3 check enforces on the result sidecars.
41
- - **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `okstra-container up` then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
41
+ - **Manual user test results**: take each item from the source implementation report's §5.7.9 Manual User Test (Draft), execute the ones reproducible in this environment (e.g. `/okstra-container-build`, which runs `okstra container up`, then the documented steps), and record `result` (`pass` / `fail` / `blocked`) + observed value in §5.8.7 (data field `finalVerification.manualUserTest`). Steps that need human-only interaction this run cannot perform are recorded as `blocked` with the reason (handed to the user), never silently skipped. A failed manual test is an Acceptance Blocker. If the draft was an exemption (`applicable=false`), reaffirm the reason in one line (`applicable=false` + `exemptionReaffirm`).
42
42
  - **Routing recommendation**: the next safe phase — one of `release-handoff`, `done`, `error-analysis`, `implementation-planning` — tied to the verdict and blocker list. `release-handoff` is allowed ONLY when the Verdict Token is `accepted`. `release-handoff` is additionally allowed ONLY when the verification scope (the `Verification scope:` line of the injected `VERIFICATION_TARGET` block, recorded as the report's `verificationScope` field) is `whole-task`; a `single-stage` accepted run routes to `release-handoff(stage-group)` (or `implementation` / `done`); plain `release-handoff` remains whole-task-only. Enforcement: `validators/validate-run.py` rejects a `single-stage` report whose routing cites plain `release-handoff`.
43
43
  - **Verified-row recording** (single-stage scope only): when the Verdict Token is `accepted`, the lead MUST run `okstra handoff record-verified --plan-run-root <plan-run-root> --stage <N> --report-path <final-report.md path> --data-json <final-report data.json path>` and quote the command + exit code in the report. The helper re-validates taskType/scope/verdict from data.json, so a non-accepted or whole-task report is rejected at the tool layer.
44
44
  - Clarification request policy (phase-specific addendum — shared policy is in `_common-contract.md`):
@@ -7,7 +7,7 @@ from pathlib import Path
7
7
 
8
8
  HOME = Path.home()
9
9
  CLAUDE_PROJECTS = HOME / ".claude" / "projects"
10
- CODEX_SESSIONS = HOME / ".codex" / "sessions"
10
+ CODEX_SESSIONS = HOME / ".agent" / "sessions"
11
11
 
12
12
 
13
13
  def utc_now() -> str:
@@ -45,4 +45,3 @@ def claude_project_dir(cwd: Path, projects_root: Path | None = None) -> Path:
45
45
  # `projects_root` 는 테스트/진단용 주입 시드 — 기본은 실제 ~/.claude/projects.
46
46
  encoded = "-" + str(cwd).strip("/").replace("/", "-")
47
47
  return (projects_root or CLAUDE_PROJECTS) / encoded
48
-
@@ -1,10 +1,10 @@
1
1
  ---
2
- name: okstra-container
2
+ name: okstra-container-build
3
3
  description: |
4
4
  Use to bring up, inspect, or tear down the okstra user-test container runtime for an implementation task — the docker-compose group okstra deploys from a task's worktree so a human can poke at the running build. Trigger words include "okstra container", "okstra container up", "okstra container down", "컨테이너 띄워", "컨테이너 올려", "컨테이너 내려", "컨테이너 상태", "컨테이너 로그", "유저 테스트 환경", "user test environment", "docker compose 띄워줘", "이 task 컨테이너로 띄워", "watcher 멈춰", "stop watcher", "감시 종료".
5
5
  ---
6
6
 
7
- # OKSTRA Container
7
+ # OKSTRA Container Build
8
8
 
9
9
  Single entry point for the okstra user-test container runtime. okstra provisions a docker-compose group from an `implementation` task's worktree (the `docker-compose.yml` at the worktree root), labels it with the task's run-trace so later sub-commands can find the group, and tails it through a tmux watcher pane. This skill drives that lifecycle. Sub-commands:
10
10
 
@@ -6,7 +6,7 @@ export const COMMAND_REGISTRY = [
6
6
  module: "./commands/lifecycle/install.mjs",
7
7
  export: "runInstall",
8
8
  category: "admin",
9
- summary: ["Install runtime; seed skills into existing agent homes"],
9
+ summary: ["Install runtime; seed skills into Agent and Claude homes"],
10
10
  },
11
11
  {
12
12
  name: "ensure-installed",
@@ -7,17 +7,15 @@ import { resolvePaths } from "../../lib/paths.mjs";
7
7
  import { fileExists } from "../../lib/proc.mjs";
8
8
  import { buildRuntimeManifest, RUNTIMES_MANIFEST_REL } from "../../lib/runtime-manifest.mjs";
9
9
  import { normalizeRuntimeRequest, resolveRuntime } from "../../lib/runtime-resolver.mjs";
10
- import { OBSOLETE_INTERNAL_SKILL_NAMES, USER_SKILL_NAMES } from "../../lib/skill-catalog.mjs";
10
+ import { OBSOLETE_SKILL_NAMES, USER_SKILL_NAMES } from "../../lib/skill-catalog.mjs";
11
11
 
12
12
  const SKILLS_MANIFEST_REL = "installed-skills.json";
13
13
  const AGENTS_MANIFEST_REL = "installed-agents.json";
14
14
  const USER_HOME = homedir();
15
15
  const CLAUDE_HOME = join(USER_HOME, ".claude");
16
- const CODEX_HOME = join(USER_HOME, ".codex");
17
- const OMP_AGENT_HOME = join(USER_HOME, ".omp", "agent");
16
+ const AGENT_HOME = join(USER_HOME, ".agent");
18
17
  const CLAUDE_SKILLS_DIR = join(CLAUDE_HOME, "skills");
19
- const CODEX_SKILLS_DIR = join(CODEX_HOME, "skills");
20
- const OMP_SKILLS_DIR = join(OMP_AGENT_HOME, "skills");
18
+ const AGENT_SKILLS_DIR = join(AGENT_HOME, "skills");
21
19
  const CLAUDE_AGENTS_DIR = join(CLAUDE_HOME, "agents");
22
20
 
23
21
  // Source template (relative to runtime root in copy mode, or repo root in link mode).
@@ -82,8 +80,7 @@ Effect (copy mode):
82
80
  ${"$HOME"}/.okstra/prompts <- runtime/prompts (lead contracts + coding-preflight pack)
83
81
  ${"$HOME"}/.okstra/templates/settings.local.json <- runtime/templates/reports/settings.template.json
84
82
  ${"$HOME"}/.claude/skills/<name> <- skills when ${"$HOME"}/.claude exists
85
- ${"$HOME"}/.codex/skills/<name> <- skills when ${"$HOME"}/.codex exists
86
- ${"$HOME"}/.omp/agent/skills/<name> <- skills fallback when Claude/Codex homes are absent
83
+ ${"$HOME"}/.agent/skills/<name> <- skills (created when needed)
87
84
  ${"$HOME"}/.claude/agents/<worker>.md <- worker agents when ${"$HOME"}/.claude exists
88
85
  ${"$HOME"}/.okstra/installed-runtimes.json <- install request/resolution manifest
89
86
  ${"$HOME"}/.okstra/installed-skills.json <- target-aware manifest of installed skills
@@ -96,8 +93,7 @@ Effect (link mode):
96
93
  ${"$HOME"}/.okstra/prompts -> <repo>/prompts
97
94
  ${"$HOME"}/.okstra/templates/settings.local.json -> <repo>/templates/reports/settings.template.json
98
95
  ${"$HOME"}/.claude/skills/<name> -> <repo>/skills/<name> when ${"$HOME"}/.claude exists
99
- ${"$HOME"}/.codex/skills/<name> -> <repo>/skills/<name> when ${"$HOME"}/.codex exists
100
- ${"$HOME"}/.omp/agent/skills/<name> -> <repo>/skills/<name> fallback
96
+ ${"$HOME"}/.agent/skills/<name> -> <repo>/skills/<name> (created when needed)
101
97
  ${"$HOME"}/.claude/agents/<worker>.md -> <repo>/agents/workers/<worker>.md when ${"$HOME"}/.claude exists
102
98
  ${"$HOME"}/.okstra/dev-link <- <repo> path stamp
103
99
  ${"$HOME"}/.okstra/version <- installed package version stamp
@@ -109,10 +105,10 @@ worker wrapper scripts without modifying the user's global settings.
109
105
 
110
106
  Worker agent definitions are installed into ${"$HOME"}/.claude/agents/ only
111
107
  when ${"$HOME"}/.claude exists, so Claude Code's subagent discovery can pick
112
- them up. Codex/OMP skill targets receive skill markdown only.
108
+ them up. Agent skill targets receive skill markdown only.
113
109
 
114
110
  Default auto runtime installation does not require host detection; it still
115
- installs shared runtime files and any agent-home skill targets it finds.
111
+ installs shared runtime files, the Agent skill target, and any Claude home it finds.
116
112
  `;
117
113
 
118
114
  const ENSURE_USAGE = `okstra ensure-installed — idempotent install check
@@ -401,16 +397,11 @@ async function installLinkMode(repoPath, paths, opts) {
401
397
 
402
398
  async function detectSkillTargets() {
403
399
  const targets = [];
404
- const [hasClaude, hasCodex] = await Promise.all([dirExists(CLAUDE_HOME), dirExists(CODEX_HOME)]);
400
+ const hasClaude = await dirExists(CLAUDE_HOME);
405
401
  if (hasClaude) {
406
402
  targets.push({ provider: "claude", home: CLAUDE_HOME, skillsDir: CLAUDE_SKILLS_DIR });
407
403
  }
408
- if (hasCodex) {
409
- targets.push({ provider: "codex", home: CODEX_HOME, skillsDir: CODEX_SKILLS_DIR });
410
- }
411
- if (targets.length === 0 && (await dirExists(OMP_AGENT_HOME))) {
412
- targets.push({ provider: "omp", home: OMP_AGENT_HOME, skillsDir: OMP_SKILLS_DIR });
413
- }
404
+ targets.push({ provider: "agent", home: AGENT_HOME, skillsDir: AGENT_SKILLS_DIR });
414
405
  return targets;
415
406
  }
416
407
 
@@ -428,7 +419,6 @@ function installedAssetOverrides(skillTargets, agentNames) {
428
419
  claudeSkills: providers.has("claude"),
429
420
  claudeAgents: agentNames.length > 0,
430
421
  codexSkills: providers.has("codex"),
431
- ompSkills: providers.has("omp"),
432
422
  skillTargets: Array.from(providers).sort(),
433
423
  };
434
424
  }
@@ -637,7 +627,7 @@ async function installSkillsCopy(runtimeRoot, target, opts) {
637
627
  copied += r.copied;
638
628
  skipped += r.skipped;
639
629
  }
640
- const pruned = await pruneObsoleteInternalSkills(target, opts);
630
+ const pruned = await pruneObsoleteSkills(target, opts);
641
631
  if (!quiet) {
642
632
  process.stdout.write(
643
633
  ` ${target.provider} skills: copied=${copied} skipped=${skipped} pruned=${pruned} -> ${target.skillsDir}/ (${USER_SKILL_NAMES.length} skills)\n`,
@@ -656,19 +646,18 @@ async function installSkillsLink(repoAbs, target, opts) {
656
646
  const action = await ensureSymlink(src, dst, { dryRun });
657
647
  if (!quiet) process.stdout.write(` ${target.provider} skills/${name}: ${action}\n`);
658
648
  }
659
- const pruned = await pruneObsoleteInternalSkills(target, opts);
649
+ const pruned = await pruneObsoleteSkills(target, opts);
660
650
  if (!quiet && pruned > 0) {
661
651
  process.stdout.write(` ${target.provider} obsolete internal skills: pruned=${pruned}\n`);
662
652
  }
663
653
  return { ...target, installed: [...USER_SKILL_NAMES] };
664
654
  }
665
655
 
666
- // Remove former internal skill directories left by older installs. Exact-name
667
- // only (OBSOLETE_INTERNAL_SKILL_NAMES) — those contracts now ship as runtime
668
- // resources under ~/.okstra/prompts/, not as agent skills.
669
- async function pruneObsoleteInternalSkills(target, opts) {
656
+ // Remove former skill directories left by older installs. Exact-name only
657
+ // (OBSOLETE_SKILL_NAMES) — never wildcard-delete okstra-*.
658
+ async function pruneObsoleteSkills(target, opts) {
670
659
  let pruned = 0;
671
- for (const name of OBSOLETE_INTERNAL_SKILL_NAMES) {
660
+ for (const name of OBSOLETE_SKILL_NAMES) {
672
661
  const path = join(target.skillsDir, name);
673
662
  if (!(await pathEntryExists(path))) continue;
674
663
  pruned += 1;
@@ -683,7 +672,7 @@ async function pruneObsoleteInternalSkills(target, opts) {
683
672
 
684
673
  async function installSkillTargetsCopy(runtimeRoot, targets, opts) {
685
674
  if (targets.length === 0) {
686
- if (!opts.quiet) process.stdout.write(" skills: no Claude, Codex, or OMP agent home found — skipped\n");
675
+ if (!opts.quiet) process.stdout.write(" skills: no skill target found — skipped\n");
687
676
  return [];
688
677
  }
689
678
  return await Promise.all(targets.map((target) => installSkillsCopy(runtimeRoot, target, opts)));
@@ -691,7 +680,7 @@ async function installSkillTargetsCopy(runtimeRoot, targets, opts) {
691
680
 
692
681
  async function installSkillTargetsLink(repoAbs, targets, opts) {
693
682
  if (targets.length === 0) {
694
- if (!opts.quiet) process.stdout.write(" skills: no Claude, Codex, or OMP agent home found — skipped\n");
683
+ if (!opts.quiet) process.stdout.write(" skills: no skill target found — skipped\n");
695
684
  return [];
696
685
  }
697
686
  return await Promise.all(targets.map((target) => installSkillsLink(repoAbs, target, opts)));
@@ -15,6 +15,8 @@ const FALLBACK_SKILL_NAMES = [
15
15
  "okstra-memory",
16
16
  "okstra-inspect",
17
17
  "okstra-schedule",
18
+ "okstra-container-build",
19
+ "okstra-container",
18
20
  "okstra-context-loader",
19
21
  "okstra-team-contract",
20
22
  "okstra-convergence",
@@ -38,16 +40,13 @@ const FALLBACK_AGENT_NAMES = [
38
40
 
39
41
  const USER_HOME = homedir();
40
42
  const CLAUDE_HOME = join(USER_HOME, ".claude");
41
- const CODEX_HOME = join(USER_HOME, ".codex");
42
- const OMP_AGENT_HOME = join(USER_HOME, ".omp", "agent");
43
+ const AGENT_HOME = join(USER_HOME, ".agent");
43
44
  const CLAUDE_SKILLS_DIR = join(CLAUDE_HOME, "skills");
44
- const CODEX_SKILLS_DIR = join(CODEX_HOME, "skills");
45
- const OMP_SKILLS_DIR = join(OMP_AGENT_HOME, "skills");
45
+ const AGENT_SKILLS_DIR = join(AGENT_HOME, "skills");
46
46
  const CLAUDE_AGENTS_DIR = join(CLAUDE_HOME, "agents");
47
47
  const FALLBACK_SKILL_ROOTS = [
48
48
  CLAUDE_SKILLS_DIR,
49
- CODEX_SKILLS_DIR,
50
- OMP_SKILLS_DIR,
49
+ AGENT_SKILLS_DIR,
51
50
  ];
52
51
  const SKILLS_MANIFEST_REL = "installed-skills.json";
53
52
  const AGENTS_MANIFEST_REL = "installed-agents.json";
@@ -61,8 +60,8 @@ Usage:
61
60
  installed-skills.json, installed-agents.json} AND
62
61
  every skill target recorded in
63
62
  installed-skills.json
64
- (~/.claude/skills, ~/.codex/skills, or
65
- ~/.omp/agent/skills). Claude worker agents are
63
+ (~/.claude/skills or ~/.agent/skills).
64
+ Claude worker agents are
66
65
  removed from ~/.claude/agents when installed.
67
66
  Missing manifests fall back to hard-coded
68
67
  Claude okstra-* / *-worker.md names.
@@ -73,7 +72,7 @@ Usage:
73
72
  (<PROJECT>/.claude/settings.local.json symlink)
74
73
  are NOT touched — remove them manually if needed.
75
74
  okstra uninstall --purge Remove the entire ~/.okstra directory AND
76
- okstra skills in Claude/Codex/OMP homes AND
75
+ okstra skills in Claude/Agent homes AND
77
76
  ~/.claude/agents/<*-worker.md> (DESTRUCTIVE).
78
77
  Requires -y or an interactive confirmation
79
78
  okstra uninstall --dry-run Print the plan without touching disk
@@ -137,7 +136,7 @@ export async function runUninstall(args) {
137
136
  if (opts.purge) {
138
137
  if (!opts.yes && !opts.dryRun) {
139
138
  const ok = await promptConfirm(
140
- `purge entire ${paths.home} AND okstra skills in Claude/Codex/OMP homes? user data will be lost.`,
139
+ `purge entire ${paths.home} AND okstra skills in Claude/Agent homes? user data will be lost.`,
141
140
  );
142
141
  if (!ok) {
143
142
  process.stdout.write("aborted.\n");
@@ -1,5 +1,5 @@
1
1
  // Single source of truth for which okstra skills are user-facing and which
2
- // former internal skill directories must be pruned from agent skill homes on
2
+ // former or renamed skill directories must be pruned from agent skill homes on
3
3
  // (re)install. The Claude plugin manifest (.claude-plugin/plugin.json) and the
4
4
  // installer both derive their skill list from here, so the public surface
5
5
  // cannot drift between discovery and install.
@@ -11,26 +11,27 @@ export const USER_SKILL_NAMES = Object.freeze([
11
11
  "okstra-memory",
12
12
  "okstra-inspect",
13
13
  "okstra-schedule",
14
- "okstra-container",
14
+ "okstra-container-build",
15
15
  ]);
16
16
 
17
- // Names okstra used to install as skills before lead/internal contracts moved
18
- // into the runtime resource tree (~/.okstra/prompts/). Install prunes these
19
- // exact directories from each target skill home so upgrades do not leave the
20
- // old internal skills behind. Exact-name only — never wildcard.
21
- export const OBSOLETE_INTERNAL_SKILL_NAMES = Object.freeze([
17
+ // Names okstra used to install as skills before they were renamed or moved
18
+ // into the runtime resource tree. Install prunes these exact directories from
19
+ // each target skill home so upgrades do not leave stale slash commands behind.
20
+ // Exact-name only — never wildcard.
21
+ export const OBSOLETE_SKILL_NAMES = Object.freeze([
22
22
  "okstra",
23
23
  "okstra-context-loader",
24
24
  "okstra-team-contract",
25
25
  "okstra-convergence",
26
26
  "okstra-report-writer",
27
27
  "okstra-coding-preflight",
28
+ "okstra-container",
28
29
  ]);
29
30
 
30
31
  export function userSkillNames() {
31
32
  return [...USER_SKILL_NAMES];
32
33
  }
33
34
 
34
- export function obsoleteInternalSkillNames() {
35
- return [...OBSOLETE_INTERNAL_SKILL_NAMES];
35
+ export function obsoleteSkillNames() {
36
+ return [...OBSOLETE_SKILL_NAMES];
36
37
  }