okstra 0.123.0 → 0.125.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.
Files changed (98) hide show
  1. package/README.md +1 -0
  2. package/docs/architecture/storage-model.md +3 -1
  3. package/docs/architecture.md +16 -2
  4. package/docs/cli.md +3 -1
  5. package/docs/for-ai/README.md +41 -35
  6. package/docs/for-ai/skills/okstra-brief-gen.md +105 -105
  7. package/docs/for-ai/skills/okstra-container-build.md +61 -61
  8. package/docs/for-ai/skills/okstra-graphify.md +64 -0
  9. package/docs/for-ai/skills/okstra-inspect.md +87 -86
  10. package/docs/for-ai/skills/okstra-manager.md +32 -32
  11. package/docs/for-ai/skills/okstra-memory.md +49 -50
  12. package/docs/for-ai/skills/okstra-pr-gen.md +48 -0
  13. package/docs/for-ai/skills/okstra-rollup.md +58 -58
  14. package/docs/for-ai/skills/okstra-run.md +95 -95
  15. package/docs/for-ai/skills/okstra-schedule-gen.md +106 -106
  16. package/docs/for-ai/skills/okstra-setup.md +63 -64
  17. package/docs/for-ai/skills/okstra-user-response.md +48 -0
  18. package/docs/performance-improvement-plan-v2.md +6 -6
  19. package/docs/pr-template-usage.md +34 -34
  20. package/docs/project-structure-overview.md +91 -70
  21. package/docs/task-process/README.md +33 -33
  22. package/docs/task-process/common-flow.md +26 -26
  23. package/docs/task-process/error-analysis.md +20 -21
  24. package/docs/task-process/final-verification.md +41 -41
  25. package/docs/task-process/implementation-planning.md +33 -33
  26. package/docs/task-process/implementation.md +38 -38
  27. package/docs/task-process/release-handoff.md +46 -46
  28. package/docs/task-process/requirements-discovery.md +22 -23
  29. package/package.json +1 -1
  30. package/runtime/BUILD.json +2 -2
  31. package/runtime/agents/workers/antigravity-worker.md +4 -4
  32. package/runtime/agents/workers/claude-worker.md +2 -2
  33. package/runtime/agents/workers/codex-worker.md +4 -4
  34. package/runtime/agents/workers/report-writer-worker.md +4 -4
  35. package/runtime/bin/lib/okstra/usage.sh +1 -1
  36. package/runtime/prompts/coding-preflight/frameworks/node-server.md +1 -1
  37. package/runtime/prompts/launch.template.md +1 -1
  38. package/runtime/prompts/lead/convergence.md +10 -20
  39. package/runtime/prompts/lead/okstra-lead-contract.md +15 -17
  40. package/runtime/prompts/lead/plan-body-verification.md +18 -18
  41. package/runtime/prompts/lead/report-writer.md +46 -44
  42. package/runtime/prompts/lead/team-contract.md +11 -122
  43. package/runtime/prompts/profiles/_common-contract.md +15 -22
  44. package/runtime/prompts/profiles/_implementation-deliverable.md +1 -1
  45. package/runtime/prompts/profiles/_implementation-executor.md +1 -1
  46. package/runtime/prompts/profiles/_implementation-verifier.md +28 -2
  47. package/runtime/prompts/profiles/error-analysis.md +2 -2
  48. package/runtime/prompts/profiles/final-verification.md +1 -1
  49. package/runtime/prompts/profiles/implementation-planning.md +13 -13
  50. package/runtime/prompts/profiles/implementation.md +2 -1
  51. package/runtime/prompts/profiles/improvement-discovery.md +1 -1
  52. package/runtime/prompts/profiles/release-handoff.md +3 -3
  53. package/runtime/prompts/profiles/requirements-discovery.md +17 -18
  54. package/runtime/python/okstra_ctl/models.py +4 -2
  55. package/runtime/python/okstra_ctl/run.py +8 -2
  56. package/runtime/python/okstra_ctl/stage_fix_carry.py +112 -0
  57. package/runtime/schemas/final-report-v1.0.schema.json +6 -6
  58. package/runtime/skills/_fragments/bash-invocation-rule.md +1 -0
  59. package/runtime/skills/_fragments/preflight-outdated-cli.md +1 -0
  60. package/runtime/skills/_fragments/python-bootstrap-note.md +1 -0
  61. package/runtime/skills/okstra-brief-gen/SKILL.md +117 -122
  62. package/runtime/skills/okstra-container-build/SKILL.md +24 -14
  63. package/runtime/skills/okstra-graphify/SKILL.md +12 -4
  64. package/runtime/skills/okstra-inspect/SKILL.md +39 -747
  65. package/runtime/skills/okstra-inspect/facets/cost.md +84 -0
  66. package/runtime/skills/okstra-inspect/facets/error-zip.md +41 -0
  67. package/runtime/skills/okstra-inspect/facets/errors.md +69 -0
  68. package/runtime/skills/okstra-inspect/facets/history.md +90 -0
  69. package/runtime/skills/okstra-inspect/facets/logs.md +89 -0
  70. package/runtime/skills/okstra-inspect/facets/recap.md +96 -0
  71. package/runtime/skills/okstra-inspect/facets/report.md +61 -0
  72. package/runtime/skills/okstra-inspect/facets/status.md +145 -0
  73. package/runtime/skills/okstra-inspect/facets/time.md +56 -0
  74. package/runtime/skills/okstra-manager/SKILL.md +1 -1
  75. package/runtime/skills/okstra-memory/SKILL.md +3 -3
  76. package/runtime/skills/okstra-pr-gen/SKILL.md +3 -3
  77. package/runtime/skills/okstra-rollup/SKILL.md +12 -6
  78. package/runtime/skills/okstra-run/SKILL.md +49 -88
  79. package/runtime/skills/okstra-schedule-gen/SKILL.md +36 -30
  80. package/runtime/skills/okstra-setup/SKILL.md +1 -1
  81. package/runtime/skills/okstra-setup/references/project-config.md +17 -16
  82. package/runtime/skills/okstra-usage/SKILL.md +5 -2
  83. package/runtime/skills/okstra-user-response/SKILL.md +15 -3
  84. package/runtime/templates/prd/brief.template.md +92 -92
  85. package/runtime/templates/project-docs/task-index.template.md +1 -1
  86. package/runtime/templates/reports/error-analysis-input.template.md +1 -1
  87. package/runtime/templates/reports/fan-out-unit.template.md +6 -6
  88. package/runtime/templates/reports/final-verification-input.template.md +6 -6
  89. package/runtime/templates/reports/implementation-input.template.md +1 -1
  90. package/runtime/templates/reports/implementation-planning-input.template.md +1 -1
  91. package/runtime/templates/reports/improvement-discovery-input.template.md +1 -1
  92. package/runtime/templates/reports/quick-input.template.md +1 -1
  93. package/runtime/templates/reports/release-handoff-input.template.md +1 -1
  94. package/runtime/templates/reports/schedule.template.md +22 -22
  95. package/runtime/templates/reports/task-brief.template.md +3 -3
  96. package/runtime/templates/reports/user-response.template.md +20 -20
  97. package/runtime/templates/worker-prompt-preamble.md +111 -13
  98. package/runtime/validators/validate-schedule.py +1 -1
