okstra 0.30.0 → 0.30.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.30.0",
3
+ "version": "0.30.2",
4
4
  "description": "Multi-agent cross-verification orchestrator runtime + Claude Code skills.",
5
5
  "license": "MIT",
6
6
  "author": "devonshin",
@@ -1,5 +1,5 @@
1
1
  {
2
- "package": "0.30.0",
3
- "builtAt": "2026-05-17T08:21:41.472Z",
2
+ "package": "0.30.2",
3
+ "builtAt": "2026-05-19T06:29:56.728Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -77,3 +77,12 @@ profile document.
77
77
  - Audit sidecar (shared — applies to every analysis-worker output and every final-report):
78
78
  - Reading Confirmation lines (one short line per input file confirming end-to-end reading) live in the **worker audit sidecar** at `runs/<task-type>/worker-results/<worker>-audit-<task-type>-<seq>.md`, NOT in the worker's main worker-results file. The worker-results body starts at section 1 (Findings). The validator fails worker-results files that contain a `## 0. Reading Confirmation` heading.
79
79
  - The audit sidecar carries any other meta the worker wants to log (tool-call counts, MCP query summaries, timing notes). The lead's final-report does NOT duplicate this content — it is consumed by the validator and by post-run audit tooling, not by end-user readers.
80
+
81
+ - Markdown authoring (shared — applies to every markdown document produced by the lead or any worker, including final-reports, worker-results, briefs, and ad-hoc notes):
82
+ - every document must begin with an `Index` section.
83
+ - include only information necessary to fulfill the user's stated purpose and directly related requirements.
84
+ - follow only the sections, format, tone, and scope specified by the user, plus the required `Index` section.
85
+ - when writing task instructions or work orders, define the scope of work clearly and specifically, including deliverables, acceptance criteria, and verification steps when relevant.
86
+ - define scope positively by stating what work is included. Work outside the defined scope must not be performed.
87
+ - before adding any structure or content not explicitly specified, ask the user for confirmation, except for the required `Index` section.
88
+ - before completion, verify that the document exactly matches the requested scope and contains no unrelated material.
@@ -43,6 +43,7 @@ from typing import Optional
43
43
 
44
44
  from .ids import _safe_fs_segment
45
45
  from . import worktree_registry
46
+ from .seeding import SettingsLinkError, ensure_project_settings_symlink
46
47
 
47
48
 
48
49
  # Project-root directories that hold okstra task state, ignored by git, or
@@ -361,6 +362,34 @@ def _link_sync_files(source_root: Path, worktree_path: Path) -> list[str]:
361
362
  return notes
362
363
 
363
364
 
365
+ def _seed_worktree_settings_symlink(worktree_path: Path) -> None:
366
+ """Seed `.claude/settings.local.json` in the worker worktree so dispatched
367
+ Claude / codex / gemini sessions inherit the okstra read-only / write
368
+ allowlist. Mirrors the main-project seeding done in `run.py` — needed
369
+ because `_link_sync_dirs` skips `.claude/` whenever `git worktree add`
370
+ already materialised the directory (e.g. tracked `.claude/handoff-*.md`).
371
+ Failures degrade to stderr warning so worktree provisioning still
372
+ succeeds.
373
+ """
374
+ try:
375
+ link = ensure_project_settings_symlink(project_root=worktree_path)
376
+ except SettingsLinkError as exc:
377
+ print(
378
+ f"okstra-settings: failed to seed worker worktree symlink at "
379
+ f"{worktree_path / '.claude/settings.local.json'} — worker dispatch "
380
+ f"may be blocked by Claude Code permissions. ({exc})",
381
+ file=__import__("sys").stderr,
382
+ )
383
+ return
384
+ if link is None:
385
+ print(
386
+ "okstra-settings: ~/.okstra/templates/settings.local.json missing — "
387
+ "re-run 'npx okstra@latest install' (0.14.0+) to provision the "
388
+ "symlink target.",
389
+ file=__import__("sys").stderr,
390
+ )
391
+
392
+
364
393
  def _copy_snapshot_files(source_root: Path, worktree_path: Path) -> list[str]:
365
394
  """Copy fixture files from MAIN → task worktree as read-only snapshots
366
395
  (FU-V3).
@@ -485,6 +514,7 @@ def provision_task_worktree(
485
514
  existing = worktree_registry.lookup(safe_project, safe_group, safe_task)
486
515
  if existing is not None and existing.status == "active":
487
516
  worktree_registry.touch_phase(safe_project, safe_group, safe_task, task_type)
517
+ _seed_worktree_settings_symlink(Path(existing.worktree_path))
488
518
  return WorktreeProvision(
489
519
  status="reused",
490
520
  path=existing.worktree_path,
@@ -586,6 +616,8 @@ def provision_task_worktree(
586
616
  _git(main_root, "branch", "-D", branch)
587
617
  raise
588
618
 
619
+ _seed_worktree_settings_symlink(worktree_path)
620
+
589
621
  base_label = (
590
622
  f"{base_origin} @ {resolved_base_ref[:12]}"
591
623
  if base_origin != "HEAD"
@@ -107,26 +107,24 @@ at external `docs/adr/`.
107
107
 
108
108
  ## Step 0: Resolve project root
109
109
 
110
- Reuse the same disk-only resolution rule as `okstra-run`:
110
+ Reuse the same literal-token-first rule as `okstra-run`. Each command below is a **separate Bash tool call** starting with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call.
111
111
 
112
112
  ```bash
113
- if command -v okstra >/dev/null 2>&1; then
114
- OKSTRA_CMD="okstra"
115
- else
116
- # `~/.okstra/bin/okstra.sh` exists on every installed machine but it is
117
- # the bash run-wrapper, NOT the Node CLI — it does not implement
118
- # `check-project`. Do not branch into it as a fallback. Go straight to
119
- # `npx` so the user always reaches a working `check-project` even when
120
- # the Node CLI is off-$PATH.
121
- OKSTRA_CMD="npx -y okstra@latest"
122
- fi
123
- $OKSTRA_CMD check-project --cwd "$(pwd)"
113
+ okstra check-project --json
124
114
  ```
125
115
 
126
- - `ok: true` use `projectRoot`.
127
- - `ok: false` → `AskUserQuestion` (free text) for an absolute project root, then
128
- re-run `okstra check-project --cwd <input>`. If still not ok, tell the user
129
- to run `okstra-setup` and stop.
116
+ Parse the JSON from stdout:
117
+
118
+ - `ok: true` use `projectRoot` from the JSON output. Carry it as a literal string into the steps below.
119
+ - `ok: false` → ask the user with a **plain text prompt** for an absolute project-root path, then rerun as a separate tool call with the literal absolute path (no `$(pwd)`, no shell variables):
120
+
121
+ ```bash
122
+ okstra check-project --cwd /abs/path/from/user --json
123
+ ```
124
+
125
+ If still `ok: false`, tell the user to run `okstra-setup` and stop.
126
+
127
+ > If the `okstra` binary is not on `PATH` at all, the command above will not run. In that case tell the user verbatim: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Do **not** try to invoke `npx -y okstra@latest ...` from this skill — `npx` is not on the literal-token allow-list this skill targets and will force a confirmation prompt on every subsequent call.
130
128
 
131
129
  ## Step 1: Choose input source
132
130
 
@@ -26,27 +26,18 @@ If the user is ambiguous, ask. Defaulting to the wrong one either wastes a fresh
26
26
 
27
27
  ## Step 0: Verify okstra runtime + project setup
28
28
 
29
- ```bash
30
- if command -v okstra >/dev/null 2>&1; then
31
- OKSTRA_CMD="okstra"
32
- else
33
- OKSTRA_CMD="npx -y okstra@latest"
34
- fi
35
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
36
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
37
- exit 1
38
- }
39
- eval "$($OKSTRA_CMD paths --shell)"
40
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
41
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
42
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
43
- echo "$OKSTRA_PROJECT_INFO" >&2
44
- exit 1
45
- }
46
- ```
29
+ Run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
30
+
31
+ 1. `okstra ensure-installed`
32
+ If this exits non-zero, tell the user: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
33
+
34
+ 2. `okstra check-project --json`
35
+ Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
36
+
37
+ - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
38
+ - `ok: true` → carry `projectRoot` as a literal string and use it to locate `.project-docs/okstra/discovery/task-catalog.json`.
47
39
 
48
- `$OKSTRA_PROJECT_INFO` is JSON `{ok, projectRoot, projectJsonPath, projectId}`
49
- use `projectRoot` to locate `.project-docs/okstra/discovery/task-catalog.json`.
40
+ Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
50
41
 
51
42
  ## Step 1: Read the Task Catalog
52
43
 
@@ -36,57 +36,45 @@ multi-MB logs; analysis-phase dispatches are typically smaller.
36
36
 
37
37
  ## Step 0: Verify okstra runtime + project setup
38
38
 
39
- Before any other step, ensure both the okstra runtime and the current
40
- project's okstra metadata are in place:
39
+ Before any other step, run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
41
40
 
42
- ```bash
43
- if command -v okstra >/dev/null 2>&1; then
44
- OKSTRA_CMD="okstra"
45
- else
46
- OKSTRA_CMD="npx -y okstra@latest"
47
- fi
48
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
49
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
50
- exit 1
51
- }
52
- eval "$($OKSTRA_CMD paths --shell)"
53
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
54
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
55
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
56
- echo "$OKSTRA_PROJECT_INFO" >&2
57
- exit 1
58
- }
59
- ```
41
+ 1. `okstra ensure-installed`
42
+ If this exits non-zero, tell the user: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
43
+
44
+ 2. `okstra check-project --json`
45
+ Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
46
+
47
+ - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
48
+ - `ok: true` carry `projectRoot` as a literal string and use it as the search root for the steps below.
60
49
 
61
- Parse `projectRoot` from the JSON and use it as the search root for the
62
- steps below.
50
+ Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
63
51
 
64
52
  ## Step 1: Inventory
65
53
 
66
54
  Find all wrapper log files and collect metadata. Use a single `find` to
67
55
  keep the I/O cost predictable, then format the results.
68
56
 
69
- ```bash
70
- PROJECT_ROOT=$(echo "$OKSTRA_PROJECT_INFO" | python3 -c 'import sys,json;print(json.load(sys.stdin)["projectRoot"])')
71
- LOGS_ROOT="$PROJECT_ROOT/.project-docs/okstra/tasks"
57
+ Construct the logs root by appending `/.project-docs/okstra/tasks` to the literal `projectRoot` value parsed in Step 0, and paste it as a literal absolute path in place of `<LOGS_ROOT>` below (no shell variables, no `$(...)`):
72
58
 
73
- # columns: size_bytes | mtime_epoch | path
74
- find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' \
59
+ ```bash
60
+ find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' \
75
61
  -printf '%s\t%T@\t%p\n' 2>/dev/null \
76
62
  | sort -k1,1nr
77
63
  ```
78
64
 
79
- On macOS, `find -printf` is unavailable. Fall back to `stat`:
65
+ The columns produced are `size_bytes | mtime_epoch | path`.
66
+
67
+ On macOS, `find -printf` is unavailable. Fall back to `stat` — again substitute the literal `<LOGS_ROOT>`:
80
68
 
81
69
  ```bash
82
- find "$LOGS_ROOT" -type f -path '*/runs/*/prompts/*.log' 2>/dev/null \
70
+ find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' 2>/dev/null \
83
71
  | while IFS= read -r p; do
84
72
  stat -f '%z%t%m%t%N' "$p"
85
73
  done \
86
74
  | sort -k1,1nr
87
75
  ```
88
76
 
89
- If the result is empty, report `No wrapper log files found under <PROJECT_ROOT>` and exit.
77
+ If the result is empty, report `No wrapper log files found under <projectRoot>` and exit.
90
78
 
91
79
  ## Step 2: Summary table
92
80
 
@@ -14,27 +14,18 @@ user-invocable: false
14
14
 
15
15
  ## Step 0: Verify okstra runtime + project setup
16
16
 
17
- ```bash
18
- if command -v okstra >/dev/null 2>&1; then
19
- OKSTRA_CMD="okstra"
20
- else
21
- OKSTRA_CMD="npx -y okstra@latest"
22
- fi
23
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
24
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
25
- exit 1
26
- }
27
- eval "$($OKSTRA_CMD paths --shell)"
28
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
29
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
30
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
31
- echo "$OKSTRA_PROJECT_INFO" >&2
32
- exit 1
33
- }
34
- ```
17
+ 각 명령은 **별도 Bash tool call** 로 실행한다. 모든 명령은 리터럴 `okstra` 토큰으로 시작해 `Bash(okstra:*)` 권한 매칭을 통과해야 한다. `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, `&&` 로 감싸지 말고, `$OKSTRA_CMD` 변수나 `npx -y okstra@latest` 폴백도 도입하지 않는다 — 첫 토큰이 비리터럴이면 권한 매칭이 깨져 호출마다 확인 프롬프트가 뜬다. 각 명령의 출력은 LLM(너) 이 보고 자연어로 분기 판단을 한다 — shell 분기는 쓰지 않는다.
18
+
19
+ 1. `okstra ensure-installed`
20
+ 비정상 종료 → 사용자에게 그대로 안내: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." 그리고 중단. 폴백으로 `npx -y okstra@latest ...` 를 호출하지 않는다.
21
+
22
+ 2. `okstra check-project --json`
23
+ 현재 작업 디렉터리 기준 프로젝트를 읽는다. stdout 의 JSON shape: `{ok, projectRoot, projectJsonPath, projectId}`.
24
+
25
+ - `ok: false` → 사용자에게 "this project has no okstra setup. Run `/okstra-setup` first." 안내 후 중단.
26
+ - `ok: true` → `projectRoot` 를 리터럴 문자열로 들고 catalog/manifest 위치를 잡는다.
35
27
 
36
- `$OKSTRA_PROJECT_INFO` (JSON `{ok, projectRoot, projectJsonPath, projectId}`)
37
- `projectRoot` 로 catalog/manifest 위치를 잡는다.
28
+ 후속 `okstra <subcmd>` 호출은 Python path 를 자체 부트스트랩하므로 본 스킬은 `okstra paths --shell` / `export PYTHONPATH=...` 를 필요로 하지 않는다.
38
29
 
39
30
  ## Step 1: Task Key로 Report 경로 찾기
40
31
 
@@ -37,30 +37,18 @@ If `--title` is omitted, derive a default title from `task-group` (e.g. `uploadF
37
37
 
38
38
  ## Preflight: Verify okstra runtime + project setup
39
39
 
40
- Run before anything else in this skill:
40
+ Before anything else in this skill, run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
41
41
 
42
- ```bash
43
- if command -v okstra >/dev/null 2>&1; then
44
- OKSTRA_CMD="okstra"
45
- else
46
- OKSTRA_CMD="npx -y okstra@latest"
47
- fi
48
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
49
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
50
- exit 1
51
- }
52
- eval "$($OKSTRA_CMD paths --shell)"
53
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
54
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
55
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
56
- echo "$OKSTRA_PROJECT_INFO" >&2
57
- exit 1
58
- }
59
- ```
42
+ 1. `okstra ensure-installed`
43
+ If this exits non-zero, tell the user: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
44
+
45
+ 2. `okstra check-project --json`
46
+ Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
47
+
48
+ - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
49
+ - `ok: true` carry `projectRoot` as a literal string and use it to locate `.project-docs/okstra/discovery/task-catalog.json` and the task-group directory.
60
50
 
61
- `$OKSTRA_PROJECT_INFO` is JSON `{ok, projectRoot, projectJsonPath, projectId}`
62
- use `projectRoot` to locate `.project-docs/okstra/discovery/task-catalog.json`
63
- and the task-group directory.
51
+ Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
64
52
 
65
53
  ## Process Procedure
66
54
 
@@ -53,44 +53,32 @@ 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
- ## Step 2: Load runtime paths
56
+ ## Step 2: Load runtime paths (no-op)
57
57
 
58
- ```bash
59
- # Prefer PATH-resolved okstra (npm-installed) over npx — avoids per-call registry lookup.
60
- if command -v okstra >/dev/null 2>&1; then
61
- OKSTRA_CMD="okstra"
62
- else
63
- OKSTRA_CMD="npx -y okstra@latest"
64
- fi
65
- eval "$($OKSTRA_CMD paths --shell)"
66
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
67
- ```
58
+ After `okstra install`, every `okstra <subcmd>` call self-bootstraps its Python path, so this skill does NOT run `okstra paths --shell` / `eval ...` / `export PYTHONPATH=...`. The previously-set `$OKSTRA_WORKSPACE`, `$OKSTRA_AGENTS_DIR`, `$OKSTRA_PYTHONPATH`, `$OKSTRA_BIN`, `$OKSTRA_HOME` env vars are NOT set by this skill. If a later step needs one of those paths, call `okstra paths --json` as a **separate Bash tool call** (literal-token-first) and read the value from JSON output.
68
59
 
69
- After this, `$OKSTRA_WORKSPACE`, `$OKSTRA_AGENTS_DIR`, `$OKSTRA_PYTHONPATH`,
70
- `$OKSTRA_BIN`, `$OKSTRA_HOME` are all set. Do not hardcode any of these — read
71
- them from the env vars.
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.
72
61
 
73
62
  ## Step 3: Resolve PROJECT_ROOT
74
63
 
75
64
  ```bash
76
- PROJECT_ROOT=$(okstra check-project --cwd "$(pwd)" | jq -r '.projectRoot')
65
+ okstra check-project --json
77
66
  ```
78
67
 
79
- The JSON includes `projectRoot` on success. Bind it into the shell so
80
- Step 4 onwards can expand `$PROJECT_ROOT`. On failure (`ok: false`,
81
- `stage: "resolve"`) ask the user (`AskUserQuestion`, free text) for an
82
- absolute project root and rerun with `--cwd <their answer>`.
68
+ Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId, stage?}`.
69
+
70
+ - `ok: true` carry `projectRoot` as a literal absolute string and paste it into every subsequent command in this skill.
71
+ - `ok: false`, `stage: "resolve"` → ask the user (`AskUserQuestion`, free text) for an absolute project root and rerun as a separate Bash tool call with the literal absolute path:
72
+
73
+ ```bash
74
+ okstra check-project --cwd /abs/path/from/user --json
75
+ ```
83
76
 
84
77
  ## Step 4: Inspect or create `project.json`
85
78
 
86
- ```bash
87
- PROJECT_JSON="$PROJECT_ROOT/.project-docs/okstra/project.json"
88
- if [ -f "$PROJECT_JSON" ]; then
89
- cat "$PROJECT_JSON"
90
- fi
91
- ```
79
+ Use the `Read` tool on the literal absolute path `<projectRoot>/.project-docs/okstra/project.json` (substitute the literal `projectRoot` value parsed in Step 3). 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.
92
80
 
93
- If the file exists, print its `projectId`/`projectRoot` and ask whether to
81
+ If the file exists, surface its `projectId`/`projectRoot` and ask whether to
94
82
  keep or overwrite. Default is to keep — okstra refuses to change `projectId`
95
83
  on an existing project (see `okstra_project.resolver.upsert_project_json`),
96
84
  so overwriting requires manually deleting the file first.
@@ -104,10 +92,10 @@ If the file does NOT exist, ask via `AskUserQuestion`:
104
92
  `error: --project-id is required (no existing project.json, not a TTY)`,
105
93
  so passing the user's empty answer through is a silent failure path.
106
94
 
107
- Then create the file:
95
+ Then create the file — paste the literal `projectRoot` from Step 3 and the literal `projectId` from the user's answer (no shell variables):
108
96
 
109
97
  ```bash
110
- okstra setup --yes --project-root "$PROJECT_ROOT" --project-id "$PROJECT_ID"
98
+ okstra setup --yes --project-root /abs/path/to/projectRoot --project-id my-project-id
111
99
  ```
112
100
 
113
101
  > After this, **Steps 4.5–4.8 are all optional**. The built-in defaults
@@ -266,7 +254,7 @@ If the user chose `나중에`, tell them they can register later with one of:
266
254
  ## Step 5: Verify
267
255
 
268
256
  ```bash
269
- $OKSTRA_CMD doctor
257
+ okstra doctor
270
258
  ```
271
259
 
272
260
  If all checks return `OK`, the setup is complete. If any check fails, surface
@@ -13,30 +13,18 @@ description: Use when the user asks for overall okstra task status, current life
13
13
 
14
14
  ## Step 0: Verify okstra runtime + project setup
15
15
 
16
- Before any other step, ensure both the okstra runtime and the current
17
- project's okstra metadata are in place:
18
-
19
- ```bash
20
- if command -v okstra >/dev/null 2>&1; then
21
- OKSTRA_CMD="okstra"
22
- else
23
- OKSTRA_CMD="npx -y okstra@latest"
24
- fi
25
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
26
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
27
- exit 1
28
- }
29
- eval "$($OKSTRA_CMD paths --shell)"
30
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
31
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
32
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
33
- echo "$OKSTRA_PROJECT_INFO" >&2
34
- exit 1
35
- }
36
- ```
16
+ Before any other step, run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
17
+
18
+ 1. `okstra ensure-installed`
19
+ If this exits non-zero, tell the user: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
20
+
21
+ 2. `okstra check-project --json`
22
+ Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
23
+
24
+ - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
25
+ - `ok: true` carry `projectRoot` as a literal string into the steps below; reuse it instead of re-resolving.
37
26
 
38
- `$OKSTRA_PROJECT_INFO` is JSON `{ok, projectRoot, projectJsonPath, projectId}`
39
- parse and reuse it instead of re-resolving in the steps below.
27
+ Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
40
28
 
41
29
  ## Step 1: Overall Project Status
42
30
 
@@ -255,4 +243,4 @@ This skill updates `task-manifest.json` only. `discovery/task-catalog.json` may
255
243
 
256
244
  ## Out-of-Scope Backlog
257
245
 
258
- - **Step 0 boilerplate duplication.** The `ensure-installed` + `paths --shell` + `check-project --json` preamble is byte-identical across every user-facing okstra skill. The Claude Code skill framework has no include/snippet mechanism today, so each skill duplicates the block. A future change should either (a) extract the preamble into a single `okstra preflight` subcommand the skill can call in one line, or (b) ship the block as a shared SKILL fragment if the framework gains include support. Not actionable inside this skill alone.
246
+ - **Step 0 boilerplate duplication.** The `ensure-installed` + `check-project --json` preamble (literal-token-first, separate Bash calls) is near-identical across every user-facing okstra skill. The Claude Code skill framework has no include/snippet mechanism today, so each skill duplicates the prose. A future change should either (a) extract the preamble into a single `okstra preflight` subcommand the skill can call in one line, or (b) ship the block as a shared SKILL fragment if the framework gains include support. Not actionable inside this skill alone.
@@ -29,27 +29,18 @@ If a run never reached Phase 7, its `team-state` will not have `durationMs` fill
29
29
 
30
30
  ## Step 0: Verify okstra runtime + project setup
31
31
 
32
- ```bash
33
- if command -v okstra >/dev/null 2>&1; then
34
- OKSTRA_CMD="okstra"
35
- else
36
- OKSTRA_CMD="npx -y okstra@latest"
37
- fi
38
- $OKSTRA_CMD ensure-installed >/dev/null 2>&1 || {
39
- echo "FAIL: okstra not installed; tell the user to run: npx okstra@latest install" >&2
40
- exit 1
41
- }
42
- eval "$($OKSTRA_CMD paths --shell)"
43
- export PYTHONPATH="$OKSTRA_PYTHONPATH"
44
- OKSTRA_PROJECT_INFO="$($OKSTRA_CMD check-project --json)" || {
45
- echo "FAIL: this project has no okstra setup. Tell the user to run /okstra-setup first." >&2
46
- echo "$OKSTRA_PROJECT_INFO" >&2
47
- exit 1
48
- }
49
- ```
32
+ Run each of the following commands as a **separate Bash tool call**. Each command starts with the literal token `okstra` so the `Bash(okstra:*)` permission match succeeds. Do **not** wrap any of them in `if`, `eval`, `export`, `$(...)`, `VAR=...`, `||`, or `&&`, and do **not** introduce a `$OKSTRA_CMD` variable or an `npx -y okstra@latest` fallback — those leading tokens defeat the permission match and force a confirmation prompt on every call. The LLM (you) inspects each command's output and decides what to do next in natural language — never in shell.
33
+
34
+ 1. `okstra ensure-installed`
35
+ If this exits non-zero, tell the user: "okstra not installed — run `npx okstra@latest install` once, then retry this skill." Then stop. Do **not** try to invoke `npx -y okstra@latest ...` as a fallback.
36
+
37
+ 2. `okstra check-project --json`
38
+ Reads the project from the current working directory. Parse the JSON from stdout. The shape is `{ok, projectRoot, projectJsonPath, projectId}`.
39
+
40
+ - `ok: false` → tell the user: "this project has no okstra setup. Run `/okstra-setup` first." Then stop.
41
+ - `ok: true` → carry `projectRoot` as a literal string and use it to locate `.project-docs/okstra/discovery/task-catalog.json`.
50
42
 
51
- `$OKSTRA_PROJECT_INFO` (JSON `{ok, projectRoot, projectJsonPath, projectId}`)
52
- gives `projectRoot` for locating `.project-docs/okstra/discovery/task-catalog.json`.
43
+ Subsequent `okstra <subcmd>` calls self-bootstrap their Python path, so this skill never needs `okstra paths --shell` / `export PYTHONPATH=...`.
53
44
 
54
45
  ## Step 1: Resolve task-id → timeline path
55
46