conduct-cli 0.4.34__tar.gz → 0.4.36__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.34
3
+ Version: 0.4.36
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.34"
7
+ version = "0.4.36"
8
8
  description = "CLI for Conduct AI — install agents, manage projects, run tests"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -1084,8 +1084,12 @@ def cmd_install(args):
1084
1084
  proj = _resolve_project(server, workspace_id, hdrs, args.project)
1085
1085
  project_id = proj["id"]
1086
1086
 
1087
- # Agent name — use friendly name, fall back to playbook API name
1088
- agent_name = args.name or _FRIENDLY_NAMES.get(slug) or pb["name"]
1087
+ # Agent name — explicit --name wins; otherwise auto-suffix with 4-char ID
1088
+ # e.g. "Security Autopilot Fix [A4B2]" so multiple installs are distinguishable
1089
+ import random, string
1090
+ _base = _FRIENDLY_NAMES.get(slug) or pb["name"]
1091
+ _uid = "".join(random.choices(string.ascii_uppercase + string.digits, k=4))
1092
+ agent_name = args.name or f"{_base} [{_uid}]"
1089
1093
 
1090
1094
  # Repo input — inject into inputs if playbook expects github_repo
1091
1095
  if args.repo:
@@ -1280,82 +1284,48 @@ def _build_state(issue: dict, repo_full_name: str) -> dict:
1280
1284
 
1281
1285
 
1282
1286
  def cmd_run(args):
1283
- path = Path(args.yaml)
1284
- if not path.exists():
1285
- print(f"ERROR: file not found: {path}")
1286
- sys.exit(1)
1287
-
1288
- raw_yaml = path.read_text()
1289
- cfg = yaml.safe_load(raw_yaml)
1290
- name = cfg.get("name", path.stem)
1291
- workflow_id = cfg.get("id")
1292
1287
  server, workspace_id, api_key, token = _require_auth(args)
1293
- on_block = cfg.get("on") or {}
1294
- trigger_type = next(iter(on_block), None)
1295
- trigger_cfg = on_block.get(trigger_type, {})
1296
-
1297
1288
  json_h = api.headers(workspace_id, token, "application/json", api_key)
1298
- yaml_h = api.headers(workspace_id, token, "application/x-yaml", api_key)
1299
-
1300
- print(f"\n{BOLD}▶ conduct run — {name}{RESET}")
1301
- print(f" server: {server}\n")
1302
1289
 
1303
- if not workflow_id:
1304
- workflow_id = api.find_or_create_workflow(server, name, json_h)
1305
- print(f" workflow: {workflow_id}")
1306
- print(f" pushing YAML… ", end="", flush=True)
1307
- api.req_text("PUT", f"{server}/workflows/{workflow_id}/yaml", yaml_h, raw_yaml)
1308
- print(f"{GREEN}ok{RESET}\n")
1309
-
1310
- if trigger_type == "github_issue_labeled":
1311
- repo = trigger_cfg.get("repo_allowlist", "")
1312
- label = trigger_cfg.get("label", "")
1290
+ # Parse --input key=value pairs into initial_state
1291
+ initial_state: dict = {}
1292
+ for kv in (args.input or []):
1293
+ if "=" not in kv:
1294
+ print(f"{RED}Bad --input format '{kv}' expected key=value{RESET}")
1295
+ sys.exit(1)
1296
+ k, v = kv.split("=", 1)
1297
+ initial_state[k] = v
1313
1298
 
1314
- print(f" Fetching issues from {repo} with label '{label}'…")
1315
- qs = urllib.parse.urlencode({"repo": repo, "label": label})
1316
- issues = api.req("GET", f"{server}/credentials/github/issues?{qs}", json_h)
1299
+ # Resolve agent by name
1300
+ target = args.agent
1301
+ workflows = api.req("GET", f"{server}/workflows", json_h)
1317
1302
 
