conduct-cli 0.4.5__tar.gz → 0.4.7__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.5
3
+ Version: 0.4.7
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.5"
7
+ version = "0.4.7"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -125,7 +125,7 @@ def _detect_ai_tool():
125
125
  return "unknown"
126
126
 
127
127
 
128
- def _post_event(tool_name, tool_input, decision, rule_id=None, message=None, tool_use_id=None):
128
+ def _post_event(tool_name, tool_input, decision, rule_id=None, message=None, session_id=None):
129
129
  try:
130
130
  cfg = json.loads(CONFIG_PATH.read_text()) if CONFIG_PATH.exists() else {}
131
131
  except Exception:
@@ -144,7 +144,7 @@ def _post_event(tool_name, tool_input, decision, rule_id=None, message=None, too
144
144
  "decision": decision,
145
145
  "rule_id": rule_id,
146
146
  "rule_message": message,
147
- "tool_use_id": tool_use_id,
147
+ "session_id": session_id,
148
148
  })
149
149
  api_url = cfg.get("api_url", "https://api.conductai.ai").rstrip("/")
150
150
  script = (
@@ -163,18 +163,19 @@ def _post_event(tool_name, tool_input, decision, rule_id=None, message=None, too
163
163
  )
164
164
 
165
165
 
166
- def _post_usage(tool_use_id, tokens_input, tokens_output, duration_ms, ai_tool):
166
+ def _post_usage(session_id, tool_name, tokens_input, tokens_output, duration_ms):
167
167
  """Fire-and-forget POST to /guard/events/usage"""
168
168
  try:
169
169
  cfg = json.loads(CONFIG_PATH.read_text()) if CONFIG_PATH.exists() else {}
170
170
  except Exception:
171
171
  return
172
172
  workspace_id = cfg.get("workspace_id")
173
- if not workspace_id or not tool_use_id:
173
+ if not workspace_id or not session_id:
174
174
  return
175
175
  payload = json.dumps({
176
176
  "workspace_id": workspace_id,
177
- "tool_use_id": tool_use_id,
177
+ "session_id": session_id,
178
+ "tool_name": tool_name,
178
179
  "tokens_input": tokens_input,
179
180
  "tokens_output": tokens_output,
180
181
  "duration_ms": duration_ms,
@@ -203,12 +204,13 @@ def post_usage_main():
203
204
  data = json.load(sys.stdin)
204
205
  except Exception:
205
206
  sys.exit(0)
206
- tool_use_id = data.get("tool_use_id")
207
+ session_id = data.get("session_id")
208
+ tool_name = (data.get("tool_name") or "").lower()
207
209
  usage = data.get("usage") or {}
208
210
  tokens_input = usage.get("input_tokens", 0)
209
211
  tokens_output = usage.get("output_tokens", 0)
210
212
  duration_ms = data.get("duration_ms")
211
- _post_usage(tool_use_id, tokens_input, tokens_output, duration_ms, _detect_ai_tool())
213
+ _post_usage(session_id, tool_name, tokens_input, tokens_output, duration_ms)
212
214
  sys.exit(0)
213
215
 
214
216
 
@@ -226,15 +228,15 @@ def main():
226
228
  print(f"[ConductGuard] {reason or 'Budget hard cap reached. Contact your manager.'}")
227
229
  sys.exit(2)
228
230
 
231
+ session_id = data.get("session_id")
229
232
  tool_name = (data.get("tool_name") or "").lower()
230
233
  tool_input = data.get("tool_input") or {}
231
- tool_use_id = data.get("tool_use_id")
232
234
 
233
235
  _, action, rule_id, message = _check_policy(tool_name, tool_input)
234
236
 
235
237
  # Always post an event — "allowed" for normal calls, "blocked"/"warned" for violations
236
238
  decision = {"block": "blocked", "warn": "warned", "approval": "blocked"}.get(action, "allowed")
237
- _post_event(tool_name, tool_input, decision, rule_id, message, tool_use_id=tool_use_id)
239
+ _post_event(tool_name, tool_input, decision, rule_id, message, session_id=session_id)
238
240
 
239
241
  if action == "block":
240
242
  print(f"[ConductGuard] {message}")
@@ -246,7 +248,10 @@ def main():
246
248
 
247
249
 
248
250
  if __name__ == "__main__":
249
- main()
251
+ if len(sys.argv) > 1 and sys.argv[1] == "post":
252
+ post_usage_main()
253
+ else:
254
+ main()
250
255
  '''
251
256
 
252
257
  # ── Guard config helpers ──────────────────────────────────────────────────────
@@ -343,16 +348,27 @@ def _install_codex_hook(hook_path: Path) -> None:
343
348
  pre.append({"matcher": ".*", "hooks": [{"type": "command", "command": pre_cmd}]})
344
349
  changed = True
345
350
 
346
- # PostToolUse
347
- post_cmd = "conductguard-post"
351
+ # PostToolUse — self-contained: python3 /path/hook.py post (no PATH dependency)
352
+ post_cmd = f"python3 {hook_path} post"
348
353
  post = hook_section.setdefault("PostToolUse", [])
354
+ # Remove stale conductguard-post entries registered by older CLI versions
355
+ stale = "conductguard-post"
356
+ cleaned = False
357
+ for h in post:
358
+ before = len(h.get("hooks", []))
359
+ h["hooks"] = [e for e in h.get("hooks", []) if e.get("command") != stale]
360
+ if len(h["hooks"]) < before:
361
+ cleaned = True
362
+ post[:] = [h for h in post if h.get("hooks")]
349
363
  post_already = any(
350
364
  e.get("command") == post_cmd
351
365
  for h in post
352
366
  for e in h.get("hooks", [])
353
367
  )
354
368
  if not post_already:
355
- post.append({"matcher": "*", "hooks": [{"type": "command", "command": post_cmd}]})
369
+ post.append({"matcher": ".*", "hooks": [{"type": "command", "command": post_cmd}]})
370
+ changed = True
371
+ if cleaned:
356
372
  changed = True
357
373
 
358
374
  if changed:
@@ -360,7 +376,7 @@ def _install_codex_hook(hook_path: Path) -> None:
360
376
  codex_hooks.write_text(json.dumps(hooks, indent=2))
361
377
  if not pre_already:
362
378
  print(f" {GREEN}Codex PreToolUse hook registered{RESET}")
363
- if not post_already:
379
+ if not post_already or cleaned:
364
380
  print(f" {GREEN}Codex PostToolUse hook registered{RESET}")
365
381
  else:
366
382
  print(f" {GRAY}Codex hooks already registered{RESET}")
@@ -439,16 +455,27 @@ def _install_claude_hook(hook_path: Path) -> None:
439
455
  pre.append({"matcher": ".*", "hooks": [{"type": "command", "command": pre_cmd}]})
440
456
  changed = True
441
457
 
442
- # PostToolUse — conductguard-post entrypoint
458
+ # PostToolUse — self-contained: python3 /path/hook.py post (no PATH dependency)
443
459
  post = hooks.setdefault("PostToolUse", [])
444
- post_cmd = "conductguard-post"
460
+ post_cmd = f"python3 {hook_path} post"
461
+ # Remove stale conductguard-post entries registered by older CLI versions
462
+ stale = "conductguard-post"
463
+ cleaned = False
464
+ for h in post:
465
+ before = len(h.get("hooks", []))
466
+ h["hooks"] = [e for e in h.get("hooks", []) if e.get("command") != stale]
467
+ if len(h["hooks"]) < before:
468
+ cleaned = True
469
+ post[:] = [h for h in post if h.get("hooks")]
445
470
  post_already = any(
446
471
  e.get("command") == post_cmd
447
472
  for h in post
448
473
  for e in h.get("hooks", [])
449
474
  )
450
475
  if not post_already:
451
- post.append({"matcher": "*", "hooks": [{"type": "command", "command": post_cmd}]})
476
+ post.append({"matcher": ".*", "hooks": [{"type": "command", "command": post_cmd}]})
477
+ changed = True
478
+ if cleaned:
452
479
  changed = True
453
480
 
454
481
  if changed:
@@ -456,7 +483,7 @@ def _install_claude_hook(hook_path: Path) -> None:
456
483
  claude_settings.write_text(json.dumps(settings, indent=2))
457
484
  if not pre_already:
458
485
  print(f" {GREEN}Claude Code PreToolUse hook registered{RESET}")
459
- if not post_already:
486
+ if not post_already or cleaned:
460
487
  print(f" {GREEN}Claude Code PostToolUse hook registered{RESET}")
461
488
  else:
462
489
  print(f" {GRAY}Claude Code hooks already registered{RESET}")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.5
3
+ Version: 0.4.7
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