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.
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/PKG-INFO +1 -1
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/pyproject.toml +1 -1
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/guard.py +77 -30
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/README.md +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/setup.cfg +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/setup.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/__init__.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/base.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/posttooluse.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/precompact.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/pretooluse.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_parser.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_report_push.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/session_start.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/hooks/stop.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/log_util.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/main.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/paxel.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli/tool_groups.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_advisory_and_hook.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_api_client.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_bash_operator_signature.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_blast_radius.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_cmd_run.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_command_word_matcher.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_drain_daemon_health.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_hook_dispatch.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_journal_drain.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_log_util.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_proxy_coverage.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_proxy_env.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_signed_policy.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_switch.py +0 -0
- {conduct_cli-0.7.43 → conduct_cli-0.7.45}/tests/test_token_lifecycle.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "conduct-cli"
|
|
7
|
-
version = "0.7.
|
|
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
|
|
611
|
-
|
|
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.
|
|
614
|
-
|
|
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
|
-
|
|
617
|
-
|
|
618
|
-
|
|
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",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|