conduct-cli 0.4.9__tar.gz → 0.4.11__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.9
3
+ Version: 0.4.11
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "conduct-cli"
7
- version = "0.4.9"
7
+ version = "0.4.11"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -198,25 +198,84 @@ def _post_usage(session_id, tool_name, tokens_input, tokens_output, duration_ms)
198
198
  )
199
199
 
200
200
 
201
+ def _read_tokens_from_transcript(transcript_path, tool_use_id):
202
+ """Read token counts from Claude Code transcript (matched by tool_use_id)."""
203
+ try:
204
+ path = Path(transcript_path)
205
+ if not path.exists() or not tool_use_id:
206
+ return 0, 0
207
+ lines = path.read_text(encoding="utf-8", errors="ignore").splitlines()
208
+ for line in reversed(lines):
209
+ if not line.strip() or "tool_use" not in line:
210
+ continue
211
+ try:
212
+ entry = json.loads(line)
213
+ except Exception:
214
+ continue
215
+ msg = entry.get("message") or {}
216
+ usage = msg.get("usage")
217
+ if not usage:
218
+ continue
219
+ content = msg.get("content") or []
220
+ if any(
221
+ isinstance(b, dict) and b.get("type") == "tool_use" and b.get("id") == tool_use_id
222
+ for b in content
223
+ ):
224
+ total_in = (
225
+ usage.get("input_tokens", 0)
226
+ + usage.get("cache_creation_input_tokens", 0)
227
+ + usage.get("cache_read_input_tokens", 0)
228
+ )
229
+ return total_in, usage.get("output_tokens", 0)
230
+ except Exception:
231
+ pass
232
+ return 0, 0
233
+
234
+
235
+ def _read_codex_tokens():
236
+ """Read last_token_usage from the most recently modified Codex session file."""
237
+ try:
238
+ sessions_dir = Path.home() / ".codex" / "sessions"
239
+ if not sessions_dir.exists():
240
+ return 0, 0
241
+ files = sorted(sessions_dir.rglob("*.jsonl"), key=lambda p: p.stat().st_mtime, reverse=True)
242
+ if not files:
243
+ return 0, 0
244
+ lines = files[0].read_text(encoding="utf-8", errors="ignore").splitlines()
245
+ for line in reversed(lines):
246
+ if "token_count" not in line:
247
+ continue
248
+ try:
249
+ entry = json.loads(line)
250
+ if entry.get("type") == "event_msg":
251
+ info = entry.get("payload", {}).get("info", {})
252
+ usage = info.get("last_token_usage", {})
253
+ if usage:
254
+ total_in = usage.get("input_tokens", 0)
255
+ total_out = usage.get("output_tokens", 0) + usage.get("reasoning_output_tokens", 0)
256
+ return total_in, total_out
257
+ except Exception:
258
+ continue
259
+ except Exception:
260
+ pass
261
+ return 0, 0
262
+
263
+
201
264
  def post_usage_main():
202
265
  """PostToolUse hook entrypoint — captures token usage and duration."""
203
266
  try:
204
267
  data = json.load(sys.stdin)
205
268
  except Exception:
206
269
  sys.exit(0)
207
- # Write full payload to debug file so we can inspect what Claude Code sends
208
- try:
209
- debug_path = GUARD_DIR / "debug_post.json"
210
- debug_path.write_text(json.dumps(data, indent=2))
211
- except Exception:
212
- pass
213
- session_id = data.get("session_id")
214
- tool_name = (data.get("tool_name") or "").lower()
215
- usage = data.get("usage") or {}
216
- tokens_input = usage.get("input_tokens", 0)
217
- tokens_output = usage.get("output_tokens", 0)
218
- duration_ms = data.get("duration_ms")
219
- _post_usage(session_id, tool_name, tokens_input, tokens_output, duration_ms)
270
+ session_id = data.get("session_id")
271
+ tool_name = (data.get("tool_name") or "").lower()
272
+ tool_use_id = data.get("tool_use_id")
273
+ transcript_path = data.get("transcript_path")
274
+ if transcript_path:
275
+ tokens_input, tokens_output = _read_tokens_from_transcript(transcript_path, tool_use_id)
276
+ else:
277
+ tokens_input, tokens_output = _read_codex_tokens()
278
+ _post_usage(session_id, tool_name, tokens_input, tokens_output, None)
220
279
  sys.exit(0)
221
280
 
222
281
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.9
3
+ Version: 0.4.11
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
File without changes
File without changes
File without changes