@zachjxyz/moxie 0.2.3 → 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 +1 -1
- package/lib/agents.sh +7 -8
- package/lib/phases.sh +5 -4
- package/lib/tokens.sh +46 -20
- package/package.json +1 -1
package/bin/moxie
CHANGED
package/lib/agents.sh
CHANGED
|
@@ -211,15 +211,14 @@ cmd_doctor() {
|
|
|
211
211
|
echo "Dependencies:"
|
|
212
212
|
_check_dep "python3" "Required for ledger parsing and token tracking" || all_ok=0
|
|
213
213
|
_check_dep "caffeinate" "Keeps machine awake during runs (macOS)" || true # non-fatal
|
|
214
|
-
if
|
|
215
|
-
if command -v gtimeout &>/dev/null; then
|
|
216
|
-
echo " [OK] gtimeout (aliased as timeout)"
|
|
217
|
-
else
|
|
218
|
-
echo " [!!] timeout — not found. Install coreutils: brew install coreutils"
|
|
219
|
-
echo " Agent turns will have no timeout protection."
|
|
220
|
-
fi
|
|
221
|
-
else
|
|
214
|
+
if command -v timeout &>/dev/null; then
|
|
222
215
|
echo " [OK] timeout"
|
|
216
|
+
elif command -v gtimeout &>/dev/null; then
|
|
217
|
+
echo " [OK] coreutils (gtimeout aliased as timeout)"
|
|
218
|
+
else
|
|
219
|
+
echo " [!!] coreutils — not found. Install: brew install coreutils"
|
|
220
|
+
echo " Provides timeout protection for agent turns (gtimeout)."
|
|
221
|
+
all_ok=0
|
|
223
222
|
fi
|
|
224
223
|
echo ""
|
|
225
224
|
|
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]
|
|
@@ -378,11 +378,12 @@ TOML
|
|
|
378
378
|
|
|
379
379
|
echo ""
|
|
380
380
|
echo "Next steps:"
|
|
381
|
-
echo " 1.
|
|
381
|
+
echo " 1. Run: moxie doctor (verify dependencies and environment)"
|
|
382
|
+
echo " 2. Review .moxie/config.toml (agent commands, timeouts)"
|
|
382
383
|
if [ ! -d "$MOXIE_DIR/context" ] || [ -z "$(ls -A "$MOXIE_DIR/context" 2>/dev/null)" ]; then
|
|
383
384
|
echo " Tip: add context docs to .moxie/context/ (roadmaps, PRDs, etc.)"
|
|
384
385
|
fi
|
|
385
|
-
echo "
|
|
386
|
+
echo " 3. Run: moxie start (background, caffeinated)"
|
|
386
387
|
echo " Or: moxie run (foreground, caffeinated)"
|
|
387
388
|
}
|
|
388
389
|
|
package/lib/tokens.sh
CHANGED
|
@@ -14,38 +14,64 @@ extract_tokens() {
|
|
|
14
14
|
return
|
|
15
15
|
fi
|
|
16
16
|
|
|
17
|
-
# ---- Strategy 2:
|
|
18
|
-
#
|
|
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
|
-
|
|
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
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
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