conduct-cli 0.4.71__tar.gz → 0.4.72__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.4.71 → conduct_cli-0.4.72}/PKG-INFO +2 -2
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/pyproject.toml +2 -2
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/guard.py +139 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli.egg-info/PKG-INFO +2 -2
- conduct_cli-0.4.72/src/conduct_cli.egg-info/requires.txt +3 -0
- conduct_cli-0.4.71/src/conduct_cli.egg-info/requires.txt +0 -3
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/README.md +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/setup.cfg +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/setup.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/hook_precompact_template.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/hook_session_start_template.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/hook_template.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/main.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/tests/test_hook_syntax.py +0 -0
- {conduct_cli-0.4.71 → conduct_cli-0.4.72}/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.
|
|
3
|
+
Version: 0.4.72
|
|
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,7 +22,7 @@ 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.
|
|
25
|
+
Requires-Dist: agent-booster[watch]>=0.2.23
|
|
26
26
|
|
|
27
27
|
# conduct-cli
|
|
28
28
|
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "conduct-cli"
|
|
7
|
-
version = "0.4.
|
|
7
|
+
version = "0.4.72"
|
|
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", "agent-booster[watch]>=0.2.
|
|
26
|
+
dependencies = ["pyyaml>=6.0", "rich>=13.0", "agent-booster[watch]>=0.2.23"]
|
|
27
27
|
|
|
28
28
|
[project.urls]
|
|
29
29
|
Homepage = "https://conductai.ai"
|
|
@@ -740,6 +740,9 @@ def cmd_guard_sync(args):
|
|
|
740
740
|
pass
|
|
741
741
|
print(f" {GREEN}Hook script updated{RESET}")
|
|
742
742
|
|
|
743
|
+
# Auto-init Agent Booster if installed but not yet set up in this project
|
|
744
|
+
_ensure_booster(Path.cwd())
|
|
745
|
+
|
|
743
746
|
# Capture savings from RTK and Agent Booster
|
|
744
747
|
_report_savings(cfg, base_url, api_key)
|
|
745
748
|
|
|
@@ -752,6 +755,48 @@ def cmd_guard_sync(args):
|
|
|
752
755
|
print(f"\n{BOLD}Policy refreshed ({rule_count} rule(s)).{RESET}")
|
|
753
756
|
|
|
754
757
|
|
|
758
|
+
def _ensure_booster(root: Path) -> None:
|
|
759
|
+
"""Auto-init and background-index booster if installed but not yet set up."""
|
|
760
|
+
import shutil
|
|
761
|
+
import subprocess
|
|
762
|
+
|
|
763
|
+
if not shutil.which("booster"):
|
|
764
|
+
return # not installed — conduct-cli 0.4.71+ installs it, but may not be on PATH yet
|
|
765
|
+
|
|
766
|
+
db_path = root / ".booster" / "symbols.db"
|
|
767
|
+
hooks_path = root / ".claude" / "hooks" / "booster-gate.py"
|
|
768
|
+
|
|
769
|
+
# Init (writes hook scripts + wires settings.json) — fast, idempotent
|
|
770
|
+
if not hooks_path.exists():
|
|
771
|
+
try:
|
|
772
|
+
subprocess.run(["booster", "init", "--yes"], capture_output=True, timeout=15, cwd=str(root))
|
|
773
|
+
print(f" {GREEN}Agent Booster:{RESET} hooks installed")
|
|
774
|
+
except Exception:
|
|
775
|
+
return
|
|
776
|
+
|
|
777
|
+
# Index in background — may take 10-60s on large repos, never blocks sync
|
|
778
|
+
if not db_path.exists():
|
|
779
|
+
try:
|
|
780
|
+
subprocess.Popen(
|
|
781
|
+
["booster", "index", "--embed"],
|
|
782
|
+
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL,
|
|
783
|
+
cwd=str(root),
|
|
784
|
+
)
|
|
785
|
+
print(f" {GREEN}Agent Booster:{RESET} indexing in background (Read/Grep intercept active shortly)")
|
|
786
|
+
except Exception:
|
|
787
|
+
pass
|
|
788
|
+
else:
|
|
789
|
+
symbols_count = 0
|
|
790
|
+
try:
|
|
791
|
+
import sqlite3
|
|
792
|
+
conn = sqlite3.connect(str(db_path))
|
|
793
|
+
symbols_count = conn.execute("SELECT COUNT(*) FROM symbols").fetchone()[0]
|
|
794
|
+
conn.close()
|
|
795
|
+
except Exception:
|
|
796
|
+
pass
|
|
797
|
+
print(f" {GREEN}Agent Booster:{RESET} {symbols_count} symbols indexed — Read/Grep intercept active")
|
|
798
|
+
|
|
799
|
+
|
|
755
800
|
def _report_savings(cfg: dict, base_url: str, api_key: str) -> None:
|
|
756
801
|
import subprocess
|
|
757
802
|
|
|
@@ -1054,9 +1099,101 @@ def register_guard_parser(sub):
|
|
|
1054
1099
|
help="Time window: 1h, 24h, 7d, 30d (default: 24h)",
|
|
1055
1100
|
)
|
|
1056
1101
|
|
|
1102
|
+
# conduct guard booster-status
|
|
1103
|
+
guard_sub.add_parser("booster-status", help="Verify Agent Booster intercept is active for this project")
|
|
1104
|
+
|
|
1057
1105
|
return guard_p, guard_sub
|
|
1058
1106
|
|
|
1059
1107
|
|
|
1108
|
+
def cmd_guard_booster_status(args):
|
|
1109
|
+
"""Show whether booster is intercepting Read/Grep in this project."""
|
|
1110
|
+
import shutil, sqlite3, subprocess
|
|
1111
|
+
|
|
1112
|
+
root = Path.cwd()
|
|
1113
|
+
db_path = root / ".booster" / "symbols.db"
|
|
1114
|
+
hooks_path = root / ".claude" / "hooks" / "booster-gate.py"
|
|
1115
|
+
settings_p = root / ".claude" / "settings.json"
|
|
1116
|
+
|
|
1117
|
+
booster_bin = shutil.which("booster")
|
|
1118
|
+
print(f"\n{BOLD}Agent Booster intercept status — {root.name}{RESET}\n")
|
|
1119
|
+
|
|
1120
|
+
# 1. Binary
|
|
1121
|
+
if booster_bin:
|
|
1122
|
+
print(f" {GREEN}✓{RESET} booster installed ({booster_bin})")
|
|
1123
|
+
else:
|
|
1124
|
+
print(f" {RED}✗{RESET} booster not found on PATH — run: pip install agent-booster")
|
|
1125
|
+
return
|
|
1126
|
+
|
|
1127
|
+
# 2. Hook scripts written
|
|
1128
|
+
if hooks_path.exists():
|
|
1129
|
+
print(f" {GREEN}✓{RESET} hook scripts present (.claude/hooks/booster-gate.py)")
|
|
1130
|
+
else:
|
|
1131
|
+
print(f" {RED}✗{RESET} hook scripts missing — run: conduct guard sync")
|
|
1132
|
+
|
|
1133
|
+
# 3. Wired in settings.json
|
|
1134
|
+
wired = False
|
|
1135
|
+
if settings_p.exists():
|
|
1136
|
+
import json as _json
|
|
1137
|
+
s = _json.loads(settings_p.read_text())
|
|
1138
|
+
for h in s.get("hooks", {}).get("PreToolUse", []):
|
|
1139
|
+
if h.get("matcher") == "Read":
|
|
1140
|
+
wired = True
|
|
1141
|
+
break
|
|
1142
|
+
if wired:
|
|
1143
|
+
print(f" {GREEN}✓{RESET} Read hook wired in .claude/settings.json")
|
|
1144
|
+
else:
|
|
1145
|
+
print(f" {RED}✗{RESET} Read hook NOT in .claude/settings.json — run: conduct guard sync")
|
|
1146
|
+
|
|
1147
|
+
# 4. Index
|
|
1148
|
+
if db_path.exists():
|
|
1149
|
+
try:
|
|
1150
|
+
conn = sqlite3.connect(str(db_path))
|
|
1151
|
+
count = conn.execute("SELECT COUNT(*) FROM symbols").fetchone()[0]
|
|
1152
|
+
files = conn.execute("SELECT COUNT(DISTINCT file) FROM symbols").fetchone()[0]
|
|
1153
|
+
conn.close()
|
|
1154
|
+
print(f" {GREEN}✓{RESET} symbols.db — {count} symbols across {files} files")
|
|
1155
|
+
except Exception:
|
|
1156
|
+
print(f" {YELLOW}?{RESET} symbols.db exists but could not be read")
|
|
1157
|
+
else:
|
|
1158
|
+
print(f" {RED}✗{RESET} symbols.db missing — Read calls fall through unintercepted")
|
|
1159
|
+
print(f" run: booster index --embed (or: conduct guard sync to trigger it)")
|
|
1160
|
+
return
|
|
1161
|
+
|
|
1162
|
+
# 5. Live intercept test — try reading a known file and check if smart-read fires
|
|
1163
|
+
print(f"\n {BOLD}Live intercept test:{RESET}")
|
|
1164
|
+
try:
|
|
1165
|
+
import tempfile, json as _json
|
|
1166
|
+
# Pick the first indexed file
|
|
1167
|
+
conn = sqlite3.connect(str(db_path))
|
|
1168
|
+
row = conn.execute("SELECT file FROM symbols LIMIT 1").fetchone()
|
|
1169
|
+
conn.close()
|
|
1170
|
+
if row:
|
|
1171
|
+
# Prefer a .py/.ts file — more likely to have symbols and trigger smart-read
|
|
1172
|
+
conn = sqlite3.connect(str(db_path))
|
|
1173
|
+
src = conn.execute(
|
|
1174
|
+
"SELECT file FROM symbols WHERE file LIKE '%.py' OR file LIKE '%.ts' LIMIT 1"
|
|
1175
|
+
).fetchone() or row
|
|
1176
|
+
conn.close()
|
|
1177
|
+
test_file = str(root / src[0])
|
|
1178
|
+
payload = _json.dumps({"tool_name": "Read", "tool_input": {"file_path": test_file}})
|
|
1179
|
+
r = subprocess.run(
|
|
1180
|
+
["python3", str(hooks_path)],
|
|
1181
|
+
input=payload, capture_output=True, text=True, timeout=10,
|
|
1182
|
+
)
|
|
1183
|
+
if r.returncode == 2:
|
|
1184
|
+
lines = r.stdout.strip().splitlines()
|
|
1185
|
+
print(f" {GREEN}✓{RESET} Read intercepted → smart-read fired ({len(lines)} lines returned)")
|
|
1186
|
+
print(f" tested on: {row[0]}")
|
|
1187
|
+
elif r.returncode == 0:
|
|
1188
|
+
print(f" {YELLOW}~{RESET} Hook ran but passed through (file may not have symbols)")
|
|
1189
|
+
else:
|
|
1190
|
+
print(f" {RED}✗{RESET} Hook errored (exit {r.returncode})")
|
|
1191
|
+
except Exception as e:
|
|
1192
|
+
print(f" {YELLOW}?{RESET} Could not run live test: {e}")
|
|
1193
|
+
|
|
1194
|
+
print()
|
|
1195
|
+
|
|
1196
|
+
|
|
1060
1197
|
def dispatch_guard(args, guard_p):
|
|
1061
1198
|
"""Dispatch to the correct guard handler. Called from main()."""
|
|
1062
1199
|
guard_command = getattr(args, "guard_command", None)
|
|
@@ -1068,6 +1205,8 @@ def dispatch_guard(args, guard_p):
|
|
|
1068
1205
|
cmd_guard_savings(args)
|
|
1069
1206
|
elif guard_command == "audit":
|
|
1070
1207
|
cmd_guard_audit(args)
|
|
1208
|
+
elif guard_command == "booster-status":
|
|
1209
|
+
cmd_guard_booster_status(args)
|
|
1071
1210
|
else:
|
|
1072
1211
|
guard_p.print_help()
|
|
1073
1212
|
sys.exit(1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: conduct-cli
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.72
|
|
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,7 +22,7 @@ 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.
|
|
25
|
+
Requires-Dist: agent-booster[watch]>=0.2.23
|
|
26
26
|
|
|
27
27
|
# conduct-cli
|
|
28
28
|
|
|
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
|