1318
- if not issues:
1319
- print(f" No open issues found with label '{label}'.")
1320
- return
1303
+ # Filter by project if given
1304
+ if args.project:
1305
+ projects = api.req("GET", f"{server}/workspaces/{workspace_id}/projects", json_h)
1306
+ proj = next((p for p in projects if p["name"].lower() == args.project.lower()), None)
1307
+ if not proj:
1308
+ print(f"{RED}Project '{args.project}' not found.{RESET}")
1309
+ sys.exit(1)
1310
+ workflows = [w for w in workflows if w.get("project_id") == proj["id"]]
1321
1311
 
1322
- print(f" Found {len(issues)} issue(s)\n")
1323
-
1324
- passed = failed = 0
1325
- for issue in issues:
1326
- print(f"{CYAN} ── Issue #{issue['number']}: {issue['title']}{RESET}")
1327
- state = _build_state(issue, repo)
1328
-
1329
- max_turns = None
1330
- try:
1331
- pf = api.req("POST", f"{server}/workflows/{workflow_id}/preflight", json_h, {
1332
- "issue_title": issue["title"],
1333
- "issue_body": issue.get("body") or "",
1334
- })
1335
- suggested = pf.get("suggested_max_turns", 20)
1336
- if suggested > 20:
1337
- print(f"{GRAY} ⚠ estimated {suggested} turns — bumping max_turns{RESET}")
1338
- max_turns = suggested
1339
- except Exception:
1340
- pass
1341
-
1342
- payload = {"triggered_by": f"cli:issue#{issue['number']}", "initial_state": state}
1343
- if max_turns:
1344
- payload["max_turns"] = max_turns
1345
- run = api.req("POST", f"{server}/workflows/{workflow_id}/runs", json_h, payload)
1346
- ok = _stream_run(server, workflow_id, run["id"], workspace_id, token, api_key)
1347
- passed += ok
1348
- failed += not ok
1349
- print()
1312
+ wf = next((w for w in workflows if w["name"].lower() == target.lower()), None)
1313
+ if not wf:
1314
+ print(f"{RED}Agent '{target}' not found. Run 'conduct agents' to list agents.{RESET}")
1315
+ sys.exit(1)
1350
1316
 
1351
- print(f"{BOLD} Summary: {passed} passed, {failed} failed{RESET}\n")
1317
+ workflow_id = wf["id"]
1318
+ print(f"\n{BOLD}▶ conduct run — {wf['name']}{RESET}")
1319
+ if initial_state:
1320
+ for k, v in initial_state.items():
1321
+ print(f" {GRAY}{k}={v}{RESET}")
1322
+ print()
1352
1323
 
1353
- else:
1354
- run = api.req("POST", f"{server}/workflows/{workflow_id}/runs", json_h, {
1355
- "triggered_by": "cli",
1356
- "initial_state": {},
1357
- })
1358
- _stream_run(server, workflow_id, run["id"], workspace_id, token)
1324
+ run = api.req("POST", f"{server}/workflows/{workflow_id}/runs", json_h, {
1325
+ "triggered_by": "cli",
1326
+ "initial_state": initial_state,
1327
+ })
1328
+ _stream_run(server, workflow_id, run["id"], workspace_id, token, api_key)
1359
1329
 
1360
1330
 
1361
1331
  # ── Entry point ───────────────────────────────────────────────────────────────
@@ -1458,8 +1428,10 @@ def main():
1458
1428
  help="Input value applied to all playbooks (repeatable)")
1459
1429
 
1460
1430
  # conduct run (existing)
1461
- run_p = sub.add_parser("run", help="Run a workflow from a YAML file")
1462
- run_p.add_argument("yaml", help="Path to workflow YAML")
1431
+ run_p = sub.add_parser("run", help="Run an installed agent by name")
1432
+ run_p.add_argument("agent", help="Agent name (e.g. 'security_autopilot_fix')")
1433
+ run_p.add_argument("--project", metavar="name", help="Narrow to a specific project")
1434
+ run_p.add_argument("--input", action="append", metavar="key=value", help="Runtime input (repeatable)")
1463
1435
 
1464
1436
  # conduct guard
1465
1437
  guard_p, _guard_sub = _guard.register_guard_parser(sub)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: conduct-cli
3
- Version: 0.4.34
3
+ Version: 0.4.36
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