conduct-cli 0.4.87__tar.gz → 0.4.89__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.87 → conduct_cli-0.4.89}/PKG-INFO +1 -1
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/pyproject.toml +2 -2
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/main.py +84 -81
- conduct_cli-0.4.89/src/conduct_cli/paxel.py +2702 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/PKG-INFO +1 -1
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/SOURCES.txt +1 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/README.md +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/setup.cfg +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/setup.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/__init__.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/api.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/guard.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/guardmcp.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/hook_precompact_template.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/hook_session_start_template.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/hook_stop_template.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/hook_template.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/mcp_server.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli/memory.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/entry_points.txt +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/requires.txt +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/src/conduct_cli.egg-info/top_level.txt +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/tests/test_guard_policy.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/tests/test_guard_savings.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/tests/test_hook_syntax.py +0 -0
- {conduct_cli-0.4.87 → conduct_cli-0.4.89}/tests/test_switch.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.4.
|
|
7
|
+
version = "0.4.89"
|
|
8
8
|
description = "CLI for Conduct AI — install agents, manage projects, run tests"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
@@ -43,4 +43,4 @@ conductguard-post = "conduct_cli.guard:post_usage_main"
|
|
|
43
43
|
where = ["src"]
|
|
44
44
|
|
|
45
45
|
[tool.setuptools.package-data]
|
|
46
|
-
conduct_cli = ["hook_template.py", "hook_precompact_template.py", "hook_session_start_template.py", "hook_stop_template.py"]
|
|
46
|
+
conduct_cli = ["hook_template.py", "hook_precompact_template.py", "hook_session_start_template.py", "hook_stop_template.py", "paxel.py"]
|
|
@@ -2133,44 +2133,29 @@ def classify_finding(text: str) -> "dict | None":
|
|
|
2133
2133
|
|
|
2134
2134
|
|
|
2135
2135
|
def cmd_session_report(args):
|
|
2136
|
-
"""
|
|
2137
|
-
Run paxel-local against local Claude Code transcripts and send a
|
|
2138
|
-
developer profile report to the workspace admin via the Conduct API.
|
|
2139
|
-
"""
|
|
2136
|
+
"""Run paxel analysis and open an HTML report in the local browser."""
|
|
2140
2137
|
import json as _json
|
|
2141
2138
|
import shutil
|
|
2142
2139
|
import subprocess
|
|
2143
2140
|
import tempfile
|
|
2144
|
-
import
|
|
2145
|
-
|
|
2146
|
-
server, workspace_id, api_key, token = _require_auth(args)
|
|
2147
|
-
hdrs = api.headers(workspace_id, token, "application/json", api_key)
|
|
2141
|
+
import webbrowser
|
|
2142
|
+
import getpass
|
|
2148
2143
|
|
|
2149
|
-
# ── 1.
|
|
2150
|
-
|
|
2144
|
+
# ── 1. Use bundled paxel ─────────────────────────────────────────────────
|
|
2145
|
+
bundled = Path(__file__).parent / "paxel.py"
|
|
2151
2146
|
tmpdir = tempfile.mkdtemp(prefix="conduct-paxel-")
|
|
2152
2147
|
paxel_script = Path(tmpdir) / "paxel.py"
|
|
2153
|
-
|
|
2154
|
-
print("Downloading paxel-local…")
|
|
2155
|
-
try:
|
|
2156
|
-
urllib.request.urlretrieve(PAXEL_URL, paxel_script)
|
|
2157
|
-
except Exception as exc:
|
|
2158
|
-
print(f"ERROR: could not download paxel-local: {exc}")
|
|
2159
|
-
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
2160
|
-
sys.exit(1)
|
|
2148
|
+
shutil.copy(bundled, paxel_script)
|
|
2161
2149
|
|
|
2162
2150
|
# ── 2. Run paxel ─────────────────────────────────────────────────────────
|
|
2163
2151
|
print("Analysing sessions…")
|
|
2164
2152
|
try:
|
|
2165
2153
|
result = subprocess.run(
|
|
2166
2154
|
[sys.executable, str(paxel_script), "--no-open"],
|
|
2167
|
-
cwd=tmpdir,
|
|
2168
|
-
capture_output=True,
|
|
2169
|
-
text=True,
|
|
2170
|
-
timeout=120,
|
|
2155
|
+
cwd=tmpdir, capture_output=True, text=True, timeout=120,
|
|
2171
2156
|
)
|
|
2172
2157
|
except subprocess.TimeoutExpired:
|
|
2173
|
-
print("ERROR:
|
|
2158
|
+
print("ERROR: analysis timed out after 120 s.")
|
|
2174
2159
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
2175
2160
|
sys.exit(1)
|
|
2176
2161
|
|
|
@@ -2178,7 +2163,7 @@ def cmd_session_report(args):
|
|
|
2178
2163
|
report_path = Path(tmpdir) / "report.md"
|
|
2179
2164
|
|
|
2180
2165
|
if not stats_path.exists():
|
|
2181
|
-
print("ERROR:
|
|
2166
|
+
print("ERROR: no transcripts found.")
|
|
2182
2167
|
if result.stderr:
|
|
2183
2168
|
print(result.stderr[:500])
|
|
2184
2169
|
shutil.rmtree(tmpdir, ignore_errors=True)
|
|
@@ -2186,28 +2171,22 @@ def cmd_session_report(args):
|
|
|
2186
2171
|
|
|
2187
2172
|
with open(stats_path) as f:
|
|
2188
2173
|
stats = _json.load(f)
|
|
2189
|
-
|
|
2190
2174
|
report_md = report_path.read_text() if report_path.exists() else ""
|
|
2191
2175
|
|
|
2192
|
-
# ── 3.
|
|
2193
|
-
|
|
2194
|
-
|
|
2195
|
-
|
|
2196
|
-
|
|
2197
|
-
|
|
2198
|
-
|
|
2199
|
-
autonomy = stats.get("autonomy", {})
|
|
2200
|
-
tools = stats.get("tools", {})
|
|
2201
|
-
velocity = stats.get("velocity", {})
|
|
2202
|
-
|
|
2176
|
+
# ── 3. Extract stats ─────────────────────────────────────────────────────
|
|
2177
|
+
developer = getattr(args, "developer", None) or getpass.getuser()
|
|
2178
|
+
volume = stats.get("volume", {})
|
|
2179
|
+
behavior = stats.get("behavior", {})
|
|
2180
|
+
autonomy = stats.get("autonomy", {})
|
|
2181
|
+
tools = stats.get("tools", {})
|
|
2182
|
+
velocity = stats.get("velocity", {})
|
|
2203
2183
|
sessions = volume.get("total_sessions", "?")
|
|
2204
2184
|
prompts = volume.get("total_prompts", "?")
|
|
2205
2185
|
autonomy_score = autonomy.get("autonomy_score_0_100", "?")
|
|
2206
|
-
top_tools = [t[0] for t in (tools.get("top_tools") or [])[:
|
|
2186
|
+
top_tools = [t[0] for t in (tools.get("top_tools") or [])[:8]]
|
|
2207
2187
|
commits = velocity.get("git_commits_real", "?") if isinstance(velocity, dict) else "?"
|
|
2208
|
-
|
|
2209
|
-
# Derive a simple archetype label from the data
|
|
2210
2188
|
planning_ratio = behavior.get("planning_ratio_explore_to_doing", 0)
|
|
2189
|
+
|
|
2211
2190
|
if autonomy_score != "?" and float(autonomy_score) >= 70:
|
|
2212
2191
|
archetype = "Autonomous Builder"
|
|
2213
2192
|
elif planning_ratio != 0 and float(planning_ratio) > 1.0:
|
|
@@ -2215,48 +2194,72 @@ def cmd_session_report(args):
|
|
|
2215
2194
|
else:
|
|
2216
2195
|
archetype = "Execution-Focused Builder"
|
|
2217
2196
|
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2222
|
-
|
|
2223
|
-
|
|
2224
|
-
|
|
2225
|
-
|
|
2226
|
-
|
|
2227
|
-
|
|
2228
|
-
|
|
2229
|
-
|
|
2230
|
-
|
|
2231
|
-
|
|
2232
|
-
|
|
2233
|
-
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2237
|
-
|
|
2238
|
-
|
|
2239
|
-
|
|
2240
|
-
|
|
2241
|
-
|
|
2242
|
-
|
|
2243
|
-
|
|
2244
|
-
|
|
2245
|
-
|
|
2246
|
-
|
|
2247
|
-
|
|
2248
|
-
|
|
2249
|
-
|
|
2250
|
-
|
|
2251
|
-
|
|
2252
|
-
|
|
2253
|
-
|
|
2254
|
-
|
|
2255
|
-
|
|
2256
|
-
|
|
2257
|
-
|
|
2258
|
-
|
|
2259
|
-
|
|
2197
|
+
tools_str = " ".join(f"<span class='tag'>{t}</span>" for t in top_tools)
|
|
2198
|
+
md_html = report_md.replace("&", "&").replace("<", "<").replace(">", ">").replace("\n", "<br>")
|
|
2199
|
+
|
|
2200
|
+
# ── 4. Generate HTML ─────────────────────────────────────────────────────
|
|
2201
|
+
html = f"""<!DOCTYPE html>
|
|
2202
|
+
<html lang="en">
|
|
2203
|
+
<head>
|
|
2204
|
+
<meta charset="UTF-8">
|
|
2205
|
+
<title>Session Report — {developer}</title>
|
|
2206
|
+
<style>
|
|
2207
|
+
* {{ box-sizing: border-box; margin: 0; padding: 0; }}
|
|
2208
|
+
body {{ font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; background: #0f0f11; color: #e2e2e8; min-height: 100vh; padding: 40px 24px; }}
|
|
2209
|
+
.wrap {{ max-width: 860px; margin: 0 auto; }}
|
|
2210
|
+
h1 {{ font-size: 26px; font-weight: 700; letter-spacing: -.02em; margin-bottom: 4px; }}
|
|
2211
|
+
.sub {{ font-size: 14px; color: #888; margin-bottom: 32px; }}
|
|
2212
|
+
.grid {{ display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 14px; margin-bottom: 32px; }}
|
|
2213
|
+
.card {{ background: #1a1a1f; border: 1px solid #2a2a33; border-radius: 10px; padding: 18px 20px; }}
|
|
2214
|
+
.card .label {{ font-size: 10px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #666; margin-bottom: 8px; }}
|
|
2215
|
+
.card .value {{ font-size: 28px; font-weight: 700; color: #fff; letter-spacing: -.02em; }}
|
|
2216
|
+
.card .unit {{ font-size: 13px; color: #888; margin-top: 2px; }}
|
|
2217
|
+
.archetype {{ background: #1a1a1f; border: 1px solid #2a2a33; border-radius: 10px; padding: 20px 24px; margin-bottom: 32px; display: flex; align-items: center; gap: 16px; }}
|
|
2218
|
+
.archetype .badge {{ font-size: 13px; font-weight: 700; background: #7c3aed22; color: #a78bfa; border: 1px solid #7c3aed44; border-radius: 8px; padding: 6px 14px; white-space: nowrap; }}
|
|
2219
|
+
.archetype .desc {{ font-size: 13px; color: #aaa; }}
|
|
2220
|
+
.section {{ margin-bottom: 32px; }}
|
|
2221
|
+
.section h2 {{ font-size: 12px; font-weight: 700; letter-spacing: .1em; text-transform: uppercase; color: #666; margin-bottom: 12px; }}
|
|
2222
|
+
.tag {{ display: inline-block; font-size: 12px; font-weight: 600; background: #1e1e28; border: 1px solid #2a2a3a; border-radius: 6px; padding: 4px 10px; margin: 3px; color: #a0aec0; }}
|
|
2223
|
+
.report {{ background: #1a1a1f; border: 1px solid #2a2a33; border-radius: 10px; padding: 20px 24px; font-size: 13px; color: #ccc; line-height: 1.7; }}
|
|
2224
|
+
</style>
|
|
2225
|
+
</head>
|
|
2226
|
+
<body>
|
|
2227
|
+
<div class="wrap">
|
|
2228
|
+
<h1>Session Report</h1>
|
|
2229
|
+
<div class="sub">{developer} · generated just now</div>
|
|
2230
|
+
|
|
2231
|
+
<div class="archetype">
|
|
2232
|
+
<span class="badge">{archetype}</span>
|
|
2233
|
+
<span class="desc">Autonomy score {autonomy_score}/100 · Planning ratio {planning_ratio}</span>
|
|
2234
|
+
</div>
|
|
2235
|
+
|
|
2236
|
+
<div class="grid">
|
|
2237
|
+
<div class="card"><div class="label">Sessions</div><div class="value">{sessions}</div></div>
|
|
2238
|
+
<div class="card"><div class="label">Prompts</div><div class="value">{prompts}</div></div>
|
|
2239
|
+
<div class="card"><div class="label">Commits</div><div class="value">{commits}</div></div>
|
|
2240
|
+
<div class="card"><div class="label">Autonomy</div><div class="value">{autonomy_score}</div><div class="unit">/ 100</div></div>
|
|
2241
|
+
</div>
|
|
2242
|
+
|
|
2243
|
+
<div class="section">
|
|
2244
|
+
<h2>Top Tools</h2>
|
|
2245
|
+
{tools_str if tools_str else "<span class='tag'>—</span>"}
|
|
2246
|
+
</div>
|
|
2247
|
+
|
|
2248
|
+
<div class="section">
|
|
2249
|
+
<h2>Report</h2>
|
|
2250
|
+
<div class="report">{md_html or "No report generated."}</div>
|
|
2251
|
+
</div>
|
|
2252
|
+
</div>
|
|
2253
|
+
</body>
|
|
2254
|
+
</html>"""
|
|
2255
|
+
|
|
2256
|
+
html_path = Path(tmpdir) / "report.html"
|
|
2257
|
+
html_path.write_text(html)
|
|
2258
|
+
|
|
2259
|
+
print(f" Archetype : {archetype}")
|
|
2260
|
+
print(f" Autonomy : {autonomy_score}/100 Sessions: {sessions} Commits: {commits}")
|
|
2261
|
+
webbrowser.open(f"file://{html_path}")
|
|
2262
|
+
print("Opening report in browser…")
|
|
2260
2263
|
|
|
2261
2264
|
|
|
2262
2265
|
def cmd_emit_finding(args):
|