PlanOpticon

planopticon / video_processor / agent / skills / cli_adapter.py
Blame History Raw 100 lines
1
"""Skill: Push artifacts to external tools via their CLIs."""
2
3
import json
4
import shutil
5
import subprocess
6
from typing import List
7
8
from video_processor.agent.skills.base import AgentContext, Artifact, Skill, register_skill
9
10
11
def _format_github(artifact: Artifact) -> List[str]:
12
"""Convert artifact to gh CLI commands."""
13
items = json.loads(artifact.content) if artifact.format == "json" else []
14
cmds = []
15
for item in items:
16
cmd = f"gh issue create --title {json.dumps(item.get('title', ''))}"
17
if item.get("body"):
18
cmd += f" --body {json.dumps(item['body'])}"
19
for label in item.get("labels", []):
20
cmd += f" --label {json.dumps(label)}"
21
cmds.append(cmd)
22
return cmds
23
24
25
def _format_jira(artifact: Artifact) -> List[str]:
26
"""Convert artifact to jira-cli commands."""
27
items = json.loads(artifact.content) if artifact.format == "json" else []
28
return [
29
f"jira issue create --summary {json.dumps(item.get('title', ''))}"
30
f" --description {json.dumps(item.get('body', item.get('description', '')))}"
31
for item in items
32
]
33
34
35
def _format_linear(artifact: Artifact) -> List[str]:
36
"""Convert artifact to linear CLI commands."""
37
items = json.loads(artifact.content) if artifact.format == "json" else []
38
return [
39
f"linear issue create --title {json.dumps(item.get('title', ''))}"
40
f" --description {json.dumps(item.get('body', item.get('description', '')))}"
41
for item in items
42
]
43
44
45
_adapters = {"github": _format_github, "jira": _format_jira, "linear": _format_linear}
46
47
48
def run_commands(commands: List[str], dry_run: bool = True) -> List[dict]:
49
"""Execute CLI commands. In dry_run mode, just return what would run."""
50
results = []
51
for cmd in commands:
52
if dry_run:
53
results.append({"command": cmd, "status": "dry_run"})
54
else:
55
proc = subprocess.run(cmd, shell=True, capture_output=True, text=True)
56
results.append(
57
{
58
"command": cmd,
59
"returncode": proc.returncode,
60
"stdout": proc.stdout.strip(),
61
"stderr": proc.stderr.strip(),
62
}
63
)
64
return results
65
66
67
class CLIAdapterSkill(Skill):
68
name = "cli_adapter"
69
description = "Push artifacts to external tools via their CLIs"
70
71
def execute(self, context: AgentContext, **kwargs) -> Artifact:
72
tool = kwargs.get("tool", "github")
73
artifact = kwargs.get("artifact")
74
if artifact is None and context.artifacts:
75
artifact = context.artifacts[-1]
76
if artifact is None:
77
return Artifact(
78
name="CLI Commands", content="[]", artifact_type="cli_commands", format="json"
79
)
80
81
formatter = _adapters.get(tool)
82
if formatter is None:
83
return Artifact(
84
name="CLI Commands",
85
content=json.dumps({"error": f"Unknown tool: {tool}"}),
86
artifact_type="cli_commands",
87
format="json",
88
)
89
90
cli_name = {"github": "gh", "jira": "jira", "linear": "linear"}[tool]
91
available = shutil.which(cli_name) is not None
92
commands = formatter(artifact)
93
content = json.dumps({"tool": tool, "available": available, "commands": commands}, indent=2)
94
return Artifact(
95
name="CLI Commands", content=content, artifact_type="cli_commands", format="json"
96
)
97
98
99
register_skill(CLIAdapterSkill())
100

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button