conduct-cli 0.7.4__tar.gz → 0.7.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 (40) hide show
  1. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/PKG-INFO +1 -1
  2. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/pyproject.toml +1 -1
  3. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/guard.py +8 -2
  4. conduct_cli-0.7.6/src/conduct_cli/hooks/session_report_push.py +137 -0
  5. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/stop.py +17 -0
  6. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  7. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/SOURCES.txt +1 -0
  8. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/README.md +0 -0
  9. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/setup.cfg +0 -0
  10. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/setup.py +0 -0
  11. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/__init__.py +0 -0
  12. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/api.py +0 -0
  13. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/guardmcp.py +0 -0
  14. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/__init__.py +0 -0
  15. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/base.py +0 -0
  16. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/posttooluse.py +0 -0
  17. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/precompact.py +0 -0
  18. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/pretooluse.py +0 -0
  19. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/session_parser.py +0 -0
  20. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/hooks/session_start.py +0 -0
  21. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/log_util.py +0 -0
  22. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/main.py +0 -0
  23. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/mcp_server.py +0 -0
  24. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/memory.py +0 -0
  25. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli/paxel.py +0 -0
  26. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  27. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  28. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/requires.txt +0 -0
  29. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/src/conduct_cli.egg-info/top_level.txt +0 -0
  30. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_advisory_and_hook.py +0 -0
  31. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_bash_operator_signature.py +0 -0
  32. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_command_word_matcher.py +0 -0
  33. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_guard_policy.py +0 -0
  34. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_guard_savings.py +0 -0
  35. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_hook_syntax.py +0 -0
  36. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_journal_drain.py +0 -0
  37. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_log_util.py +0 -0
  38. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_proxy_env.py +0 -0
  39. {conduct_cli-0.7.4 → conduct_cli-0.7.6}/tests/test_signed_policy.py +0 -0
  40. {conduct_cli-0.7.4 → conduct_cli-0.7.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.7.4
3
+ Version: 0.7.6
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.4"
7
+ version = "0.7.6"
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" }
@@ -33,8 +33,14 @@ def active_policy_path() -> Path:
33
33
  _THIN_LAUNCHERS = {
34
34
  "pretooluse": (
35
35
  "#!/usr/bin/env python3\n"
36
- "try:\n from conduct_cli.hooks.pretooluse import main; main()\n"
37
- "except (ImportError, ModuleNotFoundError): import sys; sys.exit(0)\n"
36
+ "import sys\n"
37
+ "_cmd = sys.argv[1] if len(sys.argv) > 1 else ''\n"
38
+ "if _cmd == 'post':\n"
39
+ " try:\n from conduct_cli.hooks.posttooluse import main; main()\n"
40
+ " except (ImportError, ModuleNotFoundError): sys.exit(0)\n"
41
+ "else:\n"
42
+ " try:\n from conduct_cli.hooks.pretooluse import main; main()\n"
43
+ " except (ImportError, ModuleNotFoundError): sys.exit(0)\n"
38
44
  ),
39
45
  "posttooluse": (
40
46
  "#!/usr/bin/env python3\n"
@@ -0,0 +1,137 @@
1
+ """Standalone background job: run paxel on the current session and push results
2
+ to /guard/session-reports. Invoked as a detached subprocess from the Stop hook
3
+ so it never blocks session close.
4
+
5
+ Usage (internal):
6
+ python -m conduct_cli.hooks.session_report_push <session_id>
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import getpass
11
+ import json
12
+ import shutil
13
+ import subprocess
14
+ import sys
15
+ import tempfile
16
+ import urllib.request
17
+ from pathlib import Path
18
+
19
+
20
+ def _load_config() -> dict:
21
+ for p in [
22
+ Path.home() / ".conductguard" / "config.json",
23
+ Path.home() / ".conduct" / "config.json",
24
+ ]:
25
+ if p.exists():
26
+ try:
27
+ return json.loads(p.read_text())
28
+ except Exception:
29
+ pass
30
+ return {}
31
+
32
+
33
+ def run(session_id: str = "") -> None:
34
+ cfg = _load_config()
35
+ server = (cfg.get("api_url") or cfg.get("server") or "https://api.conductai.ai").rstrip("/")
36
+ api_key = cfg.get("api_key", "")
37
+ member_token = cfg.get("member_token", "")
38
+ workspace_id = cfg.get("workspace_id", "")
39
+
40
+ if not server or not workspace_id:
41
+ return
42
+
43
+ bundled = Path(__file__).parent.parent / "paxel.py"
44
+ if not bundled.exists():
45
+ return
46
+
47
+ tmpdir = Path(tempfile.mkdtemp(prefix="conduct-paxel-bg-"))
48
+ try:
49
+ paxel_script = tmpdir / "paxel.py"
50
+ shutil.copy(bundled, paxel_script)
51
+
52
+ result = subprocess.run(
53
+ [sys.executable, str(paxel_script), "--no-open"],
54
+ cwd=str(tmpdir),
55
+ capture_output=True,
56
+ text=True,
57
+ timeout=120,
58
+ )
59
+
60
+ stats_path = tmpdir / "stats.json"
61
+ report_path = tmpdir / "report.md"
62
+
63
+ if not stats_path.exists():
64
+ return
65
+
66
+ stats = json.loads(stats_path.read_text())
67
+ report_md = report_path.read_text() if report_path.exists() else ""
68
+
69
+ volume = stats.get("volume", {})
70
+ behavior = stats.get("behavior", {})
71
+ autonomy = stats.get("autonomy", {})
72
+ tools = stats.get("tools", {})
73
+ velocity = stats.get("velocity", {})
74
+
75
+ sessions = volume.get("total_sessions", 0)
76
+ prompts = volume.get("total_prompts", 0)
77
+ active_days = volume.get("active_days", 0)
78
+ autonomy_score = autonomy.get("autonomy_score_0_100")
79
+ planning_ratio = behavior.get("planning_ratio_explore_to_doing", 0)
80
+ commits = velocity.get("git_commits_real") if isinstance(velocity, dict) else None
81
+ lines_per_hr = velocity.get("git_lines_per_active_hour") if isinstance(velocity, dict) else None
82
+ top_tools = dict(tools.get("top_tools") or [])
83
+
84
+ # Skip push for trivial sessions (< 3 prompts — not worth reporting)
85
+ if int(prompts or 0) < 3:
86
+ return
87
+
88
+ if autonomy_score is not None and float(autonomy_score) >= 70:
89
+ archetype = "Autonomous Builder"
90
+ elif planning_ratio and float(planning_ratio) > 1.0:
91
+ archetype = "Strategic Planner"
92
+ else:
93
+ archetype = "Execution-Focused Builder"
94
+
95
+ email = cfg.get("user_email") or getpass.getuser()
96
+
97
+ payload = json.dumps({
98
+ "developer_email": email,
99
+ "archetype": archetype,
100
+ "autonomy_score": float(autonomy_score) if autonomy_score is not None else None,
101
+ "planning_ratio": float(planning_ratio) if planning_ratio else None,
102
+ "sessions": int(sessions or 0),
103
+ "prompts": int(prompts or 0),
104
+ "commits": int(commits) if commits is not None else 0,
105
+ "lines_per_hour": float(lines_per_hr) if lines_per_hr is not None else None,
106
+ "active_days": int(active_days or 0),
107
+ "tools_json": top_tools,
108
+ "report_md": report_md[:50000] if report_md else None,
109
+ "source": "auto",
110
+ }).encode()
111
+
112
+ headers: dict[str, str] = {
113
+ "Content-Type": "application/json",
114
+ "X-Workspace-ID": workspace_id,
115
+ }
116
+ if member_token:
117
+ headers["Authorization"] = f"Bearer {member_token}"
118
+ elif api_key:
119
+ headers["X-API-Key"] = api_key
120
+
121
+ req = urllib.request.Request(
122
+ f"{server}/guard/session-reports",
123
+ data=payload,
124
+ headers=headers,
125
+ method="POST",
126
+ )
127
+ urllib.request.urlopen(req, timeout=15)
128
+
129
+ except Exception:
130
+ pass
131
+ finally:
132
+ shutil.rmtree(str(tmpdir), ignore_errors=True)
133
+
134
+
135
+ if __name__ == "__main__":
136
+ session_id = sys.argv[1] if len(sys.argv) > 1 else ""
137
+ run(session_id)
@@ -58,6 +58,20 @@ def _post_intent(session_id: str, ai_tool: str, cwd: str | None) -> None:
58
58
  print("Guard: session parse failed", file=sys.stderr)
59
59
 
60
60
 
61
+ def _spawn_session_report(session_id: str) -> None:
62
+ """Fire paxel analysis + Guard push in a detached subprocess. Never blocks."""
63
+ try:
64
+ import subprocess
65
+ subprocess.Popen(
66
+ [sys.executable, "-m", "conduct_cli.hooks.session_report_push", session_id],
67
+ start_new_session=True,
68
+ stdout=subprocess.DEVNULL,
69
+ stderr=subprocess.DEVNULL,
70
+ )
71
+ except Exception:
72
+ pass
73
+
74
+
61
75
  def main() -> None:
62
76
  try:
63
77
  raw = sys.stdin.read()
@@ -81,6 +95,9 @@ def main() -> None:
81
95
  except Exception:
82
96
  pass
83
97
 
98
+ # Fire paxel analysis in background — non-blocking, posts to /guard/session-reports
99
+ _spawn_session_report(session_id)
100
+
84
101
  sys.exit(0)
85
102
 
86
103
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.7.4
3
+ Version: 0.7.6
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
@@ -22,6 +22,7 @@ src/conduct_cli/hooks/posttooluse.py
22
22
  src/conduct_cli/hooks/precompact.py
23
23
  src/conduct_cli/hooks/pretooluse.py
24
24
  src/conduct_cli/hooks/session_parser.py
25
+ src/conduct_cli/hooks/session_report_push.py
25
26
  src/conduct_cli/hooks/session_start.py
26
27
  src/conduct_cli/hooks/stop.py
27
28
  tests/test_advisory_and_hook.py
File without changes
File without changes
File without changes