@zachjxyz/moxie 0.2.4 → 0.2.5

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/moxie CHANGED
@@ -16,7 +16,7 @@
16
16
 
17
17
  set -euo pipefail
18
18
 
19
- MOXIE_VERSION="0.2.4"
19
+ MOXIE_VERSION="0.2.5"
20
20
  # Resolve symlinks (npm installs bin as a symlink)
21
21
  _self="$0"
22
22
  while [ -L "$_self" ]; do
package/lib/phases.sh CHANGED
@@ -324,11 +324,11 @@ command = 'codex exec -c model_reasoning_effort="xhigh" --dangerously-bypass-app
324
324
  order = 1
325
325
 
326
326
  [agents.claude]
327
- command = "claude --dangerously-skip-permissions --effort max -p"
327
+ command = "claude --dangerously-skip-permissions --effort max --output-format json -p"
328
328
  order = 2
329
329
 
330
330
  [agents.qwen]
331
- command = "qwen --yolo"
331
+ command = "qwen --yolo -o json"
332
332
  order = 3
333
333
 
334
334
  [settings]
package/lib/tokens.sh CHANGED
@@ -14,38 +14,64 @@ extract_tokens() {
14
14
  return
15
15
  fi
16
16
 
17
- # ---- Strategy 2: Claude JSON output ----
18
- # If output-format=json, extract from usage block
17
+ # ---- Strategy 2: NDJSON / JSON array with usage block ----
18
+ # Claude (--output-format json) and Qwen (-o json) emit NDJSON or a JSON
19
+ # array. The final "type":"result" object contains a top-level "usage" key.
20
+ # Logs may also contain non-JSON noise (e.g. hook errors on stderr).
19
21
  tokens=$(python3 -c "
20
22
  import json, sys
21
23
  try:
22
- # The log may have non-JSON preamble; find the JSON object
23
24
  with open('$logfile') as f:
24
25
  text = f.read()
25
- # Try parsing the whole thing as JSON first
26
+
27
+ # Collect candidate JSON objects from three formats:
28
+ # 1) Single JSON object 2) JSON array 3) NDJSON (one obj per line)
29
+ candidates = []
30
+ # 1) Single object
26
31
  try:
27
- d = json.loads(text)
28
- except:
29
- # Find the last { ... } block (Claude JSON output is at the end)
30
- import re
31
- matches = list(re.finditer(r'\{', text))
32
- d = None
33
- for m in reversed(matches):
32
+ obj = json.loads(text)
33
+ if isinstance(obj, list):
34
+ candidates.extend(obj) # 2) JSON array
35
+ elif isinstance(obj, dict):
36
+ candidates.append(obj)
37
+ except Exception:
38
+ # 3) NDJSON — try each line independently (skips noise)
39
+ for line in text.splitlines():
40
+ line = line.strip()
41
+ if not line or line[0] not in ('{', '['):
42
+ continue
34
43
  try:
35
- d = json.loads(text[m.start():])
36
- break
37
- except:
44
+ obj = json.loads(line)
45
+ if isinstance(obj, list):
46
+ candidates.extend(obj)
47
+ elif isinstance(obj, dict):
48
+ candidates.append(obj)
49
+ except Exception:
38
50
  continue
39
- if d and 'usage' in d:
40
- u = d['usage']
41
- total = u.get('input_tokens', 0) + u.get('output_tokens', 0)
42
- total += u.get('cache_creation_input_tokens', 0)
43
- total += u.get('cache_read_input_tokens', 0)
51
+
52
+ # Find the last 'result' object with a usage block
53
+ result_usage = None
54
+ for obj in reversed(candidates):
55
+ if isinstance(obj, dict) and obj.get('type') == 'result' and 'usage' in obj:
56
+ result_usage = obj['usage']
57
+ break
58
+
59
+ # Fallback: any object with a top-level 'usage' dict
60
+ if result_usage is None:
61
+ for obj in reversed(candidates):
62
+ if isinstance(obj, dict) and isinstance(obj.get('usage'), dict):
63
+ result_usage = obj['usage']
64
+ break
65
+
66
+ if result_usage:
67
+ total = result_usage.get('input_tokens', 0) + result_usage.get('output_tokens', 0)
68
+ total += result_usage.get('cache_creation_input_tokens', 0)
69
+ total += result_usage.get('cache_read_input_tokens', 0)
44
70
  if total > 0:
45
71
  print(total)
46
72
  sys.exit(0)
47
73
  sys.exit(1)
48
- except:
74
+ except Exception:
49
75
  sys.exit(1)
50
76
  " 2>/dev/null)
51
77
  if [ -n "$tokens" ] && [ "$tokens" -gt 0 ] 2>/dev/null; then
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zachjxyz/moxie",
3
- "version": "0.2.4",
3
+ "version": "0.2.5",
4
4
  "description": "Run multiple AI coding agents (Claude, Codex, Qwen) through spec-driven phases with unanimous quorum convergence",
5
5
  "bin": {
6
6
  "moxie": "bin/moxie"