conduct-cli 0.4.93__tar.gz → 0.4.95__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 (27) hide show
  1. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/PKG-INFO +1 -1
  2. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/pyproject.toml +1 -1
  3. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/main.py +115 -14
  4. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/PKG-INFO +1 -1
  5. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/README.md +0 -0
  6. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/setup.cfg +0 -0
  7. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/setup.py +0 -0
  8. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/__init__.py +0 -0
  9. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/api.py +0 -0
  10. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/guard.py +0 -0
  11. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/guardmcp.py +0 -0
  12. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/hook_precompact_template.py +0 -0
  13. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/hook_session_start_template.py +0 -0
  14. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/hook_stop_template.py +0 -0
  15. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/hook_template.py +0 -0
  16. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/mcp_server.py +0 -0
  17. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/memory.py +0 -0
  18. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli/paxel.py +0 -0
  19. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/SOURCES.txt +0 -0
  20. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/dependency_links.txt +0 -0
  21. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/entry_points.txt +0 -0
  22. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/requires.txt +0 -0
  23. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/src/conduct_cli.egg-info/top_level.txt +0 -0
  24. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/tests/test_guard_policy.py +0 -0
  25. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/tests/test_guard_savings.py +0 -0
  26. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/tests/test_hook_syntax.py +0 -0
  27. {conduct_cli-0.4.93 → conduct_cli-0.4.95}/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.93
3
+ Version: 0.4.95
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
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "conduct-cli"
7
- version = "0.4.93"
7
+ version = "0.4.95"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -2334,18 +2334,75 @@ def cmd_emit_finding(args):
2334
2334
  print(finding_id)
2335
2335
 
2336
2336
 
2337
+ def _gh_api_get(url: str, token: str):
2338
+ import urllib.request, urllib.error
2339
+ req = urllib.request.Request(url, headers={
2340
+ "Authorization": f"Bearer {token}",
2341
+ "Accept": "application/vnd.github+json",
2342
+ "X-GitHub-Api-Version": "2022-11-28",
2343
+ })
2344
+ with urllib.request.urlopen(req, timeout=10) as resp:
2345
+ return json.loads(resp.read())
2346
+
2347
+
2348
+ def _fetch_github_issue(repo: str, issue_number: int, token: str) -> dict:
2349
+ return _gh_api_get(f"https://api.github.com/repos/{repo}/issues/{issue_number}", token)
2350
+
2351
+
2352
+ def _fetch_issues_by_label(repo: str, label: str, token: str) -> list:
2353
+ return _gh_api_get(
2354
+ f"https://api.github.com/repos/{repo}/issues?labels={label}&state=open&per_page=20",
2355
+ token,
2356
+ )
2357
+
2358
+
2359
+ def _build_issue_trigger_payload(issue: dict, repo: str) -> dict:
2360
+ owner, repo_name = (repo.split("/", 1) + [""])[:2]
2361
+ return {
2362
+ "action": "labeled",
2363
+ "issue": {
2364
+ "number": issue["number"],
2365
+ "title": issue["title"],
2366
+ "body": issue.get("body") or "",
2367
+ "html_url": issue.get("html_url", ""),
2368
+ "user": issue.get("user") or {},
2369
+ "labels": issue.get("labels") or [],
2370
+ },
2371
+ "repository": {
2372
+ "full_name": repo,
2373
+ "name": repo_name,
2374
+ "owner": {"login": owner},
2375
+ "clone_url": f"https://github.com/{repo}.git",
2376
+ "default_branch": "main",
2377
+ },
2378
+ }
2379
+
2380
+
2381
+ def _prompt_issue_choice(issues: list) -> dict:
2382
+ print(f"\n{BOLD}Multiple open issues found — choose one:{RESET}\n")
2383
+ for i, iss in enumerate(issues):
2384
+ print(f" {BOLD}{i + 1}.{RESET} #{iss['number']} — {iss['title']}")
2385
+ print()
2386
+ while True:
2387
+ raw = input(f"Enter number [1–{len(issues)}]: ").strip()
2388
+ if raw.isdigit() and 1 <= int(raw) <= len(issues):
2389
+ return issues[int(raw) - 1]
2390
+ print(f"{RED}Invalid choice.{RESET}")
2391
+
2392
+
2337
2393
  def cmd_run(args):
2338
2394
  server, workspace_id, api_key, token = _require_auth(args)
2339
2395
  json_h = api.headers(workspace_id, token, "application/json", api_key)
2340
2396
 
2341
- # Parse --input key=value pairs into initial_state
2342
- initial_state: dict = {}
2397
+ # Fix 1: --input values go into state["inputs"], not top-level state.
2398
+ # {{inputs.key}} refs in YAML only resolve when the executor finds state["inputs"].
2399
+ run_inputs: dict = {}
2343
2400
  for kv in (args.input or []):
2344
2401
  if "=" not in kv:
2345
2402
  print(f"{RED}Bad --input format '{kv}' — expected key=value{RESET}")
2346
2403
  sys.exit(1)
2347
2404
  k, v = kv.split("=", 1)
2348
- initial_state[k] = v
2405
+ run_inputs[k] = v
2349
2406
 
2350
2407
  # Resolve agent by name
2351
2408
  target = args.agent
@@ -2366,17 +2423,22 @@ def cmd_run(args):
2366
2423
  sys.exit(1)
