conduct-cli 0.7.40__tar.gz → 0.7.42__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.40 → conduct_cli-0.7.42}/PKG-INFO +1 -1
  2. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/pyproject.toml +1 -1
  3. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/guard.py +64 -1
  4. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/session_start.py +29 -0
  5. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  6. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/README.md +0 -0
  7. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/setup.cfg +0 -0
  8. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/setup.py +0 -0
  9. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/__init__.py +0 -0
  10. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/api.py +0 -0
  11. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/guardmcp.py +0 -0
  12. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/__init__.py +0 -0
  13. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/base.py +0 -0
  14. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/posttooluse.py +0 -0
  15. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/precompact.py +0 -0
  16. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/pretooluse.py +0 -0
  17. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/session_parser.py +0 -0
  18. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/session_report_push.py +0 -0
  19. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/hooks/stop.py +0 -0
  20. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/log_util.py +0 -0
  21. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/main.py +0 -0
  22. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/mcp_server.py +0 -0
  23. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/memory.py +0 -0
  24. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/paxel.py +0 -0
  25. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli/tool_groups.py +0 -0
  26. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
  27. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  28. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  29. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/requires.txt +0 -0
  30. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/src/conduct_cli.egg-info/top_level.txt +0 -0
  31. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_advisory_and_hook.py +0 -0
  32. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_api_client.py +0 -0
  33. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_bash_operator_signature.py +0 -0
  34. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_blast_radius.py +0 -0
  35. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_cmd_run.py +0 -0
  36. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_command_word_matcher.py +0 -0
  37. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_drain_daemon_health.py +0 -0
  38. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_guard_policy.py +0 -0
  39. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_guard_savings.py +0 -0
  40. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_hook_dispatch.py +0 -0
  41. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_journal_drain.py +0 -0
  42. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_log_util.py +0 -0
  43. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_proxy_coverage.py +0 -0
  44. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_proxy_env.py +0 -0
  45. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_signed_policy.py +0 -0
  46. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/tests/test_switch.py +0 -0
  47. {conduct_cli-0.7.40 → conduct_cli-0.7.42}/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.40
3
+ Version: 0.7.42
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.40"
7
+ version = "0.7.42"
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,6 +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_copilot_instructions()
444
445
 
445
446
 
446
447
  def _patch_claude_desktop_proxy(api_url: str, agent_token: str) -> None:
@@ -540,6 +541,60 @@ def _patch_cursor_global_rules() -> None:
540
541
  print(f" {GREEN}Cursor global rules updated with ConductGuard policy{RESET}")
541
542
 
542
543
 
544
+ def _patch_copilot_instructions() -> None:
545
+ """Write ConductGuard enforcement instructions into .github/copilot-instructions.md.
546
+
547
+ Copilot injects this file into every conversation automatically — soft hooks
548
+ for tools that don't support PreToolUse/PostToolUse interception.
549
+ Written to the current repo root if a .github/ dir exists, and to ~/
550
+ as a global fallback.
551
+ """
552
+ GUARD_BLOCK = (
553
+ "<!-- ConductGuard — managed by conduct guard sync, do not edit this block -->\n"
554
+ "## ConductGuard Policy\n"
555
+ "Before executing any shell command, file write, network request, API call, or code change:\n"
556
+ "- Call `guard_check` (conductguard MCP tool) with the action and parameters.\n"
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
+ )
564
+
565
+ candidates: list[Path] = []
566
+ # Repo-level: walk up from cwd to find .github/
567
+ cwd = Path.cwd()
568
+ for parent in [cwd, *cwd.parents]:
569
+ gh = parent / ".github"
570
+ if gh.is_dir():
571
+ candidates.append(gh / "copilot-instructions.md")
572
+ break
573
+ # Global fallback
574
+ candidates.append(Path.home() / ".github" / "copilot-instructions.md")
575
+
576
+ wrote_any = False
577
+ for path in candidates:
578
+ path.parent.mkdir(parents=True, exist_ok=True)
579
+ existing = path.read_text() if path.exists() else ""
580
+ if "<!-- ConductGuard" in existing:
581
+ print(f" {GRAY}Copilot instructions already contain ConductGuard policy ({path}){RESET}")
582
+ wrote_any = True
583
+ continue
584
+ updated = (existing.rstrip() + "\n\n" + GUARD_BLOCK).lstrip()
585
+ path.write_text(updated)
586
+ print(f" {GREEN}Copilot instructions updated with ConductGuard policy ({path}){RESET}")
587
+ wrote_any = True
588
+ break # repo-level wins; skip global if repo found
589
+
590
+ if not wrote_any:
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
+
597
+
543
598
  def _install_codex_hook(hook_path: Path) -> None:
