Skip to content

Commit 028b06b

Browse files
authored
Merge pull request #23 from greggroth/copilot-cli
Add built-in support for GitHub Copilot CLI agent
2 parents ee988da + 2990f88 commit 028b06b

7 files changed

Lines changed: 288 additions & 0 deletions

File tree

macos/Sources/Features/Terminal/TerminalController.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1961,6 +1961,8 @@ class TerminalController: BaseTerminalController, TabGroupCloseCoordinator.Contr
19611961
base.command = "codex resume \(session.id)"
19621962
case .opencode:
19631963
base.command = "opencode --session \(session.id)"
1964+
case .copilotCli:
1965+
base.command = "copilot --resume \(session.id)"
19641966
}
19651967

19661968
if WorktrunkPreferences.worktreeTabsEnabled {

macos/Sources/Features/Worktrunk/AgentStatus/AgentHookInstaller.swift

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,12 @@ enum AgentHookInstaller {
5151
marker: wrapperMarker,
5252
content: buildCodexWrapper()
5353
)
54+
ensureFile(
55+
url: AgentStatusPaths.copilotCliWrapperPath,
56+
mode: 0o755,
57+
marker: wrapperMarker,
58+
content: buildCopilotCliWrapper()
59+
)
5460

5561
ensureFile(
5662
url: AgentStatusPaths.opencodeGlobalPluginPath,
@@ -295,6 +301,61 @@ enum AgentHookInstaller {
295301
"""
296302
}
297303

304+
private static func buildCopilotCliWrapper() -> String {
305+
let binDir = AgentStatusPaths.binDir.path
306+
let eventsDir = AgentStatusPaths.eventsCacheDir.path
307+
return """
308+
#!/bin/bash
309+
\(wrapperMarker)
310+
# Wrapper for Copilot CLI: emits Start/Stop lifecycle events.
311+
312+
\(pathAugmentSnippet())
313+
314+
find_real_binary() {
315+
local name="$1"
316+
local IFS=:
317+
for dir in $PATH; do
318+
[ -z "$dir" ] && continue
319+
dir="${dir%/}"
320+
if [ "$dir" = "\(binDir)" ]; then
321+
continue
322+
fi
323+
if [ -x "$dir/$name" ] && [ ! -d "$dir/$name" ]; then
324+
printf "%s\\n" "$dir/$name"
325+
return 0
326+
fi
327+
done
328+
return 1
329+
}
330+
331+
REAL_BIN="$(find_real_binary "copilot")"
332+
if [ -z "$REAL_BIN" ]; then
333+
echo "Ghostree: copilot not found in PATH. Install it and ensure it is on PATH, then retry." >&2
334+
exit 127
335+
fi
336+
337+
_EVENTS_DIR="${GHOSTREE_AGENT_EVENTS_DIR:-\(eventsDir)}"
338+
339+
# Emit synthetic Start event
340+
printf '{\"timestamp\":\"%s\",\"eventType\":\"Start\",\"cwd\":\"%s\"}\\n' \\
341+
"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \\
342+
"$(pwd -P 2>/dev/null || pwd)" \\
343+
>> "$_EVENTS_DIR/agent-events.jsonl" 2>/dev/null
344+
345+
# Run copilot and capture exit code
346+
"$REAL_BIN" "$@"
347+
_EXIT=$?
348+
349+
# Emit synthetic Stop event
350+
printf '{\"timestamp\":\"%s\",\"eventType\":\"Stop\",\"cwd\":\"%s\"}\\n' \\
351+
"$(date -u +%Y-%m-%dT%H:%M:%S.000Z)" \\
352+
"$(pwd -P 2>/dev/null || pwd)" \\
353+
>> "$_EVENTS_DIR/agent-events.jsonl" 2>/dev/null
354+
355+
exit $_EXIT
356+
"""
357+
}
358+
298359
private static func buildOpenCodePlugin() -> String {
299360
let marker = AgentStatusPaths.opencodePluginMarker
300361
return """

macos/Sources/Features/Worktrunk/AgentStatus/AgentStatusPaths.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ enum AgentStatusPaths {
4646
binDir.appendingPathComponent("codex")
4747
}
4848

49+
static var copilotCliWrapperPath: URL {
50+
binDir.appendingPathComponent("copilot")
51+
}
52+
4953
static var opencodePluginMarker: String { "// Ghostree opencode plugin v5" }
5054

5155
/** @see https://opencode.ai/docs/plugins */

macos/Sources/Features/Worktrunk/WorktrunkPreferences.swift

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ enum WorktrunkAgent: String, CaseIterable, Identifiable {
55
case claude
66
case codex
77
case opencode
8+
case copilotCli
89

910
var id: String { rawValue }
1011

@@ -13,6 +14,7 @@ enum WorktrunkAgent: String, CaseIterable, Identifiable {
1314
case .claude: return "Claude Code"
1415
case .codex: return "Codex"
1516
case .opencode: return "OpenCode"
17+
case .copilotCli: return "Copilot"
1618
}
1719
}
1820

@@ -21,6 +23,7 @@ enum WorktrunkAgent: String, CaseIterable, Identifiable {
2123
case .claude: return "claude"
2224
case .codex: return "codex"
2325
case .opencode: return "opencode"
26+
case .copilotCli: return "copilot"
2427
}
2528
}
2629

@@ -76,6 +79,7 @@ enum WorktrunkDefaultAction: String, CaseIterable, Identifiable {
7679
case claude
7780
case codex
7881
case opencode
82+
case copilotCli
7983

8084
var id: String { rawValue }
8185

@@ -85,6 +89,7 @@ enum WorktrunkDefaultAction: String, CaseIterable, Identifiable {
8589
case .claude: return "Claude Code"
8690
case .codex: return "Codex"
8791
case .opencode: return "OpenCode"
92+
case .copilotCli: return "Copilot"
8893
}
8994
}
9095

@@ -94,6 +99,7 @@ enum WorktrunkDefaultAction: String, CaseIterable, Identifiable {
9499
case .claude: return .claude
95100
case .codex: return .codex
96101
case .opencode: return .opencode
102+
case .copilotCli: return .copilotCli
97103
}
98104
}
99105

0 commit comments

Comments
 (0)