conduct-cli 0.7.43__tar.gz → 0.7.45__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 (47) hide show
  1. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/PKG-INFO +1 -1
  2. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/pyproject.toml +1 -1
  3. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/guard.py +77 -30
  4. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  5. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/README.md +0 -0
  6. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/setup.cfg +0 -0
  7. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/setup.py +0 -0
  8. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/__init__.py +0 -0
  9. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/api.py +0 -0
  10. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/guardmcp.py +0 -0
  11. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/__init__.py +0 -0
  12. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/base.py +0 -0
  13. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/posttooluse.py +0 -0
  14. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/precompact.py +0 -0
  15. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/pretooluse.py +0 -0
  16. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_parser.py +0 -0
  17. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_report_push.py +0 -0
  18. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_start.py +0 -0
  19. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/stop.py +0 -0
  20. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/log_util.py +0 -0
  21. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/main.py +0 -0
  22. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/mcp_server.py +0 -0
  23. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/memory.py +0 -0
  24. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/paxel.py +0 -0
  25. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/tool_groups.py +0 -0
  26. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
  27. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  28. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  29. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/requires.txt +0 -0
  30. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/top_level.txt +0 -0
  31. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_advisory_and_hook.py +0 -0
  32. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_api_client.py +0 -0
  33. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_bash_operator_signature.py +0 -0
  34. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_blast_radius.py +0 -0
  35. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_cmd_run.py +0 -0
  36. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_command_word_matcher.py +0 -0
  37. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_drain_daemon_health.py +0 -0
  38. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_guard_policy.py +0 -0
  39. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_guard_savings.py +0 -0
  40. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_hook_dispatch.py +0 -0
  41. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_journal_drain.py +0 -0
  42. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_log_util.py +0 -0
  43. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_proxy_coverage.py +0 -0
  44. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_proxy_env.py +0 -0
  45. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_signed_policy.py +0 -0
  46. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_switch.py +0 -0
  47. {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_token_lifecycle.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.7.43
3
+ Version: 0.7.45
4
4
  Summary: CLI for Conduct AI — secure, govern, 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.7.43"
7
+ version = "0.7.45"
8
8
  description = "CLI for Conduct AI — secure, govern, install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -388,7 +388,7 @@ _MCP_TARGETS = [
388
388
  ]
389
389
 
390
390
 
391
- def _register_mcp(workspace_id: str, agent_token: str, api_url: str) -> None:
391
+ def _register_mcp(workspace_id: str, agent_token: str, api_url: str, dry_run: bool = False) -> None:
392
392
  """Write conductguard + agent-booster MCP entries into every AI tool config found.
393
393
 
394
394
  Credentials are NOT stored in the MCP config — the server reads them from
@@ -441,7 +441,7 @@ def _register_mcp(workspace_id: str, agent_token: str, api_url: str) -> None:
441
441
 
442
442
  # Cursor global rules — write Guard policies as user rules so they apply across all projects.
443
443
  _patch_cursor_global_rules()
444
- _patch_tool_instruction_files(agent_token, api_url)
444
+ _patch_tool_instruction_files(agent_token, api_url, dry_run=dry_run)
445
445
 
446
446
 
447
447
  def _patch_claude_desktop_proxy(api_url: str, agent_token: str) -> None:
@@ -501,6 +501,16 @@ def _patch_copilot_mcp(agent_token: str, api_url: str) -> None:
501
501
  print(f" {GREEN}conduct-guard MCP registered in GitHub Copilot (SSE){RESET}")
502
502
 
503
503
 
504
+ _GUARD_RULES_TEXT = (
505
+ "Before executing any shell command, file write, network request, API call, or code change:\n"
506
+ "- Call guard_check (conductguard MCP tool) with the action and parameters.\n"
507
+ "- If BLOCKED: stop immediately and explain the policy rule to the user.\n"
508
+ "- If WARNING: proceed but surface the warning. If ALLOWED: proceed normally.\n"
509
+ "- Never write credentials, API keys, or secrets to files or output.\n"
510
+ "- Never send PII (emails, names, payment data) to external endpoints."
511
+ )
512
+
513
+
504
514
  def _patch_cursor_global_rules() -> None:
505
515
  """Write ConductGuard policy rules into Cursor's global user rules setting.
