conduct-cli 0.6.4__tar.gz → 0.6.6__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.
Files changed (32) hide show
  1. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/PKG-INFO +1 -1
  2. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/pyproject.toml +1 -1
  3. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/hook_template.py +37 -5
  4. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  5. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/README.md +0 -0
  6. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/setup.cfg +0 -0
  7. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/setup.py +0 -0
  8. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/__init__.py +0 -0
  9. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/api.py +0 -0
  10. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/guard.py +0 -0
  11. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/guardmcp.py +0 -0
  12. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/hook_precompact_template.py +0 -0
  13. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/hook_session_start_template.py +0 -0
  14. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/hook_stop_template.py +0 -0
  15. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/log_util.py +0 -0
  16. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/main.py +0 -0
  17. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/mcp_server.py +0 -0
  18. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/memory.py +0 -0
  19. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli/paxel.py +0 -0
  20. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
  21. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  22. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  23. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/requires.txt +0 -0
  24. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/src/conduct_cli.egg-info/top_level.txt +0 -0
  25. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_bash_operator_signature.py +0 -0
  26. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_command_word_matcher.py +0 -0
  27. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_guard_policy.py +0 -0
  28. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_guard_savings.py +0 -0
  29. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_hook_syntax.py +0 -0
  30. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_log_util.py +0 -0
  31. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_proxy_env.py +0 -0
  32. {conduct_cli-0.6.4 → conduct_cli-0.6.6}/tests/test_switch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.6.4
3
+ Version: 0.6.6
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.6.4"
7
+ version = "0.6.6"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -179,8 +179,29 @@ def _maybe_sync_policy():
179
179
 
180
180
  remote_version = remote.get("version", "")
181
181
  local_version = ""
182
+ local_policy: dict = {}
182
183
  if POLICY_PATH.exists():
183
- local_version = json.loads(POLICY_PATH.read_text()).get("version", "")
184
+ local_policy = json.loads(POLICY_PATH.read_text())
185
+ local_version = local_policy.get("version", "")
186
+
187
+ # Meta-rule: block silent fail_closed → fail_open downgrade.
188
+ # An attacker who compromises the policy server cannot silently disable governance.
189
+ # Legitimate downgrade requires fail_mode_downgrade_token in the new policy,
190
+ # matching the token stored when fail_closed was originally set.
191
+ old_mode = local_policy.get("fail_mode", "fail_open")
192
+ new_mode = remote.get("fail_mode", "fail_open")
193
+ if old_mode == "fail_closed" and new_mode != "fail_closed":
194
+ expected = local_policy.get("fail_mode_downgrade_token")
195
+ provided = remote.get("fail_mode_downgrade_token")
196
+ if not expected or expected != provided:
197
+ _post_signature_invalid_event(
198
+ expected_sig=f"fail_mode_downgrade_token:{expected}",
199
+ computed_sig=f"fail_mode_downgrade_token:{provided}",
200
+ policy_version=remote_version,
201
+ hostname=__import__("platform").node(),
202
+ )
203
+ return # reject downgrade — keep existing fail_closed policy
204
+
184
205
  if remote_version != local_version:
185
206
  POLICY_PATH.write_text(json.dumps(remote, indent=2))
186
207
  VERSION_CACHE_PATH.write_text(json.dumps({"ts": time.time(), "version": remote_version}))
@@ -343,8 +364,18 @@ def _detect_repo(): # -> Optional[str]
343
364
 
344
365
  def _detect_ai_tool():
345
366
  import os
346
- if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_CODE_ENTRYPOINT"):
367
+ # Explicit env vars — most reliable, set by the tool itself
368
+ if os.environ.get("CLAUDE_CODE_ENTRYPOINT") or os.environ.get("CLAUDECODE"):
347
369
  return "claude-code"
370
+ if os.environ.get("CODEX_SESSION_ID") or os.environ.get("CODEX_CLI_VERSION"):
371
+ return "codex"
372
+ # TERM_PROGRAM is set by Cursor, Windsurf, and similar IDE terminals
373
+ term = os.environ.get("TERM_PROGRAM", "").lower()
374
+ if term == "cursor":
375
+ return "cursor"
376
+ if term == "windsurf":
377
+ return "windsurf"
378
+ # PATH-based fallback
348
379
  path = os.environ.get("PATH", "")
349
380
  if "Codex.app" in path or "codex" in path.lower():
350
381
  return "codex"
@@ -352,7 +383,7 @@ def _detect_ai_tool():
352
383
  return "cursor"
353
384
  if "windsurf" in path.lower():
354
385
  return "windsurf"
355
- return "unknown"
386
+ return "claude-code" # default — Claude Code is primary
356
387
 
357
388
 
358
389
  def _already_warned_this_session(session_id: str, rule_id: str) -> bool:
@@ -730,11 +761,12 @@ def post_usage_main():
730
761
  data = json.load(sys.stdin)
731
762
  except Exception:
732
763
  sys.exit(0)
733
- session_id = data.get("session_id")
734
764
  tool_name = (data.get("tool_name") or "").lower()
735
765
  tool_use_id = data.get("tool_use_id")
736
766
  transcript_path = data.get("transcript_path")
737
- is_codex = (tool_use_id or "").startswith("call_")
767
+ is_codex = (tool_use_id or "").startswith("call_")
768
+ # Codex sends transcript_path but not session_id — use path as stable session key
769
+ session_id = data.get("session_id") or (f"transcript:{transcript_path}" if transcript_path else None)
738
770
 
739
771
  if is_codex and transcript_path:
740
772
  # Write pending args; spawn delayed background reader so hook exits instantly
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.6.4
3
+ Version: 0.6.6
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