@@ -0,0 +1 @@
1
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
@@ -0,0 +1 @@
1
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
@@ -0,0 +1 @@
1
+ Every subsequent `okstra <subcmd>` call self-bootstraps its Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: okstra-brief-gen
3
- description: Use when the user wants to generate a task brief file for okstra from a requirements document, an existing markdown file, an issue-tracker ticket (Linear / Jira / GitHub / Notion), a link URL, conversation context, or short user input. Produces the markdown brief consumed by `okstra-run` Step 5 (task-brief). Trigger words include "okstra brief", "make a brief", "brief 생성", "요구사항 brief 만들어", "okstra 입력 만들어", "task brief 작성", " 티켓으로 brief", " 링크로 brief".
3
+ description: Use when the user wants to generate a task brief file for okstra from a requirements document, an existing markdown file, an issue-tracker ticket (Linear / Jira / GitHub / Notion), a link URL, conversation context, or short user input. Produces the markdown brief consumed by `okstra-run` Step 5 (task-brief). Trigger words include "okstra brief", "make a brief", "generate a brief", "make a brief from requirements", "make an okstra input", "write a task brief", "brief from this ticket", "brief from this link".
4
4
  ---
5
5
 
6
6
  # okstra-brief-gen
@@ -10,13 +10,12 @@ Generate a single `task brief` markdown file under the okstra project domain
10
10
  [`okstra-run`](../okstra-run/SKILL.md) as `--task-brief`.
11
11
 
12
12
  **Purpose — pre-discovery artifact.** A brief is the **pre-discovery** stage
13
- of an okstra task: it absorbs domain alignment, codebase evidence
14
- collection, and reporter intent capture so that the downstream expert
15
- phases (`requirements-discovery` / `error-analysis` /
16
- `implementation-planning`) can run with **zero fill-in questions** to the
17
- operator. A brief is written from a report by a domain reporter (PM, CS,
18
- ops, QA, end-user, …) **or** by a developer themselves — the input
19
- provenance does not change the output contract.
13
+ of an okstra task: it absorbs domain alignment, codebase evidence, and
14
+ reporter intent so the downstream expert phases (`requirements-discovery` /
15
+ `error-analysis` / `implementation-planning`) run with **zero fill-in
16
+ questions** to the operator. It is written from a domain reporter's report
17
+ (PM, CS, ops, QA, end-user, …) **or** by a developer provenance does not
18
+ change the output contract.
20
19
 
21
20
  This is a **labelled-handoff** to the next phase, built on two
22
21
  non-negotiables:
@@ -24,19 +23,19 @@ non-negotiables:
24
23
  - **Verbatim-preserving capture** — the reporter's words are kept
25
24
  byte-for-byte under `Source Material`. Format-only transformations
26
25
  (e.g. Jira ADF → Markdown) and codebase cross-references are mechanical
27
- and reversible; they are recorded with the `format-conversion` and
28
- `evidence-link` Augmentation labels.
26
+ and reversible, recorded with the `format-conversion` and `evidence-link`
27
+ Augmentation labels.
29
28
  - **Labelled interpretation** — anywhere the skill resolves a reporter
30
- word to a project-canonical term or infers reporter meaning that was
31
- not literally stated, the addition lives in `Augmentation` with the
32
- `terminology-mapping` or `intent-inference` label. Unlabelled
33
- augmentation is forbidden — the label is what makes the interpretation
34
- auditable and (for `intent-inference`) verifiable against the reporter.
29
+ word to a project-canonical term or infers meaning not literally stated,
30
+ the addition lives in `Augmentation` with the `terminology-mapping` or
31
+ `intent-inference` label. Unlabelled augmentation is forbidden — the
32
+ label is what makes the interpretation auditable and (for
33
+ `intent-inference`) verifiable against the reporter.
35
34
 
36
35
  A brief still does NOT write a PRD, decompose work into vertical slices,
37
- or decide modules. Those belong to later okstra phases
38
- (`requirements-discovery`, `implementation-planning`) which use
39
- cross-verification and would conflict with anything decided here.
36
+ or decide modules those belong to later cross-verification phases
37
+ (`requirements-discovery`, `implementation-planning`) and would conflict
38
+ with anything decided here.
40
39
 
41
40
  ## Intended chain
42
41
 
@@ -67,7 +66,7 @@ okstra-brief-gen (error-feedback variant)
67
66
 
68
67
  - The user pastes / points at a requirements doc, support ticket, bug report,
69
68
  or rough description and wants to start the okstra pipeline.
70
- - The user asks to "make a brief", "okstra 입력 만들어", "brief 생성", etc.
69
+ - The user asks to "make a brief", "make an okstra input", "generate a brief", etc.
71
70
  - A previous okstra task produced findings the user wants to convert into a
72
71
  new follow-up task's brief.
73
72
 
@@ -82,21 +81,17 @@ okstra-brief-gen (error-feedback variant)
82
81
 
83
82
  ## Artifact-home rule (okstra-wide)
84
83
 