506
516
 
@@ -509,16 +519,7 @@ def _patch_cursor_global_rules() -> None:
509
519
  This is soft enforcement (prompt-level), not structural like Claude Code hooks,
510
520
  but ensures Guard policies are visible to the model across all Cursor projects.
511
521
  """
512
- GUARD_RULES = (
513
- "# ConductGuard — team AI policies (managed by conduct guard sync)\n"
514
- "- ALWAYS call guard_check (via conductguard MCP) before running shell commands, "
515
- "reading or writing files, accessing external APIs, or modifying code.\n"
516
- "- NEVER write credentials, API keys, or secrets to files or output.\n"
517
- "- NEVER send PII (emails, names, SSNs, payment data) to external endpoints.\n"
518
- "- If guard_check returns BLOCKED: stop and explain the policy rule to the user.\n"
519
- "- If guard_check returns WARNING: proceed but surface the warning.\n"
520
- "- These rules are enforced by your team's ConductGuard policy. Do not bypass them."
521
- )
522
+ GUARD_RULES = _GUARD_RULES_TEXT
522
523
 
523
524
  candidates = [
524
525
  Path.home() / "Library" / "Application Support" / "Cursor" / "User" / "settings.json",
@@ -541,7 +542,7 @@ def _patch_cursor_global_rules() -> None:
541
542
  print(f" {GREEN}Cursor global rules updated with ConductGuard policy{RESET}")
542
543
 
543
544
 
544
- def _patch_tool_instruction_files(agent_token: str, api_url: str) -> None:
545
+ def _patch_tool_instruction_files(agent_token: str, api_url: str, dry_run: bool = False) -> None:
545
546
  """Write ConductGuard policy block into instruction files for all AI tools.
546
547
 
547
548
  Targets:
@@ -559,14 +560,7 @@ def _patch_tool_instruction_files(agent_token: str, api_url: str) -> None:
559
560
 
560
561
  MARKER_START = "<!-- ConductGuard — managed by conduct guard sync -->"
561
562
  MARKER_END = "<!-- /ConductGuard -->"
562
- GUARD_RULES = (
563
- "Before executing any shell command, file write, network request, API call, or code change:\n"
564
- "- Call `guard_check` (conductguard MCP tool) with the action and parameters.\n"
565
- "- If BLOCKED: stop immediately and explain the policy rule to the user.\n"
566
- "- If WARNING: proceed but surface the warning. If ALLOWED: proceed normally.\n"
567
- "- Never write credentials, API keys, or secrets to files or output.\n"
568
- "- Never send PII (emails, names, payment data) to external endpoints."
569
- )
563
+ GUARD_RULES = _GUARD_RULES_TEXT
570
564
 
571
565
  # Pull team instructions from API (non-fatal)
572
566
  team_content = ""
@@ -603,19 +597,64 @@ def _patch_tool_instruction_files(agent_token: str, api_url: str) -> None:
603
597
  path = repo_path if (repo_path and repo_path.exists()) else global_path
604
598
  if not path.exists() and repo_path:
605
599
  path = repo_path
606
- path.parent.mkdir(parents=True, exist_ok=True)
607
600
  existing = path.read_text() if path.exists() else ""
608
601
  if MARKER_START in existing:
609
602
  updated = pattern.sub(guard_block, existing)
610
- if updated == existing:
611
- print(f" {GRAY}{label} already up to date{RESET}")
603
+ if dry_run:
604
+ if updated == existing:
605
+ print(f" [dry-run] No change: {path}")
606
+ else:
607
+ print(f" [dry-run] Would write: {path}")
612
608
  else:
613
- path.write_text(updated)
614
- print(f" {GREEN}{label} updated{RESET}")
609
+ path.parent.mkdir(parents=True, exist_ok=True)
610
+ if updated == existing:
611
+ print(f" {GRAY}{label} already up to date{RESET}")
612
+ else:
613
+ path.write_text(updated)
614
+ print(f" {GREEN}{label} updated{RESET}")
615
615
  else:
