Skip to content

Commit b036757

Browse files
committed
Add ShortcutInputChatPlugin
1 parent 2b73ce6 commit b036757

File tree

2 files changed

+126
-0
lines changed

2 files changed

+126
-0
lines changed
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
import ChatPlugin
2+
import Environment
3+
import Foundation
4+
import OpenAIService
5+
import Parsing
6+
import Terminal
7+
8+
public actor ShortcutInputChatPlugin: ChatPlugin {
9+
public static var command: String { "shortcutInput" }
10+
public nonisolated var name: String { "Shortcut Input" }
11+
12+
let chatGPTService: any ChatGPTServiceType
13+
var terminal: TerminalType = Terminal()
14+
var isCancelled = false
15+
weak var delegate: ChatPluginDelegate?
16+
17+
public init(inside chatGPTService: any ChatGPTServiceType, delegate: ChatPluginDelegate) {
18+
self.chatGPTService = chatGPTService
19+
self.delegate = delegate
20+
}
21+
22+
public func send(content: String, originalMessage: String) async {
23+
delegate?.pluginDidStart(self)
24+
delegate?.pluginDidStartResponding(self)
25+
26+
defer {
27+
delegate?.pluginDidEndResponding(self)
28+
delegate?.pluginDidEnd(self)
29+
}
30+
31+
let id = "\(Self.command)-\(UUID().uuidString)"
32+
33+
var content = content[...]
34+
let firstParenthesisParser = PrefixThrough("(")
35+
let shortcutNameParser = PrefixUpTo(")")
36+
37+
_ = try? firstParenthesisParser.parse(&content)
38+
let shortcutName = try? shortcutNameParser.parse(&content)
39+
_ = try? PrefixThrough(")").parse(&content)
40+
41+
guard let shortcutName, !shortcutName.isEmpty else {
42+
let id = "\(Self.command)-\(UUID().uuidString)"
43+
let reply = ChatMessage(
44+
id: id,
45+
role: .assistant,
46+
content: "Please provide the shortcut name in format: `/\(Self.command)(shortcut name)`."
47+
)
48+
await chatGPTService.mutateHistory { history in
49+
history.append(reply)
50+
}
51+
return
52+
}
53+
54+
var input = String(content).trimmingCharacters(in: .whitespacesAndNewlines)
55+
if input.isEmpty {
56+
// if no input detected, use the previous message as input
57+
input = await chatGPTService.history.last?.content ?? ""
58+
}
59+
60+
do {
61+
if isCancelled { throw CancellationError() }
62+
63+
let env = ProcessInfo.processInfo.environment
64+
let shell = env["SHELL"] ?? "/bin/bash"
65+
let temporaryURL = FileManager.default.temporaryDirectory
66+
let temporaryInputFileURL = temporaryURL
67+
.appendingPathComponent("\(id)-input.txt")
68+
let temporaryOutputFileURL = temporaryURL
69+
.appendingPathComponent("\(id)-output")
70+
71+
try input.write(to: temporaryInputFileURL, atomically: true, encoding: .utf8)
72+
73+
let command = """
74+
shortcuts run "\(shortcutName)" \
75+
-i "\(temporaryInputFileURL.path)" \
76+
-o "\(temporaryOutputFileURL.path)"
77+
"""
78+
79+
_ = try await terminal.runCommand(
80+
shell,
81+
arguments: ["-i", "-l", "-c", command],
82+
currentDirectoryPath: "/",
83+
environment: [:]
84+
)
85+
86+
await Task.yield()
87+
88+
if FileManager.default.fileExists(atPath: temporaryOutputFileURL.path) {
89+
let data = try Data(contentsOf: temporaryOutputFileURL)
90+
if let text = String(data: data, encoding: .utf8) {
91+
if text.isEmpty { return }
92+
_ = try await chatGPTService.send(content: text, summary: nil)
93+
} else {
94+
let text = """
95+
[View File](\(temporaryOutputFileURL))
96+
"""
97+
_ = try await chatGPTService.send(content: text, summary: nil)
98+
}
99+
100+
return
101+
}
102+
} catch {
103+
let id = "\(Self.command)-\(UUID().uuidString)"
104+
let reply = ChatMessage(
105+
id: id,
106+
role: .assistant,
107+
content: error.localizedDescription
108+
)
109+
await chatGPTService.mutateHistory { history in
110+
history.append(reply)
111+
}
112+
}
113+
}
114+
115+
public func cancel() async {
116+
isCancelled = true
117+
await terminal.terminate()
118+
}
119+
120+
public func stopResponding() async {
121+
isCancelled = true
122+
await terminal.terminate()
123+
}
124+
}
125+

Core/Sources/ChatService/AllPlugins.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ let allPlugins: [ChatPlugin.Type] = [
99
MathChatPlugin.self,
1010
SearchChatPlugin.self,
1111
ShortcutChatPlugin.self,
12+
ShortcutInputChatPlugin.self,
1213
]
1314

0 commit comments

Comments
 (0)