85
- **All okstra-generated artifacts live under `<PROJECT_ROOT>/.okstra/`.**
86
- This includes briefs, run manifests, reports, worker results, status,
87
- sessions, and any other run output.
88
-
89
- All writes by this skill stay inside `.okstra/`. When domain
90
- alignment (Step 3b / Step 4.5) yields a glossary change that the user
91
- approves inline, the change is written to:
92
-
93
- - `<PROJECT_ROOT>/.okstra/glossary.md` okstra's internal
94
- glossary. Created if absent; appended to a `## Glossary` section
95
- otherwise.
96
-
97
- Paths outside `<PROJECT_ROOT>/.okstra/**` are not okstra
98
- memory. This skill reads them only when the reporter explicitly cited them
99
- as source material, and never writes them. Decision files (when raised by
84
+ **All okstra-generated artifacts live under `<PROJECT_ROOT>/.okstra/`** —
85
+ briefs, run manifests, reports, worker results, status, sessions, and any
86
+ other run output. All writes by this skill stay inside `.okstra/`. When
87
+ domain alignment (Step 3b / Step 4.5) yields a user-approved glossary
88
+ change, it is written to `<PROJECT_ROOT>/.okstra/glossary.md` (okstra's
89
+ internal glossary; created if absent, else appended to a `## Glossary`
90
+ section).
91
+
92
+ Paths outside `<PROJECT_ROOT>/.okstra/**` are not okstra memory. This skill
93
+ reads them only when the reporter explicitly cited them as source material,
94
+ and never writes them. Decision files (when raised by
100
95
  `implementation-planning`) also live inside okstra's subtree at
101
96
  `<PROJECT_ROOT>/.okstra/decisions/<NNNN>-<slug>.md`.
102
97
 
@@ -108,9 +103,8 @@ as source material, and never writes them. Decision files (when raised by
108
103
  (single / parent) or
109
104
  `<PROJECT_ROOT>/.okstra/briefs/<task-group>/sub/<ticket-id>-<file-title>.md`
110
105
  (tracker child ticket; at depth N, `sub/` is nested N times) — output
111
- location for this skill. `<ticket-id>` is the raw issue-id when the source
112
- is a tracker, otherwise free-text supplied by the user. `<file-title>` is
113
- auto-slugified from the tracker title, otherwise from user input.
106
+ location for this skill. See Step 2b for `<ticket-id>` / `<file-title>`
107
+ resolution.
114
108
  - okstra-internal glossary (write target of Step 4.5):
115
109
  `<PROJECT_ROOT>/.okstra/glossary.md`. Created if absent.
116
110
  - okstra-internal decisions (write target of `implementation-planning`
@@ -119,13 +113,23 @@ as source material, and never writes them. Decision files (when raised by
119
113
 
120
114
  ## Step 0: Preflight
121
115
 
122
- Every Bash command in this skill is a **separate Bash tool call** starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match).
116
+ Every Bash command in this skill is a **separate Bash tool call**.
117
+
118
+ <!-- BEGIN FRAGMENT: bash-invocation-rule -->
119
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
120
+ <!-- END FRAGMENT: bash-invocation-rule -->
123
121
 
124
122
  ```bash
125
123
  okstra preflight --runtime claude-code --json
126
124
  ```
127
125
 
128
- Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string into the steps below. `ok: false` (or `okstra` not on `PATH` at all) → tell the user: "okstra not set up — run `/okstra-setup` first." Then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary). Do **not** try `npx -y okstra@latest ...` as a fallback.
126
+ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string into the steps below. `ok: false` (or `okstra` not on `PATH` at all) → tell the user: "okstra not set up — run `/okstra-setup` first." Then stop.
127
+
128
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
129
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
130
+ <!-- END FRAGMENT: preflight-outdated-cli -->
131
+
132
+ Do **not** try `npx -y okstra@latest ...` as a fallback.
129
133
 
130
134
  ## Step 1: Choose input source
131
135
 
@@ -135,9 +139,9 @@ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string in
135
139
 
136
140
  - **Label**: `"Brief variant?"`
137
141
  - **Options**:
138
- 1. `Reporter input` — reporter 발화 / 티켓 / 링크 / 자유 텍스트를 verbatim 으로 흡수. 기존 4-source 흐름.
139
- 2. `Codebase scan` — 코드베이스 범위 + lens 우선순위만 받아 `improvement-discovery` phase 의 입력을 생성.
140
- 3. `Error feedback` (`error-feedback`) — `okstra error-zip` 만든 `.zip` 빈발 클러스터 1개를 `error-analysis` brief 변환 (okstra 자가수정용).
142
+ 1. `Reporter input` — absorb the reporter's utterance / ticket / link / free text verbatim. The existing 4-source flow.
143
+ 2. `Codebase scan` — take only the codebase scope + lens priorities and generate the input for the `improvement-discovery` phase.
144
+ 3. `Error feedback` (`error-feedback`) — convert one frequent cluster from the `.zip` produced by `okstra error-zip` into an `error-analysis` brief (for okstra self-repair).
141
145
 
142
146
  ### 1.A. Reporter input (variant 1)
143
147
 
@@ -171,28 +175,24 @@ in Source Material.
171
175
  Order of operations:
172
176
 
173
177
  0. **Task-group precondition (REQUIRED before any recursion)** — if the
174
- tracker fetch may yield child tickets (which is always possible until the
175
- first fetch proves otherwise), acquire `task_group` first by running Step
176
- 2a now. Step 2a is otherwise re-entered later for non-tracker sources;
177
- when reached again it MUST reuse the value collected here without asking
178
- again. Rationale: the recursion in sub-step 6 records descendant brief
179
- paths under `<task-group>/sub.../`, and those paths cannot be formed
180
- until `task_group` is known. Without this preflight the recursive walk
181
- would either stall mid-collection or invent a placeholder group.
178
+ tracker fetch may yield child tickets (always possible until the first
179
+ fetch proves otherwise), acquire `task_group` first by running Step 2a
180
+ now; when Step 2a is re-entered later it MUST reuse this value without
181
+ asking again. Why: sub-step 6's recursion records descendant brief paths
182
+ under `<task-group>/sub.../`, which cannot be formed until `task_group`
183
+ is known without this preflight the walk stalls or invents a
184
+ placeholder group.
182
185
 
183
186
  **Sequential, never batched.** Run Step 2a as its **own**
184
- `AskUserQuestion` call here, then ask sub-step 1's ticket question in a
185
- **separate** call. Do NOT combine the task-group question and the
186
- ticket-ref question into one `AskUserQuestion` they are asked one at a
187
- time, task group first (this preflight), ticket ref second. When you
188
- relay this preflight, tell the user in one line *why* task group comes
189
- before the ticket (it scopes where this ticket's brief — and any
190
- sub-ticket briefs — are written).
187
+ `AskUserQuestion` call, then ask sub-step 1's ticket question in a
188
+ **separate** call never combine the task-group and ticket-ref
189
+ questions into one. When you relay this preflight, tell the user in one
190
+ line *why* task group comes first (it scopes where this ticket's brief
191
+ and any sub-ticket briefs are written).
191
192
  1. `AskUserQuestion` (free text):
192
193
  `"Ticket key or URL (e.g. LIN-1234, PROJ-42, https://linear.app/..., https://your.atlassian.net/browse/...)"` →
193
194
  `ticket_ref`. Issue this only **after** Step 2a's task-group answer is in
194
- hand never in the same `AskUserQuestion` call as the task-group
195
- question.
195
+ hand (per sub-step 0's sequential rule).
196
196
  2. Auto-detect the tracker by URL host or key prefix (`linear.app` → Linear,
197
197
  `atlassian.net` or `JIRA_*` pattern → Jira, `github.com/.../issues/` →
198
198
  GitHub, `notion.so` → Notion). If detection fails, ask once more via
@@ -288,32 +288,32 @@ If the user picked `Codebase scan`, gather the codebase-scan-specific inputs:
288
288
 
289
289
  Validation (before Step 4 sharpening):
290
290
 
291
- - `scan_scope` path `<PROJECT_ROOT>` 안에 실제 존재하는지 `ls` 확인 없으면 질문으로 정정.
292
- - `priority_lenses` `~/.okstra/lib/python/okstra_ctl/improvement_lenses.py` `LENSES` 부분집합 (1–4) 인지 확인 — 위반 시 enum 표시 + 재선택. 최종 enforcement Step 6.6 `validate-brief` (`check_codebase_scope`) 가 수행.
293
- - `candidate_cap` 1–12 정수인지 확인.
291
+ - Confirm that each path in `scan_scope` actually exists inside `<PROJECT_ROOT>` via `ls` if not, correct it with a single question.
292
+ - Confirm that `priority_lenses` is a subset (1–4) of `LENSES` in `~/.okstra/lib/python/okstra_ctl/improvement_lenses.py` on violation, show the enum + re-select. Final enforcement is done by Step 6.6 `validate-brief` (`check_codebase_scope`).
293
+ - Confirm that `candidate_cap` is an integer in 1–12.
294
294
 
295
295
  Once validation passes, jump to Step 2 (task key).
296
296
 
297
- ### 1.C. Error feedback (`error-feedback`, variant 3) — okstra 자가수정용
297
+ ### 1.C. Error feedback (`error-feedback`, variant 3) — for okstra self-repair
298
298
 
299
- If the user picked `Error feedback`, the input is a `.zip` produced by `okstra error-zip --out <path>`. variant 빈발 에러 클러스터 1개를 골라 `error-analysis` brief 변환하며, **수정 대상은 okstra 레포 자신**이다.
299
+ If the user picked `Error feedback`, the input is a `.zip` produced by `okstra error-zip --out <path>`. This variant selects one frequent error cluster and converts it into an `error-analysis` brief, and **the repair target is the okstra repo itself**.
300
300
 
301
- 입력: `okstra error-zip` 만든 `.zip`. 사용자에게 zip 경로를 묻는다(이전에 받은 경로가 있으면 `Recommended:` 로 재사용 제안).
301
+ Input: the `.zip` produced by `okstra error-zip`. Ask the user for the zip path (if a path was received earlier, propose reusing it as `Recommended:`).
302
302
 
303
- 1. zip `report.md` 읽어 빈발 클러스터 표를 사용자에게 보여주고, **클러스터 1개**를 고르게 한다.
304
- `AskUserQuestion` (single-select, `직접 지정` always last):
305
- 1. top-1 클러스터 (빈도 최다) — `Recommended:` 표시.
306
- 2. top-2 클러스터.
307
- 3. `직접 지정`사용자가 표에서 직접 클러스터를 지정.
308
- 2. 고른 클러스터의 익명화 레코드를 brief `## Source Material` 에 인용한다 zip 내부 `errors/anonymized.jsonl` 에서 **선택한 클러스터 번호(`report.md` 표의 `#`) 일치하는 `clusterId` 레코드**만 추려, errorType · command · exitCode · phase · agent · 대표 메시지를 표/리스트로 옮긴다. (`#` ↔ `clusterId` 1:1 대응이며, 같은 errorType+phase+agent 라도 정규화 메시지가 다르면 별개 클러스터다병합하지 않는다.)
309
- 3. frontmatter `scope` reporter-input 으로 둔다 variant 익명화 에러 레코드를 `## Source Material` verbatim 으로 담는 reporter-input 계열이므로 `scope` 키를 생략한다(⇒ `reporter-input`). codebase-scan 전용 (`priority-lenses`·`scan-scope` ) 쓰지 않는다. 다음 phase 신호는 Step 6 `Recommended next phase: error-analysis` 헤더 줄로 보낸다(별도 frontmatter 키를 만들지 않는다). 본문에는 수정 대상을 명시한다: " 에러는 okstra 런타임/프롬프트/래퍼/권한 seed 결함 신호이며, 수정 대상은 okstra 소스(`prompts/`·`agents/`·`scripts/`·`templates/`)다."
310
- 4. **단일 클러스터 = 단일 실패조건** 원칙을 지킨다이질적 errorType brief 섞지 않는다(`error-analysis` symptom-lock 요건). 다른 클러스터도 고치고 싶으면 클러스터마다 별도 brief 만든다.
311
- 5. 생성된 brief 끝에 다음 단계를 인라인으로 적는다: "okstra 레포에서 `okstra-run --task-type error-analysis` 로 이 brief 를 실행하세요."
303
+ 1. Read the zip's `report.md`, show the frequent-cluster table to the user, and have them pick **one cluster**.
304
+ `AskUserQuestion` (single-select, `Specify manually` always last):
305
+ 1. top-1 cluster (highest frequency) — marked `Recommended:`.
306
+ 2. top-2 cluster.
307
+ 3. `Specify manually`the user names a cluster directly from the table.
308
+ 2. Quote the chosen cluster's anonymized records into the brief's `## Source Material` — from `errors/anonymized.jsonl` inside the zip, pull only the `clusterId` records **matching the chosen cluster number (the `#` in `report.md`'s table)**, and move their errorType · command · exitCode · phase · agent · representative message into a table/list. (`#` ↔ `clusterId` is a 1:1 mapping, and even the same errorType+phase+agent counts as a separate cluster when the normalized message differs do not merge them.)
309
+ 3. Leave frontmatter `scope` as reporter-input because this variant carries anonymized error records verbatim in `## Source Material`, it belongs to the reporter-input family, so omit the `scope` key (⇒ `reporter-input`). Do not use the codebase-scan-only keys (`priority-lenses`·`scan-scope`, etc.). Send the next-phase signal via Step 6's `Recommended next phase: error-analysis` header line (do not create a separate frontmatter key). In the body, state the repair target explicitly: "this error is a signal of an okstra runtime/prompt/wrapper/permission-seed defect, and the repair target is the okstra source (`prompts/`·`agents/`·`scripts/`·`templates/`)."
310
+ 4. Keep the **single cluster = single failure condition** principledo not mix heterogeneous errorTypes into one brief (the `error-analysis` symptom-lock requirement). To also fix another cluster, make a separate brief per cluster.
311
+ 5. At the end of the generated brief, write the next step inline: "run this brief from the okstra repo with `okstra-run --task-type error-analysis`."
312
312
 
313
313
  Validation (before Step 4 sharpening):
314
314
 
315
- - zip 경로가 실제 존재하고 `report.md` + `errors/anonymized.jsonl` 포함하는지 확인 없으면 질문으로 정정.
316
- - 고른 클러스터가 단일 errorType 인지 확인여러 errorType 섞이면 질문으로 1개만 남긴다.
315
+ - Confirm the zip path actually exists and contains `report.md` + `errors/anonymized.jsonl` if not, correct it with a single question.
316
+ - Confirm the chosen cluster is a single errorType if multiple errorTypes are mixed, keep only one with a single question.
317
317
 
318
318
  Once validation passes, jump to Step 2 (task key).
319
319
 
@@ -334,34 +334,33 @@ okstra task-list
334
334
  Parse the JSON `tasks[]` array. The `taskGroup` field is a **raw display
335
335
  name, not a slug** — `slugify` each value (lowercase + non-alphanumeric →
336
336
  `-`) and **dedupe by slug** so case/format variants collapse (e.g.
337
- `uploadFont` and `uploadfont` are one group, not two). Order the distinct
338
- slugs by their most-recent `updatedAt`. Take the **2 most-recent** distinct
339
- slugs as recommendations and show them in slug form.
337
+ `uploadFont` and `uploadfont` are one group, not two). Order distinct slugs
338
+ by most-recent `updatedAt` and take the **2 most-recent** as recommendations,
339
+ shown in slug form.
340
340
 
341
341
  `AskUserQuestion` (single-select):
342
342
 
343
343
  - **Label**: `"Task group?"`
344
- - **Options** (recommendations first, `직접 입력` always last):
344
+ - **Options** (recommendations first, `Enter directly` always last):
345
345
  1. (and 2.) each recommended existing group, most-recent first —
346
- label `<group> (기존)` (e.g. `uploadfont (기존)`).
347
- 3. `직접 입력` — free-text for a new or other group.
346
+ label `<group> (existing)` (e.g. `uploadfont (existing)`).
347
+ 3. `Enter directly` — free-text for a new or other group.
348
348
 
349
349
  When the user picks an existing group, use its value verbatim. When they
350
- pick `직접 입력`, take a free-text value via a follow-up question.
350
+ pick `Enter directly`, take a free-text value via a follow-up question.
351
351
 
352
352
  If `okstra task-list` returns an empty `tasks[]` (no prior tasks), skip the
353
353
  picker and ask one free-text question:
354
354
  `"Task group (e.g. backend-api, INV-1234, refactor)"` → `task_group`.
355
355
 
356
356
  Slug rule: same as `okstra-run` Step 3 — slugify, must have at least one
357
- alphanumeric character. **Always slugify the chosen value**, whether it came
358
- from an existing-group selection or `직접 입력` — the `taskGroup` returned by
359
- `okstra task-list` is a raw display name, not a slug, so picking one verbatim
360
- would create a non-standard directory (e.g. `briefs/uploadFont/`). The brief's
361
- on-disk directory segment (`briefs/<task-group>/`) and the frontmatter
362
- `task-group` value are both the slugified form, and `validate-brief.py`
363
- (Step 6.6) rejects any brief whose directory segment differs from
364
- `slugify(frontmatter task-group)`.
357
+ alphanumeric character. **Always slugify the chosen value**, whether from an
358
+ existing-group selection or `Enter directly` — the `taskGroup` from `okstra
359
+ task-list` is a raw display name, so picking one verbatim would create a
360
+ non-standard directory (e.g. `briefs/uploadFont/`). The on-disk directory
361
+ segment (`briefs/<task-group>/`) and the frontmatter `task-group` are both
362
+ the slugified form, and `validate-brief.py` (Step 6.6) rejects any brief
363
+ whose directory segment differs from `slugify(frontmatter task-group)`.
365
364
 
366
365
  If `task_group` was already collected by Step 1b sub-step 0 (tracker
367
366
  preflight), reuse that value silently — do NOT ask again.
@@ -482,10 +481,9 @@ on a project-internal concrete object whenever possible.
482
481
  `Read` / `Grep` to locate the concrete file path (relative to
483
482
  `<PROJECT_ROOT>`) and the canonical symbol name. Record the resolution
484
483
  under `Augmentation > Domain alignment` with the `evidence-link` label,
485
- and link the absolute path from `Related Artifacts`. This is what makes
486
- the brief portable across downstream workers (Claude / Codex / Antigravity)
487
- that may operate in separate worktrees and need to point at the same
488
- object.
484
+ and link the absolute path from `Related Artifacts` this keeps the brief
485
+ portable across downstream workers (Claude / Codex / Antigravity) in
486
+ separate worktrees that must point at the same object.
489
487
  - **Conversion-block signal** — when a reporter statement cannot be
490
488
  mapped to project vocabulary even after codebase exploration (e.g. the
491
489
  reporter names a UI element that does not exist by that name, or
@@ -549,22 +547,22 @@ or to a `> augmented: <label>` blockquote inside the required section, so
549
547
  it is clear that the content is the skill's / user's added interpretation
550
548
  rather than the original source.
551
549
 
552
- ### codebase-scan variant 전용 강화 규칙 (improvement-discovery)
550
+ ### codebase-scan variant-specific sharpening rules (improvement-discovery)
553
551
 
554
- 5 규칙은 `scope: codebase` brief 한해 적용. budget 기본 6 → **8** 로 확대.
552
+ These 5 rules apply only to `scope: codebase` briefs. The budget expands from the default 6 → **8**.
555
553
 
556
- 1. **scan-scope 경로 존재 검증.** path `ls` / `Read` 확인. 없으면 질문으로 정정 (path 가장 가까운 후보를 `Recommended:` 로 제시).
557
- 2. **모호 path narrowing.** `"백엔드"`, `"결제 모듈"` 같은 추상 path 구체 디렉토리 1개 이상으로 narrowing. codebase-first 추측 질문으로 확정.
558
- 3. **priority-lenses 화이트리스트 검증.** `~/.okstra/lib/python/okstra_ctl/improvement_lenses.py` `LENSES` 부분집합인지 확인. out-of-enum 이면 enum 가장 가까운 값을 `Recommended:` 로 제시.
559
- 4. **out-of-scope 일관성.** `out-of-scope` path `scan-scope` 부분집합인지 확인. 무관한 path 질문으로 제거 또는 scan-scope 에 흡수.
560
- 5. **lens 우선순위 근거 1줄.** priority lens *왜* scope 에서 우선인지 brief 본문에 없으면, codebase 1차 훑은 결과를 `Recommended:` 제시 한 질문.
554
+ 1. **scan-scope path existence check.** Confirm each path via `ls` / `Read`. If missing, correct it with a single question (offer the nearest path candidate as `Recommended:`).
555
+ 2. **Ambiguous path narrowing.** Narrow abstract paths like `"backend"`, `"payment module"` down to one or more concrete directories. Guess codebase-first, then confirm with a single question.
556
+ 3. **priority-lenses whitelist check.** Confirm it is a subset of `LENSES` in `~/.okstra/lib/python/okstra_ctl/improvement_lenses.py`. If out-of-enum, offer the nearest in-enum value as `Recommended:`.
557
+ 4. **out-of-scope consistency.** Confirm each path in `out-of-scope` is a subset of `scan-scope`. If a path is unrelated, remove it with a single question or absorb it into scan-scope.
558
+ 5. **One line of lens-priority rationale.** If the brief body lacks a line on *why* each priority lens takes precedence in this scope, offer the result of a first codebase pass as `Recommended:` and ask one question.
561
559
 
562
- stop conditions (codebase-scan 한정 추가):
560
+ stop conditions (codebase-scan-specific additions):
563
561
 
564
- - `scan-scope` 모든 path codebase 에 존재
565
- - `priority-lenses` valid enum 부분집합 (1–4)
562
+ - Every path in `scan-scope` exists in the codebase
563
+ - `priority-lenses` is a valid enum subset (1–4 of them)
566
564
 
567
- 조건이 모두 만족되면 budget 남아 있어도 sharpening 종료 가능.
565
+ When both of the above conditions hold, sharpening may end even if budget remains.
568
566
 
569
567
  ### Augmentation labels (REQUIRED — no unlabelled augmentation)
570
568
 
@@ -577,7 +575,7 @@ labels. Unlabelled augmentation is rejected as half-formed translation.
577
575
  | `evidence-link` | Cross-reference / file path / symbol resolved from the source against the actual codebase. Pure fact, verified by `Read` / `Grep`. | Trust without verification. |
578
576
  | `format-conversion` | Format-only transform (Jira ADF → Markdown, HTML → Markdown, etc.). Semantics unchanged. | Trust without verification. |
579
577
  | `terminology-mapping` | Reporter's word mapped to an okstra-canonical term from Step 3b. | Verify against `.okstra/glossary.md`; if mismatch, treat as a clarification candidate. |
580
- | `intent-inference` | Reporter did NOT literally state this — the skill inferred meaning from context (e.g. classifying "가끔 " as "intermittent failure on a specific path"). Qualitative only — **never** invent quantitative thresholds (numbers, latencies, percentages, counts). | **Verify with the reporter before acting.** Auto-mirrored into `Open Questions`. |
578
+ | `intent-inference` | Reporter did NOT literally state this — the skill inferred meaning from context (e.g. classifying "sometimes it doesn't work" as "intermittent failure on a specific path"). Qualitative only — **never** invent quantitative thresholds (numbers, latencies, percentages, counts). | **Verify with the reporter before acting.** Auto-mirrored into `Open Questions`. |
581
579
 
582
580
  **Inline form** — `> augmented: intent-inference — <one line>`.
583
581
  **Section form** — `- intent-inference: <one line>` under `Augmentation`.
@@ -585,12 +583,11 @@ labels. Unlabelled augmentation is rejected as half-formed translation.
585
583
  ### Auto-mirroring rule
586
584
 
587
585
  Every `intent-inference` entry must have a paired entry in `Open Questions`
588
- with the prefix `intent-check:` describing what the reporter must confirm.
589
- This is what makes the inference auditable end-to-end: the inference is
590
- visible (Augmentation), and the verification need is visible (Open
591
- Questions). If a downstream worker resolves the inference without going
592
- back to the reporter, the `intent-check:` row stays open as a record of
593
- unverified translation.
586
+ with the prefix `intent-check:` describing what the reporter must confirm
587
+ the inference stays visible (Augmentation) and its verification need stays
588
+ visible (Open Questions). If a downstream worker resolves the inference
589
+ without going back to the reporter, the `intent-check:` row stays open as a
590
+ record of unverified translation.
594
591
 
595
592
  `terminology-mapping` and `conversion-block:` follow the same dual-record
596
593
  discipline (Augmentation observation ↔ `Open Questions` `terminology:` /
@@ -906,15 +903,13 @@ started.
906
903
  - **No template author-guidance in the artifact body.** The Step 5
907
904
  template carries author guidance in HTML comments (`<!-- ... -->`);
908
905
  **strip every such comment when writing the brief** — the final artifact
909
- contains zero `<!-- ... -->` blocks. (The validator now ignores comment
910
- regions, but a clean artifact is the contract, and it is what the
911
- passing reference briefs look like.) Section headings stay clean never
912
- copy parenthetical reviewer notes onto the heading itself (e.g. emit
913
- `## Source Material`, not `## Source Material (verbatim do not modify)`).
914
- Placeholder prose inside angle brackets (`<Background / scope / why
915
- now…>`) is NOT a comment it is intentional scaffolding meant to be
916
- replaced with the reporter's actual content; replace those placeholders,
917
- do not leave them in.
906
+ contains zero `<!-- ... -->` blocks (the validator ignores comment
907
+ regions, but a clean artifact is the contract). Section headings stay
908
+ clean never copy parenthetical reviewer notes onto the heading (e.g.
909
+ emit `## Source Material`, not `## Source Material (verbatim — do not
910
+ modify)`). Placeholder prose inside angle brackets (`<Background / scope
911
+ / why now…>`) is NOT a comment it is scaffolding to replace with the
912
+ reporter's actual content, not to leave in.
918
913
  - If the tracker MCP is not connected, do not guess — ask the user to paste
919
914
  the body or skip the source.
920
915
  - If a URL fetch fails or hits an auth wall, do not guess — ask for a
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: okstra-container-build
3
3
  description: |
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", "감시 종료".
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", "bring up the container", "spin up the container", "tear down the container", "container status", "container logs", "user-test environment", "user test environment", "docker compose up please", "spin up this task in a container", "stop the watcher", "stop watcher", "stop monitoring".
5
5
  ---
6
6
 
7
7
  # OKSTRA Container Build
@@ -18,35 +18,45 @@ Single entry point for the okstra user-test container runtime. okstra provisions
18
18
 
19
19
  ## Step 0: Preflight (shared)
20
20
 
21
- Before any sub-command, run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
21
+ Before any sub-command:
22
+
23
+ <!-- BEGIN FRAGMENT: bash-invocation-rule -->
24
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
25
+ <!-- END FRAGMENT: bash-invocation-rule -->
22
26
 
23
27
  ```bash
24
28
  okstra preflight --runtime claude-code --json
25
29
  ```
26
30
 
27
- Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string and pass it as `--project-root <projectRoot>` to every `okstra container` call below. `ok: false` → tell the user to run `/okstra-setup` first, then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
31
+ Parse the stdout JSON. `ok: true` → carry `projectRoot` as a literal string and pass it as `--project-root <projectRoot>` to every `okstra container` call below. `ok: false` → tell the user to run `/okstra-setup` first, then stop.
32
+
33
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
34
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
35
+ <!-- END FRAGMENT: preflight-outdated-cli -->
28
36
 
29
37
  Docker itself must be installed and running for `up`/`status`/`down` to work. If a sub-command fails with a docker connection error, tell the user to start Docker (Desktop / daemon) and retry — do not try to start docker yourself.
30
38
 
31
- Subsequent `okstra container <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
39
+ <!-- BEGIN FRAGMENT: python-bootstrap-note -->
40
+ Every subsequent `okstra <subcmd>` call self-bootstraps its Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
41
+ <!-- END FRAGMENT: python-bootstrap-note -->
32
42
 
33
43
  ## Step 1: Dispatch by intent
34
44
 
35
45
  Classify the user's request into one sub-command using the sub-command table above.
36
46
 
37
- - Clear verbs route directly: "띄워/올려/up" → `up`; "상태/status" → `status`; "로그/logs" → `logs`; "watcher 멈춰/stop-watcher" → `stop-watcher`; "내려/down" → `down`.
38
- - If the request is ambiguous (e.g. "okstra container 좀 봐줘"), present a numbered picker. Recommend the 1–2 most likely facets first, and make the **last option always "직접 입력"** so the user can name any facet:
47
+ - Clear verbs route directly: "bring up/deploy/up" → `up`; "status" → `status`; "logs" → `logs`; "stop watcher/stop-watcher" → `stop-watcher`; "tear down/down" → `down`.
48
+ - If the request is ambiguous (e.g. "take a look at the okstra container"), present a numbered picker. Recommend the 1–2 most likely facets first, and make the **last option always "Enter directly"** so the user can name any facet:
39
49
 
40
50
  ```
41
- 무엇을 할까요?
42
- 1. status — 실행 중인 컨테이너 / watcher 상태 확인 (추천)
43
- 2. up — task 컨테이너를 띄우기
44
- 3. 직접 입력 (up / status / logs / stop-watcher / down 중에서)
51
+ What would you like to do?
52
+ 1. status — check running containers / watcher state (recommended)
53
+ 2. up — bring up this task's containers
54
+ 3. Enter directly (one of up / status / logs / stop-watcher / down)
45
55
  ```
46
56
 
47
- The full facet list is the `Sub-command | What it does` table above — never hide a facet; if the user picks "직접 입력", let them name any row.
57
+ The full facet list is the `Sub-command | What it does` table above — never hide a facet; if the user picks "Enter directly", let them name any row.
48
58
 
49
- When the user chains multiple facets in one message (e.g. "올렸다가 상태 보여줘"), execute them sequentially — Step 0 runs once, each sub-command section runs once.
59
+ When the user chains multiple facets in one message (e.g. "bring it up, then show me the status"), execute them sequentially — Step 0 runs once, each sub-command section runs once.
50
60
 
51
61
  Every sub-command needs a **task-key** (`<project-id>:<task-group>:<task-id>`) — the container group is bound to one implementation task's worktree. Resolve it the same way across sub-commands:
52
62
 
@@ -61,8 +71,8 @@ Every sub-command needs a **task-key** (`<project-id>:<task-group>:<task-id>`)
61
71
  Brings up the task's container group: integrates the implementation stages into the task worktree, synthesizes the compose env override, runs `docker compose up -d`, polls healthchecks, and attaches a tmux watcher pane.
62
72
 
63
73
  **Preconditions** (state them if unmet, do not guess):
64
- - The task must be an `implementation` task whose worktree is registered in `~/.okstra/worktrees/registry.json`. If `up` fails with "task worktree registry " → the task has no implementation worktree yet; tell the user to run the `implementation` phase first.
65
- - The worktree root must contain a `docker-compose.yml`. If `up` fails with "worktree 루트에 ... 없습니다" → no compose file shipped with this task; surface the message verbatim.
74
+ - The task must be an `implementation` task whose worktree is registered in `~/.okstra/worktrees/registry.json`. If `up` fails with "task worktree is not in the registry" → the task has no implementation worktree yet; tell the user to run the `implementation` phase first.
75
+ - The worktree root must contain a `docker-compose.yml`. If `up` fails with "there is no ... at the worktree root" → no compose file shipped with this task; surface the message verbatim.
66
76
  - Every stage declared in the approved plan's Stage Map must be `done`. `up` integrates the whole task, so a partially-finished task (e.g. only stage 1 of 3 done) is refused rather than deployed as if complete (gate in `stage_targets.py`, shared with whole-task `final-verification`). If `up` fails with `final-verification(whole-task): stage N not done — run implementation --stage N first`, surface that message verbatim and tell the user to finish the named stage via the `implementation` phase with `--stage N`.
67
77
 
68
78
  Run:
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: okstra-graphify
3
3
  description: >-
4
- Use when the user wants to build, query, or navigate a knowledge graph over an okstra project's own `.okstra/` memory — its reports, decision records, and glossary. Turns that docs corpus into a persistent graph with community detection and an honest EXTRACTED/INFERRED/AMBIGUOUS audit trail, then answers cross-document questions the flat files can't. Trigger phrases include "okstra graphify", "graph the .okstra docs", "build okstra knowledge graph", "query okstra graph", "what connects to X in the okstra docs", "okstra graph 만들어", "결정 레코드 그래프". NOT for graphing arbitrary code or non-okstra folders (that is the standalone /graphify), and NOT for single-task inspection (okstra-inspect) or rollups (okstra-rollup).
4
+ Use when the user wants to build, query, or navigate a knowledge graph over an okstra project's own `.okstra/` memory — its reports, decision records, and glossary. Turns that docs corpus into a persistent graph with community detection and an honest EXTRACTED/INFERRED/AMBIGUOUS audit trail, then answers cross-document questions the flat files can't. Trigger phrases include "okstra graphify", "graph the .okstra docs", "build okstra knowledge graph", "query okstra graph", "what connects to X in the okstra docs", "build the okstra graph", "graph the decision records". NOT for graphing arbitrary code or non-okstra folders (that is the standalone /graphify), and NOT for single-task inspection (okstra-inspect) or rollups (okstra-rollup).
5
5
  trigger: /okstra-graphify
6
6
  ---
7
7
 
@@ -22,7 +22,11 @@ Only `.okstra/**/*.md` (final reports, `decisions/*.md`, `glossary.md`, briefs)
22
22
 
23
23
  ## Step 0: Preflight (shared)
24
24
 
25
- Before any sub-command, run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
25
+ Before any sub-command:
26
+
27
+ <!-- BEGIN FRAGMENT: bash-invocation-rule -->
28
+ Run one Bash tool call, starting with the literal token `okstra` (never wrapped in `if`/`eval`/`export`/`$(...)`/`VAR=...`/`||`/`&&`/`npx` — a non-literal leading token defeats the `Bash(okstra:*)` permission match):
29
+ <!-- END FRAGMENT: bash-invocation-rule -->
26
30
 
27
31
  ```bash
28
32
  okstra preflight --runtime claude-code --json
@@ -30,13 +34,17 @@ okstra preflight --runtime claude-code --json
30
34
 
31
35
  Parse the stdout JSON:
32
36
  - `ok: true` → carry `projectRoot` as a literal string; it is the `--project-root` value for every sub-command below.
33
- - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If so, re-run `okstra preflight --runtime claude-code --cwd <that-dir> --json`. Only if this **also** returns `ok:false` do you tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop. If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
37
+ - `ok: false` → before concluding "no setup", ask whether the user pointed at a specific project directory. If so, re-run `okstra preflight --runtime claude-code --cwd <that-dir> --json`. Only if this **also** returns `ok:false` do you tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
38
+
39
+ <!-- BEGIN FRAGMENT: preflight-outdated-cli -->
40
+ If the call fails with `unknown command: preflight`, the `okstra` binary on PATH predates this skill — tell the user to update it (`npm i -g okstra@latest`), then stop (`/okstra-setup` does not update the binary).
41
+ <!-- END FRAGMENT: preflight-outdated-cli -->
34
42
 
35
43
  Every `okstra graphify` call below passes `--project-root <projectRoot>` — `--project-root` is REQUIRED and there is no cwd injection; a bare `okstra graphify build` fails with a usage error.
36
44
 
37
45
  ## Step 1: Dispatch by intent
38
46
 
39
- Classify the request into one sub-command using the table above. If ambiguous ("okstra graph 보여줘"), ask which — enumerate the full sub-command table as a numbered text list and let the user pick. `build` is the prerequisite for `query`/`path`/`explain`/`wiki`/`mcp`; if the user asks to query before a graph exists, the CLI will fail cleanly with `no graph … Run \`okstra graphify build\` first` — run `build` first.
47
+ Classify the request into one sub-command using the table above. If ambiguous ("show me the okstra graph"), ask which — enumerate the full sub-command table as a numbered text list and let the user pick. `build` is the prerequisite for `query`/`path`/`explain`/`wiki`/`mcp`; if the user asks to query before a graph exists, the CLI will fail cleanly with `no graph … Run \`okstra graphify build\` first` — run `build` first.
40
48
 
41
49
  ---
42
50