2367
2424
 
2368
2425
  workflow_id = wf["id"]
2426
+
2427
+ # Fix 4: determine trigger type from workflow metadata.
2428
+ is_github_webhook = bool(wf.get("github_webhook"))
2429
+ slug = wf.get("playbook_slug") or ""
2430
+ is_issue_trigger = is_github_webhook and "issue" in slug
2431
+
2369
2432
  print(f"\n{BOLD}▶ conduct run — {wf['name']}{RESET}")
2370
- if initial_state:
2371
- for k, v in initial_state.items():
2433
+ if run_inputs:
2434
+ for k, v in run_inputs.items():
2372
2435
  print(f" {GRAY}{k}={v}{RESET}")
2373
2436
  print()
2374
2437
 
2375
- # Preflight: estimate turns + show files likely to be modified
2438
+ # Fix 2: preflight receives run_inputs so turn estimates use real input context.
2376
2439
  try:
2377
2440
  pf = api.req("POST", f"{server}/workflows/{workflow_id}/preflight", json_h, {
2378
- "issue_title": initial_state.get("title", ""),
2379
- "issue_body": initial_state.get("body", ""),
2441
+ "run_inputs": run_inputs,
2380
2442
  })
2381
2443
  suggested = pf.get("suggested_max_turns", 20)
2382
2444
  files = pf.get("total_files", [])
@@ -2387,17 +2449,56 @@ def cmd_run(args):
2387
2449
  except Exception:
2388
2450
  suggested = 20
2389
2451
 
2390
- # Call the test-trigger endpoint so the YAML's built-in test_trigger.payload
2391
- # (PR fixture, issue fixture, etc.) is loaded and the configured repo is
2392
- # injected instead of running with an empty trigger context.
2393
- body: dict = {**initial_state}
2394
- # Use preflight suggestion unless user explicitly passed --max-turns
2452
+ # Fix 4: extract github_token before building body so it never lands in state["inputs"].
2453
+ import os as _os
2454
+ gh_token = run_inputs.pop("github_token", None) or _os.environ.get("GITHUB_TOKEN")
2455
+
2456
+ # Build the trigger body.
2457
+ # Fix 1: send inputs under "inputs" key so server puts them in state["inputs"].
2458
+ body: dict = {}
2459
+ if run_inputs:
2460
+ body["inputs"] = run_inputs
2461
+
2462
+ # Fix 4: non-webhook triggers (manual/schedule) — flag so server skips trigger validation.
2463
+ if not is_github_webhook:
2464
+ body["__manual"] = True
2465
+
2466
+ # Fix 4: github_issue_labeled — fire against a real issue when possible.
2467
+ if is_issue_trigger:
2468
+ repo = wf.get("github_hook_repo") or run_inputs.get("repo")
2469
+ label = wf.get("github_hook_label") or ""
2470
+ issue_number_raw = run_inputs.get("issue_number")
2471
+ if gh_token and repo:
2472
+ try:
2473
+ if issue_number_raw:
2474
+ issue = _fetch_github_issue(repo, int(issue_number_raw), gh_token)
2475
+ body.update(_build_issue_trigger_payload(issue, repo))
2476
+ print(f" {GRAY}issue: #{issue['number']} — {issue['title']}{RESET}\n")
2477
+ elif label:
2478
+ issues = _fetch_issues_by_label(repo, label, gh_token)
2479
+ if not issues:
2480
+ print(f"{YELLOW}⚠ No open issues with label '{label}' in {repo}. Using test payload.{RESET}\n")
2481
+ elif len(issues) == 1:
2482
+ body.update(_build_issue_trigger_payload(issues[0], repo))
2483
+ print(f" {GRAY}issue: #{issues[0]['number']} — {issues[0]['title']}{RESET}\n")
2484
+ else:
2485
+ chosen = _prompt_issue_choice(issues)
2486
+ body.update(_build_issue_trigger_payload(chosen, repo))
2487
+ print(f" {GRAY}issue: #{chosen['number']} — {chosen['title']}{RESET}\n")
2488
+ except Exception as _gh_err:
2489
+ print(f"{YELLOW}⚠ GitHub fetch failed ({_gh_err}). Using test payload.{RESET}\n")
2490
+ else:
2491
+ hint = "export GITHUB_TOKEN=<token>" if not gh_token else "workflow has no github_hook_repo"
2492
+ print(f" {GRAY}No GitHub token — using test payload. ({hint}){RESET}\n")
2493
+
2494
+ # Fix 3: /trigger returns run_id, not id.
2395
2495
  if getattr(args, "max_turns", None):
2396
2496
  body["__max_turns"] = args.max_turns
2397
2497
  elif suggested > 20:
2398
2498
  body["__max_turns"] = suggested
2399
2499
  run = api.req("POST", f"{server}/workflows/{workflow_id}/trigger", json_h, body)
2400
- _stream_run(server, workflow_id, run["id"], workspace_id, token, api_key)
2500
+ run_id = run.get("run_id") or run.get("id")
2501
+ _stream_run(server, workflow_id, run_id, workspace_id, token, api_key)
2401
2502
 
2402
2503
 
2403
2504
  # ── conduct sync / test-guard / test-security ────────────────────────────────
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.93
3
+ Version: 0.4.95
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
File without changes
File without changes
File without changes