616
- sep = "\n\n" if existing.strip() else ""
617
- path.write_text((existing.rstrip() + sep + guard_block + "\n").lstrip())
618
- print(f" {GREEN}{label} — ConductGuard block added{RESET}")
616
+ new_content = (existing.rstrip() + ("\n\n" if existing.strip() else "") + guard_block + "\n").lstrip()
617
+ if dry_run:
618
+ if new_content == existing:
619
+ print(f" [dry-run] No change: {path}")
620
+ else:
621
+ print(f" [dry-run] Would write: {path}")
622
+ else:
623
+ path.parent.mkdir(parents=True, exist_ok=True)
624
+ path.write_text(new_content)
625
+ print(f" {GREEN}{label} — ConductGuard block added{RESET}")
626
+
627
+
628
+ def _reset_tool_instruction_files() -> None:
629
+ """Remove ConductGuard blocks from all instruction files."""
630
+ import re as _re
631
+ MARKER_RE = _re.compile(
632
+ r"\n?<!-- ConductGuard -->\n.*?<!-- /ConductGuard -->\n?",
633
+ _re.DOTALL,
634
+ )
635
+ targets = [
636
+ Path.home() / "CLAUDE.md",
637
+ Path.cwd() / "CLAUDE.md",
638
+ Path.cwd() / ".github" / "copilot-instructions.md",
639
+ Path.cwd() / "AGENTS.md",
640
+ Path.cwd() / ".cursorrules",
641
+ Path.cwd() / ".windsurfrules",
642
+ ]
643
+ removed = 0
644
+ for p in targets:
645
+ if not p.exists():
646
+ continue
647
+ original = p.read_text(encoding="utf-8")
648
+ cleaned = MARKER_RE.sub("", original).strip()
649
+ if cleaned != original.strip():
650
+ p.write_text(cleaned + "\n", encoding="utf-8")
651
+ print(f" {GREEN}Removed ConductGuard block: {p}{RESET}")
652
+ removed += 1
653
+ else:
654
+ print(f" {YELLOW}No ConductGuard block found: {p}{RESET}")
655
+ if removed == 0:
656
+ print(" No ConductGuard blocks found in any instruction files.")
657
+
619
658
 
620
659
  def _install_codex_hook(hook_path: Path) -> None:
621
660
  """Register PreToolUse and PostToolUse hooks in ~/.codex/hooks.json."""
@@ -1325,6 +1364,12 @@ def cmd_guard_sync(args):
1325
1364
  # Persona selection — prompt once, skip if already chosen
1326
1365
 
1327
1366
  dry_run = getattr(args, "dry_run", False)
1367
+
1368
+ if getattr(args, "reset_instructions", False):
1369
+ print("Removing ConductGuard blocks from instruction files…")
1370
+ _reset_tool_instruction_files()
1371
+ return
1372
+
1328
1373
  print(f"{'[dry-run] ' if dry_run else ''}Syncing policy…")
1329
1374
  _check_and_upgrade_packages()
1330
1375
 
@@ -1469,7 +1514,7 @@ def cmd_guard_sync(args):
1469
1514
  _install_claude_hook(hook_path)
1470
1515
  _install_codex_hook(hook_path)
1471
1516
  cfg2 = _load_guard_config()
1472
- _register_mcp(workspace_id, cfg2.get("agent_token", ""), base_url)
1517
+ _register_mcp(workspace_id, cfg2.get("agent_token", ""), base_url, dry_run=dry_run)
1473
1518
  try:
1474
1519
  _install_session_hooks()
1475
1520
  except Exception:
@@ -2182,6 +2227,8 @@ def register_guard_parser(sub):
2182
2227
  sync_p = guard_sub.add_parser("sync", help="Refresh policy and re-scan for AI tools")
2183
2228
  sync_p.add_argument("--cursor", action="store_true", help="Write active Guard policies to .cursorrules")
2184
2229
  sync_p.add_argument("--dry-run", action="store_true", help="Preview policy changes without writing anything")
2230
+ sync_p.add_argument("--reset-instructions", action="store_true", dest="reset_instructions",
2231
+ help="Remove ConductGuard blocks from all instruction files")
2185
2232
  sync_p.add_argument("--proxy-url", default=None,
2186
2233
  help="Override the Guard proxy URL (default: https://api.conductai.ai/proxy/anthropic)")
2187
2234
  sync_p.add_argument("--no-local-audit", action="store_true",
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.7.43
3
+ Version: 0.7.45
4
4
  Summary: CLI for Conduct AI — secure, govern, 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