okstra 0.110.0 → 0.112.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.kr.md +3 -2
- package/README.md +3 -2
- package/bin/okstra +7 -1
- package/docs/for-ai/README.md +2 -2
- package/docs/for-ai/skills/okstra-brief.md +2 -3
- package/docs/for-ai/skills/okstra-container-build.md +2 -3
- package/docs/for-ai/skills/okstra-inspect.md +5 -12
- package/docs/for-ai/skills/okstra-rollup.md +3 -4
- package/docs/for-ai/skills/okstra-run.md +10 -10
- package/docs/for-ai/skills/okstra-schedule.md +2 -7
- package/docs/for-ai/skills/okstra-setup.md +1 -1
- package/docs/kr/architecture/storage-model.md +1 -2
- package/docs/kr/architecture.md +11 -12
- package/docs/kr/cli.md +6 -4
- package/docs/project-structure-overview.md +14 -12
- package/docs/task-process/README.md +4 -4
- package/docs/task-process/common-flow.md +8 -5
- package/docs/task-process/implementation.md +4 -5
- package/docs/task-process/release-handoff.md +3 -3
- package/package.json +1 -1
- package/runtime/BUILD.json +2 -2
- package/runtime/agents/workers/antigravity-worker.md +1 -0
- package/runtime/agents/workers/codex-worker.md +1 -0
- package/runtime/prompts/coding-preflight/overview.md +3 -1
- package/runtime/prompts/launch.template.md +1 -5
- package/runtime/prompts/lead/context-loader.md +2 -4
- package/runtime/prompts/lead/convergence.md +11 -240
- package/runtime/prompts/lead/okstra-lead-contract.md +70 -34
- package/runtime/prompts/lead/plan-body-verification.md +240 -0
- package/runtime/prompts/lead/report-writer.md +7 -7
- package/runtime/prompts/lead/team-contract.md +15 -17
- package/runtime/prompts/profiles/_common-contract.md +2 -38
- package/runtime/prompts/profiles/_implementation-diff-review.md +43 -0
- package/runtime/prompts/profiles/_implementation-executor.md +9 -11
- package/runtime/prompts/profiles/_implementation-self-check.md +11 -5
- package/runtime/prompts/profiles/_implementation-verifier.md +1 -1
- package/runtime/prompts/profiles/final-verification.md +1 -1
- package/runtime/prompts/profiles/implementation-planning.md +2 -2
- package/runtime/prompts/profiles/implementation.md +1 -1
- package/runtime/python/okstra_ctl/codex_dispatch.py +3 -0
- package/runtime/python/okstra_ctl/consumers.py +4 -0
- package/runtime/python/okstra_ctl/handoff.py +5 -23
- package/runtime/python/okstra_ctl/implementation_stage.py +11 -11
- package/runtime/python/okstra_ctl/path_hints.py +1 -0
- package/runtime/python/okstra_ctl/paths.py +3 -0
- package/runtime/python/okstra_ctl/render.py +8 -0
- package/runtime/python/okstra_ctl/report_views.py +76 -26
- package/runtime/python/okstra_ctl/set_work_status.py +147 -0
- package/runtime/python/okstra_ctl/stage_targets.py +152 -0
- package/runtime/python/okstra_ctl/team_reconcile.py +1 -1
- package/runtime/python/okstra_ctl/wizard.py +47 -0
- package/runtime/python/okstra_project/__init__.py +2 -0
- package/runtime/python/okstra_project/state.py +44 -2
- package/runtime/skills/okstra-brief/SKILL.md +32 -176
- package/runtime/skills/okstra-brief/references/reporter-confirmations.md +71 -0
- package/runtime/skills/okstra-brief/references/tracker-recursion.md +90 -0
- package/runtime/skills/okstra-container-build/SKILL.md +7 -20
- package/runtime/skills/okstra-graphify/SKILL.md +8 -8
- package/runtime/skills/okstra-inspect/SKILL.md +27 -43
- package/runtime/skills/okstra-rollup/SKILL.md +6 -6
- package/runtime/skills/okstra-run/SKILL.md +27 -32
- package/runtime/skills/okstra-schedule/SKILL.md +64 -419
- package/runtime/skills/okstra-setup/SKILL.md +25 -223
- package/runtime/skills/okstra-setup/references/project-config.md +188 -0
- package/runtime/templates/reports/schedule.template.md +2 -2
- package/runtime/templates/worker-prompt-preamble.md +1 -1
- package/runtime/validators/validate-run.py +7 -7
- package/src/cli-registry.mjs +14 -0
- package/src/commands/execute/wizard.mjs +3 -1
- package/src/commands/inspect/set-work-status.mjs +31 -0
- package/src/commands/inspect/task-show.mjs +11 -31
- package/src/commands/lifecycle/check-project.mjs +68 -56
- package/src/commands/lifecycle/install.mjs +1 -0
- package/src/commands/lifecycle/preflight.mjs +82 -0
|
@@ -53,13 +53,9 @@ Show the final summary line back to the user (`version stamp: x.y.z`).
|
|
|
53
53
|
If install fails, surface the stderr verbatim. Do NOT try to "fix" it by
|
|
54
54
|
running the legacy `okstra-install.sh` — that path is dev-only.
|
|
55
55
|
|
|
56
|
-
|
|
56
|
+
**Bash invocation rule (permission-friendly)**: after Step 1, every Bash command in this skill MUST begin with the literal token `okstra` and pass literal argument values. Do not introduce shell variables (`$PROJECT_ROOT`, `$PROJECT_ID`, ...), `$(...)` command substitution, leading `VAR=...` assignments, or wrap commands in `if`/`eval`/`||`/`&&` — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every call. When a prior tool call emitted a path or value, paste the literal string into the next command. Every `okstra <subcmd>` call self-bootstraps its Python path — never run `okstra paths --shell` / `export PYTHONPATH=...`.
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
**Bash invocation rule (permission-friendly)**: from this step on, every Bash command in this skill MUST begin with the literal token `okstra` and pass literal argument values. Do not introduce shell variables (`$PROJECT_ROOT`, `$PROJECT_ID`, ...), `$(...)` command substitution, leading `VAR=...` assignments, or wrap commands in `if`/`eval`/`||`/`&&` — any of those make the leading token non-literal, defeat the `Bash(okstra:*)` permission match, and force a confirmation prompt on every call. When a prior tool call emitted a path or value, read it from the tool output and paste the literal string into the next command.
|
|
61
|
-
|
|
62
|
-
## Step 3: Resolve PROJECT_ROOT
|
|
58
|
+
## Step 2: Resolve PROJECT_ROOT
|
|
63
59
|
|
|
64
60
|
```bash
|
|
65
61
|
okstra check-project --json
|
|
@@ -74,12 +70,12 @@ Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, pro
|
|
|
74
70
|
okstra check-project --cwd /abs/path/from/user --json
|
|
75
71
|
```
|
|
76
72
|
|
|
77
|
-
- `ok: false`, `stage: "project_json_missing"` → proceed to Step
|
|
73
|
+
- `ok: false`, `stage: "project_json_missing"` → proceed to Step 3 (this is the normal create path).
|
|
78
74
|
- `ok: false`, any other `stage` (`python`, `parse`, `project_json_invalid`) → show the JSON `reason` field to the user verbatim and follow the recovery it names (typically `okstra doctor` to diagnose, then `okstra ensure-installed` or re-running the Step 1 install). `reason` is the SSOT — do not hand-enumerate stage causes.
|
|
79
75
|
|
|
80
|
-
## Step
|
|
76
|
+
## Step 3: Inspect or create `project.json`
|
|
81
77
|
|
|
82
|
-
Use the `Read` tool on the literal absolute path `<projectRoot>/.okstra/project.json` (substitute the literal `projectRoot` value parsed in Step
|
|
78
|
+
Use the `Read` tool on the literal absolute path `<projectRoot>/.okstra/project.json` (substitute the literal `projectRoot` value parsed in Step 2). If `Read` errors with "file does not exist", treat that as the "create" branch below; otherwise the file exists and you can inspect its contents inline.
|
|
83
79
|
|
|
84
80
|
If the file exists, surface its `projectId`/`projectRoot` and ask whether to
|
|
85
81
|
keep or overwrite. Default is to keep — okstra refuses to change `projectId`
|
|
@@ -95,226 +91,32 @@ If the file does NOT exist, ask via `AskUserQuestion`:
|
|
|
95
91
|
`error: --project-id is required (no existing project.json, not a TTY)`,
|
|
96
92
|
so passing the user's empty answer through is a silent failure path.
|
|
97
93
|
|
|
98
|
-
Then create the file — paste the literal `projectRoot` from Step
|
|
94
|
+
Then create the file — paste the literal `projectRoot` from Step 2 and the literal `projectId` from the user's answer (no shell variables):
|
|
99
95
|
|
|
100
96
|
```bash
|
|
101
97
|
okstra setup --yes --project-root /abs/path/to/projectRoot --project-id my-project-id
|
|
102
98
|
```
|
|
103
99
|
|
|
104
|
-
|
|
105
|
-
> work for most projects — skip straight to Step 5 (`doctor`) unless the
|
|
106
|
-
> user explicitly asks to customise worktree sync, declare QA commands,
|
|
107
|
-
> or register a PR-body template. Step 4.7 (settings.local.json symlink)
|
|
108
|
-
> is automatic; it is documented for diagnostic purposes only.
|
|
109
|
-
|
|
110
|
-
## Step 4.5 (optional): customise worktree sync dirs
|
|
111
|
-
|
|
112
|
-
Each okstra run provisions a task-scoped git worktree under
|
|
113
|
-
`~/.okstra/worktrees/.../`. A small set of project-root-relative
|
|
114
|
-
directories is symlinked from the main checkout into that worktree so
|
|
115
|
-
every task sees the shared state. The built-in default is
|
|
116
|
-
`.project-docs`, `.scratch`, `graphify-out`, `.claude`.
|
|
117
|
-
Syncing a directory does not make it okstra memory; okstra-owned context
|
|
118
|
-
and writes still stay under `<PROJECT_ROOT>/.okstra/**`
|
|
119
|
-
unless the task brief explicitly authorizes a non-okstra path.
|
|
120
|
-
|
|
121
|
-
To override per-project, add a `worktreeSyncDirs` array to
|
|
122
|
-
`project.json`. Empty array disables the feature; the field is
|
|
123
|
-
preserved across the runtime's auto-upserts (see Step 4.6 for the
|
|
124
|
-
canonical list of runtime-owned fields).
|
|
125
|
-
|
|
126
|
-
```json
|
|
127
|
-
{
|
|
128
|
-
"projectId": "...",
|
|
129
|
-
"projectRoot": "...",
|
|
130
|
-
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude", "my-custom-dir"]
|
|
131
|
-
}
|
|
132
|
-
```
|
|
133
|
-
|
|
134
|
-
Resolution precedence: `OKSTRA_WORKTREE_SYNC_DIRS` env var → this
|
|
135
|
-
field → built-in default. Only edit when defaults don't cover the
|
|
136
|
-
project's working files (e.g. additional cache or local-config dirs
|
|
137
|
-
that must follow the executor into the worktree).
|
|
138
|
-
|
|
139
|
-
## Step 4.6 (optional but recommended): declare project QA commands
|
|
140
|
-
|
|
141
|
-
`implementation`-phase verifiers run an independent QA gate over the
|
|
142
|
-
executor's diff and need a project-wide baseline of check-only
|
|
143
|
-
lint / format / typecheck / test commands. okstra does NOT auto-detect
|
|
144
|
-
tooling from manifest files — declare the commands explicitly in
|
|
145
|
-
`project.json` under `qaCommands`. Skipping this declaration is
|
|
146
|
-
allowed but the verifier will then only run the plan's per-task
|
|
147
|
-
`validation` set, with `qa-command not configured: <category>`
|
|
148
|
-
recorded per missing category in the final report.
|
|
149
|
-
|
|
150
|
-
Each category is an array of `{ "label", "cmd", "language"? }`
|
|
151
|
-
objects. `language` is optional; when present the verifier MAY skip
|
|
152
|
-
commands whose language is not represented in this run's diff.
|
|
153
|
-
|
|
154
|
-
```json
|
|
155
|
-
{
|
|
156
|
-
"projectId": "...",
|
|
157
|
-
"projectRoot": "...",
|
|
158
|
-
"qaCommands": {
|
|
159
|
-
"lint": [{ "label": "cargo clippy", "cmd": "cargo clippy --all-targets -- -D warnings", "language": "rust" }],
|
|
160
|
-
"format": [{ "label": "cargo fmt", "cmd": "cargo fmt --check", "language": "rust" }],
|
|
161
|
-
"typecheck": [{ "label": "tsc", "cmd": "pnpm exec tsc --noEmit", "language": "ts" }],
|
|
162
|
-
"test": [{ "label": "cargo test", "cmd": "cargo test --workspace --locked", "language": "rust" }]
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
```
|
|
166
|
-
|
|
167
|
-
**`cmd` deny-list (mutation guard):** the verifier rejects any `cmd`
|
|
168
|
-
containing tokens that imply mutation — declare commands in their
|
|
169
|
-
check-only form only. Representative denied tokens: `--fix` / `--write`
|
|
170
|
-
(formatters), `--updateSnapshot`, `npm install` (use `npm ci`),
|
|
171
|
-
`pip install -U`, `cargo add`. The authoritative deny list is
|
|
172
|
-
`scripts/okstra_ctl/qa_commands.py::find_denied_tokens`
|
|
173
|
-
(`_DENIED_LITERAL_TOKENS` / `_DENIED_SUBSTRINGS` plus the dynamic
|
|
174
|
-
`npm install` and `INSTA_UPDATE=` checks) — do not re-enumerate it here.
|
|
175
|
-
|
|
176
|
-
Encountering a denied token aborts the verifier with status
|
|
177
|
-
`contract-violated`; re-declare the command in check-only form to
|
|
178
|
-
recover (e.g. swap `prettier --write` → `prettier --check`).
|
|
179
|
-
|
|
180
|
-
The field is preserved across the runtime's auto-upserts of
|
|
181
|
-
`project.json` — only `projectId`, `projectRoot`, `createdAt`,
|
|
182
|
-
`updatedAt` are runtime-owned, so manual edits to `qaCommands`
|
|
183
|
-
survive every subsequent `okstra setup` / `okstra run` invocation.
|
|
184
|
-
|
|
185
|
-
### Step 4.6.1 (optional): `qaEnv` — Tier 3 conformance environment
|
|
186
|
-
|
|
187
|
-
`implementation` / `final-verification` verifiers run **stage
|
|
188
|
-
conformance scripts** (Tier 3) that may need to reach a database or an
|
|
189
|
-
HTTP endpoint to prove the diff satisfies upstream requirements. Declare
|
|
190
|
-
the environment those scripts are allowed to touch under `qaEnv`. Every
|
|
191
|
-
field is optional; declare only what your conformance scripts use.
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
"qaEnv": {
|
|
195
|
-
"replicaDbDsn": "<replica/test DB DSN — never shared/staging/prod>",
|
|
196
|
-
"appBaseUrl": "http://localhost:3000",
|
|
197
|
-
"envFile": ".okstra/qa.env",
|
|
198
|
-
"surfacePatterns": { "db": ["*.sql", "*repository*"], "http": ["*controller*"] }
|
|
199
|
-
}
|
|
200
|
-
```
|
|
201
|
-
|
|
202
|
-
- `replicaDbDsn` — DSN the conformance script connects to. MUST be a
|
|
203
|
-
replica / disposable test DB, **never** a shared, staging, or
|
|
204
|
-
production database (conformance scripts may write).
|
|
205
|
-
- `appBaseUrl` — base URL for endpoint-level conformance checks
|
|
206
|
-
(local app only).
|
|
207
|
-
- `envFile` — path (under `.okstra/`) to an env file the verifier
|
|
208
|
-
sources before running conformance scripts.
|
|
209
|
-
- `surfacePatterns` — per-project **override** of the diff-surface
|
|
210
|
-
cross-check map (`capability → glob list`). The validator maps each
|
|
211
|
-
changed file to a capability surface (`db` / `http` / `io`) and fails
|
|
212
|
-
the run when the diff touches a surface no stage `requires`. The
|
|
213
|
-
built-in patterns (e.g. `*router*` for `http`, `*storage*` for `io`)
|
|
214
|
-
are broad and match many front-end files, so front-end-heavy repos
|
|
215
|
-
should override with narrower globs to avoid false BLOCKING verdicts
|
|
216
|
-
(Phase 4b review note). An over-broad pattern over-blocks; an
|
|
217
|
-
over-narrow one lets an undeclared surface through — tune to the
|
|
218
|
-
repo's real db/http/io file naming.
|
|
219
|
-
|
|
220
|
-
## Step 4.7 (automatic): project-local Claude settings symlink
|
|
221
|
-
|
|
222
|
-
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
223
|
-
provisions `<PROJECT_ROOT>/.claude/settings.local.json` as a symlink to
|
|
224
|
-
`~/.okstra/templates/settings.local.json`. The template is installed
|
|
225
|
-
by `okstra install` 0.14.0+ and contains the Bash permission rules
|
|
226
|
-
required for the codex/antigravity worker wrappers:
|
|
227
|
-
|
|
228
|
-
- `Bash($HOME/.okstra/bin/okstra-codex-exec.sh:*)`
|
|
229
|
-
- `Bash($HOME/.okstra/bin/okstra-antigravity-exec.sh:*)`
|
|
230
|
-
|
|
231
|
-
Claude Code automatically loads `.claude/settings.local.json` whenever
|
|
232
|
-
it operates inside that project, so okstra workers dispatched from
|
|
233
|
-
**any** Claude Code session (host or okstra.sh-spawned) are allowed to
|
|
234
|
-
run their wrapper scripts without further configuration.
|
|
235
|
-
|
|
236
|
-
This replaces the previous per-run `--settings` injection model
|
|
237
|
-
(`<run-dir>/okstra-runtime-settings.json`) and the earlier guidance to
|
|
238
|
-
modify the user's global `~/.claude/settings.json`.
|
|
239
|
-
|
|
240
|
-
If a non-symlink `.claude/settings.local.json` already exists, the
|
|
241
|
-
setup step backs it up to `.claude/settings.local.json.bak.<timestamp>`
|
|
242
|
-
before installing the symlink — surface that to the user so they can
|
|
243
|
-
merge any project-specific rules back into a downstream file (the
|
|
244
|
-
symlinked template is okstra-owned and gets refreshed when okstra
|
|
245
|
-
updates).
|
|
246
|
-
|
|
247
|
-
To opt out (advanced): replace the symlink with a regular file. okstra
|
|
248
|
-
will detect that it is no longer a symlink on its next setup call and
|
|
249
|
-
back it up as `.bak.<timestamp>` rather than overwriting silently.
|
|
250
|
-
|
|
251
|
-
## Step 4.8 (optional, opt-in): register a project PR body template
|
|
252
|
-
|
|
253
|
-
`release-handoff` fills the PR body from a template. By default it uses the
|
|
254
|
-
installed template at
|
|
255
|
-
`~/.okstra/templates/prd/pr-body.template.md`. Most projects
|
|
256
|
-
want their own — e.g. the repo's `.github/PULL_REQUEST_TEMPLATE.md` — to
|
|
257
|
-
keep PRs consistent with what the team already merges manually.
|
|
258
|
-
|
|
259
|
-
**Pre-registration during setup is opt-in.** The same prompt is also
|
|
260
|
-
offered on the first `release-handoff` run in this project, so it is
|
|
261
|
-
safe to defer this step (`나중에`) — most users should skip unless they
|
|
262
|
-
explicitly want to lock the template in now.
|
|
263
|
-
|
|
264
|
-
Ask the user with `AskUserQuestion` (fixed options, NOT free text — file
|
|
265
|
-
path entry happens in the follow-up plain text prompt per the
|
|
266
|
-
okstra-run prompt convention):
|
|
267
|
-
|
|
268
|
-
- **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
|
|
269
|
-
- **Options**:
|
|
270
|
-
1. `이번 프로젝트만 (project scope)` — write to `<PROJECT_ROOT>/.okstra/project.json` `prTemplatePath`.
|
|
271
|
-
2. `전역 (global scope)` — write to `~/.okstra/config.json` `prTemplatePath`.
|
|
272
|
-
3. `나중에` — skip.
|
|
273
|
-
|
|
274
|
-
If the user picks scope 1 or 2, follow up with a **plain text prompt**:
|
|
275
|
-
`"PR 본문 템플릿 파일 경로를 알려주세요. project 스코프는 project-root 기준 상대경로 또는 절대경로, global 스코프는 절대경로 또는 ~/ 시작 경로만 허용됩니다."` Consume the next user message, then run:
|
|
276
|
-
|
|
277
|
-
```bash
|
|
278
|
-
okstra config set pr-template-path "<typed-path>" --scope <project|global>
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
The command validates the value (global rejects relative paths) and
|
|
282
|
-
writes atomically. Surface its stdout JSON so the user sees which file
|
|
283
|
-
was updated.
|
|
284
|
-
|
|
285
|
-
If the user chose `나중에`, tell them they can register later with one of:
|
|
286
|
-
|
|
287
|
-
- `okstra config set pr-template-path <path> --scope project|global` from a
|
|
288
|
-
terminal, or
|
|
289
|
-
- the per-run 1회성 override prompt during the next release-handoff run
|
|
290
|
-
(the run can also persist that override to project/global scope on the
|
|
291
|
-
spot — see the okstra-run skill).
|
|
292
|
-
|
|
293
|
-
## Step 4.9 (optional): choose final report language
|
|
294
|
-
|
|
295
|
-
기본은 영어. 이 step 을 skip 하면 `reportLanguage` 필드를 두지 않고
|
|
296
|
-
runtime 이 `auto` 로 처리한다 — 즉 task brief 의 주 서술 언어를 따라
|
|
297
|
-
간다 (한국어 brief → 한국어 report, 영어 brief → 영어 report).
|
|
298
|
-
|
|
299
|
-
사용자가 명시적으로 선택하면 `okstra config set` 으로 `project.json`
|
|
300
|
-
에 기록한다.
|
|
301
|
-
|
|
302
|
-
AskUserQuestion (fixed options):
|
|
303
|
-
- Question: `"Final report 를 어느 언어로 작성할까요?"`
|
|
304
|
-
- Options:
|
|
305
|
-
1. `English (recommended)` → reportLanguage: "en"
|
|
306
|
-
2. `한국어` → reportLanguage: "ko"
|
|
307
|
-
3. `Auto (task brief 언어로 추론, 불분명하면 영어)` → reportLanguage: "auto"
|
|
308
|
-
4. `나중에` → skip (필드 미설정 → runtime 이 auto 로 처리)
|
|
309
|
-
|
|
310
|
-
If the user picks 1/2/3, run:
|
|
100
|
+
## Step 3.5 (optional): project customisation
|
|
311
101
|
|
|
312
|
-
|
|
102
|
+
The built-in defaults work for most projects — skip straight to Step 4
|
|
103
|
+
(`doctor`) unless the user explicitly asks for one of these. When they do,
|
|
104
|
+
**read `references/project-config.md` next to this SKILL.md** and follow the
|
|
105
|
+
matching section:
|
|
313
106
|
|
|
314
|
-
|
|
315
|
-
|
|
107
|
+
- **A. worktree sync dirs** (`worktreeSyncDirs`) — extra directories symlinked
|
|
108
|
+
into task worktrees.
|
|
109
|
+
- **B. project QA commands** (`qaCommands`, recommended) + **B.1 `qaEnv`** —
|
|
110
|
+
check-only lint/format/typecheck/test baseline and the Tier 3 conformance
|
|
111
|
+
environment the `implementation` verifiers use.
|
|
112
|
+
- **C. `.claude/settings.local.json` symlink** — automatic; read only to
|
|
113
|
+
diagnose a symlink/backup warning.
|
|
114
|
+
- **D. PR body template** (release-handoff) — opt-in pre-registration; also
|
|
115
|
+
offered on the first release-handoff run.
|
|
116
|
+
- **E. final report language** (`reportLanguage`) — default is auto (follows
|
|
117
|
+
the brief's language).
|
|
316
118
|
|
|
317
|
-
## Step
|
|
119
|
+
## Step 4: Verify
|
|
318
120
|
|
|
319
121
|
```bash
|
|
320
122
|
okstra doctor --runtime claude-code
|
|
@@ -323,7 +125,7 @@ okstra doctor --runtime claude-code
|
|
|
323
125
|
If all checks return `OK`, the setup is complete. If any check fails, surface
|
|
324
126
|
the output and let the user decide whether to re-run install or skip.
|
|
325
127
|
|
|
326
|
-
## Step
|
|
128
|
+
## Step 5: Hand-off
|
|
327
129
|
|
|
328
130
|
Inform the user with a short summary:
|
|
329
131
|
|
|
@@ -337,7 +139,7 @@ Inform the user with a short summary:
|
|
|
337
139
|
|---|---|---|
|
|
338
140
|
| `command not found: npx` | Node missing | Install node 18+. |
|
|
339
141
|
| `okstra ensure-installed` keeps reinstalling | `~/.okstra/version` write fails (permissions) | Check `~/.okstra` ownership and writability. |
|
|
340
|
-
| `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step
|
|
142
|
+
| `error: --project-id is required (no existing project.json, not a TTY)` | `okstra setup --yes` invoked without `--project-id`, or with empty answer to Step 3 prompt | Re-ask Step 3 and pass a non-empty id via `--project-id`. |
|
|
341
143
|
| `projectId mismatch` / `projectId 불일치` | `project.json` already exists with a different id | Decide which id is canonical; manually delete `<PROJECT_ROOT>/.okstra/project.json` to re-register, or re-run with the existing id. |
|
|
342
144
|
| `EACCES` writing under `.okstra/` | directory owned by another user (e.g. created by a previous root-shell run) | `chown -R "$USER" <PROJECT_ROOT>/.okstra` or delete and let setup recreate. |
|
|
343
145
|
| `warning: failed to provision .claude/settings.local.json symlink` | a non-symlink `.claude/settings.local.json` already exists and the backup-and-replace step failed | Inspect `<PROJECT_ROOT>/.claude/settings.local.json{,.bak.*}`; manually merge project-specific rules, then re-run setup. |
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
# Optional project configuration (okstra-setup)
|
|
2
|
+
|
|
3
|
+
Read this only when the user asks to customise one of the areas below after
|
|
4
|
+
`project.json` exists. The built-in defaults work for most projects — none of
|
|
5
|
+
these sections is required for a working setup.
|
|
6
|
+
|
|
7
|
+
The Bash invocation rule from SKILL.md applies to every command here.
|
|
8
|
+
|
|
9
|
+
## A. Worktree sync dirs (`worktreeSyncDirs`)
|
|
10
|
+
|
|
11
|
+
Each okstra run provisions a task-scoped git worktree under
|
|
12
|
+
`~/.okstra/worktrees/.../`. A small set of project-root-relative directories is
|
|
13
|
+
symlinked from the main checkout into that worktree so every task sees the
|
|
14
|
+
shared state. The built-in default is `.project-docs`, `.scratch`,
|
|
15
|
+
`graphify-out`, `.claude`. Syncing a directory does not make it okstra memory;
|
|
16
|
+
okstra-owned context and writes still stay under `<PROJECT_ROOT>/.okstra/**`
|
|
17
|
+
unless the task brief explicitly authorizes a non-okstra path.
|
|
18
|
+
|
|
19
|
+
To override per-project, add a `worktreeSyncDirs` array to `project.json`.
|
|
20
|
+
Empty array disables the feature; the field is preserved across the runtime's
|
|
21
|
+
auto-upserts (only `projectId`, `projectRoot`, `createdAt`, `updatedAt` are
|
|
22
|
+
runtime-owned).
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"projectId": "...",
|
|
27
|
+
"projectRoot": "...",
|
|
28
|
+
"worktreeSyncDirs": [".project-docs", ".scratch", "graphify-out", ".claude", "my-custom-dir"]
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Resolution precedence: `OKSTRA_WORKTREE_SYNC_DIRS` env var → this field →
|
|
33
|
+
built-in default. Only edit when defaults don't cover the project's working
|
|
34
|
+
files (e.g. additional cache or local-config dirs that must follow the
|
|
35
|
+
executor into the worktree).
|
|
36
|
+
|
|
37
|
+
## B. Project QA commands (`qaCommands`) — recommended
|
|
38
|
+
|
|
39
|
+
`implementation`-phase verifiers run an independent QA gate over the
|
|
40
|
+
executor's diff and need a project-wide baseline of check-only lint / format /
|
|
41
|
+
typecheck / test commands. okstra does NOT auto-detect tooling — declare the
|
|
42
|
+
commands explicitly in `project.json` under `qaCommands`. Skipping this is
|
|
43
|
+
allowed but the verifier will then only run the plan's per-task `validation`
|
|
44
|
+
set, with `qa-command not configured: <category>` recorded per missing
|
|
45
|
+
category in the final report.
|
|
46
|
+
|
|
47
|
+
Each category is an array of `{ "label", "cmd", "language"? }` objects.
|
|
48
|
+
`language` is optional; when present the verifier MAY skip commands whose
|
|
49
|
+
language is not represented in this run's diff.
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"projectId": "...",
|
|
54
|
+
"projectRoot": "...",
|
|
55
|
+
"qaCommands": {
|
|
56
|
+
"lint": [{ "label": "cargo clippy", "cmd": "cargo clippy --all-targets -- -D warnings", "language": "rust" }],
|
|
57
|
+
"format": [{ "label": "cargo fmt", "cmd": "cargo fmt --check", "language": "rust" }],
|
|
58
|
+
"typecheck": [{ "label": "tsc", "cmd": "pnpm exec tsc --noEmit", "language": "ts" }],
|
|
59
|
+
"test": [{ "label": "cargo test", "cmd": "cargo test --workspace --locked", "language": "rust" }]
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
**`cmd` deny-list (mutation guard):** the verifier rejects any `cmd`
|
|
65
|
+
containing tokens that imply mutation — declare commands in their check-only
|
|
66
|
+
form only. Representative denied tokens: `--fix` / `--write` (formatters),
|
|
67
|
+
`--updateSnapshot`, `npm install` (use `npm ci`), `pip install -U`,
|
|
68
|
+
`cargo add`. The authoritative deny list is
|
|
69
|
+
`scripts/okstra_ctl/qa_commands.py::find_denied_tokens`
|
|
70
|
+
(`_DENIED_LITERAL_TOKENS` / `_DENIED_SUBSTRINGS` plus the dynamic
|
|
71
|
+
`npm install` and `INSTA_UPDATE=` checks) — do not re-enumerate it here.
|
|
72
|
+
|
|
73
|
+
Encountering a denied token aborts the verifier with status
|
|
74
|
+
`contract-violated`; re-declare the command in check-only form to recover
|
|
75
|
+
(e.g. swap `prettier --write` → `prettier --check`).
|
|
76
|
+
|
|
77
|
+
The field is preserved across the runtime's auto-upserts of `project.json`, so
|
|
78
|
+
manual edits to `qaCommands` survive every subsequent `okstra setup` /
|
|
79
|
+
`okstra run` invocation.
|
|
80
|
+
|
|
81
|
+
### B.1 `qaEnv` — Tier 3 conformance environment
|
|
82
|
+
|
|
83
|
+
`implementation` / `final-verification` verifiers run **stage conformance
|
|
84
|
+
scripts** (Tier 3) that may need to reach a database or an HTTP endpoint to
|
|
85
|
+
prove the diff satisfies upstream requirements. Declare the environment those
|
|
86
|
+
scripts are allowed to touch under `qaEnv`. Every field is optional; declare
|
|
87
|
+
only what your conformance scripts use.
|
|
88
|
+
|
|
89
|
+
```json
|
|
90
|
+
"qaEnv": {
|
|
91
|
+
"replicaDbDsn": "<replica/test DB DSN — never shared/staging/prod>",
|
|
92
|
+
"appBaseUrl": "http://localhost:3000",
|
|
93
|
+
"envFile": ".okstra/qa.env",
|
|
94
|
+
"surfacePatterns": { "db": ["*.sql", "*repository*"], "http": ["*controller*"] }
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
- `replicaDbDsn` — DSN the conformance script connects to. MUST be a replica /
|
|
99
|
+
disposable test DB, **never** a shared, staging, or production database
|
|
100
|
+
(conformance scripts may write).
|
|
101
|
+
- `appBaseUrl` — base URL for endpoint-level conformance checks (local app
|
|
102
|
+
only).
|
|
103
|
+
- `envFile` — path (under `.okstra/`) to an env file the verifier sources
|
|
104
|
+
before running conformance scripts.
|
|
105
|
+
- `surfacePatterns` — per-project **override** of the diff-surface cross-check
|
|
106
|
+
map (`capability → glob list`). The validator maps each changed file to a
|
|
107
|
+
capability surface (`db` / `http` / `io`) and fails the run when the diff
|
|
108
|
+
touches a surface no stage `requires`. The built-in patterns (e.g. `*router*`
|
|
109
|
+
for `http`, `*storage*` for `io`) are broad and match many front-end files,
|
|
110
|
+
so front-end-heavy repos should override with narrower globs to avoid false
|
|
111
|
+
BLOCKING verdicts. An over-broad pattern over-blocks; an over-narrow one
|
|
112
|
+
lets an undeclared surface through — tune to the repo's real db/http/io file
|
|
113
|
+
naming.
|
|
114
|
+
|
|
115
|
+
## C. Project-local Claude settings symlink (automatic — diagnostics only)
|
|
116
|
+
|
|
117
|
+
`okstra setup` (and `okstra run` on its first invocation per project)
|
|
118
|
+
provisions `<PROJECT_ROOT>/.claude/settings.local.json` as a symlink to
|
|
119
|
+
`~/.okstra/templates/settings.local.json`. The template contains the Bash
|
|
120
|
+
permission rules required for the codex/antigravity worker wrappers:
|
|
121
|
+
|
|
122
|
+
- `Bash($HOME/.okstra/bin/okstra-codex-exec.sh:*)`
|
|
123
|
+
- `Bash($HOME/.okstra/bin/okstra-antigravity-exec.sh:*)`
|
|
124
|
+
|
|
125
|
+
Claude Code automatically loads `.claude/settings.local.json` inside that
|
|
126
|
+
project, so okstra workers dispatched from **any** Claude Code session are
|
|
127
|
+
allowed to run their wrapper scripts without further configuration.
|
|
128
|
+
|
|
129
|
+
If a non-symlink `.claude/settings.local.json` already exists, setup backs it
|
|
130
|
+
up to `.claude/settings.local.json.bak.<timestamp>` before installing the
|
|
131
|
+
symlink — surface that to the user so they can merge project-specific rules
|
|
132
|
+
back (the symlinked template is okstra-owned and refreshed on okstra updates).
|
|
133
|
+
To opt out (advanced): replace the symlink with a regular file; okstra will
|
|
134
|
+
back it up as `.bak.<timestamp>` on its next setup call rather than
|
|
135
|
+
overwriting silently.
|
|
136
|
+
|
|
137
|
+
## D. Project PR body template (release-handoff)
|
|
138
|
+
|
|
139
|
+
`release-handoff` fills the PR body from a template — by default
|
|
140
|
+
`~/.okstra/templates/prd/pr-body.template.md`. Most projects want their own
|
|
141
|
+
(e.g. `.github/PULL_REQUEST_TEMPLATE.md`). Pre-registration during setup is
|
|
142
|
+
opt-in; the same prompt is offered again on the first `release-handoff` run,
|
|
143
|
+
so deferring (`나중에`) is safe.
|
|
144
|
+
|
|
145
|
+
Ask with `AskUserQuestion` (fixed options — file path entry happens in the
|
|
146
|
+
follow-up plain text prompt):
|
|
147
|
+
|
|
148
|
+
- **Question**: `"이 프로젝트에서 release-handoff 가 사용할 PR 본문 템플릿을 등록할까요?"`
|
|
149
|
+
- **Options**:
|
|
150
|
+
1. `이번 프로젝트만 (project scope)` — writes `prTemplatePath` to `<PROJECT_ROOT>/.okstra/project.json`.
|
|
151
|
+
2. `전역 (global scope)` — writes `prTemplatePath` to `~/.okstra/config.json`.
|
|
152
|
+
3. `나중에` — skip.
|
|
153
|
+
|
|
154
|
+
If scope 1 or 2, follow up with a plain text prompt:
|
|
155
|
+
`"PR 본문 템플릿 파일 경로를 알려주세요. project 스코프는 project-root 기준 상대경로 또는 절대경로, global 스코프는 절대경로 또는 ~/ 시작 경로만 허용됩니다."`
|
|
156
|
+
Consume the next user message, then run:
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
okstra config set pr-template-path "<typed-path>" --scope <project|global>
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
The command validates the value (global rejects relative paths) and writes
|
|
163
|
+
atomically. Surface its stdout JSON. If the user chose `나중에`, tell them
|
|
164
|
+
they can register later via the same `okstra config set` command or the
|
|
165
|
+
per-run override prompt during the next release-handoff run.
|
|
166
|
+
|
|
167
|
+
## E. Final report language (`reportLanguage`)
|
|
168
|
+
|
|
169
|
+
기본은 영어. Skip 하면 `reportLanguage` 필드를 두지 않고 runtime 이 `auto` 로
|
|
170
|
+
처리한다 — task brief 의 주 서술 언어를 따라간다 (한국어 brief → 한국어
|
|
171
|
+
report, 영어 brief → 영어 report).
|
|
172
|
+
|
|
173
|
+
AskUserQuestion (fixed options):
|
|
174
|
+
- Question: `"Final report 를 어느 언어로 작성할까요?"`
|
|
175
|
+
- Options:
|
|
176
|
+
1. `English (recommended)` → `en`
|
|
177
|
+
2. `한국어` → `ko`
|
|
178
|
+
3. `Auto (task brief 언어로 추론, 불분명하면 영어)` → `auto`
|
|
179
|
+
4. `나중에` → skip (필드 미설정 → runtime 이 auto 로 처리)
|
|
180
|
+
|
|
181
|
+
If the user picks 1/2/3:
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
okstra config set report-language <en|ko|auto> --scope project
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
전역 기본값은 README 의 "global config" 안내대로 `--scope global` 로 수동
|
|
188
|
+
설정한다 — 이 flow 에서는 project 스코프만 제공한다.
|
|
@@ -81,9 +81,9 @@ _의존 정보 없음_
|
|
|
81
81
|
<!-- OPTIONAL: include only when per-task day estimates are available.
|
|
82
82
|
If included, MUST use this exact heading literal `## Gantt Chart`.
|
|
83
83
|
Render as ASCII inside a fenced ``` block with NO language tag.
|
|
84
|
-
Mermaid / PlantUML / Graphviz are forbidden
|
|
84
|
+
Mermaid / PlantUML / Graphviz are forbidden.
|
|
85
85
|
The axis is RELATIVE DAY-COUNTS (Day 1 / J1, …) — never calendar dates.
|
|
86
|
-
If day estimates are missing, omit this whole section and replace it with one blockquote: `> _Gantt Chart 생략: <reason>_
|
|
86
|
+
If day estimates are missing, omit this whole section and replace it with one blockquote: `> _Gantt Chart 생략: <reason>_`. -->
|
|
87
87
|
|
|
88
88
|
```
|
|
89
89
|
Day: 1 5 10 15 20 25 30
|
|
@@ -20,7 +20,7 @@ Work like a senior engineer who owns this result, not a commentator on it.
|
|
|
20
20
|
|
|
21
21
|
You are required to read every primary input file enumerated by the dispatcher (the lead's prompt lists them under `[Required reading]`) from the very first character to the very last character before you produce any analysis output. Skimming, partial reads, jumping to a single section, or relying on prior knowledge of a similar file's structure is not acceptable. Source files listed as fallback/evidence paths are read on demand when you need to verify a citation, resolve ambiguity, or inspect material the packet says it omitted.
|
|
22
22
|
|
|
23
|
-
### Audience-scoped enumeration (
|
|
23
|
+
### Audience-scoped enumeration (performance optimization — mandatory)
|
|
24
24
|
|
|
25
25
|
Different recipients need different files. Do NOT include `final-report-template.md` in analysis worker prompts: analysis workers produce findings (not the final report), and forcing them to read the template inflates token usage without changing finding quality.
|
|
26
26
|
|
|
@@ -1715,7 +1715,7 @@ _SINGLE_VOTE_BLOCKING_KINDS = {"a", "d"}
|
|
|
1715
1715
|
|
|
1716
1716
|
def _classify_plan_item_gate(item: dict) -> str:
|
|
1717
1717
|
"""Recompute one plan item's gate class from its per-worker verdicts,
|
|
1718
|
-
per `prompts/lead/
|
|
1718
|
+
per `prompts/lead/plan-body-verification.md` "Round protocol". Returns one of
|
|
1719
1719
|
``majority-disagree`` / ``has-dissent`` / ``full-consensus`` /
|
|
1720
1720
|
``all-non-result``. Collapses ``partial-consensus`` and
|
|
1721
1721
|
``dissent-isolated`` into ``has-dissent`` because they resolve to the
|
|
@@ -1797,12 +1797,12 @@ def _validate_plan_body_gate_recompute(data: dict, failures: list[str]) -> None:
|
|
|
1797
1797
|
f"only support `{recomputed}` (a majority DISAGREE, a DISAGREE(f) on "
|
|
1798
1798
|
"a P-Req item, or all-non-result dispatches were recorded). The gate "
|
|
1799
1799
|
"value must honestly aggregate the worker votes — do not upgrade it "
|
|
1800
|
-
"to unblock the run (
|
|
1800
|
+
"to unblock the run (plan-body-verification.md Round protocol)."
|
|
1801
1801
|
)
|
|
1802
1802
|
|
|
1803
1803
|
|
|
1804
1804
|
# Each plan-body deliverable array and the P-* verdict-item prefix it must be
|
|
1805
|
-
# extracted into for §5.5.9 cross-verification (
|
|
1805
|
+
# extracted into for §5.5.9 cross-verification (plan-body-verification.md Plan-item
|
|
1806
1806
|
# extraction). A weak item dropped from planItems escapes the gate entirely.
|
|
1807
1807
|
_PLAN_ITEM_SOURCES = (
|
|
1808
1808
|
("optionCandidates", "P-Opt", "Option Candidates (§4.5.1)"),
|
|
@@ -1848,7 +1848,7 @@ def _validate_plan_item_extraction_completeness(data: dict, failures: list[str])
|
|
|
1848
1848
|
f"planBodyVerification.planItems carries only {extracted} "
|
|
1849
1849
|
f"`{prefix}-*` verdict item(s). Every plan-body item MUST be "
|
|
1850
1850
|
"extracted for cross-verification — a dropped item escapes the "
|
|
1851
|
-
"gate (
|
|
1851
|
+
"gate (plan-body-verification.md Plan-item extraction)."
|
|
1852
1852
|
)
|
|
1853
1853
|
|
|
1854
1854
|
|
|
@@ -1885,7 +1885,7 @@ def _validate_plan_item_subject_substance(data: dict, failures: list[str]) -> No
|
|
|
1885
1885
|
f"placeholder subject `{subject}`. Give a plain-language label of "
|
|
1886
1886
|
"what the item is (e.g. 'Option A: upload v2 를 신규 모듈로 분리') so "
|
|
1887
1887
|
"the §5.5.9 reader knows what each verdict is about "
|
|
1888
|
-
"(
|
|
1888
|
+
"(plan-body-verification.md Plan-item extraction)."
|
|
1889
1889
|
)
|
|
1890
1890
|
|
|
1891
1891
|
|
|
@@ -1894,7 +1894,7 @@ def _validate_plan_body_clarification_matching(data: dict, failures: list[str])
|
|
|
1894
1894
|
must point (via `clarificationId`) at an existing `blocks: approval`
|
|
1895
1895
|
clarification row. Closes the hole where a majority-disagree item blocks the
|
|
1896
1896
|
gate but no §1 Clarification row is raised, so the user never sees why
|
|
1897
|
-
approval is withheld (
|
|
1897
|
+
approval is withheld (implementation-planning.md self-review step 7).
|
|
1898
1898
|
"""
|
|
1899
1899
|
ip = data.get("implementationPlanning")
|
|
1900
1900
|
if not isinstance(ip, dict):
|
|
@@ -1920,7 +1920,7 @@ def _validate_plan_body_clarification_matching(data: dict, failures: list[str])
|
|
|
1920
1920
|
f"final-report data.json: plan item `{item_id}` is majority-disagree "
|
|
1921
1921
|
"but carries no `clarificationId`. A blocking disagreement MUST "
|
|
1922
1922
|
"surface as a `## 1. Clarification Items` row (blocks=approval) so "
|
|
1923
|
-
"the user sees the blocker (
|
|
1923
|
+
"the user sees the blocker (implementation-planning.md self-review step 7)."
|
|
1924
1924
|
)
|
|
1925
1925
|
elif cid not in approval_ids:
|
|
1926
1926
|
reason = (
|
package/src/cli-registry.mjs
CHANGED
|
@@ -45,6 +45,13 @@ export const COMMAND_REGISTRY = [
|
|
|
45
45
|
category: "admin",
|
|
46
46
|
summary: ["Verify the current project has been registered with setup"],
|
|
47
47
|
},
|
|
48
|
+
{
|
|
49
|
+
name: "preflight",
|
|
50
|
+
module: "./commands/lifecycle/preflight.mjs",
|
|
51
|
+
export: "run",
|
|
52
|
+
category: "admin",
|
|
53
|
+
summary: ["One-call skill preflight: ensure-installed + check-project (JSON)"],
|
|
54
|
+
},
|
|
48
55
|
{
|
|
49
56
|
name: "config",
|
|
50
57
|
module: "./commands/lifecycle/config.mjs",
|
|
@@ -117,6 +124,13 @@ export const COMMAND_REGISTRY = [
|
|
|
117
124
|
category: "introspection",
|
|
118
125
|
summary: ["Resolve a bare task-id to candidate task-keys from the catalog"],
|
|
119
126
|
},
|
|
127
|
+
{
|
|
128
|
+
name: "set-work-status",
|
|
129
|
+
module: "./commands/inspect/set-work-status.mjs",
|
|
130
|
+
export: "run",
|
|
131
|
+
category: "introspection",
|
|
132
|
+
summary: ["Set a task's user-managed workStatus in task-manifest.json"],
|
|
133
|
+
},
|
|
120
134
|
{
|
|
121
135
|
name: "time-report",
|
|
122
136
|
module: "./commands/inspect/time-report.mjs",
|
|
@@ -16,6 +16,7 @@ Subcommands:
|
|
|
16
16
|
step submit an answer (or fetch the current prompt) and emit next
|
|
17
17
|
render-args emit the final --flag/value map for 'okstra render-bundle'
|
|
18
18
|
confirmation emit the multi-line confirmation echo block
|
|
19
|
+
outcome emit render args, persistence actions, and confirmation text
|
|
19
20
|
|
|
20
21
|
Usage:
|
|
21
22
|
okstra wizard new-state-file
|
|
@@ -23,6 +24,7 @@ Usage:
|
|
|
23
24
|
okstra wizard step --state-file <path> [--answer <value>]
|
|
24
25
|
okstra wizard render-args --state-file <path>
|
|
25
26
|
okstra wizard confirmation --state-file <path>
|
|
27
|
+
okstra wizard outcome --state-file <path>
|
|
26
28
|
|
|
27
29
|
'init' auto-fills --workspace-root from 'okstra paths --field workspace',
|
|
28
30
|
so callers do not pass it.
|
|
@@ -65,7 +67,7 @@ export async function run(args) {
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
const [sub, ...rest] = args;
|
|
68
|
-
if (!["new-state-file", "init", "step", "render-args", "confirmation"].includes(sub)) {
|
|
70
|
+
if (!["new-state-file", "init", "step", "render-args", "confirmation", "outcome"].includes(sub)) {
|
|
69
71
|
process.stderr.write(`error: unknown wizard subcommand '${sub}'\n\n${USAGE}`);
|
|
70
72
|
return 2;
|
|
71
73
|
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { runPythonModule } from "../../lib/python-helper.mjs";
|
|
2
|
+
|
|
3
|
+
const USAGE = `okstra set-work-status — set a task's user-managed workStatus
|
|
4
|
+
|
|
5
|
+
Usage:
|
|
6
|
+
okstra set-work-status <token> <status> [--note <text>] [--task-group <g>]
|
|
7
|
+
[--project-root <dir>] [--cwd <dir>] [--json]
|
|
8
|
+
|
|
9
|
+
<token> is a full task-key (<project-id>:<task-group>:<task-id>) or a bare
|
|
10
|
+
task-id. <status> is one of: todo | in-progress | blocked | done.
|
|
11
|
+
|
|
12
|
+
Updates workStatus + workStatusUpdatedAt in task-manifest.json (workStatusNote
|
|
13
|
+
only when --note is passed) using the same JSON serialization as the manifest
|
|
14
|
+
renderer. Output: JSON { ok, taskKey, previousWorkStatus, workStatus, ... };
|
|
15
|
+
ok:false stages: resolve | catalog | not-found | ambiguous (pick from
|
|
16
|
+
matches[]) | manifest-missing | manifest-invalid.
|
|
17
|
+
`;
|
|
18
|
+
|
|
19
|
+
export async function run(args) {
|
|
20
|
+
if (args.includes("--help") || args.includes("-h")) {
|
|
21
|
+
process.stdout.write(USAGE);
|
|
22
|
+
return 0;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const result = await runPythonModule({
|
|
26
|
+
module: "okstra_ctl.set_work_status",
|
|
27
|
+
args,
|
|
28
|
+
stdio: "inherit-stdout",
|
|
29
|
+
});
|
|
30
|
+
return result.code;
|
|
31
|
+
}
|