conduct-cli 0.7.42__tar.gz → 0.7.44__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.42 → conduct_cli-0.7.44}/PKG-INFO +1 -1
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/pyproject.toml +1 -1
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/guard.py +73 -57
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/README.md +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/setup.cfg +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/setup.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/__init__.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/base.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/posttooluse.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/precompact.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/pretooluse.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/session_parser.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/session_report_push.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/session_start.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/hooks/stop.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/log_util.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/main.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/paxel.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli/tool_groups.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_advisory_and_hook.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_api_client.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_bash_operator_signature.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_blast_radius.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_cmd_run.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_command_word_matcher.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_drain_daemon_health.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_hook_dispatch.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_journal_drain.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_log_util.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_proxy_coverage.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_proxy_env.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_signed_policy.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/tests/test_switch.py +0 -0
- {conduct_cli-0.7.42 → conduct_cli-0.7.44}/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.44"
|
|
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" }
|
|
@@ -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
|
-
|
|
444
|
+
_patch_tool_instruction_files(agent_token, api_url)
|
|
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,59 +542,74 @@ 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
|
|
545
|
-
"""Write ConductGuard
|
|
545
|
+
def _patch_tool_instruction_files(agent_token: str, api_url: str) -> None:
|
|
546
|
+
"""Write ConductGuard policy block into instruction files for all AI tools.
|
|
546
547
|
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
548
|
+
Targets:
|
|
549
|
+
CLAUDE.md — Claude Code + Claude Desktop
|
|
550
|
+
.github/copilot-instructions.md — Copilot + VS Code
|
|
551
|
+
AGENTS.md — Codex CLI + Codex Desktop
|
|
552
|
+
.cursorrules — Cursor
|
|
553
|
+
.windsurfrules — Windsurf
|
|
554
|
+
|
|
555
|
+
Merge strategy (never clobbers existing content):
|
|
556
|
+
First sync : appends block between <!-- ConductGuard --> markers
|
|
557
|
+
Re-sync : replaces only the block, leaves surrounding content untouched
|
|
551
558
|
"""
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
"- If BLOCKED: stop immediately and explain the policy rule to the user.\n"
|
|
558
|
-
"- If WARNING: proceed but surface the warning.\n"
|
|
559
|
-
"- If ALLOWED: proceed normally.\n"
|
|
560
|
-
"Never write credentials, API keys, or secrets to files or output.\n"
|
|
561
|
-
"Never send PII (emails, names, payment data) to external endpoints.\n"
|
|
562
|
-
"<!-- /ConductGuard -->"
|
|
563
|
-
)
|
|
559
|
+
import re as _re
|
|
560
|
+
|
|
561
|
+
MARKER_START = "<!-- ConductGuard — managed by conduct guard sync -->"
|
|
562
|
+
MARKER_END = "<!-- /ConductGuard -->"
|
|
563
|
+
GUARD_RULES = _GUARD_RULES_TEXT
|
|
564
564
|
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
565
|
+
# Pull team instructions from API (non-fatal)
|
|
566
|
+
team_content = ""
|
|
567
|
+
try:
|
|
568
|
+
resp = _req("GET", f"{api_url}/team-os/instructions", token=agent_token)
|
|
569
|
+
team_content = (resp.get("content") or "").strip()
|
|
570
|
+
except Exception:
|
|
571
|
+
pass
|
|
572
|
+
|
|
573
|
+
parts = [MARKER_START, "## ConductGuard Policy"]
|
|
574
|
+
if team_content:
|
|
575
|
+
parts.append(team_content)
|
|
576
|
+
parts.append(GUARD_RULES)
|
|
577
|
+
parts.append(MARKER_END)
|
|
578
|
+
guard_block = "\n".join(parts)
|
|
579
|
+
|
|
580
|
+
repo_root: Path | None = None
|
|
581
|
+
for parent in [Path.cwd(), *Path.cwd().parents]:
|
|
582
|
+
if (parent / ".git").exists() or (parent / ".github").is_dir():
|
|
583
|
+
repo_root = parent
|
|
572
584
|
break
|
|
573
|
-
# Global fallback
|
|
574
|
-
candidates.append(Path.home() / ".github" / "copilot-instructions.md")
|
|
575
585
|
|
|
576
|
-
|
|
577
|
-
|
|
586
|
+
# (repo path, global fallback, label)
|
|
587
|
+
targets = [
|
|
588
|
+
(repo_root / "CLAUDE.md" if repo_root else None, Path.home() / "CLAUDE.md", "CLAUDE.md"),
|
|
589
|
+
(repo_root / ".github" / "copilot-instructions.md" if repo_root else None, Path.home() / ".github" / "copilot-instructions.md", "Copilot instructions"),
|
|
590
|
+
(repo_root / "AGENTS.md" if repo_root else None, Path.home() / ".codex" / "instructions.md", "AGENTS.md"),
|
|
591
|
+
(repo_root / ".cursorrules" if repo_root else None, Path.home() / ".cursor" / "rules" / "conduct.md", ".cursorrules"),
|
|
592
|
+
(repo_root / ".windsurfrules" if repo_root else None, Path.home() / ".windsurf"/ "rules" / "conduct.md", ".windsurfrules"),
|
|
593
|
+
]
|
|
594
|
+
pattern = _re.compile(_re.escape(MARKER_START) + r".*?" + _re.escape(MARKER_END), _re.DOTALL)
|
|
595
|
+
|
|
596
|
+
for repo_path, global_path, label in targets:
|
|
597
|
+
path = repo_path if (repo_path and repo_path.exists()) else global_path
|
|
598
|
+
if not path.exists() and repo_path:
|
|
599
|
+
path = repo_path
|
|
578
600
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
579
601
|
existing = path.read_text() if path.exists() else ""
|
|
580
|
-
if
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
# No .github/ dir found anywhere — write global
|
|
592
|
-
global_path = Path.home() / ".github" / "copilot-instructions.md"
|
|
593
|
-
global_path.parent.mkdir(parents=True, exist_ok=True)
|
|
594
|
-
global_path.write_text(GUARD_BLOCK)
|
|
595
|
-
print(f" {GREEN}Copilot instructions written ({global_path}){RESET}")
|
|
596
|
-
|
|
602
|
+
if MARKER_START in existing:
|
|
603
|
+
updated = pattern.sub(guard_block, existing)
|
|
604
|
+
if updated == existing:
|
|
605
|
+
print(f" {GRAY}{label} already up to date{RESET}")
|
|
606
|
+
else:
|
|
607
|
+
path.write_text(updated)
|
|
608
|
+
print(f" {GREEN}{label} updated{RESET}")
|
|
609
|
+
else:
|
|
610
|
+
sep = "\n\n" if existing.strip() else ""
|
|
611
|
+
path.write_text((existing.rstrip() + sep + guard_block + "\n").lstrip())
|
|
612
|
+
print(f" {GREEN}{label} — ConductGuard block added{RESET}")
|
|
597
613
|
|
|
598
614
|
def _install_codex_hook(hook_path: Path) -> None:
|
|
599
615
|
"""Register PreToolUse and PostToolUse hooks in ~/.codex/hooks.json."""
|
|
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
|