ace-llm-providers-cli 0.27.0
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.
- checksums.yaml +7 -0
- data/.ace-defaults/llm/providers/claude.yml +24 -0
- data/.ace-defaults/llm/providers/codex.yml +22 -0
- data/.ace-defaults/llm/providers/codexoss.yml +13 -0
- data/.ace-defaults/llm/providers/gemini.yml +32 -0
- data/.ace-defaults/llm/providers/opencode.yml +26 -0
- data/.ace-defaults/llm/providers/pi.yml +43 -0
- data/CHANGELOG.md +457 -0
- data/LICENSE +21 -0
- data/README.md +36 -0
- data/Rakefile +14 -0
- data/exe/ace-llm-providers-cli-check +76 -0
- data/lib/ace/llm/providers/cli/atoms/args_normalizer.rb +82 -0
- data/lib/ace/llm/providers/cli/atoms/auth_checker.rb +74 -0
- data/lib/ace/llm/providers/cli/atoms/command_formatters.rb +19 -0
- data/lib/ace/llm/providers/cli/atoms/command_rewriter.rb +75 -0
- data/lib/ace/llm/providers/cli/atoms/execution_context.rb +28 -0
- data/lib/ace/llm/providers/cli/atoms/provider_detector.rb +48 -0
- data/lib/ace/llm/providers/cli/atoms/session_finders/claude_session_finder.rb +79 -0
- data/lib/ace/llm/providers/cli/atoms/session_finders/codex_session_finder.rb +84 -0
- data/lib/ace/llm/providers/cli/atoms/session_finders/gemini_session_finder.rb +66 -0
- data/lib/ace/llm/providers/cli/atoms/session_finders/open_code_session_finder.rb +119 -0
- data/lib/ace/llm/providers/cli/atoms/session_finders/pi_session_finder.rb +87 -0
- data/lib/ace/llm/providers/cli/atoms/skill_command_rewriter.rb +30 -0
- data/lib/ace/llm/providers/cli/atoms/worktree_dir_resolver.rb +56 -0
- data/lib/ace/llm/providers/cli/claude_code_client.rb +358 -0
- data/lib/ace/llm/providers/cli/claude_oai_client.rb +322 -0
- data/lib/ace/llm/providers/cli/cli_args_support.rb +19 -0
- data/lib/ace/llm/providers/cli/codex_client.rb +291 -0
- data/lib/ace/llm/providers/cli/codex_oai_client.rb +274 -0
- data/lib/ace/llm/providers/cli/gemini_client.rb +346 -0
- data/lib/ace/llm/providers/cli/molecules/health_checker.rb +80 -0
- data/lib/ace/llm/providers/cli/molecules/safe_capture.rb +153 -0
- data/lib/ace/llm/providers/cli/molecules/session_finder.rb +44 -0
- data/lib/ace/llm/providers/cli/molecules/skill_name_reader.rb +64 -0
- data/lib/ace/llm/providers/cli/open_code_client.rb +271 -0
- data/lib/ace/llm/providers/cli/pi_client.rb +331 -0
- data/lib/ace/llm/providers/cli/version.rb +11 -0
- data/lib/ace/llm/providers/cli.rb +47 -0
- metadata +139 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "json"
|
|
4
|
+
require "open3"
|
|
5
|
+
require "shellwords"
|
|
6
|
+
|
|
7
|
+
require_relative "cli_args_support"
|
|
8
|
+
require_relative "atoms/execution_context"
|
|
9
|
+
require_relative "atoms/command_rewriter"
|
|
10
|
+
require_relative "atoms/command_formatters"
|
|
11
|
+
require_relative "molecules/skill_name_reader"
|
|
12
|
+
|
|
13
|
+
module Ace
|
|
14
|
+
module LLM
|
|
15
|
+
module Providers
|
|
16
|
+
module CLI
|
|
17
|
+
# Client for interacting with Pi CLI
|
|
18
|
+
# Provides access to multiple AI providers through Pi's unified platform
|
|
19
|
+
# with skill command rewriting support
|
|
20
|
+
class PiClient < Ace::LLM::Organisms::BaseClient
|
|
21
|
+
include CliArgsSupport
|
|
22
|
+
|
|
23
|
+
API_BASE_URL = "https://pi.dev"
|
|
24
|
+
DEFAULT_GENERATION_CONFIG = {}.freeze
|
|
25
|
+
|
|
26
|
+
def self.provider_name
|
|
27
|
+
"pi"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
DEFAULT_MODEL = "zai/glm-4.7"
|
|
31
|
+
|
|
32
|
+
def initialize(model: nil, **options)
|
|
33
|
+
@model = model || DEFAULT_MODEL
|
|
34
|
+
@options = options
|
|
35
|
+
@generation_config = options[:generation_config] || {}
|
|
36
|
+
@skill_name_reader = Molecules::SkillNameReader.new
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def needs_credentials?
|
|
40
|
+
false
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Generate a response from the LLM
|
|
44
|
+
# @param messages [Array<Hash>] Conversation messages
|
|
45
|
+
# @param options [Hash] Generation options
|
|
46
|
+
# @return [Hash] Response with text and metadata
|
|
47
|
+
def generate(messages, **options)
|
|
48
|
+
validate_pi_availability!
|
|
49
|
+
|
|
50
|
+
prompt = format_messages_as_prompt(messages)
|
|
51
|
+
full_prompt, system_prompt = build_full_prompt(prompt, options)
|
|
52
|
+
subprocess_env = options[:subprocess_env]
|
|
53
|
+
working_dir = Atoms::ExecutionContext.resolve_working_dir(
|
|
54
|
+
working_dir: options[:working_dir],
|
|
55
|
+
subprocess_env: subprocess_env
|
|
56
|
+
)
|
|
57
|
+
full_prompt = rewrite_skill_commands(full_prompt, working_dir: working_dir)
|
|
58
|
+
|
|
59
|
+
cmd = build_pi_command(full_prompt, options, system_prompt: system_prompt)
|
|
60
|
+
stdout, stderr, status = execute_pi_command(cmd, working_dir: working_dir)
|
|
61
|
+
|
|
62
|
+
parse_pi_response(stdout, stderr, status, full_prompt, options)
|
|
63
|
+
rescue => e
|
|
64
|
+
handle_pi_error(e)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# List available Pi models
|
|
68
|
+
def list_models
|
|
69
|
+
[
|
|
70
|
+
{id: "zai/glm-4.7", name: "GLM 4.7", description: "ZAI default model", context_size: 128_000},
|
|
71
|
+
{id: "anthropic/claude-opus-4-6", name: "Claude Opus 4.6", description: "Anthropic flagship", context_size: 200_000},
|
|
72
|
+
{id: "anthropic/claude-sonnet-4-5", name: "Claude Sonnet 4.5", description: "Anthropic balanced", context_size: 200_000},
|
|
73
|
+
{id: "anthropic/claude-haiku-4-5", name: "Claude Haiku 4.5", description: "Anthropic fast", context_size: 200_000},
|
|
74
|
+
{id: "google-gemini-cli/gemini-2.5-pro", name: "Gemini 2.5 Pro", description: "Google advanced", context_size: 1_000_000},
|
|
75
|
+
{id: "google-gemini-cli/gemini-2.5-flash", name: "Gemini 2.5 Flash", description: "Google fast", context_size: 1_000_000},
|
|
76
|
+
{id: "openai-codex/gpt-5.2", name: "GPT 5.2", description: "OpenAI model", context_size: 128_000}
|
|
77
|
+
]
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
private
|
|
81
|
+
|
|
82
|
+
def format_messages_as_prompt(messages)
|
|
83
|
+
return messages if messages.is_a?(String)
|
|
84
|
+
|
|
85
|
+
formatted = messages.map do |msg|
|
|
86
|
+
role = msg[:role] || msg["role"]
|
|
87
|
+
content = msg[:content] || msg["content"]
|
|
88
|
+
|
|
89
|
+
case role
|
|
90
|
+
when "system"
|
|
91
|
+
"System: #{content}"
|
|
92
|
+
when "user"
|
|
93
|
+
"User: #{content}"
|
|
94
|
+
when "assistant"
|
|
95
|
+
"Assistant: #{content}"
|
|
96
|
+
else
|
|
97
|
+
content
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
formatted.join("\n\n")
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
# Build full prompt, using --system-prompt flag for system content
|
|
105
|
+
# when possible, otherwise prepending to prompt.
|
|
106
|
+
#
|
|
107
|
+
# @param prompt [String] The main user prompt
|
|
108
|
+
# @param options [Hash] Options that may contain system instruction keys
|
|
109
|
+
# @return [Array(String, String)] [prompt, system_prompt] pair
|
|
110
|
+
def build_full_prompt(prompt, options)
|
|
111
|
+
prompt_str = prompt.to_s
|
|
112
|
+
|
|
113
|
+
# If prompt already has system instruction from message formatting, use as-is
|
|
114
|
+
return [prompt_str, nil] if prompt_str.start_with?("System:")
|
|
115
|
+
|
|
116
|
+
system_content = options[:system_instruction] ||
|
|
117
|
+
options[:system] ||
|
|
118
|
+
options[:system_prompt] ||
|
|
119
|
+
@generation_config[:system_prompt]
|
|
120
|
+
|
|
121
|
+
[prompt_str, system_content]
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# Rewrite /name → /skill:name in the prompt for known skills
|
|
125
|
+
def rewrite_skill_commands(prompt, working_dir: nil)
|
|
126
|
+
skills_dir = resolve_skills_dir(working_dir: working_dir)
|
|
127
|
+
return prompt unless skills_dir
|
|
128
|
+
|
|
129
|
+
skill_names = @skill_name_reader.call(skills_dir)
|
|
130
|
+
return prompt if skill_names.empty?
|
|
131
|
+
|
|
132
|
+
Atoms::CommandRewriter.call(prompt, skill_names: skill_names, formatter: Atoms::CommandFormatters::PI_FORMATTER)
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
def resolve_skills_dir(working_dir: nil)
|
|
136
|
+
configured = @options[:skills_dir] || @generation_config[:skills_dir]
|
|
137
|
+
return configured if configured && Dir.exist?(configured)
|
|
138
|
+
|
|
139
|
+
working_dir ||= Atoms::ExecutionContext.resolve_working_dir
|
|
140
|
+
candidate_dir = File.join(working_dir, ".pi", "skills")
|
|
141
|
+
candidate_dir if Dir.exist?(candidate_dir)
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# Build the pi command array
|
|
145
|
+
#
|
|
146
|
+
# @param full_prompt [String] The complete prompt
|
|
147
|
+
# @param options [Hash] Generation options
|
|
148
|
+
# @param system_prompt [String, nil] System prompt for --system-prompt flag
|
|
149
|
+
# @return [Array<String>] Command array
|
|
150
|
+
def build_pi_command(full_prompt, options, system_prompt: nil)
|
|
151
|
+
cmd = ["pi"]
|
|
152
|
+
|
|
153
|
+
# Print mode (non-interactive, one-shot)
|
|
154
|
+
cmd << "-p" << full_prompt.to_s
|
|
155
|
+
|
|
156
|
+
# No session (stateless)
|
|
157
|
+
cmd << "--no-session"
|
|
158
|
+
|
|
159
|
+
# No skills (we handle skill content ourselves in one-shot mode)
|
|
160
|
+
cmd << "--no-skills"
|
|
161
|
+
|
|
162
|
+
# System prompt via native flag if available
|
|
163
|
+
if system_prompt
|
|
164
|
+
cmd << "--system-prompt" << system_prompt
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Provider/model from the model string (format: "provider/model")
|
|
168
|
+
model_to_use = @model || @generation_config[:model] || DEFAULT_MODEL
|
|
169
|
+
provider_name, model_id = split_provider_model(model_to_use)
|
|
170
|
+
if provider_name && model_id
|
|
171
|
+
cmd << "--provider" << provider_name
|
|
172
|
+
cmd << "--model" << model_id
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
# User CLI args after generated flags (last-wins precedence)
|
|
176
|
+
cmd.concat(normalized_cli_args(options))
|
|
177
|
+
|
|
178
|
+
cmd
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Split "provider/model" into ["provider", "model"]
|
|
182
|
+
# Handles multi-segment providers like "google-gemini-cli/gemini-2.5-pro"
|
|
183
|
+
# Also handles nested providers like "openrouter:openai/gpt-oss-120b"
|
|
184
|
+
def split_provider_model(model_string)
|
|
185
|
+
return [nil, nil] unless model_string
|
|
186
|
+
|
|
187
|
+
# Check for nested provider pattern (e.g., "openrouter:openai/model")
|
|
188
|
+
if model_string.count(":") > 0
|
|
189
|
+
parts = model_string.split(":", 2)
|
|
190
|
+
if parts.length == 2 && parts[1].include?("/")
|
|
191
|
+
# Nested provider: "openrouter:openai/model" -> ["openrouter", "openai/model"]
|
|
192
|
+
return [parts[0], parts[1]]
|
|
193
|
+
end
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
# Standard provider/model format
|
|
197
|
+
parts = model_string.split("/", 2)
|
|
198
|
+
return [nil, nil] unless parts.length == 2
|
|
199
|
+
|
|
200
|
+
[parts[0], parts[1]]
|
|
201
|
+
end
|
|
202
|
+
|
|
203
|
+
def execute_pi_command(cmd, timeout: nil, working_dir: nil)
|
|
204
|
+
timeout_val = timeout || @options[:timeout] || 120
|
|
205
|
+
Molecules::SafeCapture.call(
|
|
206
|
+
cmd,
|
|
207
|
+
timeout: timeout_val,
|
|
208
|
+
stdin_data: "",
|
|
209
|
+
chdir: working_dir,
|
|
210
|
+
provider_name: "Pi"
|
|
211
|
+
)
|
|
212
|
+
end
|
|
213
|
+
|
|
214
|
+
def parse_pi_response(stdout, stderr, status, prompt, options)
|
|
215
|
+
unless status.success?
|
|
216
|
+
error_msg = stderr.empty? ? stdout : stderr
|
|
217
|
+
|
|
218
|
+
if error_msg.include?("401") || error_msg.include?("Unauthorized")
|
|
219
|
+
raise Ace::LLM::AuthenticationError, "Pi authentication failed. Run 'pi login' to configure credentials."
|
|
220
|
+
end
|
|
221
|
+
|
|
222
|
+
raise Ace::LLM::ProviderError, "Pi CLI failed: #{error_msg}"
|
|
223
|
+
end
|
|
224
|
+
|
|
225
|
+
# Detect NDJSON: starts with {"type":"
|
|
226
|
+
if stdout.strip.start_with?('{"type":"')
|
|
227
|
+
text, usage = parse_ndjson(stdout)
|
|
228
|
+
response = {"usage" => normalize_usage(usage)}
|
|
229
|
+
else
|
|
230
|
+
# Plain text output
|
|
231
|
+
text = stdout.strip
|
|
232
|
+
response = {}
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
metadata = build_metadata(response, text, prompt, options)
|
|
236
|
+
|
|
237
|
+
{
|
|
238
|
+
text: text,
|
|
239
|
+
metadata: metadata
|
|
240
|
+
}
|
|
241
|
+
end
|
|
242
|
+
|
|
243
|
+
def build_metadata(response, text, prompt, options)
|
|
244
|
+
usage = response["usage"] || {}
|
|
245
|
+
|
|
246
|
+
prompt_tokens = usage["input_tokens"] || (prompt.to_s.length / 4).round
|
|
247
|
+
output_tokens = usage["output_tokens"] || (text.length / 4).round
|
|
248
|
+
|
|
249
|
+
{
|
|
250
|
+
provider: "pi",
|
|
251
|
+
model: @model || DEFAULT_MODEL,
|
|
252
|
+
input_tokens: prompt_tokens,
|
|
253
|
+
output_tokens: output_tokens,
|
|
254
|
+
total_tokens: prompt_tokens + output_tokens,
|
|
255
|
+
finish_reason: response["finish_reason"] || "success",
|
|
256
|
+
timestamp: Time.now.utc.iso8601
|
|
257
|
+
}
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
# Parse NDJSON output from Pi CLI when --mode json is used.
|
|
261
|
+
# NDJSON is one JSON object per line, with event types like message_end, agent_end.
|
|
262
|
+
#
|
|
263
|
+
# @param stdout [String] The raw stdout from Pi CLI
|
|
264
|
+
# @return [Array<String, Hash>] Tuple of [extracted_text, usage_hash]
|
|
265
|
+
def parse_ndjson(stdout)
|
|
266
|
+
lines = stdout.split("\n")
|
|
267
|
+
text_parts = []
|
|
268
|
+
usage = nil
|
|
269
|
+
|
|
270
|
+
lines.each do |line|
|
|
271
|
+
next if line.strip.empty?
|
|
272
|
+
event = JSON.parse(line)
|
|
273
|
+
case event["type"]
|
|
274
|
+
when "message_end"
|
|
275
|
+
# Extract text from content array
|
|
276
|
+
content = event.dig("message", "content") || []
|
|
277
|
+
content.each do |c|
|
|
278
|
+
text_parts << c["text"] if c["type"] == "text"
|
|
279
|
+
end
|
|
280
|
+
usage = event.dig("message", "usage")
|
|
281
|
+
when "agent_end"
|
|
282
|
+
# Fallback: extract from messages array
|
|
283
|
+
messages = event["messages"] || []
|
|
284
|
+
messages.each do |msg|
|
|
285
|
+
content = msg["content"] || []
|
|
286
|
+
content.each do |c|
|
|
287
|
+
text_parts << c["text"] if c["type"] == "text"
|
|
288
|
+
end
|
|
289
|
+
end
|
|
290
|
+
usage = messages.dig(0, "usage") if usage.nil?
|
|
291
|
+
end
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
text = text_parts.join("")
|
|
295
|
+
[text, usage || {}]
|
|
296
|
+
rescue JSON::ParserError
|
|
297
|
+
# If parsing fails, treat as plain text
|
|
298
|
+
[stdout.strip, {}]
|
|
299
|
+
end
|
|
300
|
+
|
|
301
|
+
# Normalize Pi usage field names to our standard format.
|
|
302
|
+
# Pi uses "input"/"output", we normalize to "input_tokens"/"output_tokens".
|
|
303
|
+
#
|
|
304
|
+
# @param usage [Hash] Raw usage hash from Pi response
|
|
305
|
+
# @return [Hash] Normalized usage hash
|
|
306
|
+
def normalize_usage(usage)
|
|
307
|
+
return {} unless usage
|
|
308
|
+
{
|
|
309
|
+
"input_tokens" => usage["input"] || usage["input_tokens"],
|
|
310
|
+
"output_tokens" => usage["output"] || usage["output_tokens"]
|
|
311
|
+
}.compact
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
def pi_available?
|
|
315
|
+
system("which pi > /dev/null 2>&1")
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def validate_pi_availability!
|
|
319
|
+
unless pi_available?
|
|
320
|
+
raise Ace::LLM::ProviderError, "Pi CLI not found. Install from: https://pi.dev"
|
|
321
|
+
end
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
def handle_pi_error(error)
|
|
325
|
+
raise error
|
|
326
|
+
end
|
|
327
|
+
end
|
|
328
|
+
end
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "ace/llm"
|
|
4
|
+
require_relative "cli/version"
|
|
5
|
+
require_relative "cli/molecules/safe_capture"
|
|
6
|
+
|
|
7
|
+
module Ace
|
|
8
|
+
module LLM
|
|
9
|
+
module Providers
|
|
10
|
+
module CLI
|
|
11
|
+
# Main entry point for CLI providers
|
|
12
|
+
# Simply requires the provider client classes
|
|
13
|
+
# Configuration comes from YAML files in .ace-defaults/llm/providers/
|
|
14
|
+
class << self
|
|
15
|
+
def setup
|
|
16
|
+
# Require all CLI provider client classes
|
|
17
|
+
require_cli_providers
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
|
|
22
|
+
def require_cli_providers
|
|
23
|
+
# Require each CLI provider client
|
|
24
|
+
providers = %w[
|
|
25
|
+
claude_code_client
|
|
26
|
+
claude_oai_client
|
|
27
|
+
codex_client
|
|
28
|
+
open_code_client
|
|
29
|
+
codex_oai_client
|
|
30
|
+
gemini_client
|
|
31
|
+
pi_client
|
|
32
|
+
]
|
|
33
|
+
|
|
34
|
+
providers.each do |provider|
|
|
35
|
+
require_relative "cli/#{provider}"
|
|
36
|
+
rescue LoadError => e
|
|
37
|
+
warn "Could not load CLI provider #{provider}: #{e.message}"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Auto-setup on require
|
|
43
|
+
setup
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: ace-llm-providers-cli
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.27.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Michal Czyz
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: ace-llm
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.26'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.26'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: rake
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '13.0'
|
|
33
|
+
type: :development
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '13.0'
|
|
40
|
+
- !ruby/object:Gem::Dependency
|
|
41
|
+
name: rspec
|
|
42
|
+
requirement: !ruby/object:Gem::Requirement
|
|
43
|
+
requirements:
|
|
44
|
+
- - "~>"
|
|
45
|
+
- !ruby/object:Gem::Version
|
|
46
|
+
version: '3.0'
|
|
47
|
+
type: :development
|
|
48
|
+
prerelease: false
|
|
49
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
50
|
+
requirements:
|
|
51
|
+
- - "~>"
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '3.0'
|
|
54
|
+
- !ruby/object:Gem::Dependency
|
|
55
|
+
name: rubocop
|
|
56
|
+
requirement: !ruby/object:Gem::Requirement
|
|
57
|
+
requirements:
|
|
58
|
+
- - "~>"
|
|
59
|
+
- !ruby/object:Gem::Version
|
|
60
|
+
version: '1.21'
|
|
61
|
+
type: :development
|
|
62
|
+
prerelease: false
|
|
63
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
64
|
+
requirements:
|
|
65
|
+
- - "~>"
|
|
66
|
+
- !ruby/object:Gem::Version
|
|
67
|
+
version: '1.21'
|
|
68
|
+
description: Extends ace-llm with CLI-based LLM providers like Claude Code, Codex,
|
|
69
|
+
Gemini CLI, OpenCode, and pi-agent
|
|
70
|
+
email:
|
|
71
|
+
- mc@cs3b.com
|
|
72
|
+
executables:
|
|
73
|
+
- ace-llm-providers-cli-check
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- ".ace-defaults/llm/providers/claude.yml"
|
|
78
|
+
- ".ace-defaults/llm/providers/codex.yml"
|
|
79
|
+
- ".ace-defaults/llm/providers/codexoss.yml"
|
|
80
|
+
- ".ace-defaults/llm/providers/gemini.yml"
|
|
81
|
+
- ".ace-defaults/llm/providers/opencode.yml"
|
|
82
|
+
- ".ace-defaults/llm/providers/pi.yml"
|
|
83
|
+
- CHANGELOG.md
|
|
84
|
+
- LICENSE
|
|
85
|
+
- README.md
|
|
86
|
+
- Rakefile
|
|
87
|
+
- exe/ace-llm-providers-cli-check
|
|
88
|
+
- lib/ace/llm/providers/cli.rb
|
|
89
|
+
- lib/ace/llm/providers/cli/atoms/args_normalizer.rb
|
|
90
|
+
- lib/ace/llm/providers/cli/atoms/auth_checker.rb
|
|
91
|
+
- lib/ace/llm/providers/cli/atoms/command_formatters.rb
|
|
92
|
+
- lib/ace/llm/providers/cli/atoms/command_rewriter.rb
|
|
93
|
+
- lib/ace/llm/providers/cli/atoms/execution_context.rb
|
|
94
|
+
- lib/ace/llm/providers/cli/atoms/provider_detector.rb
|
|
95
|
+
- lib/ace/llm/providers/cli/atoms/session_finders/claude_session_finder.rb
|
|
96
|
+
- lib/ace/llm/providers/cli/atoms/session_finders/codex_session_finder.rb
|
|
97
|
+
- lib/ace/llm/providers/cli/atoms/session_finders/gemini_session_finder.rb
|
|
98
|
+
- lib/ace/llm/providers/cli/atoms/session_finders/open_code_session_finder.rb
|
|
99
|
+
- lib/ace/llm/providers/cli/atoms/session_finders/pi_session_finder.rb
|
|
100
|
+
- lib/ace/llm/providers/cli/atoms/skill_command_rewriter.rb
|
|
101
|
+
- lib/ace/llm/providers/cli/atoms/worktree_dir_resolver.rb
|
|
102
|
+
- lib/ace/llm/providers/cli/claude_code_client.rb
|
|
103
|
+
- lib/ace/llm/providers/cli/claude_oai_client.rb
|
|
104
|
+
- lib/ace/llm/providers/cli/cli_args_support.rb
|
|
105
|
+
- lib/ace/llm/providers/cli/codex_client.rb
|
|
106
|
+
- lib/ace/llm/providers/cli/codex_oai_client.rb
|
|
107
|
+
- lib/ace/llm/providers/cli/gemini_client.rb
|
|
108
|
+
- lib/ace/llm/providers/cli/molecules/health_checker.rb
|
|
109
|
+
- lib/ace/llm/providers/cli/molecules/safe_capture.rb
|
|
110
|
+
- lib/ace/llm/providers/cli/molecules/session_finder.rb
|
|
111
|
+
- lib/ace/llm/providers/cli/molecules/skill_name_reader.rb
|
|
112
|
+
- lib/ace/llm/providers/cli/open_code_client.rb
|
|
113
|
+
- lib/ace/llm/providers/cli/pi_client.rb
|
|
114
|
+
- lib/ace/llm/providers/cli/version.rb
|
|
115
|
+
homepage: https://github.com/cs3b/ace
|
|
116
|
+
licenses:
|
|
117
|
+
- MIT
|
|
118
|
+
metadata:
|
|
119
|
+
homepage_uri: https://github.com/cs3b/ace
|
|
120
|
+
source_code_uri: https://github.com/cs3b/ace/tree/main/ace-llm-providers-cli/
|
|
121
|
+
changelog_uri: https://github.com/cs3b/ace/blob/main/ace-llm-providers-cli/CHANGELOG.md
|
|
122
|
+
rdoc_options: []
|
|
123
|
+
require_paths:
|
|
124
|
+
- lib
|
|
125
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
126
|
+
requirements:
|
|
127
|
+
- - ">="
|
|
128
|
+
- !ruby/object:Gem::Version
|
|
129
|
+
version: 3.2.0
|
|
130
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
131
|
+
requirements:
|
|
132
|
+
- - ">="
|
|
133
|
+
- !ruby/object:Gem::Version
|
|
134
|
+
version: '0'
|
|
135
|
+
requirements: []
|
|
136
|
+
rubygems_version: 3.6.9
|
|
137
|
+
specification_version: 4
|
|
138
|
+
summary: CLI-based LLM providers for ace-llm
|
|
139
|
+
test_files: []
|