544
599
  """Register PreToolUse and PostToolUse hooks in ~/.codex/hooks.json."""
545
600
  codex_hooks = Path.home() / ".codex" / "hooks.json"
@@ -1321,6 +1376,14 @@ def cmd_guard_sync(args):
1321
1376
  except Exception as e:
1322
1377
  print(f" {YELLOW}Warning: could not refresh token ({e}) — using cached value{RESET}")
1323
1378
 
1379
+ # Fetch team instructions version so session-start hook can detect staleness
1380
+ try:
1381
+ ver_resp = _req("GET", f"{base_url}/team-os/instructions/version", token=_token)
1382
+ if ver_resp.get("version"):
1383
+ _save_guard_config({"instructions_version": ver_resp["version"]})
1384
+ except Exception:
1385
+ pass # non-fatal
1386
+
1324
1387
  # Write LLM proxy env vars so any AI tool (Claude Code, Cursor, Codex, …)
1325
1388
  # routes through Conduct Guard. Customer-overridable via --proxy-url or
1326
1389
  # CONDUCT_PROXY_URL env var; otherwise fetched from server (workspace_config).
@@ -1351,7 +1414,7 @@ def cmd_guard_sync(args):
1351
1414
  for t in _tools:
1352
1415
  routed = "✓ Routed" if t.get("proxy_routed") else ("— Partial" if t["name"] == "codex-desktop" else "✗ Not routed")
1353
1416
  mcp = "✓" if t.get("mcp_registered") else "✗"
1354
- hook = "✓" if t.get("hook_registered") else "—"
1417
+ hook = "✓" if t.get("hook_registered") else ("~ soft" if t["name"] in ("copilot", "cursor") else "—")
1355
1418
  print(f" {t['name']:<20} {routed:<16} {mcp:<8} {hook}")
1356
1419
  print()
1357
1420
 
@@ -57,6 +57,34 @@ def _check_proxy_token() -> None:
57
57
  pass
58
58
 
59
59
 
60
+ def _check_instructions_staleness() -> None:
61
+ """Warn if team AI instructions have been updated since last sync.
62
+
63
+ Reads local instructions_version from config, hits GET /team-os/instructions/version,
64
+ prints one-line warning if stale. Timeout 2s — never blocks the session.
65
+ """
66
+ try:
67
+ import urllib.request as _req
68
+ cfg = load_config()
69
+ api_url = cfg.get("api_url", "https://api.conductai.ai").rstrip("/")
70
+ agent_token = cfg.get("agent_token", "")
71
+ local_version = cfg.get("instructions_version")
72
+ if not agent_token or not local_version:
73
+ return # not synced yet — nothing to compare
74
+
75
+ request = _req.Request(
76
+ f"{api_url}/team-os/instructions/version",
77
+ headers={"Authorization": f"Bearer {agent_token}"},
78
+ )
79
+ with _req.urlopen(request, timeout=2) as resp:
80
+ data = json.loads(resp.read())
81
+ remote_version = data.get("version")
82
+ if remote_version and remote_version != local_version:
83
+ print(f"⚠ Team AI instructions updated ({local_version} → {remote_version}) — run `conduct guard sync` to pull latest\n")
84
+ except Exception:
85
+ pass # always silent — session-start must never fail
86
+
87
+
60
88
  def _backfill_prev_session(current_session_id: str, cwd: str | None) -> None:
61
89
  """Parse + PATCH the most recent completed session (the one that just ended).
62
90
 
@@ -155,6 +183,7 @@ def main() -> None:
155
183
  pass
156
184
 
157
185
  _check_proxy_token()
186
+ _check_instructions_staleness()
158
187
 
159
188
  if not SNAPSHOT_PATH.exists():
160
189
  sys.exit(0)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.7.40
3
+ Version: 0.7.42
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