conduct-cli 0.4.70__tar.gz → 0.4.71__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 (26) hide show
  1. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/PKG-INFO +4 -1
  2. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/README.md +2 -0
  3. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/pyproject.toml +2 -2
  4. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/hook_session_start_template.py +23 -0
  5. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/hook_template.py +24 -0
  6. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/main.py +32 -0
  7. conduct_cli-0.4.71/src/conduct_cli/memory.py +96 -0
  8. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli.egg-info/PKG-INFO +4 -1
  9. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli.egg-info/SOURCES.txt +1 -0
  10. conduct_cli-0.4.71/src/conduct_cli.egg-info/requires.txt +3 -0
  11. conduct_cli-0.4.70/src/conduct_cli.egg-info/requires.txt +0 -2
  12. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/setup.cfg +0 -0
  13. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/setup.py +0 -0
  14. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/__init__.py +0 -0
  15. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/api.py +0 -0
  16. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/guard.py +0 -0
  17. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/guardmcp.py +0 -0
  18. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/hook_precompact_template.py +0 -0
  19. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli/mcp_server.py +0 -0
  20. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  21. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  22. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/src/conduct_cli.egg-info/top_level.txt +0 -0
  23. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/tests/test_guard_policy.py +0 -0
  24. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/tests/test_guard_savings.py +0 -0
  25. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/tests/test_hook_syntax.py +0 -0
  26. {conduct_cli-0.4.70 → conduct_cli-0.4.71}/tests/test_switch.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.70
3
+ Version: 0.4.71
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
@@ -22,11 +22,14 @@ Requires-Python: >=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: pyyaml>=6.0
24
24
  Requires-Dist: rich>=13.0
25
+ Requires-Dist: agent-booster[watch]>=0.2.22
25
26
 
26
27
  # conduct-cli
27
28
 
