okstra 0.30.2 → 0.30.3

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/bin/okstra CHANGED
@@ -17,6 +17,7 @@ const COMMANDS = new Map([
17
17
  ["render-bundle", () => import("../src/render-bundle.mjs").then((m) => m.run)],
18
18
  ["render-views", () => import("../src/render-views.mjs").then((m) => m.run)],
19
19
  ["wizard", () => import("../src/wizard.mjs").then((m) => m.run)],
20
+ ["token-usage", () => import("../src/token-usage.mjs").then((m) => m.run)],
20
21
  ]);
21
22
 
22
23
  const USAGE = `okstra — multi-agent cross-verification orchestrator for Claude Code
@@ -55,6 +56,9 @@ Introspection commands (JSON output, used by skills to avoid python heredocs):
55
56
  python3 -m okstra_ctl.run --render-only)
56
57
  wizard Drive the okstra-run interactive input state machine
57
58
  (init / step / render-args / confirmation)
59
+ token-usage Collect token usage for a run (wraps the installed
60
+ okstra-token-usage.py so skills avoid emitting
61
+ python3 "$HOME/..." invocations).
58
62
 
59
63
  Global options:
60
64
  --version Print okstra version and exit
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "okstra",
3
- "version": "0.30.2",
3
+ "version": "0.30.3",
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.2",
3
- "builtAt": "2026-05-19T06:29:56.728Z",
2
+ "package": "0.30.3",
3
+ "builtAt": "2026-05-19T08:41:16.438Z",
4
4
  "repoRoot": "/home/runner/work/okstra/okstra"
5
5
  }
@@ -64,14 +64,10 @@ find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' \
64
64
 
65
65
  The columns produced are `size_bytes | mtime_epoch | path`.
66
66
 
67
- On macOS, `find -printf` is unavailable. Fall back to `stat` — again substitute the literal `<LOGS_ROOT>`:
67
+ On macOS, `find -printf` is unavailable. Fall back to `-exec stat` — again substitute the literal `<LOGS_ROOT>`. The `-exec ... {} +` form contains no shell variables and no `$(...)`, so the `Bash(find:*)` permission match holds:
68
68
 
69
69
  ```bash
70
- find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' 2>/dev/null \
71
- | while IFS= read -r p; do
72
- stat -f '%z%t%m%t%N' "$p"
73
- done \
74
- | sort -k1,1nr
70
+ find <LOGS_ROOT> -type f -path '*/runs/*/prompts/*.log' -exec stat -f '%z%t%m%t%N' {} + 2>/dev/null | sort -k1,1nr
75
71
  ```
76
72
 
77
73
  If the result is empty, report `No wrapper log files found under <projectRoot>` and exit.
@@ -432,15 +432,13 @@ Token usage is collected from agent session transcripts after the run, NOT from
432
432
 
433
433
  ### How to Collect
434
434
 
435
- At the **start of Phase 7** (persistence), run the helper script with the path to this run's `team-state.json`:
435
+ At the **start of Phase 7** (persistence), run the helper via the okstra CLI with the path to this run's `team-state.json`. Substitute `<runDirectoryPath>` with a literal absolute path (no shell variables, no `$(...)`) so the `Bash(okstra:*)` permission match holds:
436
436
 
437
437
  ```bash
438
- python3 "$HOME/.okstra/lib/python/okstra-token-usage.py" \
439
- <runDirectoryPath>/state/team-state-<task-type>-<seq>.json \
440
- --write --summary
438
+ okstra token-usage /abs/path/to/run/state/team-state-<task-type>-<seq>.json --write --summary
441
439
  ```
442
440
 
443
- The script is installed at `$HOME/.okstra/lib/python/okstra-token-usage.py` by `okstra install`. The previous repo-relative path (`scripts/okstra-token-usage.py`) only exists in a working clone of the okstra repo and is not appropriate for end-user-deployed runs.
441
+ `okstra token-usage` is a thin Node-side wrapper around the python helper installed at `~/.okstra/bin/okstra-token-usage.py`. Calling the python script directly with `python3 "$HOME/..."` is forbidden the `$HOME` expansion breaks the literal-token permission match and forces a confirmation prompt every call.
444
442
 
445
443
  The script reads:
446
444
  - `~/.claude/projects/<encoded-cwd>/<sessionId>.jsonl` for the lead and every Claude-side worker (Claude worker, Report writer worker, plus the Claude wrappers around Codex/Gemini workers). Sessions are discovered by `teamName: okstra-<task-id>`, lead is identified by `lead.sessionId`, and other workers are identified by `agentName` (e.g. `claude-worker`, `codex-worker`, `gemini-worker`, `report-writer`).
@@ -0,0 +1,51 @@
1
+ import { spawn } from "node:child_process";
2
+ import { join } from "node:path";
3
+ import { promises as fs } from "node:fs";
4
+ import { resolvePaths } from "./paths.mjs";
5
+
6
+ const USAGE = `okstra token-usage — collect token usage for a run
7
+
8
+ Wraps the python helper (\`okstra-token-usage.py\`) installed under
9
+ \`~/.okstra/bin/\` so skills can call this command without emitting a
10
+ shell-expansion-bearing \`python3 "$HOME/..."\` invocation (which would
11
+ break \`Bash(okstra:*)\` permission matching and force a confirmation
12
+ prompt every call).
13
+
14
+ Usage:
15
+ okstra token-usage <state-file> [--write] [--summary] [...]
16
+
17
+ Arguments and flags after the state-file path are forwarded verbatim to
18
+ the python helper. See \`python3 ~/.okstra/bin/okstra-token-usage.py --help\`
19
+ for the full option list.
20
+ `;
21
+
22
+ export async function run(args) {
23
+ if (args.length === 0 || args.includes("--help") || args.includes("-h")) {
24
+ process.stdout.write(USAGE);
25
+ return args.length === 0 ? 2 : 0;
26
+ }
27
+
28
+ const paths = await resolvePaths();
29
+ const script = join(paths.bin, "okstra-token-usage.py");
30
+
31
+ try {
32
+ await fs.access(script);
33
+ } catch {
34
+ process.stderr.write(
35
+ `error: ${script} not found — run 'okstra install' (or 'okstra ensure-installed') first\n`,
36
+ );
37
+ return 1;
38
+ }
39
+
40
+ return await new Promise((resolve) => {
41
+ const child = spawn("python3", [script, ...args], {
42
+ stdio: "inherit",
43
+ env: { ...process.env, PYTHONPATH: paths.pythonpath },
44
+ });
45
+ child.on("error", (err) => {
46
+ process.stderr.write(`error: failed to spawn python3: ${err.message}\n`);
47
+ resolve(1);
48
+ });
49
+ child.on("close", (code) => resolve(typeof code === "number" ? code : 1));
50
+ });
51
+ }