28
29
  Official CLI for [Conduct AI](https://conductai.ai) — install AI agents, manage projects, run end-to-end tests, and enforce team AI policies with ConductGuard.
29
30
 
31
+ ![Conduct CLI demo — whoami, switch workspaces with Guard policy sync, and run an agent](assets/conduct-cli-demo.gif)
32
+
30
33
  ## Install
31
34
 
32
35
  ```bash
@@ -2,6 +2,8 @@
2
2
 
3
3
  Official CLI for [Conduct AI](https://conductai.ai) — install AI agents, manage projects, run end-to-end tests, and enforce team AI policies with ConductGuard.
4
4
 
5
+ ![Conduct CLI demo — whoami, switch workspaces with Guard policy sync, and run an agent](assets/conduct-cli-demo.gif)
6
+
5
7
  ## Install
6
8
 
7
9
  ```bash
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "conduct-cli"
7
- version = "0.4.70"
7
+ version = "0.4.71"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -23,7 +23,7 @@ classifiers = [
23
23
  "Programming Language :: Python :: 3.12",
24
24
  "Topic :: Software Development :: Libraries :: Application Frameworks",
25
25
  ]
26
- dependencies = ["pyyaml>=6.0", "rich>=13.0"]
26
+ dependencies = ["pyyaml>=6.0", "rich>=13.0", "agent-booster[watch]>=0.2.22"]
27
27
 
28
28
  [project.urls]
29
29
  Homepage = "https://conductai.ai"
@@ -46,6 +46,29 @@ def main():
46
46
  else:
47
47
  lines.append("- Memory index:\n (none)")
48
48
 
49
+ # Inject relevant team memories for the current repo
50
+ try:
51
+ repo = None
52
+ try:
53
+ import subprocess
54
+ out = subprocess.check_output(["git", "remote", "get-url", "origin"],
55
+ stderr=subprocess.DEVNULL, text=True).strip()
56
+ if "github.com" in out:
57
+ repo = out.split("github.com")[-1].lstrip("/:").rstrip(".git")
58
+ except Exception:
59
+ pass
60
+
61
+ from conduct_cli.memory import search_team_memory
62
+ results = search_team_memory("recent learnings patterns bugs", repo=repo, limit=3)
63
+ if results:
64
+ lines.append("- Team knowledge:")
65
+ for r in results[:3]:
66
+ dev = r.get("developer_id", "teammate")[:8]
67
+ summary = r.get("summary", "")[:120]
68
+ lines.append(f" {dev}: {summary}")
69
+ except Exception:
70
+ pass
71
+
49
72
  print("\n".join(lines))
50
73
  except Exception:
51
74
  pass
@@ -131,6 +131,20 @@ except Exception:
131
131
  return None, "allow", None, None
132
132
 
133
133
 
134
+ def _detect_repo() -> str | None:
135
+ try:
136
+ import subprocess
137
+ out = subprocess.check_output(["git", "remote", "get-url", "origin"],
138
+ stderr=subprocess.DEVNULL, text=True).strip()
139
+ # github.com/owner/repo or git@github.com:owner/repo
140
+ if "github.com" in out:
141
+ parts = out.split("github.com")[-1].lstrip("/:").rstrip(".git")
142
+ return parts # owner/repo
143
+ except Exception:
144
+ pass
145
+ return None
146
+
147
+
134
148
  def _detect_ai_tool():
135
149
  import os
136
150
  if os.environ.get("CLAUDECODE") or os.environ.get("CLAUDE_CODE_ENTRYPOINT"):
@@ -535,6 +549,16 @@ def main():
535
549
  except Exception:
536
550
  sys.exit(0)
537
551
 
552
+ # Stop hook — session ended, capture for team memory
553
+ if data.get("hook_event_name") == "Stop" or data.get("stop_hook_active"):
554
+ session_id = data.get("session_id", "")
555
+ transcript_path = data.get("transcript_path")
556
+ # Detect repo from CWD git remote
557
+ repo = _detect_repo()
558
+ from conduct_cli.memory import post_session_to_api
559
+ post_session_to_api(session_id, transcript_path, repo)
560
+ sys.exit(0)
561
+
538
562
  # Policy version check (cached 60s) — auto-syncs if server version differs
539
563
  _maybe_sync_policy()
540
564
 
@@ -2708,6 +2708,28 @@ def cmd_test_guard(args):
2708
2708
  print(f"\n {CYAN}→ View events: {api_url.replace('api.', 'app.')}/guard/activity{RESET}\n")
2709
2709
 
2710
2710
 
2711
+ def cmd_memory(args):
2712
+ from conduct_cli.memory import search_team_memory
2713
+ memory_command = getattr(args, "memory_command", None)
2714
+ if memory_command == "search":
2715
+ query = " ".join(args.query)
2716
+ results = search_team_memory(query, repo=getattr(args, "repo", None), limit=args.limit)
2717
+ if not results:
2718
+ print("No team memories found.")
2719
+ return
2720
+ for r in results:
2721
+ repo = r.get("repo_full_name", "unknown")
2722
+ summary = r.get("summary", "")
2723
+ tags = ", ".join(r.get("topic_tags") or [])
2724
+ created = r.get("created_at", "")[:10]
2725
+ print(f"\n{BOLD}{repo}{RESET} {GRAY}{created}{RESET}")
2726
+ if tags:
2727
+ print(f" Tags: {tags}")
2728
+ print(f" {summary}")
2729
+ else:
2730
+ print("Usage: conduct memory search <query>")
2731
+
2732
+
2711
2733
  # ── Entry point ───────────────────────────────────────────────────────────────
2712
2734
 
2713
2735
  def main():
@@ -2860,6 +2882,14 @@ def main():
2860
2882
  sr_p = sub.add_parser("session-report", help="Analyse local AI coding sessions with paxel and send report to admin")
2861
2883
  sr_p.add_argument("--developer", default=None, help="Developer name (defaults to OS username)")
2862
2884
 
2885
+ # conduct memory
2886
+ memory_p = sub.add_parser("memory", help="Search team session memories")
2887
+ memory_sub = memory_p.add_subparsers(dest="memory_command")
2888
+ mem_search_p = memory_sub.add_parser("search", help="Search team memories")
2889
+ mem_search_p.add_argument("query", nargs="+", help="Search query")
2890
+ mem_search_p.add_argument("--repo", help="Filter by repo (owner/repo)")
2891
+ mem_search_p.add_argument("--limit", type=int, default=5, help="Max results")
2892
+
2863
2893
  args = parser.parse_args()
2864
2894
 
2865
2895
  if args.command == "login":
@@ -2937,6 +2967,8 @@ def main():
2937
2967
  cmd_test_security_verify(args)
2938
2968
  elif args.command == "session-report":
2939
2969
  cmd_session_report(args)
2970
+ elif args.command == "memory":
2971
+ cmd_memory(args)
2940
2972
  else:
2941
2973
  parser.print_help()
2942
2974
 
@@ -0,0 +1,96 @@
1
+ """Team session memory helpers for Conduct CLI."""
2
+ import json
3
+ import threading
4
+ import urllib.request
5
+ from pathlib import Path
6
+
7
+
8
+ def _load_config():
9
+ cfg_path = Path.home() / ".conduct" / "config.json"
10
+ if not cfg_path.exists():
11
+ return None
12
+ try:
13
+ return json.loads(cfg_path.read_text())
14
+ except Exception:
15
+ return None
16
+
17
+
18
+ def post_session_to_api(session_id: str, transcript_path: str | None, repo: str | None) -> bool:
19
+ """Fire-and-forget POST to /team-memory/sessions. Returns True if thread started."""
20
+ cfg = _load_config()
21
+ if not cfg:
22
+ return False
23
+ server = cfg.get("server", "").rstrip("/")
24
+ api_key = cfg.get("api_key", "")
25
+ workspace_id = cfg.get("workspace_id", "")
26
+ if not server or not workspace_id:
27
+ return False
28
+
29
+ raw_transcript = None
30
+ if transcript_path:
31
+ try:
32
+ text = Path(transcript_path).read_text(errors="ignore")
33
+ raw_transcript = text[:12000]
34
+ except Exception:
35
+ pass
36
+
37
+ payload = json.dumps({
38
+ "session_id": session_id,
39
+ "tool": "claude_code",
40
+ "repo_full_name": repo,
41
+ "raw_transcript": raw_transcript,
42
+ "files_touched": [],
43
+ }).encode()
44
+
45
+ def _send():
46
+ try:
47
+ req = urllib.request.Request(
48
+ f"{server}/team-memory/sessions",
49
+ data=payload,
50
+ headers={
51
+ "Content-Type": "application/json",
52
+ "X-API-Key": api_key,
53
+ "X-Workspace-ID": workspace_id,
54
+ },
55
+ method="POST",
56
+ )
57
+ urllib.request.urlopen(req, timeout=10)
58
+ except Exception:
59
+ pass
60
+
61
+ t = threading.Thread(target=_send, daemon=True)
62
+ t.start()
63
+ return True
64
+
65
+
66
+ def search_team_memory(query: str, repo: str | None = None, limit: int = 5) -> list[dict]:
67
+ """Search team session memories. Returns list of result dicts, never raises."""
68
+ cfg = _load_config()
69
+ if not cfg:
70
+ return []
71
+ server = cfg.get("server", "").rstrip("/")
72
+ api_key = cfg.get("api_key", "")
73
+ workspace_id = cfg.get("workspace_id", "")
74
+ if not server or not workspace_id:
75
+ return []
76
+
77
+ try:
78
+ import urllib.parse
79
+ params = {"q": query, "limit": str(limit)}
80
+ if repo:
81
+ params["repo"] = repo
82
+ url = f"{server}/team-memory/search?{urllib.parse.urlencode(params)}"
83
+ req = urllib.request.Request(
84
+ url,
85
+ headers={
86
+ "X-API-Key": api_key,
87
+ "X-Workspace-ID": workspace_id,
88
+ },
89
+ )
90
+ with urllib.request.urlopen(req, timeout=5) as resp:
91
+ data = json.loads(resp.read())
92
+ if isinstance(data, list):
93
+ return data
94
+ return data.get("results", []) if isinstance(data, dict) else []
95
+ except Exception:
96
+ return []
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.70
3
+ Version: 0.4.71
4
4
  Summary: CLI for Conduct AI — install agents, manage projects, run tests
5
5
  Author-email: Conduct AI <hello@conductai.ai>
6
6
  License: MIT
@@ -22,11 +22,14 @@ Requires-Python: >=3.9
22
22
  Description-Content-Type: text/markdown
23
23
  Requires-Dist: pyyaml>=6.0
24
24
  Requires-Dist: rich>=13.0
25
+ Requires-Dist: agent-booster[watch]>=0.2.22
25
26
 
26
27
  # conduct-cli
27
28
 
28
29
  Official CLI for [Conduct AI](https://conductai.ai) — install AI agents, manage projects, run end-to-end tests, and enforce team AI policies with ConductGuard.
29
30
 
31
+ ![Conduct CLI demo — whoami, switch workspaces with Guard policy sync, and run an agent](assets/conduct-cli-demo.gif)
32
+
30
33
  ## Install
31
34
 
32
35
  ```bash
@@ -10,6 +10,7 @@ src/conduct_cli/hook_session_start_template.py
10
10
  src/conduct_cli/hook_template.py
11
11
  src/conduct_cli/main.py
12
12
  src/conduct_cli/mcp_server.py
13
+ src/conduct_cli/memory.py
13
14
  src/conduct_cli.egg-info/PKG-INFO
14
15
  src/conduct_cli.egg-info/SOURCES.txt
15
16
  src/conduct_cli.egg-info/dependency_links.txt
@@ -0,0 +1,3 @@
1
+ pyyaml>=6.0
2
+ rich>=13.0
3
+ agent-booster[watch]>=0.2.22
@@ -1,2 +0,0 @@
1
- pyyaml>=6.0
2
- rich>=13.0
File without changes
File without changes